Rocksolid Light

Welcome to novaBBS (click a section below)

mail  files  register  nodelist  faq  login

It's ten o'clock; do you know where your processes are?


programming / comp.lang.smalltalk.dolphin / Re: List of items with variable height

SubjectAuthor
* List of items with variable heightJoe Betz
`* Re: List of items with variable heightjohn.a...@gmail.com
 `* Re: List of items with variable heightdanie...@gmail.com
  `* Re: List of items with variable heightJoe Betz
   `* Re: List of items with variable heightJoe Betz
    `* Re: List of items with variable heightdanie...@gmail.com
     `* Re: List of items with variable heightJoe Betz
      +* Re: List of items with variable heightJoe Betz
      |`- Re: List of items with variable heightJoe Betz
      `* Re: List of items with variable heightdanie...@gmail.com
       `* Re: List of items with variable heightJoe Betz
        `* Re: List of items with variable heightdanie...@gmail.com
         `- Re: List of items with variable heightJoe Betz

1
Subject: List of items with variable height
From: Joe Betz
Newsgroups: comp.lang.smalltalk.dolphin
Date: Fri, 29 Oct 2021 21:04 UTC
X-Received: by 2002:ad4:4452:: with SMTP id l18mr11937006qvt.8.1635541452552;
Fri, 29 Oct 2021 14:04:12 -0700 (PDT)
X-Received: by 2002:aca:d989:: with SMTP id q131mr9675990oig.167.1635541452267;
Fri, 29 Oct 2021 14:04:12 -0700 (PDT)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Fri, 29 Oct 2021 14:04:12 -0700 (PDT)
Injection-Info: google-groups.googlegroups.com; posting-host=2001:861:51c3:bae0:28cb:94a0:4638:4277;
posting-account=P1X3VgoAAADgSty0b3mKYJl_KPjmW842
NNTP-Posting-Host: 2001:861:51c3:bae0:28cb:94a0:4638:4277
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
Subject: List of items with variable height
From: joebet...@gmail.com (Joe Betz)
Injection-Date: Fri, 29 Oct 2021 21:04:12 +0000
Content-Type: text/plain; charset="UTF-8"
Lines: 6
View all headers
Using Dolphin's UI framework, what is the correct way to generate a list of items of variable height with a vertical layout?

For example, a list of messages in a chat room. I tried ListView but that didn't work since all rows have to have the same height and will need to do a lot more customization besides. Also tried drawing each message on a canvas but that was too painful.

Is there an abstraction between these two that could solve the problem?

- Joe


Subject: Re: List of items with variable height
From: john.a...@gmail.com
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sat, 30 Oct 2021 10:22 UTC
References: 1
X-Received: by 2002:ad4:5dce:: with SMTP id m14mr16009279qvh.29.1635589357910;
Sat, 30 Oct 2021 03:22:37 -0700 (PDT)
X-Received: by 2002:a9d:4c8d:: with SMTP id m13mr12639458otf.370.1635589357616;
Sat, 30 Oct 2021 03:22:37 -0700 (PDT)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sat, 30 Oct 2021 03:22:37 -0700 (PDT)
In-Reply-To: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=85.203.71.185; posting-account=OQ6sIwoAAAC1iWrFEUhdmRsgEkeDOgOm
NNTP-Posting-Host: 85.203.71.185
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com>
Subject: Re: List of items with variable height
From: john.asp...@gmail.com (john.a...@gmail.com)
Injection-Date: Sat, 30 Oct 2021 10:22:37 +0000
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Lines: 62
View all headers
Hi Joe - not something I've had to do myself but here's a few ideas:

1) Use a ListBox created with the LBS_OWNERDRAWVARIABLE style and override wmMeasureItem:wParam:lParam in ListBox to set the size of each item. You'll also need to draw the items yourself. See https://docs.microsoft.com/en-us/windows/win32/controls/wm-measureitem

2) Use a ContainerView with a FlowLayout layout manager and add each item as a subview. Example (modified from FramingLayout class>>example1):

(shell := View desktop addSubView: ShellView new)
backcolor: Color face3d;
layoutManager: (layout := FlowLayout new);
extent: 300 @ 200.
shell insets: (10 @ 10 corner: 10 @ 10).
layout
verticalGap: 10;
horizontalGap: SmallInteger maximum. "Force one item per row"
label := shell addSubView: StaticText new.
button := shell addSubView: PushButton new.
text1 := shell addSubView: MultilineTextEdit new.
text2 := shell addSubView: MultilineTextEdit new.
label text: 'Label'.
label rectangle: (10 @ 10 extent: 100 @ 20).
button
text: 'Button';
command: #yourself.
text1
text: 'Large multiline text';
wordWrap: true.
layout resize: text1 to: (10 @ 40 corner: 130 @ 190).
text2
text: 'Small multiline text';
wordWrap: true.
shell show

You'd lose the selection behavior compared to the ListBox approach but you could create a custom view for each item which provides any required functionality (e.g. reply to message).

3) Create a complete custom view to display the entire list and manage the required behavior. Probably the most work but also the most flexible. See the Dolphin MoenTree View package for an example of a custom view of a selectable collection of objects.

Hope this helps.

John Aspinall


On Friday, October 29, 2021 at 10:04:13 PM UTC+1, joeb...@gmail.com wrote:
Using Dolphin's UI framework, what is the correct way to generate a list of items of variable height with a vertical layout?

For example, a list of messages in a chat room. I tried ListView but that didn't work since all rows have to have the same height and will need to do a lot more customization besides. Also tried drawing each message on a canvas but that was too painful.

Is there an abstraction between these two that could solve the problem?

- Joe


Subject: Re: List of items with variable height
From: danie...@gmail.com
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sat, 30 Oct 2021 21:19 UTC
References: 1 2
X-Received: by 2002:a05:6214:19e9:: with SMTP id q9mr9270658qvc.52.1635628761644;
Sat, 30 Oct 2021 14:19:21 -0700 (PDT)
X-Received: by 2002:a9d:4c8d:: with SMTP id m13mr14816635otf.370.1635628761375;
Sat, 30 Oct 2021 14:19:21 -0700 (PDT)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sat, 30 Oct 2021 14:19:21 -0700 (PDT)
In-Reply-To: <bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2600:1700:37a8:52b0:7433:eae9:9ca:8bb1;
posting-account=T9QyoQoAAADOKeQdWgBWNEPoknBuEcQZ
NNTP-Posting-Host: 2600:1700:37a8:52b0:7433:eae9:9ca:8bb1
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com> <bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
Subject: Re: List of items with variable height
From: daniels...@gmail.com (danie...@gmail.com)
Injection-Date: Sat, 30 Oct 2021 21:19:21 +0000
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Lines: 10
View all headers
Expanding on John Aspinall's post—I've used the ContainerView-with-FlowLayout approach successfully myself. It helps to make a Presenter that displays a single row, with a view resource for the components needed to do so. Then a Presenter for the list that can use `<ItemPresenter> createIn: self` to add rows, `#model:` to update them, and `#remove:` to remove them. If the list is going to be longer than 3-5 items, avoid recreating everything every time the model changes—only create or remove as many sub-presenters as necessary to match the number of items in the model. It also helps to wrap that whole step in a #noRedrawDo: so you only repaint once the sub-presenters are back in sync with the model. You can of course wrap the whole thing in a ScrollingDecorator to get scrolling behavior.


