From 9b598898476baa9cf419a1cc49bdf05fffebf5c0 Mon Sep 17 00:00:00 2001 From: Michael Natterer Date: Fri, 5 Dec 2008 11:31:30 +0000 Subject: [PATCH] =?UTF-8?q?Bug=20546285=20=E2=80=93=20Allow=20GtkEntry=20t?= =?UTF-8?q?o=20draw=20progress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2008-12-05 Michael Natterer Bug 546285 – Allow GtkEntry to draw progress * gtk/gtkentry.[ch]: add new API similar to GtkProgressBar which allows to set the entry's progress_fraction, its progress_pulse_step and to let the entry's progress pulse. * gtk/gtk.symbols: updated. * tests/testgtk.c: add progress demo code to the "Entry" window. svn path=/trunk/; revision=21846 --- ChangeLog | 12 ++ gtk/gtk.symbols | 5 + gtk/gtkentry.c | 302 ++++++++++++++++++++++++++++++++++++++++++++++-- gtk/gtkentry.h | 12 ++ tests/testgtk.c | 72 +++++++++++- 5 files changed, 392 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 41c3907928..12e8a6dce3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2008-12-05 Michael Natterer + + Bug 546285 – Allow GtkEntry to draw progress + + * gtk/gtkentry.[ch]: add new API similar to GtkProgressBar which + allows to set the entry's progress_fraction, its progress_pulse_step + and to let the entry's progress pulse. + + * gtk/gtk.symbols: updated. + + * tests/testgtk.c: add progress demo code to the "Entry" window. + 2008-12-04 Johan Dahlin * gtk/gtkstatusicon.c: diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 58d38f175e..39f32f35e7 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -1282,6 +1282,8 @@ gtk_entry_get_layout gtk_entry_get_layout_offsets gtk_entry_get_max_length gtk_entry_get_overwrite_mode +gtk_entry_get_progress_fraction +gtk_entry_get_progress_pulse_step gtk_entry_get_text gtk_entry_get_text_length gtk_entry_get_type G_GNUC_CONST @@ -1293,6 +1295,7 @@ gtk_entry_new gtk_entry_new_with_max_length gtk_entry_append_text gtk_entry_prepend_text +gtk_entry_progress_pulse gtk_entry_select_region gtk_entry_set_position gtk_entry_set_editable @@ -1305,6 +1308,8 @@ gtk_entry_set_inner_border gtk_entry_set_invisible_char gtk_entry_set_max_length gtk_entry_set_overwrite_mode +gtk_entry_set_progress_fraction +gtk_entry_set_progress_pulse_step gtk_entry_set_text gtk_entry_set_visibility gtk_entry_set_width_chars diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index b838ac4d07..be88fa5244 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -26,6 +26,8 @@ */ #include "config.h" + +#include #include #include "gdk/gdkkeysyms.h" @@ -86,14 +88,20 @@ struct _GtkEntryPrivate gfloat xalign; gint insert_pos; guint blink_time; /* time in msec the cursor has blinked since last user event */ - guint interior_focus : 1; - guint real_changed : 1; - guint invisible_char_set : 1; - guint caps_lock_warning : 1; - guint change_count : 8; + guint interior_focus : 1; + guint real_changed : 1; + guint invisible_char_set : 1; + guint caps_lock_warning : 1; + guint change_count : 8; + guint progress_pulse_mode : 1; + guint progress_pulse_way_back : 1; gint focus_width; GtkShadowType shadow_type; + + gdouble progress_fraction; + gdouble progress_pulse_fraction; + gdouble progress_pulse_current; }; typedef struct _GtkEntryPasswordHint GtkEntryPasswordHint; @@ -149,7 +157,9 @@ enum { PROP_OVERWRITE_MODE, PROP_TEXT_LENGTH, PROP_INVISIBLE_CHAR_SET, - PROP_CAPS_LOCK_WARNING + PROP_CAPS_LOCK_WARNING, + PROP_PROGRESS_FRACTION, + PROP_PROGRESS_PULSE_STEP }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -184,6 +194,8 @@ static void gtk_entry_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static void gtk_entry_draw_frame (GtkWidget *widget, GdkRectangle *area); +static void gtk_entry_draw_progress (GtkWidget *widget, + GdkEventExpose *event); static gint gtk_entry_expose (GtkWidget *widget, GdkEventExpose *event); static gint gtk_entry_button_press (GtkWidget *widget, @@ -668,6 +680,7 @@ gtk_entry_class_init (GtkEntryClass *class) P_("Whether new text overwrites existing text"), FALSE, GTK_PARAM_READWRITE)); + /** * GtkEntry:text-length: * @@ -699,8 +712,6 @@ gtk_entry_class_init (GtkEntryClass *class) FALSE, GTK_PARAM_READWRITE)); - - /** * GtkEntry:caps-lock-warning * @@ -717,7 +728,41 @@ gtk_entry_class_init (GtkEntryClass *class) TRUE, GTK_PARAM_READWRITE)); - + /** + * GtkEntry:progress-fraction: + * + * The current fraction of the task that's been completed. + * + * Since: 2.16 + */ + g_object_class_install_property (gobject_class, + PROP_PROGRESS_FRACTION, + g_param_spec_double ("progress-fraction", + P_("Progress Fraction"), + P_("The current fraction of the task that's been completed"), + 0.0, + 1.0, + 0.0, + GTK_PARAM_READWRITE)); + + /** + * GtkEntry:progress-pulse-step: + * + * The fraction of total entry width to move the progress + * bouncing block for each call to gtk_entry_progress_pulse(). + * + * Since: 2.16 + */ + g_object_class_install_property (gobject_class, + PROP_PROGRESS_PULSE_STEP, + g_param_spec_double ("progress-pulse-step", + P_("Progress Pulse Step"), + P_("The fraction of total entry width to move the progress bouncing block for each call to gtk_entry_progress_pulse()"), + 0.0, + 1.0, + 0.1, + GTK_PARAM_READWRITE)); + signals[POPULATE_POPUP] = g_signal_new (I_("populate-popup"), G_OBJECT_CLASS_TYPE (gobject_class), @@ -1142,6 +1187,14 @@ gtk_entry_set_property (GObject *object, priv->caps_lock_warning = g_value_get_boolean (value); break; + case PROP_PROGRESS_FRACTION: + gtk_entry_set_progress_fraction (entry, g_value_get_double (value)); + break; + + case PROP_PROGRESS_PULSE_STEP: + gtk_entry_set_progress_pulse_step (entry, g_value_get_double (value)); + break; + case PROP_SCROLL_OFFSET: case PROP_CURSOR_POSITION: default: @@ -1218,6 +1271,13 @@ gtk_entry_get_property (GObject *object, case PROP_CAPS_LOCK_WARNING: g_value_set_boolean (value, priv->caps_lock_warning); break; + case PROP_PROGRESS_FRACTION: + g_value_set_double (value, priv->progress_fraction); + break; + case PROP_PROGRESS_PULSE_STEP: + g_value_set_double (value, priv->progress_pulse_fraction); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1289,6 +1349,8 @@ gtk_entry_init (GtkEntry *entry) priv->shadow_type = GTK_SHADOW_IN; priv->xalign = 0.0; priv->caps_lock_warning = TRUE; + priv->progress_fraction = 0.0; + priv->progress_pulse_fraction = 0.1; gtk_drag_dest_set (GTK_WIDGET (entry), GTK_DEST_DEFAULT_HIGHLIGHT, @@ -1784,6 +1846,52 @@ gtk_entry_draw_frame (GtkWidget *widget, } } +static void +gtk_entry_draw_progress (GtkWidget *widget, + GdkEventExpose *event) +{ + GtkEntryPrivate *private = GTK_ENTRY_GET_PRIVATE (widget); + GtkEntry *entry = GTK_ENTRY (widget); + + if (private->progress_pulse_mode) + { + gdouble value = private->progress_pulse_current; + gint area_width, area_height; + + gdk_drawable_get_size (entry->text_area, &area_width, &area_height); + + gtk_paint_box (widget->style, entry->text_area, + GTK_STATE_SELECTED, GTK_SHADOW_OUT, + &event->area, widget, "entry-progress", + value * area_width, 0, + private->progress_pulse_fraction * area_width, area_height); + } + else if (private->progress_fraction > 0) + { + gdouble value = private->progress_fraction; + gint area_width, area_height; + + gdk_drawable_get_size (entry->text_area, &area_width, &area_height); + + if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL) + { + gtk_paint_box (widget->style, entry->text_area, + GTK_STATE_SELECTED, GTK_SHADOW_OUT, + &event->area, widget, "entry-progress", + area_width - value * area_width, 0, + value * area_width, area_height); + } + else + { + gtk_paint_box (widget->style, entry->text_area, + GTK_STATE_SELECTED, GTK_SHADOW_OUT, + &event->area, widget, "entry-progress", + 0, 0, + value * area_width, area_height); + } + } +} + static gint gtk_entry_expose (GtkWidget *widget, GdkEventExpose *event) @@ -1811,6 +1919,8 @@ gtk_entry_expose (GtkWidget *widget, &event->area, widget, "entry_bg", 0, 0, area_width, area_height); + gtk_entry_draw_progress (widget, event); + if (entry->dnd_position != -1) gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_DND); @@ -6628,6 +6738,179 @@ gtk_entry_get_cursor_hadjustment (GtkEntry *entry) return g_object_get_qdata (G_OBJECT (entry), quark_cursor_hadjustment); } +/** + * gtk_entry_set_progress_fraction: + * @entry: a #GtkEntry + * @fraction: fraction of the task that's been completed + * + * Causes the entry's progress indicator to "fill in" the given + * fraction of the bar. The fraction should be between 0.0 and 1.0, + * inclusive. + * + * Since: 2.16 + */ +void +gtk_entry_set_progress_fraction (GtkEntry *entry, + gdouble fraction) +{ + GtkEntryPrivate *private; + gdouble old_fraction; + + g_return_if_fail (GTK_IS_ENTRY (entry)); + + private = GTK_ENTRY_GET_PRIVATE (entry); + + if (private->progress_pulse_mode) + old_fraction = -1; + else + old_fraction = private->progress_fraction; + + fraction = CLAMP (fraction, 0.0, 1.0); + + private->progress_fraction = fraction; + private->progress_pulse_mode = FALSE; + private->progress_pulse_current = 0.0; + + if (fabs (fraction - old_fraction) > 0.0001) + gtk_entry_queue_draw (entry); + + if (fraction != old_fraction) + g_object_notify (G_OBJECT (entry), "progress-fraction"); +} + +/** + * gtk_entry_get_progress_fraction: + * @entry: a #GtkEntry + * + * Returns the current fraction of the task that's been completed. + * See gtk_entry_set_progress_fraction(). + * + * Return value: a fraction from 0.0 to 1.0 + * + * Since: 2.16 + */ +gdouble +gtk_entry_get_progress_fraction (GtkEntry *entry) +{ + GtkEntryPrivate *private; + + g_return_val_if_fail (GTK_IS_ENTRY (entry), 0.0); + + private = GTK_ENTRY_GET_PRIVATE (entry); + + return private->progress_fraction; +} + +/** + * gtk_entry_set_progress_pulse_step: + * @entry: a #GtkEntry + * @fraction: fraction between 0.0 and 1.0 + * + * Sets the fraction of total entry width to move the progress + * bouncing block for each call to gtk_entry_progress_pulse(). + * + * Since: 2.16 + */ +void +gtk_entry_set_progress_pulse_step (GtkEntry *entry, + gdouble fraction) +{ + GtkEntryPrivate *private; + + g_return_if_fail (GTK_IS_ENTRY (entry)); + + private = GTK_ENTRY_GET_PRIVATE (entry); + + fraction = CLAMP (fraction, 0.0, 1.0); + + if (fraction != private->progress_pulse_fraction) + { + private->progress_pulse_fraction = fraction; + + gtk_entry_queue_draw (entry); + + g_object_notify (G_OBJECT (entry), "progress-pulse-step"); + } +} + +/** + * gtk_entry_get_progress_pulse_step: + * @entry: a #GtkEntry + * + * Retrieves the pulse step set with gtk_entry_set_progress_pulse_step(). + * + * Return value: a fraction from 0.0 to 1.0 + * + * Since: 2.16 + */ +gdouble +gtk_entry_get_progress_pulse_step (GtkEntry *entry) +{ + GtkEntryPrivate *private; + + g_return_val_if_fail (GTK_IS_ENTRY (entry), 0.0); + + private = GTK_ENTRY_GET_PRIVATE (entry); + + return private->progress_pulse_fraction; +} + +/** + * gtk_entry_progress_pulse: + * @entry: a #GtkEntry + * + * Indicates that some progress is made, but you don't know how much. + * Causes the entry's progress indicator to enter "activity mode," + * where a block bounces back and forth. Each call to + * gtk_entry_progress_pulse() causes the block to move by a little bit + * (the amount of movement per pulse is determined by + * gtk_entry_set_progress_pulse_step()). + * + * Since: 2.16 + */ +void +gtk_entry_progress_pulse (GtkEntry *entry) +{ + GtkEntryPrivate *private; + + g_return_if_fail (GTK_IS_ENTRY (entry)); + + private = GTK_ENTRY_GET_PRIVATE (entry); + + if (private->progress_pulse_mode) + { + if (private->progress_pulse_way_back) + { + private->progress_pulse_current -= private->progress_pulse_fraction; + + if (private->progress_pulse_current < 0.0) + { + private->progress_pulse_current = 0.0; + private->progress_pulse_way_back = FALSE; + } + } + else + { + private->progress_pulse_current += private->progress_pulse_fraction; + + if (private->progress_pulse_current > 1.0 - private->progress_pulse_fraction) + { + private->progress_pulse_current = 1.0 - private->progress_pulse_fraction; + private->progress_pulse_way_back = TRUE; + } + } + } + else + { + private->progress_fraction = 0.0; + private->progress_pulse_mode = TRUE; + private->progress_pulse_way_back = FALSE; + private->progress_pulse_current = 0.0; + } + + gtk_entry_queue_draw (entry); +} + /* Caps Lock warning for password entries */ static void @@ -6793,6 +7076,5 @@ keymap_state_changed (GdkKeymap *keymap, remove_capslock_feedback (entry); } - #define __GTK_ENTRY_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkentry.h b/gtk/gtkentry.h index 1416033859..b5781d4f1e 100644 --- a/gtk/gtkentry.h +++ b/gtk/gtkentry.h @@ -212,6 +212,18 @@ void gtk_entry_set_cursor_hadjustment (GtkEntry *entry, GtkAdjustment *adjustment); GtkAdjustment* gtk_entry_get_cursor_hadjustment (GtkEntry *entry); +/* Progress API + */ +void gtk_entry_set_progress_fraction (GtkEntry *entry, + gdouble fraction); +gdouble gtk_entry_get_progress_fraction (GtkEntry *entry); + +void gtk_entry_set_progress_pulse_step (GtkEntry *entry, + gdouble fraction); +gdouble gtk_entry_get_progress_pulse_step (GtkEntry *entry); + +void gtk_entry_progress_pulse (GtkEntry *entry); + /* Deprecated compatibility functions */ diff --git a/tests/testgtk.c b/tests/testgtk.c index 49dc119c30..b5c11055c9 100644 --- a/tests/testgtk.c +++ b/tests/testgtk.c @@ -5193,6 +5193,65 @@ entry_toggle_sensitive (GtkWidget *checkbutton, gtk_widget_set_sensitive (entry, GTK_TOGGLE_BUTTON(checkbutton)->active); } +static gboolean +entry_progress_timeout (gpointer data) +{ + if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (data), "progress-pulse"))) + { + gtk_entry_progress_pulse (GTK_ENTRY (data)); + } + else + { + gdouble fraction; + + fraction = gtk_entry_get_progress_fraction (GTK_ENTRY (data)); + + fraction += 0.05; + if (fraction > 1.0001) + fraction = 0.0; + + gtk_entry_set_progress_fraction (GTK_ENTRY (data), fraction); + } + + return TRUE; +} + +static void +entry_remove_timeout (gpointer data) +{ + g_source_remove (GPOINTER_TO_UINT (data)); +} + +static void +entry_toggle_progress (GtkWidget *checkbutton, + GtkWidget *entry) +{ + if (GTK_TOGGLE_BUTTON (checkbutton)->active) + { + guint timeout = gdk_threads_add_timeout (100, + entry_progress_timeout, + entry); + g_object_set_data_full (G_OBJECT (entry), "timeout-id", + GUINT_TO_POINTER (timeout), + entry_remove_timeout); + } + else + { + g_object_set_data (G_OBJECT (entry), "timeout-id", + GUINT_TO_POINTER (0)); + + gtk_entry_set_progress_fraction (GTK_ENTRY (entry), 0.0); + } +} + +static void +entry_toggle_pulse (GtkWidget *checkbutton, + GtkWidget *entry) +{ + g_object_set_data (G_OBJECT (entry), "progress-pulse", + GINT_TO_POINTER (GTK_TOGGLE_BUTTON (checkbutton)->active)); +} + static void entry_props_clicked (GtkWidget *button, GObject *entry) @@ -5211,6 +5270,7 @@ create_entry (GtkWidget *widget) GtkWidget *hbox; GtkWidget *has_frame_check; GtkWidget *sensitive_check; + GtkWidget *progress_check; GtkWidget *entry, *cb; GtkWidget *button; GtkWidget *separator; @@ -5281,7 +5341,17 @@ create_entry (GtkWidget *widget) g_signal_connect (has_frame_check, "toggled", G_CALLBACK (entry_toggle_frame), entry); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (has_frame_check), TRUE); - + + progress_check = gtk_check_button_new_with_label("Show Progress"); + gtk_box_pack_start (GTK_BOX (box2), progress_check, FALSE, TRUE, 0); + g_signal_connect (progress_check, "toggled", + G_CALLBACK (entry_toggle_progress), entry); + + progress_check = gtk_check_button_new_with_label("Pulse Progress"); + gtk_box_pack_start (GTK_BOX (box2), progress_check, FALSE, TRUE, 0); + g_signal_connect (progress_check, "toggled", + G_CALLBACK (entry_toggle_pulse), entry); + separator = gtk_hseparator_new (); gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);