antoyo / relm

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

How does one use relm with a TextView? #284

Open john01dav opened 3 years ago

john01dav commented 3 years ago

I just went through the mini tutorial in the README.md, and I really like this library to provide structure to my GTK-rs applications, but I need to use a gtk::TextView and when I tried to make it work with the view! macro it failed.

Code:

pub struct Model{
    file_contents: String,
    // … more things
}

//in view!
gtk::TextView{
    text: {
        let text_buffer = TextBuffer::new(None);
        text_buffer.set_text(self.model.file_contents);
        Some(text_buffer)
    }
}

Error:

error: expected identifier
  --> src/main.rs:60:25
   |
60 |                         let text_buffer = TextBuffer::new(None);
   |                         ^^^

Versions of my dependencies:

[dependencies]
relm = "0.21.0"
relm-derive = "0.21.0"
gtk = "0.9.2"

MCVE (might have some other trivial errors and isn't complete (I wanted to make a trivial notepad clone to try out the library), but it demonstrates the issue):

use std::path::PathBuf;
use gtk::Orientation;
use relm_derive::{widget, Msg};
use relm::Widget;
use gtk::prelude::*;

#[derive(Msg)]
pub enum Msg{
    OpenButton,
    OpenFile,
    SaveButton
}

pub struct Model{
    path: Option<PathBuf>,
    file_contents: String
}

#[widget]
impl Widget for Win{

    fn model() -> Model{
        Model{
            path: None,
            file_contents: "".into()
        }
    }

    fn update(&mut self, event: Msg) {
        match event{
            Msg::OpenButton => {},
            Msg::OpenFile => {},
            Msg::SaveButton => {}
        }
    }

    view!{
        gtk::Window {
            gtk::Box {
                orientation: Orientation::Vertical,
                gtk::Box {
                    orientation: Orientation::Horizontal,
                    gtk::Button {
                        clicked => Msg::OpenButton,
                        label: "Open"
                    },
                    gtk::Button {
                        clicked => Msg::SaveButton,
                        label: "Save"
                    },
                    gtk::Label {
                        text: match &self.model.path {
                            Some(path) => &path.into_os_string().into_string().unwrap_or("Improper OS string in path".into()),
                            None => "File Not Saved".into()
                        }
                    }
                },
                gtk::TextView{
                    text: {
                        let text_buffer = TextBuffer::new(None);
                        text_buffer.set_text(self.model.file_contents);
                        Some(text_buffer)
                    }
                }
            }
        }
    }

}

fn main() {
    Win::run(()).unwrap();
}

I assume that this means that the view! macro doesn't support blocks, but given this, I don't see how I can use a gtk::TextView.

emmanueltouzery commented 3 years ago

What I do in this case is bind the widget, so in the view! macro:

#[name="myview"]
gtk::TextView {}

Then I manually set the contents when the widget is created and when it's updated.

self.widgets.myview.set_text(..);

For the creation, you can add to the widget a fn init_view(&mut self) {}, and updates in the fn update when events are received.

Hope that helps!

antoyo commented 3 years ago

Maybe you can create the buffer in the model() method and set it normally in the view!macro. But, as mentioned here, GTK+ models do not integrate with relm models yet, so you'll have to modify your buffer manually.