Recent

Author Topic: Graphical List / Draw List  (Read 3130 times)

kupferstecher

  • Hero Member
  • *****
  • Posts: 604
Graphical List / Draw List
« on: October 21, 2024, 06:49:50 pm »
Hi all,

I'd need some advice on how to implement a graphical list.
The (linear) list consists of different elements, that should be user drawn according to the contents. My first attempt was to use a scrollbox and just dynamically place panels with subcontrols. That worked well for small lists, but with more and more items, things got slow. I think the bottleneck is the scrollbox that triggeres a redraw of all elements once a control was removed or added. I'm now abandoning that approach and only want to draw what is actually visible on the screen. TDrawGrid seemed suitable for that and a first test was promising, but scrolling is quite odd, it seems that the scrolling is connected to the selected cell, while I just want to scroll the view and no selection. Also dragging the scrollbar doesn't immediately update the view.

As the functionality that I need is not too much I think about programming such a component from scratch (If there is not a recommendation for a suitable control).

And I wonder what would be the best way to do.

Requirements:
- The control should provide a handler/callback for each item like the DrawGrid providing a Canvas, on which can be drawn.
- Random dimensions for each element, but always one item below another
- Horizontal scrolling if it doesn't fit on the window
- Flicker-free
- Smooth pixel-wise scrolling (lower priority)
- No internal data, just a pointer for user data in each item could be enough
- Mouse control, handlers for clicks, double clicks

My basic idea is to use a TCustomControl containing two scrollbars and copying the user drawn canvas to the correct positions according to the scrollbars. Would it be better to use a Canvas for each visible item, so when a new item becomes visible only the new item has to be redrawn and the existing ones could just be copied from the buffers, or should every element just be redrawn to avoid cache issues?
Also what kind of canvas would be advisable regarding speed, a TBitmap, a TBGRABitmap? The first one could mean (slow) API-calls of the operating system, but could have faster copy mechanisms (Just guesses).

Another possibility would be to dynamically place a seperate control for each visible item and move them around according to the scrollbars. The hope would be that moving is efficiently handled by the OS on the GPU and wouldn't involve manual redrawing or data copying using the CPU. But I'm not sure there either. It also could mean a lot of API-calls and even redraws.

Or are there other approaches? I'm happy for any comments.

Thanks!

jamie

  • Hero Member
  • *****
  • Posts: 7302
Re: Graphical List / Draw List
« Reply #1 on: October 23, 2024, 12:21:49 am »
Sounds like you are looking for vector formats.

Those are element by element drawn and can be cached in a storage container.

WMF, SVG etc
The only true wisdom is knowing you know nothing

kupferstecher

  • Hero Member
  • *****
  • Posts: 604
Re: Graphical List / Draw List
« Reply #2 on: October 23, 2024, 05:11:16 pm »
It's a bit difficult to describe. I actually want to display data, so when using vector graphics I would have to create the vector graphics in realtime. I'm not sure if this is a good solution. Actually the TDrawGrid (with just using one column) would be fine if only the scolling behaviour would be different.
So the question could also be, how to reimplement a (simplified) draw grid in a way that it allows pixel wise scrolling with "live update" while scrolling.

Handoko

  • Hero Member
  • *****
  • Posts: 5485
  • My goal: build my own game engine using Lazarus
Re: Graphical List / Draw List
« Reply #3 on: October 23, 2024, 06:17:25 pm »
Lazarus TCanvas is good for cross platform compatibility, not optimized for performance and it is slow. It is okay for beginners to learn graphics programming, other than that it almost has zero real world usage for graphics programming.

TBGABitmap is as slow as TBitmap. In some cases it is about 3% slower than TBitmap, I tried and benchmarked it so I can sure to say about it. Unless you're using the 'special' features of TBGRABitmap that internally using OpenGL. TBGRABitmap's and TBitmap's performances are no better than TCanvas, don't use any of them. Alternatively, you can consider Graphics32. I haven't tried it but it sounds promising:
https://github.com/graphics32

If you're willing to learn, try OpenGL. I wrote a test that animates thousands of vector shapes using TOpenGLControl component, run perfectly smooth at 60 FPS even on 15 years old laptops. For TCanvas/TBitmap/TBGRABitmap, you usually will start to notice some sluggishness if the object count reaches about 100 even you heavily optimized your code.

I still have no idea what you want to achieve. Can you provide some screenshots or sketches? Here we have many skilled programmers, maybe they can provide you the solution but you need to provide us more information.

