fltk-rs / fl2rust

A fluid (fltk ui designer) file to Rust transpiler
MIT License
52 stars 3 forks source link

This is not an issue, but a help request. How can I pass a widget into a event handler twice? #6

Closed AllenDang closed 3 years ago

AllenDang commented 3 years ago

I indent to have a event handler which accept a reference of an Output widget, and use it to display some result log.

Idealy I could do it by:

  1. Clone a output from ui.output.
  2. Invoke on_execute by passin &mut output as parameter in every desired events.

Compiler told me this is not allowed, because the reference of &mut output could not be borrowed multiple times.

So I tried below code.

use fltk::{prelude::*, *};
use fltk_theme::{color_themes, ColorTheme};

mod mainview;

fn on_execute(url: String, o: &mut fltk::output::Output) {
    if url.len() == 0 {
        return;
    }

    let mut output = String::from(o.value());
    output.push_str(format!("{}\n", url).as_str());

    o.set_value(&output);
}

fn main() {
    let app = app::App::default().with_scheme(app::Scheme::Gtk);

    let theme = ColorTheme::from_colormap(color_themes::DARK_THEME);
    theme.apply();

    let mut ui = mainview::UserInterface::make_window();
    let mut output = ui.output.clone();
    let mut input_url = ui.input_url.clone();

    ui.win.center_screen();

    input_url.handle(move |i, evt| match evt {
        enums::Event::KeyUp => match app::event_key() {
            enums::Key::Enter => {
                on_execute(i.value(), &mut output);
                false
            }
            _ => false,
        },
        _ => false,
    });

    // I have to clone it again before invoke on_execute, is this right?
    output = ui.output.clone();

    ui.btn_download.set_callback(move |_| {
        on_execute(input_url.value(), &mut output);
    });

    app.run().unwrap();
}

It works, but I'm not sure this is correct.

I have to clone output widget each time before invoking on_execute, is this the right pattern? Or I'm on the wrong track?

Sorry to ask such a basic question, I already spent few hours on trying to understand closure and borrow, still not very clear about whether it's the right thing to do.

MoAlyousef commented 3 years ago

Yes it's ok. Widget clones are cheap. You can also scope them within the set_callback like so:

input_url.handle({
        let mut output = output.clone();
        move |i, evt| match evt {
            enums::Event::KeyUp => match app::event_key() {
                enums::Key::Enter => {
                    on_execute(i.value(), &mut output);
                    false
                }
                _ => false,
            },
            _ => false,
        }
    });

    ui.btn_download.set_callback({
        let mut output = output.clone();
        move |_| {
            on_execute(input_url.value(), &mut output);
        }
    });

There are also other event handling strategies like passing messages and defining your own Event types: https://github.com/fltk-rs/fltk-rs/wiki/Events