[skycastle-commits] SF.net SVN: skycastle: [484] trunk/skycastle/modules

  • From: zzorn@xxxxxxxxxxxxxxxxxxxxx
  • To: skycastle-commits@xxxxxxxxxxxxx
  • Date: Sat, 26 Apr 2008 13:42:12 -0700

Revision: 484
          http://skycastle.svn.sourceforge.net/skycastle/?rev=484&view=rev
Author:   zzorn
Date:     2008-04-26 13:42:11 -0700 (Sat, 26 Apr 2008)

Log Message:
-----------
Added closing handling to SimpleFrame, and hooked up the view to the model 
preliminary, as well as implemented a test action that adds a random stroke.  
Works nicely, next is putting back better navigation actions, stroke input, and 
smaller view updates when the model is updated.

Modified Paths:
--------------
    
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchView.java
    
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SkycastleSketch.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandAction.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/simpleui/SimpleFrame.java

Added Paths:
-----------
    
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchController.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/simpleui/CloseHandler.java

Added: 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchController.java
===================================================================
--- 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchController.java
                         (rev 0)
+++ 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchController.java
 2008-04-26 20:42:11 UTC (rev 484)
@@ -0,0 +1,156 @@
+package org.skycastle.sketch;
+
+import org.skycastle.sketch.model.Sketch;
+import org.skycastle.sketch.model.SketchImpl;
+import org.skycastle.sketch.model.point.StrokePointImpl;
+import org.skycastle.sketch.model.stroke.Stroke;
+import org.skycastle.sketch.model.stroke.StrokeImpl;
+import org.skycastle.util.command.*;
+
+import javax.swing.*;
+import java.util.Random;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Hans Häggström
+ */
+@SuppressWarnings( { "serial" } )
+public class SketchController
+{
+
+    //======================================================================
+    // Private Fields
+
+    private final CommandStack myCommandStack = new CommandStackImpl();
+
+
+    private final CommandAction myExitAction = new CommandAction( 
myCommandStack,
+                                                                  "Exit",
+                                                                  "Exit the 
program and loose any changes made" )
+    {
+
+        @Override
+        protected Command createCommand()
+        {
+            return new AbstractCommand( "Request Exit", false )
+            {
+
+                public void doCommand()
+                {
+                    final int selection = JOptionPane.showConfirmDialog( 
mySketchView.getMainFrame(),
+                                                                         "Are 
you sure you want to exit " + APP_NAME + "?",
+                                                                         
"Confirm Exit",
+                                                                         
JOptionPane.YES_NO_OPTION );
+                    if ( selection == JOptionPane.YES_OPTION )
+                    {
+                        // TODO: Close down the UI somehow gracefully?
+
+                        //noinspection CallToSystemExit
+                        System.exit( 0 );
+                    }
+                }
+
+            };
+        }
+
+    };
+
+    private SketchView mySketchView;
+
+    private Sketch mySketch;
+
+    //======================================================================
+    // Private Constants
+
+    private static final String APP_NAME = "Sketch Room";
+    private final CommandAction myAddTestStrokeAction = new CommandAction( 
myCommandStack,
+                                                                           
"Test Stroke",
+                                                                           
"Adds a random test stroke to the Sketch" )
+    {
+        @Override
+        protected Command createCommand()
+        {
+            final Stroke testStroke = createTestStroke();
+
+            return new AbstractCommand( "Test Stroke", true )
+            {
+
+                public void doCommand()
+                {
+                    mySketch.getRootGroup().add( testStroke );
+                }
+
+                @Override
+                public void undoCommand()
+                {
+                    mySketch.getRootGroup().remove( testStroke );
+                }
+            };
+        }
+    };
+
+    //======================================================================
+    // Public Methods
+
+    //----------------------------------------------------------------------
+    // Constructors
+
+    private static final Logger LOGGER = Logger.getLogger( 
SketchController.class.getName() );
+
+
+    /**
+     * Creates a new {@link org.skycastle.sketch.SketchController}.
+     */
+    public SketchController()
+    {
+        LOGGER.log( Level.INFO, APP_NAME + " starting up." );
+
+        // Create model
+        mySketch = new SketchImpl();
+    }
+
+    //----------------------------------------------------------------------
+    // Other Public Methods
+
+    /**
+     * Creates and opens the UI.
+     */
+    void start()
+    {
+        mySketchView = new SketchView( APP_NAME );
+
+        mySketchView.setCloseAction( myExitAction );
+
+        mySketchView.addMenuAction( "File", myExitAction );
+        mySketchView.addMenuAction( "Edit", myCommandStack.getUndoAction() );
+        mySketchView.addMenuAction( "Edit", myCommandStack.getRedoAction() );
+        mySketchView.addMenuAction( "Debug", myAddTestStrokeAction );
+
+        mySketchView.addToolbarAction( myAddTestStrokeAction );
+        mySketchView.addToolbarAction( myCommandStack.getUndoAction() );
+        mySketchView.addToolbarAction( myCommandStack.getRedoAction() );
+
+        mySketchView.setSketch( mySketch );
+    }
+
+
+    private Stroke createTestStroke()
+    {
+        final StrokeImpl stroke = new StrokeImpl();
+
+        final Random random = new Random();
+        float x = (float) ( random.nextGaussian() * 100 );
+        float y = (float) ( random.nextGaussian() * 100 );
+
+        for ( int i = 0; i < 100; i++ )
+        {
+            x += random.nextGaussian() * 10;
+            y += random.nextGaussian() * 10;
+
+            stroke.addPoint( new StrokePointImpl( x, y ) );
+        }
+
+        return stroke;
+    }
+}

Modified: 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchView.java
===================================================================
--- 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchView.java   
    2008-04-26 19:14:45 UTC (rev 483)
+++ 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchView.java   
    2008-04-26 20:42:11 UTC (rev 484)
@@ -1,11 +1,26 @@
 package org.skycastle.sketch;
 
+import com.jme.scene.Node;
 import org.skycastle.opengl.Canvas3D;
+import org.skycastle.opengl.navigationgestures.NavigationGesture;
+import org.skycastle.sketch.model.Sketch;
+import org.skycastle.sketch.model.group.Group;
+import org.skycastle.sketch.model.group.GroupElement;
+import org.skycastle.sketch.model.stroke.Stroke;
+import org.skycastle.util.ParameterChecker;
+import org.skycastle.util.listenable.collection.CollectionListener;
+import org.skycastle.util.listenable.collection.ListenableCollection;
+import org.skycastle.util.simpleui.CloseHandler;
 import org.skycastle.util.simpleui.SimpleFrame;
 
 import javax.swing.*;
-import java.awt.BorderLayout;
-import java.awt.Component;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * A view, which presents the 3D view of the sketch, and provides input events.
@@ -13,49 +28,246 @@
  * @author Hans Häggström
  */
 // TODO: Have both a navigation stack and an undo stack, that are independent  
-public class SketchView
+public final class SketchView
 {
-    private Component myUiComponent;
+
+    //======================================================================
+    // Private Fields
+
+    private final Map<String, Menu> myMenus = new HashMap<String, Menu>( 10 );
+    private final SimpleFrame myMainFrame;
+
     private Canvas3D myCanvas3D;
     private JToolBar myToolBar;
+    private MenuBar myMenuBar;
+    private Sketch mySketch;
+    private final Node myRoot3DNode;
 
+    //======================================================================
+    // Public Methods
+
+    //----------------------------------------------------------------------
+    // Constructors
+
     /**
      * Creates a new {@link org.skycastle.sketch.SketchView}.
      */
-    public SketchView()
+    public SketchView( String applicationName )
     {
+        ParameterChecker.checkNotNull( applicationName, "applicationName" );
 
         myCanvas3D = new Canvas3D();
+        myMenuBar = new MenuBar();
+        myToolBar = new JToolBar();
 
+        myCanvas3D.removeAllNavigationGestures();
+        myRoot3DNode = new Node( "SketchView_RootNode" );
+        myCanvas3D.set3DNode( myRoot3DNode );
 
         final JPanel panel = new JPanel( new BorderLayout() );
         panel.add( myCanvas3D.get3DView(), BorderLayout.CENTER );
-        myToolBar = new JToolBar();
         panel.add( myToolBar, BorderLayout.NORTH );
 
+/*
+        // Wrap toolbar in a border that gives it a bit of marigin on top, as 
the AWT menu overlaps it otherwsie.
+        myToolBar.setBorder( BorderFactory.createCompoundBorder( 
BorderFactory.createMatteBorder( 0, 0, 0, 0, new Color( 128, 128,128)),
+                                                                 
myToolBar.getBorder() ));
+*/
 
-        new SimpleFrame( panel );
+        myMainFrame = new SimpleFrame( panel );
+        myMainFrame.setMenuBar( myMenuBar );
+        myMainFrame.setTitle( applicationName );
 
 /*
         final StrokeRenderer strokeRenderer = new StrokeRenderer( 
createTestStroke() );
         myCanvas3D.set3DNode( strokeRenderer );
 */
+    }
 
+    //----------------------------------------------------------------------
+    // Other Public Methods
+
+    /**
+     * @param closeAction an action that is called when the close button of 
the main window is pressed, or the
+     *                    application is attempted to be closed in some other 
way.
+     */
+    public void setCloseAction( final Action closeAction )
+    {
+        ParameterChecker.checkNotNull( closeAction, "closeAction" );
+
+        myMainFrame.setCloseHandler( new CloseHandler()
+        {
+
+            public void onCloseButtonPressed( final Frame 
frameWithPressedButton )
+            {
+                closeAction.actionPerformed( null );
+            }
+
+        } );
     }
 
+
     /**
-     * An action / command in the application.
-     *
-     * @param action
+     * @return the frame for the application.
      */
-    void addAction( Action action )
+    public Frame getMainFrame()
     {
+        return myMainFrame;
+    }
 
+
+    /**
+     * @param action an action to add to the toolbar.
+     */
+    public void addToolbarAction( final Action action )
+    {
+        myToolBar.add( action );
     }
 
 
-    public Component getUiComponent()
+    /**
+     * @param menuName the menu to add the action to.
+     * @param action   an action to add to the menu.
+     */
+    public void addMenuAction( final String menuName, final Action action )
     {
-        return myUiComponent;
+        // We need to use old style heavy AWT components, to render on top of 
the OpenGL surface.
+        // So we do a lot of adaption here, rahter than expose any clunky 
interface.
+        // The drawback is that it looks horrible, and overlaps the toolbar on 
50% of the startups.
+        // TODO: Do a BannerBar in the style of Office 2007 instead of a 
separate menu and toolbar?
+        // It could maybe be a component that supports dockable / added 
panels/palettes too, for different tools.
+
+        final MenuItem menuItem = new MenuItem( action.getValue( Action.NAME 
).toString() );
+        menuItem.setEnabled( action.isEnabled() );
+
+        // Listen to menu invocation
+        menuItem.addActionListener( new ActionListener()
+        {
+
+            public void actionPerformed( final ActionEvent e )
+            {
+                action.actionPerformed( e );
+            }
+
+        } );
+
+        // Listen to action enabled state changes
+        action.addPropertyChangeListener( new PropertyChangeListener()
+        {
+
+            public void propertyChange( final PropertyChangeEvent evt )
+            {
+                if ( "enabled".equals( evt.getPropertyName() ) )
+                {
+                    menuItem.setEnabled( action.isEnabled() );
+                }
+            }
+
+        } );
+
+        // Add to menu
+        getOrCreateMenu( menuName ).add( menuItem );
     }
+
+
+    /**
+     * @param addedNavigationGesture a navigation gesture that recieves mouse 
and pen input from the 3D work
+     *                               area.
+     */
+    public void addNavigationGesture( final NavigationGesture 
addedNavigationGesture )
+    {
+        myCanvas3D.addNavigationGesture( addedNavigationGesture );
+    }
+
+
+    /**
+     * @param removedNavigationGesture the listener to remove.
+     */
+    public void removeNavigationGesture( final NavigationGesture 
removedNavigationGesture )
+    {
+        myCanvas3D.removeNavigationGesture( removedNavigationGesture );
+    }
+
+
+    /**
+     * @return the currently visible {@link Sketch}.
+     */
+    public Sketch getSketch()
+    {
+        return mySketch;
+    }
+
+
+    /**
+     * @param sketch the {@link Sketch} to show in the {@link SketchView}.
+     */
+    public void setSketch( final Sketch sketch )
+    {
+        ParameterChecker.checkNotNull( sketch, "sketch" );
+
+        mySketch = sketch;
+
+        reRender( sketch );
+
+        sketch.getRootGroup().addGroupListener( new 
CollectionListener<GroupElement>()
+        {
+            public void onElementAdded( final 
ListenableCollection<GroupElement> groupElementListenableCollection,
+                                        final GroupElement addedElement )
+            {
+                // TODO: Instead of this, just modify the 3D object for the 
stroke that was changed,
+                // or add a new one, or remove an removed one.
+                reRender( sketch );
+            }
+
+            public void onElementRemoved( final 
ListenableCollection<GroupElement> groupElementListenableCollection,
+                                          final GroupElement removedElement )
+            {
+                // TODO: Instead of this, just modify the 3D object for the 
stroke that was changed,
+                // or add a new one, or remove an removed one.
+                reRender( sketch );
+            }
+        } );
+    }
+
+    private void reRender( final Sketch sketch )
+    {
+        // Clean out old nodes (NOTE: detach all is usually a bit buggy / 
weird.. )
+        myRoot3DNode.detachAllChildren();
+
+        renderGroup( sketch.getRootGroup() );
+    }
+
+    private void renderGroup( final Group group )
+    {
+        for ( final GroupElement groupElement : group.getElements() )
+        {
+            // TODO: Replace with some more polymorphic way to create nodes..
+
+            if ( groupElement instanceof Stroke )
+            {
+                final Stroke stroke = (Stroke) groupElement;
+                myRoot3DNode.attachChild( new StrokeRenderer( stroke ) );
+            }
+            else if ( groupElement instanceof Group )
+            {
+                renderGroup( (Group) groupElement );
+            }
+        }
+    }
+
+    //======================================================================
+    // Private Methods
+
+    private Menu getOrCreateMenu( final String menuName )
+    {
+        Menu menu = myMenus.get( menuName );
+        if ( menu == null )
+        {
+            menu = new Menu( menuName );
+            myMenus.put( menuName, menu );
+            myMenuBar.add( menu );
+        }
+        return menu;
+    }
+
 }

