Sunday, July 02, 2006

Calculator Widgets for GWT

One of the latest additions to the GWT Widget Library (GWT-WL) is a small set of calculator tools and widgets. This article will look at how you can use them in your project, beginning with the simplest use case and then working our way to building custom implementations.

PopupCalcButton

The PopupCalcButton is a widget that allows you to add a pop-up calculator tool to any TextBox. The code for this is very compact, so the easiest way to describe it's usage is to start with an example (or view the working demo).

TextBox calcTextBox = new TextBox();
Button showCalcBtn = new Button("Show Calc");
PopupCalcPanel calc =
new PopupCalcPanel(calcTextBox, showCalcBtn);

RootPanel.get().add(calcTextBox);
RootPanel.get().add(showCalcBtn);
RootPanel.get().add(calc);

We need to create a TextBox widget to recieve the calculated value, and a Button to activate the pop-up calculator. The flow of events starts when a user clicks the "Show Calc" button, which will display the calculator just below the TextBox widget. The user can use the calculator to calculate a result, then clicks a button in the calcluator labeled "DONE" to close the calculator and copy the result to our TextArea.

The PopupCalcPanel requires a reference to both the TextBox and Botton widgets. It will add event handlers to the Button for the calculator to appear, and uses the TextBox for positioning and to place the calculated total. The PopupCalcPanel provides no other options other than CSS tweaks.

The GWT-WL includes a single default CSS stylesheet for all calculator widgets shipped with the library. The stylesheet includes all of the style classes used by the widgets. To use the default you will need to add the following in the of your HTML code.

<link rel="stylesheet" type="text/css"
href="style/gwl-calcPanel.css">

SimpleCalcPanel

The SimpleCalcPanel uses a variation of the Template design pattern, and is meant to allow for easy customization by extending the class. By default it will render a TextBox, the calculator display, and a simple calculator keypad. You may make come cosmetic changes to the calculator using CSS without having to write your own subclass.

SimpleCalcPanel calcPanel = new SimpleCalcPanel();
RootPanel.get("example").add(calcPanel);

The SimpleCalcPanel has two public methods, getValue() and clearValue(). All other methods are protected, and cannot be accessed without subclassing the widget.

Extending SimpleCalcPanel

You can customize the SimpleCalcPanel by subclassing it and overriding one or more of it's methods. When the class is created it internally calls the init() method. You may override the init method, but in most case this is not recommended. The init() method for SimpleCalcPanel is below.

protected void init ()
{
TextBox textDisplay = createTextDisplay();
createCalc(createDisplayListener(textDisplay), textDisplay);
setKeyboardListener(textDisplay);
initComplete();
}

The init() method itself does very little work, and delegates the setup work to other methods in the class. Depending on what you are trying to do, you will override the methods that init() calls so that you can customize some part of the setup. You may, for example, want to use your own KeyboardListener for custom shortcuts, or use a text display other then a simple TextBox so that you can color the output red for negative numbers.

One of the methods that can be overridden is the buildCalcLayout(CalcEngine, TextBox) method. This method is called as part of the initialization, but not by init() directly. It is called as part of the createCalc() call. This method allows you to create the widgets that make up the display, including buttons, panels, and any other widgets you need. The only widget that you won't create is the TextBox calculator display, which is passed to the method. If you override this method you will need to add the TextBox to the panel yourself, or call the method in the super class.

Here is an extended example which extends the SimpleCalcPanel by adding a Hex/Decimal selector. This requires a custom TextBox for the display which can be toggled to show hex or decimal values. We also override the buildCalcLayout method so that we can add the new controls.


package org.hanson.gwt.client;

import org.gwtwidgets.client.ui.SimpleCalcPanel;
import org.gwtwidgets.client.util.CalcEngine;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;


