Skip to content

Commit

Permalink
ComboBox: for style "mac", place popup over combobox (issue #497)
Browse files Browse the repository at this point in the history
  • Loading branch information
DevCharly committed May 12, 2022
1 parent 3ca15ad commit 9965e70
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 13 deletions.
Expand Up @@ -69,6 +69,7 @@
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.icons.FlatCheckBoxMenuItemIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade;
Expand Down Expand Up @@ -817,12 +818,19 @@ protected Rectangle computePopupBounds( int px, int py, int pw, int ph ) {
}
}

// for style "mac", add width of "checked item" icon
boolean isPopupOverComboBox = isPopupOverComboBox();
int selectedIndex = -1;
if( isPopupOverComboBox && (selectedIndex = comboBox.getSelectedIndex()) >= 0 )
displayWidth += MacCheckedItemIcon.INSTANCE.getIconWidth() + scale( CellPaddingBorder.MAC_STYLE_GAP );

// add width of vertical scroll bar
JScrollBar verticalScrollBar = scroller.getVerticalScrollBar();
if( verticalScrollBar != null )
displayWidth += verticalScrollBar.getPreferredSize().width;

// make popup wider if necessary
int pw0 = pw;
if( displayWidth > pw ) {
// limit popup width to screen width
GraphicsConfiguration gc = comboBox.getGraphicsConfiguration();
Expand All @@ -842,6 +850,30 @@ protected Rectangle computePopupBounds( int px, int py, int pw, int ph ) {
px -= diff;
}

// for style "mac", place popup over combobox
Rectangle cellBounds;
if( isPopupOverComboBox && selectedIndex >= 0 &&
(cellBounds = list.getCellBounds( 0, 0 )) != null )
{
Insets comboBoxInsets = comboBox.getInsets();
Insets listInsets = list.getInsets();
Insets popupInsets = getInsets();

// position popup so that selected item is at same Y position as combobox
py -= (cellBounds.height * (selectedIndex + 1)) + comboBoxInsets.top + listInsets.top + popupInsets.top;

// position popup slightly to the left so that a small part of the right side of the combobox stays visible
int offset = Math.min( pw - pw0, MacCheckedItemIcon.INSTANCE.getIconWidth() ) + scale( 4 );
if( comboBox.getComponentOrientation().isLeftToRight() )
px -= offset + comboBoxInsets.right + listInsets.right;
else
px += offset + comboBoxInsets.left + listInsets.left;

// not invoking super.computePopupBounds() here to let
// JPopupMenu.adjustPopupLocationToFitScreen() fix the location if necessary
return new Rectangle( px, py, pw, ph );
}

return super.computePopupBounds( px, py, pw, ph );
}

Expand Down Expand Up @@ -917,6 +949,15 @@ protected void paintChildren( Graphics g ) {
paddingBorder.uninstall();
}

private boolean isPopupOverComboBox() {
return isMacStyle() &&
!comboBox.isEditable() &&
comboBox.getItemCount() > 0 &&
comboBox.getItemCount() <= comboBox.getMaximumRowCount() &&
// for compatibility with Aqua Laf
!clientPropertyBoolean( comboBox, "JComboBox.isPopDown", false );
}

//---- class PopupListCellRenderer -----

private class PopupListCellRenderer
Expand All @@ -934,6 +975,13 @@ public Component getListCellRendererComponent( JList list, Object value,
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
c.applyComponentOrientation( comboBox.getComponentOrientation() );

// style "mac"
if( isPopupOverComboBox() && c instanceof JComponent ) {
int selectedIndex = comboBox.getSelectedIndex();
((JComponent)c).putClientProperty( CellPaddingBorder.KEY_MAC_STYLE_HINT,
(selectedIndex >= 0) ? (index == selectedIndex) : null );
}

paddingBorder.install( c );

return c;
Expand All @@ -950,10 +998,16 @@ public Component getListCellRendererComponent( JList list, Object value,
* which vertically aligns text in popup list with text in combobox.
* <p>
* The renderer border is painted on the outer side of this border.
* <p>
* For button style "mac", also used to increase insets on left side for
* "checked item" icon and to paint "checked item" icon for selected combobox item.
*/
private static class CellPaddingBorder
extends AbstractBorder
{
static final String KEY_MAC_STYLE_HINT = "FlatLaf.internal.FlatComboBoxUI.macStyleHint";
static final int MAC_STYLE_GAP = 4;

private Insets padding;
private JComponent rendererComponent;
private Border rendererBorder;
Expand Down Expand Up @@ -1000,6 +1054,8 @@ synchronized void uninstall() {
if( rendererComponent == null )
return;

rendererComponent.putClientProperty( KEY_MAC_STYLE_HINT, null );

if( rendererComponent.getBorder() == this )
rendererComponent.setBorder( rendererBorder );
rendererComponent = null;
Expand All @@ -1021,13 +1077,55 @@ synchronized public Insets getBorderInsets( Component c, Insets insets ) {
insets.bottom = padding.bottom;
insets.right = padding.right;
}

// style "mac"
if( c instanceof JComponent ) {
Boolean macStyleHint = clientPropertyBooleanStrict( (JComponent) c, KEY_MAC_STYLE_HINT, null );
if( macStyleHint != null ) {
int indent = MacCheckedItemIcon.INSTANCE.getIconWidth() + scale( MAC_STYLE_GAP );
if( c.getComponentOrientation().isLeftToRight() )
insets.left += indent;
else
insets.right += indent;
}
}

return insets;
}

@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( rendererBorder != null )
rendererBorder.paintBorder( c, g, x, y, width, height );

// style "mac"
if( c instanceof JComponent ) {
Boolean macStyleHint = clientPropertyBooleanStrict( (JComponent) c, KEY_MAC_STYLE_HINT, null );
if( macStyleHint == Boolean.TRUE ) {
// paint "checked item" icon
int ix = c.getComponentOrientation().isLeftToRight()
? x + scale( padding.left )
: x + width - scale( padding.right ) - MacCheckedItemIcon.INSTANCE.getIconWidth();
MacCheckedItemIcon.INSTANCE.paintIcon( c, g, ix, y + ((height - MacCheckedItemIcon.INSTANCE.getIconHeight()) / 2) );
}
}
}
}

//---- class MacCheckedItemIcon -------------------------------------------

/**
* Use for style "mac" to mark checked item.
*/
private static class MacCheckedItemIcon
extends FlatCheckBoxMenuItemIcon
{
static MacCheckedItemIcon INSTANCE = new MacCheckedItemIcon();

@Override
protected void paintIcon( Component c, Graphics2D g2 ) {
g2.setColor( c.getForeground() );
paintCheckmark( g2 );
}
}

Expand Down
Expand Up @@ -514,13 +514,7 @@ private void initComponents() {
"bb",
"ccc",
"dd",
"e",
"ff",
"ggg",
"hh",
"i",
"jj",
"kkk"
"e"
}));
comboBox3.setMaximumRowCount(6);
comboBox3.putClientProperty("FlatLaf.styleClass", "flatlaf-preview-combobox");
Expand Down
Expand Up @@ -201,12 +201,6 @@ new FormModel {
addElement( "ccc" )
addElement( "dd" )
addElement( "e" )
addElement( "ff" )
addElement( "ggg" )
addElement( "hh" )
addElement( "i" )
addElement( "jj" )
addElement( "kkk" )
}
"maximumRowCount": 6
"$client.FlatLaf.styleClass": "flatlaf-preview-combobox"
Expand Down

0 comments on commit 9965e70

Please sign in to comment.