Modified: 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SkycastleSketch.java
===================================================================
--- 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SkycastleSketch.java
  2008-04-26 19:14:45 UTC (rev 483)
+++ 
trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SkycastleSketch.java
  2008-04-26 20:42:11 UTC (rev 484)
@@ -6,12 +6,7 @@
 import com.jme.scene.Spatial;
 import com.jme.scene.shape.Box;
 import com.jme.scene.state.LightState;
-import org.skycastle.sketch.model.point.StrokePointImpl;
-import org.skycastle.sketch.model.stroke.Stroke;
-import org.skycastle.sketch.model.stroke.StrokeImpl;
-import org.skycastle.util.simpleui.SimpleFrame;
 
-import java.util.Random;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -44,17 +39,15 @@
      */
     public static void main( String[] args )
     {
-        LOGGER.log( Level.INFO, "Skycastle Sketch starting up." );
 
         // Do not show logging output below the WARNING level (JME outputs a 
lot of debugging info at INFO level).
         // This way the console output is a bit more relevant.
         LOGGER.setLevel( Level.WARNING );
 
-        final SketchView sketchView = new SketchView();
 
-        // TODO: Register a model modifying input listener and navigating 
input listener to the view
+        final SketchController sketchController = new SketchController();
 
-        new SimpleFrame( sketchView.getUiComponent() );
+        sketchController.start();
 
 /*
         Sketch3DUI sketch3DUI = new Sketch3DUI();
@@ -66,25 +59,6 @@
     // Private Methods
 
 
-    private static Stroke createTestStroke()
-    {
-        final StrokeImpl stroke = new StrokeImpl();
-
-        float x = 0;
-        float y = 0;
-        final Random random = new Random();
-
-        for ( int i = 0; i < 1000; i++ )
-        {
-            x += 10;
-            y += random.nextGaussian() * 3;
-
-            stroke.addPoint( new StrokePointImpl( x, y ) );
-        }
-
-        return stroke;
-    }
-
     /**
      * Create the 3D scene. In this case, this is just the rotating box.
      */

