antoyo / relm

Idiomatic, GTK+-based, GUI library, inspired by Elm, written in Rust
MIT License
2.43k stars 78 forks source link

Communication between parent and children in widget-list example #174

Closed Miav771 closed 4 years ago

Miav771 commented 5 years ago

I have this code based on the widget-list example, except it uses a ListBox and a custom HeaderBar.

pub struct CounterModel {
    counter: i32,
}

#[derive(Msg)]
pub enum CounterMsg {
    Decrement,
    Increment,
}

#[widget]
impl Widget for Counter {
    fn model() -> CounterModel {
        CounterModel { counter: 0 }
    }

    fn update(&mut self, event: CounterMsg) {
        match event {
            Decrement => self.model.counter -= 1,
            Increment => self.model.counter += 1,
        }
    }

    view! {
        gtk::ListBoxRow{
                gtk::Box {
                orientation: Horizontal,
                gtk::Button {
                    label: "+",
                    name: "inc_button",
                    clicked => Increment,
                },
                gtk::Label {
                    label: "0",
                    name: "label",
                    text: &self.model.counter.to_string(),
                },
                gtk::Button {
                    label: "-",
                    clicked => Decrement,
                },
                gtk::Button {
                    label: "Remove",
                    //Remove this widget from the ListBox and destroy it
                    clicked => unimplemented!(),
                }
            },
        },
    }
}

#[derive(Msg)]
pub enum Msg {
    Add,
    Quit,
    Remove,
}

pub struct Model {
    counters: Vec<Component<Counter>>,
}

#[widget]
impl Widget for Win {
    fn model() -> Model {
        Model { counters: vec![] }
    }

    fn init_view(&mut self) {
        self.window.set_titlebar(&self.titlebar);
    }

    fn update(&mut self, event: Msg) {
        match event {
            Add => {
                let widget = self.lbox.add_widget::<Counter>(());
                self.model.counters.push(widget);
            }
            Quit => gtk::main_quit(),
            Remove => {
                if let Some(counter) = self.model.counters.pop() {
                    self.lbox.remove_widget(counter);
                }
            }
        }
    }

    view! {
        #[name="window"]
        gtk::Window {
            gtk::ScrolledWindow{
                propagate_natural_height: true,
                min_content_height: 400,
                min_content_width: 200,
                #[name="lbox"]
                gtk::ListBox {
                    selection_mode: gtk::SelectionMode::None,
                    activate_on_single_click: true,
                    //Increment the counter that was activated
                    row_activated(_,_) => !unimplemented!(),
                },
            },            
            #[name="titlebar"]
            gtk::HeaderBar{
                title: "Title",
                show_close_button: true,
                #[name="add_button"]
                gtk::Button {
                    label: "Add",
                    clicked => Add,
                },
                #[name="remove_button"]
                gtk::Button {
                    label: "Remove",
                    clicked => Remove,
                },
            },
            delete_event(_, _) => (Quit, Inhibit(false)),
        }
    }
}

fn main() {
    Win::run(()).expect("Win::run failed");
}

I want to make activating (clicking on) a row of theListBox send a message to the ListBoxRow that was activated and increment its Counter.

I also want to add a new button to the Counter named Remove. I want clicking on this button to send a message to ListBox to remove this specific counter.

There are examples and tests that show how to achieve similar effect with a fixed amount of children but none that explain how to do it with a dynamically changing children located in a Vec in the Model.

antoyo commented 5 years ago

Sorry, I completely forgot about this issue. I don't have much time to look at that right now, but one idea would be to store the components in a vector and somehow convert the row index to the vector index in order to send the message to the right widget.

antoyo commented 4 years ago

Have you been able to try my idea? Or do you still need help?

antoyo commented 4 years ago

Please reopen this issue if you still need help on it.