FLTK input widget that won't work

I'm using fltk-rs for my gui. FLTK has a nifty input widget available, so I wrote a whole crate's worth of functions that have proven quite useful. In particular is a function named input_string() that does just that. It allows you to enter a short String. However, it has gotten sticky when I try to call it from a button callback. The widget just freezes up. No cursor shows and the widget won't receive any input. I've managed to re-create the issue with less code than my main project while trying to preserve a context that is similar to how I need to use it. The code is below. Any chance someone could tell me where I'm going wrong? Thanks.

use std::sync::Mutex;
use fltk::{frame, group, input, prelude::*, window};
use fltk::app::{set_font_size, App};
use fltk::{button::Button, enums::Color, window::Window};
use fltk::enums::CallbackTrigger;

fn main() {
    let app = App::default();

    // region  Setup the primary window.
    let mut win = Window::default().with_size(825, 900).with_pos(1000, 100);
    set_font_size(20);
    win.set_color(Color::Blue);
    win.set_label("Throwaway Title");
    win.make_resizable(true);

    win.end();
    win.show();
    // endregion

    // Proof that the  input_string()  function works.
    let txxxxxxt = input_string(&app, "Please enter a string.", 300, 90);
    println!("\n The string is:   {} \n", txxxxxxt);

    // region Create and call a two button window.
    let mut pop = popup(&app, &win);
    pop.make_modal(true);
    pop.set_color(Color::Red);

    pop.end();
    pop.show();
    //endregion

    app.run().unwrap();
}


fn popup(app: &App, win: &Window) -> Window {

    //region Calculate the position of the window.
    let popwidth = 575;
    let popheight = 100;

    let xxx = 1412 - popwidth / 2;
    let yyy = 550 - popheight / 2;
    //endregion

    //region Create the window and buttons.
    let popwin = Window::default().with_size(popwidth, popheight).with_pos(xxx, yyy);

    let mut but1 = Button::new(25, 25, 250, 40, "Fake 1");
    let mut but2 = Button::new(300, 25, 250, 40, "Fake 2");
    //endregion

    // region Do the button callbacks
    let mut winclone1 = popwin.clone();
    but1.set_callback(move |_| {
        fakefunc1();
        winclone1.hide();
    });

    let app_clone = app.clone();
    let mut winclone2 = popwin.clone();
    but2.set_callback(move |_| {
        fakefunc2(&app_clone);
        winclone2.hide();
    });
    // endregion

    popwin
}

fn fakefunc1() {
    // Called by first button.  Nothing special.
    // Proof that the buttons work.
    println!("\n The fake1 string is: asasdasdasdasds \n");
}

fn fakefunc2(app: &App) {
    // Calls the  input_string() function that isn't working.

    let somestring = input_string(&app, "Please enter a string.", 300, 90);
    println!("\n The new string is:   {} \n", somestring);
}

fn input_string(app: &App, prompt: &str, horiz: i32, vert: i32) -> String {

    // region Set up the input window and input frame
    let mut win = window::Window::default()
        .with_size(horiz, vert)
        .with_label("Input Window");
    win.make_resizable(true);

    let flex = group::Flex::default()
        .with_size(200, 75)
        .column()
        .center_of_parent();

    let _prompttext = frame::Frame::default().with_label(prompt);
    // endregion

    // region Set up the input widget inside the frame.
    let mut input_widget = input::Input::default();
    input_widget.set_trigger(CallbackTrigger::EnterKey);

                // Set the input widget's callback.
    let mut win2 = win.clone();
    input_widget.set_callback(move |_| {
        win2.hide();
    });

    flex.end();
    win.end();
    win.show();
    // endregion

    println!("\n WP 3:  input_string() setup finished. \n");  // For debug

    // region Deal with the input
    while win.shown() {
        app.wait();
    }

    println!("\n WP 4:  input_string() while loop finished. \n");  // For debug

    input_widget.value()
    // endregion
}

So, I'm still digging and simplified my code even more. With the code below, I create a button with a callback that calls a function that call my input_string() function. And...everything works fine. So, the question is what is the diff between the two versions that is causing the input widget to freeze? I haven't found it yet, so any help will be appreciated. Thanks. Here's the newly simplified code that works:

use fltk::{frame, group, input, prelude::*, window};
use fltk::app::{set_font_size, App};
use fltk::{button::Button, enums::Color, window::Window};
use fltk::enums::CallbackTrigger;

fn main() {
    let app = App::default();

    // Setup the primary window.
    let mut win = Window::default().with_size(825, 900).with_pos(1000, 100);
    set_font_size(20);
    win.set_color(Color::Blue);
    win.set_label("Throwaway Title");
    win.make_resizable(true);

    // Create the button
    let mut but2 = Button::new(300, 100, 250, 40, "Fake 2");

    win.end();
    win.show();

    // Add button callback
    let app_clone = app.clone();
    but2.set_callback(move |_| {
        fakefunc2(&app_clone);
    });

    app.run().unwrap();
}


