<div id="__MailbirdStyleContent" style="font-size: 10pt;font-family: Arial;color: #000000;text-align: left" dir="ltr">
                                        <img id="bc40476b-1053-4f66-82c1-20ed501db98c" src="cid:94efd291-fc7a-4b8c-af47-720794ff5704" width="586" height="331"></img><br><div class="mb_sig"></div>
                                        <blockquote class="history_container" type="cite" style="border-left-style: solid;border-width: 1px;margin-top: 20px;margin-left: 0px;padding-left: 10px;min-width: 500px">
                        <p style="color: #AAAAAA; margin-top: 10px;">Am 18.04.2021 19:01:02 schrieb commits@source.squeak.org <commits@source.squeak.org>:</p><div style="font-family:Arial,Helvetica,sans-serif">Marcel Taeumel uploaded a new version of Morphic to project The Trunk:<br>http://source.squeak.org/trunk/Morphic-mt.1755.mcz<br><br>==================== Summary ====================<br><br>Name: Morphic-mt.1755<br>Author: mt<br>Time: 18 April 2021, 7:00:43.878254 pm<br>UUID: e5705780-2988-a54e-b6c7-21b4194fcd5c<br>Ancestors: Morphic-mt.1754, Morphic-ct.1659<br><br>Fix our two menu-invocating buttons to act on mouse-down and actually look like menus: (1) menu button in system window and (2) change-set button in world-main-docking-bar.<br><br>Merges Morphic-ct.1659. Thanks Christoph (ct) for pointing out this issue about menu invocation through buttons. See http://forum.world.st/The-Inbox-Morphic-ct-1659-mcz-tp5116728.html<br><br>By example, this commit also adds a menu to the change-set button in the docking bar. This menu lists the latest method and class changes.<br><br>To make this work, the following things got addressed:<br>- Fixes DockingBarUpdatingItemMorph, which missed several changes from DockingBarItemMorph. (Interesting specialization challenge...)<br>- Let menus #rubberBandCells to allow for changing menu item's widths.<br>- Adds query about latest changes to #listChangesOn:. (Feel free to improve this. Maybe add preference for number of elements?)<br><br>Complements (and depends on) System-mt.1228 and  Tools-mt.1042.<br><br>=============== Diff against Morphic-mt.1754 ===============<br><br>Item was changed:<br>  ----- Method: DockingBarMorph>>addUpdatingItem: (in category 'construction') -----<br>  addUpdatingItem: aBlock<br>         | item |<br>      item := DockingBarUpdatingItemMorph new.<br>      aBlock value: item.<br>+  item subMenu ifNotNil: [:menu |<br>+              "Docking bar and protruding menu should appear visually merged."<br>+           menu morphicLayerNumber: self morphicLayerNumber + 1].<br>        self addMorphBack: item!<br><br>Item was changed:<br>  ----- Method: DockingBarMorph>>ensureSelectedItem: (in category 'events') -----<br>  ensureSelectedItem: evt<br>     <br>      self selectedItem ifNil: [<br>            self <br>                         selectItem: (<br>                                 self submorphs <br>+                                      detect: [ :each | each isMenuItemMorph ] <br>-                                    detect: [ :each | each isKindOf: DockingBarItemMorph ] <br>                                       ifNone: [ ^self ]) <br>                   event: evt ]!<br><br>Item was changed:<br>  ----- Method: DockingBarMorph>>filterEvent:for: (in category 'events-processing') -----<br>  filterEvent: aKeyboardEvent for: anObject<br>      "Provide keyboard shortcuts."<br>       <br>      | index itemToSelect |<br>  <br>    aKeyboardEvent controlKeyPressed<br>              ifFalse: [^ aKeyboardEvent].<br>  <br>      aKeyboardEvent isKeystroke<br>            ifFalse: [^ aKeyboardEvent].<br>                          <br>      "Search field."<br>     aKeyboardEvent keyCharacter = $0<br>              ifTrue: [<br>                     self searchBarMorph ifNotNil: [ :morph |<br>                              morph model activate: aKeyboardEvent in: morph ].<br>                     ^ aKeyboardEvent ignore "hit!!"].<br>   <br>      "Select menu items."<br>        (aKeyboardEvent keyValue <br>             between: $1 asciiValue <br>               and: $9 asciiValue)<br>                   ifFalse: [^ aKeyboardEvent].    <br>                      <br>      index := aKeyboardEvent keyValue - $1 asciiValue + 1.<br>         itemToSelect := (self submorphs select: [ :each | <br>+           each isMenuItemMorph ]) <br>-             each isKindOf: DockingBarItemMorph ]) <br>                        at: index <br>                    ifAbsent: [^ aKeyboardEvent].<br>                         <br>      self activate: aKeyboardEvent.<br>        self <br>                 selectItem: itemToSelect<br>              event: aKeyboardEvent.<br>  <br>    ^ aKeyboardEvent ignore "hit!!"!<br><br>Item was changed:<br>  ----- Method: DockingBarMorph>>moveSelectionDown:event: (in category 'control') -----<br>  moveSelectionDown: direction event: evt<br>     "Move the current selection up or down by one, presumably under keyboard control.<br>        direction = +/-1"<br>  <br>    | index |<br>     index := (submorphs indexOf: selectedItem ifAbsent: [1-direction]) + direction.<br>       submorphs do: "Ensure finite"<br>               [:unused | | m |<br>              m := submorphs atWrap: index.<br>+                (m isMenuItemMorph and: [m isEnabled]) ifTrue:<br>-               ((m isKindOf: DockingBarItemMorph) and: [m isEnabled]) ifTrue:<br>                        [^ self selectItem: m event: evt].<br>            "Keep looking for an enabled item"<br>                  index := index + direction sign].<br>     ^ self selectItem: nil event: evt!<br><br>Item was added:<br>+ ----- Method: DockingBarUpdatingItemMorph>>adjacentTo (in category 'selecting') -----<br>+ adjacentTo<br>+ <br>+       | roundedCornersOffset verticalOffset |<br>+      roundedCornersOffset := MenuMorph roundedMenuCorners<br>+                 ifTrue: [Morph preferredCornerRadius negated]<br>+                ifFalse: [0].<br>+        verticalOffset := 2.<br>+ <br>+     owner isFloating<br>+             ifTrue: [^ {self bounds bottomLeft + (roundedCornersOffset @ verticalOffset)}].<br>+      owner isAdheringToTop<br>+                ifTrue: [^ {self bounds bottomLeft + (roundedCornersOffset @ verticalOffset)}].<br>+      owner isAdheringToLeft<br>+               ifTrue: [^ {self bounds topRight + (roundedCornersOffset @ verticalOffset)}].<br>+        owner isAdheringToBottom<br>+             ifTrue: [^ {self bounds topLeft + (roundedCornersOffset @ verticalOffset)}].<br>+         owner isAdheringToRight<br>+              ifTrue: [^ {self bounds topLeft + (roundedCornersOffset @ verticalOffset negated)}].<br>+         ^ {self bounds bottomLeft + (roundedCornersOffset @ 5)}!<br><br>Item was added:<br>+ ----- Method: DockingBarUpdatingItemMorph>>createSubmenu (in category 'private') -----<br>+ createSubmenu<br>+ <br>+     ^DockingBarMenuMorph new!<br><br>Item was added:<br>+ ----- Method: DockingBarUpdatingItemMorph>>createUpdatingSubmenu (in category 'private') -----<br>+ createUpdatingSubmenu<br>+ <br>+    ^DockingBarUpdatingMenuMorph new!<br><br>Item was changed:<br>+ ----- Method: DockingBarUpdatingItemMorph>>decorateOwner (in category 'world') -----<br>- ----- Method: DockingBarUpdatingItemMorph>>decorateOwner (in category 'as yet unclassified') -----<br>  decorateOwner<br>  <br>         "Ignore."!<br><br>Item was added:<br>+ ----- Method: DockingBarUpdatingItemMorph>>drawIconOn: (in category 'drawing') -----<br>+ drawIconOn: aCanvas <br>+ <br>+    | pos |<br>+      self hasIcon ifTrue: [<br>+               | iconForm | <br>+                iconForm := self iconForm.<br>+ <br>+               pos := (contents<br>+                     ifEmpty: [self left + (self width - iconForm width // 2)]<br>+                    ifNotEmpty: [self left])<br>+                             @ (self top + (self height - iconForm height // 2)).<br>+ <br>+             aCanvas<br>+                      translucentImage: iconForm <br>+                  at: pos].!<br><br>Item was added:<br>+ ----- Method: DockingBarUpdatingItemMorph>>drawLabelOn: (in category 'drawing') -----<br>+ drawLabelOn: aCanvas <br>+ <br>+    | stringBounds |        <br>+     self contents ifEmpty: [^ self].<br>+     <br>+     stringBounds := bounds.<br>+      <br>+     self hasIcon ifTrue: [<br>+               stringBounds := stringBounds left: stringBounds left + self iconForm width + 2 ].<br>+    <br>+     "Vertical centering."<br>+      stringBounds := stringBounds top: stringBounds top + stringBounds bottom - self fontToUse height // 2.<br>+       "Horizontal centering."<br>+    stringBounds := stringBounds left: stringBounds left + (stringBounds width - (self fontToUse widthOfString: contents) // 2) abs.<br>+ <br>+         aCanvas<br>+              drawString: contents<br>+                 in: stringBounds<br>+             font: self fontToUse<br>+                 color: self colorToUse.!<br><br>Item was added:<br>+ ----- Method: DockingBarUpdatingItemMorph>>drawSubMenuMarkerOn: (in category 'drawing') -----<br>+ drawSubMenuMarkerOn: aCanvas <br>+  "Ignore."!<br><br>Item was added:<br>+ ----- Method: DockingBarUpdatingItemMorph>>mouseDown: (in category 'events') -----<br>+ mouseDown: evt<br>+        "Handle a mouse down event. Menu items get activated when the mouse is over them."<br>+ <br>+     (evt shiftPressed and:[self wantsKeyboardFocusOnShiftClick]) ifTrue: [ ^super mouseDown: evt ].  "enable label editing" <br>+   isSelected<br>+           ifTrue: [<br>+                    owner selectItem: nil event: evt. ]<br>+          ifFalse: [<br>+                   owner activate: evt. "Redirect to menu for valid transitions"<br>+                      owner selectItem: self event: evt. ]<br>+ !<br><br>Item was changed:<br>+ ----- Method: DockingBarUpdatingItemMorph>>mouseEnter: (in category 'events') -----<br>- ----- Method: DockingBarUpdatingItemMorph>>mouseEnter: (in category 'as yet unclassified') -----<br>  mouseEnter: evt<br>      "Do not hover docking bar items directory. Mouse-down required. But if you already see a submenu, support hovering."<br>  <br>    owner selectedItem ifNotNil: [owner selectItem: self event: evt]!<br><br>Item was added:<br>+ ----- Method: DockingBarUpdatingItemMorph>>mouseUp: (in category 'events') -----<br>+ mouseUp: evt<br>+       "Handle a mouse up event. Menu items get activated when the mouse is over them. Do nothing if we're not in a 'valid menu transition', meaning that the current hand focus must be aimed at the owning menu."<br>+       <br>+     evt hand mouseFocus == owner ifFalse: [ ^self ].<br>+     self contentString ifNotNil: [<br>+               self contents: self contentString withMarkers: true inverse: true.<br>+           self refreshWorld.<br>+           (Delay forMilliseconds: 200) wait ].!<br><br>Item was added:<br>+ ----- Method: DockingBarUpdatingItemMorph>>select: (in category 'selecting') -----<br>+ select: evt<br>+ <br>+      super select: evt.<br>+   subMenu ifNotNil: [<br>+          evt hand newKeyboardFocus: subMenu ]!<br><br>Item was added:<br>+ ----- Method: DockingBarUpdatingItemMorph>>wantsKeyboardFocusOnShiftClick (in category 'events') -----<br>+ wantsKeyboardFocusOnShiftClick<br>+   "set this preference to false to prevent user editing of docking bar menu items"<br>+   ^Preferences valueOfPreference: #allowMenubarItemEditing ifAbsent: [false]!<br><br>Item was added:<br>+ ----- Method: DockingBarUpdatingMenuMorph>>morphicLayerNumber: (in category 'update') -----<br>+ morphicLayerNumber: n<br>+ <br>+     super morphicLayerNumber: n.<br>+         !<br><br>Item was changed:<br>  ----- Method: MenuMorph>>initialize (in category 'initialization') -----<br>  initialize<br>        super initialize.<br>  <br>         self setDefaultParameters.<br>  <br>        self changeTableLayout.<br>       self listDirection: #topToBottom.<br>     self hResizing: #shrinkWrap.<br>          self vResizing: #shrinkWrap.<br>+         self rubberBandCells: true.<br>   self disableLayout: true.<br>     self morphicLayerNumber: self class menuLayer.<br>        defaultTarget := nil.<br>         selectedItem := nil.<br>          stayUp := false.<br>      popUpOwner := nil.!<br><br>Item was changed:<br>  ----- Method: SystemWindow>>createMenuBox (in category 'initialization') -----<br>  createMenuBox<br>     ^ (self createBox: self class menuBoxImage)<br>           actionSelector: #offerWindowMenu;<br>+            setBalloonText: 'window menu' translated;<br>+            actWhen: #buttonDown;<br>+                yourself!<br>-            setBalloonText: 'window menu' translated!<br><br>Item was changed:<br>+ ----- Method: TheWorldMainDockingBar>>browseChanges (in category 'submenu - changes') -----<br>- ----- Method: TheWorldMainDockingBar>>browseChanges (in category 'right side') -----<br>  browseChanges<br>  <br>        ChangeSorter open.!<br><br>Item was changed:<br>+ ----- Method: TheWorldMainDockingBar>>browseChangesLabel (in category 'submenu - changes') -----<br>- ----- Method: TheWorldMainDockingBar>>browseChangesLabel (in category 'right side') -----<br>  browseChangesLabel<br>   "The project name is the same as the current change set."<br>   <br>      ^ Project current name!<br><br>Item was added:<br>+ ----- Method: TheWorldMainDockingBar>>changesMenuOn: (in category 'submenu - changes') -----<br>+ changesMenuOn: aDockingBar<br>+       <br>+     aDockingBar addUpdatingItem: [:item |<br>+                item<br>+                         help: 'Browse this project''s changes' translated;<br>+                   wordingProvider: self<br>+                        wordingSelector: #browseChangesLabel;<br>+                        subMenuUpdater: self<br>+                         selector: #listChangesOn:].!<br><br>Item was changed:<br>  ----- Method: TheWorldMainDockingBar>>fillDockingBar: (in category 'construction') -----<br>  fillDockingBar: aDockingBar <br>   "Private - fill the given docking bar"<br>      <br>      self menusOn: aDockingBar.<br>    aDockingBar addSpacer.<br>+       self changesMenuOn: aDockingBar.<br>-     self projectNameOn: aDockingBar.<br>      aDockingBar addSpacer.<br>        self rightSideOn: aDockingBar.<br>        aDockingBar<br>           setProperty: #mainDockingBarTimeStamp <br>                toValue: self class timeStamp.!<br><br>Item was added:<br>+ ----- Method: TheWorldMainDockingBar>>listChangesOn: (in category 'submenu - changes') -----<br>+ listChangesOn: menu<br>+ <br>+  | latestMethodChanges latestClassChanges|<br>+    latestMethodChanges := (Array streamContents: [:s |<br>+          ChangeSet current changedMethodsDo: [:method :changeType :dateAndTime :category |<br>+                    s nextPut: { dateAndTime. method. changeType. category }]])<br>+                  sorted: [:a :b | a first >= b first].<br>+ <br>+         1 to: (10 min: latestMethodChanges size) do: [:index | | spec method |<br>+               spec := latestMethodChanges at: index.<br>+               method := spec second.<br>+               menu addItem: [:item |<br>+                       item<br>+                                 contents: ('{1} {2} \{{3}\} \{{4}\}' format: {method methodClass. method selector. spec fourth. method methodClass category}) ;<br>+                              target: ToolSet;<br>+                             balloonText: spec third asString;<br>+                            icon: ((#(remove addedThenRemoved) includes: spec third) ifTrue: [MenuIcons smallDeleteIcon] ifFalse: [<br>+                                      spec third = #add ifTrue: [MenuIcons smallNewIcon] ifFalse: [MenuIcons blankIcon]]);<br>+                                 selector: (method isInstalled ifTrue: [#browseMethod:] ifFalse: [#browseMethodVersion:]);<br>+                            arguments: {method}]].<br>+                               <br>+     latestClassChanges := (Array streamContents: [:s |<br>+           ChangeSet current changedClassesDo: [:class :changeTypes :dateAndTime :category |<br>+                    "We are not interested in classes whose method's did only change."<br>+                         changeTypes ifNotEmpty: [s nextPut: { dateAndTime. class. changeTypes. category }]]])<br>+                        sorted: [:a :b | a first >= b first].<br>+ <br>+         latestClassChanges ifNotEmpty: [menu addLine].<br>+       1 to: (10 min: latestClassChanges size) do: [:index | | spec class |<br>+                 spec := latestClassChanges at: index.<br>+                class := spec second.<br>+                menu addItem: [:item |<br>+                       item<br>+                                 contents: ('{1} \{{2}\}' format: {class name. spec fourth }) ;<br>+                               target: ToolSet;<br>+                             balloonText: (spec third sorted joinSeparatedBy: Character space);<br>+                           icon: ((spec third includesAnyOf: #(remove addedThenRemoved))<br>+                                        ifTrue: [MenuIcons smallDeleteIcon]<br>+                                  ifFalse: [<br>+                                           (spec third includes: #add)<br>+                                                  ifTrue: [MenuIcons smallNewIcon]<br>+                                                     ifFalse: [MenuIcons blankIcon]]);<br>+                            selector: ((spec third includes: #remove) ifTrue: [#inspect:] ifFalse: [#browseClass:]);<br>+                             arguments: {class}]].<br>+                                <br>+     menu addLine; addItem: [:item |<br>+              item<br>+                         contents: 'Browse current change set...' translated;<br>+                         target: self;<br>+                        selector: #browseChanges].!<br><br>Item was removed:<br>- ----- Method: TheWorldMainDockingBar>>projectNameOn: (in category 'right side') -----<br>- projectNameOn: aDockingBar<br>-        <br>-     aDockingBar addUpdatingItem: [:item |<br>-                item<br>-                         help: 'Browse this project''s changes';<br>-                      target: self;<br>-                        selector: #browseChanges;<br>-                    wordingProvider: self<br>-                        wordingSelector: #browseChangesLabel].!<br><br>Item was changed:<br>+ (PackageInfo named: 'Morphic') postscript: 'TheWorldMainDockingBar updateInstances.'!<br>- (PackageInfo named: 'Morphic') postscript: 'Transcript showln: ''[NOTICE] There is a new preference called "Interactive print-it". Please check your preference browser to choose the preferred value.'''!<br><br><br></div></blockquote></div>