Marcel Taeumel uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-mt.2112.mcz
==================== Summary ====================
Name: Morphic-mt.2112 Author: mt Time: 9 June 2023, 1:53:13.652748 pm UUID: b55c81ff-2d7a-354d-9bf8-3012d349e9bf Ancestors: Morphic-mt.2111
Speed up tree widgets: - Bypass #fullBounds for each item in a tree - Bypass #adoptPaneColor: in transform morphs - Use Morphic's layout-policy mechansim via new PluggableLayoutPolicy - Dispatch user-input events through layout-policy if available to optimize submorphs-enumeration
Also document a mismatch between #removeAllMorphsIn: and #privateAddAllMorphs:atIndex:.
=============== Diff against Morphic-mt.2111 ===============
Item was changed: ----- Method: IndentingListItemMorph>>collapse (in category 'container protocol') ----- collapse
self isExpanded ifFalse: [^ self]. self isExpanded: false. firstChild ifNotNil: [:collapsingNode | | toDelete | toDelete := OrderedCollection new. collapsingNode withSiblingsDo: [:aNode | aNode recursiveAddTo: toDelete]. container noteRemovalOfAll: toDelete. + firstChild := nil].! - firstChild := nil]. - - self changed.!
Item was changed: ----- Method: IndentingListItemMorph>>expand (in category 'container protocol') ----- expand
| newChildren c |
(self isExpanded or: [self canExpand not]) ifTrue: [^ self]. (c := self getChildren) ifEmpty: [ "Due to the guessing in #canExpand, it may still fail here." ^ self].
self isExpanded: true.
newChildren := container addSubmorphsAfter: self fromCollection: c allowSorting: true. + container changed. "See mismatch between #removeAllMorphsIn: and #privateAddAllMorphs:atIndex:"
firstChild := newChildren first.!
Item was added: + ----- Method: IndentingListItemMorph>>fullBounds (in category 'layout') ----- + fullBounds + "For a better performance when containers have thousands of the receiver's kind, avoid extra layout checks since we already know that the receiver changes its appearance only via #drawOn: like other StringMorph's." + + ^ fullBounds ifNil: [fullBounds := bounds]!
Item was changed: ----- Method: IndentingListItemMorph>>openPath: (in category 'container protocol - private') ----- openPath: anArray | found | anArray isEmpty ifTrue: [^ container setSelectedMorph: nil]. found := nil. self withSiblingsDo: [:each | found ifNil: [(each complexContents asString = anArray first or: [anArray first isNil]) ifTrue: [found := each]]]. found ifNil: ["try again with no case sensitivity" self withSiblingsDo: [:each | found ifNil: [(each complexContents asString sameAs: anArray first) ifTrue: [found := each]]]]. found ifNotNil: [found isExpanded + ifFalse: [found toggleExpandedState]. - ifFalse: [found toggleExpandedState. - container adjustSubmorphPositions]. - found changed. anArray size = 1 ifTrue: [^ container setSelectedMorph: found]. ^ found firstChild ifNil: [container setSelectedMorph: nil] ifNotNil: [found firstChild openPath: anArray allButFirst]]. ^ container setSelectedMorph: nil!
Item was changed: ----- Method: IndentingListItemMorph>>update: (in category 'updating') ----- update: aspect "See ListItemWrapper and subclasses for possible change aspects." aspect = #contents ifTrue: [ canExpand := nil. self isExpanded ifTrue: [self toggleExpandedState]. + self canExpand ifTrue: [self toggleExpandedState]]. - self canExpand ifTrue: [self toggleExpandedState]. - container adjustSubmorphPositions]. super update: aspect.!
Item was added: + ----- Method: LayoutPolicy>>isPluggableLayout (in category 'testing') ----- + isPluggableLayout + + ^ false!
Item was added: + ----- Method: LayoutPolicy>>submorphsOf:do: (in category 'layout') ----- + submorphsOf: aMorph do: aBlock + "Enumerate submorphs of aMorph, evaluating aBlock on each submorph. Layout policies can override this for a more efficient enumeration." + + aMorph submorphsDo: aBlock.!
Item was changed: ----- Method: Morph>>removeAllMorphsIn: (in category 'submorphs - add/remove') ----- removeAllMorphsIn: aCollection "greatly speeds up the removal of *lots* of submorphs" | set myWorld | set := IdentitySet new: aCollection size * 4 // 3. aCollection do: [:each | each owner == self ifTrue: [ set add: each]]. myWorld := self world. + self flag: #todo. "mt: The use of #invalidRect: here is inconsistent with what #privateAddAllMorphs:atIndex: does... Maybe redraw the receiver in both cases?" (fullBounds notNil or:[myWorld notNil]) ifTrue:[self invalidRect: self fullBounds]. set do: [:m | myWorld ifNotNil: [ m outOfWorld: myWorld ]. m privateOwner: nil]. submorphs := submorphs reject: [ :each | set includes: each]. set do: [ :m | self removedMorph: m ]. self layoutChanged. !
Item was changed: ----- Method: MorphicEventDispatcher>>dispatchEvent:toSubmorphsOf: (in category 'support') ----- dispatchEvent: anEvent toSubmorphsOf: aMorph "Dispatch the given event to the submorphs of the given morph. For coordinate transformations, work only with copies. Either return the given event or a copy of any filtered event to employ immutability to some extent. --- PRIVATE!!"
+ | dispatchBlock localEvent filteredEvent | + dispatchBlock := [:child | - | localEvent filteredEvent | - aMorph submorphsDo: [:child | localEvent := anEvent transformedBy: (child transformedFrom: aMorph). filteredEvent := child processEvent: localEvent. filteredEvent == #rejected ifFalse: [ "some event or #rejected symbol or any other object" filteredEvent isMorphicEvent ifFalse: [filteredEvent := localEvent]. self flag: #overlappingChildren. "mt: We cannot give two overlapping siblings the chance to handle the event!!" ^ self nextFromOriginal: anEvent local: localEvent filtered: filteredEvent]].
+ aMorph layoutPolicy + ifNil: [aMorph submorphsDo: dispatchBlock] + ifNotNil: [:policy | policy submorphsOf: aMorph do: dispatchBlock]. + ^ #rejected!
Item was added: + LayoutPolicy subclass: #PluggableLayoutPolicy + instanceVariableNames: 'target layoutSelector minExtentSelector flushCacheSelector submorphsSelector' + classVariableNames: '' + poolDictionaries: '' + category: 'Morphic-Layouts'!
Item was added: + ----- Method: PluggableLayoutPolicy class>>on:layout: (in category 'instance creation') ----- + on: target layout: layoutSelector + + ^ self new + target: target; + layoutSelector: layoutSelector; + yourself!
Item was added: + ----- Method: PluggableLayoutPolicy class>>on:layout:minExtent: (in category 'instance creation') ----- + on: target layout: layoutSelector minExtent: minExtentSelector + + ^ self new + target: target; + layoutSelector: layoutSelector; + minExtentSelector: minExtentSelector; + yourself!
Item was added: + ----- Method: PluggableLayoutPolicy class>>on:layout:minExtent:flushCache: (in category 'instance creation') ----- + on: target layout: layoutSelector minExtent: minExtentSelector flushCache: flushCacheSelector + + ^ self new + target: target; + layoutSelector: layoutSelector; + minExtentSelector: minExtentSelector; + flushCacheSelector: flushCacheSelector; + yourself!
Item was added: + ----- Method: PluggableLayoutPolicy class>>on:layout:submorphs: (in category 'instance creation') ----- + on: target layout: layoutSelector submorphs: submorphsSelector + + ^ self new + target: target; + layoutSelector: layoutSelector; + submorphsSelector: submorphsSelector; + yourself!
Item was added: + ----- Method: PluggableLayoutPolicy>>flushCacheSelector (in category 'accessing') ----- + flushCacheSelector + + ^ flushCacheSelector!
Item was added: + ----- Method: PluggableLayoutPolicy>>flushCacheSelector: (in category 'accessing') ----- + flushCacheSelector: anObject + + flushCacheSelector := anObject.!
Item was added: + ----- Method: PluggableLayoutPolicy>>flushLayoutCache (in category 'layout') ----- + flushLayoutCache + + flushCacheSelector + ifNil: [super flushLayoutCache] + ifNotNil: [target perform: flushCacheSelector].!
Item was added: + ----- Method: PluggableLayoutPolicy>>isPluggableLayout (in category 'testing') ----- + isPluggableLayout + + ^ true!
Item was added: + ----- Method: PluggableLayoutPolicy>>layout:in: (in category 'layout') ----- + layout: aMorph in: newBounds + + | arity | + layoutSelector ifNil: [^ super layout: aMorph in: newBounds]. + arity := layoutSelector numArgs. + arity = 0 ifTrue: [^ target perform: layoutSelector]. + arity = 1 ifTrue: [^ target perform: layoutSelector with: newBounds "target should know about aMorph already"]. + arity = 2 ifTrue: [^ target perform: layoutSelector with: aMorph with: newBounds "keep #layout:in: order"]. + self error: 'Layout selectors must be symbols that take 0-2 arguments'!
Item was added: + ----- Method: PluggableLayoutPolicy>>layoutSelector (in category 'accessing') ----- + layoutSelector + + ^ layoutSelector!
Item was added: + ----- Method: PluggableLayoutPolicy>>layoutSelector: (in category 'accessing') ----- + layoutSelector: anObject + + layoutSelector := anObject.!
Item was added: + ----- Method: PluggableLayoutPolicy>>minExtentOf:in: (in category 'layout') ----- + minExtentOf: aMorph in: newBounds + + | arity | + minExtentSelector ifNil: [^ super minExtentOf: aMorph in: newBounds]. + arity := minExtentSelector numArgs. + arity = 0 ifTrue: [^ target perform: minExtentSelector]. + arity = 1 ifTrue: [^ target perform: minExtentSelector with: newBounds "target should know about aMorph already"]. + arity = 2 ifTrue: [^ target perform: minExtentSelector with: aMorph with: newBounds "keep #layout:in: order"]. + self error: 'Layout selectors must be symbols that take 0-2 arguments'!
Item was added: + ----- Method: PluggableLayoutPolicy>>minExtentSelector (in category 'accessing') ----- + minExtentSelector + + ^ minExtentSelector!
Item was added: + ----- Method: PluggableLayoutPolicy>>minExtentSelector: (in category 'accessing') ----- + minExtentSelector: anObject + + minExtentSelector := anObject.!
Item was added: + ----- Method: PluggableLayoutPolicy>>submorphsOf:do: (in category 'layout') ----- + submorphsOf: aMorph do: aBlock + + | arity | + submorphsSelector ifNil: [^ super submorphsOf: aMorph do: aBlock]. + arity := submorphsSelector numArgs. + arity = 1 ifTrue: [^ target perform: submorphsSelector with: aBlock "target should know about aMorph already"]. + arity = 2 ifTrue: [^ target perform: submorphsSelector with: aMorph with: aBlock "keep #submorphsOf:do: order"]. + self error: 'Layout selectors must be symbols that take 1-2 arguments'!
Item was added: + ----- Method: PluggableLayoutPolicy>>submorphsSelector (in category 'accessing') ----- + submorphsSelector + + ^ submorphsSelector!
Item was added: + ----- Method: PluggableLayoutPolicy>>submorphsSelector: (in category 'accessing') ----- + submorphsSelector: anObject + + submorphsSelector := anObject.!
Item was added: + ----- Method: PluggableLayoutPolicy>>target (in category 'accessing') ----- + target + + ^ target!
Item was added: + ----- Method: PluggableLayoutPolicy>>target: (in category 'accessing') ----- + target: anObject + + target := anObject.!
Item was removed: - ----- Method: PluggableListMorph>>itemFromPoint: (in category 'accessing - items') ----- - itemFromPoint: aPoint - "Return the list element (morph) at the given point or nil if outside" - | ptY | - scroller hasSubmorphs ifFalse:[^nil]. - (scroller fullBounds containsPoint: aPoint) ifFalse:[^nil]. - ptY := (scroller firstSubmorph point: aPoint from: self) y. - "note: following assumes that submorphs are vertical, non-overlapping, and ordered" - scroller firstSubmorph top > ptY ifTrue:[^nil]. - scroller lastSubmorph bottom < ptY ifTrue:[^nil]. - "now use binary search" - ^scroller - findSubmorphBinary:[:item| - (item top <= ptY and:[item bottom >= ptY]) - ifTrue:[0] "found" - ifFalse:[ (item top + item bottom // 2) > ptY ifTrue:[-1] ifFalse:[1]]]!
Item was changed: ----- Method: PluggableListMorph>>scrollSelectionIntoView (in category 'selection') ----- scrollSelectionIntoView "make sure that the current selection is visible"
| row | + (row := self selectionIndex) = 0 ifFalse: [ + self fullBounds. "Compute layout for correct scrolling." + self scrollToShow: (self listMorph drawBoundsForRow: row)]! - (row := self selectionIndex) = 0 - ifFalse: [self scrollToShow: (self listMorph drawBoundsForRow: row)]!
Item was changed: ----- Method: ScrollPane>>scrollToShow: (in category 'scrolling') ----- scrollToShow: aRectangle + "Change the receiver's scroller's offset to make aRectangle visible, which is determined by the scroller's current bounds on screen. Note that if aRectangle depends on the receiver's layout being already computed, you must call #fullBounds before fetching the info used for aRectangle." + - | newOffset | newOffset := self offsetToShow: aRectangle. self hScrollBar setValue: newOffset x. self vScrollBar setValue: newOffset y.!
Item was added: + ----- Method: SimpleHierarchicalListMorph>>adjustOffset (in category 'layout') ----- + adjustOffset + "Ignored for performance reasons because #submorphBoundsForShrinkWrap can be very expensive."!
Item was changed: + ----- Method: SimpleHierarchicalListMorph>>adjustSubmorphPositions (in category 'layout') ----- - ----- Method: SimpleHierarchicalListMorph>>adjustSubmorphPositions (in category 'private') ----- adjustSubmorphPositions + "Expensive. Do layout for the receiver's scroller's items. Note that we must update vertical coordinate to ensure a mapping from the scroller's vertical offset to its submorphs indexes."
| p | p := 0@0. scroller submorphsDo: [ :each | | h | h := each height. each privateBounds: (p extent: self preferredSubmorphWidth@h). each visible ifTrue: [p := p + (0@h)]]. - scroller layoutChanged. selectedMorphIndex := nil. "to catch all collapse/expand events" + self clearColumnsCache.! - self - clearColumnsCache; - changed; - layoutChanged; - setScrollDeltas. - !
Item was changed: ----- Method: SimpleHierarchicalListMorph>>applyUserInterfaceTheme (in category 'updating') ----- applyUserInterfaceTheme
super applyUserInterfaceTheme. + scroller submorphsDo: [:ea | ea refreshIcon].! - - scroller submorphsDo: [:ea | ea refreshIcon]. - self adjustSubmorphPositions.!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>collapseAll (in category 'events') ----- collapseAll "Do not use #collapse for each child to avoid intermediate layout updates." | orphans | orphans := OrderedCollection new. self roots do: [:ea | ea isExpanded: false. ea firstChild ifNotNil: [:child | child withSiblingsDo: [:node | node recursiveAddTo: orphans]]. ea firstChild: nil]. + self noteRemovalOfAll: orphans.! - self noteRemovalOfAll: orphans. - - self adjustSubmorphPositions.!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>drawHoverOn: (in category 'drawing') ----- drawHoverOn: aCanvas
self hoveredMorph ifNil: [^ self]. PluggableListMorph highlightHoveredRow ifFalse: [^ self]. + self hoveredMorph drawHoverOn: aCanvas.! - - aCanvas - transformBy: scroller transform - clippingTo: scroller innerBounds - during: [:c | self hoveredMorph drawHoverOn: c].!
Item was removed: - ----- Method: SimpleHierarchicalListMorph>>drawLinesOn: (in category 'drawing') ----- - drawLinesOn: aCanvas - - | lColor | - lColor := self lineColor. - aCanvas - transformBy: scroller transform - clippingTo: scroller innerBounds - during:[:clippedCanvas | - scroller submorphsDo: [ :submorph | - (submorph visible and: [(submorph isExpanded - or: [clippedCanvas isVisible: submorph fullBounds] ) - or: [ submorph nextSibling notNil and: [clippedCanvas isVisible: submorph nextSibling fullBounds]]]) - ifTrue: [submorph drawLinesOn: clippedCanvas lineColor: lColor indentThreshold: 0] ] ] - smoothing: scroller smoothing. - !
Item was changed: ----- Method: SimpleHierarchicalListMorph>>drawOn: (in category 'drawing') ----- drawOn: aCanvas super drawOn: aCanvas. + self drawBackgroundOn: aCanvas.! - - self drawBackgroundOn: aCanvas. - - self drawHoverOn: aCanvas. - self drawSelectionOn: aCanvas. - self drawLinesOn: aCanvas.!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>drawSelectionOn: (in category 'drawing') ----- drawSelectionOn: aCanvas
+ self selectedMorph ifNotNil: [:m | m drawSelectionOn: aCanvas].! - self selectedMorph ifNotNil: [:m | - aCanvas - transformBy: scroller transform - clippingTo: scroller innerBounds - during: [:c | m drawSelectionOn: c] ].!
Item was added: + ----- Method: SimpleHierarchicalListMorph>>drawSubmorphsOn: (in category 'drawing') ----- + drawSubmorphsOn: aCanvas + + | drawBlock | + submorphs isEmpty ifTrue: [^self]. + drawBlock := [:canvas | submorphs reverseDo: [:m | + m == scroller + ifFalse: [canvas fullDrawMorph: m] + ifTrue: [self drawVisibleItemsOn: canvas]]]. + self clipSubmorphs + ifTrue: [aCanvas clipBy: self clippingBounds during: drawBlock] + ifFalse: [drawBlock value: aCanvas]!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>expandAll (in category 'events') ----- expandAll
+ selectedMorph ifNotNil: [self expandAll: selectedMorph].! - selectedMorph ifNil: [^ self]. - self expandAll: selectedMorph. - self adjustSubmorphPositions!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>expandAllRoots (in category 'events') ----- expandAllRoots "Expand all, starting with the receiver's roots. Unlike #expandAll, which starts with the current selection." + self roots do: [:ea | self expandAll: ea].! - self roots do: [:ea | self expandAll: ea]. - self adjustSubmorphPositions.!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>expandAllSafely: (in category 'events') ----- expandAllSafely: aMorph
aMorph ifNil: [^ self]. Cursor wait showWhile: [ + [self expandAllSafely: aMorph seen: IdentitySet new] - [self expandAllSafely: aMorph seen: IdentitySet new. self adjustSubmorphPositions] on: TooManyItemsInTree do: [:ex | "Stop operation." - self adjustSubmorphPositions. self inform: 'Too many children in sub-tree. Raise #expandAllLimit preference if needed.' translated. self flag: #todo. "mt: Let the user choose to continue the operation for another n items..."]].!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>expandRoots (in category 'events') ----- expandRoots + + self roots do: [:each | each expand].! - "Expand all the receiver's roots" - self roots - do: [:each | - (each canExpand and: [each isExpanded not]) - ifTrue: [each toggleExpandedState]]. - self adjustSubmorphPositions!
Item was removed: - ----- Method: SimpleHierarchicalListMorph>>extent: (in category 'geometry') ----- - extent: newExtent - bounds extent = newExtent ifTrue: [^ self]. - super extent: newExtent. - self setScrollDeltas !
Item was changed: ----- Method: SimpleHierarchicalListMorph>>initialize (in category 'initialization') ----- initialize + - "initialize the state of the receiver" super initialize. + + scroller + wantsYellowButtonMenu: false; + layoutPolicy: (PluggableLayoutPolicy on: self + layout: #adjustSubmorphPositions + submorphs: #visibleItemsDo:).! - self setProperty: #autoExpand toValue: false. - scroller wantsYellowButtonMenu: false. - self - on: #mouseMove - send: #mouseStillDown:onItem: - to: self!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>insertNewMorphs: (in category 'private') ----- insertNewMorphs: morphList
scroller addAllMorphs: morphList. + self selection: self getCurrentSelectionItem.! - self adjustSubmorphPositions. - self selection: self getCurrentSelectionItem. - self setScrollDeltas. - !
Item was changed: + ----- Method: SimpleHierarchicalListMorph>>itemFromPoint: (in category 'geometry') ----- - ----- Method: SimpleHierarchicalListMorph>>itemFromPoint: (in category 'event handling') ----- itemFromPoint: aPoint + "Return the list element (morph) at the given point or nil if outside. See #adjustSubmorphPositions." + + | items numItems ptY row item | - "Return the list element (morph) at the given point or nil if outside" - | items ptY firstVisibleRow lastVisibleRow row itemTop result | items := scroller submorphs. + (numItems := items size) = 0 ifTrue: [^nil]. - items ifEmpty: [^nil]. - (scroller fullBounds containsPoint: aPoint) ifFalse:[^nil]. + ptY := (items first point: aPoint from: self) y. + row := ((ptY max: 0) // self minItemHeight) + 1. + item := items at: numItems. - firstVisibleRow := items at: (items findFirst: [:m | m visible]). - lastVisibleRow := items at: (items findLast: [:m | m visible]). + "1) Linear search to account for non-uniform item height." + [row < numItems and: [(item := items at: row) bottom <= ptY]] + whileTrue: [row := row + 1]. + "2) Check visibility to support type-in filter." + [row < numItems and: [(item := items at: row) visible not]] + whileTrue: [row := row + 1]. + "3) Upper bound is the last visible item." + row = numItems ifTrue: [ + [(item := items at: row) visible] whileFalse: [row := row - 1]]. + + ^ item! - ptY := (firstVisibleRow point: aPoint from: self) y. - "note: following assumes that submorphs are vertical, non-overlapping, and ordered" - firstVisibleRow top > ptY ifTrue:[^nil]. - lastVisibleRow bottom <= ptY ifTrue:[^lastVisibleRow]. - "now use binary search" - row := items - findBinaryIndex:[:item| - (item top <= ptY and: [item bottom >= ptY]) - ifTrue:[0] "found" - ifFalse:[ (item top + item bottom // 2) > ptY ifTrue:[-1] ifFalse:[1]]] - ifNone: [^nil]. - "ignore invisible rows, find first visible at #top; see #adjustSubmorphPositions." - itemTop := (items at: row) top. row := row - 1. - [row > 1 and: [(items at: row) top = itemTop]] whileTrue: [row := row-1] . - row := row + 1. - [(result := items at: row) visible] whileFalse: [row := row+1]. - ^ result!
Item was added: + ----- Method: SimpleHierarchicalListMorph>>itemsChanged (in category 'layout') ----- + itemsChanged + "The receiver's items (or submorphs) changed due to some node being collapsed or expanded or refreshed. We need to re-layout all items and re-draw the entire field the next time." + + scroller layoutChanged; changed.!
Item was removed: - ----- Method: SimpleHierarchicalListMorph>>listItemHeight (in category 'initialization') ----- - listItemHeight - "This should be cleaned up. The list should get spaced by this parameter." - ^ 12!
Item was added: + ----- Method: SimpleHierarchicalListMorph>>resizeScroller (in category 'layout - resizing') ----- + resizeScroller + "For performance, skip re-layouting all items if layout only changed from the outside, which we know if our scroller still has its fullBounds. This happens, for example, when the receiver is resized via #extent:." + + | doLayout | + doLayout := scroller layoutComputed not. + scroller privateBounds: self layoutBounds. + doLayout + ifTrue: [scroller fullBounds] + ifFalse: [scroller privateFullBounds: scroller bounds].!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>scrollDeltaHeight (in category 'geometry') ----- scrollDeltaHeight + + ^ self minItemHeight! - ^ scroller hasSubmorphs - ifTrue: [scroller firstSubmorph height] - ifFalse: [super scrollDeltaHeight]!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>scrollSelectionAndChildrenIntoView (in category 'selection') ----- scrollSelectionAndChildrenIntoView "Make sure to show the current selection and as many children as possible." self selectedMorph ifNil: [self scrollToTop] ifNotNil: [:m | + m isExpanded ifTrue: [ + self fullBounds. "Compute layout for correct scrolling." + self scrollToShow: m lastVisibleChild bounds]. - m isExpanded ifTrue: [self scrollToShow: m lastVisibleChild bounds]. self scrollSelectionIntoView].!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>scrollSelectionAndExtraIntoView (in category 'selection') ----- scrollSelectionAndExtraIntoView "Make sure to show the current selection and some of the previous and next items." | numExtraItemsToShow | numExtraItemsToShow := 2. self selectedMorph ifNil: [self scrollToTop] ifNotNil: [:m | + self fullBounds. "Compute layout for correct scrolling." self scrollToShow: (m bounds outsetBy: (0@ (m height * numExtraItemsToShow))). self scrollSelectionIntoView].!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>scrollSelectionIntoView (in category 'selection') ----- scrollSelectionIntoView "Ensure that the user can see the current selection. If there is no current selection, keep the view stable." + self selectedMorph ifNotNil: [:m | + self fullBounds. "Compute layout for correct scrolling." + self scrollToShow: m bounds].! - self selectedMorph ifNotNil: [:m | self scrollToShow: m bounds].!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>scrollSelectionParentIntoView: (in category 'selection') ----- scrollSelectionParentIntoView: parentOrNil "Try to make current selection's parent visible. Yet, ensure that the selection stays visible." + parentOrNil ifNotNil: [:pm | + self fullBounds. "Compute layout for correct scrolling." + self scrollToShow: pm bounds]. - parentOrNil ifNotNil: [:pm | self scrollToShow: pm bounds]. self scrollSelectionIntoView.!
Item was changed: ----- Method: SimpleHierarchicalListMorph>>toggleExpandedState: (in category 'keyboard navigation') ----- toggleExpandedState: aMorph aMorph toggleExpandedState. - self adjustSubmorphPositions. !
Item was changed: ----- Method: SimpleHierarchicalListMorph>>toggleExpandedState:event: (in category 'events') ----- toggleExpandedState: aMorph event: aMouseEvent "Mouse navigation. Thus, avoid changing scroll position. See #toggleExpandedState: for keyboard navigation."
autoExpand == true ifTrue: [^ self]. aMorph toggleExpandedState. - self adjustSubmorphPositions. !
Item was added: + ----- Method: TransformMorph>>adoptPaneColor: (in category 'as yet unclassified') ----- + adoptPaneColor: aColor + "Ignore. This is a window-color feature and thus not used for submorphs that are in a scroll container. Note that other layout containers are not effected such as button rows."!
Item was changed: + (PackageInfo named: 'Morphic') postscript: '"Endable layoutPolicy for all tree widgets." + SimpleHierarchicalListMorph allSubInstancesDo: [:treeWidget | + treeWidget scroller + layoutPolicy: (PluggableLayoutPolicy on: treeWidget + layout: #adjustSubmorphPositions + submorphs: #visibleItemsDo:)].'! - (PackageInfo named: 'Morphic') postscript: 'IndentingListItemMorph applyUserInterfaceTheme. "Filter background color"'!
packages@lists.squeakfoundation.org