fn fakefunc2(app: &App) {
    let somestring = input_string(&app, "Please enter a string.", 300, 90);
    println!("\n The new string is:   {} \n", somestring);
}

fn input_string(app: &App, prompt: &str, horiz: i32, vert: i32) -> String {

    // region Set up the input window and input frame
    let mut win = window::Window::default()
        .with_size(horiz, vert)
        .with_label("Input Window");
    win.make_resizable(true);

    let flex = group::Flex::default()
        .with_size(200, 75)
        .column()
        .center_of_parent();

    let _prompttext = frame::Frame::default().with_label(prompt);
    // endregion

    // region Set up the input widget inside the frame.
    let mut input_widget = input::Input::default();
    input_widget.set_trigger(CallbackTrigger::EnterKey);

                // Set the input widget's callback.
    let mut win2 = win.clone();
    input_widget.set_callback(move |_| {
        win2.hide();
    });

    flex.end();
    win.end();
    win.show();
    // endregion

    // region Deal with the input

    while win.shown() {
        app.wait();
    }

    input_widget.value()

    // endregion
}

Ok, so I took the super-simplified code from above (that works) and slowly added functionality. For instance, I can easily add a button to the primary window that calls the fakefunc2() function and everything works fine. I can add the secondary popup window pop using the popup() function (with the button calls removed) and all works. But, when I add the button and callback to the secondary window, my input_string() function fails. And......it should work! Below is my latest version that fails. Anybody see what I'm doing wrong?

use fltk::{frame, group, input, prelude::*, window};
use fltk::app::{set_font_size, App};
use fltk::{button::Button, enums::Color, window::Window};
use fltk::enums::CallbackTrigger;

fn main() {
    let app = App::default();

    // region  Setup the primary window.
    let mut win = Window::default().with_size(825, 900).with_pos(1000, 100);
    set_font_size(20);
    win.set_color(Color::Blue);
    win.set_label("Throwaway Title");
    win.make_resizable(true);

    win.end();
    win.show();
    // endregion

    // region Create and call a new window with the button
    let mut pop = popup(&app, &win);
    pop.make_modal(true);
    pop.set_color(Color::Red);

    pop.end();
    pop.show();
    // endregion

    app.run().unwrap();
}

fn popup(app: &App, win: &Window) -> Window {
    //region Calculate the position of the window.
    let popwidth = 575;
    let popheight = 100;

    let xxx = 1412 - popwidth / 2;
    let yyy = 550 - popheight / 2;
    //endregion

    //region Create the window and buttons.
    let popwin = Window::default().with_size(popwidth, popheight).with_pos(xxx, yyy);
    let mut but2 = Button::new(300, 25, 250, 40, "Fake 2");
    // endregion

    // region Do the button callback
    let app_clone = app.clone();
    let mut winclone2 = popwin.clone();
    but2.set_callback(move |_| {
        fakefunc2(&app_clone);
        winclone2.hide();
    });
    // endregion

    popwin
}


fn fakefunc2(app: &App) {
    // Calls the  input_string() function that isn't working.
    let somestring = input_string(&app, "Please enter a string.", 300, 90);
    println!("\n The new string is:   {} \n", somestring);
}

fn input_string(app: &App, prompt: &str, horiz: i32, vert: i32) -> String {
    // region Set up the input window and input frame
    let mut win = window::Window::default()
        .with_size(horiz, vert)
        .with_label("Input Window");
    win.make_resizable(true);

    let flex = group::Flex::default()
        .with_size(200, 75)
        .column()
        .center_of_parent();

    let _prompttext = frame::Frame::default().with_label(prompt);
    // endregion

    // region Set up the input widget inside the frame.
    let mut input_widget = input::Input::default();
    input_widget.set_trigger(CallbackTrigger::EnterKey);

                // Set the input widget's callback.
    let mut win2 = win.clone();
    input_widget.set_callback(move |_| {
        win2.hide();
    });

    flex.end();
    win.end();
    win.show();
    // endregion

    // region Deal with the input
    while win.shown() {
        app.wait();
    }

    input_widget.value()
    // endregion
}

The issue is with pop.make_modal(true);.
Modal windows will grab keyboard events by default. If you need your input window to grab keyboard events while the popup window is shown, then you need to remove that line.

1 Like

And...that fixed it! Funny, but I added that line as an attempt at fixing the problem. Oh, well! Thanks for your help! :nerd_face: :old_man:

1 Like