Subject: Re: List of items with variable height
From: Joe Betz
Newsgroups: comp.lang.smalltalk.dolphin
Date: Mon, 1 Nov 2021 04:38 UTC
References: 1 2 3
X-Received: by 2002:ac8:570c:: with SMTP id 12mr27961702qtw.138.1635741535125;
Sun, 31 Oct 2021 21:38:55 -0700 (PDT)
X-Received: by 2002:a05:6830:10a:: with SMTP id i10mr4833794otp.67.1635741534863;
Sun, 31 Oct 2021 21:38:54 -0700 (PDT)
Path: i2pn2.org!i2pn.org!weretis.net!feeder8.news.weretis.net!news.mixmin.net!proxad.net!feeder1-2.proxad.net!209.85.160.216.MISMATCH!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sun, 31 Oct 2021 21:38:54 -0700 (PDT)
In-Reply-To: <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2001:861:51c3:bae0:bc1c:5076:1555:ccb2;
posting-account=P1X3VgoAAADgSty0b3mKYJl_KPjmW842
NNTP-Posting-Host: 2001:861:51c3:bae0:bc1c:5076:1555:ccb2
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
<bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com> <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <f6b52b27-7d4c-4afc-ac11-ba86f575483an@googlegroups.com>
Subject: Re: List of items with variable height
From: joebet...@gmail.com (Joe Betz)
Injection-Date: Mon, 01 Nov 2021 04:38:55 +0000
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
View all headers
Thanks! I went with John's second idea + Daniel's advice. Exactly what I needed.

The trickiest part was figuring out how to resize the MessagePresenter's based on the size of the message content. Ended up implementing `StaticText>>resizeToFitContent` with Blair's solution in https://groups.google.com/g/comp.lang.smalltalk.dolphin/c/uPXvG-V2DGo/m/G1uVuasFCAIJ

StaticText>>resizeToFitContent
| contentSize |
contentSize := self canvas
font: self actualFont;
textExtent: self value
width: self width
alignment: self alignment.
self extent: (self calcExtentFromClientExtent: contentSize + 2)


Subject: Re: List of items with variable height
From: Joe Betz
Newsgroups: comp.lang.smalltalk.dolphin
Date: Tue, 2 Nov 2021 04:55 UTC
References: 1 2 3 4
X-Received: by 2002:ac8:1e95:: with SMTP id c21mr36221123qtm.412.1635828911235;
Mon, 01 Nov 2021 21:55:11 -0700 (PDT)
X-Received: by 2002:aca:d989:: with SMTP id q131mr2929011oig.167.1635828910906;
Mon, 01 Nov 2021 21:55:10 -0700 (PDT)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Mon, 1 Nov 2021 21:55:10 -0700 (PDT)
In-Reply-To: <f6b52b27-7d4c-4afc-ac11-ba86f575483an@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2001:861:51c3:bae0:d58e:2468:23e5:7805;
posting-account=P1X3VgoAAADgSty0b3mKYJl_KPjmW842
NNTP-Posting-Host: 2001:861:51c3:bae0:d58e:2468:23e5:7805
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
<bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com> <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
<f6b52b27-7d4c-4afc-ac11-ba86f575483an@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <aa8e9ded-56a8-4f22-997b-3d7196f5d481n@googlegroups.com>
Subject: Re: List of items with variable height
From: joebet...@gmail.com (Joe Betz)
Injection-Date: Tue, 02 Nov 2021 04:55:11 +0000
Content-Type: text/plain; charset="UTF-8"
Lines: 5
View all headers

