<b>=============== Summary ===============</b><br>
<br>
Change Set:        backupAndHideDropShadows<br>
Date:            24 February 2022<br>
Author:            Christoph Thiede<br>
<br>
This changeset hides the drop shadow of any morph while it is being dragged, scaled/resized, or rotated via halos. We already do this for bordered morphs while resizing them via grips (see CornerGripMorph>>mouseDown:) for performance reasons, and this changeset generalizes and reuses the same mechanism for any interaction via halo, too.<br>
<br>
In particular, this changeset restores the old and previously unsent Etoys hooks for 'halo notifications' (#aboutToBeGrownViaHalo et al.) and integrates them into Morphic-Kernel.<br>
<br>
<b>=============== Diff ===============</b><br>
<br>
<b>CornerGripMorph>>mouseDown: {event handling} · ct 2/24/2022 12:01 (changed)</b><br>
mouseDown: anEvent <br>
    "Disable drop shadow to improve performance while resizing."<br>
<br>
    super mouseDown: anEvent.<br>
<br>
    self target ifNil: [^ self].<br>
    self target fastFramingOn ifFalse: [<br>
<s><font color="#0000FF">-         self setProperty: #targetHadDropShadow toValue: target hasDropShadow.<br>
-         self target hasDropShadow: false].<br>
</font></s><font color="#FF0000">+         self target backupAndHideDropShadows].</font><br>
<br>
<b>CornerGripMorph>>mouseUp: {event handling} · ct 2/24/2022 12:02 (changed)</b><br>
mouseUp: anEvent <br>
    "Restore target drop shadow if there was one. See #mouseDown:."<br>
    <br>
    self target ifNil: [^ self].<br>
    self target fastFramingOn ifFalse: [<br>
<s><font color="#0000FF">-         (self valueOfProperty: #targetHadDropShadow ifAbsent: [false]) ifTrue: [self target hasDropShadow: true].<br>
-         self removeProperty: #targetHadDropShadow].<br>
</font></s><font color="#FF0000">+         self target restoreDropShadows].</font><br>
<br>
<b>HaloMorph>>endInteraction: {private} · ct 2/24/2022 12:31 (changed)</b><br>
endInteraction: event<br>
    "Clean up after a user interaction with the a halo control"<br>
<br>
    | m |<br>
    self isMagicHalo: false.    "no longer"<br>
    self magicAlpha: 1.0.<br>
    (target isInWorld not or: [owner isNil]) ifTrue: [^self].<br>
    [target isFlexMorph and: [target hasNoScaleOrRotation]] whileTrue: <br>
            [m := target firstSubmorph.<br>
            target removeFlexShell.<br>
            target := m].<br>
<font color="#FF0000">+     target changedViaHalo: self.<br>
</font>    self isInWorld <br>
        ifTrue: <br>
            ["make sure handles show in front, even if flex shell added"<br>
            self flag: #tofix. "mt: Try to avoid deleting and re-creating an event handler (here: the handle) while handling the event."<br>
            self comeToFront.<br>
            self addHandles.<br>
            event hand newMouseFocus: self].<br>
    (self valueOfProperty: #commandInProgress) ifNotNil: <br>
            [:cmd | <br>
            self rememberCommand: cmd.<br>
            self removeProperty: #commandInProgress].<br>
<br>
<b>HaloMorph>>handleMouseUp: {events} · ct 2/24/2022 12:32</b><br>
<font color="#FF0000">+ handleMouseUp: evt<br>
+ <br>
+     super handleMouseUp: evt.<br>
+     <br>
+     target changedViaHalo: self.</font><br>
<br>
<b>HaloMorph>>startDrag:with: {private} · ct 2/24/2022 12:21 (changed)</b><br>
startDrag: evt with: dragHandle<br>
    "Drag my target without removing it from its owner."<br>
<br>
    | itsOwner |<br>
    self obtainHaloForEvent: evt andRemoveAllHandlesBut: dragHandle.<br>
<font color="#FF0000">+     target aboutToBeDraggedViaHalo.<br>
+     <br>
</font>    positionOffset := dragHandle center - (target point: target position in: owner).<br>
<br>
     ((itsOwner := target topRendererOrSelf owner) notNil and:<br>
            [itsOwner automaticViewing]) ifTrue:<br>
                [target openViewerForArgument]<br>
<br>
<b>HaloMorph>>startGrow:with: {private} · ct 2/24/2022 12:18 (changed)</b><br>
startGrow: evt with: growHandle<br>
    "Initialize resizing of my target.  Launch a command representing it, to support Undo"<br>
<br>
    | botRt |<br>
    self obtainHaloForEvent: evt andRemoveAllHandlesBut: growHandle.<br>
<font color="#FF0000">+     target aboutToBeGrownViaHalo.<br>
+     <br>
</font>    botRt := target point: target bottomRight in: owner.<br>
    positionOffset := (self world viewBox containsPoint: botRt)<br>
        ifTrue: [evt cursorPoint - botRt]<br>
        ifFalse: [0@0].<br>
<br>
    self setProperty: #commandInProgress toValue:<br>
        (Command new<br>
            cmdWording: ('resize ' translated, target nameForUndoWording);<br>
            undoTarget: target renderedMorph selector: #setFlexExtentFromHalo: argument: target extent).<br>
<br>
    originalExtent := target extent<br>
<br>
<b>HaloMorph>>startResizeTarget: {dragging or resizing} · ct 2/24/2022 12:20 (changed)</b><br>
startResizeTarget: event<br>
    "Begin resizing the target"<br>
    growingOrRotating := true.<br>
    positionOffset := event position.<br>
<font color="#FF0000">+     target aboutToBeScaledViaHalo.<br>
+     <br>
</font>    originalExtent := target extent.<br>
    self removeAllHandlesBut: nil.<br>
    event hand newMouseFocus: self.<br>
    event hand addMouseListener: self. "add handles back on mouse-up"<br>
<br>
<b>HaloMorph>>startRot:with: {private} · ct 2/24/2022 15:09 (changed)</b><br>
startRot: evt with: rotHandle<br>
    "Initialize rotation of my target if it is rotatable.  Launch a command object to represent the action"<br>
<br>
    self obtainHaloForEvent: evt andRemoveAllHandlesBut: rotHandle.<br>
<font color="#FF0000">+     target aboutToBeRotatedViaHalo.<br>
</font>    target isFlexMorph ifFalse: <br>
        [target isInWorld ifFalse: [self setTarget: target player costume].<br>
        target addFlexShellIfNecessary].<br>
    growingOrRotating := true.<br>
<br>
    self removeAllHandlesBut: rotHandle.  "remove all other handles"<br>
    angleOffset := evt cursorPoint - (target pointInWorld: target referencePosition).<br>
    angleOffset := Point<br>
            r: angleOffset r<br>
            degrees: angleOffset degrees - target rotationDegrees.<br>
    self setProperty: #commandInProgress toValue:<br>
        (Command new<br>
            cmdWording: ('rotate ' translated, target nameForUndoWording);<br>
            undoTarget: target renderedMorph selector: #heading: argument: target rotationDegrees)<br>
<br>
<br>
<br>
<b>HaloMorph>>startScale:with: {private} · ct 2/24/2022 15:08 (changed)</b><br>
startScale: evt with: scaleHandle<br>
    "Initialize scaling of my target."<br>
<br>
    self obtainHaloForEvent: evt andRemoveAllHandlesBut: scaleHandle.<br>
<font color="#FF0000">+     target aboutToBeScaledViaHalo.<br>
</font>    target isFlexMorph ifFalse: [target addFlexShellIfNecessary].<br>
    growingOrRotating := true.<br>
    positionOffset := 0@0.<br>
<br>
    self setProperty: #commandInProgress toValue:<br>
        (Command new<br>
            cmdWording: ('resize ' translated, target nameForUndoWording);<br>
            undoTarget: target renderedMorph selector: #setFlexExtentFromHalo: argument: target extent).<br>
    originalExtent := target extent<br>
<br>
<br>
<b>Morph>>aboutToBeDraggedViaHalo {halo notification} · ct 2/24/2022 12:20</b><br>
<font color="#FF0000">+ aboutToBeDraggedViaHalo<br>
+     "The receiver is about to be dragged via the halo."</font><br>
<br>
<b>Morph>>aboutToBeGrabbedBy: {dropping/grabbing} · ar 10/5/2000 20:00 (changed)</b><br>
aboutToBeGrabbedBy: aHand<br>
    "The receiver is being grabbed by a hand.<br>
    Perform necessary adjustments (if any) and return the actual morph<br>
    that should be added to the hand."<br>
    | extentToHandToHand cmd |<br>
    self formerOwner: owner.<br>
    self formerPosition: self position.<br>
    cmd := self undoGrabCommand.<br>
    cmd ifNotNil:[self setProperty: #undoGrabCommand toValue: cmd].<br>
    (extentToHandToHand := self valueOfProperty: #expandedExtent)<br>
            ifNotNil:<br>
                [self removeProperty: #expandedExtent.<br>
                self extent: extentToHandToHand].<br>
    ^self "Grab me"<br>
<br>
<b>Morph>>aboutToBeGrownViaHalo {halo notification} · ct 2/24/2022 12:18 (changed and recategorized)</b><br>
aboutToBeGrownViaHalo<br>
<s><font color="#0000FF">-     "The receiver is about to be grown via the halo."<br>
</font></s><font color="#FF0000">+     "The receiver is about to be grown via the halo."<br>
+     <br>
+     self backupAndHideDropShadows.</font><br>
<br>
<b>Morph>>aboutToBeRotatedViaHalo {halo notification} · ct 2/24/2022 15:09 (changed and recategorized)</b><br>
aboutToBeRotatedViaHalo<br>
<s><font color="#0000FF">-     "The receiver is about to be rotated via the halo."<br>
</font></s><font color="#FF0000">+     "The receiver is about to be rotated via the halo."<br>
+ <br>
+     self backupAndHideDropShadows.</font><br>
<br>
<b>Morph>>aboutToBeScaledViaHalo {halo notification} · ct 2/24/2022 12:20 (changed and recategorized)</b><br>
aboutToBeScaledViaHalo<br>
<s><font color="#0000FF">-     "The receiver is about to be scaled via the halo."<br>
</font></s><font color="#FF0000">+     "The receiver is about to be scaled via the halo."<br>
+ <br>
+     self backupAndHideDropShadows.</font><br>
<br>
<b>Morph>>backupAndHideDropShadows {drop shadows} · ct 2/24/2022 12:47</b><br>
<font color="#FF0000">+ backupAndHideDropShadows<br>
+     "Turn off any drop shadows for the receiver temporarily but preserve the old configuration. This is used to accelerate certain gestures that directly manipulate the receiver. The prior shadow should be restored later via #restoreDropShadows. See also #updateDropShadowCache."<br>
+ <br>
+     self hasDropShadow ifTrue: [<br>
+         self setProperty: #hadDropShadow toValue: self hasDropShadow.<br>
+         self hasDropShadow: false].</font><br>
<br>
<b>Morph>>changedViaHalo: {halo notification} · ct 2/24/2022 12:41</b><br>
<font color="#FF0000">+ changedViaHalo: halo<br>
+     "The receiver has been manipulated from a halo in an operation that has completed now."<br>
+ <br>
+     self restoreDropShadows.</font><br>
<br>
<b>Morph>>restoreDropShadows {drop shadows} · ct 2/24/2022 12:41</b><br>
<font color="#FF0000">+ restoreDropShadows<br>
+     "Restore any drop shadows in the receiver that have been temporarily hidden via #backupAndHideDropShadows."<br>
+ <br>
+     (self valueOfProperty: #hadDropShadow ifAbsent: [false]) ifTrue: [<br>
+         self hasDropShadow: true.<br>
+         self removeProperty: #hadDropShadow].</font><br>
<br>
<b>Morph>>updateDropShadowCache {drawing} · ct 2/24/2022 12:03 (changed)</b><br>
updateDropShadowCache<br>
    "Draws the receiver's drop shadow into a separate form (or cache) to be used repeatedly in #drawDropShadowOn:, which is itself guarded via #hasDropShadow (see #fullDrawOn:).<br>
    <br>
    Note that this cache is not so much about performance as it is about visual aesthetics. While the shadow itself is just one or more repeated calls to fill/frame a (rounded rectangle), we finally cut out (or mask or erase) the inner portion so that translucent receiver's wont look awkward. This is not possible with direct drawing calls to BitBlt onto Display.<br>
    <br>
    Also note that with the advent of the Spur object memory (http://www.mirandabanda.org/cogblog/category/spur/) in the OpenSmalltalk VM, we got a different garbage collector (GC) that does not yet have the most efficient incremental collection strategies. As an effect, repeated invalidation of the drop-shadow cache now entails frequent full-GC pauses and thus noticeable lags (or stuttering) in the environment. This has been the case since the release of Squeak 5.0, where we started to use the Spur object memory and hence the new GC.<br>
    <br>
<s><font color="#0000FF">-     To make the full-GC pauses less noticeable, we started to temporarily disable the drop shadow in situations where responsiveness is importent. For example, we do this for frequently used morphs such as all system windows when being resized using their corner (or edge) grips. You can get an overview of thus points by browsing senders of #targetHadDropShadow and #hasDropShadow: (or actually the code 'hasDropShadow: false', which typically starts the temporary disabling of the shadow).<br>
</font></s><font color="#FF0000">+     To make the full-GC pauses less noticeable, we started to temporarily disable the drop shadow in situations where responsiveness is importent. For example, we do this for frequently used morphs such as all system windows when being resized using their corner (or edge) grips. You can get an overview of thus points by browsing senders of #backupAndHideDropShadows and #hasDropShadow: (or actually the code 'hasDropShadow: false', which typically starts the temporary disabling of the shadow).<br>
</font>    <br>
    February 2022: We are currently working on improving the incremental compaction in the OpenSmalltalk VM. Once that issue has been solved, we can remove that source code that disables the drop shadow temporarily."<br>
<br>
    | shadowOffset shadowBounds offset form canvas drawBlock localBounds mask maskCanvas |<br>
    self flag: #hasDropShadow. "Marker for senders browsing."<br>
<s><font color="#0000FF">-     self flag: #targetHadDropShadow. "Marker for senders browsing."<br>
</font></s><font color="#FF0000">+     self flag: #backupAndHideDropShadows. "Marker for senders browsing."<br>
</font>    <br>
    (shadowOffset := self shadowOffset) isRectangle<br>
        ifTrue: [<br>
            shadowBounds := 0@0 corner: (self bounds outsetBy: shadowOffset) extent.<br>
            offset := 0@0.<br>
            localBounds := shadowOffset topLeft extent: self extent ]<br>
        ifFalse: [<br>
            | extent |<br>
            extent := self extent.<br>
            shadowBounds := 0@0 corner: extent + shadowOffset abs.<br>
            offset := shadowOffset max: 0@0.<br>
            localBounds := (shadowOffset negated max: 0@0) extent: extent ].<br>
        <br>
    form := Form extent: shadowBounds extent depth: Display depth.<br>
    canvas := form getCanvas.<br>
<br>
    drawBlock := self useSoftDropShadow<br>
        ifFalse: [<br>
            [:c | self wantsRoundedCorners<br>
                    ifTrue: [c fillRoundRect: localBounds radius: self cornerRadius fillStyle: self shadowColor]<br>
                    ifFalse: [c fillRectangle: localBounds fillStyle: self shadowColor]]]<br>
        ifTrue: [<br>
            [:c | self wantsRoundedCorners<br>
                    ifTrue: [0 to: 9 do: [:i |<br>
                        c<br>
                            fillRoundRect: (shadowBounds insetBy: i)<br>
                            radius: (self cornerRadius max: 20) -i<br>
                            fillStyle: (self shadowColor alpha: self shadowColor alpha * (i+1))]]<br>
                    ifFalse: [0 to: 9 do: [:i | <br>
                        c<br>
                            fillRoundRect: (shadowBounds insetBy: i) radius: 20-i<br>
                            fillStyle: (self shadowColor alpha: self shadowColor alpha * (i+1))]]]].<br>
            <br>
    canvas <br>
        translateBy: offset<br>
        during: [ :shadowCanvas | drawBlock value: shadowCanvas].<br>
<br>
    "Support transparent morph colors without having the shadow to shine through.."<br>
    mask := Form extent: shadowBounds extent depth: Display depth.<br>
    maskCanvas := mask getCanvas.<br>
    self wantsRoundedCorners<br>
        ifTrue: [maskCanvas fillRoundRect: (localBounds insetBy: self borderWidth) radius: self cornerRadius fillStyle: Color black]<br>
        ifFalse: [maskCanvas fillRectangle: (localBounds insetBy: self borderWidth) fillStyle: Color black].<br>
    mask<br>
        displayOn: form<br>
        at: 0@0<br>
        rule: Form erase.<br>
    <br>
    self setProperty: #dropShadow toValue: form.<br>
<br>
<b>SystemWindow>>justDroppedInto:event: {geometry} · ct 2/24/2022 12:05 (changed)</b><br>
justDroppedInto: aMorph event: anEvent<br>
<br>
    isCollapsed<br>
        ifTrue: [self position: ((self position max: 0@0) grid: 8@8).<br>
                collapsedFrame := self bounds]<br>
        ifFalse: [fullFrame := self bounds].<br>
<br>
    self beKeyWindow.<br>
    self hasDropShadow: Preferences menuAppearance3d. "See #startDragFromLabel:."<br>
            <br>
    aMorph == self world ifTrue: [self assureLabelAreaVisible].<br>
<br>
    (Project uiManager openToolsAttachedToMouseCursor and: (self hasProperty: #initialDrop))<br>
        ifTrue: [<br>
            self removeProperty: #initialDrop.<br>
            (self submorphs detect: [:m | m isKindOf: BottomRightGripMorph] ifNone: [])<br>
                ifNotNil: [:grip | <br>
<s><font color="#0000FF">-                     grip<br>
-                         referencePoint: anEvent position - grip position;<br>
-                         setProperty: #targetHadDropShadow toValue: true "See MorphicToolBuilder >> #open:".<br>
</font></s><font color="#FF0000">+                     grip referencePoint: anEvent position - grip position.<br>
</font>                    self<br>
<s><font color="#0000FF">-                         hasDropShadow: false;<br>
-                         lookFocused.<br>
-                     anEvent hand newMouseFocus: grip.]].<br>
</font></s><font color="#FF0000">+                         lookFocused;<br>
+                         backupAndHideDropShadows "See MorphicToolBuilder >> #open:".<br>
+                     anEvent hand newMouseFocus: grip]].<br>
</font>            <br>
    ^super justDroppedInto: aMorph event: anEvent<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>