Modified: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandAction.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandAction.java
   2008-04-26 19:14:45 UTC (rev 483)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandAction.java
   2008-04-26 20:42:11 UTC (rev 484)
@@ -31,7 +31,12 @@
 
     public final void actionPerformed( final ActionEvent e )
     {
-        myCommandStack.invoke( createCommand() );
+        final Command command = createCommand();
+
+        if ( command != null )
+        {
+            myCommandStack.invoke( command );
+        }
     }
 
     //======================================================================
@@ -98,7 +103,7 @@
 
     /**
      * @return a {@link Command} for executing the action, containing both the 
parameters for the command and
-     *         the logic to run, and optionally undo logic.
+     *         the logic to run, and optionally undo logic, or null if no 
command should be run after all.
      */
     protected abstract Command createCommand();
 

Added: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/simpleui/CloseHandler.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/simpleui/CloseHandler.java
                           (rev 0)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/simpleui/CloseHandler.java
   2008-04-26 20:42:11 UTC (rev 484)
@@ -0,0 +1,18 @@
+package org.skycastle.util.simpleui;
+
+import java.awt.Frame;
+
+/**
+ * Allows the implementor to decide what should be done when the close button 
on a frame is pressed.
+ *
+ * @author Hans Haggstrom
+ */
+public interface CloseHandler
+{
+    /**
+     * Called when the user presses the exit button on the frame.
+     *
+     * @param frameWithPressedButton the frame whose close button was pressed.
+     */
+    void onCloseButtonPressed( Frame frameWithPressedButton );
+}