Then a Presenter for the list that can use `<ItemPresenter> createIn: self` to add rows, `#model:` to update them, and `#remove:` to remove them.

When the View is being generated for the first time, where should the `<ItemPresenter> create: self` messages be called to create presenters for the initial list of items?

Naively, I put it all in `model:`, but that broke when it was initialized with a nonempty list, presumably because the view for the container presenter is still a DeafObject at that point.


Subject: Re: List of items with variable height
From: danie...@gmail.com
Newsgroups: comp.lang.smalltalk.dolphin
Date: Fri, 5 Nov 2021 15:29 UTC
References: 1 2 3 4 5
X-Received: by 2002:a05:6214:c6f:: with SMTP id t15mr10313610qvj.49.1636126173768;
Fri, 05 Nov 2021 08:29:33 -0700 (PDT)
X-Received: by 2002:a05:6808:11c8:: with SMTP id p8mr22589046oiv.72.1636126173456;
Fri, 05 Nov 2021 08:29:33 -0700 (PDT)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Fri, 5 Nov 2021 08:29:33 -0700 (PDT)
In-Reply-To: <aa8e9ded-56a8-4f22-997b-3d7196f5d481n@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2600:1700:37a8:52b0:6972:7418:32f2:3641;
posting-account=T9QyoQoAAADOKeQdWgBWNEPoknBuEcQZ
NNTP-Posting-Host: 2600:1700:37a8:52b0:6972:7418:32f2:3641
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
<bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com> <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
<f6b52b27-7d4c-4afc-ac11-ba86f575483an@googlegroups.com> <aa8e9ded-56a8-4f22-997b-3d7196f5d481n@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <4da5ec27-c390-4893-a318-8856264f017dn@googlegroups.com>
Subject: Re: List of items with variable height
From: daniels...@gmail.com (danie...@gmail.com)
Injection-Date: Fri, 05 Nov 2021 15:29:33 +0000
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Lines: 8
View all headers
Naively, I put it all in `model:`, but that broke when it was initialized with a nonempty list, presumably because the view for the container presenter is still a DeafObject at that point.

What I did is have a method (e.g. `#updateListPresenters`) that brings the model and sub-presenters/views in sync, but bails if either the model or the view is missing (nil/deaf). Then I called it from both `#onViewAvailable` and `#model:`. That way it doesn't matter what order things happen in, we'll get everything in sync as soon as we have everything we need.


Subject: Re: List of items with variable height
From: Joe Betz
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sun, 7 Nov 2021 10:52 UTC
References: 1 2 3 4 5 6
X-Received: by 2002:a0c:ebca:: with SMTP id k10mr25262756qvq.51.1636282374769;
Sun, 07 Nov 2021 02:52:54 -0800 (PST)
X-Received: by 2002:a05:6820:28d:: with SMTP id q13mr16872116ood.59.1636282374454;
Sun, 07 Nov 2021 02:52:54 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sun, 7 Nov 2021 02:52:54 -0800 (PST)
In-Reply-To: <4da5ec27-c390-4893-a318-8856264f017dn@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2001:861:51c3:bae0:94cd:6a12:22d3:77f;
posting-account=P1X3VgoAAADgSty0b3mKYJl_KPjmW842
NNTP-Posting-Host: 2001:861:51c3:bae0:94cd:6a12:22d3:77f
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
<bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com> <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
<f6b52b27-7d4c-4afc-ac11-ba86f575483an@googlegroups.com> <aa8e9ded-56a8-4f22-997b-3d7196f5d481n@googlegroups.com>
<4da5ec27-c390-4893-a318-8856264f017dn@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <2a82301b-f4c3-41a1-88e4-97f6a0d0fda3n@googlegroups.com>
Subject: Re: List of items with variable height
From: joebet...@gmail.com (Joe Betz)
Injection-Date: Sun, 07 Nov 2021 10:52:54 +0000
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Lines: 58
View all headers
Okay, at this point I've tried both the ContainerView with FlowLayout and ListBox with custom draw method and neither worked out. In short, the former ended up being too slow and the former just ends up being all sorts of wrong on an implementation level.

2) Use a ContainerView with a FlowLayout layout manager and add each item as a subview. Example (modified from FramingLayout class>>example1):
ou'd lose the selection behavior compared to the ListBox approach but you could create a custom view for each item which provides any required functionality (e.g. reply to message).

The problem here was the messages took way too long to draw. For 50 messages it took 500ms rather than 0 or 1ms when I just shoved them into a ListView. My intuition was that it was an issue with the FlowLayout having to recompute each time a new message was added, but even after removing the layout it was still orders of magnitude too slow. The only thing that made any impact on the draw time was removing as many MVP triads as possible and render the entire message with a single TextPresenter. That got it down to 150ms, but that still isn't nearly good enough.

Running the profiler on the loop that calls `MessagePresenter createIn: self` for each message was interesting but not very enlightening. If interpreted it correctly, most of the time seemed to be spent in InputState, probably waiting for responses from Windows APIs. Another hotspot seemed to be code for replacing view proxies with actual views.

In the end, my conclusion is that there is significant performance overhead in using the MVP triad and that it doesn't scale well for non-standard controls.

1) Use a ListBox created with the LBS_OWNERDRAWVARIABLE style and override wmMeasureItem:wParam:lParam in ListBox to set the size of each item. You'll also need to draw the items yourself. See https://docs.microsoft.com/en-us/windows/win32/controls/wm-measureitem

