ProgrammaticGUI
Aus ExpeccoWiki
Inhaltsverzeichnis |
Motivation
Using JavaScript or Smalltalk code, almost any kind of GUI can be generated. Even hightly dynamic ones are possible - for example, where the look or layout depends upon varying input values. This simple example should give you an entry to GUI programming in expecco. For more information, please refer to the "Smalltalk/X online documentation" .
In the following examples, we will develop a programmed 2-pane selection-in-list dialog. The code for the examples is found in the testSuite "ProgrammaticGUIs.ets" (see link at the end).
Opening a GUI in an Elementary Block
Let's start with a block which opens a very simple, empty window:
execute() {
var topView;
topView = new StandardSystemView;
topView.label("My First Window");
topView.openModal();
//
// arrive here, when the topView is closed
//
halt();
}
[ SimpleView1-block ]
This uses an instance of the StandardSystemView class (which is a standard Smalltalk window class) as a container.
You can of course pass the window's label via an input pin (in this case, the pin named "labelInput"). Also, the windows initial size can be specified via calls to the width() and height() functions:
execute() {
var topView;
topView = new StandardSystemView;
topView.label( labelIn.value() );
topView.width( 300 );
topView.height( 250 );
topView.openModal();
//
// arrive here, when the topView is closed
//
}
[ SimpleView2-block ]
Adding a Panel for Buttons
The next example adds a panel, containing two buttons, for "OK" and "Cancel". Please notice the use of a layout object to specify the panel's dimension. Layout objects define a views' dimension as a combination of a relative (to the container's size) and an offset part. Here, we want the panel to occupy a 30 pixel high strip at the bottom of the topView (topFraction=1, topOffset=-30). Layout objects are described in "Smalltak/X View Programming" .
The button-actions are defined as callback functions. Both buttons close the top-window:
execute() {
var topView, panel;
var okButton, cancelButton;
var layout;
var okPressed = false;
topView = new StandardSystemView;
topView.label( labelIn.value() );
topView.width( 300 );
topView.height( 250 );
panel = new HorizontalPanelView;
layout = new LayoutFrame;
layout.topFraction( 1.0 );
layout.topOffset( -30 );
layout.leftFraction( 0.0 );
layout.bottomFraction( 1.0 );
layout.rightFraction( 1.0 );
panel.layout ( layout );
topView.add( panel );
okButton = new Button;
okButton.label( "OK" );
panel.add( okButton );
cancelButton = new Button;
cancelButton.label( "Cancel" );
panel.add( cancelButton );
function okCallBack() {
okPressed = true;
topView.close();
};
function cancelCallBack() {
topView.close();
};
okButton.action ( okCallBack );
cancelButton.action ( cancelCallBack );
topView.openModal();
}
[ SimpleView3-block ]
Adding a SelectionList
Next, we add a selectionList in the top area. Its layout is defined to occupy the whole window area, except for the bottom 30 pixels (the panel's area). The list's contents is defined to be input via a "listIn" pin. Whenever the user changes the selection, another callback function is invoked.
execute() {
var topView, panel;
var okButton, cancelButton;
var selectionInListView;
var layout1, layout2;
var okPressed = false;
topView = new StandardSystemView;
topView.label( labelIn.value() );
topView.width( 300 );
topView.height( 250 );
panel = new HorizontalPanelView;
layout1 = new LayoutFrame;
layout1.topFraction( 1.0 );
layout1.topOffset( -30 );
layout1.leftFraction( 0.0 );
layout1.bottomFraction( 1.0 );
layout1.rightFraction( 1.0 );
panel.layout ( layout1 );
topView.add( panel );
okButton = new Button;
okButton.label( "OK" );
panel.add( okButton );
cancelButton = new Button;
cancelButton.label( "Cancel" );
panel.add( cancelButton );
function okCallBack() {
okPressed = true;
topView.close();
};
function cancelCallBack() {
topView.close();
};
okButton.action ( okCallBack );
cancelButton.action ( cancelCallBack );
selectionInListView = new SelectionInListView;
layout2 = new LayoutFrame;
layout2.topFraction( 0.0 );
layout2.leftFraction( 0.0 );
layout2.bottomFraction( 1.0 );
layout2.bottomOffset( -30 );
layout2.rightFraction( 1.0 );
selectionInListView.layout ( layout2 );
selectionInListView.list ( listIn.value );
function selectionCallBack( selIndex ) {
Transcript.showCR( selIndex );
};
selectionInListView.action ( selectionCallBack );
topView.add( selectionInListView );
topView.openModal();
}
[ SimpleView4-block ]
Finally, we add an output pin ("selectedValueOut"), and send the selected entry to it, when the user has pressed the "OK"-button. Notice, that we pass the list's selected value, not the index of the selected line:
execute() {
var topView, panel;
var okButton, cancelButton;
var selectionInListView;
var layout1, layout2;
var okPressed = false;
var selectedLineNumber, selectedLine;
topView = new StandardSystemView;
topView.label( labelIn.value() );
topView.width( 300 );
topView.height( 250 );
panel = new HorizontalPanelView;
layout1 = new LayoutFrame;
layout1.topFraction( 1.0 );
layout1.topOffset( -30 );
layout1.leftFraction( 0.0 );
layout1.bottomFraction( 1.0 );
layout1.rightFraction( 1.0 );
panel.layout ( layout1 );
topView.add( panel );
okButton = new Button;
okButton.label( "OK" );
panel.add( okButton );
cancelButton = new Button;
cancelButton.label( "Cancel" );
panel.add( cancelButton );
function okCallBack() {
okPressed = true;
topView.close();
};
function cancelCallBack() {
topView.close();
};
okButton.action ( okCallBack );
cancelButton.action ( cancelCallBack );
selectionInListView = new SelectionInListView;
layout2 = new LayoutFrame;
layout2.topFraction( 0.0 );
layout2.leftFraction( 0.0 );
layout2.bottomFraction( 1.0 );
layout2.bottomOffset( -30 );
layout2.rightFraction( 1.0 );
selectionInListView.layout ( layout2 );
selectionInListView.list ( listIn.value );
function selectionCallBack( arg ) {
// Transcript.showCR( arg );
selectedLineNumber = arg;
};
selectionInListView.action ( selectionCallBack );
topView.add( selectionInListView );
topView.openModal();
//
// arrive here, after the view has been closed
//
if (okPressed) {
selectedLine = listIn.value[ selectedLineNumber ];
selectedValueOut.value( selectedLine );
}
}
[ SelectionInList1-block ]
Bells & Whistles
To make the dialog look better, you can have the panel to resize its button-components to the same (max-) size:
...
panel.horizontalLayout ( "centerMax".asSymbol );
...
[ SelectionInList2-block ]
wrap the list into a scrollable view:
...
scrollableView = new HVScrollableView;
scrollableView.scrolledView( selectionInListView );
scrollableView.layout( layout2 );
topView.add( scrollableView );
...
[ SelectionInList2-block ]
and allow for doubleClick in the list:
...
function doubleClickCallBack( arg ) {
// Transcript.showCR( arg );
selectedLineNumber = arg;
okPressed = true;
topView.close();
};
selectionInListView.doubleClickAction ( doubleClickCallBack );
...
[ SelectionInList2-block ]
and add another output pin, which gets triggered when the "Cancel"-button was pressed.
Two Lists, Side by Side
Adding a second list is easy. First, change the first lists dimension, to only occupy half of the container's width (hint: set its rightFraction to 0.5). Then, add a second selectionInListView, which occupies the right half of its container's area (set its leftFraction to 0.5, and its rightFraction to 1.0). Setup the callbacks and selectionHolders as above.
Download
Here is the project to be loaded into expecco: [ProgrammaticGUIs.ets]
Back to Examples.