Marcel Taeumel uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-mt.1582.mcz
==================== Summary ====================
Name: Morphic-mt.1582 Author: mt Time: 23 October 2019, 3:31:24.67861 pm UUID: 82af72b9-4198-4bd2-8e7e-e8733d9018c7 Ancestors: Morphic-mt.1581
Adds things for column-specific list filtering:
- backgroundColor for LazyListMorph - optional filter-term indication for LazyListMorph - column highlights for multi-column lists - Character tab to cycle between columns
Note that I opted to not use the [space] key for column toggling because (a) we have to widget-focus cycling via [tab] at the moment and (b) the space character might be a valuable filter term.
=============== Diff against Morphic-mt.1581 ===============
Item was changed: Morph subclass: #LazyListMorph + instanceVariableNames: 'listItems listIcons listFilterOffsets font selectedRow selectedRows preSelectedRow listSource maxWidth columnIndex iconExtent backgroundColor showFilter' - instanceVariableNames: 'listItems listIcons listFilterOffsets font selectedRow selectedRows preSelectedRow listSource maxWidth columnIndex iconExtent' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Widgets'!
!LazyListMorph commentStamp: 'mt 10/13/2019 19:44' prior: 0! The morph that displays the list in a PluggableListMorph. It is "lazy" because it will only request the list items that it actually needs to display.
I will cache the maximum width of my items in maxWidth to avoid this potentially expensive and frequent computation.
The following layout properties are supported: - #cellPositioning: #leftCenter [default], #center, #rightCenter - #cellInset: [default: 3@0 corner: 3@0]!
Item was added: + ----- Method: LazyListMorph>>backgroundColor (in category 'accessing') ----- + backgroundColor + "Since #color is this morph's default text color, this extra property is used for the actual background color. Supports nil." + + ^ backgroundColor!
Item was added: + ----- Method: LazyListMorph>>backgroundColor: (in category 'accessing') ----- + backgroundColor: aColor + + backgroundColor = aColor ifTrue: [^ self]. + backgroundColor := aColor. + + self changed. + "Invalidate owner because we want to fill the vertical axis in the viewport entirely." + self owner ifNotNil: [:o | o changed].!
Item was changed: ----- Method: LazyListMorph>>displayFilterOn:for:in:font: (in category 'drawing') ----- displayFilterOn: canvas for: row in: drawBounds font: font "Draw filter matches if any." | fillStyle fillHeight | + self showFilter ifFalse: [^ self]. - listSource filterableList ifFalse: [^ self]. fillHeight := font height. fillStyle := self filterColor isColor ifTrue: [SolidFillStyle color: self filterColor] ifFalse: [self filterColor]. fillStyle isGradientFill ifTrue: [ fillStyle origin: drawBounds topLeft. fillStyle direction: 0@ fillHeight]. (self filterOffsets: row) do: [:offset | | highlightRectangle | highlightRectangle := ((drawBounds left + offset first first) @ drawBounds top corner: (drawBounds left + offset first last) @ (drawBounds top + fillHeight)). canvas frameAndFillRoundRect: (highlightRectangle outsetBy: 1@0) radius: (3 * RealEstateAgent scaleFactor) truncated fillStyle: fillStyle borderWidth: (1 * RealEstateAgent scaleFactor) truncated borderColor: fillStyle asColor twiceDarker. canvas drawString: offset second in: highlightRectangle font: font color: self filterTextColor].!
Item was changed: ----- Method: LazyListMorph>>drawOn: (in category 'drawing') ----- drawOn: aCanvas
| topRow bottomRow | + self backgroundColor ifNotNil: [:color | + aCanvas fillRectangle: (self topLeft corner: self right @ ((self owner ifNil: [self]) bottom)) color: color]. + self getListSize = 0 ifTrue: [ ^self ]. self drawPreSelectionOn: aCanvas. topRow := self topVisibleRowForCanvas: aCanvas. bottomRow := self bottomVisibleRowForCanvas: aCanvas.
"Draw multi-selection." self listSource hasMultiSelection ifTrue: [ topRow to: bottomRow do: [ :row | (self listSource itemSelectedAmongMultiple: row) ifTrue: [ self drawBackgroundForMulti: row on: aCanvas ] ] ]. self drawSelectionOn: aCanvas.
"Draw hovered row if preference enabled." PluggableListMorph highlightHoveredRow ifTrue: [ self listSource hoverRow > 0 ifTrue: [ self highlightHoverRow: listSource hoverRow on: aCanvas ] ].
"Draw all visible rows." topRow to: bottomRow do: [ :row | self display: (self item: row) atRow: row on: aCanvas ].
"Finally, highlight drop row for drag/drop operations.." self listSource potentialDropRow > 0 ifTrue: [ self highlightPotentialDropRow: self listSource potentialDropRow on: aCanvas ].!
Item was added: + ----- Method: LazyListMorph>>showFilter (in category 'accessing') ----- + showFilter + + ^ (showFilter ~~ false and: [listSource filterableList])!
Item was added: + ----- Method: LazyListMorph>>showFilter: (in category 'accessing') ----- + showFilter: aBoolean + + showFilter = aBoolean ifTrue: [^ self]. + showFilter := aBoolean. + self changed.!
Item was added: + ----- Method: PluggableMultiColumnListMorph>>filterColumnColor (in category 'filtering') ----- + filterColumnColor + + ^ (Color gray: 0.85) alpha: 0.4!
Item was added: + ----- Method: PluggableMultiColumnListMorph>>filterColumnIndex (in category 'filtering') ----- + filterColumnIndex + "Which column to apply the filter to?" + + | i | + i := 0. + self listMorphs + detect: [:m | i := i + 1. m backgroundColor notNil] + ifNone: [i := 0]. + ^ i!
Item was added: + ----- Method: PluggableMultiColumnListMorph>>filterList:columnIndex:matching: (in category 'filtering') ----- + filterList: columns columnIndex: index matching: aPattern + "A matching row has a match in at least one column." + + | frontMatching substringMatching rowCount columnCount tmp | + aPattern ifEmpty: [^ columns]. + columns ifEmpty: [^ columns]. + + rowCount := columns first size. + rowCount = 0 ifTrue: [^ columns]. + columnCount := columns size. + + frontMatching := Array new: columnCount. + 1 to: columnCount do: [:c | frontMatching at: c put: OrderedCollection new]. + substringMatching := Array new: columnCount. + 1 to: columnCount do: [:c | substringMatching at: c put: OrderedCollection new]. + + modelToView := Dictionary new. + viewToModel := Dictionary new. + tmp := OrderedCollection new. + + 1 to: rowCount do: [:rowIndex | + | match foundPos | + match := false. + foundPos := self + filterListItem: ((columns at: index) at: rowIndex) + matching: aPattern. + foundPos = 1 + ifTrue: [ + 1 to: columnCount do: [:colIndex | + (frontMatching at: colIndex) add: ((columns at: colIndex) at: rowIndex)]. + modelToView at: rowIndex put: frontMatching first size. + viewToModel at: frontMatching first size put: rowIndex] + ifFalse: [foundPos > 1 ifTrue: [ + 1 to: columnCount do: [:colIndex | + (substringMatching at: colIndex) add: ((columns at: colIndex) at: rowIndex)]. + tmp add: rowIndex; add: substringMatching first size]] + ]. + + tmp pairsDo: [:modelIndex :viewIndex | + modelToView at: modelIndex put: viewIndex + frontMatching first size. + viewToModel at: viewIndex + frontMatching first size put: modelIndex]. + + ^ (1 to: columnCount) collect: [:colIndex | + (frontMatching at: colIndex), (substringMatching at: colIndex)] + + + + + + + + + + + + + + !
Item was changed: ----- Method: PluggableMultiColumnListMorph>>filterList:matching: (in category 'filtering') ----- filterList: columns matching: aPattern "A matching row has a match in at least one column." | frontMatching substringMatching rowCount columnCount tmp | aPattern ifEmpty: [^ columns]. columns ifEmpty: [^ columns]. + "Enable column-specific filtering." + self filterColumnIndex in: [:index | + index > 0 ifTrue: [^ self filterList: columns columnIndex: index matching: aPattern]]. + rowCount := columns first size. rowCount = 0 ifTrue: [^ columns]. columnCount := columns size. frontMatching := Array new: columnCount. 1 to: columnCount do: [:c | frontMatching at: c put: OrderedCollection new]. substringMatching := Array new: columnCount. 1 to: columnCount do: [:c | substringMatching at: c put: OrderedCollection new]. modelToView := Dictionary new. viewToModel := Dictionary new. tmp := OrderedCollection new. 1 to: rowCount do: [:rowIndex | | match foundPos | match := false. foundPos := 0. 1 to: columnCount do: [:colIndex | match := match or: [(foundPos := (self filterListItem: ((columns at: colIndex) at: rowIndex) matching: aPattern)+colIndex) > colIndex]]. match & (foundPos = 2) "means front match in first column" ifTrue: [ 1 to: columnCount do: [:colIndex | (frontMatching at: colIndex) add: ((columns at: colIndex) at: rowIndex)]. modelToView at: rowIndex put: frontMatching first size. viewToModel at: frontMatching first size put: rowIndex] ifFalse: [match ifTrue: [ 1 to: columnCount do: [:colIndex | (substringMatching at: colIndex) add: ((columns at: colIndex) at: rowIndex)]. tmp add: rowIndex; add: substringMatching first size]] ]. tmp pairsDo: [:modelIndex :viewIndex | modelToView at: modelIndex put: viewIndex + frontMatching first size. viewToModel at: viewIndex + frontMatching first size put: modelIndex].
^ (1 to: columnCount) collect: [:colIndex | (frontMatching at: colIndex), (substringMatching at: colIndex)]
!
Item was added: + ----- Method: PluggableMultiColumnListMorph>>highlightNextColumn (in category 'filtering') ----- + highlightNextColumn + + | i currentColumn nextColumn | + i := self filterColumnIndex. + i = 0 ifTrue: [self listMorphs do: [:m | m showFilter: false]]. + + currentColumn := self listMorphs at: (i max: 1). + nextColumn := self listMorphs at: i \ self listMorphs size + 1. + + currentColumn + showFilter: false; + backgroundColor: nil. + + nextColumn + showFilter: true; + backgroundColor: self filterColumnColor.!
Item was added: + ----- Method: PluggableMultiColumnListMorph>>highlightNoColumn (in category 'filtering') ----- + highlightNoColumn + + self listMorphs do: [:m | + m showFilter: true; backgroundColor: nil].!
Item was added: + ----- Method: PluggableMultiColumnListMorph>>removeFilter (in category 'filtering') ----- + removeFilter + + self highlightNoColumn. + super removeFilter. + !
Item was added: + ----- Method: PluggableMultiColumnListMorph>>specialKeyPressed: (in category 'filtering') ----- + specialKeyPressed: asciiValue + "Use the [Tab] key to filter specific columns." + + ^ asciiValue = Character tab asciiValue + ifTrue: [self highlightNextColumn] + ifFalse: [super specialKeyPressed: asciiValue].!
Item was changed: ----- Method: PluggableMultiColumnListMorph>>updateColumns (in category 'updating') ----- updateColumns "The number of columns must match the number of list morphs." | columnsChanged | columnsChanged := self columnCount ~= listMorphs size. [self columnCount < listMorphs size] whileTrue: [ listMorphs removeLast delete]. [self columnCount > listMorphs size] whileTrue: [ listMorphs addLast: self createListMorph. self scroller addMorphBack: listMorphs last]. listMorphs doWithIndex: [:listMorph :columnIndex | listMorph columnIndex: columnIndex; + color: self textColor; cellPositioning: (self cellPositioningAtColumn: columnIndex); cellInset: (self cellInsetAtColumn: columnIndex); hResizing: (self hResizingAtColumn: columnIndex); spaceFillWeight: (self spaceFillWeightAtColumn: columnIndex)]. columnsChanged ifTrue: [self setListParameters].!
packages@lists.squeakfoundation.org