--- a/ShowMeHowItWorks.st Mon Oct 14 14:49:21 2019 +0200
+++ b/ShowMeHowItWorks.st Mon Oct 14 20:55:04 2019 +0200
@@ -95,6 +95,29 @@
"Modified (comment): / 23-07-2019 / 10:26:42 / Claus Gittinger"
!
+do:specArray from:startLabelOrNil withUI:withUIBoolean
+ "spec contains a list of action commands (show: / moveTo: etc.)"
+
+ self new
+ do:specArray
+ from:startLabelOrNil
+ withUI:withUIBoolean
+
+ "
+ ShowMeHowItWorks
+ do:#(
+ (language: de)
+ (show: 'üben üben üben')
+ (wait: 0.5)
+ (moveTo: NameOfComponent)
+ )
+ withUI:true
+ "
+
+ "Created: / 19-07-2019 / 10:52:59 / Claus Gittinger"
+ "Modified (comment): / 23-07-2019 / 10:26:42 / Claus Gittinger"
+!
+
do:specArray withUI:withUIBoolean
"spec contains a list of action commands (show: / moveTo: etc.)"
@@ -144,6 +167,12 @@
"Modified: / 23-07-2019 / 11:06:13 / Claus Gittinger"
!
+label:nameOfLabel
+ <action>
+
+ "/ skipped here; see goto
+!
+
language:lang
<action>
@@ -331,8 +360,8 @@
verifying ifTrue:[^ self].
endTime := Timestamp now + seconds.
- [
- (self findComponent:componentName) notNil ifTrue:[
+ [
+ (self findComponent:componentName inAllApplications:true ifMultiple:nil) notNil ifTrue:[
^ self
].
Delay waitForSeconds:0.05.
@@ -408,6 +437,134 @@
"Created: / 19-07-2019 / 16:09:58 / Claus Gittinger"
!
+drag:itemsIndexOrLabelOrPattern toComponent:targetComponentName dropAt:where
+ "drag an item (by index or label) from the current treeView,
+ into another component.
+ where is one of: #top, #center or #bottom.
+ allowed after moving to:
+ aSelectionInHierarchyView
+ "
+
+ <action>
+
+ |sourceComponent targetComponent idx yPos sourcePos sourcePosOnScreen
+ targetPos targetPosOnScreen targetPosInSource|
+
+ self assert:( #(top center bottom) includes:where).
+
+ verifying ifTrue:[^ self].
+
+ targetComponent := self findComponent:targetComponentName.
+ self assert:targetComponent notNil.
+
+ ((sourceComponent := lastComponent) isKindOf:ScrollableView) ifTrue:[
+ sourceComponent := sourceComponent scrolledView.
+ ].
+
+ (sourceComponent isKindOf:HierarchicalListView) ifTrue:[
+ where == #top ifTrue:[
+ targetPos := targetComponent topCenter.
+ ] ifFalse:[
+ where == #center ifTrue:[
+ targetPos := targetComponent center
+ ] ifFalse:[
+ where == #bottom ifTrue:[
+ targetPos := targetComponent bottomCenter - (0 @ 1)
+ ].
+ ].
+ ].
+
+ itemsIndexOrLabelOrPattern isInteger ifTrue:[
+ idx := itemsIndexOrLabelOrPattern
+ ] ifFalse:[
+ itemsIndexOrLabelOrPattern includesMatchCharacters ifTrue:[
+ idx := sourceComponent indexOfElementForWhich:[:el | itemsIndexOrLabelOrPattern match:el label string].
+ ] ifFalse:[
+ idx := sourceComponent indexOfElementForWhich:[:el | el label string = itemsIndexOrLabelOrPattern].
+ ].
+ idx == 0 ifTrue:[
+ self error:'no such item in hierarchicalList: ',itemsIndexOrLabelOrPattern
+ ].
+ ].
+ yPos := sourceComponent yVisibleOfLine:idx.
+ self movePointerToComponent:sourceComponent rightOffset:(10 @ (yPos + 3)).
+ Delay waitForSeconds:0.5.
+ sourcePosOnScreen := Display pointerPosition.
+ sourcePos := Display translatePoint:sourcePosOnScreen from:nil to:sourceComponent.
+
+ targetPosOnScreen := Display translatePoint:targetPos from:targetComponent to:nil.
+ targetPosInSource := Display translatePoint:targetPosOnScreen from:nil to:sourceComponent.
+
+ sourceComponent simulateButtonPress:1 at:sourcePos sendDisplayEvent:true.
+ Delay waitForSeconds:0.5.
+ self movePointerToComponent:targetComponent offset:targetPos.
+ sourceComponent simulateButtonMotion:1 to:targetPosInSource sendDisplayEvent:false.
+ sourceComponent simulateButtonRelease:1 at:targetPosInSource sendDisplayEvent:false.
+self halt.
+ sourceComponent simulateButtonMotion:1 to:targetPosInSource sendDisplayEvent:false.
+ Delay waitForSeconds:0.5.
+ sourceComponent simulateButtonRelease:1 at:targetPosInSource sendDisplayEvent:true.
+
+"/ self movePointerToComponent:targetComponent offset:targetPos.
+"/ Delay waitForSeconds:2.
+"/ self press:1.
+"/ Delay waitForSeconds:2.
+"/ self release:1.
+"/ Delay waitForSeconds:0.5.
+ self halt.
+ ^ self
+ ].
+
+ self error:'cannot expand in this component'
+
+ "Created: / 19-07-2019 / 12:34:25 / Claus Gittinger"
+ "Modified (format): / 19-07-2019 / 14:55:34 / Claus Gittinger"
+!
+
+expand:itemsIndexOrLabelOrPattern
+ "expand an item in a treeView by label,
+ allowed after moving to:
+ aSelectionInHierarchyView
+ "
+
+ <action>
+
+ |component|
+
+ verifying ifTrue:[^ self].
+
+ ((component := lastComponent) isKindOf:ScrollableView) ifTrue:[
+ component := component scrolledView.
+ ].
+
+ (component isKindOf:HierarchicalListView) ifTrue:[
+ |idx yPos|
+
+ itemsIndexOrLabelOrPattern isInteger ifTrue:[
+ idx := itemsIndexOrLabelOrPattern
+ ] ifFalse:[
+ itemsIndexOrLabelOrPattern includesMatchCharacters ifTrue:[
+ idx := component indexOfElementForWhich:[:el | itemsIndexOrLabelOrPattern match:el label string].
+ ] ifFalse:[
+ idx := component indexOfElementForWhich:[:el | el label string = itemsIndexOrLabelOrPattern].
+ ].
+ idx == 0 ifTrue:[
+ self error:'no such item in hierarchicalList: ',itemsIndexOrLabelOrPattern
+ ].
+ ].
+ yPos := component yVisibleOfLine:idx.
+ self movePointerToComponent:component offset:(0 @ (yPos + 3)).
+ Delay waitForSeconds:0.5.
+ (component listAt:idx) expand.
+ ^ self
+ ].
+
+ self error:'cannot expand in this component'
+
+ "Created: / 19-07-2019 / 12:34:25 / Claus Gittinger"
+ "Modified (format): / 19-07-2019 / 14:55:34 / Claus Gittinger"
+!
+
fastMoveTo:componentName
"move the mouse to componentName without circling"
@@ -445,7 +602,7 @@
"Modified: / 19-07-2019 / 15:38:11 / Claus Gittinger"
!
-select:itemsLabel
+select:itemsIndexOrLabelOrPattern
"select an item by label,
allowed after moving to:
aComboBox
@@ -467,8 +624,17 @@
self movePointerToComponent:component menuButton.
self click:1 inComponent:component menuButton.
Delay waitForSeconds:0.3.
- (idx := component list indexOf:itemsLabel ifAbsent:[nil]) isNil ifTrue:[
- self error:'no such item in comboList: ',itemsLabel
+ itemsIndexOrLabelOrPattern isInteger ifTrue:[
+ idx := itemsIndexOrLabelOrPattern
+ ] ifFalse:[
+ itemsIndexOrLabelOrPattern includesMatchCharacters ifTrue:[
+ idx := component list findFirst:[:lbl | itemsIndexOrLabelOrPattern match:lbl]
+ ] ifFalse:[
+ idx := component list indexOf:itemsIndexOrLabelOrPattern.
+ ].
+ idx == 0 ifTrue:[
+ self error:'no such item in comboList: ',itemsIndexOrLabelOrPattern
+ ].
].
component select:idx.
Delay waitForSeconds:0.3.
@@ -478,13 +644,26 @@
^ self
].
(component isKindOf:HierarchicalListView) ifTrue:[
- component
- selectElementForWhich:[:el |
- el label string = itemsLabel
- ]
- ifAbsent:[
- self error:'no such item in hierarchicalList: ',itemsLabel
+ |idx yPos|
+
+ itemsIndexOrLabelOrPattern isInteger ifTrue:[
+ idx := itemsIndexOrLabelOrPattern
+ ] ifFalse:[
+ itemsIndexOrLabelOrPattern includesMatchCharacters ifTrue:[
+ idx := component indexOfElementForWhich:[:el | itemsIndexOrLabelOrPattern match:el label string].
+ ] ifFalse:[
+ idx := component indexOfElementForWhich:[:el | el label string = itemsIndexOrLabelOrPattern].
].
+ idx == 0 ifTrue:[
+ self error:'no such item in hierarchicalList: ',itemsIndexOrLabelOrPattern
+ ].
+ ].
+ yPos := component yVisibleOfLine:idx.
+ self movePointerToComponent:component offset:(0 @ (yPos + 3)).
+ component simulateButtonPress:1 at:(0 @ (yPos + 3)) sendDisplayEvent:false.
+ Delay waitForSeconds:0.1.
+ component simulateButtonRelease:1 at:(0 @ (yPos + 3)) sendDisplayEvent:false.
+ "/ component selection:idx.
^ self
].
@@ -533,7 +712,6 @@
Delay waitForSeconds:0.5.
widget select:itemsIndex.
Delay waitForSeconds:0.5.
-self halt.
^ self
].
(widget isKindOf:SelectionInListView) ifTrue:[
@@ -775,51 +953,32 @@
"find a component by name - in the active and possibly in any app.
Can return either a view or a menu item"
- |component candidates modalGroup|
-
- application notNil ifTrue:[
- (component := self findComponent:componentName in:application) notNil ifTrue:[
- ^ component.
- ].
- ].
- candidates := OrderedCollection new.
-
- "/ is there a modal dialog open for the app?
- (modalGroup := application windowGroup modalGroup) notNil ifTrue:[
- modalGroup topViews do:[:eachModalTopView |
- component := self findComponent:componentName in:eachModalTopView.
- component notNil ifTrue:[
- candidates add:component
- ].
- ].
- candidates size == 1 ifTrue:[
- ^ candidates first
- ].
- ].
- true "application isNil" ifTrue:[
- "/ search through all current applications
- WindowGroup scheduledWindowGroups do:[:eachWG |
- |eachApp|
-
- (eachApp := eachWG application) notNil ifTrue:[
- component := self findComponent:componentName in:eachApp.
- component notNil ifTrue:[
- candidates add:component
- ].
- ].
+ ^ self
+ findComponent:componentName
+ inAllApplications:true
+ ifMultiple:[
+ self proceedableError:('multiple components found by name: ',componentName).
+ nil
].
- candidates size == 1 ifTrue:[
- ^ candidates first
- ].
- candidates notEmpty ifTrue:[
- "/ multiple elements (probably there are multiple topviews open...
- "/ check the current windowGroup
- self error:'multiple components found by name: ',componentName.
- ].
- ].
+ "
+ ShowMeHowItWorks basicNew findComponent:'Classes'
+ ShowMeHowItWorks basicNew findComponent:'Klassen'
+ "
- ^ nil
+ "Created: / 19-07-2019 / 12:02:30 / Claus Gittinger"
+ "Modified: / 19-07-2019 / 16:44:30 / Claus Gittinger"
+ "Modified (comment): / 23-07-2019 / 09:32:44 / Claus Gittinger"
+!
+
+findComponent:componentName ifMultiple:exceptionalValue
+ "find a component by name - in the active and possibly in any app.
+ Can return either a view or a menu item"
+
+ ^ self
+ findComponent:componentName
+ inAllApplications:true
+ ifMultiple:exceptionalValue
"
ShowMeHowItWorks basicNew findComponent:'Classes'
@@ -954,6 +1113,66 @@
"Modified (comment): / 23-07-2019 / 09:31:34 / Claus Gittinger"
!
+findComponent:componentName inAllApplications:inAllApplicationsBool ifMultiple:exceptionalValue
+ "find a component by name - in the active and possibly in any app.
+ Can return either a view or a menu item"
+
+ |component candidates modalGroup|
+
+ application notNil ifTrue:[
+ (component := self findComponent:componentName in:application) notNil ifTrue:[
+ ^ component.
+ ].
+ ].
+ candidates := OrderedCollection new.
+
+ "/ is there a modal dialog open for the app?
+ (modalGroup := application windowGroup modalGroup) notNil ifTrue:[
+ modalGroup topViews do:[:eachModalTopView |
+ component := self findComponent:componentName in:eachModalTopView.
+ component notNil ifTrue:[
+ candidates add:component
+ ].
+ ].
+ candidates size == 1 ifTrue:[
+ ^ candidates first
+ ].
+ ].
+ inAllApplicationsBool ifTrue:[
+ "/ search through all current applications
+ WindowGroup scheduledWindowGroups do:[:eachWG |
+ |eachApp|
+
+ (eachApp := eachWG application) notNil ifTrue:[
+ component := self findComponent:componentName in:eachApp.
+ component notNil ifTrue:[
+ candidates add:component
+ ].
+ ].
+ ].
+
+ candidates size == 1 ifTrue:[
+ ^ candidates first
+ ].
+ candidates notEmpty ifTrue:[
+ "/ multiple elements (probably there are multiple topviews open...
+ "/ check the current windowGroup
+ ^ exceptionalValue value
+ ].
+ ].
+
+ ^ nil
+
+ "
+ ShowMeHowItWorks basicNew findComponent:'Classes'
+ ShowMeHowItWorks basicNew findComponent:'Klassen'
+ "
+
+ "Created: / 19-07-2019 / 12:02:30 / Claus Gittinger"
+ "Modified: / 19-07-2019 / 16:44:30 / Claus Gittinger"
+ "Modified (comment): / 23-07-2019 / 09:32:44 / Claus Gittinger"
+!
+
screenBoundsOfComponent:aWidgetOrMenuItem
aWidgetOrMenuItem isView ifTrue:[
^ aWidgetOrMenuItem screenBounds
@@ -1029,7 +1248,7 @@
x := position x.
y := position y.
- self movePointerToScreenPosition:position.
+ "/ self movePointerToScreenPosition:position.
false "OperatingSystem isOSXlike" ifTrue:[
|osxPos|
@@ -1060,7 +1279,7 @@
x := position x.
y := position y.
- self movePointerToScreenPosition:position.
+ "/ self movePointerToScreenPosition:position.
false "OperatingSystem isOSXlike" ifTrue:[
|osxPos|
@@ -1169,6 +1388,33 @@
"Modified: / 23-07-2019 / 09:36:57 / Claus Gittinger"
!
+movePointerToComponent:aWidgetOrMenuItem rightBottomOffset:offset
+ "move the mouse to position inside aWidget's.
+ Offset is from the rightBottom"
+
+ |bounds|
+
+ bounds := self screenBoundsOfComponent:aWidgetOrMenuItem.
+ self movePointerToScreenPosition:(bounds corner - offset) rounded.
+
+ "Created: / 19-07-2019 / 16:18:58 / Claus Gittinger"
+ "Modified: / 23-07-2019 / 09:36:57 / Claus Gittinger"
+!
+
+movePointerToComponent:aWidgetOrMenuItem rightOffset:offset
+ "move the mouse to position inside aWidget's.
+ Offset is from the rightTop (i.e. x is subtracted, y is added)"
+
+ |bounds pos|
+
+ bounds := self screenBoundsOfComponent:aWidgetOrMenuItem.
+ pos := (bounds right - offset x) @ (bounds top + offset y).
+ self movePointerToScreenPosition:pos rounded.
+
+ "Created: / 19-07-2019 / 16:18:58 / Claus Gittinger"
+ "Modified: / 23-07-2019 / 09:36:57 / Claus Gittinger"
+!
+
movePointerToComponent:aWidgetOrMenuItem speed:pixelsPerSecond
"move the mouse to aWidget's center"
@@ -1233,35 +1479,11 @@
"must run as a separate process;
otherwise - if started by the app itself -
no events will be processed while running"
-
- |wasActive|
-
- (wasActive := ActiveHelp isActive) ifTrue:[
- ActiveHelp stop.
- ].
- self prepare.
-
- "/ run pnce in verifying mode
- [
- self verify:specArray.
- ] ensure:[
- wasActive ifTrue:[ActiveHelp start].
- ].
+ |startLabel|
- [
- ActiveHelp stop.
- [
- Error handle:[:ex |
- Dialog warn:(self class classResources
- stringWithCRs:'An error was encountered in the show:\\%1'
- with:ex description withCRs)
- ] do:[
- self doStream:(specArray readStream)
- ].
- ] ensure:[
- wasActive ifTrue:[ActiveHelp start].
- ].
- ] fork.
+ startLabel := 'start'.
+ self do:specArray from:startLabel
+
"
ShowMeHowItWorks do:
@@ -1275,6 +1497,29 @@
"Modified: / 25-07-2019 / 11:48:53 / Claus Gittinger"
!
+do:specArray from:startLabelOrNil withUI:withUIBoolean
+ "must run as a separate process;
+ otherwise - if started by the app itself -
+ no events will be processed while running"
+
+ withUIBoolean ifTrue:[
+ ui := ShowMeHowItWorksRunner openOn:self.
+ ].
+ self do:specArray from:startLabelOrNil
+
+ "
+ ShowMeHowItWorks
+ do:#(
+ (show: 'blah blah')
+ (moveTo: NameOfComponent)
+ )
+ withUI:true
+ "
+
+ "Created: / 23-07-2019 / 10:24:53 / Claus Gittinger"
+ "Modified: / 25-07-2019 / 11:48:53 / Claus Gittinger"
+!
+
do:specArray withUI:withUIBoolean
"must run as a separate process;
otherwise - if started by the app itself -
@@ -1283,7 +1528,7 @@
withUIBoolean ifTrue:[
ui := ShowMeHowItWorksRunner openOn:self.
].
- self do:specArray
+ self do:specArray from:nil
"
ShowMeHowItWorks
@@ -1333,6 +1578,52 @@
!ShowMeHowItWorks methodsFor:'running - private'!
+do:specArray from:startLabelOrNil
+ "must run as a separate process;
+ otherwise - if started by the app itself -
+ no events will be processed while running"
+
+ |wasActive|
+
+ (wasActive := ActiveHelp isActive) ifTrue:[
+ ActiveHelp stop.
+ ].
+ self prepare.
+
+ "/ run once in verifying mode
+ [
+ self verify:specArray.
+ ] ensure:[
+ wasActive ifTrue:[ActiveHelp start].
+ ].
+
+ [
+ ActiveHelp stop.
+ [
+ Error handle:[:ex |
+ Dialog warn:(self class classResources
+ stringWithCRs:'An error was encountered in the show:\\%1'
+ with:ex description withCRs)
+ ] do:[
+ self doStream:(specArray readStream) from:startLabelOrNil
+ ].
+ ] ensure:[
+ wasActive ifTrue:[ActiveHelp start].
+ ].
+ ] fork.
+
+ "
+ ShowMeHowItWorks do:
+ #(
+ (show: 'blah blah')
+ (moveTo: NameOfComponent)
+ )
+ "
+
+ "Created: / 23-07-2019 / 10:24:53 / Claus Gittinger"
+ "Modified: / 25-07-2019 / 11:48:53 / Claus Gittinger"
+!
+
doCommand:op
"execute a single command"
@@ -1396,6 +1687,41 @@
!
doStream:specStream
+ self doStream:specStream from:nil
+
+"<<END
+ ShowMeHowItWorks do:#(
+ showing: 'Choose the number of arguments'
+ do: (
+ moveTo: NumberOfArguments
+ select: '1'
+ )
+ showing: 'Click into the "receiver" field'
+ do: (
+ moveTo: ReceiverEditor
+ click: ReceiverEditor
+ )
+ showing: 'Enter a value (or expression) into "receiver" field'
+ do: (
+ enter: '100'
+ )
+ showing: 'Click into the "first argument" field'
+ do: (
+ moveTo: Arg1Editor
+ click: ReceiverEditor
+ )
+ showing: 'Enter a value (or expression) into "receiver" field'
+ do: (
+ enter: '100'
+ )
+ )
+END"
+
+ "Created: / 19-07-2019 / 10:52:24 / Claus Gittinger"
+ "Modified: / 23-07-2019 / 11:48:45 / Claus Gittinger"
+!
+
+doStream:specStream from:startLabelOrNil
|previousStream resources|
resources := self class classResources.
@@ -1405,9 +1731,32 @@
previousStream := opStream.
[
+ |nextCommand|
+
opStream := specStream.
+ startLabelOrNil notNil ifTrue:[
+ |found|
+
+ "/ skip for that label
+ found := false.
+ [ found ] whileFalse:[
+ |cmd|
+
+ cmd := opStream nextOrNil.
+ cmd isNil ifTrue:[
+ self proceedableError:'label not found: ',startLabelOrNil.
+ ^ self.
+ ].
+ (cmd = 'label:') ifTrue:[
+ found := (opStream next = startLabelOrNil)
+ ].
+ ].
+ ].
+
[opStream atEnd] whileFalse:[
- self nextCommand.
+ nextCommand := opStream next.
+ self doCommand:nextCommand.
+
Display shiftDown ifTrue:[
(IntroShownCount ? 0) > 3 ifFalse:[
self tell:(self possiblyTranslate:'You pressed the SHIFT key.').
@@ -1458,41 +1807,6 @@
"Modified: / 23-07-2019 / 11:48:45 / Claus Gittinger"
!
-nextCommand
- self doCommand:(opStream next).
-
-"<<END
- ShowMeHowItWorks do:#(
- showing: 'Choose the number of arguments'
- do: (
- moveTo: NumberOfArguments
- select: '1'
- )
- showing: 'Click into the "receiver" field'
- do: (
- moveTo: ReceiverEditor
- click: ReceiverEditor
- )
- showing: 'Enter a value (or expression) into "receiver" field'
- do: (
- enter: '100'
- )
- showing: 'Click into the "first argument" field'
- do: (
- moveTo: Arg1Editor
- click: ReceiverEditor
- )
- showing: 'Enter a value (or expression) into "receiver" field'
- do: (
- enter: '100'
- )
- )
-END"
-
- "Created: / 19-07-2019 / 10:54:04 / Claus Gittinger"
- "Modified: / 19-07-2019 / 15:35:15 / Claus Gittinger"
-!
-
possiblyTranslate:aString
translate ifTrue:[^ self class classResources string:aString].
^ aString