setSize(int,int)   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 4
rs 10
eloc 4
1
package org.gannacademy.cdf.graphics.ui;
2
3
import javax.swing.*;
4
import java.awt.*;
5
6
/**
7
 * <p>Extendable window controller for {@link DrawingPanel} objects</p>
8
 *
9
 * <p>Extend this class to create your own drawings. Override the abstract method {@link #setup()} to make your drawing
10
 * instructions. The recommended approach is to create a new Java class file and to type exactly this (replacing
11
 * {@code MyDrawingApp} with your preferred name, of course):</p>
12
 *
13
 * <pre>
14
 *   public class MyDrawingApp extends AppWindow {}
15
 * </pre>
16
 *
17
 * <p>This will generate a number of errors in your IDE. Go ahead and let the IDE fix them automagically. The IDE will
18
 * import the AppWindow class, and then create a method stub for {@code setup()}, resulting in something like this:</p>
19
 *
20
 * <pre>
21
 *   import org.gannacademy.cdf.graphics.ui.*;
22
 *
23
 *   public class MyDrawingApp extends AppWindow {
24
 *     &#064;Override
25
 *     public void setup() {
26
 *
27
 *     }
28
 *   }
29
 * </pre>
30
 *
31
 * <p>Instantiate a {@code MyDrawingApp} object to create a window containing your drawing.</p>
32
 *
33
 * <p>Additional overrideable methods include {@link #loop()} and {@link #done()}. {@code loop()} will be
34
 * called repeatedly (inside a loop!) while {@code done()} returns {@code false} &mdash; when {@code done()} returns
35
 * {@code true}, the control loop ends (although the application will continue running until the window is closed).</p>
36
 *
37
 * @author <a href="https://github.com/gann-cdf/graphics/issues" target="_blank">Seth Battis</a>
38
 */
39
public abstract class AppWindow extends JFrame {
40
    public static final String DEFAULT_TITLE = "Gann Graphics App";
41
    public static final boolean DEFAULT_FULLSCREEN = false;
42
    public static final long DEFAULT_REPAINT_DELAY = 10;
43
44
    private DrawingPanel drawingPanel;
45
    private boolean threadStarted = false;
0 ignored issues
show
Unused Code introduced by
Consider removing the unused private field threadStarted.
Loading history...
46
47
    /**
48
     * Construct a new {@link DrawingPanel} in a window with the default title
49
     */
50
    public AppWindow() {
51
        this(DEFAULT_TITLE, DEFAULT_FULLSCREEN, DEFAULT_REPAINT_DELAY);
52
    }
53
54
    /**
55
     * Construct a new {@link DrawingPanel} in a window with a custom name
56
     *
57
     * @param title The title of the window (shown in the draggable titlebar)
58
     */
59
    public AppWindow(String title) {
60
        this(title, DEFAULT_FULLSCREEN, DEFAULT_REPAINT_DELAY);
61
    }
62
63
    public AppWindow(String title, boolean isFullScreen) {
64
        this(title, isFullScreen, DEFAULT_REPAINT_DELAY);
65
    }
66
67
    /**
68
     * <p>Construct a new {@link DrawingPanel} in a window</p>
69
     *
70
     * <p>Full screen windows default to the main display.</p>
71
     *
72
     * @param title        for the window
73
     * @param isFullScreen whether or not the window is framed or full screen
74
     */
75
    public AppWindow(String title, boolean isFullScreen, long repaintDelay) {
76
        super(title);
77
        AppWindow self = this;
78
        SwingUtilities.invokeLater(new Runnable() {
79
            @Override
80
            public void run() {
81
                setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
82
                drawingPanel = new DrawingPanel();
83
                add(drawingPanel);
84
                if (isFullScreen) {
85
                    setExtendedState(JFrame.MAXIMIZED_BOTH);
86
                    setUndecorated(true);
87
                    DisplayMode display = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0].getDisplayMode();
88
                    setSize(display.getWidth(), display.getHeight());
89
                }
90
                pack();
91
                setup();
92
                setLocationRelativeTo(null);
93
                setVisible(true);
94
                (new Repainter(self, repaintDelay)).execute();
95
                (new Animator(self)).execute();
96
            }
97
        });
98
    }
