[gtk/tagged-entry] wip: Add a tagged entry
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/tagged-entry] wip: Add a tagged entry
- Date: Fri, 22 Feb 2019 17:30:56 +0000 (UTC)
commit c2429bfa9c034ad7b3ac5495a07b41532ed4db6d
Author: Matthias Clasen <mclasen redhat com>
Date: Fri Feb 22 12:29:22 2019 -0500
wip: Add a tagged entry
Not polished in any form, yet.
gtk/gtk.h | 1 +
gtk/gtktaggedentry.c | 524 +++++++++++++++++++++++++++++++++++++++++
gtk/gtktaggedentry.h | 104 ++++++++
gtk/meson.build | 1 +
gtk/theme/Adwaita/_common.scss | 27 +++
tests/test-tagged-entry.c | 113 +++++++++
6 files changed, 770 insertions(+)
---
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 62a3bb3d7f..209dc4a486 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -212,6 +212,7 @@
#include <gtk/gtkstylecontext.h>
#include <gtk/gtkstyleprovider.h>
#include <gtk/gtkswitch.h>
+#include <gtk/gtktaggedentry.h>
#include <gtk/gtktext.h>
#include <gtk/gtktextbuffer.h>
#include <gtk/gtktextchild.h>
diff --git a/gtk/gtktaggedentry.c b/gtk/gtktaggedentry.c
new file mode 100644
index 0000000000..f384f5eccc
--- /dev/null
+++ b/gtk/gtktaggedentry.c
@@ -0,0 +1,524 @@
+/* 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 "gtktaggedentry.h"
+
+#include "gtkaccessible.h"
+#include "gtktextprivate.h"
+#include "gtkeditable.h"
+#include "gtklabel.h"
+#include "gtkbutton.h"
+#include "gtkbox.h"
+#include "gtkimage.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
+#include "gtkmarshalers.h"
+#include "gtkstylecontext.h"
+#include "gtkgesturemultipress.h"
+
+#include "a11y/gtkentryaccessible.h"
+
+/**
+ * SECTION:gtktaggedhentry
+ * @Short_description: An entry that can show tags
+ * @Title: GtkTaggedEntry
+ *
+ * #GtkTaggedEntry is an entry that can show tags in
+ * addition to text.
+ */
+
+typedef struct {
+ GtkWidget *box;
+ GtkWidget *entry;
+} GtkTaggedEntryPrivate;
+
+static void gtk_tagged_entry_editable_init (GtkEditableInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkTaggedEntry, gtk_tagged_entry, GTK_TYPE_WIDGET,
+ G_ADD_PRIVATE (GtkTaggedEntry)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, gtk_tagged_entry_editable_init))
+
+static void
+gtk_tagged_entry_init (GtkTaggedEntry *entry)
+{
+ GtkTaggedEntryPrivate *priv = gtk_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
+gtk_tagged_entry_dispose (GObject *object)
+{
+ GtkTaggedEntry *entry = GTK_TAGGED_ENTRY (object);
+ GtkTaggedEntryPrivate *priv = gtk_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 (gtk_tagged_entry_parent_class)->dispose (object);
+}
+
+static void
+gtk_tagged_entry_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (gtk_tagged_entry_parent_class)->finalize (object);
+}
+
+static void
+gtk_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
+gtk_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
+gtk_tagged_entry_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ GtkTaggedEntry *entry = GTK_TAGGED_ENTRY (widget);
+ GtkTaggedEntryPrivate *priv = gtk_tagged_entry_get_instance_private (entry);
+
+ gtk_widget_measure (priv->box, orientation, for_size,
+ minimum, natural,
+ minimum_baseline, natural_baseline);
+}
+
+static void
+gtk_tagged_entry_size_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
+{
+ GtkTaggedEntry *entry = GTK_TAGGED_ENTRY (widget);
+ GtkTaggedEntryPrivate *priv = gtk_tagged_entry_get_instance_private (entry);
+
+ gtk_widget_size_allocate (priv->box,
+ &(GtkAllocation) { 0, 0, width, height },
+ baseline);
+}
+
+static void
+gtk_tagged_entry_grab_focus (GtkWidget *widget)
+{
+ GtkTaggedEntry *entry = GTK_TAGGED_ENTRY (widget);
+ GtkTaggedEntryPrivate *priv = gtk_tagged_entry_get_instance_private (entry);
+
+ gtk_widget_grab_focus (priv->entry);
+}
+
+static void
+gtk_tagged_entry_class_init (GtkTaggedEntryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gtk_tagged_entry_dispose;
+ object_class->finalize = gtk_tagged_entry_finalize;
+ object_class->get_property = gtk_tagged_entry_get_property;
+ object_class->set_property = gtk_tagged_entry_set_property;
+
+ widget_class->measure = gtk_tagged_entry_measure;
+ widget_class->size_allocate = gtk_tagged_entry_size_allocate;
+ widget_class->grab_focus = gtk_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, I_("entry"));
+}
+
+static GtkEditable *
+gtk_tagged_entry_get_delegate (GtkEditable *editable)
+{
+ GtkTaggedEntry *entry = GTK_TAGGED_ENTRY (editable);
+ GtkTaggedEntryPrivate *priv = gtk_tagged_entry_get_instance_private (entry);
+
+ return GTK_EDITABLE (priv->entry);
+}
+
+static void
+gtk_tagged_entry_editable_init (GtkEditableInterface *iface)
+{
+ iface->get_delegate = gtk_tagged_entry_get_delegate;
+}
+
+/**
+ * gtk_tagged_entry_new:
+ *
+ * Creates a #GtkTaggedEntry.
+ *
+ * Returns: a new #GtkTaggedEntry
+ */
+GtkWidget *
+gtk_tagged_entry_new (void)
+{
+ return GTK_WIDGET (g_object_new (GTK_TYPE_TAGGED_ENTRY, NULL));
+}
+
+void
+gtk_tagged_entry_add_tag (GtkTaggedEntry *entry,
+ GtkWidget *tag)
+{
+ GtkTaggedEntryPrivate *priv = gtk_tagged_entry_get_instance_private (entry);
+
+ g_return_if_fail (GTK_IS_TAGGED_ENTRY (entry));
+
+ gtk_container_add (GTK_CONTAINER (priv->box), tag);
+}
+
+void
+gtk_tagged_entry_insert_tag (GtkTaggedEntry *entry,
+ GtkWidget *tag,
+ int position)
+{
+ GtkTaggedEntryPrivate *priv = gtk_tagged_entry_get_instance_private (entry);
+
+ g_return_if_fail (GTK_IS_TAGGED_ENTRY (entry));
+
+ if (position == -1)
+ gtk_container_add (GTK_CONTAINER (priv->box), tag);
+ else
+ {
+ GList *children = gtk_container_get_children (GTK_CONTAINER (priv->box));
+ GtkWidget *sibling = g_list_nth_data (children, position);
+ gtk_box_insert_child_after (GTK_BOX (priv->box), tag, sibling);
+ g_list_free (children);
+ }
+}
+
+void
+gtk_tagged_entry_remove_tag (GtkTaggedEntry *entry,
+ GtkWidget *tag)
+{
+ GtkTaggedEntryPrivate *priv = gtk_tagged_entry_get_instance_private (entry);
+
+ g_return_if_fail (GTK_IS_TAGGED_ENTRY (entry));
+
+ gtk_container_remove (GTK_CONTAINER (priv->box), tag);
+}
+
+#define GTK_TYPE_ENTRY_TAG (gtk_entry_tag_get_type ())
+#define GTK_ENTRY_TAG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_ENTRY_TAG,
GtkEntryTag))
+#define GTK_ENTRY_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_ENTRY_TAG,
GtkEntryTag))
+#define GTK_IS_ENTRY_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_ENTRY_TAG))
+#define GTK_IS_ENTRY_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ENTRY_TAG))
+#define GTK_ENTRY_TAG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_ENTRY_TAG,
GtkEntryTagClass))
+
+typedef struct _GtkEntryTag GtkEntryTag;
+typedef struct _GtkEntryTagClass GtkEntryTagClass;
+
+struct _GtkEntryTag
+{
+ GtkWidget parent;
+ GtkWidget *box;
+ GtkWidget *label;
+ GtkWidget *button;
+
+ gboolean has_close_button;
+ char *style;
+};
+
+struct _GtkEntryTagClass
+{
+ 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 (GtkEntryTag, gtk_entry_tag, GTK_TYPE_WIDGET)
+
+static void
+on_released (GtkGestureMultiPress *gesture,
+ int n_press,
+ double x,
+ double y,
+ GtkEntryTag *tag)
+{
+ g_signal_emit (tag, signals[SIGNAL_CLICKED], 0);
+}
+
+static void
+gtk_entry_tag_init (GtkEntryTag *tag)
+{
+ GtkGesture *gesture;
+
+ 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));
+}
+
+static void
+gtk_entry_tag_dispose (GObject *object)
+{
+ GtkEntryTag *tag = GTK_ENTRY_TAG (object);
+
+ g_clear_pointer (&tag->box, gtk_widget_unparent);
+
+ G_OBJECT_CLASS (gtk_entry_tag_parent_class)->dispose (object);
+}
+
+static void
+gtk_entry_tag_finalize (GObject *object)
+{
+ GtkEntryTag *tag = GTK_ENTRY_TAG (object);
+
+ g_clear_pointer (&tag->box, gtk_widget_unparent);
+ g_clear_pointer (&tag->style, g_free);
+
+ G_OBJECT_CLASS (gtk_entry_tag_parent_class)->finalize (object);
+}
+
+static void
+gtk_entry_tag_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkEntryTag *tag = GTK_ENTRY_TAG (object);
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ gtk_entry_tag_set_label (tag, g_value_get_string (value));
+ break;
+
+ case PROP_HAS_CLOSE_BUTTON:
+ gtk_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
+gtk_entry_tag_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkEntryTag *tag = GTK_ENTRY_TAG (object);
+
+ switch (prop_id)
+ {
+ case PROP_LABEL:
+ g_value_set_string (value, gtk_entry_tag_get_label (tag));
+ break;
+
+ case PROP_HAS_CLOSE_BUTTON:
+ g_value_set_boolean (value, gtk_entry_tag_get_has_close_button (tag));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_entry_tag_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ GtkEntryTag *tag = GTK_ENTRY_TAG (widget);
+
+ gtk_widget_measure (tag->box, orientation, for_size,
+ minimum, natural,
+ minimum_baseline, natural_baseline);
+}
+
+static void
+gtk_entry_tag_size_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
+{
+ GtkEntryTag *tag = GTK_ENTRY_TAG (widget);
+
+ gtk_widget_size_allocate (tag->box,
+ &(GtkAllocation) { 0, 0, width, height },
+ baseline);
+}
+
+static void
+gtk_entry_tag_class_init (GtkEntryTagClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+ object_class->dispose = gtk_entry_tag_dispose;
+ object_class->finalize = gtk_entry_tag_finalize;
+ object_class->set_property = gtk_entry_tag_set_property;
+ object_class->get_property = gtk_entry_tag_get_property;
+
+ widget_class->measure = gtk_entry_tag_measure;
+ widget_class->size_allocate = gtk_entry_tag_size_allocate;
+
+ signals[SIGNAL_CLICKED] =
+ g_signal_new ("clicked",
+ GTK_TYPE_ENTRY_TAG,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+ signals[SIGNAL_BUTTON_CLICKED] =
+ g_signal_new ("button-clicked",
+ GTK_TYPE_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, GTK_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, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
+
+ gtk_widget_class_set_css_name (widget_class, I_("tag"));
+}
+
+GtkEntryTag *
+gtk_entry_tag_new (const char *label)
+{
+ return GTK_ENTRY_TAG (g_object_new (GTK_TYPE_ENTRY_TAG, "label", label, NULL));
+}
+
+const char *
+gtk_entry_tag_get_label (GtkEntryTag *tag)
+{
+ g_return_val_if_fail (GTK_IS_ENTRY_TAG (tag), NULL);
+
+ return gtk_label_get_label (GTK_LABEL (tag->label));
+}
+
+void
+gtk_entry_tag_set_label (GtkEntryTag *tag,
+ const char *label)
+{
+ g_return_if_fail (GTK_IS_ENTRY_TAG (tag));
+
+ gtk_label_set_label (GTK_LABEL (tag->label), label);
+}
+
+static void
+on_button_clicked (GtkButton *button, GtkEntryTag *tag)
+{
+ g_signal_emit (tag, signals[SIGNAL_BUTTON_CLICKED], 0);
+}
+
+void
+gtk_entry_tag_set_has_close_button (GtkEntryTag *tag,
+ gboolean has_close_button)
+{
+ g_return_if_fail (GTK_IS_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");
+ gtk_image_set_pixel_size (GTK_IMAGE (image), 16);
+ tag->button = gtk_button_new ();
+ gtk_container_add (GTK_CONTAINER (tag->button), image);
+ 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
+gtk_entry_tag_get_has_close_button (GtkEntryTag *tag)
+{
+ g_return_val_if_fail (GTK_IS_ENTRY_TAG (tag), FALSE);
+
+ return tag->button != NULL;
+}
diff --git a/gtk/gtktaggedentry.h b/gtk/gtktaggedentry.h
new file mode 100644
index 0000000000..e7a98df968
--- /dev/null
+++ b/gtk/gtktaggedentry.h
@@ -0,0 +1,104 @@
+/* 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 __GTK_TAGGED_ENTRY_H__
+#define __GTK_TAGGED_ENTRY_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtkentry.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_TAGGED_ENTRY (gtk_tagged_entry_get_type ())
+#define GTK_TAGGED_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TAGGED_ENTRY,
GtkTaggedEntry))
+#define GTK_TAGGED_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TAGGED_ENTRY,
GtkTaggedEntryClass))
+#define GTK_IS_TAGGED_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TAGGED_ENTRY))
+#define GTK_IS_TAGGED_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TAGGED_ENTRY))
+#define GTK_TAGGED_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TAGGED_ENTRY,
GtkTaggedEntryClass))
+
+typedef struct _GtkTaggedEntry GtkTaggedEntry;
+typedef struct _GtkTaggedEntryClass GtkTaggedEntryClass;
+
+struct _GtkTaggedEntry
+{
+ GtkWidget parent;
+};
+
+struct _GtkTaggedEntryClass
+{
+ GtkWidgetClass parent_class;
+};
+
+#define GTK_TYPE_ENTRY_TAG (gtk_entry_tag_get_type ())
+#define GTK_ENTRY_TAG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_ENTRY_TAG,
GtkEntryTag))
+#define GTK_ENTRY_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_ENTRY_TAG,
GtkEntryTag))
+#define GTK_IS_ENTRY_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_ENTRY_TAG))
+#define GTK_IS_ENTRY_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ENTRY_TAG))
+#define GTK_ENTRY_TAG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_ENTRY_TAG,
GtkEntryTagClass))
+
+typedef struct _GtkEntryTag GtkEntryTag;
+typedef struct _GtkEntryTagClass GtkEntryTagClass;
+
+
+GDK_AVAILABLE_IN_ALL
+GType gtk_tagged_entry_get_type (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_ALL
+GType gtk_entry_tag_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GtkWidget * gtk_tagged_entry_new (void);
+
+
+GDK_AVAILABLE_IN_ALL
+void gtk_tagged_entry_add_tag (GtkTaggedEntry *entry,
+ GtkWidget *widget);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_tagged_entry_insert_tag (GtkTaggedEntry *entry,
+ GtkWidget *widget,
+ int position);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_tagged_entry_remove_tag (GtkTaggedEntry *entry,
+ GtkWidget *widget);
+
+GDK_AVAILABLE_IN_ALL
+GtkEntryTag * gtk_entry_tag_new (const char *label);
+
+GDK_AVAILABLE_IN_ALL
+const char * gtk_entry_tag_get_label (GtkEntryTag *tag);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_entry_tag_set_label (GtkEntryTag *tag,
+ const char *label);
+
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_entry_tag_get_has_close_button (GtkEntryTag *tag);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_entry_tag_set_has_close_button (GtkEntryTag *tag,
+ gboolean has_close_button);
+
+G_END_DECLS
+
+#endif /* __GTK_TAGGED_ENTRY_H__ */
diff --git a/gtk/meson.build b/gtk/meson.build
index 85f75c8c51..ffdbf0274e 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -352,6 +352,7 @@ gtk_public_sources = files([
'gtkstylecontext.c',
'gtkstyleprovider.c',
'gtkswitch.c',
+ 'gtktaggedentry.c',
'gtktestutils.c',
'gtktext.c',
'gtktextattributes.c',
diff --git a/gtk/theme/Adwaita/_common.scss b/gtk/theme/Adwaita/_common.scss
index aa8acc88e5..52e1fdddbc 100644
--- a/gtk/theme/Adwaita/_common.scss
+++ b/gtk/theme/Adwaita/_common.scss
@@ -4704,3 +4704,30 @@ popover.emoji-completion contents row box {
popover.emoji-completion .emoji:hover {
background-color: $popover_hover_color;
}
+
+entry tag {
+ background: blue;
+ border-radius: 12px;
+ margin: 6px 3px;
+}
+
+entry tag label {
+ color: white;
+}
+
+entry tag button {
+ color: white;
+ padding: 0;
+}
+
+entry tag box {
+ border-spacing: 4px;
+}
+
+entry tag label.first-child {
+ margin-left: 10px;
+}
+
+entry tag label.last-child {
+ margin-right: 10px;
+}
diff --git a/tests/test-tagged-entry.c b/tests/test-tagged-entry.c
new file mode 100644
index 0000000000..045fe8a255
--- /dev/null
+++ b/tests/test-tagged-entry.c
@@ -0,0 +1,113 @@
+#include <gtk/gtk.h>
+
+static GtkEntryTag *toggle_tag;
+
+static void
+on_tag_clicked (GtkEntryTag *tag,
+ gpointer useless)
+{
+ g_print ("tag clicked: %s\n", gtk_entry_tag_get_label (tag));
+}
+
+static void
+on_tag_button_clicked (GtkEntryTag *tag,
+ GtkTaggedEntry *entry)
+{
+ g_print ("tag button clicked: %s\n", gtk_entry_tag_get_label (tag));
+ gtk_tagged_entry_remove_tag (entry, tag);
+}
+
+static void
+on_toggle_visible (GtkButton *button,
+ GtkWidget *entry)
+{
+ gboolean active;
+
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+
+ g_print ("%s tagged entry\n", active ? "show" : "hide");
+ gtk_widget_set_visible (entry, active);
+}
+
+static void
+on_toggle_tag (GtkButton *button,
+ GtkTaggedEntry *entry)
+{
+ gboolean active;
+
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+
+ if (active)
+ {
+ g_print ("adding tag 'Toggle Tag'\n");
+ gtk_tagged_entry_insert_tag (entry, toggle_tag, 0);
+ }
+ else
+ {
+ g_print ("removing tag 'Toggle Tag'\n");
+ gtk_tagged_entry_remove_tag (entry, toggle_tag);
+ }
+}
+
+gint
+main (gint argc,
+ gchar ** argv)
+{
+ GtkWidget *window, *box, *entry, *toggle_visible_button, *toggle_tag_button;
+ GtkEntryTag *tag;
+
+ gtk_init ();
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_size_request (window, 300, 20);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (window), box);
+
+ entry = GTK_WIDGET (gtk_tagged_entry_new ());
+ gtk_container_add (GTK_CONTAINER (box), entry);
+
+ tag = gtk_entry_tag_new ("Blah1");
+ g_object_set (tag, "has-close-button", TRUE, NULL);
+ g_signal_connect(tag, "clicked", G_CALLBACK (on_tag_clicked), NULL);
+ g_signal_connect(tag, "button-clicked", G_CALLBACK (on_tag_button_clicked), entry);
+
+ gtk_tagged_entry_add_tag (GTK_TAGGED_ENTRY (entry), tag);
+
+ tag = gtk_entry_tag_new ("Blah2");
+ g_object_set (tag, "has-close-button", TRUE, NULL);
+ g_signal_connect(tag, "clicked", G_CALLBACK (on_tag_clicked), NULL);
+ g_signal_connect(tag, "button-clicked", G_CALLBACK (on_tag_button_clicked), entry);
+ gtk_tagged_entry_insert_tag (GTK_TAGGED_ENTRY (entry), tag, -1);
+
+ tag = gtk_entry_tag_new ("Blah3");
+ g_signal_connect(tag, "clicked", G_CALLBACK (on_tag_clicked), NULL);
+ g_signal_connect(tag, "button-clicked", G_CALLBACK (on_tag_button_clicked), entry);
+ gtk_tagged_entry_insert_tag (GTK_TAGGED_ENTRY (entry), tag, 0);
+
+ toggle_visible_button = gtk_toggle_button_new_with_label ("Visible");
+ gtk_widget_set_vexpand (toggle_visible_button, TRUE);
+ gtk_widget_set_valign (toggle_visible_button, GTK_ALIGN_END);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle_visible_button), TRUE);
+ g_signal_connect (toggle_visible_button, "toggled",
+ G_CALLBACK (on_toggle_visible), entry);
+ gtk_container_add (GTK_CONTAINER (box), toggle_visible_button);
+
+ gtk_tagged_entry_add_tag (entry, g_object_new (GTK_TYPE_SPINNER, "active", TRUE, NULL));
+ toggle_tag = gtk_entry_tag_new ("Toggle Tag");
+ g_signal_connect(toggle_tag, "clicked", G_CALLBACK (on_tag_clicked), NULL);
+ g_signal_connect(toggle_tag, "button-clicked", G_CALLBACK (on_tag_button_clicked), NULL);
+ g_object_ref_sink (toggle_tag);
+
+ toggle_tag_button = gtk_toggle_button_new_with_label ("Toggle Tag");
+ g_signal_connect (toggle_tag_button, "toggled",
+ G_CALLBACK (on_toggle_tag), entry);
+ gtk_container_add (GTK_CONTAINER (box), toggle_tag_button);
+
+ gtk_widget_show (window);
+ gtk_main ();
+
+ gtk_widget_destroy (window);
+
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]