6490753: JComboBox doesn't look as native combobox in different states of component
Reviewed-by: serb, alexsch
Contributed-by: Martin Mraz <mraz.martin.dev@gmail.com>
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/AnimationController.java Mon Mar 06 08:06:54 2017 +0300
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/AnimationController.java Mon Mar 06 17:03:26 2017 +0300
@@ -36,7 +36,6 @@
import javax.swing.*;
-
import com.sun.java.swing.plaf.windows.TMSchema.State;
import static com.sun.java.swing.plaf.windows.TMSchema.State.*;
import com.sun.java.swing.plaf.windows.TMSchema.Part;
@@ -383,18 +382,25 @@
updateProgress();
if (! isDone()) {
Graphics2D g = (Graphics2D) _g.create();
- skin.paintSkinRaw(g, dx, dy, dw, dh, startState);
- float alpha;
- if (isForward) {
- alpha = progress;
+ if (skin.haveToSwitchStates()) {
+ skin.paintSkinRaw(g, dx, dy, dw, dh, state);
+ g.setComposite(AlphaComposite.SrcOver.derive(1 - progress));
+ skin.paintSkinRaw(g, dx, dy, dw, dh, startState);
} else {
- alpha = 1 - progress;
+ skin.paintSkinRaw(g, dx, dy, dw, dh, startState);
+ float alpha;
+ if (isForward) {
+ alpha = progress;
+ } else {
+ alpha = 1 - progress;
+ }
+ g.setComposite(AlphaComposite.SrcOver.derive(alpha));
+ skin.paintSkinRaw(g, dx, dy, dw, dh, state);
}
- g.setComposite(AlphaComposite.SrcOver.derive(alpha));
- skin.paintSkinRaw(g, dx, dy, dw, dh, state);
g.dispose();
} else {
skin.paintSkinRaw(_g, dx, dy, dw, dh, state);
+ skin.switchStates(false);
}
}
boolean isDone() {
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/TMSchema.java Mon Mar 06 08:06:54 2017 +0300
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/TMSchema.java Mon Mar 06 17:03:26 2017 +0300
@@ -121,6 +121,12 @@
LBP_LISTBOX(Control.LISTBOX, 0),
+ LBCP_BORDER_HSCROLL (Control.LISTBOX, 1),
+ LBCP_BORDER_HVSCROLL (Control.LISTBOX, 2),
+ LBCP_BORDER_NOSCROLL (Control.LISTBOX, 3),
+ LBCP_BORDER_VSCROLL (Control.LISTBOX, 4),
+ LBCP_ITEM (Control.LISTBOX, 5),
+
LVP_LISTVIEW(Control.LISTVIEW, 0),
PP_PROGRESS (Control.PROGRESS, 0),
@@ -343,6 +349,12 @@
stateMap.put(Part.HP_HEADERSORTARROW,
new State[] {SORTEDDOWN, SORTEDUP});
+ State[] listBoxStates = new State[] { NORMAL, PRESSED, HOT, DISABLED};
+ stateMap.put(Part.LBCP_BORDER_HSCROLL, listBoxStates);
+ stateMap.put(Part.LBCP_BORDER_HVSCROLL, listBoxStates);
+ stateMap.put(Part.LBCP_BORDER_NOSCROLL, listBoxStates);
+ stateMap.put(Part.LBCP_BORDER_VSCROLL, listBoxStates);
+
State[] scrollBarStates = new State[] { NORMAL, HOT, PRESSED, DISABLED, HOVER };
stateMap.put(Part.SBP_SCROLLBAR, scrollBarStates);
stateMap.put(Part.SBP_THUMBBTNVERT, scrollBarStates);
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java Mon Mar 06 08:06:54 2017 +0300
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java Mon Mar 06 17:03:26 2017 +0300
@@ -41,6 +41,7 @@
import sun.swing.DefaultLookup;
import sun.swing.StringUIClientPropertyKey;
+import com.sun.java.swing.plaf.windows.WindowsBorders.DashedBorder;
/**
* Windows combo box.
@@ -97,6 +98,9 @@
} else if (source instanceof XPComboBoxButton) {
rv = ((XPComboBoxButton) source)
.getWindowsComboBoxUI().comboBox;
+ } else if (source instanceof JTextField &&
+ ((JTextField) source).getParent() instanceof JComboBox) {
+ rv = (JComboBox) ((JTextField) source).getParent();
}
return rv;
}
@@ -149,6 +153,8 @@
//is initialized after installListeners is invoked
comboBox.addMouseListener(rolloverListener);
arrowButton.addMouseListener(rolloverListener);
+ // set empty border as default to see vista animated border
+ comboBox.setBorder(new EmptyBorder(0,0,0,0));
}
}
@@ -224,6 +230,9 @@
state = State.DISABLED;
} else if (isPopupVisible(comboBox)) {
state = State.PRESSED;
+ } else if (comboBox.isEditable()
+ && comboBox.getEditor().getEditorComponent().isFocusOwner()) {
+ state = State.PRESSED;
} else if (isRollover) {
state = State.HOT;
}
@@ -242,7 +251,7 @@
skin = xp.getSkin(c, Part.CP_READONLY);
}
if (skin == null) {
- skin = xp.getSkin(c, Part.CP_COMBOBOX);
+ skin = xp.getSkin(c, Part.CP_BORDER);
}
skin.paintSkin(g, 0, 0, c.getWidth(), c.getHeight(), state);
}
@@ -368,7 +377,7 @@
}
protected ComboPopup createPopup() {
- return super.createPopup();
+ return new WinComboPopUp(comboBox);
}
/**
@@ -414,8 +423,10 @@
@SuppressWarnings("serial") // Superclass is not serializable across versions
private class XPComboBoxButton extends XPStyle.GlyphButton {
+ private State prevState = null;
+
public XPComboBoxButton(XPStyle xp) {
- super(null,
+ super(comboBox,
(! xp.isSkinDefined(comboBox, Part.CP_DROPDOWNBUTTONRIGHT))
? Part.CP_DROPDOWNBUTTON
: (comboBox.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
@@ -428,18 +439,33 @@
@Override
protected State getState() {
State rv;
+
+ getModel().setPressed(comboBox.isPopupVisible());
+
rv = super.getState();
XPStyle xp = XPStyle.getXP();
if (rv != State.DISABLED
- && comboBox != null && ! comboBox.isEditable()
- && xp != null && xp.isSkinDefined(comboBox,
- Part.CP_DROPDOWNBUTTONRIGHT)) {
+ && comboBox != null && ! comboBox.isEditable()
+ && xp != null && xp.isSkinDefined(comboBox,
+ Part.CP_DROPDOWNBUTTONRIGHT)) {
/*
* for non editable ComboBoxes Vista seems to have the
* same glyph for all non DISABLED states
*/
rv = State.NORMAL;
}
+ if (rv == State.NORMAL && (prevState == State.HOT || prevState == State.PRESSED)) {
+ /*
+ * State NORMAL of combobox button cannot overpaint states HOT or PRESSED
+ * Therefore HOT state must be painted from alpha 1 to 0 and not as usual that
+ * NORMAL state is painted from alpha 0 to alpha 1.
+ */
+ skin.switchStates(true);
+ }
+ if (rv != prevState) {
+ prevState = rv;
+ }
+
return rv;
}
@@ -484,6 +510,39 @@
}
}
+ @SuppressWarnings("serial") // Same-version serialization only
+ protected class WinComboPopUp extends BasicComboPopup {
+ private Skin listBoxBorder = null;
+ private XPStyle xp;
+
+ public WinComboPopUp(JComboBox<Object> combo) {
+ super(combo);
+ xp = XPStyle.getXP();
+ if (xp != null && xp.isSkinDefined(combo, Part.LBCP_BORDER_NOSCROLL)) {
+ this.listBoxBorder = new Skin(combo, Part.LBCP_BORDER_NOSCROLL);
+ this.setBorder(new EmptyBorder(1,1,1,1));
+ }
+ }
+
+ protected KeyListener createKeyListener() {
+ return new InvocationKeyHandler();
+ }
+
+ protected class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler {
+ protected InvocationKeyHandler() {
+ WinComboPopUp.this.super();
+ }
+ }
+
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ if (this.listBoxBorder != null) {
+ this.listBoxBorder.paintSkinRaw(g, this.getX(), this.getY(),
+ this.getWidth(), this.getHeight(), State.HOT);
+ }
+ }
+ }
+
/**
* Subclassed to highlight selected item in an editable combo box.
@@ -498,6 +557,7 @@
protected JTextField createEditorComponent() {
JTextField editor = super.createEditorComponent();
Border border = (Border)UIManager.get("ComboBox.editorBorder");
+
if (border != null) {
editor.setBorder(border);
}
@@ -524,6 +584,31 @@
private static final Object BORDER_KEY
= new StringUIClientPropertyKey("BORDER_KEY");
private static final Border NULL_BORDER = new EmptyBorder(0, 0, 0, 0);
+
+ // Create own version of DashedBorder with more space on left side
+ private class WindowsComboBoxDashedBorder extends DashedBorder {
+
+ public WindowsComboBoxDashedBorder(Color color, int thickness) {
+ super(color, thickness);
+ }
+
+ public WindowsComboBoxDashedBorder(Color color) {
+ super(color);
+ }
+
+ @Override
+ public Insets getBorderInsets(Component c, Insets i) {
+ return new Insets(0,2,0,0);
+ }
+ }
+
+ public WindowsComboBoxRenderer() {
+ super();
+
+ // correct space on the left side of text items in the combo popup list
+ Insets i = getBorder().getBorderInsets(this);
+ setBorder(new EmptyBorder(0, 2, 0, i.right));
+ }
/**
* {@inheritDoc}
*/
@@ -542,7 +627,7 @@
if (index == -1 && isSelected) {
Border border = component.getBorder();
Border dashedBorder =
- new WindowsBorders.DashedBorder(list.getForeground());
+ new WindowsComboBoxDashedBorder(list.getForeground());
component.setBorder(dashedBorder);
//store current border in client property if needed
if (component.getClientProperty(BORDER_KEY) == null) {
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java Mon Mar 06 08:06:54 2017 +0300
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java Mon Mar 06 17:03:26 2017 +0300
@@ -672,7 +672,7 @@
"ComboBox.buttonHighlight", ControlHighlightColor,
"ComboBox.selectionBackground", SelectionBackgroundColor,
"ComboBox.selectionForeground", SelectionTextColor,
- "ComboBox.editorBorder", new XPValue(new EmptyBorder(1,2,1,1),
+ "ComboBox.editorBorder", new XPValue(new EmptyBorder(1,4,1,1),
new EmptyBorder(1,4,1,4)),
"ComboBox.disabledBackground",
new XPColorValue(Part.CP_COMBOBOX, State.DISABLED,
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/XPStyle.java Mon Mar 06 08:06:54 2017 +0300
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/XPStyle.java Mon Mar 06 17:03:26 2017 +0300
@@ -479,6 +479,7 @@
private final String string;
private Dimension size = null;
+ private boolean switchStates = false;
Skin(Component component, Part part) {
this(component, part, null);
@@ -513,6 +514,14 @@
return (insets != null) ? insets : new Insets(0, 0, 0, 0);
}
+ boolean haveToSwitchStates() {
+ return switchStates;
+ }
+
+ void switchStates(boolean b) {
+ switchStates = b;
+ }
+
private int getWidth(State state) {
if (size == null) {
size = getPartSize(part, state);
@@ -689,7 +698,7 @@
@SuppressWarnings("serial") // Superclass is not serializable across versions
static class GlyphButton extends JButton {
- private Skin skin;
+ protected Skin skin;
public GlyphButton(Component parent, Part part) {
XPStyle xp = getXP();