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!

1 Like