Skip to content

Commit 450145b

Browse files
committed
Refactor ListView (WIP)
Currently not again in a working state. Two major changes: 1) Use ListView only for Status containers (Timelines and Threads). Reason for this is mostly to not need to adapt all the other Widgets). 2) In order to use the performance benefit of ListView: differente for Status between the setup of the widget and the (re)initialisation of the widget with content. Point 2 could performancewise be (probably significantly) improved by moving more of the initialisation that's not content specific out of init() and into the empty Status constructor.
1 parent 8db0624 commit 450145b

20 files changed

+270
-273
lines changed

src/API/Conversation.vala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
public class Tuba.API.Conversation : Entity, Widgetizable {
1+
public class Tuba.API.Conversation : Entity, WidgetizableForListView {
22

33
public string id { get; set; }
44
public Gee.ArrayList<API.Account>? accounts { get; set; }

src/API/Status.vala

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
public class Tuba.API.Status : Entity, Widgetizable, SearchResult {
1+
public class Tuba.API.Status : Entity, WidgetizableForListView, SearchResult {
22

33
~Status () {
44
debug (@"[OBJ] Destroyed $(uri ?? "")");
@@ -219,7 +219,13 @@ public class Tuba.API.Status : Entity, Widgetizable, SearchResult {
219219
}
220220

221221
public override Gtk.Widget to_widget () {
222-
return new Widgets.Status (this);
222+
return new Widgets.Status ();
223+
}
224+
225+
public override void fill_widget_with_content (Gtk.Widget widget) throws Oopsie {
226+
((Widgets.Status) widget).kind_instigator = this.account;
227+
((Widgets.Status) widget).status = this;
228+
((Widgets.Status) widget).init (this);
223229
}
224230

225231
public override void open () {

src/Dialogs/Report.vala

+1-5
Original file line numberDiff line numberDiff line change
@@ -475,11 +475,7 @@ public class Tuba.Dialogs.Report : Adw.Dialog {
475475
widget_status.can_target = false;
476476
widget_status.focusable = false;
477477
widget_status.actions.visible = false;
478-
#if USE_LISTVIEW
479-
widget_status.can_be_opened = false;
480-
#else
481-
widget_status.activatable = false;
482-
#endif
478+
widget_status.can_be_opened = false;
483479
listbox.append (new StatusRow (checkbutton, widget_status));
484480
});
485481

src/Views/Base.vala

+4-10
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,7 @@ public class Tuba.Views.Base : Adw.BreakpointBin {
6868
// [GtkChild] protected unowned Adw.Clamp clamp;
6969
// [GtkChild] protected unowned Gtk.Box column_view;
7070
[GtkChild] protected unowned Gtk.Stack states;
71-
#if USE_LISTVIEW
72-
[GtkChild] protected unowned Adw.ClampScrollable content_box;
73-
#else
74-
[GtkChild] protected unowned Adw.Clamp content_box;
75-
#endif
71+
[GtkChild] protected unowned Adw.ClampScrollable content_box;
7672
[GtkChild] protected unowned Gtk.Button status_button;
7773
[GtkChild] unowned Gtk.Image status_image;
7874
[GtkChild] unowned Gtk.Stack status_stack;
@@ -186,11 +182,9 @@ public class Tuba.Views.Base : Adw.BreakpointBin {
186182
base.dispose ();
187183
}
188184

189-
#if !USE_LISTVIEW
190-
public virtual void unbind_listboxes () {
191-
this.last_widget = null;
192-
}
193-
#endif
185+
public virtual void unbind_listboxes () {
186+
this.last_widget = null;
187+
}
194188

195189
protected virtual void build_actions () {}
196190

src/Views/ContentBase.vala

+28-73
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
public class Tuba.Views.ContentBase : Views.Base {
2-
3-
#if USE_LISTVIEW
4-
protected Gtk.ListView content;
5-
#else
6-
protected Gtk.ListBox content;
7-
#endif
2+
protected Gtk.ListBox content;
83
protected signal void reached_close_to_top ();
94
public GLib.ListStore model;
105
private bool bottom_reached_locked = false;
@@ -16,32 +11,21 @@ public class Tuba.Views.ContentBase : Views.Base {
1611
construct {
1712
model = new GLib.ListStore (typeof (Widgetizable));
1813

19-
#if USE_LISTVIEW
20-
Gtk.SignalListItemFactory signallistitemfactory = new Gtk.SignalListItemFactory ();
21-
signallistitemfactory.bind.connect (bind_listitem_cb);
22-
23-
content = new Gtk.ListView (new Gtk.NoSelection (model), signallistitemfactory) {
24-
css_classes = { "contentbase", "content", "background" },
25-
single_click_activate = true
26-
};
27-
28-
content.activate.connect (on_content_item_activated);
29-
model.items_changed.connect (on_content_changed);
30-
#else
31-
model.items_changed.connect (on_content_changed);
32-
content = new Gtk.ListBox () {
33-
selection_mode = Gtk.SelectionMode.NONE,
34-
css_classes = { "content", "background" }
35-
};
36-
37-
content.row_activated.connect (on_content_item_activated);
38-
content.bind_model (model, on_create_model_widget);
39-
#endif
14+
model.items_changed.connect (on_content_changed);
15+
16+
content = new Gtk.ListBox () {
17+
selection_mode = Gtk.SelectionMode.NONE,
18+
css_classes = { "fake-content", "background" }
19+
};
20+
21+
content.row_activated.connect (on_content_item_activated);
22+
content.bind_model (model, on_create_model_widget);
4023
content_box.child = content;
4124

4225
scrolled.vadjustment.value_changed.connect (on_scrolled_vadjustment_value_change);
4326
scroll_to_top_rev.bind_property ("child-revealed", scroll_to_top_rev, "visible", GLib.BindingFlags.SYNC_CREATE);
4427
}
28+
4529
~ContentBase () {
4630
debug ("Destroying ContentBase");
4731
}
@@ -71,26 +55,9 @@ public class Tuba.Views.ContentBase : Views.Base {
7155
scroll_to_top_rev.reveal_child = reveal;
7256
}
7357

74-
#if USE_LISTVIEW
75-
protected virtual void bind_listitem_cb (GLib.Object item) {
76-
((Gtk.ListItem) item).child = on_create_model_widget (((Gtk.ListItem) item).item);
77-
78-
var gtklistitemwidget = ((Gtk.ListItem) item).child.get_parent ();
79-
if (gtklistitemwidget != null) {
80-
gtklistitemwidget.add_css_class ("card");
81-
gtklistitemwidget.add_css_class ("card-spacing");
82-
gtklistitemwidget.focusable = true;
83-
84-
// Thread lines overflow slightly
85-
gtklistitemwidget.overflow = Gtk.Overflow.HIDDEN;
86-
}
87-
}
88-
#endif
8958

9059
public override void dispose () {
91-
#if !USE_LISTVIEW
92-
unbind_listboxes ();
93-
#endif
60+
unbind_listboxes ();
9461
base.dispose ();
9562
}
9663

@@ -114,31 +81,25 @@ public class Tuba.Views.ContentBase : Views.Base {
11481
}
11582
}
11683

117-
#if !USE_LISTVIEW
118-
public override void unbind_listboxes () {
119-
if (content != null)
120-
content.bind_model (null, null);
121-
base.unbind_listboxes ();
122-
}
123-
#endif
84+
public override void unbind_listboxes () {
85+
if (content != null)
86+
content.bind_model (null, null);
87+
base.unbind_listboxes ();
88+
}
12489

12590
public virtual Gtk.Widget on_create_model_widget (Object obj) {
12691
var obj_widgetable = obj as Widgetizable;
12792
if (obj_widgetable == null)
12893
Process.exit (0);
12994
try {
130-
#if !USE_LISTVIEW
131-
Gtk.Widget widget = obj_widgetable.to_widget ();
132-
widget.add_css_class ("card");
133-
widget.add_css_class ("card-spacing");
134-
widget.focusable = true;
135-
136-
// Thread lines overflow slightly
137-
widget.overflow = Gtk.Overflow.HIDDEN;
138-
return widget;
139-
#else
140-
return obj_widgetable.to_widget ();
141-
#endif
95+
Gtk.Widget widget = obj_widgetable.to_widget ();
96+
widget.add_css_class ("card");
97+
widget.add_css_class ("card-spacing");
98+
widget.focusable = true;
99+
100+
// Thread lines overflow slightly
101+
widget.overflow = Gtk.Overflow.HIDDEN;
102+
return widget;
142103
} catch (Oopsie e) {
143104
warning (@"Error on_create_model_widget: $(e.message)");
144105
Process.exit (0);
@@ -155,13 +116,7 @@ public class Tuba.Views.ContentBase : Views.Base {
155116
}, Priority.LOW);
156117
}
157118

158-
#if USE_LISTVIEW
159-
public virtual void on_content_item_activated (uint pos) {
160-
((Widgetizable) ((ListModel) content.model).get_item (pos)).open ();
161-
}
162-
#else
163-
public virtual void on_content_item_activated (Gtk.ListBoxRow row) {
164-
Signal.emit_by_name (row, "open");
165-
}
166-
#endif
119+
public virtual void on_content_item_activated (Gtk.ListBoxRow row) {
120+
Signal.emit_by_name (row, "open");
121+
}
167122
}

src/Views/ContentBaseListView.vala

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
public class Tuba.Views.ContentBaseListView : Views.Base {
2+
3+
protected Gtk.ListView content;
4+
protected signal void reached_close_to_top ();
5+
public GLib.ListStore model;
6+
private bool bottom_reached_locked = false;
7+
8+
public bool empty {
9+
get { return model.get_n_items () <= 0; }
10+
}
11+
12+
construct {
13+
model = new GLib.ListStore (typeof (WidgetizableForListView));
14+
15+
Gtk.SignalListItemFactory signallistitemfactory = new Gtk.SignalListItemFactory ();
16+
signallistitemfactory.setup.connect (setup_listitem_cb);
17+
signallistitemfactory.bind.connect (bind_listitem_cb);
18+
19+
content = new Gtk.ListView (new Gtk.NoSelection (model), signallistitemfactory) {
20+
css_classes = { "content", "background" },
21+
single_click_activate = true
22+
};
23+
24+
content.activate.connect (on_content_item_activated);
25+
content_box.child = content;
26+
27+
scrolled.vadjustment.value_changed.connect (on_scrolled_vadjustment_value_change);
28+
scroll_to_top_rev.bind_property ("child-revealed", scroll_to_top_rev, "visible", GLib.BindingFlags.SYNC_CREATE);
29+
}
30+
~ContentBaseListView () {
31+
debug ("Destroying ContentBaseListView");
32+
}
33+
34+
protected virtual void on_scrolled_vadjustment_value_change () {
35+
if (
36+
!bottom_reached_locked
37+
&& scrolled.vadjustment.value > scrolled.vadjustment.upper - scrolled.vadjustment.page_size * 2
38+
) {
39+
bottom_reached_locked = true;
40+
on_bottom_reached ();
41+
}
42+
43+
var is_close_to_top = scrolled.vadjustment.value <= 100;
44+
set_scroll_to_top_reveal_child (
45+
!is_close_to_top
46+
&& scrolled.vadjustment.value + scrolled.vadjustment.page_size + 100 < scrolled.vadjustment.upper
47+
);
48+
}
49+
50+
protected void set_scroll_to_top_reveal_child (bool reveal) {
51+
if (reveal == scroll_to_top_rev.reveal_child) return;
52+
if (reveal) scroll_to_top_rev.visible = true;
53+
54+
scroll_to_top_rev.reveal_child = reveal;
55+
}
56+
57+
protected void setup_listitem_cb (GLib.Object item) {
58+
Gtk.ListItem i = (Gtk.ListItem) item;
59+
i.child = on_create_model_widget (i.item);
60+
61+
var gtklistitemwidget = i.child.get_parent ();
62+
if (gtklistitemwidget != null) {
63+
gtklistitemwidget.add_css_class ("card");
64+
gtklistitemwidget.add_css_class ("card-spacing");
65+
gtklistitemwidget.focusable = true;
66+
67+
// Thread lines overflow slightly
68+
gtklistitemwidget.overflow = Gtk.Overflow.HIDDEN;
69+
}
70+
}
71+
72+
protected virtual void bind_listitem_cb (GLib.Object item) {
73+
var obj_widgetable = ((Gtk.ListItem) item).item as WidgetizableForListView;
74+
if (obj_widgetable == null)
75+
Process.exit (0);
76+
77+
try {
78+
obj_widgetable.fill_widget_with_content(((Gtk.ListItem) item).child);
79+
} catch (Oopsie e) {
80+
warning (@"Error bind_listitem_cb: $(e.message)");
81+
Process.exit (0);
82+
}
83+
}
84+
85+
public override void dispose () {
86+
base.dispose ();
87+
}
88+
89+
public override void clear () {
90+
base.clear ();
91+
this.model.remove_all ();
92+
}
93+
94+
protected virtual void clear_all_but_first (int i = 1) {
95+
base.clear ();
96+
97+
print("before splice!\n");
98+
if (model.n_items > i)
99+
model.splice (i, model.n_items - i, {});
100+
}
101+
102+
public override void on_content_changed () {
103+
if (empty) {
104+
base_status = new StatusMessage ();
105+
} else {
106+
base_status = null;
107+
}
108+
}
109+
110+
public override void unbind_listboxes () {}
111+
public virtual Gtk.Widget on_create_model_widget (Object obj) {
112+
var obj_widgetable = obj as Widgetizable;
113+
if (obj_widgetable == null)
114+
Process.exit (0);
115+
try {
116+
return obj_widgetable.to_widget ();
117+
} catch (Oopsie e) {
118+
warning (@"Error on_create_model_widget: $(e.message)");
119+
Process.exit (0);
120+
}
121+
}
122+
123+
public virtual void on_bottom_reached () {
124+
uint timeout = 0;
125+
timeout = Timeout.add (1000, () => {
126+
bottom_reached_locked = false;
127+
GLib.Source.remove (timeout);
128+
129+
return true;
130+
}, Priority.LOW);
131+
}
132+
133+
public virtual void on_content_item_activated (uint pos) {
134+
((WidgetizableForListView) ((ListModel) content.model).get_item (pos)).open ();
135+
}
136+
}

src/Views/EditHistory.vala

+3-9
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,11 @@ public class Tuba.Views.EditHistory : Views.Timeline {
1414

1515
widget_status.actions.visible = false;
1616
widget_status.menu_button.visible = false;
17-
#if USE_LISTVIEW
18-
widget_status.can_be_opened = false;
19-
widget_status.content.selectable = true;
20-
#else
21-
widget_status.activatable = false;
22-
#endif
17+
widget_status.can_be_opened = false;
18+
widget_status.content.selectable = true;
2319

2420
return widget_status;
2521
}
2622

27-
#if USE_LISTVIEW
28-
public override void on_content_item_activated (uint pos) {}
29-
#endif
23+
public override void on_content_item_activated (uint pos) {}
3024
}

src/Views/Lists.vala

-14
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,6 @@ public class Tuba.Views.Lists : Views.Timeline {
3030
action_box.append (edit_button);
3131
action_box.append (delete_button);
3232

33-
#if !USE_LISTVIEW
34-
this.activated.connect (() => open ());
35-
#endif
36-
3733
this.activatable = true;
3834
this.add_suffix (action_box);
3935

@@ -142,16 +138,6 @@ public class Tuba.Views.Lists : Views.Timeline {
142138
public Adw.PreferencesDialog create_edit_preferences_dialog (API.List t_list) {
143139
return new Dialogs.ListEdit (t_list);
144140
}
145-
146-
#if !USE_LISTVIEW
147-
public virtual signal void open () {
148-
if (this.list == null)
149-
return;
150-
151-
var view = new Views.List (list);
152-
app.main_window.open_view (view);
153-
}
154-
#endif
155141
}
156142

157143
public new bool empty {

0 commit comments

Comments
 (0)