Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/macosx/classes/com/apple/laf/AquaProgressBarUI.java
41154 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
import java.awt.geom.AffineTransform;
31
import java.beans.*;
32
33
import javax.swing.*;
34
import javax.swing.event.*;
35
import javax.swing.plaf.*;
36
37
import sun.swing.SwingUtilities2;
38
39
import apple.laf.JRSUIStateFactory;
40
import apple.laf.JRSUIConstants.*;
41
import apple.laf.JRSUIState.ValueState;
42
43
import com.apple.laf.AquaUtilControlSize.*;
44
import com.apple.laf.AquaUtils.RecyclableSingleton;
45
46
public class AquaProgressBarUI extends ProgressBarUI implements ChangeListener, PropertyChangeListener, AncestorListener, Sizeable {
47
private static final boolean ADJUSTTIMER = true;
48
49
private static final RecyclableSingleton<SizeDescriptor> sizeDescriptor = new RecyclableSingleton<SizeDescriptor>() {
50
@Override
51
protected SizeDescriptor getInstance() {
52
return new SizeDescriptor(new SizeVariant(146, 20)) {
53
public SizeVariant deriveSmall(final SizeVariant v) { v.alterMinSize(0, -6); return super.deriveSmall(v); }
54
};
55
}
56
};
57
static SizeDescriptor getSizeDescriptor() {
58
return sizeDescriptor.get();
59
}
60
61
protected Size sizeVariant = Size.REGULAR;
62
63
protected Color selectionForeground;
64
65
private Animator animator;
66
protected boolean isAnimating;
67
protected boolean isCircular;
68
69
protected final AquaPainter<ValueState> painter = AquaPainter.create(JRSUIStateFactory.getProgressBar());
70
71
protected JProgressBar progressBar;
72
73
public static ComponentUI createUI(final JComponent x) {
74
return new AquaProgressBarUI();
75
}
76
77
protected AquaProgressBarUI() { }
78
79
public void installUI(final JComponent c) {
80
progressBar = (JProgressBar)c;
81
installDefaults();
82
installListeners();
83
}
84
85
public void uninstallUI(final JComponent c) {
86
uninstallDefaults();
87
uninstallListeners();
88
stopAnimationTimer();
89
progressBar = null;
90
}
91
92
protected void installDefaults() {
93
progressBar.setOpaque(false);
94
LookAndFeel.installBorder(progressBar, "ProgressBar.border");
95
LookAndFeel.installColorsAndFont(progressBar, "ProgressBar.background", "ProgressBar.foreground", "ProgressBar.font");
96
selectionForeground = UIManager.getColor("ProgressBar.selectionForeground");
97
}
98
99
protected void uninstallDefaults() {
100
LookAndFeel.uninstallBorder(progressBar);
101
}
102
103
protected void installListeners() {
104
progressBar.addChangeListener(this); // Listen for changes in the progress bar's data
105
progressBar.addPropertyChangeListener(this); // Listen for changes between determinate and indeterminate state
106
progressBar.addAncestorListener(this);
107
AquaUtilControlSize.addSizePropertyListener(progressBar);
108
}
109
110
protected void uninstallListeners() {
111
AquaUtilControlSize.removeSizePropertyListener(progressBar);
112
progressBar.removeAncestorListener(this);
113
progressBar.removePropertyChangeListener(this);
114
progressBar.removeChangeListener(this);
115
}
116
117
public void stateChanged(final ChangeEvent e) {
118
progressBar.repaint();
119
}
120
121
public void propertyChange(final PropertyChangeEvent e) {
122
final String prop = e.getPropertyName();
123
if ("indeterminate".equals(prop)) {
124
if (!progressBar.isIndeterminate()) return;
125
stopAnimationTimer();
126
// start the animation thread
127
if (progressBar.isDisplayable()) {
128
startAnimationTimer();
129
}
130
}
131
132
if ("JProgressBar.style".equals(prop)) {
133
isCircular = "circular".equalsIgnoreCase(e.getNewValue() + "");
134
progressBar.repaint();
135
}
136
}
137
138
// listen for Ancestor events to stop our timer when we are no longer visible
139
// <rdar://problem/5405035> JProgressBar: UI in Aqua look and feel causes memory leaks
140
public void ancestorRemoved(final AncestorEvent e) {
141
stopAnimationTimer();
142
}
143
144
public void ancestorAdded(final AncestorEvent e) {
145
if (!progressBar.isIndeterminate()) return;
146
if (progressBar.isDisplayable()) {
147
startAnimationTimer();
148
}
149
}
150
151
public void ancestorMoved(final AncestorEvent e) { }
152
153
public void paint(final Graphics g, final JComponent c) {
154
revalidateAnimationTimers(); // revalidate to turn on/off timers when values change
155
156
painter.state.set(getState(c));
157
painter.state.set(isHorizontal() ? Orientation.HORIZONTAL : Orientation.VERTICAL);
158
painter.state.set(isAnimating ? Animating.YES : Animating.NO);
159
160
if (progressBar.isIndeterminate()) {
161
if (isCircular) {
162
painter.state.set(Widget.PROGRESS_SPINNER);
163
painter.paint(g, c, 2, 2, 16, 16);
164
return;
165
}
166
167
painter.state.set(Widget.PROGRESS_INDETERMINATE_BAR);
168
paint(g);
169
return;
170
}
171
172
painter.state.set(Widget.PROGRESS_BAR);
173
painter.state.setValue(checkValue(progressBar.getPercentComplete()));
174
paint(g);
175
}
176
177
static double checkValue(final double value) {
178
return Double.isNaN(value) ? 0 : value;
179
}
180
181
protected void paint(final Graphics g) {
182
// this is questionable. We may want the insets to mean something different.
183
final Insets i = progressBar.getInsets();
184
final int width = progressBar.getWidth() - (i.right + i.left);
185
final int height = progressBar.getHeight() - (i.bottom + i.top);
186
187
Graphics2D g2 = (Graphics2D) g;
188
final AffineTransform savedAT = g2.getTransform();
189
if (!progressBar.getComponentOrientation().isLeftToRight()) {
190
//Scale operation: Flips component about pivot
191
//Translate operation: Moves component back into original position
192
g2.scale(-1, 1);
193
g2.translate(-progressBar.getWidth(), 0);
194
}
195
painter.paint(g, progressBar, i.left, i.top, width, height);
196
197
g2.setTransform(savedAT);
198
if (progressBar.isStringPainted() && !progressBar.isIndeterminate()) {
199
paintString(g, i.left, i.top, width, height);
200
}
201
}
202
203
protected State getState(final JComponent c) {
204
if (!c.isEnabled()) return State.INACTIVE;
205
if (!AquaFocusHandler.isActive(c)) return State.INACTIVE;
206
return State.ACTIVE;
207
}
208
209
protected void paintString(final Graphics g, final int x, final int y, final int width, final int height) {
210
if (!(g instanceof Graphics2D)) return;
211
212
final Graphics2D g2 = (Graphics2D)g;
213
final String progressString = progressBar.getString();
214
g2.setFont(progressBar.getFont());
215
final Point renderLocation = getStringPlacement(g2, progressString, x, y, width, height);
216
final Rectangle oldClip = g2.getClipBounds();
217
218
if (isHorizontal()) {
219
g2.setColor(selectionForeground);
220
SwingUtilities2.drawString(progressBar, g2, progressString, renderLocation.x, renderLocation.y);
221
} else { // VERTICAL
222
// We rotate it -90 degrees, then translate it down since we are going to be bottom up.
223
final AffineTransform savedAT = g2.getTransform();
224
g2.transform(AffineTransform.getRotateInstance(0.0f - (Math.PI / 2.0f), 0, 0));
225
g2.translate(-progressBar.getHeight(), 0);
226
227
// 0,0 is now the bottom left of the viewable area, so we just draw our image at
228
// the render location since that calculation knows about rotation.
229
g2.setColor(selectionForeground);
230
SwingUtilities2.drawString(progressBar, g2, progressString, renderLocation.x, renderLocation.y);
231
232
g2.setTransform(savedAT);
233
}
234
235
g2.setClip(oldClip);
236
}
237
238
/**
239
* Designate the place where the progress string will be painted. This implementation places it at the center of the
240
* progress bar (in both x and y). Override this if you want to right, left, top, or bottom align the progress
241
* string or if you need to nudge it around for any reason.
242
*/
243
protected Point getStringPlacement(final Graphics g, final String progressString, int x, int y, int width, int height) {
244
final FontMetrics fontSizer = progressBar.getFontMetrics(progressBar.getFont());
245
final int stringWidth = fontSizer.stringWidth(progressString);
246
247
if (!isHorizontal()) {
248
// Calculate the location for the rotated text in real component coordinates.
249
// swapping x & y and width & height
250
final int oldH = height;
251
height = width;
252
width = oldH;
253
254
final int oldX = x;
255
x = y;
256
y = oldX;
257
}
258
259
return new Point(x + Math.round(width / 2 - stringWidth / 2), y + ((height + fontSizer.getAscent() - fontSizer.getLeading() - fontSizer.getDescent()) / 2) - 1);
260
}
261
262
static Dimension getCircularPreferredSize() {
263
return new Dimension(20, 20);
264
}
265
266
public Dimension getPreferredSize(final JComponent c) {
267
if (isCircular) {
268
return getCircularPreferredSize();
269
}
270
271
final FontMetrics metrics = progressBar.getFontMetrics(progressBar.getFont());
272
273
final Dimension size = isHorizontal() ? getPreferredHorizontalSize(metrics) : getPreferredVerticalSize(metrics);
274
final Insets insets = progressBar.getInsets();
275
276
size.width += insets.left + insets.right;
277
size.height += insets.top + insets.bottom;
278
return size;
279
}
280
281
protected Dimension getPreferredHorizontalSize(final FontMetrics metrics) {
282
final SizeVariant variant = getSizeDescriptor().get(sizeVariant);
283
final Dimension size = new Dimension(variant.w, variant.h);
284
if (!progressBar.isStringPainted()) return size;
285
286
// Ensure that the progress string will fit
287
final String progString = progressBar.getString();
288
final int stringWidth = metrics.stringWidth(progString);
289
if (stringWidth > size.width) {
290
size.width = stringWidth;
291
}
292
293
// This uses both Height and Descent to be sure that
294
// there is more than enough room in the progress bar
295
// for everything.
296
// This does have a strange dependency on
297
// getStringPlacememnt() in a funny way.
298
final int stringHeight = metrics.getHeight() + metrics.getDescent();
299
if (stringHeight > size.height) {
300
size.height = stringHeight;
301
}
302
return size;
303
}
304
305
protected Dimension getPreferredVerticalSize(final FontMetrics metrics) {
306
final SizeVariant variant = getSizeDescriptor().get(sizeVariant);
307
final Dimension size = new Dimension(variant.h, variant.w);
308
if (!progressBar.isStringPainted()) return size;
309
310
// Ensure that the progress string will fit.
311
final String progString = progressBar.getString();
312
final int stringHeight = metrics.getHeight() + metrics.getDescent();
313
if (stringHeight > size.width) {
314
size.width = stringHeight;
315
}
316
317
// This is also for completeness.
318
final int stringWidth = metrics.stringWidth(progString);
319
if (stringWidth > size.height) {
320
size.height = stringWidth;
321
}
322
return size;
323
}
324
325
public Dimension getMinimumSize(final JComponent c) {
326
if (isCircular) {
327
return getCircularPreferredSize();
328
}
329
330
final Dimension pref = getPreferredSize(progressBar);
331
332
// The Minimum size for this component is 10.
333
// The rationale here is that there should be at least one pixel per 10 percent.
334
if (isHorizontal()) {
335
pref.width = 10;
336
} else {
337
pref.height = 10;
338
}
339
340
return pref;
341
}
342
343
public Dimension getMaximumSize(final JComponent c) {
344
if (isCircular) {
345
return getCircularPreferredSize();
346
}
347
348
final Dimension pref = getPreferredSize(progressBar);
349
350
if (isHorizontal()) {
351
pref.width = Short.MAX_VALUE;
352
} else {
353
pref.height = Short.MAX_VALUE;
354
}
355
356
return pref;
357
}
358
359
public void applySizeFor(final JComponent c, final Size size) {
360
painter.state.set(sizeVariant = size == Size.MINI ? Size.SMALL : sizeVariant); // CUI doesn't support mini progress bars right now
361
}
362
363
protected void startAnimationTimer() {
364
if (animator == null) animator = new Animator();
365
animator.start();
366
isAnimating = true;
367
}
368
369
protected void stopAnimationTimer() {
370
if (animator != null) animator.stop();
371
isAnimating = false;
372
}
373
374
private final Rectangle fUpdateArea = new Rectangle(0, 0, 0, 0);
375
private final Dimension fLastSize = new Dimension(0, 0);
376
protected Rectangle getRepaintRect() {
377
int height = progressBar.getHeight();
378
int width = progressBar.getWidth();
379
380
if (isCircular) {
381
return new Rectangle(20, 20);
382
}
383
384
if (fLastSize.height == height && fLastSize.width == width) {
385
return fUpdateArea;
386
}
387
388
int x = 0;
389
int y = 0;
390
fLastSize.height = height;
391
fLastSize.width = width;
392
393
final int maxHeight = getMaxProgressBarHeight();
394
395
if (isHorizontal()) {
396
final int excessHeight = height - maxHeight;
397
y += excessHeight / 2;
398
height = maxHeight;
399
} else {
400
final int excessHeight = width - maxHeight;
401
x += excessHeight / 2;
402
width = maxHeight;
403
}
404
405
fUpdateArea.setBounds(x, y, width, height);
406
407
return fUpdateArea;
408
}
409
410
protected int getMaxProgressBarHeight() {
411
return getSizeDescriptor().get(sizeVariant).h;
412
}
413
414
protected boolean isHorizontal() {
415
return progressBar.getOrientation() == SwingConstants.HORIZONTAL;
416
}
417
418
protected void revalidateAnimationTimers() {
419
if (progressBar.isIndeterminate()) return;
420
421
if (!isAnimating) {
422
startAnimationTimer(); // only starts if supposed to!
423
return;
424
}
425
426
final BoundedRangeModel model = progressBar.getModel();
427
final double currentValue = model.getValue();
428
if ((currentValue == model.getMaximum()) || (currentValue == model.getMinimum())) {
429
stopAnimationTimer();
430
}
431
}
432
433
protected void repaint() {
434
final Rectangle repaintRect = getRepaintRect();
435
if (repaintRect == null) {
436
progressBar.repaint();
437
return;
438
}
439
440
progressBar.repaint(repaintRect);
441
}
442
443
protected class Animator implements ActionListener {
444
private static final int MINIMUM_DELAY = 5;
445
private Timer timer;
446
private long previousDelay; // used to tune the repaint interval
447
private long lastCall; // the last time actionPerformed was called
448
private int repaintInterval;
449
450
public Animator() {
451
repaintInterval = UIManager.getInt("ProgressBar.repaintInterval");
452
453
// Make sure repaintInterval is reasonable.
454
if (repaintInterval <= 0) repaintInterval = 100;
455
}
456
457
protected void start() {
458
previousDelay = repaintInterval;
459
lastCall = 0;
460
461
if (timer == null) {
462
timer = new Timer(repaintInterval, this);
463
} else {
464
timer.setDelay(repaintInterval);
465
}
466
467
if (ADJUSTTIMER) {
468
timer.setRepeats(false);
469
timer.setCoalesce(false);
470
}
471
472
timer.start();
473
}
474
475
protected void stop() {
476
timer.stop();
477
}
478
479
public void actionPerformed(final ActionEvent e) {
480
if (!ADJUSTTIMER) {
481
repaint();
482
return;
483
}
484
485
final long time = System.currentTimeMillis();
486
487
if (lastCall > 0) {
488
// adjust nextDelay
489
int nextDelay = (int)(previousDelay - time + lastCall + repaintInterval);
490
if (nextDelay < MINIMUM_DELAY) {
491
nextDelay = MINIMUM_DELAY;
492
}
493
494
timer.setInitialDelay(nextDelay);
495
previousDelay = nextDelay;
496
}
497
498
timer.start();
499
lastCall = time;
500
501
repaint();
502
}
503
}
504
}
505
506