/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
* required for a production-quality application, such as security checks,
* input validation and proper error handling, might not be present in
* this sample code.
*/
package transparentruler;
import java.awt.*;
import java.awt.GraphicsDevice.WindowTranslucency;
import static java.awt.GraphicsDevice.WindowTranslucency.*;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Path2D.Float;
import java.lang.reflect.InvocationTargetException;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
/**
* This sample demonstrates shaped and translucent window feature.
* @author Alexander Kouznetsov
*/
@SuppressWarnings("serial")
public class Ruler extends JFrame {
private static final Color BACKGROUND = Color.RED;
private static final Color FOREGROUND = Color.WHITE;
private static final int OPACITY = 180;
private static final int W = 70;
private static final int F_HEIGHT = 400;
private static final int F_WIDTH = (int) (F_HEIGHT * 1.618 + 0.5);
private static boolean translucencySupported;
private static boolean transparencySupported;
private static boolean checkTranslucencyMode(WindowTranslucency arg) {
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
return gd.isWindowTranslucencySupported(arg);
}
public Shape buildShape() {
int h = getHeight();
int w = getWidth();
float a = (float) Math.hypot(h, w);
Float path = new java.awt.geom.Path2D.Float();
path.moveTo(0, 0);
path.lineTo(w, 0);
path.lineTo(0, h);
path.closePath();
path.moveTo(W, W);
path.lineTo(W, h - W * (a + h) / w);
path.lineTo(w - W * (a + w) / h, W);
path.closePath();
return path;
}
private final ComponentAdapter componentListener = new ComponentAdapter() {
/**
* Applies the shape to window. It is recommended to apply shape in
* componentResized() method
*/
@Override
public void componentResized(ComponentEvent e) {
// We do apply shape only if PERPIXEL_TRANSPARENT is supported
if (transparencySupported) {
setShape(buildShape());
}
}
};
private final Action exitAction = new AbstractAction("Exit") {
{
putValue(Action.MNEMONIC_KEY, KeyEvent.VK_X);
}
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
};
private final JPopupMenu jPopupMenu = new JPopupMenu();
{
jPopupMenu.add(new JMenuItem(exitAction));
// To avoid popup cutting by main window shape forbid light-weight popups
jPopupMenu.setLightWeightPopupEnabled(false);
}
/**
* Implements mouse-related behavior: window dragging and popup menu
* invocation
*/
private final MouseAdapter mouseListener = new MouseAdapter() {
int x, y;
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
x = e.getX();
y = e.getY();
}
}
@Override
public void mouseDragged(MouseEvent e) {
if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
setLocation(e.getXOnScreen() - x, e.getYOnScreen() - y);
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
jPopupMenu.show(getContentPane(), e.getX(), e.getY());
}
}
};
/**
* Implements keyboard navigation. Arrows move by 5 pixels, Ctrl + arrows
* move by 50 pixels, Alt + arrows move by 1 pixel.
* Esc exits the application.
*/
private final KeyAdapter keyboardListener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int step = e.isControlDown() ? 50 : e.isAltDown() ? 1 : 5;
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
setLocation(getX() - step, getY());
break;
case KeyEvent.VK_RIGHT:
setLocation(getX() + step, getY());
break;
case KeyEvent.VK_UP:
setLocation(getX(), getY() - step);
break;
case KeyEvent.VK_DOWN:
setLocation(getX(), getY() + step);
break;
case KeyEvent.VK_ESCAPE:
exitAction.actionPerformed(null);
}
}
};
public Ruler() {
setUndecorated(true);
// Enables perpixel translucency
setBackground(new Color(BACKGROUND.getRed(), BACKGROUND.getGreen(),
BACKGROUND.getBlue(), OPACITY));
addMouseListener(mouseListener);
addMouseMotionListener(mouseListener);
addComponentListener(componentListener);
addKeyListener(keyboardListener);
setContentPane(new JPanel() {
@Override
protected void paintComponent(Graphics g) {
Graphics2D gg = (Graphics2D) g.create();
int w = getWidth();
int h = getHeight();
int hh = gg.getFontMetrics().getAscent();
// This is an approach to apply shape when PERPIXEL_TRANSPARENT
// isn't supported
if (!transparencySupported) {
gg.setBackground(new Color(0, 0, 0, 0));
gg.clearRect(0, 0, w, h);
gg.clip(buildShape());
gg.setBackground(Ruler.this.getBackground());
gg.clearRect(0, 0, w, h);
}
gg.setColor(FOREGROUND);
for (int x = 0; x < w * (h - 8) / h - 5; x += 5) {
boolean hi = x % 50 == 0;
gg.drawLine(x + 5, 0, x + 5,
hi ? 20 : (x % 25 == 0 ? 13 : 8));
if (hi) {
String number = Integer.toString(x);
int ww = gg.getFontMetrics().stringWidth(number);
gg.drawString(number, x + 5 - ww / 2, 20 + hh);
}
}
gg.dispose();
}
});
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(F_WIDTH, F_HEIGHT);
setLocationByPlatform(true);
}
/**
* @param args the command line arguments are ignored
*/
public static void main(String[] args) throws InterruptedException, InvocationTargetException {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
translucencySupported = checkTranslucencyMode(PERPIXEL_TRANSLUCENT);
transparencySupported = checkTranslucencyMode(PERPIXEL_TRANSPARENT);
if (!translucencySupported) {
System.err.println("This application requires "
+ "'PERPIXEL_TRANSLUCENT' translucency mode to "
+ "be supported.");
System.exit(-1);
}
Ruler ruler = new Ruler();
ruler.setVisible(true);
}
});
}
}