Hi Marcel,<br>
<br>
> Please report whether that list of recent changes makes you (want to) open the (dual) change sorter less often.<br>
<br>
Some retrospection: In plenty cases, the list of recent changes was actually a helpful shortcut for me. :-) Still, I find it a bit counterintuitive to have the generic items of the change menu at the bottom of the menu, i.e., as far away as possible from the top of the menu. I don't know if the following is already an accepted UI pattern, but IMHO dynamic menu contents should always be at the bottom of a list or menu ...<br>
<br>
Apart from that, it's nice to see all the 4 tools we have for browsing changes, but there's still some duplication. :-) For instance, what is the advantage of "browse current change set" over a single change sorter? It only seems to "miss" the list for selecting a different change sorter.<br>
<br>
Please find my proposal on rearranging the items in Morphic-ct.1781. :-)<br>
<br>
Best,<br>
Christoph<br>
<br>
<font color="#808080">---<br>
</font><i><font color="#808080">Sent from </font></i><i><u><a href="https://github.com/hpi-swa-lab/squeak-inbox-talk"><font color="#808080">Squeak Inbox Talk</font></a></u></i><br>
<br>
On 2021-04-20T11:55:18+00:00, christoph.thiede@student.hpi.uni-potsdam.de wrote:<br>
<br>
> Fair point and nice comic! :-) I will keep this in mind and report back later ...<br>
> <br>
> <br>
> Maybe we could need some kind of telemetry solution to make such evaluation more efficient. :D<br>
> <br>
> <br>
> Best,<br>
> <br>
> Christoph<br>
> <br>
> <http://www.hpi.de/><br>
> ________________________________<br>
> Von: Squeak-dev <squeak-dev-bounces at lists.squeakfoundation.org> im Auftrag von Taeumel, Marcel<br>
> Gesendet: Dienstag, 20. April 2021 10:15:31<br>
> An: squeak-dev<br>
> Betreff: Re: [squeak-dev] The Trunk: Morphic-mt.1755.mcz<br>
> <br>
> *scnr* https://xkcd.com/1172/ ^__^<br>
> <br>
> Please report whether that list of recent changes makes you (want to) open the (dual) change sorter less often.<br>
> <br>
> Best,<br>
> Marcel<br>
> <br>
> Am 19.04.2021 12:58:05 schrieb Thiede, Christoph <christoph.thiede at student.hpi.uni-potsdam.de>:<br>
> <br>
> Hi Marcel,<br>
> <br>
> <br>
> interesting concept, I will try out the usability the next time! Some first thoughts:<br>
> <br>
> <br>
> * I very often used this button to open a change sorter window. (The new menu is not bad, but of course, it only offers a subset of all functionalities of a change sorter). Can we maybe move the "Browse" item to the top of the menu?<br>
> * Also, we might want to add a link to the DualChangeSorter there. I use that tool very often and I think this would be a way to access it even faster.<br>
> <br>
> Best,<br>
> Christoph<br>
> <br>
> ________________________________<br>
> Von: Squeak-dev <squeak-dev-bounces at lists.squeakfoundation.org> im Auftrag von Taeumel, Marcel<br>
> Gesendet: Sonntag, 18. April 2021 19:05 Uhr<br>
> An: squeak-dev<br>
> Betreff: Re: [squeak-dev] The Trunk: Morphic-mt.1755.mcz<br>
> <br>
> [cid:94efd291-fc7a-4b8c-af47-720794ff5704]<br>
> <br>
> Am 18.04.2021 19:01:02 schrieb commits at source.squeak.org <commits at source.squeak.org>:<br>
> <br>
> 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>
> -------------- next part --------------<br>
> An HTML attachment was scrubbed...<br>
> URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20210420/1c31bc65/attachment-0001.html><br>
> -------------- next part --------------<br>
> A non-text attachment was scrubbed...<br>
> Name: image.png<br>
> Type: image/png<br>
> Size: 160977 bytes<br>
> Desc: image.png<br>
> URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20210420/1c31bc65/attachment-0001.png><br>
> <br>