I want to limit the visibility of the game board to X cells away from the player (and that this automatically is updated when the player moves). See also the image below...
For example, the area I have delineated with yellow lines) & automatic update of this when the player moves.
import java.io.*;
import java.lang.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Mov {
private final MoveModel moveModel;
private final Showvisible showvisible;
public Mov() {
moveModel = new MoveModel();
showvisible = new Showvisible(moveModel);
showvisible.getRndMoverButton().addActionListener(e-> movePlayer());
showvisible.getMoveEastButton().addActionListener(e-> movePlayerX(50));
}
//move player to a random position (works fine)
private void movePlayer() {
final Random rnd = new Random();
moveModel.setPlayerX(rnd.nextInt(100));
moveModel.setPlayerY(rnd.nextInt(100));
showvisible.refresh();
}
//Move player in the x direction to the East (positive) or west (negative
private void movePlayerX(final int distance) {
moveModel.setPlayerX(moveModel.getPlayerX()+distance);
showvisible.refresh();
}
public static void main(final String[] args) {
new Mov();
}
}
class Showvisible {
private final static int GAP = 2;
MoveModel MoveModel;
private MainPanel mainPanel;
Showvisible(final MoveModel MoveModel){
this.MoveModel = MoveModel;
createAndShowGUI();
}
void refresh() {
mainPanel.repaint();
}
private void createAndShowGUI() {
final JFrame board = new JFrame("Single Player Game");
board.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainPanel = new MainPanel();
board.add(mainPanel);
board.pack();
board.setVisible(true);
}
JButton getRndMoverButton() { return mainPanel.getRndMoverButton(); }
JButton getMoveEastButton() { return mainPanel.getMoveEastButton(); }
class MainPanel extends JPanel {
private final BottomPanel bPanel;
MainPanel() {
setLayout(new BorderLayout(GAP,GAP));
add(new TopPanel(), BorderLayout.PAGE_START);
add(new BoardPanel(), BorderLayout.CENTER);
bPanel = new BottomPanel();
add(bPanel, BorderLayout.PAGE_START);
}
JButton getRndMoverButton() { return bPanel.getRndMoverButton(); }
JButton getMoveEastButton() { return bPanel.getMoveEastButton(); }
}
class TopPanel extends JPanel {
TopPanel() {
setLayout(new FlowLayout(FlowLayout.CENTER));
add(new JLabel("This is an amazing game "));
}
}
class BoardPanel extends JPanel {
Player player;
BoardPanel() {
setBorder(BorderFactory.createLineBorder(Color.BLACK, GAP));
final GridLayout layout = new GridLayout(MoveModel.getBoardRows(),
MoveModel.getBoardCols());
setLayout(layout);
for (int i = 0; i <MoveModel.getBoardRows(); i++) {
for (int j = 0; j < MoveModel.getBoardCols(); j++) {
add(new Tile());
}
}
player = new Player();
player.setBounds(new Rectangle(1000,1000, 1000,1000));
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
player.paint(g);
}
}
class Tile extends JLabel {
Tile() {
setPreferredSize(new Dimension(MoveModel.getSquareSize(), MoveModel.getSquareSize()));
setBorder(BorderFactory.createLineBorder(Color.ORANGE, GAP));
}
}
class Player extends JLabel{
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(MoveModel.getPlayerX(), MoveModel.getPlayerY(), MoveModel.getPlayerSize(),MoveModel.getPlayerSize());
}
}
class BottomPanel extends JPanel {
JButton rndMoveButton = new JButton("Random Move");
JButton moveEastButton =new JButton("move East");
BottomPanel(){
add(rndMoveButton);
add(moveEastButton);
}
JButton getRndMoverButton() { return rndMoveButton; }
JButton getMoveEastButton() { return moveEastButton; }
}
}
class MoveModel{
private final int boardRows = 9, boardCols = 9, squareSize = 50;
int playerX = 0;
private int playerY = 0;
private final int playerSize =25;
int getPlayerX() { return playerX; }
void setPlayerX(final int playerX) { this.playerX = playerX; }
int getPlayerY() {return playerY; }
void setPlayerY(final int playerY) { this.playerY = playerY; }
int getPlayerSize() {return playerSize; }
int getBoardRows() {return boardRows; }
int getBoardCols() {return boardCols; }
int getSquareSize() {return squareSize; }
}
Output:
When painting BoardPanel you can mask the entire area with mask color (black) and clear part of the mask :
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//mask all over
g.setColor(Color.BLACK);
g.fillRect(0, 0,getWidth(), getHeight());
//clear part of mask
Rectangle r = getVisibleBounds();
g.clearRect(r.x, r.y, r.width, r.height);
player.paint(g);
}
private Rectangle getVisibleBounds() {
//bounds returned may be outside painted area
return new Rectangle(moveModel.getPlayerX() - VISIBLE_SIZE/ 2,
moveModel.getPlayerY() - VISIBLE_SIZE/ 2, VISIBLE_SIZE, VISIBLE_SIZE);
}
The complete one-file (Mov.java) code can be copy-pasted from here
Related
I am attempting to use jsliders to allow a user to pinpoint the origin of a circle to be drawn on a canvas. I am using a button to show and hide the circle. I am using paint on an inner jpanel so that paint will not write over components. However, the coordinates inside the jpanel are different than the coordinates for the entire frame. So, it is very difficult for me to get the coordinates of the jslider and then translate it to the jpanel to draw the circle. Is there an easy way to figure this out without a ton of guess and check? I am also using the custom layout miglayout. I have included the code for my GUI class as well as my custom JPanel I made so I could mess with the paint method.
public class CircleGUI extends JFrame implements ActionListener {
private MigLayout layout = new MigLayout();
private CustomPanel innerpanel;
private JSlider x,y;
private JColorChooser colorpick;
private JButton state;
private boolean bstate;
CircleGUI() {
initialize();
}
private void initialize() {
Border blackline = BorderFactory.createLineBorder(Color.black);
bstate = false;
x = new JSlider(JSlider.HORIZONTAL,650,325);
x.setPaintTicks(true);
x.setPaintLabels(true);
x.setPreferredSize(new Dimension(650,0));
y = new JSlider(JSlider.HORIZONTAL,650,325);
y.setPaintTicks(true);
y.setPaintLabels(true);
y.setInverted(true);
y.setOrientation(JSlider.VERTICAL);
y.setPreferredSize(new Dimension (0,600));
colorpick = new JColorChooser();
state = new JButton("Show");
state.addActionListener(e -> {
if(!bstate) {
int positionx = x.getValue() - 80;
int positiony = y.getValue();
Color c = colorpick.getColor();
innerpanel.setColor(c);
innerpanel.setX(positionx);
innerpanel.setY(positiony);
innerpanel.repaint();
state.setText("Hide");
bstate = true;
} else {
Color transparent = new Color(0,0,0,0);
innerpanel.setColor(transparent);
innerpanel.repaint();
state.setText("Show");
bstate = false;
}
});
JPanel outerpanel = new JPanel(layout);
innerpanel = new CustomPanel();
innerpanel.setPreferredSize(new Dimension(600,600));
innerpanel.setBorder(blackline);
outerpanel.add(x,"wrap");
outerpanel.add(y,"split 2");
outerpanel.add(innerpanel);
outerpanel.add(state,"wrap");
outerpanel.add(colorpick);
this.setSize(1000, 1000);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(outerpanel);
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
public class CustomPanel extends JPanel implements ActionListener {
private Color c;
private int x;
private int y;
public CustomPanel() {
c = null;
}
#Override
public void actionPerformed (ActionEvent e) {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(c);
g2.fill(new Ellipse2D.Double(x, y, 100, 100));
}
public void setColor(Color c) {
this.c = c;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
}
Your problem is you are trying to make a one-to-one mapping between the value of the JSlider and the coordinate in your CustomPanel. You should use the JSlider value as a percentage, i.e. minimum value zero and maximum value 100. If you want the circle to appear in the middle of the CustomPanel so you place both JSliders in their mid-points, i.e. both at 50%. Then you calculate 50% of the corresponding dimension to get the coordinate. If the width of CustomPanel is 600, then 50% of 600 is 300 so positionx needs to be 300.
The only thing I changed in your code is the calculation of positionx and positiony.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.border.Border;
import net.miginfocom.swing.MigLayout;
public class CircleGUI extends JFrame implements ActionListener {
private MigLayout layout = new MigLayout();
private CustomPanel innerpanel;
private JSlider x,y;
private JColorChooser colorpick;
private JButton state;
private boolean bstate;
CircleGUI() {
initialize();
}
private void initialize() {
Border blackline = BorderFactory.createLineBorder(Color.black);
bstate = false;
// x = new JSlider(JSlider.HORIZONTAL, 650, 325);
x = new JSlider(0, 100, 10);
x.setPaintTicks(true);
x.setPaintLabels(true);
x.setPreferredSize(new Dimension(650, 0));
// y = new JSlider(JSlider.HORIZONTAL, 650, 325);
y = new JSlider(0, 100, 10);
y.setPaintTicks(true);
y.setPaintLabels(true);
y.setInverted(true);
y.setOrientation(JSlider.VERTICAL);
y.setPreferredSize(new Dimension(0, 600));
colorpick = new JColorChooser();
state = new JButton("Show");
state.addActionListener(e -> {
if (!bstate) {
int positionx = Math.round(x.getValue() / 100.0f * innerpanel.getSize().width) - 50;
int positiony = Math.round(y.getValue() / 100.0f * innerpanel.getSize().height) - 50;
Color c = colorpick.getColor();
innerpanel.setColor(c);
innerpanel.setX(positionx);
innerpanel.setY(positiony);
innerpanel.repaint();
state.setText("Hide");
bstate = true;
}
else {
Color transparent = new Color(0, 0, 0, 0);
innerpanel.setColor(transparent);
innerpanel.repaint();
state.setText("Show");
bstate = false;
}
});
JPanel outerpanel = new JPanel(layout);
innerpanel = new CustomPanel();
innerpanel.setPreferredSize(new Dimension(600, 600));
innerpanel.setBorder(blackline);
outerpanel.add(x, "wrap");
outerpanel.add(y, "split 2");
outerpanel.add(innerpanel);
outerpanel.add(state, "wrap");
outerpanel.add(colorpick);
this.setSize(1000, 1000);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(outerpanel);
}
#Override
public void actionPerformed(ActionEvent e) {
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
CircleGUI cg = new CircleGUI();
cg.setVisible(true);
});
}
}
class CustomPanel extends JPanel implements ActionListener {
private Color c;
private int x;
private int y;
public CustomPanel() {
c = null;
}
#Override
public void actionPerformed(ActionEvent e) {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(c);
g2.fill(new Ellipse2D.Double(x, y, 100, 100));
}
public void setColor(Color c) {
this.c = c;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
}
For a game in Java, I want to move the player object in a specified direction with a specified distance. While - using the Java Random API - it works fine to move the player randomly on the board, I can't unfortunately find any suitable method to move the player over a specified distance in the X and Y direction. In the code below are some codes I tried (as comments, with //) , but none of them comes close to working.
package high;
Any solutions that work?
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class mov {
int distance=50;
private MoveModel MoveModel;
private Showvisible showvisible;
public mov() {
MoveModel = new MoveModel();
showvisible = new Showvisible(MoveModel);
showvisible.getButton().addActionListener(e-> movePlayer());
//showvisible.getButton3(à.addActionListener(e))
}
//move player to a random position (works fine)
private void movePlayer() {
final Random rnd = new Random();
MoveModel.setPlayerX(rnd.nextInt(100));
MoveModel.setPlayerY(rnd.nextInt(100));
showvisible.refresh();
}
//Move player in the x direction to the East (doesn't work)
private void movePlayer2(int distance) {
MoveModel.setPlayerX(+50);
// MoveModel.setPlayerY(distance++);
//MoveModel.setPlayerX(distance);
//MoveModel.setPlayerX(playerX+distance); //Many variations tried, none of them works
showvisible.refresh();
}
public static void main(String[] args) {
new mov();
}
}
class Showvisible {
private final static int GAP = 2;
MoveModel MoveModel;
private MainPanel mainPanel;
Showvisible(MoveModel MoveModel){
this.MoveModel = MoveModel;
createAndShowGUI();
}
void refresh() {
mainPanel.repaint();
}
private void createAndShowGUI() {
JFrame board = new JFrame("Single Player Game");
board.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainPanel = new MainPanel();
board.add(mainPanel);
board.pack();
board.setVisible(true);
}
JButton getButton() { return mainPanel.getButton(); }
JButton getButton2() { return mainPanel.getButton(); }
class MainPanel extends JPanel {
private BottomPanel bPanel;
MainPanel() {
setLayout(new BorderLayout(GAP,GAP));
add(new TopPanel(), BorderLayout.PAGE_START);
add(new BoardPanel(), BorderLayout.CENTER);
bPanel = new BottomPanel();
add(bPanel, BorderLayout.PAGE_START);
}
JButton getButton() { return bPanel.getButton(); }
JButton getButton2() { return bPanel.getButton2(); }
}
class TopPanel extends JPanel {
TopPanel() {
setLayout(new FlowLayout(FlowLayout.CENTER));
add(new JLabel("This is an amazing game "));
}
}
class BoardPanel extends JPanel {
Player player;
BoardPanel() {
setBorder(BorderFactory.createLineBorder(Color.BLACK, GAP));
GridLayout layout = new GridLayout(MoveModel.getBoardRows(),
MoveModel.getBoardCols());
setLayout(layout);
for (int i = 0; i <MoveModel.getBoardRows(); i++) {
for (int j = 0; j < MoveModel.getBoardCols(); j++) {
add(new Tile());
}
}
player = new Player();
player.setBounds(new Rectangle(1000,1000, 1000,1000));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
player.paint(g);
}
}
class Tile extends JLabel {
Tile() {
setPreferredSize(new Dimension(MoveModel.getSquareSize(), MoveModel.getSquareSize()));
setBorder(BorderFactory.createLineBorder(Color.ORANGE, GAP));
}
}
class Player extends JLabel{
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(MoveModel.getPlayerX(), MoveModel.getPlayerY(), MoveModel.getPlayerSize(),MoveModel.getPlayerSize());
}
}
class BottomPanel extends JPanel {
JButton button = new JButton("Random Move");
JButton button2 =new JButton("move East");
BottomPanel(){
add(button);
add(button2);
}
JButton getButton() { return button; }
JButton getButton2() { return button2; }
}
}
class MoveModel{
private int boardRows = 9, boardCols = 9, squareSize = 50;
int playerX = 0;
private int playerY = 0;
private int playerSize =25;
int getPlayerX() { return playerX; }
void setPlayerX(int playerX) { this.playerX = playerX; }
int getPlayerY() {return playerY; }
void setPlayerY(int playerY) { this.playerY = playerY; }
int getPlayerSize() {return playerSize; }
int getBoardRows() {return boardRows; }
int getBoardCols() {return boardCols; }
int getSquareSize() {return squareSize; }
In Showvisible change
JButton getButton2() { return mainPanel.getButton(); }
to
JButton getButton2() { return mainPanel.getButton2(); }
In Mov constructor (do not use mov for a class name) assign an action listener to the button:
showvisible.getButton2().addActionListener(e-> movePlayerX(50));
And add this method to Mov :
//Move player in the x direction to the East (positive) or west (negative
private void movePlayerX(final int distance) {
moveModel.setPlayerX(moveModel.getPlayerX() + distance);//get current x and add to it
showvisible.refresh();
}
To increment X, you use getPlayerX() to retrieve the current position and add to it.
The complete modified code can be copied from here
I have a Jframe with two buttons: 'A' and 'B'. Clicking the button 'A' should display the capital letter A in the JPanel. The problem here is that on repeated mouse click the previous letters drawn are not show. I want to make sure that whatever is drawn stays in the drawing. The following is the code for my program.
Code for JFrame:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DrawFrame extends JFrame{
private final int WIDTH = 500;
private final int HEIGHT = 300;
private GUIModel model;
private JButton number1;
private JButton number2;
private JPanel numberPanel;
private DrawPanel graphicsPanel;
public DrawFrame()
{
this.model = new GUIModel(" ");
createSelectionPanel();
createGraphicsPanel();
this.setSize(WIDTH, HEIGHT);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
private void createSelectionPanel()
{
numberPanel = new JPanel();
ButtonListener listener = new ButtonListener();
number1 = new JButton("A");
number1.addActionListener(listener);
number2 = new JButton("B");
number2.addActionListener(listener);
numberPanel.setLayout(new GridLayout(2,2));
numberPanel.add(number1);
numberPanel.add(number2);
this.add(numberPanel, BorderLayout.WEST);
}
private void createGraphicsPanel()
{
//instantiate drawing panel
graphicsPanel = new DrawPanel(WIDTH, HEIGHT, model);
//add drawing panel to right
add(graphicsPanel);
}
private class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
model.setDisplayString(event.getActionCommand());
}
}
//creates a drawing frame
public static void main(String[] args)
{
DrawFrame draw = new DrawFrame();
}
}
Code for JPanel:
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
public class DrawPanel extends JPanel{
private static final long serialVersionUID = 3443814601865936618L;
private Font font = new Font("Default", Font.BOLD, 30);
private static final Color DEFAULT_TEXT_COLOR = Color.BLACK;
private static final Color HOVER_TEXT_COLOR = Color.RED;
private Color color = DEFAULT_TEXT_COLOR;
private List<GUIModel> numberList = new ArrayList<GUIModel>();
private GUIModel model;
boolean mouseHover = false;
public DrawPanel(int width, int height, GUIModel model){
this.setPreferredSize(new Dimension(width, height));
this.model = model;
//set white background for drawing panel
setBackground(Color.WHITE);
//add mouse listeners
MouseHandler mouseHandler = new MouseHandler();
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
}
// void checkForHover(MouseEvent event) {
// FontMetrics metrics = getFontMetrics(font);
//
// Graphics g = getGraphics();
// Rectangle textBounds = metrics.getStringBounds("C", g).getBounds();
// g.dispose();
//
// int index = 0;
// while (index < numberList.size()) {
// Double x = numberList.get(index).getCoordinate().getX();
// Double y = numberList.get(index).getCoordinate().getY();
//
// textBounds.translate(x.intValue(), y.intValue());
//
// if (textBounds.contains(event.getPoint())) {
// color = HOVER_TEXT_COLOR;
// }
// else {
// color = DEFAULT_TEXT_COLOR;
// }
// index++;
// }
// }
this is my PaintComponent method where I'm going through the list of letters I've drawn at different coordinates and drawing them on the canvas
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setFont(font);
g.setColor(color);
int index = 0;
while (index < numberList.size()) {
Double x = numberList.get(index).getCoordinate().getX();
Double y = numberList.get(index).getCoordinate().getY();
String display = numberList.get(index).getDisplayString();
g.drawString(display, x.intValue(), y.intValue());
index++;
}
if (model.getCoordinate() != null) {
Point p = model.getCoordinate();
g.drawString(model.getDisplayString(), p.x, p.y);
numberList.add(model);
}
}
//class to handle all mouse events
private class MouseHandler extends MouseAdapter implements MouseMotionListener
{
#Override
public void mousePressed(MouseEvent event)
{
model.setCoordinate(event.getPoint());
}
#Override
public void mouseReleased(MouseEvent event)
{
DrawPanel.this.repaint();
}
// #Override
// public void mouseEntered(MouseEvent event) {
// checkForHover(event);
// }
//
// #Override
// public void mouseMoved(MouseEvent event) {
// checkForHover(event);
// }
}
}
Code for GUIModel:
import java.awt.Point;
public class GUIModel {
private String displayString;
private Point coordinate;
public GUIModel(String displayString) {
this.displayString = displayString;
}
public void setDisplayString(String displayString) {
this.displayString = displayString;
}
public String getDisplayString() {
return displayString;
}
public Point getCoordinate() {
return coordinate;
}
public void setCoordinate(int x, int y) {
this.coordinate = new Point(x, y);
}
public void setCoordinate(Point coordinate) {
this.coordinate = coordinate;
}
}
Any help or suggestions would be much appreciated. Thanks.
#MadProgrammer Your comment helped me fix my code. Like you said I needed to create a new GUIModel and add it to my numberList.
I modified my paintComponent method to reflect that and it now works. So, Thanks!
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setFont(font);
g.setColor(color);
int index = 0;
while (index < numberList.size()) {
Double x = numberList.get(index).getCoordinate().getX();
Double y = numberList.get(index).getCoordinate().getY();
String display = numberList.get(index).getDisplayString();
g.drawString(display, x.intValue(), y.intValue());
index++;
}
if (model.getCoordinate() != null) {
Point p = model.getCoordinate();
g.drawString(model.getDisplayString(), p.x, p.y);
GUIModel number = new GUIModel();
number.setCoordinate(p);
number.setDisplayString(model.getDisplayString());
numberList.add(number);
}
}
I have a Jframe with two buttons: 'A' and 'B'. Clicking the button 'A' should display the capital letter A in the JPanel. On mouse hover only, any 'A' letter within the canvas should be displayed in red. When the mouse leaves, the text color should be back to black.
I've coded for this and it works only once. The letter 'A' changes to red but does not change back to black. Also, it does not work for multiple 'A's
Code for JFrame:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DrawFrame extends JFrame{
private final int WIDTH = 500;
private final int HEIGHT = 300;
private GUIModel model;
private JButton number1;
private JButton number2;
private JPanel numberPanel;
private DrawPanel graphicsPanel;
public DrawFrame()
{
this.model = new GUIModel(" ");
createSelectionPanel();
createGraphicsPanel();
this.setSize(WIDTH, HEIGHT);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
private void createSelectionPanel()
{
numberPanel = new JPanel();
ButtonListener listener = new ButtonListener();
number1 = new JButton("A");
number1.addActionListener(listener);
number2 = new JButton("B");
number2.addActionListener(listener);
numberPanel.setLayout(new GridLayout(2,2));
numberPanel.add(number1);
numberPanel.add(number2);
this.add(numberPanel, BorderLayout.WEST);
}
private void createGraphicsPanel()
{
//instantiate drawing panel
graphicsPanel = new DrawPanel(WIDTH, HEIGHT, model);
//add drawing panel to right
add(graphicsPanel);
}
private class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
model.setDisplayString(event.getActionCommand());
}
}
//creates a drawing frame
public static void main(String[] args)
{
DrawFrame draw = new DrawFrame();
}
}
Code for JPanel:
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
public class DrawPanel extends JPanel{
private static final long serialVersionUID = 3443814601865936618L;
private Font font = new Font("Default", Font.BOLD, 30);
private static final Color DEFAULT_TEXT_COLOR = Color.BLACK;
private static final Color HOVER_TEXT_COLOR = Color.RED;
private Color color = DEFAULT_TEXT_COLOR;
private List<GUIModel> numberList = new ArrayList<GUIModel>();
private GUIModel model;
boolean mouseHover = false;
public DrawPanel(int width, int height, GUIModel model){
this.setPreferredSize(new Dimension(width, height));
this.model = model;
//set white background for drawing panel
setBackground(Color.WHITE);
//add mouse listeners
MouseHandler mouseHandler = new MouseHandler();
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
}
void checkForHover(MouseEvent event) {
FontMetrics metrics = getFontMetrics(font);
Graphics g = getGraphics();
Rectangle textBounds = metrics.getStringBounds("A", g).getBounds();
g.dispose();
int index = 0;
while (index < numberList.size()) {
Double x = numberList.get(index).getCoordinate().getX();
Double y = numberList.get(index).getCoordinate().getY();
textBounds.translate(x.intValue(), y.intValue());
if (textBounds.contains(event.getPoint())) {
color = HOVER_TEXT_COLOR;
}
else {
color = DEFAULT_TEXT_COLOR;
}
index++;
}
repaint(textBounds);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setFont(font);
g.setColor(color);
int index = 0;
while (index < numberList.size()) {
Double x = numberList.get(index).getCoordinate().getX();
Double y = numberList.get(index).getCoordinate().getY();
String display = numberList.get(index).getDisplayString();
g.drawString(display, x.intValue(), y.intValue());
index++;
}
if (model.getCoordinate() != null) {
Point p = model.getCoordinate();
g.drawString(model.getDisplayString(), p.x, p.y);
GUIModel number = new GUIModel();
number.setCoordinate(p);
number.setDisplayString(model.getDisplayString());
numberList.add(number);
}
}
//class to handle all mouse events
private class MouseHandler extends MouseAdapter implements MouseMotionListener
{
#Override
public void mousePressed(MouseEvent event)
{
model.setCoordinate(event.getPoint());
}
#Override
public void mouseReleased(MouseEvent event)
{
DrawPanel.this.repaint();
}
#Override
public void mouseEntered(MouseEvent event) {
checkForHover(event);
}
#Override
public void mouseMoved(MouseEvent event) {
checkForHover(event);
}
}
}
Code for GUIModel:
import java.awt.Point;
public class GUIModel {
private String displayString;
private Point coordinate;
public GUIModel() {}
public GUIModel(String displayString) {
this.displayString = displayString;
}
public void setDisplayString(String displayString) {
this.displayString = displayString;
}
public String getDisplayString() {
return displayString;
}
public Point getCoordinate() {
return coordinate;
}
public void setCoordinate(int x, int y) {
this.coordinate = new Point(x, y);
}
public void setCoordinate(Point coordinate) {
this.coordinate = coordinate;
}
}
Any help would be much appreciated. Thanks!
There's several misconceptions.
Graphics#drawString doesn't paint the text at the x/y position, so that the x/y is the top left corner of the String, but instead, the x/y position is the baseline of the font, this means that much of the text is draw above the y position, see Font Concepts for more details. This means that when you try and calculate the the Rectangle of the text, it's actually lower then where you painting it. Instead, you need to use y + ascent to get the text to position properly.
paintComponent can be called at any time for any number of reasons, many of which you don't control. To this end, paintComponent should only be used to paint the current state of the component and should never update or modify the state of the component. So adding a new GUIModel within the method is the wrong thing to do, instead, it should be added in the mouseClicked event of the MouseListener.
You're relying to much on the GUIModel variables. You should create a model only when you actually need it
Conceptually, this example address most of the issues mentioned above
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DrawFrame extends JFrame {
private final int WIDTH = 500;
private final int HEIGHT = 300;
// private GUIModel model;
private JButton number1;
private JButton number2;
private JPanel numberPanel;
private DrawPanel graphicsPanel;
public DrawFrame() {
// this.model = new GUIModel(" ");
createSelectionPanel();
createGraphicsPanel();
this.setSize(WIDTH, HEIGHT);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
private void createSelectionPanel() {
numberPanel = new JPanel();
ButtonListener listener = new ButtonListener();
number1 = new JButton("A");
number1.addActionListener(listener);
number2 = new JButton("B");
number2.addActionListener(listener);
numberPanel.setLayout(new GridLayout(2, 2));
numberPanel.add(number1);
numberPanel.add(number2);
this.add(numberPanel, BorderLayout.WEST);
}
private void createGraphicsPanel() {
//instantiate drawing panel
graphicsPanel = new DrawPanel(WIDTH, HEIGHT);
//add drawing panel to right
add(graphicsPanel);
}
private class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
graphicsPanel.setDisplayString(event.getActionCommand());
}
}
//creates a drawing frame
public static void main(String[] args) {
DrawFrame draw = new DrawFrame();
}
public static class DrawPanel extends JPanel {
private static final long serialVersionUID = 3443814601865936618L;
private Font font = new Font("Default", Font.BOLD, 30);
private static final Color DEFAULT_TEXT_COLOR = Color.BLACK;
private static final Color HOVER_TEXT_COLOR = Color.RED;
private Color color = DEFAULT_TEXT_COLOR;
private List<GUIModel> numberList = new ArrayList<GUIModel>();
boolean mouseHover = false;
private String displayString;
private GUIModel hoverModel;
public DrawPanel(int width, int height) {
this.setPreferredSize(new Dimension(width, height));
//set white background for drawing panel
setBackground(Color.WHITE);
//add mouse listeners
MouseHandler mouseHandler = new MouseHandler();
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
}
protected Rectangle getBounds(GUIModel model) {
FontMetrics metrics = getFontMetrics(font);
Double x = model.getCoordinate().getX();
Double y = model.getCoordinate().getY();
Rectangle textBounds = new Rectangle(
x.intValue(),
y.intValue(),
metrics.stringWidth(model.getDisplayString()),
metrics.getHeight());
return textBounds;
}
void checkForHover(MouseEvent event) {
Rectangle textBounds = null;
if (hoverModel != null) {
textBounds = getBounds(hoverModel);
}
hoverModel = null;
if (textBounds != null) {
repaint(textBounds);
}
for (GUIModel model : numberList) {
textBounds = getBounds(model);
if (textBounds.contains(event.getPoint())) {
hoverModel = model;
repaint(textBounds);
break;
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(font);
g.setColor(DEFAULT_TEXT_COLOR);
FontMetrics fm = g.getFontMetrics();
for (GUIModel model : numberList) {
if (model != hoverModel) {
Double x = model.getCoordinate().getX();
Double y = model.getCoordinate().getY();
String display = model.getDisplayString();
g.drawString(display, x.intValue(), y.intValue() + fm.getAscent());
}
}
if (hoverModel != null) {
g.setColor(HOVER_TEXT_COLOR);
Double x = hoverModel.getCoordinate().getX();
Double y = hoverModel.getCoordinate().getY();
String display = hoverModel.getDisplayString();
g.drawString(display, x.intValue(), y.intValue() + fm.getAscent());
}
// if (model.getCoordinate() != null) {
// Point p = model.getCoordinate();
// g.drawString(model.getDisplayString(), p.x, p.y);
//// GUIModel number = new GUIModel();
//// number.setCoordinate(p);
//// number.setDisplayString(model.getDisplayString());
//// numberList.add(number);
// }
}
public void setDisplayString(String text) {
displayString = text;
}
//class to handle all mouse events
private class MouseHandler extends MouseAdapter implements MouseMotionListener {
#Override
public void mouseClicked(MouseEvent e) {
GUIModel model = new GUIModel(displayString);
model.setCoordinate(e.getPoint());
numberList.add(model);
repaint();
}
#Override
public void mouseMoved(MouseEvent event) {
checkForHover(event);
}
}
}
public static class GUIModel {
private String displayString;
private Point coordinate;
public GUIModel() {
}
public GUIModel(String displayString) {
this.displayString = displayString;
}
public void setDisplayString(String displayString) {
this.displayString = displayString;
}
public String getDisplayString() {
return displayString;
}
public Point getCoordinate() {
return coordinate;
}
public void setCoordinate(int x, int y) {
this.coordinate = new Point(x, y);
}
public void setCoordinate(Point coordinate) {
this.coordinate = coordinate;
}
}
}
I have a JFrame with dimension 500 X 500: it contains a JComponent with size 1000 X 1000 and this JComponent is contained into a JScrollBar with scrollbar always present.
The screenshot
The code
/**component */
public class LinesComponent extends JPanel
....
/***other class: CommandPanel contains the LinesComponents*/
public class CommandPanel extends JPanel{
....
private LinesComponent panel;
/*JScrollPanel*/
private void buildScrollPanel(Container container) {
JScrollPane scroll = new JScrollPane(panel);
scroll.setSize(1000,1000);
scroll.setBorder(new LineBorder(Color.BLACK));
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
container.add(scroll);
}
/**JFrame*/
private void buildFrame(String title) {
this.testFrame = new JFrame(title);
this.testFrame.setLayout(new FlowLayout());
this.testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.testFrame.setLocationRelativeTo(null);
this.testFrame.setSize(500,500);
this.testFrame.setBackground(EnumColor.BACKGROUND_PANEL.getValue());
}
//end CommandPanel
As showed into screenshot, the window has the scrollbar but it isn't working.
I tried to change the dimensions of JPanel and JFrame but the situation doesn't change.
I know that the scroll appears when the dimension of JComponent are greater of dimensione of JPanel container, but in this moment I see that I lose some information but I don't understand what is.
Do you have any suggest, please?
PS into screenshot I show that the scroll doesn't work, but in my desiderata the scroll has to work in vertical and horizontal senses
PS PS all my code in only one class
package testDebug;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.LineBorder;
public class AllClasses {
public static void main(String[] a){
CommandPanel commandPanel=new CommandPanel("test", new ArrayList<MyPoint>());
}
}
class LinesComponent extends JPanel{
private static final long serialVersionUID = 1L;
private final LinkedList<Line> lines = new LinkedList<Line>();
private static class Line {
final int x1;//x del primo punto
final int y1;//y del primo punto
final int x2;//x del secondo punto
final int y2;//y del secondo punto
final Color color;
final int pressure;
public Line(int x1, int y1, int x2, int y2, Color color) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.color = color;
this.pressure=3;
}
public Line(int x1, int y1, int x2, int y2, int newPressure, Color color) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.pressure=newPressure;
this.color = color;
}
}//Line
public LinesComponent(){
setBorder(new LineBorder(Color.BLACK));
setBackground(Color.WHITE);
setPreferredSize(new Dimension(400, 400));
}
public void clearLines() {
this.lines.clear();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Line line : this.lines) {
g.setColor(line.color);
g.fillOval(line.x1,line.y1, line.pressure, line.pressure);
}
}
public void addPoint(MyPoint p, Color randomColor) {
//call addLine(double x, double y, double xtilt, double ytilt, Color randomColor);
}
private void addLine(double x, double y, double xtilt, double ytilt, Color randomColor) {
this.lines.add(new Line((int) x, (int) y, (int) xtilt, (int) ytilt, randomColor));
repaint();
}
}
enum EnumButton {
DECREASE_X("-"),
INCREASE_X("+"),
DECREASE_Y("-"),
INCREASE_Y("+"),
DECREASE_ZOOM("-"),
INCREASE_ZOOM("+");
private JButton button;
public JButton button(){
return this.button;
}
private EnumButton(String s){
this.button=new JButton(s);
}
public void addActionListener(ActionListener a){
this.button().addActionListener(a);
}
}
class CommandPanel extends JPanel {
private static final long serialVersionUID = 1L;
private static final String INITIAL_POSITION="0";
private JFrame testFrame = null;
private LinesComponent panelSignature;
private JTextField positionX=new JTextField(5);
private JTextField positionY=new JTextField(5);
public CommandPanel(String title, List<MyPoint> newPoints) {
super();
positionX.setText(INITIAL_POSITION);
positionY.setText(INITIAL_POSITION);
buildFrame(title);
Container container = testFrame.getContentPane();
this.panelSignature=initPanel();
buildScrollPanel(container);
paintLine(newPoints);
allignButtons();
defineFrame();
}
private void buildScrollPanel(Container container) {
JScrollPane scroll = new JScrollPane(panelSignature);
scroll.setSize(300,300);
scroll.setBorder(new LineBorder(Color.BLACK));
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
container.add(scroll);
}
private void defineFrame() {
this.testFrame.pack();
this.testFrame.setVisible(true);
}
private void paintLine(List<MyPoint> points) {
Iterator<MyPoint> iterator = points.iterator();
while (iterator.hasNext()) {
MyPoint point = iterator.next();
this.panelSignature.addPoint(point,Color.BLUE);
}
}//disegnaLinea
private void allignButtons() {
JPanel buttonPanel = new JPanel(new FlowLayout());
sectionHorizontalMovement(buttonPanel);
sectionVerticalMovement(buttonPanel);
sectionZoom(buttonPanel);
this.testFrame.add(buttonPanel);
}
private void sectionZoom(JPanel buttonPanel) {
buttonPanel.add(EnumLabel.ZOOM.label());
buttonPanel.add(EnumButton.INCREASE_ZOOM.button());
buttonPanel.add(EnumButton.DECREASE_ZOOM.button());
buttonPanel.add(EnumLabel.ZOOM_DIRECTION.label());
buttonPanel.add(EnumLabel.EMPTY.label());
}//sezioneZoom
private void sectionVerticalMovement(JPanel pannelloPulsanti) {
pannelloPulsanti.add(EnumLabel.MOVE_UP_DOWN.label());
pannelloPulsanti.add(EnumButton.INCREASE_Y.button());
pannelloPulsanti.add(EnumButton.DECREASE_Y.button());
pannelloPulsanti.add(EnumLabel.Y_DIRECTION.label());
pannelloPulsanti.add(EnumLabel.MAX_Y_ALLOWED.label());
}//sezioneSpostamentoVerticale
private void sectionHorizontalMovement(JPanel pannelloPulsanti) {
pannelloPulsanti.add(EnumLabel.MOVE_RIGHT_LEFT.label());
pannelloPulsanti.add(EnumButton.INCREASE_X.button());
pannelloPulsanti.add(EnumButton.DECREASE_X.button());
pannelloPulsanti.add(EnumLabel.X_DIRECTION.label());
pannelloPulsanti.add(EnumLabel.MAX_X_ALLOWED.label());
}//sezioneSpostamentoOrizzontale
public LinesComponent initPanel() {
LinesComponent linesComponent= new LinesComponent();
linesComponent.setBorder(new LineBorder(Color.GRAY));
return linesComponent;
}
private void buildFrame(String titolo) {
this.testFrame = new JFrame(titolo);
this.testFrame.setLayout(new FlowLayout());
this.testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.testFrame.setLocationRelativeTo(null);
this.testFrame.setSize(700,700);
this.testFrame.setBackground(Color.WHITE);
}
}
enum EnumLayout {
FRAME(new FlowLayout()), BUTTON_PANEL(new GridLayout(3, 5));
private LayoutManager value;
private EnumLayout(LayoutManager manager){
this.value=manager;
}
public LayoutManager layout() {
return value;
}
}
enum EnumLabel {
MOVE_RIGHT_LEFT("Sposta in orizzontale"),
MOVE_UP_DOWN("Sposta in verticale"),
ZOOM("Zoom"), EMPTY(""),
MAX_X_ALLOWED("LARGHEZZA: 600"), MAX_Y_ALLOWED("ALTEZZA: 600"),
X_DIRECTION("0"), Y_DIRECTION("0"), ZOOM_DIRECTION("1")
;
private JLabel label;
private EnumLabel(String lab){
this.label=new JLabel(lab);
}
public JLabel label (){
return this.label;
}
}
class MyPoint {
// class with the data: useless for position of panel
}
Scrollpane and its scrollbars entirely rely on the size of the component in the viewport, which, by default, depends of the preferred size of that component.
Check this code which shows you a basic example of how this all works:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestScrollPane {
public static class CustomComponent extends JPanel {
private static final int RADIUS = 20;
private int x = 0;
private int y = 0;
private double speed = 18;
private double dx;
private double dy;
public CustomComponent() {
dx = speed;
dy = speed;
Timer t = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
x += dx;
y += dy;
if (x + RADIUS > getWidth()) {
x = getWidth() - RADIUS;
dx = -speed;
} else if (x < 0) {
x = 0;
dx = speed;
}
if (y + RADIUS > getHeight()) {
y = getHeight() - RADIUS;
dy = -speed;
} else if (y < 0) {
y = 0;
dy = speed;
}
repaint();
}
});
t.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillOval(x, y, RADIUS, RADIUS);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
}
protected void initUI() {
JFrame window = new JFrame(TestScrollPane.class.getSimpleName());
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scroll = new JScrollPane(new CustomComponent(), JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
window.add(scroll);
window.setSize(600, 500);
window.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestScrollPane().initUI();
}
});
}
}
Actually your JScrollPane is 1000x1000px, so if there are scroll bars, you won't see them, because your frame is 500x500. Note that the JScrollPane acts on its content, that means that it shows scrollbars if the panel you are adding to it is bigger than the JScrollPane itself, but doesn't add scrollbars if the container is smaller.
The viewable area/space is defined (in most cases) by the view's preferred size. This suggests that your problem lies with your scroll panes view and not the scroll pane or anything else you've provided us
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestScrollPane02 {
public static void main(String[] args) {
new TestScrollPane02();
}
public TestScrollPane02() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(new LargePane()));
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class LargePane extends JPanel {
public LargePane() {
setLayout(new GridBagLayout());
add(new JLabel("I'm a large panel"));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.drawLine(0, 0, getWidth(), getHeight());
g.drawLine(getWidth(), 0, 0, getHeight());
}
}
}
You can also have a look the Scrollable interface which provides additional hints back to the scroll pane...