[gnome-builder/wip/chergert/docs] use custom model and single listbox
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/docs] use custom model and single listbox
- Date: Fri, 12 Jul 2019 00:15:16 +0000 (UTC)
commit 2fc60dc7759f9b1b33f872b1f4dcf107a278e198
Author: Christian Hergert <chergert redhat com>
Date: Thu Jul 11 17:13:19 2019 -0700
use custom model and single listbox
src/libide/docs/ide-docs-item.c | 22 ++
src/libide/docs/ide-docs-item.h | 3 +
src/libide/docs/ide-docs-search-model.c | 282 ++++++++++++++++++++++++
src/libide/docs/ide-docs-search-model.h | 39 ++++
src/libide/docs/ide-docs-search-row.c | 37 +++-
src/libide/docs/ide-docs-search-section.c | 51 ++---
src/libide/docs/meson.build | 1 +
src/libide/themes/themes/shared/shared-docs.css | 21 +-
src/plugins/devhelp/gbp-devhelp-docs-provider.c | 1 +
9 files changed, 412 insertions(+), 45 deletions(-)
---
diff --git a/src/libide/docs/ide-docs-item.c b/src/libide/docs/ide-docs-item.c
index e485460d9..ff86cbc6e 100644
--- a/src/libide/docs/ide-docs-item.c
+++ b/src/libide/docs/ide-docs-item.c
@@ -684,3 +684,25 @@ ide_docs_item_truncate (IdeDocsItem *self,
while (priv->children.length > max_items)
ide_docs_item_remove (self, priv->children.tail->data);
}
+
+/**
+ * ide_docs_item_get_nth_child:
+ * @self: a #IdeDocsItem
+ * @nth: the index (starting from zero) of the child
+ *
+ * Gets the @nth item from the children.
+ *
+ * Returns: (transfer none) (nullable): an #IdeDocsItem or %NULL
+ *
+ * Since: 3.34
+ */
+IdeDocsItem *
+ide_docs_item_get_nth_child (IdeDocsItem *self,
+ guint nth)
+{
+ IdeDocsItemPrivate *priv = ide_docs_item_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_DOCS_ITEM (self), NULL);
+
+ return g_list_nth_data (priv->children.head, nth);
+}
diff --git a/src/libide/docs/ide-docs-item.h b/src/libide/docs/ide-docs-item.h
index 6d228cf2b..0ad2ae762 100644
--- a/src/libide/docs/ide-docs-item.h
+++ b/src/libide/docs/ide-docs-item.h
@@ -113,6 +113,9 @@ void ide_docs_item_remove (IdeDocsItem *self,
IDE_AVAILABLE_IN_3_34
gboolean ide_docs_item_has_child (IdeDocsItem *self);
IDE_AVAILABLE_IN_3_34
+IdeDocsItem *ide_docs_item_get_nth_child (IdeDocsItem *self,
+ guint nth);
+IDE_AVAILABLE_IN_3_34
guint ide_docs_item_get_n_children (IdeDocsItem *self);
IDE_AVAILABLE_IN_3_34
const GList *ide_docs_item_get_children (IdeDocsItem *self);
diff --git a/src/libide/docs/ide-docs-search-model.c b/src/libide/docs/ide-docs-search-model.c
new file mode 100644
index 000000000..c6a327716
--- /dev/null
+++ b/src/libide/docs/ide-docs-search-model.c
@@ -0,0 +1,282 @@
+/* ide-docs-search-model.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-docs-search-model"
+
+#include "config.h"
+
+#include "ide-docs-search-model.h"
+
+#define DEFAULT_MAX_CHILDREN 3
+
+struct _IdeDocsSearchModel
+{
+ GObject parent_instance;
+ GArray *groups;
+};
+
+typedef struct
+{
+ IdeDocsItem *group;
+ guint expanded : 1;
+} Group;
+
+static GType
+ide_docs_search_model_get_item_type (GListModel *model)
+{
+ return IDE_TYPE_DOCS_ITEM;
+}
+
+static guint
+ide_docs_search_model_get_n_items (GListModel *model)
+{
+ IdeDocsSearchModel *self = (IdeDocsSearchModel *)model;
+ guint n_items = 0;
+
+ g_assert (IDE_IS_DOCS_SEARCH_MODEL (self));
+
+ for (guint i = 0; i < self->groups->len; i++)
+ {
+ const Group *g = &g_array_index (self->groups, Group, i);
+ guint n_children = ide_docs_item_get_n_children (g->group);
+
+ /* Add the group title */
+ n_items++;
+
+ /* Add the items (depending on expanded state) */
+ if (g->expanded)
+ n_items += n_children;
+ else
+ n_items += MIN (n_children, DEFAULT_MAX_CHILDREN);
+ }
+
+ return n_items;
+}
+
+static gpointer
+ide_docs_search_model_get_item (GListModel *model,
+ guint position)
+{
+ IdeDocsSearchModel *self = (IdeDocsSearchModel *)model;
+
+ g_assert (IDE_IS_DOCS_SEARCH_MODEL (self));
+ g_assert (position < ide_docs_search_model_get_n_items (model));
+
+ for (guint i = 0; i < self->groups->len; i++)
+ {
+ const Group *g = &g_array_index (self->groups, Group, i);
+ guint n_children = ide_docs_item_get_n_children (g->group);
+
+ if (position == 0)
+ return g_object_ref (g->group);
+
+ position--;
+
+ if (!g->expanded)
+ n_children = MIN (n_children, DEFAULT_MAX_CHILDREN);
+
+ if (position >= n_children)
+ {
+ position -= n_children;
+ continue;
+ }
+
+ return g_object_ref (ide_docs_item_get_nth_child (g->group, position));
+ }
+
+ g_return_val_if_reached (NULL);
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+ iface->get_item_type = ide_docs_search_model_get_item_type;
+ iface->get_n_items = ide_docs_search_model_get_n_items;
+ iface->get_item = ide_docs_search_model_get_item;
+}
+
+G_DEFINE_TYPE_WITH_CODE (IdeDocsSearchModel, ide_docs_search_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static void
+clear_group_cb (gpointer data)
+{
+ Group *g = data;
+ g_clear_object (&g->group);
+}
+
+static void
+ide_docs_search_model_finalize (GObject *object)
+{
+ IdeDocsSearchModel *self = (IdeDocsSearchModel *)object;
+
+ g_clear_pointer (&self->groups, g_array_unref);
+
+ G_OBJECT_CLASS (ide_docs_search_model_parent_class)->finalize (object);
+}
+
+static void
+ide_docs_search_model_class_init (IdeDocsSearchModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_docs_search_model_finalize;
+}
+
+static void
+ide_docs_search_model_init (IdeDocsSearchModel *self)
+{
+ self->groups = g_array_new (FALSE, FALSE, sizeof (Group));
+ g_array_set_clear_func (self->groups, clear_group_cb);
+}
+
+IdeDocsSearchModel *
+ide_docs_search_model_new (void)
+{
+ return g_object_new (IDE_TYPE_DOCS_SEARCH_MODEL, NULL);
+}
+
+void
+ide_docs_search_model_add_group (IdeDocsSearchModel *self,
+ IdeDocsItem *group)
+{
+ Group to_add = {0};
+ guint position = 0;
+ guint added;
+ gint priority;
+
+ g_return_if_fail (IDE_IS_DOCS_SEARCH_MODEL (self));
+ g_return_if_fail (IDE_IS_DOCS_ITEM (group));
+
+ if (ide_docs_item_get_n_children (group) == 0)
+ return;
+
+ to_add.group = g_object_ref (group);
+ to_add.expanded = FALSE;
+
+ priority = ide_docs_item_get_priority (group);
+ added = ide_docs_item_get_n_children (group);
+ if (added > DEFAULT_MAX_CHILDREN)
+ added = DEFAULT_MAX_CHILDREN;
+
+ /* Add the group header */
+ added++;
+
+ for (guint i = 0; i < self->groups->len; i++)
+ {
+ const Group *g = &g_array_index (self->groups, Group, i);
+ guint n_children;
+
+ if (ide_docs_item_get_priority (g->group) > priority)
+ {
+ g_array_insert_val (self->groups, i, to_add);
+ g_list_model_items_changed (G_LIST_MODEL (self), position, 0, added);
+ return;
+ }
+
+ /* Skip the group header */
+ position++;
+
+ n_children = ide_docs_item_get_n_children (g->group);
+
+ if (g->expanded)
+ position += n_children;
+ else
+ position += MIN (n_children, DEFAULT_MAX_CHILDREN);
+ }
+
+ g_assert (position == ide_docs_search_model_get_n_items (G_LIST_MODEL (self)));
+
+ g_array_append_val (self->groups, to_add);
+ g_list_model_items_changed (G_LIST_MODEL (self), position, 0, added);
+}
+
+static void
+ide_docs_search_model_toggle (IdeDocsSearchModel *self,
+ IdeDocsItem *group,
+ gboolean expanded)
+{
+ guint position = 0;
+
+ g_return_if_fail (IDE_IS_DOCS_SEARCH_MODEL (self));
+ g_return_if_fail (IDE_IS_DOCS_ITEM (group));
+
+ for (guint i = 0; i < self->groups->len; i++)
+ {
+ Group *g = &g_array_index (self->groups, Group, i);
+ guint n_children = ide_docs_item_get_n_children (g->group);
+ guint removed = 0;
+ guint added = 0;
+
+ /* Skip the group header */
+ position++;
+
+ if (g->group != group)
+ {
+ if (g->expanded)
+ position += n_children;
+ else
+ position += MIN (DEFAULT_MAX_CHILDREN, n_children);
+
+ continue;
+ }
+
+ if (g->expanded == expanded)
+ return;
+
+ g->expanded = !g->expanded;
+
+ if (g->expanded)
+ {
+ /* expanding */
+ removed = MIN (DEFAULT_MAX_CHILDREN, n_children);
+ added = n_children;
+ }
+ else
+ {
+ /* collapsing */
+ removed = n_children;
+ added = MIN (DEFAULT_MAX_CHILDREN, n_children);
+ }
+
+ g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
+ break;
+ }
+}
+
+void
+ide_docs_search_model_collapse_group (IdeDocsSearchModel *self,
+ IdeDocsItem *group)
+{
+ g_return_if_fail (IDE_IS_DOCS_SEARCH_MODEL (self));
+ g_return_if_fail (IDE_IS_DOCS_ITEM (group));
+
+ ide_docs_search_model_toggle (self, group, FALSE);
+}
+
+void
+ide_docs_search_model_expand_group (IdeDocsSearchModel *self,
+ IdeDocsItem *group)
+{
+ g_return_if_fail (IDE_IS_DOCS_SEARCH_MODEL (self));
+ g_return_if_fail (IDE_IS_DOCS_ITEM (group));
+
+ ide_docs_search_model_toggle (self, group, TRUE);
+}
diff --git a/src/libide/docs/ide-docs-search-model.h b/src/libide/docs/ide-docs-search-model.h
new file mode 100644
index 000000000..eb2a825e7
--- /dev/null
+++ b/src/libide/docs/ide-docs-search-model.h
@@ -0,0 +1,39 @@
+/* ide-docs-search-model.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-docs-item.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DOCS_SEARCH_MODEL (ide_docs_search_model_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDocsSearchModel, ide_docs_search_model, IDE, DOCS_SEARCH_MODEL, GObject)
+
+IdeDocsSearchModel *ide_docs_search_model_new (void);
+void ide_docs_search_model_add_group (IdeDocsSearchModel *self,
+ IdeDocsItem *group);
+void ide_docs_search_model_collapse_group (IdeDocsSearchModel *self,
+ IdeDocsItem *group);
+void ide_docs_search_model_expand_group (IdeDocsSearchModel *self,
+ IdeDocsItem *group);
+
+G_END_DECLS
diff --git a/src/libide/docs/ide-docs-search-row.c b/src/libide/docs/ide-docs-search-row.c
index e07407183..bd4db79fd 100644
--- a/src/libide/docs/ide-docs-search-row.c
+++ b/src/libide/docs/ide-docs-search-row.c
@@ -24,6 +24,8 @@
#include "ide-docs-search-row.h"
+#define DEFAULT_MAX_CHILDREN 3
+
struct _IdeDocsSearchRow
{
DzlListBoxRow parent_instance;
@@ -49,8 +51,11 @@ static void
ide_docs_search_row_set_item (IdeDocsSearchRow *self,
IdeDocsItem *item)
{
+ g_autofree gchar *with_size = NULL;
+ GtkStyleContext *style_context;
const gchar *icon_name;
const gchar *title;
+ IdeDocsItemKind kind;
gboolean use_markup;
g_return_if_fail (IDE_IS_DOCS_SEARCH_ROW (self));
@@ -61,14 +66,9 @@ ide_docs_search_row_set_item (IdeDocsSearchRow *self,
if (item == NULL)
return;
- gtk_label_set_use_markup (self->label, FALSE);
-
- if ((title = ide_docs_item_get_display_name (self->item)))
- use_markup = TRUE;
- else
- title = ide_docs_item_get_title (self->item);
+ kind = ide_docs_item_get_kind (self->item);
- switch (ide_docs_item_get_kind (self->item))
+ switch (kind)
{
case IDE_DOCS_ITEM_KIND_FUNCTION:
icon_name = "lang-function-symbolic";
@@ -117,6 +117,29 @@ ide_docs_search_row_set_item (IdeDocsSearchRow *self,
break;
}
+ gtk_label_set_use_markup (self->label, FALSE);
+
+ if ((title = ide_docs_item_get_display_name (self->item)))
+ use_markup = TRUE;
+ else
+ title = ide_docs_item_get_title (self->item);
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+
+ if (kind == IDE_DOCS_ITEM_KIND_BOOK && ide_docs_item_has_child (item))
+ {
+ guint n_children = ide_docs_item_get_n_children (item);
+
+ gtk_style_context_add_class (style_context, "header");
+
+ if (n_children > DEFAULT_MAX_CHILDREN)
+ title = with_size = g_strdup_printf ("%s +%u", title, n_children - DEFAULT_MAX_CHILDREN);
+ }
+ else
+ {
+ gtk_style_context_remove_class (style_context, "header");
+ }
+
g_object_set (self->image, "icon-name", icon_name, NULL);
gtk_label_set_label (self->label, title);
diff --git a/src/libide/docs/ide-docs-search-section.c b/src/libide/docs/ide-docs-search-section.c
index 0fbee384c..a19ede91b 100644
--- a/src/libide/docs/ide-docs-search-section.c
+++ b/src/libide/docs/ide-docs-search-section.c
@@ -25,19 +25,21 @@
#include <dazzle.h>
#include "ide-docs-search-group.h"
+#include "ide-docs-search-model.h"
+#include "ide-docs-search-row.h"
#include "ide-docs-search-section.h"
#define MAX_ALLOWED_BY_GROUP 1000
struct _IdeDocsSearchSection
{
- GtkBin parent_instance;
+ GtkBin parent_instance;
- DzlPriorityBox *groups;
+ DzlListBox *groups;
- gchar *title;
+ gchar *title;
- gint priority;
+ gint priority;
};
G_DEFINE_TYPE (IdeDocsSearchSection, ide_docs_search_section, GTK_TYPE_BIN)
@@ -163,11 +165,13 @@ ide_docs_search_section_class_init (IdeDocsSearchSectionClass *klass)
static void
ide_docs_search_section_init (IdeDocsSearchSection *self)
{
- self->groups = g_object_new (DZL_TYPE_PRIORITY_BOX,
- "orientation", GTK_ORIENTATION_VERTICAL,
- "spacing", 14,
+ self->groups = g_object_new (DZL_TYPE_LIST_BOX,
+ "row-type", IDE_TYPE_DOCS_SEARCH_ROW,
+ "property-name", "item",
+ "selection-mode", GTK_SELECTION_NONE,
"visible", TRUE,
NULL);
+ dzl_list_box_set_recycle_max (self->groups, 100);
gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->groups));
}
@@ -212,43 +216,32 @@ void
ide_docs_search_section_add_groups (IdeDocsSearchSection *self,
IdeDocsItem *parent)
{
- gboolean show_more_items = FALSE;
+ g_autoptr(IdeDocsSearchModel) model = NULL;
g_return_if_fail (IDE_IS_DOCS_SEARCH_SECTION (self));
g_return_if_fail (IDE_IS_DOCS_ITEM (parent));
- /* If there is a single group within the section, we want to show more
- * items than we otherwise would.
- */
- if (ide_docs_item_get_n_children (parent) == 1)
- show_more_items = TRUE;
+ dzl_list_box_set_model (self->groups, NULL);
+
+ model = ide_docs_search_model_new ();
+
+ gtk_widget_hide (GTK_WIDGET (self->groups));
for (const GList *iter = ide_docs_item_get_children (parent);
iter != NULL;
iter = iter->next)
{
IdeDocsItem *child = iter->data;
- IdeDocsSearchGroup *group;
- const gchar *title;
- gint priority;
g_assert (IDE_IS_DOCS_ITEM (child));
- title = ide_docs_item_get_title (child);
- priority = ide_docs_item_get_priority (child);
- group = g_object_new (IDE_TYPE_DOCS_SEARCH_GROUP,
- "title", title,
- "priority", priority,
- "visible", TRUE,
- NULL);
-
- if (show_more_items)
- ide_docs_search_group_set_max_items (group, 25);
-
/* Truncate to a reasonable number to avoid very large lists */
ide_docs_item_truncate (child, MAX_ALLOWED_BY_GROUP);
- ide_docs_search_group_add_items (group, child);
- gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (group));
+ ide_docs_search_model_add_group (model, child);
+
+ dzl_list_box_set_model (self->groups, G_LIST_MODEL (model));
}
+
+ gtk_widget_show (GTK_WIDGET (self->groups));
}
diff --git a/src/libide/docs/meson.build b/src/libide/docs/meson.build
index 31fe3fd9a..89f736f62 100644
--- a/src/libide/docs/meson.build
+++ b/src/libide/docs/meson.build
@@ -28,6 +28,7 @@ libide_docs_public_sources = [
'ide-docs-provider.c',
'ide-docs-query.c',
'ide-docs-search-group.c',
+ 'ide-docs-search-model.c',
'ide-docs-search-row.c',
'ide-docs-search-section.c',
'ide-docs-search-view.c',
diff --git a/src/libide/themes/themes/shared/shared-docs.css b/src/libide/themes/themes/shared/shared-docs.css
index 8f10e42b7..d3fc226bc 100644
--- a/src/libide/themes/themes/shared/shared-docs.css
+++ b/src/libide/themes/themes/shared/shared-docs.css
@@ -1,28 +1,31 @@
-IdeDocsSearchGroup list {
+IdeDocsSearchSection list {
background-color: transparent;
}
-IdeDocsSearchGroup box.header {
+IdeDocsSearchSection list row.header:not(:first-child) {
+ margin-top: 24px;
+ }
+IdeDocsSearchSection list row.header box {
padding: 7px;
}
-IdeDocsSearchGroup box.header label {
+IdeDocsSearchSection list row.header box label {
font-size: 0.8333em;
font-weight: bold;
color: @theme_selected_bg_color;
}
+IdeDocsSearchSection list row:selected.header box label {
+ color: @theme_selected_fg_color;
+ }
IdeDocsSearchView box.titles label {
font-size: 1.3em;
font-weight: 500;
color: alpha(currentColor, 0.5);
}
-IdeDocsSearchGroup row:first-child {
- border-top: 1px solid alpha(@borders, 0.5);
- }
-IdeDocsSearchGroup row {
+IdeDocsSearchSection list row {
border-bottom: 1px solid alpha(@borders, 0.5);
}
-IdeDocsSearchGroup row > box {
+IdeDocsSearchSection list row > box {
padding: 7px;
}
-IdeDocsSearchGroup row > box image:last-child {
+IdeDocsSearchSection list row > box image:last-child {
min-width: 16px;
}
diff --git a/src/plugins/devhelp/gbp-devhelp-docs-provider.c b/src/plugins/devhelp/gbp-devhelp-docs-provider.c
index aae78a7bb..6d0616306 100644
--- a/src/plugins/devhelp/gbp-devhelp-docs-provider.c
+++ b/src/plugins/devhelp/gbp-devhelp-docs-provider.c
@@ -120,6 +120,7 @@ gbp_devhelp_docs_provider_search_async (IdeDocsProvider *provider,
group = ide_docs_item_new ();
ide_docs_item_set_title (group, parser->book.title);
+ ide_docs_item_set_kind (group, IDE_DOCS_ITEM_KIND_BOOK);
for (guint j = 0; j < parser->keywords->len; j++)
{
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]