<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
</head>
<body>
<style type="text/css" style="display:none;"><!-- P {margin-top:0;margin-bottom:0;} --></style>
<div id="divtagdefaultwrapper" style="font-size:12pt;color:#000000;font-family:Calibri,Helvetica,sans-serif;" dir="ltr">
<p>Looks great! Some ideas:</p>
<p><br>
</p>
<p>- I find it confusing that filters from columns that are not currently selected are not displayed, but still active. In this example, I filtered the left column for "tex" and then selected the right one. Now there is no visual indication of the left filter:</p>
<p><br>
</p>
<p><img size="136784" contenttype="image/png" id="img339071" style="max-width: 99.9%; user-select: none;" contextid="img60989" tabindex="0" src="cid:17e775c3-8a76-46c9-9596-4e080bfb8df5"><br>
</p>
<p><br>
</p>
<p>Would it be possible to display such filters as by giving them a grey background color?</p>
<p><br>
</p>
<p>- If I pressed Tab again on the screenshot above, I would like no column to be selected again. Why not implement tab cycling along the series "all columns, col1, col2, ..., coln, all columns"?</p>
<p>- Wouldn't it be more convenient if you could also use the cursor to focus a column? :)</p>
<p><br>
</p>
<p>Best,</p>
<p>Christoph</p>
<div id="Signature">
<div name="divtagdefaultwrapper" style="font-family:Calibri,Arial,Helvetica,sans-serif; font-size:; margin:0">
<div><font size="2" color="#808080"></font></div>
</div>
</div>
</div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>Von:</b> Squeak-dev <squeak-dev-bounces@lists.squeakfoundation.org> im Auftrag von Chris Muller <asqueaker@gmail.com><br>
<b>Gesendet:</b> Freitag, 25. Oktober 2019 01:36:31<br>
<b>An:</b> squeak dev<br>
<b>Cc:</b> packages@lists.squeakfoundation.org<br>
<b>Betreff:</b> Re: [squeak-dev] The Trunk: Morphic-mt.1582.mcz</font>
<div> </div>
</div>
<div>
<div dir="ltr">Very nice, looking forward to trying it out.
<div><br>
</div>
<div>Thanks!</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Wed, Oct 23, 2019 at 8:31 AM <<a href="mailto:commits@source.squeak.org" target="_blank">commits@source.squeak.org</a>> wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Marcel Taeumel uploaded a new version of Morphic to project The Trunk:<br>
<a href="http://source.squeak.org/trunk/Morphic-mt.1582.mcz" rel="noreferrer" target="_blank">http://source.squeak.org/trunk/Morphic-mt.1582.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: Morphic-mt.1582<br>
Author: mt<br>
Time: 23 October 2019, 3:31:24.67861 pm<br>
UUID: 82af72b9-4198-4bd2-8e7e-e8733d9018c7<br>
Ancestors: Morphic-mt.1581<br>
<br>
Adds things for column-specific list filtering:<br>
<br>
- backgroundColor for LazyListMorph<br>
- optional filter-term indication for LazyListMorph<br>
- column highlights for multi-column lists<br>
- Character tab to cycle between columns<br>
<br>
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.<br>
<br>
=============== Diff against Morphic-mt.1581 ===============<br>
<br>
Item was changed:<br>
  Morph subclass: #LazyListMorph<br>
+       instanceVariableNames: 'listItems listIcons listFilterOffsets font selectedRow selectedRows preSelectedRow listSource maxWidth columnIndex iconExtent backgroundColor showFilter'<br>
-       instanceVariableNames: 'listItems listIcons listFilterOffsets font selectedRow selectedRows preSelectedRow listSource maxWidth columnIndex iconExtent'<br>
        classVariableNames: ''<br>
        poolDictionaries: ''<br>
        category: 'Morphic-Widgets'!<br>
<br>
  !LazyListMorph commentStamp: 'mt 10/13/2019 19:44' prior: 0!<br>
  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.<br>
<br>
  I will cache the maximum width of my items in maxWidth to avoid this potentially expensive and frequent computation.<br>
<br>
  The following layout properties are supported:<br>
  - #cellPositioning: #leftCenter [default], #center, #rightCenter<br>
  - #cellInset: [default: 3@0 corner: 3@0]!<br>
