[gtk+/wip/matthiasc/calendar] Add a multichoice widget
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/matthiasc/calendar] Add a multichoice widget
- Date: Fri, 4 Sep 2015 05:34:06 +0000 (UTC)
commit e0852438798a4890c77abab7c0aff37522026d80
Author: Matthias Clasen <mclasen redhat com>
Date: Thu Sep 3 00:52:23 2015 -0400
Add a multichoice widget
gtk/Makefile.am | 3 +
gtk/gtkmultichoice.c | 478 +++++++++++++++++++++++++++++++++++++++++++
gtk/gtkmultichoiceprivate.h | 56 +++++
gtk/ui/gtkmultichoice.ui | 65 ++++++
4 files changed, 602 insertions(+), 0 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 799c0b0..8291477 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -474,6 +474,7 @@ gtk_private_h_sources = \
gtkmodifierstyle.h \
gtkmodulesprivate.h \
gtkmountoperationprivate.h \
+ gtkmultichoiceprivate.h \
gtkorientableprivate.h \
gtkpango.h \
gtkpathbar.h \
@@ -753,6 +754,7 @@ gtk_base_c_sources = \
gtkmodifierstyle.c \
gtkmodules.c \
gtkmountoperation.c \
+ gtkmultichoice.c \
gtknotebook.c \
gtkoffscreenwindow.c \
gtkorientable.c \
@@ -1095,6 +1097,7 @@ templates = \
ui/gtkinfobar.ui \
ui/gtklockbutton.ui \
ui/gtkmessagedialog.ui \
+ ui/gtkmultichoice.ui \
ui/gtkpagesetupunixdialog.ui \
ui/gtkpathbar.ui \
ui/gtkplacesview.ui \
diff --git a/gtk/gtkmultichoice.c b/gtk/gtkmultichoice.c
new file mode 100644
index 0000000..278b4de
--- /dev/null
+++ b/gtk/gtkmultichoice.c
@@ -0,0 +1,478 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkmultichoiceprivate.h"
+#include "gtkbox.h"
+#include "gtklabel.h"
+#include "gtkstack.h"
+#include "gtkwidgetprivate.h"
+#include "gtkmarshalers.h"
+
+#include "gtkintl.h"
+#include "gtkprivate.h"
+
+struct _GtkMultiChoice
+{
+ GtkBox parent;
+ GtkWidget *down_button;
+ GtkWidget *stack;
+ GtkWidget *up_button;
+ gint value;
+ gint min_value;
+ gint max_value;
+ gboolean wrap;
+ gboolean animate;
+ GtkWidget **choices;
+ gint n_choices;
+ guint click_id;
+ GtkWidget *active;
+ GtkWidget *label1;
+ GtkWidget *label2;
+ GtkMultiChoiceFormatCallback format_cb;
+ gpointer format_data;
+ GDestroyNotify format_destroy;
+};
+
+struct _GtkMultiChoiceClass
+{
+ GtkBoxClass parent_class;
+};
+
+enum {
+ PROP_VALUE = 1,
+ PROP_MIN_VALUE,
+ PROP_MAX_VALUE,
+ PROP_WRAP,
+ PROP_ANIMATE,
+ PROP_CHOICES,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *choice_properties[NUM_PROPERTIES] = { NULL, };
+
+enum {
+ WRAPPED,
+ LAST_SIGNAL
+};
+
+static guint choice_signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GtkMultiChoice, gtk_multi_choice, GTK_TYPE_BOX)
+
+static gchar *
+get_value_string (GtkMultiChoice *choice,
+ gint value)
+{
+ if (choice->format_cb)
+ return choice->format_cb (choice, value, choice->format_data);
+ else if (0 <= value && value < choice->n_choices)
+ return g_strdup (gtk_label_get_label (GTK_LABEL (choice->choices[value])));
+ else
+ return g_strdup_printf ("%d", value);
+}
+
+static void
+set_value (GtkMultiChoice *choice,
+ gint value,
+ GtkStackTransitionType transition)
+{
+ gchar *text;
+ const gchar *name;
+ GtkWidget *label;
+
+ value = CLAMP (value, choice->min_value, choice->max_value);
+
+ if (choice->value == value)
+ return;
+
+ choice->value = value;
+
+ if (gtk_stack_get_visible_child (GTK_STACK (choice->stack)) == choice->label1)
+ {
+ name = "label2";
+ label = choice->label2;
+ }
+ else
+ {
+ name = "label1";
+ label = choice->label1;
+ }
+
+ text = get_value_string (choice, value);
+ gtk_label_set_text (GTK_LABEL (label), text);
+ g_free (text);
+
+ if (!choice->animate)
+ transition = GTK_STACK_TRANSITION_TYPE_NONE;
+
+ gtk_stack_set_visible_child_full (GTK_STACK (choice->stack), name, transition);
+
+ gtk_widget_set_sensitive (choice->down_button,
+ choice->wrap || choice->value > choice->min_value);
+ gtk_widget_set_sensitive (choice->up_button,
+ choice->wrap || choice->value < choice->max_value);
+
+ g_object_notify_by_pspec (G_OBJECT (choice), choice_properties[PROP_VALUE]);
+}
+
+static void
+go_up (GtkMultiChoice *choice)
+{
+ gint value;
+ gboolean wrapped = FALSE;
+
+ value = choice->value + 1;
+ if (value > choice->max_value)
+ {
+ if (!choice->wrap)
+ return;
+
+ value = choice->min_value;
+ wrapped = TRUE;
+ }
+
+ set_value (choice, value, GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
+
+ if (wrapped)
+ g_signal_emit (choice, choice_signals[WRAPPED], 0);
+}
+
+static void
+go_down (GtkMultiChoice *choice)
+{
+ gint value;
+ gboolean wrapped = FALSE;
+
+ value = choice->value - 1;
+ if (value < choice->min_value)
+ {
+ if (!choice->wrap)
+ return;
+
+ value = choice->max_value;
+ wrapped = TRUE;
+ }
+
+ set_value (choice, value, GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
+
+ if (wrapped)
+ g_signal_emit (choice, choice_signals[WRAPPED], 0);
+}
+
+static gboolean
+button_activate (GtkMultiChoice *choice,
+ GtkWidget *button)
+{
+ if (button == choice->down_button)
+ go_down (choice);
+ else if (button == choice->up_button)
+ go_up (choice);
+ else
+ g_assert_not_reached ();
+
+ return TRUE;
+}
+
+static gboolean
+button_timeout (gpointer user_data)
+{
+ GtkMultiChoice *choice = GTK_MULTI_CHOICE (user_data);
+ gboolean res;
+
+ if (choice->click_id == 0)
+ return G_SOURCE_REMOVE;
+
+ res = button_activate (choice, choice->active);
+ if (!res)
+ {
+ g_source_remove (choice->click_id);
+ choice->click_id = 0;
+ }
+
+ return res;
+}
+
+static gboolean
+button_press_cb (GtkWidget *widget,
+ GdkEventButton *button,
+ GtkMultiChoice *choice)
+{
+ gint double_click_time;
+
+ if (button->type != GDK_BUTTON_PRESS)
+ return TRUE;
+
+ g_object_get (gtk_widget_get_settings (widget),
+ "gtk-double-click-time", &double_click_time,
+ NULL);
+
+ if (choice->click_id != 0)
+ g_source_remove (choice->click_id);
+
+ choice->active = widget;
+
+ choice->click_id = gdk_threads_add_timeout (double_click_time,
+ button_timeout,
+ choice);
+ g_source_set_name_by_id (choice->click_id, "[gtk+] button_timeout");
+ button_timeout (choice);
+
+ return TRUE;
+}
+
+static gboolean
+button_release_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ GtkMultiChoice *choice)
+{
+ if (choice->click_id != 0)
+ {
+ g_source_remove (choice->click_id);
+ choice->click_id = 0;
+ }
+
+ choice->active = NULL;
+
+ return TRUE;
+}
+
+static void
+button_clicked_cb (GtkWidget *button,
+ GtkMultiChoice *choice)
+{
+ if (choice->click_id != 0)
+ return;
+
+ button_activate (choice, button);
+}
+
+static void
+gtk_multi_choice_init (GtkMultiChoice *choice)
+{
+ gtk_widget_init_template (GTK_WIDGET (choice));
+}
+
+static void
+gtk_multi_choice_dispose (GObject *object)
+{
+ GtkMultiChoice *choice = GTK_MULTI_CHOICE (object);
+
+ if (choice->click_id != 0)
+ {
+ g_source_remove (choice->click_id);
+ choice->click_id = 0;
+ }
+
+ g_free (choice->choices);
+ choice->choices = NULL;
+
+ if (choice->format_destroy)
+ {
+ choice->format_destroy (choice->format_data);
+ choice->format_destroy = NULL;
+ }
+
+ G_OBJECT_CLASS (gtk_multi_choice_parent_class)->dispose (object);
+}
+
+static void
+gtk_multi_choice_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkMultiChoice *choice = GTK_MULTI_CHOICE (object);
+
+ switch (property_id)
+ {
+ case PROP_VALUE:
+ g_value_set_int (value, choice->value);
+ break;
+ case PROP_MIN_VALUE:
+ g_value_set_int (value, choice->min_value);
+ break;
+ case PROP_MAX_VALUE:
+ g_value_set_int (value, choice->max_value);
+ break;
+ case PROP_WRAP:
+ g_value_set_boolean (value, choice->wrap);
+ break;
+ case PROP_ANIMATE:
+ g_value_set_boolean (value, choice->animate);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_multi_choice_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkMultiChoice *choice = GTK_MULTI_CHOICE (object);
+
+ switch (property_id)
+ {
+ case PROP_VALUE:
+ gtk_multi_choice_set_value (choice, g_value_get_int (value));
+ break;
+ case PROP_MIN_VALUE:
+ choice->min_value = g_value_get_int (value);
+ g_object_notify_by_pspec (object, choice_properties[PROP_MIN_VALUE]);
+ gtk_multi_choice_set_value (choice, choice->value);
+ break;
+ case PROP_MAX_VALUE:
+ choice->max_value = g_value_get_int (value);
+ g_object_notify_by_pspec (object, choice_properties[PROP_MAX_VALUE]);
+ gtk_multi_choice_set_value (choice, choice->value);
+ break;
+ case PROP_WRAP:
+ choice->wrap = g_value_get_boolean (value);
+ g_object_notify_by_pspec (object, choice_properties[PROP_WRAP]);
+ break;
+ case PROP_ANIMATE:
+ choice->animate = g_value_get_boolean (value);
+ g_object_notify_by_pspec (object, choice_properties[PROP_ANIMATE]);
+ break;
+ case PROP_CHOICES:
+ gtk_multi_choice_set_choices (choice, (const gchar **)g_value_get_boxed (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_multi_choice_class_init (GtkMultiChoiceClass *class)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->dispose = gtk_multi_choice_dispose;
+ object_class->set_property = gtk_multi_choice_set_property;
+ object_class->get_property = gtk_multi_choice_get_property;
+
+ choice_properties[PROP_VALUE] =
+ g_param_spec_int ("value", P_("Value"), P_("Value"),
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ choice_properties[PROP_MIN_VALUE] =
+ g_param_spec_int ("min-value", P_("Minimum Value"), P_("Minimum Value"),
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ choice_properties[PROP_MAX_VALUE] =
+ g_param_spec_int ("max-value", P_("Maximum Value"), P_("Maximum Value"),
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ choice_properties[PROP_WRAP] =
+ g_param_spec_boolean ("wrap", P_("Wrap"), P_("Wrap"),
+ FALSE,
+ G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ choice_properties[PROP_ANIMATE] =
+ g_param_spec_boolean ("animate", P_("Animate"), P_("Animate"),
+ FALSE,
+ G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+ choice_properties[PROP_CHOICES] =
+ g_param_spec_boxed ("choices", P_("Choices"), P_("Choices"),
+ G_TYPE_STRV,
+ G_PARAM_WRITABLE|G_PARAM_EXPLICIT_NOTIFY);
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, choice_properties);
+
+ choice_signals[WRAPPED] =
+ g_signal_new (I_("wrapped"),
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ _gtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkmultichoice.ui");
+ gtk_widget_class_bind_template_child (widget_class, GtkMultiChoice, down_button);
+ gtk_widget_class_bind_template_child (widget_class, GtkMultiChoice, up_button);
+ gtk_widget_class_bind_template_child (widget_class, GtkMultiChoice, stack);
+ gtk_widget_class_bind_template_child (widget_class, GtkMultiChoice, label1);
+ gtk_widget_class_bind_template_child (widget_class, GtkMultiChoice, label2);
+
+ gtk_widget_class_bind_template_callback (widget_class, button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, button_press_cb);
+ gtk_widget_class_bind_template_callback (widget_class, button_release_cb);
+}
+
+GtkWidget *
+gtk_multi_choice_new (void)
+{
+ return GTK_WIDGET (g_object_new (GTK_TYPE_MULTI_CHOICE, NULL));
+}
+
+void
+gtk_multi_choice_set_value (GtkMultiChoice *choice,
+ gint value)
+{
+ set_value (choice, value, GTK_STACK_TRANSITION_TYPE_NONE);
+}
+
+gint
+gtk_multi_choice_get_value (GtkMultiChoice *choice)
+{
+ return choice->value;
+}
+
+void
+gtk_multi_choice_set_choices (GtkMultiChoice *choice,
+ const gchar **choices)
+{
+ gint i;
+
+ for (i = 0; i < choice->n_choices; i++)
+ gtk_container_remove (GTK_CONTAINER (choice->stack), choice->choices[i]);
+ g_free (choice->choices);
+
+ choice->n_choices = g_strv_length ((gchar **)choices);
+ choice->choices = g_new (GtkWidget *, choice->n_choices);
+ for (i = 0; i < choice->n_choices; i++)
+ {
+ choice->choices[i] = gtk_label_new (choices[i]);
+ gtk_widget_show (choice->choices[i]);
+ gtk_stack_add_named (GTK_STACK (choice->stack),
+ choice->choices[i],
+ choices[i]);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (choice), choice_properties[PROP_CHOICES]);
+}
+
+void
+gtk_multi_choice_set_format_callback (GtkMultiChoice *choice,
+ GtkMultiChoiceFormatCallback callback,
+ gpointer user_data,
+ GDestroyNotify destroy)
+{
+ if (choice->format_destroy)
+ choice->format_destroy (choice->format_data);
+
+ choice->format_cb = callback;
+ choice->format_data = user_data;
+ choice->format_destroy = destroy;
+}
diff --git a/gtk/gtkmultichoiceprivate.h b/gtk/gtkmultichoiceprivate.h
new file mode 100644
index 0000000..0e0c48e
--- /dev/null
+++ b/gtk/gtkmultichoiceprivate.h
@@ -0,0 +1,56 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_MULTI_CHOICE_PRIVATE_H__
+#define __GTK_MULTI_CHOICE_PRIVATE_H__
+
+#include <gtk/gtkbin.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_MULTI_CHOICE (gtk_multi_choice_get_type ())
+#define GTK_MULTI_CHOICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_MULTI_CHOICE,
GtkMultiChoice))
+#define GTK_MULTI_CHOICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_MULTI_CHOICE,
GtkMultiChoiceClass))
+#define GTK_IS_MULTI_CHOICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_MULTI_CHOICE))
+#define GTK_IS_MULTI_CHOICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_MULTI_CHOICE))
+#define GTK_MULTI_CHOICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_MULTI_CHOICE,
GtkMultiChoiceClass))
+
+
+typedef struct _GtkMultiChoice GtkMultiChoice;
+typedef struct _GtkMultiChoiceClass GtkMultiChoiceClass;
+
+GType gtk_multi_choice_get_type (void) G_GNUC_CONST;
+
+GtkWidget *gtk_multi_choice_new (void);
+void gtk_multi_choice_set_value (GtkMultiChoice *choice,
+ gint value);
+gint gtk_multi_choice_get_value (GtkMultiChoice *choice);
+void gtk_multi_choice_set_choices (GtkMultiChoice *choice,
+ const gchar **choices);
+
+typedef gchar * (* GtkMultiChoiceFormatCallback) (GtkMultiChoice *choice,
+ gint value,
+ gpointer user_data);
+
+void gtk_multi_choice_set_format_callback (GtkMultiChoice *choice,
+ GtkMultiChoiceFormatCallback callback,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+G_END_DECLS
+
+#endif /* __GTK_MULTI_CHOICE_PRIVATE_H__ */
diff --git a/gtk/ui/gtkmultichoice.ui b/gtk/ui/gtkmultichoice.ui
new file mode 100644
index 0000000..4626328
--- /dev/null
+++ b/gtk/ui/gtkmultichoice.ui
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="gtk30">
+ <requires lib="gtk+" version="3.16"/>
+ <template class="GtkMultiChoice" parent="GtkBox">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkButton" id="down_button">
+ <property name="visible">True</property>
+ <property name="relief">none</property>
+ <signal name="button-press-event" handler="button_press_cb"/>
+ <signal name="button-release-event" handler="button_release_cb"/>
+ <signal name="clicked" handler="button_clicked_cb"/>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon_name">pan-start-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="name">label1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="name">label2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="up_button">
+ <property name="visible">True</property>
+ <property name="relief">none</property>
+ <signal name="button-press-event" handler="button_press_cb"/>
+ <signal name="button-release-event" handler="button_release_cb"/>
+ <signal name="clicked" handler="button_clicked_cb"/>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon_name">pan-end-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]