From 21412ea3ba3b30d1ced87d4a16dd6fdec04ff467 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sat, 11 Mar 2023 15:39:22 -0300 Subject: search-button: Port results to GtkListView Port the search results list to GtkListview. Use bindings to set the title, icon, and subtitle of each row. Remove GcalSearchHitRow as it's unused now. --- src/gui/gcal-search-button.c | 147 +++++++++++++++++++++---------- src/gui/gcal-search-button.ui | 123 ++++++++++++++++++++++++-- src/gui/gcal-search-hit-row.c | 193 ----------------------------------------- src/gui/gcal-search-hit-row.h | 36 -------- src/gui/gcal-search-hit-row.ui | 53 ----------- src/gui/gui.gresource.xml | 1 - src/gui/meson.build | 1 - src/theme/style-dark.css | 8 ++ src/theme/style.css | 4 + 9 files changed, 228 insertions(+), 338 deletions(-) delete mode 100644 src/gui/gcal-search-hit-row.c delete mode 100644 src/gui/gcal-search-hit-row.h delete mode 100644 src/gui/gcal-search-hit-row.ui diff --git a/src/gui/gcal-search-button.c b/src/gui/gcal-search-button.c index 3f5e4d13..d8ed14d1 100644 --- a/src/gui/gcal-search-button.c +++ b/src/gui/gcal-search-button.c @@ -24,7 +24,6 @@ #include "gcal-debug.h" #include "gcal-search-button.h" #include "gcal-search-hit.h" -#include "gcal-search-hit-row.h" #include @@ -36,8 +35,9 @@ struct _GcalSearchButton GtkEditable *entry; GtkWidget *popover; - GtkListBox *results_listbox; + GtkListView *results_listview; GtkRevealer *results_revealer; + GtkSingleSelection *results_selection_model; GtkStack *stack; GCancellable *cancellable; @@ -88,11 +88,14 @@ quit_search_entry (GcalSearchButton *self) gtk_editable_set_text (self->entry, ""); } -static GtkWidget * -create_widget_func (gpointer item, - gpointer user_data) +static inline void +scroll_to_result (GcalSearchButton *self, + guint position) { - return gcal_search_hit_row_new (item); + gtk_widget_activate_action (GTK_WIDGET (self->results_listview), + "list.scroll-to-item", + "u", + position); } static void @@ -101,11 +104,7 @@ set_model (GcalSearchButton *self, { GCAL_ENTRY; - gtk_list_box_bind_model (self->results_listbox, - model, - create_widget_func, - self, - NULL); + gtk_single_selection_set_model (self->results_selection_model, model); if (model) show_suggestions (self); @@ -120,6 +119,18 @@ set_model (GcalSearchButton *self, * Callbacks */ +static gchar * +escape_markup_cb (GcalSearchHit *hit, + const gchar *string) +{ + g_autofree gchar *escaped_string = NULL; + + escaped_string = g_markup_escape_text (string, -1); + escaped_string = g_strstrip (escaped_string); + + return g_steal_pointer (&escaped_string); +} + static void on_button_clicked_cb (GtkButton *button, GcalSearchButton *self) @@ -164,6 +175,68 @@ on_search_finished_cb (GObject *source_object, GCAL_EXIT; } +static void +on_entry_activate_cb (GtkSearchEntry *entry, + GcalSearchButton *self) +{ + GcalSearchHit *hit; + + GCAL_ENTRY; + + hit = gtk_single_selection_get_selected_item (self->results_selection_model); + + if (hit) + { + GCAL_TRACE_MSG ("Activating \"%s\"", gcal_search_hit_get_title (hit)); + + gcal_search_hit_activate (hit, GTK_WIDGET (self)); + quit_search_entry (self); + } + + GCAL_EXIT; +} + +static void +on_entry_next_match_cb (GtkSearchEntry *entry, + GcalSearchButton *self) +{ + guint selected; + + GCAL_ENTRY; + + selected = gtk_single_selection_get_selected (self->results_selection_model); + + if (selected != GTK_INVALID_LIST_POSITION && + selected + 1 < g_list_model_get_n_items (G_LIST_MODEL (self->results_selection_model))) + { + GCAL_TRACE_MSG ("Changing selection to %u", selected + 1); + gtk_single_selection_set_selected (self->results_selection_model, selected + 1); + scroll_to_result (self, selected + 1); + } + + GCAL_EXIT; +} + +static void +on_entry_previous_match_cb (GtkSearchEntry *entry, + GcalSearchButton *self) +{ + guint selected; + + GCAL_ENTRY; + + selected = gtk_single_selection_get_selected (self->results_selection_model); + + if (selected > 0 && selected != GTK_INVALID_LIST_POSITION) + { + GCAL_TRACE_MSG ("Changing selection to %u", selected - 1); + gtk_single_selection_set_selected (self->results_selection_model, selected - 1); + scroll_to_result (self, selected - 1); + } + + GCAL_EXIT; +} + static void on_entry_search_changed_cb (GtkSearchEntry *entry, GcalSearchButton *self) @@ -204,16 +277,18 @@ on_entry_stop_search_cb (GtkSearchEntry *search_entry, } static void -on_results_listbox_row_activated_cb (GtkListBox *listbox, - GcalSearchHitRow *row, - GcalSearchButton *self) +on_results_listview_activated_cb (GtkListBox *listbox, + guint position, + GcalSearchButton *self) { GcalSearchHit *search_hit; - search_hit = gcal_search_hit_row_get_search_hit (row); + search_hit = g_list_model_get_item (G_LIST_MODEL (self->results_selection_model), position); + g_assert (GCAL_IS_SEARCH_HIT (search_hit)); + gcal_search_hit_activate (search_hit, GTK_WIDGET (self)); - hide_suggestions (self); + quit_search_entry (self); } static void @@ -225,32 +300,11 @@ on_results_revealer_child_reveal_state_changed_cb (GtkRevealer *revealer, gtk_popover_popdown (GTK_POPOVER (self->popover)); } - -/* - * GtkWidget overrides - */ - static gboolean -gcal_search_button_focus (GtkWidget *widget, - GtkDirectionType direction) +string_is_not_empty_cb (GcalSearchHit *hit, + const gchar *string) { - GcalSearchButton *self = GCAL_SEARCH_BUTTON (widget); - - if (!gtk_widget_get_visible (GTK_WIDGET (self->popover))) - return gtk_widget_child_focus (GTK_WIDGET (self->stack), direction); - - if (direction == GTK_DIR_DOWN) - { - GtkListBoxRow *first_row = gtk_list_box_get_row_at_index (self->results_listbox, 0); - - if (!first_row) - return gtk_widget_child_focus (GTK_WIDGET (self->stack), direction); - - gtk_widget_grab_focus (GTK_WIDGET (first_row)); - return TRUE; - } - - return gtk_widget_child_focus (GTK_WIDGET (self->stack), direction); + return string != NULL && *string != '\0'; } @@ -319,7 +373,6 @@ gcal_search_button_set_property (GObject *object, } } - static void gcal_search_button_class_init (GcalSearchButtonClass *klass) { @@ -331,8 +384,6 @@ gcal_search_button_class_init (GcalSearchButtonClass *klass) object_class->get_property = gcal_search_button_get_property; object_class->set_property = gcal_search_button_set_property; - widget_class->focus = gcal_search_button_focus; - /** * GcalSearchButton::context: * @@ -350,16 +401,22 @@ gcal_search_button_class_init (GcalSearchButtonClass *klass) gtk_widget_class_bind_template_child (widget_class, GcalSearchButton, entry); gtk_widget_class_bind_template_child (widget_class, GcalSearchButton, popover); - gtk_widget_class_bind_template_child (widget_class, GcalSearchButton, results_listbox); + gtk_widget_class_bind_template_child (widget_class, GcalSearchButton, results_listview); + gtk_widget_class_bind_template_child (widget_class, GcalSearchButton, results_selection_model); gtk_widget_class_bind_template_child (widget_class, GcalSearchButton, results_revealer); gtk_widget_class_bind_template_child (widget_class, GcalSearchButton, stack); + gtk_widget_class_bind_template_callback (widget_class, escape_markup_cb); gtk_widget_class_bind_template_callback (widget_class, on_button_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, on_focus_controller_leave_cb); + gtk_widget_class_bind_template_callback (widget_class, on_entry_activate_cb); + gtk_widget_class_bind_template_callback (widget_class, on_entry_next_match_cb); + gtk_widget_class_bind_template_callback (widget_class, on_entry_previous_match_cb); gtk_widget_class_bind_template_callback (widget_class, on_entry_search_changed_cb); gtk_widget_class_bind_template_callback (widget_class, on_entry_stop_search_cb); - gtk_widget_class_bind_template_callback (widget_class, on_results_listbox_row_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, on_results_listview_activated_cb); gtk_widget_class_bind_template_callback (widget_class, on_results_revealer_child_reveal_state_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, string_is_not_empty_cb); gtk_widget_class_set_css_name (widget_class, "searchbutton"); } diff --git a/src/gui/gcal-search-button.ui b/src/gui/gcal-search-button.ui index d9bcf1c8..25db262a 100644 --- a/src/gui/gcal-search-button.ui +++ b/src/gui/gcal-search-button.ui @@ -52,8 +52,29 @@ 0 0 + + + + + + + + + Up + signal(previous-match) + + + + + Down + signal(next-match) + + + + + @@ -65,7 +86,7 @@ bottom False - results_listbox + results_listview @@ -84,18 +105,102 @@ never - - True + natural natural - - - - none - + True + + + True + False - + + + + + + + + ]]> + + + diff --git a/src/gui/gcal-search-hit-row.c b/src/gui/gcal-search-hit-row.c deleted file mode 100644 index 6819a270..00000000 --- a/src/gui/gcal-search-hit-row.c +++ /dev/null @@ -1,193 +0,0 @@ -/* gcal-search-hit-row.c - * - * Copyright 2022 Georges Basile Stavracas Neto - * - * 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 . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#include "gcal-search-hit-row.h" - -struct _GcalSearchHitRow -{ - GtkListBoxRow parent_instance; - - GtkImage *image; - GtkWidget *separator; - GtkLabel *subtitle; - GtkLabel *title; - - GcalSearchHit *search_hit; -}; - -G_DEFINE_FINAL_TYPE (GcalSearchHitRow, gcal_search_hit_row, GTK_TYPE_LIST_BOX_ROW) - -enum -{ - PROP_0, - PROP_SEARCH_HIT, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - - -/* - * Auxiliary methods - */ - -static void -update_search_hit (GcalSearchHitRow *self) -{ - g_autofree gchar *escaped_title = NULL; - const gchar *subtitle; - - gtk_image_set_from_paintable (self->image, gcal_search_hit_get_primary_icon (self->search_hit)); - - escaped_title = g_markup_escape_text (gcal_search_hit_get_title (self->search_hit), -1); - gtk_label_set_label (self->title, escaped_title); - - subtitle = gcal_search_hit_get_subtitle (self->search_hit); - if (subtitle) - { - g_autofree gchar *escaped_subtitle = NULL; - - escaped_subtitle = g_markup_escape_text (subtitle, -1); - escaped_subtitle = g_strstrip (escaped_subtitle); - gtk_label_set_label (self->subtitle, escaped_subtitle); - - gtk_widget_set_visible (self->separator, escaped_subtitle && *escaped_subtitle != '\0'); - } - else - { - gtk_widget_set_visible (self->separator, FALSE); - } -} - - -/* - * Callbacks - */ - -static void -on_search_hit_changed_cb (GcalSearchHit *search_hit, - GParamSpec *pspec, - GcalSearchHitRow *self) -{ - update_search_hit (self); -} - -/* - * GObject overrides - */ - -static void -gcal_search_hit_row_finalize (GObject *object) -{ - GcalSearchHitRow *self = (GcalSearchHitRow *)object; - - g_clear_object (&self->search_hit); - - G_OBJECT_CLASS (gcal_search_hit_row_parent_class)->finalize (object); -} - -static void -gcal_search_hit_row_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GcalSearchHitRow *self = GCAL_SEARCH_HIT_ROW (object); - - switch (prop_id) - { - case PROP_SEARCH_HIT: - g_value_set_object (value, self->search_hit); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gcal_search_hit_row_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GcalSearchHitRow *self = GCAL_SEARCH_HIT_ROW (object); - - switch (prop_id) - { - case PROP_SEARCH_HIT: - g_assert (self->search_hit == NULL); - self->search_hit = g_value_dup_object (value); - g_signal_connect_object (self->search_hit, "notify", G_CALLBACK (on_search_hit_changed_cb), self, 0); - update_search_hit (self); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gcal_search_hit_row_class_init (GcalSearchHitRowClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = gcal_search_hit_row_finalize; - object_class->get_property = gcal_search_hit_row_get_property; - object_class->set_property = gcal_search_hit_row_set_property; - - properties[PROP_SEARCH_HIT] = g_param_spec_object ("search-hit", - NULL, - NULL, - GCAL_TYPE_SEARCH_HIT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/gui/gcal-search-hit-row.ui"); - - gtk_widget_class_bind_template_child (widget_class, GcalSearchHitRow, image); - gtk_widget_class_bind_template_child (widget_class, GcalSearchHitRow, separator); - gtk_widget_class_bind_template_child (widget_class, GcalSearchHitRow, subtitle); - gtk_widget_class_bind_template_child (widget_class, GcalSearchHitRow, title); -} - -static void -gcal_search_hit_row_init (GcalSearchHitRow *self) -{ - gtk_widget_init_template (GTK_WIDGET (self)); -} - -GtkWidget * -gcal_search_hit_row_new (GcalSearchHit *search_hit) -{ - return g_object_new (GCAL_TYPE_SEARCH_HIT_ROW, - "search-hit", search_hit, - NULL); -} - -GcalSearchHit * -gcal_search_hit_row_get_search_hit (GcalSearchHitRow *self) -{ - g_return_val_if_fail (GCAL_IS_SEARCH_HIT_ROW (self), NULL); - - return self->search_hit; -} diff --git a/src/gui/gcal-search-hit-row.h b/src/gui/gcal-search-hit-row.h deleted file mode 100644 index 947c964c..00000000 --- a/src/gui/gcal-search-hit-row.h +++ /dev/null @@ -1,36 +0,0 @@ -/* gcal-search-hit-row.h - * - * Copyright 2022 Georges Basile Stavracas Neto - * - * 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 . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#pragma once - -#include - -#include "gcal-search-hit.h" - -G_BEGIN_DECLS - -#define GCAL_TYPE_SEARCH_HIT_ROW (gcal_search_hit_row_get_type()) -G_DECLARE_FINAL_TYPE (GcalSearchHitRow, gcal_search_hit_row, GCAL, SEARCH_HIT_ROW, GtkListBoxRow) - -GtkWidget * gcal_search_hit_row_new (GcalSearchHit *search_hit); - -GcalSearchHit * gcal_search_hit_row_get_search_hit (GcalSearchHitRow *self); - -G_END_DECLS diff --git a/src/gui/gcal-search-hit-row.ui b/src/gui/gcal-search-hit-row.ui deleted file mode 100644 index 23b0c0ea..00000000 --- a/src/gui/gcal-search-hit-row.ui +++ /dev/null @@ -1,53 +0,0 @@ - - - - diff --git a/src/gui/gui.gresource.xml b/src/gui/gui.gresource.xml index c9b96b98..41e61589 100644 --- a/src/gui/gui.gresource.xml +++ b/src/gui/gui.gresource.xml @@ -7,7 +7,6 @@ gcal-meeting-row.ui gcal-quick-add-popover.ui gcal-search-button.ui - gcal-search-hit-row.ui gcal-toolbar-end.ui gcal-weather-settings.ui gcal-window.ui diff --git a/src/gui/meson.build b/src/gui/meson.build index f5d3cad6..6f167647 100644 --- a/src/gui/meson.build +++ b/src/gui/meson.build @@ -23,7 +23,6 @@ sources += files( 'gcal-overflow-bin-layout.c', 'gcal-quick-add-popover.c', 'gcal-search-button.c', - 'gcal-search-hit-row.c', 'gcal-toolbar-end.c', 'gcal-weather-settings.c', 'gcal-window.c', diff --git a/src/theme/style-dark.css b/src/theme/style-dark.css index 2690aa01..7dbc5740 100644 --- a/src/theme/style-dark.css +++ b/src/theme/style-dark.css @@ -6,3 +6,11 @@ event.timed { color: @light_1; outline-color: rgba(0, 0, 0, 0.3); } + +/* + * Search + */ + +searchbutton > popover listview > row:selected { + background-color: alpha(@accent_bg_color, 0.2); +} diff --git a/src/theme/style.css b/src/theme/style.css index f53956b8..82ae7026 100644 --- a/src/theme/style.css +++ b/src/theme/style.css @@ -392,6 +392,10 @@ searchbutton > popover > arrow { border: none; } +searchbutton > popover listview > row:selected { + background-color: alpha(@accent_bg_color, 0.11); +} + /* * Month selector */ -- cgit v1.2.1