For my main project, I found myself needing a small, no-frills editor that would let the user enter some text that would then be stored in one field of a struct. The FLTK GUI crate had what I needed and it really wasn't very hard to get a small basics-only text editor built. Here's what I came up with and it works just fine (I'll get to the real problem in a moment):
fn main() {
let returned_text = quick_editor("Passing this on to the function, \n", "Quick Editor");
println!("The returned text is: \n\n {} \n", returned_text);
}
pub fn quick_editor(startertext: &str, win_label: &str) -> String {
let qcktxtapp = app::App::default();
let mut buf = text::TextBuffer::default();
let mut win = window::Window::default().with_size(800, 300);
win.set_color(Color::Blue);
win.set_label(win_label);
buf.set_text(startertext);
let mut txt = text::TextEditor::default().with_size(790, 290)
.center_of_parent();
txt.set_buffer(buf.clone()); // Clone is used here to avoid an ownership error.
txt.wrap_mode(text::WrapMode::AtBounds, 0);
txt.set_color(Color::White);
txt.set_text_size(22);
txt.set_text_color(Color::Black);
win.end();
win.show();
qcktxtapp.run().unwrap();
buf.text() // Returns the text that was entered.
}
As I said, this works fine, but when I call the quick_editor() function from my project, the quick_editor window doesn't close. It still returns the entered text, but the window simply doesn't close. Here's the code that calls quick_editor():
I can't see how I'm doing anything different, but when I call quick_editor() from the main program, the editor window it calls up simply won't close. I have to ctrl-c the terminal window to get rid of it.
Are you setting a callback for the FLTK window, or do you handle events thru the Window::handle method? When hitting the close button, it calls the window's callback. So users might inadvertently override what happens when the close button is clicked.
Are you instantiating the window from a different thread than the main thread? The FLTK window is a native window (i.e HWND on windows, NSWindow on macos, XWindow or wayland window on linux etc). These might have different behaviors when instantiated from a thread other than the main thread.
Well, I honestly don't know how to answer that. This is my first foray into using FLTK seriously. Terms like "callback" and "Window::handle" are unfamiliar to me. I'm using PopOS Linux and at this time the main application is completely terminal-based. I put together this little mini-editor in FLTK to solve a problem. The rest of the program is still terminal-based. (I did find that FLTK is easy to work with, so likely will eventually be moving the app over to that platform.)
Yes. The window is called from a menu that is itself called by main(). I suspect this is where the error is.
Hmmm.....I may be misunderstanding your question. This app is not multi-threaded. There is only one thread, if that is what you mean.
No. No TUI. I do use the dialoguer crate in the menu itself, but it just returns a usize that I then use in a match statement. The match statement then calls a function that then calls the FLTK window. Like this:
match choice {
1 => {
let mut qst1 = Question::new();
qst_enter_edit_content(&mut qst1); // This function calls the function
// that sets up and opens the FLTK window.
// More content.
}
pub fn qst_enter_edit_content(question: &mut Question) {
let nowtext = quick_editor(question.qtext.as_str(), "Qbank Question Editor"); // Opens the FLTK window.
question.qtext = nowtext;
}
The quick_editor() function (copied above in my first post) calls the FLTK window that isn't closing as expected.
Dialoguer uses console, a tui crate. However it seems to work correctly with FLTK. I tried the following example:
use dialoguer::{theme::ColorfulTheme, Select};
use fltk::{prelude::*, enums::*, *};
fn main() {
let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Pick your input")
.default(0)
.items(&[
"Quick Editor",
"Console",
])
.interact()
.unwrap();
match selection {
0 => {
let returned_text = quick_editor("Passing this on to the function, \n", "Quick Editor");
println!("The returned text is: \n\n {} \n", returned_text);
}
1 => {
let returned_text = console_editor();
println!("The returned text is: \n\n {} \n", returned_text);
}
_ => unreachable!(),
}
}
pub fn quick_editor(startertext: &str, win_label: &str) -> String {
let qcktxtapp = app::App::default();
let mut buf = text::TextBuffer::default();
let mut win = window::Window::default().with_size(800, 300);
win.set_color(Color::Blue);
win.set_label(win_label);
buf.set_text(startertext);
let mut txt = text::TextEditor::default().with_size(790, 290)
.center_of_parent();
txt.set_buffer(buf.clone()); // Clone is used here to avoid an ownership error.
txt.wrap_mode(text::WrapMode::AtBounds, 0);
txt.set_color(Color::White);
txt.set_text_size(22);
txt.set_text_color(Color::Black);
win.end();
win.show();
qcktxtapp.run().unwrap();
buf.text() // Returns the text that was entered.
}
pub fn console_editor() -> String {
let mut line = String::new();
std::io::stdin().read_line(&mut line).ok();
line
}
The issue might lie elsewhere.
Since I can't repro, you might try calling win.hide(); before buf.text() in the quick_editor() function. Alternatively, you can try fltk::app::quit(); after getting the returned_text.
It might be a far stretch, but you might try with another terminal emulator as well.
I've only run into a similar behavior when trying to create a systray menu on windows in an fltk app.
Usually in such circumstances, you would need to reconcile 2 event loops.
Well, neither of those suggestions worked. Tried win_hide() and fltk::app::quit(); separately and then together, and the problem continues. I need to set up a better way to deal with quitting the editor window (right now I'm just clicking the 'x' in the upper right corner of the window. I've been kind of sloppy about this because this is just an intermediate goal. I've been working to get a "prototype" of my project working in terminal-only code with the goal of letting that then guide me into bringing the project into giving the program a better, more efficient structure. That will likely be the best time to bring in a GUI interface, too. I brought this little FLTK editor in now because it was the only solution I could find that would let me continue working on getting that "prototype" up and working, but this snag is getting in the way.
I suspect the problem is still with dialoguer and console, even if you couldn't reproduce it. I just did a quick run-through of all my use imports and Cargo.toml dependencies to see if there might be anything else getting in the way. The non-standard crates I'm using are:
serde, num_traits, dialoguer, console.
That's basically it. There are some small line-editors out there that use crossterm and ratatui, but I really need more than a line editor. I even have a full-on editor (that I got here) that would do the job in a massively overkill kind of way. I need a small editor that lets me work with paragraphs and has word-wrap, which is why I chose to try this one from FLTK.
And now I'm rambling on with a lot of whining, so I think I'll quit for now. Thank you, @MoAlyousef for putting so much effort into trying to help me solve this problem. I do appreciate it.