From daac2702d3fc8559fe60a49aaeb872395f1b78d0 Mon Sep 17 00:00:00 2001 From: Felipe Borges Date: Fri, 28 Jan 2022 10:16:30 +0100 Subject: [PATCH] Backport Multitasking panel Multitasking settings are the kind of configurability that our customers tend to appreciate. Signed-off-by: Felipe Borges Resolves: rhbz#2047723 --- backport-multitasking-panel.patch | 3475 +++++++++++++++++++++++++++++ gnome-control-center.spec | 7 +- 2 files changed, 3481 insertions(+), 1 deletion(-) create mode 100644 backport-multitasking-panel.patch diff --git a/backport-multitasking-panel.patch b/backport-multitasking-panel.patch new file mode 100644 index 0000000..0fcbab4 --- /dev/null +++ b/backport-multitasking-panel.patch @@ -0,0 +1,3475 @@ +From 81f55583e06779c2d3c308d826a45b92b7c66638 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Sun, 29 Mar 2020 21:13:35 -0300 +Subject: [PATCH 1/5] Introduce the Multitasking panel + +The Multitasking panel brings some additional settings +from Tweaks that are relevant to the general platform. + +Related: https://gitlab.gnome.org/GNOME/gnome-control-center/issues/558 +--- + gnome-control-center.doap | 2 +- + panels/meson.build | 1 + + .../assets/active-screen-edges.svg | 118 +++ + panels/multitasking/assets/hot-corner.svg | 124 ++++ + .../assets/workspaces-primary-display.svg | 130 ++++ + .../assets/workspaces-span-displays.svg | 178 +++++ + panels/multitasking/cc-multitasking-panel.c | 150 ++++ + panels/multitasking/cc-multitasking-panel.h | 30 + + panels/multitasking/cc-multitasking-panel.ui | 357 +++++++++ + panels/multitasking/cc-multitasking-row.c | 684 ++++++++++++++++++ + panels/multitasking/cc-multitasking-row.h | 63 ++ + panels/multitasking/cc-multitasking-row.ui | 122 ++++ + .../gnome-multitasking-panel.desktop.in.in | 14 + + panels/multitasking/icons/meson.build | 4 + + ...g.gnome.Settings-multitasking-symbolic.svg | 106 +++ + panels/multitasking/meson.build | 49 ++ + .../multitasking/multitasking.gresource.xml | 13 + + shell/cc-panel-list.c | 1 + + shell/cc-panel-loader.c | 2 + + shell/gnome-control-center.gresource.xml | 6 + + shell/icons/multitasking-symbolic.svg | 98 +++ + 21 files changed, 2251 insertions(+), 1 deletion(-) + create mode 100644 panels/multitasking/assets/active-screen-edges.svg + create mode 100644 panels/multitasking/assets/hot-corner.svg + create mode 100644 panels/multitasking/assets/workspaces-primary-display.svg + create mode 100644 panels/multitasking/assets/workspaces-span-displays.svg + create mode 100644 panels/multitasking/cc-multitasking-panel.c + create mode 100644 panels/multitasking/cc-multitasking-panel.h + create mode 100644 panels/multitasking/cc-multitasking-panel.ui + create mode 100644 panels/multitasking/cc-multitasking-row.c + create mode 100644 panels/multitasking/cc-multitasking-row.h + create mode 100644 panels/multitasking/cc-multitasking-row.ui + create mode 100644 panels/multitasking/gnome-multitasking-panel.desktop.in.in + create mode 100644 panels/multitasking/icons/meson.build + create mode 100644 panels/multitasking/icons/scalable/org.gnome.Settings-multitasking-symbolic.svg + create mode 100644 panels/multitasking/meson.build + create mode 100644 panels/multitasking/multitasking.gresource.xml + create mode 100644 shell/icons/multitasking-symbolic.svg + +diff --git a/gnome-control-center.doap b/gnome-control-center.doap +index 600fd66c8..7986d9150 100644 +--- a/gnome-control-center.doap ++++ b/gnome-control-center.doap +@@ -13,7 +13,7 @@ + + C + +- ++ + + + Georges Basile Stavracas Neto +diff --git a/panels/meson.build b/panels/meson.build +index 2f4fdc5e3..1318904ae 100644 +--- a/panels/meson.build ++++ b/panels/meson.build +@@ -15,6 +15,7 @@ panels = [ + 'lock', + 'microphone', + 'mouse', ++ 'multitasking', + 'notifications', + 'online-accounts', + 'power', +diff --git a/panels/multitasking/assets/active-screen-edges.svg b/panels/multitasking/assets/active-screen-edges.svg +new file mode 100644 +index 000000000..34dc67b4e +--- /dev/null ++++ b/panels/multitasking/assets/active-screen-edges.svg +@@ -0,0 +1,118 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/panels/multitasking/assets/hot-corner.svg b/panels/multitasking/assets/hot-corner.svg +new file mode 100644 +index 000000000..0a1732c0e +--- /dev/null ++++ b/panels/multitasking/assets/hot-corner.svg +@@ -0,0 +1,124 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/panels/multitasking/assets/workspaces-primary-display.svg b/panels/multitasking/assets/workspaces-primary-display.svg +new file mode 100644 +index 000000000..56e05c4bb +--- /dev/null ++++ b/panels/multitasking/assets/workspaces-primary-display.svg +@@ -0,0 +1,130 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/panels/multitasking/assets/workspaces-span-displays.svg b/panels/multitasking/assets/workspaces-span-displays.svg +new file mode 100644 +index 000000000..7b3655df9 +--- /dev/null ++++ b/panels/multitasking/assets/workspaces-span-displays.svg +@@ -0,0 +1,178 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/panels/multitasking/cc-multitasking-panel.c b/panels/multitasking/cc-multitasking-panel.c +new file mode 100644 +index 000000000..6a3177fe9 +--- /dev/null ++++ b/panels/multitasking/cc-multitasking-panel.c +@@ -0,0 +1,150 @@ ++/* cc-multitasking-panel.h ++ * ++ * Copyright 2020 Georges Basile Stavracas Neto ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ */ ++ ++ ++#include "cc-multitasking-panel.h" ++ ++#include "cc-multitasking-resources.h" ++#include "cc-multitasking-row.h" ++#include "list-box-helper.h" ++ ++struct _CcMultitaskingPanel ++{ ++ CcPanel parent_instance; ++ ++ GSettings *interface_settings; ++ GSettings *mutter_settings; ++ GSettings *overrides_settings; ++ GSettings *shell_settings; ++ GSettings *wm_settings; ++ ++ GtkSwitch *active_screen_edges_switch; ++ GtkToggleButton *current_workspace_radio; ++ GtkToggleButton *dynamic_workspaces_radio; ++ GtkToggleButton *fixed_workspaces_radio; ++ GtkSwitch *hot_corner_switch; ++ GtkSpinButton *number_of_workspaces_spin; ++ GtkToggleButton *workspaces_primary_display_radio; ++ GtkToggleButton *workspaces_span_displays_radio; ++}; ++ ++CC_PANEL_REGISTER (CcMultitaskingPanel, cc_multitasking_panel) ++ ++/* GObject overrides */ ++ ++static void ++cc_multitasking_panel_finalize (GObject *object) ++{ ++ CcMultitaskingPanel *self = (CcMultitaskingPanel *)object; ++ ++ g_clear_object (&self->interface_settings); ++ g_clear_object (&self->mutter_settings); ++ g_clear_object (&self->overrides_settings); ++ g_clear_object (&self->shell_settings); ++ g_clear_object (&self->wm_settings); ++ ++ G_OBJECT_CLASS (cc_multitasking_panel_parent_class)->finalize (object); ++} ++ ++static void ++cc_multitasking_panel_class_init (CcMultitaskingPanelClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); ++ ++ g_type_ensure (CC_TYPE_MULTITASKING_ROW); ++ ++ object_class->finalize = cc_multitasking_panel_finalize; ++ ++ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/multitasking/cc-multitasking-panel.ui"); ++ ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, active_screen_edges_switch); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, current_workspace_radio); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, dynamic_workspaces_radio); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, fixed_workspaces_radio); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, hot_corner_switch); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, number_of_workspaces_spin); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, workspaces_primary_display_radio); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, workspaces_span_displays_radio); ++} ++ ++static void ++cc_multitasking_panel_init (CcMultitaskingPanel *self) ++{ ++ g_resources_register (cc_multitasking_get_resource ()); ++ ++ gtk_widget_init_template (GTK_WIDGET (self)); ++ ++ self->interface_settings = g_settings_new ("org.gnome.desktop.interface"); ++ g_settings_bind (self->interface_settings, ++ "enable-hot-corners", ++ self->hot_corner_switch, ++ "active", ++ G_SETTINGS_BIND_DEFAULT); ++ ++ self->mutter_settings = g_settings_new ("org.gnome.mutter"); ++ ++ if (g_settings_get_boolean (self->mutter_settings, "workspaces-only-on-primary")) ++ gtk_toggle_button_set_active (self->workspaces_primary_display_radio, TRUE); ++ else ++ gtk_toggle_button_set_active (self->workspaces_span_displays_radio, TRUE); ++ ++ g_settings_bind (self->mutter_settings, ++ "workspaces-only-on-primary", ++ self->workspaces_primary_display_radio, ++ "active", ++ G_SETTINGS_BIND_DEFAULT); ++ g_settings_bind (self->mutter_settings, ++ "edge-tiling", ++ self->active_screen_edges_switch, ++ "active", ++ G_SETTINGS_BIND_DEFAULT); ++ ++ self->overrides_settings = g_settings_new ("org.gnome.shell.overrides"); ++ ++ if (g_settings_get_boolean (self->overrides_settings, "dynamic-workspaces")) ++ gtk_toggle_button_set_active (self->dynamic_workspaces_radio, TRUE); ++ else ++ gtk_toggle_button_set_active (self->fixed_workspaces_radio, TRUE); ++ ++ g_settings_bind (self->overrides_settings, ++ "dynamic-workspaces", ++ self->dynamic_workspaces_radio, ++ "active", ++ G_SETTINGS_BIND_DEFAULT); ++ ++ self->wm_settings = g_settings_new ("org.gnome.desktop.wm.preferences"); ++ g_settings_bind (self->wm_settings, ++ "num-workspaces", ++ self->number_of_workspaces_spin, ++ "value", ++ G_SETTINGS_BIND_DEFAULT); ++ ++ self->shell_settings = g_settings_new ("org.gnome.shell.app-switcher"); ++ ++ if (g_settings_get_boolean (self->shell_settings, "current-workspace-only")) ++ gtk_toggle_button_set_active (self->current_workspace_radio, TRUE); ++ ++ g_settings_bind (self->shell_settings, ++ "current-workspace-only", ++ self->current_workspace_radio, ++ "active", ++ G_SETTINGS_BIND_DEFAULT); ++} +diff --git a/panels/multitasking/cc-multitasking-panel.h b/panels/multitasking/cc-multitasking-panel.h +new file mode 100644 +index 000000000..81e78f071 +--- /dev/null ++++ b/panels/multitasking/cc-multitasking-panel.h +@@ -0,0 +1,30 @@ ++/* cc-multitasking-panel.h ++ * ++ * Copyright 2020 Georges Basile Stavracas Neto ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ */ ++ ++#pragma once ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define CC_TYPE_MULTITASKING_PANEL (cc_multitasking_panel_get_type()) ++G_DECLARE_FINAL_TYPE (CcMultitaskingPanel, cc_multitasking_panel, CC, MULTITASKING_PANEL, CcPanel) ++ ++G_END_DECLS +diff --git a/panels/multitasking/cc-multitasking-panel.ui b/panels/multitasking/cc-multitasking-panel.ui +new file mode 100644 +index 000000000..5b5725038 +--- /dev/null ++++ b/panels/multitasking/cc-multitasking-panel.ui +@@ -0,0 +1,357 @@ ++ ++ ++ ++ ++ ++ 1.0 ++ 1.0 ++ 4.0 ++ ++ 36.0 ++ ++ +diff --git a/panels/multitasking/cc-multitasking-row.c b/panels/multitasking/cc-multitasking-row.c +new file mode 100644 +index 000000000..608e4c48f +--- /dev/null ++++ b/panels/multitasking/cc-multitasking-row.c +@@ -0,0 +1,684 @@ ++/* cc-multitasking-row.c ++ * ++ * Copyright 2018 Purism SPC ++ * 2021 Georges Basile Stavracas Neto ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * SPDX-License-Identifier: GPL-3.0-or-later ++ */ ++ ++#include "cc-multitasking-row.h" ++ ++struct _CcMultitaskingRow ++{ ++ HdyPreferencesRow parent; ++ ++ GtkBox *artwork_box; ++ GtkBox *header; ++ GtkImage *image; ++ GtkBox *prefixes; ++ GtkLabel *subtitle; ++ GtkBox *suffixes; ++ GtkLabel *title; ++ GtkBox *title_box; ++ ++ GtkWidget *previous_parent; ++ ++ gboolean use_underline; ++ gint title_lines; ++ gint subtitle_lines; ++ GtkWidget *activatable_widget; ++}; ++ ++static void cc_multitasking_row_buildable_init (GtkBuildableIface *iface); ++ ++G_DEFINE_TYPE_WITH_CODE (CcMultitaskingRow, cc_multitasking_row, HDY_TYPE_PREFERENCES_ROW, ++ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, cc_multitasking_row_buildable_init)) ++ ++enum ++{ ++ PROP_0, ++ PROP_ICON_NAME, ++ PROP_ACTIVATABLE_WIDGET, ++ PROP_SUBTITLE, ++ PROP_USE_UNDERLINE, ++ PROP_TITLE_LINES, ++ PROP_SUBTITLE_LINES, ++ N_PROPS, ++}; ++ ++enum ++{ ++ SIGNAL_ACTIVATED, ++ SIGNAL_LAST_SIGNAL, ++}; ++ ++static GParamSpec *props[N_PROPS] = { NULL, }; ++static guint signals[SIGNAL_LAST_SIGNAL] = { 0, }; ++ ++static void ++row_activated_cb (CcMultitaskingRow *self, ++ GtkListBoxRow *row) ++{ ++ /* No need to use GTK_LIST_BOX_ROW() for a pointer comparison. */ ++ if ((GtkListBoxRow *) self == row) ++ cc_multitasking_row_activate (self); ++} ++ ++static void ++parent_cb (CcMultitaskingRow *self) ++{ ++ GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (self)); ++ ++ if (self->previous_parent != NULL) ++ { ++ g_signal_handlers_disconnect_by_func (self->previous_parent, ++ G_CALLBACK (row_activated_cb), ++ self); ++ self->previous_parent = NULL; ++ } ++ ++ if (parent == NULL || !GTK_IS_LIST_BOX (parent)) ++ return; ++ ++ self->previous_parent = parent; ++ g_signal_connect_swapped (parent, "row-activated", G_CALLBACK (row_activated_cb), self); ++} ++ ++static void ++update_subtitle_visibility (CcMultitaskingRow *self) ++{ ++ gtk_widget_set_visible (GTK_WIDGET (self->subtitle), ++ gtk_label_get_text (self->subtitle) != NULL && ++ g_strcmp0 (gtk_label_get_text (self->subtitle), "") != 0); ++} ++ ++static void ++cc_multitasking_row_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ CcMultitaskingRow *self = CC_MULTITASKING_ROW (object); ++ ++ switch (prop_id) ++ { ++ case PROP_ICON_NAME: ++ g_value_set_string (value, cc_multitasking_row_get_icon_name (self)); ++ break; ++ case PROP_ACTIVATABLE_WIDGET: ++ g_value_set_object (value, (GObject *) cc_multitasking_row_get_activatable_widget (self)); ++ break; ++ case PROP_SUBTITLE: ++ g_value_set_string (value, cc_multitasking_row_get_subtitle (self)); ++ break; ++ case PROP_SUBTITLE_LINES: ++ g_value_set_int (value, cc_multitasking_row_get_subtitle_lines (self)); ++ break; ++ case PROP_TITLE_LINES: ++ g_value_set_int (value, cc_multitasking_row_get_title_lines (self)); ++ break; ++ case PROP_USE_UNDERLINE: ++ g_value_set_boolean (value, cc_multitasking_row_get_use_underline (self)); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static void ++cc_multitasking_row_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ CcMultitaskingRow *self = CC_MULTITASKING_ROW (object); ++ ++ switch (prop_id) ++ { ++ case PROP_ICON_NAME: ++ cc_multitasking_row_set_icon_name (self, g_value_get_string (value)); ++ break; ++ case PROP_ACTIVATABLE_WIDGET: ++ cc_multitasking_row_set_activatable_widget (self, (GtkWidget*) g_value_get_object (value)); ++ break; ++ case PROP_SUBTITLE: ++ cc_multitasking_row_set_subtitle (self, g_value_get_string (value)); ++ break; ++ case PROP_SUBTITLE_LINES: ++ cc_multitasking_row_set_subtitle_lines (self, g_value_get_int (value)); ++ break; ++ case PROP_TITLE_LINES: ++ cc_multitasking_row_set_title_lines (self, g_value_get_int (value)); ++ break; ++ case PROP_USE_UNDERLINE: ++ cc_multitasking_row_set_use_underline (self, g_value_get_boolean (value)); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static void ++cc_multitasking_row_dispose (GObject *object) ++{ ++ CcMultitaskingRow *self = CC_MULTITASKING_ROW (object); ++ ++ if (self->previous_parent != NULL) { ++ g_signal_handlers_disconnect_by_func (self->previous_parent, G_CALLBACK (row_activated_cb), self); ++ self->previous_parent = NULL; ++ } ++ ++ G_OBJECT_CLASS (cc_multitasking_row_parent_class)->dispose (object); ++} ++ ++static void ++cc_multitasking_row_show_all (GtkWidget *widget) ++{ ++ CcMultitaskingRow *self = CC_MULTITASKING_ROW (widget); ++ ++ g_return_if_fail (CC_IS_MULTITASKING_ROW (self)); ++ ++ gtk_container_foreach (GTK_CONTAINER (self->prefixes), ++ (GtkCallback) gtk_widget_show_all, ++ NULL); ++ ++ gtk_container_foreach (GTK_CONTAINER (self->suffixes), ++ (GtkCallback) gtk_widget_show_all, ++ NULL); ++ ++ GTK_WIDGET_CLASS (cc_multitasking_row_parent_class)->show_all (widget); ++} ++ ++static void ++cc_multitasking_row_destroy (GtkWidget *widget) ++{ ++ CcMultitaskingRow *self = CC_MULTITASKING_ROW (widget); ++ ++ if (self->header) ++ { ++ gtk_widget_destroy (GTK_WIDGET (self->header)); ++ self->header = NULL; ++ } ++ ++ cc_multitasking_row_set_activatable_widget (self, NULL); ++ ++ self->prefixes = NULL; ++ self->suffixes = NULL; ++ ++ GTK_WIDGET_CLASS (cc_multitasking_row_parent_class)->destroy (widget); ++} ++ ++static void ++cc_multitasking_row_add (GtkContainer *container, ++ GtkWidget *child) ++{ ++ CcMultitaskingRow *self = CC_MULTITASKING_ROW (container); ++ ++ /* When constructing the widget, we want the box to be added as the child of ++ * the GtkListBoxRow, as an implementation detail. ++ */ ++ if (!self->header) ++ { ++ GTK_CONTAINER_CLASS (cc_multitasking_row_parent_class)->add (container, child); ++ } ++ else ++ { ++ gtk_container_add (GTK_CONTAINER (self->suffixes), child); ++ gtk_widget_show (GTK_WIDGET (self->suffixes)); ++ } ++} ++ ++static void ++cc_multitasking_row_remove (GtkContainer *container, ++ GtkWidget *child) ++{ ++ CcMultitaskingRow *self = CC_MULTITASKING_ROW (container); ++ ++ if (child == GTK_WIDGET (self->header)) ++ GTK_CONTAINER_CLASS (cc_multitasking_row_parent_class)->remove (container, child); ++ else if (gtk_widget_get_parent (child) == GTK_WIDGET (self->prefixes)) ++ gtk_container_remove (GTK_CONTAINER (self->prefixes), child); ++ else ++ gtk_container_remove (GTK_CONTAINER (self->suffixes), child); ++} ++ ++typedef struct { ++ CcMultitaskingRow *row; ++ GtkCallback callback; ++ gpointer callback_data; ++} ForallData; ++ ++static void ++for_non_internal_child (GtkWidget *widget, ++ gpointer callback_data) ++{ ++ ForallData *data = callback_data; ++ CcMultitaskingRow *self = data->row; ++ ++ if (widget != (GtkWidget *) self->image && ++ widget != (GtkWidget *) self->prefixes && ++ widget != (GtkWidget *) self->suffixes && ++ widget != (GtkWidget *) self->title_box) ++ { ++ data->callback (widget, data->callback_data); ++ } ++} ++ ++static void ++cc_multitasking_row_forall (GtkContainer *container, ++ gboolean include_internals, ++ GtkCallback callback, ++ gpointer callback_data) ++{ ++ CcMultitaskingRow *self = CC_MULTITASKING_ROW (container); ++ ForallData data; ++ ++ if (include_internals) ++ { ++ GTK_CONTAINER_CLASS (cc_multitasking_row_parent_class)->forall (GTK_CONTAINER (self), ++ include_internals, ++ callback, ++ callback_data); ++ return; ++ } ++ ++ data.row = self; ++ data.callback = callback; ++ data.callback_data = callback_data; ++ ++ if (self->prefixes) ++ { ++ GTK_CONTAINER_GET_CLASS (self->prefixes)->forall (GTK_CONTAINER (self->prefixes), ++ include_internals, ++ for_non_internal_child, ++ &data); ++ } ++ if (self->suffixes) ++ { ++ GTK_CONTAINER_GET_CLASS (self->suffixes)->forall (GTK_CONTAINER (self->suffixes), ++ include_internals, ++ for_non_internal_child, ++ &data); ++ } ++ if (self->header) ++ { ++ GTK_CONTAINER_GET_CLASS (self->header)->forall (GTK_CONTAINER (self->header), ++ include_internals, ++ for_non_internal_child, ++ &data); ++ } ++} ++ ++static void ++cc_multitasking_row_class_init (CcMultitaskingRowClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); ++ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); ++ ++ object_class->get_property = cc_multitasking_row_get_property; ++ object_class->set_property = cc_multitasking_row_set_property; ++ object_class->dispose = cc_multitasking_row_dispose; ++ ++ widget_class->destroy = cc_multitasking_row_destroy; ++ widget_class->show_all = cc_multitasking_row_show_all; ++ ++ container_class->add = cc_multitasking_row_add; ++ container_class->remove = cc_multitasking_row_remove; ++ container_class->forall = cc_multitasking_row_forall; ++ ++ props[PROP_ICON_NAME] = ++ g_param_spec_string ("icon-name", ++ "Icon name", ++ "Icon name", ++ "", ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); ++ ++ props[PROP_ACTIVATABLE_WIDGET] = ++ g_param_spec_object ("activatable-widget", ++ "Activatable widget", ++ "The widget to be activated when the row is activated", ++ GTK_TYPE_WIDGET, ++ G_PARAM_READWRITE); ++ ++ props[PROP_SUBTITLE] = ++ g_param_spec_string ("subtitle", ++ "Subtitle", ++ "Subtitle", ++ "", ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); ++ ++ props[PROP_USE_UNDERLINE] = ++ g_param_spec_boolean ("use-underline", ++ "Use underline", ++ "If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key", ++ FALSE, ++ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); ++ ++ props[PROP_TITLE_LINES] = ++ g_param_spec_int ("title-lines", ++ "Number of title lines", ++ "The desired number of title lines", ++ 0, G_MAXINT, ++ 1, ++ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); ++ ++ props[PROP_SUBTITLE_LINES] = ++ g_param_spec_int ("subtitle-lines", ++ "Number of subtitle lines", ++ "The desired number of subtitle lines", ++ 0, G_MAXINT, ++ 1, ++ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); ++ ++ g_object_class_install_properties (object_class, N_PROPS, props); ++ ++ signals[SIGNAL_ACTIVATED] = ++ g_signal_new ("activated", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, NULL, NULL, ++ G_TYPE_NONE, ++ 0); ++ ++ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/multitasking/cc-multitasking-row.ui"); ++ ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingRow, artwork_box); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingRow, header); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingRow, image); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingRow, prefixes); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingRow, subtitle); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingRow, suffixes); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingRow, title); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingRow, title_box); ++} ++ ++static gboolean ++string_is_not_empty (GBinding *binding, ++ const GValue *from_value, ++ GValue *to_value, ++ gpointer user_data) ++{ ++ const gchar *string = g_value_get_string (from_value); ++ ++ g_value_set_boolean (to_value, string != NULL && g_strcmp0 (string, "") != 0); ++ ++ return TRUE; ++} ++ ++static void ++cc_multitasking_row_init (CcMultitaskingRow *self) ++{ ++ self->title_lines = 1; ++ self->subtitle_lines = 1; ++ ++ gtk_widget_init_template (GTK_WIDGET (self)); ++ ++ g_object_bind_property_full (self, "title", ++ self->title, "visible", ++ G_BINDING_SYNC_CREATE, ++ string_is_not_empty, ++ NULL, NULL, NULL); ++ ++ update_subtitle_visibility (self); ++ ++ g_signal_connect (self, "notify::parent", G_CALLBACK (parent_cb), NULL); ++} ++ ++static void ++cc_multitasking_row_buildable_add_child (GtkBuildable *buildable, ++ GtkBuilder *builder, ++ GObject *child, ++ const gchar *type) ++{ ++ CcMultitaskingRow *self = CC_MULTITASKING_ROW (buildable); ++ ++ if (self->header == NULL || !type) ++ gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (child)); ++ else if (type && strcmp (type, "prefix") == 0) ++ cc_multitasking_row_add_prefix (self, GTK_WIDGET (child)); ++ else if (type && strcmp (type, "artwork") == 0) ++ cc_multitasking_row_add_artwork(self, GTK_WIDGET (child)); ++ else ++ GTK_BUILDER_WARN_INVALID_CHILD_TYPE (self, type); ++} ++ ++static void ++cc_multitasking_row_buildable_init (GtkBuildableIface *iface) ++{ ++ iface->add_child = cc_multitasking_row_buildable_add_child; ++} ++ ++const gchar * ++cc_multitasking_row_get_subtitle (CcMultitaskingRow *self) ++{ ++ g_return_val_if_fail (CC_IS_MULTITASKING_ROW (self), NULL); ++ ++ return gtk_label_get_text (self->subtitle); ++} ++ ++void ++cc_multitasking_row_set_subtitle (CcMultitaskingRow *self, ++ const gchar *subtitle) ++{ ++ g_return_if_fail (CC_IS_MULTITASKING_ROW (self)); ++ ++ if (g_strcmp0 (gtk_label_get_text (self->subtitle), subtitle) == 0) ++ return; ++ ++ gtk_label_set_text (self->subtitle, subtitle); ++ gtk_widget_set_visible (GTK_WIDGET (self->subtitle), ++ subtitle != NULL && g_strcmp0 (subtitle, "") != 0); ++ ++ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SUBTITLE]); ++} ++ ++const gchar * ++cc_multitasking_row_get_icon_name (CcMultitaskingRow *self) ++{ ++ const gchar *icon_name; ++ ++ g_return_val_if_fail (CC_IS_MULTITASKING_ROW (self), NULL); ++ ++ gtk_image_get_icon_name (self->image, &icon_name, NULL); ++ ++ return icon_name; ++} ++ ++void ++cc_multitasking_row_set_icon_name (CcMultitaskingRow *self, ++ const gchar *icon_name) ++{ ++ const gchar *old_icon_name; ++ ++ g_return_if_fail (CC_IS_MULTITASKING_ROW (self)); ++ ++ gtk_image_get_icon_name (self->image, &old_icon_name, NULL); ++ if (g_strcmp0 (old_icon_name, icon_name) == 0) ++ return; ++ ++ gtk_image_set_from_icon_name (self->image, icon_name, GTK_ICON_SIZE_INVALID); ++ gtk_widget_set_visible (GTK_WIDGET (self->image), ++ icon_name != NULL && g_strcmp0 (icon_name, "") != 0); ++ ++ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ICON_NAME]); ++} ++ ++GtkWidget * ++cc_multitasking_row_get_activatable_widget (CcMultitaskingRow *self) ++{ ++ g_return_val_if_fail (CC_IS_MULTITASKING_ROW (self), NULL); ++ ++ return self->activatable_widget; ++} ++ ++static void ++activatable_widget_weak_notify (gpointer data, ++ GObject *where_the_object_was) ++{ ++ CcMultitaskingRow *self = CC_MULTITASKING_ROW (data); ++ ++ self->activatable_widget = NULL; ++ ++ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ACTIVATABLE_WIDGET]); ++} ++ ++void ++cc_multitasking_row_set_activatable_widget (CcMultitaskingRow *self, ++ GtkWidget *widget) ++{ ++ g_return_if_fail (CC_IS_MULTITASKING_ROW (self)); ++ g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget)); ++ ++ if (self->activatable_widget == widget) ++ return; ++ ++ if (self->activatable_widget) ++ g_object_weak_unref (G_OBJECT (self->activatable_widget), ++ activatable_widget_weak_notify, ++ self); ++ ++ self->activatable_widget = widget; ++ ++ if (self->activatable_widget != NULL) { ++ g_object_weak_ref (G_OBJECT (self->activatable_widget), ++ activatable_widget_weak_notify, ++ self); ++ gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (self), TRUE); ++ } ++ ++ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ACTIVATABLE_WIDGET]); ++} ++ ++gboolean ++cc_multitasking_row_get_use_underline (CcMultitaskingRow *self) ++{ ++ g_return_val_if_fail (CC_IS_MULTITASKING_ROW (self), FALSE); ++ ++ return self->use_underline; ++} ++ ++void ++cc_multitasking_row_set_use_underline (CcMultitaskingRow *self, ++ gboolean use_underline) ++{ ++ g_return_if_fail (CC_IS_MULTITASKING_ROW (self)); ++ ++ use_underline = !!use_underline; ++ ++ if (self->use_underline == use_underline) ++ return; ++ ++ self->use_underline = use_underline; ++ hdy_preferences_row_set_use_underline (HDY_PREFERENCES_ROW (self), self->use_underline); ++ gtk_label_set_use_underline (self->title, self->use_underline); ++ gtk_label_set_use_underline (self->subtitle, self->use_underline); ++ gtk_label_set_mnemonic_widget (self->title, GTK_WIDGET (self)); ++ gtk_label_set_mnemonic_widget (self->subtitle, GTK_WIDGET (self)); ++ ++ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_USE_UNDERLINE]); ++} ++ ++gint ++cc_multitasking_row_get_title_lines (CcMultitaskingRow *self) ++{ ++ g_return_val_if_fail (CC_IS_MULTITASKING_ROW (self), 0); ++ ++ return self->title_lines; ++} ++ ++void ++cc_multitasking_row_set_title_lines (CcMultitaskingRow *self, ++ gint title_lines) ++{ ++ g_return_if_fail (CC_IS_MULTITASKING_ROW (self)); ++ g_return_if_fail (title_lines >= 0); ++ ++ if (self->title_lines == title_lines) ++ return; ++ ++ self->title_lines = title_lines; ++ ++ gtk_label_set_lines (self->title, title_lines); ++ gtk_label_set_ellipsize (self->title, title_lines == 0 ? PANGO_ELLIPSIZE_NONE : PANGO_ELLIPSIZE_END); ++ ++ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TITLE_LINES]); ++} ++ ++gint ++cc_multitasking_row_get_subtitle_lines (CcMultitaskingRow *self) ++{ ++ g_return_val_if_fail (CC_IS_MULTITASKING_ROW (self), 0); ++ ++ return self->subtitle_lines; ++} ++ ++void ++cc_multitasking_row_set_subtitle_lines (CcMultitaskingRow *self, ++ gint subtitle_lines) ++{ ++ g_return_if_fail (CC_IS_MULTITASKING_ROW (self)); ++ g_return_if_fail (subtitle_lines >= 0); ++ ++ if (self->subtitle_lines == subtitle_lines) ++ return; ++ ++ self->subtitle_lines = subtitle_lines; ++ ++ gtk_label_set_lines (self->subtitle, subtitle_lines); ++ gtk_label_set_ellipsize (self->subtitle, subtitle_lines == 0 ? PANGO_ELLIPSIZE_NONE : PANGO_ELLIPSIZE_END); ++ ++ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SUBTITLE_LINES]); ++} ++ ++void ++cc_multitasking_row_add_prefix (CcMultitaskingRow *self, ++ GtkWidget *widget) ++{ ++ g_return_if_fail (CC_IS_MULTITASKING_ROW (self)); ++ g_return_if_fail (GTK_IS_WIDGET (self)); ++ ++ gtk_box_pack_start (self->prefixes, widget, FALSE, TRUE, 0); ++ gtk_widget_show (GTK_WIDGET (self->prefixes)); ++} ++ ++void ++cc_multitasking_row_add_artwork (CcMultitaskingRow *self, ++ GtkWidget *widget) ++{ ++ g_return_if_fail (CC_IS_MULTITASKING_ROW (self)); ++ g_return_if_fail (GTK_IS_WIDGET (self)); ++ ++ /* HACK: the artwork box pushes the title too much to the top, so we ++ * need to compensate this here. ++ */ ++ gtk_widget_set_margin_top (GTK_WIDGET (self->header), 12); ++ ++ gtk_box_pack_start (self->artwork_box, widget, FALSE, TRUE, 0); ++ gtk_widget_show (GTK_WIDGET (self->artwork_box)); ++} ++ ++void ++cc_multitasking_row_activate (CcMultitaskingRow *self) ++{ ++ g_return_if_fail (CC_IS_MULTITASKING_ROW (self)); ++ ++ if (self->activatable_widget) ++ gtk_widget_mnemonic_activate (self->activatable_widget, FALSE); ++ ++ g_signal_emit (self, signals[SIGNAL_ACTIVATED], 0); ++} +diff --git a/panels/multitasking/cc-multitasking-row.h b/panels/multitasking/cc-multitasking-row.h +new file mode 100644 +index 000000000..c35d9d688 +--- /dev/null ++++ b/panels/multitasking/cc-multitasking-row.h +@@ -0,0 +1,63 @@ ++/* cc-multitasking-row.h ++ * ++ * Copyright 2018 Purism SPC ++ * 2021 Georges Basile Stavracas Neto ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * SPDX-License-Identifier: GPL-3.0-or-later ++ */ ++ ++#pragma once ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define CC_TYPE_MULTITASKING_ROW (cc_multitasking_row_get_type()) ++G_DECLARE_FINAL_TYPE (CcMultitaskingRow, cc_multitasking_row, CC, MULTITASKING_ROW, HdyPreferencesRow) ++ ++const gchar *cc_multitasking_row_get_subtitle (CcMultitaskingRow *self); ++void cc_multitasking_row_set_subtitle (CcMultitaskingRow *self, ++ const gchar *subtitle); ++ ++const gchar *cc_multitasking_row_get_icon_name (CcMultitaskingRow *self); ++void cc_multitasking_row_set_icon_name (CcMultitaskingRow *self, ++ const gchar *icon_name); ++ ++GtkWidget *cc_multitasking_row_get_activatable_widget (CcMultitaskingRow *self); ++void cc_multitasking_row_set_activatable_widget (CcMultitaskingRow *self, ++ GtkWidget *widget); ++ ++gboolean cc_multitasking_row_get_use_underline (CcMultitaskingRow *self); ++void cc_multitasking_row_set_use_underline (CcMultitaskingRow *self, ++ gboolean use_underline); ++ ++gint cc_multitasking_row_get_title_lines (CcMultitaskingRow *self); ++void cc_multitasking_row_set_title_lines (CcMultitaskingRow *self, ++ gint title_lines); ++ ++gint cc_multitasking_row_get_subtitle_lines (CcMultitaskingRow *self); ++void cc_multitasking_row_set_subtitle_lines (CcMultitaskingRow *self, ++ gint subtitle_lines); ++ ++void cc_multitasking_row_add_prefix (CcMultitaskingRow *self, ++ GtkWidget *widget); ++ ++void cc_multitasking_row_add_artwork (CcMultitaskingRow *self, ++ GtkWidget *widget); ++ ++void cc_multitasking_row_activate (CcMultitaskingRow *self); ++ ++G_END_DECLS +diff --git a/panels/multitasking/cc-multitasking-row.ui b/panels/multitasking/cc-multitasking-row.ui +new file mode 100644 +index 000000000..a3377229f +--- /dev/null ++++ b/panels/multitasking/cc-multitasking-row.ui +@@ -0,0 +1,122 @@ ++ ++ ++ ++ +diff --git a/panels/multitasking/gnome-multitasking-panel.desktop.in.in b/panels/multitasking/gnome-multitasking-panel.desktop.in.in +new file mode 100644 +index 000000000..675879bc2 +--- /dev/null ++++ b/panels/multitasking/gnome-multitasking-panel.desktop.in.in +@@ -0,0 +1,14 @@ ++[Desktop Entry] ++Name=Multitasking ++Comment=Manage preferences for productivity and multitasking ++Exec=gnome-control-center multitasking ++# Translators: Do NOT translate or transliterate this text (this is an icon file name)! ++Icon=org.gnome.Settings-multitasking-symbolic ++Terminal=false ++Type=Application ++NoDisplay=true ++StartupNotify=true ++Categories=GNOME;GTK;Settings;DesktopSettings;X-GNOME-Settings-Panel;X-GNOME-PersonalizationSettings; ++OnlyShowIn=GNOME; ++# Translators: Search terms to find the Search panel. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! ++Keywords=Multitasking;Multitask;Productivity;Customize;Desktop; +diff --git a/panels/multitasking/icons/meson.build b/panels/multitasking/icons/meson.build +new file mode 100644 +index 000000000..b274dd102 +--- /dev/null ++++ b/panels/multitasking/icons/meson.build +@@ -0,0 +1,4 @@ ++install_data( ++ 'scalable/org.gnome.Settings-multitasking-symbolic.svg', ++ install_dir: join_paths(control_center_icondir, 'hicolor', 'scalable', 'apps') ++) +diff --git a/panels/multitasking/icons/scalable/org.gnome.Settings-multitasking-symbolic.svg b/panels/multitasking/icons/scalable/org.gnome.Settings-multitasking-symbolic.svg +new file mode 100644 +index 000000000..0e09bbcd5 +--- /dev/null ++++ b/panels/multitasking/icons/scalable/org.gnome.Settings-multitasking-symbolic.svg +@@ -0,0 +1,106 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/panels/multitasking/meson.build b/panels/multitasking/meson.build +new file mode 100644 +index 000000000..772b63813 +--- /dev/null ++++ b/panels/multitasking/meson.build +@@ -0,0 +1,49 @@ ++panels_list += cappletname ++desktop = 'gnome-@0@-panel.desktop'.format(cappletname) ++ ++desktop_in = configure_file( ++ input: desktop + '.in.in', ++ output: desktop + '.in', ++ configuration: desktop_conf ++) ++ ++i18n.merge_file( ++ desktop, ++ type: 'desktop', ++ input: desktop_in, ++ output: desktop, ++ po_dir: po_dir, ++ install: true, ++ install_dir: control_center_desktopdir ++) ++ ++sources = files( ++ 'cc-multitasking-panel.c', ++ 'cc-multitasking-row.c', ++) ++ ++resource_data = files( ++ 'cc-multitasking-panel.ui', ++) ++ ++sources += gnome.compile_resources( ++ 'cc-' + cappletname + '-resources', ++ cappletname + '.gresource.xml', ++ c_name: 'cc_' + cappletname, ++ dependencies: resource_data, ++ export: true ++) ++ ++cflags += [ ++ '-DDATADIR="@0@"'.format(control_center_datadir) ++] ++ ++panels_libs += static_library( ++ cappletname, ++ sources: sources, ++ include_directories: [ top_inc, common_inc ], ++ dependencies: common_deps, ++ c_args: cflags ++) ++ ++subdir('icons') +diff --git a/panels/multitasking/multitasking.gresource.xml b/panels/multitasking/multitasking.gresource.xml +new file mode 100644 +index 000000000..db8365f68 +--- /dev/null ++++ b/panels/multitasking/multitasking.gresource.xml +@@ -0,0 +1,13 @@ ++ ++ ++ ++ cc-multitasking-panel.ui ++ cc-multitasking-row.ui ++ ++ ++ assets/active-screen-edges.svg ++ assets/hot-corner.svg ++ assets/workspaces-primary-display.svg ++ assets/workspaces-span-displays.svg ++ ++ +diff --git a/shell/cc-panel-list.c b/shell/cc-panel-list.c +index e23da0b87..38be643ea 100644 +--- a/shell/cc-panel-list.c ++++ b/shell/cc-panel-list.c +@@ -386,6 +386,7 @@ static const gchar * const panel_order[] = { + "background", + "notifications", + "search", ++ "multitasking", + "applications", + "privacy", + "online-accounts", +diff --git a/shell/cc-panel-loader.c b/shell/cc-panel-loader.c +index f20384394..fcbf398ca 100644 +--- a/shell/cc-panel-loader.c ++++ b/shell/cc-panel-loader.c +@@ -43,6 +43,7 @@ extern GType cc_display_panel_get_type (void); + extern GType cc_info_overview_panel_get_type (void); + extern GType cc_keyboard_panel_get_type (void); + extern GType cc_mouse_panel_get_type (void); ++extern GType cc_multitasking_panel_get_type (void); + #ifdef BUILD_NETWORK + extern GType cc_network_panel_get_type (void); + extern GType cc_wifi_panel_get_type (void); +@@ -107,6 +108,7 @@ static CcPanelLoaderVtable default_panels[] = + PANEL_TYPE("lock", cc_lock_panel_get_type, NULL), + PANEL_TYPE("microphone", cc_microphone_panel_get_type, NULL), + PANEL_TYPE("mouse", cc_mouse_panel_get_type, NULL), ++ PANEL_TYPE("multitasking", cc_multitasking_panel_get_type, NULL), + #ifdef BUILD_NETWORK + PANEL_TYPE("network", cc_network_panel_get_type, NULL), + PANEL_TYPE("wifi", cc_wifi_panel_get_type, cc_wifi_panel_static_init_func), +diff --git a/shell/gnome-control-center.gresource.xml b/shell/gnome-control-center.gresource.xml +index 9be077ec9..5550440e1 100644 +--- a/shell/gnome-control-center.gresource.xml ++++ b/shell/gnome-control-center.gresource.xml +@@ -6,4 +6,10 @@ + help-overlay.ui + style.css + ++ ++ ++ ++ icons/multitasking-symbolic.svg ++ style.css ++ + +diff --git a/shell/icons/multitasking-symbolic.svg b/shell/icons/multitasking-symbolic.svg +new file mode 100644 +index 000000000..7959ddbfc +--- /dev/null ++++ b/shell/icons/multitasking-symbolic.svg +@@ -0,0 +1,98 @@ ++ ++ ++ ++ ++ ++ image/svg+xml ++ ++ Gnome Symbolic Icon Theme ++ ++ ++ ++ Gnome Symbolic Icon Theme ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +-- +2.35.0.rc2 + + +From 8168174fad79dd054beed80272f9905fba4a86c6 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Fri, 13 Aug 2021 18:39:57 -0300 +Subject: [PATCH 2/5] multitasking: Use Mutter settings for dynamic workspaces + +The overrides weren't meant to be used like that. +--- + panels/multitasking/cc-multitasking-panel.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/panels/multitasking/cc-multitasking-panel.c b/panels/multitasking/cc-multitasking-panel.c +index 6a3177fe9..1523e35b1 100644 +--- a/panels/multitasking/cc-multitasking-panel.c ++++ b/panels/multitasking/cc-multitasking-panel.c +@@ -31,7 +31,6 @@ struct _CcMultitaskingPanel + + GSettings *interface_settings; + GSettings *mutter_settings; +- GSettings *overrides_settings; + GSettings *shell_settings; + GSettings *wm_settings; + +@@ -56,7 +55,6 @@ cc_multitasking_panel_finalize (GObject *object) + + g_clear_object (&self->interface_settings); + g_clear_object (&self->mutter_settings); +- g_clear_object (&self->overrides_settings); + g_clear_object (&self->shell_settings); + g_clear_object (&self->wm_settings); + +@@ -117,14 +115,12 @@ cc_multitasking_panel_init (CcMultitaskingPanel *self) + "active", + G_SETTINGS_BIND_DEFAULT); + +- self->overrides_settings = g_settings_new ("org.gnome.shell.overrides"); +- +- if (g_settings_get_boolean (self->overrides_settings, "dynamic-workspaces")) ++ if (g_settings_get_boolean (self->mutter_settings, "dynamic-workspaces")) + gtk_toggle_button_set_active (self->dynamic_workspaces_radio, TRUE); + else + gtk_toggle_button_set_active (self->fixed_workspaces_radio, TRUE); + +- g_settings_bind (self->overrides_settings, ++ g_settings_bind (self->mutter_settings, + "dynamic-workspaces", + self->dynamic_workspaces_radio, + "active", +-- +2.35.0.rc2 + + +From 179f8e8ebf6b0712db720421839cb23cff1f2f71 Mon Sep 17 00:00:00 2001 +From: Felipe Borges +Date: Wed, 6 Oct 2021 12:37:44 +0200 +Subject: [PATCH 3/5] multitasking: Expand row subtitles to a second line + +Depending on the language used, the label could get long and +unreadable (ellipsized). + +Fixes #1486 +--- + panels/multitasking/cc-multitasking-row.ui | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/panels/multitasking/cc-multitasking-row.ui b/panels/multitasking/cc-multitasking-row.ui +index a3377229f..dcb90e9d4 100644 +--- a/panels/multitasking/cc-multitasking-row.ui ++++ b/panels/multitasking/cc-multitasking-row.ui +@@ -75,7 +75,7 @@ + end + start + True +- 1 ++ 2 + True + word-char + 0 +-- +2.35.0.rc2 + + +From b797a8a11a9e7271a225a636ae3bb2219ea49d93 Mon Sep 17 00:00:00 2001 +From: Jakub Steiner +Date: Fri, 28 Jan 2022 09:51:22 +0100 +Subject: [PATCH 4/5] multitasking: make graphics work in dark mode + +- more of a workaround than a solution. Getting rid of the white fills + at the expense of clarity and style. + +Fixes #1565 +--- + .../assets/active-screen-edges.svg | 159 +++++------- + panels/multitasking/assets/hot-corner.svg | 175 ++++++------- + .../assets/workspaces-primary-display.svg | 187 ++++++-------- + .../assets/workspaces-span-displays.svg | 244 +++++++----------- + 4 files changed, 301 insertions(+), 464 deletions(-) + +diff --git a/panels/multitasking/assets/active-screen-edges.svg b/panels/multitasking/assets/active-screen-edges.svg +index 34dc67b4e..66694cdca 100644 +--- a/panels/multitasking/assets/active-screen-edges.svg ++++ b/panels/multitasking/assets/active-screen-edges.svg +@@ -1,118 +1,79 @@ + +- +- + ++ + +- +- +- +- +- +- +- +- ++ showborder="false" ++ inkscape:current-layer="svg20"> ++ ++ ++ ++ + +- ++ transform="matrix(0.26458,0,0,0.26458,-400.06245,1156.76)" ++ style="display:inline" ++ id="g16"> + ++ ry="3" ++ rx="3" ++ y="-4353.0068" ++ x="1520.006" ++ height="58" ++ width="76" ++ style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#3584e4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" ++ id="rect6" /> ++ + +- ++ style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#3584e4;stroke-opacity:1" ++ id="g14"> ++ + +- +- +- +- ++ style="color:#000000;display:block;overflow:visible;visibility:visible;fill:#3584e4;fill-opacity:1;fill-rule:nonzero;stroke:#3584e4;stroke-width:0.612915;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" ++ d="m 26.605,2.318 v 16.44 l 3.712,-3.624 2.122,4.331 c 0.52,1.172 3.22,0.23 2.452,-1.337 l -2.099,-4.496 h 4.685 z" ++ transform="matrix(1.6306,0,0,1.63249,1484.124,-4348.541)" ++ id="path12" /> + +- + ++ + +diff --git a/panels/multitasking/assets/hot-corner.svg b/panels/multitasking/assets/hot-corner.svg +index 0a1732c0e..f540458ce 100644 +--- a/panels/multitasking/assets/hot-corner.svg ++++ b/panels/multitasking/assets/hot-corner.svg +@@ -1,124 +1,97 @@ + +- +- + + ++ inkscape:current-layer="svg31" /> + ++ id="defs5"> + +- ++ + + + +- +- +- +- +- +- +- +- +- +- ++ style="display:inline" ++ id="g11"> ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + ++ style="display:inline" ++ id="g27"> + +- +- +- +- ++ style="color:#000;display:block;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#3584e4;stroke-width:0.612915;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:1.22583, 0.612915;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" ++ d="M29.691 6.634v16.44l3.712-3.624 2.122 4.331c.52 1.171 3.22.23 2.452-1.337l-2.099-4.497h4.685z" ++ transform="matrix(.42866 0 0 .42916 1.739 7.533)" ++ id="path25" /> + + + +diff --git a/panels/multitasking/assets/workspaces-primary-display.svg b/panels/multitasking/assets/workspaces-primary-display.svg +index 56e05c4bb..5930fab69 100644 +--- a/panels/multitasking/assets/workspaces-primary-display.svg ++++ b/panels/multitasking/assets/workspaces-primary-display.svg +@@ -1,130 +1,99 @@ + +- +- + + ++ inkscape:current-layer="svg29" ++ width="326px"> ++ ++ + ++ id="defs5"> + +- ++ ++ d="m 1448,-3870.014 h 127 v 73 h -127 z" ++ id="path2" /> + + +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- ++ ++ ++ ++ ++ ++ ++ ++ ++ + +diff --git a/panels/multitasking/assets/workspaces-span-displays.svg b/panels/multitasking/assets/workspaces-span-displays.svg +index 7b3655df9..a18c74eae 100644 +--- a/panels/multitasking/assets/workspaces-span-displays.svg ++++ b/panels/multitasking/assets/workspaces-span-displays.svg +@@ -1,178 +1,112 @@ + +- +- + + ++ inkscape:current-layer="svg29"> ++ ++ + ++ id="defs5"> + +- ++ +- +- +- +- +- +- ++ d="m 1448,-3870.014 h 127 v 73 h -127 z" ++ id="path2" /> + + ++ ++ ++ ++ ++ ++ ++ ++ + ++ id="g4804" ++ transform="translate(43.921149,-0.5285854)"> + +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- ++ style="opacity:1;fill:#3584e4;fill-opacity:1;stroke:#ed333b;stroke-width:0;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none" ++ id="rect4795" ++ width="26.458509" ++ height="15.345935" ++ x="7.9374046" ++ y="5.2917023" ++ rx="1.3229257" ++ ry="1.3229256" /> ++ ++ + + +-- +2.35.0.rc2 + + +From 8249f17e22c73271570a9e77fd50e1e3882b7f96 Mon Sep 17 00:00:00 2001 +From: Christopher Davis +Date: Thu, 6 Jan 2022 18:24:25 -0800 +Subject: [PATCH 5/5] cc-multitasking-panel: Fix initial state for "all + workspaces" setting + +--- + panels/multitasking/cc-multitasking-panel.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/panels/multitasking/cc-multitasking-panel.c b/panels/multitasking/cc-multitasking-panel.c +index 1523e35b1..9a4041e43 100644 +--- a/panels/multitasking/cc-multitasking-panel.c ++++ b/panels/multitasking/cc-multitasking-panel.c +@@ -35,6 +35,7 @@ struct _CcMultitaskingPanel + GSettings *wm_settings; + + GtkSwitch *active_screen_edges_switch; ++ GtkToggleButton *all_workspaces_radio; + GtkToggleButton *current_workspace_radio; + GtkToggleButton *dynamic_workspaces_radio; + GtkToggleButton *fixed_workspaces_radio; +@@ -74,6 +75,7 @@ cc_multitasking_panel_class_init (CcMultitaskingPanelClass *klass) + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/multitasking/cc-multitasking-panel.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, active_screen_edges_switch); ++ gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, all_workspaces_radio); + gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, current_workspace_radio); + gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, dynamic_workspaces_radio); + gtk_widget_class_bind_template_child (widget_class, CcMultitaskingPanel, fixed_workspaces_radio); +@@ -137,6 +139,8 @@ cc_multitasking_panel_init (CcMultitaskingPanel *self) + + if (g_settings_get_boolean (self->shell_settings, "current-workspace-only")) + gtk_toggle_button_set_active (self->current_workspace_radio, TRUE); ++ else ++ gtk_toggle_button_set_active (self->all_workspaces_radio, TRUE); + + g_settings_bind (self->shell_settings, + "current-workspace-only", +-- +2.35.0.rc2 + diff --git a/gnome-control-center.spec b/gnome-control-center.spec index 44f8838..0eff311 100644 --- a/gnome-control-center.spec +++ b/gnome-control-center.spec @@ -14,7 +14,7 @@ Name: gnome-control-center Version: 40.0 -Release: 18%{?dist} +Release: 19%{?dist} Summary: Utilities to configure the GNOME desktop License: GPLv2+ and CC-BY-SA @@ -32,6 +32,7 @@ Patch2: power-profiles-backport.patch Patch3: wwan-backport-gnome-40.patch Patch4: subscription-manager-support.patch Patch5: application-use-icon-name-that-exists.patch +Patch6: backport-multitasking-panel.patch BuildRequires: chrpath BuildRequires: cups-devel @@ -221,6 +222,10 @@ chrpath --delete $RPM_BUILD_ROOT%{_bindir}/gnome-control-center %dir %{_datadir}/gnome/wm-properties %changelog +* Fri Jan 28 2022 Felipe Borges - 40.0-19 +- Backport Multitasking panel +- Resolves: #2047723 + * Thu Jan 20 2022 Felipe Borges - 40.0-18 - Fix typo in the previous patch - Resolves: #2041348