0

I am trying to write a small app in gtk-rs that takes an input and checks if it is correct against a value and displays an error if it is not. I am struggling with handling the code to display the error.

I have seen a number of questions covering similar subjects however I have not been able to successfully implement any of the suggestions.

There is also this thread that suggests using glib::MainContext::default() however I am already using that and I am not sure what they are doing differently to me.

A working example of my problem is below:

use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button, Orientation};
use std::time::Duration;
use std::sync::{Arc, Mutex};

use glib;
use fragile;

const APP_ID: &str = "org.my_gtk_app.app";

fn main() {
    let app = Application::builder().application_id(APP_ID).build();
    app.connect_activate(build_ui);
    app.run();
}

pub fn build_ui(app: &Application) {
    let button = Button::builder()
        .label("Submit")
        .build();

    let input = gtk::Entry::builder()
        .placeholder_text("input")
        .build();

    let failed_attempt = gtk::Label::new(Some("Attept failed."));
    failed_attempt.set_visible(false);
    failed_attempt.add_css_class("failed_login");
    let failed_clone = fragile::Fragile::new(failed_attempt.clone());

    let this_box = gtk::Box::builder()
        .orientation(Orientation::Vertical)
        .build();
    this_box.append(&input);
    this_box.append(&failed_attempt);
    this_box.append(&button);

    let guess       = Arc::new(Mutex::new(String::new()));
    let guess_clone = Arc::clone(&guess);
    
    let window = ApplicationWindow::builder()
        .application(app)
        .title("my_gtk_app")
        .child(&this_box)
        .build(); 

    window.show();
    
    let window_clone = fragile::Fragile::new(window.clone());

    std::thread::spawn(move || loop {
        //let failed_clone = fragile::Fragile::new(failed_attempt.clone()); // Moving this inside the loop causes a *mut c_void` cannot be shared between threads safely error
        std::thread::sleep(Duration::from_millis(100));
        if guess_clone.lock().unwrap().to_string() == String::from("Guess!") {
            glib::MainContext::default().invoke(move || {
                window_clone.get().maximize();
            });
            break; 
        }
        else if guess_clone.lock().unwrap().to_string().len() > 0 {
            glib::MainContext::default().invoke(move || {
                failed_clone.get().set_visible(true);
            });
            //break; //Adding this break in causes the program to run successfully but only for one iteration.
        }
    });
}

The answer I have seen most commonly in other questions is to move the assignment of the variable to be worked on inside the loop however this is causing a separate error.

I have also tried wrapping an if statement around the glib::MainContext::default() in the else block to ensure it can only run once however that did not work either.

What I need to know is how do I conditionally make a GtK::Label visible based on user input? If there is a completely different method than what I am trying to achieve here that would be easier than rectifying what I have or a fix for this example code that would be great.

John554
  • 145
  • 1
  • 7
  • Try removing the `move` from that `else if ...` branch. – cadolphs Jan 17 '23 at 04:27
  • And from that other branch too – cadolphs Jan 17 '23 at 04:27
  • TL;DR when you create the thread, the `move` makes sense because we want the thread to take ownership of the stuff it operates on. But then inside the thread, when you do that `invoke` stuff, I don't think the closure should take ownership; instead, it should just borrow. If you don't add `move`, the compiler will try to do the "least committing" thing. First `&` if possible, then `&mut` if necessary, finally `move`. – cadolphs Jan 17 '23 at 04:29
  • When I remove the `move` I get an error stating that guess_clone is borrowed and the closure may outlive the current function. `closure may outlive the current function, but it borrows `guess_clone`, which is owned by the current function` – John554 Jan 17 '23 at 14:50
  • My bad; based on the `invoke` I thought the closure was going to be executed "right away". – cadolphs Jan 18 '23 at 04:25

1 Answers1

0

Removing the thread and using a connect-clicked enclosure solved the issue.

button.connect_clicked(move |_| {
        let mut guess = guess.lock().unwrap();
        *guess = String::from(input.text());
        input.set_text("");
        if *guess == String::from("Guess!") {
            window_clone.get().maximize();
        }
        else {
            failed_attempt.set_visible(true);
        }
    });
John554
  • 145
  • 1
  • 7