[libadwaita/ebassi/tagged-entry: 1/2] Add AdwTaggedEntry




commit 9a8ac6c5fcea2e83b27abaf1a4ef9f3c7efb55c7
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Fri Feb 4 14:09:08 2022 +0000

    Add AdwTaggedEntry
    
    A "tagged entry" is an entry widget that allows defining additional "tags"
    to describe the contents of the entry. It can be used for showing
    refinements to a search entry, or for introducing tags in a description
    form.
    
    Current existing users of this type of UI component are GNOME Photos,
    Nautilus, and Epiphany; these projects use the tagged entry widget provided
    by the copy-and-paste library "libgd".

 src/adw-tagged-entry.c | 649 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/adw-tagged-entry.h |  66 +++++
 src/adwaita.h          |   1 +
 src/meson.build        |   2 +
 4 files changed, 718 insertions(+)
---
diff --git a/src/adw-tagged-entry.c b/src/adw-tagged-entry.c
new file mode 100644
index 00000000..4f794b09
--- /dev/null
+++ b/src/adw-tagged-entry.c
@@ -0,0 +1,649 @@
+/* adw-tagged-entry.c: Tagged entry widget
+ *
+ * SPDX-FileCopyrightText: 2022 Emmanuele Bassi
+ * SPDX-FileCopyrightText: 2019 Matthias Clasen
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "adw-tagged-entry.h"
+
+/**
+ * AdwTag:
+ *
+ * A tag inside a tagged entry.
+ */
+struct _AdwTag
+{
+  GObject parent_instance;
+
+  AdwTaggedEntry *entry;
+
+  char *name;
+  char *label;
+
+  GtkWidget *tag;
+  GtkWidget *tag_label;
+  GtkWidget *tag_close;
+
+  gboolean show_close;
+};
+
+/**
+ * AdwTaggedEntry:
+ *
+ * An entry that allows you to have tags near the text.
+ *
+ * ## AdwTaggedEntry as GtkBuildable
+ *
+ * You can include tags directly inside the UI definition of a tagged entry
+ * by using the `<child>` element to add objects of type `AdwTag`; for
+ * instance, the following definition:
+ *
+ * ```xml
+ * <object class="AdwTaggedEntry">
+ *   <child>
+ *     <object class="AdwTag">
+ *       <property name="name">first-tag</property>
+ *       <property name="label">First Tag</property>
+ *       <property name="show-close">False</property>
+ *     </object>
+ *   </child>
+ * </object>
+ * ```
+ *
+ * while create an `AdwTaggedEntry` with a single tag, whose label is set to
+ * "First Tag"; the tag will not have a "close" button.
+ *
+ * ## CSS nodes
+ *
+ * `AdwTaggedEntry` has a single CSS node with the name `entry` and the
+ * CSS class `tagged`.
+ */
+struct _AdwTaggedEntry
+{
+  GtkWidget parent_instance;
+
+  GtkWidget *text;
+  GtkWidget *tags_box;
+
+  GListModel *tags;
+};
+
+enum
+{
+  PROP_PLACEHOLDER_TEXT = 1,
+  N_PROPS
+};
+
+static void buildable_iface_init (GtkBuildableIface *iface);
+static void editable_iface_init (GtkEditableInterface *iface);
+
+static GtkBuildableIface *parent_buildable_iface;
+
+static GParamSpec *entry_props[N_PROPS];
+
+G_DEFINE_TYPE_WITH_CODE (AdwTaggedEntry, adw_tagged_entry, GTK_TYPE_WIDGET,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init)
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, editable_iface_init))
+
+static GtkEditable *
+adw_tagged_entry_editable_get_delegate (GtkEditable *editable)
+{
+  AdwTaggedEntry *self = ADW_TAGGED_ENTRY (editable);
+
+  return GTK_EDITABLE (self->text);
+}
+
+static void
+editable_iface_init (GtkEditableInterface *iface)
+{
+  iface->get_delegate = adw_tagged_entry_editable_get_delegate;
+}
+
+static AdwTag *
+adw_tagged_entry_add_tag_internal (AdwTaggedEntry *self,
+                                   AdwTag         *tag,
+                                   gboolean        unref_tag)
+{
+  tag->entry = self;
+
+  g_list_store_append (G_LIST_STORE (self->tags), tag);
+
+  gtk_flow_box_append (GTK_FLOW_BOX (self->tags_box), tag->tag);
+
+  if (unref_tag)
+    g_object_unref (tag);
+
+  return tag;
+}
+
+static void
+adw_tagged_entry_buildable_add_child (GtkBuildable *buildable,
+                                      GtkBuilder   *builder,
+                                      GObject      *child,
+                                      const char   *type)
+{
+  if (ADW_IS_TAG (child)) {
+    adw_tagged_entry_add_tag_internal (ADW_TAGGED_ENTRY (buildable), ADW_TAG (child), FALSE);
+  } else {
+    parent_buildable_iface->add_child (buildable, builder, child, type);
+  }
+}
+
+static void
+buildable_iface_init (GtkBuildableIface *iface)
+{
+  parent_buildable_iface = g_type_interface_peek_parent (iface);
+
+  iface->add_child = adw_tagged_entry_buildable_add_child;
+}
+
+static void
+adw_tagged_entry_set_property (GObject      *gobject,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  AdwTaggedEntry *self = ADW_TAGGED_ENTRY (gobject);
+
+  if (gtk_editable_delegate_set_property (gobject, prop_id, value, pspec))
+    return;
+
+  switch (prop_id) {
+  case PROP_PLACEHOLDER_TEXT:
+    adw_tagged_entry_set_placeholder_text (self, g_value_get_string (value));
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+  }
+}
+
+static void
+adw_tagged_entry_get_property (GObject    *gobject,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  AdwTaggedEntry *self = ADW_TAGGED_ENTRY (gobject);
+
+  if (gtk_editable_delegate_get_property (gobject, prop_id, value, pspec))
+    return;
+
+  switch (prop_id) {
+  case PROP_PLACEHOLDER_TEXT:
+    g_value_set_string (value, adw_tagged_entry_get_placeholder_text (self));
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+  }
+}
+
+static void
+adw_tagged_entry_dispose (GObject *gobject)
+{
+  AdwTaggedEntry *self = ADW_TAGGED_ENTRY (gobject);
+
+  if (self->text != NULL)
+    gtk_editable_finish_delegate (GTK_EDITABLE (self));
+
+  adw_tagged_entry_remove_all_tags (self);
+
+  g_clear_object (&self->tags);
+
+  g_clear_pointer (&self->text, gtk_widget_unparent);
+  g_clear_pointer (&self->tags_box, gtk_widget_unparent);
+
+  G_OBJECT_CLASS (adw_tagged_entry_parent_class)->dispose (gobject);
+}
+
+static void
+adw_tagged_entry_class_init (AdwTaggedEntryClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  gobject_class->set_property = adw_tagged_entry_set_property;
+  gobject_class->get_property = adw_tagged_entry_get_property;
+  gobject_class->dispose = adw_tagged_entry_dispose;
+
+  /**
+   * AdwTaggedEntry:placeholder-text:
+   *
+   * The text that will be displayed in the tagged entry when it is empty
+   * and unfocused.
+   */
+  entry_props[PROP_PLACEHOLDER_TEXT] =
+    g_param_spec_string ("placeholder-text", NULL, NULL,
+                         NULL,
+                         G_PARAM_READWRITE |
+                         G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, N_PROPS, entry_props);
+  gtk_editable_install_properties (gobject_class, N_PROPS);
+
+  gtk_widget_class_set_css_name (widget_class, "entry");
+  gtk_widget_class_set_layout_manager_type (GTK_WIDGET_CLASS (klass), GTK_TYPE_BOX_LAYOUT);
+  gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_TEXT_BOX);
+}
+
+static void
+adw_tagged_entry_init (AdwTaggedEntry *self)
+{
+  gtk_widget_add_css_class (GTK_WIDGET (self), "tagged");
+
+  g_type_ensure (ADW_TYPE_TAG);
+
+  self->text = gtk_text_new ();
+  gtk_widget_set_hexpand (self->text, TRUE);
+  gtk_widget_set_vexpand (self->text, TRUE);
+  gtk_widget_set_parent (self->text, GTK_WIDGET (self));
+  gtk_editable_init_delegate (GTK_EDITABLE (self));
+  gtk_editable_set_width_chars (GTK_EDITABLE (self->text), 12);
+  gtk_editable_set_max_width_chars (GTK_EDITABLE (self->text), 12);
+
+  self->tags_box = gtk_flow_box_new ();
+  gtk_flow_box_set_min_children_per_line (GTK_FLOW_BOX (self->tags_box), 4);
+  gtk_widget_set_parent (self->tags_box, GTK_WIDGET (self));
+  gtk_widget_add_css_class (self->tags_box, "tags");
+
+  self->tags = G_LIST_MODEL (g_list_store_new (ADW_TYPE_TAG));
+}
+
+/**
+ * adw_tagged_entry_new:
+ *
+ * Creates a new tagged entry widget.
+ *
+ * Returns: (transfer floating): the new tagged entry widget
+ */
+GtkWidget *
+adw_tagged_entry_new (void)
+{
+  return g_object_new (ADW_TYPE_TAGGED_ENTRY, NULL);
+}
+
+/**
+ * adw_tagged_entry_add_tag:
+ * @self: the tagged entry we want to update
+ * @name: the name of the tag we want to add
+ *
+ * Adds a new tag into the tagged entry for the given @name.
+ *
+ * Returns: (transfer none): the tag object
+ */
+AdwTag *
+adw_tagged_entry_add_tag (AdwTaggedEntry *self,
+                          const char     *name)
+{
+  g_return_val_if_fail (ADW_IS_TAGGED_ENTRY (self), NULL);
+  g_return_val_if_fail (name != NULL, NULL);
+
+  guint n_tags = g_list_model_get_n_items (self->tags);
+  for (guint i = 0; i < n_tags; i++) {
+    g_autoptr(AdwTag) tag = g_list_model_get_item (self->tags, i);
+
+    if (g_str_equal (tag->name, name)) {
+      g_critical ("Tag ā€œ%sā€ with label ā€œ%sā€ already exists",
+                  tag->name,
+                  tag->label);
+      return NULL;
+    }
+  }
+
+  AdwTag *tag = g_object_new (ADW_TYPE_TAG, "name", name, NULL);
+
+  return adw_tagged_entry_add_tag_internal (self, tag, TRUE);
+}
+
+/**
+ * adw_tagged_entry_remove_tag:
+ * @self: the tagged entry we want to update
+ * @name: the name of the tag we want to remove
+ *
+ * Removes the given tag from the tagged entry.
+ */
+void
+adw_tagged_entry_remove_tag (AdwTaggedEntry *self,
+                             const char     *name)
+{
+  g_return_if_fail (ADW_IS_TAGGED_ENTRY (self));
+  g_return_if_fail (name != NULL);
+
+  guint n_tags = g_list_model_get_n_items (self->tags);
+  for (guint i = 0; i < n_tags; i++) {
+    g_autoptr(AdwTag) tag = g_list_model_get_item (self->tags, i);
+
+    if (g_str_equal (tag->name, name)) {
+      GtkWidget *parent = gtk_widget_get_parent (tag->tag);
+      gtk_flow_box_remove (GTK_FLOW_BOX (self->tags_box), parent);
+      g_list_store_remove (G_LIST_STORE (self->tags), i);
+      break;
+    }
+  }
+}
+
+/**
+ * adw_tagged_entry_get_tags:
+ * @self: the tagged entry we want to query
+ *
+ * Retrieves a list model of all tags inside the tagged entry widget.
+ *
+ * Returns: (transfer full): a list model of all the tags
+ */
+GListModel *
+adw_tagged_entry_get_tags (AdwTaggedEntry *self)
+{
+  g_return_val_if_fail (ADW_IS_TAGGED_ENTRY (self), NULL);
+
+  return self->tags;
+}
+
+/**
+ * adw_tagged_entry_remove_all_tags:
+ * @self: the tagged entry we want to change
+ *
+ * Removes all tags from the tagged entry widget.
+ */
+void
+adw_tagged_entry_remove_all_tags (AdwTaggedEntry *self)
+{
+  g_return_if_fail (ADW_IS_TAGGED_ENTRY (self));
+
+  g_list_store_remove_all (G_LIST_STORE (self->tags));
+}
+
+/**
+ * adw_tagged_entry_set_placeholder_text:
+ * @self: the tagged entry to update
+ * @text: (nullable): the placeholder text
+ *
+ * Sets text to be displayed in the tagged entry when it is empty.
+ */
+void
+adw_tagged_entry_set_placeholder_text (AdwTaggedEntry *self,
+                                       const char     *text)
+{
+  g_return_if_fail (ADW_IS_TAGGED_ENTRY (self));
+
+  gtk_text_set_placeholder_text (GTK_TEXT (self->text), text);
+  gtk_accessible_update_property (GTK_ACCESSIBLE (self),
+                                  GTK_ACCESSIBLE_PROPERTY_PLACEHOLDER, text,
+                                  -1);
+
+  g_object_notify_by_pspec (G_OBJECT (self), entry_props[PROP_PLACEHOLDER_TEXT]);
+}
+
+/**
+ * adw_tagged_entry_get_placeholder_text:
+ * @self: the tagged entry to query
+ *
+ * Retrieves the placeholder text of the tagged entry.
+ *
+ * Returns: (transfer none) (nullable): the placeholder text
+ */
+const char *
+adw_tagged_entry_get_placeholder_text (AdwTaggedEntry *self)
+{
+  g_return_val_if_fail (ADW_IS_TAGGED_ENTRY (self), NULL);
+
+  return gtk_text_get_placeholder_text (GTK_TEXT (self->text));
+}
+
+/* }}} */
+
+/* {{{ AdwTag */
+enum
+{
+  PROP_TAG_NAME = 1,
+  PROP_TAG_LABEL,
+  PROP_TAG_SHOW_CLOSE,
+
+  N_TAG_PROPS
+};
+
+static GParamSpec *tag_props[N_TAG_PROPS];
+
+G_DEFINE_TYPE (AdwTag, adw_tag, G_TYPE_OBJECT)
+
+static void
+adw_tag_close_clicked (AdwTag *self)
+{
+  adw_tagged_entry_remove_tag (self->entry, self->name);
+}
+
+static void
+adw_tag_dispose (GObject *gobject)
+{
+  AdwTag *self = ADW_TAG (gobject);
+
+  // Keep a reference, as remove_tag() will release the initial reference
+  g_object_ref (self);
+  adw_tagged_entry_remove_tag (self->entry, self->name);
+
+  g_clear_pointer (&self->label, g_free);
+  g_clear_pointer (&self->name, g_free);
+
+  G_OBJECT_CLASS (adw_tag_parent_class)->dispose (gobject);
+}
+
+static void
+adw_tag_set_property (GObject      *gobject,
+                      guint         prop_id,
+                      const GValue *value,
+                      GParamSpec   *pspec)
+{
+  AdwTag *self = ADW_TAG (gobject);
+
+  switch (prop_id) {
+  case PROP_TAG_NAME:
+    g_free (self->name);
+    self->name = g_value_dup_string (value);
+    break;
+
+  case PROP_TAG_LABEL:
+    adw_tag_set_label (self, g_value_get_string (value));
+    break;
+
+  case PROP_TAG_SHOW_CLOSE:
+    adw_tag_set_show_close (self, g_value_get_boolean (value));
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+  }
+}
+
+static void
+adw_tag_get_property (GObject    *gobject,
+                      guint       prop_id,
+                      GValue     *value,
+                      GParamSpec *pspec)
+{
+  AdwTag *self = ADW_TAG (gobject);
+
+  switch (prop_id) {
+  case PROP_TAG_NAME:
+    g_value_set_string (value, self->name);
+    break;
+
+  case PROP_TAG_LABEL:
+    g_value_set_string (value, self->label);
+    break;
+
+  case PROP_TAG_SHOW_CLOSE:
+    g_value_set_boolean (value, self->show_close);
+    break;
+
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+  }
+}
+
+static void
+adw_tag_class_init (AdwTagClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->dispose = adw_tag_dispose;
+  gobject_class->set_property = adw_tag_set_property;
+  gobject_class->get_property = adw_tag_get_property;
+
+  /**
+   * AdwTag:name:
+   *
+   * The name of the tag, used for accessing the tag in a tagged entry
+   * and for styling purposes.
+   */
+  tag_props[PROP_TAG_NAME] =
+    g_param_spec_string ("name", NULL, NULL,
+                         NULL,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+  /**
+   * AdwTag:label:
+   *
+   * The user readable name of the tag.
+   */
+  tag_props[PROP_TAG_LABEL] =
+    g_param_spec_string ("label", NULL, NULL,
+                         NULL,
+                         G_PARAM_READWRITE |
+                         G_PARAM_STATIC_STRINGS |
+                         G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * AdwTag:show-close:
+   *
+   * Whether the tag should show a close button to remove itself
+   * from the entry.
+   */
+  tag_props[PROP_TAG_SHOW_CLOSE] =
+    g_param_spec_boolean ("show-close", NULL, NULL,
+                          TRUE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_STRINGS |
+                          G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (gobject_class, N_TAG_PROPS, tag_props);
+}
+
+static void
+adw_tag_init (AdwTag *self)
+{
+  self->show_close = TRUE;
+
+  self->tag = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+
+  self->tag_label = gtk_label_new ("");
+  gtk_box_append (GTK_BOX (self->tag), self->tag_label);
+  g_object_bind_property (self, "label",
+                          self->tag_label, "label",
+                          G_BINDING_DEFAULT);
+
+  self->tag_close = gtk_button_new_from_icon_name ("window-close-symbolic");
+  gtk_box_append (GTK_BOX (self->tag), self->tag_close);
+  g_object_bind_property (self, "show-close",
+                          self->tag_close, "visible",
+                          G_BINDING_DEFAULT);
+  g_signal_connect_swapped (self->tag_close, "clicked", G_CALLBACK (adw_tag_close_clicked), self);
+}
+
+/**
+ * adw_tag_get_name:
+ * @self: the tag we want to query
+ *
+ * Retrieves the name of the tag.
+ *
+ * Returns: (transfer none): the name of the tag
+ */
+const char *
+adw_tag_get_name (AdwTag *self)
+{
+  g_return_val_if_fail (ADW_IS_TAG (self), NULL);
+
+  return self->name;
+}
+
+/**
+ * adw_tag_get_label:
+ * @self: the tag we want to query
+ *
+ * Retrieves the user readable label of the tag.
+ *
+ * Returns: (transfer none): the label of the tag
+ */
+const char *
+adw_tag_get_label (AdwTag *self)
+{
+  g_return_val_if_fail (ADW_IS_TAG (self), NULL);
+
+  return self->label;
+}
+
+/**
+ * adw_tag_set_label:
+ * @self: the tag we want to update
+ * @label: (not nullable): the label of the tag
+ *
+ * Sets the user readable label of the tag.
+ */
+void
+adw_tag_set_label (AdwTag     *self,
+                   const char *label)
+{
+  g_return_if_fail (ADW_IS_TAG (self));
+  g_return_if_fail (label != NULL);
+
+  if (g_strcmp0 (self->label, label) == 0)
+    return;
+
+  g_free (self->label);
+  self->label = g_strdup (label);
+
+  g_object_notify_by_pspec (G_OBJECT (self), tag_props[PROP_TAG_LABEL]);
+}
+
+/**
+ * adw_tag_get_show_close:
+ * @self: the tag we want to query
+ *
+ * Checks whether the tag should show a close button or not.
+ *
+ * Returns: true if the tag has a visible close button
+ */
+gboolean
+adw_tag_get_show_close (AdwTag *self)
+{
+  g_return_val_if_fail (ADW_IS_TAG (self), FALSE);
+
+  return self->show_close;
+}
+
+/**
+ * adw_tag_set_show_close:
+ * @self: the tag we want to update
+ *
+ * Sets whether the tag should show a close button or not.
+ */
+void
+adw_tag_set_show_close (AdwTag   *self,
+                        gboolean  show_close)
+{
+  g_return_if_fail (ADW_IS_TAG (self));
+
+  show_close = !!show_close;
+
+  if (self->show_close != show_close) {
+    self->show_close = show_close;
+
+    g_object_notify_by_pspec (G_OBJECT (self), tag_props[PROP_TAG_SHOW_CLOSE]);
+  }
+}
+
+/* }}} */
diff --git a/src/adw-tagged-entry.h b/src/adw-tagged-entry.h
new file mode 100644
index 00000000..eadbf6f7
--- /dev/null
+++ b/src/adw-tagged-entry.h
@@ -0,0 +1,66 @@
+/* adw-tagged-entry.h: Tagged entry widget
+ *
+ * SPDX-FileCopyrightText: 2022 Emmanuele Bassi
+ * SPDX-FileCopyrightText: 2019 Matthias Clasen
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#if !defined(_ADWAITA_INSIDE) && !defined(ADWAITA_COMPILATION)
+#error "Only <adwaita.h> can be included directly."
+#endif
+
+#include "adw-version.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define ADW_TYPE_TAG (adw_tag_get_type())
+
+ADW_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (AdwTag, adw_tag, ADW, TAG, GObject)
+
+ADW_AVAILABLE_IN_ALL
+const char *adw_tag_get_name (AdwTag *self);
+
+ADW_AVAILABLE_IN_ALL
+const char *adw_tag_get_label (AdwTag     *self);
+ADW_AVAILABLE_IN_ALL
+void        adw_tag_set_label (AdwTag     *self,
+                               const char *label);
+
+ADW_AVAILABLE_IN_ALL
+gboolean adw_tag_get_show_close (AdwTag   *self);
+ADW_AVAILABLE_IN_ALL
+void     adw_tag_set_show_close (AdwTag   *self,
+                                 gboolean  show_close);
+
+#define ADW_TYPE_TAGGED_ENTRY (adw_tagged_entry_get_type())
+
+ADW_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (AdwTaggedEntry, adw_tagged_entry, ADW, TAGGED_ENTRY, GtkWidget)
+
+ADW_AVAILABLE_IN_ALL
+GtkWidget *adw_tagged_entry_new (void);
+
+ADW_AVAILABLE_IN_ALL
+AdwTag *    adw_tagged_entry_add_tag         (AdwTaggedEntry *self,
+                                              const char     *name);
+ADW_AVAILABLE_IN_ALL
+void        adw_tagged_entry_remove_tag      (AdwTaggedEntry *self,
+                                              const char     *name);
+ADW_AVAILABLE_IN_ALL
+void        adw_tagged_entry_remove_all_tags (AdwTaggedEntry *self);
+ADW_AVAILABLE_IN_ALL
+GListModel *adw_tagged_entry_get_tags        (AdwTaggedEntry *self);
+
+ADW_AVAILABLE_IN_ALL
+const char *adw_tagged_entry_get_placeholder_text (AdwTaggedEntry *self);
+ADW_AVAILABLE_IN_ALL
+void        adw_tagged_entry_set_placeholder_text (AdwTaggedEntry *self,
+                                                   const char     *text);
+
+G_END_DECLS
diff --git a/src/adwaita.h b/src/adwaita.h
index 3371b1c9..72f0dc96 100644
--- a/src/adwaita.h
+++ b/src/adwaita.h
@@ -61,6 +61,7 @@ G_BEGIN_DECLS
 #include "adw-swipeable.h"
 #include "adw-tab-bar.h"
 #include "adw-tab-view.h"
+#include "adw-tagged-entry.h"
 #include "adw-timed-animation.h"
 #include "adw-toast-overlay.h"
 #include "adw-toast.h"
diff --git a/src/meson.build b/src/meson.build
index cfd52054..edefb398 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -120,6 +120,7 @@ src_headers = [
   'adw-swipeable.h',
   'adw-tab-bar.h',
   'adw-tab-view.h',
+  'adw-tagged-entry.h',
   'adw-timed-animation.h',
   'adw-toast.h',
   'adw-toast-overlay.h',
@@ -189,6 +190,7 @@ src_sources = [
   'adw-tab-bar.c',
   'adw-tab-box.c',
   'adw-tab-view.c',
+  'adw-tagged-entry.c',
   'adw-timed-animation.c',
   'adw-toast.c',
   'adw-toast-overlay.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]