Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/classes/com/apple/laf/AquaComboBoxPopup.java
38831 views
1
/*
2
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package com.apple.laf;
27
28
import java.awt.*;
29
import java.awt.event.*;
30
31
import javax.swing.*;
32
import javax.swing.plaf.basic.BasicComboPopup;
33
34
import sun.lwawt.macosx.CPlatformWindow;
35
36
class AquaComboBoxPopup extends BasicComboPopup {
37
static final int FOCUS_RING_PAD_LEFT = 6;
38
static final int FOCUS_RING_PAD_RIGHT = 6;
39
static final int FOCUS_RING_PAD_BOTTOM = 5;
40
41
protected Component topStrut;
42
protected Component bottomStrut;
43
protected boolean isPopDown = false;
44
45
public AquaComboBoxPopup(final JComboBox cBox) {
46
super(cBox);
47
}
48
49
@Override
50
protected void configurePopup() {
51
super.configurePopup();
52
53
setBorderPainted(false);
54
setBorder(null);
55
updateContents(false);
56
57
// TODO: CPlatformWindow?
58
putClientProperty(CPlatformWindow.WINDOW_FADE_OUT, new Integer(150));
59
}
60
61
public void updateContents(final boolean remove) {
62
// for more background on this issue, see AquaMenuBorder.getBorderInsets()
63
64
isPopDown = isPopdown();
65
if (isPopDown) {
66
if (remove) {
67
if (topStrut != null) {
68
this.remove(topStrut);
69
}
70
if (bottomStrut != null) {
71
this.remove(bottomStrut);
72
}
73
} else {
74
add(scroller);
75
}
76
} else {
77
if (topStrut == null) {
78
topStrut = Box.createVerticalStrut(4);
79
bottomStrut = Box.createVerticalStrut(4);
80
}
81
82
if (remove) remove(scroller);
83
84
this.add(topStrut);
85
this.add(scroller);
86
this.add(bottomStrut);
87
}
88
}
89
90
protected Dimension getBestPopupSizeForRowCount(final int maxRowCount) {
91
final int currentElementCount = comboBox.getModel().getSize();
92
final int rowCount = Math.min(maxRowCount, currentElementCount);
93
94
final Dimension popupSize = new Dimension();
95
final ListCellRenderer renderer = list.getCellRenderer();
96
97
for (int i = 0; i < rowCount; i++) {
98
final Object value = list.getModel().getElementAt(i);
99
final Component c = renderer.getListCellRendererComponent(list, value, i, false, false);
100
101
final Dimension prefSize = c.getPreferredSize();
102
popupSize.height += prefSize.height;
103
popupSize.width = Math.max(prefSize.width, popupSize.width);
104
}
105
106
popupSize.width += 10;
107
108
return popupSize;
109
}
110
111
protected boolean shouldScroll() {
112
return comboBox.getItemCount() > comboBox.getMaximumRowCount();
113
}
114
115
protected boolean isPopdown() {
116
return shouldScroll() || AquaComboBoxUI.isPopdown(comboBox);
117
}
118
119
@Override
120
public void show() {
121
final int startItemCount = comboBox.getItemCount();
122
123
final Rectangle popupBounds = adjustPopupAndGetBounds();
124
if (popupBounds == null) return; // null means don't show
125
126
comboBox.firePopupMenuWillBecomeVisible();
127
show(comboBox, popupBounds.x, popupBounds.y);
128
129
// hack for <rdar://problem/4905531> JComboBox does not fire popupWillBecomeVisible if item count is 0
130
final int afterShowItemCount = comboBox.getItemCount();
131
if (afterShowItemCount == 0) {
132
hide();
133
return;
134
}
135
136
if (startItemCount != afterShowItemCount) {
137
final Rectangle newBounds = adjustPopupAndGetBounds();
138
list.setSize(newBounds.width, newBounds.height);
139
pack();
140
141
final Point newLoc = comboBox.getLocationOnScreen();
142
setLocation(newLoc.x + newBounds.x, newLoc.y + newBounds.y);
143
}
144
// end hack
145
146
list.requestFocusInWindow();
147
}
148
149
@Override
150
protected JList createList() {
151
return new JList(comboBox.getModel()) {
152
@Override
153
public void processMouseEvent(MouseEvent e) {
154
if (e.isMetaDown()) {
155
e = new MouseEvent((Component)e.getSource(), e.getID(), e.getWhen(), e.getModifiers() ^ InputEvent.META_MASK, e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), MouseEvent.NOBUTTON);
156
}
157
super.processMouseEvent(e);
158
}
159
};
160
}
161
162
protected Rectangle adjustPopupAndGetBounds() {
163
if (isPopDown != isPopdown()) {
164
updateContents(true);
165
}
166
167
final Dimension popupSize = getBestPopupSizeForRowCount(comboBox.getMaximumRowCount());
168
final Rectangle popupBounds = computePopupBounds(0, comboBox.getBounds().height, popupSize.width, popupSize.height);
169
if (popupBounds == null) return null; // returning null means don't show anything
170
171
final Dimension realPopupSize = popupBounds.getSize();
172
scroller.setMaximumSize(realPopupSize);
173
scroller.setPreferredSize(realPopupSize);
174
scroller.setMinimumSize(realPopupSize);
175
list.invalidate();
176
177
final int selectedIndex = comboBox.getSelectedIndex();
178
if (selectedIndex == -1) {
179
list.clearSelection();
180
} else {
181
list.setSelectedIndex(selectedIndex);
182
}
183
list.ensureIndexIsVisible(list.getSelectedIndex());
184
185
return popupBounds;
186
}
187
188
// Get the bounds of the screen where the menu should appear
189
// p is the origin of the combo box in screen bounds
190
Rectangle getBestScreenBounds(final Point p) {
191
//System.err.println("GetBestScreenBounds p: "+ p.x + ", " + p.y);
192
final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
193
final GraphicsDevice[] gs = ge.getScreenDevices();
194
//System.err.println(" gs.length = " + gs.length);
195
final Rectangle comboBoxBounds = comboBox.getBounds();
196
if (gs.length == 1) {
197
final Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize();
198
199
//System.err.println(" scrSize: "+ scrSize);
200
201
// If the combo box is totally off screen, don't show a popup
202
if ((p.x + comboBoxBounds.width < 0) || (p.y + comboBoxBounds.height < 0) || (p.x > scrSize.width) || (p.y > scrSize.height)) {
203
return null;
204
}
205
return new Rectangle(0, 22, scrSize.width, scrSize.height - 22);
206
}
207
208
for (final GraphicsDevice gd : gs) {
209
final GraphicsConfiguration[] gc = gd.getConfigurations();
210
for (final GraphicsConfiguration element0 : gc) {
211
final Rectangle gcBounds = element0.getBounds();
212
if (gcBounds.contains(p)) return gcBounds;
213
}
214
}
215
216
// Hmm. Origin's off screen, but is any part on?
217
comboBoxBounds.setLocation(p);
218
for (final GraphicsDevice gd : gs) {
219
final GraphicsConfiguration[] gc = gd.getConfigurations();
220
for (final GraphicsConfiguration element0 : gc) {
221
final Rectangle gcBounds = element0.getBounds();
222
if (gcBounds.intersects(comboBoxBounds)) return gcBounds;
223
}
224
}
225
226
return null;
227
}
228
229
@Override
230
protected Rectangle computePopupBounds(int px, int py, int pw, int ph) {
231
final int itemCount = comboBox.getModel().getSize();
232
final boolean isPopdown = isPopdown();
233
final boolean isTableCellEditor = AquaComboBoxUI.isTableCellEditor(comboBox);
234
if (isPopdown && !isTableCellEditor) {
235
// place the popup just below the button, which is
236
// near the center of a large combo box
237
py = Math.min((py / 2) + 9, py); // if py is less than new y we have a clipped combo, so leave it alone.
238
}
239
240
// px & py are relative to the combo box
241
242
// **** Common calculation - applies to the scrolling and menu-style ****
243
final Point p = new Point(0, 0);
244
SwingUtilities.convertPointToScreen(p, comboBox);
245
//System.err.println("First Converting from point to screen: 0,0 is now " + p.x + ", " + p.y);
246
final Rectangle scrBounds = getBestScreenBounds(p);
247
//System.err.println("BestScreenBounds is " + scrBounds);
248
249
// If the combo box is totally off screen, do whatever super does
250
if (scrBounds == null) return super.computePopupBounds(px, py, pw, ph);
251
252
// line up with the bottom of the text field/button (or top, if we have to go above it)
253
// and left edge if left-to-right, right edge if right-to-left
254
final Insets comboBoxInsets = comboBox.getInsets();
255
final Rectangle comboBoxBounds = comboBox.getBounds();
256
257
if (shouldScroll()) {
258
pw += 15;
259
}
260
261
if (isPopdown) {
262
pw += 4;
263
}
264
265
// the popup should be wide enough for the items but not wider than the screen it's on
266
final int minWidth = comboBoxBounds.width - (comboBoxInsets.left + comboBoxInsets.right);
267
pw = Math.max(minWidth, pw);
268
269
final boolean leftToRight = AquaUtils.isLeftToRight(comboBox);
270
if (leftToRight) {
271
px += comboBoxInsets.left;
272
if (!isPopDown) px -= FOCUS_RING_PAD_LEFT;
273
} else {
274
px = comboBoxBounds.width - pw - comboBoxInsets.right;
275
if (!isPopDown) px += FOCUS_RING_PAD_RIGHT;
276
}
277
py -= (comboBoxInsets.bottom); //sja fix was +kInset
278
279
// Make sure it's all on the screen - shift it by the amount it's off
280
p.x += px;
281
p.y += py; // Screen location of px & py
282
if (p.x < scrBounds.x) px -= (p.x + scrBounds.x);
283
if (p.y < scrBounds.y) py -= (p.y + scrBounds.y);
284
285
final Point top = new Point(0, 0);
286
SwingUtilities.convertPointFromScreen(top, comboBox);
287
//System.err.println("Converting from point to screen: 0,0 is now " + top.x + ", " + top.y);
288
289
// Since the popup is at zero in this coord space, the maxWidth == the X coord of the screen right edge
290
// (it might be wider than the screen, if the combo is off the left edge)
291
final int maxWidth = Math.min(scrBounds.width, top.x + scrBounds.x + scrBounds.width) - 2; // subtract some buffer space
292
293
pw = Math.min(maxWidth, pw);
294
if (pw < minWidth) {
295
px -= (minWidth - pw);
296
pw = minWidth;
297
}
298
299
// this is a popup window, and will continue calculations below
300
if (!isPopdown) {
301
// popup windows are slightly inset from the combo end-cap
302
pw -= 6;
303
return computePopupBoundsForMenu(px, py, pw, ph, itemCount, scrBounds);
304
}
305
306
// don't attempt to inset table cell editors
307
if (!isTableCellEditor) {
308
pw -= (FOCUS_RING_PAD_LEFT + FOCUS_RING_PAD_RIGHT);
309
if (leftToRight) {
310
px += FOCUS_RING_PAD_LEFT;
311
}
312
}
313
314
final Rectangle r = new Rectangle(px, py, pw, ph);
315
// Check whether it goes below the bottom of the screen, if so flip it
316
if (r.y + r.height < top.y + scrBounds.y + scrBounds.height) return r;
317
318
return new Rectangle(px, -r.height + comboBoxInsets.top, r.width, r.height);
319
}
320
321
// The one to use when itemCount <= maxRowCount. Size never adjusts for arrows
322
// We want it positioned so the selected item is right above the combo box
323
protected Rectangle computePopupBoundsForMenu(final int px, final int py, final int pw, final int ph, final int itemCount, final Rectangle scrBounds) {
324
//System.err.println("computePopupBoundsForMenu: " + px + "," + py + " " + pw + "," + ph);
325
//System.err.println("itemCount: " +itemCount +" src: "+ scrBounds);
326
int elementSize = 0; //kDefaultItemSize;
327
if (list != null && itemCount > 0) {
328
final Rectangle cellBounds = list.getCellBounds(0, 0);
329
if (cellBounds != null) elementSize = cellBounds.height;
330
}
331
332
int offsetIndex = comboBox.getSelectedIndex();
333
if (offsetIndex < 0) offsetIndex = 0;
334
list.setSelectedIndex(offsetIndex);
335
336
final int selectedLocation = elementSize * offsetIndex;
337
338
final Point top = new Point(0, scrBounds.y);
339
final Point bottom = new Point(0, scrBounds.y + scrBounds.height - 20); // Allow some slack
340
SwingUtilities.convertPointFromScreen(top, comboBox);
341
SwingUtilities.convertPointFromScreen(bottom, comboBox);
342
343
final Rectangle popupBounds = new Rectangle(px, py, pw, ph);// Relative to comboBox
344
345
final int theRest = ph - selectedLocation;
346
347
// If the popup fits on the screen and the selection appears under the mouse w/o scrolling, cool!
348
// If the popup won't fit on the screen, adjust its position but not its size
349
// and rewrite this to support arrows - JLists always move the contents so they all show
350
351
// Test to see if it extends off the screen
352
final boolean extendsOffscreenAtTop = selectedLocation > -top.y;
353
final boolean extendsOffscreenAtBottom = theRest > bottom.y;
354
355
if (extendsOffscreenAtTop) {
356
popupBounds.y = top.y + 1;
357
// Round it so the selection lines up with the combobox
358
popupBounds.y = (popupBounds.y / elementSize) * elementSize;
359
} else if (extendsOffscreenAtBottom) {
360
// Provide blank space at top for off-screen stuff to scroll into
361
popupBounds.y = bottom.y - popupBounds.height; // popupBounds.height has already been adjusted to fit
362
} else { // fits - position it so the selectedLocation is under the mouse
363
popupBounds.y = -selectedLocation;
364
}
365
366
// Center the selected item on the combobox
367
final int height = comboBox.getHeight();
368
final Insets insets = comboBox.getInsets();
369
final int buttonSize = height - (insets.top + insets.bottom);
370
final int diff = (buttonSize - elementSize) / 2 + insets.top;
371
popupBounds.y += diff - FOCUS_RING_PAD_BOTTOM;
372
373
return popupBounds;
374
}
375
}
376
377