1

I'm trying to reduce the load time of a ColumnView widget with lots of rows. quoting from gtk list widget documentation:

While it is not a problem for short lists to instantiate widgets for every item in the model, once lists grow to thousands or millions of elements, this gets less feasible. Because of this, the views only create a limited amount of listitems and recycle them by binding them to new items. In general, views try to keep listitems available only for the items that can actually be seen on screen.

However in my code example although the recycling(bind, unbind) works; 205 items are setup; whereas only a few items are visible:

use gtk::*;
use gtk::prelude::*;
use gtk::gio::ListStore;
use gtk::glib::BoxedAnyObject;

fn main() {
    let application = Application::builder().application_id("column.view.test").build();
    application.connect_activate(|application| {
        let list_store = ListStore::new(BoxedAnyObject::static_type());
        for i in 0..10000 {
            list_store.append(&BoxedAnyObject::new(i));
        }
        let column_view = ColumnView::builder().model(&NoSelection::new(Some(list_store))).build();
        let scrolled_window = ScrolledWindow::builder().child(&column_view).build();
        let item_factory = SignalListItemFactory::new();
        item_factory.connect_setup(|_, item| {
            let list_item = item.downcast_ref::<ListItem>().unwrap();
            list_item.set_child(Some(&Label::new(None)));
            println!("setup");
        });
        item_factory.connect_bind(|_, item| {
            let list_item = item.downcast_ref::<ListItem>().unwrap();
            list_item.child().and_downcast::<Label>().unwrap().set_label("a");
            println!("bind");
        });
        item_factory.connect_unbind(|_, _| { println!("unbind"); });
        item_factory.connect_teardown(|_, _| { println!("teardown"); });
        column_view.append_column(&ColumnViewColumn::builder().factory(&item_factory).build());
        ApplicationWindow::builder().application(application).child(&scrolled_window).build().present();
    });
    application.run();
}

How can I setup only items that are visible(or a bit more) or a hard coded amount of items to reduce the load time of my widget?

Progman
  • 16,827
  • 6
  • 33
  • 48
raven
  • 775
  • 6
  • 22

3 Answers3

1

The very short answer is : you can’t. I had the same problem trying to use a few heavy weight large items (Texteditor, WEbKit View). The widget is work in progress and you might need to create your own widget if you really think you need to. At least that is what I do.

Lothar
  • 12,537
  • 6
  • 72
  • 121
1

Controlling the amount of items setuped by the widget is not possible, this is decided based on several factors and not just the number of visible items. One thing you could try is splitting your model using a GtkSliceListModel, controlling the amount of items supplied to the widget and indirectly deciding how many items can be setuped by it each time.

Kripto
  • 46
  • 1
  • 3
0

I tried Kripto's suggestion of using GtkSliceListModel but still a lot of setting up and tearing down happens, so there's not much recycling going on; However you could reduce the initial items setup this way so the load time of the widget would be reduced.

const PAGE_SIZE: u32 = 40;
const TOTAL: i32 = 100;

fn main() {
    let application = Application::builder().application_id("column.view.test").build();
    application.connect_activate(|application| {
        let list_store = ListStore::new::<BoxedAnyObject>();
        let slice_list_model = SliceListModel::builder().model(&list_store).size(PAGE_SIZE).build();
        for i in 1..TOTAL {
            list_store.append(&BoxedAnyObject::new(i));
        }
        let column_view = ColumnView::builder().build();
        column_view.connect_realize({
            let slice_list_model = slice_list_model.clone();
            move |column_view| {
                column_view.vadjustment().unwrap().connect_value_changed({
                    let slice_list_model = slice_list_model.clone();
                    let last_value = Cell::new(0.0);
                    let value_change_because_of_offset_change = Cell::new(false);
                    move |adjustment| {
                        if value_change_because_of_offset_change.get() {
                            value_change_because_of_offset_change.set(false);
                        } else {
                            let value = adjustment.value();
                            let fraction = value / (adjustment.upper() - adjustment.page_size());
                            let delta = if fraction > 0.7 && last_value.get() < value {
                                10
                            } else if fraction < 0.3 && last_value.get() > value {
                                -10
                            } else { 0 };
                            last_value.set(value);
                            if delta != 0 {
                                value_change_because_of_offset_change.set(true);
                                slice_list_model.set_offset((slice_list_model.offset() as i32 + delta)
                                    .clamp(0, TOTAL - PAGE_SIZE as i32) as u32);
                            }
                        }
                    }
                });
            }
        });
        column_view.set_model(Some(&NoSelection::new(Some(slice_list_model))));
        let scrolled_window = ScrolledWindow::builder().child(&column_view).build();
        let item_factory = SignalListItemFactory::new();
        let setup_count = Cell::new(0);
        item_factory.connect_setup(move |_, item| {
            let list_item = item.downcast_ref::<ListItem>().unwrap();
            list_item.set_child(Some(&Label::new(None)));
            setup_count.set(setup_count.get() + 1);
            println!("setup {}", setup_count.get());
        });
        let bind_count = Cell::new(0);
        item_factory.connect_bind(move |_, item| {
            let list_item = item.downcast_ref::<ListItem>().unwrap();
            let string = list_item.item().and_downcast::<BoxedAnyObject>().unwrap().borrow::<i32>().to_string();
            list_item.child().and_downcast::<Label>().unwrap().set_label(&string);
            bind_count.set(bind_count.get() + 1);
            println!("bind {}", bind_count.get());
        });
        let unbind_count = Cell::new(0);
        item_factory.connect_unbind(move |_, _| {
            unbind_count.set(unbind_count.get() + 1);
            println!("unbind {}", unbind_count.get());
        });
        let teardown_count = Cell::new(0);
        item_factory.connect_teardown(move |_, _| {
            teardown_count.set(teardown_count.get() + 1);
            println!("teardown {}", teardown_count.get());
        });
        column_view.append_column(&ColumnViewColumn::builder().factory(&item_factory).build());
        ApplicationWindow::builder().application(application).child(&scrolled_window).build().present();
    });
    application.run();
}
raven
  • 775
  • 6
  • 22