# HG changeset patch # User Claus Gittinger # Date 1571582320 -7200 # Node ID eafc4c1bb53edc234505d63e702120e8eeb300fa # Parent 5ab4b911e388d0297bd0a8d35802a393c862e1d2 #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 diff -r 5ab4b911e388 -r eafc4c1bb53e ShowMeHowItWorks.st --- 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 + + + |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 @@ -312,6 +334,25 @@ self halt. ! +selectPin:pinName ofStep:stepName + + + |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" + + + + 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" + + + + |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" + + + + |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'!