We often show screenshots of Glamorous Toolkit in our communication. For example, consider this one:
This screenshot contains an elaborate scene with three panes. In the first pane, we have a Coder on the GtLudoGame
Object subclass: #GtLudoGame
instanceVariableNames: 'players squares startSquares goalSquares die announcer feedback winner needToRollDie lastDieRolled playerQueue routeCache'
classVariableNames: ''
package: 'GToolkit-Demo-Ludo-Model'
class, in which we have expanded GtLudoGame>>#moveTokenNamed:
moveTokenNamed: aTokenName
^ self moveToken: (self tokenNamed: aTokenName)
method and in that method we expanded a message. Then we looked for references by pressing a shortcut and this produced a second pane with a filter tool. In that filter tool we scrolled down to GtLudoRecordingGameExamples>>#gameShowingAllMoves2
gameShowingAllMoves2
<gtExample>
| game gameMoves |
game := self gameShowingAllMoves1.
gameMoves := game moves size.
game
roll: 6;
moveTokenNamed: 'D'.
self assert: game moves size - gameMoves = 1.
self assert: game moves last numberOfTokensMoved = 1.
^ game
, a method that happens to be an example (which is like a test that returns an object). We expanded the method and we executed the example by clicking on the button from the contextual toolbar of the method coder. This then produced the third pane containing an inspector with the result. And a little detail: as the mouse hovered over the button there is also a tooltip associated with that button.
These actions can be produced manually. But a proper graphical stack should allow us to simulate manual interactions programmatically, too. In Glamorous Toolkit we can.
For example, the script that produces a scene like in the picture from above is found in GtTour>>#scripterWithElaborateScenario
scripterWithElaborateScenario
<gtExample>
| scripter pageWidth moveTokenMethod allMovesMethod |
scripter := BlScripter new.
scripter do
block: [ :aSpace | aSpace extent: 1600 @ 870 ];
onSpace;
play.
moveTokenMethod := GtLudoGame >> #moveTokenNamed:.
scripter memoryLogger
runFor: self beaconSignals
during: [ :aStep |
aStep element: (GtPager createWrappedOn: (GtCoder forMethod: moveTokenMethod)) ].
pageWidth := scripter space width / 3.
scripter do
block: [ :aPage | aPage width: pageWidth ];
// (GtPagerPageElementId indexed: 1);
play.
scripter methodCoders
forCompiledMethod: moveTokenMethod do: [ :aMethodCoderStep |
aMethodCoderStep shortcut
combination: BlKeyCombination primaryN;
// GtSourceCoderEditorId;
onChildAt: 1;
onChildAt: 1.
aMethodCoderStep
clickOnMethodCoderExpander: 1 insideDo: [ :aStep1 | ] ];
// (GtPagerPageElementId indexed: 1);
// GtPharoStreamingMethodsCoderElement;
play.
scripter do
block: [ :aPage | aPage width: pageWidth ];
// (GtPagerPageElementId indexed: 2);
play.
allMovesMethod := GtLudoRecordingGameExamples >> #gameShowingAllMoves2.
scripter methodCoders
expandAndFocusCompiledMethod: allMovesMethod;
scrollToCompiledMethod: allMovesMethod;
forCompiledMethod: allMovesMethod do: [ :aMethodCoderStep |
aMethodCoderStep
clickOnPlayAndInspectExampleButton ];
// (GtPagerPageElementId indexed: 2);
// GtPharoStreamingMethodsCoderElement;
play.
scripter do
block: [ :aPage | aPage width: pageWidth ];
// (GtPagerPageElementId indexed: 3);
play.
scripter methodCoders
forCompiledMethod: allMovesMethod do: [ :aMethodCoderStep |
aMethodCoderStep
clickOnMethodCoderExpander: 1 insideDo: [ :aStep1 | ] ];
scrollToCompiledMethod: allMovesMethod;
forCompiledMethod: allMovesMethod do: [ :aMethodCoderStep |
aMethodCoderStep mouseMoveOver
// GtMethodCoderPlayAndInspectExampleActionId;
onTopMost ];
// (GtPagerPageElementId indexed: 2);
// GtPharoStreamingMethodsCoderElement;
play.
^ scripter
. The script makes use of BlScripter
Object subclass: #BlScripter
uses: TBlDevScripterActionStep + TBlDevScripterCheckStepCreation
instanceVariableNames: 'element space events rootStep eventHandler maxPulseElapsedTime'
classVariableNames: ''
package: 'Bloc-Scripter-Scripter'
, a scripting engine for the graphical stack. Scripter offers an an API that provides default abilities for searching for elements and for simulating actions (such as click
or shortcut
). The API is also extensible with higher level queries, such as methodCoder
which gives us more concise abilities to work with a tool like Coder. For example, we can ask quite concisely a methodCoder
to clickOnPlayAndInspectExampleButton
.
The above script is quite elaborate. But the good news is that you can build it iteratively. Here is how it looks like in the editor that we used to write this very article. Executing the code from the snippet simply shows an inspector on the BlScripter
Object subclass: #BlScripter
uses: TBlDevScripterActionStep + TBlDevScripterCheckStepCreation
instanceVariableNames: 'element space events rootStep eventHandler maxPulseElapsedTime'
classVariableNames: ''
package: 'Bloc-Scripter-Scripter'
instance. And this inspector shows multiple dedicated views, one of which is the preview of the resulting scene.