<b>=============== Summary ===============</b><br>
<br>
Change Set:        interactionModes<br>
Date:            1 April 2022<br>
Author:            Christoph Thiede<br>
<br>
Explicates the concept of 'interaction modes' to fix handling of <font color="#000080">#openToolsAttachedToMouseCursor</font> after a dialog or debugger has been completed. Now, all of the following situations work:<br>
<br>
    For example, you can evaluate the following via keyboard and then click Ok/Proceed via mouse:<br>
        <font color="#800000">self</font><font color="#000000"> </font><font color="#000080">inform:</font><font color="#000000"> </font><font color="#800080">'Hello world!'</font><font color="#000000">.</font><font color="#FF0000"><br>
        </font><font color="#800080">'Hello world again!'</font><font color="#000000"> </font><font color="#000080">edit</font><font color="#000000">.</font><br>
    Here is another example:<br>
        <font color="#800000">self</font><font color="#000000"> </font><font color="#000080">halt</font><font color="#000000">.</font><font color="#000000"> </font><font color="#800000">self</font><font color="#000000"> </font><font color="#000080">halt</font><font color="#000000">.</font><br>
<br>
There were multiple limitiations with the previous approach, which relied on <font color="#000080">#currentEvent</font>: 1) After running a sub world cycle (e.g., for a dialog window), <font color="#000080">#currentEvent</font> is outdated and does no longer represent the current 'interaction mode'. 2) <font color="#000080">#currentEvent</font> is scoped to the active process, but the process instance is invisible to the user and might change during some interactions (e.g., when closing a debugger). New <font color="#000000">HandMorph</font><font color="#000080">>></font><font color="#000080">#currentInteractionMode</font> resolves these limitations by a) relying on <font color="#000080">#lastEvent</font> rather than <font color="#000080">#currentEvent</font> and b) providing an execute-around method (<font color="#000080">#freezeInteractionModeFromEvent:during:</font>) for overriding the interaction mode when changing the process.<br>
<br>
Depends on lastEvent.cs for honoring keyboard events in <font color="#000080">#lastEvent</font>, too.<br>
<br>
<b>=============== Diff ===============</b><br>
<br>
<b>HandMorph>>currentInteractionMode {accessing} · ct 4/1/2022 14:43</b><br>
<font color="#FF0000">+ currentInteractionMode<br>
+ <br>
+     ^ Processor activeProcess environmentAt: {self. #interactionMode} ifAbsent: [<br>
+         self interactionModeForEvent: self lastEvent]</font><br>
<br>
<b>HandMorph>>freezeInteractionModeFromEvent:during: {accessing} · ct 4/1/2022 15:13</b><br>
<font color="#FF0000">+ freezeInteractionModeFromEvent: anEvent during: aBlock<br>
+ <br>
+     | outer |<br>
+     outer := self valueOfProperty: #frozenInteractionMode.<br>
+     self setProperty: #frozenInteractionMode toValue: (self interactionModeForEvent: anEvent).<br>
+     ^ aBlock ensure: [<br>
+         self setProperty: #frozenInteractionMode toValue: outer]</font><br>
<br>
<b>HandMorph>>interactionModeForEvent: {private events} · ct 4/1/2022 14:44</b><br>
<font color="#FF0000">+ interactionModeForEvent: anEvent<br>
+ <br>
+     ^ (anEvent isMouse or: [anEvent isDropEvent])<br>
+         ifTrue: [#mouse]<br>
+         ifFalse: [#keyboard]</font><br>
<br>
<b>MorphicDebugger class>>openOn:context:label:contents:fullView: {opening} · ct 4/1/2022 15:01 (changed)</b><br>
openOn: processToDebug context: context label: title contents: contentsStringOrNil fullView: full <br>
    <br>
    | debugger uiBlock |<br>
    debugger := self new<br>
        process: processToDebug context: context;<br>
        errorWasInUIProcess: (Project current spawnNewProcessIfThisIsUI: processToDebug).<br>
    <br>
    uiBlock := [<br>
        full<br>
            ifTrue: [debugger openFullNoSuspendLabel: title]<br>
            ifFalse: [debugger openNotifierNoSuspendContents: contentsStringOrNil label: title].<br>
    <br>
        "Try layouting the debugger tool at least once to avoid freeze."<br>
        debugger topView ifNotNil: [:window |<br>
            "There are way too many #fullBounds sends. Layout errors might already have happened."<br>
            window allMorphsDo: [:m | (m hasProperty: #errorOnLayout) ifTrue: [self error: 'Layout error']].<br>
            window world doLayout. "Not safely!"].<br>
        "Try drawing the debugger tool at least once to avoid freeze."<br>
        debugger topView ifNotNil: [:window | window world displayWorld. "Not safely!"].<br>
    ].<br>
        <br>
    "Schedule debugging in a deferred UI message if necessary. Note that only the ui process should execute ui code."<br>
    (Project current uiProcess isActiveProcess not or: [processToDebug isActiveProcess])<br>
        ifFalse: uiBlock<br>
<s><font color="#0000FF">-         ifTrue: [ | event |<br>
-             self flag: #discuss. "mt: We need to preserve the currentEvent for #openToolsAttachedToMouseCursor ..."<br>
-             event := self currentEvent.<br>
-             Project current addDeferredUIMessage: [event becomeActiveDuring: uiBlock]].<br>
</font></s><font color="#FF0000">+         ifTrue: [ | hand event |<br>
+             "Preserve current interaction mode for #openToolsAttachedToMouseCursor."<br>
+             hand := self currentHand.<br>
+             event := hand lastEvent.<br>
+             Project current addDeferredUIMessage: [<br>
+                 hand freezeInteractionModeFromEvent: event during: uiBlock]].<br>
</font>    <br>
    processToDebug suspend.<br>
    <br>
    "Get here only if active process is not the process-to-debug. So in tests, use a helper process if you want to access this return value."<br>
    ^ debugger<br>
<br>
<b>SystemWindow>>openAsTool {*ToolBuilder-Morphic-opening} · ct 4/1/2022 14:55 (changed)</b><br>
openAsTool<br>
    "Open this window as a tool, that is, honor the preferences such as #reuseWindows and #openToolsAttachedToMouseCursor."<br>
<s><font color="#0000FF">- <br>
</font></s><font color="#FF0000">+     <br>
</font>    | meOrSimilarWindow |<br>
    meOrSimilarWindow := self openInWorldExtent: self extent.<br>
<s><font color="#0000FF">-     (Project uiManager openToolsAttachedToMouseCursor "and: [ | event |<br>
-         event := self currentEvent.<br>
-         event isMouse and: [event isMouseUp]]") ifTrue: [<br>
-         meOrSimilarWindow setProperty: #initialDrop toValue: true.<br>
-         meOrSimilarWindow hasDropShadow: false.<br>
-         self currentHand attachMorph: meOrSimilarWindow].<br>
</font></s><font color="#FF0000">+     (Project uiManager openToolsAttachedToMouseCursor<br>
+         and: [<br>
+             "Rather than relying on #currentEvent or #lastEvent here, dispatch detection of the interaction mode to the current hand. In particular, we must support situations where the interaction mode is carried older from another process or world cycle. For example, you can evaluate the following via keyboard and then click Ok/Proceed via mouse:<br>
+                 self inform: 'Hello world!'.<br>
+                 'Hello world again!' edit.<br>
+             Here is another example:<br>
+                 self halt. self halt.<br>
+             "<br>
+             self currentHand currentInteractionMode = #mouse])<br>
+                 ifTrue: [<br>
+                     meOrSimilarWindow setProperty: #initialDrop toValue: true.<br>
+                     meOrSimilarWindow hasDropShadow: false.<br>
+                     self currentHand attachMorph: meOrSimilarWindow].<br>
</font>    ^ meOrSimilarWindow<br>
<br>
["interactionModes.1.cs"]<br>
<br>
<font color="#808080">---<br>
</font><font color="#808080"><i>Sent from </i></font><font color="#808080"><i><a href="https://github.com/hpi-swa-lab/squeak-inbox-talk"><u><font color="#808080">Squeak Inbox Talk</font></u></a></i></font><br>
["interactionModes.1.cs"]