@Override
    public void layoutContainer(Container parent) {
      JScrollPane pane = (JScrollPane)parent;
      // Calculate inner bounds of the scroll pane
      Rectangle viewportBounds = new Rectangle(pane.getWidth(), pane.getHeight());
      final boolean isOverlappingScrollBar = isOverlappingScrollBar(pane);
      @NotNull Rectangle wholePaneBounds = viewportBounds.getBounds();
      JBInsets.removeFrom(viewportBounds, pane.getInsets());
      // Determine positions of scroll bars on the scroll pane
      Object property = pane.getClientProperty(Flip.class);
      Flip flip = property instanceof Flip ? (Flip)property : Flip.NONE;
      boolean hsbOnTop = flip == Flip.BOTH || flip == Flip.VERTICAL;
      boolean vsbOnLeft = pane.getComponentOrientation().isLeftToRight()
                          ? flip == Flip.BOTH || flip == Flip.HORIZONTAL
                          : flip == Flip.NONE || flip == Flip.VERTICAL;
      // If there's a visible row header remove the space it needs.
      // The row header is treated as if it were fixed width, arbitrary height.
      Rectangle rowHeadBounds = new Rectangle(viewportBounds.x, 0, 0, 0);
      if (rowHead != null && rowHead.isVisible()) {
        rowHeadBounds.width = min(viewportBounds.width, rowHead.getPreferredSize().width);
        viewportBounds.width -= rowHeadBounds.width;
        if (vsbOnLeft) {
          rowHeadBounds.x += viewportBounds.width;
        }
        else {
          viewportBounds.x += rowHeadBounds.width;
        }
      }
      // If there's a visible column header remove the space it needs.
      // The column header is treated as if it were fixed height, arbitrary width.
      Rectangle colHeadBounds = new Rectangle(0, viewportBounds.y, 0, 0);
      if (colHead != null && colHead.isVisible()) {
        colHeadBounds.height = min(viewportBounds.height, colHead.getPreferredSize().height);
        viewportBounds.height -= colHeadBounds.height;
        if (hsbOnTop) {
          colHeadBounds.y += viewportBounds.height;
        }
        else {
          viewportBounds.y += colHeadBounds.height;
        }
      }
      // If there's a JScrollPane.viewportBorder, remove the space it occupies
      Border border = pane.getViewportBorder();
      Insets insets = border == null ? null : border.getBorderInsets(parent);
      JBInsets.removeFrom(viewportBounds, insets);
      if (insets == null) insets = EMPTY_INSETS;
      // At this point:
      // colHeadBounds is correct except for its width and x
      // rowHeadBounds is correct except for its height and y
      // bounds - the space available for the viewport and scroll bars
      // Once we're through computing the dimensions of these three parts
      // we can go back and set the bounds for the corners and the dimensions of
      // colHeadBounds.x, colHeadBounds.width, rowHeadBounds.y, rowHeadBounds.height.

      // Don't bother checking the Scrollable methods if there is no room for the viewport,
      // we aren't going to show any scroll bars in this case anyway.
      boolean isEmpty = viewportBounds.width < 0 || viewportBounds.height < 0;

      Component view = viewport == null ? null : viewport.getView();
      Dimension viewPreferredSize = view == null ? new Dimension() : view.getPreferredSize();
      if (view instanceof JComponent && !view.isPreferredSizeSet()) {
        JBInsets.removeFrom(viewPreferredSize, JBViewport.getViewInsets((JComponent)view));
      }
      Dimension viewportExtentSize = viewport == null ? new Dimension() : viewport.toViewCoordinates(viewportBounds.getSize());

      // workaround for installed JBViewport.ViewBorder:
      // do not hide scroll bars if view is not aligned
      Point viewLocation = new Point();
      if (view != null) viewLocation = view.getLocation(viewLocation);
      // If there's a vertical scroll bar and we need one, allocate space for it.
      // A vertical scroll bar is considered to be fixed width, arbitrary height.
      boolean vsbRequiresSpace = false;
      boolean vsbNeeded = false;
      int vsbPolicy = pane.getVerticalScrollBarPolicy();
      if (!isEmpty && vsbPolicy != VERTICAL_SCROLLBAR_NEVER) {
        vsbNeeded = vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS
                    || isVerticalScrollBarNeeded(view, viewLocation, viewPreferredSize, viewportExtentSize);
      }
      Rectangle vsbBounds = new Rectangle(0, viewportBounds.y - insets.top, 0, 0);
      if (vsb != null) {
        if (isAlwaysOpaque(view)) vsb.setOpaque(true);
        vsbRequiresSpace = vsb.isOpaque() && !isOverlappingScrollBar;
        if (vsbNeeded) {
          adjustForVSB(viewportBounds, insets, vsbBounds, vsbRequiresSpace, vsbOnLeft, isOverlappingScrollBar, wholePaneBounds);
          if (vsbRequiresSpace && viewport != null) {
            viewportExtentSize = viewport.toViewCoordinates(viewportBounds.getSize());
          }
        }
      }
      // If there's a horizontal scroll bar and we need one, allocate space for it.
      // A horizontal scroll bar is considered to be fixed height, arbitrary width.
      boolean hsbRequiresSpace = false;
      boolean hsbNeeded = false;
      int hsbPolicy = pane.getHorizontalScrollBarPolicy();
      if (!isEmpty && hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER) {
        hsbNeeded = hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS
                    || isHorizontalScrollBarNeeded(view, viewLocation, viewPreferredSize, viewportExtentSize);
      }
      Rectangle hsbBounds = new Rectangle(viewportBounds.x - insets.left, 0, 0, 0);
      if (hsb != null) {
        if (isAlwaysOpaque(view)) hsb.setOpaque(true);
        hsbRequiresSpace = hsb.isOpaque() && !isOverlappingScrollBar;
        if (hsbNeeded) {
          adjustForHSB(viewportBounds, insets, hsbBounds, hsbRequiresSpace, hsbOnTop, isOverlappingScrollBar, wholePaneBounds);
          // If we added the horizontal scrollbar and reduced the vertical space
          // we may have to add the vertical scrollbar, if that hasn't been done so already.
          if (vsb != null && !vsbNeeded && vsbPolicy != VERTICAL_SCROLLBAR_NEVER) {
            if (!hsbRequiresSpace) {
              viewPreferredSize.height += hsbBounds.height;
            }
            else if (viewport != null) {
              viewportExtentSize = viewport.toViewCoordinates(viewportBounds.getSize());
            }
            vsbNeeded = isScrollBarNeeded(viewLocation.y, viewPreferredSize.height, viewportExtentSize.height);
            if (vsbNeeded) adjustForVSB(viewportBounds, insets, vsbBounds, vsbRequiresSpace, vsbOnLeft, isOverlappingScrollBar, wholePaneBounds);
          }
        }
      }
      // Set the size of the viewport first, and then recheck the Scrollable methods.
      // Some components base their return values for the Scrollable methods on the size of the viewport,
      // so that if we don't ask after resetting the bounds we may have gotten the wrong answer.
      if (viewport != null) {
        viewport.setBounds(viewportBounds);
        if (!isEmpty && view instanceof Scrollable) {
          viewportExtentSize = viewport.toViewCoordinates(viewportBounds.getSize());

          boolean vsbNeededOld = vsbNeeded;
          if (vsb != null && vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED) {
            boolean vsbNeededNew = isVerticalScrollBarNeeded(view, viewLocation, viewPreferredSize, viewportExtentSize);
            if (vsbNeeded != vsbNeededNew) {
              vsbNeeded = vsbNeededNew;
              if (vsbNeeded) {
                adjustForVSB(viewportBounds, insets, vsbBounds, vsbRequiresSpace, vsbOnLeft, isOverlappingScrollBar, wholePaneBounds);
              }
              else if (vsbRequiresSpace) {
                viewportBounds.width += vsbBounds.width;
              }
              if (vsbRequiresSpace) viewportExtentSize = viewport.toViewCoordinates(viewportBounds.getSize());
            }
          }
          boolean hsbNeededOld = hsbNeeded;
          if (hsb != null && hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED) {
            boolean hsbNeededNew = isHorizontalScrollBarNeeded(view, viewLocation, viewPreferredSize, viewportExtentSize);
            if (hsbNeeded != hsbNeededNew) {
              hsbNeeded = hsbNeededNew;
              if (hsbNeeded) {
                adjustForHSB(viewportBounds, insets, hsbBounds, hsbRequiresSpace, hsbOnTop, isOverlappingScrollBar, wholePaneBounds);
              }
              else if (hsbRequiresSpace) {
                viewportBounds.height += hsbBounds.height;
              }
              if (hsbRequiresSpace && vsb != null && !vsbNeeded && vsbPolicy != VERTICAL_SCROLLBAR_NEVER) {
                viewportExtentSize = viewport.toViewCoordinates(viewportBounds.getSize());
                vsbNeeded = isScrollBarNeeded(viewLocation.y, viewPreferredSize.height, viewportExtentSize.height);
                if (vsbNeeded) {
                  adjustForVSB(viewportBounds, insets, vsbBounds, vsbRequiresSpace, vsbOnLeft, isOverlappingScrollBar, wholePaneBounds);
                }
              }
            }
          }
          if (hsbNeededOld != hsbNeeded || vsbNeededOld != vsbNeeded) {
            viewport.setBounds(viewportBounds);
            // You could argue that we should recheck the Scrollable methods again until they stop changing,
            // but they might never stop changing, so we stop here and don't do any additional checks.
          }
        }
      }
      // Set the bounds of the row header.
      rowHeadBounds.y = viewportBounds.y - insets.top;
      rowHeadBounds.height = viewportBounds.height + insets.top + insets.bottom;
      boolean fillLowerCorner = false;
      if (rowHead != null) {
        if (hsbRequiresSpace) {
          Component corner = hsbOnTop ? (vsbOnLeft ? upperRight : upperLeft) : (vsbOnLeft ? lowerRight : lowerLeft);
          fillLowerCorner = corner == null && UIManager.getBoolean("ScrollPane.fillLowerCorner");
          if (!fillLowerCorner && ScrollSettings.isHeaderOverCorner(viewport)) {
            if (hsbOnTop) rowHeadBounds.y -= hsbBounds.height;
            rowHeadBounds.height += hsbBounds.height;
          }
        }
        rowHead.setBounds(rowHeadBounds);
        rowHead.putClientProperty(Alignment.class, vsbOnLeft ? Alignment.RIGHT : Alignment.LEFT);
      }
      // Set the bounds of the column header.
      colHeadBounds.x = viewportBounds.x - insets.left;
      colHeadBounds.width = viewportBounds.width + insets.left + insets.right;
      boolean fillUpperCorner = false;
      boolean hasStatusComponent = statusComponent != null && statusComponent.isShowing();
      if (colHead != null) {
        if (vsbRequiresSpace) {
          Component corner = vsbOnLeft ? (hsbOnTop ? lowerLeft : upperLeft) : (hsbOnTop ? lowerRight : upperRight);
          fillUpperCorner = corner == null && UIManager.getBoolean("ScrollPane.fillUpperCorner") && !hasStatusComponent;
          if (!fillUpperCorner && ScrollSettings.isHeaderOverCorner(viewport)) {
            if (vsbOnLeft) colHeadBounds.x -= vsbBounds.width;
            colHeadBounds.width += vsbBounds.width;
          }
        }
        colHead.setBounds(colHeadBounds);
        colHead.putClientProperty(Alignment.class, hsbOnTop ? Alignment.BOTTOM : Alignment.TOP);
      }
      // Calculate overlaps for translucent scroll bars
      int overlapWidth = 0;
      int overlapHeight = 0;
      if (vsbNeeded && !vsbRequiresSpace && hsbNeeded && !hsbRequiresSpace) {
        overlapWidth = vsbBounds.width; // shrink horizontally
        //overlapHeight = hsbBounds.height; // shrink vertically
      }
      // Set the bounds of the vertical scroll bar.
      vsbBounds.y = viewportBounds.y - insets.top;
      vsbBounds.height = viewportBounds.height + insets.top + insets.bottom;

      // Forked bounds that are actually used for setting vertical scroll bar bounds
      // after possible modification with statusComponent bounds.
      Rectangle actualVsbBounds = new Rectangle(vsbBounds);
      if (vsb != null) {
        vsb.setVisible(vsbNeeded);
        if (vsbNeeded) {
          if (fillUpperCorner) {
            // This is used primarily for GTK L&F, which needs to extend
            // the vertical scrollbar to fill the upper corner near the column header.
            // Note that we skip this step (and use the default behavior)
            // if the user has set a custom corner component.
            if (!hsbOnTop) vsbBounds.y -= colHeadBounds.height;
            vsbBounds.height += colHeadBounds.height;
          }
          int overlapY = !hsbOnTop ? 0 : overlapHeight;
          actualVsbBounds.y += overlapY;
          actualVsbBounds.height -= overlapHeight;
          vsb.putClientProperty(Alignment.class, vsbOnLeft ? Alignment.LEFT : Alignment.RIGHT);
        }
        // Modify the bounds of the translucent scroll bar.
        if (!vsbRequiresSpace) {
          if (!vsbOnLeft) vsbBounds.x += vsbBounds.width;
          vsbBounds.width = 0;
        }
      }
      // Set the bounds of the horizontal scroll bar.
      hsbBounds.x = viewportBounds.x - insets.left;
      hsbBounds.width = viewportBounds.width + insets.left + insets.right;
      if (hsb != null) {
        hsb.setVisible(hsbNeeded);
        if (hsbNeeded) {
          if (fillLowerCorner) {
            // This is used primarily for GTK L&F, which needs to extend
            // the horizontal scrollbar to fill the lower corner near the row header.
            // Note that we skip this step (and use the default behavior)
            // if the user has set a custom corner component.
            if (!vsbOnLeft) hsbBounds.x -= rowHeadBounds.width;
            hsbBounds.width += rowHeadBounds.width;
          }
          int overlapX = !vsbOnLeft ? 0 : overlapWidth;
          hsb.setBounds(hsbBounds.x + overlapX, hsbBounds.y, hsbBounds.width - overlapWidth, hsbBounds.height);
          hsb.putClientProperty(Alignment.class, hsbOnTop ? Alignment.TOP : Alignment.BOTTOM);
        }
        // Modify the bounds of the translucent scroll bar.
        if (!hsbRequiresSpace) {
          if (!hsbOnTop) hsbBounds.y += hsbBounds.height;
          hsbBounds.height = 0;
        }
      }

      if (hasStatusComponent) {
        Dimension scSize = statusComponent.getPreferredSize();

        switch (flip) {
          case NONE -> {
            statusComponent.setBounds(actualVsbBounds.x + actualVsbBounds.width - scSize.width, actualVsbBounds.y, scSize.width,
                                      scSize.height);
            actualVsbBounds.y += scSize.height;
          }
          case HORIZONTAL -> {
            statusComponent.setBounds(actualVsbBounds.x, actualVsbBounds.y, scSize.width, scSize.height);
            actualVsbBounds.y += scSize.height;
          }
          case VERTICAL -> statusComponent.setBounds(actualVsbBounds.x + actualVsbBounds.width - scSize.width,
                                                     actualVsbBounds.y + actualVsbBounds.height - scSize.height, scSize.width,
                                                     scSize.height);
          case BOTH -> statusComponent.setBounds(actualVsbBounds.x,
                                                 actualVsbBounds.y + actualVsbBounds.height - scSize.height, scSize.width, scSize.height);
        }

        actualVsbBounds.height -= scSize.height;
      }

      if (vsb != null && vsbNeeded) {
        vsb.setBounds(actualVsbBounds);
      }

      // Set the bounds of the corners.
      Rectangle left = vsbOnLeft ? vsbBounds : rowHeadBounds;
      Rectangle right = vsbOnLeft ? rowHeadBounds : vsbBounds;
      Rectangle upper = hsbOnTop ? hsbBounds : colHeadBounds;
      Rectangle lower = hsbOnTop ? colHeadBounds : hsbBounds;
      if (lowerLeft != null) {
        Rectangle lowerLeftBounds = new Rectangle(left.x, left.y + left.height, 0, 0);
        if (left.width > 0 && lower.height > 0) updateCornerBounds(lowerLeftBounds, lower.x, lower.y + lower.height);
        lowerLeft.setBounds(lowerLeftBounds);
      }
      if (lowerRight != null) {
        Rectangle lowerRightBounds = new Rectangle(lower.x + lower.width, right.y + right.height, 0, 0);
        if (right.width > 0 && lower.height > 0) updateCornerBounds(lowerRightBounds, right.x + right.width, lower.y + lower.height);
        lowerRight.setBounds(lowerRightBounds);
      }
      if (upperLeft != null) {
        Rectangle upperLeftBounds = new Rectangle(left.x, upper.y, 0, 0);
        if (left.width > 0 && upper.height > 0) updateCornerBounds(upperLeftBounds, upper.x, left.y);
        upperLeft.setBounds(upperLeftBounds);
      }
      if (upperRight != null) {
        Rectangle upperRightBounds = new Rectangle(upper.x + upper.width, upper.y, 0, 0);
        if (right.width > 0 && upper.height > 0) updateCornerBounds(upperRightBounds, right.x + right.width, right.y);
        upperRight.setBounds(upperRightBounds);
      }
      if (!vsbRequiresSpace && vsbNeeded || !hsbRequiresSpace && hsbNeeded) {
        fixComponentZOrder(vsb, 0);
        fixComponentZOrder(viewport, -1);
      }
      else if (hasStatusComponent) {
        fixComponentZOrder(statusComponent, 0);
        fixComponentZOrder(viewport, -1);
      }
    }
@Override
    public void layoutContainer(Container parent)