Skip to content
This repository has been archived by the owner on Jun 8, 2021. It is now read-only.

Struggling with accessing the same widget from multiple connect calls #111

Closed
anfractuosity opened this issue Jul 2, 2015 · 13 comments
Closed

Comments

@anfractuosity
Copy link

I'm struggling with the following code I've written

        let libraryview : Viewport = builder.get_object("libraryview").unwrap();
        let scroll : ScrolledWindow = builder.get_object("mainscroll").unwrap();

        librarylbl.connect_button_press_event(move |_,_,| {
            scroll.remove(&libraryview);
            Inhibit(true)
        }); 

        newrecipelbl.connect_button_press_event(move|_,_| {
            scroll.remove(&libraryview);
                Inhibit(true)
         });

Which results in a borrow error for scroll and libraryview, whereas it looks like you could do something similar in rgtk, using &mut || { } closures as far as I can tell?

@GuillaumeGomez
Copy link
Member

You have to use Rc<RefCell<T>> objects to modify objects within the closures. For example:

let libraryview : Rc<RefCell<Viewport>> = Rc::new(RefCell::new(builder.get_object("libraryview").unwrap()));
let scroll : Rc<RefCell<ScrolledWindow>> = Rc::new(RefCell::new(builder.get_object("mainscroll").unwrap()));

let b_libraryview = libraryview.clone();
let b_libraryview2 = libraryview.clone();
let b_scroll = scroll.clone();
let b_scroll2 = scroll.clone();
librarylbl.connect_button_press_event(move |_,_,| {
    (*b_scroll.borrow_mut()).remove(b_libraryview.borrow());
    Inhibit(true)
}); 

newrecipelbl.connect_button_press_event(move|_,_| {
    (*b_scroll2.borrow_mut()).remove(b_libraryview2.borrow());
    Inhibit(true)
});

(Or nearby).

@gkoz
Copy link
Member

gkoz commented Jul 2, 2015

@GuillaumeGomez You don't have to do that for refcounted types (particularly, any widgets) because they already are a kind of Rc<RefCell<X>> underneath. You just need to clone them.

I'd also look into cloning the closure as well. This might work:

        let libraryview : Viewport = builder.get_object("libraryview").unwrap();
        let scroll : ScrolledWindow = builder.get_object("mainscroll").unwrap();

        let env = (scroll.clone(), library_view.clone());
        let handler = move |_,_| {
            let (ref scroll, ref library_view) = env;
            scroll.remove(libraryview);
            Inhibit(true)
        };

        librarylbl.connect_button_press_event(handler.clone()); 
        newrecipelbl.connect_button_press_event(handler);

@GuillaumeGomez
Copy link
Member

I didn't pay attention. Thanks for correcting me @gkoz.

@anfractuosity
Copy link
Author

When trying to clone the closure I got

 error: type `[closure src/hymntoninkasi.rs:92:22: 95:4]` does not implement any method in scope named `clone`

I'm wondering, is cloning each widget the only way to do this? As i'm going to use a number of
.connect_button_press_event's with closures accessing the same widgets, so the code will get quite verbose, if I have to clone them all.

@gkoz
Copy link
Member

gkoz commented Jul 2, 2015

When trying to clone the closure I got

I was mistaken then, you can't clone a closure :(
You could make a wrapper around Fn that would hide some boilerplate, but implementing Fn is impossible in stable Rust.

I'm wondering, is cloning each widget the only way to do this?

Currently closures have unbounded ('static) lifetimes so they need to own the variables they close over -- that's why you need to move the variables into them and can't capture references. The lack of a clone variant of move might be a shortcoming in Rust that needs to be addressed.

One can always try to design some clever macros ;)

@gkoz
Copy link
Member

gkoz commented Jul 2, 2015

Here's a macro that will clone the variables for you: http://is.gd/xQlEHR
It'd be nice to integrate the macro with closures a little more but I'm not sure how to do that yet.

@anfractuosity
Copy link
Author

Cheers! Really appreciate your help

@anfractuosity
Copy link
Author

I'm just having a little problem with this macro,

I can do https://play.rust-lang.org/?gist=7ad1859c73cd05a19e2c&version=stable which works fine.

But the following

macro_rules! clone {
    ($($n:ident),+ ; $b:expr) => (
            {
            $(let $n = $n.clone();)+
                    $b
            }
        );
}

librarylbl.connect_button_press_event(clone!(scroll,libraryview,newrecipeview;move |_,_|{
    Inhibit(true)
}));    

newrecipelbl.connect_button_press_event(clone!(scroll,libraryview,newrecipeview;move |_,_|{
    Inhibit(true)
}));    

Results in:

Global is external, but doesn't have external or weak linkage!
i8 (%closure.6*, i128, %"17.gdk::events::EventButton"*)* @_ZN7example4main12closure.4542E
invalid linkage type for function declaration
i8 (%closure.6*, i128, %"17.gdk::events::EventButton"*)* @_ZN7example4main12closure.4542E
Global is external, but doesn't have external or weak linkage!
i8 (%closure.7*, i128, %"17.gdk::events::EventButton"*)* @_ZN7example4main12closure.4559E
invalid linkage type for function declaration
i8 (%closure.7*, i128, %"17.gdk::events::EventButton"*)* @_ZN7example4main12closure.4559E
LLVM ERROR: Broken module found, compilation aborted!

@gkoz
Copy link
Member

gkoz commented Jul 2, 2015

Interesting! Does Rust nightly produce the same error?

@anfractuosity
Copy link
Author

Just about to try that :)

Edit: Yup, it's fixed in nightly, and compiles fine

@anfractuosity
Copy link
Author

This is slightly off-topic but I'm just wondering I'm doing the following:

        librarylbl.connect_button_press_event(clone!(scroll,libraryview,newrecipeview;move |_,_|{
            scroll.remove(&libraryview);
            scroll.remove(&newrecipeview);
            libraryview.reparent(&scroll);
            Inhibit(true)
        }));    

        newrecipelbl.connect_button_press_event(clone!(scroll,libraryview,newrecipeview;move |_,_|{
            scroll.remove(&libraryview);
            scroll.remove(&newrecipeview);
            newrecipeview.reparent(&scroll);
            Inhibit(true)
        }));    

So when I click the 'newrecipelbl' I get the newrecipeview shown in the GtkScrolledWindow, and when I click 'librarylbl' I get the libraryview shown in the GtkScrolledWindow.

This works, if i click each of these buttons once, but as soon as I press one more than once.

I get

(hymntoninkasi:27267): Gtk-CRITICAL **: gtk_widget_reparent: assertion 'priv->parent != NULL' failed

Which seems odd, as I've removed the elements from the scrolled window, as far as I can tell.

@gkoz
Copy link
Member

gkoz commented Jul 2, 2015

I'll probably need a full working sample to play with.

@anfractuosity
Copy link
Author

I managed to solve it through re-parenting the view port to an offscreen window :)

    librarylbl.connect_button_press_event(clone!(scroll,libraryview,newrecipeview;move |_,_|{
            newrecipeview.reparent(&rwindow);
            libraryview.reparent(&scroll);
            Inhibit(true)
    }));    

    newrecipelbl.connect_button_press_event(clone!(scroll,libraryview,newrecipeview;move |_,_|{
            libraryview.reparent(&lwindow);
            newrecipeview.reparent(&scroll);
            Inhibit(true)
    }));        

@gkoz gkoz closed this as completed Oct 15, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants