My question is on how do I go about know what the text of the drag and drop location is. This is current working code.
gameCell.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e){
JButton button = (JButton)e.getSource();
int currentNumber = Integer.parseInt(button.getText());
TransferHandler handle = button.getTransferHandler();
handle.exportAsDrag(button, e, TransferHandler.COPY);
So the idea is there is a gameboard which is just a bunch of cells (all JButtons), one large table. When I drag one cell to another then the dragged cell's value will become the clicked cell's value, so therefore how do I tell the original value of the JButton cell before it is copied over by the dragged cell.
If you are just trying to "copy" the text from one button to another then you can use code like the following:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DragIcon extends JPanel
{
public DragIcon()
{
TransferHandler iconHandler = new TransferHandler( "icon" );
MouseListener dragListener = new DragMouseAdapter();
JLabel label1 = new JLabel("Label1");
label1.setTransferHandler( iconHandler );
label1.addMouseListener(dragListener);
label1.setIcon( new ImageIcon("copy16.gif") );
JLabel label2 = new JLabel("Label2");
label2.setTransferHandler( iconHandler );
label2.addMouseListener(dragListener);
add( label1 );
add( label2 );
}
private class DragMouseAdapter extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
JComponent c = (JComponent)e.getSource();
TransferHandler handler = c.getTransferHandler();
handler.exportAsDrag(c, e, TransferHandler.COPY);
// handler.exportAsDrag(c, e, TransferHandler.MOVE);
}
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Drag Icon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DragIcon());
frame.setLocationByPlatform( true );
frame.setSize(200, 100);
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
The default TransferHandler allows you to specify a property that you want to copy. In my example I'm copying the Icon. In your case you would use:
TransferHandler iconHandler = new TransferHandler( "text" );
to copy the text.
Note in my example I also tried to "move" the Icon from one label to another but it doesn't work. I'm not sure what needs to be changed to move a property.
Related
Below is the minimum reproducible code. In MouseClicked method with mouseevent i got that Jpanel is clicked by using getelementat() method.
But which button on JPanel is clicked can not figure. Tried with convert point but making some mistake. Need to check which button on Jpanel is clicked. Need some help.
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class JListTest {
private JList list;
private DefaultListModel dataModel ;
private JTableTest tableTest;
private JPanel panel = new JPanel();
public JListTest() {
dataModel = new DefaultListModel();
list = new JList<>(dataModel);
list.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
super.mouseClicked(me);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JList target = (JList) me.getSource();
int index = target.locationToIndex(me.getPoint());
JPanel item = (JPanel) target.getModel().getElementAt(index);
Point p=SwingUtilities.convertPoint(target,me.getPoint(),item);
item.getComponentAt(p);
}
});
}
});
list.setCellRenderer(new PanelRenderer());
}
public static void main(String []args){
JListTest test=new JListTest();
test.dataModel.addElement("Lable 1");
test.dataModel.addElement("Lable 2");
test.dataModel.addElement("Lable 3");
JButton button1 = new JButton("Button 1");
JButton button2 = new JButton("Button 2");
test.panel.add(button1);
test.panel.add(button2);
test.dataModel.addElement(test.panel);
test.showUi();
}
public void showUi(){
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("Demo list");
frame.setAlwaysOnTop(true);
frame.setType(Window.Type.UTILITY);
frame.setResizable(true);
frame.getContentPane().setLayout(new BorderLayout());
JScrollPane scrollPane = new JScrollPane(list);
scrollPane.setPreferredSize(new Dimension(400, 250));
frame.getContentPane().add(scrollPane, BorderLayout.NORTH);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setSize(500, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
class PanelRenderer implements ListCellRenderer {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if(value instanceof String){
return new DefaultListCellRenderer().getListCellRendererComponent(list,value,index,isSelected,cellHasFocus);
}
return (Component) value;
}
}
}
Blockquote
item.getComponentAt(p);
What is the point of that statement? How can you tell if the code worked or not since you never assign the result of the method to a variable?
Turns out that because the panel is not really a component displayed on the frame you can't just do as I originally suggested.
If you add code like:
JPanel item = (JPanel) target.getModel().getElementAt(index);
System.out.println( item.getBounds() );
You get output like:
java.awt.Rectangle[x=-487,y=-36,width=0,height=0]
Which doesn't make any sense.
So, I changed the logic to assign the bounds to the panel as if it was displayed on the JList:
item.setBounds(target.getCellBounds(index, index));
Now, I get output like:
java.awt.Rectangle[x=0,y=54,width=487,height=36]
Which does make more sense. However, that still doesn't help because if you add:
Point p = SwingUtilities.convertPoint(target,me.getPoint(),item);
System.out.println( p );
You get something like:
java.awt.Point[x=834,y=166]
The conversion of the point does do what I expected.
So, I decided to convert the point manually:
int y = me.getY() - item.getBounds().y;
Point p = new Point(me.getX(), y);
Putting it all together you get something like:
JList target = (JList) me.getSource();
int index = target.locationToIndex(me.getPoint());
JPanel item = (JPanel) target.getModel().getElementAt(index);
item.setBounds(target.getCellBounds(index, index));
int y = me.getY() - item.getBounds().y;
Point p = new Point(me.getX(), y);
JButton button = (JButton)item.getComponentAt(p);
System.out.println(button.getText());
And you get the text of the button when you click on it.
Of course, you get Exceptions if you click anywhere else. I'll leave it up to you do handle the Exception logic.
Note, I see your latest question is about adding labels and a panel to a frame. It is a much better approach to use real components as you can add ActionListeners to your buttons.
I have written this code however I am having trouble with one aspect I wish to code into it. I want to make the green square change size when I press one of the three buttons I have so when I press the button 'small' the square changes size to small e.g. 100 and when I press the button 'medium' it changes size to medium e.g. 400. This is my code so far:
package Lab2;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main {
public static void main(String[] args) {
FilledFrame frame = new FilledFrame();
frame.setVisible( true );
}
}
class FilledFrame extends JFrame {
int size = 400;
public FilledFrame()
{
JButton butSmall = new JButton("Small");
JButton butMedium = new JButton("Medium");
JButton butLarge = new JButton("Large");
JButton butMessage = new JButton("Say Hi");
SquarePanel panel = new SquarePanel(this);
JPanel butPanel = new JPanel();
butPanel.add(butSmall);
butPanel.add(butMedium);
butPanel.add(butLarge);
butPanel.add(butMessage);
add(butPanel, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
setSize( size+100, size+100 ); } }
class SquarePanel extends JPanel {
FilledFrame theApp;
SquarePanel(FilledFrame app)
{
theApp = app;
}
public void paintComponent ( Graphics g)
{
super.paintComponent(g);
g.setColor(Color.green);
g.fillRect(20, 20, theApp.size, theApp.size);
}
}
class buttonHandler implements ActionListener {
FilledFrame theApp;
int size;
public buttonHandler(FilledFrame app, int size) {
theApp = app;
this.size = size;
}
#Override
public void actionPerformed (ActionEvent e){
theApp.setSize(this.size, this.size);
}
}
As I don't see any event listeners for your buttons, I assume this is all the code you have. Your buttons will not do anything unless you tell them to do it. You need to add event listeners, and through that change the size and update the panel.
Example:
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
theApp.size = 200;
frame.getContentPane().repaint();
//OR frame.repaint();
}
});
EDIT:
The problem with using the button handler class is you would need to find which buttton was pressed, instead its easier to use the way I showed above. I edited the code above, try copy pasting to one of the buttons.
I have two JPanels combined in a JSplitPane. The user should be able to move the split-pane-divider very freely, but the divider shouldn't be too big (at best case only 1px).
Is there a way to keep the dividerSize at 1 but to increase the hitbox for clicking on the divider? So that maybe 20px to each side the divider is also grabbed when the mouse is clicked?
You can add MouseListener to the components an manually adjust the divider location.
Here is a basic example to get you started:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DividerListener extends MouseAdapter
{
private JSplitPane splitPane;
private Component component;
private int pressedX;
private int dividerLocation;
public DividerListener(JSplitPane splitPane, Component component)
{
this.splitPane = splitPane;
this.component = component;
component.addMouseListener(this);
component.addMouseMotionListener(this);
}
#Override
public void mousePressed(MouseEvent e)
{
pressedX = SwingUtilities.convertPoint(component, e.getPoint(), splitPane).x;
dividerLocation = splitPane.getDividerLocation();
}
#Override
public void mouseDragged(MouseEvent e)
{
int draggedX = SwingUtilities.convertPoint(component, e.getPoint(), splitPane).x;
int deltaX = draggedX - pressedX;
splitPane.setDividerLocation(dividerLocation + deltaX);
}
private static void createAndShowGUI()
{
JSplitPane splitPane = new JSplitPane();
JLabel left = new JLabel("LEFT");
splitPane.setLeftComponent(left);
new DividerListener(splitPane, left);
JLabel right = new JLabel("RIGHT");
splitPane.setRightComponent(right);
new DividerListener(splitPane, right);
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( splitPane);
frame.setLocationByPlatform( true );
frame.setSize(200, 200);
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
You will need to customize the logic to check if the mouse is pressed at the right edge of the left component or the left edge of the right component. So you will probably need to add another parameter so you know how to do the bounds checking.
Then you would need to set a flag so the mouseDragged code can either process the event or ignore it when you are not close enough to the edge.
I have JSplitPane that has oneTouchExpandable set to true.
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
splitPane.setDividerSize(10);
splitPane.setOneTouchExpandable(true);
The problem is that I do not know how to attach key bindings to up and down arrows on JSplitPane's divider. For up arrow I want Ctrl+U and for down - Ctrl + D.
Thanks!
Implementation of the arrow button shown by OneTouchExpandable is UI label and will take extra work unnecessarily to bind them. You can easily use Key Binding on JSplitPane itself to control the JSplitPane divider location using setDividerLocation(int). Increase on Ctrl + U and Decrease on Ctrl + D. For example:
Action incrDividerLoc = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JSplitPane srcSplitPan = (JSplitPane) e.getSource();
(srcSplitPan).setDividerLocation(srcSplitPan.getDividerLocation()+10);
}
};
Action decrDividerLoc = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JSplitPane srcSplitPan = (JSplitPane) e.getSource();
(srcSplitPan).setDividerLocation(srcSplitPan.getDividerLocation()-10);
}
};
jSplitPane1.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK),
"increaseDivider");
jSplitPane1.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK),
"decreaseDivider");
jSplitPane1.getActionMap().put("increaseDivider", incrDividerLoc);
jSplitPane1.getActionMap().put("decreaseDivider", decrDividerLoc);
Note: method A value less than 0 passed to setDividerLocation(int) implies the divider should be reset to a value that attempts to honor the preferred size of the left/top component. After notifying the listeners, the last divider location is updated, via setLastDividerLocation.
The problem is that I do not know how to attach key bindings to up and down arrows on JSplitPane's divider.
Normally you would try to access the Action of the button. In many cases the component will already define an Action that you can use. See Key Bindings for a list of the default bindings for a JSplitPane. Unfortunately there is no Action to support the one touch clicking options.
So we need to access the buttons directly from the UI:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
public class SplitPaneDividerAction extends AbstractAction
{
private boolean leading;
public SplitPaneDividerAction(boolean leading)
{
this.leading = leading;
}
#Override
public void actionPerformed(ActionEvent e)
{
JSplitPane splitPane = (JSplitPane)e.getSource();
BasicSplitPaneUI ui = (BasicSplitPaneUI)splitPane.getUI();
BasicSplitPaneDivider divider = ui.getDivider();
if (leading)
((JButton)divider.getComponent(0)).doClick();
else
((JButton)divider.getComponent(1)).doClick();
}
private static void createAndShowUI()
{
JPanel leading = new JPanel();
leading.setPreferredSize( new Dimension(200, 100) );
leading.setBackground( Color.BLUE );
leading.setFocusable(true);
JPanel trailing = new JPanel();
trailing.setPreferredSize( new Dimension(200, 100) );
trailing.setBackground( Color.RED );
trailing.setFocusable(true);
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leading, trailing);
splitPane.setOneTouchExpandable(true);
splitPane.setDividerLocation(100);
InputMap im = splitPane.getInputMap(JSplitPane.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
ActionMap am = splitPane.getActionMap();
im.put(KeyStroke.getKeyStroke("control U"), "leading");
im.put(KeyStroke.getKeyStroke("control D"), "trailing");
am.put("leading", new SplitPaneDividerAction(true));
am.put("trailing", new SplitPaneDividerAction(false));
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( splitPane );
frame.setSize(200, 200);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Of course this approach will only work if your LAF extends from the BasicSplitPaneUI.
I have a JTreeTable and have successfully implemented a MouseMotionListener to show a tooltip whenever the mouse is over one of the cells. However when clicking on the cell the tooltip does not show up. I've tried several things like setting the text on the mouseClicked and mouseReleased events but that doesn't work. I found this code -
Action toolTipAction = treeTable.getActionMap().get("postTip");
if(toolTipAction != null){
ActionEvent postTip = new ActionEvent(treeTable,ActionEvent.ACTION_PERFORMED, "");
toolTipAction.actionPerformed(postTip);
}
to use in the mouseReleased method, which does make the tooltip popup, but it's then in the wrong position. So next i tried overriding the getTooltipLocation method on the JTreeTable, and this works fine for mouseMoved events but doesn't get called with the above method. Can anyone shed some light on how to do this?
Thanks
Andy
You can use the following approach to show the tooltip (there will be a slight delay). Then you can override the getToolTipLocation() method since a MouseEvent will now be generated:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ToolTipOnRelease extends JPanel
{
public ToolTipOnRelease()
{
JLabel label = new JLabel( "First Name:" );
add( label );
JTextField textField = new JTextField(15);
add( textField );
MouseListener ml = new MouseAdapter()
{
public void mouseReleased(MouseEvent e)
{
JComponent component = (JComponent)e.getSource();
component.setToolTipText("Mouse released on: " + component.getClass().toString());
MouseEvent phantom = new MouseEvent(
component,
MouseEvent.MOUSE_MOVED,
System.currentTimeMillis(),
0,
0,
0,
0,
false);
ToolTipManager.sharedInstance().mouseMoved(phantom);
}
};
label.addMouseListener( ml );
textField.addMouseListener( ml );
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("ToolTipOnRelease");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new ToolTipOnRelease() );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
org.apache.jorphan.gui.JTreeTable extends javax.swing.JComponent
javax.swing.JComponent#setToopTipText() doesn't work?
I do realize that you want to use Action but for tooltips? I would use Action when multiple UI actions would need to share it.