Merge branch '31-app-list-search' into 'main'
restrict-applications-dialog: Add support for searching the app list Closes #31 See merge request pwithnall/malcontent!155
This commit is contained in:
commit
cbad81565e
|
@ -55,11 +55,18 @@ struct _MctRestrictApplicationsDialog
|
|||
|
||||
MctRestrictApplicationsSelector *selector;
|
||||
AdwPreferencesGroup *group;
|
||||
GtkSearchEntry *search_entry;
|
||||
|
||||
MctAppFilter *app_filter; /* (owned) (not nullable) */
|
||||
gchar *user_display_name; /* (owned) (nullable) */
|
||||
};
|
||||
|
||||
static void search_entry_stop_search_cb (GtkSearchEntry *search_entry,
|
||||
gpointer user_data);
|
||||
static gboolean focus_search_cb (GtkWidget *widget,
|
||||
GVariant *arguments,
|
||||
gpointer user_data);
|
||||
|
||||
G_DEFINE_TYPE (MctRestrictApplicationsDialog, mct_restrict_applications_dialog, ADW_TYPE_PREFERENCES_WINDOW)
|
||||
|
||||
typedef enum
|
||||
|
@ -140,6 +147,19 @@ mct_restrict_applications_dialog_dispose (GObject *object)
|
|||
G_OBJECT_CLASS (mct_restrict_applications_dialog_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
mct_restrict_applications_dialog_map (GtkWidget *widget)
|
||||
{
|
||||
MctRestrictApplicationsDialog *self = (MctRestrictApplicationsDialog *)widget;
|
||||
|
||||
GTK_WIDGET_CLASS (mct_restrict_applications_dialog_parent_class)->map (widget);
|
||||
|
||||
/* Clear and focus the search entry, in case the dialogue is being shown for
|
||||
* a second time. */
|
||||
gtk_editable_set_text (GTK_EDITABLE (self->search_entry), "");
|
||||
gtk_widget_grab_focus (GTK_WIDGET (self->search_entry));
|
||||
}
|
||||
|
||||
static void
|
||||
mct_restrict_applications_dialog_class_init (MctRestrictApplicationsDialogClass *klass)
|
||||
{
|
||||
|
@ -151,6 +171,8 @@ mct_restrict_applications_dialog_class_init (MctRestrictApplicationsDialogClass
|
|||
object_class->set_property = mct_restrict_applications_dialog_set_property;
|
||||
object_class->dispose = mct_restrict_applications_dialog_dispose;
|
||||
|
||||
widget_class->map = mct_restrict_applications_dialog_map;
|
||||
|
||||
/**
|
||||
* MctRestrictApplicationsDialog:app-filter: (not nullable)
|
||||
*
|
||||
|
@ -197,6 +219,15 @@ mct_restrict_applications_dialog_class_init (MctRestrictApplicationsDialogClass
|
|||
|
||||
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsDialog, selector);
|
||||
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsDialog, group);
|
||||
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsDialog, search_entry);
|
||||
|
||||
gtk_widget_class_bind_template_callback (widget_class, search_entry_stop_search_cb);
|
||||
|
||||
gtk_widget_class_add_binding (widget_class,
|
||||
GDK_KEY_f, GDK_CONTROL_MASK,
|
||||
focus_search_cb,
|
||||
NULL);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -206,6 +237,8 @@ mct_restrict_applications_dialog_init (MctRestrictApplicationsDialog *self)
|
|||
g_type_ensure (MCT_TYPE_RESTRICT_APPLICATIONS_SELECTOR);
|
||||
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
|
||||
gtk_search_entry_set_key_capture_widget (self->search_entry, GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -225,6 +258,25 @@ update_description (MctRestrictApplicationsDialog *self)
|
|||
adw_preferences_group_set_description (self->group, description);
|
||||
}
|
||||
|
||||
static void
|
||||
search_entry_stop_search_cb (GtkSearchEntry *search_entry,
|
||||
gpointer user_data)
|
||||
{
|
||||
/* Clear the search text as the search filtering is bound to that. */
|
||||
gtk_editable_set_text (GTK_EDITABLE (search_entry), "");
|
||||
}
|
||||
|
||||
static gboolean
|
||||
focus_search_cb (GtkWidget *widget,
|
||||
GVariant *arguments,
|
||||
gpointer user_data)
|
||||
{
|
||||
MctRestrictApplicationsDialog *self = MCT_RESTRICT_APPLICATIONS_DIALOG (widget);
|
||||
|
||||
gtk_widget_grab_focus (GTK_WIDGET (self->search_entry));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* mct_restrict_applications_dialog_new:
|
||||
* @app_filter: (transfer none): the initial app filter configuration to show
|
||||
|
|
|
@ -14,7 +14,21 @@
|
|||
<!-- Translated dynamically: -->
|
||||
<property name="description">Restrict {username} from using the following installed applications.</property>
|
||||
<child>
|
||||
<object class="MctRestrictApplicationsSelector" id="selector" />
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkSearchEntry" id="search_entry">
|
||||
<property name="placeholder-text" translatable="yes">Search for applications…</property>
|
||||
<signal name="stop-search" handler="search_entry_stop_search_cb" />
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="MctRestrictApplicationsSelector" id="selector">
|
||||
<property name="search" bind-source="search_entry" bind-property="text" />
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
|
|
@ -41,6 +41,7 @@ static void app_info_changed_cb (GAppInfoMonitor *monitor,
|
|||
static void reload_apps (MctRestrictApplicationsSelector *self);
|
||||
static GtkWidget *create_row_for_app_cb (gpointer item,
|
||||
gpointer user_data);
|
||||
static char *app_info_dup_name (GAppInfo *app_info);
|
||||
|
||||
/**
|
||||
* MctRestrictApplicationsSelector:
|
||||
|
@ -54,6 +55,11 @@ static GtkWidget *create_row_for_app_cb (gpointer item,
|
|||
* #MctAppFilterBuilder using
|
||||
* mct_restrict_applications_selector_build_app_filter().
|
||||
*
|
||||
* Search terms may be applied using #MctRestrictApplicationsSelector:search.
|
||||
* These will filter the list of displayed apps so that only ones matching the
|
||||
* search terms (by name, using UTF-8 normalisation and casefolding) will be
|
||||
* displayed.
|
||||
*
|
||||
* Since: 0.5.0
|
||||
*/
|
||||
struct _MctRestrictApplicationsSelector
|
||||
|
@ -61,10 +67,11 @@ struct _MctRestrictApplicationsSelector
|
|||
GtkBox parent_instance;
|
||||
|
||||
GtkListBox *listbox;
|
||||
GtkLabel *placeholder;
|
||||
|
||||
GList *cached_apps; /* (nullable) (owned) (element-type GAppInfo) */
|
||||
GListStore *apps; /* (owned) */
|
||||
GListStore *apps;
|
||||
GtkFilterListModel *filtered_apps;
|
||||
GtkStringFilter *search_filter;
|
||||
GAppInfoMonitor *app_info_monitor; /* (owned) */
|
||||
gulong app_info_monitor_changed_id;
|
||||
GHashTable *blocklisted_apps; /* (owned) (element-type GAppInfo) */
|
||||
|
@ -75,6 +82,8 @@ struct _MctRestrictApplicationsSelector
|
|||
FlatpakInstallation *user_installation; /* (owned) */
|
||||
|
||||
GtkCssProvider *css_provider; /* (owned) */
|
||||
|
||||
gchar *search; /* (nullable) (owned) */
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (MctRestrictApplicationsSelector, mct_restrict_applications_selector, GTK_TYPE_BOX)
|
||||
|
@ -82,9 +91,10 @@ G_DEFINE_TYPE (MctRestrictApplicationsSelector, mct_restrict_applications_select
|
|||
typedef enum
|
||||
{
|
||||
PROP_APP_FILTER = 1,
|
||||
PROP_SEARCH,
|
||||
} MctRestrictApplicationsSelectorProperty;
|
||||
|
||||
static GParamSpec *properties[PROP_APP_FILTER + 1];
|
||||
static GParamSpec *properties[PROP_SEARCH + 1];
|
||||
|
||||
enum {
|
||||
SIGNAL_CHANGED,
|
||||
|
@ -125,6 +135,9 @@ mct_restrict_applications_selector_get_property (GObject *object,
|
|||
case PROP_APP_FILTER:
|
||||
g_value_set_boxed (value, self->app_filter);
|
||||
break;
|
||||
case PROP_SEARCH:
|
||||
g_value_set_string (value, self->search);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
|
@ -144,6 +157,9 @@ mct_restrict_applications_selector_set_property (GObject *object,
|
|||
case PROP_APP_FILTER:
|
||||
mct_restrict_applications_selector_set_app_filter (self, g_value_get_boxed (value));
|
||||
break;
|
||||
case PROP_SEARCH:
|
||||
mct_restrict_applications_selector_set_search (self, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
|
@ -156,7 +172,6 @@ mct_restrict_applications_selector_dispose (GObject *object)
|
|||
MctRestrictApplicationsSelector *self = (MctRestrictApplicationsSelector *)object;
|
||||
|
||||
g_clear_pointer (&self->blocklisted_apps, g_hash_table_unref);
|
||||
g_clear_object (&self->apps);
|
||||
g_clear_list (&self->cached_apps, g_object_unref);
|
||||
|
||||
if (self->app_info_monitor != NULL && self->app_info_monitor_changed_id != 0)
|
||||
|
@ -169,6 +184,7 @@ mct_restrict_applications_selector_dispose (GObject *object)
|
|||
g_clear_object (&self->system_installation);
|
||||
g_clear_object (&self->user_installation);
|
||||
g_clear_object (&self->css_provider);
|
||||
g_clear_pointer (&self->search, g_free);
|
||||
|
||||
G_OBJECT_CLASS (mct_restrict_applications_selector_parent_class)->dispose (object);
|
||||
}
|
||||
|
@ -203,6 +219,23 @@ mct_restrict_applications_selector_class_init (MctRestrictApplicationsSelectorCl
|
|||
G_PARAM_STATIC_STRINGS |
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* MctRestrictApplicationsSelector:search: (nullable)
|
||||
*
|
||||
* Search terms to filter the displayed list of apps by, or %NULL to not
|
||||
* filter the search.
|
||||
*
|
||||
* Since: 0.12.0
|
||||
*/
|
||||
properties[PROP_SEARCH] =
|
||||
g_param_spec_string ("search",
|
||||
"Search",
|
||||
"Search terms to filter the displayed list of apps by.",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS |
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
|
||||
|
||||
/**
|
||||
|
@ -224,34 +257,30 @@ mct_restrict_applications_selector_class_init (MctRestrictApplicationsSelectorCl
|
|||
gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/MalcontentUi/ui/restrict-applications-selector.ui");
|
||||
|
||||
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsSelector, listbox);
|
||||
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsSelector, placeholder);
|
||||
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsSelector, apps);
|
||||
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsSelector, filtered_apps);
|
||||
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsSelector, search_filter);
|
||||
|
||||
gtk_widget_class_bind_template_callback (widget_class, app_info_dup_name);
|
||||
}
|
||||
|
||||
static void
|
||||
mct_restrict_applications_selector_init (MctRestrictApplicationsSelector *self)
|
||||
{
|
||||
guint n_apps;
|
||||
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
|
||||
self->apps = g_list_store_new (G_TYPE_APP_INFO);
|
||||
self->cached_apps = NULL;
|
||||
|
||||
self->app_info_monitor = g_app_info_monitor_get ();
|
||||
self->app_info_monitor_changed_id =
|
||||
g_signal_connect (self->app_info_monitor, "changed",
|
||||
(GCallback) app_info_changed_cb, self);
|
||||
|
||||
gtk_list_box_bind_model (self->listbox,
|
||||
G_LIST_MODEL (self->apps),
|
||||
G_LIST_MODEL (self->filtered_apps),
|
||||
create_row_for_app_cb,
|
||||
self,
|
||||
NULL);
|
||||
|
||||
/* Hide placeholder if not empty */
|
||||
n_apps = g_list_model_get_n_items (G_LIST_MODEL (self->apps));
|
||||
gtk_widget_set_visible (GTK_WIDGET (self->placeholder), n_apps != 0);
|
||||
|
||||
self->blocklisted_apps = g_hash_table_new_full (g_direct_hash,
|
||||
g_direct_equal,
|
||||
g_object_unref,
|
||||
|
@ -368,6 +397,12 @@ create_row_for_app_cb (gpointer item,
|
|||
return row;
|
||||
}
|
||||
|
||||
static char *
|
||||
app_info_dup_name (GAppInfo *app_info)
|
||||
{
|
||||
return g_strdup (g_app_info_get_name (app_info));
|
||||
}
|
||||
|
||||
static gint
|
||||
compare_app_info_cb (gconstpointer a,
|
||||
gconstpointer b,
|
||||
|
@ -776,7 +811,7 @@ mct_restrict_applications_selector_set_app_filter (MctRestrictApplicationsSelect
|
|||
self->app_filter = mct_app_filter_ref (app_filter);
|
||||
|
||||
/* Update the status of each app row. */
|
||||
n_apps = g_list_model_get_n_items (G_LIST_MODEL (self->apps));
|
||||
n_apps = g_list_model_get_n_items (G_LIST_MODEL (self->filtered_apps));
|
||||
|
||||
for (guint i = 0; i < n_apps; i++)
|
||||
{
|
||||
|
@ -795,3 +830,50 @@ mct_restrict_applications_selector_set_app_filter (MctRestrictApplicationsSelect
|
|||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_APP_FILTER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* mct_restrict_applications_selector_get_search:
|
||||
* @self: an #MctRestrictApplicationsSelector
|
||||
*
|
||||
* Get the value of #MctRestrictApplicationsSelector:search.
|
||||
*
|
||||
* Returns: current search terms, or %NULL if no search filtering is active
|
||||
* Since: 0.12.0
|
||||
*/
|
||||
const gchar *
|
||||
mct_restrict_applications_selector_get_search (MctRestrictApplicationsSelector *self)
|
||||
{
|
||||
g_return_val_if_fail (MCT_IS_RESTRICT_APPLICATIONS_SELECTOR (self), NULL);
|
||||
|
||||
return self->search;
|
||||
}
|
||||
|
||||
/**
|
||||
* mct_restrict_applications_selector_set_search:
|
||||
* @self: an #MctRestrictApplicationsSelector
|
||||
* @search: (nullable): search terms, or %NULL to not filter the app list
|
||||
*
|
||||
* Set the value of #MctRestrictApplicationsSelector:search, or clear it to
|
||||
* %NULL.
|
||||
*
|
||||
* Since: 0.12.0
|
||||
*/
|
||||
void
|
||||
mct_restrict_applications_selector_set_search (MctRestrictApplicationsSelector *self,
|
||||
const gchar *search)
|
||||
{
|
||||
g_return_if_fail (MCT_IS_RESTRICT_APPLICATIONS_SELECTOR (self));
|
||||
|
||||
/* Squash empty search terms down to nothing. */
|
||||
if (search != NULL && *search == '\0')
|
||||
search = NULL;
|
||||
|
||||
if (g_strcmp0 (search, self->search) == 0)
|
||||
return;
|
||||
|
||||
g_clear_pointer (&self->search, g_free);
|
||||
self->search = g_strdup (search);
|
||||
|
||||
gtk_string_filter_set_search (self->search_filter, self->search);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH]);
|
||||
}
|
||||
|
|
|
@ -41,4 +41,8 @@ void mct_restrict_applications_selector_set_app_filter (MctRestrictAppl
|
|||
void mct_restrict_applications_selector_build_app_filter (MctRestrictApplicationsSelector *self,
|
||||
MctAppFilterBuilder *builder);
|
||||
|
||||
const gchar *mct_restrict_applications_selector_get_search (MctRestrictApplicationsSelector *self);
|
||||
void mct_restrict_applications_selector_set_search (MctRestrictApplicationsSelector *self,
|
||||
const gchar *search);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
<property name="selection-mode">none</property>
|
||||
|
||||
<child type="placeholder">
|
||||
<object class="GtkLabel" id="placeholder">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">No applications found to restrict.</property>
|
||||
<property name="margin-top">12</property>
|
||||
<property name="margin-bottom">12</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
|
@ -20,4 +22,19 @@
|
|||
</object>
|
||||
</child>
|
||||
</template>
|
||||
|
||||
<object class="GListStore" id="apps">
|
||||
<property name="item-type">GAppInfo</property>
|
||||
</object>
|
||||
|
||||
<object class="GtkFilterListModel" id="filtered_apps">
|
||||
<property name="model">apps</property>
|
||||
<property name="filter">search_filter</property>
|
||||
</object>
|
||||
|
||||
<object class="GtkStringFilter" id="search_filter">
|
||||
<property name="expression">
|
||||
<closure type="gchararray" function="app_info_dup_name" />
|
||||
</property>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
|
@ -702,6 +702,7 @@ mct_user_controls_finalize (GObject *object)
|
|||
g_clear_object (&self->user);
|
||||
g_clear_pointer (&self->user_locale, g_free);
|
||||
g_clear_pointer (&self->user_display_name, g_free);
|
||||
g_clear_pointer (&self->description, g_free);
|
||||
|
||||
if (self->permission != NULL && self->permission_allowed_id != 0)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
project('malcontent', 'c',
|
||||
version : '0.11.1',
|
||||
version : '0.12.0',
|
||||
meson_version : '>= 0.59.0',
|
||||
license: ['LGPL-2.1-or-later', 'GPL-2.0-or-later'],
|
||||
default_options : [
|
||||
|
|
Loading…
Reference in New Issue