<div dir="ltr"><div>I'm not sure why, but this diff is showing a bunch of extra stuff.  Use MC's diff to see just the two changes.<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Jan 10, 2020 at 7:33 PM <<a href="mailto:commits@source.squeak.org" target="_blank">commits@source.squeak.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Chris Muller uploaded a new version of Morphic to project The Inbox:<br>
<a href="http://source.squeak.org/inbox/Morphic-cmm.1617.mcz" rel="noreferrer" target="_blank">http://source.squeak.org/inbox/Morphic-cmm.1617.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: Morphic-cmm.1617<br>
Author: cmm<br>
Time: 10 January 2020, 7:32:31.635312 pm<br>
UUID: ce0033f9-62f5-4c12-9b75-24d613d56c50<br>
Ancestors: Morphic-tpr.1616<br>
<br>
- Fix inescapable modal dialog.<br>
- Fix truncation of the first line of PluggableListMorphs when a font larger than the default is used.<br>
<br>
=============== Diff against Morphic-tpr.1616 ===============<br>
<br>
Item was added:<br>
+ ----- Method: AlternatePluggableListMorphOfMany>>mouseLeaveDragging: (in category 'event handling') -----<br>
+ mouseLeaveDragging: anEvent<br>
+       "Dragging means changing the list's multi-selection state. Thus, there is no support for drag-and-drop of elements within a selection."<br>
+       <br>
+       self hoverRow: nil.<br>
+       self resetPotentialDropRow.!<br>
<br>
Item was changed:<br>
  ----- Method: DialogWindow>>getUserResponse (in category 'running') -----<br>
  getUserResponse<br>
<br>
        | hand world |<br>
        self message ifEmpty: [messageMorph delete]. "Do not waste space."<br>
        self paneMorph submorphs<br>
                ifEmpty: ["Do not waste space and avoid strange button-row wraps."<br>
                        self paneMorph delete.<br>
                        self buttonRowMorph wrapDirection: #none]. <br>
<br>
        hand := self currentHand.<br>
        world := self currentWorld.<br>
<br>
        self fullBounds.<br>
        self moveToPreferredPosition.<br>
        self openInWorld: world.<br>
<br>
        hand showTemporaryCursor: nil. "Since we are out of context, reset the cursor."<br>
<br>
        hand keyboardFocus in: [:priorKeyboardFocus |<br>
                hand mouseFocus in: [:priorMouseFocus |<br>
                        self exclusive ifTrue: [hand newMouseFocus: self].<br>
                        hand newKeyboardFocus: self.<br>
<br>
                        [[self isInWorld] whileTrue: [world doOneSubCycle]]<br>
                                ifCurtailed: [self cancelDialog].<br>
<br>
                        hand newKeyboardFocus: priorKeyboardFocus.<br>
+                       hand releaseMouseFocus]].<br>
-                       hand newMouseFocus: priorMouseFocus]].<br>
<br>
        ^ result!<br>
<br>
Item was changed:<br>
  ----- Method: FontChooserTool>>selectedPointSizeIndex: (in category 'point size') -----<br>
  selectedPointSizeIndex: anIndex<br>
<br>
        anIndex = 0 ifTrue: [^self].<br>
        pointSize := (self pointSizeList at: anIndex) withBlanksTrimmed asNumber.<br>
+       self changed: #selectedPointSizeIndex.<br>
        self changed: #pointSizeList.<br>
        self changed: #contents.!<br>
<br>
Item was changed:<br>
  Morph subclass: #HandMorph<br>