It turns out that the draw and measure events don't get sent to the ListBox but rather their "owner", which ends up being whatever the parent view of ListBox is. E.g., if it's a ContainerView, then it needs to be subclassed and the wmDrawItem: and wmMeasureItem: methods need to be overriden in order to receive draw and measure events for the ListBox. Really annoying.

This is feasible for wmDrawItem: because the DRAWEVENTSTRUCT contains a reference to the ListBox view which you can then delegate the message to. It doesn't work so well with wmMeasureItem: since it contains no such reference, meaning that the ContainerView needed to hold either a reference to the ListBox or its model in order to even know what its measuring.

All in all it sortof works but seems to break the standard way of organizing views and would require a significant refactor to work in a sane way.

3) Create a complete custom view to display the entire list and manage the required behavior. Probably the most work but also the most flexible. See the Dolphin MoenTree View package for an example of a custom view of a selectable collection of objects.

So yeah, I'm left with this, which I strongly suspected was going to be the case from the beginning. The fact that the class hierarchy tree seems to be able to draw hundreds of objects nearly instantly is promising and what I'm aiming for.


Subject: Re: List of items with variable height
From: Joe Betz
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sun, 7 Nov 2021 10:54 UTC
References: 1 2 3 4 5 6 7
X-Received: by 2002:ac8:5d8e:: with SMTP id d14mr15573253qtx.227.1636282458538;
Sun, 07 Nov 2021 02:54:18 -0800 (PST)
X-Received: by 2002:aca:6105:: with SMTP id v5mr361079oib.20.1636282458371;
Sun, 07 Nov 2021 02:54:18 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sun, 7 Nov 2021 02:54:18 -0800 (PST)
In-Reply-To: <2a82301b-f4c3-41a1-88e4-97f6a0d0fda3n@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2001:861:51c3:bae0:94cd:6a12:22d3:77f;
posting-account=P1X3VgoAAADgSty0b3mKYJl_KPjmW842
NNTP-Posting-Host: 2001:861:51c3:bae0:94cd:6a12:22d3:77f
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
<bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com> <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
<f6b52b27-7d4c-4afc-ac11-ba86f575483an@googlegroups.com> <aa8e9ded-56a8-4f22-997b-3d7196f5d481n@googlegroups.com>
<4da5ec27-c390-4893-a318-8856264f017dn@googlegroups.com> <2a82301b-f4c3-41a1-88e4-97f6a0d0fda3n@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <65c9ef7a-be68-45fb-a219-c89cfea87549n@googlegroups.com>
Subject: Re: List of items with variable height
From: joebet...@gmail.com (Joe Betz)
Injection-Date: Sun, 07 Nov 2021 10:54:18 +0000
Content-Type: text/plain; charset="UTF-8"
Lines: 0
View all headers
**In short, the former ended up being too slow and the latter just ends up being all sorts of wrong on an implementation level.


Subject: Re: List of items with variable height
From: Joe Betz
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sun, 7 Nov 2021 10:55 UTC
References: 1 2 3 4 5 6 7 8
X-Received: by 2002:ac8:5e4e:: with SMTP id i14mr75268480qtx.129.1636282530382;
Sun, 07 Nov 2021 02:55:30 -0800 (PST)
X-Received: by 2002:a05:6808:118c:: with SMTP id j12mr30937966oil.65.1636282530154;
Sun, 07 Nov 2021 02:55:30 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sun, 7 Nov 2021 02:55:29 -0800 (PST)
In-Reply-To: <65c9ef7a-be68-45fb-a219-c89cfea87549n@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2001:861:51c3:bae0:94cd:6a12:22d3:77f;
posting-account=P1X3VgoAAADgSty0b3mKYJl_KPjmW842
NNTP-Posting-Host: 2001:861:51c3:bae0:94cd:6a12:22d3:77f
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
<bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com> <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
<f6b52b27-7d4c-4afc-ac11-ba86f575483an@googlegroups.com> <aa8e9ded-56a8-4f22-997b-3d7196f5d481n@googlegroups.com>
<4da5ec27-c390-4893-a318-8856264f017dn@googlegroups.com> <2a82301b-f4c3-41a1-88e4-97f6a0d0fda3n@googlegroups.com>
<65c9ef7a-be68-45fb-a219-c89cfea87549n@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <842c990f-5c6a-41ee-91fd-32a1cc2857den@googlegroups.com>
Subject: Re: List of items with variable height
From: joebet...@gmail.com (Joe Betz)
Injection-Date: Sun, 07 Nov 2021 10:55:30 +0000
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Lines: 6
View all headers
What I did is have a method (e.g. `#updateListPresenters`) that brings the model and sub-presenters/views in sync, but bails if either the model or the view is missing (nil/deaf). Then I called it from both `#onViewAvailable` and `#model:`. That way it doesn't matter what order things happen in, we'll get everything in sync as soon as we have everything we need.

Makes sense, thanks!


