[snow-cvs] r53 - in trunk: docs docs/images src/java/snow

Alessio Stalla astalla at common-lisp.net
Mon Feb 8 22:54:56 UTC 2010


Author: astalla
Date: Mon Feb  8 17:54:55 2010
New Revision: 53

Log:
small refactoring, updated tutorial.


Modified:
   trunk/docs/images/oh-no.png
   trunk/docs/tutorial.html
   trunk/src/java/snow/AbstractSnowlet.java

Modified: trunk/docs/images/oh-no.png
==============================================================================
Binary files. No diff available.

Modified: trunk/docs/tutorial.html
==============================================================================
--- trunk/docs/tutorial.html	(original)
+++ trunk/docs/tutorial.html	Mon Feb  8 17:54:55 2010
@@ -5,7 +5,7 @@
 </head>
 <body>
 <h1>Snow Tutorial</h1>
-<h4>Last modified 2009-11-26</h4>
+<h4>Last modified 2010-02-08</h4>
 <ol>
 	<li><a href="#ch001">Getting and Installing Snow</a></li>
 	<li><a href="#building">Building Snow from source (optional)</a></li>
@@ -24,7 +24,7 @@
   <li><h4>Lisp applications:</h4>
     <ul>
       <li>Snow comes prepackaged with ABCL 0.17, and it wraps the ABCL launcher with its own, that makes sure to load Snow prior to your application. So you can just follow the procedure for Java applications above, and use the snow.Snow class in place of org.armedbear.lisp.Main as the main Java class to launch, e.g. via a shell script. The only difference is that, when launched with no command-line switches, Snow will pop up a GUI repl. You can pass a dummy --no-gui-repl switch to inhibit that. If you are new to Java, the classpath is a list of search places that the JVM uses to resolve classes (think asdf:*central-registry* if you will). It can be set with the environment variable CLASSPATH or with the -classpath command line switch to the java bytecode interpreter (the 'java' command). It is a list of directories and/or .jar files, separated by a platform-dependent character (':' on Linux, ';' on Windows, I don't know about Macs). So for example, you can launch Snow on Linux with '<code>java -classpath snow.jar:lib/abcl.jar:lib/binding-2.0.6.jar:lib/commons-logging.jar:lib/miglayout-3.7.1.jar snow.Snow</code>'.</li>
-      <li>Also, Snow has its own version of Cells built in. It is a random, but fairly recent version from CVS, with some fixes to make it run on ABCL. I'm looking forward to having those fixes merged with trunk, so you'll be able to freely update Cells independently.</li>
+      <li>Also, Snow has its own version of Cells built in. It is a random and not too recent version from CVS, with some fixes to make it run on ABCL. I'm looking forward to having those fixes merged with trunk, so you'll be able to freely update Cells independently.</li>
       <li>Last but not least, Snow is built with ASDF, so if you are brave enough you can extract the contents of snow.jar (it is a regular zip file), it will create a directory tree full of .lisp source files, fasls and compiled Java classes (.class files). You will then be able to load Snow with ASDF using your own version of ABCL and/or Cells, provided you still meet the requirements about the classpath for Java applications. (there are two .asd files, one in snow/ and one in snow/swing).</li>
     </ul>
   </li>
@@ -33,7 +33,7 @@
 <a name="building" /><h3>Building Snow from source (optional)</h3>
 Snow is built using the Ant program (a Java make-like tool). You can get it from <<a href="http://ant.apache.org/">http://ant.apache.org/</a>>. To obtain the source code for Snow, either download a source release from the <a href="http://common-lisp.net/project/snow">project page</a> or, if you want the latest & greatest stuff, follow the instructions at <<a href="http://common-lisp.net/faq.shtml">http://common-lisp.net/faq.shtml</a>> to checkout it from the SVN repository. Once you have the source in a given directory, cd to that directory and issue the command <code>ant snow.jar</code> to build Snow. <code>ant snow.clean</code> removes all compiled files.
 <a name="repl" /><h3>The Snow REPL</h3>
-Being based on Lisp, Snow offers a REPL (read-eval-print-loop), an interactive prompt that allows you to evaluate arbitrary pieces of code. If you launch Snow through its main class (snow.Snow) with no command-line arguments, it will show a window containing the REPL (which is nothing more than a wrapped ABCL REPL). It should print
+Being based on Lisp, Snow offers a REPL (read-eval-print-loop), an interactive prompt that allows you to evaluate arbitrary pieces of code. If you launch Snow through its main class (snow.Snow) with no command-line arguments, it will show a window containing the REPL. It should print
 <br /><br />
 <code>SNOW-USER(1): </code>
 <br /><br />
@@ -41,7 +41,7 @@
 <pre class="paste-area">
 (frame (<span class="lisp-keyword">:title</span> <span class="lisp-string">"Snow Example"</span>)
   (button <span class="lisp-keyword">:text</span> <span class="lisp-string">"Hello, world!"</span>
-          <span class="lisp-keyword">:on-action</span> (<span class="lisp-special-op">lambda</span> (event)
+          <span class="lisp-keyword">:on-action</span> (<span class="lisp-special-op">lambda</span> ()
                        (<span class="lisp-cl-function">print</span> <span class="lisp-string">"Hello, world!"</span>)))
   (pack self)
   (show self))
@@ -76,7 +76,7 @@
 <pre class="paste-area">(panel (<i>...here go the properties...</i>) ...here goes the body...)</pre>
  - the list serves to separate them from the body. Non-containers have no body and thus their properties do not require to be wrapped in a list: <pre class="paste-area">(label <i>...here go the properties...</i>)</pre>
 <h4>How does this work?</h4>
-The Snow API consists of a set of macros that can be used to declaratively construct a tree of widgets. These macros are designed in such a way to make the tree structure of Lisp source code closely mirror the GUI widget tree structure (in the general case). The macros expand to code that uses a functional interface to create widgets, however it is not recommended to use this functional API directly since it depends on the context established by the macros.
+The Snow API consists of a set of macros that can be used to declaratively construct a tree of widgets. These macros are designed in such a way to make the tree structure of Lisp source code closely mirror the GUI widget tree structure (in the general case). The macros expand to code that uses functions to create widgets, but usually you won't deal with those functions directly.
 
 The main user-level aspects of the Snow API are:
 
@@ -128,12 +128,15 @@
 Snow provides operators to alter this default behavior:
 <ul>
   <li><code>(dont-add form)</code> will always execute form in a dynamic context in which no parent widget is defined.</li>
-  <li><code>(add-child container child &optional layout-constraints)</code> will force child to be added to container, even if container is not the value of *parent*.</li>
+  <li><code>(child widget &rest args)</code> will force the result of evaluating widget to be added to *parent*.</li>
 </ul>
   
 <h4>widget id</h4>
 
-Additionally, all container widget macros support a pseudo-property called <code><b>id</b></code> which can be used to bind a lexical variable of choice to the widget locally to the macro body. Example:
+Additionally, all widget macros support a pseudo-property called <code><b>id</b></code> which can be set to a symbol and has several uses:
+<ul>
+  <li>if a lexical variable with the same name (symbol) as the id exists, it will be assigned the widget.</li>
+  <li>if the widget is a container, a lexical variable with the same name as the id will be bound to the widget around the body of the container. Example:
 <pre class="paste-area">
 (frame (:id foo)
   (print foo)) 
@@ -142,8 +145,13 @@
 <pre class="paste-area">
 #<javax.swing.JFrame ...frame.toString()... {identityHashCode}>
 </pre>
+  </li>
+  <li>if a Java backing bean is present (see further below), and it has a property with the same name as the id, it will be injected the widget through the setter method of that property. The name of the id symbol will be translated from lisp-hyphenated-convention to javaCamelCasedConvention.</li>
+</ul>
+<h4>special variable <code>*backing-bean*</code></h4>
+This variable is usually set from Java code embedding Snow (see the <a href="#embedding">appropriate section</a>). It is used delegate some things to a Java object instead of coding them in Lisp. Apart from the injection of widgets to properties matching their :id mentioned above, a backing bean can also be used to implement event handlers; see <a href="#events">Event handling</a> for more information.
 <a name="layout" /><h3>Layout</h3>
-By default, Snow uses <a href="www.miglayout.com/">MiG Layout</a> as the layout manager to organize components inside a container. When you create a component that will be automatically added to <code>*parent*</code> by Snow, you can use the pseudo-property <code>:layout</code> to specify (as a string) additional information for the layout manager. If you use <code>add-child</code>, instead, you have to pass this string to <code>add-child</code> as its last optional parameter (I hope I can fix this inconsistency). Here's a quick cheat sheet of the constraints you can use with MiG Layout: <a href="http://www.migcalendar.com/miglayout/cheatsheet.html">http://www.migcalendar.com/miglayout/cheatsheet.html</a> (look for "Component Constraints").<br />
+By default, Snow uses <a href="www.miglayout.com/">MiG Layout</a> as the layout manager to organize components inside a container. When you create a component that will be automatically added to <code>*parent*</code> by Snow, you can use the pseudo-property <code>:layout</code> to specify (as a string) additional information for the layout manager. Here's a quick cheat sheet of the constraints you can use with MiG Layout: <a href="http://www.migcalendar.com/miglayout/cheatsheet.html">http://www.migcalendar.com/miglayout/cheatsheet.html</a> (look for "Component Constraints").<br />
 You can use another layout instead of MiG: to do so, use the <code>layout-manager</code> property of the container. The values you can pass are:
 <ul>
   <li>a keyword (supported ones are <code>:default</code>, <code>:mig</code>, <code>:border</code>, <code>:box</code>) to select the layout manager by name; the names refer to layout managers available in Swing;</li>
@@ -151,32 +159,37 @@
   <li>a Java object which can be used by Swing as a layout manager (e.g. <code>(new "java.awt.FlowLayout")</code>).</li>
 </ul>
 <a name="events" /><h3>Event handling</h3>
-Certain widgets can trigger events on certain types of user actions. These events can be handled by user code. Event-handling callbacks can be set using properties named <code>:on-<i>event-name</i></code> (for example, <code>:on-action</code> for handling clicks on buttons, or ActionEvents in Swing/AWT parlance). Currently extremely few events are supported! I'll add new ones in future releases.<br />
-A callback for an event is either a Lisp function with a single argument (the event object), or an appropriate native Java event handler for the event (e.g., an instance of <code>java.awt.ActionListener</code>).<br />
-Events happen on a dedicated thread (in Swing's terminology, the EDT - Event Dispatching Thread). That's why, in the Hello World example, the string got printed to the console and not to the REPL! In fact, the REPL has its own dynamic, thread-local context, which rebinds the value of <code>*terminal-io*</code> to a stream that reads and writes on the REPL; the event, instead, is run in another thread, which doesn't have access to this context, and thus uses the global value of <code>*terminal-io*</code>. If you want to capture the value of a dynamic variable from the thread that creates the event handler, you have to explicitly do so like this:
-<pre class="paste-area">
-(button :on-action (let ((tmp *some-thread-local-variable*))
-                     (lambda (event)
-                       (let ((*some-thread-local-variable* tmp))
-                         ...do stuff...))))
-</pre>
+Certain widgets can trigger events on certain types of user actions. These events can be handled by user code. Event-handling callbacks can be set using properties named <code>:on-<i>event-name</i></code> (for example, <code>:on-action</code> for handling clicks on buttons, or ActionEvents in Swing/AWT parlance). Currently very few events are supported! I'll add new ones in future releases.<br />
+A callback for an event can be:
+<ul>
+  <li>a Lisp function with no arguments. An object representing the event will be available as the value of the special variable *event*.</li>
+  <li>an appropriate native Java event handler for the event (e.g., an instance of <code>java.awt.ActionListener</code>).</li>
+  <li>a string. In this case, a backing bean must be used, or an error will be signaled. The string will be used as the name of a method to call on the backing bean. The method must have a single argument, of type appropriate for the event to handle (e.g. java.awt.MouseEvent for mouse events).</li>
+</ul>
+Events happen on a dedicated thread (in Swing's terminology, the EDT - Event Dispatching Thread). This thread is used to paint components too, so make sure your events don't take too much to be handled, or you'll freeze the GUI.
 
 <a name="embedding" /><h3>Embedding Snow</h3>
-Snow can easily be embedded in a Java application by using JSR-223. The snow.Snow class has some static methods that can be used to load some Snow source code from a .lisp file (or classpath resource), or to obtain an instance of <code>javax.script.ScriptEngine</code> which you can use for more advanced stuff (e.g. compiling Lisp code, or calling specific Lisp functions). When embedding Snow to define (part of) the application's GUI, it is recommended that you modularize the Snow code in functions, which you'll call from Java to obtain the GUI components:
+Snow can easily be embedded in a Java application; it leverages the JSR-223 implementation in ABCL. The snow.Snow class has some static methods that can be used to load some Snow source code from a .lisp file (or classpath resource), or to obtain an instance of <code>javax.script.ScriptEngine</code> which you can use for more advanced stuff (e.g. explicitly calling specific Lisp functions).<br />
+However, a Java application wishing to use Snow for its GUI will typically rely on a simple abstraction called a <i>Snowlet</i>. Snowlets are objects that represent a resource to be evaluated in a Snow-aware context. There are two types of Snowlets: interpreted and compiled. They can be obtained via two static methods in the class snow.Snow, <code>getInterpretedSnowlet</code> and <code>getCompiledSnowlet</code>. Interpreted Snowlets can be only evaluated once. Compiled Snowlets can be evaluated infinite times and execute faster, but take longer to be created.<br />
+
+A typical Snow file will have the following structure:
 <h4><code>file.lisp</code></h4>
 <pre class="paste-area">
 (in-package :snow-user)
+(in-readtable snow:syntax)
 
-(defun create-main-frame (&rest args)
-  ...snow code...)
+...snow code...
 </pre>
+and will be used like this:
 <h4><code>MyClass.java</code></h4>
 <pre class="paste-area">
 ...
-Snow.evalResource(new FileReader("file.lisp"));	
-JFrame mainFrame = (JFrame) Snow.getInvocable().invokeFunction("create-main-frame", args);
+Snowlet s = Snow.getInterpretedSnowlet(getClass().getResource("file.lisp"));
+s.setBackingBean(this);
+s.eval();
 ...
 </pre>
+You can look at the <a href="http://common-lisp.net/project/snow/examples/index.html">Snow Examples</a> to get a better idea.
 <a name="ch008" /><h3>Data Binding</h3>
 Keeping the GUI state in sync with the application objects state is generally tedious and error-prone. <i>Data Binding</i> is the process of automating the synchronization of state between two objects, in this case a GUI component and an application-level object. Snow supports several kinds of data binding, and it uses two libraries to do so: <a href="https://binding.dev.java.net/">JGoodies Binding</a> on the Java side and <a href="http://common-lisp.net/projects/cells/">Cells</a> on the Lisp side.
 <h4>General concepts</h4>
@@ -214,13 +227,14 @@
 (defvar *x* (make-instance 'my-class :my-slot (c-in 42)))
 (text-field :text (make-slot-data-binding *x* 'my-slot-accessor))
 </pre></li>
-<li><b>Binding to a calculated expression</b> using Cells. Syntax: <code>(make-cells-data-binding <expression> [writer])</code><br />This is useful to bind a widget to a quick-and-dirty Cells expression without creating a class instance specifically to hold it in a slot. You can optionally provide a writer function so that the widget will be able to alter the value of the expression when its value changes. Example:
+<li><b>Binding to a calculated expression</b> using Cells. Syntax: <code>(make-cell-data-binding <expression> [writer])</code><br />This is useful to bind a widget to a quick-and-dirty Cells expression without creating a class instance specifically to hold it in a slot. You can optionally provide a writer function so that the widget will be able to alter the value of the expression when its value changes. Example:
 <pre class="paste-area">
 (defvar *x* (make-instance 'my-class :slot (c-in 42)))
-(text-field :text (make-cells-data-binding
-                    *x* (c? (* 2 (my-slot *x*)))
+(text-field :text (make-cell-data-binding
+                    (c? (* 2 (my-slot *x*)))
                     (lambda (new-value) (setf (my-slot *x*) new-value))))
-</pre></li>
+</pre>
+Note: Snow's variables (introduced with make-var) are in fact Cells objects and can be used in Cells expression too. So for example, <code>(c? (1+ (var foo)))</code> is a valid expression that evaluates to 1 + the value of foo and is updated anytime the value of foo is changed.</li>
 </ul>
 <h4>Syntactic sugar</h4>
 To avoid the verbosity of make-foo-data-binding, Snow provides convenient syntax to cover the most common cases of data binding. You can enable this special syntax by evaluating the form <code>(in-readtable snow:syntax)</code>, for example placing it in every source file right after the <code>(in-package :snow-user)</code> form at the top of the file.<br />
@@ -232,7 +246,7 @@
 <li><code>${bean.path}</code> is a bit more complex. This syntax resembles that of the "Expression Language" used in JSP and JSF. First, <code>bean</code> is used as the name of a bean: the function stored in the special variable <code>*bean-factory*</code> is called with it as an argument to produce a Java object. The default function simply reads and evaluates <code>bean</code> as the name of a Lisp variable, but you can customize this behavior: for example, you can provide a callback that gets the bean from a Spring application context<sup><a href="#notes_2">2</a></sup>. Then, once the bean has been obtained, two things can happen: if <code>path</code> is a simple property (i.e. it has no dots) and the bean is an instance of JGoodies PresentationModel, a binding to the property is asked to the presentation model, as by <code>(make-bean-data-binding bean path)</code>; else, a property path data binding is constructed, as by <code>(make-property-data-binding bean path-as-list)</code>.</li>
 </ul>
 <a name="more" /><h3>What's more?</h3>
-I haven't covered which widgets are supported and how much of their API is supported. At this stage, Snow is in a early stage of development, so very little of the Swing API is covered. The best way to learn about Snow usage is to look at the examples included with Snow: the debugger (debugger.lisp), inspector (inspector.lisp) and the REPL (repl.lisp and swing/swing.lisp). Also, I haven't talked about how to use your custom widgets with Snow, and probably other things. Drop me a line at alessiostalla @ Google's mail service, and I'll be happy to help you.
+I haven't covered which widgets are supported and how much of their API is supported. At this point, Snow is in a early stage of development, so only a little of the Swing API is covered. The best way to learn about Snow usage is to look at the examples included with it. Looking at the debugger (debugger.lisp), inspector (inspector.lisp) and the REPL (repl.lisp and swing.lisp), included as part of the Snow distribution, can be useful as well. Also, I haven't talked about how to use your custom widgets with Snow, and probably other things. Drop me a line at alessiostalla @ Google's mail service, and I'll be happy to help you.
 <hr />
 <h3>Footnotes</h3>
 <ol>

Modified: trunk/src/java/snow/AbstractSnowlet.java
==============================================================================
--- trunk/src/java/snow/AbstractSnowlet.java	(original)
+++ trunk/src/java/snow/AbstractSnowlet.java	Mon Feb  8 17:54:55 2010
@@ -40,17 +40,21 @@
     private PresentationModel presentationModel;
     private Object backingBean;
 
-    public Object eval() {
+    protected Bindings createBindings() {
 	Bindings b = Snow.getScriptEngine().createBindings();
 	b.put("snow:*backing-bean*", backingBean);
 	b.put("snow:*presentation-model*", presentationModel);
+	return b;
+    }
+
+    public Object eval() {
 	try {
-	    return eval(b);
+	    return eval(createBindings());
 	} catch(Exception e) {
 	    throw new RuntimeException("Exception while evaluating snowlet " + this, e);
 	}
     }
-    
+
     protected abstract Object eval(Bindings bindings) throws Exception;
 
     public PresentationModel getPresentationModel() {
@@ -68,4 +72,5 @@
     public void setBackingBean(Object backingBean) {
 	this.backingBean = backingBean;
     }
+
 }
\ No newline at end of file




More information about the snow-cvs mailing list