/[snow]/trunk/docs/tutorial.html
ViewVC logotype

Contents of /trunk/docs/tutorial.html

Parent Directory Parent Directory | Revision Log Revision Log


Revision 19 - (show annotations)
Thu Nov 19 23:05:35 2009 UTC (4 years, 5 months ago) by astalla
File MIME type: text/html
File size: 23524 byte(s)
Minor updates in the docs.
1 <html>
2 <head>
3 <title>Snow Tutorial</title>
4 <link rel="stylesheet" type="text/css" href="style.css">
5 </head>
6 <body>
7 <h1>Snow Tutorial</h1>
8 <ol>
9 <li><a href="#ch001">Getting and Installing Snow</a></li>
10 <li><a href="#terminology">Terminology</a></li>
11 <li><a href="#repl">The Snow REPL</a></li>
12 <li><a href="#basic-concepts">Basic Concepts</a></li>
13 <li><a href="#layout">Layout</a></li>
14 <li><a href="#events">Event handling</a></li>
15 <li><a href="#embedding">Embedding Snow</a></li>
16 <li><a href="#ch008">Data Binding</a></li>
17 <li><a href="#more">What's more?</a></li>
18 </ol>
19 <a name="ch001" /><h3>Getting and Installing Snow</h3>
20 You can download the latest Snow binary distribution from <a href="http://common-lisp.net/projects/snow/">http://common-lisp.net/projects/snow/</a>. It contains Snow and all its dependencies in a single Zip file. Since Snow can be used both in Lisp and Java applications, procedures for installing it can vary in each of the two cases.
21 <ul>
22 <li><h4>Java applications:</h4>simply make sure snow.jar and all the jars in the lib/ folder are in the classpath of your application. Snow uses JSR-223 and is built with Java 1.6, so that's the minimum Java version you can use. However, it should be possible to run Snow on 1.5 as well, but you'll need to recompile both Snow and ABCL from sources with a JSR-223 implementation in your classpath. See the <a href="#embedding">Embedding Snow</a> section below for details about using Snow inside your Java application.</li>
23 <li><h4>Lisp applications:</h4>
24 <ul>
25 <li>Snow come prepackaged with ABCL 0.16, 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.6.2.jar snow.Snow</code>'.</li>
26 <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>
27 <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>
28 </ul>
29 </li>
30 </ul>
31 Currently Snow, when run from the jar, requires a temporary directory to load itself; make sure your application has write permissions on your OS's tmp directory. Snow should automatically clear its temporary files when the application exits.
32 <a name="terminology" /><h3>Terminology</h3>
33 The boring part :) you can skip this if you know Lisp, since I'm going to loosely define some terms Snow borrows from Lisp that will be used in this tutorial.
34 <ul>
35 <li><dd><strong>car</strong></dd><dt>the first element of a list.</dt></li>
36 <li><dd><strong>cdr</strong></dd><dt>the rest of a list (all elements except the first).</dt></li>
37 <li><dd><strong>nil</strong></dd><dt>the empty list, and the only boolean false value.</dt></li>
38 <li><dd><strong>t</strong></dd><dt>a self-evaluating symbol representing the canonical boolean true value (among other things).</dt></li>
39 <li><dd><strong>form</strong></dd><dt>an expression to be evaluated or compiled.</dt></li>
40 <li><dd><strong>keyword</strong></dd><dt>a self-evaluating symbol starting with a colon (like <code>:title</code>). More correctly, a symbol in the KEYWORD package.</dt></li>
41 </ul>
42 <a name="repl" /><h3>The Snow REPL</h3>
43 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
44 <br /><br />
45 <code>SNOW-USER(1): </code>
46 <br /><br />
47 SNOW-USER is the active package (namespace), (1) is the line number of the REPL. Now, the obligatory hello world:
48 <pre class="paste-area">
49 (frame (<span class="lisp-keyword">:title</span> <span class="lisp-string">"Snow Example"</span>)
50 (button <span class="lisp-keyword">:text</span> <span class="lisp-string">"Hello, world!"</span>
51 <span class="lisp-keyword">:on-action</span> (<span class="lisp-special-op">lambda</span> (event)
52 (<span class="lisp-cl-function">print</span> <span class="lisp-string">"Hello, world!"</span>)))
53 (pack self)
54 (show self))
55 </pre>
56 Evaluating this will show a window containing a single button which, when pressed, will output "Hello, world!". The terminology should be familiar to Swing developers. Actually, the output from the button will NOT go to the REPL, but to the OS console instead; I'll explain this later, please ignore it for now. <br />
57 The REPL is great for experimenting: the code you input is immediately executed by an interpreter. You can also compile your code, either on the fly in the REPL or from a file; this is outside the scope of this tutorial, but you can find more information in any decent tutorial or book about Common Lisp (I suggest the free ebook Practical Common Lisp by Peter Seibel, available at <a href="http://gigamonkeys.com/book/">http://gigamonkeys.com/book/</a>). However, experiments sometimes go wrong; if you make a mistake - for example, evaluating an unexisting function - you will end in the debugger. Try typing the function call
58 <pre class="paste-area">
59 (oh-no!)
60 </pre>
61 - you should see something like this:
62 <br /><br />
63 <img src="images/oh-no.png" alt="Debugger window"/>
64 <br /><br />
65 The <i>restarts</i> are the actions the system can perform to recover the situation; this is an important feature of Common Lisp, worth studying by itself. You'll always be able to choose the TOP-LEVEL restart, which will bring you back to the REPL.<br />
66 You can quit the REPL (and terminate the application) any time by closing the REPL window, or by typing (quit)<sup><a href="#notes_1">1</a></sup>.
67 <a name="basic-concepts" /><h3>Basic Concepts</h3>
68 As you can see from the previous examples, Snow code consists of a tree of widgets; nesting in the code means nesting in the widget hierarchy, for example:
69 <pre class="paste-area">
70 (frame (:visible-p t)
71 (panel (:layout "wrap")
72 (label <span class="lisp-keyword">:text</span> <span class="lisp-string">"1"</span>)
73 (label <span class="lisp-keyword">:text</span> <span class="lisp-string">"2"</span>)
74 (label <span class="lisp-keyword">:text</span> <span class="lisp-string">"3"</span>))
75 (panel ()
76 (label <span class="lisp-keyword">:text</span> <span class="lisp-string">"4"</span>)
77 (label <span class="lisp-keyword">:text</span> <span class="lisp-string">"5"</span>)
78 (label <span class="lisp-keyword">:text</span> <span class="lisp-string">"6"</span>))
79 (pack self))
80 </pre>
81 Creates a frame with two children, both panels with 3 children each - one has labels from 1 to 3, the other from 4 to 6.<br />
82 You can set the <i>properties</i> of the widgets using keyword-value pairs like <code>:visible-p t</code> which means <code>setVisible(true)</code> (-p is a suffix traditionally used in Lisp to name predicates, T is the canonical true value). Containers must have their properties set in a list after the widget name -
83 <pre class="paste-area">(panel (<i>...here go the properties...</i>) ...here goes the body...)</pre>
84 - 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>
85 <h4>How does this work?</h4>
86 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.
87
88 The aspects of such context of interest to the user are:
89
90 <h4>lexical variable <code>self</code></h4>
91
92 Holds the current widget being processed. Example:
93 <pre class="paste-area">
94 (frame ()
95 (print self))
96 </pre>
97 will output something like:
98 <pre class="paste-area">
99 #&lt;javax.swing.JFrame ...frame.toString()... {identityHashCode}&gt;
100 </pre>
101 <h4>special variable <code>*parent*</code></h4>
102 Holds the current parent widget. When *parent* is non-nil, any widget created through a macro will be automatically added to the container referenced by *parent*. All Snow widget macros process the forms in their body in the following way:
103 <ul>
104 <li>if the form is a list, its car is a symbol, and this symbol has a property named widget-p, then the form is wrapped in a <code>(let ((*parent* self)) ...)</code> i.e., it is evaluated in a dynamic context where the parent is the widget created by the macro.</li>
105 <li>else, the form is wrapped in a <code>(let ((*parent* nil)) ...)</code>, i.e., it is evaluated in a dynamic context where no parent widget is defined (and thus widgets created by the form are not added to any widget).</li>
106 </ul>
107
108 These rules make the nesting of Snow widget macros work in an intuitive way:
109 <ul>
110 <li>a widget defined at the top level in the body of another (container) widget w will be added to w. Example: <code>(frame () (panel ()))</code> will result in a frame with a panel child.</li>
111 <li>widgets appearing in code in the body of another widgets, but not at top level, will not be added. Example: <code>(frame () (let ((p (panel ())))))</code> will result in a frame with no children, and will create an &quot;orphan&quot; panel.</li>
112 </ul>
113 Snow provides operators to alter this default behavior:
114 <ul>
115 <li><code>(dont-add form)</code> will always execute form in a dynamic context in which no parent widget is defined.</li>
116 <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>
117 </ul>
118
119 <h4>widget id</h4>
120
121 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:
122 <pre class="paste-area">
123 (frame (:id foo)
124 (print foo))
125 </pre>
126 will output something like:
127 <pre class="paste-area">
128 #&lt;javax.swing.JFrame ...frame.toString()... {identityHashCode}&gt;
129 </pre>
130 <a name="layout" /><h3>Layout</h3>
131 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 &quot;Component Constraints&quot;).<br />
132 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:
133 <ul>
134 <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>
135 <li>a list whose car is one of the symbols above, and whose cdr is a list of the arguments passed to the layout manager (e.g. <code>(list :box :y)</code> to have components be laid out in a vertical stack);</li>
136 <li>a Java object which can be used by Swing as a layout manager (e.g. <code>(new "java.awt.FlowLayout")</code>).</li>
137 </ul>
138 <a name="events" /><h3>Event handling</h3>
139 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 />
140 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 />
141 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:
142 <pre class="paste-area">
143 (button :on-action (let ((tmp *some-thread-local-variable*))
144 (lambda (event)
145 (let ((*some-thread-local-variable* tmp))
146 ...do stuff...))))
147 </pre>
148
149 <a name="embedding" /><h3>Embedding Snow</h3>
150 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:
151 <h4><code>file.lisp</code></h4>
152 <pre class="paste-area">
153 (in-package :snow-user)
154
155 (defun create-main-frame (&rest args)
156 ...snow code...)
157 </pre>
158 <h4><code>MyClass.java</code></h4>
159 <pre class="paste-area">
160 ...
161 Snow.evalResource(new FileReader("file.lisp"));
162 JFrame mainFrame = (JFrame) Snow.getInvocable().invokeFunction("create-main-frame", args);
163 ...
164 </pre>
165 <a name="ch008" /><h3>Data Binding</h3>
166 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.
167 <h4>General concepts</h4>
168 There are two general ways to <i>bind</i> or <i>connect</i> a widget to some object's property: one is by using the <code>:binding</code> property of the widget, letting the framework choose which property of the widget to bind, e.g. the text property for a text field; for example:
169 <pre class="paste-area">
170 (text-field :binding (make-simple-data-binding x))
171 </pre>
172 the other is to provide as the value of a widget's property an object representing the binding, as in
173 <pre class="paste-area">
174 (button :enabled-p (make-simple-data-binding x))
175 </pre>
176 this will connect the specific property of the widget with the user-provided object or property.
177 <h4>Types of data binding</h4>
178 Snow supports several types of data binding; some are more indicated for Lisp applications, others for Java applications.
179 <ul>
180 <li><b>Binding to a variable.</b> Syntax: <code>(make-simple-data-binding &lt;variable&gt;)</code><br />This is the simplest form of data binding: you connect a widget's property to a suitably instrumented Lisp variable. Such a variable must be initialized with <code>(make-var &lt;value&gt;)</code>, read with <code>(var &lt;name&gt;)</code>, and written with <code>(setf (var &lt;name&gt;) &lt;value&gt;)</code>. Example:
181 <pre class="paste-area">
182 (defvar *x* (make-var "Initial value"))
183 (setf (var *x*) "new value")
184 (button :text (make-simple-data-binding *x*))
185 </pre></li>
186 <li><b>Binding to a Presentation Model.</b> Syntax: <code>(make-bean-data-binding &lt;object&gt; &lt;property&gt; ...other args...)</code><br />This kind of binding uses Martin Fowler's <i>Presentation Model</i> pattern as implemented by JGoodies Binding. You implement, in Java, a suitable subclass of <code>PresentationModel</code> (in simple cases, you can just use the base class provided by JGoodies); you then bind a widget to a model returned by an instance of this class for a bean property. Example:
187 <pre class="paste-area">
188 (defvar *presentation-model* (new "my.presentation.Model"))
189 (text-field :text (make-bean-data-binding *presentation-model* "myProperty"))
190 </pre>
191 The presentation model acts as a glue between the GUI and the application logic, and provides advanced functionality (e.g. it lets you control when to synchronize the state). You can tune it with additional arguments to <code>make-bean-data-binding</code>; for example, you can obtain a buffered model with <code>:buffered-p t</code>.</li>
192 <li><b>Binding to a bean property</b>, arbitrarily nested. Syntax: <code>(make-property-data-binding &lt;bean&gt; &lt;property path&gt;)</code><br />This is a convenient way to adapt a pre-existing bean with the binding framework. It connects a property path, which can be expressed in &quot;dot notation&quot;; to properly observe changes, every object which is part of the path must support the standard Java Bean facility for listening to property changes. Example:
193 <pre class="paste-area">
194 (defvar *x* (new "Person"))
195 (text-field :text (make-property-data-binding *x* "address.street"))
196 </pre></li>
197 <li><b>Binding to a Cells-powered slot</b> of a CLOS object. Syntax: <code>(make-slot-data-binding &lt;object&gt; &lt;slot-accessor-name&gt;)</code><br />You can use this kind of binding to connect a widget property to a slot of a CLOS object using the Cells dataflow library. Example:
198 <pre class="paste-area">
199 (defvar *x* (make-instance 'my-class :my-slot (c-in 42)))
200 (text-field :text (make-slot-data-binding *x* 'my-slot-accessor))
201 </pre></li>
202 <li><b>Binding to a calculated expression</b> using Cells. Syntax: <code>(make-cells-data-binding &lt;expression&gt; [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:
203 <pre class="paste-area">
204 (defvar *x* (make-instance 'my-class :slot (c-in 42)))
205 (text-field :text (make-cells-data-binding
206 *x* (c? (* 2 (my-slot *x*)))
207 (lambda (new-value) (setf (my-slot *x*) new-value))))
208 </pre></li>
209 </ul>
210 <h4>Syntactic sugar</h4>
211 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 />
212 This special syntax is accessed by using the prefix $. It covers the following cases:
213 <ul>
214 <li><code>$foo</code> is equivalent to <code>(make-simple-data-binding foo)</code>.</li>
215 <li><code>$(c? ...)</code> is equivalent to <code>(make-cell-data-binding (c? ...))</code>.</li>
216 <li><code>$(slot ...)</code> is equivalent to <code>(make-slot-data-binding ...)</code>.</li>
217 <li><code>${bean.path}</code> is a bit more complex. This syntax resembles that of the &quot;Expression Language&quot; 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>
218 </ul>
219 <a name="more" /><h3>What's more?</h3>
220 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.
221 <hr />
222 <h3>Footnotes</h3>
223 <ol>
224 <li><a name="notes_1" />If you <i>really</i> mess things up, you can change the package of the REPL to one where the symbol QUIT is not visible. If you find yourself in this situation, type (ext:quit) to exit.</li>
225 <li><a name="notes_2" />Snow provides a convenient Java class - <code>org.armedbear.lisp.Callback</code> - that you can subclass to create your custom callback function. You have just to implement the appropriate overload of the <code>call</code> method, or alternatively use one of the static methods <code>Callback.fromXXX</code> to create a Callback from several kinds of Java callbacks.</li>
226 </ol>
227 </body>
228 </html>

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.5