Modified: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/simpleui/SimpleFrame.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/simpleui/SimpleFrame.java
    2008-04-26 19:14:45 UTC (rev 483)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/simpleui/SimpleFrame.java
    2008-04-26 20:42:11 UTC (rev 484)
@@ -6,6 +6,8 @@
 import java.awt.BorderLayout;
 import java.awt.Component;
 import java.awt.Dimension;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
 
 /**
  * A simple to use frame, suitable for spikes and scripts.
@@ -23,6 +25,8 @@
 
     private final JPanel myMainPanel = new JPanel();
 
+    private CloseHandler myCloseHandler = null;
+
     //======================================================================
     // Private Constants
 
@@ -114,8 +118,26 @@
     {
         super( title );
 
-        setDefaultCloseOperation( EXIT_ON_CLOSE );
+        // Allow overriding the close behaviour.
+        setDefaultCloseOperation( DO_NOTHING_ON_CLOSE );
+        addWindowListener( new WindowAdapter()
+        {
 
+            @Override
+            public void windowClosing( WindowEvent winEvt )
+            {
+                if ( myCloseHandler != null )
+                {
+                    myCloseHandler.onCloseButtonPressed( SimpleFrame.this );
+                }
+                else
+                {
+                    System.exit( 0 );
+                }
+            }
+
+        } );
+
         myMainPanel.setLayout( new BorderLayout() );
         myMainPanel.setPreferredSize( new Dimension( width, height ) );
         add( myMainPanel );
@@ -135,6 +157,26 @@
     // Other Public Methods
 
     /**
+     * @return the current {@link CloseHandler}, or null if none defined.
+     */
+    public final CloseHandler getCloseHandler()
+    {
+        return myCloseHandler;
+    }
+
+
+    /**
+     * @param closeHandler a {@link CloseHandler} that is notified when the 
close button of the {@link
+     *                     SimpleFrame} is clicked.  If null (the default), 
the {@link SimpleFrame} exists on
+     *                     close.
+     */
+    public final void setCloseHandler( final CloseHandler closeHandler )
+    {
+        myCloseHandler = closeHandler;
+    }
+
+
+    /**
      * @return the main panel that contains the user specified UI component.
      */
     public JPanel getMainPanel()


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

Other related posts:

  • » [skycastle-commits] SF.net SVN: skycastle: [484] trunk/skycastle/modules