-
-
Save Misko-2083/3288b448edd6c14ede71a8e1c0aed8b1 to your computer and use it in GitHub Desktop.
GtkProgressbar with an animated image
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <gtk/gtk.h> | |
#include <gdk/gdk.h> | |
#include <glib.h> | |
#define ANIMATION "hhh.gif" | |
/* Meep animated progressbar - in honor of hhh | |
gcc -Wall -Wextra -o meep-bar meep-bar.c `pkg-config --cflags --libs gtk+-3.0` | |
Code: Miloš Pavlović 2021 | |
depends: gcc libgtk-3-dev (3.24) | |
*/ | |
guint threadID = 0; | |
typedef struct { | |
GtkWidget *progress_bar; | |
GtkWidget *button1; | |
GtkWidget *progress_animation; | |
GtkWidget *progress_label; | |
GtkStyleProvider *progressbar_style_provider; | |
} app_widgets; | |
void destroy_handler (GtkApplication* app, gpointer data) | |
{ | |
(void) app; | |
g_application_quit(G_APPLICATION (data)); | |
} | |
void | |
stop_progress_cb (gpointer user_data) | |
{ | |
app_widgets *widgets = (app_widgets *) user_data; | |
gdouble fraction; | |
fraction = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR(widgets->progress_bar)); | |
g_print("Meep progress: %.0f %%\n", fraction*100); | |
} | |
static gboolean | |
fill (gpointer user_data) | |
{ | |
app_widgets *widgets = (app_widgets *) user_data; | |
GtkAllocation *alloc = g_new(GtkAllocation, 1); | |
gdouble fraction; | |
/*Get the current progress*/ | |
fraction = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR(widgets->progress_bar)); | |
if (fraction > 0.99) { | |
fraction = 0; | |
} | |
/*Increase the bar by 10% each time this function is called*/ | |
if (fraction < 0.99) | |
fraction += 0.1; | |
gtk_widget_get_allocation (widgets->progress_bar, alloc); | |
gtk_widget_set_margin_start(widgets->progress_animation, | |
alloc->width*fraction); | |
/*Fill in the bar with the new fraction*/ | |
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widgets->progress_bar), | |
fraction); | |
gchar temp[256]; | |
memset(&temp, 0x0, 256); | |
if (fraction > 0.99) { | |
snprintf(temp, 255, "Complete: %.0f %%", fraction*100); | |
gtk_button_set_label (GTK_BUTTON(widgets->button1), | |
"Repeat"); | |
threadID = 0; | |
} | |
else { | |
snprintf(temp, 255, "Working: %.0f %%", fraction*100); | |
} | |
gtk_label_set_text (GTK_LABEL(widgets->progress_label), temp); | |
g_free(alloc); | |
/*Ensures that the fraction stays below 1.0*/ | |
if (fraction < 0.99) | |
return TRUE; | |
return FALSE; | |
} | |
void | |
button1_clicked_cb (GtkButton *button, | |
app_widgets *widgets) | |
{ | |
if (threadID == 0) | |
{ | |
threadID = g_timeout_add_full (0, 500, fill, | |
widgets, stop_progress_cb); | |
gtk_button_set_label (button, | |
"\xe2\x8f\xb8"); | |
} | |
else | |
{ | |
GSource *source = g_main_context_find_source_by_id(NULL, | |
threadID); | |
if (source) | |
{ | |
g_source_destroy (source); | |
} | |
threadID = 0; | |
gtk_button_set_label (button, | |
"\xe2\x8f\xaf"); | |
} | |
} | |
static void | |
progress_bar_size_allocate (GtkWidget *progress_bar, | |
GdkRectangle *allocation, | |
gpointer user_data) | |
{ | |
(void) progress_bar; | |
app_widgets *widgets = (app_widgets *) user_data; | |
gdouble fraction; | |
/*Get the current progress*/ | |
fraction = gtk_progress_bar_get_fraction | |
(GTK_PROGRESS_BAR(widgets->progress_bar)); | |
/*Set the margin of animation when the window width changes*/ | |
gtk_widget_set_margin_start(widgets->progress_animation, | |
allocation->width*fraction); | |
} | |
static void | |
progress_animation_size_allocate (GtkWidget *animation, | |
GdkRectangle *allocation, | |
gpointer user_data) | |
{ | |
(void) animation; | |
app_widgets *widgets = (app_widgets *) user_data; | |
GtkAllocation *progress_bar_allocation = g_new(GtkAllocation, 1); | |
char *css_text; | |
/*Get progress bar allocation*/ | |
gtk_widget_get_allocation (widgets->progress_bar, | |
progress_bar_allocation); | |
/*The height of the progress bar | |
has to be bigger than the height of an image*/ | |
if ((progress_bar_allocation->height = allocation->height) || | |
(progress_bar_allocation->height < allocation->height)) | |
{ | |
css_text = g_strdup_printf ("progressbar trough,\n" | |
"progressbar progress\n" | |
"{\n" | |
" min-height: %dpx;\n" | |
"}\n", | |
allocation->height); | |
gtk_css_provider_load_from_data (GTK_CSS_PROVIDER | |
(widgets->progressbar_style_provider), | |
css_text, -1, NULL); | |
} | |
g_free(progress_bar_allocation); | |
g_free (css_text); | |
} | |
static void | |
activate (GtkApplication *app, | |
gpointer user_data) | |
{ | |
(void) user_data; | |
GtkSizeGroup *size_group; | |
GtkWidget *window; | |
GtkWidget *grid; | |
GtkWidget *button2; | |
GtkWidget *progress_overlay; | |
GdkPixbufAnimation *animation; | |
GError *error = NULL; | |
GtkWidget *box; | |
app_widgets *widgets = g_slice_new(app_widgets); | |
gdouble fraction = 0.0; | |
/*Create a window with a title, and a default size*/ | |
window = gtk_application_window_new (app); | |
gtk_window_set_title (GTK_WINDOW (window), | |
"Gtk Progress Bar"); | |
gtk_window_set_default_size (GTK_WINDOW (window), | |
420, 60); | |
/*Create a grid container to store the widgets*/ | |
grid = gtk_grid_new (); | |
gtk_grid_set_row_spacing (GTK_GRID (grid), | |
10); | |
gtk_grid_set_column_spacing (GTK_GRID (grid), | |
10); | |
gtk_grid_set_column_homogeneous (GTK_GRID (grid), | |
TRUE); | |
gtk_grid_set_row_homogeneous (GTK_GRID (grid), | |
FALSE); | |
/*Create a progressbar*/ | |
widgets->progress_bar = gtk_progress_bar_new(); | |
gtk_progress_bar_set_inverted (GTK_PROGRESS_BAR(widgets->progress_bar), | |
FALSE); | |
gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR(widgets->progress_bar), | |
FALSE); | |
/*Fill in the given fraction of the bar. | |
It has to be between 0.0-1.0 inclusive*/ | |
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (widgets->progress_bar), fraction); | |
widgets->progress_label = gtk_label_new ("Meep"); | |
/*Create buttons*/ | |
widgets->button1 = gtk_button_new_with_label ("\xe2\x96\xb6"); | |
button2 = gtk_button_new_with_label ("Cancel"); | |
/*Create an overlay*/ | |
progress_overlay = gtk_overlay_new (); | |
gtk_widget_set_hexpand (progress_overlay, TRUE); | |
gtk_widget_set_vexpand (progress_overlay, FALSE); | |
gtk_container_add (GTK_CONTAINER (progress_overlay), | |
widgets->progress_bar); | |
/*Create an animation pixbuf*/ | |
animation = gdk_pixbuf_animation_new_from_file(ANIMATION, &error); | |
if (error) { | |
g_warning("No image found\n*ERROR %s\n", error->message); | |
destroy_handler(NULL, app); | |
} | |
widgets->progress_animation = gtk_image_new_from_animation (animation); | |
gtk_widget_set_vexpand(widgets->progress_bar, FALSE); | |
gtk_widget_set_name (widgets->progress_animation, | |
"progress-animation"); | |
/*Image has to be in a (box) container with a one pixel padding | |
or else the progress will expand causing | |
the image to resize and causing the progress to expand*/ | |
/*create a box container for the image*/ | |
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); | |
gtk_box_pack_start (GTK_BOX(box), widgets->progress_animation, FALSE, FALSE, 1); // this one pixel is essential | |
gtk_widget_set_halign (widgets->progress_animation, | |
GTK_ALIGN_START); | |
gtk_overlay_add_overlay (GTK_OVERLAY (progress_overlay), | |
box); | |
gtk_overlay_set_overlay_pass_through (GTK_OVERLAY (progress_overlay), | |
box, TRUE); | |
/*Size group and add widgets*/ | |
size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL); | |
gtk_size_group_add_widget (size_group, widgets->progress_bar); | |
gtk_size_group_add_widget (size_group, box); | |
/*Create a style provider for the progessbar*/ | |
widgets->progressbar_style_provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ()); | |
gtk_style_context_add_provider (gtk_widget_get_style_context (widgets->progress_bar), | |
widgets->progressbar_style_provider, | |
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); | |
g_signal_connect (widgets->progress_animation, | |
"size-allocate", | |
G_CALLBACK (progress_animation_size_allocate), | |
widgets); | |
g_signal_connect (widgets->progress_bar, | |
"size-allocate", | |
G_CALLBACK (progress_bar_size_allocate), | |
widgets); | |
gtk_grid_attach (GTK_GRID (grid), progress_overlay, 0, 0, 4, 1); | |
gtk_grid_attach (GTK_GRID (grid), widgets->progress_label, 0, 1, 2, 1); | |
gtk_grid_attach (GTK_GRID (grid), widgets->button1, 2, 1, 1, 1); | |
gtk_grid_attach_next_to (GTK_GRID (grid), button2, widgets->button1, GTK_POS_RIGHT, 1, 1); | |
gtk_container_add (GTK_CONTAINER (window), grid); | |
gtk_container_set_border_width(GTK_CONTAINER(grid),12); | |
gtk_container_set_border_width(GTK_CONTAINER(window),5); | |
g_signal_connect (G_OBJECT(window), "destroy", | |
G_CALLBACK (destroy_handler), app); | |
g_signal_connect (widgets->button1, "clicked", | |
G_CALLBACK (button1_clicked_cb), widgets); | |
g_signal_connect (button2, "clicked", | |
G_CALLBACK (destroy_handler), app); | |
gtk_widget_show_all (window); | |
} | |
int | |
main (int argc, char **argv) | |
{ | |
GtkApplication *app; | |
int status; | |
app = gtk_application_new ("org.gtk.meep_bar", G_APPLICATION_FLAGS_NONE); | |
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); | |
status = g_application_run (G_APPLICATION (app), argc, argv); | |
g_object_unref (app); | |
return status; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment