[<< wikibooks] Canvas 2D Web Apps/Responsive Buttons
This chapter extends the chapter on static buttons to cover responsive buttons that change their appearance as users click them or move the mouse pointer over them.


=== The Example ===
The example of this chapter (which is available online; also as a downloadable version) extends the example of the chapter on static buttons by using one of three images (“normal,” “focused,” and “pressed”) for rendering buttons depending on their current state. Thus, the example also has to include a cuiButton object for each button, which keeps track of its state. 
In spite of these changes, the basic structure of the example is still the same: the function myPageProcess repaints the canvas and processes events while calling each button's process function to handle its repainting and the processing of events that affect it. 


=== Discussion ===
This discussion assumes that you have read the chapter on static buttons and focuses on differences to the example presented in that chapter.
There are now three images (imageNormalButton, imageFocusedButton, and imagePressedButton). We set onload to cuiRepaint() for all of them to make sure that they are correctly rendered as soon as they are loaded.
The code no longer sets myPage.isDraggableWithOneFinger to false because we don't let users change the color to black by clicking on the background and can therefore allow them to drag the page instead.
The main new feature of this example are three cuiButton objects:

Note that these objects constructors have no arguments because the objects all start in the “normal” state and their appearance is defined by the call to their process function. For the same reasons, most of the constructors of GUI elements in cui2d have no arguments. (cuiPage is an exception, mainly because its process function is only called internally in the render loop of cui2d.)
The three buttons are rendered and processed by calling their process function:

A button's process function either repaints the button (for null == event) or tries to process the event (for null != event). It returns true if the event has been processed. In this case, we check whether the button is clicked with the isClicked() method. If this is the case, myColor is set accordingly and cuiRepaint() is called to request a repaint of the canvas.


=== Stepping Back ===
At this point, it might be worthwhile to step back a bit and look at the structure of the code. The general structure of a call to a widget in cui2d is:

var  = new ();

...
if (.process(, )) {



return true;

}

The important point is that all the relevant information is in one place:

The layout of the widget on the page is specified by the configuration arguments of the widget's process function
The appearance of the widget is also specified by the configuration arguments.
The reaction to any widget events by the program is specified right after the call to the widget's process function.The only part that is not in the same place is the call to the constructor. However, that call usually provides no relevant information since most widget constructors in cui2d have no arguments. Thus, all the relevant parts are together in one place in the code.
Compare this with standard GUI programming where it is considered good programming style to separate the definition of the layout from the definition of the appearance and from the event handling — often even across different files. While there might be good arguments to separate these things, it makes code necessarily more difficult to read and to change; thus, prototyping is more difficult, which is likely to result in less prototyping and, therefore, in worse products.


=== Implementation of cuiButton ===
The cuiButton type is implemented in cui2d.js. It defines a constructor without arguments, which creates a new object in the initial state. This initial state is always the state before users have interacted with the object:

Then a function to check whether the button has been clicked is defined:

Furthermore, a process function is defined. For event == null this is just a function for repainting the button. For event != null it is probably best to think of it as the step of an automaton; i.e., based on the current state of the button (as defined in this) and the event, a new state is set. Note that it is usually best to let the top-level if statements distinguish between different values of the variables in this (for example: if (!this.isPointerInside && !this.isPointerDown)) because this represents the state of the button. Only inside such if statements should the code distinguish between different kinds of events (for example: if (isIn && ("touchend" == event.type))). This structure allows programmers to easily check that all states and all relevant events for all states are covered. Using this structure in combination with appropriate indentation also allows readers to easily identify a certain state and all transitions from that state — which is exactly what a graphical state-transition diagram is also good for. (In fact, if the code describing the transitions is well structured, well commented, and well formatted, it might be almost as readable as a state-transition diagram but it has the advantage of being machine-readable.)

< Canvas 2D Web Apps