Is there a way to offer the user unlimited relative mouse drag events in one of the Java GUI frameworks?
I would like to have a GUI element that the user can click and drag, but that element does not move: rather, some parameter (say color, volume, zoom level, etc.) gets adjusted in proportion to how far the user moves the mouse.
The trouble with Swing and SWT is that they want to report mouse coordinates, and those are bounded by the dimensions of the screen. If the mouse pointer hits the edge of the screen during the drag operation, I'm afraid my GUI element will suddenly stop adjusting to further mouse movements by the user.
Is there a way to make this work in Java?
You will probably want to look into the Robot class, specifically Robot#mouseMove(int, int).
You can listen for mouse drag events on the UI component which is being dragged via a MouseMotionListener, find the difference in the drag and reposition the mouse back onto the component.
This is a quick and dirty test to show that the idea works. Note: you will probably want to clean up the positioning of the mouse to stay at the same location; currently it just repositions the mouse to the centre of the JLabel
public static void main(String[] args) throws AWTException {
final JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(400, 400));
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
final Robot r = new Robot();
JLabel label = new JLabel("Hello World");
label.setBackground(Color.ORANGE);
label.setOpaque(true);
label.addMouseMotionListener(new MouseMotionListener() {
public void mouseMoved(MouseEvent e) {}
public void mouseDragged(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
Point point = label.getLocationOnScreen();
point.x += (label.getWidth() / 2);
point.y += (label.getHeight() / 2);
r.mouseMove(point.x, point.y);
Point movedPoint = e.getLocationOnScreen();
int diffX = point.x - movedPoint.x;
int diffY = point.y - movedPoint.y;
System.out.println("Dragged: " + diffX + ", " + diffY);
}
});
panel.add(label);
frame.add(panel);
frame.setVisible(true);
}
Related
I have a JButton and a Point (it's motion controlled by leap motion) on the same JPanel.
However, they are overlapping with JButton on top.
Is there a way to have my Point always on top in the JPanel application window?
Here's a code snippet:
public leapPanel()
{
setLayout(null); //18-12-13
setBackground(Color.WHITE);
setVisible(true); //18-12-13
button = new JButton();
button.setBounds(100, 150, 100, 100);
button.setBackground(Color.BLACK);
add(button);
points[nPoints] = new Point(PWIDTH/2, PHEIGHT/2);
nPoints++;
listener = new leapListener(this);
controller = new Controller();
controller.addListener(listener);
}
public Dimension getPreferredSize()
{
return new Dimension(PWIDTH, PHEIGHT);
}
public void paintComponent(Graphics shape)
{
super.paintComponent(shape);
Graphics2D shaped = (Graphics2D)shape;
shaped.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for(int i=0; i<nPoints; i++)
{
shaped.setColor(Color.ORANGE);
shaped.fillOval(points[i].x, points[i].y, 12, 12);
}
}
private Point2D.Float calcScreenNorm(Hand hand, Screen screen)
/* The dot position is calculated using the screen position that the
user's hand is pointing at, which is then normalized to an (x,y)
value between -1 and 1, where (0,0) is the center of the screen.
*/
{
Vector palm = hand.palmPosition();
Vector direction = hand.direction();
Vector intersect = screen.intersect(palm, direction, true);
// intersection is in screen coordinates
// test for NaN (not-a-number) result of intersection
if (Float.isNaN(intersect.getX()) || Float.isNaN(intersect.getY()))
return null;
float xNorm = (Math.min(1, Math.max(0, intersect.getX())) - 0.5f)*2; // constrain to -1 -- 1
float yNorm = (Math.min(1, Math.max(0, (1-intersect.getY()))) - 0.5f)*2;
return new Point2D.Float(xNorm, yNorm);
} // end of calcScreenNorm()
I have a JButton and a Point (it's motion controlled by leap motion) on the same JPanel.
Not when components are on the same panel. The order of painting is to paint the component first (ie. your paintComponent() method is invoked). Then the child components of the panel are painted (ie. the button is painted). This is how Swing implements the parent/child relationship between components.
Try using two panels. The main panel will have a BorderLayout. Then you can use:
main.add(button, BorderLayout.NORTH);
main.add(leapPanel, BorderLayout.CENTER);
The other option is to try to use the OverlayLayout. It allows you to stack two components on top of one another, although I must admit I have problems controlling the exact location of components when using this layout. The basic code would be:
JPanel main = new JPanel();
main.setLayout( new OverlayLayout(main) );
JPanel buttonPanel = new JPanel();
buttonPanel.add( button );
main.add(buttonPanel);
main.add(leapPanel);
Using the OverlayLayout you may experience weird painting problems with the button. If so then check out the suggestion to override isOptimizedDrawingEnabled() from Overlap Layout.
JPanel main = new JPanel();
main.setLayout(new OverlayLayout(main));
//main.setBackground(Color.WHITE);
setSize(800, 600); //18-12-13
Container con = getContentPane();
con.setBackground(Color.WHITE);
BPanel = new buttonPanel();
panel = new leapPanel();
main.add(BPanel);
main.add(panel);
con.add(main);
This only allows me to show only BPanel on the application window.
What i need is to have both the point(panel) and button(BPanel) to be displayed on the application window with the point always on top.
Correct me if i am missing something here. Thanks!
so I would like to know if it's possible to put things like buttons, text boxes, words, progress bars, ect, ect, on top of an already existing, in this example, JLabel.
Here is the image of the undercoated frame I made, followed by the snippet of code that is associated with this undercoated frame.
(I dont have 10 reputation, so here is a link to a photo)
http://prntscr.com/15516f
Map.setTitle("Map");
Map.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Map.setUndecorated(true);
Map.setBackground(new Color(0,0,0,0));
Map.setLayout(new FlowLayout());
JLabel Background = new JLabel(new ImageIcon(getClass().getResource("Map.png")));
Background.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
initialClick = e.getPoint();
getComponentAt(initialClick);
}
});
Background.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
// get location of Window
int thisX = Map.getLocation().x;
int thisY = Map.getLocation().y;
// Determine how much the mouse moved since the initial click
int xMoved = (thisX + e.getX()) - (thisX + initialClick.x);
int yMoved = (thisY + e.getY()) - (thisY + initialClick.y);
// Move window to this position
int X = thisX + xMoved;
int Y = thisY + yMoved;
Map.setLocation(X, Y);
}
});
Map.add(Background);
Map.setSize(507,512);
Map.setLocation(0, 100);
Map.setResizable(false);
Map.setVisible(false);
on a side note, and I KNOW this is FlowLayout(), but when I try to add something else, it'll just put itself above, or below my map.
I'd just like to know if I could put things on top of this Map.
Maybe I should put the image in in another way besides the JLabel?
Look into JLayeredPane and similar strategies. See How to Use Layered Panes for more details.
I would like to create something like the following in Swing:
The top part is relatively easy: I can just create a table and display it. What I'm having trouble with is the square plus and minus buttons at the bottom, which are designed to add a new item or remove the selected item respectively. In particular, I haven't been able to make the square shape because on Mac OS X and some other platforms, JButtons are rectangles with rounded corners and I can't find a way to change that. Also, I'm wanting to make sure it's a perfect square and without any space in between buttons.
How can this be accomplished in a cross-platform way on Swing?
JButtons are rectangles with rounded corners and I can't find a way to change that.
Change the Border:
button.setBorder( new LineBorder(Color.BLACK) );
Edit.
Another approach is to create your own icon from an existing button. Something like the following:
JButton button = new JButton("+");
Dimension size = button.getPreferredSize();
size.x += 6;
size.y += 6;
button.setPreferredSize(size);
Rectangle rectangle = new Rectangle(3, 3, size.x - 3, size.y - 3);
ScreenImage buttonImage = ScreenImage(button, rectangle);
ImageIcon icon = new ImageIcon(buttonImage);
JButton plus = new JButton(icon);
plus.setBorder( ... );
The above code should create an image of your button on any platform. I increased the preferred size to avoid taking an image of the border.
You will need to use the Screen Image class.
This is most easily achieved by returning a preferred size that is NxN - where N is the larger of preferred width or height.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
class SquareButton extends JButton {
SquareButton(String s) {
super(s);
}
#Override
public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
int s = (int)(d.getWidth()<d.getHeight() ? d.getHeight() : d.getWidth());
return new Dimension (s,s);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JComponent gui = new JPanel(new FlowLayout());
for (int ii=0; ii<5; ii++) {
gui.add(new SquareButton("" + ii));
}
gui.setBorder(new EmptyBorder(4, 8, 4, 8));
JFrame f = new JFrame("Square Buttons");
f.add(gui);
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See http://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
You can set the size of a button using using setPreferredSize():
JButton button = new JButton("+");
button.setPreferredSize(new Dimension(10, 10));
You might be able to remove the rounded corners using:
button.setBorder(BorderFactory.createEmptyBorder());
If this does not work then you can override the paintComponent() method on JButton.
Well in order to make them square, you have 2 options: 1. Make the button hold an icon image of just a square image of a transparent image. 2. you could set the button dimensions on your own. I am not sure how to set the dimensions, but that is a an option you could choose. You can just create a JToolBar that is set on the BorderLayout.SOUTH end of the window whenever you add, and whatever buttons are added onto that, will be right next to each other. To add the buttons do this:
JButton button1 = new JButton("+");
JButton button2 = new JButton("-");
JToolBar toolbar = new JToolBar();
<JPanel,JFrame,Whatever>.add(toolbar, BorderLayout.SOUTH);
toolbar.add(button1);
toolbar.add(button2);
This will add the toolbar onto the JFrame, JPanel, or whatever you're adding it onto, and it will set it to the bottom of the screen as you see above.
Hope this helps!
I need to create an area on which one would normally apply a scrollbar, it has to scroll horizontally (the contents is only a view into a larger logical area), but I have to use some special controls placed left and right to the control in order to scroll.
I have thougth about using absolute values (according to the logical view and subtract an offset. Thus, the controls right to the offset would be placed with negative x- values and thus discarded. Controls with x values above the width would also be discarded.
Is this a valid approach?
Best regards
Soeren
You can can create a JScrollPane over a Component (your larger logical area) and remove the scrollbars.
You can then add buttons to scroll left and right. When clicked these buttons should move the view of your scrollpane. This is done by setting the absolute position of the view. You can make this relative by first getting the absolute position of the view and then incrementing/decrementing it and setting it again.
Here's a class that shows a scrollable window of a larger image.
public class ViewScroller {
public ViewScroller() {
JFrame frame = new JFrame("ViewScroller");
final ImageIcon image = new ImageIcon("path\\to\\my\\image");
JLabel label = new JLabel(image);
final JScrollPane scrollPane = new JScrollPane(label);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
JButton left = new JButton("<");
left.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Point p = scrollPane.getViewport().getViewPosition();
p.x = p.x < 10 ? 0 : p.x - 10;
scrollPane.getViewport().setViewPosition(p);
}
});
JButton right = new JButton(">");
right.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Point p = scrollPane.getViewport().getViewPosition();
int offset = p.x + scrollPane.getViewport().getWidth();
p.x = offset + 10 > image.getIconWidth() ? p.x : p.x + 10;
scrollPane.getViewport().setViewPosition(p);
}
});
frame.add(right, BorderLayout.EAST);
frame.add(left, BorderLayout.WEST);
frame.add(scrollPane, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setVisible(true);
}
}
So I've been programming in java for a semester or so, and I've had this problem a few times and finally got around to asking.
If I make a JFrame and then set the size, like setSize(400,800) for example. The frame is not actually 800 pixels long. From what I can tell it is actually more like 770 (or maybe 769) pixels long. Also, if you set the vertical size very low (below 30), the frame doesn't even show up, only the top window bar from the OS and the frame doesn't get bigger until you go to a value over 30 (so setSize(400,0) looks the same as setSize(400,20)). Why is this, it's not hard to fix but its weird and I'm curious why this is?
If you need more information about anything just ask and I'll get it to you.
JFrame SetSize() contains the the Area + Border.
I think you have to set the size of ContentPane of that
jFrame.getContentPane().setSize(800,400);
So I would advise you to use JPanel embedded in a JFrame and you draw on that JPanel. This would minimize your problem.
JFrame jf = new JFrame();
JPanel jp = new JPanel();
jp.setPreferredSize(new Dimension(400,800));// changed it to preferredSize, Thanks!
jf.getContentPane().add( jp );// adding to content pane will work here. Please read the comment bellow.
jf.pack();
I am reading this from Javadoc
The JFrame class is slightly
incompatible with Frame. Like all
other JFC/Swing top-level containers,
a JFrame contains a JRootPane as its
only child. The content pane provided
by the root pane should, as a rule,
contain all the non-menu components
displayed by the JFrame. This is
different from the AWT Frame case. For
example, to add a child to an AWT
frame you'd write:
frame.add(child);
However using JFrame you need to add
the child to the JFrame's content pane
instead:
frame.getContentPane().add(child);
It's probably because size of a frame includes the size of the border.
A Frame is a top-level window with a title and a border. The size of the frame includes any area designated for the border. The dimensions of the border area may be obtained using the getInsets method. Since the border area is included in the overall size of the frame, the border effectively obscures a portion of the frame, constraining the area available for rendering and/or displaying subcomponents to the rectangle which has an upper-left corner location of (insets.left, insets.top), and has a size of width - (insets.left + insets.right) by height - (insets.top + insets.bottom).
Source:
http://download.oracle.com/javase/tutorial/uiswing/components/frame.html
There are lots of good reasons for setting the size of a frame. One is to remember the last size the user set, and restore those settings. I have this code which seems to work for me:
package javatools.swing;
import java.util.prefs.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.JFrame;
public class FramePositionMemory {
public static final String WIDTH_PREF = "-width";
public static final String HEIGHT_PREF = "-height";
public static final String XPOS_PREF = "-xpos";
public static final String YPOS_PREF = "-ypos";
String prefix;
Window frame;
Class<?> cls;
public FramePositionMemory(String prefix, Window frame, Class<?> cls) {
this.prefix = prefix;
this.frame = frame;
this.cls = cls;
}
public void loadPosition() {
Preferences prefs = (Preferences)Preferences.userNodeForPackage(cls);
// Restore the most recent mainframe size and location
int width = prefs.getInt(prefix + WIDTH_PREF, frame.getWidth());
int height = prefs.getInt(prefix + HEIGHT_PREF, frame.getHeight());
System.out.println("WID: " + width + " HEI: " + height);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int xpos = (screenSize.width - width) / 2;
int ypos = (screenSize.height - height) / 2;
xpos = prefs.getInt(prefix + XPOS_PREF, xpos);
ypos = prefs.getInt(prefix + YPOS_PREF, ypos);
frame.setPreferredSize(new Dimension(width, height));
frame.setLocation(xpos, ypos);
frame.pack();
}
public void storePosition() {
Preferences prefs = (Preferences)Preferences.userNodeForPackage(cls);
prefs.putInt(prefix + WIDTH_PREF, frame.getWidth());
prefs.putInt(prefix + HEIGHT_PREF, frame.getHeight());
Point loc = frame.getLocation();
prefs.putInt(prefix + XPOS_PREF, (int)loc.getX());
prefs.putInt(prefix + YPOS_PREF, (int)loc.getY());
System.out.println("STORE: " + frame.getWidth() + " " + frame.getHeight() + " " + loc.getX() + " " + loc.getY());
}
}
public class Main {
void main(String[] args) {
JFrame frame = new Frame();
// SET UP YOUR FRAME HERE.
final FramePositionMemory fm = new FramePositionMemory("scannacs2", frame, Main.class);
frame.setSize(400, 400); // default size in the absence of previous setting
fm.loadPosition();
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
fm.storePosition();
}
});
frame.setVisible(true);
}
}
}
On OS X, you need to take into account existing window decorations. They add 22 pixels to the height. So on a JFrame, you need to tell the program this:
frame.setSize(width, height + 22);
I know that this question is about 6+ years old, but the answer by #Kyle doesn't work.
Using this
setSize(width - (getInsets().left + getInsets().right), height - (getInsets().top + getInsets().bottom));
But this always work in any size:
setSize(width + 14, height + 7);
If you don't want the border to border, and only want the white area, here:
setSize(width + 16, height + 39);
Also this only works on Windows 10, for MacOS users, use #ben's answer.
The top border of frame is of size 30.You can write code for printing the coordinate of any point on the frame using MouseInputAdapter.You will find when the cursor is just below the top border of the frame the y coordinate is not zero , its close to 30.Hence if you give size to frame 300 * 300 , the size available for putting the components on the frame is only 300 * 270.So if you need to have size 300 * 300 ,give 300 * 330 size of the frame.