#FEATURE by exept draft
authorClaus Gittinger <cg@exept.de>
Fri, 21 Feb 2020 18:31:56 +0100
changeset 3861 11efde58092f
parent 3860 c1824d0bfeb5
child 3862 bbe57509fc55
#FEATURE by exept class: ShowMeHowItWorks class definition added: #fastMoveAbsolute: #fastMovePointerToComponent: #fastMovePointerToComponent:at: #fastMoveTo:at: #goto: #movePointerToComponent:at: #movePointerToComponent:at:speed: #useApplication: comment/format in: #click:inComponent: #clickIn: #fastMoveTo: #findApplication:ifMultiple: #movePointerToComponent: #movePointerToComponent:offset: #movePointerToComponent:rightBottomOffset: #movePointerToComponent:rightOffset: #movePointerToComponent:speed: #movePointerToScreenPosition: changed: #click:inComponent:clickTime: #doShowFile: #doStream:from: #findApplication: #movePointerToScreenPosition:speed: #where:inComponent: category of: #closeApplication #findApplication: #open: #popApplication #pushApplication: class: ShowMeHowItWorks class added: #example3
ShowMeHowItWorks.st
--- a/ShowMeHowItWorks.st	Thu Feb 20 15:49:39 2020 +0100
+++ b/ShowMeHowItWorks.st	Fri Feb 21 18:31:56 2020 +0100
@@ -104,6 +104,11 @@
     )
 !
 
+example3
+    ShowMeHowItWorks new
+        doShowFile:'../../../exept/expecco/resources/shows/Browser_overview_en' asFilename
+!
+
 scriptFormat
 "
 
@@ -289,12 +294,6 @@
 
 !ShowMeHowItWorks methodsFor:'commands'!
 
-closeApplication
-    <action>
-
-    application closeRequest.
-!
-
 freezePin:pinName ofStep:stepName with:freezeString
     <action>
 
@@ -318,6 +317,32 @@
     self key:#Return.
 !
 
+goto:nameOfLabel
+    <action>
+
+    "/ leaves opStream positioned after label: nameOfLabel
+    |savedPosition found|
+
+    savedPosition := opStream position.
+    opStream position:0.
+
+    "/ skip for that label
+    found := false.
+    [ found ] whileFalse:[
+        |cmd|
+
+        cmd := opStream nextOrNil.
+        cmd isNil ifTrue:[
+            opStream position:savedPosition.
+            self proceedableError:'label not found: ',nameOfLabel.
+            ^ self.
+        ].
+        (cmd = 'label:') ifTrue:[
+            found := (opStream next = nameOfLabel)
+        ].
+    ].
+!
+
 intro
     <action>
 
@@ -354,22 +379,6 @@
     "Created: / 23-07-2019 / 10:27:02 / Claus Gittinger"
 !
 
-open:applicationClassName
-    <action>
-
-    |appClass|
-
-    (appClass := Smalltalk classNamed:applicationClassName) isNil ifTrue:[
-        self error:'no such application class'
-    ].
-    verifying ifFalse:[
-        application := appClass new openAndWaitUntilVisible.
-        closeApplicationWhenFinished := true.
-    ].
-
-    "Created: / 19-07-2019 / 15:09:45 / Claus Gittinger"
-!
-
 pause
     <action>
     
@@ -380,14 +389,6 @@
     "Modified: / 19-07-2019 / 16:13:33 / Claus Gittinger"
 !
 
-popApplication
-    <action>
-
-    "go back to the previous app"
-
-    application := applicationStack removeLast.
-!
-
 pronounce:word as:pronunciation
     <action>
 
@@ -397,27 +398,6 @@
     pronunciations at:word put:pronunciation
 !
 
-pushApplication:newApplicationOrName
-    <action>
-
-    "goto another app, remembering where we were before"
-
-    |newApplication|
-
-    (newApplication := newApplicationOrName) isString ifTrue:[
-        newApplication := self findApplication:newApplicationOrName ifMultiple:nil.
-        newApplication isNil ifTrue:[
-            self error:('Could not find an application named "%1" (to switch to)' bindWith:newApplicationOrName).
-        ].
-        "/ self assert: newApplication notNil.
-    ].
-
-    applicationStack isNil ifTrue:[applicationStack := OrderedCollection new].
-
-    applicationStack add:application.
-    self application:newApplication.
-!
-
 raise:what
     <action>
 
