2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
29 package jexer
.backend
;
31 import java
.awt
.Color
;
32 import java
.awt
.Cursor
;
34 import java
.awt
.Graphics
;
35 import java
.awt
.Insets
;
36 import java
.awt
.Point
;
37 import java
.awt
.Toolkit
;
38 import java
.awt
.event
.ComponentListener
;
39 import java
.awt
.event
.KeyListener
;
40 import java
.awt
.event
.MouseListener
;
41 import java
.awt
.event
.MouseMotionListener
;
42 import java
.awt
.event
.MouseWheelListener
;
43 import java
.awt
.event
.WindowListener
;
44 import java
.awt
.image
.BufferedImage
;
45 import java
.awt
.image
.BufferStrategy
;
46 import javax
.swing
.JComponent
;
47 import javax
.swing
.JFrame
;
48 import javax
.swing
.SwingUtilities
;
51 * Wrapper for integrating with Swing, because JFrame and JComponent have
52 * separate hierarchies.
54 class SwingComponent
{
56 // ------------------------------------------------------------------------
57 // Variables --------------------------------------------------------------
58 // ------------------------------------------------------------------------
61 * If true, use triple buffering when drawing to a JFrame.
63 public static boolean tripleBuffer
= true;
66 * The frame reference, if we are drawing to a JFrame.
71 * The component reference, if we are drawing to a JComponent.
73 private JComponent component
;
76 * An optional border in pixels to add.
78 private static final int BORDER
= 1;
81 * Adjustable Insets for this component. This has the effect of adding a
82 * black border around the drawing area.
84 Insets adjustInsets
= new Insets(BORDER
+ 5, BORDER
, BORDER
, BORDER
);
86 // ------------------------------------------------------------------------
87 // Constructors -----------------------------------------------------------
88 // ------------------------------------------------------------------------
91 * Construct using a JFrame.
93 * @param frame the JFrame to draw to
95 public SwingComponent(final JFrame frame
) {
101 * Construct using a JComponent.
103 * @param component the JComponent to draw to
105 public SwingComponent(final JComponent component
) {
106 this.component
= component
;
110 // ------------------------------------------------------------------------
111 // SwingComponent ---------------------------------------------------------
112 // ------------------------------------------------------------------------
115 * Get the BufferStrategy object needed for triple-buffering.
117 * @return the BufferStrategy
118 * @throws IllegalArgumentException if this function is called when
119 * not rendering to a JFrame
121 public BufferStrategy
getBufferStrategy() {
123 return frame
.getBufferStrategy();
125 throw new IllegalArgumentException("BufferStrategy not used " +
126 "for JComponent access");
131 * Get the JFrame reference.
133 * @return the frame, or null if this is drawing to a JComponent
135 public JFrame
getFrame() {
140 * Get the JComponent reference.
142 * @return the component, or null if this is drawing to a JFrame
144 public JComponent
getComponent() {
149 * Setup to render to an existing JComponent.
151 public void setupComponent() {
152 component
.setBackground(Color
.black
);
154 // Kill the X11 cursor
155 // Transparent 16 x 16 pixel cursor image.
156 BufferedImage cursorImg
= new BufferedImage(16, 16,
157 BufferedImage
.TYPE_INT_ARGB
);
158 // Create a new blank cursor.
159 Cursor blankCursor
= Toolkit
.getDefaultToolkit().createCustomCursor(
160 cursorImg
, new Point(0, 0), "blank cursor");
161 component
.setCursor(blankCursor
);
163 // Be capable of seeing Tab / Shift-Tab
164 component
.setFocusTraversalKeysEnabled(false);
168 * Setup to render to an existing JFrame.
170 public void setupFrame() {
171 frame
.setTitle("Jexer Application");
172 frame
.setBackground(Color
.black
);
175 // Kill the X11 cursor
176 // Transparent 16 x 16 pixel cursor image.
177 BufferedImage cursorImg
= new BufferedImage(16, 16,
178 BufferedImage
.TYPE_INT_ARGB
);
179 // Create a new blank cursor.
180 Cursor blankCursor
= Toolkit
.getDefaultToolkit().createCustomCursor(
181 cursorImg
, new Point(0, 0), "blank cursor");
182 frame
.setCursor(blankCursor
);
184 // Be capable of seeing Tab / Shift-Tab
185 frame
.setFocusTraversalKeysEnabled(false);
187 // Setup triple-buffering
189 frame
.setIgnoreRepaint(true);
190 frame
.createBufferStrategy(3);
195 * Set the window title.
197 * @param title the new title
199 public void setTitle(final String title
) {
201 frame
.setTitle(title
);
206 * Paints this component.
208 * @param g the graphics context to use for painting
210 public void paint(Graphics g
) {
219 * Repaints this component.
221 public void repaint() {
230 * Repaints the specified rectangle of this component.
232 * @param x the x coordinate
233 * @param y the y coordinate
234 * @param width the width
235 * @param height the height
237 public void repaint(int x
, int y
, int width
, int height
) {
239 frame
.repaint(x
, y
, width
, height
);
241 component
.repaint(x
, y
, width
, height
);
246 * If a border has been set on this component, returns the border's
247 * insets; otherwise calls super.getInsets.
249 * @return the value of the insets property
251 public Insets
getInsets() {
252 Insets swingInsets
= null;
254 swingInsets
= frame
.getInsets();
256 swingInsets
= component
.getInsets();
258 Insets result
= new Insets(swingInsets
.top
+ adjustInsets
.top
,
259 swingInsets
.left
+ adjustInsets
.left
,
260 swingInsets
.bottom
+ adjustInsets
.bottom
,
261 swingInsets
.right
+ adjustInsets
.right
);
266 * Returns the current width of this component.
268 * @return the current width of this component
270 public int getWidth() {
272 return frame
.getWidth();
274 return component
.getWidth();
279 * Returns the current height of this component.
281 * @return the current height of this component
283 public int getHeight() {
285 return frame
.getHeight();
287 return component
.getHeight();
292 * Gets the font of this component.
294 * @return this component's font; if a font has not been set for this
295 * component, the font of its parent is returned
297 public Font
getFont() {
299 return frame
.getFont();
301 return component
.getFont();
306 * Sets the font of this component.
308 * @param f the font to become this component's font; if this parameter
309 * is null then this component will inherit the font of its parent
311 public void setFont(final Font f
) {
315 component
.setFont(f
);
320 * Shows or hides this Window depending on the value of parameter b.
322 * @param b if true, make visible, else make invisible
324 public void setVisible(final boolean b
) {
328 component
.setVisible(b
);
333 * Creates a graphics context for this component. This method will return
334 * null if this component is currently not displayable.
336 * @return a graphics context for this component, or null if it has none
338 public Graphics
getGraphics() {
340 return frame
.getGraphics();
342 return component
.getGraphics();
347 * Releases all of the native screen resources used by this Window, its
348 * subcomponents, and all of its owned children. That is, the resources
349 * for these Components will be destroyed, any memory they consume will
350 * be returned to the OS, and they will be marked as undisplayable.
352 public void dispose() {
356 component
.getParent().remove(component
);
361 * Resize the component to match the font dimensions.
363 * @param width the new width in pixels
364 * @param height the new height in pixels
366 public void setDimensions(final int width
, final int height
) {
367 if (SwingUtilities
.isEventDispatchThread()) {
368 // We are in the Swing thread and can safely set the size.
370 // Figure out the thickness of borders and use that to set the
373 Insets insets
= getInsets();
374 frame
.setSize(width
+ insets
.left
+ insets
.right
,
375 height
+ insets
.top
+ insets
.bottom
);
377 Insets insets
= getInsets();
378 component
.setSize(width
+ insets
.left
+ insets
.right
,
379 height
+ insets
.top
+ insets
.bottom
);
384 SwingUtilities
.invokeLater(new Runnable() {
386 // Figure out the thickness of borders and use that to set
389 Insets insets
= getInsets();
390 frame
.setSize(width
+ insets
.left
+ insets
.right
,
391 height
+ insets
.top
+ insets
.bottom
);
393 Insets insets
= getInsets();
394 component
.setSize(width
+ insets
.left
+ insets
.right
,
395 height
+ insets
.top
+ insets
.bottom
);
402 * Adds the specified component listener to receive component events from
403 * this component. If listener l is null, no exception is thrown and no
404 * action is performed.
406 * @param l the component listener
408 public void addComponentListener(ComponentListener l
) {
410 frame
.addComponentListener(l
);
412 component
.addComponentListener(l
);
417 * Adds the specified key listener to receive key events from this
418 * component. If l is null, no exception is thrown and no action is
421 * @param l the key listener.
423 public void addKeyListener(KeyListener l
) {
425 frame
.addKeyListener(l
);
427 component
.addKeyListener(l
);
432 * Adds the specified mouse listener to receive mouse events from this
433 * component. If listener l is null, no exception is thrown and no action
436 * @param l the mouse listener
438 public void addMouseListener(MouseListener l
) {
440 frame
.addMouseListener(l
);
442 component
.addMouseListener(l
);
447 * Adds the specified mouse motion listener to receive mouse motion
448 * events from this component. If listener l is null, no exception is
449 * thrown and no action is performed.
451 * @param l the mouse motion listener
453 public void addMouseMotionListener(MouseMotionListener l
) {
455 frame
.addMouseMotionListener(l
);
457 component
.addMouseMotionListener(l
);
462 * Adds the specified mouse wheel listener to receive mouse wheel events
463 * from this component. Containers also receive mouse wheel events from
466 * @param l the mouse wheel listener
468 public void addMouseWheelListener(MouseWheelListener l
) {
470 frame
.addMouseWheelListener(l
);
472 component
.addMouseWheelListener(l
);
477 * Adds the specified window listener to receive window events from this
478 * window. If l is null, no exception is thrown and no action is
481 * @param l the window listener
483 public void addWindowListener(WindowListener l
) {
485 frame
.addWindowListener(l
);