«GUI: Scrolling list of multi-view items» by jamshark70

on 16 Jan'16 22:07 in guiscrolllistlayoutlayoutsmultiline

A common GUI requirement is a scrollable list of similar items. If the list consists of strings, it's easy -- use ListView. If, for example, each "line" should have a pop-up menu, a text field and a toggle button, there's ScrollView. We also have Qt layouts, which are supposed to save us from the trouble of calculating coordinates. Scrolling lists are usually arranged vertically, so: VLayout.

But, there are a couple of tricks:

  • If you set the .layout of a ScrollView directly, the layout will use the visible area only -- no scrolling.

SOLUTION: Explicitly replace the ScrollView's canvas with your own View(), and apply the layout to the canvas. (This is in the help file, but it's easy to overlook.)

  • Now, if you just start adding views into the VLayout, empty space will be distributed equally between them. Normal behavior is that the first few entries appear at the top, and fill downwards.

SOLUTION: According to the help, 'nil', added to any layout, is a stretchable "spacer." But, this doesn't interact well with the next trick. So use an empty View() instead.

  • layout.insert(view, 0) will display the items in reverse order -- new items at the top.

SOLUTION: layout.insert(view, scrollview.children.size - 1)

Each line, then, is a View() (with appropriate size hints). Apply a layout (probably HLayout), add the operating views into that layout -- and then insert this parent view into the ScrollView's canvas layout.

Scrolling behavior is as expected: No scrollbars are visible until the views go below the visible area. If you scroll all the way to the bottom, there's no empty space under the last text view.

Note also that you can delete items simply by locating one of the ScrollView's children and calling .remove on it. The empty space closes up automatically!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
g = { |view| view.background_(Color.rand(0.5, 0.9)) };

w = Window("scroll layout", Rect(800, 200, 500, 400)).front;
v = g.value(ScrollView(w, w.view.bounds.insetBy(2, 2)));
v.canvas = View();
v.canvas.layout = l = VLayout(View()); // View() here is the stretching spacer

(
{
	30.do { |i|
		var view = g.value(View())
		.fixedHeight_(22)
		.layout_(HLayout(
			StaticText().string_(i.asString).fixedWidth_(30),
			Button().states_([["click me"]])
			.action_({ |view| "% was clicked\n".postf(i) });
		).margins_(1));
		l.insert(view, v.children.size - 1);
		0.5.wait;
	};
}.fork(AppClock);
)

// Delete an item by .remove

v.children.choose.remove;
raw 674 chars (focus & ctrl+a+c to copy)
reception
comments