You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by gb...@apache.org on 2010/03/26 15:38:07 UTC
svn commit: r927875 - in /pivot/trunk/tutorials/www:
stock-tracker.data-binding.xml stock-tracker.events.xml
stock-tracker.localization.xml stock-tracker.web-queries.xml
Author: gbrown
Date: Fri Mar 26 14:38:06 2010
New Revision: 927875
URL: http://svn.apache.org/viewvc?rev=927875&view=rev
Log:
Finish Stock Tracker tutorial updates.
Modified:
pivot/trunk/tutorials/www/stock-tracker.data-binding.xml
pivot/trunk/tutorials/www/stock-tracker.events.xml
pivot/trunk/tutorials/www/stock-tracker.localization.xml
pivot/trunk/tutorials/www/stock-tracker.web-queries.xml
Modified: pivot/trunk/tutorials/www/stock-tracker.data-binding.xml
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/www/stock-tracker.data-binding.xml?rev=927875&r1=927874&r2=927875&view=diff
==============================================================================
--- pivot/trunk/tutorials/www/stock-tracker.data-binding.xml (original)
+++ pivot/trunk/tutorials/www/stock-tracker.data-binding.xml Fri Mar 26 14:38:06 2010
@@ -43,28 +43,25 @@ limitations under the License.
<p>
The <tt>Dictionary</tt> argument passed to these methods provides the "bind context":
a collection of name/value pairs representing the data to which the components are
- bound. Each bindable component can be assigned a "bind key" that associates the
- component with a value in the context. The default implementations do nothing;
- components that support data binding override them to import data to the component
- from the context during a load, and export data from the component to the context
- during a store.
+ bound. Each bindable property of a component can be assigned a "bind key" that
+ associates the property with a value in the context. Data is imported from the context
+ into the property during a load, and exported from the property to the context during
+ a store.
</p>
<p>
- Data binding is most often supported by components that accept and present user input
- (such as a text field), but it can also be implemented by read-only components, such
- as labels and progress meters. Most components allow a caller to bind only to a single
- value (such as the "text" property of a label), though some support additional bindings
- (for example, a checked list view that allows a caller to bind to both its items'
- checked and selected states).
+ Many component support data binding including text inputs, checkboxes and radio buttons,
+ list views, and table views, among others. Some components support binding to multiple
+ properties; for example, a caller can bind to both the list data as well as the selection
+ state of a <tt>ListView</tt> component.
</p>
<p>
- It is important to note that it is not possible to bind to a container directly.
- However, containers may act as nested bind contexts - when a bind key is assigned to a
- container, it is assumed to point to a nested <tt>Dictionary</tt> instance representing
- a subordinate bind context. This enables complex JSON object graphs returned from a
- web query to be seamlessly mapped to a set of data-bound components arranged in a
+ It is important to note that it is not possible to directly bind to a container
+ component. However, containers may act as nested bind contexts - when a bind key is
+ assigned to a container, it is assumed to point to a nested <tt>Dictionary</tt> instance
+ representing a subordinate bind context. This enables complex object graphs returned
+ from a web query to be seamlessly mapped to a set of data-bound components arranged in a
non-trivial layout, for example.
</p>
@@ -73,190 +70,138 @@ limitations under the License.
<p>
The Stock Tracker demo isn't quite that sophisticated. It uses a single, flat bind
context to populate the fields in the quote detail view. The bind context is actually
- the row data retrieved from the web query for the selected stock. This is why we
- requested more data than we seemed to need from the GET query: the extra fields are
- used to fill in the data in the detail form.
+ the row data retrieved from the web query for the selected stock. This is why the
+ application requests more data than it seems to need from the GET query: the extra
+ fields are used to fill in the data in the detail form.
</p>
<p>
- The bound components, in this case, are read-only labels - Stock Tracker uses a
- one-way binding to map the retrieved quote data to the text property of each. We
- specified the name of the key to use for each label in the
- <tt>stocktracker.detail.wtkx</tt> file:
+ The bound components, in this case, are labels - Stock Tracker maps values from the
+ retrieved quote data to the text property of each. The name of the key to use for each
+ label is specified via the "textKey" property:
</p>
<source type="xml">
<![CDATA[
- ...
- <Label Form.label="%value" textKey="value"/>
- ...
+ <Form styles="{padding:0, fill:true, showFlagIcons:false, showFlagHighlight:false,
+ leftAlignLabels:true}">
+ <sections>
+ <Form.Section>
+ <wtkx:define>
+ <stocktracker:ValueMapping wtkx:id="valueMapping"/>
+ <stocktracker:ChangeMapping wtkx:id="changeMapping"/>
+ <stocktracker:VolumeMapping wtkx:id="volumeMapping"/>
+ </wtkx:define>
+
+ <Label wtkx:id="valueLabel" Form.label="%value"
+ textKey="value" textBindMapping="$valueMapping"
+ styles="{horizontalAlignment:'right'}"/>
+ <Label wtkx:id="changeLabel" Form.label="%change"
+ textKey="change" textBindMapping="$valueMapping"
+ styles="{horizontalAlignment:'right'}"/>
+ <Label wtkx:id="openingValueLabel" Form.label="%openingValue"
+ textKey="openingValue" textBindMapping="$valueMapping"
+ styles="{horizontalAlignment:'right'}"/>
+ <Label wtkx:id="highValueLabel" Form.label="%highValue"
+ textKey="highValue" textBindMapping="$valueMapping"
+ styles="{horizontalAlignment:'right'}"/>
+ <Label wtkx:id="lowValueLabel" Form.label="%lowValue"
+ textKey="lowValue" textBindMapping="$changeMapping"
+ styles="{horizontalAlignment:'right'}"/>
+ <Label wtkx:id="volumeLabel" Form.label="%volume"
+ textKey="volume" textBindMapping="$volumeMapping"
+ styles="{horizontalAlignment:'right'}"/>
+ </Form.Section>
+ </sections>
+ </Form>
]]>
</source>
<p>
- The actual binding occurs when the selection changes in the table view; as we saw in
- the <a href="stock-tracker.events.html">Event Handling</a> section, the selection
+ The actual binding occurs when the selection changes in the table view; the selection
change handler calls the <tt>refreshDetail()</tt> method in response to a selection
- change event. The code for this method is as follows:
+ change event:
</p>
<source type="java">
<![CDATA[
+ @SuppressWarnings("unchecked")
private void refreshDetail() {
- int firstSelectedIndex = stocksTableView.getFirstSelectedIndex();
- removeSymbolsButton.setEnabled(firstSelectedIndex != -1);
-
StockQuote stockQuote = null;
- Form.setFlag(detailChangeLabel, (Form.Flag)null);
+ int firstSelectedIndex = stocksTableView.getFirstSelectedIndex();
if (firstSelectedIndex != -1) {
int lastSelectedIndex = stocksTableView.getLastSelectedIndex();
if (firstSelectedIndex == lastSelectedIndex) {
List<StockQuote> tableData = (List<StockQuote>)stocksTableView.getTableData();
stockQuote = tableData.get(firstSelectedIndex);
-
- if (stockQuote.getChange() < 0) {
- Form.setFlag(detailChangeLabel, new Form.Flag(MessageType.ERROR));
- }
+ } else {
+ stockQuote = new StockQuote();
}
- }
-
- if (stockQuote == null) {
- detailRootPane.load(new HashMap<String, Object>());
} else {
- StockQuoteView stockQuoteView = new StockQuoteView(stockQuote);
- detailRootPane.load(stockQuoteView);
+ stockQuote = new StockQuote();
}
- }
+ detailPane.load(new BeanDictionary(stockQuote));
+ }
]]>
</source>
<p>
- The method does the following:
- </p>
-
- <ul>
- <li>
- <p>
- Obtains the first selected index in the table (if more than one item is
- selected, we don't want to show anything in the detail)
- </p>
- </li>
- <li>
- <p>
- Clears the "flag" attribute from the detail change label (the <tt>Form</tt>
- container allows a caller to tag fields with a flag value, for use in data
- validation or just simple notification - here, a flag is used to indicate a
- negative change in the stock value)
- </p>
- </li>
- <li>
- <p>
- Gets a reference to the table view's data model and then to the data for the
- selected row
- </p>
- </li>
- <li>
- <p>
- If the change percentage is negative, shows a red "error" flag next to the
- detail label
- </p>
- </li>
- <li>
- <p>
- Wraps the row data in an instance of <tt>StockQuoteView</tt> and calls
- <tt>load()</tt>, populating the form with data from the selected quote
- </p>
- </li>
- </ul>
-
- <p>
- <tt>StockQuoteView</tt> is a "decorator" (in the
- <a href="http://en.wikipedia.org/wiki/Design_pattern_(computer_science)">design
- pattern</a> sense, not the WTK sense) - it ensures that it is formatted and presented
- in a readable manner in the detail view:
- </p>
+ Note that the <tt>load()</tt> method is actually called on the detail pane itself
+ rather than on the parent container of the detail labels (an instance of <tt>Form</tt>).
+ This is because the application also needs to bind to the label that contains the
+ company name, which is not a child of the <tt>Form</tt>.
+ <p>
- <source type="java" location="org/apache/pivot/tutorials/stocktracker/StockQuoteView.java">
+ <source type="xml">
<![CDATA[
- package org.apache.pivot.tutorials.stocktracker;
+ <Label textKey="companyName" styles="{font:{size:12, bold:true}}"/>
+ ]]>
+ </source>
- import java.text.DecimalFormat;
+ </p>
+ This example demostrates that a nested container does not automatically imply the
+ existence of a sub-context - sub-contexts are created only when a nested container is
+ assigned its own bind key. Because a bind key is not defined for it, the form simply
+ inherits the bind context that was passed to its parent, the root
+ <tt><BoxPane></tt> representing the detail pane.
+ </p>
- import org.apache.pivot.beans.BeanDictionary;
+ <h2>Bind Mappings</h2>
- public class StockQuoteView extends BeanDictionary {
- private static final DecimalFormat valueFormat = new DecimalFormat("$0.00");
- private static final DecimalFormat changeFormat = new DecimalFormat("+0.00;-0.00");
- private static final DecimalFormat volumeFormat = new DecimalFormat();
+ <p>
+ Also note the use of the "textBindMapping" attributes. Bind mappings allow a caller
+ to modify the data during the bind process. Incoming data can be converted before it
+ is assigned to a property, and outgoing data can be converted before it is stored in
+ the bind context. For example, the <tt>ValueMapping</tt> class formats incoming data
+ as a currency value in U.S. dollars:
+ </p>
- public StockQuoteView(StockQuote stockQuote) {
- super(stockQuote);
- }
+ <source type="java">
+ <![CDATA[
+ public class ValueMapping implements Label.TextBindMapping {
+ private static final DecimalFormat FORMAT = new DecimalFormat("$0.00");
@Override
- public Object get(String key) {
- if (key == null) {
- throw new IllegalArgumentException("key is null.");
- }
-
- Object value = null;
- StockQuote stockQuote = (StockQuote)getBean();
-
- if (stockQuote == null) {
- value = "";
- } else {
- value = super.get(key);
-
- if (key.equals("value")
- || key.equals("openingValue")
- || key.equals("highValue")
- || key.equals("lowValue")) {
- try {
- Float floatValue = (Float)value;
- if (floatValue.isNaN()) {
- value = "n/a";
- } else {
- value = valueFormat.format(floatValue);
- }
- } catch(Exception exception) {
- value = "";
- }
- } else if (key.equals("change")) {
- try {
- value = changeFormat.format(value);
- } catch(Exception exception) {
- value = "";
- }
- } else if (key.equals("volume")) {
- try {
- value = volumeFormat.format(value);
- } catch(Exception exception) {
- value = "";
- }
- } else {
- if (value != null) {
- value = value.toString();
- }
- }
- }
+ public String toString(Object value) {
+ return Float.isNaN((Float)value) ? null : FORMAT.format(value);
+ }
- return value;
+ @Override
+ public Object valueOf(String text) {
+ throw new UnsupportedOperationException();
}
}
]]>
</source>
<p>
- Note that the <tt>load()</tt> method is actually called on the parent container of
- the <tt>Form</tt>, rather than on the form itself. This is because we also want to
- bind to the label that contains the company name, which is not a child of the
- <tt>Form</tt>. A nested container does not automatically imply the existence of a
- sub-context - sub-contexts are created only when a nested container is assigned its
- own bind key. Because a bind key is not defined for it, the form simply inherits the
- bind context that was passed to its parent.
+ The <tt>toString() method is called to perform the translation during a <tt>load()</tt>
+ operation</tt>. The <tt>valueOf()</tt> method would be called during <tt>store()</tt>,
+ but throws <tt>UnsupportedOperationException</tt> because <tt>store()</tt> is never
+ called by the Stock Tracker application.
</p>
</body>
</document>
Modified: pivot/trunk/tutorials/www/stock-tracker.events.xml
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/www/stock-tracker.events.xml?rev=927875&r1=927874&r2=927875&view=diff
==============================================================================
--- pivot/trunk/tutorials/www/stock-tracker.events.xml (original)
+++ pivot/trunk/tutorials/www/stock-tracker.events.xml Fri Mar 26 14:38:06 2010
@@ -68,29 +68,29 @@ limitations under the License.
<p>
Retrieves the "langauge" argument that was provided to the application context
when it was created - for desktop applications, this is a command-line
- argument; in the browser, it is passed via an applet parameter.
+ argument; in the browser, it is passed via an applet parameter
</p>
</li>
<li>
<p>
Sets the default locale to an instance corresponding to the language
- argument.
+ argument
</p>
</li>
<li>
<p>
- Loads the application resources.
+ Loads the application resources
</p>
</li>
<li>
<p>
Creates an instance of <tt>WTKXSerializer</tt> and loads the WTKX source for
- the main window.
+ the main window
</p>
</li>
<li>
<p>
- Opens the main window, causing the application to appear on the screen.
+ Opens the main window, causing the application to appear on the screen
</p>
</li>
</ul>
Modified: pivot/trunk/tutorials/www/stock-tracker.localization.xml
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/www/stock-tracker.localization.xml?rev=927875&r1=927874&r2=927875&view=diff
==============================================================================
--- pivot/trunk/tutorials/www/stock-tracker.localization.xml (original)
+++ pivot/trunk/tutorials/www/stock-tracker.localization.xml Fri Mar 26 14:38:06 2010
@@ -81,26 +81,39 @@ limitations under the License.
<p class="caption">StockTracker_fr.json</p>
<p>
- As we've seen, references to these string resources can be embedded directly within a
- WTKX file:
+ As noted in earlier sections, references to these string resources can be embedded
+ directly within a WTKX file:
</p>
<source type="xml">
<![CDATA[
- <Form styles="{fill:true}">
+ <Form styles="{padding:0, fill:true, showFlagIcons:false, showFlagHighlight:false,
+ leftAlignLabels:true}">
<sections>
<Form.Section>
- <Label Form.name="%value" textKey="value"
+ <wtkx:define>
+ <stocktracker:ValueMapping wtkx:id="valueMapping"/>
+ <stocktracker:ChangeMapping wtkx:id="changeMapping"/>
+ <stocktracker:VolumeMapping wtkx:id="volumeMapping"/>
+ </wtkx:define>
+
+ <Label wtkx:id="valueLabel" Form.label="%value"
+ textKey="value" textBindMapping="$valueMapping"
styles="{horizontalAlignment:'right'}"/>
- <Label wtkx:id="changeLabel" Form.name="%change" textKey="change"
+ <Label wtkx:id="changeLabel" Form.label="%change"
+ textKey="change" textBindMapping="$valueMapping"
styles="{horizontalAlignment:'right'}"/>
- <Label Form.name="%openingValue" textKey="openingValue"
+ <Label wtkx:id="openingValueLabel" Form.label="%openingValue"
+ textKey="openingValue" textBindMapping="$valueMapping"
styles="{horizontalAlignment:'right'}"/>
- <Label Form.name="%highValue" textKey="highValue"
+ <Label wtkx:id="highValueLabel" Form.label="%highValue"
+ textKey="highValue" textBindMapping="$valueMapping"
styles="{horizontalAlignment:'right'}"/>
- <Label Form.name="%lowValue" textKey="lowValue"
+ <Label wtkx:id="lowValueLabel" Form.label="%lowValue"
+ textKey="lowValue" textBindMapping="$changeMapping"
styles="{horizontalAlignment:'right'}"/>
- <Label Form.name="%volume" textKey="volume"
+ <Label wtkx:id="volumeLabel" Form.label="%volume"
+ textKey="volume" textBindMapping="$volumeMapping"
styles="{horizontalAlignment:'right'}"/>
</Form.Section>
</sections>
Modified: pivot/trunk/tutorials/www/stock-tracker.web-queries.xml
URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/www/stock-tracker.web-queries.xml?rev=927875&r1=927874&r2=927875&view=diff
==============================================================================
--- pivot/trunk/tutorials/www/stock-tracker.web-queries.xml (original)
+++ pivot/trunk/tutorials/www/stock-tracker.web-queries.xml Fri Mar 26 14:38:06 2010
@@ -155,7 +155,7 @@ limitations under the License.
</p>
<p>
- By default, <tt>CSVSerializer</tt> creates an instance of
+ <tt>CSVSerializer</tt> will, by default, create an instance of
<tt>org.apache.pivot.collections.HashMap<String, Object></tt> for each row it
encounters in the CSV stream. However, a caller can specify the name of a different
class using the <tt>setItemClass()</tt> method. This avoids the performance penalty of
@@ -313,7 +313,7 @@ limitations under the License.
<tt>pivot.util.concurrent.Task</tt> class, of which <tt>GetQuery</tt> is a subclass.
<tt>TaskAdapter</tt> is defined in the <tt>pivot.wtk</tt> package and ensures that the
callback occurs on the UI thread (otherwise, the listener is called in the context of
- the background thread).
+ the background thread, which can produce non-deterministic results).
</p>
<p>
@@ -337,23 +337,48 @@ limitations under the License.
<source type="java">
<![CDATA[
+ @Override
public void taskExecuted(Task<Object> task) {
if (task == getQuery) {
- Sequence<Span> selectedRanges = stocksTableView.getSelectedRanges();
+ List<Object> quotes = (List<Object>)task.getResult();
+
+ // Preserve any existing sort and selection
+ Sequence<?> selectedStocks = stocksTableView.getSelectedRows();
+
+ List<Object> tableData = (List<Object>)stocksTableView.getTableData();
+ Comparator<Object> comparator = tableData.getComparator();
+ quotes.setComparator(comparator);
- List<StockQuote> quotes = (List<StockQuote>)task.getResult();
stocksTableView.setTableData(quotes);
- if (selectedRanges.getLength() > 0) {
- stocksTableView.setSelectedRanges(selectedRanges);
+ if (selectedStocks.getLength() > 0) {
+ // Select current indexes of selected stocks
+ for (int i = 0, n = selectedStocks.getLength(); i < n; i++) {
+ Object selectedStock = selectedStocks.get(i);
+
+ int index = 0;
+ for (Object stock : stocksTableView.getTableData()) {
+ String symbol = JSON.getString(stock, "symbol");
+ String selectedSymbol = JSON.getString(selectedStock, "symbol");
+
+ if (symbol.equals(selectedSymbol)) {
+ stocksTableView.addSelectedIndex(index);
+ break;
+ }
+
+ index++;
+ }
+ }
} else {
if (quotes.getLength() > 0) {
stocksTableView.setSelectedIndex(0);
}
}
+ refreshDetail();
+
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,
- DateFormat.MEDIUM, Locale.getDefault());
+ DateFormat.MEDIUM, Locale.getDefault());
lastUpdateLabel.setText(dateFormat.format(new Date()));
getQuery = null;
@@ -368,14 +393,22 @@ limitations under the License.
</p>
<ul>
- <li><p>Caches the current selection state of the quote table view</p></li>
+ <li>
+ <p>
+ Caches the current sort and selection state of the quote table view
+ </p>
+ </li>
<li>
<p>
Gets the result of the query and casts it to the appropriate type
- (<tt>List<StockQuote></tt>)
+ (<tt>List<Object></tt>)
+ </p>
+ </li>
+ <li>
+ <p>
+ Sets the list as the model data for the table view
</p>
</li>
- <li><p>Sets the list as the model data for the table view</p></li>
<li>
<p>
Restores any selection state (which would have been lost when the model data