Merge branch 'gbsneto/gtk4' into 'main'
GTK4 See merge request pwithnall/malcontent!141
This commit is contained in:
commit
2da5d5597e
|
@ -7,7 +7,7 @@ cache:
|
||||||
- _ccache/
|
- _ccache/
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
DEBIAN_IMAGE: "registry.freedesktop.org/pwithnall/malcontent/debian-unstable:v2"
|
DEBIAN_IMAGE: "registry.freedesktop.org/pwithnall/malcontent/debian-unstable:v3"
|
||||||
MESON_TEST_TIMEOUT_MULTIPLIER: 2
|
MESON_TEST_TIMEOUT_MULTIPLIER: 2
|
||||||
G_MESSAGES_DEBUG: all
|
G_MESSAGES_DEBUG: all
|
||||||
MESON_COMMON_OPTIONS: "--buildtype debug --wrap-mode=nodownload"
|
MESON_COMMON_OPTIONS: "--buildtype debug --wrap-mode=nodownload"
|
||||||
|
|
|
@ -5,13 +5,14 @@ RUN apt-get update -qq && apt-get install --no-install-recommends -qq -y \
|
||||||
git \
|
git \
|
||||||
gtk-doc-tools \
|
gtk-doc-tools \
|
||||||
lcov \
|
lcov \
|
||||||
|
libadwaita-1-dev \
|
||||||
libaccountsservice-dev \
|
libaccountsservice-dev \
|
||||||
libappstream-dev \
|
libappstream-dev \
|
||||||
libflatpak-dev \
|
libflatpak-dev \
|
||||||
libgirepository1.0-dev \
|
libgirepository1.0-dev \
|
||||||
libglib2.0-dev \
|
libglib2.0-dev \
|
||||||
libglib-testing-0-dev \
|
libglib-testing-0-dev \
|
||||||
libgtk-3-dev \
|
libgtk-4-dev \
|
||||||
libpam0g-dev \
|
libpam0g-dev \
|
||||||
libpolkit-gobject-1-dev \
|
libpolkit-gobject-1-dev \
|
||||||
libxml2-utils \
|
libxml2-utils \
|
||||||
|
|
|
@ -122,7 +122,8 @@ Dependencies
|
||||||
* gio-unix-2.0 ≥ 2.60
|
* gio-unix-2.0 ≥ 2.60
|
||||||
* glib-2.0 ≥ 2.60
|
* glib-2.0 ≥ 2.60
|
||||||
* gobject-2.0 ≥ 2.60
|
* gobject-2.0 ≥ 2.60
|
||||||
* gtk+-3.0
|
* gtk4
|
||||||
|
* libadwaita
|
||||||
* polkit-gobject-1
|
* polkit-gobject-1
|
||||||
|
|
||||||
Licensing
|
Licensing
|
||||||
|
|
|
@ -28,7 +28,8 @@ libmalcontent_ui_public_deps = [
|
||||||
dependency('gio-2.0', version: '>= 2.44'),
|
dependency('gio-2.0', version: '>= 2.44'),
|
||||||
dependency('glib-2.0', version: '>= 2.54.2'),
|
dependency('glib-2.0', version: '>= 2.54.2'),
|
||||||
dependency('gobject-2.0', version: '>= 2.54'),
|
dependency('gobject-2.0', version: '>= 2.54'),
|
||||||
dependency('gtk+-3.0', version: '>= 3.24'),
|
dependency('gtk4', version: '>= 4.6'),
|
||||||
|
libadwaita_dep,
|
||||||
libmalcontent_dep,
|
libmalcontent_dep,
|
||||||
]
|
]
|
||||||
libmalcontent_ui_private_deps = [
|
libmalcontent_ui_private_deps = [
|
||||||
|
@ -86,7 +87,7 @@ if xmllint.found()
|
||||||
'validate-ui', xmllint,
|
'validate-ui', xmllint,
|
||||||
args: [
|
args: [
|
||||||
'--nonet', '--noblanks', '--noout',
|
'--nonet', '--noblanks', '--noout',
|
||||||
'--relaxng', join_paths(gtk_prefix, 'share', 'gtk-3.0', 'gtkbuilder.rng'),
|
'--relaxng', join_paths(gtk_prefix, 'share', 'gtk-4.0', 'gtk4builder.rng'),
|
||||||
files(
|
files(
|
||||||
'restrict-applications-dialog.ui',
|
'restrict-applications-dialog.ui',
|
||||||
'restrict-applications-selector.ui',
|
'restrict-applications-selector.ui',
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
#include <glib/gi18n-lib.h>
|
#include <glib/gi18n-lib.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <adwaita.h>
|
||||||
|
|
||||||
#include "restrict-applications-dialog.h"
|
#include "restrict-applications-dialog.h"
|
||||||
#include "restrict-applications-selector.h"
|
#include "restrict-applications-selector.h"
|
||||||
|
@ -53,13 +54,13 @@ struct _MctRestrictApplicationsDialog
|
||||||
GtkDialog parent_instance;
|
GtkDialog parent_instance;
|
||||||
|
|
||||||
MctRestrictApplicationsSelector *selector;
|
MctRestrictApplicationsSelector *selector;
|
||||||
GtkLabel *description;
|
AdwPreferencesGroup *group;
|
||||||
|
|
||||||
MctAppFilter *app_filter; /* (owned) (not nullable) */
|
MctAppFilter *app_filter; /* (owned) (not nullable) */
|
||||||
gchar *user_display_name; /* (owned) (nullable) */
|
gchar *user_display_name; /* (owned) (nullable) */
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE (MctRestrictApplicationsDialog, mct_restrict_applications_dialog, GTK_TYPE_DIALOG)
|
G_DEFINE_TYPE (MctRestrictApplicationsDialog, mct_restrict_applications_dialog, ADW_TYPE_PREFERENCES_WINDOW)
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
|
@ -195,7 +196,7 @@ mct_restrict_applications_dialog_class_init (MctRestrictApplicationsDialogClass
|
||||||
gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/MalcontentUi/ui/restrict-applications-dialog.ui");
|
gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/MalcontentUi/ui/restrict-applications-dialog.ui");
|
||||||
|
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsDialog, selector);
|
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsDialog, selector);
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsDialog, description);
|
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsDialog, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -214,15 +215,14 @@ update_description (MctRestrictApplicationsDialog *self)
|
||||||
|
|
||||||
if (self->user_display_name == NULL)
|
if (self->user_display_name == NULL)
|
||||||
{
|
{
|
||||||
gtk_widget_hide (GTK_WIDGET (self->description));
|
adw_preferences_group_set_description (self->group, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Translators: the placeholder is a user’s full name */
|
/* Translators: the placeholder is a user’s full name */
|
||||||
description = g_strdup_printf (_("Restrict %s from using the following installed applications."),
|
description = g_strdup_printf (_("Restrict %s from using the following installed applications."),
|
||||||
self->user_display_name);
|
self->user_display_name);
|
||||||
gtk_label_set_text (self->description, description);
|
adw_preferences_group_set_description (self->group, description);
|
||||||
gtk_widget_show (GTK_WIDGET (self->description));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,13 +24,14 @@
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <adwaita.h>
|
||||||
#include <libmalcontent/manager.h>
|
#include <libmalcontent/manager.h>
|
||||||
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define MCT_TYPE_RESTRICT_APPLICATIONS_DIALOG (mct_restrict_applications_dialog_get_type ())
|
#define MCT_TYPE_RESTRICT_APPLICATIONS_DIALOG (mct_restrict_applications_dialog_get_type ())
|
||||||
G_DECLARE_FINAL_TYPE (MctRestrictApplicationsDialog, mct_restrict_applications_dialog, MCT, RESTRICT_APPLICATIONS_DIALOG, GtkDialog)
|
G_DECLARE_FINAL_TYPE (MctRestrictApplicationsDialog, mct_restrict_applications_dialog, MCT, RESTRICT_APPLICATIONS_DIALOG, AdwPreferencesWindow)
|
||||||
|
|
||||||
MctRestrictApplicationsDialog *mct_restrict_applications_dialog_new (MctAppFilter *app_filter,
|
MctRestrictApplicationsDialog *mct_restrict_applications_dialog_new (MctAppFilter *app_filter,
|
||||||
const gchar *user_display_name);
|
const gchar *user_display_name);
|
||||||
|
|
|
@ -2,48 +2,22 @@
|
||||||
<!-- Copyright © 2020 Endless, Inc. -->
|
<!-- Copyright © 2020 Endless, Inc. -->
|
||||||
<interface domain="malcontent">
|
<interface domain="malcontent">
|
||||||
<requires lib="gtk+" version="3.12"/>
|
<requires lib="gtk+" version="3.12"/>
|
||||||
<template class="MctRestrictApplicationsDialog" parent="GtkDialog">
|
<template class="MctRestrictApplicationsDialog" parent="AdwPreferencesWindow">
|
||||||
<property name="title" translatable="yes">Restrict Applications</property>
|
<property name="title" translatable="yes">Restrict Applications</property>
|
||||||
<property name="skip-taskbar-hint">True</property>
|
<property name="default-width">500</property>
|
||||||
<property name="default-width">300</property>
|
|
||||||
<property name="default-height">500</property>
|
<property name="default-height">500</property>
|
||||||
<child internal-child="headerbar">
|
<property name="search-enabled">False</property>
|
||||||
<object class="GtkHeaderBar">
|
<child>
|
||||||
<property name="title" translatable="yes">Restrict Applications</property>
|
<object class="AdwPreferencesPage">
|
||||||
<property name="show-close-button">True</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child internal-child="vbox">
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="margin">18</property>
|
|
||||||
<property name="spacing">12</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="description">
|
<object class="AdwPreferencesGroup" id="group">
|
||||||
<!-- Translated dynamically: -->
|
<!-- Translated dynamically: -->
|
||||||
<property name="label">Restrict {username} from using the following installed applications.</property>
|
<property name="description">Restrict {username} from using the following installed applications.</property>
|
||||||
<property name="visible">False</property>
|
<child>
|
||||||
<property name="ellipsize">none</property>
|
<object class="MctRestrictApplicationsSelector" id="selector" />
|
||||||
<property name="wrap">True</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<child internal-child="accessible">
|
|
||||||
<object class="AtkObject">
|
|
||||||
<property name="accessible-role">static</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="MctRestrictApplicationsSelector" id="selector">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
#include <glib/gi18n-lib.h>
|
#include <glib/gi18n-lib.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <adwaita.h>
|
||||||
#include <libmalcontent/app-filter.h>
|
#include <libmalcontent/app-filter.h>
|
||||||
|
|
||||||
#include "restrict-applications-selector.h"
|
#include "restrict-applications-selector.h"
|
||||||
|
@ -60,6 +61,7 @@ struct _MctRestrictApplicationsSelector
|
||||||
GtkBox parent_instance;
|
GtkBox parent_instance;
|
||||||
|
|
||||||
GtkListBox *listbox;
|
GtkListBox *listbox;
|
||||||
|
GtkLabel *placeholder;
|
||||||
|
|
||||||
GList *cached_apps; /* (nullable) (owned) (element-type GAppInfo) */
|
GList *cached_apps; /* (nullable) (owned) (element-type GAppInfo) */
|
||||||
GListStore *apps; /* (owned) */
|
GListStore *apps; /* (owned) */
|
||||||
|
@ -222,11 +224,14 @@ 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_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, listbox);
|
||||||
|
gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsSelector, placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mct_restrict_applications_selector_init (MctRestrictApplicationsSelector *self)
|
mct_restrict_applications_selector_init (MctRestrictApplicationsSelector *self)
|
||||||
{
|
{
|
||||||
|
guint n_apps;
|
||||||
|
|
||||||
gtk_widget_init_template (GTK_WIDGET (self));
|
gtk_widget_init_template (GTK_WIDGET (self));
|
||||||
|
|
||||||
self->apps = g_list_store_new (G_TYPE_APP_INFO);
|
self->apps = g_list_store_new (G_TYPE_APP_INFO);
|
||||||
|
@ -243,6 +248,10 @@ mct_restrict_applications_selector_init (MctRestrictApplicationsSelector *self)
|
||||||
self,
|
self,
|
||||||
NULL);
|
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,
|
self->blocklisted_apps = g_hash_table_new_full (g_direct_hash,
|
||||||
g_direct_equal,
|
g_direct_equal,
|
||||||
g_object_unref,
|
g_object_unref,
|
||||||
|
@ -312,9 +321,8 @@ create_row_for_app_cb (gpointer item,
|
||||||
MctRestrictApplicationsSelector *self = MCT_RESTRICT_APPLICATIONS_SELECTOR (user_data);
|
MctRestrictApplicationsSelector *self = MCT_RESTRICT_APPLICATIONS_SELECTOR (user_data);
|
||||||
GAppInfo *app = G_APP_INFO (item);
|
GAppInfo *app = G_APP_INFO (item);
|
||||||
g_autoptr(GIcon) icon = NULL;
|
g_autoptr(GIcon) icon = NULL;
|
||||||
GtkWidget *box, *w;
|
GtkWidget *row, *w;
|
||||||
const gchar *app_name;
|
const gchar *app_name;
|
||||||
gint size;
|
|
||||||
GtkStyleContext *context;
|
GtkStyleContext *context;
|
||||||
|
|
||||||
app_name = g_app_info_get_name (app);
|
app_name = g_app_info_get_name (app);
|
||||||
|
@ -327,23 +335,15 @@ create_row_for_app_cb (gpointer item,
|
||||||
else
|
else
|
||||||
g_object_ref (icon);
|
g_object_ref (icon);
|
||||||
|
|
||||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
row = adw_action_row_new ();
|
||||||
gtk_container_set_border_width (GTK_CONTAINER (box), 12);
|
|
||||||
gtk_widget_set_margin_end (box, 12);
|
|
||||||
|
|
||||||
/* Icon */
|
/* Icon */
|
||||||
w = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
|
w = gtk_image_new_from_gicon (icon);
|
||||||
gtk_icon_size_lookup (GTK_ICON_SIZE_DND, &size, NULL);
|
gtk_image_set_icon_size (GTK_IMAGE (w), GTK_ICON_SIZE_LARGE);
|
||||||
gtk_image_set_pixel_size (GTK_IMAGE (w), size);
|
adw_action_row_add_prefix (ADW_ACTION_ROW (row), w);
|
||||||
gtk_container_add (GTK_CONTAINER (box), w);
|
|
||||||
|
|
||||||
/* App name label */
|
/* App name label */
|
||||||
w = g_object_new (GTK_TYPE_LABEL,
|
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), app_name);
|
||||||
"label", app_name,
|
|
||||||
"hexpand", TRUE,
|
|
||||||
"xalign", 0.0,
|
|
||||||
NULL);
|
|
||||||
gtk_container_add (GTK_CONTAINER (box), w);
|
|
||||||
|
|
||||||
/* Switch */
|
/* Switch */
|
||||||
w = g_object_new (GTK_TYPE_SWITCH,
|
w = g_object_new (GTK_TYPE_SWITCH,
|
||||||
|
@ -354,16 +354,16 @@ create_row_for_app_cb (gpointer item,
|
||||||
gtk_style_context_add_provider (context,
|
gtk_style_context_add_provider (context,
|
||||||
GTK_STYLE_PROVIDER (self->css_provider),
|
GTK_STYLE_PROVIDER (self->css_provider),
|
||||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
|
||||||
gtk_container_add (GTK_CONTAINER (box), w);
|
adw_action_row_add_suffix (ADW_ACTION_ROW (row), w);
|
||||||
|
adw_action_row_set_activatable_widget (ADW_ACTION_ROW (row), w);
|
||||||
gtk_widget_show_all (box);
|
|
||||||
|
|
||||||
/* Fetch status from AccountService */
|
/* Fetch status from AccountService */
|
||||||
|
g_object_set_data (G_OBJECT (row), "GtkSwitch", w);
|
||||||
g_object_set_data_full (G_OBJECT (w), "GAppInfo", g_object_ref (app), g_object_unref);
|
g_object_set_data_full (G_OBJECT (w), "GAppInfo", g_object_ref (app), g_object_unref);
|
||||||
update_listbox_row_switch (self, GTK_SWITCH (w));
|
update_listbox_row_switch (self, GTK_SWITCH (w));
|
||||||
g_signal_connect (w, "notify::active", G_CALLBACK (on_switch_active_changed_cb), self);
|
g_signal_connect (w, "notify::active", G_CALLBACK (on_switch_active_changed_cb), self);
|
||||||
|
|
||||||
return box;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
|
@ -779,20 +779,13 @@ mct_restrict_applications_selector_set_app_filter (MctRestrictApplicationsSelect
|
||||||
for (guint i = 0; i < n_apps; i++)
|
for (guint i = 0; i < n_apps; i++)
|
||||||
{
|
{
|
||||||
GtkListBoxRow *row;
|
GtkListBoxRow *row;
|
||||||
GtkWidget *box, *w;
|
GtkWidget *w;
|
||||||
g_autoptr(GList) children = NULL; /* (element-type GtkWidget) */
|
|
||||||
|
|
||||||
/* Navigate the widget hierarchy set up in create_row_for_app_cb(). */
|
/* Navigate the widget hierarchy set up in create_row_for_app_cb(). */
|
||||||
row = gtk_list_box_get_row_at_index (self->listbox, i);
|
row = gtk_list_box_get_row_at_index (self->listbox, i);
|
||||||
g_assert (row != NULL && GTK_IS_LIST_BOX_ROW (row));
|
g_assert (row != NULL && GTK_IS_LIST_BOX_ROW (row));
|
||||||
|
|
||||||
box = gtk_bin_get_child (GTK_BIN (row));
|
w = g_object_get_data (G_OBJECT (row), "GtkSwitch");
|
||||||
g_assert (box != NULL && GTK_IS_BOX (box));
|
|
||||||
|
|
||||||
children = gtk_container_get_children (GTK_CONTAINER (box));
|
|
||||||
g_assert (children != NULL);
|
|
||||||
|
|
||||||
w = g_list_nth_data (children, 2);
|
|
||||||
g_assert (w != NULL && GTK_IS_SWITCH (w));
|
g_assert (w != NULL && GTK_IS_SWITCH (w));
|
||||||
|
|
||||||
update_listbox_row_switch (self, GTK_SWITCH (w));
|
update_listbox_row_switch (self, GTK_SWITCH (w));
|
||||||
|
|
|
@ -4,28 +4,19 @@
|
||||||
<requires lib="gtk+" version="3.12"/>
|
<requires lib="gtk+" version="3.12"/>
|
||||||
<template class="MctRestrictApplicationsSelector" parent="GtkBox">
|
<template class="MctRestrictApplicationsSelector" parent="GtkBox">
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScrolledWindow">
|
<object class="GtkListBox" id="listbox">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="hexpand">True</property>
|
<property name="hexpand">True</property>
|
||||||
<property name="hscrollbar-policy">never</property>
|
<property name="selection-mode">none</property>
|
||||||
<property name="min-content-height">100</property>
|
|
||||||
<property name="max-content-height">400</property>
|
|
||||||
<property name="propagate-natural-height">True</property>
|
|
||||||
<property name="shadow-type">etched-in</property>
|
|
||||||
|
|
||||||
<child>
|
<child type="placeholder">
|
||||||
<object class="GtkListBox" id="listbox">
|
<object class="GtkLabel" id="placeholder">
|
||||||
<property name="visible">True</property>
|
<property name="label" translatable="yes">No applications found to restrict.</property>
|
||||||
<property name="selection-mode">none</property>
|
|
||||||
|
|
||||||
<child type="placeholder">
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="label" translatable="yes">No applications found to restrict.</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
<class name="content"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
/* FIXME: This ‘negative’ variant of a GtkSwitch should probably be
|
/* FIXME: This ‘negative’ variant of a GtkSwitch should probably be
|
||||||
* upstreamed to GTK. See https://gitlab.gnome.org/GNOME/gtk/issues/2470 */
|
* upstreamed to GTK. See https://gitlab.gnome.org/GNOME/gtk/issues/2470 */
|
||||||
switch:checked.restricts {
|
switch:checked.restricts {
|
||||||
background-color: #fffd33;
|
background-color: @yellow_5;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch:checked.restricts, switch:checked.restricts slider {
|
switch:checked.restricts, switch:checked.restricts slider {
|
||||||
border-color: #ffd52b;
|
border-color: @yellow_3;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch:disabled.restricts {
|
switch:disabled.restricts {
|
||||||
|
@ -14,5 +14,5 @@ switch:disabled.restricts {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch:disabled.restricts slider {
|
switch:disabled.restricts slider {
|
||||||
border-color: #bfb8b1;
|
filter: grayscale(100%);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,22 +73,19 @@ static const guint32 oars_disabled_age = (guint32) -1;
|
||||||
*/
|
*/
|
||||||
struct _MctUserControls
|
struct _MctUserControls
|
||||||
{
|
{
|
||||||
GtkGrid parent_instance;
|
AdwBin parent_instance;
|
||||||
|
|
||||||
|
GtkLabel *description_label;
|
||||||
GMenu *age_menu;
|
GMenu *age_menu;
|
||||||
GtkSwitch *restrict_software_installation_switch;
|
GtkSwitch *restrict_software_installation_switch;
|
||||||
GtkLabel *restrict_software_installation_description;
|
AdwActionRow *restrict_software_installation_row;
|
||||||
GtkSwitch *restrict_web_browsers_switch;
|
GtkSwitch *restrict_web_browsers_switch;
|
||||||
GtkLabel *restrict_web_browsers_description;
|
AdwActionRow *restrict_web_browsers_row;
|
||||||
GtkButton *oars_button;
|
GtkMenuButton *oars_button;
|
||||||
GtkLabel *oars_button_label;
|
GtkPopoverMenu *oars_popover;
|
||||||
GtkPopover *oars_popover;
|
|
||||||
MctRestrictApplicationsDialog *restrict_applications_dialog;
|
MctRestrictApplicationsDialog *restrict_applications_dialog;
|
||||||
GtkLabel *restrict_applications_description;
|
GtkLabel *restrict_applications_description;
|
||||||
GtkListBoxRow *restrict_applications_row;
|
AdwActionRow *restrict_applications_row;
|
||||||
|
|
||||||
GtkListBox *application_usage_permissions_listbox;
|
|
||||||
GtkListBox *software_installation_permissions_listbox;
|
|
||||||
|
|
||||||
GSimpleActionGroup *action_group; /* (owned) */
|
GSimpleActionGroup *action_group; /* (owned) */
|
||||||
|
|
||||||
|
@ -111,6 +108,7 @@ struct _MctUserControls
|
||||||
ActUserAccountType user_account_type;
|
ActUserAccountType user_account_type;
|
||||||
gchar *user_locale; /* (nullable) (owned) */
|
gchar *user_locale; /* (nullable) (owned) */
|
||||||
gchar *user_display_name; /* (nullable) (owned) */
|
gchar *user_display_name; /* (nullable) (owned) */
|
||||||
|
gchar *description; /* (nullable) (owned) */
|
||||||
};
|
};
|
||||||
|
|
||||||
static gboolean blocklist_apps_cb (gpointer data);
|
static gboolean blocklist_apps_cb (gpointer data);
|
||||||
|
@ -123,20 +121,12 @@ static void on_restrict_web_browsers_switch_active_changed_cb (GtkSwitch
|
||||||
GParamSpec *pspec,
|
GParamSpec *pspec,
|
||||||
MctUserControls *self);
|
MctUserControls *self);
|
||||||
|
|
||||||
static void on_restrict_applications_button_clicked_cb (GtkButton *button,
|
static void on_restrict_applications_action_activated (GSimpleAction *action,
|
||||||
gpointer user_data);
|
GVariant *param,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
static gboolean on_restrict_applications_dialog_delete_event_cb (GtkWidget *widget,
|
static gboolean on_restrict_applications_dialog_close_request_cb (GtkWidget *widget,
|
||||||
GdkEvent *event,
|
gpointer user_data);
|
||||||
gpointer user_data);
|
|
||||||
|
|
||||||
static void on_restrict_applications_dialog_response_cb (GtkDialog *dialog,
|
|
||||||
gint response_id,
|
|
||||||
gpointer user_data);
|
|
||||||
|
|
||||||
static void on_application_usage_permissions_listbox_activated_cb (GtkListBox *list_box,
|
|
||||||
GtkListBoxRow *row,
|
|
||||||
gpointer user_data);
|
|
||||||
|
|
||||||
static void on_set_age_action_activated (GSimpleAction *action,
|
static void on_set_age_action_activated (GSimpleAction *action,
|
||||||
GVariant *param,
|
GVariant *param,
|
||||||
|
@ -146,7 +136,7 @@ static void on_permission_allowed_cb (GObject *obj,
|
||||||
GParamSpec *pspec,
|
GParamSpec *pspec,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
|
|
||||||
G_DEFINE_TYPE (MctUserControls, mct_user_controls, GTK_TYPE_GRID)
|
G_DEFINE_TYPE (MctUserControls, mct_user_controls, ADW_TYPE_BIN)
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
|
@ -157,12 +147,14 @@ typedef enum
|
||||||
PROP_USER_LOCALE,
|
PROP_USER_LOCALE,
|
||||||
PROP_USER_DISPLAY_NAME,
|
PROP_USER_DISPLAY_NAME,
|
||||||
PROP_DBUS_CONNECTION,
|
PROP_DBUS_CONNECTION,
|
||||||
|
PROP_DESCRIPTION,
|
||||||
} MctUserControlsProperty;
|
} MctUserControlsProperty;
|
||||||
|
|
||||||
static GParamSpec *properties[PROP_DBUS_CONNECTION + 1];
|
static GParamSpec *properties[PROP_DESCRIPTION + 1];
|
||||||
|
|
||||||
static const GActionEntry actions[] = {
|
static const GActionEntry actions[] = {
|
||||||
{ "set-age", on_set_age_action_activated, "u", NULL, NULL, { 0, }}
|
{ "set-age", on_set_age_action_activated, "u", NULL, NULL, { 0, }},
|
||||||
|
{ "restrict-applications", on_restrict_applications_action_activated, NULL, NULL, NULL, { 0, }}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Auxiliary methods */
|
/* Auxiliary methods */
|
||||||
|
@ -401,7 +393,7 @@ update_oars_level (MctUserControls *self)
|
||||||
selected_age = maximum_age;
|
selected_age = maximum_age;
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_label_set_label (self->oars_button_label, rating_age_category);
|
gtk_menu_button_set_label (self->oars_button, rating_age_category);
|
||||||
self->selected_age = selected_age;
|
self->selected_age = selected_age;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,19 +464,21 @@ update_labels_from_name (MctUserControls *self)
|
||||||
{
|
{
|
||||||
g_autofree gchar *l = NULL;
|
g_autofree gchar *l = NULL;
|
||||||
|
|
||||||
|
gtk_label_set_markup (self->description_label, self->description);
|
||||||
|
|
||||||
/* Translators: The placeholder is a user’s display name. */
|
/* Translators: The placeholder is a user’s display name. */
|
||||||
l = g_strdup_printf (_("Prevents %s from running web browsers. Limited web content may still be available in other applications."), self->user_display_name);
|
l = g_strdup_printf (_("Prevents %s from running web browsers. Limited web content may still be available in other applications."), self->user_display_name);
|
||||||
gtk_label_set_label (self->restrict_web_browsers_description, l);
|
adw_action_row_set_subtitle (self->restrict_web_browsers_row, l);
|
||||||
g_clear_pointer (&l, g_free);
|
g_clear_pointer (&l, g_free);
|
||||||
|
|
||||||
/* Translators: The placeholder is a user’s display name. */
|
/* Translators: The placeholder is a user’s display name. */
|
||||||
l = g_strdup_printf (_("Prevents specified applications from being used by %s."), self->user_display_name);
|
l = g_strdup_printf (_("Prevents specified applications from being used by %s."), self->user_display_name);
|
||||||
gtk_label_set_label (self->restrict_applications_description, l);
|
adw_action_row_set_subtitle (self->restrict_applications_row, l);
|
||||||
g_clear_pointer (&l, g_free);
|
g_clear_pointer (&l, g_free);
|
||||||
|
|
||||||
/* Translators: The placeholder is a user’s display name. */
|
/* Translators: The placeholder is a user’s display name. */
|
||||||
l = g_strdup_printf (_("Prevents %s from installing applications."), self->user_display_name);
|
l = g_strdup_printf (_("Prevents %s from installing applications."), self->user_display_name);
|
||||||
gtk_label_set_label (self->restrict_software_installation_description, l);
|
adw_action_row_set_subtitle (self->restrict_software_installation_row, l);
|
||||||
g_clear_pointer (&l, g_free);
|
g_clear_pointer (&l, g_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,29 +579,29 @@ on_restrict_web_browsers_switch_active_changed_cb (GtkSwitch *s,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_restrict_applications_button_clicked_cb (GtkButton *button,
|
on_restrict_applications_action_activated (GSimpleAction *action,
|
||||||
gpointer user_data)
|
GVariant *param,
|
||||||
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
MctUserControls *self = MCT_USER_CONTROLS (user_data);
|
MctUserControls *self = MCT_USER_CONTROLS (user_data);
|
||||||
GtkWidget *toplevel;
|
GtkRoot *root;
|
||||||
|
|
||||||
/* Show the restrict applications dialogue modally, making sure to update its
|
/* Show the restrict applications dialogue modally, making sure to update its
|
||||||
* state first. */
|
* state first. */
|
||||||
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
|
root = gtk_widget_get_root (GTK_WIDGET (self));
|
||||||
if (GTK_IS_WINDOW (toplevel))
|
if (GTK_IS_WINDOW (root))
|
||||||
gtk_window_set_transient_for (GTK_WINDOW (self->restrict_applications_dialog),
|
gtk_window_set_transient_for (GTK_WINDOW (self->restrict_applications_dialog),
|
||||||
GTK_WINDOW (toplevel));
|
GTK_WINDOW (root));
|
||||||
|
|
||||||
mct_restrict_applications_dialog_set_user_display_name (self->restrict_applications_dialog, self->user_display_name);
|
mct_restrict_applications_dialog_set_user_display_name (self->restrict_applications_dialog, self->user_display_name);
|
||||||
mct_restrict_applications_dialog_set_app_filter (self->restrict_applications_dialog, self->filter);
|
mct_restrict_applications_dialog_set_app_filter (self->restrict_applications_dialog, self->filter);
|
||||||
|
|
||||||
gtk_widget_show (GTK_WIDGET (self->restrict_applications_dialog));
|
gtk_window_present (GTK_WINDOW (self->restrict_applications_dialog));
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
on_restrict_applications_dialog_delete_event_cb (GtkWidget *widget,
|
on_restrict_applications_dialog_close_request_cb (GtkWidget *widget,
|
||||||
GdkEvent *event,
|
gpointer user_data)
|
||||||
gpointer user_data)
|
|
||||||
{
|
{
|
||||||
MctUserControls *self = MCT_USER_CONTROLS (user_data);
|
MctUserControls *self = MCT_USER_CONTROLS (user_data);
|
||||||
|
|
||||||
|
@ -622,27 +616,6 @@ on_restrict_applications_dialog_delete_event_cb (GtkWidget *widget,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
on_restrict_applications_dialog_response_cb (GtkDialog *dialog,
|
|
||||||
gint response_id,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
MctUserControls *self = MCT_USER_CONTROLS (user_data);
|
|
||||||
|
|
||||||
on_restrict_applications_dialog_delete_event_cb (GTK_WIDGET (dialog), NULL, self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_application_usage_permissions_listbox_activated_cb (GtkListBox *list_box,
|
|
||||||
GtkListBoxRow *row,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
MctUserControls *self = MCT_USER_CONTROLS (user_data);
|
|
||||||
|
|
||||||
if (row == self->restrict_applications_row)
|
|
||||||
on_restrict_applications_button_clicked_cb (NULL, self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_set_age_action_activated (GSimpleAction *action,
|
on_set_age_action_activated (GSimpleAction *action,
|
||||||
GVariant *param,
|
GVariant *param,
|
||||||
|
@ -665,13 +638,13 @@ on_set_age_action_activated (GSimpleAction *action,
|
||||||
|
|
||||||
/* Update the button */
|
/* Update the button */
|
||||||
if (age == oars_disabled_age)
|
if (age == oars_disabled_age)
|
||||||
gtk_label_set_label (self->oars_button_label, _("All Ages"));
|
gtk_menu_button_set_label (self->oars_button, _("All Ages"));
|
||||||
|
|
||||||
for (i = 0; age != oars_disabled_age && entries[i] != NULL; i++)
|
for (i = 0; age != oars_disabled_age && entries[i] != NULL; i++)
|
||||||
{
|
{
|
||||||
if (ages[i] == age)
|
if (ages[i] == age)
|
||||||
{
|
{
|
||||||
gtk_label_set_label (self->oars_button_label, entries[i]);
|
gtk_menu_button_set_label (self->oars_button, entries[i]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -688,53 +661,6 @@ on_set_age_action_activated (GSimpleAction *action,
|
||||||
schedule_update_blocklisted_apps (self);
|
schedule_update_blocklisted_apps (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
list_box_header_func (GtkListBoxRow *row,
|
|
||||||
GtkListBoxRow *before,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
GtkWidget *current;
|
|
||||||
|
|
||||||
if (before == NULL)
|
|
||||||
{
|
|
||||||
gtk_list_box_row_set_header (row, NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = gtk_list_box_row_get_header (row);
|
|
||||||
if (current == NULL)
|
|
||||||
{
|
|
||||||
current = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
|
|
||||||
gtk_widget_show (current);
|
|
||||||
gtk_list_box_row_set_header (row, current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
on_keynav_failed (GtkWidget *listbox,
|
|
||||||
GtkDirectionType direction,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
MctUserControls *self = MCT_USER_CONTROLS (user_data);
|
|
||||||
GtkWidget *new_widget = NULL;
|
|
||||||
|
|
||||||
/* There are currently two listboxes, so don’t over-complicate this function. */
|
|
||||||
if (listbox == GTK_WIDGET (self->application_usage_permissions_listbox) &&
|
|
||||||
direction == GTK_DIR_DOWN)
|
|
||||||
new_widget = GTK_WIDGET (self->software_installation_permissions_listbox);
|
|
||||||
else if (listbox == GTK_WIDGET (self->software_installation_permissions_listbox) &&
|
|
||||||
direction == GTK_DIR_UP)
|
|
||||||
new_widget = GTK_WIDGET (self->application_usage_permissions_listbox);
|
|
||||||
|
|
||||||
if (new_widget != NULL)
|
|
||||||
{
|
|
||||||
gtk_widget_child_focus (new_widget, direction);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* GObject overrides */
|
/* GObject overrides */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -850,6 +776,10 @@ mct_user_controls_get_property (GObject *object,
|
||||||
g_value_set_object (value, self->dbus_connection);
|
g_value_set_object (value, self->dbus_connection);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PROP_DESCRIPTION:
|
||||||
|
g_value_set_string (value, self->description);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
}
|
}
|
||||||
|
@ -895,6 +825,10 @@ mct_user_controls_set_property (GObject *object,
|
||||||
self->dbus_connection = g_value_dup_object (value);
|
self->dbus_connection = g_value_dup_object (value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PROP_DESCRIPTION:
|
||||||
|
mct_user_controls_set_description (self, g_value_get_string (value));
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
}
|
}
|
||||||
|
@ -1013,6 +947,25 @@ mct_user_controls_class_init (MctUserControlsClass *klass)
|
||||||
G_PARAM_STATIC_STRINGS |
|
G_PARAM_STATIC_STRINGS |
|
||||||
G_PARAM_EXPLICIT_NOTIFY);
|
G_PARAM_EXPLICIT_NOTIFY);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MctUserControls:description: (nullable)
|
||||||
|
*
|
||||||
|
* The description for the currently selected user account, or %NULL if no
|
||||||
|
* user is selected.
|
||||||
|
*
|
||||||
|
* If set, it must be valid UTF-8 and non-empty.
|
||||||
|
*
|
||||||
|
* Since: 0.11.0
|
||||||
|
*/
|
||||||
|
properties[PROP_DESCRIPTION] =
|
||||||
|
g_param_spec_string ("description",
|
||||||
|
"Description",
|
||||||
|
"The description for the currently selected user account, or %NULL if no user is selected.",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_STATIC_STRINGS |
|
||||||
|
G_PARAM_EXPLICIT_NOTIFY);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MctUserControls:dbus-connection: (not nullable)
|
* MctUserControls:dbus-connection: (not nullable)
|
||||||
*
|
*
|
||||||
|
@ -1036,32 +989,24 @@ mct_user_controls_class_init (MctUserControlsClass *klass)
|
||||||
gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/MalcontentUi/ui/user-controls.ui");
|
gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/MalcontentUi/ui/user-controls.ui");
|
||||||
|
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, age_menu);
|
gtk_widget_class_bind_template_child (widget_class, MctUserControls, age_menu);
|
||||||
|
gtk_widget_class_bind_template_child (widget_class, MctUserControls, description_label);
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_software_installation_switch);
|
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_software_installation_switch);
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_software_installation_description);
|
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_software_installation_row);
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_web_browsers_switch);
|
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_web_browsers_switch);
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_web_browsers_description);
|
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_web_browsers_row);
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, oars_button);
|
gtk_widget_class_bind_template_child (widget_class, MctUserControls, oars_button);
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, oars_button_label);
|
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, oars_popover);
|
gtk_widget_class_bind_template_child (widget_class, MctUserControls, oars_popover);
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_applications_dialog);
|
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_applications_dialog);
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_applications_description);
|
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_applications_row);
|
gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_applications_row);
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, application_usage_permissions_listbox);
|
|
||||||
gtk_widget_class_bind_template_child (widget_class, MctUserControls, software_installation_permissions_listbox);
|
|
||||||
|
|
||||||
gtk_widget_class_bind_template_callback (widget_class, on_restrict_installation_switch_active_changed_cb);
|
gtk_widget_class_bind_template_callback (widget_class, on_restrict_installation_switch_active_changed_cb);
|
||||||
gtk_widget_class_bind_template_callback (widget_class, on_restrict_web_browsers_switch_active_changed_cb);
|
gtk_widget_class_bind_template_callback (widget_class, on_restrict_web_browsers_switch_active_changed_cb);
|
||||||
gtk_widget_class_bind_template_callback (widget_class, on_restrict_applications_button_clicked_cb);
|
gtk_widget_class_bind_template_callback (widget_class, on_restrict_applications_dialog_close_request_cb);
|
||||||
gtk_widget_class_bind_template_callback (widget_class, on_restrict_applications_dialog_delete_event_cb);
|
|
||||||
gtk_widget_class_bind_template_callback (widget_class, on_restrict_applications_dialog_response_cb);
|
|
||||||
gtk_widget_class_bind_template_callback (widget_class, on_application_usage_permissions_listbox_activated_cb);
|
|
||||||
gtk_widget_class_bind_template_callback (widget_class, on_keynav_failed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mct_user_controls_init (MctUserControls *self)
|
mct_user_controls_init (MctUserControls *self)
|
||||||
{
|
{
|
||||||
g_autoptr(GError) error = NULL;
|
|
||||||
g_autoptr(GtkCssProvider) provider = NULL;
|
g_autoptr(GtkCssProvider) provider = NULL;
|
||||||
|
|
||||||
/* Ensure the types used in the UI are registered. */
|
/* Ensure the types used in the UI are registered. */
|
||||||
|
@ -1072,9 +1017,9 @@ mct_user_controls_init (MctUserControls *self)
|
||||||
provider = gtk_css_provider_new ();
|
provider = gtk_css_provider_new ();
|
||||||
gtk_css_provider_load_from_resource (provider,
|
gtk_css_provider_load_from_resource (provider,
|
||||||
"/org/freedesktop/MalcontentUi/ui/restricts-switch.css");
|
"/org/freedesktop/MalcontentUi/ui/restricts-switch.css");
|
||||||
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
|
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
|
||||||
GTK_STYLE_PROVIDER (provider),
|
GTK_STYLE_PROVIDER (provider),
|
||||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
|
||||||
|
|
||||||
self->selected_age = (guint) -1;
|
self->selected_age = (guint) -1;
|
||||||
|
|
||||||
|
@ -1090,13 +1035,7 @@ mct_user_controls_init (MctUserControls *self)
|
||||||
"permissions",
|
"permissions",
|
||||||
G_ACTION_GROUP (self->action_group));
|
G_ACTION_GROUP (self->action_group));
|
||||||
|
|
||||||
gtk_popover_bind_model (self->oars_popover, G_MENU_MODEL (self->age_menu), NULL);
|
gtk_popover_menu_set_menu_model (self->oars_popover, G_MENU_MODEL (self->age_menu));
|
||||||
|
|
||||||
/* Automatically add separators between rows. */
|
|
||||||
gtk_list_box_set_header_func (self->application_usage_permissions_listbox,
|
|
||||||
list_box_header_func, NULL, NULL);
|
|
||||||
gtk_list_box_set_header_func (self->software_installation_permissions_listbox,
|
|
||||||
list_box_header_func, NULL, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1461,6 +1400,38 @@ mct_user_controls_set_user_display_name (MctUserControls *self,
|
||||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USER_DISPLAY_NAME]);
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USER_DISPLAY_NAME]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mct_user_controls_set_description:
|
||||||
|
* @self: an #MctUserControls
|
||||||
|
* @description: (nullable) (transfer none): the description shown
|
||||||
|
* above the controls, or %NULL if none.
|
||||||
|
*
|
||||||
|
* Set the value of #MctUserControls:description.
|
||||||
|
*
|
||||||
|
* Since: 0.11.0
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
mct_user_controls_set_description (MctUserControls *self,
|
||||||
|
const gchar *description)
|
||||||
|
{
|
||||||
|
g_return_if_fail (MCT_IS_USER_CONTROLS (self));
|
||||||
|
g_return_if_fail (description != NULL);
|
||||||
|
|
||||||
|
/* If we have pending unsaved changes from the previous user, force them to be
|
||||||
|
* saved first. */
|
||||||
|
flush_update_blocklisted_apps (self);
|
||||||
|
|
||||||
|
if (g_strcmp0 (self->description, description) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_clear_pointer (&self->description, g_free);
|
||||||
|
self->description = g_strdup (description);
|
||||||
|
|
||||||
|
setup_parental_control_settings (self);
|
||||||
|
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DESCRIPTION]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mct_user_controls_build_app_filter:
|
* mct_user_controls_build_app_filter:
|
||||||
* @self: an #MctUserControls
|
* @self: an #MctUserControls
|
||||||
|
|
|
@ -27,13 +27,14 @@
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <adwaita.h>
|
||||||
#include <libmalcontent/malcontent.h>
|
#include <libmalcontent/malcontent.h>
|
||||||
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define MCT_TYPE_USER_CONTROLS (mct_user_controls_get_type())
|
#define MCT_TYPE_USER_CONTROLS (mct_user_controls_get_type())
|
||||||
G_DECLARE_FINAL_TYPE (MctUserControls, mct_user_controls, MCT, USER_CONTROLS, GtkGrid)
|
G_DECLARE_FINAL_TYPE (MctUserControls, mct_user_controls, MCT, USER_CONTROLS, AdwBin)
|
||||||
|
|
||||||
ActUser *mct_user_controls_get_user (MctUserControls *self);
|
ActUser *mct_user_controls_get_user (MctUserControls *self);
|
||||||
void mct_user_controls_set_user (MctUserControls *self,
|
void mct_user_controls_set_user (MctUserControls *self,
|
||||||
|
@ -59,6 +60,9 @@ const gchar *mct_user_controls_get_user_display_name (MctUserControls *self);
|
||||||
void mct_user_controls_set_user_display_name (MctUserControls *self,
|
void mct_user_controls_set_user_display_name (MctUserControls *self,
|
||||||
const gchar *user_display_name);
|
const gchar *user_display_name);
|
||||||
|
|
||||||
|
void mct_user_controls_set_description (MctUserControls *self,
|
||||||
|
const gchar *description);
|
||||||
|
|
||||||
void mct_user_controls_build_app_filter (MctUserControls *self,
|
void mct_user_controls_build_app_filter (MctUserControls *self,
|
||||||
MctAppFilterBuilder *builder);
|
MctAppFilterBuilder *builder);
|
||||||
|
|
||||||
|
|
|
@ -2,491 +2,130 @@
|
||||||
<!-- Copyright © 2018, 2019, 2020 Endless, Inc. -->
|
<!-- Copyright © 2018, 2019, 2020 Endless, Inc. -->
|
||||||
<interface domain="malcontent">
|
<interface domain="malcontent">
|
||||||
<requires lib="gtk+" version="3.12"/>
|
<requires lib="gtk+" version="3.12"/>
|
||||||
<template class="MctUserControls" parent="GtkGrid">
|
<template class="MctUserControls" parent="AdwBin">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="row-spacing">6</property>
|
|
||||||
<property name="column-spacing">12</property>
|
|
||||||
<property name="valign">start</property>
|
|
||||||
|
|
||||||
<!-- Application Usage Restrictions -->
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="spacing">24</property>
|
||||||
<property name="xalign">0.0</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="label" translatable="yes">Application Usage Restrictions</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="weight" value="bold" />
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="top-attach">0</property>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<child>
|
|
||||||
<object class="GtkFrame">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="label_xalign">0</property>
|
|
||||||
<property name="shadow_type">in</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBox" id="application_usage_permissions_listbox">
|
<object class="AdwPreferencesGroup">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="selection_mode">none</property>
|
|
||||||
<property name="activate-on-single-click">True</property>
|
|
||||||
<signal name="row-activated" handler="on_application_usage_permissions_listbox_activated_cb" object="MctUserControls" swapped="no" />
|
|
||||||
<signal name="keynav-failed" handler="on_keynav_failed" object="MctUserControls" swapped="no" />
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBoxRow">
|
<object class="GtkLabel" id="description_label">
|
||||||
<property name="visible">True</property>
|
<property name="wrap">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="xalign">0.0</property>
|
||||||
<property name="activatable">False</property>
|
<property name="yalign">0.0</property>
|
||||||
<property name="selectable">False</property>
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<!-- Application Usage Restrictions -->
|
||||||
|
<child>
|
||||||
|
<object class="AdwPreferencesGroup">
|
||||||
|
<property name="title" translatable="yes">Application Usage Restrictions</property>
|
||||||
|
<child>
|
||||||
|
<object class="AdwActionRow" id="restrict_web_browsers_row">
|
||||||
|
<property name="title" translatable="yes">Restrict _Web Browsers</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="activatable_widget">restrict_web_browsers_switch</property>
|
||||||
|
<!-- Set dynamically from user-controls.c: -->
|
||||||
|
<property name="subtitle">Prevents {username} from running web browsers. Limited web content may still be available in other applications.</property>
|
||||||
|
<property name="subtitle_lines">0</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkGrid">
|
<object class="GtkSwitch" id="restrict_web_browsers_switch">
|
||||||
<property name="visible">True</property>
|
<property name="halign">end</property>
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="valign">center</property>
|
<property name="valign">center</property>
|
||||||
<property name="margin-left">12</property>
|
<signal name="notify::active" handler="on_restrict_web_browsers_switch_active_changed_cb" object="MctUserControls" swapped="no" />
|
||||||
<property name="margin-right">12</property>
|
<style>
|
||||||
<property name="margin-top">8</property>
|
<class name="restricts" />
|
||||||
<property name="margin-bottom">8</property>
|
</style>
|
||||||
<property name="row-spacing">4</property>
|
|
||||||
<property name="column-spacing">4</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="restrict_web_browsers_label">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="ellipsize">end</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="label" translatable="yes">Restrict _Web Browsers</property>
|
|
||||||
<property name="use_underline">True</property>
|
|
||||||
<property name="mnemonic_widget">restrict_web_browsers_switch</property>
|
|
||||||
<accessibility>
|
|
||||||
<relation target="restrict_web_browsers_switch" type="label-for"/>
|
|
||||||
</accessibility>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="restrict_web_browsers_description">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="ellipsize">none</property>
|
|
||||||
<property name="wrap">True</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<!-- Set dynamically from user-controls.c: -->
|
|
||||||
<property name="label">Prevents {username} from running web browsers. Limited web content may still be available in other applications.</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="scale" value="0.88"/>
|
|
||||||
</attributes>
|
|
||||||
<style>
|
|
||||||
<class name="dim-label" />
|
|
||||||
</style>
|
|
||||||
<accessibility>
|
|
||||||
<relation target="restrict_web_browsers_switch" type="description-for"/>
|
|
||||||
</accessibility>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSwitch" id="restrict_web_browsers_switch">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="halign">end</property>
|
|
||||||
<property name="valign">center</property>
|
|
||||||
<signal name="notify::active" handler="on_restrict_web_browsers_switch_active_changed_cb" object="MctUserControls" swapped="no" />
|
|
||||||
<style>
|
|
||||||
<class name="restricts" />
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">1</property>
|
|
||||||
<property name="top-attach">0</property>
|
|
||||||
<property name="height">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBoxRow" id="restrict_applications_row">
|
<object class="AdwActionRow" id="restrict_applications_row">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="activatable">True</property>
|
<property name="activatable">True</property>
|
||||||
<property name="selectable">False</property>
|
<property name="title" translatable="yes">_Restrict Applications</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<!-- Set dynamically from user-controls.c: -->
|
||||||
|
<property name="subtitle">Prevents specified applications from being used by {username}.</property>
|
||||||
|
<property name="subtitle_lines">0</property>
|
||||||
|
<property name="action-name">permissions.restrict-applications</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkGrid">
|
<object class="GtkImage">
|
||||||
<property name="visible">True</property>
|
<property name="icon-name">go-next-symbolic</property>
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="valign">center</property>
|
|
||||||
<property name="margin-left">12</property>
|
|
||||||
<property name="margin-right">12</property>
|
|
||||||
<property name="margin-top">8</property>
|
|
||||||
<property name="margin-bottom">8</property>
|
|
||||||
<property name="row-spacing">4</property>
|
|
||||||
<property name="column-spacing">4</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="restrict_applications_label">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="ellipsize">end</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="label" translatable="yes">_Restrict Applications</property>
|
|
||||||
<property name="use_underline">True</property>
|
|
||||||
<property name="mnemonic_widget">restrict_applications_button</property>
|
|
||||||
<accessibility>
|
|
||||||
<relation target="restrict_applications_button" type="label-for"/>
|
|
||||||
</accessibility>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="restrict_applications_description">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="ellipsize">none</property>
|
|
||||||
<property name="wrap">True</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<!-- Set dynamically from user-controls.c: -->
|
|
||||||
<property name="label">Prevents specified applications from being used by {username}.</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="scale" value="0.88"/>
|
|
||||||
</attributes>
|
|
||||||
<style>
|
|
||||||
<class name="dim-label" />
|
|
||||||
</style>
|
|
||||||
<accessibility>
|
|
||||||
<relation target="restrict_applications_button" type="description-for"/>
|
|
||||||
</accessibility>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="restrict_applications_button">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="halign">end</property>
|
|
||||||
<property name="valign">center</property>
|
|
||||||
<property name="relief">none</property>
|
|
||||||
<signal name="clicked" handler="on_restrict_applications_button_clicked_cb" object="MctUserControls" swapped="no" />
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="icon-name">go-next-symbolic</property>
|
|
||||||
<property name="icon-size">4</property><!-- GTK_ICON_SIZE_BUTTON -->
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">1</property>
|
|
||||||
<property name="top-attach">0</property>
|
|
||||||
<property name="height">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="content"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="top-attach">1</property>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<!-- Software Installation Restrictions -->
|
<!-- Software Installation Restrictions -->
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="margin-top">12</property>
|
|
||||||
<property name="xalign">0.0</property>
|
|
||||||
<property name="label" translatable="yes">Software Installation Restrictions</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="weight" value="bold" />
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="top-attach">2</property>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<child>
|
|
||||||
<object class="GtkFrame">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="label_xalign">0</property>
|
|
||||||
<property name="shadow_type">in</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBox" id="software_installation_permissions_listbox">
|
<object class="AdwPreferencesGroup">
|
||||||
<property name="visible">True</property>
|
<property name="title" translatable="yes">Software Installation Restrictions</property>
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="selection_mode">none</property>
|
|
||||||
<property name="activate_on_single_click">False</property>
|
|
||||||
<signal name="keynav-failed" handler="on_keynav_failed" object="MctUserControls" swapped="no" />
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBoxRow">
|
<object class="AdwActionRow" id="restrict_software_installation_row">
|
||||||
<property name="visible" bind-source="restrict_software_installation_switch" bind-property="visible" bind-flags="default|sync-create" />
|
<property name="visible" bind-source="restrict_software_installation_switch" bind-property="visible" bind-flags="default|sync-create" />
|
||||||
<property name="can_focus">True</property>
|
<property name="title" translatable="yes">Restrict Application _Installation</property>
|
||||||
<property name="activatable">False</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="selectable">False</property>
|
<property name="activatable_widget">restrict_software_installation_switch</property>
|
||||||
|
<!-- Set dynamically from user-controls.c: -->
|
||||||
|
<property name="subtitle">Prevents {username} from installing applications.</property>
|
||||||
|
<property name="subtitle_lines">0</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkGrid">
|
<object class="GtkSwitch" id="restrict_software_installation_switch">
|
||||||
<property name="visible">True</property>
|
<property name="halign">end</property>
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="valign">center</property>
|
<property name="valign">center</property>
|
||||||
<property name="margin-left">12</property>
|
<signal name="notify::active" handler="on_restrict_installation_switch_active_changed_cb" object="MctUserControls" swapped="no" />
|
||||||
<property name="margin-right">12</property>
|
<style>
|
||||||
<property name="margin-top">8</property>
|
<class name="restricts" />
|
||||||
<property name="margin-bottom">8</property>
|
</style>
|
||||||
<property name="row-spacing">4</property>
|
|
||||||
<property name="column-spacing">4</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="restrict_software_installation_label">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="ellipsize">end</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="label" translatable="yes">Restrict Application _Installation</property>
|
|
||||||
<property name="use_underline">True</property>
|
|
||||||
<property name="mnemonic_widget">restrict_software_installation_switch</property>
|
|
||||||
<accessibility>
|
|
||||||
<relation target="restrict_software_installation_switch" type="label-for"/>
|
|
||||||
</accessibility>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="restrict_software_installation_description">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="ellipsize">none</property>
|
|
||||||
<property name="wrap">True</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<!-- Set dynamically from user-controls.c: -->
|
|
||||||
<property name="label">Prevents {username} from installing applications.</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="scale" value="0.88"/>
|
|
||||||
</attributes>
|
|
||||||
<style>
|
|
||||||
<class name="dim-label" />
|
|
||||||
</style>
|
|
||||||
<accessibility>
|
|
||||||
<relation target="restrict_software_installation_switch" type="description-for"/>
|
|
||||||
</accessibility>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSwitch" id="restrict_software_installation_switch">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="halign">end</property>
|
|
||||||
<property name="valign">center</property>
|
|
||||||
<signal name="notify::active" handler="on_restrict_installation_switch_active_changed_cb" object="MctUserControls" swapped="no" />
|
|
||||||
<style>
|
|
||||||
<class name="restricts" />
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">1</property>
|
|
||||||
<property name="top-attach">0</property>
|
|
||||||
<property name="height">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBoxRow">
|
<object class="AdwActionRow">
|
||||||
<property name="visible">True</property>
|
<property name="title" translatable="yes">Application _Suitability</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="activatable">False</property>
|
<property name="activatable_widget">oars_button</property>
|
||||||
<property name="selectable">False</property>
|
<property name="subtitle" translatable="yes">Restricts browsing or installation of applications to applications suitable for certain ages or above.</property>
|
||||||
|
<property name="subtitle_lines">0</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkGrid">
|
<object class="GtkMenuButton" id="oars_button">
|
||||||
<property name="visible">True</property>
|
<property name="halign">end</property>
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="valign">center</property>
|
<property name="valign">center</property>
|
||||||
<property name="margin-left">12</property>
|
<property name="popover">oars_popover</property>
|
||||||
<property name="margin-right">12</property>
|
<property name="always-show-arrow">True</property>
|
||||||
<property name="margin-top">8</property>
|
|
||||||
<property name="margin-bottom">8</property>
|
|
||||||
<property name="row-spacing">4</property>
|
|
||||||
<property name="column-spacing">4</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="oars_label">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="ellipsize">end</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="label" translatable="yes">Application _Suitability</property>
|
|
||||||
<property name="use_underline">True</property>
|
|
||||||
<property name="mnemonic_widget">oars_button</property>
|
|
||||||
<accessibility>
|
|
||||||
<relation target="oars_button" type="label-for"/>
|
|
||||||
<relation target="oars_button" type="flows-to"/>
|
|
||||||
</accessibility>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="oars_description">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="ellipsize">none</property>
|
|
||||||
<property name="wrap">True</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="label" translatable="yes">Restricts browsing or installation of applications to applications suitable for certain ages or above.</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="scale" value="0.88"/>
|
|
||||||
</attributes>
|
|
||||||
<style>
|
|
||||||
<class name="dim-label" />
|
|
||||||
</style>
|
|
||||||
<accessibility>
|
|
||||||
<relation target="oars_button" type="description-for"/>
|
|
||||||
</accessibility>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkMenuButton" id="oars_button">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="halign">end</property>
|
|
||||||
<property name="valign">center</property>
|
|
||||||
<property name="direction">right</property>
|
|
||||||
<property name="popover">oars_popover</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="orientation">horizontal</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="oars_button_label">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="label"></property>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="xalign">0.0</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="icon-name">pan-down-symbolic</property>
|
|
||||||
<property name="icon-size">4</property><!-- GTK_ICON_SIZE_BUTTON -->
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">1</property>
|
|
||||||
<property name="top-attach">0</property>
|
|
||||||
<property name="height">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="content"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="top-attach">3</property>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<object class="GtkPopoverMenu" id="oars_popover">
|
<object class="GtkPopoverMenu" id="oars_popover" />
|
||||||
<accessibility>
|
|
||||||
<relation target="oars_button" type="popup-for"/>
|
|
||||||
</accessibility>
|
|
||||||
</object>
|
|
||||||
|
|
||||||
<menu id="age_menu" />
|
<menu id="age_menu" />
|
||||||
|
|
||||||
<object class="GtkSizeGroup">
|
|
||||||
<property name="mode">horizontal</property>
|
|
||||||
<widgets>
|
|
||||||
<widget name="oars_button" />
|
|
||||||
<widget name="oars_popover" />
|
|
||||||
</widgets>
|
|
||||||
</object>
|
|
||||||
|
|
||||||
<object class="GtkSizeGroup">
|
|
||||||
<property name="mode">horizontal</property>
|
|
||||||
<widgets>
|
|
||||||
<widget name="restrict_web_browsers_label" />
|
|
||||||
<widget name="restrict_web_browsers_description" />
|
|
||||||
<widget name="restrict_applications_label" />
|
|
||||||
<widget name="restrict_applications_description" />
|
|
||||||
<widget name="oars_label" />
|
|
||||||
<widget name="restrict_software_installation_label" />
|
|
||||||
</widgets>
|
|
||||||
</object>
|
|
||||||
|
|
||||||
<object class="MctRestrictApplicationsDialog" id="restrict_applications_dialog">
|
<object class="MctRestrictApplicationsDialog" id="restrict_applications_dialog">
|
||||||
<property name="visible">False</property>
|
<property name="visible">False</property>
|
||||||
<property name="modal">True</property>
|
<property name="modal">True</property>
|
||||||
<property name="destroy-with-parent">False</property>
|
<property name="destroy-with-parent">False</property>
|
||||||
<property name="use-header-bar">1</property>
|
<signal name="close-request" handler="on_restrict_applications_dialog_close_request_cb" />
|
||||||
<signal name="delete-event" handler="on_restrict_applications_dialog_delete_event_cb" />
|
|
||||||
<signal name="response" handler="on_restrict_applications_dialog_response_cb" />
|
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <glib/gi18n-lib.h>
|
#include <glib/gi18n-lib.h>
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <adwaita.h>
|
||||||
#include <libmalcontent-ui/malcontent-ui.h>
|
#include <libmalcontent-ui/malcontent-ui.h>
|
||||||
#include <polkit/polkit.h>
|
#include <polkit/polkit.h>
|
||||||
|
|
||||||
|
@ -76,8 +77,7 @@ struct _MctApplication
|
||||||
MctUserSelector *user_selector;
|
MctUserSelector *user_selector;
|
||||||
MctUserControls *user_controls;
|
MctUserControls *user_controls;
|
||||||
GtkStack *main_stack;
|
GtkStack *main_stack;
|
||||||
GtkLabel *error_title;
|
AdwStatusPage *error_page;
|
||||||
GtkLabel *error_message;
|
|
||||||
GtkLockButton *lock_button;
|
GtkLockButton *lock_button;
|
||||||
GtkButton *user_accounts_panel_button;
|
GtkButton *user_accounts_panel_button;
|
||||||
GtkLabel *help_label;
|
GtkLabel *help_label;
|
||||||
|
@ -209,11 +209,9 @@ mct_application_activate (GApplication *application)
|
||||||
self->main_stack = GTK_STACK (gtk_builder_get_object (builder, "main_stack"));
|
self->main_stack = GTK_STACK (gtk_builder_get_object (builder, "main_stack"));
|
||||||
self->user_selector = MCT_USER_SELECTOR (gtk_builder_get_object (builder, "user_selector"));
|
self->user_selector = MCT_USER_SELECTOR (gtk_builder_get_object (builder, "user_selector"));
|
||||||
self->user_controls = MCT_USER_CONTROLS (gtk_builder_get_object (builder, "user_controls"));
|
self->user_controls = MCT_USER_CONTROLS (gtk_builder_get_object (builder, "user_controls"));
|
||||||
self->error_title = GTK_LABEL (gtk_builder_get_object (builder, "error_title"));
|
self->error_page = ADW_STATUS_PAGE (gtk_builder_get_object (builder, "error_page"));
|
||||||
self->error_message = GTK_LABEL (gtk_builder_get_object (builder, "error_message"));
|
|
||||||
self->lock_button = GTK_LOCK_BUTTON (gtk_builder_get_object (builder, "lock_button"));
|
self->lock_button = GTK_LOCK_BUTTON (gtk_builder_get_object (builder, "lock_button"));
|
||||||
self->user_accounts_panel_button = GTK_BUTTON (gtk_builder_get_object (builder, "user_accounts_panel_button"));
|
self->user_accounts_panel_button = GTK_BUTTON (gtk_builder_get_object (builder, "user_accounts_panel_button"));
|
||||||
self->help_label = GTK_LABEL (gtk_builder_get_object (builder, "help_label"));
|
|
||||||
|
|
||||||
/* Connect signals. */
|
/* Connect signals. */
|
||||||
g_signal_connect_object (self->user_selector, "notify::user",
|
g_signal_connect_object (self->user_selector, "notify::user",
|
||||||
|
@ -230,7 +228,7 @@ mct_application_activate (GApplication *application)
|
||||||
user_selector_notify_user_cb (G_OBJECT (self->user_selector), NULL, self);
|
user_selector_notify_user_cb (G_OBJECT (self->user_selector), NULL, self);
|
||||||
user_manager_notify_is_loaded_cb (G_OBJECT (self->user_manager), NULL, self);
|
user_manager_notify_is_loaded_cb (G_OBJECT (self->user_manager), NULL, self);
|
||||||
|
|
||||||
gtk_widget_show (GTK_WIDGET (window));
|
gtk_window_present (GTK_WINDOW (window));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bring the window to the front. */
|
/* Bring the window to the front. */
|
||||||
|
@ -250,6 +248,8 @@ mct_application_startup (GApplication *application)
|
||||||
/* Chain up. */
|
/* Chain up. */
|
||||||
G_APPLICATION_CLASS (mct_application_parent_class)->startup (application);
|
G_APPLICATION_CLASS (mct_application_parent_class)->startup (application);
|
||||||
|
|
||||||
|
adw_init ();
|
||||||
|
|
||||||
g_action_map_add_action_entries (G_ACTION_MAP (application), app_entries,
|
g_action_map_add_action_entries (G_ACTION_MAP (application), app_entries,
|
||||||
G_N_ELEMENTS (app_entries), application);
|
G_N_ELEMENTS (app_entries), application);
|
||||||
|
|
||||||
|
@ -323,13 +323,16 @@ about_action_cb (GSimpleAction *action, GVariant *parameters, gpointer user_data
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
help_action_cb (GSimpleAction *action, GVariant *parameters, gpointer user_data)
|
on_malcontent_help_shown_finished_cb (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
MctApplication *self = MCT_APPLICATION (user_data);
|
MctApplication *self = MCT_APPLICATION (user_data);
|
||||||
g_autoptr(GError) local_error = NULL;
|
g_autoptr(GError) local_error = NULL;
|
||||||
|
|
||||||
if (!gtk_show_uri_on_window (mct_application_get_main_window (self), "help:malcontent",
|
if (!gtk_show_uri_full_finish (mct_application_get_main_window (self),
|
||||||
gtk_get_current_event_time (), &local_error))
|
result,
|
||||||
|
&local_error))
|
||||||
{
|
{
|
||||||
GtkWidget *dialog = gtk_message_dialog_new (mct_application_get_main_window (self),
|
GtkWidget *dialog = gtk_message_dialog_new (mct_application_get_main_window (self),
|
||||||
GTK_DIALOG_MODAL,
|
GTK_DIALOG_MODAL,
|
||||||
|
@ -337,13 +340,23 @@ help_action_cb (GSimpleAction *action, GVariant *parameters, gpointer user_data)
|
||||||
GTK_BUTTONS_OK,
|
GTK_BUTTONS_OK,
|
||||||
_("The help contents could not be displayed"));
|
_("The help contents could not be displayed"));
|
||||||
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", local_error->message);
|
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", local_error->message);
|
||||||
|
gtk_window_present (GTK_WINDOW (dialog));
|
||||||
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
||||||
|
|
||||||
gtk_widget_destroy (dialog);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
help_action_cb (GSimpleAction *action, GVariant *parameters, gpointer user_data)
|
||||||
|
{
|
||||||
|
MctApplication *self = MCT_APPLICATION (user_data);
|
||||||
|
|
||||||
|
gtk_show_uri_full (mct_application_get_main_window (self),
|
||||||
|
"help:malcontent",
|
||||||
|
GDK_CURRENT_TIME,
|
||||||
|
NULL,
|
||||||
|
on_malcontent_help_shown_finished_cb,
|
||||||
|
self);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
quit_action_cb (GSimpleAction *action, GVariant *parameters, gpointer user_data)
|
quit_action_cb (GSimpleAction *action, GVariant *parameters, gpointer user_data)
|
||||||
{
|
{
|
||||||
|
@ -371,10 +384,10 @@ update_main_stack (MctApplication *self)
|
||||||
if ((is_user_manager_loaded && act_user_manager_no_service (self->user_manager)) ||
|
if ((is_user_manager_loaded && act_user_manager_no_service (self->user_manager)) ||
|
||||||
self->permission_error != NULL)
|
self->permission_error != NULL)
|
||||||
{
|
{
|
||||||
gtk_label_set_label (self->error_title,
|
adw_status_page_set_title (self->error_page,
|
||||||
_("Failed to load user data from the system"));
|
_("Failed to load user data from the system"));
|
||||||
gtk_label_set_label (self->error_message,
|
adw_status_page_set_description (self->error_page,
|
||||||
_("Please make sure that the AccountsService is installed and enabled."));
|
_("Please make sure that the AccountsService is installed and enabled."));
|
||||||
|
|
||||||
new_page_name = "error";
|
new_page_name = "error";
|
||||||
new_focus_widget = NULL;
|
new_focus_widget = NULL;
|
||||||
|
@ -410,7 +423,7 @@ update_main_stack (MctApplication *self)
|
||||||
"with %s. <a href='https://www.commonsensemedia.org/privacy-and-internet-safety'>"
|
"with %s. <a href='https://www.commonsensemedia.org/privacy-and-internet-safety'>"
|
||||||
"Read guidance</a> on what to consider."),
|
"Read guidance</a> on what to consider."),
|
||||||
act_user_get_real_name (selected_user));
|
act_user_get_real_name (selected_user));
|
||||||
gtk_label_set_markup (self->help_label, help_label);
|
mct_user_controls_set_description (self->user_controls, help_label);
|
||||||
|
|
||||||
mct_user_controls_set_user (self->user_controls, selected_user);
|
mct_user_controls_set_user (self->user_controls, selected_user);
|
||||||
|
|
||||||
|
@ -427,12 +440,7 @@ update_main_stack (MctApplication *self)
|
||||||
gtk_stack_set_visible_child_name (self->main_stack, new_page_name);
|
gtk_stack_set_visible_child_name (self->main_stack, new_page_name);
|
||||||
|
|
||||||
if (new_focus_widget != NULL && !g_str_equal (old_page_name, new_page_name))
|
if (new_focus_widget != NULL && !g_str_equal (old_page_name, new_page_name))
|
||||||
{
|
gtk_widget_grab_focus (new_focus_widget);
|
||||||
if (gtk_widget_get_can_focus (new_focus_widget))
|
|
||||||
gtk_widget_grab_focus (new_focus_widget);
|
|
||||||
else
|
|
||||||
gtk_widget_child_focus (new_focus_widget, GTK_DIR_TAB_FORWARD);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
* - Felipe Borges <felipeborges@gnome.org>
|
* - Felipe Borges <felipeborges@gnome.org>
|
||||||
|
* - Georges Basile Stavracas Neto <georges@endlessos.org>
|
||||||
* - Philip Withnall <withnall@endlessm.com>
|
* - Philip Withnall <withnall@endlessm.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -29,13 +30,16 @@
|
||||||
|
|
||||||
#define ARROW_SIZE 20
|
#define ARROW_SIZE 20
|
||||||
|
|
||||||
|
#define MCT_TYPE_CAROUSEL_LAYOUT (mct_carousel_layout_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (MctCarouselLayout, mct_carousel_layout, MCT, CAROUSEL_LAYOUT, GtkLayoutManager)
|
||||||
|
|
||||||
struct _MctCarouselItem {
|
struct _MctCarouselItem {
|
||||||
GtkRadioButton parent;
|
GtkButton parent;
|
||||||
|
|
||||||
gint page;
|
gint page;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE (MctCarouselItem, mct_carousel_item, GTK_TYPE_RADIO_BUTTON)
|
G_DEFINE_TYPE (MctCarouselItem, mct_carousel_item, GTK_TYPE_BUTTON)
|
||||||
|
|
||||||
GtkWidget *
|
GtkWidget *
|
||||||
mct_carousel_item_new (void)
|
mct_carousel_item_new (void)
|
||||||
|
@ -43,28 +47,36 @@ mct_carousel_item_new (void)
|
||||||
return g_object_new (MCT_TYPE_CAROUSEL_ITEM, NULL);
|
return g_object_new (MCT_TYPE_CAROUSEL_ITEM, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mct_carousel_item_set_child (MctCarouselItem *self,
|
||||||
|
GtkWidget *child)
|
||||||
|
{
|
||||||
|
g_return_if_fail (MCT_IS_CAROUSEL_ITEM (self));
|
||||||
|
|
||||||
|
gtk_button_set_child (GTK_BUTTON (self), child);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mct_carousel_item_class_init (MctCarouselItemClass *klass)
|
mct_carousel_item_class_init (MctCarouselItemClass *klass)
|
||||||
{
|
{
|
||||||
|
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (klass), "carousel-item");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mct_carousel_item_init (MctCarouselItem *self)
|
mct_carousel_item_init (MctCarouselItem *self)
|
||||||
{
|
{
|
||||||
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (self), FALSE);
|
|
||||||
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)),
|
|
||||||
"carousel-item");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct _MctCarousel {
|
struct _MctCarousel {
|
||||||
GtkRevealer parent;
|
AdwBin parent;
|
||||||
|
|
||||||
|
GtkRevealer *revealer;
|
||||||
|
|
||||||
GList *children;
|
GList *children;
|
||||||
gint visible_page;
|
gint visible_page;
|
||||||
MctCarouselItem *selected_item;
|
MctCarouselItem *selected_item;
|
||||||
GtkWidget *last_box;
|
GtkWidget *last_box;
|
||||||
GtkWidget *arrow;
|
GtkWidget *arrow;
|
||||||
gint arrow_start_x;
|
|
||||||
|
|
||||||
/* Widgets */
|
/* Widgets */
|
||||||
GtkStack *stack;
|
GtkStack *stack;
|
||||||
|
@ -74,7 +86,7 @@ struct _MctCarousel {
|
||||||
GtkStyleProvider *provider;
|
GtkStyleProvider *provider;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE (MctCarousel, mct_carousel, GTK_TYPE_REVEALER)
|
G_DEFINE_TYPE (MctCarousel, mct_carousel, ADW_TYPE_BIN)
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ITEM_ACTIVATED,
|
ITEM_ACTIVATED,
|
||||||
|
@ -91,9 +103,9 @@ mct_carousel_item_get_x (MctCarouselItem *item,
|
||||||
{
|
{
|
||||||
GtkWidget *widget, *parent;
|
GtkWidget *widget, *parent;
|
||||||
gint width;
|
gint width;
|
||||||
gint dest_x;
|
gdouble dest_x;
|
||||||
|
|
||||||
parent = GTK_WIDGET (carousel->stack);
|
parent = GTK_WIDGET (carousel->revealer);
|
||||||
widget = GTK_WIDGET (item);
|
widget = GTK_WIDGET (item);
|
||||||
|
|
||||||
width = gtk_widget_get_allocated_width (widget);
|
width = gtk_widget_get_allocated_width (widget);
|
||||||
|
@ -116,8 +128,6 @@ mct_carousel_move_arrow (MctCarousel *self)
|
||||||
GtkStyleContext *context;
|
GtkStyleContext *context;
|
||||||
gchar *css;
|
gchar *css;
|
||||||
gint end_x;
|
gint end_x;
|
||||||
GtkSettings *settings;
|
|
||||||
gboolean animations;
|
|
||||||
|
|
||||||
if (!self->selected_item)
|
if (!self->selected_item)
|
||||||
return;
|
return;
|
||||||
|
@ -129,31 +139,9 @@ mct_carousel_move_arrow (MctCarousel *self)
|
||||||
gtk_style_context_remove_provider (context, self->provider);
|
gtk_style_context_remove_provider (context, self->provider);
|
||||||
g_clear_object (&self->provider);
|
g_clear_object (&self->provider);
|
||||||
|
|
||||||
settings = gtk_widget_get_settings (GTK_WIDGET (self));
|
css = g_strdup_printf ("* { margin-left: %dpx; }", end_x);
|
||||||
g_object_get (settings, "gtk-enable-animations", &animations, NULL);
|
|
||||||
|
|
||||||
/* Animate the arrow movement if animations are enabled. Otherwise,
|
|
||||||
* jump the arrow to the right location instantly. */
|
|
||||||
if (animations)
|
|
||||||
{
|
|
||||||
css = g_strdup_printf ("@keyframes arrow_keyframes-%d-%d {\n"
|
|
||||||
" from { margin-left: %dpx; }\n"
|
|
||||||
" to { margin-left: %dpx; }\n"
|
|
||||||
"}\n"
|
|
||||||
"* {\n"
|
|
||||||
" animation-name: arrow_keyframes-%d-%d;\n"
|
|
||||||
"}\n",
|
|
||||||
self->arrow_start_x, end_x,
|
|
||||||
self->arrow_start_x, end_x,
|
|
||||||
self->arrow_start_x, end_x);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
css = g_strdup_printf ("* { margin-left: %dpx }", end_x);
|
|
||||||
}
|
|
||||||
|
|
||||||
self->provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ());
|
self->provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ());
|
||||||
gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (self->provider), css, -1, NULL);
|
gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (self->provider), css, -1);
|
||||||
gtk_style_context_add_provider (context, self->provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
gtk_style_context_add_provider (context, self->provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||||
|
|
||||||
g_free (css);
|
g_free (css);
|
||||||
|
@ -234,10 +222,7 @@ mct_carousel_select_item (MctCarousel *self,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self->selected_item != NULL)
|
if (self->selected_item != NULL)
|
||||||
{
|
page_changed = (self->selected_item->page != item->page);
|
||||||
page_changed = (self->selected_item->page != item->page);
|
|
||||||
self->arrow_start_x = mct_carousel_item_get_x (self->selected_item, self);
|
|
||||||
}
|
|
||||||
|
|
||||||
self->selected_item = item;
|
self->selected_item = item;
|
||||||
self->visible_page = item->page;
|
self->visible_page = item->page;
|
||||||
|
@ -300,59 +285,44 @@ mct_carousel_goto_next_page (GtkWidget *button,
|
||||||
mct_carousel_select_item_at_index (self, self->visible_page * ITEMS_PER_PAGE);
|
mct_carousel_select_item_at_index (self, self->visible_page * ITEMS_PER_PAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
mct_carousel_add (GtkContainer *container,
|
mct_carousel_add (MctCarousel *self,
|
||||||
GtkWidget *widget)
|
MctCarouselItem *item)
|
||||||
{
|
{
|
||||||
MctCarousel *self = MCT_CAROUSEL (container);
|
|
||||||
gboolean last_box_is_full;
|
gboolean last_box_is_full;
|
||||||
|
|
||||||
if (!MCT_IS_CAROUSEL_ITEM (widget))
|
g_return_if_fail (MCT_IS_CAROUSEL (self));
|
||||||
{
|
g_return_if_fail (MCT_IS_CAROUSEL_ITEM (item));
|
||||||
GTK_CONTAINER_CLASS (mct_carousel_parent_class)->add (container, widget);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gtk_style_context_add_class (gtk_widget_get_style_context (widget), "menu");
|
self->children = g_list_append (self->children, item);
|
||||||
gtk_button_set_relief (GTK_BUTTON (widget), GTK_RELIEF_NONE);
|
item->page = get_last_page_number (self);
|
||||||
|
g_signal_connect (item, "clicked", G_CALLBACK (on_item_toggled), self);
|
||||||
self->children = g_list_append (self->children, widget);
|
|
||||||
MCT_CAROUSEL_ITEM (widget)->page = get_last_page_number (self);
|
|
||||||
if (self->selected_item != NULL)
|
|
||||||
gtk_radio_button_join_group (GTK_RADIO_BUTTON (widget), GTK_RADIO_BUTTON (self->selected_item));
|
|
||||||
g_signal_connect (widget, "button-press-event", G_CALLBACK (on_item_toggled), self);
|
|
||||||
|
|
||||||
last_box_is_full = ((g_list_length (self->children) - 1) % ITEMS_PER_PAGE == 0);
|
last_box_is_full = ((g_list_length (self->children) - 1) % ITEMS_PER_PAGE == 0);
|
||||||
if (last_box_is_full)
|
if (last_box_is_full)
|
||||||
{
|
{
|
||||||
g_autofree gchar *page = NULL;
|
g_autofree gchar *page = NULL;
|
||||||
|
|
||||||
page = g_strdup_printf ("%d", MCT_CAROUSEL_ITEM (widget)->page);
|
page = g_strdup_printf ("%d", item->page);
|
||||||
self->last_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
self->last_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 18);
|
||||||
gtk_widget_show (self->last_box);
|
gtk_widget_set_hexpand (self->last_box, TRUE);
|
||||||
gtk_widget_set_valign (self->last_box, GTK_ALIGN_CENTER);
|
gtk_widget_set_valign (self->last_box, GTK_ALIGN_CENTER);
|
||||||
|
gtk_box_set_homogeneous (GTK_BOX (self->last_box), TRUE);
|
||||||
gtk_stack_add_named (self->stack, self->last_box, page);
|
gtk_stack_add_named (self->stack, self->last_box, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_widget_show_all (widget);
|
gtk_box_append (GTK_BOX (self->last_box), GTK_WIDGET (item));
|
||||||
gtk_box_pack_start (GTK_BOX (self->last_box), widget, TRUE, FALSE, 10);
|
|
||||||
|
|
||||||
update_buttons_visibility (self);
|
update_buttons_visibility (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
destroy_widget_cb (GtkWidget *widget,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
gtk_widget_destroy (widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
mct_carousel_purge_items (MctCarousel *self)
|
mct_carousel_purge_items (MctCarousel *self)
|
||||||
{
|
{
|
||||||
gtk_container_forall (GTK_CONTAINER (self->stack),
|
GtkWidget *child;
|
||||||
destroy_widget_cb,
|
|
||||||
NULL);
|
while ((child = gtk_widget_get_first_child (GTK_WIDGET (self->stack))) != NULL)
|
||||||
|
gtk_stack_remove (self->stack, child);
|
||||||
|
|
||||||
g_list_free (self->children);
|
g_list_free (self->children);
|
||||||
self->children = NULL;
|
self->children = NULL;
|
||||||
|
@ -386,7 +356,6 @@ mct_carousel_class_init (MctCarouselClass *klass)
|
||||||
{
|
{
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
|
GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
|
||||||
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
|
|
||||||
|
|
||||||
gtk_widget_class_set_template_from_resource (wclass,
|
gtk_widget_class_set_template_from_resource (wclass,
|
||||||
"/org/freedesktop/MalcontentControl/ui/carousel.ui");
|
"/org/freedesktop/MalcontentControl/ui/carousel.ui");
|
||||||
|
@ -395,13 +364,14 @@ mct_carousel_class_init (MctCarouselClass *klass)
|
||||||
gtk_widget_class_bind_template_child (wclass, MctCarousel, go_back_button);
|
gtk_widget_class_bind_template_child (wclass, MctCarousel, go_back_button);
|
||||||
gtk_widget_class_bind_template_child (wclass, MctCarousel, go_next_button);
|
gtk_widget_class_bind_template_child (wclass, MctCarousel, go_next_button);
|
||||||
gtk_widget_class_bind_template_child (wclass, MctCarousel, arrow);
|
gtk_widget_class_bind_template_child (wclass, MctCarousel, arrow);
|
||||||
|
gtk_widget_class_bind_template_child (wclass, MctCarousel, revealer);
|
||||||
|
|
||||||
gtk_widget_class_bind_template_callback (wclass, mct_carousel_goto_previous_page);
|
gtk_widget_class_bind_template_callback (wclass, mct_carousel_goto_previous_page);
|
||||||
gtk_widget_class_bind_template_callback (wclass, mct_carousel_goto_next_page);
|
gtk_widget_class_bind_template_callback (wclass, mct_carousel_goto_next_page);
|
||||||
|
|
||||||
object_class->dispose = mct_carousel_dispose;
|
gtk_widget_class_set_layout_manager_type (wclass, MCT_TYPE_CAROUSEL_LAYOUT);
|
||||||
|
|
||||||
container_class->add = mct_carousel_add;
|
object_class->dispose = mct_carousel_dispose;
|
||||||
|
|
||||||
signals[ITEM_ACTIVATED] =
|
signals[ITEM_ACTIVATED] =
|
||||||
g_signal_new ("item-activated",
|
g_signal_new ("item-activated",
|
||||||
|
@ -414,19 +384,6 @@ mct_carousel_class_init (MctCarouselClass *klass)
|
||||||
MCT_TYPE_CAROUSEL_ITEM);
|
MCT_TYPE_CAROUSEL_ITEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
on_size_allocate (MctCarousel *self)
|
|
||||||
{
|
|
||||||
if (self->selected_item == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (gtk_stack_get_transition_running (self->stack))
|
|
||||||
return;
|
|
||||||
|
|
||||||
self->arrow_start_x = mct_carousel_item_get_x (self->selected_item, self);
|
|
||||||
mct_carousel_move_arrow (self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_transition_running (MctCarousel *self)
|
on_transition_running (MctCarousel *self)
|
||||||
{
|
{
|
||||||
|
@ -445,13 +402,12 @@ mct_carousel_init (MctCarousel *self)
|
||||||
gtk_css_provider_load_from_resource (GTK_CSS_PROVIDER (provider),
|
gtk_css_provider_load_from_resource (GTK_CSS_PROVIDER (provider),
|
||||||
"/org/freedesktop/MalcontentControl/ui/carousel.css");
|
"/org/freedesktop/MalcontentControl/ui/carousel.css");
|
||||||
|
|
||||||
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
|
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
|
||||||
provider,
|
provider,
|
||||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
|
||||||
|
|
||||||
g_object_unref (provider);
|
g_object_unref (provider);
|
||||||
|
|
||||||
g_signal_connect_swapped (self->stack, "size-allocate", G_CALLBACK (on_size_allocate), self);
|
|
||||||
g_signal_connect_swapped (self->stack, "notify::transition-running", G_CALLBACK (on_transition_running), self);
|
g_signal_connect_swapped (self->stack, "notify::transition-running", G_CALLBACK (on_transition_running), self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,3 +416,77 @@ mct_carousel_get_item_count (MctCarousel *self)
|
||||||
{
|
{
|
||||||
return g_list_length (self->children);
|
return g_list_length (self->children);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mct_carousel_set_revealed (MctCarousel *self,
|
||||||
|
gboolean revealed)
|
||||||
|
{
|
||||||
|
g_return_if_fail (MCT_IS_CAROUSEL (self));
|
||||||
|
|
||||||
|
gtk_revealer_set_reveal_child (self->revealer, revealed);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _MctCarouselLayout {
|
||||||
|
GtkLayoutManager parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_FINAL_TYPE (MctCarouselLayout, mct_carousel_layout, GTK_TYPE_LAYOUT_MANAGER)
|
||||||
|
|
||||||
|
static void
|
||||||
|
mct_carousel_layout_measure (GtkLayoutManager *layout_manager,
|
||||||
|
GtkWidget *widget,
|
||||||
|
GtkOrientation orientation,
|
||||||
|
int for_size,
|
||||||
|
int *minimum,
|
||||||
|
int *natural,
|
||||||
|
int *minimum_baseline,
|
||||||
|
int *natural_baseline)
|
||||||
|
{
|
||||||
|
MctCarousel *carousel;
|
||||||
|
|
||||||
|
g_assert (MCT_IS_CAROUSEL (widget));
|
||||||
|
|
||||||
|
carousel = MCT_CAROUSEL (widget);
|
||||||
|
|
||||||
|
gtk_widget_measure (GTK_WIDGET (carousel->revealer),
|
||||||
|
orientation, for_size,
|
||||||
|
minimum, natural,
|
||||||
|
minimum_baseline, natural_baseline);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mct_carousel_layout_allocate (GtkLayoutManager *layout_manager,
|
||||||
|
GtkWidget *widget,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int baseline)
|
||||||
|
{
|
||||||
|
MctCarousel *carousel;
|
||||||
|
|
||||||
|
g_assert (MCT_IS_CAROUSEL (widget));
|
||||||
|
|
||||||
|
carousel = MCT_CAROUSEL (widget);
|
||||||
|
gtk_widget_allocate (GTK_WIDGET (carousel->revealer), width, height, baseline, NULL);
|
||||||
|
|
||||||
|
if (carousel->selected_item == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (gtk_stack_get_transition_running (carousel->stack))
|
||||||
|
return;
|
||||||
|
|
||||||
|
mct_carousel_move_arrow (carousel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mct_carousel_layout_class_init (MctCarouselLayoutClass *klass)
|
||||||
|
{
|
||||||
|
GtkLayoutManagerClass *layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (klass);
|
||||||
|
|
||||||
|
layout_manager_class->measure = mct_carousel_layout_measure;
|
||||||
|
layout_manager_class->allocate = mct_carousel_layout_allocate;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mct_carousel_layout_init (MctCarouselLayout *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
margin-bottom: -2px;
|
margin-bottom: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.carousel-item {
|
carousel-item {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
|
@ -18,24 +18,28 @@
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
* - Felipe Borges <felipeborges@gnome.org>
|
* - Felipe Borges <felipeborges@gnome.org>
|
||||||
|
* - Georges Basile Stavracas Neto <georges@endlessos.org>
|
||||||
* - Philip Withnall <withnall@endlessm.com>
|
* - Philip Withnall <withnall@endlessm.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <adwaita.h>
|
||||||
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define MCT_TYPE_CAROUSEL_ITEM (mct_carousel_item_get_type ())
|
#define MCT_TYPE_CAROUSEL_ITEM (mct_carousel_item_get_type ())
|
||||||
G_DECLARE_FINAL_TYPE (MctCarouselItem, mct_carousel_item, MCT, CAROUSEL_ITEM, GtkRadioButton)
|
G_DECLARE_FINAL_TYPE (MctCarouselItem, mct_carousel_item, MCT, CAROUSEL_ITEM, GtkButton)
|
||||||
|
|
||||||
#define MCT_TYPE_CAROUSEL (mct_carousel_get_type ())
|
#define MCT_TYPE_CAROUSEL (mct_carousel_get_type ())
|
||||||
G_DECLARE_FINAL_TYPE (MctCarousel, mct_carousel, MCT, CAROUSEL, GtkRevealer)
|
G_DECLARE_FINAL_TYPE (MctCarousel, mct_carousel, MCT, CAROUSEL, AdwBin)
|
||||||
|
|
||||||
GtkWidget *mct_carousel_item_new (void);
|
GtkWidget *mct_carousel_item_new (void);
|
||||||
|
|
||||||
|
void mct_carousel_item_set_child (MctCarouselItem *self,
|
||||||
|
GtkWidget *child);
|
||||||
|
|
||||||
MctCarousel *mct_carousel_new (void);
|
MctCarousel *mct_carousel_new (void);
|
||||||
|
|
||||||
void mct_carousel_purge_items (MctCarousel *self);
|
void mct_carousel_purge_items (MctCarousel *self);
|
||||||
|
@ -49,4 +53,10 @@ void mct_carousel_select_item (MctCarousel *self,
|
||||||
|
|
||||||
guint mct_carousel_get_item_count (MctCarousel *self);
|
guint mct_carousel_get_item_count (MctCarousel *self);
|
||||||
|
|
||||||
|
void mct_carousel_add (MctCarousel *self,
|
||||||
|
MctCarouselItem *item);
|
||||||
|
|
||||||
|
void mct_carousel_set_revealed (MctCarousel *self,
|
||||||
|
gboolean revealed);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
|
@ -3,100 +3,76 @@
|
||||||
<!-- Copyright © 2020 Endless, Inc. -->
|
<!-- Copyright © 2020 Endless, Inc. -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.12"/>
|
<requires lib="gtk+" version="3.12"/>
|
||||||
<template class="MctCarousel" parent="GtkRevealer">
|
<template class="MctCarousel" parent="AdwBin">
|
||||||
<property name="transition_duration">400</property>
|
|
||||||
<property name="reveal-child">True</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkOverlay">
|
<object class="GtkRevealer" id="revealer">
|
||||||
<property name="visible">True</property>
|
<property name="transition_duration">400</property>
|
||||||
<property name="hexpand">True</property>
|
<property name="reveal-child">True</property>
|
||||||
<property name="border_width">16</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkStack" id="stack">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="transition_duration">400</property>
|
|
||||||
<property name="transition_type">GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT</property>
|
|
||||||
<style>
|
|
||||||
<class name="location-bar"/>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child type="overlay">
|
|
||||||
<object class="GtkOverlay">
|
<object class="GtkOverlay">
|
||||||
<property name="visible">True</property>
|
<property name="hexpand">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkStack" id="stack">
|
||||||
<property name="visible">True</property>
|
<property name="margin-top">16</property>
|
||||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
<property name="margin-bottom">16</property>
|
||||||
<property name="border_width">12</property>
|
<property name="margin-start">16</property>
|
||||||
<child>
|
<property name="margin-end">16</property>
|
||||||
<object class="GtkButton" id="go_back_button">
|
<property name="transition_duration">400</property>
|
||||||
<property name="visible">False</property>
|
<property name="transition_type">GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT</property>
|
||||||
<property name="can_focus">True</property>
|
<style>
|
||||||
<property name="valign">center</property>
|
<class name="location-bar"/>
|
||||||
<style>
|
</style>
|
||||||
<class name="circular"/>
|
</object>
|
||||||
</style>
|
</child>
|
||||||
<child>
|
<child type="overlay">
|
||||||
<object class="GtkImage">
|
<object class="GtkButton" id="go_back_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">False</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="halign">start</property>
|
||||||
<property name="icon-size">4</property>
|
<property name="valign">center</property>
|
||||||
<property name="icon_name">go-previous-symbolic</property>
|
<property name="margin-top">12</property>
|
||||||
<child internal-child="accessible">
|
<property name="margin-bottom">12</property>
|
||||||
<object class="AtkObject">
|
<property name="margin-start">12</property>
|
||||||
<property name="accessible-name" translatable="yes">Previous Page</property>
|
<property name="margin-end">12</property>
|
||||||
</object>
|
<property name="icon_name">go-previous-symbolic</property>
|
||||||
</child>
|
<accessibility>
|
||||||
</object>
|
<property name="label" translatable="yes">Previous Page</property>
|
||||||
</child>
|
</accessibility>
|
||||||
<signal name="clicked" handler="mct_carousel_goto_previous_page" object="MctCarousel" swapped="no"/>
|
<style>
|
||||||
</object>
|
<class name="circular"/>
|
||||||
<packing>
|
</style>
|
||||||
<property name="pack_type">GTK_PACK_START</property>
|
<signal name="clicked" handler="mct_carousel_goto_previous_page" object="MctCarousel" swapped="no"/>
|
||||||
</packing>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child type="overlay">
|
||||||
<object class="GtkButton" id="go_next_button">
|
<object class="GtkButton" id="go_next_button">
|
||||||
<property name="can_focus">True</property>
|
<property name="valign">center</property>
|
||||||
<property name="valign">center</property>
|
<property name="halign">end</property>
|
||||||
<style>
|
<property name="margin-top">12</property>
|
||||||
<class name="circular"/>
|
<property name="margin-bottom">12</property>
|
||||||
</style>
|
<property name="margin-start">12</property>
|
||||||
<child>
|
<property name="margin-end">12</property>
|
||||||
<object class="GtkImage">
|
<property name="hexpand">True</property>
|
||||||
<property name="visible">True</property>
|
<property name="icon_name">go-next-symbolic</property>
|
||||||
<property name="can_focus">False</property>
|
<accessibility>
|
||||||
<property name="icon-size">4</property>
|
<property name="label" translatable="yes">Next Page</property>
|
||||||
<property name="icon_name">go-next-symbolic</property>
|
</accessibility>
|
||||||
<child internal-child="accessible">
|
<style>
|
||||||
<object class="AtkObject">
|
<class name="circular"/>
|
||||||
<property name="accessible-name" translatable="yes">Next Page</property>
|
</style>
|
||||||
</object>
|
<signal name="clicked" handler="mct_carousel_goto_next_page" object="MctCarousel" swapped="no"/>
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<signal name="clicked" handler="mct_carousel_goto_next_page" object="MctCarousel" swapped="no"/>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="pack_type">GTK_PACK_END</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child type="overlay">
|
<child type="overlay">
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="valign">GTK_ALIGN_END</property>
|
<property name="valign">GTK_ALIGN_END</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="carousel-arrow-container"/>
|
<class name="carousel-arrow-container"/>
|
||||||
</style>
|
</style>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkOverlay">
|
<object class="GtkOverlay">
|
||||||
<property name="visible">True</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox" id="arrow">
|
<object class="GtkBox" id="arrow">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="halign">GTK_ALIGN_END</property>
|
<property name="halign">GTK_ALIGN_END</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="carousel-arrow"/>
|
<class name="carousel-arrow"/>
|
||||||
|
@ -105,7 +81,6 @@
|
||||||
</child>
|
</child>
|
||||||
<child type="overlay">
|
<child type="overlay">
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="halign">GTK_ALIGN_END</property>
|
<property name="halign">GTK_ALIGN_END</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="carousel-inner-arrow"/>
|
<class name="carousel-inner-arrow"/>
|
||||||
|
@ -115,14 +90,8 @@
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="pass-through">True</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="pass-through">True</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
|
@ -2,297 +2,163 @@
|
||||||
<!-- Copyright © 2019, 2020 Endless Mobile, Inc. -->
|
<!-- Copyright © 2019, 2020 Endless Mobile, Inc. -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.12"/>
|
<requires lib="gtk+" version="3.12"/>
|
||||||
<object class="GtkApplicationWindow" id="main_window">
|
<object class="AdwApplicationWindow" id="main_window">
|
||||||
<property name="default-width">540</property>
|
<property name="default-width">540</property>
|
||||||
<property name="default-height">580</property>
|
<property name="default-height">580</property>
|
||||||
<child type="titlebar">
|
|
||||||
<object class="GtkHeaderBar" id="header">
|
|
||||||
<property name="show-close-button">True</property>
|
|
||||||
<!-- Translators: This is the title of the main window -->
|
|
||||||
<property name="title" translatable="yes">Parental Controls</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkMenuButton" id="primary_menu">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="direction">none</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
<property name="use-popover">True</property>
|
|
||||||
<property name="menu-model">primary-menu</property>
|
|
||||||
<accelerator key="F10" signal="activate"/>
|
|
||||||
<style>
|
|
||||||
<class name="image-button"/>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="pack-type">end</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkStack" id="main_stack">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkHeaderBar" id="header">
|
||||||
<property name="visible">True</property>
|
<property name="show-title-buttons">True</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="title-widget">
|
||||||
<property name="border_width">0</property>
|
<object class="AdwWindowTitle">
|
||||||
<child>
|
<!-- Translators: This is the title of the main window -->
|
||||||
<object class="MctUserSelector" id="user_selector">
|
<property name="title" translatable="yes">Parental Controls</property>
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="user-manager">user_manager</property>
|
|
||||||
<property name="show-administrators">False</property>
|
|
||||||
<accessibility>
|
|
||||||
<relation target="user_controls" type="controller-for"/>
|
|
||||||
</accessibility>
|
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
</property>
|
||||||
<property name="fill">False</property>
|
<child type="end">
|
||||||
<property name="expand">False</property>
|
<object class="GtkMenuButton" id="primary_menu">
|
||||||
</packing>
|
<property name="direction">none</property>
|
||||||
</child>
|
<property name="menu-model">primary-menu</property>
|
||||||
<child>
|
<style>
|
||||||
<object class="GtkScrolledWindow">
|
<class name="image-button"/>
|
||||||
<property name="visible">True</property>
|
</style>
|
||||||
<property name="hscrollbar-policy">never</property>
|
|
||||||
<property name="min-content-height">370</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkShortcutController">
|
||||||
<property name="margin">18</property>
|
<property name="scope">global</property>
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">18</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="help_label">
|
<object class="GtkShortcut">
|
||||||
<!-- Content is set in code; this string is just a placeholder -->
|
<property name="trigger">F10</property>
|
||||||
<property name="label">It’s recommended that restrictions are set as part of an ongoing conversation with $name. Read guidance on what to consider.</property>
|
<property name="action">activate</property>
|
||||||
<property name="visible">True</property>
|
</object>
|
||||||
<property name="wrap">True</property>
|
</child>
|
||||||
<property name="xalign">0.0</property>
|
</object>
|
||||||
<property name="yalign">0.0</property>
|
</child>
|
||||||
<child internal-child="accessible">
|
</object>
|
||||||
<object class="AtkObject">
|
</child>
|
||||||
<property name="AtkObject::accessible-role">static</property>
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkStack" id="main_stack">
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<object class="GtkStackPage">
|
||||||
|
<property name="name">controls</property>
|
||||||
|
<property name="child">
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="MctUserSelector" id="user_selector">
|
||||||
|
<property name="user-manager">user_manager</property>
|
||||||
|
<property name="show-administrators">False</property>
|
||||||
|
<accessibility>
|
||||||
|
<relation name="controls">user_controls</relation>
|
||||||
|
</accessibility>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="AdwPreferencesPage">
|
||||||
|
<child>
|
||||||
|
<object class="AdwPreferencesGroup">
|
||||||
|
<child>
|
||||||
|
<object class="MctUserControls" id="user_controls">
|
||||||
|
<!-- Content is set in code; this string is just a placeholder -->
|
||||||
|
<property name="description">It’s recommended that restrictions are set as part of an ongoing conversation with $name. Read guidance on what to consider.</property>
|
||||||
|
<property name="height-request">270</property>
|
||||||
|
<property name="dbus-connection">dbus_connection</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<object class="GtkStackPage">
|
||||||
|
<property name="name">unlock</property>
|
||||||
|
<property name="child">
|
||||||
|
<object class="AdwStatusPage">
|
||||||
|
<property name="title" translatable="yes">Permission Required</property>
|
||||||
|
<property name="description" translatable="yes">Permission is required to view and change user parental controls settings.</property>
|
||||||
|
<property name="icon-name">org.freedesktop.MalcontentControl</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="MctUserControls" id="user_controls">
|
<object class="GtkLockButton" id="lock_button">
|
||||||
<property name="halign">center</property>
|
<property name="halign">center</property>
|
||||||
<property name="visible">True</property>
|
<style>
|
||||||
<property name="dbus-connection">dbus_connection</property>
|
<class name="suggested-action" />
|
||||||
|
<class name="pill" />
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</property>
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="name">controls</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="vexpand">True</property>
|
|
||||||
<child type="center">
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">12</property>
|
|
||||||
<property name="margin">18</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="label" translatable="yes">Permission Required</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="scale" value="1.4"/>
|
|
||||||
</attributes>
|
|
||||||
<child internal-child="accessible">
|
|
||||||
<object class="AtkObject">
|
|
||||||
<property name="AtkObject::accessible-role">static</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="label" translatable="yes">Permission is required to view and change user parental controls settings.</property>
|
|
||||||
<property name="wrap">True</property>
|
|
||||||
<child internal-child="accessible">
|
|
||||||
<object class="AtkObject">
|
|
||||||
<property name="AtkObject::accessible-role">static</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLockButton" id="lock_button">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="halign">center</property>
|
|
||||||
<property name="can-default">True</property>
|
|
||||||
<property name="has-default">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
<!-- Give 18px spacing between the label and the button -->
|
|
||||||
<property name="margin-top">6</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="name">unlock</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkStackPage">
|
||||||
<property name="visible">True</property>
|
<property name="name">no-other-users</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="child">
|
||||||
<property name="hexpand">True</property>
|
<object class="AdwStatusPage">
|
||||||
<property name="vexpand">True</property>
|
|
||||||
<child type="center">
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">12</property>
|
|
||||||
<property name="margin">18</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage">
|
|
||||||
<property name="icon-name">system-users-symbolic</property>
|
<property name="icon-name">system-users-symbolic</property>
|
||||||
<property name="pixel-size">96</property>
|
<property name="title" translatable="yes">No Standard User Accounts</property>
|
||||||
<property name="visible">True</property>
|
<property name="description" translatable="yes">Parental controls can only be applied to standard user
|
||||||
<child internal-child="accessible">
|
accounts. These can be created in the user settings.</property>
|
||||||
<object class="AtkObject">
|
<child>
|
||||||
<property name="AtkObject::accessible-role">static</property>
|
<object class="GtkButton" id="user_accounts_panel_button">
|
||||||
|
<property name="label" translatable="yes">_User Settings</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
<style>
|
||||||
|
<class name="suggested-action"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</property>
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="label" translatable="yes">No Standard User Accounts</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="scale" value="1.4"/>
|
|
||||||
</attributes>
|
|
||||||
<child internal-child="accessible">
|
|
||||||
<object class="AtkObject">
|
|
||||||
<property name="AtkObject::accessible-role">static</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="justify">center</property>
|
|
||||||
<property name="label" translatable="yes">Parental controls can only be applied to standard user
|
|
||||||
accounts. These can be created in the user settings.</property>
|
|
||||||
<property name="wrap">True</property>
|
|
||||||
<child internal-child="accessible">
|
|
||||||
<object class="AtkObject">
|
|
||||||
<property name="AtkObject::accessible-role">static</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="user_accounts_panel_button">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="label" translatable="yes">_User Settings</property>
|
|
||||||
<property name="halign">center</property>
|
|
||||||
<property name="can-default">True</property>
|
|
||||||
<property name="has-default">True</property>
|
|
||||||
<property name="use-underline">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
<!-- Give 18px spacing between the label and the button -->
|
|
||||||
<property name="margin-top">6</property>
|
|
||||||
<style>
|
|
||||||
<class name="suggested-action"/>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="name">no-other-users</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkStackPage">
|
||||||
<property name="visible">True</property>
|
<property name="name">loading</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="child">
|
||||||
<property name="hexpand">True</property>
|
<object class="GtkBox">
|
||||||
<property name="vexpand">True</property>
|
<property name="orientation">vertical</property>
|
||||||
<child type="center">
|
<property name="hexpand">True</property>
|
||||||
<object class="GtkLabel">
|
<property name="vexpand">True</property>
|
||||||
<property name="visible">True</property>
|
<child type="center">
|
||||||
<property name="label" translatable="yes">Loading…</property>
|
<object class="GtkLabel">
|
||||||
<attributes>
|
<property name="label" translatable="yes">Loading…</property>
|
||||||
<attribute name="scale" value="1.4"/>
|
<property name="vexpand">True</property>
|
||||||
</attributes>
|
<style>
|
||||||
<child internal-child="accessible">
|
<class name="title-1" />
|
||||||
<object class="AtkObject">
|
</style>
|
||||||
<property name="AtkObject::accessible-role">static</property>
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="name">loading</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkStackPage">
|
||||||
<property name="visible">True</property>
|
<property name="name">error</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="child">
|
||||||
<property name="hexpand">True</property>
|
<object class="AdwStatusPage" id="error_page">
|
||||||
<property name="vexpand">True</property>
|
<property name="icon-name">dialog-error-symbolic</property>
|
||||||
<child type="center">
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">12</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="error_title">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="label"></property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="scale" value="1.4"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</property>
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="error_message">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="label"></property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child internal-child="accessible">
|
|
||||||
<object class="AtkObject">
|
|
||||||
<property name="AtkObject::accessible-role">alert</property>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
|
||||||
<property name="name">error</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
|
@ -27,8 +27,9 @@ malcontent_control = executable('malcontent-control',
|
||||||
dependency('gio-2.0', version: '>= 2.44'),
|
dependency('gio-2.0', version: '>= 2.44'),
|
||||||
dependency('glib-2.0', version: '>= 2.54.2'),
|
dependency('glib-2.0', version: '>= 2.54.2'),
|
||||||
dependency('gobject-2.0', version: '>= 2.54'),
|
dependency('gobject-2.0', version: '>= 2.54'),
|
||||||
dependency('gtk+-3.0'),
|
dependency('gtk4', version: '>= 4.6'),
|
||||||
dependency('polkit-gobject-1'),
|
dependency('polkit-gobject-1'),
|
||||||
|
libadwaita_dep,
|
||||||
libmalcontent_dep,
|
libmalcontent_dep,
|
||||||
libmalcontent_ui_dep,
|
libmalcontent_ui_dep,
|
||||||
],
|
],
|
||||||
|
@ -83,7 +84,7 @@ if xmllint.found()
|
||||||
'validate-ui', xmllint,
|
'validate-ui', xmllint,
|
||||||
args: [
|
args: [
|
||||||
'--nonet', '--noblanks', '--noout',
|
'--nonet', '--noblanks', '--noout',
|
||||||
'--relaxng', join_paths(gtk_prefix, 'share', 'gtk-3.0', 'gtkbuilder.rng'),
|
'--relaxng', join_paths(gtk_prefix, 'share', 'gtk-4.0', 'gtk4builder.rng'),
|
||||||
files(
|
files(
|
||||||
'carousel.ui',
|
'carousel.ui',
|
||||||
'main.ui',
|
'main.ui',
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
*
|
*
|
||||||
|
* Copyright © 2022 Endless Mobile, Inc.
|
||||||
* Copyright © 2015 Red Hat, Inc.
|
* Copyright © 2015 Red Hat, Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -16,10 +17,11 @@
|
||||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
|
* - Georges Basile Stavracas Neto <georges@endlessos.org>
|
||||||
* - Ondrej Holy <oholy@redhat.com>
|
* - Ondrej Holy <oholy@redhat.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <adwaita.h>
|
||||||
#include <act/act.h>
|
#include <act/act.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
@ -28,108 +30,49 @@
|
||||||
|
|
||||||
struct _MctUserImage
|
struct _MctUserImage
|
||||||
{
|
{
|
||||||
GtkImage parent_instance;
|
AdwBin parent_instance;
|
||||||
|
|
||||||
|
AdwAvatar *avatar;
|
||||||
|
|
||||||
ActUser *user;
|
ActUser *user;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE (MctUserImage, mct_user_image, GTK_TYPE_IMAGE)
|
G_DEFINE_TYPE (MctUserImage, mct_user_image, ADW_TYPE_BIN)
|
||||||
|
|
||||||
static GdkPixbuf *
|
static GdkTexture *
|
||||||
round_image (GdkPixbuf *pixbuf)
|
render_user_icon_texture (ActUser *user)
|
||||||
{
|
{
|
||||||
GdkPixbuf *dest = NULL;
|
g_autoptr(GdkTexture) texture = NULL;
|
||||||
cairo_surface_t *surface;
|
g_autoptr(GError) error = NULL;
|
||||||
cairo_t *cr;
|
|
||||||
gint size;
|
|
||||||
|
|
||||||
size = gdk_pixbuf_get_width (pixbuf);
|
|
||||||
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size, size);
|
|
||||||
cr = cairo_create (surface);
|
|
||||||
|
|
||||||
/* Clip a circle */
|
|
||||||
cairo_arc (cr, size / 2, size / 2, size / 2, 0, 2 * G_PI);
|
|
||||||
cairo_clip (cr);
|
|
||||||
cairo_new_path (cr);
|
|
||||||
|
|
||||||
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
|
|
||||||
cairo_paint (cr);
|
|
||||||
|
|
||||||
dest = gdk_pixbuf_get_from_surface (surface, 0, 0, size, size);
|
|
||||||
cairo_surface_destroy (surface);
|
|
||||||
cairo_destroy (cr);
|
|
||||||
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
static cairo_surface_t *
|
|
||||||
render_user_icon (ActUser *user,
|
|
||||||
gint icon_size,
|
|
||||||
gint scale)
|
|
||||||
{
|
|
||||||
g_autoptr(GdkPixbuf) source_pixbuf = NULL;
|
|
||||||
GdkPixbuf *pixbuf = NULL;
|
|
||||||
GError *error;
|
|
||||||
const gchar *icon_file;
|
const gchar *icon_file;
|
||||||
cairo_surface_t *surface = NULL;
|
|
||||||
|
|
||||||
g_return_val_if_fail (ACT_IS_USER (user), NULL);
|
g_return_val_if_fail (ACT_IS_USER (user), NULL);
|
||||||
g_return_val_if_fail (icon_size > 12, NULL);
|
|
||||||
|
|
||||||
icon_file = act_user_get_icon_file (user);
|
icon_file = act_user_get_icon_file (user);
|
||||||
pixbuf = NULL;
|
if (icon_file == NULL)
|
||||||
if (icon_file)
|
return NULL;
|
||||||
|
|
||||||
|
texture = gdk_texture_new_from_filename (icon_file, &error);
|
||||||
|
if (error != NULL)
|
||||||
{
|
{
|
||||||
source_pixbuf = gdk_pixbuf_new_from_file_at_size (icon_file,
|
g_warning ("Error loading user icon: %s", error->message);
|
||||||
icon_size * scale,
|
return NULL;
|
||||||
icon_size * scale,
|
|
||||||
NULL);
|
|
||||||
if (source_pixbuf)
|
|
||||||
pixbuf = round_image (source_pixbuf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pixbuf != NULL)
|
return g_steal_pointer (&texture);
|
||||||
goto out;
|
|
||||||
|
|
||||||
error = NULL;
|
|
||||||
pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
|
|
||||||
"avatar-default",
|
|
||||||
icon_size * scale,
|
|
||||||
GTK_ICON_LOOKUP_FORCE_SIZE,
|
|
||||||
&error);
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
g_warning ("%s", error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
|
|
||||||
if (pixbuf != NULL)
|
|
||||||
{
|
|
||||||
surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
|
|
||||||
g_object_unref (pixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return surface;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
render_image (MctUserImage *image)
|
render_image (MctUserImage *image)
|
||||||
{
|
{
|
||||||
cairo_surface_t *surface;
|
g_autoptr(GdkTexture) texture = NULL;
|
||||||
gint scale, pixel_size;
|
|
||||||
|
|
||||||
if (image->user == NULL)
|
if (image->user == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pixel_size = gtk_image_get_pixel_size (GTK_IMAGE (image));
|
texture = render_user_icon_texture (image->user);
|
||||||
scale = gtk_widget_get_scale_factor (GTK_WIDGET (image));
|
adw_avatar_set_custom_image (image->avatar, GDK_PAINTABLE (texture));
|
||||||
surface = render_user_icon (image->user,
|
adw_avatar_set_text (image->avatar, act_user_get_real_name (image->user));
|
||||||
pixel_size > 0 ? pixel_size : 48,
|
|
||||||
scale);
|
|
||||||
gtk_image_set_from_surface (GTK_IMAGE (image), surface);
|
|
||||||
cairo_surface_destroy (surface);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -163,8 +106,8 @@ mct_user_image_class_init (MctUserImageClass *class)
|
||||||
static void
|
static void
|
||||||
mct_user_image_init (MctUserImage *image)
|
mct_user_image_init (MctUserImage *image)
|
||||||
{
|
{
|
||||||
g_signal_connect_swapped (image, "notify::scale-factor", G_CALLBACK (render_image), image);
|
image->avatar = ADW_AVATAR (adw_avatar_new (48, NULL, TRUE));
|
||||||
g_signal_connect_swapped (image, "notify::pixel-size", G_CALLBACK (render_image), image);
|
adw_bin_set_child (ADW_BIN (image), GTK_WIDGET (image->avatar));
|
||||||
}
|
}
|
||||||
|
|
||||||
GtkWidget *
|
GtkWidget *
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
*
|
*
|
||||||
|
* Copyright © 2022 Endless Mobile, Inc.
|
||||||
* Copyright © 2015 Red Hat, Inc.
|
* Copyright © 2015 Red Hat, Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -16,19 +17,20 @@
|
||||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
|
* - Georges Basile Stavracas Neto <georges@endlessos.org>
|
||||||
* - Ondrej Holy <oholy@redhat.com>
|
* - Ondrej Holy <oholy@redhat.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <adwaita.h>
|
||||||
#include <act/act.h>
|
#include <act/act.h>
|
||||||
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define MCT_TYPE_USER_IMAGE (mct_user_image_get_type ())
|
#define MCT_TYPE_USER_IMAGE (mct_user_image_get_type ())
|
||||||
G_DECLARE_FINAL_TYPE (MctUserImage, mct_user_image, MCT, USER_IMAGE, GtkImage)
|
G_DECLARE_FINAL_TYPE (MctUserImage, mct_user_image, MCT, USER_IMAGE, AdwBin)
|
||||||
|
|
||||||
GtkWidget *mct_user_image_new (void);
|
GtkWidget *mct_user_image_new (void);
|
||||||
void mct_user_image_set_user (MctUserImage *image,
|
void mct_user_image_set_user (MctUserImage *image,
|
||||||
|
|
|
@ -370,11 +370,11 @@ reload_users (MctUserSelector *self,
|
||||||
g_object_get (settings, "gtk-enable-animations", &animations, NULL);
|
g_object_get (settings, "gtk-enable-animations", &animations, NULL);
|
||||||
g_object_set (settings, "gtk-enable-animations", FALSE, NULL);
|
g_object_set (settings, "gtk-enable-animations", FALSE, NULL);
|
||||||
|
|
||||||
mct_carousel_purge_items (self->carousel);
|
|
||||||
|
|
||||||
list = act_user_manager_list_users (self->user_manager);
|
list = act_user_manager_list_users (self->user_manager);
|
||||||
g_debug ("Got %u users", g_slist_length (list));
|
g_debug ("Got %u users", g_slist_length (list));
|
||||||
|
|
||||||
|
mct_carousel_purge_items (self->carousel);
|
||||||
|
|
||||||
list = g_slist_sort (list, (GCompareFunc) sort_users);
|
list = g_slist_sort (list, (GCompareFunc) sort_users);
|
||||||
for (l = list; l; l = l->next)
|
for (l = list; l; l = l->next)
|
||||||
{
|
{
|
||||||
|
@ -397,7 +397,7 @@ reload_users (MctUserSelector *self,
|
||||||
|
|
||||||
g_object_set (settings, "gtk-enable-animations", animations, NULL);
|
g_object_set (settings, "gtk-enable-animations", animations, NULL);
|
||||||
|
|
||||||
gtk_revealer_set_reveal_child (GTK_REVEALER (self->carousel), TRUE);
|
mct_carousel_set_revealed (self->carousel, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GtkWidget *
|
static GtkWidget *
|
||||||
|
@ -411,7 +411,7 @@ create_carousel_entry (MctUserSelector *self,
|
||||||
|
|
||||||
widget = mct_user_image_new ();
|
widget = mct_user_image_new ();
|
||||||
mct_user_image_set_user (MCT_USER_IMAGE (widget), user);
|
mct_user_image_set_user (MCT_USER_IMAGE (widget), user);
|
||||||
gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
|
gtk_box_append (GTK_BOX (box), widget);
|
||||||
|
|
||||||
label = g_strdup_printf ("<b>%s</b>",
|
label = g_strdup_printf ("<b>%s</b>",
|
||||||
get_real_or_user_name (user));
|
get_real_or_user_name (user));
|
||||||
|
@ -419,7 +419,7 @@ create_carousel_entry (MctUserSelector *self,
|
||||||
gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
|
gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
|
||||||
gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
|
gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
|
||||||
gtk_widget_set_margin_top (widget, 5);
|
gtk_widget_set_margin_top (widget, 5);
|
||||||
gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
|
gtk_box_append (GTK_BOX (box), widget);
|
||||||
g_free (label);
|
g_free (label);
|
||||||
|
|
||||||
if (act_user_get_uid (user) == getuid ())
|
if (act_user_get_uid (user) == getuid ())
|
||||||
|
@ -431,7 +431,7 @@ create_carousel_entry (MctUserSelector *self,
|
||||||
gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
|
gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
|
||||||
g_free (label);
|
g_free (label);
|
||||||
|
|
||||||
gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
|
gtk_box_append (GTK_BOX (box), widget);
|
||||||
gtk_style_context_add_class (gtk_widget_get_style_context (widget),
|
gtk_style_context_add_class (gtk_widget_get_style_context (widget),
|
||||||
"dim-label");
|
"dim-label");
|
||||||
|
|
||||||
|
@ -455,10 +455,10 @@ user_added_cb (ActUserManager *user_manager,
|
||||||
|
|
||||||
widget = create_carousel_entry (self, user);
|
widget = create_carousel_entry (self, user);
|
||||||
item = mct_carousel_item_new ();
|
item = mct_carousel_item_new ();
|
||||||
gtk_container_add (GTK_CONTAINER (item), widget);
|
mct_carousel_item_set_child (MCT_CAROUSEL_ITEM (item), widget);
|
||||||
|
|
||||||
g_object_set_data (G_OBJECT (item), "uid", GINT_TO_POINTER (act_user_get_uid (user)));
|
g_object_set_data (G_OBJECT (item), "uid", GINT_TO_POINTER (act_user_get_uid (user)));
|
||||||
gtk_container_add (GTK_CONTAINER (self->carousel), item);
|
mct_carousel_add (self->carousel, MCT_CAROUSEL_ITEM (item));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
<template class="MctUserSelector" parent="GtkBox">
|
<template class="MctUserSelector" parent="GtkBox">
|
||||||
<child>
|
<child>
|
||||||
<object class="MctCarousel" id="carousel">
|
<object class="MctCarousel" id="carousel">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="hexpand">True</property>
|
<property name="hexpand">True</property>
|
||||||
<signal name="item-activated" handler="carousel_item_activated"/>
|
<signal name="item-activated" handler="carousel_item_activated"/>
|
||||||
</object>
|
</object>
|
||||||
|
|
|
@ -134,6 +134,12 @@ else
|
||||||
'Malcontent-' + libmalcontent_api_version + '.typelib']
|
'Malcontent-' + libmalcontent_api_version + '.typelib']
|
||||||
endif
|
endif
|
||||||
if get_option('ui').enabled()
|
if get_option('ui').enabled()
|
||||||
|
libadwaita_dep = dependency(
|
||||||
|
'libadwaita-1',
|
||||||
|
version: '>= 1.1',
|
||||||
|
fallback: ['libadwaita', 'libadwaita_dep'],
|
||||||
|
default_options: ['examples=false', 'introspection=disabled', 'tests=false', 'vapi=false'],
|
||||||
|
)
|
||||||
subdir('libmalcontent-ui')
|
subdir('libmalcontent-ui')
|
||||||
endif
|
endif
|
||||||
subdir('malcontent-client')
|
subdir('malcontent-client')
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[wrap-git]
|
||||||
|
url = https://gitlab.gnome.org/GNOME/libadwaita.git
|
||||||
|
revision = 1.1.0
|
||||||
|
depth = 1
|
Loading…
Reference in New Issue