slint-ui / slint

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
https://slint.dev
Other
17.58k stars 604 forks source link

Add filter example to the gallery #3917

Open stolen-programmer opened 1 year ago

stolen-programmer commented 1 year ago

To show how to filter the columns of the StandardTableView an examples should be added to the TableViewPage of the gallery. The page could be extended with a LineEdit that allows to input text e.g. to filter the table columns by Header 1. To filter the columns a FilterModel can be used in main.rs and main.cpp. There is already a SortModel used to sort the columns.

originally description


import {StandardTableView} from "std-widgets.slint";

export component Win {
    VerticalLayout {

        filter := TextInput {
            height: 20px;
        }

        v := StandardTableView {
            // width: 230px;
            // height: 200px;
            columns: [
                { title: "Header 1" },
                { title: "Header 2" },
            ];
            rows: [
                [
                    { text: "Item 1" }, { text: "Item 2" },
                ],
                [
                    { text: "Item 1" }, { text: "Item 2" },
                ],
                [
                    { text: "Item 1" }, { text: "Item 2" },
                ]
            ];
        }

    }

}

I'm sorry, but if I missed anything, please let me know

ogoffart commented 1 year ago

You can filter that in native code with the FilterModel. Make a callback when the filter line is edited to update thebfilter. We should probably have an example with that (or a recipe in the docs)

InakiBF commented 6 months ago

Slint code:

import {VerticalBox, StandardTableView, GroupBox, LineEdit} from "std-widgets.slint";

export global TableViewPageAdapter  {
    in property <[[StandardListViewItem]]> row_data: 
    [

    ];
}

export global LineEditAdapter  {
    callback editing(string);
    in property <string> line_edit_text: "";
}

export component AppWindow inherits Window {
    title: "TableView";
    width: 1800px;
    height: 1000px;

    VerticalBox {
        horizontal-stretch: 1;
        LineEdit {
            edited(t) => {
                LineEditAdapter.editing(t);
            }
            placeholder-text: "Filter by name";
        }
        GroupBox {
            title: "Phone list";
            horizontal-stretch: 0;

            StandardTableView {
                 columns: [
                    { title: "Name" },
                    { title: "Phone" },
                ];
                rows: TableViewPageAdapter.row_data;
            }

        }   
    }
}

Rust code:

use slint::{Model, ModelExt, StandardListViewItem, VecModel};
use std::rc::Rc;

slint::include_modules!();

struct Phone {
    name: String,
    phone: String,
}

fn main() -> Result<(), slint::PlatformError> {
    let ui = AppWindow::new()?;

    let phones_vec: Vec<Phone> = vec![
        Phone {
            name: String::from("Bob"),
            phone: String::from("5555555"),
        },
        Phone {
            name: String::from("John"),
            phone: String::from("6666666"),
        },
        Phone {
            name: String::from("Peter"),
            phone: String::from("77777777"),
        },
        Phone {
            name: String::from("Mike"),
            phone: String::from("8888888888"),
        },
    ];

    let row_data: Rc<VecModel<slint::ModelRc<StandardListViewItem>>> = Rc::new(VecModel::default());

    for phone in phones_vec.iter() {
        let fields = Rc::new(VecModel::default());

        fields.push(slint::format!("{}", &phone.name).into());
        fields.push(slint::format!("{}", &phone.phone).into());

        row_data.push(fields.into());
    }

    ui.global::<TableViewPageAdapter>()
        .set_row_data(row_data.clone().into());

    ui.global::<LineEditAdapter>().on_editing({
            let ui_weak = ui.as_weak();

             move |t| {
                let row_data_filtered = Rc::new(row_data.clone().filter(move |row| {
                    // 0 is the first value of the row (name)
                    row.row_data(0).unwrap().text.to_lowercase().as_str().contains(t.to_lowercase().as_str())
                }));

                ui_weak.unwrap().global::<TableViewPageAdapter>().set_row_data(row_data_filtered.into());
            }
    });

    ui.run()
}
stolen-programmer commented 6 months ago

This is somewhat different from the filtering I imagined When there are a large number of data entries, performance may be worrying (although this is not common)

InakiBF commented 6 months ago

I am new to Rust and Slint and I responded because I was looking for a solution for a day (more examples are needed) and in the end I found the solution myself based on the ordering functions in the example. In my application I use an array of about 10,000 x 100 strings to store plant records and the filtering is immediate (I don't know if in really large arrays it can slow down too much). I also thought that the array clonings (necessary because of the particularities of Rust) would be expensive, but if I'm not mistaken only pointers are cloned.