+       instanceVariableNames: 'mouseFocus keyboardFocus eventListeners mouseListeners keyboardListeners eventCaptureFilters mouseCaptureFilters keyboardCaptureFilters mouseClickState mouseOverHandler mouseWheelState lastMouseEvent targetOffset damageRecorder cacheCanvas cachedCanvasHasHoles temporaryCursor temporaryCursorOffset hardwareCursor hasChanged savedPatch userInitials lastEventBuffer genieGestureProcessor keyboardInterpreter'<br>
+       classVariableNames: 'CompositionWindowManager DoubleClickTime DragThreshold EventStats NewEventRules NormalCursor PasteBuffer SendMouseWheelToKeyboardFocus ShowEvents SynthesizeMouseWheelEvents'<br>
-       instanceVariableNames: 'mouseFocus keyboardFocus eventListeners mouseListeners keyboardListeners eventCaptureFilters mouseCaptureFilters keyboardCaptureFilters mouseClickState mouseOverHandler targetOffset lastMouseEvent damageRecorder cacheCanvas cachedCanvasHasHoles temporaryCursor temporaryCursorOffset hardwareCursor hasChanged savedPatch userInitials lastEventBuffer genieGestureProcessor keyboardInterpreter'<br>
-       classVariableNames: 'CompositionWindowManager DoubleClickTime DragThreshold EventStats MinimalWheelDelta NewEventRules NormalCursor PasteBuffer SendMouseWheelToKeyboardFocus ShowEvents SynthesizeMouseWheelEvents'<br>
        poolDictionaries: 'EventSensorConstants'<br>
        category: 'Morphic-Kernel'!<br>
<br>
  !HandMorph commentStamp: '<historical>' prior: 0!<br>
  The cursor may be thought of as the HandMorph.  The hand's submorphs hold anything being carried by dragging.  <br>
<br>
  There is some minimal support for multiple hands in the same world.!<br>
<br>
Item was removed:<br>
- ----- Method: HandMorph class>>minimumWheelDelta (in category 'preferences') -----<br>
- minimumWheelDelta<br>
-       <preference: 'Minimal Mouse Wheel Scroll Delta'<br>
-               categoryList: #(Morphic mouse)<br>
-               description: 'Answer the minimal scroll increment taken into account<br>
- Defaults to 120, corresponding to a single mouse wheel notch.<br>
- Use a lower value (20) if wanting smoother scrolling with trackpads.'<br>
-               type: #Number><br>
-       ^MinimalWheelDelta ifNil: [120].!<br>
<br>
Item was removed:<br>
- ----- Method: HandMorph class>>minimumWheelDelta: (in category 'preferences') -----<br>
- minimumWheelDelta: anInteger<br>
-       MinimalWheelDelta := anInteger ifNotNil: [anInteger clampLow: 20 high: 120]!<br>
<br>
Item was changed:<br>
  ----- Method: HandMorph>>generateMouseWheelEvent: (in category 'private events') -----<br>
  generateMouseWheelEvent: evtBuf<br>
        "Generate the appropriate mouse wheel event for the given raw event buffer"<br>
<br>
+       | buttons modifiers deltaX deltaY stamp |<br>
-       | buttons modifiers deltaX deltaY stamp nextEvent |<br>
        stamp := evtBuf second.<br>
        stamp = 0 ifTrue: [stamp := Time eventMillisecondClock].<br>
        deltaX := evtBuf third.<br>
        deltaY := evtBuf fourth.<br>
+       modifiers := evtBuf fifth.<br>
+       buttons := (modifiers bitShift: 3) bitOr: (lastMouseEvent buttons bitAnd: 7).<br>
-       buttons := evtBuf fifth.<br>
-       modifiers := evtBuf sixth.<br>
-       [(deltaX abs + deltaY abs < self class minimumWheelDelta)<br>
-                       and: [(nextEvent := Sensor peekEvent) notNil<br>
-                       and: [nextEvent first = evtBuf first<br>
-                       and: [nextEvent fifth = evtBuf fifth <br>
-                       and: [nextEvent sixth = evtBuf sixth]<br>
-                       and: [nextEvent third isZero = evtBuf third isZero "both horizontal or vertical"]]]]]<br>
-               whileTrue:<br>
-                       ["nextEvent is similar.  Remove it from the queue, and check the next."<br>
-                       nextEvent := Sensor nextEvent.<br>
-                       deltaX := deltaX + nextEvent third.<br>
-                       deltaY := deltaY + nextEvent fourth].<br>
        ^ MouseWheelEvent new<br>
                setType: #mouseWheel<br>
                position: self position<br>
                delta: deltaX@deltaY<br>
+               direction: 2r0000<br>
                buttons: buttons        <br>
                hand: self<br>
                stamp: stamp!<br>
