<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>