Subject: Re: List of items with variable height
From: danie...@gmail.com
Newsgroups: comp.lang.smalltalk.dolphin
Date: Tue, 9 Nov 2021 04:23 UTC
References: 1 2 3 4 5 6 7
X-Received: by 2002:a05:6214:2307:: with SMTP id gc7mr4300750qvb.34.1636431815046;
Mon, 08 Nov 2021 20:23:35 -0800 (PST)
X-Received: by 2002:a05:6830:2aa7:: with SMTP id s39mr3491026otu.67.1636431814685;
Mon, 08 Nov 2021 20:23:34 -0800 (PST)
Path: i2pn2.org!i2pn.org!aioe.org!news.uzoreto.com!newsfeed.xs4all.nl!newsfeed8.news.xs4all.nl!news-out.netnews.com!news.alt.net!fdc2.netnews.com!peer01.ams1!peer.ams1.xlned.com!news.xlned.com!peer01.iad!feed-me.highwinds-media.com!news.highwinds-media.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Mon, 8 Nov 2021 20:23:34 -0800 (PST)
In-Reply-To: <2a82301b-f4c3-41a1-88e4-97f6a0d0fda3n@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2600:1700:37a8:52b0:645b:e036:e6ed:69d1;
posting-account=T9QyoQoAAADOKeQdWgBWNEPoknBuEcQZ
NNTP-Posting-Host: 2600:1700:37a8:52b0:645b:e036:e6ed:69d1
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
<bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com> <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
<f6b52b27-7d4c-4afc-ac11-ba86f575483an@googlegroups.com> <aa8e9ded-56a8-4f22-997b-3d7196f5d481n@googlegroups.com>
<4da5ec27-c390-4893-a318-8856264f017dn@googlegroups.com> <2a82301b-f4c3-41a1-88e4-97f6a0d0fda3n@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <6decbc65-afee-4c71-b6b9-181112ba20dan@googlegroups.com>
Subject: Re: List of items with variable height
From: daniels...@gmail.com (danie...@gmail.com)
Injection-Date: Tue, 09 Nov 2021 04:23:35 +0000
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
X-Received-Bytes: 12638
View all headers
Yeah, performance does start to suffer with massive numbers of controls on screen. The situations I've used this technique in have generally been limited to a dozen or so rows in the common case, with some performance degradation being expected and acceptable if it grows beyond that. You may well be right, in the end, that a fully custom control will be the only performant solution, but I have a few thoughts—if nothing else, I'm curious about what's really going on here:

1. When profiling, make sure you're really profiling everything—some things (painting in particular, but that in turn may be what triggers layout) may only happen when the main event loop returns to processing messages. As a start, however it is you measured the 500ms you refer to, make sure your profiler sample set actually contains that many samples, actually covers the whole time period you're interested in. If it doesn't...I'm not familiar enough with the guts of the profiler and the event loop to just tell you what you need to know offhand, but have a look at InputState>>loopWhile:, and at the way the profiler makes a distinction between a sample set being "active" or somesuch and the profiler actually _running_. You may not be able to use the simple Profiler class>>profile: API, but it should be possible to manually create a sample set, start the sampling process before opening the list, and stop it after all processing is done—a #postToInputQueue block (*not* the newer #postToMessageQueue, you want to wait until *all* Win32 messages are processed) might be a good place, or...I think there's a method #onEnterIdle somewhere, do a selector search for *idle* maybe. Conceptually this is more-or-less a hook for WM_IDLE, though that may not be the implementation.

2. When you say it takes 500ms, is that for the first render of 50 messages, or is that every time you add/remove/update one? Make sure your "sync" method, that gets the right number of sub-presenters in place, isn't doing extra work—only add or remove if the *number* of items changes, and only as much as you need to. Mine looked something like (pseudocode):

self view noRedrawDo: [
    [self model size > self subPresenters size] whileTrue: [SubPresenter createIn: self].
    [self subPresenters size > self model size] whileTrue: [self remove: self subPresenters last].
    self subPresenters with: self model do: [:presenter :item | presenter model: item]].

Where only one, and perhaps neither, of the while loops will run. If you know things about what items may have changed, you can optimize which presenters have their model replaced as well.

3. You can compute the height of a row without actually instantiating a view for it—DrawText[Ex] with DT_CALCRECT, you can work backwards from there to figure out what Dolphin exposes it as. So, now you know the height the whole list *would* take up, and you can optimize the initial creation of sub-presenters to be lazy—only create as many as are actually visible on-screen, but size the container within the ScrollingDecorator to the full height it will eventually need. Then intercept scrolling and add new rows as needed—though this will probably force you to choose between laggy scrolling, if you do it in a synchronous/blocking way, or a flash of empty space before the new items fill in. It'll be...*interesting*...to get it to work with click-and-drag-the-scrollbar scrolling, too, since that involves mouse capture, but...well, actually you could fill the extra space with an ImageView displaying a tiled mockup of a row, so the user knows there will be *something* there but it only populates when they release the mouse. Or, you could create the visible ones on initial load, then the rest one-at-a-time in...oh, any number of approaches—WM_IDLE handler, fast-firing WM_TIMER, forked Process that calls back in with #postToInputQueue, etc—so that most likely they would all be created by the time the user interacts with them, but the event loop is only ever blocked for as long as it takes to create a single row.

4. You might experiment with hard-coding the creation of the row views rather than instantiating them from a resource. You would do something like:

newSubPresenter := self add: ItemPresenter new.
newView := ContainerView new.
newView name: (newView addSubView: StaticText new) as: 'message'.
....etc...
self view addSubView: newView.
newSubPresenter view: newView.

Not sure if this would be faster than the resource, but you said about switching from proxies to views being slow, so maybe? Also, I'm not sure whether creating the entire row view before adding it to the parent will work—some views have issues being re-parented, so if anything you do while configuring the children of the row causes it to be fully realized (i.e. #create'ed), it might not slot in to the outer container properly. If needed you can add it immediately when you create it, e.g. newView := self view addSubView: ContainerView new. Adding it later *might* have better performance, but it also might not, so check. In any case probably the most important thing is to wrap the whole thing in `self view noRedrawDo:`!