@@ -656,6 +636,73 @@
     ] loop
 ! !
 
+!ShowMeHowItWorks methodsFor:'commands - application'!
+
+closeApplication
+    <action>
+
+    application closeRequest.
+!
+
+open:applicationClassName
+    <action>
+
+    |appClass|
+
+    (appClass := Smalltalk classNamed:applicationClassName) isNil ifTrue:[
+        self error:'no such application class'
+    ].
+    verifying ifFalse:[
+        application := appClass new openAndWaitUntilVisible.
+        closeApplicationWhenFinished := true.
+    ].
+
+    "Created: / 19-07-2019 / 15:09:45 / Claus Gittinger"
+!
+
+popApplication
+    <action>
+
+    "go back to the previous app"
+
+    application := applicationStack removeLast.
+!
+
+pushApplication:newApplicationOrName
+    <action>
+
+    "goto another app, remembering where we were before"
+
+    |newApplication|
+
+    (newApplication := newApplicationOrName) isString ifTrue:[
+        newApplication := self findApplication:newApplicationOrName ifMultiple:nil.
+        newApplication isNil ifTrue:[
+            self error:('Could not find an application named "%1" (to switch to)' bindWith:newApplicationOrName).
+        ].
+        "/ self assert: newApplication notNil.
+    ].
+
+    applicationStack isNil ifTrue:[applicationStack := OrderedCollection new].
+
+    applicationStack add:application.
+    self application:newApplication.
+!
+
+useApplication:classNameOrWindowTitle 
+    <action>
+
+    "find an application by name and return it (useful as arg to application)"
+
+    application := 
+        self 
+            findApplication:classNameOrWindowTitle 
+            ifMultiple:[
+                self error:('multiple applications named "%1"' bindWith:classNameOrWindowTitle).
+                nil
+        ]
+! !
+
 !ShowMeHowItWorks methodsFor:'commands - checking'!
 
 isEmpty:componentName
@@ -738,6 +785,10 @@
     
     <action>
 
+    (self componentNamed:componentName) == lastComponent ifFalse:[
+        self fastMoveTo:componentName.
+        self wait:0.25.
+    ].
     ^ self click:1 inComponent:(self componentNamed:componentName)
 
     "Created: / 19-07-2019 / 16:09:58 / Claus Gittinger"
@@ -874,6 +925,21 @@
     "Modified (format): / 19-07-2019 / 14:55:34 / Claus Gittinger"
 !
 
+fastMoveAbsolute:pos
+    "move the mouse to a new position within the current component"
+    
+    <action>
+
+    |screen newPos currentPos|
+
+    verifying ifTrue:[^ self].
+
+    screen := Screen current.
+    currentPos := screen pointerPosition.   
+    newPos := currentPos + pos.
+    self fastMovePointerToScreenPosition:newPos
+!
+
 fastMoveRelativeX:dX y:dY
     "move the mouse to a new relative position"
     
@@ -890,7 +956,7 @@
 !
 
 fastMoveTo:componentName
-    "move the mouse to componentName without circling.
+    "move the mouse to the center of componentName without circling.
      Leaves component in lastComponent"
 
     <action>
@@ -910,6 +976,20 @@
     "Modified (comment): / 23-07-2019 / 09:33:31 / Claus Gittinger"
 !
 
+fastMoveTo:componentName at:positionOrSymbol
+    "move the mouse to a new position within the current component"
+    
+    <action>
+
+    |newPos component|
+
+    component := self componentNamed:componentName.
+    (newPos := positionOrSymbol) isSymbol ifTrue:[
+        newPos := self where:positionOrSymbol inComponent:component.
+    ].
+    self fastMovePointerToComponent:component at:newPos
+!
+
 key:aKeySymbol
     "press/release a key in last component"
 
@@ -1490,7 +1570,7 @@
 !
 
 findApplication:classNameOrWindowTitle ifMultiple:exceptionalValue
-    "find an application by name"
+    "find an application by name and return it (useful as arg to application)"
     
     |candidates|
 
@@ -1927,9 +2007,11 @@
     |t|
 
     t := self shortClickTime.