99
100
    private void repaintOnEDT() {
101
        SwingUtilities.invokeLater(new Runnable() {
102
            @Override
103
            public void run() {
104
                repaint();
105
            }
106
        });
107
    }
108
109
    private static class Animator extends SwingWorker<Void,Void> {
110
111
        AppWindow window;
112
113
        Animator(AppWindow window) {
114
            this.window = window;
115
        }
116
117
        @Override
118
        protected Void doInBackground() throws Exception {
119
            while(!window.done()) {
120
                window.loop();
121
                window.repaintOnEDT();
122
            }
123
            return null;
124
        }
125
    }
126
127
    private static class Repainter extends SwingWorker<Void,Void> {
128
        AppWindow window;
129
        long delay;
130
131
        Repainter(AppWindow window, long delay) {
132
            this.window = window;
133
            this.delay = Math.max(1, delay);
134
        }
135
136
        @Override
137
        protected Void doInBackground() throws Exception {
138
            //noinspection InfiniteLoopStatement
139
            while (true) {
0 ignored issues
show
introduced by
Add an end condition to this loop.
Loading history...
140
                window.repaintOnEDT();
141
                //noinspection BusyWait
142
                Thread.sleep(delay);
143
            }
144
        }
145
    }
146
    /**
147
     * Set the size of the window
148
     *
149
     * @param width  in pixels
150
     * @param height in pixels
151
     */
152
    public void setSize(int width, int height) {
153
        drawingPanel.setPreferredSize(new Dimension(width, height));
154
        pack();
155
        setLocationRelativeTo(null);
156
    }
157
158
    /**
159
     * Pause the control loop
160
     *
161
     * @param delay in milliseconds
162
     */
163
    protected void sleep(long delay) {
164
        try {
165
            Thread.sleep(delay);
166
        } catch (InterruptedException e) {
0 ignored issues
show
introduced by
Either re-interrupt this method or rethrow the "InterruptedException".
Loading history...
167
            e.printStackTrace();
0 ignored issues
show
Best Practice introduced by
Throwable.printStackTrace writes to the console which might not be available at runtime. Using a logger is preferred.
Loading history...
168
        }
169
    }
170
171
    /**
172
     * <p>Override this method to define your drawing</p>
173
     *
174
     * <p>This method is called before the window is made visible. All drawing components that need to be rendered at the
175
     * start of the program run should be defined in this method.</p>
176
     */
177
    protected abstract void setup();
178
179
    /**
180
     * <p>Override this method to update drawings in the control loop</p>
181
     *
182
     * <p>This method represents a <i>single</i> iteration of the control loop. Care should be taken to avoid writing
183
     * blocking code in this method (e.g. {@code for} loops designed to animate a single object) &mdash; rather, drawing
184
     * components should be adjusted based on the values of instance variables, whose values are changed incrementally
185
     * in each call to this method. For example:</p>
186
     *
187
     * <pre>
188
     *   private double x, y;
189
     *   private Rectangle r;
190
     *
191
     *   // setup() and other methods&hellip;
192
     *
193
     *   &#064;Override
194
     *   public void loop() {
195
     *     x += 1;
196
     *     y = 20 * Math.sin(x * 20);
197
     *     r.moveTo(x, y);
198
     *   }
199
     * </pre>
200
     *
201
     * <p>The above code animates a rectangle moving elegantly in a sine wave (scaled up 20&times;)across the window.</p>
202
     */
203
    protected void loop() {
204
    }
205
206
    /**
207
     * <p>Override this method to set the condition that ends the control loop</p>
208
     *
209
     * <p>By default, this method will always return {@code false}, so that the control loop runs indefinitely.</p>
210
     *
211
     * @return {@code true} if the control loop should end, {@code false} otherwise
212
     */
213
    protected boolean done() {
214
        return false;
215
    }
216
217
    /**
218
     * <p>Access the {@link DrawingPanel} object contained in this window</p>
219
     *
220
     * <p>All drawing instructions require a drawing panel on which to execute them. This is the default drawing panel.</p>
221
     *
222
     * @return The drawing panel in this window
223
     */
224
    public DrawingPanel getDrawingPanel() {
225
        return drawingPanel;
226
    }
227
}
228