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.
Related
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.
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 want to implement a JSplitPane (horizontal split) where the right component has a constant width i.e. when the divider is dragged it will jump back to the correct size, unless the divider is dragged far enough right in which case the right most component will have zero width.
To re-display the right component the user can then drag the divider far enough to the left.
I've got this mostly working, but when I resize the window depending on how much and how fast I change the window size the divider jumps reveal or hide the right component, where as what I want is that it should not change 'state' ie if the right component was not visible then it should remain invisible and vice versa.
I've tried heaps of things but the main obstacle is that there seems to be no way of knowing weather the divider was dragged by the user via mouse or if the code (my divider logic and/or JSplitPane internal logic) changed the divider position.
Here is a self contained test case, run it and try to drag the horizontal divider to hide and reveal the right side panel and with those hidden/shown try to resize the window.
Does not work as intended on Mac OS X Java 1.6 (Apple) or Java 7 (Oracle). With the Oracle stuff the rendering is much slower and the problem is more severe. Resizing the window slowly works, but fast window size changes cause problems.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import net.miginfocom.swing.MigLayout;
public class SplitTest {
public static class MySplitPane extends JSplitPane {
boolean m_RightCollapsed;
public MySplitPane(int orientation, JComponent left, JComponent right) {
super(orientation, left, right);
addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
}
});
addComponentListener(new ComponentListener() {
#Override
public void componentShown(ComponentEvent e) {
reposDivider();
}
#Override
public void componentResized(ComponentEvent e) {
reposDivider();
}
#Override
public void componentMoved(ComponentEvent e) {
reposDivider();
}
#Override
public void componentHidden(ComponentEvent e) {
}
});
}
public void reposDivider() {
setDividerLocation(getDividerLocation());
}
public void setDividerLocation(int location) {
int newLocation;
m_RightCollapsed = location > getSize().width - getRightComponent().getPreferredSize().width / 2;
if (m_RightCollapsed)
newLocation = getSize().width;
else
newLocation = getSize().width - getInsets().right - getDividerSize() - getRightComponent().getPreferredSize().width;
super.setDividerLocation(newLocation);
}
}
static class MyScrollable extends JPanel implements Scrollable {
int m_Height;
public MyScrollable(int height) {
m_Height = height;
}
#Override
public void paint(java.awt.Graphics g) {
super.paint(g);
g.setColor(Color.CYAN);
g.fillOval(0, 0, getWidth(), 500);
}
#Override
public Dimension getPreferredScrollableViewportSize() {
//return super.getPreferredSize();
return new Dimension(100, m_Height);
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
// TODO Auto-generated method stub
return 10;
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 20;
}
#Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
public static class ShrinkGrow extends JPanel {
public ShrinkGrow(final JComponent component, final JSplitPane split) {
JButton grow = new JButton("+++");
JButton shrink = new JButton("---");
add(grow);
add(shrink);
grow.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Dimension oldSize = component.getPreferredSize();
Dimension newSize = new Dimension(oldSize.width, oldSize.height + 10);
component.setPreferredSize(newSize);
component.setSize(newSize);
}
});
shrink.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Dimension oldSize = component.getPreferredSize();
Dimension newSize = new Dimension(oldSize.width, oldSize.height - 10);
component.setPreferredSize(newSize);
component.setSize(newSize);
}
});
}
}
public static void main(String[] args) {
JFrame window = new JFrame();
JPanel mainView = new JPanel();
JPanel top = new JPanel();
top.setLayout(new BoxLayout(top, BoxLayout.Y_AXIS));
JPanel bottom = new JPanel();
bottom.setLayout(new BoxLayout(bottom, BoxLayout.Y_AXIS));
final JSplitPane rightSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
JPanel topContent = new MyScrollable(200);
JPanel topFixed = new ShrinkGrow(topContent, rightSplit);
topFixed.setLayout(new BoxLayout(topFixed, BoxLayout.X_AXIS));
JScrollPane topFlexible = new JScrollPane(topContent);
topFlexible.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
topFlexible.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
JPanel bottomContent = new MyScrollable(300);
JPanel bottomFixed = new ShrinkGrow(bottomContent, rightSplit);
bottomFixed.setLayout(new BoxLayout(bottomFixed, BoxLayout.X_AXIS));
JScrollPane bottomFlexible = new JScrollPane(bottomContent);
bottomFlexible.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
bottomFlexible.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
mainView.setBackground(Color.red);
topFixed.setBackground(Color.green);
topContent.setBackground(Color.green.darker());
bottomFixed.setBackground(Color.blue);
bottomContent.setBackground(Color.blue.darker());
mainView.setMinimumSize(new Dimension(100, 100));
mainView.setPreferredSize(new Dimension(400, 300));
mainView.setMaximumSize(new Dimension(10000, 10000));
topFixed.setMinimumSize(new Dimension(topFixed.getMinimumSize().width, 30));
topFixed.setPreferredSize(new Dimension(topFixed.getPreferredSize().width, 30));
topFixed.setMaximumSize(new Dimension(topFixed.getMaximumSize().width, 30));
bottomFixed.setMinimumSize(new Dimension(bottomFixed.getMinimumSize().width, 40));
bottomFixed.setPreferredSize(new Dimension(bottomFixed.getPreferredSize().width, 40));
bottomFixed.setMaximumSize(new Dimension(bottomFixed.getMaximumSize().width, 40));
topContent.setPreferredSize(new Dimension(100, 500));
bottomContent.setPreferredSize(new Dimension(100, 400));
top.add(topFixed);
top.add(topFlexible);
bottom.add(bottomFixed);
bottom.add(bottomFlexible);
rightSplit.setLeftComponent(top);
rightSplit.setRightComponent(bottom);
rightSplit.setMinimumSize(new Dimension(0, 0));
final JSplitPane mainSplit = new MySplitPane(JSplitPane.HORIZONTAL_SPLIT, mainView, rightSplit);
window.add(mainSplit);
window.pack();
window.setVisible(true);
}
}
Not sure if possible to catch dragging event, but for sure you can catch propertyChange event. Catching events after you move a JSplitPane‘s divider can be made possible through the PropertyChangeListener JSplitPane class. Make sure you supply the DIVIDER_LOCATION_PROPERTY as the parameter so that this listener will listent to modified divider location events.
If you do not supply this as the first parameter in the addPropertyChangeListener() method, you can always place a conditional statement if the PropertyChangeEvent‘s getPropertyName() method returns dividerLocation as the value.
jSplitPane1.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pce) {
// do here
}
});
Add a MouseListener to the JSplitPane Divider to detect when the divider is being dragged. When dragged, respond to the property change events. Sample:
https://community.oracle.com/thread/1352161?start=0&tstart=0
SplitPaneUI spui = splitPane.getUI();
if (spui instanceof BasicSplitPaneUI) {
// Setting a mouse listener directly on split pane does not work, because no events are being received.
((BasicSplitPaneUI) spui).getDivider().addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
...unless the divider is dragged far enough right in which case the right
most component will have zero width.
To re-display the right component the user can then drag the divider
far enough to the left.
sounds like all you have to do is disable the splitPane to disable dragging,
then setOneTouchExpandable() to true. you may need to remove one of the 'expandable'
buttons to disable expanding the wrong way
I have a Swing app with a large panel which is wrapped in a JScrollPane. Users normally move between the panel's subcomponents by tabbing, so when they tab to something out view, I want the scroll pane to autoscroll so the component with input focus is always visible.
I've tried using KeyboardFocusManager to listen for input focus changes, and then calling scrollRectToVisible.
Here's an SSCCE displaying my current strategy (just copy/paste and run!):
import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class FollowFocus {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final int ROWS = 100;
final JPanel content = new JPanel();
content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
content.add(new JLabel(
"Thanks for helping out. Use tab to move around."));
for (int i = 0; i < ROWS; i++) {
JTextField field = new JTextField("" + i);
field.setName("field#" + i);
content.add(field);
}
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("focusOwner",
new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (!(evt.getNewValue() instanceof JComponent)) {
return;
}
JComponent focused = (JComponent) evt.getNewValue();
if (content.isAncestorOf(focused)) {
System.out.println("Scrolling to " + focused.getName());
focused.scrollRectToVisible(focused.getBounds());
}
}
});
JFrame window = new JFrame("Follow focus");
window.setContentPane(new JScrollPane(content));
window.setSize(200, 200);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
});
}
}
If you run this example, you'll notice it doesn't work very well. It does get the focus change notifications, but the call to scrollRectToVisible doesn't appear to have any effect. In my app (which is too complex to show here), scrollRectToVisible works about half the time when I tab into something outside of the viewport.
Is there an established way to solve this problem? If it makes any difference, the Swing app is built on Netbeans RCP (and most of our customers run Windows).
My comment to the other answer:
scrollRectToVisible on the component itself is the whole point of that
method ;-) It's passed up the hierarchy until a parent doing the
scroll is found
... except when the component itself handles it - as JTextField does: it's implemented to scroll horizontally to make the caret visible. The way out is to call the method on the field's parent.
Edit
just for clarity, the replaced line is
content.scrollRectToVisible(focused.getBounds());
you have to take Rectangle from JPanel and JViewPort too, then compare, for example
notice (against down-voting) for final and nice output required some work for positions in the JViewPort
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
//http://stackoverflow.com/questions/8245328/how-do-i-make-jscrollpane-scroll-to-follow-input-focus
public class FollowFocus {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final int ROWS = 100;
final JPanel content = new JPanel();
content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
content.add(new JLabel(
"Thanks for helping out. Use tab to move around."));
for (int i = 0; i < ROWS; i++) {
JTextField field = new JTextField("" + i);
field.setName("field#" + i);
content.add(field);
}
final JScrollPane scroll = new JScrollPane(content);
KeyboardFocusManager.getCurrentKeyboardFocusManager().
addPropertyChangeListener("focusOwner", new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (!(evt.getNewValue() instanceof JComponent)) {
return;
}
JViewport viewport = (JViewport) content.getParent();
JComponent focused = (JComponent) evt.getNewValue();
if (content.isAncestorOf(focused)) {
System.out.println("Scrolling to " + focused.getName());
Rectangle rect = focused.getBounds();
Rectangle r2 = viewport.getVisibleRect();
content.scrollRectToVisible(new Rectangle(rect.x, rect.y, (int) r2.getWidth(), (int) r2.getHeight()));
}
}
});
JFrame window = new JFrame("Follow focus");
window.setContentPane(new JScrollPane(content));
window.setSize(200, 200);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
});
}
}
Here my short summary.
Add this to your Tools class:
public static void addOnEnter(Component c, Consumer<FocusEvent> onEnter) {
FocusListener fl = new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
onEnter.accept(e);
}
#Override
public void focusLost(FocusEvent e) { }
};
c.addFocusListener(fl);
}
public static void scrollToFocus(FocusEvent e) {
((JComponent) e.getComponent().getParent()).scrollRectToVisible(
e.getComponent().getBounds());
}
and use it like this:
Tools.addOnEnter(component, Tools::scrollToFocus);
component can be JTextField, JButton, ...
One major issue in your code is:
focused.scrollRectToVisible(focused.getBounds());
You are calling scrollRectToVisible on the component itself! Presumably a typo.
Make your JScrollPane a final variable and call
scrollPane.getViewport().scrollRectToVisible(focused.getBounds());
Here jtextbox is the component you want to focus and jscrollpane is your scrollpane:
jScrollpane.getVerticalScrollBar().setValue(jtextbox.getLocation().x);
Current I can add a bunch of customed component objects to the JPanel by pressing "add" JButton. I also got a "delete" JButton which I wish to do the opposite of "add".
My intention is that I can select a component with a mouse and click the delete button and pressto!, the component is gone.
I hook a MouseListener to the panel, and use MouseEvent, e.getComponent() to get w/e current component the mouse clicks on. So if it returns a custom component then a variable "private myComponent current" (already set to null) will point to that component. Then I can just click on "delete" button to remove it. An actionListener already added in "delete" button and in the body it calls this.remove(current) (if current is not null).
However, this doesn't work as I can't remove a component! Any pointer?
If there is an elegant way to managing add/remove components please suggest!
public class MainDisplayPanel extends JPanel implements ActionListener, MouseListener{
private JButton newClassButton;
private JButton deleteButton;
private Resizable current;
private Resizable resizer;
public MainDisplayPanel(LayoutManager layout) {
super(layout);
newClassButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addResizer();
}
});
deleteButton = new JButton("Delete");
deleteButton.addActionListener(this);
this.addMouseListener(this);
this.add(newClassButton);
this.add(deleteButton);
}
public void addResizer() {
//JPanel panel = new JPanel();
//panel.setBackground(Color.white);
resizer = new Resizable( new ClassBox());
this.add(resizer);
this.revalidate();
this.repaint();
}
public void actionPerformed(ActionEvent e) {
if(current!=null)
{
this.remove(current);
this.revalidate();
this.repaint();
}
}
public void mouseClicked(MouseEvent e) {
System.out.println(e);
Component component = e.getComponent();
if(component instanceof Resizable)
current= (Resizable) e.getComponent();
}
public static void main(String[] args) {
JFrame jframe = new JFrame();
jframe.add(new MainDisplayPanel(null));
jframe.setSize(new Dimension(600,400));
jframe.setVisible(true);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Doh!
Now, in the addResizer() method. Every time I press the add button to add a new Resizable object, what'd happen to the previously added objects? I'm certain that they become null because resizer variable no longer referring to it them??? Even if this is the case, they are still displayed on the panel...And if I pressed delete only the newly added Resizable object gets removed. So am I on the right track here?
Edit: to sum up my problem, I hooked the MouseListener to wrong object. It should be Resizable object instead of the panel. Therefore, variable current is always null.
Your problem is your MouseLisetener. You are listening to the MainDisplayPanel, and so when you click on the JPanel, the MouseEvent#getComponent method returned by, e, in your mousePressed method will return the MainDisplayPanel instance since that is what is being listened to, not the Resizable instance that is under the mouse.
Solutions include:
creating one MouseListener object and adding this same object to each Resizable as a MouseListener for the Resizable, or
using your current set up, but hold your Resizable's in an ArrayList and then iterating through the array list in the mousePressed method to see if any Resizable has been clicked by using the componentAt(...) method.
Note that I had to create my own SSCCE to solve this. Again in the future, please do us all a favor and do this for us as it really is in your and our best interest, and shows that you respect our time and our help.
Edit 1
My SSCCE:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class MainDisplayPanel extends JPanel {
private static final int RESIZABLE_COUNT = 40;
private JButton deleteButton;
private Resizable current;
private Resizable resizer;
private List<Resizable> resizableList = new ArrayList<Resizable>();
public MainDisplayPanel(LayoutManager layout) {
super(layout);
deleteButton = new JButton("Delete");
deleteButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
deleteButtonActionPerformed(e);
}
});
this.addMouseListener(new MyMouseAdapter());
this.add(deleteButton);
for (int i = 0; i < RESIZABLE_COUNT; i++) {
addResizer();
}
}
private void deleteButtonActionPerformed(ActionEvent e) {
if (current != null) {
this.remove(current);
resizableList.remove(current);
current = null;
this.revalidate();
this.repaint();
}
}
public void addResizer() {
resizer = new Resizable();
this.add(resizer);
resizableList.add(resizer);
this.revalidate();
this.repaint();
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
current = null;
Component c = getComponentAt(e.getPoint());
for (Resizable resizable : resizableList) {
if (resizable == c) {
current = resizable;
resizable.setFill(true);
} else {
resizable.setFill(false);
}
}
}
}
public static void main(String[] args) {
JFrame jframe = new JFrame();
// !! jframe.add(new MainDisplayPanel(null));
jframe.add(new MainDisplayPanel(new FlowLayout()));
jframe.setSize(new Dimension(600, 400));
jframe.setVisible(true);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
#SuppressWarnings("serial")
class Resizable extends JPanel {
private static final int RESIZE_WIDTH = 50;
private static final int RESIZE_HEIGHT = 40;
private static final int THICKNESS = 5;
private static final Color FILL_COLOR = Color.pink;
public Resizable() {
Random rand = new Random();
// different color border so we can see that it was the clicked one that was deleted.
Color color = new Color(
rand.nextInt(255),
rand.nextInt(255),
rand.nextInt(255));
setBorder(BorderFactory.createLineBorder(color, THICKNESS));
}
#Override // so we can see it
public Dimension getPreferredSize() {
return new Dimension(RESIZE_WIDTH, RESIZE_HEIGHT);
}
public void setFill(boolean fill) {
Color fillColor = fill ? FILL_COLOR : null;
setBackground(fillColor);
repaint();
}
}
it very crazy idea, but everything is possible, but
1) in case that you Layed JComponent by using some of LayoutManager you can remove JComponents from Container, and thenafter you must/have to call revalidate() + repaint(), but this actions has side effect -> ReLayout Container and then Container's contents could be look very ***
2) in case that you layed Container with AbsoluteLayout, that should be maybe nicest but question is what with emtpy space inside Container
there is very easy way how to do it, you need to add JPopupMenu to the Container,
on RightMouseClick you have to finding JComponent under the MouseCursor
then call Container#remove(myComponent), thenafter you have to call revalidate() + repaint() for refresh GUI
or is same for me
call myComponent.setVisible(false), no re-layout, no revalidate + repaint, JComponent waiting on same place for (eventually) reusing
excelent thread about how to LayoutManagers add/remove JComponents + revalidate + repaint
I believe the problem is you need to force Swing to layout the components again after removing one. After you remove(current), call revalidate().