<div id="__MailbirdStyleContent" style="font-size: 10pt;font-family: Arial;color: #000000">
                                        
                                        
                                            
                                        
                                        
                                        <div><span style="font-size: 10pt">Hi Christoph.</span></div><div><span style="font-size: 10pt"><br></span></div><div><span style="font-size: 10pt">> </span><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">I find it confusing that filters from columns that are not currently selected are not displayed, but still active.</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"><br></span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">Columns have no filter (term). The entire list has the filter. </span><span style="font-size: 13.3333px">Your notion of a column "being active" is interesting. The filter changes only when typing letters. The user can hit tab to either configure that next filter action or look at the matching terms in a specific column. There is no new "being active" mode. It's still just "all items" or "filtered items". It could well be that two different columns would yield the same filter result.</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"><br></span></div>Hmm... I would rather not "over-design" that feature. :-) At the moment, column cycling does not reset the filter and the active column will show the matching items. A simple extension would be to always show the matching terms for all columns. I disabled that to make the active column pop out a little bit more.<div><br></div><div>> <span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">Would it be possible to display such filters as by giving them a grey background color?</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"><br></span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">That would require a new color (or property) to be spread across all existing UI themes. I suppose. While that would be possible, I don't think it would improve things substantially. Just my two cents. :-)</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"><br></span></div><div>> <span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">Wouldn't it be more convenient if you could also use the cursor to focus a column? :)</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"><br></span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">I suppose not. You are talking about actual selection. There is no actual selection of the column. Let's better not mix those things up unless we can really provide column selection for the model. So make it visually and interactively different from actual selection. For now. :-)</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"><br></span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">It's no spreadsheet. I suppose the number of columns is usually small. Cycle-through should work.</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"><br></span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">> </span><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"> </span><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">I would like no column to be selected again.</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"><br></span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">Sounds like a good idea. Would you also reset the current filter if you cycle through columns?</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"><br></span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">> </span><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">Looks great! Some ideas.</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"><br></span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">Keep them coming. :-) Watch out for trade-offs vs. coherent/sound new features. Column filtering is definitely a trade-off not to be pushed too far. Try looking at other widget libraries in other systems to learn about what users expect these days...</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px"><br></span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">Best,</span></div><div><span style="font-family: Calibri, Helvetica, sans-serif;font-size: 16px">Marcel</span></div><div class="mb_sig"></div>
                                        
                                        <blockquote class="history_container" type="cite" style="border-left-style: solid;border-width: 1px;margin-top: 20px;margin-left: 0px;padding-left: 10px;min-width: 500px">
                        <p style="color: #AAAAAA; margin-top: 10px;">Am 02.11.2019 00:43:13 schrieb Thiede, Christoph <christoph.thiede@student.hpi.uni-potsdam.de>:</p><div style="font-family:Arial,Helvetica,sans-serif">
<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"></img><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><span style="font-size: 10pt;color: #808080"></span></div>
</div>
</div>
</div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><span style="font-family: Calibri, sans-serif;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</span>
<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;min-width: 500px">
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>
</div></blockquote></div>