X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2Fbackend%2FSwingComponent.java;h=df3633398b699585757cab519b1102214983a50c;hb=c4cefaa04ec122fc02efb6542451a31fdf722c32;hp=84e147263476050530bfed776a948d3a9fcb8e76;hpb=42873e30bf487bc0b695d60652dba44f82185dbb;p=nikiroo-utils.git diff --git a/src/jexer/backend/SwingComponent.java b/src/jexer/backend/SwingComponent.java index 84e1472..df36333 100644 --- a/src/jexer/backend/SwingComponent.java +++ b/src/jexer/backend/SwingComponent.java @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (C) 2017 Kevin Lamonte + * Copyright (C) 2019 Kevin Lamonte * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -43,8 +43,11 @@ import java.awt.event.MouseWheelListener; import java.awt.event.WindowListener; import java.awt.image.BufferedImage; import java.awt.image.BufferStrategy; +import java.io.IOException; +import javax.imageio.ImageIO; import javax.swing.JComponent; import javax.swing.JFrame; +import javax.swing.SwingUtilities; /** * Wrapper for integrating with Swing, because JFrame and JComponent have @@ -52,26 +55,14 @@ import javax.swing.JFrame; */ class SwingComponent { - /** - * If true, use triple buffering when drawing to a JFrame. - */ - public static boolean tripleBuffer = false; + // ------------------------------------------------------------------------ + // Variables -------------------------------------------------------------- + // ------------------------------------------------------------------------ /** - * Get the BufferStrategy object needed for triple-buffering. - * - * @return the BufferStrategy - * @throws IllegalArgumentException if this function is called when - * not rendering to a JFrame + * If true, use triple buffering when drawing to a JFrame. */ - public BufferStrategy getBufferStrategy() { - if (frame != null) { - return frame.getBufferStrategy(); - } else { - throw new IllegalArgumentException("BufferStrategy not used " + - "for JComponent access"); - } - } + public static boolean tripleBuffer = true; /** * The frame reference, if we are drawing to a JFrame. @@ -84,22 +75,19 @@ class SwingComponent { private JComponent component; /** - * Get the JFrame reference. - * - * @return the frame, or null if this is drawing to a JComponent + * An optional border in pixels to add. */ - public JFrame getFrame() { - return frame; - } + private static final int BORDER = 1; /** - * Get the JComponent reference. - * - * @return the component, or null if this is drawing to a JFrame + * Adjustable Insets for this component. This has the effect of adding a + * black border around the drawing area. */ - public JComponent getComponent() { - return component; - } + Insets adjustInsets = null; + + // ------------------------------------------------------------------------ + // Constructors ----------------------------------------------------------- + // ------------------------------------------------------------------------ /** * Construct using a JFrame. @@ -108,6 +96,16 @@ class SwingComponent { */ public SwingComponent(final JFrame frame) { this.frame = frame; + if (System.getProperty("os.name").startsWith("Linux")) { + // On my Linux dev system, a Swing frame draws its contents just + // a little off. No idea why, but I've seen it on both Debian + // and Fedora with KDE. These adjustments to the adjustments + // seem to center it OK in the frame. + adjustInsets = new Insets(BORDER + 5, BORDER, + BORDER - 3, BORDER + 2); + } else { + adjustInsets = new Insets(BORDER, BORDER, BORDER, BORDER); + } setupFrame(); } @@ -118,23 +116,74 @@ class SwingComponent { */ public SwingComponent(final JComponent component) { this.component = component; + adjustInsets = new Insets(BORDER, BORDER, BORDER, BORDER); setupComponent(); } + // ------------------------------------------------------------------------ + // SwingComponent --------------------------------------------------------- + // ------------------------------------------------------------------------ + + /** + * Get the BufferStrategy object needed for triple-buffering. + * + * @return the BufferStrategy + * @throws IllegalArgumentException if this function is called when + * not rendering to a JFrame + */ + public BufferStrategy getBufferStrategy() { + if (frame != null) { + return frame.getBufferStrategy(); + } else { + throw new IllegalArgumentException("BufferStrategy not used " + + "for JComponent access"); + } + } + + /** + * Get the JFrame reference. + * + * @return the frame, or null if this is drawing to a JComponent + */ + public JFrame getFrame() { + return frame; + } + + /** + * Get the JComponent reference. + * + * @return the component, or null if this is drawing to a JFrame + */ + public JComponent getComponent() { + return component; + } + /** * Setup to render to an existing JComponent. */ public void setupComponent() { component.setBackground(Color.black); - // Kill the X11 cursor - // Transparent 16 x 16 pixel cursor image. - BufferedImage cursorImg = new BufferedImage(16, 16, - BufferedImage.TYPE_INT_ARGB); - // Create a new blank cursor. - Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor( - cursorImg, new Point(0, 0), "blank cursor"); - component.setCursor(blankCursor); + if (System.getProperty("jexer.Swing.mouseImage") != null) { + component.setCursor(getMouseImage()); + } else if (System.getProperty("jexer.Swing.mouseStyle") != null) { + component.setCursor(getMouseCursor()); + } else if (System.getProperty("jexer.textMouse", + "true").equals("false") + ) { + // If the user has suppressed the text mouse, don't kill the X11 + // mouse. + component.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else { + // Kill the X11 cursor + // Transparent 16 x 16 pixel cursor image. + BufferedImage cursorImg = new BufferedImage(16, 16, + BufferedImage.TYPE_INT_ARGB); + // Create a new blank cursor. + Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor( + cursorImg, new Point(0, 0), "blank cursor"); + component.setCursor(blankCursor); + } // Be capable of seeing Tab / Shift-Tab component.setFocusTraversalKeysEnabled(false); @@ -148,14 +197,26 @@ class SwingComponent { frame.setBackground(Color.black); frame.pack(); - // Kill the X11 cursor - // Transparent 16 x 16 pixel cursor image. - BufferedImage cursorImg = new BufferedImage(16, 16, - BufferedImage.TYPE_INT_ARGB); - // Create a new blank cursor. - Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor( - cursorImg, new Point(0, 0), "blank cursor"); - frame.setCursor(blankCursor); + if (System.getProperty("jexer.Swing.mouseImage") != null) { + frame.setCursor(getMouseImage()); + } else if (System.getProperty("jexer.Swing.mouseStyle") != null) { + frame.setCursor(getMouseCursor()); + } else if (System.getProperty("jexer.textMouse", + "true").equals("false") + ) { + // If the user has suppressed the text mouse, don't kill the X11 + // mouse. + frame.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else { + // Kill the X11 cursor + // Transparent 16 x 16 pixel cursor image. + BufferedImage cursorImg = new BufferedImage(16, 16, + BufferedImage.TYPE_INT_ARGB); + // Create a new blank cursor. + Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor( + cursorImg, new Point(0, 0), "blank cursor"); + frame.setCursor(blankCursor); + } // Be capable of seeing Tab / Shift-Tab frame.setFocusTraversalKeysEnabled(false); @@ -167,6 +228,80 @@ class SwingComponent { } } + /** + * Load an image named in jexer.Swing.mouseImage as the mouse cursor. + * The image must be on the classpath. + * + * @return the cursor + */ + private Cursor getMouseImage() { + Cursor cursor = Cursor.getDefaultCursor(); + String filename = System.getProperty("jexer.Swing.mouseImage"); + assert (filename != null); + + try { + ClassLoader loader = Thread.currentThread(). + getContextClassLoader(); + + java.net.URL url = loader.getResource(filename); + if (url == null) { + // User named a file, but it's not on the classpath. Bail + // out. + return cursor; + } + + BufferedImage cursorImage = ImageIO.read(url); + java.awt.Dimension cursorSize = Toolkit.getDefaultToolkit(). + getBestCursorSize( + cursorImage.getWidth(), cursorImage.getHeight()); + + cursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, + new Point((int) Math.min(cursorImage.getWidth() / 2, + cursorSize.getWidth() - 1), + (int) Math.min(cursorImage.getHeight() / 2, + cursorSize.getHeight() - 1)), + "custom cursor"); + } catch (IOException e) { + e.printStackTrace(); + } + + return cursor; + } + + /** + * Get the appropriate mouse cursor based on jexer.Swing.mouseStyle. + * + * @return the cursor + */ + private Cursor getMouseCursor() { + Cursor cursor = Cursor.getDefaultCursor(); + String style = System.getProperty("jexer.Swing.mouseStyle"); + assert (style != null); + + style = style.toLowerCase(); + + if (style.equals("none")) { + // Transparent 16 x 16 pixel cursor image. + BufferedImage cursorImg = new BufferedImage(16, 16, + BufferedImage.TYPE_INT_ARGB); + // Create a new blank cursor. + cursor = Toolkit.getDefaultToolkit().createCustomCursor( + cursorImg, new Point(0, 0), "blank cursor"); + } else if (style.equals("default")) { + cursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); + } else if (style.equals("hand")) { + cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); + } else if (style.equals("text")) { + cursor = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR); + } else if (style.equals("move")) { + cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); + } else if (style.equals("crosshair")) { + cursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); + } + + return cursor; + } + /** * Set the window title. * @@ -225,11 +360,17 @@ class SwingComponent { * @return the value of the insets property */ public Insets getInsets() { + Insets swingInsets = null; if (frame != null) { - return frame.getInsets(); + swingInsets = frame.getInsets(); } else { - return component.getInsets(); + swingInsets = component.getInsets(); } + Insets result = new Insets(swingInsets.top + adjustInsets.top, + swingInsets.left + adjustInsets.left, + swingInsets.bottom + adjustInsets.bottom, + swingInsets.right + adjustInsets.right); + return result; } /** @@ -334,17 +475,38 @@ class SwingComponent { * @param height the new height in pixels */ public void setDimensions(final int width, final int height) { - // Figure out the thickness of borders and use that to set the final - // size. - Insets insets = getInsets(); - - if (frame != null) { - frame.setSize(width + insets.left + insets.right, - height + insets.top + insets.bottom); - } else { - component.setSize(width + insets.left + insets.right, - height + insets.top + insets.bottom); + if (SwingUtilities.isEventDispatchThread()) { + // We are in the Swing thread and can safely set the size. + + // Figure out the thickness of borders and use that to set the + // final size. + if (frame != null) { + Insets insets = getInsets(); + frame.setSize(width + insets.left + insets.right, + height + insets.top + insets.bottom); + } else { + Insets insets = getInsets(); + component.setSize(width + insets.left + insets.right, + height + insets.top + insets.bottom); + } + return; } + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + // Figure out the thickness of borders and use that to set + // the final size. + if (frame != null) { + Insets insets = getInsets(); + frame.setSize(width + insets.left + insets.right, + height + insets.top + insets.bottom); + } else { + Insets insets = getInsets(); + component.setSize(width + insets.left + insets.right, + height + insets.top + insets.bottom); + } + } + }); } /** @@ -435,4 +597,16 @@ class SwingComponent { } } + /** + * Requests that this Component get the input focus, if this Component's + * top-level ancestor is already the focused Window. + */ + public void requestFocusInWindow() { + if (frame != null) { + frame.requestFocusInWindow(); + } else { + component.requestFocusInWindow(); + } + } + }