Or if you don't need it in a hurry, you can contact me personally. The basic features of my graphics engine has already work as I want it to, it internally uses TOpenGLControl so it should be cross platform too. I don't mind to subtract the code and make some necessary changes to suit your case. It is free but only if you can wait because I don't do programming whole day long.
« Last Edit: October 23, 2024, 06:23:42 pm by Handoko »

kupferstecher

  • Hero Member
  • *****
  • Posts: 604
Re: Graphical List / Draw List
« Reply #4 on: October 23, 2024, 07:29:18 pm »
Thanks for the reply! The TBitmap/TBGRABitmap comparison is very interesting.

Attached an example, what I have in mind. The "table" I'm talking about is in the left of the screenshot (read frame). There it is a list of interfaces and clicking an item opens a detailed view with settings (middle and right box). In this application each item of the list is a panel and that I create on runtime and populate it with several controls, like TLabel, TImage and I draw the background manually in the Paint procedure. The list is placed on a scrollbox. As this list with ports/interfaces won't get too long, this appoach works fine. But for an other usecase with a similar list I need several hundred of entries and then the scrollbox approach is getting too slow.

So as said, I tried to use the TDrawGrid component instead and I just draw the contents in the CellDraw event. In general it works, but scrolling is not nice.

Opengl I would like to avoid, I think performance wise it should be possible to render such a list with LCL means. There is no animatition or anything and the contents are not too complicated, only the smooth scrolling is the critical point.

Hope my intention is a bit clearer now.

TRon

  • Hero Member
  • *****
  • Posts: 4377
