source: trunk/src/org/expeditee/gio/swing/SwingGraphicsManager.java@ 1097

Last change on this file since 1097 was 1097, checked in by davidb, 6 years ago

Newly structured files from Corey's work on logic/graphics separation

File size: 18.1 KB
Line 
1package org.expeditee.gio.swing;
2
3import java.awt.AlphaComposite;
4import java.awt.Component;
5import java.awt.Container;
6import java.awt.FontMetrics;
7import java.awt.Graphics;
8import java.awt.Graphics2D;
9import java.awt.GraphicsEnvironment;
10import java.awt.RenderingHints;
11import java.awt.Toolkit;
12import java.awt.event.ComponentListener;
13import java.awt.event.KeyListener;
14import java.awt.event.WindowListener;
15import java.awt.event.WindowStateListener;
16import java.awt.geom.AffineTransform;
17import java.awt.image.ImageObserver;
18
19import javax.swing.JFrame;
20import javax.swing.JLayeredPane;
21import javax.swing.JMenuBar;
22import javax.swing.JOptionPane;
23import javax.swing.RepaintManager;
24import javax.swing.SwingUtilities;
25import javax.swing.TransferHandler;
26
27import org.expeditee.core.Clip;
28import org.expeditee.core.Colour;
29import org.expeditee.core.Cursor;
30import org.expeditee.core.Dimension;
31import org.expeditee.core.Fill;
32import org.expeditee.core.Font;
33import org.expeditee.core.GradientFill;
34import org.expeditee.core.Image;
35import org.expeditee.core.Point;
36import org.expeditee.core.Stroke;
37import org.expeditee.core.TextLayout;
38import org.expeditee.core.bounds.PolygonBounds;
39import org.expeditee.gio.GraphicsManager;
40import org.expeditee.gio.GraphicsSurfaceStack;
41import org.expeditee.gio.swing.SwingImageManager.BlockingImageObserver;
42import org.expeditee.gio.swing.SwingImageManager.SelfAnimatingImageObserver;
43import org.expeditee.gui.DisplayController;
44import org.expeditee.items.widgets.Widget;
45import org.expeditee.items.widgets.SwingWidget;
46
47// TODO: Make stack thread-safe
48public class SwingGraphicsManager extends GraphicsManager {
49
50 private static SwingGraphicsManager _instance;
51
52 public static SwingGraphicsManager getInstance()
53 {
54 if (_instance == null) {
55 try {
56 SwingUtilities.invokeAndWait(new Runnable() {
57 @Override
58 public void run() {
59 _instance = new SwingGraphicsManager();
60 }
61 });
62 } catch (Exception e) {
63 System.err.println("Error while initialising GraphicsManager. Aborting...");
64 e.printStackTrace(System.err);
65 System.exit(1);
66 }
67
68 // Block until the window is ready
69
70 }
71
72 return _instance;
73 }
74
75 private JFrame _jFrame;
76
77 private GraphicsSurfaceStack<Graphics2D> _surfaceStack;
78
79 private SwingGraphicsManager()
80 {
81 _jFrame = new JFrame() {
82 private static final long serialVersionUID = 5179259234365906415L;
83
84 // Override the paint() method so that Expeditee's graphics are drawn
85 // when the window refreshes.
86 @Override
87 public void paint(Graphics g)
88 {
89 DisplayController.requestRefresh(false);
90 }
91 };
92
93 setWindowIcon();
94
95 /*
96 * See Java bug ID 4016934. They say that window closed events are
97 * called once the JFrame is disposed.
98 */
99 _jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
100
101 // Expeditee handles its own repainting of AWT/Swing components
102 RepaintManager.setCurrentManager(ExpediteeRepaintManager.getInstance());
103
104 // required to accept TAB key
105 _jFrame.setFocusTraversalKeysEnabled(false);
106
107 _jFrame.pack();
108
109 // Moved to here to work with JFXPanel
110 // TODO: Is the above comment still relevant? cts16
111 _jFrame.getContentPane().setLayout(new AbsoluteLayout());
112
113 // Set visible must be just after DisplayIO.Init for the message box to be the right size
114 // TODO: Is the above comment still relevant? cts16
115 _jFrame.setVisible(true);
116
117 // Create the surface stack
118 _surfaceStack = new GraphicsSurfaceStack<Graphics2D>() {
119 @Override
120 public Graphics2D getSurfaceFromImage(Image image)
121 {
122 return SwingMiscManager.getIfUsingSwingImageManager().getImageGraphics(image);
123 }
124
125 @Override
126 public void setSurfaceClip(Graphics2D surface, Clip clip)
127 {
128 if (surface == null) return;
129
130 if (clip == null) {
131 surface.setClip(null);
132 return;
133 }
134
135 if (clip.isFullyClipped()) return;
136
137 surface.setClip(SwingConversions.toSwingRectangle(clip.getBounds()));
138 }
139 };
140 refreshRootSurface();
141 }
142
143 @Override
144 protected GraphicsSurfaceStack<?> getGraphicsSurfaceStack()
145 {
146 return _surfaceStack;
147 }
148
149 public Dimension getScreenSize()
150 {
151 java.awt.Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
152
153 return SwingConversions.fromSwingDimension(size);
154 }
155
156 public Point getWindowLocation()
157 {
158 return SwingConversions.fromSwingPoint(_jFrame.getContentPane().getLocationOnScreen());
159 }
160
161 public void setWindowLocation(Point p)
162 {
163 _jFrame.setLocation(p.x, p.y);
164 }
165
166 public Dimension getWindowSize()
167 {
168 return SwingConversions.fromSwingDimension(_jFrame.getContentPane().getSize());
169 }
170
171 public void setWindowSize(Dimension d)
172 {
173 _jFrame.setSize(d.width, d.height);
174 _jFrame.setPreferredSize(SwingConversions.toSwingDimension(d));
175 }
176
177 public void requestFocus()
178 {
179 _jFrame.requestFocus();
180 }
181
182 public void setCompositeAlpha(float alpha)
183 {
184 _surfaceStack.getCurrentSurface().setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
185 }
186
187 public void setAntialiasing(boolean on)
188 {
189 setAntialiasing(_surfaceStack.getCurrentSurface(), on);
190 }
191
192 private void setAntialiasing(Graphics2D surface, boolean on)
193 {
194 if (surface == null) return;
195
196 if (on) {
197 surface.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
198 } else {
199 surface.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
200 }
201 }
202
203 public void setFont(Font font)
204 {
205 _surfaceStack.getCurrentSurface().setFont(SwingMiscManager.getIfUsingSwingFontManager().getInternalFont(font));
206 }
207
208 public void drawImage(Image image, Point topLeft, Dimension size, double angle, Point cropTopLeft, Dimension cropSize)
209 {
210 // Can't draw nothing
211 if (image == null) return;
212
213 // Can't draw nowhere
214 if (topLeft == null) return;
215
216 // If the cropped area is degenerate, abort
217 if (cropSize != null && (cropSize.width <= 0 || cropSize.height <= 0)) return;
218
219 // If the crop area is the entire image, pretend we are not cropping
220 if (Point.ORIGIN.equals(cropTopLeft) && image.getSize().equals(cropSize)) {
221 cropTopLeft = null;
222 cropSize = null;
223 }
224
225 SwingImageManager manager = SwingMiscManager.getIfUsingSwingImageManager();
226
227 ImageObserver animator = null;
228 if (image.isAnimated()) {
229 animator = manager.getAnimator(image);
230 if (animator != null) ((SelfAnimatingImageObserver)animator).activate();
231 } else {
232 animator = new SwingImageManager.LoadCompletedImageObserver();
233 }
234
235 // Default is the entire image
236 Image croppedImage = image;
237
238 // If we are cropping, do this now into a temporary image
239 if (cropTopLeft != null && cropSize != null) {
240 croppedImage = manager.createImage(cropSize.width, cropSize.height);
241 manager.getInternalImage(croppedImage).getGraphics().drawImage(manager.getInternalImage(image), 0, 0, cropSize.width - 1, cropSize.height - 1, cropTopLeft.x, cropTopLeft.y, cropTopLeft.x + cropSize.width - 1, cropTopLeft.y + cropSize.height - 1, animator);
242 animator = new BlockingImageObserver();
243 }
244
245 // Transform the image
246 AffineTransform tx = new AffineTransform();
247 if (size != null) tx.scale(((double) size.width) / croppedImage.getWidth(), ((double) size.height) / croppedImage.getHeight());
248 if (angle != 0.0) tx.rotate(angle);
249 if (topLeft != null) tx.translate(topLeft.x, topLeft.y);
250
251 // Draw the image to the current surface
252 boolean drawn = _surfaceStack.getCurrentSurface().drawImage(manager.getInternalImage(croppedImage), tx, animator);
253
254 // If the draw didn't succeed, try again after waiting for the image to load
255 if (!drawn && animator instanceof BlockingImageObserver) {
256 ((BlockingImageObserver) animator).attemptWait();
257 _surfaceStack.getCurrentSurface().drawImage(manager.getInternalImage(croppedImage), tx, animator);
258 }
259 }
260
261 public void drawRectangle(Point topLeft, Dimension size, double angle, Fill fill, Colour borderColour, Stroke borderStroke, Dimension cornerRadius)
262 {
263 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
264
265 if (angle != 0.0) {
266 AffineTransform tx = new AffineTransform();
267 tx.rotate(angle);
268 g.transform(tx);
269 }
270
271 if (fill != null) {
272 if (fill instanceof GradientFill) {
273 g.setPaint(SwingConversions.toSwingGradientPaint((GradientFill) fill));
274 } else {
275 g.setColor(SwingConversions.toSwingColor(fill.getColour()));
276 }
277
278 if (cornerRadius != null) {
279 g.fillRoundRect(topLeft.x, topLeft.y, size.width, size.height, cornerRadius.width, cornerRadius.height);
280 } else {
281 g.fillRect(topLeft.x, topLeft.y, size.width, size.height);
282 }
283 }
284
285 if (borderColour != null && borderStroke != null) {
286 g.setColor(SwingConversions.toSwingColor(borderColour));
287 g.setStroke(SwingConversions.toSwingStroke(borderStroke));
288
289 if (cornerRadius != null) {
290 g.drawRoundRect(topLeft.x, topLeft.y, size.width, size.height, cornerRadius.width, cornerRadius.height);
291 } else {
292 g.drawRect(topLeft.x, topLeft.y, size.width, size.height);
293 }
294 }
295
296 g.dispose();
297 }
298
299 public void drawOval(Point centre, Dimension diameters, double angle, Fill fill, Colour borderColour, Stroke borderStroke)
300 {
301 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
302
303 if (angle != 0.0) {
304 AffineTransform tx = new AffineTransform();
305 tx.rotate(angle);
306 g.transform(tx);
307 }
308
309 Point topLeft = new Point(centre.x - diameters.width / 2, centre.y - diameters.height / 2);
310
311 if (fill != null) {
312 if (fill instanceof GradientFill) {
313 g.setPaint(SwingConversions.toSwingGradientPaint((GradientFill) fill));
314 } else {
315 g.setColor(SwingConversions.toSwingColor(fill.getColour()));
316 }
317
318 g.fillOval(topLeft.x, topLeft.y, diameters.width, diameters.height);
319 }
320
321 if (borderColour != null && borderStroke != null) {
322 g.setColor(SwingConversions.toSwingColor(borderColour));
323 g.setStroke(SwingConversions.toSwingStroke(borderStroke));
324
325 g.drawOval(topLeft.x, topLeft.y, diameters.width, diameters.height);
326 }
327
328 g.dispose();
329 }
330
331 public void drawPolygon(PolygonBounds points, Point offset, Dimension scale, double angle, Fill fill, Colour borderColour, Stroke borderStroke)
332 {
333 if (points == null || points.getPointCount() < 2) return;
334 if (fill == null && (borderColour == null || borderStroke == null)) return;
335
336 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
337
338 AffineTransform tx = new AffineTransform();
339 Point centre = points.getCentre();
340 tx.translate(-centre.x, -centre.y);
341 if (angle != 0.0) tx.rotate(angle);
342 if (scale != null) {
343 double xScale = ((double) scale.width) / (points.getMaxX() - points.getMinX());
344 double yScale = ((double) scale.height) / (points.getMaxY() - points.getMinY());
345 tx.scale(xScale, yScale);
346 }
347 tx.translate(centre.x, centre.y);
348 if (offset != null) tx.translate(offset.x, offset.y);
349 g.transform(tx);
350
351 if (fill != null) {
352 if (fill instanceof GradientFill) {
353 g.setPaint(SwingConversions.toSwingGradientPaint((GradientFill) fill));
354 } else {
355 g.setColor(SwingConversions.toSwingColor(fill.getColour()));
356 }
357
358 g.fillPolygon(SwingConversions.toSwingPolygon(points));
359 }
360
361 if (borderColour != null && borderStroke != null) {
362 g.setColor(SwingConversions.toSwingColor(borderColour));
363 g.setStroke(SwingConversions.toSwingStroke(borderStroke));
364
365 if (points.isClosed()) {
366 g.drawPolygon(SwingConversions.toSwingPolygon(points));
367 } else {
368 int nPoints = points.getPointCount();
369 int[] xPoints = new int[nPoints];
370 int[] yPoints = new int[nPoints];
371
372 for (int i = 0; i < nPoints; i++) {
373 Point point = points.getPoint(i);
374 xPoints[i] = point.x;
375 yPoints[i] = point.y;
376 }
377
378 g.drawPolyline(xPoints, yPoints, nPoints);
379 }
380 }
381
382 g.dispose();
383 }
384
385 @Override
386 public void drawLine(int x1, int y1, int x2, int y2, Colour colour, Stroke stroke)
387 {
388 if (colour == null || stroke == null) return;
389
390 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
391 g.setColor(SwingConversions.toSwingColor(colour));
392 g.setStroke(SwingConversions.toSwingStroke(stroke));
393
394 g.drawLine(x1, y1, x2, y2);
395
396 g.dispose();
397 }
398
399 @Override
400 public void drawString(String string, Point position, Font font, Colour colour)
401 {
402 if (string == null || position == null || font == null || colour == null) return;
403
404 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
405 g.setColor(SwingConversions.toSwingColor(colour));
406 g.setFont(SwingMiscManager.getIfUsingSwingFontManager().getInternalFont(font));
407
408 g.drawString(string, position.x, position.y);
409
410 g.dispose();
411 }
412
413 @Override
414 public void drawTextLayout(TextLayout layout, Point position, Colour colour)
415 {
416 if (layout == null) return;
417
418 SwingTextLayoutManager layoutManager = SwingMiscManager.getIfUsingSwingTextLayoutManager();
419 if (layoutManager == null) {
420 drawString(layout.getLine(), position, layout.getFont(), colour);
421 return;
422 }
423
424 java.awt.font.TextLayout swingLayout = layoutManager.getInternalLayout(layout);
425
426 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
427 g.setColor(SwingConversions.toSwingColor(colour));
428
429 swingLayout.draw(g, position.x, position.y);
430 }
431
432 private Dimension _preFullscreenSize = null;
433 private boolean _fullScreenTransitionPending = false;
434
435 /**
436 * Whether or not we are in the middle of transitioning to/from fullscreen.
437 */
438 public boolean isFullscreenTransitionPending()
439 {
440 return _fullScreenTransitionPending;
441 }
442
443 /**
444 * Should be called when the SwingInputManager is notified that the fullscreen transition
445 * has finished.
446 */
447 public void finishFullscreenTransition()
448 {
449 _fullScreenTransitionPending = false;
450 }
451
452 @Override
453 public boolean canGoFullscreen() {
454 return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().isFullScreenSupported();
455 }
456
457 @Override
458 public boolean isFullscreen() {
459 return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getFullScreenWindow() == _jFrame;
460 }
461
462 @Override
463 public void goFullscreen() {
464 if (!canGoFullscreen()) {
465 System.err.println("Warning: GraphicsManager::goFullScreen() called when not available -- ignoring");
466 return;
467 }
468
469 _preFullscreenSize = getWindowSize();
470
471 _fullScreenTransitionPending = true;
472
473 _jFrame.dispose();
474 _jFrame.setUndecorated(true);
475 _jFrame.pack();
476 _jFrame.setVisible(true);
477
478 GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(_jFrame);
479 }
480
481 @Override
482 public void exitFullscreen() {
483 _fullScreenTransitionPending = true;
484
485 _jFrame.dispose();
486 _jFrame.setUndecorated(false);
487 setWindowSize(_preFullscreenSize);
488
489 GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(null);
490 _jFrame.pack();
491 _jFrame.setVisible(true);
492
493 }
494
495 @Override
496 public void setCursor(Cursor cursor)
497 {
498 if (cursor == null) return;
499
500 java.awt.Cursor swingCursor;
501 if (cursor.getType() == Cursor.CursorType.CUSTOM) {
502 SwingImageManager imageManager = SwingMiscManager.getIfUsingSwingImageManager();
503
504 if (imageManager == null) return;
505
506 swingCursor = Toolkit.getDefaultToolkit().createCustomCursor(imageManager.getInternalImage(cursor.getImage()), SwingConversions.toSwingPoint(cursor.getHotspot()), cursor.getName());
507 } else {
508 swingCursor = new java.awt.Cursor(SwingConversions.toSwingCursorType(cursor.getType()));
509 }
510
511 _jFrame.setCursor(swingCursor);
512 }
513
514 @Override
515 public Dimension getBestCursorSize(Dimension desiredSize) {
516 Toolkit toolkit = Toolkit.getDefaultToolkit();
517 java.awt.Dimension best_cursor_dim = toolkit.getBestCursorSize(desiredSize.width, desiredSize.height);
518 return SwingConversions.fromSwingDimension(best_cursor_dim);
519 }
520
521 @Override
522 public void setWindowTitle(String title)
523 {
524 _jFrame.setTitle(title);
525 }
526
527 @Override
528 public boolean showDialog(String title, String message) {
529 int result = JOptionPane.showConfirmDialog(_jFrame, message, title, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
530 return result == JOptionPane.OK_OPTION;
531 }
532
533 @Override
534 public Dimension getCurrentDrawingSurfaceSize()
535 {
536 Image currentImage = _surfaceStack.getCurrentImage();
537
538 if (currentImage != null) {
539 return currentImage.getSize();
540 } else {
541 return getWindowSize();
542 }
543 }
544
545 public void refreshRootSurface()
546 {
547 Graphics2D rootSurface = (Graphics2D) _jFrame.getContentPane().getGraphics();
548 setAntialiasing(rootSurface, true);
549 rootSurface.setFont(rootSurface.getFont().deriveFont(40f));
550 _surfaceStack.setRootSurface(rootSurface);
551 }
552
553 public JFrame getJFrame()
554 {
555 return _jFrame;
556 }
557
558 public Container getContentPane()
559 {
560 return _jFrame.getContentPane();
561 }
562
563 public JLayeredPane getLayeredPane()
564 {
565 return _jFrame.getLayeredPane();
566 }
567
568 public void setTransferHandler(TransferHandler newHandler)
569 {
570 _jFrame.setTransferHandler(newHandler);
571 }
572
573 public void addWindowListener(WindowListener l)
574 {
575 _jFrame.addWindowListener(l);
576 }
577
578 public void addWindowStateListener(WindowStateListener l)
579 {
580 _jFrame.addWindowStateListener(l);
581 }
582
583 public void addKeyListener(KeyListener l)
584 {
585 _jFrame.addKeyListener(l);
586 }
587
588 public void addComponentListener(ComponentListener l)
589 {
590 _jFrame.addComponentListener(l);
591 }
592
593 public void setGlassPane(Component glassPane)
594 {
595 _jFrame.setGlassPane(glassPane);
596 }
597
598 public Component getGlassPane()
599 {
600 return _jFrame.getGlassPane();
601 }
602
603 public JMenuBar getJMenuBar()
604 {
605 return _jFrame.getJMenuBar();
606 }
607
608 public FontMetrics getFontMetrics(java.awt.Font font)
609 {
610 return _jFrame.getFontMetrics(font);
611 }
612
613 public java.awt.Font getFont()
614 {
615 return _jFrame.getFont();
616 }
617
618 public Graphics2D getCurrentSurface()
619 {
620 return _surfaceStack.getCurrentSurface();
621 }
622
623 @Override
624 public boolean addInteractiveWidget(Widget iw)
625 {
626 if (super.addInteractiveWidget(iw)) {
627 _jFrame.add(((SwingWidget) iw).getComponent());
628 return true;
629 }
630
631 return false;
632 }
633
634 @Override
635 public boolean removeInteractiveWidget(Widget iw)
636 {
637 if (super.removeInteractiveWidget(iw)) {
638 _jFrame.remove(((SwingWidget) iw).getComponent());
639 return true;
640 }
641
642 return false;
643 }
644
645 @Override
646 protected void setWindowIcon(Image image)
647 {
648 SwingImageManager imageManager = SwingMiscManager.getIfUsingSwingImageManager();
649
650 if (imageManager != null) {
651 _jFrame.setIconImage(imageManager.getInternalImage(image));
652 }
653 }
654}
Note: See TracBrowser for help on using the repository browser.