CS1102 Discussion Assignment Unit 6 - Details of the mouse event handling

Questions

Considering the Java program in section 6.3.3 “Dragging” of Eck (2019), describe what it does in response to specific operations of the mouse (mousePressed, mouseDragged, and mouseReleased), and how it does it.

You are encouraged to run the SimplePaint program for yourself to test its behavior. Then read through the program carefully to understand how that behavior arises.

1. Overview of the SimplePaint.java

To create a JavaFX program, the SimplePaint class extends the abstract class Application and overrides the abstract method start() in it. In the overridden start() method, the author creates a root node as the container and a canvas node inside it. The scene graph is as follows:
SceneGraph

To respond to the mouse operations, including mouse pressed, mouse dragged and mouse released, the author uses three lambda expressions to create three corresponding listeners, namely mouse pressed listener, mouse dragged listener, and the mouse released listener, and then he registers these listeners to the mouse event source. Hence, when the mouse event occurs, the mouse event source will automatically send the event object to the registered listener, and the listener object will call the corresponding method to process the event.
Relationship

2. Details of the Mouse Event Handling

2.1. Explanation of the Event Handling Methods

Mouse Pressed: The author defines a mousePressed() method to handle the mouse pressed event. In the mousePressed(), the author uses the nested if statement to handle 3 different cases: First, the click is on the white drawing area. If so, the program will save the previous location of the mouse, set the boolean variable dragging to be true, and the width and color of the line for drawing. Second, the click is one the clear button. In this case, the program will clearAndDrawPalette() to fill the canvas with white and redraw the color palette. Third, the click is on the color palette. under this circumstance, the program will call the changeColor() method to change the drawing color.

Mouse Dragged: The author defines a mouseDragged() method to handle the mouse dragged event. In the mouseDragged(), the author uses the boolean variable dragging to determine whether the user is drawing. If the value is true, it means the user is drawing. Then, the author will adjust the coordinate of the mouse to ensure drawing on the drawing area. Finally, the author calls the strokeLine() method to draw the line.

Mouse Released: The author defines a mouseReleased() method to handle the mouse released event. In the mouseReleased(), the author set the boolean variable dragging to be false. This makes sense because the drawing stops if the mouse is released.

2.2. The Processs of How Event Handling Works

The difficulty in understanding SimplePaint.java is how the mouse event handling works. Specifically, it is how the event listener is created and registered. In the program, the author uses the lambda espressions to achieve these operations (Eck, 2019, p. 288):

1
2
3
4
/* Respond to mouse events on the canvas, by calling methods in this class. */
canvas.setOnMousePressed(e -> mousePressed(e));
canvas.setOnMouseDragged(e -> mouseDragged(e));
canvas.setOnMouseReleased(e -> mouseReleased(e));

Although the lambda expression is a concise way to specify a listener, it is not conducive to understanding the event handling because lambda expression cannot allow us to see the things that are done behind the code to handle the event. Conversely, a better way to understand event handling is to declare a class to implement the Eventhandler interface and override the handle() method in it. And then use this class to create a listener and register it to the corresponding event source.

For example, to create a mouse pressed listener, the author uses the lambda expression: e -> mousePressed(e). In fact, this expression does the following things. First, declare a class to implement the interface EventHandler. Second, override the handle() method of the interface. Third, use the class to create a listener object. Finally, register the listener to the event source.
Versions

Appendix

I rewrote the SimplePaint.java by declaring three classes that implement the interface EventHandler. And I will attach the codes in the end, and I hope this is helpful for understanding event handling.

The Lambda Version:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package discussion_assignment;


import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;