<br>
Item was added:<br>
+ ----- Method: LazyListMorph>>backgroundColor (in category 'accessing') -----<br>
+ backgroundColor<br>
+       "Since #color is this morph's default text color, this extra property is used for the actual background color. Supports nil."<br>
+       <br>
+       ^ backgroundColor!<br>
<br>
Item was added:<br>
+ ----- Method: LazyListMorph>>backgroundColor: (in category 'accessing') -----<br>
+ backgroundColor: aColor<br>
+ <br>
+       backgroundColor = aColor ifTrue: [^ self].<br>
+       backgroundColor := aColor.<br>
+ <br>
+       self changed.<br>
+       "Invalidate owner because we want to fill the vertical axis in the viewport entirely."<br>
+       self owner ifNotNil: [:o | o changed].!<br>
<br>
Item was changed:<br>
  ----- Method: LazyListMorph>>displayFilterOn:for:in:font: (in category 'drawing') -----<br>
  displayFilterOn: canvas for: row in: drawBounds font: font<br>
        "Draw filter matches if any."<br>
<br>
        | fillStyle fillHeight |<br>
+       self showFilter ifFalse: [^ self].<br>
-       listSource filterableList ifFalse: [^ self].<br>
<br>
        fillHeight := font height.<br>
        fillStyle := self filterColor isColor<br>
                ifTrue: [SolidFillStyle color: self filterColor]<br>
                ifFalse: [self filterColor].<br>
        fillStyle isGradientFill ifTrue: [<br>
                fillStyle origin: drawBounds topLeft.<br>
                fillStyle direction: 0@ fillHeight].<br>
<br>
        (self filterOffsets: row) do: [:offset |<br>
                | highlightRectangle |<br>
                highlightRectangle := ((drawBounds left + offset first first) @ drawBounds top<br>
                        corner: (drawBounds left + offset first last) @ (drawBounds top + fillHeight)).<br>
                canvas<br>
                        frameAndFillRoundRect: (highlightRectangle outsetBy: 1@0)<br>
                        radius: (3 * RealEstateAgent scaleFactor) truncated<br>
                        fillStyle: fillStyle<br>
                        borderWidth: (1 * RealEstateAgent scaleFactor) truncated<br>
                        borderColor: fillStyle asColor twiceDarker.<br>
                canvas<br>
                        drawString: offset second<br>
                        in: highlightRectangle<br>
                        font: font<br>
                        color: self filterTextColor].!<br>
<br>
Item was changed:<br>
  ----- Method: LazyListMorph>>drawOn: (in category 'drawing') -----<br>
  drawOn: aCanvas<br>
<br>
        | topRow bottomRow |<br>
+       self backgroundColor ifNotNil: [:color |<br>
+               aCanvas fillRectangle: (self topLeft corner: self right @ ((self owner ifNil: [self]) bottom)) color: color].<br>
+ <br>
        self getListSize = 0 ifTrue: [ ^self ].<br>
<br>
        self drawPreSelectionOn: aCanvas.<br>
<br>
        topRow := self topVisibleRowForCanvas: aCanvas.<br>
        bottomRow := self bottomVisibleRowForCanvas: aCanvas.<br>
<br>
        "Draw multi-selection."<br>
        self listSource hasMultiSelection ifTrue: [<br>
                topRow to: bottomRow do: [ :row |<br>
                        (self listSource itemSelectedAmongMultiple: row) ifTrue: [<br>
                                self drawBackgroundForMulti: row on: aCanvas ] ] ].<br>
        self drawSelectionOn: aCanvas.<br>
<br>
        "Draw hovered row if preference enabled."<br>
        PluggableListMorph highlightHoveredRow ifTrue: [<br>
                self listSource hoverRow > 0 ifTrue: [<br>
                        self highlightHoverRow: listSource hoverRow on: aCanvas ] ].<br>
<br>
        "Draw all visible rows."<br>
        topRow to: bottomRow do: [ :row |<br>
                self display: (self item: row) atRow: row on: aCanvas ].<br>
<br>
        "Finally, highlight drop row for drag/drop operations.."<br>
        self listSource potentialDropRow > 0 ifTrue: [<br>
                self highlightPotentialDropRow: self listSource potentialDropRow on: aCanvas ].!<br>
