[Pkg] The Trunk: Morphic-mt.1582.mcz

commits at source.squeak.org commits at source.squeak.org
Wed Oct 23 13:31:33 UTC 2019


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 at 0 corner: 3 at 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 at 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].!



More information about the Packages mailing list