<br>
Item was changed:<br>
  ----- Method: HandMorph>>handleEvent: (in category 'events-processing') -----<br>
  handleEvent: unfilteredEvent<br>
<br>
        | filteredEvent |<br>
        owner ifNil: [^ unfilteredEvent  "not necessary but good style -- see Morph >> #handleEvent:"].<br>
<br>
        self logEvent: unfilteredEvent.<br>
<br>
        "Mouse-over events occur really, really, really often. They are kind of the heart beat of the Morphic UI process."<br>
        unfilteredEvent isMouseOver ifTrue: [^ self sendMouseEvent: unfilteredEvent].<br>
<br>
        self showEvent: unfilteredEvent.<br>
        self sendListenEvents: unfilteredEvent.<br>
<br>
        filteredEvent := self sendFilterEventCapture: unfilteredEvent for: nil.<br>
        "filteredEvent := unfilteredEvent" " <-- use this to disable global capture filters"<br>
<br>
        filteredEvent wasIgnored ifTrue: [<br>
                self mouseOverHandler processMouseOver: lastMouseEvent.<br>
                ^ filteredEvent].<br>
<br>
        filteredEvent isWindowEvent ifTrue: [<br>
                self sendEvent: filteredEvent focus: nil.<br>
                self mouseOverHandler processMouseOver: lastMouseEvent.<br>
                ^ filteredEvent].<br>
<br>
        filteredEvent isKeyboard ifTrue:[<br>
                self sendKeyboardEvent: filteredEvent.<br>
                self mouseOverHandler processMouseOver: lastMouseEvent.<br>
                ^ filteredEvent].<br>
<br>
        filteredEvent isDropEvent ifTrue:[<br>
                self sendEvent: filteredEvent focus: nil.<br>
                self mouseOverHandler processMouseOver: lastMouseEvent.<br>
                ^ filteredEvent].<br>
<br>
        filteredEvent isMouse ifFalse: [<br>
                self mouseOverHandler processMouseOver: lastMouseEvent.<br>
                ^ filteredEvent].<br>
<br>
        " ********** MOUSE EVENT *********** "<br>
<br>
        lastMouseEvent := filteredEvent.<br>
<br>
        "Check for pending drag or double click operations."<br>
        mouseClickState ifNotNil:[<br>
                (mouseClickState handleEvent: filteredEvent from: self) ifFalse:[<br>
                        "Possibly dispatched #click: or something and will not re-establish otherwise"<br>
                        self mouseOverHandler processMouseOver: lastMouseEvent.<br>
                        ^ filteredEvent]].<br>
<br>
        filteredEvent isMouseWheel ifTrue: [<br>
+               mouseWheelState ifNil: [mouseWheelState := MouseWheelState new].<br>
+               mouseWheelState handleEvent: filteredEvent from: self.<br>
-               self class sendMouseWheelToKeyboardFocus<br>
-                       ifFalse: [self sendMouseEvent: filteredEvent]<br>
-                       ifTrue: [self sendEvent: filteredEvent focus: self keyboardFocus clear: [self keyboardFocus: nil]].<br>
                self mouseOverHandler processMouseOver: lastMouseEvent.<br>
                ^ filteredEvent].<br>
<br>
        filteredEvent isMove ifTrue:[<br>
                self position: filteredEvent position.<br>
                self sendMouseEvent: filteredEvent.<br>
                self mouseOverHandler processMouseOver: lastMouseEvent.<br>
                ^ filteredEvent].<br>
<br>
        "Issue a synthetic move event if we're not at the position of the event"<br>
        filteredEvent position = self position<br>
                ifFalse: [self moveToEvent: filteredEvent].<br>
<br>
        "Drop submorphs on button events"<br>
        self hasSubmorphs<br>
                ifTrue:[self dropMorphs: filteredEvent]<br>
                ifFalse:[self sendMouseEvent: filteredEvent].<br>
<br>
        self mouseOverHandler processMouseOver: lastMouseEvent.<br>
        ^ filteredEvent "not necessary but good style -- see Morph >> #handleEvent:"    !<br>