<br>
Item was added:<br>
+ ----- Method: LazyListMorph>>showFilter (in category 'accessing') -----<br>
+ showFilter<br>
+ <br>
+       ^ (showFilter ~~ false and: [listSource filterableList])!<br>
<br>
Item was added:<br>
+ ----- Method: LazyListMorph>>showFilter: (in category 'accessing') -----<br>
+ showFilter: aBoolean<br>
+ <br>
+       showFilter = aBoolean ifTrue: [^ self].<br>
+       showFilter := aBoolean.<br>
+       self changed.!<br>
<br>
Item was added:<br>
+ ----- Method: PluggableMultiColumnListMorph>>filterColumnColor (in category 'filtering') -----<br>
+ filterColumnColor<br>
+ <br>
+       ^ (Color gray: 0.85) alpha: 0.4!<br>
<br>
Item was added:<br>
+ ----- Method: PluggableMultiColumnListMorph>>filterColumnIndex (in category 'filtering') -----<br>
+ filterColumnIndex<br>
+       "Which column to apply the filter to?"<br>
+ <br>
+       | i |<br>
+       i := 0.<br>
+       self listMorphs<br>
+               detect: [:m | i := i + 1. m backgroundColor notNil]<br>
+               ifNone: [i := 0].<br>
+       ^ i!<br>
<br>
Item was added:<br>
+ ----- Method: PluggableMultiColumnListMorph>>filterList:columnIndex:matching: (in category 'filtering') -----<br>
+ filterList: columns columnIndex: index matching: aPattern<br>
+       "A matching row has a match in at least one column."<br>
+       <br>
+       | frontMatching substringMatching rowCount columnCount tmp |<br>
+       aPattern ifEmpty: [^ columns].<br>
+       columns ifEmpty: [^ columns].<br>
+       <br>
+       rowCount := columns first size.<br>
+       rowCount = 0 ifTrue: [^ columns].<br>
+       columnCount := columns size.<br>
+       <br>
+       frontMatching := Array new: columnCount.<br>
+       1 to: columnCount do: [:c | frontMatching at: c put: OrderedCollection new].<br>
+       substringMatching := Array new: columnCount.<br>
+       1 to: columnCount do: [:c | substringMatching at: c put: OrderedCollection new].<br>
+       <br>
+       modelToView := Dictionary new.<br>
+       viewToModel := Dictionary new.<br>
+       tmp := OrderedCollection new.<br>
+       <br>
+       1 to: rowCount do: [:rowIndex |<br>
+               | match foundPos |<br>
+               match := false.<br>
+               foundPos := self<br>
+                                               filterListItem: ((columns at: index) at: rowIndex)<br>
+                                               matching: aPattern.<br>
+               foundPos = 1<br>
+                       ifTrue: [<br>
+                               1 to: columnCount do: [:colIndex |<br>
+                                       (frontMatching at: colIndex) add: ((columns at: colIndex) at: rowIndex)].<br>
+                               modelToView at: rowIndex put: frontMatching first size.<br>
+                               viewToModel at: frontMatching first size put: rowIndex]<br>
+                       ifFalse: [foundPos > 1 ifTrue: [<br>
+                               1 to: columnCount do: [:colIndex |<br>
+                                       (substringMatching at: colIndex) add: ((columns at: colIndex) at: rowIndex)].<br>
+                               tmp add: rowIndex; add: substringMatching first size]]<br>
+       ].<br>
+       <br>
+       tmp pairsDo: [:modelIndex :viewIndex |<br>
+               modelToView at: modelIndex put: viewIndex + frontMatching first size.<br>
+               viewToModel at: viewIndex + frontMatching first size put: modelIndex].<br>
+ <br>
+       ^ (1 to: columnCount) collect: [:colIndex |<br>
+               (frontMatching at: colIndex), (substringMatching at: colIndex)]<br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ <br>
+ !<br>
<br>
Item was changed:<br>
  ----- Method: PluggableMultiColumnListMorph>>filterList:matching: (in category 'filtering') -----<br>
  filterList: columns matching: aPattern<br>
        "A matching row has a match in at least one column."<br>
<br>
        | frontMatching substringMatching rowCount columnCount tmp |<br>
        aPattern ifEmpty: [^ columns].<br>
        columns ifEmpty: [^ columns].<br>
<br>
+       "Enable column-specific filtering."<br>
+       self filterColumnIndex in: [:index |<br>
+               index > 0 ifTrue: [^ self filterList: columns columnIndex: index matching: aPattern]].<br>
+       <br>
        rowCount := columns first size.<br>
        rowCount = 0 ifTrue: [^ columns].<br>
        columnCount := columns size.<br>
<br>
        frontMatching := Array new: columnCount.<br>
        1 to: columnCount do: [:c | frontMatching at: c put: OrderedCollection new].<br>
        substringMatching := Array new: columnCount.<br>
        1 to: columnCount do: [:c | substringMatching at: c put: OrderedCollection new].<br>
<br>
        modelToView := Dictionary new.<br>
        viewToModel := Dictionary new.<br>
        tmp := OrderedCollection new.<br>
