jschuss
jschuss

Reputation: 655

Gtk-rs pass window as value

New to Rust and I'm trying to build a simple Rust GUI using gtk-rs following the docs here. What I'm wondering is whether it's possible to pass around the window that gets built in the build_ui function, so that I can update it from other operations in my code. The following code lives inside of a function that does other stuff too, further down in the function I would like to access the window so that I can add a new child widget (or something like that). Is this possible? If so how can I go about it? Unfortunately none of the examples in the project cover something like this.

    fn main() {

       let app = Application::builder()
          .application_id("org.gtk-rs.example")
          .build();

       app.connect_activate(build_ui);

       let mut win: ApplicationWindow; <---- I want to store the window here to use later

       fn build_ui(app: &Application) {
           // Create a window and set the title
           let window = ApplicationWindow::builder()
               .application(app)
               .title("My GTK App")
               .build();
    
           win = window; <---- 
       }

      // do other UI bootstrapping stuff

      // when ready to mount the window
      window.present();

    }

Would appreciate any help, thanks

Upvotes: 0

Views: 510

Answers (1)

rodrigo
rodrigo

Reputation: 98348

What I do for Gtk-rs is as follows:

    struct Context {
        wnd: Option<ApplicationWindow>,
        //... other values associated to that window
    }
    let ctx = Context {
        wnd: None,
        //...
    };
    let ctx: Rc<RefCell<Context>> = Rc::new(RefCell::new(ctx));
    app.connect_activate(clone!(
        @strong ctx =>
        move |app| {
            let mut ctx = ctx.borrow_mut();
            let window = ApplicationWindow::builder()
               .application(app)
               .title("My GTK App")
               .build();
    
            ctx.wnd = Some(window);
        }
    ));

And then most of the connected signals clone the ctx in a kind of clone-chain.

The details vary depending on your type of application:

  • If your application has only one main window, it is easier to create it when creating the new context instead of inside the activate signal. That way your wnd: Option<ApplicationWindow> turns into a wnd: ApplicationWindow that is easier to manage.
  • If your application can have many main windows, then you will need to handle a list of some kind:
    struct Context {
       wnds: Vec<WindowContext>,
       //global state
    }
    struct WindowContext {
       wnd: ApplicationWindow,
       //local state
    }

Just remember that calling borrow_mut in a RefCell gives you an exclusive borrow. If you try to borrow twice and one of them is exclusive it will panic. So if some function raises a signal that calls another callback that borrows your global ctx then you will have to do some dancing.

    my_thing.connect_signal(clone!(
        @strong ctx =>
        move |_| {
            let ctx_ = ctx.borrow_mut();
            ctx_.do_something_that_that_does_not_borrow();
            drop(ctx_); //<-- release the borrow!
            do_something_else_that_may_borrow();
            //or also:
            let w = ctx.borrow().wnd.clone();
            do_something_else_that_may_borrow_with_window(w);
        }
    ));

Upvotes: 2

Related Questions