I would like to ask the same thing than this question but using SWT: Is there a way to make a Button with your own button graphic not just with an image inside the button? If not is another way to create a custom button in java?
public class ImageButton extends Canvas {
private int mouse = 0;
private boolean hit = false;
public ImageButton(Composite parent, int style) {
super(parent, style);
this.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
switch (mouse) {
case 0:
// Default state
e.gc.drawString("Normal", 5, 5);
break;
case 1:
// Mouse over
e.gc.drawString("Mouse over", 5, 5);
break;
case 2:
// Mouse down
e.gc.drawString("Hit", 5, 5);
break;
}
}
});
this.addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent e) {
if (!hit)
return;
mouse = 2;
if (e.x < 0 || e.y < 0 || e.x > getBounds().width
|| e.y > getBounds().height) {
mouse = 0;
}
redraw();
}
});
this.addMouseTrackListener(new MouseTrackAdapter() {
public void mouseEnter(MouseEvent e) {
mouse = 1;
redraw();
}
public void mouseExit(MouseEvent e) {
mouse = 0;
redraw();
}
});
this.addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
hit = true;
mouse = 2;
redraw();
}
public void mouseUp(MouseEvent e) {
hit = false;
mouse = 1;
if (e.x < 0 || e.y < 0 || e.x > getBounds().width
|| e.y > getBounds().height) {
mouse = 0;
}
redraw();
if (mouse == 1)
notifyListeners(SWT.Selection, new Event());
}
});
this.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.keyCode == '\r' || e.character == ' ') {
Event event = new Event();
notifyListeners(SWT.Selection, event);
}
}
});
}
}
No, you can add a PaintListener to a button, but it will probably look really strange.
What you would need to do is to set the style of the window to "owner drawn" and than add your drawing code in the Button#wmDrawChild method. This means you need to add dependencies on internal SWT-classes and it will only work for Windows.
Related
I have created a swt textbox and trying to do undo and redo functionality but when i press "ctrl+z" the listener itself is not working .so how can we do undo and redo operations.the code to implement undo and redo is as follows
private static class CTabItemControl extends Composite {
private static class UndoRedoStack<T> {
private Stack<T> undo;
private Stack<T> redo;
public UndoRedoStack() {
undo = new Stack<T>();
redo = new Stack<T>();
}
public void pushUndo(T delta) {
undo.add(delta);
}
public void pushRedo(T delta) {
redo.add(delta);
}
public T popUndo() {
T res = undo.pop();
return res;
}
public T popRedo() {
T res = redo.pop();
return res;
}
public T peekUndo() {
T res = undo.peek();
return res;
}
public void clearRedo() {
redo.clear();
}
public void clearUndo() {
undo.clear();
}
public boolean hasUndo() {
return !undo.isEmpty();
}
public boolean hasRedo() {
return !redo.isEmpty();
}
}
//private StyledText editor;
private UndoRedoStack<ExtendedModifyEvent> stack;
private boolean isUndo;
private boolean isRedo;
public CTabItemControl(Composite parentComposite,final CTabItem tabitem){
super(parentComposite, SWT.NONE);
setLayout(new GridLayout(1, false));
editor = new StyledText(this, SWT.MULTI | SWT.V_SCROLL);
editor.setLayoutData(new GridData(GridData.FILL_BOTH));
editor.setFont(new Font(Display.getDefault(),"Cambria", 10, SWT.NORMAL));
editor.addListener(SWT.KeyDown, new Listener(){
public void handleEvent(Event event) {
event.doit = true;
if(!tabitem.getText().contains("*"))
{
tabitem.setText('*'+tabitem.getText());
System.out.println("inserted *");
}
}
});
editor.addExtendedModifyListener(new ExtendedModifyListener(){
//editor.addKeyListener(this);
public void modifyText(ExtendedModifyEvent event) {
if (isUndo) {
stack.pushRedo(event);
} else { // is Redo or a normal user action
stack.pushUndo(event);
if (!isRedo) {
stack.clearRedo();
}
}
}
});
editor.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
// Listen to CTRL+Z for Undo, to CTRL+Y or CTRL+SHIFT+Z for Redo
boolean isCtrl = (e.stateMask & SWT.CTRL) > 0;
boolean isAlt = (e.stateMask & SWT.ALT) > 0;
if (isCtrl && !isAlt) {
boolean isShift = (e.stateMask & SWT.SHIFT) > 0;
if (!isShift && e.keyCode == 'z') {
{
System.out.println("call undo");
undo();
}
} else if (!isShift && e.keyCode == 'y' || isShift
&& e.keyCode == 'z') {
redo();
}
}
if(e.stateMask == SWT.CTRL && e.keyCode == 'a'){
editor.selectAll();
}
}
public void keyReleased(KeyEvent e) {
// ignore
}
});
//this.editor = editor;
stack = new UndoRedoStack<ExtendedModifyEvent>();
}
private void revertEvent(ExtendedModifyEvent event) {
System.out.println("calling revertevent");
editor.replaceTextRange(event.start, event.length, event.replacedText);
// (causes the modifyText() listener method to be called)
editor.setSelectionRange(event.start, event.replacedText.length());
}
private void undo() {
System.out.println("calling undo");
if (stack.hasUndo()) {
isUndo = true;
revertEvent(stack.popUndo());
isUndo = false;
}
}
private void redo() {
if (stack.hasRedo()) {
isRedo = true;
revertEvent(stack.popRedo());
isRedo = false;
}
}
public void clearUndoRedo() {
stack.clearUndo();
stack.clearRedo();
}
public boolean hasUndo() {
return stack.hasUndo();
}
public String peekUndo() {
return stack.peekUndo().toString();
}
}
When you press Ctrl + Z or Ctrl + Y, you get 2 key events, one for Ctrl and one for another key. So it is better to check for the character of the second event
e.character == 0x1a
for Ctr + Z, and
e.character == 0x19
for Ctrl + Y
I have two classes (Sampling and Stacker). The Sampling class (my Main class) is extends JFrame and has a JButton with an ActionListener to open the Stacker class.
The problem is when the button is clicked, the Stacker class will open but only a frame without any components. When I switch the main method into the Stacker class, the program works fine. What is the problem?
Here is the code:
The Sampling class:
public class Sampling extends JFrame implements ActionListener
{
private JButton openStacker;
Stacker st;
public Sampling()
{
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
setLocationRelativeTo(null);
openStacker = new JButton("Start Stacker!");
add(openStacker);
openStacker.addActionListener(this);
setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
dispose();
st = new Stacker();
}
public static void main (String args[])
{
new Sampling();
}
}
The Stacker game class:
public class Stacker extends JFrame implements KeyListener
{
int iteration = 1;
double time = 200;
int last = 0;
int m = 10;
int n = 20;
JButton b[][];
int length[] = {5,5};
int layer = 19;
int deltax[] = {0,0};
boolean press = false;
boolean forward = true;
boolean start = true;
public Stacker()
{
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(400,580);
this.setUndecorated(false);
this.setLocationRelativeTo(null);
b = new JButton [m][n];
setLayout(new GridLayout(n,m));
for (int y = 0;y<n;y++)
{
for (int x = 0;x<m;x++)
{
b[x][y] = new JButton(" ");
b[x][y].setBackground(Color.DARK_GRAY);
add(b[x][y]);
b[x][y].setEnabled(false);
}//end inner for
}
this.setFocusable(true);
this.pack();
this.addKeyListener(this);
this.setVisible(true);
go();
}
public void go()
{
int tmp = 0;
Component temporaryLostComponent = null;
do{
if (forward == true)
{
forward();
} else {
back();
}
if (deltax[1] == 10-length[1])
{
forward = false;
} else if (deltax[1] == 0)
{
forward = true;
}
draw();
try
{
Thread.sleep((long) time);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}while(press == false);
if (layer>12)
{
time= 150-(iteration*iteration*2-iteration);
} else
{
time = time - 2.2;
}
iteration++;
layer--;
press = false;
tmp = check();
length[0] = length[1];
length[1] = tmp;
if (layer == -1)
{
JOptionPane.showMessageDialog(temporaryLostComponent, "Congratulations! You beat the game!");
repeat();
}
if (length[1] <= 0)
{
JOptionPane.showMessageDialog(temporaryLostComponent, "Game over! You reached line "+(18-layer)+"!");
repeat();
}
last = deltax[1];
start = false;
go();
}
public int check()
{
if (start == true)
{
return length[1];
}
else if (last<deltax[1])
{
if (deltax[1]+length[1]-1 <= last+length[0]-1)
{
return length[1];
}
else
{
return length[1]-Math.abs((deltax[1]+length[1])-(last+length[0]));
}
}
else if (last>deltax[1])
{
return length[1]-Math.abs(deltax[1]-last);
}
else
{
return length[1];
}
}
public void forward()
{
deltax[0] = deltax[1];
deltax[1]++;
}
public void back()
{
deltax[0] = deltax[1];
deltax[1]--;
}
public void draw()
{
for (int x = 0;x<length[1];x++)
{
b[x+deltax[0]][layer].setBackground(Color.DARK_GRAY);
}
for (int x = 0;x<length[1];x++)
{
b[x+deltax[1]][layer].setBackground(Color.CYAN);
}
}
public void repeat()
{
if(JOptionPane.showConfirmDialog(null, "PLAY AGAIN?","WARNING",JOptionPane.YES_NO_OPTION)== JOptionPane.YES_OPTION)
{
dispose();
new Stacker();
}else{
System.exit(0);
}
}
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_SPACE)
{
press = true;
}
}
public void keyReleased(KeyEvent arg0)
{
}
public void keyTyped(KeyEvent arg0)
{
}
}
Just to put all my comments into an answer, and give you somewhere to start with:
Comment 1:
Take out go(); see that happens. I tested it and it will work. If you leave it there, even the frame's close button is jammed. You're blocking the edt with the while->Thread.sleep junk. You'll want to do some refactoring. You're code it hard to follow and I have no idea what you're trying to do, so I didn't even attempt it
Comment 2:
If you're wondering why it works when you just run the main from the Stacker class, it's probably because you are running it outside the EDT,
public static void main(String[] args) { new Stacker(); }. What happens when you click the button, that action is performed within the EDT, and hence your new Stacker() will be run on the EDT. In which case the EDT gets blocked by your while loop. If you try run the program from the Stacker class, but wrap it in a SwingUtilities.invokeLater, you will also notice the program fails to work. Swing programs should be run on the EDT though.
Comment 2: Read the first few sections on Concurrency with Swing
So what you can do is use a Swing Timer (which operates on the EDT) for the game loop. What I did was refactor your code a bit. It doesn't operate the way you want it to yet, only because I didn't really understand the logic of your code. So I couldn't get it to work. What I did though, is put some of the logic into the Timer.
Timer timer = new Timer((int)time, new ActionListener(){
public void actionPerformed(ActionEvent event) {
if (forward == true) {
forward();
} else {
back();
}
if (deltax[1] == 10 - length[1]) {
forward = false;
} else if (deltax[1] == 0) {
forward = true;
}
draw();
}
});
And when the go() method is called, it just starts the timer by calling timer.start(). Basically what you need to know about the timer, is that every tick (the milliseconds you pass it), the actionPerformed will be called. So you can update the game state in that method, just like you did in the while loop each iteration.
Take some time to go over How to Use Swing Timers
To get the game working properly, you still need to make some adjustments, but this should give you a head start.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Sampling extends JFrame implements ActionListener {
private JButton openStacker;
Stacker st;
public Sampling() {
setSize(300, 300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
setLocationRelativeTo(null);
openStacker = new JButton("Start Stacker!");
add(openStacker);
openStacker.addActionListener(this);
setVisible(true);
}
public void actionPerformed(ActionEvent e) {
dispose();
st = new Stacker();
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new Sampling();
}
});
}
}
class Stacker extends JFrame implements KeyListener {
int iteration = 1;
double time = 200;
int last = 0;
int m = 10;
int n = 20;
JButton b[][];
int length[] = {5, 5};
int layer = 19;
int deltax[] = {0, 0};
boolean press = false;
boolean forward = true;
boolean start = true;
Timer timer = new Timer((int)time, new ActionListener(){
public void actionPerformed(ActionEvent event) {
if (forward == true) {
forward();
} else {
back();
}
if (deltax[1] == 10 - length[1]) {
forward = false;
} else if (deltax[1] == 0) {
forward = true;
}
draw();
}
});
public Stacker() {
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(400, 580);
this.setUndecorated(false);
this.setLocationRelativeTo(null);
b = new JButton[m][n];
setLayout(new GridLayout(n, m));
for (int y = 0; y < n; y++) {
for (int x = 0; x < m; x++) {
b[x][y] = new JButton(" ");
b[x][y].setBackground(Color.DARK_GRAY);
add(b[x][y]);
b[x][y].setEnabled(false);
}//end inner for
}
this.setFocusable(true);
this.pack();
JPanel panel = (JPanel)getContentPane();
panel.addKeyListener(this);
this.setVisible(true);
panel.requestFocusInWindow();
go();
}
public void go() {
int tmp = 0;
Component temporaryLostComponent = null;
timer.start();
if (layer > 12) {
time = 150 - (iteration * iteration * 2 - iteration);
} else {
time = time - 2.2;
}
iteration++;
layer--;
press = false;
tmp = check();
length[0] = length[1];
length[1] = tmp;
if (layer == -1) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Congratulations! You beat the game!");
repeat();
}
if (length[1] <= 0) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Game over! You reached line " + (18 - layer) + "!");
repeat();
}
last = deltax[1];
start = false;
//go();
}
public int check() {
if (start == true) {
return length[1];
} else if (last < deltax[1]) {
if (deltax[1] + length[1] - 1 <= last + length[0] - 1) {
return length[1];
} else {
return length[1] - Math.abs((deltax[1] + length[1]) - (last + length[0]));
}
} else if (last > deltax[1]) {
return length[1] - Math.abs(deltax[1] - last);
} else {
return length[1];
}
}
public void forward() {
deltax[0] = deltax[1];
deltax[1]++;
}
public void back() {
deltax[0] = deltax[1];
deltax[1]--;
}
public void draw() {
for (int x = 0; x < length[1]; x++) {
b[x + deltax[0]][layer].setBackground(Color.DARK_GRAY);
}
for (int x = 0; x < length[1]; x++) {
b[x + deltax[1]][layer].setBackground(Color.CYAN);
}
}
public void repeat() {
if (JOptionPane.showConfirmDialog(null, "PLAY AGAIN?", "WARNING", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
dispose();
new Stacker();
} else {
System.exit(0);
}
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
System.out.println("Pressed");
press = true;
}
}
public void keyReleased(KeyEvent arg0) {
}
public void keyTyped(KeyEvent arg0) {
}
}
Notice the SwingUtilities.invokeLater in the main. That's how you can start up the program on the EDT. The link on Concurrency In Swing will give you more information.
I'm trying to use Java's KeyListener to update a JLabel as I type. Essentially, I'm making my own text field. Here's what I have:
/**
* Constructor for objects of class Dictionary
*/
public Dictionary()
{
frame = new JFrame();
frame.setTitle("Shori Dictionary");
frame.setLayout(new GridBagLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void createWord()
{
frame.remove(pane);
pane = new PaintPane(field.getImage());
pane.setLayout(new BorderLayout());
frame.add(pane);
frame.pack();
newWord = new JLabel(text);
newWord.setFont(newWord.getFont().deriveFont(Font.BOLD, 28));
newWord.setForeground(Color.BLACK);
newWord.setHorizontalTextPosition(JLabel.LEFT);
newWord.setVerticalAlignment(JLabel.TOP);
newWord.setVerticalTextPosition(JLabel.TOP);
newWord.setBorder(BorderFactory.createEmptyBorder(445, 150, 0, 0));
pane.add(newWord);
frame.pack();
frame.setLocationRelativeTo(null);
pane.setFocusable(true);
updateInteraction();
}
private void keyPress()
{
pane.addKeyListener(new KeyListener()
{
public void keyTyped(KeyEvent e) {
for(int i = 97; i <= 122; i++){
//Cycles through every lowercase letter
if(e.getKeyChar() == (char)(i)&& pane.returnImage() == field.getImage()){
text += (char)(i);
break;
}
}
//Even in the Debugger, these next if-elses have never worked
if(e.getKeyCode() == KeyEvent.VK_SPACE&& pane.returnImage() == field.getImage()) text += " ";
else if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE&& pane.returnImage() == field.getImage()){
int x = text.length();
text = text.substring(0,x-1); //Not sure if this works, haven't been able to test it yet
}
else if(e.getKeyCode() == KeyEvent.VK_ENTER&& pane.returnImage() == field.getImage()){
//do something with the text
text = "";
//exit the word creator
}
newWord.setText(text);
newWord.repaint(); //Apparently this isn't necessary...
}
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
);
}
private void mouseAction()
{
pane.addMouseListener(new MouseListener()
{
public void mouseClicked(MouseEvent arg0) {
//cover page
if(open.contains(arg0.getPoint())&& pane.returnImage() == cover.getImage()) displayPages();
else if(search.contains(arg0.getPoint())&& pane.returnImage() == cover.getImage()) searchWord();
else if(enter.contains(arg0.getPoint())&& pane.returnImage() == cover.getImage()) createWord();
//inner pages
else if(nextPage.contains(arg0.getPoint())&& pane.returnImage() == pages.getImage()) pageFlip("next");
else if(prevPage.contains(arg0.getPoint())&& pane.returnImage() == pages.getImage()) pageFlip("previous");
else if(cancel.contains(arg0.getPoint())&& pane.returnImage() == field.getImage()) coverPage();
frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
}
public void mouseReleased(MouseEvent arg0) {
}
}
);
}
private void mouseMovement()
{
pane.addMouseMotionListener(new MouseMotionListener()
{
#Override
public void mouseMoved(MouseEvent e) {
if(search.contains(e.getPoint())&& pane.returnImage() == cover.getImage()){
frame.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
else if(enter.contains(e.getPoint())&& pane.returnImage() == cover.getImage()){
frame.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
else if(open.contains(e.getPoint())&& pane.returnImage() == cover.getImage()){
frame.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
else if(nextPage.contains(e.getPoint())&& pane.returnImage() == pages.getImage()){
frame.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
else if(prevPage.contains(e.getPoint())&& pane.returnImage() == pages.getImage()){
frame.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
else if(cancel.contains(e.getPoint())&& pane.returnImage() == field.getImage()){
frame.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
else{
frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
#Override
public void mouseDragged(MouseEvent e) {
}
}
);
}
private void updateInteraction(){
mouseMovement();
mouseAction();
keyPress();
}
public class PaintPane extends JPanel {
private Image background;
private Graphics g2d;
public PaintPane(Image image) {
background = image;
}
#Override
public Dimension getPreferredSize() {
return background == null ? new Dimension(0, 0) : new Dimension(background.getWidth(this), background.getHeight(this));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
Insets insets = getInsets();
int width = getWidth() - 1 - (insets.left + insets.right);
int height = getHeight() - 1 - (insets.top + insets.bottom);
int x = (width - background.getWidth(this)) / 2;
int y = (height - background.getHeight(this)) / 2;
g.drawImage(background, x, y, this);
}
//g.fillRect(654, 798, 358, 77); //for testing rectangle positioning
}
public Image returnImage() {
return background;
}
}
I'm using BluJ to write this, and it has a built in Debugger. I just tried adding keyPress(); before the updateInteraction(); in createWord(), and ran the Debugger to go through each method step by step. Everything worked perfectly. Then I tried without the Debugger, and it wouldn't display any text while I was typing. So, I turned on the Debugger again. It didn't detect any keys being typed at all. I don't know why it only worked that once, but it was definitely working. This is my first time working with KeyListener, MouseListener, and MouseMotionListener. Is there a better way to get this program to run properly?
and it wouldn't display any text while I was typing
A component needs to have focus in order to respond to KeyEvents. A JPanel is not focusable by default.
I'm making my own text field
Why? What functionality is missing from JTextField?
I would just use JTextField and then add a DocumentListener to the document from the text field. Read the section from the Swing tutorial on How to Write a Document Listener for more information.
Your Component must have the focus for keyboard input on Key Event. However, to check it in action, first make your JPanle focus-able by panel.setFocusable(true). Then try with panel.requestFocusInWindow()
to get the focus to panel on application start-up.
However, in your code:
for(int i = 97; i <= 122; i++){
//Cycles through every lowercase letter
if(e.getKeyChar() == (char)(i)&& pane.returnImage() == field.getImage()){
text += (char)(i);
break;
}
}
Why do you have to use a loop here? Just simply putting:
if(evt.getKeyChar() >=97 && evt.getKeyChar() <=122 && pane.returnImage() == field.getImage())
text += evt.getKeyChar()
Should do the work.
If you are just doing a learning exercise, then it's OK to make your own implementation instead of using JTextField. You can make a JPanel receive key events by calling setFocusable(true) on it.
Even after that, you should be aware of something that will probably trip you up: keyTyped callbacks don't fill out the keyCode property of KeyEvents. This is because it's possible to type in characters that do not have a single associated key. For example, on Windows you can fire keyPressed once by typing ALT+0160. You hit 5 keys but entered one character (a space) and got a single keyPressed callback.
To summarize: keyPressed and keyReleased deliver keyCodes and might deliver keyChar. keyTyped never gets a keyCode, and always gets a keyChar.
public class PanelKeyListener extends JFrame {
private JPanel _contentPane;
StringBuilder _stringBuilder = new StringBuilder();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
PanelKeyListener frame = new PanelKeyListener();
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public PanelKeyListener() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
_contentPane = new JPanel() {
protected void paintComponent(java.awt.Graphics g) {
g.drawString(_stringBuilder.toString(), 10, 20);
}
};
setContentPane(_contentPane);
_contentPane.setFocusable(true);
_contentPane.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {
System.out.println("keyTyped char[" + e.getKeyChar()
+ "] code[" + e.getKeyCode() + "]");
_stringBuilder.append(e.getKeyChar());
_contentPane.repaint();
}
public void keyReleased(KeyEvent e) {
System.out.println("keyReleased char[" + e.getKeyChar()
+ "] code[" + e.getKeyCode() + "]");
}
public void keyPressed(KeyEvent e) {
System.out.println("keyPressed char[" + e.getKeyChar()
+ "] code[" + e.getKeyCode() + "]");
}
});
}
}
You shouldn't reinvent the wheel if you can avoid it. Use/extend JTextField if it's practical to do so and meets your needs.
I wrote a simple implementation of the Game of life with java applets.
Here's is the source code for the Applet and the Model.
When I click the button to get the next iteration these Exceptions get thrown.
Z:\GameOfLife>appletviewer driver.html
Exception in thread "AWT-EventQueue-1" java.lang.ArrayIndexOutOfBoundsException:
65
at GameOfLifeApplet.mouseClicked(GameOfLifeApplet.java:63)
at java.awt.Component.processMouseEvent(Component.java:6219)
at java.awt.Component.processEvent(Component.java:5981)
at java.awt.Container.processEvent(Container.java:2041)
at java.awt.Component.dispatchEventImpl(Component.java:4583)
at java.awt.Container.dispatchEventImpl(Container.java:2099)
at java.awt.Component.dispatchEvent(Component.java:4413)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThre ad.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThre ad.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Try this code that adds the button once rather than every call to paint(). Note that this source still throws AIOOBE if you click outside the grid (and not on the button), but that seems like a basic logic error you should investigate once the button is fixed.
// <applet code='GameOfLifeApplet' width=580 height=650></applet>
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class GameOfLifeApplet extends Applet implements MouseListener,ActionListener
{
//the x and y coordinates to get the location of the clicked points
private int xCo, yCo;
private int diff_x, diff_y;
private GameOfLife game = new GameOfLife();
private Button nextButton = null;
public void init()
{
setLayout(null);
nextButton = new Button("Next Stage");
diff_x = diff_y = 600 / game.getGridSize();
nextButton.setLocation(250, 575);
nextButton.setSize(120, 30);
// add the button once only!
add(nextButton);
addMouseListener(this);
}
private void drawEmptyGrid(Graphics g)
{
g.setColor(Color.white);
g.fillRect(0,0,600,600);
g.setColor(Color.black);
for(int i=0;i<game.getGridSize();++i)
{
g.drawLine(0,i*diff_x,600,i*diff_x);
g.drawLine(i*diff_x,0,i*diff_x,600);
}
g.setColor(Color.white);
}
public void paint(Graphics g)
{
drawEmptyGrid(g);
g.setColor(Color.red);
for(int i=0;i<game.getGridSize();++i)
{
for(int j=0;j<game.getGridSize();++j)
{
if( game.grid[i][j] )
{
g.fillRect(i*diff_x,j*diff_y,diff_x,diff_y);
}
}
}
g.setColor(Color.white);
}
// This method will be called when the mouse has been clicked.
public void mouseClicked (MouseEvent me) {
// Save the coordinates of the click lke this.
xCo = me.getX();
yCo = me.getY();
int x_init = xCo / diff_x;
int y_init = yCo / diff_y;
System.out.println(x_init + "x" + y_init);
game.grid[x_init][y_init] = true;
//show the results of the click
repaint();
}
// This is called when the mous has been pressed
public void mousePressed (MouseEvent me) {}
// When it has been released
// not that a click also calls these Mouse-Pressed and Released.
// since they are empty nothing hapens here.
public void mouseReleased (MouseEvent me) {}
// This is executed when the mouse enters the applet. it will only
// be executed again when the mouse has left and then re-entered.
public void mouseEntered (MouseEvent me) {}
// When the Mouse leaves the applet.
public void mouseExited (MouseEvent me) {}
public void actionPerformed(ActionEvent evt)
{
// Here we will ask what component called this method
if (evt.getSource() == nextButton)
{
System.out.println("I got clicked!");
game.nextIteration();
repaint();
}
}
}
class GameOfLife
{
private final int GRID_SIZE = 64;
public boolean [][] grid = new boolean[GRID_SIZE][GRID_SIZE];
//default constructor
public GameOfLife()
{
for(int i=0;i<GRID_SIZE;++i)
{
for(int j=0;j<GRID_SIZE;++j)
{
grid[i][j] = false;
}
}
}
public int getGridSize()
{
return GRID_SIZE;
}
public int getLiveNeighbors(int i,int j)
{
int neighbors = 0;
for( int tmp_i = i-1; tmp_i <= i+1; ++tmp_i )
{
for( int tmp_j = j-1; tmp_j <= j+1; ++tmp_j )
{
if( tmp_i < 0 || tmp_i >= GRID_SIZE || tmp_j < 0 || tmp_j >= GRID_SIZE )
{}
else
{
if( grid[tmp_i][tmp_j] )
{
neighbors++;
}
}
}
}
return neighbors;
}
public void nextIteration()
{
boolean [][] newGrid = new boolean[GRID_SIZE][GRID_SIZE];
for(int i=0;i<GRID_SIZE;++i)
{
for(int j=0;j<GRID_SIZE;++j)
{
newGrid[i][j] = grid[i][j];
}
}
for( int i=0;i<GRID_SIZE;++i)
{
for( int j=0;j<GRID_SIZE;++j)
{
int my_neighbors = getLiveNeighbors(i,j);
if( !newGrid[i][j] && my_neighbors == 3)
{
grid[i][j] = true;
}
else if( newGrid[i][j] && ( my_neighbors == 2 || my_neighbors == 3 ) )
{
grid[i][j] = true;
}
else
{
grid[i][j] = false;
}
}
}
System.out.println("Change of assignment");
}
}
Further tips
Don't use AWT components in this millennium, use Swing instead.
Don't use null layouts. The custom rendered area does not need it, and the button should be sized and positioned using a layout manager (with maybe a border to pad it out).
Update
This code implements the 2nd suggestion from above 'use layouts', but leaves it as an exercise for the reader to update the components to something that might be used in this millennium (i.e. Swing).
The source below 'cheats' in a sense to show the GUI at it's natural size. This is tricky to do in an applet, since the size is set by the HTML. But put the GUI into a Swing based JOptionPane and it can be put on-screen, packed to its natural size, in just a couple of lines of code.
Here is what it looks like at the 'natural size' (I played with some numbers, to make the GUI smaller).
// <applet code='GameOfLifeApplet' width=320 height=350></applet>
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class GameOfLifeApplet extends Applet implements ActionListener
{
private Button nextButton = null;
private Ecosystem ecosystem;
public void init()
{
add(getGui());
}
public Component getGui() {
Panel gui = new Panel(new BorderLayout(3,3));
ecosystem = new Ecosystem();
gui.add(ecosystem, BorderLayout.CENTER);
nextButton = new Button("Next Stage");
Panel p = new Panel(new FlowLayout());
p.add(nextButton);
gui.add(p, BorderLayout.SOUTH);
nextButton.addActionListener(this);
return gui;
}
public void actionPerformed(ActionEvent evt)
{
// Here we will ask what component called this method
if (evt.getSource() == nextButton)
{
System.out.println("I got clicked!");
ecosystem.nextIteration();
ecosystem.repaint();
}
}
public static void main(String[] args) {
GameOfLifeApplet gola = new GameOfLifeApplet();
// quick cheat to get it on-screen (packed).
javax.swing.JOptionPane.showMessageDialog(null,gola.getGui());
}
}
class Ecosystem extends Panel implements MouseListener {
private GameOfLife game = new GameOfLife();
//the x and y coordinates to get the location of the clicked points
private int xCo, yCo;
private int diff_x, diff_y;
private int size = 300;
Ecosystem() {
diff_x = diff_y = 600 / game.getGridSize();
setPreferredSize(new Dimension(size,size));
addMouseListener(this);
}
public void nextIteration() {
game.nextIteration();
}
private void drawEmptyGrid(Graphics g)
{
g.setColor(Color.white);
g.fillRect(0,0,size,size);
g.setColor(Color.black);
for(int i=0;i<game.getGridSize();++i)
{
g.drawLine(0,i*diff_x,size,i*diff_x);
g.drawLine(i*diff_x,0,i*diff_x,size);
}
g.setColor(Color.white);
}
public void paint(Graphics g)
{
drawEmptyGrid(g);
g.setColor(Color.red);
for(int i=0;i<game.getGridSize();++i)
{
for(int j=0;j<game.getGridSize();++j)
{
if( game.grid[i][j] )
{
g.fillRect(i*diff_x,j*diff_y,diff_x,diff_y);
}
}
}
g.setColor(Color.white);
}
// This method will be called when the mouse has been clicked.
public void mouseClicked (MouseEvent me) {
Point point = me.getPoint();
// Save the coordinates of the click lke this.
xCo = (int)point.getX();
yCo = (int)point.getY();
int x_init = xCo / diff_x;
int y_init = yCo / diff_y;
System.out.println(x_init + "x" + y_init);
game.grid[x_init][y_init] = true;
//show the results of the click
repaint();
}
// This is called when the mous has been pressed
public void mousePressed (MouseEvent me) {}
// When it has been released
// not that a click also calls these Mouse-Pressed and Released.
// since they are empty nothing hapens here.
public void mouseReleased (MouseEvent me) {}
// This is executed when the mouse enters the applet. it will only
// be executed again when the mouse has left and then re-entered.
public void mouseEntered (MouseEvent me) {}
// When the Mouse leaves the applet.
public void mouseExited (MouseEvent me) {}
}
class GameOfLife
{
private final int GRID_SIZE = 60;
public boolean [][] grid = new boolean[GRID_SIZE][GRID_SIZE];
//default constructor
public GameOfLife()
{
for(int i=0;i<GRID_SIZE;++i)
{
for(int j=0;j<GRID_SIZE;++j)
{
grid[i][j] = false;
}
}
}
public int getGridSize()
{
return GRID_SIZE;
}
public int getLiveNeighbors(int i,int j)
{
int neighbors = 0;
for( int tmp_i = i-1; tmp_i <= i+1; ++tmp_i )
{
for( int tmp_j = j-1; tmp_j <= j+1; ++tmp_j )
{
if( tmp_i < 0 || tmp_i >= GRID_SIZE || tmp_j < 0 || tmp_j >= GRID_SIZE )
{}
else
{
if( grid[tmp_i][tmp_j] )
{
neighbors++;
}
}
}
}
return neighbors;
}
public void nextIteration()
{
boolean [][] newGrid = new boolean[GRID_SIZE][GRID_SIZE];
for(int i=0;i<GRID_SIZE;++i)
{
for(int j=0;j<GRID_SIZE;++j)
{
newGrid[i][j] = grid[i][j];
}
}
for( int i=0;i<GRID_SIZE;++i)
{
for( int j=0;j<GRID_SIZE;++j)
{
int my_neighbors = getLiveNeighbors(i,j);
if( !newGrid[i][j] && my_neighbors == 3)
{
grid[i][j] = true;
}
else if( newGrid[i][j] && ( my_neighbors == 2 || my_neighbors == 3 ) )
{
grid[i][j] = true;
}
else
{
grid[i][j] = false;
}
}
}
System.out.println("Change of assignment");
}
}
Other matters
The code moves the custom painting to a Panel. This gets around the common problems of painting directly to a top-level container. It also allows easy re-use of the same GUI in different containers. In this case both an applet, and for the 'application' (which would usually be put in a frame), a JOptionPane. It is now what is known as a 'hybrid applet/application' (easier for testing).
The custom painted component Ecosystem (shrugs) informs the layout what size it prefers to be. This helps us to avoid needing to set the size or bounds of anything.
The button will be exactly as big as it needs to be.
First, I think you're reading the exception trace wrong. The exception is an ArrayIndexOutOfBoundsException and occurs on line 63 of GameOfLifeApplet.java. That your app is an applet or that the exception occurs on the thread AWT-EventQueue-1 bears no relevance at all.
The root cause is that you've not properly synchronized the model and view's idea of how many cells there are in your grid. At the least, you should consider checking that the user actually clicked inside the grid before accessing the array element.
Been working with JTabbedPane and trying to customize it when using SCROLL_TAB_LAYOUT specifically with the scroll direction buttons.
I'm extending BasicTabbedPaneUI, but I don't see a method or ability to change the location of the scroll buttons. Searched around and don't see any one doing this other than just using different look and feels. Control over the location of scroll direction buttons would be very useful in general I feel.
Does anyone have any ideas how to do this while extending BasicTabbedPaneUI or any other method?
I'm assuming you want to move the scroll backwards button to the other side of the tabs.
In the BasicTabbedPaneUI class, there's a createDecreaseButton method that's package only (no access modifier).
It appears you're going to have to create your own BasicTabbedPaneUI class, with your own version of createDecreaseButton.
I changed the scroll button position without using many other components but simply extends the BasicTabbedPaneUI and hacking its paint() method to adjust the button position.
Here is the code:
public class MyTabbedScrollPane extends JTabbedPane {
public MyTabbedScrollPane ()
{
super ();
}
public MyTabbedScrollPane ( final int tabPlacement )
{
super ( tabPlacement );
}
public MyTabbedScrollPane ( final int tabPlacement, final int tabLayoutPolicy )
{
super ( tabPlacement, tabLayoutPolicy );
initialize();
}
public void initialize() {
setUI(new MyTabbedPaneUI());
}
private class MyTabbedPaneUI extends BasicTabbedPaneUI {
private int leadingTabIndex;
private Point tabViewPosition;
private Component adjustedButton;
private boolean scrollableTabLayoutEnabled() {
return tabPane.getTabLayoutPolicy() == SCROLL_TAB_LAYOUT;
}
/*
* Target button and view port utilities
*/
private Component findBackwardButton() {
Component[] comps = tabPane.getComponents();
for(Component comp:comps) {
if(comp instanceof BasicArrowButton) {
int direction = ((BasicArrowButton)comp).getDirection();
if(tabPane.getTabPlacement() == TOP || tabPane.getTabPlacement() == BOTTOM) {
if(direction == WEST) {
return comp;
}
}
}
}
return null;
}
private JViewport findViewPort() {
Component[] comps = tabPane.getComponents();
for(Component comp:comps) {
if(comp instanceof JViewport) {
return (JViewport)comp;
}
}
return null;
}
/*
* Override View port controlling (copy from BasicTabbedPaneUI.java)
*/
public void scrollForward(int tabPlacement) {
JViewport viewport = findViewPort();
Dimension viewSize = viewport.getViewSize();
Rectangle viewRect = viewport.getViewRect();
if (tabPlacement == TOP || tabPlacement == BOTTOM) {
if (viewRect.width >= viewSize.width - viewRect.x) {
return; // no room left to scroll
}
} else { // tabPlacement == LEFT || tabPlacement == RIGHT
if (viewRect.height >= viewSize.height - viewRect.y) {
return;
}
}
setLeadingTabIndex(tabPlacement, leadingTabIndex+1);
}
public void scrollBackward(int tabPlacement) {
if (leadingTabIndex == 0) {
return; // no room left to scroll
}
setLeadingTabIndex(tabPlacement, leadingTabIndex-1);
}
public void setLeadingTabIndex(int tabPlacement, int index) {
JViewport viewport = findViewPort();
leadingTabIndex = index;
Dimension viewSize = viewport.getViewSize();
Rectangle viewRect = viewport.getViewRect();
int offsetX = adjustedButton.getWidth()+2;
switch(tabPlacement) {
case TOP:
case BOTTOM:
tabViewPosition.x = leadingTabIndex == 0? 0-offsetX : rects[leadingTabIndex].x-offsetX;
if ((viewSize.width - tabViewPosition.x) < viewRect.width) {
// We've scrolled to the end, so adjust the viewport size
// to ensure the view position remains aligned on a tab boundary
Dimension extentSize = new Dimension(viewSize.width - tabViewPosition.x,
viewRect.height);
viewport.setExtentSize(extentSize);
}
break;
case LEFT:
case RIGHT:
tabViewPosition.y = leadingTabIndex == 0? 0 : rects[leadingTabIndex].y;
if ((viewSize.height - tabViewPosition.y) < viewRect.height) {
// We've scrolled to the end, so adjust the viewport size
// to ensure the view position remains aligned on a tab boundary
Dimension extentSize = new Dimension(viewRect.width,
viewSize.height - tabViewPosition.y);
viewport.setExtentSize(extentSize);
}
}
viewport.setViewPosition(tabViewPosition);
}
/*
* UI Rendering
*/
public void paint(final Graphics g, JComponent c) {
super.paint(g, c);
if(scrollableTabLayoutEnabled()) {
if(adjustedButton == null) {
adjustedButton = findBackwardButton();
tabViewPosition = new Point(0-(adjustedButton.getWidth()+2), 0);
Component[] comps = tabPane.getComponents();
for(Component comp:comps) {
if(comp instanceof BasicArrowButton) {
if(comp instanceof BasicArrowButton) {
BasicArrowButton button = (BasicArrowButton)comp;
int direction = button.getDirection();
if(tabPane.getTabPlacement() == TOP || tabPane.getTabPlacement() == BOTTOM) {
// left align the west button
if(direction == WEST) {
button.removeActionListener(button.getActionListeners()[0]);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
scrollBackward(tabPane.getTabPlacement());
}
});
} else if(direction == EAST) {
button.removeActionListener(button.getActionListeners()[0]);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
scrollForward(tabPane.getTabPlacement());
}
});
}
}
}
}
}
}
if(adjustedButton != null && adjustedButton.isVisible()) {
// move the scroll button
int by = adjustedButton.getY();
adjustedButton.setLocation(0, by);
findViewPort().setViewPosition(tabViewPosition);
return;
}
}
}
}
}