5. If you want to get *really* clever, you could do what the iOS and Android list components do, and recycle subviews when scrolling in order to *only* ever have as many as fit on screen at once. In your case you would reuse the Presenters as well, giving them a new model. Actually, now that I think about it, this might be quite doable—you would need to manually position the row views (still maintaining a container representing the overall size of the list, to get decent scrolling behavior out of the box), maintain a list of the order the item presenters are currently in and what index in the list is the first visible item, and cycle them around as you scroll, moving the first one to the end when scrolling down and vice-versa. You'd need to watch resize events in this case to add/remove sub-presenters.

----

Re: the owner-drawn ListBox—there are places elsewhere in Dolphin where a parent view automatically redirects such notifications to the appropriate child—I want to say all the NM_ messages work this way, or rather they are all *actually* a WM_NOTIFY to the parent but it re-dispatches to the appropriate child? You might be able to take a similar approach, and you might find you can get quite far with loose methods and additions to the message tables held in class variables, without needing to subclass ContainerView and without *technically* modifying core code at all.

For WM_MEASUREITEM specifically, I see something about "CtlID (Type: UINT): The identifier of the combo box or list box. This member is not used for a menu."—it's not a handle, but it might be enough information to identify the originating control in a generic way that you could be comfortable adding to ContainerView-in-general rather than a hack for your specific case. You'll have to search for what it actually means and how to use it to retrieve the actual control.

----

Okay, whew, wall of text. Hope that helps, let me know how it goes!

On Sunday, November 7, 2021 at 5:52:55 AM UTC-5, joeb...@gmail.com wrote:
Okay, at this point I've tried both the ContainerView with FlowLayout and ListBox with custom draw method and neither worked out. In short, the former ended up being too slow and the former just ends up being all sorts of wrong on an implementation level.
2) Use a ContainerView with a FlowLayout layout manager and add each item as a subview. Example (modified from FramingLayout class>>example1):
ou'd lose the selection behavior compared to the ListBox approach but you could create a custom view for each item which provides any required functionality (e.g. reply to message).
The problem here was the messages took way too long to draw. For 50 messages it took 500ms rather than 0 or 1ms when I just shoved them into a ListView. My intuition was that it was an issue with the FlowLayout having to recompute each time a new message was added, but even after removing the layout it was still orders of magnitude too slow. The only thing that made any impact on the draw time was removing as many MVP triads as possible and render the entire message with a single TextPresenter. That got it down to 150ms, but that still isn't nearly good enough.

Running the profiler on the loop that calls `MessagePresenter createIn: self` for each message was interesting but not very enlightening. If interpreted it correctly, most of the time seemed to be spent in InputState, probably waiting for responses from Windows APIs. Another hotspot seemed to be code for replacing view proxies with actual views.

In the end, my conclusion is that there is significant performance overhead in using the MVP triad and that it doesn't scale well for non-standard controls.
1) Use a ListBox created with the LBS_OWNERDRAWVARIABLE style and override wmMeasureItem:wParam:lParam in ListBox to set the size of each item. You'll also need to draw the items yourself. See https://docs.microsoft.com/en-us/windows/win32/controls/wm-measureitem
It turns out that the draw and measure events don't get sent to the ListBox but rather their "owner", which ends up being whatever the parent view of ListBox is. E.g., if it's a ContainerView, then it needs to be subclassed and the wmDrawItem: and wmMeasureItem: methods need to be overriden in order to receive draw and measure events for the ListBox. Really annoying.

This is feasible for wmDrawItem: because the DRAWEVENTSTRUCT contains a reference to the ListBox view which you can then delegate the message to. It doesn't work so well with wmMeasureItem: since it contains no such reference, meaning that the ContainerView needed to hold either a reference to the ListBox or its model in order to even know what its measuring.

Click here to read the complete article
Subject: Re: List of items with variable height
From: Joe Betz
Newsgroups: comp.lang.smalltalk.dolphin
Date: Mon, 22 Nov 2021 04:11 UTC
References: 1 2 3 4 5 6 7 8
X-Received: by 2002:a05:620a:1a8d:: with SMTP id bl13mr44813796qkb.200.1637554306454;
Sun, 21 Nov 2021 20:11:46 -0800 (PST)
X-Received: by 2002:aca:5f44:: with SMTP id t65mr18161729oib.131.1637554306112;
Sun, 21 Nov 2021 20:11:46 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!border1.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Sun, 21 Nov 2021 20:11:45 -0800 (PST)
In-Reply-To: <6decbc65-afee-4c71-b6b9-181112ba20dan@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2001:861:51c3:bae0:dcaf:6e44:3fdc:56bb;
posting-account=P1X3VgoAAADgSty0b3mKYJl_KPjmW842
NNTP-Posting-Host: 2001:861:51c3:bae0:dcaf:6e44:3fdc:56bb
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
<bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com> <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
<f6b52b27-7d4c-4afc-ac11-ba86f575483an@googlegroups.com> <aa8e9ded-56a8-4f22-997b-3d7196f5d481n@googlegroups.com>
<4da5ec27-c390-4893-a318-8856264f017dn@googlegroups.com> <2a82301b-f4c3-41a1-88e4-97f6a0d0fda3n@googlegroups.com>
<6decbc65-afee-4c71-b6b9-181112ba20dan@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <cb163431-8a1b-413d-8cbf-be80186df470n@googlegroups.com>
Subject: Re: List of items with variable height
From: joebet...@gmail.com (Joe Betz)
Injection-Date: Mon, 22 Nov 2021 04:11:46 +0000
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Lines: 60
View all headers
Okay, whew, wall of text. Hope that helps, let me know how it goes!