<br>
Item was removed:<br>
- ----- Method: MouseWheelEvent>>setDirection (in category 'initialization') -----<br>
- setDirection<br>
-       delta x > 0 ifTrue: [self setWheelRight].<br>
-       delta x < 0 ifTrue: [self setWheelLeft].<br>
- <br>
-       delta y > 0 ifTrue: [self setWheelUp].<br>
-       delta y < 0 ifTrue: [self setWheelDown].!<br>
<br>
Item was removed:<br>
- ----- Method: MouseWheelEvent>>setType:position:delta:buttons:hand:stamp: (in category 'private') -----<br>
- setType: evtType position: evtPos delta: evtDelta buttons: evtButtons hand: evtHand stamp: stamp<br>
-       type := evtType.<br>
-       position := evtPos.<br>
-       buttons := evtButtons.<br>
-       source := evtHand.<br>
-       wasHandled := false.<br>
-       direction := 2r0000.<br>
-       delta := evtDelta.<br>
-       timeStamp := stamp.<br>
-       self setDirection!<br>
<br>
Item was added:<br>
+ Object subclass: #MouseWheelState<br>
+       instanceVariableNames: 'currentDelta'<br>
+       classVariableNames: ''<br>
+       poolDictionaries: ''<br>
+       category: 'Morphic-Events'!<br>
<br>
Item was added:<br>
+ ----- Method: MouseWheelState>>handleEvent:from: (in category 'event processing') -----<br>
+ handleEvent: aMouseWheelEvent from: aHand<br>
+       "Every 120 units, raise the wheel flags for convenient mouse wheel programming. We choose not to send multiple mouse-wheel events for multiples of 120 because applications can always react to the actual delta values if they want to do more scrolling or zooming."<br>
+       <br>
+       | sign |<br>
+       currentDelta := currentDelta + aMouseWheelEvent wheelDelta.<br>
+ <br>
+       sign := currentDelta sign.<br>
+       currentDelta := currentDelta abs.<br>
+ <br>
+       (currentDelta x // 120) > 0 ifTrue: [<br>
+               sign x = 1<br>
+                       ifTrue: [aMouseWheelEvent setWheelRight]<br>
+                       ifFalse: [aMouseWheelEvent setWheelLeft]].<br>
+ <br>
+       (currentDelta y // 120) > 0 ifTrue: [<br>
+               sign y = 1<br>
+                       ifTrue: [aMouseWheelEvent setWheelUp]<br>
+                       ifFalse: [aMouseWheelEvent setWheelDown]].<br>
+               <br>
+       currentDelta := currentDelta \\ 120.<br>
+       currentDelta := currentDelta * sign.<br>
+ <br>
+       "Finally, send the event."<br>
+       HandMorph sendMouseWheelToKeyboardFocus<br>
+               ifFalse: [aHand sendMouseEvent: aMouseWheelEvent]<br>
+               ifTrue: [aHand sendEvent: aMouseWheelEvent focus: aHand keyboardFocus clear: [aHand keyboardFocus: nil]].<br>
+ !<br>
<br>
Item was added:<br>
+ ----- Method: MouseWheelState>>initialize (in category 'initialize-release') -----<br>
+ initialize<br>
+ <br>
+       super initialize.<br>
+       currentDelta := 0@0.!<br>
<br>
Item was changed:<br>
  ----- Method: PluggableListMorph>>initialize (in category 'initialization') -----<br>
  initialize<br>
- <br>
        listMorph := self createListMorph.<br>
        super initialize.<br>
- <br>
        self scroller<br>
                layoutPolicy: TableLayout new;<br>
                addMorph: listMorph.    <br>
+       self<br>
+               minimumWidth: (self font widthOf: $m) * 5 ;<br>
+               minimumHeight: self font height<br>
<br>
-       self minimumWidth: (self font widthOf: $m) * 5.<br>
-       <br>
        !<br>
<br>
Item was changed:<br>
  ----- Method: PluggableListMorph>>mouseUp: (in category 'event handling') -----<br>
  mouseUp: event <br>
<br>
        | row |<br>
        model okToChange ifFalse: [^ self].<br>
+       (self containsPoint: event position) ifFalse: [^ self].<br>
<br>
        row := self rowAtLocation: event position.<br>
        row = self selectionIndex<br>
                ifTrue: [(autoDeselect ifNil: [true]) ifTrue: [row = 0 ifFalse: [self changeModelSelection: 0] ]]<br>
                ifFalse: [self changeModelSelection: (self modelIndexFor: row)].<br>