+
+    "/ longer click time for Buttons and Menus, to allow redraw to be seen
     ((component isKindOf:Button) 
       or:[(component askFor:#isMenuItem)]
-    )ifTrue:[
+    ) ifTrue:[
         t := self longClickTime
     ].    
     self click:buttonNr inComponent:component clickTime:t
@@ -1944,12 +2026,15 @@
     self withViewAndPositionFor:viewOrMenuItem do:[:viewToClick :clickPos |
         |clickDone|
 
+        Transcript showCR:'click pos is %1' with:clickPos.
+
         clickDone := false.
         Display activePointerGrab == Display rootView ifTrue:[
             |pos|
 
             pos := Display translatePoint:clickPos fromView:viewToClick toView:nil.
             Error handle:[:ex |
+                Transcript showCR:'failed to click (unupported in OS?)'
             ] do:[    
                 OperatingSystem isOSXlike ifTrue:[
                     OperatingSystem 
@@ -1964,12 +2049,16 @@
                         yourself.
                     ^ self.
                 ].
+            ].
+            Display ungrabPointer.
+            false ifFalse:[
                 Display rootView simulateButtonPress:buttonNr at:pos sendDisplayEvent:true.
                 Delay waitForSeconds:clickTime.
                 Display rootView simulateButtonRelease:buttonNr at:pos sendDisplayEvent:true.
                 clickDone := true.
             ].
         ].
+
         clickDone ifFalse:[
             viewToClick simulateButtonPress:buttonNr at:clickPos sendDisplayEvent:false.
             Delay waitForSeconds:clickTime.
@@ -2073,6 +2162,27 @@
     "Created: / 23-07-2019 / 09:37:46 / Claus Gittinger"
 !
 
+fastMovePointerToComponent:aWidgetOrMenuItem
+    "move the mouse to a widget's center"
+
+    |bounds|
+
+    bounds := self screenBoundsOfComponent:aWidgetOrMenuItem.
+    self fastMovePointerToScreenPosition:(bounds center rounded).
+
+    "Created: / 19-07-2019 / 13:11:33 / Claus Gittinger"
+    "Modified: / 23-07-2019 / 09:37:01 / Claus Gittinger"
+!
+
+fastMovePointerToComponent:aWidgetOrMenuItem at:offset
+    "move the mouse to a widget's center"
+
+    |bounds|
+
+    bounds := self screenBoundsOfComponent:aWidgetOrMenuItem.
+    self fastMovePointerToScreenPosition:(bounds origin + offset).
+!
+
 fastMovePointerToScreenPosition:position
     self movePointerToScreenPosition:position speed:(self pointerMoveSpeedFast).
 
@@ -2080,7 +2190,7 @@
 !
 
 movePointerToComponent:aWidgetOrMenuItem
-    "move the mouse to aWidget's center"
+    "move the mouse to a widget's center"
 
     |bounds|
 
@@ -2091,8 +2201,33 @@
     "Modified: / 23-07-2019 / 09:37:01 / Claus Gittinger"
 !
 
+movePointerToComponent:aWidgetOrMenuItem at:offset
+    "move the mouse to a widget's center"
+
+    |bounds|
+
+    bounds := self screenBoundsOfComponent:aWidgetOrMenuItem.
+    self movePointerToScreenPosition:(bounds origin + offset).
+!
+
+movePointerToComponent:aWidgetOrMenuItemOrTab at:offset speed:pixelsPerSecond
+    "move the mouse to a position within a widget"
+
+    |bounds position|
+    
+    bounds := self screenBoundsOfComponent:aWidgetOrMenuItemOrTab.
+    bounds isNil ifTrue:[
+        self error:('no bounds found for: %1 (%2)' bindWith:lastComponentName with:aWidgetOrMenuItemOrTab printString).
+    ].
+    position := bounds origin + offset.
+    self movePointerToScreenPosition:position speed:pixelsPerSecond.
+
+    "Created: / 20-07-2019 / 08:12:49 / Claus Gittinger"
+    "Modified: / 23-07-2019 / 09:37:27 / Claus Gittinger"
+!
+
 movePointerToComponent:aWidgetOrMenuItem offset:offset
-    "move the mouse to position inside aWidget's"
+    "move the mouse to position inside a widget"
     
     |bounds|
 
@@ -2104,7 +2239,7 @@
 !
 
 movePointerToComponent:aWidgetOrMenuItem rightBottomOffset:offset
-    "move the mouse to position inside aWidget's.
+    "move the mouse to position inside a widget.
      Offset is from the rightBottom"
     
     |bounds|
@@ -2117,7 +2252,7 @@
 !
 
 movePointerToComponent:aWidgetOrMenuItem rightOffset:offset
-    "move the mouse to position inside aWidget's.
+    "move the mouse to position inside a widget.
      Offset is from the rightTop (i.e. x is subtracted, y is added)"
     
     |bounds pos|
@@ -2131,7 +2266,7 @@
 !
 
 movePointerToComponent:aWidgetOrMenuItemOrTab speed:pixelsPerSecond
-    "move the mouse to aWidget's center"
+    "move the mouse to a widget's center"
 
     |bounds position|
     
@@ -2147,7 +2282,7 @@
 !
 
 movePointerToScreenPosition:newPosition
-    "move the mouse to newPosition"
+    "move the mouse to a new screenPosition"
 
     self movePointerToScreenPosition:newPosition speed:(self pointerMoveSpeed)
 
@@ -2165,7 +2300,7 @@
     start := screen pointerPosition.   
 
     distance := start dist:newPosition.
-    distance = 0 ifTrue:[
+    distance < 10 ifTrue:[
         "/ already there
         screen setPointerPosition:newPosition. "/ do it anyway, in case of rounding errors
         ^ self
@@ -2196,32 +2331,35 @@
 !
 
 where:where inComponent:aComponent
+    |bounds|
+
+    bounds := aComponent bounds.
     where == #center ifTrue:[
-        ^ aComponent center
+        ^ bounds center rounded
     ].
     where == #leftCenter ifTrue:[
-        ^ aComponent leftCenter + (5@0). 
+        ^ bounds leftCenter rounded + (5@0). 
     ].
     where == #rightCenter ifTrue:[
-        ^ aComponent rightCenter - (5@0). 
+        ^ bounds rightCenter rounded - (5@0). 
     ].
     where == #topLeft ifTrue:[
-        ^ aComponent topLeft + (5@5). 
+        ^ bounds topLeft + (5@5). 
     ].
     where == #topCenter ifTrue:[
-        ^ aComponent topCenter + (0@5). 
+        ^ bounds topCenter rounded + (0@5). 
     ].
     where == #topRight ifTrue:[
-        ^ aComponent topRight - (5@5). 
+        ^ bounds topRight - (5@5). 
     ].
     where == #bottomCenter ifTrue:[
-        ^ aComponent bottomCenter - (0 @ 5)
+        ^ bounds bottomCenter rounded - (0 @ 5)
     ].
     where == #bottomLeft ifTrue:[
-        ^ aComponent bottomLeft + (5 @ -5)
+        ^ bounds bottomLeft + (5 @ -5)
     ].
     where == #bottomRight ifTrue:[
-        ^ aComponent bottomLeft - (5 @ 5)
+        ^ bounds bottomLeft - (5 @ 5)
     ].
     self error:'where is this'
 ! !
@@ -2323,18 +2461,27 @@
     "Modified: / 25-07-2019 / 11:48:53 / Claus Gittinger"
 !
 
-doShowFile:aFilename
-    "the file from which the show was loaded"
-
-    |spec|
-
-    theShowFile := aFilename.
-    aFilename readingFileWithEncoding:#utf8 do:[:s |
+doShowFile:aFilenameOrDirectory
+    "the file from which the show was loaded.
+     aFilenameOrDirectory must be either a show file,
+     or a folder containing start.show"
+
+    |spec startFile|
+
+    aFilenameOrDirectory isDirectory ifTrue:[
+        "/ look for a start.show in there
+        (startFile := aFilenameOrDirectory / 'start.show') exists ifTrue:[
+            self doShowFile:startFile.
+            ^ self.
+        ].
+        self error:'no "startFile" in show''s folder'.
+    ].
+
+    theShowFile := aFilenameOrDirectory.
+    aFilenameOrDirectory readingFileWithEncoding:#utf8 do:[:s |
         spec := Array readFrom:s.
     ].
     self do:spec.
-
-
 !
 
 prepare
@@ -2525,22 +2672,7 @@
 
         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)
-                ].
-            ].
+            self goto:startLabelOrNil
         ].
 
         [opStream atEnd] whileFalse:[