Re: Graphical List / Draw List
« Reply #5 on: October 23, 2024, 07:41:32 pm »
@kupferstecher:
looks to me like something for a listview in virtual mode using custom drawing. Perhaps even a normal listbox using custom drawing (but a listbox doesn't have a virtual mode).
« Last Edit: October 23, 2024, 07:47:16 pm by TRon »
Today is tomorrow's yesterday.

kupferstecher

  • Hero Member
  • *****
  • Posts: 604
Re: Graphical List / Draw List
« Reply #6 on: October 23, 2024, 08:02:02 pm »
TListView I saw before, but in the wiki it said that the examples perhaps only work under Windows, so I though it might not be too reliable. But I'll have a further look.

What does "virtual mode" mean?

Handoko

  • Hero Member
  • *****
  • Posts: 5485
  • My goal: build my own game engine using Lazarus
Re: Graphical List / Draw List
« Reply #7 on: October 23, 2024, 08:16:52 pm »
The TBitmap/TBGRABitmap comparison is very interesting.

In 2016 (if I remember correctly), here we had a graphics contest. I was preparing my 2D vertical scrolling spaceship shooting game. I wanted to use purely Lazarus components, without any third party component. Finally the basic prototype seemed to work correctly, the graphics was drawn using TCanvas lines only, no fill color, no bitmap images. The performance wasn't good, although all the objects were just some simple line drawings. So it was the time for optimizing the code. Not much can be done. If the object count reached about 150, the game wasn't playable. I tested it on an Intel G2020 machine, which was a bit faster than Core2 Duo.

I heard people frequently talk about BGRABitmap. Some searching on the web, I learned we can easily switch our TCanvas/TBitmap code to use TBGRABitmap easily. That really was a good selling point. So I tried it. To my surprise, I didn't notice any performance differences. I modified the code for benchmarking. The result shown TBGRABitmap was some percentages slower than TCanvas. I haven't inspect into TBGRABitmap's source code, but that makes me think TBGRABitmap was actually TCanvas/TBitmap + some extra codes. I could be wrong but that is what I believe.

If TBGRABitmap wasn't that slow I wouldn't start learning OpenGL.
« Last Edit: October 23, 2024, 08:19:15 pm by Handoko »

TRon

  • Hero Member
  • *****
  • Posts: 4377
Re: Graphical List / Draw List
« Reply #8 on: October 23, 2024, 08:20:06 pm »
TListView I saw before, but in the wiki it said that the examples perhaps only work under Windows, so I though it might not be too reliable.
tbh I did not considered that.

That what you would like to accomplish is basic functionality of the component so I can't really imagine this ot to be working on/for other widgetsets. If something does fail then there is also a custom drawn version that could perhaps be used (TCDListview)

Quote
What does "virtual mode" mean?
In basics it means that the data that you would normally store inside the items of the Listview itself are not really stored there but are requested by the component when they are needed. That way you can store data in any custom format that you desire and retrieve the actual contents when the component asks for it (in your particular case several pieces of information).

Virtual mode allows you f.e. to set the number of items in the listview to a certain number (which can virtually be unlimited) without occupying the memory that it usually would take (theoretically allowing for a unlimited amount of items 'stored' in the list).

I do not know for sure if it works the same for every widgetset but that also means that only the visible items  (or portions thereof) are drawn (if you put the list in customdrawn mode then the component will request to draw only those parts).
Today is tomorrow's yesterday.

Handoko

  • Hero Member
  • *****
  • Posts: 5485
  • My goal: build my own game engine using Lazarus
Re: Graphical List / Draw List
« Reply #9 on: October 23, 2024, 08:30:32 pm »
TListView I saw before, but in the wiki it said that the examples perhaps only work under Windows ...

Not sure it is updated or not. See the screenshot below.
Source: https://wiki.lazarus.freepascal.org/Roadmap

kupferstecher

  • Hero Member
  • *****
  • Posts: 604
Re: Graphical List / Draw List
« Reply #10 on: October 23, 2024, 09:59:01 pm »
OK, sounds promissing. I'll try it! Thank you!

kupferstecher

  • Hero Member
  • *****
  • Posts: 604
Re: Graphical List / Draw List
« Reply #11 on: October 24, 2024, 12:03:45 am »
Now I tested the ListView and ListBox, both owner drawn, see attached screenshots. From vertical scrolling they are both good, fast and with realtime update whle scrolling. Scrolling is always alligned to a line, no pixel scrolling, but that actually doesn't matter.

The ListView has no property for the row height and after a quick internet search I still don't know how to set the height of the rows. Seems some hack is required, like assigning an (empty) ImageList that has a height property.

The Listbox on the other hand has a height property, but doesn't support horizontal scrolling, seems the width of each line is according to the listbox size. Also the number of rows is determined by the items property (TStrings). So no virtual mode as @TRon already pointed out. But I could live with that, I think.

So still not the perfect solution, yet. Or did I miss some properties/settings?

Btw. the draw callback for the TListBox is fired for all visible items once there is a change, while the TListView seems to buffer each item seperately, as only the "new" items that enter the screen while scrolling are called for drawing.

zeljko

  • Hero Member
  • *****
  • Posts: 1792
    • http://wiki.lazarus.freepascal.org/User:Zeljan
Re: Graphical List / Draw List
« Reply #12 on: October 24, 2024, 01:11:38 pm »
Look for TVirtualStringTree, IMO that's something you're searching for.

dbannon

  • Hero Member
  • *****
  • Posts: 3556
    • tomboy-ng, a rewrite of the classic Tomboy
Re: Graphical List / Draw List
« Reply #13 on: October 24, 2024, 01:33:30 pm »
Now I tested the ListView and ListBox, both owner drawn, ...

Don't confuse Owner Drawn with Virtual Mode. I use ListView in Virtual Mode and it works well on Windows, Mac and Qtx but gtk2 still draws the entire contents each time.

The idea is you have the content stored in some fast structure, I use FPList, you tell ListView to only render the content that is visible. The list view says, "hmm, lines 356-396 are visible, I'll ask the data structure what I should show for those lines". So, if you have a large set of data, maybe > 20K lines OR, heavy rendering of an image per line, its far more efficient.

I also have several Indexes into that data structure so sorting the display, in my case, alphabetically or by date is easy.

Its well doc'ed on the wiki (from memory).

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

kupferstecher

  • Hero Member
  • *****
  • Posts: 604
Re: Graphical List / Draw List
« Reply #14 on: October 24, 2024, 11:26:24 pm »
Look for TVirtualStringTree, IMO that's something you're searching for.
Thanks, I'll also have a look at it!

Now I tested the ListView and ListBox, both owner drawn, ...

Don't confuse Owner Drawn with Virtual Mode.
Can you eleborate a bit more? What I meant with owner drawn is that I use the handlers ListView1CustomDrawItem (ListBox1DrawItem, respectively) and draw on the canvas there. As I understood @TRon is that the virtual mode just means the component doesn't store any cell/list data. As said, drawing worked all right, the issue with ListView "only" was that I couldn't set the height of the rows. Neither individually nor one height for all rows.

Quote
The idea is you have the content stored in some fast structure, I use FPList, you tell ListView to only render the content that is visible. The list view says, "hmm, lines 356-396 are visible, I'll ask the data structure what I should show for those lines". So, if you have a large set of data, maybe > 20K lines OR, heavy rendering of an image per line, its far more efficient.
Yes, thats what I need :)

 

TinyPortal © 2005-2018