Reputation: 161
I have an application in which I have a Log in screen. There is a button for logging in. When pressed, the GtkButton
should be set to insensitive to give the user feedback.
After clicking the button, a login request is done via CURL. Because this is done synchronously, the button is not redrawn (and therefore will not (yet) appear insensitive) before sending the CURL request.
Since this CURL request could take up to 5 seconds, I want the user to get feedback that the button is clicked and cannot be clicked again.
How can I make the button insensitive after clicking without having to wait for it to be redrawn? Or is there an other approach I am not seeing?
static void on_login_pressed(GtkWidget *widget, GdkEvent *event, gpointer data)
{
gtk_widget_set_sensitive(GTK_WIDGET(login_cred_screen->login_button), FALSE);
//gtk_widget_queue_draw(GTK_WIDGET(login_cred_screen->login_button)); //have tried this, does not work right away
//this is where I need the button to be redrawn
g_signal_emit_by_name(login_cred_screen->login_button, "login-button-clicked"); //after this is where the CURL request is done
}
Upvotes: 2
Views: 219
Reputation: 2743
Although @BobMorane's is technically correct, the only proper way to solve this problem is to make the cURL request asynchronous (by running it in a different thread for example). The nature of having a UI thread implicitly requires you to handle blocking work in a different thread since the UI will block as well -unless you jump through hoops with unexpected consequences.
Upvotes: 3
Reputation: 4306
You have to force GTK to run the event loop and clear all pending events, one of which is a redraw triggered by gtk_widget_set_sensitive
that didn't have time, yet, to be handled. Here is a complete working example using GTK3:
#include <unistd.h> // sleep()
#include <gtk/gtk.h>
void simulate_long_curl_request(GtkWidget *widget)
{
sleep(5);
gtk_widget_set_sensitive(widget, true);
}
static void on_login_pressed(GtkWidget *loginButton, GdkEvent *event, gpointer data)
{
gtk_widget_set_sensitive(loginButton, false);
// Force GTK to treat pending events in the event loop
// before moving on. This includes your `set_sensitive`
// call the line before...
while(gtk_events_pending())
{
gtk_main_iteration();
}
simulate_long_curl_request(loginButton);
}
void on_activate(GtkApplication *application, gpointer user_data)
{
// Setup application window:
GtkWidget *loginWindow;
loginWindow = gtk_application_window_new(application);
gtk_window_set_title(GTK_WINDOW(loginWindow), "Login window");
gtk_window_set_default_size(GTK_WINDOW(loginWindow), 200, 200);
// Setup login button:
GtkWidget *loginButton;
loginButton = gtk_button_new_with_label("Login");
g_signal_connect(loginButton, "clicked", G_CALLBACK(on_login_pressed), NULL);
// Add button to window:
gtk_container_add(GTK_CONTAINER(loginWindow), loginButton);
// Run application:
gtk_widget_show_all(loginWindow);
}
int main(int argc, char **argv)
{
// Setup application:
GtkApplication *application;
application = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect(application, "activate", G_CALLBACK(on_activate), NULL);
const int status = g_application_run(G_APPLICATION(application), argc, argv);
g_object_unref(application);
return status;
}
You can read about the main loop here. You mentioned how gtk_widget_queue_draw
did not work. The description of the function says (my emphasis):
Invalidates the area of widget defined by region by calling
gdk_window_invalidate_region()
on the widget’s window and all its child windows. Once the main loop becomes idle (after the current batch of events has been processed, roughly), the window will receive expose events for the union of all regions that have been invalidated.
So the function does not really redraw, it only sends a request to the main loop to redraw once it can. The call to gtk_widget_set_sensitive
most probably makes a call, internally, to gtk_widget_queue_draw
.
Be careful with this solution, though, as the following lines:
while(gtk_events_pending())
{
gtk_main_iteration();
}
will force-handle all events, including keyboard events and so on...
Upvotes: 2