I have reworked parts of the BasicTabbedPaneUI.TabbedPaneLayout layout manager. This makes the layout and switching-between-tabs more efficient and correct (I hope).
This also includes some JDK1.5 additions wrt rollover tabs which wrote some time ago and which I originally wanted to complete first (in the MetalTabbedPaneUI) but which I'll commit now anyway. Also this includes some older adjustments to mouse handling in tabbed panes. The painting of the metal tabs is still ugly, I'll work on this next. 2006-03-20 Roman Kennke <[EMAIL PROTECTED]> * javax/swing/plaf/basic/BasicTabbedPaneUI.java (MouseHandler.mousePressed): Rewritten for clearer and simpler code. (MouseHandler.mouseEntered): Implemented to set the rollover tab. (MouseHandler.mouseMoved): Implemented to set the rollover tab. (MouseHandler.mouseExited): Implemented to unset the rollover tab. (TabbedPaneLayout.calculateLayoutInfo): Don't set the component's bounds here. That is moved into layoutContainer(). (calculateSize): Correctly respect insets. Made code slightly more clear and efficient. (calculateTabRects): Rewritten completely. The old code was not quite right and unstable in some situations. (layoutContainer): Moved layout of tabbed pane's subcomponents here. (tabRunsDirty): New field. (rolloverTab): New field. (tabForCoordinate): Rewritten for simplicity and correctness. (setRolloverTab): New method. (getRolloverTab): New method. /Roman -- “Improvement makes straight roads, but the crooked roads, without Improvement, are roads of Genius.” - William Blake
Index: javax/swing/plaf/basic/BasicTabbedPaneUI.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java,v retrieving revision 1.33 diff -u -r1.33 BasicTabbedPaneUI.java --- javax/swing/plaf/basic/BasicTabbedPaneUI.java 16 Feb 2006 14:39:14 -0000 1.33 +++ javax/swing/plaf/basic/BasicTabbedPaneUI.java 20 Mar 2006 11:12:43 -0000 @@ -130,52 +130,49 @@ */ public void mousePressed(MouseEvent e) { - int x = e.getX(); - int y = e.getY(); - int tabCount = tabPane.getTabCount(); - - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + if (tabPane.isEnabled()) { - if (e.getSource() == incrButton) + int index = tabForCoordinate(tabPane, e.getX(), e.getY()); + if (index >= 0 && tabPane.isEnabledAt(index)) { - if (++currentScrollLocation >= tabCount) - currentScrollLocation = tabCount - 1; - - int width = 0; - for (int i = currentScrollLocation - 1; i < tabCount; i++) - width += rects[i].width; - if (width < viewport.getWidth()) - // FIXME: Still getting mouse events after the button is disabled. - // incrButton.setEnabled(false); - currentScrollLocation--; - else if (! decrButton.isEnabled()) - decrButton.setEnabled(true); - tabPane.revalidate(); - tabPane.repaint(); - return; - } - else if (e.getSource() == decrButton) - { - if (--currentScrollLocation < 0) - currentScrollLocation = 0; - if (currentScrollLocation == 0) - decrButton.setEnabled(false); - else if (! incrButton.isEnabled()) - incrButton.setEnabled(true); - tabPane.revalidate(); - tabPane.repaint(); - return; + tabPane.setSelectedIndex(index); } } + } - int index = tabForCoordinate(tabPane, x, y); - - // We need to check since there are areas where tabs cannot be - // e.g. in the inset area. - if (index != -1 && tabPane.isEnabledAt(index)) - tabPane.setSelectedIndex(index); - tabPane.revalidate(); - tabPane.repaint(); + /** + * Receives notification when the mouse pointer has entered the tabbed + * pane. + * + * @param ev the mouse event + */ + public void mouseEntered(MouseEvent ev) + { + int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY()); + setRolloverTab(tabIndex); + } + + /** + * Receives notification when the mouse pointer has exited the tabbed + * pane. + * + * @param ev the mouse event + */ + public void mouseExited(MouseEvent ev) + { + setRolloverTab(-1); + } + + /** + * Receives notification when the mouse pointer has moved over the tabbed + * pane. + * + * @param ev the mouse event + */ + public void mouseMoved(MouseEvent ev) + { + int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY()); + setRolloverTab(tabIndex); } } @@ -241,21 +238,10 @@ */ public void calculateLayoutInfo() { - assureRectsCreated(tabPane.getTabCount()); - contentRect = SwingUtilities.calculateInnerArea(tabPane, contentRect); - - calculateTabRects(tabPane.getTabPlacement(), tabPane.getTabCount()); - - if (tabPane.getSelectedIndex() != -1) - { - Component visible = getVisibleComponent(); - Insets insets = getContentBorderInsets(tabPane.getTabPlacement()); - if (visible != null) - visible.setBounds(contentRect.x + insets.left, - contentRect.y + insets.top, - contentRect.width - insets.left - insets.right, - contentRect.height - insets.top - insets.bottom); - } + int count = tabPane.getTabCount(); + assureRectsCreated(count); + calculateTabRects(tabPane.getTabPlacement(), count); + tabRunsDirty = false; } /** @@ -269,45 +255,51 @@ protected Dimension calculateSize(boolean minimum) { int tabPlacement = tabPane.getTabPlacement(); + int width = 0; int height = 0; - - int componentHeight = 0; - int componentWidth = 0; Component c; Dimension dims; + + // Find out the minimum/preferred size to display the largest child + // of the tabbed pane. for (int i = 0; i < tabPane.getTabCount(); i++) { c = tabPane.getComponentAt(i); if (c == null) continue; - calcRect = c.getBounds(); - dims = c.getPreferredSize(); + dims = minimum ? c.getMinimumSize() : c.getPreferredSize(); if (dims != null) { - componentHeight = Math.max(componentHeight, dims.height); - componentWidth = Math.max(componentWidth, dims.width); + height = Math.max(height, dims.height); + width = Math.max(width, dims.width); } } + + Insets tabAreaInsets = getTabAreaInsets(tabPlacement); if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { int min = calculateMaxTabWidth(tabPlacement); - width = Math.max(min, componentWidth); - - int tabAreaHeight = preferredTabAreaHeight(tabPlacement, width); - height = tabAreaHeight + componentHeight; + width = Math.max(min, width); + int tabAreaHeight = preferredTabAreaHeight(tabPlacement, + width - tabAreaInsets.left + -tabAreaInsets.right); + height += tabAreaHeight; } else { int min = calculateMaxTabHeight(tabPlacement); - height = Math.max(min, componentHeight); - - int tabAreaWidth = preferredTabAreaWidth(tabPlacement, height); - width = tabAreaWidth + componentWidth; + height = Math.max(min, height); + int tabAreaWidth = preferredTabAreaWidth(tabPlacement, + height - tabAreaInsets.top + - tabAreaInsets.bottom); + width += tabAreaWidth; } - return new Dimension(width, height); + Insets tabPaneInsets = tabPane.getInsets(); + return new Dimension(width + tabPaneInsets.left + tabPaneInsets.right, + height + tabPaneInsets.top + tabPaneInsets.bottom); } // if tab placement is LEFT OR RIGHT, they share width. @@ -330,192 +322,197 @@ */ protected void calculateTabRects(int tabPlacement, int tabCount) { + Insets insets = tabPane.getInsets(); + Insets tabAreaInsets = getTabAreaInsets(tabPlacement); + Dimension size = tabPane.getSize(); + + // The coordinates of the upper left corner of the tab area. + int x; + int y; + // The location at which the runs must be broken. + int breakAt; + + // Calculate the bounds for the tab area. + switch (tabPlacement) + { + case LEFT: + maxTabWidth = calculateMaxTabWidth(tabPlacement); + x = insets.left + tabAreaInsets.left; + y = insets.top + tabAreaInsets.top; + breakAt = size.height - (insets.bottom + tabAreaInsets.bottom); + break; + case RIGHT: + maxTabWidth = calculateMaxTabWidth(tabPlacement); + x = size.width - (insets.right + tabAreaInsets.right) - maxTabWidth; + y = insets.top + tabAreaInsets.top; + breakAt = size.height - (insets.bottom + tabAreaInsets.bottom); + break; + case BOTTOM: + maxTabHeight = calculateMaxTabHeight(tabPlacement); + x = insets.left + tabAreaInsets.left; + y = size.height - (insets.bottom + tabAreaInsets.bottom) + - maxTabHeight; + breakAt = size.width - (insets.right + tabAreaInsets.right); + break; + case TOP: + default: + maxTabHeight = calculateMaxTabHeight(tabPlacement); + x = insets.left + tabAreaInsets.left; + y = insets.top + tabAreaInsets.top; + breakAt = size.width - (insets.right + tabAreaInsets.right); + break; + } + if (tabCount == 0) return; FontMetrics fm = getFontMetrics(); - SwingUtilities.calculateInnerArea(tabPane, calcRect); - Insets tabAreaInsets = getTabAreaInsets(tabPlacement); - Insets insets = tabPane.getInsets(); - int max = 0; - int runs = 0; - int start = getTabRunIndent(tabPlacement, 1); + runCount = 0; + selectedRun = -1; + int selectedIndex = tabPane.getSelectedIndex(); + + Rectangle rect; + + // Go through all the tabs and build the tab runs. if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int maxHeight = calculateMaxTabHeight(tabPlacement); - - calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; - max = calcRect.width + tabAreaInsets.left + insets.left; - start += tabAreaInsets.left + insets.left; - int width = 0; - int runWidth = start; - for (int i = 0; i < tabCount; i++) { - width = calculateTabWidth(tabPlacement, i, fm); - if (runWidth + width > max) + rect = rects[i]; + if (i > 0) { - runWidth = tabAreaInsets.left + insets.left - + getTabRunIndent(tabPlacement, ++runs); - rects[i] = new Rectangle(runWidth, - insets.top + tabAreaInsets.top, - width, maxHeight); - runWidth += width; - if (runs > tabRuns.length - 1) - expandTabRunsArray(); - tabRuns[runs] = i; + rect.x = rects[i - 1].x + rects[i - 1].width; } else { - rects[i] = new Rectangle(runWidth, - insets.top + tabAreaInsets.top, - width, maxHeight); - runWidth += width; + tabRuns[0] = 0; + runCount = 1; + maxTabWidth = 0; + rect.x = x; } - } - runs++; - tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; - tabAreaRect.height = runs * maxTabHeight - - (runs - 1) * tabRunOverlay - + tabAreaInsets.top + tabAreaInsets.bottom; - contentRect.width = tabAreaRect.width; - contentRect.height = tabPane.getHeight() - insets.top - - insets.bottom - tabAreaRect.height; - contentRect.x = insets.left; - tabAreaRect.x = insets.left; - if (tabPlacement == SwingConstants.BOTTOM) - { - contentRect.y = insets.top; - tabAreaRect.y = contentRect.y + contentRect.height; - } - else - { - tabAreaRect.y = insets.top; - contentRect.y = tabAreaRect.y + tabAreaRect.height; + rect.width = calculateTabWidth(tabPlacement, i, fm); + maxTabWidth = Math.max(maxTabWidth, rect.width); + + if (rect.x != 2 + insets.left && rect.x + rect.width > breakAt) + { + if (runCount > tabRuns.length - 1) + expandTabRunsArray(); + tabRuns[runCount] = i; + runCount++; + rect.x = x; + } + + rect.y = y; + rect.height = maxTabHeight; + if (i == selectedIndex) + selectedRun = runCount - 1; + } } else { - int maxWidth = calculateMaxTabWidth(tabPlacement); - calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; - max = calcRect.height + tabAreaInsets.top + insets.top; - - int height = 0; - start += tabAreaInsets.top + insets.top; - int runHeight = start; - - int fontHeight = fm.getHeight(); - for (int i = 0; i < tabCount; i++) { - height = calculateTabHeight(tabPlacement, i, fontHeight); - if (runHeight + height > max) + rect = rects[i]; + if (i > 0) { - runHeight = tabAreaInsets.top + insets.top - + getTabRunIndent(tabPlacement, ++runs); - rects[i] = new Rectangle(insets.left + tabAreaInsets.left, - runHeight, maxWidth, height); - runHeight += height; - if (runs > tabRuns.length - 1) - expandTabRunsArray(); - tabRuns[runs] = i; + rect.y = rects[i - 1].y + rects[i - 1].height; } else { - rects[i] = new Rectangle(insets.left + tabAreaInsets.left, - runHeight, maxWidth, height); - runHeight += height; + tabRuns[0] = 0; + runCount = 1; + maxTabHeight = 0; + rect.y = y; } - } - runs++; + rect.height = calculateTabHeight(tabPlacement, i, + fm.getHeight()); + maxTabHeight = Math.max(maxTabHeight, rect.height); - tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay - + tabAreaInsets.left + tabAreaInsets.right; - tabAreaRect.height = tabPane.getHeight() - insets.top - - insets.bottom; - tabAreaRect.y = insets.top; - contentRect.width = tabPane.getWidth() - insets.left - insets.right - - tabAreaRect.width; - contentRect.height = tabAreaRect.height; - contentRect.y = insets.top; - if (tabPlacement == SwingConstants.LEFT) - { - tabAreaRect.x = insets.left; - contentRect.x = tabAreaRect.x + tabAreaRect.width; - } - else - { - contentRect.x = insets.left; - tabAreaRect.x = contentRect.x + contentRect.width; - } - } - runCount = runs; - if (runCount > tabRuns.length) - expandTabRunsArray(); + if (rect.y != 2 + insets.top && rect.y + rect.height > breakAt) + { + if (runCount > tabRuns.length - 1) + expandTabRunsArray(); + tabRuns[runCount] = i; + runCount++; + rect.y = y; + } - tabRuns[0] = 0; - normalizeTabRuns(tabPlacement, tabCount, start, max); - selectedRun = getRunForTab(tabCount, tabPane.getSelectedIndex()); - if (shouldRotateTabRuns(tabPlacement)) - rotateTabRuns(tabPlacement, selectedRun); - - // Need to pad the runs and move them to the correct location. - for (int i = 0; i < runCount; i++) - { - int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; - if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, i); - if (shouldPadTabRun(tabPlacement, i)) - padTabRun(tabPlacement, first, last, max); + rect.x = x; + rect.width = maxTabWidth; - // Done padding, now need to move it. - if (tabPlacement == SwingConstants.TOP && i > 0) - { - for (int j = first; j <= last; j++) - rects[j].y += (runCount - i) * maxTabHeight - - (runCount - i) * tabRunOverlay; + if (i == selectedIndex) + selectedRun = runCount - 1; } + } - if (tabPlacement == SwingConstants.BOTTOM) + if (runCount > 1) + { + int start; + if (tabPlacement == SwingConstants.TOP + || tabPlacement == SwingConstants.BOTTOM) + start = y; + else + start = x; + normalizeTabRuns(tabPlacement, tabCount, start, breakAt); + selectedRun = getRunForTab(tabCount, selectedIndex); + if (shouldRotateTabRuns(tabPlacement)) { - int height = tabPane.getBounds().height - insets.bottom - - tabAreaInsets.bottom; - int adjustment; - if (i == 0) - adjustment = height - maxTabHeight; - else - adjustment = height - (runCount - i + 1) * maxTabHeight - - (runCount - i) * tabRunOverlay; - - for (int j = first; j <= last; j++) - rects[j].y = adjustment; + rotateTabRuns(tabPlacement, selectedRun); } + } - if (tabPlacement == SwingConstants.LEFT && i > 0) + // Pad the runs. + int tabRunOverlay = getTabRunOverlay(tabPlacement); + for (int i = runCount - 1; i >= 0; --i) + { + int start = tabRuns[i]; + int nextIndex; + if (i == runCount - 1) + nextIndex = 0; + else + nextIndex = i + 1; + int next = tabRuns[nextIndex]; + int end = (next != 0 ? next - 1 : tabCount - 1); + if (tabPlacement == SwingConstants.TOP + || tabPlacement == SwingConstants.BOTTOM) { - for (int j = first; j <= last; j++) - rects[j].x += (runCount - i) * maxTabWidth - - (runCount - i) * tabRunOverlay; + for (int j = start; j <= end; ++j) + { + rect = rects[j]; + rect.y = y; + rect.x += getTabRunIndent(tabPlacement, i); + } + if (shouldPadTabRun(tabPlacement, i)) + { + padTabRun(tabPlacement, start, end, breakAt); + } + if (tabPlacement == BOTTOM) + y -= (maxTabHeight - tabRunOverlay); + else + y += (maxTabHeight - tabRunOverlay); } - - if (tabPlacement == SwingConstants.RIGHT) + else { - int width = tabPane.getBounds().width - insets.right - - tabAreaInsets.right; - int adjustment; - if (i == 0) - adjustment = width - maxTabWidth; + for (int j = start; j <= end; ++j) + { + rect = rects[j]; + rect.x = x; + rect.y += getTabRunIndent(tabPlacement, i); + } + if (shouldPadTabRun(tabPlacement, i)) + { + padTabRun(tabPlacement, start, end, breakAt); + } + if (tabPlacement == RIGHT) + x -= (maxTabWidth - tabRunOverlay); else - adjustment = width - (runCount - i + 1) * maxTabWidth - + (runCount - i) * tabRunOverlay; - - for (int j = first; j <= last; j++) - rects[j].x = adjustment; + x += (maxTabWidth - tabRunOverlay); + } } - padSelectedTab(tabPlacement, tabPane.getSelectedIndex()); + padSelectedTab(tabPlacement, selectedIndex); } /** @@ -528,6 +525,58 @@ public void layoutContainer(Container parent) { calculateLayoutInfo(); + + int tabPlacement = tabPane.getTabPlacement(); + Insets insets = tabPane.getInsets(); + int childCount = tabPane.getComponentCount(); + if (childCount > 0) + { + int compX; + int compY; + int tabAreaWidth = 0; + int tabAreaHeight = 0; + switch (tabPlacement) + { + case LEFT: + tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount, + maxTabWidth); + compX = tabAreaWidth + insets.left + contentBorderInsets.left; + compY = insets.top + contentBorderInsets.top; + break; + case RIGHT: + tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount, + maxTabWidth); + compX = insets.left + contentBorderInsets.left; + compY = insets.top + contentBorderInsets.top; + break; + case BOTTOM: + tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount, + maxTabHeight); + compX = insets.left + contentBorderInsets.left; + compY = insets.top + contentBorderInsets.top; + break; + case TOP: + default: + tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount, + maxTabHeight); + compX = insets.left + contentBorderInsets.left; + compY = tabAreaHeight + insets.top + contentBorderInsets.top; + } + Rectangle bounds = tabPane.getBounds(); + int compWidth = bounds.width - tabAreaWidth - insets.left + - insets.right - contentBorderInsets.left + - contentBorderInsets.right; + int compHeight = bounds.height - tabAreaHeight - insets.top + - insets.bottom - contentBorderInsets.top + - contentBorderInsets.bottom; + + + for (int i = 0; i < childCount; ++i) + { + Component c = tabPane.getComponent(i); + c.setBounds(compX, compY, compWidth, compHeight); + } + } } /** @@ -1288,6 +1337,12 @@ protected int[] tabRuns; /** + * Indicates if the layout of the tab runs is ok or not. This is package + * private to avoid a synthetic accessor method. + */ + boolean tabRunsDirty; + + /** * This is the keystroke for moving down. * * @deprecated 1.3 @@ -1343,6 +1398,11 @@ transient Rectangle contentRect; /** + * The index over which the mouse is currently moving. + */ + private int rolloverTab; + + /** * Creates a new BasicTabbedPaneUI object. */ public BasicTabbedPaneUI() @@ -2332,23 +2392,23 @@ */ public int tabForCoordinate(JTabbedPane pane, int x, int y) { - Point p = new Point(x, y); + if (! tabPane.isValid()) + tabPane.validate(); + int tabCount = tabPane.getTabCount(); - int currRun = 1; - for (int i = 0; i < runCount; i++) + int index = -1; + for (int i = 0; i < tabCount; ++i) { - int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; - if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, currRun); - for (int j = first; j <= last; j++) + if (rects[i].contains(x, y)) { - if (getTabBounds(pane, j).contains(p)) - return j; + index = i; + break; } - currRun = getNextTabRun(currRun); } - return -1; + + // FIXME: Handle scrollable tab layout. + + return index; } /** @@ -3068,4 +3128,33 @@ break; } } + + /** + * Sets the tab which should be highlighted when in rollover mode. And + * <code>index</code> of <code>-1</code> means that the rollover tab + * is deselected (i.e. the mouse is outside of the tabarea). + * + * @param index the index of the tab that is under the mouse, <code>-1</code> + * for no tab + * + * @since 1.5 + */ + protected void setRolloverTab(int index) + { + rolloverTab = index; + } + + /** + * Retunrs the index of the tab over which the mouse is currently moving, + * or <code>-1</code> for no tab. + * + * @return the index of the tab over which the mouse is currently moving, + * or <code>-1</code> for no tab + * + * @since 1.5 + */ + protected int getRolloverTab() + { + return rolloverTab; + } }
signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil