Java: Allow mouse scrolling over text areas [duplicate] - java

I have an array of JTextPanes inside JScrollPanes. All those components I mentioned are inside a JPanel inside a JScrollPane.
At the start of the program, none of the JTextPanes have any text, so the scrollbars for them are not visible. The scrollbars for the JPanel are visible, because I have a lot of components in it.
My issue is that if the cursor is over one of the JTextPanes and I try to scroll, nothing happens because the computer thinks I want to scroll with the JTextPane's scrollbars. What I would like to happen is for the computer to realize that I'm trying to scroll with the JPanel's scrollbar. Is there any way I could accomplish this?
Thanks!
Edit:
You could produce a similar UI to the one above with this code ( this is the constructor of a class extending JFrame - apologies for ignoring a lot of good coding habits ):
public JFrameTest() {
JPanel panel = new JPanel( new GridLayout( 10 , 10 , 10 , 10 ) );
for ( int i = 0 ; i < 10 ; i ++ ) {
for ( int j = 0 ; j < 10 ; j ++ ) {
JScrollPane paneToAdd = new JScrollPane( new JTextPane() ) {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredSize() {
return new Dimension( 100 , 100 );
}
};
panel.add( paneToAdd );
}
}
add( new JScrollPane( panel ) );
setSize( 700 , 500 );
setVisible( true );
setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
}

The following code was taken as a quick hack from Mouse Wheel Controller.
Basically it intercepts the MouseWheelEvent for the scroll pane containing the text area. If the scrollbar is visible it redispatches the event back to the same scroll pane otherwise it finds the parent scrollPane and dispatches the event to that scrollPane.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MouseWheelToParent implements MouseWheelListener
{
private JScrollPane scrollPane;
private MouseWheelListener[] realListeners;
public MouseWheelToParent(JScrollPane scrollPane)
{
this.scrollPane = scrollPane;
install();
}
public void install()
{
if (realListeners != null) return;
// Keep track of original listeners so we can use them to
// redispatch an altered MouseWheelEvent
realListeners = scrollPane.getMouseWheelListeners();
for (MouseWheelListener mwl : realListeners)
{
scrollPane.removeMouseWheelListener(mwl);
}
// Intercept events so they can be redispatched
scrollPane.addMouseWheelListener(this);
}
/**
* Remove the class as the default listener and reinstall the original
* listeners.
*/
public void uninstall()
{
if (realListeners == null) return;
// Remove this class as the default listener
scrollPane.removeMouseWheelListener( this );
// Install the default listeners
for (MouseWheelListener mwl : realListeners)
{
scrollPane.addMouseWheelListener( mwl );
}
realListeners = null;
}
// Implement MouseWheelListener interface
/**
* Redispatch a MouseWheelEvent to the real MouseWheelListeners
*/
public void mouseWheelMoved(MouseWheelEvent e)
{
// System.out.println(e.getScrollType() + " : " + e.getScrollAmount() + " : " + e.getWheelRotation());
JScrollPane scrollPane = (JScrollPane)e.getComponent();
if (scrollPane.getVerticalScrollBar().isVisible())
{
// Redispatch the event to original MouseWheelListener
for (MouseWheelListener mwl : realListeners)
{
mwl.mouseWheelMoved( e );
}
}
else
{
dispatchToParent(e, scrollPane);
return;
}
}
private void dispatchToParent(MouseWheelEvent e, JScrollPane scrollPane)
{
Component ancestor = SwingUtilities.getAncestorOfClass(JScrollPane.class, scrollPane);
MouseWheelEvent mwe = new MouseWheelEvent(
ancestor,
e.getID(),
e.getWhen(),
e.getModifiersEx(),
e.getX(),
e.getY(),
e.getXOnScreen(),
e.getYOnScreen(),
e.getClickCount(),
e.isPopupTrigger(),
e.getScrollType(),
e.getScrollAmount(),
e.getWheelRotation());
ancestor.dispatchEvent(mwe);
}
private static void createAndShowUI()
{
JPanel panel = new JPanel( new GridBagLayout() );
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(10, 10, 10, 10);
for (int y = 0; y < 10; y++)
{
for (int x = 0; x < 5; x++)
{
gbc.gridx = x;
gbc.gridy = y;
JTextArea textArea = new JTextArea(5, 20);
JScrollPane scrollPane = new JScrollPane( textArea );
scrollPane.setMinimumSize( scrollPane.getPreferredSize() );
new MouseWheelToParent(scrollPane);
panel.add(scrollPane, gbc);
if (x == 0 && y ==0)
{
textArea.append("1\n2\n3\n4\n5\n6\n7\n8\n9");
textArea.setCaretPosition(0);
}
}
}
JFrame frame = new JFrame("TextAreaSSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new JScrollPane( panel ) );
frame.setSize(400, 400);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}

Related

Make TitledBorder editable upon double click

This is an extension of : Make a TitledBorder title editable
To be more specific, I want the TitledBorder to be editable when the title is double clicked. I'm placing the Border around a java Box that uses BoxLayout. The double click would preferably open a JTextField right there, but if that cannot be done, opening another window to edit the title is acceptable.
TitledBorder editableBorder = new TitledBorder(editableString);
editableBorder.setTitleJustification(TitledBorder.CENTER);
Box containerBox = new Box(BoxLayout.PAGE_AXIS);
containerBox.setBorder(new CompoundBorder(editableBorder, new EmptyBorder(10, 0, 10, 0)));
Box insideBox = new Box(BoxLayout.PAGE_AXIS);
insideBox.add(new JLabel(new ImageIcon(new BufferedImage(200,40,BufferedImage.TYPE_INT_RGB))));
containerBox.add(insideBox);
Here is an example. The MouseListener uses a JPopupMenu to display the text field. This means you can cancel editing by using the Escape key.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class TitledBorderListener extends MouseAdapter
{
private JPopupMenu editPopup;
private JTextField editTextField;
private TitledBorder titledBorder;
#Override
public void mouseClicked(MouseEvent e)
{
if (e.getClickCount != 2)
return;
// Edit the border on a double click
JComponent component = (JComponent)e.getSource();
Border border = component.getBorder();
if (border instanceof TitledBorder)
{
titledBorder = (TitledBorder)border;
FontMetrics fm = component.getFontMetrics( titledBorder.getTitleFont() );
int titleWidth = fm.stringWidth(titledBorder.getTitle()) + 20;
Rectangle bounds = new Rectangle(0, 0, titleWidth, fm.getHeight());
if (bounds.contains(e.getPoint()))
{
if (editPopup == null)
createEditPopup();
// Position the popup editor over top of the title
editTextField.setText( titledBorder.getTitle() );
Dimension d = editTextField.getPreferredSize();
d.width = titleWidth;
editPopup.setPreferredSize(d);
editPopup.show(component, 0, 0);
editTextField.selectAll();
editTextField.requestFocusInWindow();
}
}
}
private void createEditPopup()
{
editTextField = new JTextField();
// Add an Action to the text field to save the new title text
editTextField.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String value = editTextField.getText();
titledBorder.setTitle( value );
editPopup.setVisible(false);
editPopup.getInvoker().revalidate();
editPopup.getInvoker().repaint();
}
});
// Add the editor to the popup
editPopup = new JPopupMenu();
editPopup.setBorder( new EmptyBorder(0, 0, 0, 0) );
editPopup.add(editTextField);
}
private static void createAndShowUI()
{
JPanel panel = new JPanel();
panel.setBorder( new TitledBorder("Double Click to Edit") );
panel.addMouseListener( new TitledBorderListener() );
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( panel );
frame.setSize(200, 200);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}

Java Layout with Component always in Top Right

