[gtk] Add a tagged entry demo
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk] Add a tagged entry demo
- Date: Fri, 15 Mar 2019 17:21:49 +0000 (UTC)
commit 34e859a5bc65744f34bcb9a594b24fc5d764ecea
Author: Matthias Clasen <mclasen redhat com>
Date: Fri Mar 15 10:34:06 2019 -0400
Add a tagged entry demo
This can serve as an example for how composite entries
can now be done outside of GTK, easily.
demos/gtk-demo/demo.gresource.xml | 8 +
demos/gtk-demo/demotaggedentry.c | 493 ++++++++++++++++++++++++++++++++++++++
demos/gtk-demo/demotaggedentry.h | 89 +++++++
demos/gtk-demo/meson.build | 9 +-
demos/gtk-demo/tagged_entry.c | 112 +++++++++
demos/gtk-demo/tagstyle.css | 19 ++
6 files changed, 729 insertions(+), 1 deletion(-)
---
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index fa0c245c87..cdf86c3c35 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -207,6 +207,7 @@
<file>spinbutton.c</file>
<file>spinner.c</file>
<file>tabs.c</file>
+ <file>tagged_entry.c</file>
<file>textview.c</file>
<file>textscroll.c</file>
<file>theming_style_classes.c</file>
@@ -218,6 +219,9 @@
<gresource prefix="/textview">
<file>floppybuddy.gif</file>
</gresource>
+ <gresource prefix="/tagged_entry">
+ <file>tagstyle.css</file>
+ </gresource>
<gresource prefix="/listbox">
<file>listbox.ui</file>
<file>messages.txt</file>
@@ -262,6 +266,10 @@
<gresource prefix="/dnd">
<file>dnd.css</file>
</gresource>
+ <gresource prefix="/tagged_entry">
+ <file>demotaggedentry.c</file>
+ <file>demotaggedentry.h</file>
+ </gresource>
<gresource prefix="/org/gtk/Demo">
<file>icons/16x16/actions/application-exit.png</file>
<file>icons/16x16/actions/document-new.png</file>
diff --git a/demos/gtk-demo/demotaggedentry.c b/demos/gtk-demo/demotaggedentry.c
new file mode 100644
index 0000000000..3f9f63a437
--- /dev/null
+++ b/demos/gtk-demo/demotaggedentry.c
@@ -0,0 +1,493 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2019 Red Hat, Inc.
+ *
+ * Authors:
+ * - Matthias Clasen <mclasen redhat com>
+ *
+ * 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 "demotaggedentry.h"
+
+#include <gtk/gtk.h>
+#include <gtk/gtk-a11y.h>
+
+typedef struct {
+ GtkWidget *box;
+ GtkWidget *entry;
+} DemoTaggedEntryPrivate;
+
+static void demo_tagged_entry_editable_init (GtkEditableInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (DemoTaggedEntry, demo_tagged_entry, GTK_TYPE_WIDGET,
+ G_ADD_PRIVATE (DemoTaggedEntry)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, demo_tagged_entry_editable_init))
+
+static void
+demo_tagged_entry_init (DemoTaggedEntry *entry)
+{
+ DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry);
+
+ gtk_widget_set_has_surface (GTK_WIDGET (entry), FALSE);
+
+ priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_widget_set_parent (priv->box, GTK_WIDGET (entry));
+
+ priv->entry = gtk_text_new ();
+ gtk_widget_set_hexpand (priv->entry, TRUE);
+ gtk_widget_set_vexpand (priv->entry, TRUE);
+ gtk_widget_set_hexpand (priv->box, FALSE);
+ gtk_widget_set_vexpand (priv->box, FALSE);
+ gtk_container_add (GTK_CONTAINER (priv->box), priv->entry);
+ gtk_editable_init_delegate (GTK_EDITABLE (entry));
+}
+
+static void
+demo_tagged_entry_dispose (GObject *object)
+{
+ DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (object);
+ DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry);
+
+ if (priv->entry)
+ gtk_editable_finish_delegate (GTK_EDITABLE (entry));
+
+ g_clear_pointer (&priv->entry, gtk_widget_unparent);
+ g_clear_pointer (&priv->box, gtk_widget_unparent);
+
+ G_OBJECT_CLASS (demo_tagged_entry_parent_class)->dispose (object);
+}
+
+static void
+demo_tagged_entry_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (demo_tagged_entry_parent_class)->finalize (object);
+}
+
+static void
+demo_tagged_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ if (gtk_editable_delegate_set_property (object, prop_id, value, pspec))
+ return;
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+demo_tagged_entry_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ if (gtk_editable_delegate_get_property (object, prop_id, value, pspec))
+ return;
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+demo_tagged_entry_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (widget);
+ DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry);
+
+ gtk_widget_measure (priv->box, orientation, for_size,
+ minimum, natural,
+ minimum_baseline, natural_baseline);
+}
+
+static void
+demo_tagged_entry_size_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
+{
+ DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (widget);
+ DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry);
+
+ gtk_widget_size_allocate (priv->box,
+ &(GtkAllocation) { 0, 0, width, height },
+ baseline);
+}
+
+static void
+demo_tagged_entry_grab_focus (GtkWidget *widget)
+{
+ DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (widget);
+ DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry);
+
+ gtk_widget_grab_focus (priv->entry);
+}
+
+static void
+demo_tagged_entry_class_init (DemoTaggedEntryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = demo_tagged_entry_dispose;
+ object_class->finalize = demo_tagged_entry_finalize;
+ object_class->get_property = demo_tagged_entry_get_property;
+ object_class->set_property = demo_tagged_entry_set_property;
+
+ widget_class->measure = demo_tagged_entry_measure;
+ widget_class->size_allocate = demo_tagged_entry_size_allocate;
+ widget_class->grab_focus = demo_tagged_entry_grab_focus;
+
+ gtk_editable_install_properties (object_class, 1);
+
+ gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ENTRY_ACCESSIBLE);
+ gtk_widget_class_set_css_name (widget_class, "entry");
+}
+
+static GtkEditable *
+demo_tagged_entry_get_delegate (GtkEditable *editable)
+{
+ DemoTaggedEntry *entry = DEMO_TAGGED_ENTRY (editable);
+ DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry);
+
+ return GTK_EDITABLE (priv->entry);
+}
+
+static void
+demo_tagged_entry_editable_init (GtkEditableInterface *iface)
+{
+ iface->get_delegate = demo_tagged_entry_get_delegate;
+}
+
+GtkWidget *
+demo_tagged_entry_new (void)
+{
+ return GTK_WIDGET (g_object_new (DEMO_TYPE_TAGGED_ENTRY, NULL));
+}
+
+void
+demo_tagged_entry_add_tag (DemoTaggedEntry *entry,
+ GtkWidget *tag)
+{
+ DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry);
+
+ g_return_if_fail (DEMO_IS_TAGGED_ENTRY (entry));
+
+ gtk_container_add (GTK_CONTAINER (priv->box), tag);
+}
+
+void
+demo_tagged_entry_insert_tag_after (DemoTaggedEntry *entry,
+ GtkWidget *tag,
+ GtkWidget *sibling)
+{
+ DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry);
+
+ g_return_if_fail (DEMO_IS_TAGGED_ENTRY (entry));
+
+ if (sibling == NULL)
+ gtk_container_add (GTK_CONTAINER (priv->box), tag);
+ else
+ gtk_box_insert_child_after (GTK_BOX (priv->box), tag, sibling);
+}
+
+void
+demo_tagged_entry_remove_tag (DemoTaggedEntry *entry,
+ GtkWidget *tag)
+{
+ DemoTaggedEntryPrivate *priv = demo_tagged_entry_get_instance_private (entry);
+
+ g_return_if_fail (DEMO_IS_TAGGED_ENTRY (entry));
+
+ gtk_container_remove (GTK_CONTAINER (priv->box), tag);
+}
+
+struct _DemoTaggedEntryTag
+{
+ GtkWidget parent;
+ GtkWidget *box;
+ GtkWidget *label;
+ GtkWidget *button;
+
+ gboolean has_close_button;
+ char *style;
+};
+
+struct _DemoTaggedEntryTagClass
+{
+ GtkWidgetClass parent_class;
+};
+
+enum {
+ PROP_0,
+ PROP_LABEL,
+ PROP_HAS_CLOSE_BUTTON,
+};
+
+enum {
+ SIGNAL_CLICKED,
+ SIGNAL_BUTTON_CLICKED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (DemoTaggedEntryTag, demo_tagged_entry_tag, GTK_TYPE_WIDGET)
+
+static void
+on_released (GtkGestureMultiPress *gesture,
+ int n_press,
+ double x,
+ double y,
+ DemoTaggedEntryTag *tag)
+{
+ g_signal_emit (tag, signals[SIGNAL_CLICKED], 0);
+}
+
+static void
+demo_tagged_entry_tag_init (DemoTaggedEntryTag *tag)
+{
+ GtkGesture *gesture;
+ GtkCssProvider *provider;
+
+ gtk_widget_set_has_surface (GTK_WIDGET (tag), FALSE);
+
+ tag->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_widget_set_parent (tag->box, GTK_WIDGET (tag));
+ tag->label = gtk_label_new ("");
+ gtk_container_add (GTK_CONTAINER (tag->box), tag->label);
+
+ gesture = gtk_gesture_multi_press_new ();
+ g_signal_connect (gesture, "released", G_CALLBACK (on_released), tag);
+ gtk_widget_add_controller (GTK_WIDGET (tag), GTK_EVENT_CONTROLLER (gesture));
+
+ provider = gtk_css_provider_new ();
+ gtk_css_provider_load_from_resource (provider, "/tagged_entry/tagstyle.css");
+ gtk_style_context_add_provider_for_display (gdk_display_get_default (),
+ GTK_STYLE_PROVIDER (provider),
+ 800);
+ g_object_unref (provider);
+}
+
+static void
+demo_tagged_entry_tag_dispose (GObject *object)
+{
+ DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object);
+
+ g_clear_pointer (&tag->box, gtk_widget_unparent);
+
+ G_OBJECT_CLASS (demo_tagged_entry_tag_parent_class)->dispose (object);
+}
+
+static void
+demo_tagged_entry_tag_finalize (GObject *object)
+{
+ DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object);
+
+ g_clear_pointer (&tag->box, gtk_widget_unparent);
+ g_clear_pointer (&tag->style, g_free);
+
+ G_OBJECT_CLASS (demo_tagged_entry_tag_parent_class)->finalize (object);
+}
+
+static void
+demo_tagged_entry_tag_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object);
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ demo_tagged_entry_tag_set_label (tag, g_value_get_string (value));
+ break;
+
+ case PROP_HAS_CLOSE_BUTTON:
+ demo_tagged_entry_tag_set_has_close_button (tag, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+demo_tagged_entry_tag_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (object);
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ g_value_set_string (value, demo_tagged_entry_tag_get_label (tag));
+ break;
+
+ case PROP_HAS_CLOSE_BUTTON:
+ g_value_set_boolean (value, demo_tagged_entry_tag_get_has_close_button (tag));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+demo_tagged_entry_tag_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (widget);
+
+ gtk_widget_measure (tag->box, orientation, for_size,
+ minimum, natural,
+ minimum_baseline, natural_baseline);
+}
+
+static void
+demo_tagged_entry_tag_size_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
+{
+ DemoTaggedEntryTag *tag = DEMO_TAGGED_ENTRY_TAG (widget);
+
+ gtk_widget_size_allocate (tag->box,
+ &(GtkAllocation) { 0, 0, width, height },
+ baseline);
+}
+
+static void
+demo_tagged_entry_tag_class_init (DemoTaggedEntryTagClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+ object_class->dispose = demo_tagged_entry_tag_dispose;
+ object_class->finalize = demo_tagged_entry_tag_finalize;
+ object_class->set_property = demo_tagged_entry_tag_set_property;
+ object_class->get_property = demo_tagged_entry_tag_get_property;
+
+ widget_class->measure = demo_tagged_entry_tag_measure;
+ widget_class->size_allocate = demo_tagged_entry_tag_size_allocate;
+
+ signals[SIGNAL_CLICKED] =
+ g_signal_new ("clicked",
+ DEMO_TYPE_TAGGED_ENTRY_TAG,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+ signals[SIGNAL_BUTTON_CLICKED] =
+ g_signal_new ("button-clicked",
+ DEMO_TYPE_TAGGED_ENTRY_TAG,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ g_object_class_install_property (object_class, PROP_LABEL,
+ g_param_spec_string ("label", "Label", "Label",
+ NULL, G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_HAS_CLOSE_BUTTON,
+ g_param_spec_boolean ("has-close-button", "Has close button", "Whether this tag has a close button",
+ FALSE, G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
+
+ gtk_widget_class_set_css_name (widget_class, "tag");
+}
+
+DemoTaggedEntryTag *
+demo_tagged_entry_tag_new (const char *label)
+{
+ return DEMO_TAGGED_ENTRY_TAG (g_object_new (DEMO_TYPE_TAGGED_ENTRY_TAG,
+ "label", label,
+ NULL));
+}
+
+const char *
+demo_tagged_entry_tag_get_label (DemoTaggedEntryTag *tag)
+{
+ g_return_val_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag), NULL);
+
+ return gtk_label_get_label (GTK_LABEL (tag->label));
+}
+
+void
+demo_tagged_entry_tag_set_label (DemoTaggedEntryTag *tag,
+ const char *label)
+{
+ g_return_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag));
+
+ gtk_label_set_label (GTK_LABEL (tag->label), label);
+}
+
+static void
+on_button_clicked (GtkButton *button,
+ DemoTaggedEntryTag *tag)
+{
+ g_signal_emit (tag, signals[SIGNAL_BUTTON_CLICKED], 0);
+}
+
+void
+demo_tagged_entry_tag_set_has_close_button (DemoTaggedEntryTag *tag,
+ gboolean has_close_button)
+{
+ g_return_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag));
+
+ if ((tag->button != NULL) == has_close_button)
+ return;
+
+ if (!has_close_button && tag->button)
+ {
+ gtk_container_remove (GTK_CONTAINER (tag->box), tag->button);
+ tag->button = NULL;
+ }
+ else if (has_close_button && tag->button == NULL)
+ {
+ GtkWidget *image;
+
+ image = gtk_image_new_from_icon_name ("window-close-symbolic");
+ tag->button = gtk_button_new ();
+ gtk_container_add (GTK_CONTAINER (tag->button), image);
+ gtk_widget_set_halign (tag->button, GTK_ALIGN_CENTER);
+ gtk_widget_set_valign (tag->button, GTK_ALIGN_CENTER);
+ gtk_button_set_relief (GTK_BUTTON (tag->button), GTK_RELIEF_NONE);
+ gtk_container_add (GTK_CONTAINER (tag->box), tag->button);
+ g_signal_connect (tag->button, "clicked", G_CALLBACK (on_button_clicked), tag);
+ }
+
+ g_object_notify (G_OBJECT (tag), "has-close-button");
+}
+
+gboolean
+demo_tagged_entry_tag_get_has_close_button (DemoTaggedEntryTag *tag)
+{
+ g_return_val_if_fail (DEMO_IS_TAGGED_ENTRY_TAG (tag), FALSE);
+
+ return tag->button != NULL;
+}
diff --git a/demos/gtk-demo/demotaggedentry.h b/demos/gtk-demo/demotaggedentry.h
new file mode 100644
index 0000000000..aef2c34509
--- /dev/null
+++ b/demos/gtk-demo/demotaggedentry.h
@@ -0,0 +1,89 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2019 Red Hat, Inc.
+ *
+ * Authors:
+ * - Matthias Clasen <mclasen redhat com>
+ *
+ * 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 __DEMO_TAGGED_ENTRY_H__
+#define __DEMO_TAGGED_ENTRY_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define DEMO_TYPE_TAGGED_ENTRY (demo_tagged_entry_get_type ())
+#define DEMO_TAGGED_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DEMO_TYPE_TAGGED_ENTRY,
DemoTaggedEntry))
+#define DEMO_TAGGED_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DEMO_TYPE_TAGGED_ENTRY,
DemoTaggedEntryClass))
+#define DEMO_IS_TAGGED_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DEMO_TYPE_TAGGED_ENTRY))
+#define DEMO_IS_TAGGED_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DEMO_TYPE_TAGGED_ENTRY))
+#define DEMO_TAGGED_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DEMO_TYPE_TAGGED_ENTRY,
DemoTaggedEntryClass))
+
+typedef struct _DemoTaggedEntry DemoTaggedEntry;
+typedef struct _DemoTaggedEntryClass DemoTaggedEntryClass;
+
+struct _DemoTaggedEntry
+{
+ GtkWidget parent;
+};
+
+struct _DemoTaggedEntryClass
+{
+ GtkWidgetClass parent_class;
+};
+
+#define DEMO_TYPE_TAGGED_ENTRY_TAG (demo_tagged_entry_tag_get_type ())
+#define DEMO_TAGGED_ENTRY_TAG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
DEMO_TYPE_TAGGED_ENTRY_TAG, DemoTaggedEntryTag))
+#define DEMO_TAGGED_ENTRY_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
DEMO_TYPE_TAGGED_ENTRY_TAG, DemoTaggedEntryTag))
+#define DEMO_IS_TAGGED_ENTRY_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
DEMO_TYPE_TAGGED_ENTRY_TAG))
+#define DEMO_IS_TAGGED_ENTRY_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
DEMO_TYPE_TAGGED_ENTRY_TAG))
+#define DEMO_TAGGED_ENTRY_TAG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
DEMO_TYPE_TAGGED_ENTRY_TAG, DemoTaggedEntryTagClass))
+
+typedef struct _DemoTaggedEntryTag DemoTaggedEntryTag;
+typedef struct _DemoTaggedEntryTagClass DemoTaggedEntryTagClass;
+
+
+GType demo_tagged_entry_get_type (void) G_GNUC_CONST;
+GType demo_tagged_entry_tag_get_type (void) G_GNUC_CONST;
+
+GtkWidget * demo_tagged_entry_new (void);
+
+void demo_tagged_entry_add_tag (DemoTaggedEntry *entry,
+ GtkWidget *tag);
+
+void demo_tagged_entry_insert_tag_after (DemoTaggedEntry *entry,
+ GtkWidget *tag,
+ GtkWidget *sibling);
+
+void demo_tagged_entry_remove_tag (DemoTaggedEntry *entry,
+ GtkWidget *tag);
+
+DemoTaggedEntryTag *
+ demo_tagged_entry_tag_new (const char *label);
+
+const char * demo_tagged_entry_tag_get_label (DemoTaggedEntryTag *tag);
+
+void demo_tagged_entry_tag_set_label (DemoTaggedEntryTag *tag,
+ const char *label);
+
+gboolean demo_tagged_entry_tag_get_has_close_button (DemoTaggedEntryTag *tag);
+
+void demo_tagged_entry_tag_set_has_close_button (DemoTaggedEntryTag *tag,
+ gboolean has_close_button);
+
+G_END_DECLS
+
+#endif /* __DEMO_TAGGED_ENTRY_H__ */
diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index e331001abb..86fd6d6b80 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -65,6 +65,7 @@ demos = files([
'spinner.c',
'stack.c',
'tabs.c',
+ 'tagged_entry.c',
'textmask.c',
'textview.c',
'textscroll.c',
@@ -76,7 +77,13 @@ demos = files([
gtkdemo_deps = [ libgtk_dep, ]
-extra_demo_sources = files(['main.c', 'gtkfishbowl.c', 'fontplane.c', 'gtkgears.c', 'puzzlepiece.c',
'bluroverlay.c'])
+extra_demo_sources = files(['main.c',
+ 'gtkfishbowl.c',
+ 'fontplane.c',
+ 'gtkgears.c',
+ 'puzzlepiece.c',
+ 'bluroverlay.c',
+ 'demotaggedentry.c'])
if harfbuzz_dep.found() and pangoft_dep.found()
demos += files('font_features.c')
diff --git a/demos/gtk-demo/tagged_entry.c b/demos/gtk-demo/tagged_entry.c
new file mode 100644
index 0000000000..6405d19643
--- /dev/null
+++ b/demos/gtk-demo/tagged_entry.c
@@ -0,0 +1,112 @@
+/* Entry/Tagged Entry
+ *
+ * This example shows how to build a complex composite
+ * entry using GtkText, outside of GTK.
+ *
+ * This tagged entry can display tags and other widgets
+ * inside the entry area.
+ */
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include "demotaggedentry.h"
+
+static GtkWidget *spinner = NULL;
+
+static void
+closed_cb (DemoTaggedEntryTag *tag, DemoTaggedEntry *entry)
+{
+ demo_tagged_entry_remove_tag (entry, GTK_WIDGET (tag));
+}
+
+static void
+add_tag (GtkButton *button, DemoTaggedEntry *entry)
+{
+ DemoTaggedEntryTag *tag;
+
+ tag = demo_tagged_entry_tag_new ("Blue");
+ gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (tag)), "blue");
+ demo_tagged_entry_tag_set_has_close_button (tag, TRUE);
+ g_signal_connect (tag, "button-clicked", G_CALLBACK (closed_cb), entry);
+
+ if (spinner == NULL)
+ demo_tagged_entry_add_tag (entry, GTK_WIDGET (tag));
+ else
+ demo_tagged_entry_insert_tag_after (entry, GTK_WIDGET (tag), gtk_widget_get_prev_sibling (spinner));
+}
+
+static void
+toggle_spinner (GtkCheckButton *button, DemoTaggedEntry *entry)
+{
+ if (spinner)
+ {
+ demo_tagged_entry_remove_tag (entry, spinner);
+ spinner = NULL;
+ }
+ else
+ {
+ spinner = gtk_spinner_new ();
+ gtk_spinner_start (GTK_SPINNER (spinner));
+ demo_tagged_entry_add_tag (entry, spinner);
+ }
+}
+
+GtkWidget *
+do_tagged_entry (GtkWidget *do_widget)
+{
+ static GtkWidget *window = NULL;
+ GtkWidget *box;
+ GtkWidget *box2;
+ GtkWidget *header;
+ GtkWidget *entry;
+ GtkWidget *button;
+
+ if (!window)
+ {
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_display (GTK_WINDOW (window),
+ gtk_widget_get_display (do_widget));
+ header = gtk_header_bar_new ();
+ gtk_header_bar_set_title (GTK_HEADER_BAR (header), "A tagged entry");
+ gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), FALSE);
+ gtk_window_set_titlebar (GTK_WINDOW (window), header);
+ gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
+ gtk_window_set_deletable (GTK_WINDOW (window), FALSE);
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (gtk_widget_destroyed), &window);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ g_object_set (box, "margin", 18, NULL);
+ gtk_container_add (GTK_CONTAINER (window), box);
+
+ entry = demo_tagged_entry_new ();
+ gtk_container_add (GTK_CONTAINER (box), entry);
+
+ box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_widget_set_halign (box2, GTK_ALIGN_END);
+ gtk_container_add (GTK_CONTAINER (box), box2);
+
+ button = gtk_button_new_with_mnemonic ("Add _Tag");
+ g_signal_connect (button, "clicked", G_CALLBACK (add_tag), entry);
+ gtk_container_add (GTK_CONTAINER (box2), button);
+
+ button = gtk_check_button_new_with_mnemonic ("_Spinner");
+ g_signal_connect (button, "toggled", G_CALLBACK (toggle_spinner), entry);
+ gtk_container_add (GTK_CONTAINER (box2), button);
+
+ button = gtk_button_new_with_mnemonic ("_Done");
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action");
+ g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
+ gtk_header_bar_pack_end (GTK_HEADER_BAR (header), button);
+
+ gtk_widget_set_can_default (button, TRUE);
+ gtk_window_set_default (GTK_WINDOW (window), button);
+ }
+
+ if (!gtk_widget_get_visible (window))
+ gtk_widget_show (window);
+ else
+ gtk_widget_destroy (window);
+
+ return window;
+}
diff --git a/demos/gtk-demo/tagstyle.css b/demos/gtk-demo/tagstyle.css
new file mode 100644
index 0000000000..c470a5e2e2
--- /dev/null
+++ b/demos/gtk-demo/tagstyle.css
@@ -0,0 +1,19 @@
+tag {
+ margin: 4px;
+ padding: 4px;
+ border-radius: 4px;
+ background: lightskyblue;
+}
+tag box {
+ border-spacing: 4px;
+}
+tag label,
+tag image {
+ color: white;
+}
+tag button {
+ min-height: 0;
+ min-width: 0;
+ padding: 0;
+ border: 1px solid white;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]