#FEATURE by exept
class: ShowMeHowItWorks
added:
#clear
#fastMoveRelativeX:y:
#freezePin:ofStep:with:
#key:
#selectPin:ofStep:
changed:
#click:inComponent:clickTime:
#findComponent:in:
#movePointerToScreenPosition:speed:
#screenBoundsOfComponent:
#select:
#withViewAndPositionFor:do:
class: ShowMeHowItWorks::ItemInView
added:
#isItemInView
#isVisible
--- a/ShowMeHowItWorks.st Sun Oct 20 13:44:05 2019 +0200
+++ b/ShowMeHowItWorks.st Sun Oct 20 16:38:40 2019 +0200
@@ -230,6 +230,28 @@
!ShowMeHowItWorks methodsFor:'commands'!
+freezePin:pinName ofStep:stepName with:freezeString
+ <action>
+
+ |diagramEditor pinPO|
+
+ (diagramEditor := lastComponent) isScrollWrapper ifTrue:[
+ diagramEditor := lastComponent scrolledView
+ ].
+ pinPO := diagramEditor
+ detectPOForWhich:[:po |
+ po isPin
+ and:[ po name = pinName
+ and:[ po owningStepPO name = stepName ]]
+ ].
+ diagramEditor selection:{ pinPO }.
+ diagramEditor doubleClickOn:pinPO.
+ self fastMoveTo:(lastComponentName,'/','EditField').
+ self click.
+ self type:freezeString.
+ self key:#Return.
+!
+
intro
<action>
@@ -312,6 +334,25 @@
self halt.
!
+selectPin:pinName ofStep:stepName
+ <action>
+
+ |diagramEditor pinPo|
+
+ (diagramEditor := lastComponent) isScrollWrapper ifTrue:[
+ diagramEditor := lastComponent scrolledView
+ ].
+ pinPo := diagramEditor
+ detectPOForWhich:[:po |
+ po isPin
+ and:[ po name = pinName
+ and:[ po owningStepPO name = stepName ]]
+ ].
+ self movePointerToComponent:diagramEditor offset:pinPo frame center.
+ diagramEditor selection:{ pinPo }.
+
+!
+
show:message
"showing (and speak) some message."
@@ -488,6 +529,16 @@
!ShowMeHowItWorks methodsFor:'commands - mouse & keyboard'!
+clear
+ "clear entry fields"
+
+ <action>
+
+ verifying ifTrue:[^ self].
+
+ lastComponent contents:nil
+!
+
click
"press-release"
@@ -651,6 +702,21 @@
"Modified (format): / 19-07-2019 / 14:55:34 / Claus Gittinger"
!
+fastMoveRelativeX:dX y:dY
+ "move the mouse to a new relative position"
+
+ <action>
+
+ |screen newPos currentPos|
+
+ verifying ifTrue:[^ self].
+
+ screen := Screen current.
+ currentPos := screen pointerPosition.
+ newPos := currentPos + (dX @ dY).
+ self fastMovePointerToScreenPosition:newPos
+!
+
fastMoveTo:componentName
"move the mouse to componentName without circling"
@@ -671,6 +737,22 @@
"Modified (comment): / 23-07-2019 / 09:33:31 / Claus Gittinger"
!
+key:aKeySymbol
+ "press/release a key"
+
+ <action>
+
+ |t|
+
+ verifying ifTrue:[^ self].
+
+ t := Display ctrlDown ifTrue:[0.05] ifFalse:[0.1].
+ lastComponent
+ simulateKey:aKeySymbol at:(lastComponent extent // 2) sendDisplayEvent:false keyPressTime:t
+
+ "Created: / 19-07-2019 / 15:50:40 / Claus Gittinger"
+!
+
moveTo:componentName
"move the mouse to componentName,
then circle around it a few times"
@@ -827,8 +909,38 @@
"/ component selection:idx.
^ self
].
+ (component isKindOf:SelectionInListModelView) ifTrue:[
+ |idx yPos|
- self error:'cannot select this component'
+ itemsIndexOrLabelOrPattern isInteger ifTrue:[
+ idx := itemsIndexOrLabelOrPattern
+ ] ifFalse:[
+ itemsIndexOrLabelOrPattern includesMatchCharacters ifTrue:[
+ idx := component indexOfElementForWhich:[:el | el notNil and:[itemsIndexOrLabelOrPattern match:el string]].
+ ] ifFalse:[
+ idx := component indexOfElementForWhich:[:el | el notNil and:[el 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
+ ].
+
+ (component isKindOf:ItemInView) ifTrue:[
+ component isMenuItem ifTrue:[
+ self movePointerToComponent:component.
+ self halt.
+ ].
+ ].
+
+ self error:('cannot select this component: %1' bindWith:component className)
"Created: / 19-07-2019 / 12:34:25 / Claus Gittinger"
"Modified (format): / 19-07-2019 / 14:55:34 / Claus Gittinger"
@@ -1156,21 +1268,35 @@
Uses the NameKey of the spec, and optionally the label or modelKey.
Can return either a view or a menu item"
- |idxString idx app window component componentNameSymbol
+ |quoted idxString idx app window component componentNameSymbol
foundByName foundByHelpKey foundByTitle foundByLabel item
checkIfAllMenuItemsDoTheSame|
- (componentNameOrPath includes:$/) ifTrue:[
- (idx := componentNameOrPath indexOf:$/) ~~ 0 ifTrue:[
- |containerName restPath container|
+ "/ split at "/";
+ "/ but not if inside quotes
+ quoted := false.
+ componentNameOrPath doWithIndex:[:ch :idx |
+ ch == $" ifTrue:[
+ quoted := quoted not.
+ ] ifFalse:[
+ ((ch == $/) and:[quoted not]) ifTrue:[
+ |containerName restPath container|
- containerName := componentNameOrPath copyTo:idx-1.
- restPath := componentNameOrPath copyFrom:idx+1.
- container := self findComponent:containerName in:anApplicationOrViewOrMenuItem.
- container isNil ifTrue:[ ^ nil ].
- ^ self findComponent:restPath in:container
+ containerName := componentNameOrPath copyTo:idx-1.
+ restPath := componentNameOrPath copyFrom:idx+1.
+ container := self findComponent:containerName in:anApplicationOrViewOrMenuItem.
+ container isNil ifTrue:[ ^ nil ].
+ ^ self findComponent:restPath in:container
+ ]
]
].
+
+ (anApplicationOrViewOrMenuItem isKindOf:ItemInView) ifTrue:[
+ anApplicationOrViewOrMenuItem isMenuItem ifTrue:[
+ ^ self findComponent:componentNameOrPath in:anApplicationOrViewOrMenuItem item submenu
+ ].
+ ].
+
(componentNameOrPath startsWith:'item[') ifTrue:[
anApplicationOrViewOrMenuItem isMenu ifFalse:[
self assert:false message:'container is not a menu'.
@@ -1180,9 +1306,24 @@
idx := Integer readFrom:idxString.
item := anApplicationOrViewOrMenuItem itemAtIndex:idx.
] ifFalse:[
- item := anApplicationOrViewOrMenuItem itemAt:idxString.
+ idxString := idxString withoutQuotes.
+ idxString includesMatchCharacters ifTrue:[
+ item :=
+ anApplicationOrViewOrMenuItem itemForWhich:[:item |
+ (item nameKey matches: idxString)
+ or:[ (item textLabel matches: idxString)
+ or:[ (item value isSymbol and:[item value matches: idxString])]]
+ ]
+ ] ifFalse:[
+ item :=
+ anApplicationOrViewOrMenuItem itemForWhich:[:item |
+ item nameKey = idxString
+ or:[ item textLabel = idxString
+ or:[ (item value isSymbol and:[ item value = idxString ])]]
+ ]
+ ].
].
- self assert:item notNil.
+ self assert:item notNil message:('no menu item named "%1"' bindWith:idxString).
^ ItemInView new view:anApplicationOrViewOrMenuItem item:item boundsInView:(item layout)
].
(componentNameOrPath startsWith:'tab[') ifTrue:[
@@ -1194,6 +1335,7 @@
idx := Integer readFrom:idxString.
item := anApplicationOrViewOrMenuItem tabAtIndex:idx.
] ifFalse:[
+ idxString := idxString withoutQuotes.
item := anApplicationOrViewOrMenuItem tabForWhich:[:tab | tab tabItem rawLabel = idxString].
item isNil ifTrue:[
item := anApplicationOrViewOrMenuItem tabForWhich:[:tab | tab printableLabel = idxString].
@@ -1398,7 +1540,7 @@
aWidgetOrMenuItemOrTab isVisible ifTrue:[
(itemLayout := aWidgetOrMenuItemOrTab layout) notNil ifTrue:[
- menuPanel := aWidgetOrMenuItemOrTab menuPanel.
+ menuPanel := aWidgetOrMenuItemOrTab view.
menuPanel shown ifTrue:[
menuBounds := menuPanel screenBounds.
^ itemLayout + menuBounds origin
@@ -1444,9 +1586,27 @@
"press-release in a component"
self withViewAndPositionFor:viewOrMenuItem do:[:viewToClick :clickPos |
- viewToClick simulateButtonPress:buttonNr at:clickPos sendDisplayEvent:false.
- Delay waitForSeconds:clickTime.
- viewToClick simulateButtonRelease:buttonNr at:clickPos sendDisplayEvent:false.
+ Display activePointerGrab == Display rootView ifTrue:[
+ |pos|
+
+ pos := Display translatePoint:clickPos fromView:viewToClick toView:nil.
+ OperatingSystem isOSXlike ifTrue:[
+ OperatingSystem
+ generateMouseMoveEventX:pos x y:pos y;
+ generateButtonEvent:buttonNr down:true x:pos x y:pos y;
+ generateButtonEvent:buttonNr down:false x:pos x y:pos y;
+ generateButtonEvent:buttonNr down:true x:pos x y:pos y;
+ generateButtonEvent:buttonNr down:false x:pos x y:pos y.
+ ^ self.
+ ].
+ Display rootView simulateButtonPress:buttonNr at:pos sendDisplayEvent:true.
+ Delay waitForSeconds:clickTime.
+ Display rootView simulateButtonRelease:buttonNr at:pos sendDisplayEvent:true.
+ ] ifFalse:[
+ viewToClick simulateButtonPress:buttonNr at:clickPos sendDisplayEvent:false.
+ Delay waitForSeconds:clickTime.
+ viewToClick simulateButtonRelease:buttonNr at:clickPos sendDisplayEvent:false.
+ ].
].
!
@@ -1474,8 +1634,7 @@
self assert:viewOrMenuItem notNil.
verifying ifTrue:[^ self].
- ((viewOrMenuItem askFor:#isMenuItem)
- or:[ (viewOrMenuItem askFor:#isNoteBookTab) ]) ifTrue:[
+ (viewOrMenuItem isKindOf:ItemInView) ifTrue:[
viewToClick := viewOrMenuItem view.
clickPos := viewOrMenuItem layout center.
self assert:(clickPos notNil).
@@ -1638,12 +1797,19 @@
start := screen pointerPosition.
distance := start dist:newPosition.
+ distance = 0 ifTrue:[
+ "/ already there
+ screen setPointerPosition:newPosition. "/ do it anyway, in case of rounding errors
+ ^ self
+ ].
+
moveTime := (distance / pixelsPerSecond) seconds. "/ time to move
stepDelayTime := self pointerAnimationDelay. "/ update every 50ms
numSteps := moveTime / stepDelayTime.
numSteps = 0 ifTrue:[
"/ already there
+ screen setPointerPosition:newPosition. "/ do it anyway, in case of rounding errors
^ self
].
@@ -2111,12 +2277,21 @@
!ShowMeHowItWorks::ItemInView methodsFor:'testing'!
+isItemInView
+ ^ true
+!
+
isMenuItem
^ item askFor:#isMenuItem
!
isNoteBookTab
^ item isKindOf:NoteBookView::Tab
+!
+
+isVisible
+ ^ view shown
+ and:[item isVisible]
! !
!ShowMeHowItWorks class methodsFor:'documentation'!