The primary GUI of my application is composed of a JDesktopPane at the CENTER of a frame's content pane using a BorderLayout. I am hoping to have a component placed in the top right of the screen that still allows the user to drag JInternalFrames within the space to the left and and bottom of this component.
Setting the component to the NORTH or EAST of the BorderLayout seems to fill the entire space. I am thinking BorderLayout may not be the best layout manager for what I am trying to accomplish? Any suggestions on a better approach?
Check out the OverlayLayout. It allows you to stack components on top of one another.
You need to manipulate the setAlignmentX(..) and setAlignmentY(...)` methods to get the layout you want. It is not always intuitive how these alignments work together but setting the component to the top/left is relatively easy.
Here is a little demo for you to play with:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class OverlayLayoutTest extends JPanel
implements ActionListener
{
JPanel green;
JPanel red;
JLabel greenLabel;
JLabel redLabel;
JComboBox greenAlignmentX;
JComboBox greenAlignmentY;
JComboBox redAlignmentX;
JComboBox redAlignmentY;
public OverlayLayoutTest()
{
setLayout( new BorderLayout(10, 10) );
add(createNorthPanel(), BorderLayout.NORTH);
add(createCenterPanel(), BorderLayout.CENTER);
add(createSouthPanel(), BorderLayout.SOUTH);
}
private JPanel createNorthPanel()
{
JPanel panel = new JPanel();
panel.add( new JLabel("Green:") );
greenLabel = new JLabel();
panel.add( greenLabel );
panel.add( new JLabel("Red:") );
redLabel = new JLabel();
panel.add( redLabel );
return panel;
}
private JPanel createCenterPanel()
{
JPanel panel = new JPanel();
panel.setLayout( new OverlayLayout(panel) );
panel.setBackground( Color.ORANGE );
panel.setPreferredSize( new Dimension(200, 200) );
red = new JPanel();
red.setBackground( Color.RED );
red.setPreferredSize( new Dimension(50, 50) );
red.setMaximumSize( red.getPreferredSize() );
red.setMinimumSize( red.getPreferredSize() );
panel.add( red );
green = new JPanel();
green.setBackground( Color.GREEN );
green.setPreferredSize( new Dimension(100, 100) );
green.setMaximumSize( green.getPreferredSize() );
green.setMinimumSize( green.getPreferredSize() );
panel.add( green );
JPanel wrap = new JPanel();
wrap.add( panel );
return wrap;
}
private JPanel createSouthPanel()
{
JPanel panel = new JPanel( new GridLayout(1, 0, 10, 10) );
JPanel green = new JPanel(new GridLayout(0, 2, 5, 5) );
green.setBorder( new TitledBorder("Green Alignment") );
green.add( new JLabel("X Alignment:") );
greenAlignmentX = createComboBox();
green.add( greenAlignmentX );
green.add( new JLabel("Y Alignment:") );
greenAlignmentY = createComboBox();
green.add( greenAlignmentY );
panel.add( green );
JPanel red = new JPanel(new GridLayout(0, 2, 5, 5) );
red.setBorder( new TitledBorder("Red Alignment") );
red.add( new JLabel("X Alignment:") );
redAlignmentX = createComboBox();
red.add( redAlignmentX );
red.add( new JLabel("Y Alignment:") );
redAlignmentY = createComboBox();
red.add( redAlignmentY );
panel.add( red );
JButton reset = new JButton("Reset Alignment");
reset.addActionListener( this );
panel.add( reset );
return panel;
}
public void actionPerformed(ActionEvent e)
{
green.setAlignmentX( ((Float)greenAlignmentX.getSelectedItem()) );
green.setAlignmentY( ((Float)greenAlignmentY.getSelectedItem()) );
red.setAlignmentX( ((Float)redAlignmentX.getSelectedItem()) );
red.setAlignmentY( ((Float)redAlignmentY.getSelectedItem()) );
JPanel parent = (JPanel)green.getParent();
parent.revalidate();
/*
System.out.print(green.getAlignmentX() + " : ");
System.out.print(green.getAlignmentY() + " : ");
System.out.print(red.getAlignmentX() + " : ");
System.out.print(red.getAlignmentY() + " : ");
System.out.println();
*/
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
greenLabel.setText( green.getLocation().toString() );
redLabel.setText( red.getLocation().toString() );
}
});
}
private JComboBox createComboBox()
{
JComboBox<Float> comboBox = new JComboBox<Float>();
comboBox.addItem( new Float(0f) );
comboBox.addItem( new Float(0.25f) );
comboBox.addItem( new Float(0.5f) );
comboBox.addItem( new Float(0.75f) );
comboBox.addItem( new Float(1.0f) );
comboBox.setSelectedItem(0.5f);
return comboBox;
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("OverlayLayoutTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new OverlayLayoutTest() );
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
set the alignment X values to 1.0 for both components
set the alignment Y values to 0.0 for both components
and you should get the layout you want.
Edit:
Missed the part about dragging a JInternalFrame. So this imples you are using a JDesktopPane to support the dragging. A JDesktopPane uses a null layout to allow you to drag components around.
There is no reason you can't add another component (other than a JInternalFrame) to the desktop. You just need to set the size/location of this component to be displayed at the top right of the desktop. You would then need to add a ComponentListener to the desktop pane to listen for the componentResized event. When this event is fired you would need to recalucate the location of the component to reset it to the top right.

How to remove item inside JPanel which is designated by an ArrayList

There is a JPanel with GridLayout which is randomly populated by JButtons created using an ArrayList. Each element of the array (a Tile) contains a character. It's supposed to be a tile removing game. However, I realised that the code I wrote didn't work, because once a tile is removed, the others' indexes change.
A KeyListener is applied to the JPanel. This method is in my Model class, where the ArrayList is first created. getChar, in the Tile class, simply returns its character.
public void removeChar(char typedChar) {
for(Tile t: _al){
if(t.getChar() == typedChar) {
System.out.println(typedChar + " is in the game, at index " + _al.indexOf(t) + " of the array.");
_p.remove(_al.indexOf(t));
}
With my code, I basically just want to remove the corresponding tile when its character key is typed. Can anyone help me see a better (well, working) way to do this? My code seems to become much more complicated because the ArrayList is full of objects, but I will need the objects later.
Don't use a KeyListener. Swing was designed to be used with Key Bindings.
Add a Key Binding to each button. Then when the letter is typed the button will be the source of the event so you can just remove the button from the panel.
For an example of how key bindings work see below:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class CalculatorPanel extends JPanel
{
private JTextField display;
public CalculatorPanel()
{
Action numberAction = new AbstractAction()
{
#Override
public void actionPerformed(ActionEvent e)
{
// display.setCaretPosition( display.getDocument().getLength() );
display.replaceSelection(e.getActionCommand());
}
};
setLayout( new BorderLayout() );
display = new JTextField();
display.setEditable( false );
display.setHorizontalAlignment(JTextField.RIGHT);
add(display, BorderLayout.NORTH);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout( new GridLayout(0, 5) );
add(buttonPanel, BorderLayout.CENTER);
for (int i = 0; i < 10; i++)
{
String text = String.valueOf(i);
JButton button = new JButton( text );
button.addActionListener( numberAction );
button.setBorder( new LineBorder(Color.BLACK) );
button.setPreferredSize( new Dimension(30, 30) );
buttonPanel.add( button );
InputMap inputMap = button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke(text), text);
inputMap.put(KeyStroke.getKeyStroke("NUMPAD" + text), text);
button.getActionMap().put(text, numberAction);
}
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("Calculator Panel");
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.add( new CalculatorPanel() );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
So in your case the code in the actionPerformed() method of the Action would be something like:
JButton button = (JButton)e.getSource();
JPanel parent = (JPanel)button.getParent();
parent.remove(button);
parent.revalidate();
parent.repaint();
Edit:
When using a KeyListener you might use HashMap to bind the character to the related button:
HashMap<Character, JButton> buttons = new HashMap<Character, JButton>();
...
buttons.put('a', aButton);
buttons.put('b', bButton);
Then the keyTyped() code in the KeyListener code would be something like:
JButton button = buttons.get( e.getKeyChar() );
panel.remove( button );
panel.revalidate();
panel.repaint();

Java Scrolling with the More Logical ScrollBar

I have an array of JTextPanes inside JScrollPanes. All those components I mentioned are inside a JPanel inside a JScrollPane.
At the start of the program, none of the JTextPanes have any text, so the scrollbars for them are not visible. The scrollbars for the JPanel are visible, because I have a lot of components in it.
My issue is that if the cursor is over one of the JTextPanes and I try to scroll, nothing happens because the computer thinks I want to scroll with the JTextPane's scrollbars. What I would like to happen is for the computer to realize that I'm trying to scroll with the JPanel's scrollbar. Is there any way I could accomplish this?
Thanks!
Edit:
You could produce a similar UI to the one above with this code ( this is the constructor of a class extending JFrame - apologies for ignoring a lot of good coding habits ):
public JFrameTest() {
JPanel panel = new JPanel( new GridLayout( 10 , 10 , 10 , 10 ) );
for ( int i = 0 ; i < 10 ; i ++ ) {
for ( int j = 0 ; j < 10 ; j ++ ) {
JScrollPane paneToAdd = new JScrollPane( new JTextPane() ) {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredSize() {
return new Dimension( 100 , 100 );
}
};
panel.add( paneToAdd );
}
}
add( new JScrollPane( panel ) );
setSize( 700 , 500 );
setVisible( true );
setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
}
The following code was taken as a quick hack from Mouse Wheel Controller.
Basically it intercepts the MouseWheelEvent for the scroll pane containing the text area. If the scrollbar is visible it redispatches the event back to the same scroll pane otherwise it finds the parent scrollPane and dispatches the event to that scrollPane.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MouseWheelToParent implements MouseWheelListener
{
private JScrollPane scrollPane;
private MouseWheelListener[] realListeners;
public MouseWheelToParent(JScrollPane scrollPane)
{
this.scrollPane = scrollPane;
install();
}
public void install()
{
if (realListeners != null) return;
// Keep track of original listeners so we can use them to
// redispatch an altered MouseWheelEvent
realListeners = scrollPane.getMouseWheelListeners();
for (MouseWheelListener mwl : realListeners)
{
scrollPane.removeMouseWheelListener(mwl);
}
// Intercept events so they can be redispatched
scrollPane.addMouseWheelListener(this);
}
/**
* Remove the class as the default listener and reinstall the original
* listeners.
*/
public void uninstall()
{
if (realListeners == null) return;
// Remove this class as the default listener
scrollPane.removeMouseWheelListener( this );
// Install the default listeners
for (MouseWheelListener mwl : realListeners)
{
scrollPane.addMouseWheelListener( mwl );
}
realListeners = null;
}
// Implement MouseWheelListener interface
/**
* Redispatch a MouseWheelEvent to the real MouseWheelListeners
*/
public void mouseWheelMoved(MouseWheelEvent e)
{
// System.out.println(e.getScrollType() + " : " + e.getScrollAmount() + " : " + e.getWheelRotation());
JScrollPane scrollPane = (JScrollPane)e.getComponent();
if (scrollPane.getVerticalScrollBar().isVisible())
{
// Redispatch the event to original MouseWheelListener
for (MouseWheelListener mwl : realListeners)
{
mwl.mouseWheelMoved( e );
}
}
else
{
dispatchToParent(e, scrollPane);
return;
}
}
private void dispatchToParent(MouseWheelEvent e, JScrollPane scrollPane)
{
Component ancestor = SwingUtilities.getAncestorOfClass(JScrollPane.class, scrollPane);
MouseWheelEvent mwe = new MouseWheelEvent(
ancestor,
e.getID(),
e.getWhen(),
e.getModifiersEx(),
e.getX(),
e.getY(),
e.getXOnScreen(),
e.getYOnScreen(),
e.getClickCount(),
e.isPopupTrigger(),
e.getScrollType(),
e.getScrollAmount(),
e.getWheelRotation());
ancestor.dispatchEvent(mwe);
}
private static void createAndShowUI()
{
JPanel panel = new JPanel( new GridBagLayout() );
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(10, 10, 10, 10);
for (int y = 0; y < 10; y++)
{
for (int x = 0; x < 5; x++)
{
gbc.gridx = x;
gbc.gridy = y;
JTextArea textArea = new JTextArea(5, 20);
JScrollPane scrollPane = new JScrollPane( textArea );
scrollPane.setMinimumSize( scrollPane.getPreferredSize() );
new MouseWheelToParent(scrollPane);
panel.add(scrollPane, gbc);
if (x == 0 && y ==0)
{
textArea.append("1\n2\n3\n4\n5\n6\n7\n8\n9");
textArea.setCaretPosition(0);
}
}
}
JFrame frame = new JFrame("TextAreaSSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new JScrollPane( panel ) );
frame.setSize(400, 400);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}

Update JscrollBar without resetting position

I have a class which extends JScrollPane, its viewport is another class which extends JComponent and implements Scrollable. When the size of the component changes the JscrollBars do not update unless I call revalidate() however this resets the position of the scroll bars to the top left. Is there a way of updating the size of the scroll bars while maintaining their current position?
Thanks, Rob
Is the problem your custom scroll pane or your custom component? We can't begin to guess what kind of changes you may have made. Post your SSCCE that demonstrates the problem.
It works fine for me.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ScrollSSCCE extends JPanel
{
public ScrollSSCCE()
{
setLayout( new BorderLayout() );
final JPanel panel = new JPanel();
panel.setPreferredSize( new Dimension(200, 200) );
JScrollPane scrollPane = new JScrollPane( panel );
add( scrollPane );
JButton button = new JButton("Adjust");
add(button, BorderLayout.SOUTH);
button.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
Dimension d = panel.getPreferredSize();
d.width +=50;
d.height +=50;
panel.setPreferredSize(d);
panel.revalidate();
}
});
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("ScrollSSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new ScrollSSCCE() );
frame.setSize(150, 150);
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}

Categories