/**
* A simple program where the user can sketch curves in a variety of
* colors. A color palette is shown along the right edge of the canvas.
* The user can select a drawing color by clicking on a color in the
* palette. Under the colors is a "Clear button" that the user
* can click to clear the sketch. The user draws by clicking and
* dragging in a large white area that occupies most of the canvas.
*/
public class SimperPaintLambdaVersion extends Application {

/**
* This main routine allows this class to be run as a program.
* The main() method can be omitted
*/
/* public static void main(String[] args) {
launch(args);
}*/


/*
* Array of colors corresponding to available colors in the palette.
* (The last color is a slightly darker version of yellow for
* better visibility on a white background.)
*/
private final Color[] palette = {
Color.BLACK, Color.RED, Color.GREEN, Color.BLUE,
Color.CYAN, Color.MAGENTA, Color.color(0.95, 0.9, 0)
};

private int currentColorNum = 0; // The currently selected drawing color,
// coded as an index into the above array

private double prevX, prevY; // The previous location of the mouse, when
// the user is drawing by dragging the mouse.

private boolean dragging; // This is set to true while the user is drawing.

private Canvas canvas; // The canvas on which everything is drawn.

private GraphicsContext g; // For drawing on the canvas.


/**
* The start() method creates the GUI, sets up event listening, and
* shows the window on the screen.
*/
public void start(Stage stage) {

/* Create the canvans and draw its content for the first time. */
canvas = new Canvas(600, 400);
g = canvas.getGraphicsContext2D();
clearAndDrawPalette();

/* Respond to mouse events on the canvas, by calling methods in this class. */
canvas.setOnMousePressed(e -> mousePressed(e));
canvas.setOnMouseDragged(e -> mouseDragged(e));
canvas.setOnMouseReleased(e -> mouseReleased(e));

/* Configure the GUI and show the window. */
Pane root = new Pane(canvas);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setResizable(false);
stage.setTitle("Simple Paint");
stage.show();
}


/**
* Fills the canvas with white and draws the color palette and (simulated)
* "Clear" button on the right edge of the canvas. This method is called when
* the canvas is created and when the user clicks "Clear."
*/
public void clearAndDrawPalette() {
int width = (int) canvas.getWidth(); // Width of the canvas.
int height = (int) canvas.getHeight(); // Height of the canvas.

g.setFill(Color.WHITE);
g.fillRect(0, 0, width, height);

int colorSpacing = (height - 56) / 7;
// Distance between the top of one colored rectangle in the palette
// and the top of the rectangle below it. The height of the
// rectangle will be colorSpacing - 3. There are 7 colored rectangles,
// so the available space is divided by 7. The available space allows
// for the gray border and the 50-by-50 CLEAR button.

/* Draw a 3-pixel border around the canvas in gray. This has to be
done by drawing three rectangles of different sizes. */

g.setStroke(Color.GRAY);
g.setLineWidth(3);
g.strokeRect(1.5, 1.5, width - 3, height - 3);

/* Draw a 56-pixel wide gray rectangle along the right edge of the canvas.
The color palette and Clear button will be drawn on top of this.
(This covers some of the same area as the border I just drew. */

g.setFill(Color.GRAY);
g.fillRect(width - 56, 0, 56, height);

/* Draw the "Clear button" as a 50-by-50 white rectangle in the lower right
corner of the canvas, allowing for a 3-pixel border. */

g.setFill(Color.WHITE);
g.fillRect(width - 53, height - 53, 50, 50);
g.setFill(Color.BLACK);
g.fillText("CLEAR", width - 48, height - 23);

/* Draw the seven color rectangles. */

for (int N = 0; N < 7; N++) {
g.setFill(palette[N]);
g.fillRect(width - 53, 3 + N * colorSpacing, 50, colorSpacing - 3);
}

/* Draw a 2-pixel white border around the color rectangle
of the current drawing color. */

g.setStroke(Color.WHITE);
g.setLineWidth(2);
g.strokeRect(width - 54, 2 + currentColorNum * colorSpacing, 52, colorSpacing - 1);

} // end clearAndDrawPalette()


/**
* Change the drawing color after the user has clicked the
* mouse on the color palette at a point with y-coordinate y.
*/
private void changeColor(int y) {

int width = (int) canvas.getWidth();
int height = (int) canvas.getHeight();
int colorSpacing = (height - 56) / 7; // Space for one color rectangle.
int newColor = y / colorSpacing; // Which color number was clicked?

if (newColor < 0 || newColor > 6) // Make sure the color number is valid.
return;

/* Remove the highlight from the current color, by drawing over it in gray.
Then change the current drawing color and draw a highlight around the
new drawing color. */
g.setLineWidth(2);
g.setStroke(Color.GRAY);
g.strokeRect(width - 54, 2 + currentColorNum * colorSpacing, 52, colorSpacing - 1);
currentColorNum = newColor;
g.setStroke(Color.WHITE);
g.strokeRect(width - 54, 2 + currentColorNum * colorSpacing, 52, colorSpacing - 1);

} // end changeColor()


/**
* This is called when the user presses the mouse anywhere in the canvas.
* There are three possible responses, depending on where the user clicked:
* Change the current color, clear the drawing, or start drawing a curve.
* (Or do nothing if user clicks on the border.)
*/
public void mousePressed(MouseEvent evt) {

if (dragging == true) // Ignore mouse presses that occur
return; // when user is already drawing a curve.
// (This can happen if the user presses
// two mouse buttons at the same time.)

int x = (int) evt.getX(); // x-coordinate where the user clicked.
int y = (int) evt.getY(); // y-coordinate where the user clicked.

int width = (int) canvas.getWidth(); // Width of the canvas.
int height = (int) canvas.getHeight(); // Height of the canvas.

if (x > width - 53) {
// User clicked to the right of the drawing area.
// This click is either on the clear button or
// on the color palette.
if (y > height - 53)
clearAndDrawPalette(); // Clicked on "CLEAR button".
else
changeColor(y); // Clicked on the color palette.
} else if (x > 3 && x < width - 56 && y > 3 && y < height - 3) {
// The user has clicked on the white drawing area.
// Start drawing a curve from the point (x,y).
prevX = x;
prevY = y;
dragging = true;
g.setLineWidth(2); // Use a 2-pixel-wide line for drawing.
g.setStroke(palette[currentColorNum]);
}

} // end mousePressed()


/**
* Called whenever the user releases the mouse button. Just sets
* dragging to false.
*/
public void mouseReleased(MouseEvent evt) {
dragging = false;
}


/**
* Called whenever the user moves the mouse while a mouse button is held down.
* If the user is drawing, draw a line segment from the previous mouse location
* to the current mouse location, and set up prevX and prevY for the next call.
* Note that in case the user drags outside of the drawing area, the values of
* x and y are "clamped" to lie within this area. This avoids drawing on the color
* palette or clear button.
*/
public void mouseDragged(MouseEvent evt) {

if (dragging == false)
return; // Nothing to do because the user isn't drawing.

double x = evt.getX(); // x-coordinate of mouse.
double y = evt.getY(); // y-coordinate of mouse.

if (x < 3) // Adjust the value of x,
x = 3; // to make sure it's in
if (x > canvas.getWidth() - 57) // the drawing area.
x = (int) canvas.getWidth() - 57;

if (y < 3) // Adjust the value of y,
y = 3; // to make sure it's in
if (y > canvas.getHeight() - 4) // the drawing area.
y = canvas.getHeight() - 4;

g.strokeLine(prevX, prevY, x, y); // Draw the line.

prevX = x; // Get ready for the next line segment in the curve.
prevY = y;

} // end mouseDragged()
} // end class SimplePaint