At this point I've divorced from MVP and standard Windows controls completely and am now drawing everything using GDI+. Regressing to a lower level API was painful and it took some time to get rendering working correctly, but it's got both the performance and flexibility I was lacking with other options. With a double buffered view, it is able to handle redrawing a list of 100 messages for mouse over and mouse leave events with no visible flicker or delay (the message is outlined on focus).

I'm not sure what the implications are of using GDI+ (i.e., GdiplusGraphics) rather than the corresponding UserLibrary methods, but so far it seems to perform just as well with a better API.

1. When profiling, make sure you're really profiling everything—some things (painting in particular, but that in turn may be what triggers layout) may only happen when the main event loop returns to processing messages. As a start, however it is you measured the 500ms you refer to, make sure your profiler sample set actually contains that many samples, actually covers the whole time period you're interested in. If it doesn't...I'm not familiar enough with the guts of the profiler and the event loop to just tell you what you need to know offhand, but have a look at InputState>>loopWhile:, and at the way the profiler makes a distinction between a sample set being "active" or somesuch and the profiler actually _running_. You may not be able to use the simple Profiler class>>profile: API, but it should be possible to manually create a sample set, start the sampling process before opening the list, and stop it after all processing is done—a #postToInputQueue block (*not* the newer #postToMessageQueue, you want to wait until *all* Win32 messages are processed) might be a good place, or...I think there's a method #onEnterIdle somewhere, do a selector search for *idle* maybe. Conceptually this is more-or-less a hook for WM_IDLE, though that may not be the implementation.

Noted. I didn't dig any deeper with profiling the MVP solution, but good to know for the future profiling work.

2. When you say it takes 500ms, is that for the first render of 50 messages, or is that every time you add/remove/update one?
Just on the first render. It did well enough for add/remove/update events, aside from a flicker which I assume could have been fixed with a double buffered view.
3. You can compute the height of a row without actually instantiating a view for it—DrawText[Ex] with DT_CALCRECT, you can work backwards from there to figure out what Dolphin exposes it as. So, now you know the height the whole list *would* take up, and you can optimize the initial creation of sub-presenters to be lazy—only create as many as are actually visible on-screen, but size the container within the ScrollingDecorator to the full height it will eventually need.

Yup, makes sense. I think that's called virtual scrolling? It's something I want and might eventually need but am punting to the future now that the critical performance issues are resolved.

Re: the owner-drawn ListBox—there are places elsewhere in Dolphin where a parent view automatically redirects such notifications to the appropriate child—I want to say all the NM_ messages work this way, or rather they are all *actually* a WM_NOTIFY to the parent but it re-dispatches to the appropriate child?

Huh, interesting. I'll check if ever I come back to standard controls, but I'm quite happy having ditched them so that might not ever happen. :D


Subject: Re: List of items with variable height
From: danie...@gmail.com
Newsgroups: comp.lang.smalltalk.dolphin
Date: Wed, 24 Nov 2021 16:52 UTC
References: 1 2 3 4 5 6 7 8 9
X-Received: by 2002:a05:620a:1713:: with SMTP id az19mr7372740qkb.297.1637772732744;
Wed, 24 Nov 2021 08:52:12 -0800 (PST)
X-Received: by 2002:a05:6830:2683:: with SMTP id l3mr15011170otu.258.1637772732416;
Wed, 24 Nov 2021 08:52:12 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Wed, 24 Nov 2021 08:52:12 -0800 (PST)
In-Reply-To: <cb163431-8a1b-413d-8cbf-be80186df470n@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2600:1700:37a8:52b0:30f0:42b8:1ff7:fd7d;
posting-account=T9QyoQoAAADOKeQdWgBWNEPoknBuEcQZ
NNTP-Posting-Host: 2600:1700:37a8:52b0:30f0:42b8:1ff7:fd7d
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
<bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com> <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
<f6b52b27-7d4c-4afc-ac11-ba86f575483an@googlegroups.com> <aa8e9ded-56a8-4f22-997b-3d7196f5d481n@googlegroups.com>
<4da5ec27-c390-4893-a318-8856264f017dn@googlegroups.com> <2a82301b-f4c3-41a1-88e4-97f6a0d0fda3n@googlegroups.com>
<6decbc65-afee-4c71-b6b9-181112ba20dan@googlegroups.com> <cb163431-8a1b-413d-8cbf-be80186df470n@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <3996f4d3-e476-4f05-876d-179ecdd8d62cn@googlegroups.com>
Subject: Re: List of items with variable height
From: daniels...@gmail.com (danie...@gmail.com)
Injection-Date: Wed, 24 Nov 2021 16:52:12 +0000
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Lines: 19
View all headers
At this point I've divorced from MVP and standard Windows controls completely and am now drawing everything using GDI+.

Yeah, that sounded like the direction you were going, I just got carried away since I've dealt with this stuff before.

I think that's called virtual scrolling?

