Skip to content

Commit 0c9c9f7

Browse files
pporvatovintellij-monorepo-bot
authored andcommitted
IDEA-286480 CheckBox baseline is incorrect on JBR17
- Refactored Checkbox/RadioButton preferred size methods (functionality shouldn't be affected) GitOrigin-RevId: 6f29d2deef071a922cc4b4059b422b9739e63f50
1 parent 35c072e commit 0c9c9f7

File tree

7 files changed

+140
-87
lines changed

7 files changed

+140
-87
lines changed

platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/AbstractButtonLayout.java

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,23 @@ class AbstractButtonLayout {
1515
public final Rectangle textRect = new Rectangle();
1616

1717
private final AbstractButton button;
18+
private final Dimension size;
19+
private final boolean removeInsetsBeforeLayout;
1820
private final String text;
1921
private final FontMetrics fontMetrics;
20-
private final Dimension size;
2122

2223
/**
2324
* @param removeInsetsBeforeLayout remove insets before layout. A very strange parameter, looks like a hack because of the following bug:
2425
* CheckBox.borderInsets (RadioButton as well) property ignores top/bottom offsets while vertical align.
2526
* Normally should be always true and removed later
2627
*/
27-
AbstractButtonLayout(AbstractButton button, boolean removeInsetsBeforeLayout, Icon defaultIcon) {
28+
AbstractButtonLayout(AbstractButton button, Dimension size, boolean removeInsetsBeforeLayout, Icon defaultIcon) {
2829
this.button = button;
29-
size = button.getSize();
30+
this.size = size;
31+
this.removeInsetsBeforeLayout = removeInsetsBeforeLayout;
3032
fontMetrics = button.getFontMetrics(button.getFont());
3133

32-
Rectangle viewRect = new Rectangle(button.getSize());
34+
Rectangle viewRect = new Rectangle(size);
3335
if (removeInsetsBeforeLayout) {
3436
JBInsets.removeFrom(viewRect, button.getInsets());
3537
}
@@ -38,7 +40,8 @@ class AbstractButtonLayout {
3840
button, fontMetrics, button.getText(), defaultIcon,
3941
button.getVerticalAlignment(), button.getHorizontalAlignment(),
4042
button.getVerticalTextPosition(), button.getHorizontalTextPosition(),
41-
viewRect, iconRect, textRect, button.getIconTextGap());
43+
viewRect, iconRect, textRect,
44+
button.getText() == null ? 0 : button.getIconTextGap());
4245
}
4346

4447
public void paint(Graphics g, Color disabledTextColor, int mnemonicIndex) {
@@ -50,6 +53,28 @@ public void paint(Graphics g, Color disabledTextColor, int mnemonicIndex) {
5053
drawText(g, disabledTextColor, mnemonicIndex);
5154
}
5255

56+
public Dimension getPreferredSize() {
57+
Insets insets = button.getInsets();
58+
Rectangle iconRectResult;
59+
// todo a strange logic that should be revised together with removeInsetsBeforeLayout
60+
// The following code looks more logical
61+
/*
62+
Rectangle rect = iconRect.union(textRect);
63+
JBInsets.addTo(rect, button.getInsets());
64+
return new Dimension(rect.width, rect.height);
65+
*/
66+
if (removeInsetsBeforeLayout) {
67+
iconRectResult = iconRect.getBounds();
68+
JBInsets.addTo(iconRectResult, insets);
69+
} else {
70+
iconRectResult = iconRect;
71+
}
72+
Rectangle textRectResult = textRect.getBounds();
73+
JBInsets.addTo(textRectResult, insets);
74+
Rectangle rect = iconRectResult.union(textRectResult);
75+
return new Dimension(rect.width, rect.height);
76+
}
77+
5378
private void drawText(Graphics g, Color disabledTextColor, int mnemonicIndex) {
5479
if (text != null) {
5580
View v = (View)button.getClientProperty(BasicHTML.propertyKey);

platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaCheckBoxUI.java

Lines changed: 9 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
import com.intellij.ide.ui.laf.darcula.DarculaUIUtil;
66
import com.intellij.ui.ComponentUtil;
77
import com.intellij.ui.scale.JBUIScale;
8-
import com.intellij.util.ui.*;
9-
import org.jetbrains.annotations.Nullable;
8+
import com.intellij.util.ui.EmptyIcon;
9+
import com.intellij.util.ui.LafIconLookup;
10+
import com.intellij.util.ui.MacUIUtil;
11+
import com.intellij.util.ui.ThreeStateCheckBox;
1012

1113
import javax.swing.*;
1214
import javax.swing.plaf.ComponentUI;
@@ -71,7 +73,7 @@ protected int textIconGap() {
7173
public void paint(Graphics g2d, JComponent c) {
7274
Graphics2D g = (Graphics2D)g2d;
7375
AbstractButton button = (AbstractButton)c;
74-
AbstractButtonLayout layout = new AbstractButtonLayout(button, removeInsetsBeforeLayout(button), getDefaultIcon());
76+
AbstractButtonLayout layout = new AbstractButtonLayout(button, button.getSize(), removeInsetsBeforeLayout(button), getDefaultIcon());
7577

7678
layout.paint(g, getDisabledTextColor(), getMnemonicIndex(button));
7779
drawCheckIcon(c, g, button, layout.iconRect, button.isSelected(), button.isEnabled());
@@ -117,75 +119,17 @@ protected int getMnemonicIndex(AbstractButton b) {
117119

118120
@Override
119121
public Dimension getPreferredSize(JComponent c) {
120-
Dimension dimension = computeOurPreferredSize(c);
121-
return dimension != null ? dimension : super.getPreferredSize(c);
122+
AbstractButton button = (AbstractButton)c;
123+
AbstractButtonLayout layout = new AbstractButtonLayout(button, new Dimension(Short.MAX_VALUE, Short.MAX_VALUE),
124+
removeInsetsBeforeLayout(button), getDefaultIcon());
125+
return layout.getPreferredSize();
122126
}
123127

124128
@Override
125129
public Dimension getMaximumSize(JComponent c) {
126130
return getPreferredSize(c);
127131
}
128132

129-
protected Dimension computeOurPreferredSize(JComponent c) {
130-
return computeCheckboxPreferredSize(c, getDefaultIcon());
131-
}
132-
133-
/**
134-
* @see javax.swing.plaf.basic.BasicRadioButtonUI#getPreferredSize
135-
* The difference is that we do not include `DarculaCheckBoxBorder` insets to the icon size.
136-
*/
137-
@Nullable
138-
static Dimension computeCheckboxPreferredSize(JComponent c, Icon defaultIcon) {
139-
if (c.getComponentCount() > 0) {
140-
return null;
141-
}
142-
143-
AbstractButton b = (AbstractButton)c;
144-
Rectangle prefViewRect = new Rectangle();
145-
Rectangle prefIconRect = new Rectangle();
146-
Rectangle prefTextRect = new Rectangle();
147-
148-
String text = b.getText();
149-
150-
Icon buttonIcon = b.getIcon();
151-
if (buttonIcon == null) {
152-
buttonIcon = defaultIcon;
153-
}
154-
155-
Font font = b.getFont();
156-
FontMetrics fm = b.getFontMetrics(font);
157-
158-
prefViewRect.x = prefViewRect.y = 0;
159-
prefViewRect.width = Short.MAX_VALUE;
160-
prefViewRect.height = Short.MAX_VALUE;
161-
prefIconRect.x = prefIconRect.y = prefIconRect.width = prefIconRect.height = 0;
162-
prefTextRect.x = prefTextRect.y = prefTextRect.width = prefTextRect.height = 0;
163-
164-
SwingUtilities.layoutCompoundLabel(
165-
c, fm, text, buttonIcon,
166-
b.getVerticalAlignment(), b.getHorizontalAlignment(),
167-
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
168-
prefViewRect, prefIconRect, prefTextRect,
169-
text == null ? 0 : b.getIconTextGap());
170-
171-
Insets insets = b.getInsets();
172-
if (!(b.getBorder() instanceof DarculaCheckBoxBorder)) {
173-
JBInsets.addTo(prefIconRect, insets);
174-
}
175-
JBInsets.addTo(prefTextRect, insets);
176-
177-
// find the union of the icon and text rects (from Rectangle.java)
178-
int x1 = Math.min(prefIconRect.x, prefTextRect.x);
179-
int x2 = Math.max(prefIconRect.x + prefIconRect.width,
180-
prefTextRect.x + prefTextRect.width);
181-
int y1 = Math.min(prefIconRect.y, prefTextRect.y);
182-
int y2 = Math.max(prefIconRect.y + prefIconRect.height,
183-
prefTextRect.y + prefTextRect.height);
184-
int width = x2 - x1;
185-
int height = y2 - y1;
186-
return new Dimension(width, height);
187-
}
188-
189133
@Override
190134
public Icon getDefaultIcon() {
191135
return DEFAULT_ICON;

platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonUI.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private static void updateTextPosition(AbstractButton b) {
6767
public void paint(Graphics g2d, JComponent c) {
6868
Graphics2D g = (Graphics2D)g2d;
6969
AbstractButton button = (AbstractButton)c;
70-
AbstractButtonLayout layout = new AbstractButtonLayout(button, removeInsetsBeforeLayout(button), getDefaultIcon());
70+
AbstractButtonLayout layout = new AbstractButtonLayout(button, button.getSize(), removeInsetsBeforeLayout(button), getDefaultIcon());
7171

7272
layout.paint(g, getDisabledTextColor(), getMnemonicIndex(button));
7373
paintFocus(button, g, layout.textRect);
@@ -96,19 +96,17 @@ protected int getMnemonicIndex(AbstractButton b) {
9696

9797
@Override
9898
public Dimension getPreferredSize(JComponent c) {
99-
Dimension dimension = computeOurPreferredSize(c);
100-
return dimension != null ? dimension : super.getPreferredSize(c);
99+
AbstractButton button = (AbstractButton)c;
100+
AbstractButtonLayout layout = new AbstractButtonLayout(button, new Dimension(Short.MAX_VALUE, Short.MAX_VALUE),
101+
removeInsetsBeforeLayout(button), getDefaultIcon());
102+
return layout.getPreferredSize();
101103
}
102104

103105
@Override
104106
public Dimension getMaximumSize(JComponent c) {
105107
return getPreferredSize(c);
106108
}
107109

108-
protected Dimension computeOurPreferredSize(JComponent c) {
109-
return DarculaCheckBoxUI.computeCheckboxPreferredSize(c, getDefaultIcon());
110-
}
111-
112110
@Override
113111
protected void paintFocus(Graphics g, Rectangle t, Dimension d) {}
114112

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
package com.intellij.internal.ui.uiDslTestAction
3+
4+
import com.intellij.ui.components.JBCheckBox
5+
import com.intellij.ui.components.JBRadioButton
6+
import com.intellij.ui.dsl.builder.Cell
7+
import com.intellij.ui.dsl.builder.DslComponentProperty
8+
import com.intellij.ui.dsl.builder.panel
9+
import com.intellij.ui.dsl.gridLayout.Gaps
10+
import com.intellij.ui.dsl.gridLayout.VerticalAlign
11+
import com.intellij.util.ui.JBUI
12+
import org.jetbrains.annotations.ApiStatus
13+
import java.awt.Color
14+
import javax.swing.BoxLayout
15+
import javax.swing.JCheckBox
16+
import javax.swing.JPanel
17+
import javax.swing.JToggleButton
18+
import javax.swing.border.Border
19+
20+
@Suppress("DialogTitleCapitalization")
21+
@ApiStatus.Internal
22+
internal class CheckBoxRadioButtonPanel {
23+
val panel = panel {
24+
row {
25+
panel {
26+
buttonsGroup {
27+
row {
28+
label("Marker")
29+
}
30+
for (i in 1..5) {
31+
row { checkBox("DcheckBox$i") }
32+
}
33+
}
34+
}.resizableColumn()
35+
.verticalAlign(VerticalAlign.TOP)
36+
panel {
37+
row {
38+
label("Marker")
39+
}
40+
buttonsGroup {
41+
for (i in 1..5) {
42+
row { radioButton("DradioButton$i") }
43+
}
44+
}
45+
}.resizableColumn()
46+
.verticalAlign(VerticalAlign.TOP)
47+
}
48+
49+
buttonsGroup {
50+
row {
51+
checkBox("Border: 2,10,20,30")
52+
.customize(Color.GREEN, JBUI.Borders.customLine(Color.ORANGE, 2, 10, 20, 30))
53+
54+
radioButton("Border: 10,20,30,2")
55+
.customize(Color.ORANGE, JBUI.Borders.customLine(Color.GREEN, 10, 20, 30, 2))
56+
57+
checkBox("Border: 0,0,0,0")
58+
.customize(Color.GREEN, JBUI.Borders.customLine(Color.ORANGE, 0, 0, 0, 0))
59+
60+
radioButton("Border: 0,0,0,0")
61+
.customize(Color.ORANGE, JBUI.Borders.customLine(Color.GREEN, 0, 0, 0, 0))
62+
}
63+
}
64+
65+
buttonsGroup {
66+
row {
67+
val checkBox = JBCheckBox()
68+
cell(checkBox)
69+
.comment("checkBox(null), prefSize = ${checkBox.preferredSize}")
70+
71+
val radioButton = JBRadioButton()
72+
cell(radioButton)
73+
.comment("radioButton(null), prefSize = ${radioButton.preferredSize}")
74+
}
75+
}
76+
77+
row {
78+
val panel = JPanel()
79+
panel.layout = BoxLayout(panel, BoxLayout.Y_AXIS)
80+
for (i in 1..5) {
81+
panel.add(JCheckBox("BoxLayout$i"))
82+
}
83+
cell(panel)
84+
}
85+
}
86+
}
87+
88+
private fun Cell<JToggleButton>.customize(background: Color, border: Border) {
89+
applyToComponent {
90+
isOpaque = true
91+
this.background = background
92+
this.border = border
93+
putClientProperty(DslComponentProperty.VISUAL_PADDINGS, Gaps.EMPTY)
94+
}
95+
}

platform/platform-impl/src/com/intellij/internal/ui/uiDslTestAction/UiDslTestAction.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ private class UiDslTestDialog(project: Project?) : DialogWrapper(project, null,
5959
result.addTab("Resizable Rows", createResizableRows())
6060
result.addTab("Others", OthersPanel().panel)
6161
result.addTab("Deprecated Api", JScrollPane(DeprecatedApiPanel().panel))
62+
result.addTab("CheckBox/RadioButton", CheckBoxRadioButtonPanel().panel)
6263

6364
return result
6465
}

plugins/laf/win10/src/com/intellij/laf/win10/WinIntelliJCheckBoxUI.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@ protected boolean removeInsetsBeforeLayout(AbstractButton b) {
3131
return true;
3232
}
3333

34-
@Override
35-
protected Dimension computeOurPreferredSize(JComponent c) {
36-
return null;
37-
}
38-
3934
@Override
4035
protected void drawCheckIcon(JComponent c, Graphics2D g, AbstractButton b, Rectangle iconRect, boolean selected, boolean enabled) {
4136
Graphics2D g2 = (Graphics2D)g.create();

plugins/laf/win10/src/com/intellij/laf/win10/WinIntelliJRadioButtonUI.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@ protected boolean removeInsetsBeforeLayout(AbstractButton b) {
2020
return true;
2121
}
2222

23-
@Override
24-
protected Dimension computeOurPreferredSize(JComponent c) {
25-
return null;
26-
}
27-
2823
@SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass", "UnusedDeclaration"})
2924
public static ComponentUI createUI(JComponent c) {
3025
AbstractButton b = (AbstractButton)c;

0 commit comments

Comments
 (0)