Author: rwhitcomb Date: Fri Jun 26 23:29:43 2015 New Revision: 1687874 URL: http://svn.apache.org/r1687874 Log: PIVOT-972: Add a small tutorial application: a simple calculator app, to demonstrate keyboard mappings and action mappings.
This is the first cut of the app, which works, except for some issues: * The "Settings" button does nothing. * There are issues with the result overflowing the display sometimes. * Arithmetic errors are not trapped and handled gracefully. * No shortcut key for the "+/-" button. * Would like to "push" the buttons when the corresponding keys are pressed. * No help is available. * There may be a problem with "Clear" when an operator is pending. * There is no check on the input size, which can easily overflow the display area. * Backspace after an operator is chosen may not work correctly. * The native window should be sized nicely to just fit the form. * Needs Javadoc in the code. Add another "famfamfam" icon for use here, and update the NOTICE file. Add several Keyboard.KeyCode values that are used here for available virtual keys. Added: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/Calculator.java pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/asterisk_orange.png (with props) pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/calculator.bxml pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/calculator_styles.json Modified: pivot/trunk/LICENSE pivot/trunk/wtk/src/org/apache/pivot/wtk/Keyboard.java Modified: pivot/trunk/LICENSE URL: http://svn.apache.org/viewvc/pivot/trunk/LICENSE?rev=1687874&r1=1687873&r2=1687874&view=diff ============================================================================== --- pivot/trunk/LICENSE (original) +++ pivot/trunk/LICENSE Fri Jun 26 23:29:43 2015 @@ -354,6 +354,7 @@ The following components are licensed un tutorials/src/org/apache/pivot/tutorials/buttons/resultset_previous.png tutorials/src/org/apache/pivot/tutorials/clock.png tutorials/src/org/apache/pivot/tutorials/cup.png + tutorials/src/org/apache/pivot/tutorials/calculator/asterisk_orange.png tutorials/src/org/apache/pivot/tutorials/explorer/buttons/resultset_next.png tutorials/src/org/apache/pivot/tutorials/explorer/menus/coins.png tutorials/src/org/apache/pivot/tutorials/explorer/navigation/bell.png Added: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/Calculator.java URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/Calculator.java?rev=1687874&view=auto ============================================================================== --- pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/Calculator.java (added) +++ pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/Calculator.java Fri Jun 26 23:29:43 2015 @@ -0,0 +1,364 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pivot.tutorials.calculator; + +import java.io.*; +import java.math.*; +import org.apache.pivot.beans.*; +import org.apache.pivot.collections.*; +import org.apache.pivot.serialization.*; +import org.apache.pivot.wtk.*; + +public class Calculator + extends Application.Adapter +{ + private BXMLSerializer serializer; + + @BXML private Window mainWindow; + @BXML private PushButton settingsButton; + @BXML private Label resultText; + @BXML private PushButton clearButton; + @BXML private PushButton changeSignButton; + @BXML private PushButton percentButton; + @BXML private PushButton divideButton; + @BXML private PushButton multiplyButton; + @BXML private PushButton minusButton; + @BXML private PushButton plusButton; + @BXML private PushButton equalsButton; + @BXML private PushButton zeroButton; + @BXML private PushButton oneButton; + @BXML private PushButton twoButton; + @BXML private PushButton threeButton; + @BXML private PushButton fourButton; + @BXML private PushButton fiveButton; + @BXML private PushButton sixButton; + @BXML private PushButton sevenButton; + @BXML private PushButton eightButton; + @BXML private PushButton nineButton; + @BXML private PushButton dotButton; + + private static MathContext MC = MathContext.DECIMAL64; + + private static StringBuilder resultBuffer = new StringBuilder("0"); + private static BigDecimal result = BigDecimal.ZERO; + private static boolean seenDecimalPoint = false; + private static BigDecimal accumulator = BigDecimal.ZERO; + private static Operator currentOperator = null; + + private static Calculator instance; + + @Override + public void startup(Display display, Map<String, String> properties) { + try { + serializer.readObject(Calculator.class, "calculator.bxml"); + serializer.bind(this); + + mainWindow.getComponentKeyListeners().add(new ComponentKeyListener.Adapter() { + @Override + public boolean keyTyped(Component comp, char ch) { + // Some keys don't give us virtual key mappings, so we need to + // listen here for the relevant keys typed + switch (ch) { + case '*': + changeOperator(Operator.MULTIPLY); + return true; + case '+': + changeOperator(Operator.ADD); + return true; + case '%': + ACTION.PERCENT.perform(comp); + return true; + case '\u0008': + case '\u007F': + ACTION.BACKSPACE.perform(comp); + return true; + default: + return false; + } + } + }); + + mainWindow.open(display); + mainWindow.requestFocus(); + } + catch (IOException | SerializationException ex) { + } + } + + private static void updateResult() { + // TODO: pay attention to scaling, representation + resultBuffer = new StringBuilder(result.toString()); + instance.resultText.setText(resultBuffer.toString()); + } + + private static void digit(char digit) { + switch (digit) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + resultBuffer.append(digit); + break; + case '.': + if (!seenDecimalPoint) { + seenDecimalPoint = true; + resultBuffer.append(digit); + instance.resultText.setText(resultBuffer.toString()); + return; + } + break; + } + result = new BigDecimal(resultBuffer.toString()); + updateResult(); + } + + private enum Operator + { + ADD, + SUBTRACT, + MULTIPLY, + DIVIDE, + EQUALS + } + + private static void changeOperator(Operator newOperator) { + if (currentOperator == null) { + currentOperator = newOperator; + accumulator = result; + resultBuffer = new StringBuilder(); + seenDecimalPoint = false; + // TODO: highlight (somehow) the selected operator + } + else { + // Perform the currentOperator function on accumulator x result + switch (currentOperator) { + case ADD: + result = accumulator.add(result, MC); + break; + case SUBTRACT: + result = accumulator.subtract(result, MC); + break; + case MULTIPLY: + result = accumulator.multiply(result, MC); + break; + case DIVIDE: + result = accumulator.divide(result, MC); + break; + } + if (newOperator == Operator.EQUALS) { + currentOperator = null; + accumulator = BigDecimal.ZERO; + } + else { + currentOperator = newOperator; + accumulator = result; + } + seenDecimalPoint = false; + updateResult(); + resultBuffer = new StringBuilder(); + } + } + + private interface ActionDoer + { + void perform(Component source); + } + + private enum ACTION implements ActionDoer + { + DOT { + @Override + public void perform(Component source) { + digit('.'); + } + }, + ZERO { + @Override + public void perform(Component source) { + digit('0'); + } + }, + ONE { + @Override + public void perform(Component source) { + digit('1'); + } + }, + TWO { + @Override + public void perform(Component source) { + digit('2'); + } + }, + THREE { + @Override + public void perform(Component source) { + digit('3'); + } + }, + FOUR { + @Override + public void perform(Component source) { + digit('4'); + } + }, + FIVE { + @Override + public void perform(Component source) { + digit('5'); + } + }, + SIX { + @Override + public void perform(Component source) { + digit('6'); + } + }, + SEVEN { + @Override + public void perform(Component source) { + digit('7'); + } + }, + EIGHT { + @Override + public void perform(Component source) { + digit('8'); + } + }, + NINE { + @Override + public void perform(Component source) { + digit('9'); + } + }, + ADD { + @Override + public void perform(Component source) { + changeOperator(Operator.ADD); + } + }, + SUBTRACT { + @Override + public void perform(Component source) { + changeOperator(Operator.SUBTRACT); + } + }, + MULTIPLY { + @Override + public void perform(Component source) { + changeOperator(Operator.MULTIPLY); + } + }, + DIVIDE { + @Override + public void perform(Component source) { + changeOperator(Operator.DIVIDE); + } + }, + EQUALS { + @Override + public void perform(Component source) { + changeOperator(Operator.EQUALS); + } + }, + NEGATE { + @Override + public void perform(Component source) { + result = result.negate(MC); + updateResult(); + } + }, + PERCENT { + @Override + public void perform(Component source) { + result = result.scaleByPowerOfTen(-2); + updateResult(); + } + }, + BACKSPACE { + @Override + public void perform(Component source) { + if (resultBuffer.length() > 0 && !resultBuffer.toString().equals("0")) { + char ch = resultBuffer.charAt(resultBuffer.length() - 1); + if (ch == '.') { + seenDecimalPoint = false; + } + resultBuffer.deleteCharAt(resultBuffer.length() - 1); + instance.resultText.setText(resultBuffer.toString()); + } + } + }, + CLEAR { + @Override + public void perform(Component source) { + result = BigDecimal.ZERO; + seenDecimalPoint = false; + accumulator = BigDecimal.ZERO; + currentOperator = null; + updateResult(); + } + }, + QUIT { + @Override + public void perform(Component source) { + DesktopApplicationContext.exit(); + } + } + } + + private class CalculatorAction extends Action + { + private ACTION action; + + public CalculatorAction(ACTION action) { + this.action = action; + Action.getNamedActions().put(action.toString(), this); + } + + @Override + public void perform(Component source) { + action.perform(source); + } + } + + + public void registerActions() { + for (ACTION act : ACTION.values()) { + new CalculatorAction(act); + } + } + + public Calculator() { + serializer = new BXMLSerializer(); + ApplicationContext.applyStylesheet("@org/apache/pivot/tutorials/calculator/calculator_styles.json"); + registerActions(); + instance = this; + } + + public static void main(String[] args) { + DesktopApplicationContext.main(Calculator.class, args); + } + +} + Added: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/asterisk_orange.png URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/asterisk_orange.png?rev=1687874&view=auto ============================================================================== Binary file - no diff available. Propchange: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/asterisk_orange.png ------------------------------------------------------------------------------ svn:executable = * Propchange: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/asterisk_orange.png ------------------------------------------------------------------------------ svn:mime-type = application/octet-stream Added: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/calculator.bxml URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/calculator.bxml?rev=1687874&view=auto ============================================================================== --- pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/calculator.bxml (added) +++ pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/calculator.bxml Fri Jun 26 23:29:43 2015 @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to you under the Apache License, +Version 2.0 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<Window title="Calculator" + maximized="true" + bxml:id="mainWindow" + xmlns:bxml="http://pivot.apache.org/bxml" + xmlns:content="org.apache.pivot.wtk.content" + xmlns="org.apache.pivot.wtk"> + <actionMappings> + <Window.ActionMapping action="ZERO" keyStroke="N0" /> + <Window.ActionMapping action="ONE" keyStroke="N1" /> + <Window.ActionMapping action="TWO" keyStroke="N2" /> + <Window.ActionMapping action="THREE" keyStroke="N3" /> + <Window.ActionMapping action="FOUR" keyStroke="N4" /> + <Window.ActionMapping action="FIVE" keyStroke="N5" /> + <Window.ActionMapping action="SIX" keyStroke="N6" /> + <Window.ActionMapping action="SEVEN" keyStroke="N7" /> + <Window.ActionMapping action="EIGHT" keyStroke="N8" /> + <Window.ActionMapping action="NINE" keyStroke="N9" /> + <Window.ActionMapping action="DOT" keyStroke="PERIOD" /> + <Window.ActionMapping action="ADD" keyStroke="PLUS" /> + <Window.ActionMapping action="ADD" keyStroke="ADD" /> + <Window.ActionMapping action="SUBTRACT" keyStroke="MINUS" /> + <Window.ActionMapping action="SUBTRACT" keyStroke="SUBTRACT" /> + <Window.ActionMapping action="MULTIPLY" keyStroke="MULTIPLY" /> + <Window.ActionMapping action="MULTIPLY" keyStroke="X" /> + <Window.ActionMapping action="MULTIPLY" keyStroke="ASTERISK" /> + <Window.ActionMapping action="DIVIDE" keyStroke="DIVIDE" /> + <Window.ActionMapping action="DIVIDE" keyStroke="SLASH" /> + <Window.ActionMapping action="EQUALS" keyStroke="EQUALS" /> + <Window.ActionMapping action="EQUALS" keyStroke="ENTER" /> + <Window.ActionMapping action="BACKSPACE" keyStroke="BACKSPACE" /> + <Window.ActionMapping action="CLEAR" keyStroke="C" /> + <Window.ActionMapping action="QUIT" keyStroke="Q" /> + <Window.ActionMapping action="QUIT" keyStroke="Alt-F4" /> + <Window.ActionMapping action="QUIT" keyStroke="Cmd-Q" /> + </actionMappings> + <TablePane bxml:id="mainTablePane"> + <columns> + <TablePane.Column width="1*"/> + </columns> + <rows> + <TablePane.Row height="-1"> + <TablePane> + <columns> + <TablePane.Column width="-1"/> + <TablePane.Column width="1*"/> + </columns> + <rows> + <TablePane.Row height="-1"> + <BoxPane styles="{horizontalAlignment:'left',backgroundColor:'black'}"> + <PushButton bxml:id="settingsButton"> + <content:ButtonData icon="@asterisk_orange.png"/> + </PushButton> + </BoxPane> + <BoxPane styles="{padding:6,horizontalAlignment:'right',backgroundColor:'black'}"> + <Label bxml:id="resultText" text="0" styleName="resultLabel"/> + </BoxPane> + </TablePane.Row> + </rows> + </TablePane> + </TablePane.Row> + <TablePane.Row height="-1"> + <TablePane> + <columns> + <TablePane.Column width="1*"/> + <TablePane.Column width="1*"/> + <TablePane.Column width="1*"/> + <TablePane.Column width="1*"/> + </columns> + <rows> + <TablePane.Row height="-1"> + <PushButton bxml:id="clearButton" buttonData="C" action="CLEAR" styleName="greyButton"/> + <PushButton bxml:id="changeSignButton" buttonData="+/−" action="NEGATE" styleName="greyButton"/> + <PushButton bxml:id="percentButton" buttonData="%%" action="PERCENT" styleName="greyButton"/> + <PushButton bxml:id="divideButton" buttonData="÷" action="DIVIDE" styleName="orangeButton"/> + </TablePane.Row> + <TablePane.Row height="-1"> + <PushButton bxml:id="sevenButton" buttonData="7" action="SEVEN" styleName="whiteButton"/> + <PushButton bxml:id="eightButton" buttonData="8" action="EIGHT" styleName="whiteButton"/> + <PushButton bxml:id="nineButton" buttonData="9" action="NINE" styleName="whiteButton"/> + <PushButton bxml:id="multiplyButton" buttonData="×" action="MULTIPLY" styleName="orangeButton"/> + </TablePane.Row> + <TablePane.Row height="-1"> + <PushButton bxml:id="fourButton" buttonData="4" action="FOUR" styleName="whiteButton"/> + <PushButton bxml:id="fiveButton" buttonData="5" action="FIVE" styleName="whiteButton"/> + <PushButton bxml:id="sixButton" buttonData="6" action="SIX" styleName="whiteButton"/> + <PushButton bxml:id="minusButton" buttonData="−" action="SUBTRACT" styleName="orangeButton"/> + </TablePane.Row> + <TablePane.Row height="-1"> + <PushButton bxml:id="oneButton" buttonData="1" action="ONE" styleName="whiteButton"/> + <PushButton bxml:id="twoButton" buttonData="2" action="TWO" styleName="whiteButton"/> + <PushButton bxml:id="threeButton" buttonData="3" action="THREE" styleName="whiteButton"/> + <PushButton bxml:id="plusButton" buttonData="+" action="ADD" styleName="orangeButton"/> + </TablePane.Row> + <TablePane.Row height="-1"> + <PushButton TablePane.columnSpan="2" bxml:id="zeroButton" action="ZERO" buttonData="0" styleName="whiteButton"/> + <TablePane.Filler/> + <PushButton bxml:id="dotButton" buttonData="∙" action="DOT" styleName="whiteButton"/> + <PushButton bxml:id="equalsButton" buttonData="=" action="EQUALS" styleName="orangeButton"/> + </TablePane.Row> + </rows> + </TablePane> + </TablePane.Row> + </rows> + </TablePane> +</Window> + Added: pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/calculator_styles.json URL: http://svn.apache.org/viewvc/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/calculator_styles.json?rev=1687874&view=auto ============================================================================== --- pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/calculator_styles.json (added) +++ pivot/trunk/tutorials/src/org/apache/pivot/tutorials/calculator/calculator_styles.json Fri Jun 26 23:29:43 2015 @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +{ + resultLabel : { + color : 'white', + backgroundColor : 'black', + font : 'Sans Serif 40', + }, + + greyButton : { + backgroundColor: '#CDCDCD', + color : 'black', + font : 'Verdana 24', + padding : { top : 8, bottom : 8 }, + }, + + orangeButton : { + backgroundColor : '#FF9901', + color : 'black', + font : 'Verdana 24', + padding : { top : 8, bottom : 8 }, + }, + + whiteButton : { + backgroundColor : '#F7F7F7', + color : 'black', + font : 'Verdana 24', + padding : { top : 8, bottom : 8 }, + }, + +} + Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/Keyboard.java URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/Keyboard.java?rev=1687874&r1=1687873&r2=1687874&view=diff ============================================================================== --- pivot/trunk/wtk/src/org/apache/pivot/wtk/Keyboard.java (original) +++ pivot/trunk/wtk/src/org/apache/pivot/wtk/Keyboard.java Fri Jun 26 23:29:43 2015 @@ -194,6 +194,8 @@ public final class Keyboard { public static final int N8 = KeyEvent.VK_8; public static final int N9 = KeyEvent.VK_9; + public static final int PERIOD = KeyEvent.VK_PERIOD; + public static final int TAB = KeyEvent.VK_TAB; public static final int SPACE = KeyEvent.VK_SPACE; public static final int ENTER = KeyEvent.VK_ENTER; @@ -237,6 +239,9 @@ public final class Keyboard { public static final int MULTIPLY = KeyEvent.VK_MULTIPLY; public static final int DIVIDE = KeyEvent.VK_DIVIDE; + public static final int SLASH = KeyEvent.VK_SLASH; + public static final int ASTERISK = KeyEvent.VK_ASTERISK; + public static final int F1 = KeyEvent.VK_F1; public static final int F2 = KeyEvent.VK_F2; public static final int F3 = KeyEvent.VK_F3;