<br>
        event hand newKeyboardFocus: self. <br>
        hasFocus := true.<br>
        Cursor normal show.!<br>
<br>
Item was changed:<br>
  ----- Method: ProportionalSplitterMorph>>topBoundary (in category 'queries - geometry') -----<br>
  topBoundary<br>
        "Answer the topmost x position the receiver could be moved to."<br>
<br>
        | splitter morphs |<br>
        splitter := self splitterAbove.<br>
        morphs := self commonNeighbours: leftOrTop with: splitter.<br>
-       <br>
        ^ (splitter<br>
                ifNil: [owner isSystemWindow ifTrue: [owner panelRect top]<br>
                                ifFalse: [owner innerBounds top]]<br>
                ifNotNil: [splitter bottom])<br>
                + (self minimumHeightOf: morphs)!<br>
<br>
Item was changed:<br>
  ----- Method: ScrollPane>>mouseWheel: (in category 'event handling') -----<br>
  mouseWheel: evt<br>
<br>
+       evt isWheelUp ifTrue: [scrollBar scrollUp: 3].<br>
+       evt isWheelDown ifTrue: [scrollBar scrollDown: 3].!<br>
-       evt isWheelUp ifTrue: [scrollBar scrollUp: (3 * evt wheelDelta y abs // 120 max: 1)].<br>
-       evt isWheelDown ifTrue: [scrollBar scrollDown: (3 * evt wheelDelta y abs // 120 max: 1)].<br>
-       evt isWheelLeft ifTrue: [hScrollBar scrollUp: (3 * evt wheelDelta x abs // 120 max: 1)].<br>
-       evt isWheelRight ifTrue: [hScrollBar scrollDown: (3 * evt wheelDelta x abs // 120 max: 1)].!<br>
<br>
Item was changed:<br>
  ----- Method: StringMorph>>contents: (in category 'accessing') -----<br>
  contents: newContents <br>
<br>
        newContents isText<br>
                ifTrue: [^ self initializeFromText: newContents].<br>
<br>
        contents = newContents<br>
+               ifTrue: [^ self "No substantive change."].<br>
-               ifTrue: [^ self "no substantive change"].<br>
<br>
        contents := newContents.<br>
+       self changed. "New contents need to be drawn."<br>
+               <br>
+       self fitContents. "Resize if necessary."!<br>
-       <br>
-       self fitContents.!<br>
<br>
Item was changed:<br>
  ----- Method: StringMorph>>fitContents (in category 'layout') -----<br>
  fitContents<br>
<br>
+       self extent: self measureContents.!<br>
-       | newBounds boundsChanged |<br>
-       newBounds := self measureContents.<br>
-       boundsChanged := bounds extent ~= newBounds.<br>
-       self extent: newBounds.         "default short-circuits if bounds not changed"<br>
-       boundsChanged ifFalse: [self changed]!<br>
<br>
Item was changed:<br>
  ----- Method: UpdatingStringMorph>>fitContents (in category 'layout') -----<br>
  fitContents<br>
+       "Overridden to respect minimum and maximum widfth."<br>
+       <br>
- <br>
        | newExtent |<br>
+       newExtent :=  self measureContents.<br>
+       self extent: ((newExtent x max: self minimumWidth) min: self maximumWidth) @ newExtent y.!<br>
-       newExtent := self measureContents.<br>
-       newExtent := ((newExtent x max: self minimumWidth) min: self maximumWidth) @ newExtent y.<br>
-       (self extent = newExtent) ifFalse:<br>
-               [self extent: newExtent.<br>
-               self changed]<br>
- !<br>
<br>
Item was changed:<br>
  ----- Method: UpdatingStringMorph>>updateContentsFrom: (in category 'stepping') -----<br>
  updateContentsFrom: aValue<br>
        self growable<br>
                ifTrue:<br>
+                       [self contentsFitted: aValue]<br>
-                       [self contents: aValue]<br>
                ifFalse:<br>
                        [self contentsClipped: aValue]!<br>
<br>
<br>
</blockquote></div>