public class MyApplication implements EntryPoint
{

public void onModuleLoad ()
{
RootPanel.get().add(new HexCalcPanel());
}


class HexCalcDisplay extends TextBox
{
public boolean isHexMode = false;

public void setText (String text)
{
if (isHexMode) {
double num = Double.parseDouble(text);
String hexString = Integer.toHexString((int) num);
hexString = hexString.toUpperCase();
super.setText(hexString);
}
else {
super.setText(text);
}
}
}

class HexCalcPanel extends SimpleCalcPanel
{
private HexCalcDisplay textBox;

protected TextBox createTextDisplay ()
{
textBox = new HexCalcDisplay();
textBox.setStyleName("simpleCalcDisplay");
return textBox;
}

protected void buildCalcLayout (
final CalcEngine calcEngine,
TextBox textDisplay)
{
RadioButton dec = new RadioButton("output", "Dec");
dec.setChecked(true);
dec.addClickListener(new ClickListener(){
public void onClick (Widget sender)
{
textBox.isHexMode = false;
calcEngine.refreshDisplay();
}
});

RadioButton hex = new RadioButton("output", "Hex");
hex.addClickListener(new ClickListener(){
public void onClick (Widget sender)
{
textBox.isHexMode = true;
calcEngine.refreshDisplay();
}
});

HorizontalPanel opts = new HorizontalPanel();
opts.add(dec);
opts.add(hex);
add(opts);

super.buildCalcLayout(calcEngine, textDisplay);
}
}
}



The CalcEngine

In some cases you will need to customize the control beyond what the SimpleCalcPanel allows. At the very heart of the calculator is the CalcEngine. The CalcEngine keeps track of the state of the calculator, the contents of the display, and the contents of the total register. It allows you to override methods for many of the operators, and gives you a programmatic way to refresh the display. It also has utility methods to adding number and operator buttons to your display. It is in every sense the heart of the system.

I believe that in most cases you will not need to override any of the CalcEngine methods, more likely you will use it as the core of your own calculator panel. In the future you can expect to see new functions added here in the core, which can then be used by calculator panels. The obvious enhancements include the addition of trigonometric and algebraic functions.

Conclusion

The calculator widget and engine set allows for ease of use, ease of extension, and the ability to create highly customized calculators. Future extensions will expand on it's potential, hopefully leading to productive components.

14 comments:

Anonymous said...

Probably title shoud be "Calculator..."

Anonymous said...

How about using some tool that can add colors for Java source? For example Colorer. It is hard to read plain text.

Robert Hanson said...

> Probably title shoud be "Calculator..."

Err... yeah... oops.

CoralPoetry said...

Hi,

Nothing beats a minature solid gold abacus.

Coral

Anonymous said...

Try 3+2*3 and see the result ... you should not rely so much on this "calculator" !

Robert Hanson said...

Is that your idea of a bug report? If it is, there is a facility for submitting these, http://sourceforge.net/tracker/?group_id=169692. You also forgot to mention what you feel is the right answer? Is it 11 or 15?

Seriously "Anonymous", if you are interested in using an open source beta product it is more productive to help solve problems than it is to dismiss the product.

Anonymous said...

I think you're making a worse mistake in being dismissive of Anonymous's very valid point - this calculator does NOT behave like a 'normal' calculator, so why trust or use it? A normal (non-RPN anyway) calculator will obviously compute 3+2*3 as: 3+2 makes 5, times 3 is 15. Also, why not allow the user to simply type a number and press DONE, and why not allow them to type a number, hit equal and type DONE? It is still a textbox - an input field after all. P.S. I'm not the same anonymous, and certainly don't see why you can't make use of (I think) constructive comments entered here rather than 'through the right channels'.

Robert Hanson said...

I wasn't trying to be dismissive, and I don't even disagree. I just didn't really like the tone.

From my point of view I spent a lot of time building something and gave it away for free.

I appreciate *constructive* comments, bug reports, and patches. I don't believe the comment in question falls into any of these categories.

As for your commets, I can't say that I disagree. It has been a long time since I even looked at that code, and it probably needs some fixing.

Currently I am ankle deep in Gears ORM, viewports, book promotion, my regular job, and then the "family" time on top of all of that, so it may be some time before I get to look at it.

But this leads me back to what I said before... patches are always welcome, and I always attribute the patch to the patch author.

obdobion said...

I will be uploading the first release of "algebrain" to sourceforge.net over the weekend. I think it would make a real powerful "CalcEngine" for this widget. I don't have the GWT experience to do it but maybe someone who does have that experience might give it a try.

Robert Hanson said...

Thanks for the info Chris, but I think that might be problematic. The issue is that algebrain is released under the GPL, and the GWT-WL is under Apache 2. The two are pretty incompitable, and I am not sure I want to get involved with dual licensing.

Anonymous said...

At the beginning of this post, "set of calendar tools" should be "set of calculator tools".

Anonymous said...

Also, in the last paragraph of your demo page, "customize your calendar" should be "customize your calendar".

Robert Hanson said...

Thanks Damon, noted and fixed.

Anonymous said...
This comment has been removed by a blog administrator.