1 /* 2 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * - Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * - Neither the name of Oracle nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * This source code is provided to illustrate the usage of a given feature 34 * or technique and has been deliberately simplified. Additional steps 35 * required for a production-quality application, such as security checks, 36 * input validation and proper error handling, might not be present in 37 * this sample code. 38 */ 39 40 package transparentruler; 41 42 43 import java.awt.*; 44 import java.awt.GraphicsDevice.WindowTranslucency; 45 import static java.awt.GraphicsDevice.WindowTranslucency.*; 46 import java.awt.event.ActionEvent; 47 import java.awt.event.ComponentAdapter; 48 import java.awt.event.ComponentEvent; 49 import java.awt.event.KeyAdapter; 50 import java.awt.event.KeyEvent; 51 import java.awt.event.MouseAdapter; 52 import java.awt.event.MouseEvent; 53 import java.awt.geom.Path2D.Float; 54 import java.lang.reflect.InvocationTargetException; 55 import javax.swing.AbstractAction; 56 import javax.swing.Action; 57 import javax.swing.JFrame; 58 import javax.swing.JMenuItem; 59 import javax.swing.JPanel; 60 import javax.swing.JPopupMenu; 61 import javax.swing.SwingUtilities; 62 import javax.swing.WindowConstants; 63 64 65 /** 66 * This sample demonstrates shaped and translucent window feature. 67 * @author Alexander Kouznetsov 68 */ 69 @SuppressWarnings("serial") 70 public class Ruler extends JFrame { 71 72 private static final Color BACKGROUND = Color.RED; 73 private static final Color FOREGROUND = Color.WHITE; 74 private static final int OPACITY = 180; 75 private static final int W = 70; 76 private static final int F_HEIGHT = 400; 77 private static final int F_WIDTH = (int) (F_HEIGHT * 1.618 + 0.5); 78 79 private static boolean translucencySupported; 80 private static boolean transparencySupported; 81 82 private static boolean checkTranslucencyMode(WindowTranslucency arg) { 83 GraphicsEnvironment ge = 84 GraphicsEnvironment.getLocalGraphicsEnvironment(); 85 GraphicsDevice gd = ge.getDefaultScreenDevice(); 86 return gd.isWindowTranslucencySupported(arg); 87 } 88 89 public Shape buildShape() { 90 int h = getHeight(); 91 int w = getWidth(); 92 float a = (float) Math.hypot(h, w); 93 Float path = new java.awt.geom.Path2D.Float(); 94 path.moveTo(0, 0); 95 path.lineTo(w, 0); 96 path.lineTo(0, h); 97 path.closePath(); 98 path.moveTo(W, W); 99 path.lineTo(W, h - W * (a + h) / w); 100 path.lineTo(w - W * (a + w) / h, W); 101 path.closePath(); 102 return path; 103 } 104 105 private final ComponentAdapter componentListener = new ComponentAdapter() { 106 107 /** 108 * Applies the shape to window. It is recommended to apply shape in 109 * componentResized() method 110 */ 111 @Override 112 public void componentResized(ComponentEvent e) { 113 114 // We do apply shape only if PERPIXEL_TRANSPARENT is supported 115 if (transparencySupported) { 116 setShape(buildShape()); 117 } 118 } 119 }; 120 121 private final Action exitAction = new AbstractAction("Exit") { 122 123 { 124 putValue(Action.MNEMONIC_KEY, KeyEvent.VK_X); 125 } 126 127 @Override 128 public void actionPerformed(ActionEvent e) { 129 System.exit(0); 130 } 131 }; 132 133 private final JPopupMenu jPopupMenu = new JPopupMenu(); 134 135 { 136 jPopupMenu.add(new JMenuItem(exitAction)); 137 138 // To avoid popup cutting by main window shape forbid light-weight popups 139 jPopupMenu.setLightWeightPopupEnabled(false); 140 } 141 142 /** 143 * Implements mouse-related behavior: window dragging and popup menu 144 * invocation 145 */ 146 private final MouseAdapter mouseListener = new MouseAdapter() { 147 148 int x, y; 149 150 @Override 151 public void mousePressed(MouseEvent e) { 152 if (e.getButton() == MouseEvent.BUTTON1) { 153 x = e.getX(); 154 y = e.getY(); 155 } 156 } 157 158 @Override 159 public void mouseDragged(MouseEvent e) { 160 if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0) { 161 setLocation(e.getXOnScreen() - x, e.getYOnScreen() - y); 162 } 163 } 164 165 @Override 166 public void mouseReleased(MouseEvent e) { 167 if (e.isPopupTrigger()) { 168 jPopupMenu.show(getContentPane(), e.getX(), e.getY()); 169 } 170 } 171 }; 172 173 /** 174 * Implements keyboard navigation. Arrows move by 5 pixels, Ctrl + arrows 175 * move by 50 pixels, Alt + arrows move by 1 pixel. 176 * Esc exits the application. 177 */ 178 private final KeyAdapter keyboardListener = new KeyAdapter() { 179 180 @Override 181 public void keyPressed(KeyEvent e) { 182 int step = e.isControlDown() ? 50 : e.isAltDown() ? 1 : 5; 183 switch (e.getKeyCode()) { 184 case KeyEvent.VK_LEFT: 185 setLocation(getX() - step, getY()); 186 break; 187 case KeyEvent.VK_RIGHT: 188 setLocation(getX() + step, getY()); 189 break; 190 case KeyEvent.VK_UP: 191 setLocation(getX(), getY() - step); 192 break; 193 case KeyEvent.VK_DOWN: 194 setLocation(getX(), getY() + step); 195 break; 196 case KeyEvent.VK_ESCAPE: 197 exitAction.actionPerformed(null); 198 } 199 } 200 }; 201 202 public Ruler() { 203 setUndecorated(true); 204 205 // Enables perpixel translucency 206 setBackground(new Color(BACKGROUND.getRed(), BACKGROUND.getGreen(), 207 BACKGROUND.getBlue(), OPACITY)); 208 209 addMouseListener(mouseListener); 210 addMouseMotionListener(mouseListener); 211 addComponentListener(componentListener); 212 addKeyListener(keyboardListener); 213 setContentPane(new JPanel() { 214 215 @Override 216 protected void paintComponent(Graphics g) { 217 Graphics2D gg = (Graphics2D) g.create(); 218 int w = getWidth(); 219 int h = getHeight(); 220 int hh = gg.getFontMetrics().getAscent(); 221 222 // This is an approach to apply shape when PERPIXEL_TRANSPARENT 223 // isn't supported 224 if (!transparencySupported) { 225 gg.setBackground(new Color(0, 0, 0, 0)); 226 gg.clearRect(0, 0, w, h); 227 gg.clip(buildShape()); 228 229 gg.setBackground(Ruler.this.getBackground()); 230 gg.clearRect(0, 0, w, h); 231 } 232 233 gg.setColor(FOREGROUND); 234 for (int x = 0; x < w * (h - 8) / h - 5; x += 5) { 235 boolean hi = x % 50 == 0; 236 gg.drawLine(x + 5, 0, x + 5, 237 hi ? 20 : (x % 25 == 0 ? 13 : 8)); 238 if (hi) { 239 String number = Integer.toString(x); 240 int ww = gg.getFontMetrics().stringWidth(number); 241 gg.drawString(number, x + 5 - ww / 2, 20 + hh); 242 } 243 } 244 245 gg.dispose(); 246 } 247 }); 248 setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 249 setSize(F_WIDTH, F_HEIGHT); 250 setLocationByPlatform(true); 251 } 252 253 /** 254 * @param args the command line arguments are ignored 255 */ 256 public static void main(String[] args) throws InterruptedException, InvocationTargetException { 257 258 SwingUtilities.invokeAndWait(new Runnable() { 259 260 @Override 261 public void run() { 262 translucencySupported = checkTranslucencyMode(PERPIXEL_TRANSLUCENT); 263 transparencySupported = checkTranslucencyMode(PERPIXEL_TRANSPARENT); 264 265 if (!translucencySupported) { 266 System.err.println("This application requires " 267 + "'PERPIXEL_TRANSLUCENT' translucency mode to " 268 + "be supported."); 269 System.exit(-1); 270 } 271 272 Ruler ruler = new Ruler(); 273 ruler.setVisible(true); 274 } 275 }); 276 } 277 }