The interface version:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
package discussion_assignment;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;


/**
* A simple program where the user can sketch curves in a variety of
* colors. A color palette is shown along the right edge of the canvas.
* The user can select a drawing color by clicking on a color in the
* palette. Under the colors is a "Clear button" that the user
* can click to clear the sketch. The user draws by clicking and
* dragging in a large white area that occupies most of the canvas.
*/
public class SimplePaintInterfaceVersion extends Application {

/**
* This main routine allows this class to be run as a program.
*/
public static void main(String[] args) {
launch(args);
}


/*
* Array of colors corresponding to available colors in the palette.
* (The last color is a slightly darker version of yellow for
* better visibility on a white background.)
*/
private final Color[] palette = {
Color.BLACK, Color.RED, Color.GREEN, Color.BLUE,
Color.CYAN, Color.MAGENTA, Color.color(0.95, 0.9, 0)
};

private int currentColorNum = 0; // The currently selected drawing color,
// coded as an index into the above array

private double prevX, prevY; // The previous location of the mouse, when
// the user is drawing by dragging the mouse.

private boolean dragging; // This is set to true while the user is drawing.

private Canvas canvas; // The canvas on which everything is drawn.

private GraphicsContext g; // For drawing on the canvas.


/**
* The start() method creates the GUI, sets up event listening, and
* shows the window on the screen.
*/
public void start(Stage stage) {

/* Create the canvans and draw its content for the first time. */
canvas = new Canvas(600, 400);
g = canvas.getGraphicsContext2D();
clearAndDrawPalette();

//Create listeners
MouseDraggedListener mouseDraggedListener = new MouseDraggedListener();
MousePressedListener mousePressedListener = new MousePressedListener();
MouseReleasedListener mouseReleasedListener = new MouseReleasedListener();

/* Respond to mouse events on the canvas, by calling methods in this class. */
canvas.setOnMouseDragged(mouseDraggedListener);
canvas.setOnMousePressed(mousePressedListener);
canvas.setOnMouseReleased(mouseReleasedListener);

/* Configure the GUI and show the window. */
Pane root = new Pane(canvas);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setResizable(false);
stage.setTitle("Simple Paint");
stage.show();
}


/**
* Fills the canvas with white and draws the color palette and (simulated)
* "Clear" button on the right edge of the canvas. This method is called when
* the canvas is created and when the user clicks "Clear."
*/
public void clearAndDrawPalette() {

int width = (int) canvas.getWidth(); // Width of the canvas.
int height = (int) canvas.getHeight(); // Height of the canvas.

g.setFill(Color.WHITE);
g.fillRect(0, 0, width, height);

int colorSpacing = (height - 56) / 7;
// Distance between the top of one colored rectangle in the palette
// and the top of the rectangle below it. The height of the
// rectangle will be colorSpacing - 3. There are 7 colored rectangles,
// so the available space is divided by 7. The available space allows
// for the gray border and the 50-by-50 CLEAR button.

/* Draw a 3-pixel border around the canvas in gray. This has to be
done by drawing three rectangles of different sizes. */

g.setStroke(Color.GRAY);
g.setLineWidth(3);
g.strokeRect(1.5, 1.5, width - 3, height - 3);

/* Draw a 56-pixel wide gray rectangle along the right edge of the canvas.
The color palette and Clear button will be drawn on top of this.
(This covers some of the same area as the border I just drew. */

g.setFill(Color.GRAY);
g.fillRect(width - 56, 0, 56, height);

/* Draw the "Clear button" as a 50-by-50 white rectangle in the lower right
corner of the canvas, allowing for a 3-pixel border. */

g.setFill(Color.WHITE);
g.fillRect(width - 53, height - 53, 50, 50);
g.setFill(Color.BLACK);
g.fillText("CLEAR", width - 48, height - 23);

/* Draw the seven color rectangles. */

for (int N = 0; N < 7; N++) {
g.setFill(palette[N]);
g.fillRect(width - 53, 3 + N * colorSpacing, 50, colorSpacing - 3);
}

/* Draw a 2-pixel white border around the color rectangle
of the current drawing color. */

g.setStroke(Color.WHITE);
g.setLineWidth(2);
g.strokeRect(width - 54, 2 + currentColorNum * colorSpacing, 52, colorSpacing - 1);

} // end clearAndDrawPalette()


/**
* Change the drawing color after the user has clicked the
* mouse on the color palette at a point with y-coordinate y.
*/
private void changeColor(int y) {

int width = (int) canvas.getWidth();
int height = (int) canvas.getHeight();
int colorSpacing = (height - 56) / 7; // Space for one color rectangle.
int newColor = y / colorSpacing; // Which color number was clicked?

if (newColor < 0 || newColor > 6) // Make sure the color number is valid.
return;

/* Remove the highlight from the current color, by drawing over it in gray.
Then change the current drawing color and draw a highlight around the
new drawing color. */

g.setLineWidth(2);
g.setStroke(Color.GRAY);
g.strokeRect(width - 54, 2 + currentColorNum * colorSpacing, 52, colorSpacing - 1);
currentColorNum = newColor;
g.setStroke(Color.WHITE);
g.strokeRect(width - 54, 2 + currentColorNum * colorSpacing, 52, colorSpacing - 1);

} // end changeColor()

private class MousePressedListener implements EventHandler<MouseEvent>{

@Override
public void handle(MouseEvent mouseEvent) {
if (dragging == true) // Ignore mouse presses that occur
return; // when user is already drawing a curve.
// (This can happen if the user presses
// two mouse buttons at the same time.)

int x = (int) mouseEvent.getX(); // x-coordinate where the user clicked.
int y = (int) mouseEvent.getY(); // y-coordinate where the user clicked.

int width = (int) canvas.getWidth(); // Width of the canvas.
int height = (int) canvas.getHeight(); // Height of the canvas.

if (x > width - 53) {
// User clicked to the right of the drawing area.
// This click is either on the clear button or
// on the color palette.
if (y > height - 53)
clearAndDrawPalette(); // Clicked on "CLEAR button".
else
changeColor(y); // Clicked on the color palette.
} else if (x > 3 && x < width - 56 && y > 3 && y < height - 3) {
// The user has clicked on the white drawing area.
// Start drawing a curve from the point (x,y).
prevX = x;
prevY = y;
dragging = true;
g.setLineWidth(2); // Use a 2-pixel-wide line for drawing.
g.setStroke(palette[currentColorNum]);
}
}
}


private class MouseReleasedListener implements EventHandler<MouseEvent>{

@Override
public void handle(MouseEvent mouseEvent) {
dragging = false;
}
}

private class MouseDraggedListener implements EventHandler<MouseEvent>{

@Override
public void handle(MouseEvent e) {
if (dragging == false)
return; // Nothing to do because the user isn't drawing.

double x = e.getX(); // x-coordinate of mouse.
double y = e.getY(); // y-coordinate of mouse.

if (x < 3) // Adjust the value of x,
x = 3; // to make sure it's in
if (x > canvas.getWidth() - 57) // the drawing area.
x = (int) canvas.getWidth() - 57;

if (y < 3) // Adjust the value of y,
y = 3; // to make sure it's in
if (y > canvas.getHeight() - 4) // the drawing area.
y = canvas.getHeight() - 4;

g.strokeLine(prevX, prevY, x, y); // Draw the line.

prevX = x; // Get ready for the next line segment in the curve.
prevY = y;
}
}

} // end class SimplePaint

References

1.Eck, D. J. (2019). Introduction to Programming Using Java.
http://math.hws.edu/javanotes

​


This is the ending, thanks for reading.