Yes, exactly—in fact this is already implemented in the native ListView/ListBox controls, and Dolphin ListViews use it by default (it's controlled by the #isVirtual aspect). It's actually probably *easier* to implement with a custom control, since you're just redrawing the whole thing and don't have to keep track of which subviews have which models etc, just ask for the scroll offset and figure out which item to start with and where.

Or, this *might* be a reason to explore the owner-drawn ListView option—tradeoff between figuring out the message redirection but getting virtual scrolling for free vs. no redirection (and perhaps more precise control of things like dividers, I realize) but having to implement virtual scrolling yourself.


Subject: Re: List of items with variable height
From: Joe Betz
Newsgroups: comp.lang.smalltalk.dolphin
Date: Tue, 21 Dec 2021 15:24 UTC
References: 1 2 3 4 5 6 7 8 9 10
X-Received: by 2002:a05:6214:29e9:: with SMTP id jv9mr2709342qvb.67.1640100241147;
Tue, 21 Dec 2021 07:24:01 -0800 (PST)
X-Received: by 2002:a05:6808:218b:: with SMTP id be11mr2905769oib.80.1640100240689;
Tue, 21 Dec 2021 07:24:00 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.smalltalk.dolphin
Date: Tue, 21 Dec 2021 07:24:00 -0800 (PST)
In-Reply-To: <3996f4d3-e476-4f05-876d-179ecdd8d62cn@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=5.48.59.174; posting-account=P1X3VgoAAADgSty0b3mKYJl_KPjmW842
NNTP-Posting-Host: 5.48.59.174
References: <084b20fe-a589-4ce6-93be-b83ff02cc1dan@googlegroups.com>
<bda56037-4808-424c-bb07-46e247d94ec6n@googlegroups.com> <11541306-0db6-4bb6-9bb8-47babdf022b3n@googlegroups.com>
<f6b52b27-7d4c-4afc-ac11-ba86f575483an@googlegroups.com> <aa8e9ded-56a8-4f22-997b-3d7196f5d481n@googlegroups.com>
<4da5ec27-c390-4893-a318-8856264f017dn@googlegroups.com> <2a82301b-f4c3-41a1-88e4-97f6a0d0fda3n@googlegroups.com>
<6decbc65-afee-4c71-b6b9-181112ba20dan@googlegroups.com> <cb163431-8a1b-413d-8cbf-be80186df470n@googlegroups.com>
<3996f4d3-e476-4f05-876d-179ecdd8d62cn@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <fb24934e-5d7e-40b1-9309-9bd99c8c44ben@googlegroups.com>
Subject: Re: List of items with variable height
From: joebet...@gmail.com (Joe Betz)
Injection-Date: Tue, 21 Dec 2021 15:24:01 +0000
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Lines: 71
View all headers
I revisited the owner drawn ListBox approach just to get an idea of how it compared to my custom GDI+ list view. The message redirection ended up being less of an issue since I ended up subclassing ContainerView for other reasons.

Virtual scrolling definitely seems to be helping as there is definitely less flicker when scrolling, and hover effects look really nice, but it introduced a couple of issues of its own that made it worse overall.

The biggest issue is there doesn't seem to be a way to add a new list item without invalidating the entire view. Or I couldn't figure out how the hell to do it. Neither LB_INSERTSTRING (in ListBox>>basicAdd:atIndex) nor LB_SETITEMDATA seem to be intelligent enough to just send the minimal WM_DRAWITEM and WM_MEASUREITEM messages necessary to update the canvas. They have no immediate visible effect, which is particularly bad when the list is initially empty and gets populated dynamically. The only way I could get the new items to show up was by calling ListBox>>refreshContents, but each call to results in a nasty flicker effect that ruins the performance benefits. Even debouncing the updates so they're spaced out by at least 100ms was still an eyesore.

Another issue was that selecting an item adjusts the view so that the full item content is visible. Really not what I want for a list with variable heights. And the only reason I wanted to set the selection at all was to capture hover states since there doesn't seem to be a concept for a particular item being in focus. Perhaps a virtual focus could work, but then I'd lose the advantages of being able to use WM_DRAWITEM for precise updates which seems to be the biggest advantage of using ListBox thus far.

I'm still puzzled by the first issue since the inability to support dynamic list modifications makes it almost useless. I'm guessing it's a peculiarity of it being owner drawn, but eh, I've wasted too much time on it already. Maybe I'll come back to it later.

For WM_MEASUREITEM specifically, I see something about "CtlID (Type: UINT): The identifier of the combo box or list box. This member is not used for a menu."—it's not a handle, but it might be enough information to identify the originating control in a generic way that you could be comfortable adding to ContainerView-in-general rather than a hack for your specific case. You'll have to search for what it actually means and how to use it to retrieve the actual control.

You're right, it is possible to use CtlID to get originating control. :)

Here's how:

ContainerView>>wmMeasureItem: message wParam: wParam lParam: lParam
| struct control itemExtent |
struct := MEASUREITEMSTRUCT fromAddress: lParam.
control := nil.
self subViewsDo:
[:subView |
| contrlId |
controlId := UserLibrary default getDlgCtrlID: subView handle.
controlId = struct CtlID ifTrue: [control := subView]].
control
ifNotNil:
[itemExtent := control measureItem: struct.
struct
itemWidth: itemExtent x;
itemHeight: itemExtent y].
^TRUE

UserLibrary>>getDlgCtrlID: aWindowHandle
"Retrieves the identifier of the specified control.
 
int GetDlgCtrlID(
[in] HWND hWnd
);"

<stdcall: sdword GetDlgCtrlID handle>
^self invalidCall: _failureCode


1
rocksolid light 0.7.2
clearneti2ptor