88import java .awt .event .MouseEvent ;
99import java .awt .event .WindowAdapter ;
1010import java .awt .event .WindowEvent ;
11+ import javax .swing .Box ;
12+ import javax .swing .JButton ;
1113import javax .swing .JFrame ;
14+ import javax .swing .JLabel ;
15+ import javax .swing .JMenu ;
16+ import javax .swing .JMenuBar ;
17+ import javax .swing .JMenuItem ;
18+ import javax .swing .JPanel ;
19+ import javax .swing .KeyStroke ;
1220import javax .swing .SwingUtilities ;
1321import javax .swing .Timer ;
1422import jtamaro .graphic .Graphic ;
1523import jtamaro .graphic .GuiGraphicCanvas ;
24+ import jtamaro .io .IO ;
1625
1726/**
1827 * GUI that allows the user to interact with an {@link Interaction}.
1928 */
2029final class InteractionFrame <M > extends JFrame {
2130
31+ static {
32+ // Use system menu bar on macOS
33+ System .setProperty ("apple.laf.useScreenMenuBar" , "true" );
34+ }
35+
2236 private final Interaction <M > interaction ;
2337
2438 private final InteractionState <M > state ;
@@ -27,6 +41,12 @@ final class InteractionFrame<M> extends JFrame {
2741
2842 private final Timer timer ;
2943
44+ private final JMenuItem startStopItem ;
45+
46+ private final JButton startStopButton ;
47+
48+ private final JLabel tickLabel ;
49+
3050 private final GuiGraphicCanvas graphicCanvas ;
3151
3252 public InteractionFrame (Interaction <M > interaction ) {
@@ -36,14 +56,66 @@ public InteractionFrame(Interaction<M> interaction) {
3656 this .timer = new Timer (interaction .getMsBetweenTicks (), this ::onTick );
3757
3858 setTitle (interaction .getName ());
59+ setDefaultCloseOperation (JFrame .DISPOSE_ON_CLOSE );
60+ addWindowListener (new WindowAdapter () {
61+ @ Override
62+ public void windowClosing (WindowEvent winEvt ) {
63+ // No need to call the stopTimer() method, we don't need
64+ // to update UI components at this point.
65+ timer .stop ();
66+ }
67+ });
68+
69+ // Menu bar
70+ final JMenuBar menuBar = new JMenuBar ();
71+ setJMenuBar (menuBar );
72+
73+ final JMenu fileMenu = new JMenu ("File" );
74+ fileMenu .setMnemonic (KeyEvent .VK_F );
75+ menuBar .add (fileMenu );
76+
77+ startStopItem = new JMenuItem ();
78+ startStopItem .setAccelerator (KeyStroke .getKeyStroke (KeyEvent .VK_F8 , 0 ));
79+ startStopItem .addActionListener (e -> toggleTimer ());
80+ fileMenu .add (startStopItem );
81+
82+ final JMenuItem viewFrame = new JMenuItem ("Inspect Frame" );
83+ viewFrame .setAccelerator (KeyStroke .getKeyStroke (KeyEvent .VK_F1 , 0 ));
84+ viewFrame .addActionListener (e -> SwingUtilities .invokeLater (() -> {
85+ stopTimer ();
86+ IO .show (interaction .getRenderer ().apply (state .getModel ()));
87+ }));
88+ fileMenu .add (viewFrame );
89+
90+ final JMenu viewMenu = new JMenu ("View" );
91+ viewMenu .setMnemonic (KeyEvent .VK_V );
92+ menuBar .add (viewMenu );
93+
94+ final JMenuItem viewTrace = new JMenuItem ("Trace" );
95+ viewTrace .setAccelerator (KeyStroke .getKeyStroke (KeyEvent .VK_F2 , 0 ));
96+ viewTrace .addActionListener (e -> SwingUtilities .invokeLater (() ->
97+ new TraceFrame (eventsTrace ).setVisible (true )));
98+ viewMenu .add (viewTrace );
99+
100+ final JMenuItem viewModel = new JMenuItem ("Model" );
101+ viewModel .setAccelerator (KeyStroke .getKeyStroke (KeyEvent .VK_F3 , 0 ));
102+ viewModel .addActionListener (e -> SwingUtilities .invokeLater (() ->
103+ new ModelFrame <>(interaction , eventsTrace ).setVisible (true )));
104+ viewMenu .add (viewModel );
39105
40106 // Toolbar
41- final InteractionToolbar <M > toolbar = new InteractionToolbar <>(interaction ,
42- state ,
43- timer ,
44- eventsTrace );
107+ final JPanel toolbar = new JPanel ();
45108 add (toolbar , BorderLayout .NORTH );
46109
110+ tickLabel = new JLabel ();
111+ toolbar .add (tickLabel , BorderLayout .WEST );
112+
113+ toolbar .add (Box .createHorizontalStrut (20 ));
114+
115+ startStopButton = new JButton ();
116+ startStopButton .addActionListener (e -> toggleTimer ());
117+ toolbar .add (startStopButton , BorderLayout .EAST );
118+
47119 // Canvas component
48120 int canvasWidth = interaction .getCanvasWidth ();
49121 int canvasHeight = interaction .getCanvasHeight ();
@@ -104,14 +176,6 @@ public void mouseDragged(MouseEvent ev) {
104176 traceEvent (new TraceEvent .MouseMoved (coordinate ));
105177 }
106178 });
107-
108- setDefaultCloseOperation (JFrame .DISPOSE_ON_CLOSE );
109- addWindowListener (new WindowAdapter () {
110- @ Override
111- public void windowClosing (WindowEvent winEvt ) {
112- timer .stop ();
113- }
114- });
115179 add (graphicCanvas , BorderLayout .CENTER );
116180
117181 pack ();
@@ -122,18 +186,47 @@ public void windowClosing(WindowEvent winEvt) {
122186 graphicCanvas .requestFocus ();
123187
124188 // Start the timer
125- timer . start ();
189+ startTimer ();
126190 // The timer is now running, configure the toolbar buttons
127- toolbar . configureButtons ( );
191+ viewFrame . setEnabled ( true );
128192 });
129193 }
130194
195+ private void toggleTimer () {
196+ if (timer .isRunning ()) {
197+ stopTimer ();
198+ } else {
199+ startTimer ();
200+ }
201+ }
202+
203+ private void stopTimer () {
204+ if (!timer .isRunning ()) {
205+ return ;
206+ }
207+
208+ startStopItem .setText ("Start" );
209+ startStopButton .setText ("Start" );
210+ timer .stop ();
211+ }
212+
213+ private void startTimer () {
214+ if (timer .isRunning ()) {
215+ return ;
216+ }
217+
218+ startStopItem .setText ("Stop" );
219+ startStopButton .setText ("Stop" );
220+ timer .start ();
221+ }
222+
131223 private void onTick (ActionEvent ev ) {
132224 if (interaction .getStoppingPredicate ().apply (state .getModel ())) {
133- timer . stop ();
225+ stopTimer ();
134226 } else {
135227 traceEvent (new TraceEvent .Tick ());
136228 state .tick ();
229+ tickLabel .setText (String .valueOf (state .getTick ()));
137230 }
138231 }
139232
0 commit comments