<br>
        1 to: rowCount do: [:rowIndex |<br>
                | match foundPos |<br>
                match := false.<br>
                foundPos := 0.<br>
                1 to: columnCount do: [:colIndex |<br>
                        match := match or: [(foundPos := (self<br>
                                                                        filterListItem: ((columns at: colIndex) at: rowIndex)<br>
                                                                        matching: aPattern)+colIndex) > colIndex]].<br>
                match & (foundPos = 2) "means front match in first column"<br>
                        ifTrue: [<br>
                                1 to: columnCount do: [:colIndex |<br>
                                        (frontMatching at: colIndex) add: ((columns at: colIndex) at: rowIndex)].<br>
                                modelToView at: rowIndex put: frontMatching first size.<br>
                                viewToModel at: frontMatching first size put: rowIndex]<br>
                        ifFalse: [match ifTrue: [<br>
                                1 to: columnCount do: [:colIndex |<br>
                                        (substringMatching at: colIndex) add: ((columns at: colIndex) at: rowIndex)].<br>
                                tmp add: rowIndex; add: substringMatching first size]]<br>
        ].<br>
<br>
        tmp pairsDo: [:modelIndex :viewIndex |<br>
                modelToView at: modelIndex put: viewIndex + frontMatching first size.<br>
                viewToModel at: viewIndex + frontMatching first size put: modelIndex].<br>
<br>
        ^ (1 to: columnCount) collect: [:colIndex |<br>
                (frontMatching at: colIndex), (substringMatching at: colIndex)]<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
  !<br>
<br>
Item was added:<br>
+ ----- Method: PluggableMultiColumnListMorph>>highlightNextColumn (in category 'filtering') -----<br>
+ highlightNextColumn<br>
+ <br>
+       | i currentColumn nextColumn |<br>
+       i := self filterColumnIndex.<br>
+       i = 0 ifTrue: [self listMorphs do: [:m | m showFilter: false]].<br>
+ <br>
+       currentColumn := self listMorphs at: (i max: 1).<br>
+       nextColumn := self listMorphs at: i \\ self listMorphs size + 1.<br>
+       <br>
+       currentColumn<br>
+               showFilter: false;<br>
+               backgroundColor: nil.<br>
+               <br>
+       nextColumn<br>
+               showFilter: true;<br>
+               backgroundColor: self filterColumnColor.!<br>
<br>
Item was added:<br>
+ ----- Method: PluggableMultiColumnListMorph>>highlightNoColumn (in category 'filtering') -----<br>
+ highlightNoColumn<br>
+ <br>
+       self listMorphs do: [:m |<br>
+               m showFilter: true; backgroundColor: nil].!<br>
<br>
Item was added:<br>
+ ----- Method: PluggableMultiColumnListMorph>>removeFilter (in category 'filtering') -----<br>
+ removeFilter<br>
+ <br>
+       self highlightNoColumn.<br>
+       super removeFilter.<br>
+ !<br>
<br>
Item was added:<br>
+ ----- Method: PluggableMultiColumnListMorph>>specialKeyPressed: (in category 'filtering') -----<br>
+ specialKeyPressed: asciiValue<br>
+       "Use the [Tab] key to filter specific columns."<br>
+       <br>
+       ^ asciiValue = Character tab asciiValue<br>
+               ifTrue: [self highlightNextColumn]<br>
+               ifFalse: [super specialKeyPressed: asciiValue].!<br>
<br>
Item was changed:<br>
  ----- Method: PluggableMultiColumnListMorph>>updateColumns (in category 'updating') -----<br>
  updateColumns<br>
        "The number of columns must match the number of list morphs."<br>
<br>
        | columnsChanged |<br>
        columnsChanged := self columnCount ~= listMorphs size.<br>
<br>
        [self columnCount < listMorphs size]<br>
                whileTrue: [<br>
                        listMorphs removeLast delete].<br>
<br>
        [self columnCount > listMorphs size]<br>
                whileTrue: [<br>
                        listMorphs addLast: self createListMorph.<br>
                        self scroller addMorphBack: listMorphs last].<br>
<br>
        listMorphs doWithIndex: [:listMorph :columnIndex |<br>
                listMorph<br>
                        columnIndex: columnIndex;<br>
+                       color: self textColor;<br>
                        cellPositioning: (self cellPositioningAtColumn: columnIndex);<br>
                        cellInset: (self cellInsetAtColumn: columnIndex);<br>
                        hResizing: (self hResizingAtColumn: columnIndex);<br>
                        spaceFillWeight: (self spaceFillWeightAtColumn: columnIndex)].<br>
<br>
        columnsChanged ifTrue: [self setListParameters].!<br>
<br>
<br>
</blockquote>
</div>
</div>
</body>
</html>