Passed
Push — master ( 94678b...4033fa )
by Seth
03:21
created

org.gannacademy.cdf.graphics.ui.AppWindow.Animator   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 14
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
rs 10
eloc 9
wmc 3

2 Methods

Rating   Name   Duplication   Size   Complexity  
A Animator(AppWindow) 0 2 1
A doInBackground() 0 6 2
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">Seth Battis</a>
38
 */
39
public abstract class AppWindow extends JFrame {
40
    public static final String DEFAULT_TITLE = "Gann Graphics App";
41
42
    private DrawingPanel drawingPanel;
43
    private boolean threadStarted = false;
0 ignored issues
show
Unused Code introduced by
Consider removing the unused private field threadStarted.
Loading history...
44
45
    /**
46
     * Construct a new {@link DrawingPanel} in a window with the default title
47
     */
48
    public AppWindow() {
49
        this(DEFAULT_TITLE, false);
50
    }
51
52
    /**
53
     * Construct a new {@link DrawingPanel} in a window with a custom name
54
     *
55
     * @param title The title of the window (shown in the draggable titlebar)
56
     */
57
    public AppWindow(String title) {
58
        this(title, false);
59
    }
60
61
    /**
62
     * <p>Construct a new {@link DrawingPanel} in a window</p>
63
     *
64
     * <p>Full screen windows default to the main display.</p>
65
     *
66
     * @param title        for the window
67
     * @param isFullScreen whether or not the window is framed or full screen
68
     */
69
    public AppWindow(String title, boolean isFullScreen) {
70
        super(title);
71
        AppWindow self = this;
72
        SwingUtilities.invokeLater(new Runnable() {
73
            @Override
74
            public void run() {
75
                setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
76
                drawingPanel = new DrawingPanel();
77
                add(drawingPanel);
78
                if (isFullScreen) {
79
                    setExtendedState(JFrame.MAXIMIZED_BOTH);
80
                    setUndecorated(true);
81
                    DisplayMode display = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0].getDisplayMode();
82
                    setSize(display.getWidth(), display.getHeight());
83
                }
84
                pack();
85
                setup();
86
                setLocationRelativeTo(null);
87
                setVisible(true);
88
                (new Repainter(self)).execute();
89
                (new Animator(self)).execute();
90
            }
91
        });
92
    }
93
94
    private static class Animator extends SwingWorker<Object,Object> {
95
96
        AppWindow window;
97
98
        Animator(AppWindow window) {
99
            this.window = window;
100
        }
101
102
        @Override
103
        protected Object doInBackground() throws Exception {
104
            while(!window.done()) {
105
                window.loop();
106
            }
107
            return null;
108
        }
109
    }
110
111
    private static class Repainter extends SwingWorker<Object,Object> {
112
        AppWindow window;
113
114
        Repainter(AppWindow window) {
115
            this.window = window;
116
        }
117
118
        @Override
119
        protected Object doInBackground() throws Exception {
120
            while (true) {
0 ignored issues
show
introduced by
Add an end condition to this loop.
Loading history...
121
                SwingUtilities.invokeLater(new Runnable() {
122
                    @Override
123
                    public void run() {
124
                        window.repaint();
125
                    }
126
                });
127
                Thread.sleep(10);
128
            }
129
        }
130
    }
131
    /**
132
     * Set the size of the window
133
     *
134
     * @param width  in pixels
135
     * @param height in pixels
136
     */
137
    public void setSize(int width, int height) {
138
        drawingPanel.setPreferredSize(new Dimension(width, height));
139
        pack();
140
        setLocationRelativeTo(null);
141
    }
142
143
    /**
144
     * Pause the control loop
145
     *
146
     * @param delay in milliseconds
147
     */
148
    protected void sleep(long delay) {
149
        try {
150
            Thread.sleep(delay);
151
        } catch (InterruptedException e) {
0 ignored issues
show
introduced by
Either re-interrupt this method or rethrow the "InterruptedException".
Loading history...
152
            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...
153
        }
154
    }
155
156
    /**
157
     * <p>Override this method to define your drawing</p>
158
     *
159
     * <p>This method is called before the window is made visible. All drawing components that need to be rendered at the
160
     * start of the program run should be defined in this method.</p>
161
     */
162
    protected abstract void setup();
163
164
    /**
165
     * <p>Override this method to update drawings in the control loop</p>
166
     *
167
     * <p>This method represents a <i>single</i> iteration of the control loop. Care should be taken to avoid writing
168
     * blocking code in this method (e.g. {@code for} loops designed to animate a single object) &mdash; rather, drawing
169
     * components should be adjusted based on the values of instance variables, whose values are changed incrementally
170
     * in each call to this method. For example:</p>
171
     *
172
     * <pre>
173
     *   private double x, y;
174
     *   private Rectangle r;
175
     *
176
     *   // setup() and other methods&hellip;
177
     *
178
     *   &#064;Override
179
     *   public void loop() {
180
     *     x += 1;
181
     *     y = 20 * Math.sin(x * 20);
182
     *     r.moveTo(x, y);
183
     *   }
184
     * </pre>
185
     *
186
     * <p>The above code animates a rectangle moving elegantly in a sine wave (scaled up 20&times;)across the window.</p>
187
     */
188
    protected void loop() {
189
    }
190
191
    /**
192
     * <p>Override this method to set the condition that ends the control loop</p>
193
     *
194
     * <p>By default, this method will always return {@code false}, so that the control loop runs indefinitely.</p>
195
     *
196
     * @return {@code true} if the control loop should end, {@code false} otherwise
197
     */
198
    protected boolean done() {
199
        return false;
200
    }
201
202
    /**
203
     * <p>Access the {@link DrawingPanel} object contained in this window</p>
204
     *
205
     * <p>All drawing instructions require a drawing panel on which to execute them. This is the default drawing panel.</p>
206
     *
207
     * @return The drawing panel in this window
208
     */
209
    public DrawingPanel getDrawingPanel() {
210
        return drawingPanel;
211
    }
212
}
213