Compare commits
No commits in common. 'c10-beta' and 'c9' have entirely different histories.
@ -1 +1 @@
|
||||
SOURCES/gnome-shell-extensions-47.alpha.tar.xz
|
||||
SOURCES/gnome-shell-extensions-40.7.tar.xz
|
||||
|
@ -1 +1 @@
|
||||
c3c6c96b0db36cada61b74e35f86e9eede07a647 SOURCES/gnome-shell-extensions-47.alpha.tar.xz
|
||||
a905a152407590d18e8dc14bb4133fbde0e03abb SOURCES/gnome-shell-extensions-40.7.tar.xz
|
||||
|
@ -0,0 +1,438 @@
|
||||
From cca3ca69a5b5a5551a9130ab4b9ea6909666108a Mon Sep 17 00:00:00 2001
|
||||
From: rpm-build <rpm-build>
|
||||
Date: Thu, 28 Jan 2021 00:06:12 +0100
|
||||
Subject: [PATCH 1/5] Add gesture-inhibitor extension
|
||||
|
||||
This extension may disable default GNOME Shell gestures.
|
||||
---
|
||||
extensions/gesture-inhibitor/extension.js | 75 +++++++++++++++++++
|
||||
extensions/gesture-inhibitor/meson.build | 8 ++
|
||||
extensions/gesture-inhibitor/metadata.json.in | 12 +++
|
||||
...l.extensions.gesture-inhibitor.gschema.xml | 25 +++++++
|
||||
extensions/gesture-inhibitor/stylesheet.css | 1 +
|
||||
meson.build | 1 +
|
||||
6 files changed, 122 insertions(+)
|
||||
create mode 100644 extensions/gesture-inhibitor/extension.js
|
||||
create mode 100644 extensions/gesture-inhibitor/meson.build
|
||||
create mode 100644 extensions/gesture-inhibitor/metadata.json.in
|
||||
create mode 100644 extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
create mode 100644 extensions/gesture-inhibitor/stylesheet.css
|
||||
|
||||
diff --git a/extensions/gesture-inhibitor/extension.js b/extensions/gesture-inhibitor/extension.js
|
||||
new file mode 100644
|
||||
index 00000000..e74ede2f
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/extension.js
|
||||
@@ -0,0 +1,75 @@
|
||||
+/* extension.js
|
||||
+ *
|
||||
+ * 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 <http://www.gnu.org/licenses/>.
|
||||
+ *
|
||||
+ * SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+ */
|
||||
+
|
||||
+/* exported init */
|
||||
+
|
||||
+const Clutter = imports.gi.Clutter;
|
||||
+const ExtensionUtils = imports.misc.extensionUtils;
|
||||
+const Me = ExtensionUtils.getCurrentExtension();
|
||||
+const ViewSelector = imports.ui.viewSelector;
|
||||
+const EdgeDragAction = imports.ui.edgeDragAction;
|
||||
+const WindowManager = imports.ui.windowManager;
|
||||
+const St = imports.gi.St;
|
||||
+const Gio = imports.gi.Gio;
|
||||
+
|
||||
+class Extension {
|
||||
+ constructor() {
|
||||
+ this._settings = ExtensionUtils.getSettings();
|
||||
+ let actions = global.stage.get_actions();
|
||||
+
|
||||
+ actions.forEach(a => {
|
||||
+ if (a instanceof ViewSelector.ShowOverviewAction)
|
||||
+ this._showOverview = a;
|
||||
+ else if (a instanceof WindowManager.AppSwitchAction)
|
||||
+ this._appSwitch = a;
|
||||
+ else if (a instanceof EdgeDragAction.EdgeDragAction &&
|
||||
+ a._side == St.Side.BOTTOM)
|
||||
+ this._showOsk = a;
|
||||
+ else if (a instanceof EdgeDragAction.EdgeDragAction &&
|
||||
+ a._side == St.Side.TOP)
|
||||
+ this._unfullscreen = a;
|
||||
+ else if (a instanceof EdgeDragAction.EdgeDragAction)
|
||||
+ this._showAppGrid = a;
|
||||
+ });
|
||||
+
|
||||
+ this._map = [
|
||||
+ { setting: 'overview', action: this._showOverview },
|
||||
+ { setting: 'app-switch', action: this._appSwitch },
|
||||
+ { setting: 'show-osk', action: this._showOsk },
|
||||
+ { setting: 'unfullscreen', action: this._unfullscreen },
|
||||
+ { setting: 'show-app-grid', action: this._showAppGrid }
|
||||
+ ];
|
||||
+ }
|
||||
+
|
||||
+ enable() {
|
||||
+ this._map.forEach(m => {
|
||||
+ this._settings.bind(m.setting, m.action, 'enabled',
|
||||
+ Gio.SettingsBindFlags.DEFAULT);
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ disable() {
|
||||
+ this._map.forEach(m => {
|
||||
+ m.action.enabled = true;
|
||||
+ });
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+function init() {
|
||||
+ return new Extension();
|
||||
+}
|
||||
diff --git a/extensions/gesture-inhibitor/meson.build b/extensions/gesture-inhibitor/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..fdad5cc8
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/meson.build
|
||||
@@ -0,0 +1,8 @@
|
||||
+extension_data += configure_file(
|
||||
+ input: metadata_name + '.in',
|
||||
+ output: metadata_name,
|
||||
+ configuration: metadata_conf
|
||||
+)
|
||||
+
|
||||
+# extension_sources += files('prefs.js')
|
||||
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
diff --git a/extensions/gesture-inhibitor/metadata.json.in b/extensions/gesture-inhibitor/metadata.json.in
|
||||
new file mode 100644
|
||||
index 00000000..37d6a117
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/metadata.json.in
|
||||
@@ -0,0 +1,12 @@
|
||||
+{
|
||||
+ "uuid": "@uuid@",
|
||||
+ "extension-id": "@extension_id@",
|
||||
+ "settings-schema": "@gschemaname@",
|
||||
+ "gettext-domain": "@gettext_domain@",
|
||||
+ "name": "Gesture Inhibitor",
|
||||
+ "description": "Makes touchscreen gestures optional.",
|
||||
+ "shell-version": [ "@shell_current@" ],
|
||||
+ "original-authors": [ "cgarnach@redhat.com" ],
|
||||
+ "url": "@url@"
|
||||
+}
|
||||
+
|
||||
diff --git a/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
new file mode 100644
|
||||
index 00000000..1d67dcc0
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
@@ -0,0 +1,25 @@
|
||||
+<schemalist>
|
||||
+ <schema id="org.gnome.shell.extensions.gesture-inhibitor" path="/org/gnome/shell/extensions/gesture-inhibitor/">
|
||||
+ <key name="show-app-grid" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show app grid gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="show-osk" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show OSK gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="overview" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show Overview gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="app-switch" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Application switch gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="unfullscreen" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Unfullscreen gesture</summary>
|
||||
+ </key>
|
||||
+ </schema>
|
||||
+</schemalist>
|
||||
+
|
||||
diff --git a/extensions/gesture-inhibitor/stylesheet.css b/extensions/gesture-inhibitor/stylesheet.css
|
||||
new file mode 100644
|
||||
index 00000000..37b93f21
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/stylesheet.css
|
||||
@@ -0,0 +1 @@
|
||||
+/* Add your custom extension styling here */
|
||||
diff --git a/meson.build b/meson.build
|
||||
index ec600041..615dc5b0 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -50,6 +50,7 @@ all_extensions += [
|
||||
'custom-menu',
|
||||
'dash-to-dock',
|
||||
'dash-to-panel',
|
||||
+ 'gesture-inhibitor',
|
||||
'native-window-placement',
|
||||
'panel-favorites',
|
||||
'systemMonitor',
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
||||
From 45e88e7b5bb9537c44384a23af7d00f023d55793 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 20 Oct 2021 19:48:46 +0200
|
||||
Subject: [PATCH 2/5] gesture-inhibitor: Fix up indentation
|
||||
|
||||
---
|
||||
extensions/gesture-inhibitor/extension.js | 59 +++++++++++------------
|
||||
1 file changed, 29 insertions(+), 30 deletions(-)
|
||||
|
||||
diff --git a/extensions/gesture-inhibitor/extension.js b/extensions/gesture-inhibitor/extension.js
|
||||
index e74ede2f..734d61cc 100644
|
||||
--- a/extensions/gesture-inhibitor/extension.js
|
||||
+++ b/extensions/gesture-inhibitor/extension.js
|
||||
@@ -29,44 +29,43 @@ const Gio = imports.gi.Gio;
|
||||
|
||||
class Extension {
|
||||
constructor() {
|
||||
- this._settings = ExtensionUtils.getSettings();
|
||||
- let actions = global.stage.get_actions();
|
||||
+ this._settings = ExtensionUtils.getSettings();
|
||||
+ let actions = global.stage.get_actions();
|
||||
|
||||
- actions.forEach(a => {
|
||||
- if (a instanceof ViewSelector.ShowOverviewAction)
|
||||
- this._showOverview = a;
|
||||
- else if (a instanceof WindowManager.AppSwitchAction)
|
||||
- this._appSwitch = a;
|
||||
- else if (a instanceof EdgeDragAction.EdgeDragAction &&
|
||||
- a._side == St.Side.BOTTOM)
|
||||
- this._showOsk = a;
|
||||
- else if (a instanceof EdgeDragAction.EdgeDragAction &&
|
||||
- a._side == St.Side.TOP)
|
||||
- this._unfullscreen = a;
|
||||
- else if (a instanceof EdgeDragAction.EdgeDragAction)
|
||||
- this._showAppGrid = a;
|
||||
- });
|
||||
+ actions.forEach(a => {
|
||||
+ if (a instanceof ViewSelector.ShowOverviewAction)
|
||||
+ this._showOverview = a;
|
||||
+ else if (a instanceof WindowManager.AppSwitchAction)
|
||||
+ this._appSwitch = a;
|
||||
+ else if (a instanceof EdgeDragAction.EdgeDragAction &&
|
||||
+ a._side == St.Side.BOTTOM)
|
||||
+ this._showOsk = a;
|
||||
+ else if (a instanceof EdgeDragAction.EdgeDragAction &&
|
||||
+ a._side == St.Side.TOP)
|
||||
+ this._unfullscreen = a;
|
||||
+ else if (a instanceof EdgeDragAction.EdgeDragAction)
|
||||
+ this._showAppGrid = a;
|
||||
+ });
|
||||
|
||||
- this._map = [
|
||||
- { setting: 'overview', action: this._showOverview },
|
||||
- { setting: 'app-switch', action: this._appSwitch },
|
||||
- { setting: 'show-osk', action: this._showOsk },
|
||||
- { setting: 'unfullscreen', action: this._unfullscreen },
|
||||
- { setting: 'show-app-grid', action: this._showAppGrid }
|
||||
- ];
|
||||
+ this._map = [
|
||||
+ { setting: 'overview', action: this._showOverview },
|
||||
+ { setting: 'app-switch', action: this._appSwitch },
|
||||
+ { setting: 'show-osk', action: this._showOsk },
|
||||
+ { setting: 'unfullscreen', action: this._unfullscreen },
|
||||
+ { setting: 'show-app-grid', action: this._showAppGrid }
|
||||
+ ];
|
||||
}
|
||||
|
||||
enable() {
|
||||
- this._map.forEach(m => {
|
||||
- this._settings.bind(m.setting, m.action, 'enabled',
|
||||
- Gio.SettingsBindFlags.DEFAULT);
|
||||
- });
|
||||
+ this._map.forEach(m => {
|
||||
+ this._settings.bind(m.setting, m.action, 'enabled',
|
||||
+ Gio.SettingsBindFlags.DEFAULT);
|
||||
+ });
|
||||
}
|
||||
|
||||
disable() {
|
||||
- this._map.forEach(m => {
|
||||
- m.action.enabled = true;
|
||||
- });
|
||||
+ this._map.forEach(
|
||||
+ m => (m.action.enabled = true));
|
||||
}
|
||||
}
|
||||
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
||||
From fe0dd05f0c8c5cfeb5edbc6b9bb73417d42f6ee8 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 20 Oct 2021 19:47:05 +0200
|
||||
Subject: [PATCH 3/5] gesture-inhibitor: Adjust for GNOME 40 changes
|
||||
|
||||
---
|
||||
extensions/gesture-inhibitor/extension.js | 11 +++--------
|
||||
...ome.shell.extensions.gesture-inhibitor.gschema.xml | 4 ----
|
||||
2 files changed, 3 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/extensions/gesture-inhibitor/extension.js b/extensions/gesture-inhibitor/extension.js
|
||||
index 734d61cc..13586108 100644
|
||||
--- a/extensions/gesture-inhibitor/extension.js
|
||||
+++ b/extensions/gesture-inhibitor/extension.js
|
||||
@@ -21,8 +21,8 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
-const ViewSelector = imports.ui.viewSelector;
|
||||
const EdgeDragAction = imports.ui.edgeDragAction;
|
||||
+const Main = imports.ui.main;
|
||||
const WindowManager = imports.ui.windowManager;
|
||||
const St = imports.gi.St;
|
||||
const Gio = imports.gi.Gio;
|
||||
@@ -33,9 +33,7 @@ class Extension {
|
||||
let actions = global.stage.get_actions();
|
||||
|
||||
actions.forEach(a => {
|
||||
- if (a instanceof ViewSelector.ShowOverviewAction)
|
||||
- this._showOverview = a;
|
||||
- else if (a instanceof WindowManager.AppSwitchAction)
|
||||
+ if (a instanceof WindowManager.AppSwitchAction)
|
||||
this._appSwitch = a;
|
||||
else if (a instanceof EdgeDragAction.EdgeDragAction &&
|
||||
a._side == St.Side.BOTTOM)
|
||||
@@ -43,16 +41,13 @@ class Extension {
|
||||
else if (a instanceof EdgeDragAction.EdgeDragAction &&
|
||||
a._side == St.Side.TOP)
|
||||
this._unfullscreen = a;
|
||||
- else if (a instanceof EdgeDragAction.EdgeDragAction)
|
||||
- this._showAppGrid = a;
|
||||
});
|
||||
|
||||
this._map = [
|
||||
- { setting: 'overview', action: this._showOverview },
|
||||
+ { setting: 'overview', action: Main.overview._swipeTracker },
|
||||
{ setting: 'app-switch', action: this._appSwitch },
|
||||
{ setting: 'show-osk', action: this._showOsk },
|
||||
{ setting: 'unfullscreen', action: this._unfullscreen },
|
||||
- { setting: 'show-app-grid', action: this._showAppGrid }
|
||||
];
|
||||
}
|
||||
|
||||
diff --git a/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
index 1d67dcc0..4bdf9260 100644
|
||||
--- a/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
+++ b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
@@ -1,9 +1,5 @@
|
||||
<schemalist>
|
||||
<schema id="org.gnome.shell.extensions.gesture-inhibitor" path="/org/gnome/shell/extensions/gesture-inhibitor/">
|
||||
- <key name="show-app-grid" type="b">
|
||||
- <default>true</default>
|
||||
- <summary>Show app grid gesture</summary>
|
||||
- </key>
|
||||
<key name="show-osk" type="b">
|
||||
<default>true</default>
|
||||
<summary>Show OSK gesture</summary>
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
||||
From 952fa19311faecf50b02ab0f8807c2bc890848be Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 18 Nov 2021 15:54:23 +0100
|
||||
Subject: [PATCH 4/5] gesture-inhibitor: Unbind setting on disable
|
||||
|
||||
---
|
||||
extensions/gesture-inhibitor/extension.js | 6 ++++--
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/extensions/gesture-inhibitor/extension.js b/extensions/gesture-inhibitor/extension.js
|
||||
index 13586108..02b34ec4 100644
|
||||
--- a/extensions/gesture-inhibitor/extension.js
|
||||
+++ b/extensions/gesture-inhibitor/extension.js
|
||||
@@ -59,8 +59,10 @@ class Extension {
|
||||
}
|
||||
|
||||
disable() {
|
||||
- this._map.forEach(
|
||||
- m => (m.action.enabled = true));
|
||||
+ this._map.forEach(m => {
|
||||
+ Gio.Settings.unbind(m.action, 'enabled');
|
||||
+ m.action.enabled = true;
|
||||
+ });
|
||||
}
|
||||
}
|
||||
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
||||
From ef7a6cb1eac7b3d6d4d047174502d88f4e78959e Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 18 Nov 2021 16:06:09 +0100
|
||||
Subject: [PATCH 5/5] gesture-inhibitor: Override :enabled property
|
||||
|
||||
Otherwise gnome-shell can re-enable an inhibited gesture behind our
|
||||
back.
|
||||
---
|
||||
extensions/gesture-inhibitor/extension.js | 23 ++++++++++++++++++++++-
|
||||
1 file changed, 22 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/gesture-inhibitor/extension.js b/extensions/gesture-inhibitor/extension.js
|
||||
index 02b34ec4..fb8a6dc0 100644
|
||||
--- a/extensions/gesture-inhibitor/extension.js
|
||||
+++ b/extensions/gesture-inhibitor/extension.js
|
||||
@@ -49,18 +49,39 @@ class Extension {
|
||||
{ setting: 'show-osk', action: this._showOsk },
|
||||
{ setting: 'unfullscreen', action: this._unfullscreen },
|
||||
];
|
||||
+
|
||||
+ this._enabledDesc = Object.getOwnPropertyDescriptor(
|
||||
+ Clutter.ActorMeta.prototype, 'enabled');
|
||||
+ }
|
||||
+
|
||||
+ _overrideEnabledSetter(obj, set) {
|
||||
+ if (!(obj instanceof Clutter.ActorMeta))
|
||||
+ return;
|
||||
+
|
||||
+ const desc = set
|
||||
+ ? { ...this._enabledDesc, set }
|
||||
+ : { ...this._enabledDesc };
|
||||
+ Object.defineProperty(obj, 'enabled', desc);
|
||||
}
|
||||
|
||||
enable() {
|
||||
+ const settings = this._settings;
|
||||
+
|
||||
this._map.forEach(m => {
|
||||
- this._settings.bind(m.setting, m.action, 'enabled',
|
||||
+ settings.bind(m.setting, m.action, 'enabled',
|
||||
Gio.SettingsBindFlags.DEFAULT);
|
||||
+
|
||||
+ this._overrideEnabledSetter(m.action, function (value) {
|
||||
+ if (settings.get_boolean(m.setting))
|
||||
+ this.set_enabled(value);
|
||||
+ });
|
||||
});
|
||||
}
|
||||
|
||||
disable() {
|
||||
this._map.forEach(m => {
|
||||
Gio.Settings.unbind(m.action, 'enabled');
|
||||
+ this._overrideEnabledSetter(m.action);
|
||||
m.action.enabled = true;
|
||||
});
|
||||
}
|
||||
--
|
||||
2.41.0
|
||||
|
@ -0,0 +1,43 @@
|
||||
From 9ca03a744552c43251523fd23292b243130e1f89 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Mon, 24 May 2021 15:36:04 +0200
|
||||
Subject: [PATCH] Update style
|
||||
|
||||
---
|
||||
data/gnome-shell-sass | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Submodule data/gnome-shell-sass 9d66f7d..60230f4:
|
||||
diff --git a/data/gnome-shell-sass/widgets/_panel.scss b/data/gnome-shell-sass/widgets/_panel.scss
|
||||
index 1f46507..ad638b2 100644
|
||||
--- a/data/gnome-shell-sass/widgets/_panel.scss
|
||||
+++ b/data/gnome-shell-sass/widgets/_panel.scss
|
||||
@@ -67,6 +67,11 @@ $panel_transition_duration: 250ms; // same as the overview transition duration
|
||||
}
|
||||
}
|
||||
|
||||
+ .panel-logo-icon {
|
||||
+ padding-right: .4em;
|
||||
+ icon-size: 1em;
|
||||
+ }
|
||||
+
|
||||
// status area icons
|
||||
.system-status-icon {
|
||||
icon-size: $base_icon_size;
|
||||
diff --git a/data/gnome-classic.css b/data/gnome-classic.css
|
||||
index 52e5367..3be81e9 100644
|
||||
--- a/data/gnome-classic.css
|
||||
+++ b/data/gnome-classic.css
|
||||
@@ -1234,6 +1234,9 @@ StScrollBar {
|
||||
box-shadow: none; }
|
||||
#panel .panel-button.clock-display:hover .clock, #panel .panel-button.clock-display:active .clock, #panel .panel-button.clock-display:overview .clock, #panel .panel-button.clock-display:focus .clock, #panel .panel-button.clock-display:checked .clock {
|
||||
box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.2); }
|
||||
+ #panel .panel-button .panel-logo-icon {
|
||||
+ padding-right: .4em;
|
||||
+ icon-size: 1em; }
|
||||
#panel .panel-button .system-status-icon {
|
||||
icon-size: 1.09em;
|
||||
padding: 5px;
|
||||
--
|
||||
2.28.0
|
||||
|
@ -0,0 +1,29 @@
|
||||
From fe13aa54e7c104f63689fcd15957ab16ffc0c3ef Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 17 Mar 2016 17:15:38 +0100
|
||||
Subject: [PATCH] apps-menu: Explicitly set label_actor
|
||||
|
||||
For some reason orca fails to pick up the label of category items,
|
||||
so set the label_actor explicitly as workaround.
|
||||
---
|
||||
extensions/apps-menu/extension.js | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js
|
||||
index 983a4e7..f8cef41 100644
|
||||
--- a/extensions/apps-menu/extension.js
|
||||
+++ b/extensions/apps-menu/extension.js
|
||||
@@ -113,7 +113,9 @@ class CategoryMenuItem extends PopupMenu.PopupBaseMenuItem {
|
||||
else
|
||||
name = _('Favorites');
|
||||
|
||||
- this.add_child(new St.Label({ text: name }));
|
||||
+ const label = new St.Label({ text: name });
|
||||
+ this.add_child(label);
|
||||
+ this.actor.label_actor = label;
|
||||
this.connect('motion-event', this._onMotionEvent.bind(this));
|
||||
this.connect('notify::active', this._onActiveChanged.bind(this));
|
||||
}
|
||||
--
|
||||
2.32.0
|
||||
|
@ -0,0 +1,66 @@
|
||||
From 08e720c793baa0cb12ed99c4333c75df46e3a9ed Mon Sep 17 00:00:00 2001
|
||||
From: Ray Strode <rstrode@redhat.com>
|
||||
Date: Tue, 21 Jan 2014 16:48:17 -0500
|
||||
Subject: [PATCH] apps-menu: add logo icon to Applications menu
|
||||
|
||||
Brand requested it.
|
||||
---
|
||||
extensions/apps-menu/extension.js | 22 +++++++++++++++++++++-
|
||||
1 file changed, 21 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js
|
||||
index e36b0fe..983a4e7 100644
|
||||
--- a/extensions/apps-menu/extension.js
|
||||
+++ b/extensions/apps-menu/extension.js
|
||||
@@ -364,13 +364,24 @@ class ApplicationsButton extends PanelMenu.Button {
|
||||
// role ATK_ROLE_MENU like other elements of the panel.
|
||||
this.accessible_role = Atk.Role.LABEL;
|
||||
|
||||
+ const hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
|
||||
+
|
||||
+ const iconFile = Gio.File.new_for_path(
|
||||
+ '/usr/share/icons/hicolor/scalable/apps/start-here.svg');
|
||||
+ this._icon = new St.Icon({
|
||||
+ gicon: new Gio.FileIcon({ file: iconFile }),
|
||||
+ style_class: 'panel-logo-icon',
|
||||
+ });
|
||||
+ hbox.add_actor(this._icon);
|
||||
+
|
||||
this._label = new St.Label({
|
||||
text: _('Applications'),
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
+ hbox.add_actor(this._label);
|
||||
|
||||
- this.add_actor(this._label);
|
||||
+ this.add_actor(hbox);
|
||||
this.name = 'panelApplications';
|
||||
this.label_actor = this._label;
|
||||
|
||||
@@ -404,6 +415,14 @@ class ApplicationsButton extends PanelMenu.Button {
|
||||
this._display();
|
||||
this._installedChangedId = appSys.connect('installed-changed',
|
||||
this._onTreeChanged.bind(this));
|
||||
+ this._sessionUpdatedId = Main.sessionMode.connect('updated',
|
||||
+ this._sessionUpdated.bind(this));
|
||||
+ this._sessionUpdated();
|
||||
+ }
|
||||
+
|
||||
+ _sessionUpdated() {
|
||||
+ this._icon.visible =
|
||||
+ !Main.sessionMode.panel.left.includes('activities');
|
||||
}
|
||||
|
||||
_onTreeChanged() {
|
||||
@@ -429,6 +448,7 @@ class ApplicationsButton extends PanelMenu.Button {
|
||||
|
||||
Main.overview.disconnect(this._showingId);
|
||||
Main.overview.disconnect(this._hidingId);
|
||||
+ Main.sessionMode.disconnect(this._sessionUpdatedId);
|
||||
appSys.disconnect(this._installedChangedId);
|
||||
this._tree.disconnect(this._treeChangedId);
|
||||
this._tree = null;
|
||||
--
|
||||
2.32.0
|
||||
|
@ -0,0 +1,68 @@
|
||||
From ffba821e1142c3cb96b9ced24dd1f161f0609d2a Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 14 Dec 2022 16:55:51 +0100
|
||||
Subject: [PATCH] classification-banner: Handle fullscreen monitors
|
||||
|
||||
When a monitor is in fullscreen, we don't want its classification
|
||||
banner to be offset by an imaginary panel, but at the top of the
|
||||
screen.
|
||||
---
|
||||
extensions/classification-banner/extension.js | 22 +++++++++++++++----
|
||||
1 file changed, 18 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/extensions/classification-banner/extension.js b/extensions/classification-banner/extension.js
|
||||
index cc046e01..ea788022 100644
|
||||
--- a/extensions/classification-banner/extension.js
|
||||
+++ b/extensions/classification-banner/extension.js
|
||||
@@ -27,16 +27,19 @@ const Main = imports.ui.main;
|
||||
const ClassificationBanner = GObject.registerClass(
|
||||
class ClassificationBanner extends Clutter.Actor {
|
||||
_init(index) {
|
||||
+ const constraint = new Layout.MonitorConstraint({index});
|
||||
super._init({
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
- constraints: new Layout.MonitorConstraint({
|
||||
- work_area: true,
|
||||
- index,
|
||||
- }),
|
||||
+ constraints: constraint,
|
||||
});
|
||||
+ this._monitorConstraint = constraint;
|
||||
|
||||
this._settings = ExtensionUtils.getSettings();
|
||||
this.connect('destroy', () => {
|
||||
+ if (this._fullscreenChangedId)
|
||||
+ global.display.disconnect(this._fullscreenChangedId);
|
||||
+ delete this._fullscreenChangedId;
|
||||
+
|
||||
this._settings?.run_dispose();
|
||||
this._settings = null;
|
||||
});
|
||||
@@ -94,6 +97,11 @@ class ClassificationBanner extends Clutter.Actor {
|
||||
userLabel, 'visible',
|
||||
Gio.SettingsBindFlags.GET);
|
||||
|
||||
+ this._fullscreenChangedId =
|
||||
+ global.display.connect('in-fullscreen-changed',
|
||||
+ () => this._updateMonitorConstraint());
|
||||
+ this._updateMonitorConstraint();
|
||||
+
|
||||
this._settings.connect('changed::color',
|
||||
() => this._updateStyles());
|
||||
this._settings.connect('changed::background-color',
|
||||
@@ -110,6 +118,12 @@ class ClassificationBanner extends Clutter.Actor {
|
||||
return `${key}: rgba(${red},${green},${blue},${alpha / 255});`;
|
||||
}
|
||||
|
||||
+ _updateMonitorConstraint() {
|
||||
+ const {index} = this._monitorConstraint;
|
||||
+ this._monitorConstraint.work_area =
|
||||
+ !global.display.get_monitor_in_fullscreen(index);
|
||||
+ }
|
||||
+
|
||||
_updateStyles() {
|
||||
const bgStyle = this._getColorSetting('background-color');
|
||||
const fgStyle = this._getColorSetting('color');
|
||||
--
|
||||
2.38.1
|
||||
|
@ -0,0 +1,39 @@
|
||||
From b9ba6b8708c18fb14033150fdb02a508457e0a17 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 2 Feb 2024 15:39:32 +0100
|
||||
Subject: [PATCH] classification-banner: Hide from picks
|
||||
|
||||
Banners are laid out via a fullscreen actor. While the actor is
|
||||
not reactive, it can still interfere with picks (for example
|
||||
during drag-and-drop operations).
|
||||
|
||||
Avoid that by explicitly hiding the actor from picks.
|
||||
---
|
||||
extensions/classification-banner/extension.js | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/classification-banner/extension.js b/extensions/classification-banner/extension.js
|
||||
index ea788022..2bde741e 100644
|
||||
--- a/extensions/classification-banner/extension.js
|
||||
+++ b/extensions/classification-banner/extension.js
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
/* exported init */
|
||||
|
||||
-const { Clutter, Gio, GLib, GObject, St } = imports.gi;
|
||||
+const { Clutter, Gio, GLib, GObject, Shell, St } = imports.gi;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Layout = imports.ui.layout;
|
||||
@@ -34,6 +34,8 @@ class ClassificationBanner extends Clutter.Actor {
|
||||
});
|
||||
this._monitorConstraint = constraint;
|
||||
|
||||
+ Shell.util_set_hidden_from_pick(this, true);
|
||||
+
|
||||
this._settings = ExtensionUtils.getSettings();
|
||||
this.connect('destroy', () => {
|
||||
if (this._fullscreenChangedId)
|
||||
--
|
||||
2.43.0
|
||||
|
@ -0,0 +1,33 @@
|
||||
From 8bea7c892c24694efda753ad1d76ab470032c6fe Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 15 Dec 2022 17:09:45 +0100
|
||||
Subject: [PATCH] desktop-icons: Don't grab focus on click
|
||||
|
||||
We will move keyboard focus away immediately, either when opening
|
||||
the context menu or when starting the rubberband.
|
||||
|
||||
In theory the grab is still useful, because it will move keyboard
|
||||
focus to the grid when restoring focus after ending the rubberband
|
||||
or closing the menu, however as keyboard navigation support is
|
||||
lacking, all it does is preventing the focus to return to the
|
||||
focus window after the operation.
|
||||
---
|
||||
extensions/desktop-icons/desktopGrid.js | 2 --
|
||||
1 file changed, 2 deletions(-)
|
||||
|
||||
diff --git a/extensions/desktop-icons/desktopGrid.js b/extensions/desktop-icons/desktopGrid.js
|
||||
index 002803c7..9a89d5a3 100644
|
||||
--- a/extensions/desktop-icons/desktopGrid.js
|
||||
+++ b/extensions/desktop-icons/desktopGrid.js
|
||||
@@ -559,8 +559,6 @@ var DesktopGrid = GObject.registerClass({
|
||||
let button = event.get_button();
|
||||
let [x, y] = event.get_coords();
|
||||
|
||||
- this._grid.grab_key_focus();
|
||||
-
|
||||
if (button == 1) {
|
||||
let shiftPressed = !!(event.get_state() & Clutter.ModifierType.SHIFT_MASK);
|
||||
let controlPressed = !!(event.get_state() & Clutter.ModifierType.CONTROL_MASK);
|
||||
--
|
||||
2.38.1
|
||||
|
@ -0,0 +1,33 @@
|
||||
From b48dae39341a3ba24eb3d142f99eb37d6b14ab41 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 2 Nov 2023 20:51:45 +0100
|
||||
Subject: [PATCH] desktop-icons: Don't try spawn with non-existent workdir
|
||||
|
||||
g_spawn_async() will fail if the specified workdir doesn't exist.
|
||||
That means that opening a terminal from the context menu will fail
|
||||
when the desktop directory doesn't exist.
|
||||
|
||||
The extension doesn't really make sense in that case, but when we
|
||||
show an "Open in Terminal" menu item even then, users expect it
|
||||
to work.
|
||||
---
|
||||
extensions/desktop-icons/desktopIconsUtil.js | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/extensions/desktop-icons/desktopIconsUtil.js b/extensions/desktop-icons/desktopIconsUtil.js
|
||||
index 57bedc13..c3fe5977 100644
|
||||
--- a/extensions/desktop-icons/desktopIconsUtil.js
|
||||
+++ b/extensions/desktop-icons/desktopIconsUtil.js
|
||||
@@ -50,6 +50,9 @@ function launchTerminal(workdir) {
|
||||
* https://gitlab.gnome.org/GNOME/gnome-shell/blob/gnome-3-30/js/misc/util.js
|
||||
*/
|
||||
|
||||
+ if (!GLib.file_test(workdir, GLib.FileTest.EXISTS))
|
||||
+ workdir = null;
|
||||
+
|
||||
var success, pid;
|
||||
try {
|
||||
[success, pid] = GLib.spawn_async(workdir, argv, null,
|
||||
--
|
||||
2.41.0
|
||||
|
@ -0,0 +1,108 @@
|
||||
From 2a1dd773a529c89b5f9577b53ae3c88aea2efc48 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 17 Jan 2023 20:31:21 +0100
|
||||
Subject: [PATCH] desktop-icons: Don't use blocking IO
|
||||
|
||||
---
|
||||
extensions/desktop-icons/desktopManager.js | 45 +++++++++++++++-------
|
||||
1 file changed, 32 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/extensions/desktop-icons/desktopManager.js b/extensions/desktop-icons/desktopManager.js
|
||||
index 74d0e6bd..75b2a22a 100644
|
||||
--- a/extensions/desktop-icons/desktopManager.js
|
||||
+++ b/extensions/desktop-icons/desktopManager.js
|
||||
@@ -54,6 +54,21 @@ function findMonitorIndexForPos(x, y) {
|
||||
return getDpy().get_monitor_index_for_rect(new Meta.Rectangle({x, y}));
|
||||
}
|
||||
|
||||
+async function queryInfo(file, attributes = DesktopIconsUtil.DEFAULT_ATTRIBUTES, cancellable = null) {
|
||||
+ const flags = Gio.FileQueryInfoFlags.NONE;
|
||||
+ const priority = GLib.PRIORITY_DEFAULT;
|
||||
+ return new Promise((resolve, reject) => {
|
||||
+ file.query_info_async(attributes, flags, priority, cancellable, (o, res) => {
|
||||
+ try {
|
||||
+ const info = file.query_info_finish(res);
|
||||
+ resolve(info);
|
||||
+ } catch (e) {
|
||||
+ reject(e);
|
||||
+ }
|
||||
+ });
|
||||
+ });
|
||||
+}
|
||||
+
|
||||
|
||||
var DesktopManager = GObject.registerClass({
|
||||
Properties: {
|
||||
@@ -272,9 +287,7 @@ var DesktopManager = GObject.registerClass({
|
||||
|
||||
if (!this._unixMode) {
|
||||
let desktopDir = DesktopIconsUtil.getDesktopDir();
|
||||
- let fileInfo = desktopDir.query_info(Gio.FILE_ATTRIBUTE_UNIX_MODE,
|
||||
- Gio.FileQueryInfoFlags.NONE,
|
||||
- null);
|
||||
+ let fileInfo = await queryInfo(desktopDir, Gio.FILE_ATTRIBUTE_UNIX_MODE);
|
||||
this._unixMode = fileInfo.get_attribute_uint32(Gio.FILE_ATTRIBUTE_UNIX_MODE);
|
||||
this._setWritableByOthers((this._unixMode & S_IWOTH) != 0);
|
||||
}
|
||||
@@ -283,7 +296,7 @@ var DesktopManager = GObject.registerClass({
|
||||
let items = [];
|
||||
for (let item of await this._enumerateDesktop())
|
||||
items.push(item);
|
||||
- for (let item of this._getMounts())
|
||||
+ for (let item of await this._getMounts())
|
||||
items.push(item);
|
||||
|
||||
let tmpFileItems = new Map();
|
||||
@@ -328,14 +341,22 @@ var DesktopManager = GObject.registerClass({
|
||||
Gio.FileQueryInfoFlags.NONE,
|
||||
GLib.PRIORITY_DEFAULT,
|
||||
this._desktopEnumerateCancellable,
|
||||
- (source, result) => {
|
||||
+ async (source, result) => {
|
||||
try {
|
||||
let fileEnum = source.enumerate_children_finish(result);
|
||||
+ let extraFolders = await Promise.all(DesktopIconsUtil.getExtraFolders()
|
||||
+ .map(async ([folder, extras]) => {
|
||||
+ const info = await queryInfo(folder,
|
||||
+ DesktopIconsUtil.DEFAULT_ATTRIBUTES,
|
||||
+ this._desktopEnumerateCancellable);
|
||||
+ return [folder, info, extras];
|
||||
+ }));
|
||||
+
|
||||
let resultGenerator = function *() {
|
||||
+ for (let [newFolder, info, extras] of extraFolders)
|
||||
+ yield [newFolder, info, extras];
|
||||
+
|
||||
let info;
|
||||
- for (let [newFolder, extras] of DesktopIconsUtil.getExtraFolders()) {
|
||||
- yield [newFolder, newFolder.query_info(DesktopIconsUtil.DEFAULT_ATTRIBUTES, Gio.FileQueryInfoFlags.NONE, this._desktopEnumerateCancellable), extras];
|
||||
- }
|
||||
while ((info = fileEnum.next_file(null)))
|
||||
yield [fileEnum.get_child(info), info, Prefs.FileType.NONE];
|
||||
}.bind(this);
|
||||
@@ -359,19 +380,17 @@ var DesktopManager = GObject.registerClass({
|
||||
this._monitorDesktopDir.connect('changed', (obj, file, otherFile, eventType) => this._updateDesktopIfChanged(file, otherFile, eventType));
|
||||
}
|
||||
|
||||
- _getMounts() {
|
||||
+ async _getMounts() {
|
||||
let files = [];
|
||||
if (!Prefs.settings.get_boolean('show-mount'))
|
||||
return files;
|
||||
|
||||
- this._mountMonitor.get_mounts().forEach( mount => {
|
||||
+ this._mountMonitor.get_mounts().forEach(async mount => {
|
||||
if (this._isNetworkMount(mount))
|
||||
return;
|
||||
|
||||
let file = mount.get_root();
|
||||
- let info = file.query_info(DesktopIconsUtil.DEFAULT_ATTRIBUTES,
|
||||
- Gio.FileQueryInfoFlags.NONE,
|
||||
- null);
|
||||
+ let info = await queryInfo(file);
|
||||
files.push([file, info, Prefs.FileType.MOUNT_DISK]);
|
||||
});
|
||||
|
||||
--
|
||||
2.38.1
|
||||
|
@ -0,0 +1,209 @@
|
||||
From 73000f25e578b3ce6654fdf0d3da2ec3d9b95dd2 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@redhat.com>
|
||||
Date: Tue, 2 Nov 2021 09:20:11 +0100
|
||||
Subject: [PATCH] desktop-icons: Fix stuck grab issue with rubber banding
|
||||
|
||||
The desktop icons extension can get into a state where the desktop no longer
|
||||
takes mouse input.
|
||||
|
||||
This happens if a user starts a rubber banding operation and then drags
|
||||
the mouse to somewhere on screen that has a pop up menu, and then pops
|
||||
the menu up.
|
||||
|
||||
This commit addresses the bug by limiting the grab actor to the
|
||||
backgrounds, and by explicitly ending the rubber banding operation
|
||||
when one of the icons own menus is shown.
|
||||
|
||||
One side effect of limiting the grab actor to the backgrounds, is the
|
||||
rubber banding code never gets to see motion outside of the backgrounds
|
||||
anymore. In order to keep drag operations feeling fluid when the user moves
|
||||
toward the edge of the screen, this commit also overrides the
|
||||
grab helpers captured-event handler so those motion events keep coming.
|
||||
|
||||
We also start to end the rubber band if for any reason the grab it had
|
||||
was released.
|
||||
---
|
||||
extensions/desktop-icons/desktopGrid.js | 1 +
|
||||
extensions/desktop-icons/desktopManager.js | 109 ++++++++++++---------
|
||||
extensions/desktop-icons/fileItem.js | 1 +
|
||||
3 files changed, 67 insertions(+), 44 deletions(-)
|
||||
|
||||
diff --git a/extensions/desktop-icons/desktopGrid.js b/extensions/desktop-icons/desktopGrid.js
|
||||
index 002803c..c7846bf 100644
|
||||
--- a/extensions/desktop-icons/desktopGrid.js
|
||||
+++ b/extensions/desktop-icons/desktopGrid.js
|
||||
@@ -388,6 +388,7 @@ var DesktopGrid = GObject.registerClass({
|
||||
}
|
||||
|
||||
_openMenu(x, y) {
|
||||
+ Extension.desktopManager.endRubberBand();
|
||||
Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0);
|
||||
this._submenu.menu.removeAll();
|
||||
let templates = Extension.templateManager.getTemplates();
|
||||
diff --git a/extensions/desktop-icons/desktopManager.js b/extensions/desktop-icons/desktopManager.js
|
||||
index 10e3ce0..08bc82b 100644
|
||||
--- a/extensions/desktop-icons/desktopManager.js
|
||||
+++ b/extensions/desktop-icons/desktopManager.js
|
||||
@@ -81,6 +81,7 @@ var DesktopManager = GObject.registerClass({
|
||||
this._unixMode = null;
|
||||
this._writableByOthers = null;
|
||||
this._discreteGpuAvailable = false;
|
||||
+ this._rubberBandActive = false;
|
||||
|
||||
this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', () => this._recreateDesktopIcons());
|
||||
this._rubberBand = new St.Widget({ style_class: 'rubber-band' });
|
||||
@@ -94,6 +95,20 @@ var DesktopManager = GObject.registerClass({
|
||||
this._mountRemovedId = this._mountMonitor.connect('mount-removed', (monitor, mount) => {
|
||||
this._recreateDesktopIcons(); });
|
||||
|
||||
+ let origCapturedEvent = this._grabHelper.onCapturedEvent;
|
||||
+ this._grabHelper.onCapturedEvent = (event) => {
|
||||
+ if (event.type() === Clutter.EventType.MOTION) {
|
||||
+ /* We handle motion events from a captured event handler so we
|
||||
+ * we can see motion over actors that are on other parts of the
|
||||
+ * stage.
|
||||
+ */
|
||||
+ this._handleMotion(event);
|
||||
+ return Clutter.EVENT_STOP;
|
||||
+ }
|
||||
+
|
||||
+ return origCapturedEvent.bind(this._grabHelper)(event);
|
||||
+ };
|
||||
+
|
||||
this._addDesktopIcons();
|
||||
this._monitorDesktopFolder();
|
||||
|
||||
@@ -133,57 +148,67 @@ var DesktopManager = GObject.registerClass({
|
||||
this._rubberBandInitialY = y;
|
||||
this._updateRubberBand(x, y);
|
||||
this._rubberBand.show();
|
||||
- this._grabHelper.grab({ actor: global.stage });
|
||||
+ this._rubberBandActive = true;
|
||||
+ this._grabHelper.grab({
|
||||
+ actor: Main.layoutManager._backgroundGroup,
|
||||
+ onUngrab: () => this.endRubberBand(false),
|
||||
+ });
|
||||
Extension.lockActivitiesButton = true;
|
||||
this._stageReleaseEventId = global.stage.connect('button-release-event', (actor, event) => {
|
||||
this.endRubberBand();
|
||||
});
|
||||
this._rubberBandId = global.stage.connect('motion-event', (actor, event) => {
|
||||
- /* In some cases, when the user starts a rubberband selection and ends it
|
||||
- * (by releasing the left button) over a window instead of doing it over
|
||||
- * the desktop, the stage doesn't receive the "button-release" event.
|
||||
- * This happens currently with, at least, Dash to Dock extension, but
|
||||
- * it probably also happens with other applications or extensions.
|
||||
- * To fix this, we also end the rubberband selection if we detect mouse
|
||||
- * motion in the stage without the left button pressed during a
|
||||
- * rubberband selection.
|
||||
- * */
|
||||
- let button = event.get_state();
|
||||
- if (!(button & Clutter.ModifierType.BUTTON1_MASK)) {
|
||||
- this.endRubberBand();
|
||||
- return;
|
||||
- }
|
||||
- [x, y] = event.get_coords();
|
||||
- this._updateRubberBand(x, y);
|
||||
- let x0, y0, x1, y1;
|
||||
- if (x >= this._rubberBandInitialX) {
|
||||
- x0 = this._rubberBandInitialX;
|
||||
- x1 = x;
|
||||
- } else {
|
||||
- x1 = this._rubberBandInitialX;
|
||||
- x0 = x;
|
||||
- }
|
||||
- if (y >= this._rubberBandInitialY) {
|
||||
- y0 = this._rubberBandInitialY;
|
||||
- y1 = y;
|
||||
- } else {
|
||||
- y1 = this._rubberBandInitialY;
|
||||
- y0 = y;
|
||||
- }
|
||||
- for (let [fileUri, fileItem] of this._fileItems) {
|
||||
- fileItem.emit('selected', true, true,
|
||||
- fileItem.intersectsWith(x0, y0, x1 - x0, y1 - y0));
|
||||
- }
|
||||
});
|
||||
}
|
||||
|
||||
- endRubberBand() {
|
||||
+ _handleMotion(event) {
|
||||
+ /* In some cases, when the user starts a rubberband selection and ends it
|
||||
+ * (by releasing the left button) over a window instead of doing it over
|
||||
+ * the desktop, the stage doesn't receive the "button-release" event.
|
||||
+ * This happens currently with, at least, Dash to Dock extension, but
|
||||
+ * it probably also happens with other applications or extensions.
|
||||
+ * To fix this, we also end the rubberband selection if we detect mouse
|
||||
+ * motion in the stage without the left button pressed during a
|
||||
+ * rubberband selection.
|
||||
+ * */
|
||||
+ let button = event.get_state();
|
||||
+ if (!(button & Clutter.ModifierType.BUTTON1_MASK)) {
|
||||
+ this.endRubberBand();
|
||||
+ return;
|
||||
+ }
|
||||
+ let [x, y] = event.get_coords();
|
||||
+ this._updateRubberBand(x, y);
|
||||
+ let x0, y0, x1, y1;
|
||||
+ if (x >= this._rubberBandInitialX) {
|
||||
+ x0 = this._rubberBandInitialX;
|
||||
+ x1 = x;
|
||||
+ } else {
|
||||
+ x1 = this._rubberBandInitialX;
|
||||
+ x0 = x;
|
||||
+ }
|
||||
+ if (y >= this._rubberBandInitialY) {
|
||||
+ y0 = this._rubberBandInitialY;
|
||||
+ y1 = y;
|
||||
+ } else {
|
||||
+ y1 = this._rubberBandInitialY;
|
||||
+ y0 = y;
|
||||
+ }
|
||||
+ for (let [fileUri, fileItem] of this._fileItems) {
|
||||
+ fileItem.emit('selected', true, true,
|
||||
+ fileItem.intersectsWith(x0, y0, x1 - x0, y1 - y0));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ endRubberBand(ungrab=true) {
|
||||
+ if (!this._rubberBandActive)
|
||||
+ return;
|
||||
+
|
||||
+ this._rubberBandActive = false;
|
||||
this._rubberBand.hide();
|
||||
Extension.lockActivitiesButton = false;
|
||||
- this._grabHelper.ungrab();
|
||||
- global.stage.disconnect(this._rubberBandId);
|
||||
+ if (ungrab)
|
||||
+ this._grabHelper.ungrab();
|
||||
global.stage.disconnect(this._stageReleaseEventId);
|
||||
- this._rubberBandId = 0;
|
||||
this._stageReleaseEventId = 0;
|
||||
|
||||
this._selection = new Set([...this._selection, ...this._currentSelection]);
|
||||
@@ -825,10 +850,6 @@ var DesktopManager = GObject.registerClass({
|
||||
global.stage.disconnect(this._stageReleaseEventId);
|
||||
this._stageReleaseEventId = 0;
|
||||
|
||||
- if (this._rubberBandId)
|
||||
- global.stage.disconnect(this._rubberBandId);
|
||||
- this._rubberBandId = 0;
|
||||
-
|
||||
this._rubberBand.destroy();
|
||||
|
||||
if (this._queryFileInfoCancellable)
|
||||
diff --git a/extensions/desktop-icons/fileItem.js b/extensions/desktop-icons/fileItem.js
|
||||
index 1e8ea89..37ee54d 100644
|
||||
--- a/extensions/desktop-icons/fileItem.js
|
||||
+++ b/extensions/desktop-icons/fileItem.js
|
||||
@@ -747,6 +747,7 @@ var FileItem = GObject.registerClass({
|
||||
}
|
||||
|
||||
_onPressButton(actor, event) {
|
||||
+ Extension.desktopManager.endRubberBand();
|
||||
this._updateClickState(event);
|
||||
let button = event.get_button();
|
||||
if (button == 3) {
|
||||
--
|
||||
2.31.1
|
||||
|
@ -0,0 +1,29 @@
|
||||
From ba4208c00504439bad19de4680fac68210767798 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 27 Jan 2021 11:51:28 +0100
|
||||
Subject: [PATCH] desktop-icons: Update Japanese translation
|
||||
|
||||
---
|
||||
po/ja.po | 6 +-----
|
||||
1 file changed, 1 insertion(+), 5 deletions(-)
|
||||
|
||||
diff --git a/po/ja.po b/po/ja.po
|
||||
index 8eb7725..ddf1eb7 100644
|
||||
--- a/po/ja.po
|
||||
+++ b/po/ja.po
|
||||
@@ -897,11 +897,7 @@ msgstr "押し込み量 (ピクセル)"
|
||||
#: desktopGrid.js:359
|
||||
#, fuzzy
|
||||
msgid "Display Settings"
|
||||
-msgstr ""
|
||||
-"#-#-#-#-# ja.po (gnome-shell-extensions master) #-#-#-#-#\n"
|
||||
-"ディスプレイ設定\n"
|
||||
-"#-#-#-#-# ja.po (desktop-icons master) #-#-#-#-#\n"
|
||||
-"ディスプレイの設定"
|
||||
+msgstr "ディスプレイ設定"
|
||||
|
||||
#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
|
||||
#, fuzzy
|
||||
--
|
||||
2.32.0
|
||||
|
@ -0,0 +1,60 @@
|
||||
From 62289dff5cb2e615a277b72f034fa42f45aad639 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 15 Dec 2022 15:14:08 +0100
|
||||
Subject: [PATCH] desktopManager: Hook into LayoutManager to create grids
|
||||
|
||||
Right now we track the `monitors-changed` signal to recreate the
|
||||
per-monitor grids. Usually that's enough, but if something else
|
||||
causes backgrounds to update, we'll end up without desktop icons
|
||||
until some other change (settings, mounts, monitor/resolution
|
||||
changes, ...) results in a reload of the grid.
|
||||
|
||||
To address this, hook into LayoutManager to always create the grid
|
||||
when backgrounds are updated.
|
||||
---
|
||||
extensions/desktop-icons/desktopManager.js | 15 +++++++++++----
|
||||
1 file changed, 11 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/extensions/desktop-icons/desktopManager.js b/extensions/desktop-icons/desktopManager.js
|
||||
index 08bc82b7..74d0e6bd 100644
|
||||
--- a/extensions/desktop-icons/desktopManager.js
|
||||
+++ b/extensions/desktop-icons/desktopManager.js
|
||||
@@ -83,7 +83,6 @@ var DesktopManager = GObject.registerClass({
|
||||
this._discreteGpuAvailable = false;
|
||||
this._rubberBandActive = false;
|
||||
|
||||
- this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', () => this._recreateDesktopIcons());
|
||||
this._rubberBand = new St.Widget({ style_class: 'rubber-band' });
|
||||
this._rubberBand.hide();
|
||||
Main.layoutManager._backgroundGroup.add_child(this._rubberBand);
|
||||
@@ -109,6 +108,13 @@ var DesktopManager = GObject.registerClass({
|
||||
return origCapturedEvent.bind(this._grabHelper)(event);
|
||||
};
|
||||
|
||||
+ this._origUpdateBackgrounds =
|
||||
+ Main.layoutManager._updateBackgrounds;
|
||||
+ Main.layoutManager._updateBackgrounds = () => {
|
||||
+ this._origUpdateBackgrounds.call(Main.layoutManager);
|
||||
+ this._recreateDesktopIcons();
|
||||
+ };
|
||||
+
|
||||
this._addDesktopIcons();
|
||||
this._monitorDesktopFolder();
|
||||
|
||||
@@ -843,9 +849,10 @@ var DesktopManager = GObject.registerClass({
|
||||
GLib.source_remove(this._deleteChildrenId);
|
||||
this._deleteChildrenId = 0;
|
||||
|
||||
- if (this._monitorsChangedId)
|
||||
- Main.layoutManager.disconnect(this._monitorsChangedId);
|
||||
- this._monitorsChangedId = 0;
|
||||
+ if (this._origUpdateBackgrounds)
|
||||
+ Main.layoutManager._updateBackgrounds = this._origUpdateBackgrounds;
|
||||
+ delete this._origUpdateBackgrounds;
|
||||
+
|
||||
if (this._stageReleaseEventId)
|
||||
global.stage.disconnect(this._stageReleaseEventId);
|
||||
this._stageReleaseEventId = 0;
|
||||
--
|
||||
2.38.1
|
||||
|
@ -0,0 +1,29 @@
|
||||
From 3edf3c0be7638bf9161c0d192dd3c2de1e3b9845 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 14 Dec 2023 14:41:04 +0100
|
||||
Subject: [PATCH] docking: Only remove spacer if necessary
|
||||
|
||||
There may not be a main dock at the time when restoring the dash.
|
||||
|
||||
Handle that case by not removing a non-existent spacer, instead of
|
||||
triggering an error.
|
||||
---
|
||||
extensions/dash-to-dock/docking.js | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/dash-to-dock/docking.js b/extensions/dash-to-dock/docking.js
|
||||
index daa9de59..14e2ced6 100644
|
||||
--- a/extensions/dash-to-dock/docking.js
|
||||
+++ b/extensions/dash-to-dock/docking.js
|
||||
@@ -1796,7 +1796,7 @@ var DockManager = class DashToDock_DockManager {
|
||||
|
||||
let overviewControls = Main.overview._overview._controls;
|
||||
Main.overview._overview._controls.layout_manager._dash = this._oldDash;
|
||||
- if (this.mainDock._dashSpacer) {
|
||||
+ if (this.mainDock?._dashSpacer) {
|
||||
Main.overview._overview._controls.remove_child(this.mainDock._dashSpacer);
|
||||
}
|
||||
|
||||
--
|
||||
2.43.0
|
||||
|
@ -0,0 +1,40 @@
|
||||
From c70a1fa37f68687b8c0a013d2328e6262f8419d0 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 9 Dec 2022 15:31:08 +0100
|
||||
Subject: [PATCH] gesture-inhibitor: Allow inhibiting workspace switch gesture
|
||||
|
||||
---
|
||||
extensions/gesture-inhibitor/extension.js | 1 +
|
||||
.../org.gnome.shell.extensions.gesture-inhibitor.gschema.xml | 4 ++++
|
||||
2 files changed, 5 insertions(+)
|
||||
|
||||
diff --git a/extensions/gesture-inhibitor/extension.js b/extensions/gesture-inhibitor/extension.js
|
||||
index fb8a6dc0..d103d5b8 100644
|
||||
--- a/extensions/gesture-inhibitor/extension.js
|
||||
+++ b/extensions/gesture-inhibitor/extension.js
|
||||
@@ -48,6 +48,7 @@ class Extension {
|
||||
{ setting: 'app-switch', action: this._appSwitch },
|
||||
{ setting: 'show-osk', action: this._showOsk },
|
||||
{ setting: 'unfullscreen', action: this._unfullscreen },
|
||||
+ { setting: 'workspace-switch', action: Main.wm._workspaceAnimation._swipeTracker },
|
||||
];
|
||||
|
||||
this._enabledDesc = Object.getOwnPropertyDescriptor(
|
||||
diff --git a/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
index 4bdf9260..b06d027a 100644
|
||||
--- a/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
+++ b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
@@ -12,6 +12,10 @@
|
||||
<default>true</default>
|
||||
<summary>Application switch gesture</summary>
|
||||
</key>
|
||||
+ <key name="workspace-switch" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Workspace switch gesture</summary>
|
||||
+ </key>
|
||||
<key name="unfullscreen" type="b">
|
||||
<default>true</default>
|
||||
<summary>Unfullscreen gesture</summary>
|
||||
--
|
||||
2.38.1
|
||||
|
@ -0,0 +1,27 @@
|
||||
From f0e4618bf0752aaf094d78b4c810ebda817ccaad Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 2 Jun 2021 17:32:21 +0200
|
||||
Subject: [PATCH] top-icons: Don't use wm_class as role
|
||||
|
||||
This prevents adding icons for multiple instances of the same app,
|
||||
which may be desirable in some circumstances.
|
||||
---
|
||||
extensions/top-icons/extension.js | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/top-icons/extension.js b/extensions/top-icons/extension.js
|
||||
index 79e2f42..3dfba46 100644
|
||||
--- a/extensions/top-icons/extension.js
|
||||
+++ b/extensions/top-icons/extension.js
|
||||
@@ -63,7 +63,7 @@ class SysTray {
|
||||
button.destroy();
|
||||
});
|
||||
|
||||
- let role = wmClass || `${icon}`;
|
||||
+ const role = `${icon}`;
|
||||
Main.panel.addToStatusArea(role, button);
|
||||
}
|
||||
|
||||
--
|
||||
2.32.0
|
||||
|
@ -0,0 +1,46 @@
|
||||
From a31f4b6ca703faab25c306dc33056763642a83cb Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 30 Sep 2022 18:16:16 +0200
|
||||
Subject: [PATCH] window-list: Explicitly dispose settings on destroy
|
||||
|
||||
This will not only disconnect the signal handler, but also remove
|
||||
any bindings. This works around a crash that happens if a setting
|
||||
that triggers the binding changes at the same time as a setting
|
||||
that rebuilds the window list; in that case, the binding handler
|
||||
runs after gjs has dropped its wrapper object, but before the
|
||||
binding is removed automaticalled when the object is finalized.
|
||||
|
||||
https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/issues/416
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/243>
|
||||
---
|
||||
extensions/window-list/extension.js | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
||||
index 89413818..91ee3e6b 100644
|
||||
--- a/extensions/window-list/extension.js
|
||||
+++ b/extensions/window-list/extension.js
|
||||
@@ -842,8 +842,8 @@ class WindowList extends St.Widget {
|
||||
this._dndWindow = null;
|
||||
|
||||
this._settings = ExtensionUtils.getSettings();
|
||||
- this._groupingModeChangedId = this._settings.connect(
|
||||
- 'changed::grouping-mode', this._groupingModeChanged.bind(this));
|
||||
+ this._settings.connect('changed::grouping-mode',
|
||||
+ () => this._groupingModeChanged());
|
||||
this._grouped = undefined;
|
||||
this._groupingModeChanged();
|
||||
}
|
||||
@@ -1112,7 +1112,7 @@ class WindowList extends St.Widget {
|
||||
Main.xdndHandler.disconnect(this._dragBeginId);
|
||||
Main.xdndHandler.disconnect(this._dragEndId);
|
||||
|
||||
- this._settings.disconnect(this._groupingModeChangedId);
|
||||
+ this._settings.run_dispose();
|
||||
|
||||
let windows = global.get_window_actors();
|
||||
for (let i = 0; i < windows.length; i++)
|
||||
--
|
||||
2.39.1
|
||||
|
@ -1,51 +0,0 @@
|
||||
From 97d71d4a7ef4b1d4c9c2eab55db62173311f5366 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 2 Jul 2024 19:04:10 +0200
|
||||
Subject: [PATCH] workspace-indicator: Re-fittsify workspace previews
|
||||
|
||||
For the window-list extension, it is important that the workspace
|
||||
previews extend to the bottom edge for easier click targets.
|
||||
|
||||
That broke while merging the code with the workspace-indicator,
|
||||
fix it again by moving the padding from the parent box into the
|
||||
thumbnail children.
|
||||
---
|
||||
.../workspace-indicator/stylesheet-dark.css | 15 ++++++++++++++-
|
||||
1 file changed, 14 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/stylesheet-dark.css b/extensions/workspace-indicator/stylesheet-dark.css
|
||||
index b4a716b8..3c57c3e6 100644
|
||||
--- a/extensions/workspace-indicator/stylesheet-dark.css
|
||||
+++ b/extensions/workspace-indicator/stylesheet-dark.css
|
||||
@@ -18,7 +18,6 @@
|
||||
}
|
||||
|
||||
.workspace-indicator .workspaces-box {
|
||||
- padding: 5px;
|
||||
spacing: 3px;
|
||||
}
|
||||
|
||||
@@ -27,6 +26,20 @@
|
||||
spacing: 6px;
|
||||
}
|
||||
|
||||
+.workspace-indicator .workspace-box {
|
||||
+ padding-top: 5px;
|
||||
+ padding-bottom: 5px;
|
||||
+}
|
||||
+
|
||||
+.workspace-indicator StButton:first-child:ltr > .workspace-box,
|
||||
+.workspace-indicator StButton:last-child:rtl > .workspace-box {
|
||||
+ padding-left: 5px;
|
||||
+}
|
||||
+.workspace-indicator StButton:last-child:ltr > .workspace-box,
|
||||
+.workspace-indicator StButton:first-child:rtl > .workspace-box {
|
||||
+ padding-right: 5px;
|
||||
+}
|
||||
+
|
||||
.workspace-indicator-menu .workspace-box {
|
||||
spacing: 6px;
|
||||
}
|
||||
--
|
||||
2.45.2
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,231 @@
|
||||
From afa394114c57197e96f18e7942729634ece5d3c4 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 19 May 2021 16:46:59 +0200
|
||||
Subject: [PATCH 1/2] desktop-icons: Revert "Use GTK-Theme CSS for selected
|
||||
file-item and rubberband"
|
||||
|
||||
GtkStyleContext requires a (X11) display connection, which may not
|
||||
be available with xwayland-on-demand. Better use some hardcoded
|
||||
colors than crashing the session.
|
||||
|
||||
This reverts commit 8dc524aa4efd6a3fbad67480bd6c904b0c0c99d6.
|
||||
---
|
||||
extensions/desktop-icons/desktopIconsUtil.js | 11 ----------
|
||||
extensions/desktop-icons/desktopManager.js | 12 -----------
|
||||
extensions/desktop-icons/fileItem.js | 22 ++++----------------
|
||||
extensions/desktop-icons/stylesheet.css | 9 ++++++++
|
||||
4 files changed, 13 insertions(+), 41 deletions(-)
|
||||
|
||||
diff --git a/extensions/desktop-icons/desktopIconsUtil.js b/extensions/desktop-icons/desktopIconsUtil.js
|
||||
index 696c945..57bedc1 100644
|
||||
--- a/extensions/desktop-icons/desktopIconsUtil.js
|
||||
+++ b/extensions/desktop-icons/desktopIconsUtil.js
|
||||
@@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
-const Gtk = imports.gi.Gtk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
@@ -114,16 +113,6 @@ function getFileExtensionOffset(filename, isDirectory) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
-function getGtkClassBackgroundColor(classname, state) {
|
||||
- let widget = new Gtk.WidgetPath();
|
||||
- widget.append_type(Gtk.Widget);
|
||||
-
|
||||
- let context = new Gtk.StyleContext();
|
||||
- context.set_path(widget);
|
||||
- context.add_class(classname);
|
||||
- return context.get_background_color(state);
|
||||
-}
|
||||
-
|
||||
// Reference the extension org.gnome.shell.extensions.drive-menu
|
||||
function eject(mount) {
|
||||
let unmountArgs = [
|
||||
diff --git a/extensions/desktop-icons/desktopManager.js b/extensions/desktop-icons/desktopManager.js
|
||||
index 1aad8c6..10e3ce0 100644
|
||||
--- a/extensions/desktop-icons/desktopManager.js
|
||||
+++ b/extensions/desktop-icons/desktopManager.js
|
||||
@@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
-const Gtk = imports.gi.Gtk;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gio = imports.gi.Gio;
|
||||
@@ -132,7 +131,6 @@ var DesktopManager = GObject.registerClass({
|
||||
startRubberBand(x, y) {
|
||||
this._rubberBandInitialX = x;
|
||||
this._rubberBandInitialY = y;
|
||||
- this._initRubberBandColor();
|
||||
this._updateRubberBand(x, y);
|
||||
this._rubberBand.show();
|
||||
this._grabHelper.grab({ actor: global.stage });
|
||||
@@ -235,16 +233,6 @@ var DesktopManager = GObject.registerClass({
|
||||
this._desktopGrids = {};
|
||||
}
|
||||
|
||||
- /**
|
||||
- * Initialize rubberband color from the GTK rubberband class
|
||||
- * */
|
||||
- _initRubberBandColor() {
|
||||
- let rgba = DesktopIconsUtil.getGtkClassBackgroundColor('rubberband', Gtk.StateFlags.NORMAL);
|
||||
- let background_color =
|
||||
- 'rgba(' + rgba.red * 255 + ', ' + rgba.green * 255 + ', ' + rgba.blue * 255 + ', 0.4)';
|
||||
- this._rubberBand.set_style('background-color: ' + background_color);
|
||||
- }
|
||||
-
|
||||
async _scanFiles() {
|
||||
for (let [fileItem, fileItemHandler] of this._fileItemHandlers)
|
||||
Object.values(fileItemHandler).forEach(id => fileItem.disconnect(id));
|
||||
diff --git a/extensions/desktop-icons/fileItem.js b/extensions/desktop-icons/fileItem.js
|
||||
index 9987e7f..1e8ea89 100644
|
||||
--- a/extensions/desktop-icons/fileItem.js
|
||||
+++ b/extensions/desktop-icons/fileItem.js
|
||||
@@ -16,7 +16,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
-const Gtk = imports.gi.Gtk;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
@@ -649,17 +648,6 @@ var FileItem = GObject.registerClass({
|
||||
DBusUtils.openFileWithOtherApplication(this.file.get_path());
|
||||
}
|
||||
|
||||
- _getSelectionStyle() {
|
||||
- let rgba = DesktopIconsUtil.getGtkClassBackgroundColor('view', Gtk.StateFlags.SELECTED);
|
||||
- let background_color =
|
||||
- 'rgba(' + rgba.red * 255 + ', ' + rgba.green * 255 + ', ' + rgba.blue * 255 + ', 0.6)';
|
||||
- let border_color =
|
||||
- 'rgba(' + rgba.red * 255 + ', ' + rgba.green * 255 + ', ' + rgba.blue * 255 + ', 0.8)';
|
||||
-
|
||||
- return 'background-color: ' + background_color + ';' +
|
||||
- 'border-color: ' + border_color + ';';
|
||||
- }
|
||||
-
|
||||
get menu() {
|
||||
return this._menu;
|
||||
}
|
||||
@@ -901,12 +889,10 @@ var FileItem = GObject.registerClass({
|
||||
if (isSelected == this._isSelected)
|
||||
return;
|
||||
|
||||
- if (isSelected) {
|
||||
- this._container.set_style(this._getSelectionStyle());
|
||||
- } else {
|
||||
- this._container.set_style('background-color: transparent');
|
||||
- this._container.set_style('border-color: transparent');
|
||||
- }
|
||||
+ if (isSelected)
|
||||
+ this._container.add_style_pseudo_class('selected');
|
||||
+ else
|
||||
+ this._container.remove_style_pseudo_class('selected');
|
||||
|
||||
this._isSelected = isSelected;
|
||||
}
|
||||
diff --git a/extensions/desktop-icons/stylesheet.css b/extensions/desktop-icons/stylesheet.css
|
||||
index 61b4ce8..4fd31c3 100644
|
||||
--- a/extensions/desktop-icons/stylesheet.css
|
||||
+++ b/extensions/desktop-icons/stylesheet.css
|
||||
@@ -8,6 +8,15 @@
|
||||
background-color: rgba(238, 238, 238, 0.2);
|
||||
}
|
||||
|
||||
+.file-item:selected {
|
||||
+ background-color: rgba(74, 144, 217, 0.6);
|
||||
+ border-color: rgba(74, 144, 217, 0.8);
|
||||
+}
|
||||
+
|
||||
+.rubber-band {
|
||||
+ background-color: rgba(74, 144, 238, 0.4);
|
||||
+}
|
||||
+
|
||||
.name-label {
|
||||
text-shadow: 1px 1px black;
|
||||
color: white;
|
||||
--
|
||||
2.32.0
|
||||
|
||||
|
||||
From ca050d098240b3e757f172d2012f7d1b91db3ff6 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 21 May 2021 00:50:52 +0200
|
||||
Subject: [PATCH 2/2] desktop-icons: Port prefs to GTK4
|
||||
|
||||
... for compatibility with GNOME 40.
|
||||
---
|
||||
extensions/desktop-icons/prefs.js | 32 ++++++++++++++++++-------------
|
||||
1 file changed, 19 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/extensions/desktop-icons/prefs.js b/extensions/desktop-icons/prefs.js
|
||||
index 890bcdb..c390aa8 100644
|
||||
--- a/extensions/desktop-icons/prefs.js
|
||||
+++ b/extensions/desktop-icons/prefs.js
|
||||
@@ -98,23 +98,29 @@ function get_schema(schema) {
|
||||
|
||||
function buildPrefsWidget() {
|
||||
initTranslations();
|
||||
- let frame = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, border_width: 10, spacing: 10 });
|
||||
-
|
||||
- frame.add(buildSelector('icon-size', _("Size for the desktop icons"), { 'small': _("Small"), 'standard': _("Standard"), 'large': _("Large") }));
|
||||
- frame.add(buildSwitcher('show-home', _("Show the personal folder in the desktop")));
|
||||
- frame.add(buildSwitcher('show-trash', _("Show the trash icon in the desktop")));
|
||||
- frame.add(buildSwitcher('show-mount', _("Show mounted drives in the desktop")));
|
||||
- frame.show_all();
|
||||
+ let frame = new Gtk.Box({
|
||||
+ orientation: Gtk.Orientation.VERTICAL,
|
||||
+ margin_top: 10,
|
||||
+ margin_bottom: 10,
|
||||
+ margin_start: 10,
|
||||
+ margin_end: 10,
|
||||
+ spacing: 10,
|
||||
+ });
|
||||
+
|
||||
+ frame.append(buildSelector('icon-size', _("Size for the desktop icons"), { 'small': _("Small"), 'standard': _("Standard"), 'large': _("Large") }));
|
||||
+ frame.append(buildSwitcher('show-home', _("Show the personal folder in the desktop")));
|
||||
+ frame.append(buildSwitcher('show-trash', _("Show the trash icon in the desktop")));
|
||||
+ frame.append(buildSwitcher('show-mount', _("Show mounted drives in the desktop")));
|
||||
return frame;
|
||||
}
|
||||
|
||||
function buildSwitcher(key, labelText) {
|
||||
let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10 });
|
||||
- let label = new Gtk.Label({ label: labelText, xalign: 0 });
|
||||
+ let label = new Gtk.Label({ label: labelText, xalign: 0, hexpand: true });
|
||||
let switcher = new Gtk.Switch({ active: settings.get_boolean(key) });
|
||||
settings.bind(key, switcher, 'active', 3);
|
||||
- hbox.pack_start(label, true, true, 0);
|
||||
- hbox.add(switcher);
|
||||
+ hbox.append(label);
|
||||
+ hbox.append(switcher);
|
||||
return hbox;
|
||||
}
|
||||
|
||||
@@ -131,15 +137,15 @@ function buildSelector(key, labelText, elements) {
|
||||
listStore.set (iter, [0, 1], [visibleText, val]);
|
||||
}
|
||||
let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10 });
|
||||
- let label = new Gtk.Label({ label: labelText, xalign: 0 });
|
||||
+ let label = new Gtk.Label({ label: labelText, xalign: 0, hexpand: true });
|
||||
let combo = new Gtk.ComboBox({model: listStore});
|
||||
let rendererText = new Gtk.CellRendererText();
|
||||
combo.pack_start (rendererText, false);
|
||||
combo.add_attribute (rendererText, 'text', 0);
|
||||
combo.set_id_column(1);
|
||||
settings.bind(key, combo, 'active-id', 3);
|
||||
- hbox.pack_start(label, true, true, 0);
|
||||
- hbox.add(combo);
|
||||
+ hbox.append(label);
|
||||
+ hbox.append(combo);
|
||||
return hbox;
|
||||
}
|
||||
|
||||
--
|
||||
2.32.0
|
||||
|
@ -1,158 +0,0 @@
|
||||
From 778e3f5ec9b8897af89af1919381a14e2e3494f6 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 20 May 2015 17:44:50 +0200
|
||||
Subject: [PATCH 1/5] Add top-icons extension
|
||||
|
||||
---
|
||||
extensions/top-icons/extension.js | 91 +++++++++++++++++++++++++++
|
||||
extensions/top-icons/meson.build | 9 +++
|
||||
extensions/top-icons/metadata.json.in | 10 +++
|
||||
meson.build | 1 +
|
||||
4 files changed, 111 insertions(+)
|
||||
create mode 100644 extensions/top-icons/extension.js
|
||||
create mode 100644 extensions/top-icons/meson.build
|
||||
create mode 100644 extensions/top-icons/metadata.json.in
|
||||
|
||||
diff --git a/extensions/top-icons/extension.js b/extensions/top-icons/extension.js
|
||||
new file mode 100644
|
||||
index 00000000..c28f1386
|
||||
--- /dev/null
|
||||
+++ b/extensions/top-icons/extension.js
|
||||
@@ -0,0 +1,91 @@
|
||||
+// SPDX-FileCopyrightText: 2018 Adel Gadllah <adel.gadllah@gmail.com>
|
||||
+// SPDX-FileCopyrightText: 2018 Florian Müllner <fmuellner@gnome.org>
|
||||
+//
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+import Clutter from 'gi://Clutter';
|
||||
+import Shell from 'gi://Shell';
|
||||
+import St from 'gi://St';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+import {Button as PanelButton} from 'resource:///org/gnome/shell/ui/panelMenu.js';
|
||||
+
|
||||
+const PANEL_ICON_SIZE = 16;
|
||||
+
|
||||
+const STANDARD_TRAY_ICON_IMPLEMENTATIONS = [
|
||||
+ 'bluetooth-applet',
|
||||
+ 'gnome-sound-applet',
|
||||
+ 'nm-applet',
|
||||
+ 'gnome-power-manager',
|
||||
+ 'keyboard',
|
||||
+ 'a11y-keyboard',
|
||||
+ 'kbd-scrolllock',
|
||||
+ 'kbd-numlock',
|
||||
+ 'kbd-capslock',
|
||||
+ 'ibus-ui-gtk',
|
||||
+];
|
||||
+
|
||||
+export default class SysTray {
|
||||
+ constructor() {
|
||||
+ this._icons = new Map();
|
||||
+ this._tray = null;
|
||||
+ }
|
||||
+
|
||||
+ _onTrayIconAdded(o, icon) {
|
||||
+ let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
|
||||
+ if (STANDARD_TRAY_ICON_IMPLEMENTATIONS.includes(wmClass))
|
||||
+ return;
|
||||
+
|
||||
+ let button = new PanelButton(0.5, null, true);
|
||||
+
|
||||
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
+ let iconSize = PANEL_ICON_SIZE * scaleFactor;
|
||||
+
|
||||
+ icon.set({
|
||||
+ width: iconSize,
|
||||
+ height: iconSize,
|
||||
+ x_align: Clutter.ActorAlign.CENTER,
|
||||
+ y_align: Clutter.ActorAlign.CENTER,
|
||||
+ });
|
||||
+
|
||||
+ let iconBin = new St.Widget({
|
||||
+ layout_manager: new Clutter.BinLayout(),
|
||||
+ style_class: 'system-status-icon',
|
||||
+ });
|
||||
+ iconBin.add_child(icon);
|
||||
+ button.add_child(iconBin);
|
||||
+
|
||||
+ this._icons.set(icon, button);
|
||||
+
|
||||
+ button.connect('button-release-event',
|
||||
+ (actor, event) => icon.click(event));
|
||||
+ button.connect('key-press-event',
|
||||
+ (actor, event) => icon.click(event));
|
||||
+
|
||||
+ const role = `${icon}`;
|
||||
+ Main.panel.addToStatusArea(role, button);
|
||||
+ }
|
||||
+
|
||||
+ _onTrayIconRemoved(o, icon) {
|
||||
+ const button = this._icons.get(icon);
|
||||
+ button?.destroy();
|
||||
+ this._icons.delete(icon);
|
||||
+ }
|
||||
+
|
||||
+ enable() {
|
||||
+ this._tray = new Shell.TrayManager();
|
||||
+ this._tray.connect('tray-icon-added',
|
||||
+ this._onTrayIconAdded.bind(this));
|
||||
+ this._tray.connect('tray-icon-removed',
|
||||
+ this._onTrayIconRemoved.bind(this));
|
||||
+ this._tray.manage_screen(Main.panel);
|
||||
+ }
|
||||
+
|
||||
+ disable() {
|
||||
+ this._icons.forEach(button => button.destroy());
|
||||
+ this._icons.clear();
|
||||
+
|
||||
+ this._tray.unmanage_screen();
|
||||
+ this._tray = null;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/top-icons/meson.build b/extensions/top-icons/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..b30272ad
|
||||
--- /dev/null
|
||||
+++ b/extensions/top-icons/meson.build
|
||||
@@ -0,0 +1,9 @@
|
||||
+# SPDX-FileCopyrightText: 2018 Florian Müllner <fmuellner@gnome.org>
|
||||
+#
|
||||
+# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+extension_data += configure_file(
|
||||
+ input: metadata_name + '.in',
|
||||
+ output: metadata_name,
|
||||
+ configuration: metadata_conf
|
||||
+)
|
||||
diff --git a/extensions/top-icons/metadata.json.in b/extensions/top-icons/metadata.json.in
|
||||
new file mode 100644
|
||||
index 00000000..1d2e0bc2
|
||||
--- /dev/null
|
||||
+++ b/extensions/top-icons/metadata.json.in
|
||||
@@ -0,0 +1,10 @@
|
||||
+{
|
||||
+"extension-id": "@extension_id@",
|
||||
+"uuid": "@uuid@",
|
||||
+"settings-schema": "@gschemaname@",
|
||||
+"gettext-domain": "@gettext_domain@",
|
||||
+"name": "Top Icons",
|
||||
+"description": "Show legacy tray icons on top",
|
||||
+"shell-version": [ "@shell_current@" ],
|
||||
+"url": "@url@"
|
||||
+}
|
||||
diff --git a/meson.build b/meson.build
|
||||
index 536efe49..a7294c18 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -43,6 +43,7 @@ default_extensions += [
|
||||
'light-style',
|
||||
'screenshot-window-sizer',
|
||||
'system-monitor',
|
||||
+ 'top-icons',
|
||||
'windowsNavigator',
|
||||
'workspace-indicator'
|
||||
]
|
||||
--
|
||||
2.45.2
|
||||
|
@ -1,181 +0,0 @@
|
||||
From ff5063cb006a3723f422017d44787cfd908bb147 Mon Sep 17 00:00:00 2001
|
||||
From: Carlos Garnacho <carlosg@gnome.org>
|
||||
Date: Thu, 28 Jan 2021 00:06:12 +0100
|
||||
Subject: [PATCH 2/5] Add gesture-inhibitor extension
|
||||
|
||||
This extension may disable default GNOME Shell gestures.
|
||||
---
|
||||
extensions/gesture-inhibitor/extension.js | 79 +++++++++++++++++++
|
||||
extensions/gesture-inhibitor/meson.build | 8 ++
|
||||
extensions/gesture-inhibitor/metadata.json.in | 12 +++
|
||||
...l.extensions.gesture-inhibitor.gschema.xml | 25 ++++++
|
||||
meson.build | 1 +
|
||||
5 files changed, 125 insertions(+)
|
||||
create mode 100644 extensions/gesture-inhibitor/extension.js
|
||||
create mode 100644 extensions/gesture-inhibitor/meson.build
|
||||
create mode 100644 extensions/gesture-inhibitor/metadata.json.in
|
||||
create mode 100644 extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
|
||||
diff --git a/extensions/gesture-inhibitor/extension.js b/extensions/gesture-inhibitor/extension.js
|
||||
new file mode 100644
|
||||
index 00000000..872020ba
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/extension.js
|
||||
@@ -0,0 +1,79 @@
|
||||
+// SPDX-FileCopyrightText: 2021 Carlos Garnacho <carlosg@gnome.org>
|
||||
+//
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+//
|
||||
+
|
||||
+import Clutter from 'gi://Clutter';
|
||||
+import Gio from 'gi://Gio';
|
||||
+import St from 'gi://St';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+
|
||||
+import {AppSwitchAction} from 'resource:///org/gnome/shell/ui/windowManager.js';
|
||||
+import {EdgeDragAction} from 'resource:///org/gnome/shell/ui/edgeDragAction.js';
|
||||
+
|
||||
+import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
+
|
||||
+export default class GestureInhibitorExtension extends Extension {
|
||||
+ constructor(metadata) {
|
||||
+ super(metadata);
|
||||
+
|
||||
+ let actions = global.stage.get_actions();
|
||||
+
|
||||
+ actions.forEach(a => {
|
||||
+ if (a instanceof AppSwitchAction)
|
||||
+ this._appSwitch = a;
|
||||
+ else if (a instanceof EdgeDragAction &&
|
||||
+ a._side === St.Side.BOTTOM)
|
||||
+ this._showOsk = a;
|
||||
+ else if (a instanceof EdgeDragAction &&
|
||||
+ a._side === St.Side.TOP)
|
||||
+ this._unfullscreen = a;
|
||||
+ });
|
||||
+
|
||||
+ this._map = [
|
||||
+ {setting: 'overview', action: Main.overview._swipeTracker},
|
||||
+ {setting: 'app-switch', action: this._appSwitch},
|
||||
+ {setting: 'show-osk', action: this._showOsk},
|
||||
+ {setting: 'unfullscreen', action: this._unfullscreen},
|
||||
+ {setting: 'workspace-switch', action: Main.wm._workspaceAnimation._swipeTracker},
|
||||
+ ];
|
||||
+
|
||||
+ this._enabledDesc = Object.getOwnPropertyDescriptor(
|
||||
+ Clutter.ActorMeta.prototype, 'enabled');
|
||||
+ }
|
||||
+
|
||||
+ _overrideEnabledSetter(obj, set) {
|
||||
+ if (!(obj instanceof Clutter.ActorMeta))
|
||||
+ return;
|
||||
+
|
||||
+ const desc = set
|
||||
+ ? {...this._enabledDesc, set}
|
||||
+ : {...this._enabledDesc};
|
||||
+ Object.defineProperty(obj, 'enabled', desc);
|
||||
+ }
|
||||
+
|
||||
+ enable() {
|
||||
+ const settings = this.getSettings();
|
||||
+
|
||||
+ this._map.forEach(m => {
|
||||
+ settings.bind(m.setting, m.action, 'enabled',
|
||||
+ Gio.SettingsBindFlags.DEFAULT);
|
||||
+
|
||||
+ this._overrideEnabledSetter(m.action, function (value) {
|
||||
+ if (settings.get_boolean(m.setting)) {
|
||||
+ // eslint-disable-next-line no-invalid-this
|
||||
+ this.set_enabled(value);
|
||||
+ }
|
||||
+ });
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ disable() {
|
||||
+ this._map.forEach(m => {
|
||||
+ Gio.Settings.unbind(m.action, 'enabled');
|
||||
+ this._overrideEnabledSetter(m.action);
|
||||
+ m.action.enabled = true;
|
||||
+ });
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/gesture-inhibitor/meson.build b/extensions/gesture-inhibitor/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..fdad5cc8
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/meson.build
|
||||
@@ -0,0 +1,8 @@
|
||||
+extension_data += configure_file(
|
||||
+ input: metadata_name + '.in',
|
||||
+ output: metadata_name,
|
||||
+ configuration: metadata_conf
|
||||
+)
|
||||
+
|
||||
+# extension_sources += files('prefs.js')
|
||||
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
diff --git a/extensions/gesture-inhibitor/metadata.json.in b/extensions/gesture-inhibitor/metadata.json.in
|
||||
new file mode 100644
|
||||
index 00000000..37d6a117
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/metadata.json.in
|
||||
@@ -0,0 +1,12 @@
|
||||
+{
|
||||
+ "uuid": "@uuid@",
|
||||
+ "extension-id": "@extension_id@",
|
||||
+ "settings-schema": "@gschemaname@",
|
||||
+ "gettext-domain": "@gettext_domain@",
|
||||
+ "name": "Gesture Inhibitor",
|
||||
+ "description": "Makes touchscreen gestures optional.",
|
||||
+ "shell-version": [ "@shell_current@" ],
|
||||
+ "original-authors": [ "cgarnach@redhat.com" ],
|
||||
+ "url": "@url@"
|
||||
+}
|
||||
+
|
||||
diff --git a/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
new file mode 100644
|
||||
index 00000000..b06d027a
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
@@ -0,0 +1,25 @@
|
||||
+<schemalist>
|
||||
+ <schema id="org.gnome.shell.extensions.gesture-inhibitor" path="/org/gnome/shell/extensions/gesture-inhibitor/">
|
||||
+ <key name="show-osk" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show OSK gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="overview" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show Overview gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="app-switch" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Application switch gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="workspace-switch" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Workspace switch gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="unfullscreen" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Unfullscreen gesture</summary>
|
||||
+ </key>
|
||||
+ </schema>
|
||||
+</schemalist>
|
||||
+
|
||||
diff --git a/meson.build b/meson.build
|
||||
index a7294c18..e36d948d 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -51,6 +51,7 @@ default_extensions += [
|
||||
all_extensions = default_extensions
|
||||
all_extensions += [
|
||||
'auto-move-windows',
|
||||
+ 'gesture-inhibitor',
|
||||
'native-window-placement',
|
||||
'user-theme'
|
||||
]
|
||||
--
|
||||
2.45.2
|
||||
|
@ -1,479 +0,0 @@
|
||||
From 950f0fded26d8664bce5410db4c280674147cdd8 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 2 Dec 2021 19:39:50 +0100
|
||||
Subject: [PATCH] Add classification-banner
|
||||
|
||||
---
|
||||
extensions/classification-banner/extension.js | 163 +++++++++++++++
|
||||
extensions/classification-banner/meson.build | 9 +
|
||||
.../classification-banner/metadata.json.in | 11 +
|
||||
...tensions.classification-banner.gschema.xml | 29 +++
|
||||
extensions/classification-banner/prefs.js | 192 ++++++++++++++++++
|
||||
.../classification-banner/stylesheet.css | 3 +
|
||||
meson.build | 1 +
|
||||
7 files changed, 408 insertions(+)
|
||||
create mode 100644 extensions/classification-banner/extension.js
|
||||
create mode 100644 extensions/classification-banner/meson.build
|
||||
create mode 100644 extensions/classification-banner/metadata.json.in
|
||||
create mode 100644 extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml
|
||||
create mode 100644 extensions/classification-banner/prefs.js
|
||||
create mode 100644 extensions/classification-banner/stylesheet.css
|
||||
|
||||
diff --git a/extensions/classification-banner/extension.js b/extensions/classification-banner/extension.js
|
||||
new file mode 100644
|
||||
index 00000000..32c7d794
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/extension.js
|
||||
@@ -0,0 +1,163 @@
|
||||
+// SPDX-FileCopyrightText: 2021 Florian Müllner <fmuellner@gnome.org>
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+import Clutter from 'gi://Clutter';
|
||||
+import Cogl from 'gi://Cogl';
|
||||
+import Gio from 'gi://Gio';
|
||||
+import GLib from 'gi://GLib';
|
||||
+import GObject from 'gi://GObject';
|
||||
+import Shell from 'gi://Shell';
|
||||
+import St from 'gi://St';
|
||||
+
|
||||
+import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+import {MonitorConstraint} from 'resource:///org/gnome/shell/ui/layout.js';
|
||||
+
|
||||
+class ClassificationBanner extends Clutter.Actor {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ #topBanner;
|
||||
+ #bottomBanner;
|
||||
+ #monitorConstraint;
|
||||
+ #settings;
|
||||
+
|
||||
+ constructor(index, settings) {
|
||||
+ const constraint = new MonitorConstraint({index});
|
||||
+ super({
|
||||
+ layout_manager: new Clutter.BinLayout(),
|
||||
+ constraints: constraint,
|
||||
+ });
|
||||
+ this.#monitorConstraint = constraint;
|
||||
+
|
||||
+ Shell.util_set_hidden_from_pick(this, true);
|
||||
+
|
||||
+ this.#settings = settings;
|
||||
+
|
||||
+ this.#topBanner = new St.BoxLayout({
|
||||
+ style_class: 'classification-banner',
|
||||
+ x_expand: true,
|
||||
+ y_expand: true,
|
||||
+ y_align: Clutter.ActorAlign.START,
|
||||
+ });
|
||||
+ this.add_child(this.#topBanner);
|
||||
+ this.#settings.bind('top-banner',
|
||||
+ this.#topBanner, 'visible',
|
||||
+ Gio.SettingsBindFlags.GET);
|
||||
+
|
||||
+ this.#bottomBanner = new St.BoxLayout({
|
||||
+ style_class: 'classification-banner',
|
||||
+ x_expand: true,
|
||||
+ y_expand: true,
|
||||
+ y_align: Clutter.ActorAlign.END,
|
||||
+ });
|
||||
+ this.add_child(this.#bottomBanner);
|
||||
+ this.#settings.bind('bottom-banner',
|
||||
+ this.#bottomBanner, 'visible',
|
||||
+ Gio.SettingsBindFlags.GET);
|
||||
+
|
||||
+ for (const banner of [this.#topBanner, this.#bottomBanner]) {
|
||||
+ const label = new St.Label({
|
||||
+ style_class: 'classification-message',
|
||||
+ x_align: Clutter.ActorAlign.CENTER,
|
||||
+ x_expand: true,
|
||||
+ });
|
||||
+ banner.add_child(label);
|
||||
+
|
||||
+ this.#settings.bind('message',
|
||||
+ label, 'text',
|
||||
+ Gio.SettingsBindFlags.GET);
|
||||
+ }
|
||||
+
|
||||
+ const hostLabel = new St.Label({
|
||||
+ style_class: 'classification-system-info',
|
||||
+ text: GLib.get_host_name(),
|
||||
+ });
|
||||
+ this.#topBanner.insert_child_at_index(hostLabel, 0);
|
||||
+ this.#settings.bind('system-info',
|
||||
+ hostLabel, 'visible',
|
||||
+ Gio.SettingsBindFlags.GET);
|
||||
+
|
||||
+ const userLabel = new St.Label({
|
||||
+ style_class: 'classification-system-info',
|
||||
+ text: GLib.get_user_name(),
|
||||
+ });
|
||||
+ this.#topBanner.add_child(userLabel);
|
||||
+ this.#settings.bind('system-info',
|
||||
+ userLabel, 'visible',
|
||||
+ Gio.SettingsBindFlags.GET);
|
||||
+
|
||||
+ global.display.connectObject('in-fullscreen-changed',
|
||||
+ () => this.#updateMonitorConstraint(), this);
|
||||
+ this.#updateMonitorConstraint();
|
||||
+
|
||||
+ this.#settings.connectObject(
|
||||
+ 'changed::color', () => this.#updateStyles(),
|
||||
+ 'changed::background-color', () => this.#updateStyles(),
|
||||
+ this);
|
||||
+ this.#updateStyles();
|
||||
+ }
|
||||
+
|
||||
+ #getColorSetting(key) {
|
||||
+ const str = this.#settings.get_string(key);
|
||||
+ const [valid, color] = Cogl.Color.from_string(str);
|
||||
+ if (!valid)
|
||||
+ return '';
|
||||
+ const {red, green, blue, alpha} = color;
|
||||
+ return `${key}: rgba(${red},${green},${blue},${alpha / 255});`;
|
||||
+ }
|
||||
+
|
||||
+ #updateMonitorConstraint() {
|
||||
+ const {index} = this.#monitorConstraint;
|
||||
+ this.#monitorConstraint.work_area =
|
||||
+ !global.display.get_monitor_in_fullscreen(index);
|
||||
+ }
|
||||
+
|
||||
+ #updateStyles() {
|
||||
+ const bgStyle = this.#getColorSetting('background-color');
|
||||
+ const fgStyle = this.#getColorSetting('color');
|
||||
+ const style = `${bgStyle}${fgStyle}`;
|
||||
+ this.#topBanner.set({style});
|
||||
+ this.#bottomBanner.set({style});
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+export default class ClassificationBannerExtension extends Extension {
|
||||
+ #banners = [];
|
||||
+
|
||||
+ #updateMonitors() {
|
||||
+ const {monitors, panelBox, primaryIndex} = Main.layoutManager;
|
||||
+ if (monitors.length !== this.#banners.length) {
|
||||
+ this.#clearBanners();
|
||||
+
|
||||
+ const settings = this.getSettings();
|
||||
+ for (let i = 0; i < monitors.length; i++) {
|
||||
+ const banner = new ClassificationBanner(i, settings);
|
||||
+ Main.uiGroup.add_child(banner);
|
||||
+ this.#banners.push(banner);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ const primaryBanner = this.#banners[primaryIndex];
|
||||
+ if (primaryBanner)
|
||||
+ Main.uiGroup.set_child_below_sibling(primaryBanner, panelBox);
|
||||
+ }
|
||||
+
|
||||
+ #clearBanners() {
|
||||
+ this.#banners.forEach(b => b.destroy());
|
||||
+ this.#banners = [];
|
||||
+ }
|
||||
+
|
||||
+ enable() {
|
||||
+ Main.layoutManager.connectObject('monitors-changed',
|
||||
+ () => this.#updateMonitors(), this);
|
||||
+ this.#updateMonitors();
|
||||
+ }
|
||||
+
|
||||
+ disable() {
|
||||
+ Main.layoutManager.disconnectObject(this);
|
||||
+ this.#clearBanners();
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/classification-banner/meson.build b/extensions/classification-banner/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..aa943741
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/meson.build
|
||||
@@ -0,0 +1,9 @@
|
||||
+extension_data += configure_file(
|
||||
+ input: metadata_name + '.in',
|
||||
+ output: metadata_name,
|
||||
+ configuration: metadata_conf
|
||||
+)
|
||||
+extension_data += files('stylesheet.css')
|
||||
+
|
||||
+extension_sources += files('prefs.js')
|
||||
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
diff --git a/extensions/classification-banner/metadata.json.in b/extensions/classification-banner/metadata.json.in
|
||||
new file mode 100644
|
||||
index 00000000..f93b1a2d
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/metadata.json.in
|
||||
@@ -0,0 +1,11 @@
|
||||
+{
|
||||
+"extension-id": "@extension_id@",
|
||||
+"uuid": "@uuid@",
|
||||
+"settings-schema": "@gschemaname@",
|
||||
+"gettext-domain": "@gettext_domain@",
|
||||
+"name": "Classification Banner",
|
||||
+"description": "Display classification level banner",
|
||||
+"shell-version": [ "@shell_current@" ],
|
||||
+"session-modes": [ "gdm", "unlock-dialog", "user" ],
|
||||
+"url": "@url@"
|
||||
+}
|
||||
diff --git a/extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml b/extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml
|
||||
new file mode 100644
|
||||
index 00000000..0314ef60
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml
|
||||
@@ -0,0 +1,29 @@
|
||||
+<schemalist gettext-domain="gnome-shell-extensions">
|
||||
+ <schema id="org.gnome.shell.extensions.classification-banner"
|
||||
+ path="/org/gnome/shell/extensions/classification-banner/">
|
||||
+ <key name="top-banner" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show a banner at the top</summary>
|
||||
+ </key>
|
||||
+ <key name="bottom-banner" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show a banner at the bottom</summary>
|
||||
+ </key>
|
||||
+ <key name="message" type="s">
|
||||
+ <default>"UNCLASSIFIED"</default>
|
||||
+ <summary>classification message</summary>
|
||||
+ </key>
|
||||
+ <key name="color" type="s">
|
||||
+ <default>"#fff"</default>
|
||||
+ <summary>text color</summary>
|
||||
+ </key>
|
||||
+ <key name="background-color" type="s">
|
||||
+ <default>"rgba(0,122,51,0.75)"</default>
|
||||
+ <summary>background color</summary>
|
||||
+ </key>
|
||||
+ <key name="system-info" type="b">
|
||||
+ <default>false</default>
|
||||
+ <summary>Include system info in top banner</summary>
|
||||
+ </key>
|
||||
+ </schema>
|
||||
+</schemalist>
|
||||
diff --git a/extensions/classification-banner/prefs.js b/extensions/classification-banner/prefs.js
|
||||
new file mode 100644
|
||||
index 00000000..dc73ddae
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/prefs.js
|
||||
@@ -0,0 +1,192 @@
|
||||
+import Adw from 'gi://Adw';
|
||||
+import Gdk from 'gi://Gdk';
|
||||
+import Gio from 'gi://Gio';
|
||||
+import GObject from 'gi://GObject';
|
||||
+import Gtk from 'gi://Gtk';
|
||||
+
|
||||
+import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
||||
+
|
||||
+class GenericPrefs extends Adw.PreferencesGroup {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ #actionGroup = new Gio.SimpleActionGroup();
|
||||
+ #settings;
|
||||
+
|
||||
+ constructor(settings) {
|
||||
+ super();
|
||||
+
|
||||
+ this.#settings = settings;
|
||||
+ this.insert_action_group('options', this.#actionGroup);
|
||||
+
|
||||
+ this.#actionGroup.add_action(settings.create_action('top-banner'));
|
||||
+ this.#actionGroup.add_action(settings.create_action('bottom-banner'));
|
||||
+ this.#actionGroup.add_action(settings.create_action('system-info'));
|
||||
+
|
||||
+ this.add(new Adw.SwitchRow({
|
||||
+ title: _('Top Banner'),
|
||||
+ action_name: 'options.top-banner',
|
||||
+ }));
|
||||
+
|
||||
+ this.add(new Adw.SwitchRow({
|
||||
+ title: _('Bottom Banner'),
|
||||
+ action_name: 'options.bottom-banner',
|
||||
+ }));
|
||||
+
|
||||
+ this.add(new Adw.SwitchRow({
|
||||
+ title: _('System Info'),
|
||||
+ action_name: 'options.system-info',
|
||||
+ }));
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class BannerPreset extends GObject.Object {
|
||||
+ static [GObject.properties] = {
|
||||
+ 'message': GObject.ParamSpec.string(
|
||||
+ 'message', 'message', 'message',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ null),
|
||||
+ 'color': GObject.ParamSpec.string(
|
||||
+ 'color', 'color', 'color',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ null),
|
||||
+ 'background-color': GObject.ParamSpec.string(
|
||||
+ 'background-color', 'background-color', 'background-color',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ null),
|
||||
+ };
|
||||
+
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class AppearancePrefs extends Adw.PreferencesGroup {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ #settings;
|
||||
+
|
||||
+ constructor(settings) {
|
||||
+ super();
|
||||
+
|
||||
+ this.#settings = settings;
|
||||
+
|
||||
+ const model = new Gio.ListStore({item_type: BannerPreset.$gtype});
|
||||
+ model.append(new BannerPreset({
|
||||
+ message: 'UNCLASSIFIED',
|
||||
+ color: '#fff',
|
||||
+ background_color: 'rgba(0, 122, 51, 0.75)',
|
||||
+ }));
|
||||
+ model.append(new BannerPreset({
|
||||
+ message: 'CONFIDENTIAL',
|
||||
+ color: '#fff',
|
||||
+ background_color: 'rgba(0, 51, 160, 0.75)',
|
||||
+ }));
|
||||
+ model.append(new BannerPreset({
|
||||
+ message: 'SECRET',
|
||||
+ color: '#fff',
|
||||
+ background_color: 'rgba(200, 16, 46, 0.75)',
|
||||
+ }));
|
||||
+ model.append(new BannerPreset({
|
||||
+ message: 'TOP SECRET',
|
||||
+ color: '#fff',
|
||||
+ background_color: 'rgba(255, 103, 31, 0.75)',
|
||||
+ }));
|
||||
+ model.append(new BannerPreset({
|
||||
+ message: 'TOP SECRET//SCI',
|
||||
+ color: '#000',
|
||||
+ background_color: 'rgba(247, 234, 72, 0.75)',
|
||||
+ }));
|
||||
+
|
||||
+ let row, activatableWidget;
|
||||
+ row = this.#createPresetsRow(model);
|
||||
+ row.connect('notify::selected-item', comboRow => {
|
||||
+ const {message, color, backgroundColor} = comboRow.selected_item;
|
||||
+ this.#settings.set_string('message', message);
|
||||
+ this.#settings.set_string('color', color);
|
||||
+ this.#settings.set_string('background-color', backgroundColor);
|
||||
+ });
|
||||
+ this.add(row);
|
||||
+
|
||||
+ activatableWidget = new Gtk.Entry({
|
||||
+ valign: Gtk.Align.CENTER,
|
||||
+ });
|
||||
+ this.#settings.bind('message',
|
||||
+ activatableWidget, 'text',
|
||||
+ Gio.SettingsBindFlags.DEFAULT);
|
||||
+ row = new Adw.ActionRow({title: _('Message'), activatableWidget});
|
||||
+ row.add_suffix(activatableWidget);
|
||||
+ this.add(row);
|
||||
+
|
||||
+ activatableWidget = this.#createColorButton('background-color', {
|
||||
+ use_alpha: true,
|
||||
+ });
|
||||
+ row = new Adw.ActionRow({title: _('Background color'), activatableWidget});
|
||||
+ row.add_suffix(activatableWidget);
|
||||
+ this.add(row);
|
||||
+
|
||||
+ activatableWidget = this.#createColorButton('color');
|
||||
+ row = new Adw.ActionRow({title: _('Text color'), activatableWidget});
|
||||
+ row.add_suffix(activatableWidget);
|
||||
+ this.add(row);
|
||||
+ }
|
||||
+
|
||||
+ #createPresetsRow(model) {
|
||||
+ const listFactory = new Gtk.SignalListItemFactory();
|
||||
+ listFactory.connect('setup',
|
||||
+ (f, item) => item.set_child(new Gtk.Label()));
|
||||
+ listFactory.connect('bind', (f, listItem) => {
|
||||
+ const {child, item} = listItem;
|
||||
+
|
||||
+ const provider = new Gtk.CssProvider();
|
||||
+ provider.load_from_data(`* {
|
||||
+ border-radius: 99px;
|
||||
+ padding: 6px;
|
||||
+ color: ${item.color};
|
||||
+ background-color: ${item.background_color};
|
||||
+ }`, -1);
|
||||
+ child.get_style_context().add_provider(provider,
|
||||
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
+ child.label = item.message;
|
||||
+ });
|
||||
+
|
||||
+ return new Adw.ComboRow({
|
||||
+ title: _('Presets'),
|
||||
+ model,
|
||||
+ listFactory,
|
||||
+ expression: Gtk.ConstantExpression.new_for_value(''),
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ #createColorButton(key, params = {}) {
|
||||
+ const rgba = new Gdk.RGBA();
|
||||
+ rgba.parse(this.#settings.get_string(key));
|
||||
+
|
||||
+ const button = new Gtk.ColorButton({
|
||||
+ ...params,
|
||||
+ rgba,
|
||||
+ valign: Gtk.Align.CENTER,
|
||||
+ });
|
||||
+ this.#settings.connect(`changed::${key}`, () => {
|
||||
+ const newRgba = new Gdk.RGBA();
|
||||
+ newRgba.parse(this.#settings.get_string(key));
|
||||
+ if (!newRgba.equal(button.rgba))
|
||||
+ button.set({rgba: newRgba});
|
||||
+ });
|
||||
+ button.connect('notify::rgba',
|
||||
+ () => this.#settings.set_string(key, button.rgba.to_string()));
|
||||
+ return button;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+export default class ClassificationPrefs extends ExtensionPreferences {
|
||||
+ getPreferencesWidget() {
|
||||
+ const page = new Adw.PreferencesPage();
|
||||
+ page.add(new AppearancePrefs(this.getSettings()));
|
||||
+ page.add(new GenericPrefs(this.getSettings()));
|
||||
+ return page;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/classification-banner/stylesheet.css b/extensions/classification-banner/stylesheet.css
|
||||
new file mode 100644
|
||||
index 00000000..fb6a697e
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/stylesheet.css
|
||||
@@ -0,0 +1,3 @@
|
||||
+.classification-system-info { padding: 0 24px; }
|
||||
+.classification-message { font-weight: bold; }
|
||||
+.classification-banner { font-size: 0.9em; }
|
||||
diff --git a/meson.build b/meson.build
|
||||
index e36d948d..63bd9ee0 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -51,6 +51,7 @@ default_extensions += [
|
||||
all_extensions = default_extensions
|
||||
all_extensions += [
|
||||
'auto-move-windows',
|
||||
+ 'classification-banner',
|
||||
'gesture-inhibitor',
|
||||
'native-window-placement',
|
||||
'user-theme'
|
||||
--
|
||||
2.45.2
|
||||
|
@ -1,719 +0,0 @@
|
||||
From b146a94c18e9e9ddbc7f29e8d885768d19fd7d49 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 12 Jan 2023 19:43:52 +0100
|
||||
Subject: [PATCH 5/5] Add custom-menu extension
|
||||
|
||||
---
|
||||
extensions/custom-menu/config.js | 445 ++++++++++++++++++++++++
|
||||
extensions/custom-menu/extension.js | 201 +++++++++++
|
||||
extensions/custom-menu/meson.build | 7 +
|
||||
extensions/custom-menu/metadata.json.in | 10 +
|
||||
meson.build | 1 +
|
||||
5 files changed, 664 insertions(+)
|
||||
create mode 100644 extensions/custom-menu/config.js
|
||||
create mode 100644 extensions/custom-menu/extension.js
|
||||
create mode 100644 extensions/custom-menu/meson.build
|
||||
create mode 100644 extensions/custom-menu/metadata.json.in
|
||||
|
||||
diff --git a/extensions/custom-menu/config.js b/extensions/custom-menu/config.js
|
||||
new file mode 100644
|
||||
index 00000000..d08e3201
|
||||
--- /dev/null
|
||||
+++ b/extensions/custom-menu/config.js
|
||||
@@ -0,0 +1,445 @@
|
||||
+import Gio from 'gi://Gio';
|
||||
+import GLib from 'gi://GLib';
|
||||
+import Json from 'gi://Json';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||||
+
|
||||
+import {getLogger} from './extension.js';
|
||||
+
|
||||
+class Entry {
|
||||
+ constructor(prop) {
|
||||
+ this.type = prop.type;
|
||||
+ this.title = prop.title || "";
|
||||
+ this.__vars = prop.__vars || [];
|
||||
+ this.updateEnv(prop);
|
||||
+ }
|
||||
+
|
||||
+ setTitle(text) {
|
||||
+ this.item.label.get_clutter_text().set_text(text);
|
||||
+ }
|
||||
+
|
||||
+ updateEnv(prop) {
|
||||
+ this.__env = {}
|
||||
+ if (!this.__vars) return;
|
||||
+ for (let i in this.__vars) {
|
||||
+ let v = this.__vars[i];
|
||||
+ this.__env[v] = prop[v] ? String(prop[v]) : "";
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // the pulse function should be read as "a pulse arrives"
|
||||
+ pulse() {
|
||||
+ }
|
||||
+
|
||||
+ _try_destroy() {
|
||||
+ try {
|
||||
+ if (this.item && this.item.destroy) {
|
||||
+ this.item.destroy();
|
||||
+ }
|
||||
+ } catch(e) { /* Ignore all errors during destory*/ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class DerivedEntry {
|
||||
+ constructor(prop) {
|
||||
+ if (!prop.base) {
|
||||
+ throw new Error("Base entry not specified in type definition.");
|
||||
+ }
|
||||
+ this.base = prop.base;
|
||||
+ this.vars = prop.vars || [];
|
||||
+ delete prop.base;
|
||||
+ delete prop.vars;
|
||||
+ this.prop = prop;
|
||||
+ }
|
||||
+
|
||||
+ createInstance(addit_prop) {
|
||||
+ let cls = type_map[this.base];
|
||||
+ if (!cls) {
|
||||
+ throw new Error("Bad base class.");
|
||||
+ }
|
||||
+ if (cls.createInstance) {
|
||||
+ throw new Error("Not allowed to derive from dervied types");
|
||||
+ }
|
||||
+ for (let rp in this.prop) {
|
||||
+ addit_prop[rp] = this.prop[rp];
|
||||
+ }
|
||||
+ addit_prop.__vars = this.vars;
|
||||
+ let instance = new cls(addit_prop);
|
||||
+ return instance;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+let __pipeOpenQueue = [];
|
||||
+
|
||||
+/* callback: function (stdout, stderr, exit_status) { } */
|
||||
+function pipeOpen(cmdline, env, callback) {
|
||||
+ if (cmdline === undefined || callback === undefined) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ realPipeOpen(cmdline, env, callback);
|
||||
+ return true;
|
||||
+} /**/
|
||||
+
|
||||
+function realPipeOpen(cmdline, env, callback) {
|
||||
+ let user_cb = callback;
|
||||
+ let proc;
|
||||
+
|
||||
+ function wait_cb(_, _res) {
|
||||
+ let stdout_pipe = proc.get_stdout_pipe();
|
||||
+ let stderr_pipe = proc.get_stderr_pipe();
|
||||
+ let stdout_content;
|
||||
+ let stderr_content;
|
||||
+
|
||||
+ // Only the first GLib.MAXINT16 characters are fetched for optimization.
|
||||
+ stdout_pipe.read_bytes_async(GLib.MAXINT16, 0, null, function(osrc, ores) {
|
||||
+ const decoder = new TextDecoder();
|
||||
+ stdout_content = decoder.decode(stdout_pipe.read_bytes_finish(ores).get_data());
|
||||
+ stdout_pipe.close(null);
|
||||
+ stderr_pipe.read_bytes_async(GLib.MAXINT16, 0, null, function(esrc, eres) {
|
||||
+ stderr_content = decoder.decode(stderr_pipe.read_bytes_finish(eres).get_data());
|
||||
+ stderr_pipe.close(null);
|
||||
+ user_cb(stdout_content, stderr_content, proc.get_exit_status());
|
||||
+ });
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ if (user_cb) {
|
||||
+ let _pipedLauncher = new Gio.SubprocessLauncher({
|
||||
+ flags:
|
||||
+ Gio.SubprocessFlags.STDERR_PIPE |
|
||||
+ Gio.SubprocessFlags.STDOUT_PIPE
|
||||
+ });
|
||||
+ for (let key in env) {
|
||||
+ _pipedLauncher.setenv(key, env[key], true);
|
||||
+ }
|
||||
+ proc = _pipedLauncher.spawnv(['bash', '-c', cmdline]);
|
||||
+ proc.wait_async(null, wait_cb);
|
||||
+ } else {
|
||||
+ // Detached launcher is used to spawn commands that we are not concerned about its result.
|
||||
+ let _detacLauncher = new Gio.SubprocessLauncher();
|
||||
+ for (let key in env) {
|
||||
+ _detacLauncher.setenv(key, env[key], true);
|
||||
+ }
|
||||
+ proc = _detacLauncher.spawnv(['bash', '-c', cmdline]);
|
||||
+ }
|
||||
+ getLogger().info("Spawned " + cmdline);
|
||||
+ return proc.get_identifier();
|
||||
+}
|
||||
+
|
||||
+function _generalSpawn(command, env, title) {
|
||||
+ title = title || "Process";
|
||||
+ pipeOpen(command, env, function(stdout, stderr, exit_status) {
|
||||
+ if (exit_status != 0) {
|
||||
+ log
|
||||
+ getLogger().warning(stderr);
|
||||
+ getLogger().notify("proc", title + " exited with status " + exit_status, stderr);
|
||||
+ }
|
||||
+ });
|
||||
+}
|
||||
+
|
||||
+// Detect menu toggle on startup
|
||||
+function _toggleDetect(command, env, object) {
|
||||
+ pipeOpen(command, env, function(stdout, stderr, exit_status) {
|
||||
+ if (exit_status == 0) {
|
||||
+ object.item.setToggleState(true);
|
||||
+ }
|
||||
+ });
|
||||
+} /**/
|
||||
+
|
||||
+function quoteShellArg(arg) {
|
||||
+ arg = arg.replace(/'/g, "'\"'\"'");
|
||||
+ return "'" + arg + "'";
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * This cache is used to reduce detector cost.
|
||||
+ * Each time creating an item, it check if the result of this detector is cached,
|
||||
+ * which prevent the togglers from running detector on each creation.
|
||||
+ * This is useful especially in search mode.
|
||||
+ */
|
||||
+let _toggler_state_cache = { };
|
||||
+
|
||||
+class TogglerEntry extends Entry {
|
||||
+ constructor(prop) {
|
||||
+ super(prop);
|
||||
+ this.command_on = prop.command_on || "";
|
||||
+ this.command_off = prop.command_off || "";
|
||||
+ this.detector = prop.detector || "";
|
||||
+ this.auto_on = prop.auto_on || false;
|
||||
+ this.notify_when = prop.notify_when || [];
|
||||
+ // if the switch is manually turned off, auto_on is disabled.
|
||||
+ this._manually_switched_off = false;
|
||||
+ this.pulse(); // load initial state
|
||||
+ }
|
||||
+
|
||||
+ createItem() {
|
||||
+ this._try_destroy();
|
||||
+ this.item = new PopupMenu.PopupSwitchMenuItem(this.title, false);
|
||||
+ this.item.label.get_clutter_text().set_use_markup(true);
|
||||
+ this.item.connect('toggled', this._onManuallyToggled.bind(this));
|
||||
+ this._loadState();
|
||||
+ _toggleDetect(this.detector, this.__env, this);
|
||||
+ return this.item;
|
||||
+ }
|
||||
+
|
||||
+ _onManuallyToggled(_, state) {
|
||||
+ // when switched on again, this flag will get cleared.
|
||||
+ this._manually_switched_off = !state;
|
||||
+ this._storeState(state);
|
||||
+ this._onToggled(state);
|
||||
+ }
|
||||
+
|
||||
+ _onToggled(state) {
|
||||
+ if (state) {
|
||||
+ _generalSpawn(this.command_on, this.__env, this.title);
|
||||
+ } else {
|
||||
+ _generalSpawn(this.command_off, this.__env, this.title);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _detect(callback) {
|
||||
+ // abort detecting if detector is an empty string
|
||||
+ if (!this.detector) {
|
||||
+ return;
|
||||
+ }
|
||||
+ pipeOpen(this.detector, this.__env, function(out) {
|
||||
+ out = String(out);
|
||||
+ callback(!Boolean(out.match(/^\s*$/)));
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ // compare the new state with cached state notify when state is different
|
||||
+ compareState(new_state) {
|
||||
+ let old_state = _toggler_state_cache[this.detector];
|
||||
+ if (old_state === undefined) return;
|
||||
+ if (old_state == new_state) return;
|
||||
+
|
||||
+ if (this.notify_when.indexOf(new_state ? "on" : "off") >= 0) {
|
||||
+ let not_str = this.title + (new_state ? " started." : " stopped.");
|
||||
+ if (!new_state && this.auto_on) {
|
||||
+ not_str += " Attempt to restart it now.";
|
||||
+ }
|
||||
+ getLogger().notify("state", not_str);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _storeState(state) {
|
||||
+ let hash = JSON.stringify({ env: this.__env, detector: this.detector });
|
||||
+ _toggler_state_cache[hash] = state;
|
||||
+ }
|
||||
+
|
||||
+ _loadState() {
|
||||
+ let hash = JSON.stringify({ env: this.__env, detector: this.detector });
|
||||
+ let state = _toggler_state_cache[hash];
|
||||
+ if (state !== undefined) {
|
||||
+ this.item.setToggleState(state); // doesn't emit 'toggled'
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pulse() {
|
||||
+ this._detect(state => {
|
||||
+ this.compareState(state);
|
||||
+ this._storeState(state);
|
||||
+ this._loadState();
|
||||
+ if (!state && !this._manually_switched_off && this.auto_on) {
|
||||
+ // do not call setToggleState here, because command_on may fail
|
||||
+ this._onToggled(this.item, true);
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ perform() {
|
||||
+ this.item.toggle();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class LauncherEntry extends Entry {
|
||||
+ constructor(prop) {
|
||||
+ super(prop);
|
||||
+ this.command = prop.command || "";
|
||||
+ }
|
||||
+
|
||||
+ createItem() {
|
||||
+ this._try_destroy();
|
||||
+ this.item = new PopupMenu.PopupMenuItem(this.title);
|
||||
+ this.item.label.get_clutter_text().set_use_markup(true);
|
||||
+ this.item.connect('activate', this._onClicked.bind(this));
|
||||
+ return this.item;
|
||||
+ }
|
||||
+
|
||||
+ _onClicked(_) {
|
||||
+ _generalSpawn(this.command, this.__env, this.title);
|
||||
+ }
|
||||
+
|
||||
+ perform() {
|
||||
+ this.item.emit('activate');
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class SubMenuEntry extends Entry {
|
||||
+ constructor(prop) {
|
||||
+ super(prop);
|
||||
+
|
||||
+ if (prop.entries == undefined) {
|
||||
+ throw new Error("Expected entries provided in submenu entry.");
|
||||
+ }
|
||||
+ this.entries = [];
|
||||
+ for (let i in prop.entries) {
|
||||
+ let entry_prop = prop.entries[i];
|
||||
+ let entry = createEntry(entry_prop);
|
||||
+ this.entries.push(entry);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ createItem() {
|
||||
+ this._try_destroy();
|
||||
+ this.item = new PopupMenu.PopupSubMenuMenuItem(this.title);
|
||||
+ this.item.label.get_clutter_text().set_use_markup(true);
|
||||
+ for (let i in this.entries) {
|
||||
+ let entry = this.entries[i];
|
||||
+ this.item.menu.addMenuItem(entry.createItem());
|
||||
+ }
|
||||
+ return this.item;
|
||||
+ }
|
||||
+
|
||||
+ pulse() {
|
||||
+ for (let i in this.entries) {
|
||||
+ let entry = this.entries[i];
|
||||
+ entry.pulse();
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class SeparatorEntry extends Entry {
|
||||
+ createItem() {
|
||||
+ this._try_destroy();
|
||||
+ this.item = new PopupMenu.PopupSeparatorMenuItem(this.title);
|
||||
+ this.item.label.get_clutter_text().set_use_markup(true);
|
||||
+ return this.item;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+let type_map = {};
|
||||
+
|
||||
+////////////////////////////////////////////////////////////////////////////////
|
||||
+// Config Loader loads config from JSON file.
|
||||
+
|
||||
+// convert Json Nodes (GLib based) to native javascript value.
|
||||
+function convertJson(node) {
|
||||
+ if (node.get_node_type() == Json.NodeType.VALUE) {
|
||||
+ return node.get_value();
|
||||
+ }
|
||||
+ if (node.get_node_type() == Json.NodeType.OBJECT) {
|
||||
+ let obj = {}
|
||||
+ node.get_object().foreach_member(function(_, k, v_n) {
|
||||
+ obj[k] = convertJson(v_n);
|
||||
+ });
|
||||
+ return obj;
|
||||
+ }
|
||||
+ if (node.get_node_type() == Json.NodeType.ARRAY) {
|
||||
+ let arr = []
|
||||
+ node.get_array().foreach_element(function(_, i, elem) {
|
||||
+ arr.push(convertJson(elem));
|
||||
+ });
|
||||
+ return arr;
|
||||
+ }
|
||||
+ return null;
|
||||
+}
|
||||
+
|
||||
+//
|
||||
+function createEntry(entry_prop) {
|
||||
+ if (!entry_prop.type) {
|
||||
+ throw new Error("No type specified in entry.");
|
||||
+ }
|
||||
+ let cls = type_map[entry_prop.type];
|
||||
+ if (!cls) {
|
||||
+ throw new Error("Incorrect type '" + entry_prop.type + "'");
|
||||
+ } else if (cls.createInstance) {
|
||||
+ return cls.createInstance(entry_prop);
|
||||
+ }
|
||||
+ return new cls(entry_prop);
|
||||
+}
|
||||
+
|
||||
+export class Loader {
|
||||
+ constructor(filename) {
|
||||
+ if (filename) {
|
||||
+ this.loadConfig(filename);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ loadConfig(filename) {
|
||||
+ // reset type_map everytime load the config
|
||||
+ type_map = {
|
||||
+ launcher: LauncherEntry,
|
||||
+ toggler: TogglerEntry,
|
||||
+ submenu: SubMenuEntry,
|
||||
+ separator: SeparatorEntry
|
||||
+ };
|
||||
+
|
||||
+ type_map.systemd = new DerivedEntry({
|
||||
+ base: 'toggler',
|
||||
+ vars: ['unit'],
|
||||
+ command_on: "pkexec systemctl start ${unit}",
|
||||
+ command_off: "pkexec systemctl stop ${unit}",
|
||||
+ detector: "systemctl status ${unit} | grep Active:\\\\s\\*activ[ei]",
|
||||
+ });
|
||||
+
|
||||
+ type_map.tmux = new DerivedEntry({
|
||||
+ base: 'toggler',
|
||||
+ vars: ['command', 'session'],
|
||||
+ command_on: 'tmux new -d -s ${session} bash -c "${command}"',
|
||||
+ command_off: 'tmux kill-session -t ${session}',
|
||||
+ detector: 'tmux has -t "${session}" 2>/dev/null && echo yes',
|
||||
+ });
|
||||
+
|
||||
+ /*
|
||||
+ * Refer to README file for detailed config file format.
|
||||
+ */
|
||||
+ this.entries = []; // CAUTION: remove all entries.
|
||||
+
|
||||
+ let config_parser = new Json.Parser();
|
||||
+ config_parser.load_from_file(filename);
|
||||
+ let conf = convertJson(config_parser.get_root());
|
||||
+ if (conf.entries == undefined) {
|
||||
+ throw new Error("Key 'entries' not found.");
|
||||
+ }
|
||||
+ if (conf.deftype) {
|
||||
+ for (let tname in conf.deftype) {
|
||||
+ if (type_map[tname]) {
|
||||
+ throw new Error("Type \""+tname+"\" duplicated.");
|
||||
+ }
|
||||
+ type_map[tname] = new DerivedEntry(conf.deftype[tname]);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for (let conf_i in conf.entries) {
|
||||
+ let entry_prop = conf.entries[conf_i];
|
||||
+ this.entries.push(createEntry(entry_prop));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ saveDefaultConfig(filename) {
|
||||
+ // Write default config
|
||||
+ const PERMISSIONS_MODE = 0o640;
|
||||
+ const jsonString = JSON.stringify({
|
||||
+ "_homepage_": "https://github.com/andreabenini/gnome-plugin.custom-menu-panel",
|
||||
+ "_examples_": "https://github.com/andreabenini/gnome-plugin.custom-menu-panel/tree/main/examples",
|
||||
+ "entries": [ {
|
||||
+ "type": "launcher",
|
||||
+ "title": "Edit menu",
|
||||
+ "command": "gedit $HOME/.entries.json"
|
||||
+ } ]
|
||||
+ }, null, 4);
|
||||
+ let fileConfig = Gio.File.new_for_path(filename);
|
||||
+ if (GLib.mkdir_with_parents(fileConfig.get_parent().get_path(), PERMISSIONS_MODE) === 0) {
|
||||
+ fileConfig.replace_contents(jsonString, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null);
|
||||
+ }
|
||||
+ // Try to load newly saved file
|
||||
+ try {
|
||||
+ this.loadConfig(filename);
|
||||
+ } catch(e) {
|
||||
+ Main.notify(_('Cannot create and load file: '+filename));
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/custom-menu/extension.js b/extensions/custom-menu/extension.js
|
||||
new file mode 100644
|
||||
index 00000000..9edbc548
|
||||
--- /dev/null
|
||||
+++ b/extensions/custom-menu/extension.js
|
||||
@@ -0,0 +1,201 @@
|
||||
+/* extension.js
|
||||
+ *
|
||||
+ * This program is free software: you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation, either version 3 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public License
|
||||
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
+ *
|
||||
+ * SPDX-License-Identifier: GPL-3.0-or-later
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * @author Ben
|
||||
+ * @see https://github.com/andreabenini/gnome-plugin.custom-menu-panel
|
||||
+ */
|
||||
+
|
||||
+const CONFIGURATION_FILE = '/.entries.json';
|
||||
+
|
||||
+import Gio from 'gi://Gio';
|
||||
+import GLib from 'gi://GLib';
|
||||
+import GObject from 'gi://GObject';
|
||||
+import St from 'gi://St';
|
||||
+
|
||||
+import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+import * as BackgroundMenu from 'resource:///org/gnome/shell/ui/backgroundMenu.js';
|
||||
+import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
|
||||
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||||
+
|
||||
+import * as Config from './config.js';
|
||||
+
|
||||
+const LOGGER_INFO = 0;
|
||||
+const LOGGER_WARNING = 1;
|
||||
+const LOGGER_ERROR = 2;
|
||||
+
|
||||
+const {BackgroundMenu: OriginalBackgroundMenu} = BackgroundMenu;
|
||||
+
|
||||
+
|
||||
+class CustomMenu extends PopupMenu.PopupMenu {
|
||||
+ constructor(sourceActor) {
|
||||
+ super(sourceActor, 0.0, St.Side.TOP, 0);
|
||||
+
|
||||
+ this._loadSetup();
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * LOAD Program settings from .entries.json file
|
||||
+ */
|
||||
+ _loadSetup() {
|
||||
+ this.removeAll();
|
||||
+ // Loading configuration from file
|
||||
+ this.configLoader = new Config.Loader();
|
||||
+ try {
|
||||
+ this.configLoader.loadConfig(GLib.get_home_dir() + CONFIGURATION_FILE); // $HOME/.entries.json
|
||||
+ } catch(e) {
|
||||
+ this.configLoader.saveDefaultConfig(GLib.get_home_dir() + CONFIGURATION_FILE); // create default entries
|
||||
+ }
|
||||
+ // Build the menu
|
||||
+ let i = 0;
|
||||
+ for (let i in this.configLoader.entries) {
|
||||
+ let item = this.configLoader.entries[i].createItem();
|
||||
+ this.addMenuItem(item);
|
||||
+ }
|
||||
+ } /**/
|
||||
+}
|
||||
+
|
||||
+class CustomBackgroundMenu extends CustomMenu {
|
||||
+ constructor(layoutManager) {
|
||||
+ super(layoutManager.dummyCursor);
|
||||
+
|
||||
+ this.actor.add_style_class_name('background-menu');
|
||||
+
|
||||
+ layoutManager.uiGroup.add_actor(this.actor);
|
||||
+ this.actor.hide();
|
||||
+
|
||||
+ this.connect('open-state-changed', (menu, open) => {
|
||||
+ if (open)
|
||||
+ this._updateMaxHeight();
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ _updateMaxHeight() {
|
||||
+ const monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
||||
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
|
||||
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
|
||||
+ const vMargins = this.actor.margin_top + this.actor.margin_bottom;
|
||||
+ const {y: offsetY} = this.sourceActor;
|
||||
+
|
||||
+ const maxHeight = Math.round((monitor.height - offsetY - vMargins) / scaleFactor);
|
||||
+ this.actor.style = `max-height: ${maxHeight}px;`;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+const Indicator = GObject.registerClass(
|
||||
+ class Indicator extends PanelMenu.Button {
|
||||
+ _init() {
|
||||
+ super._init(0.0, _('Custom Menu Panel Indicator'), true);
|
||||
+ this.add_child(new St.Icon({
|
||||
+ icon_name: 'view-list-bullet-symbolic',
|
||||
+ style_class: 'system-status-icon',
|
||||
+ }));
|
||||
+
|
||||
+ this.setMenu(new CustomMenu(this));
|
||||
+ }
|
||||
+ }
|
||||
+); /**/
|
||||
+
|
||||
+
|
||||
+class Logger {
|
||||
+ constructor(log_file) {
|
||||
+ this._log_file = log_file;
|
||||
+ // initailize log_backend
|
||||
+ if (!log_file) {
|
||||
+ this._initEmptyLog();
|
||||
+ } else if(log_file == "gnome-shell") {
|
||||
+ this._initGnomeLog();
|
||||
+ } else {
|
||||
+ this._initFileLog();
|
||||
+ }
|
||||
+ this.level = LOGGER_WARNING;
|
||||
+ this.info = function(t) {
|
||||
+ if (this.level <= LOGGER_INFO) {
|
||||
+ this.log(t);
|
||||
+ }
|
||||
+ };
|
||||
+ this.warning = function(t) {
|
||||
+ if (this.level <= LOGGER_WARNING) {
|
||||
+ this.log(t);
|
||||
+ }
|
||||
+ };
|
||||
+ this.error = function(t) {
|
||||
+ if (this.level <= LOGGER_ERROR) {
|
||||
+ this.log(t);
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ _initEmptyLog() {
|
||||
+ this.log = function(_) { };
|
||||
+ }
|
||||
+
|
||||
+ _initGnomeLog() {
|
||||
+ this.log = function(s) {
|
||||
+ global.log("custom-menu-panel> " + s);
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ _initFileLog() {
|
||||
+ this.log = function(s) {
|
||||
+ // all operations are synchronous: any needs to optimize?
|
||||
+ if (!this._output_file || !this._output_file.query_exists(null) || !this._fstream || this._fstream.is_closed()) {
|
||||
+ this._output_file = Gio.File.new_for_path(this._log_file);
|
||||
+ this._fstream = this._output_file.append_to(Gio.FileCreateFlags.NONE, null);
|
||||
+ if (!this._fstream instanceof Gio.FileIOStream) {
|
||||
+ this._initGnomeLog();
|
||||
+ this.log("IOError: Failed to append to " + this._log_file + " [Gio.IOErrorEnum:" + this._fstream + "]");
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ this._fstream.write(String(new Date())+" "+s+"\n", null);
|
||||
+ this._fstream.flush(null);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ notify(t, str, details) {
|
||||
+ this.ncond = this.ncond || ['proc', 'ext', 'state'];
|
||||
+ if (this.ncond.indexOf(t) < 0) {
|
||||
+ return;
|
||||
+ }
|
||||
+ Main.notify(str, details || "");
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// lazy-evaluation
|
||||
+let logger = null;
|
||||
+export function getLogger() {
|
||||
+ if (logger === null) {
|
||||
+ logger = new Logger("gnome-shell");
|
||||
+ }
|
||||
+ return logger;
|
||||
+} /**/
|
||||
+
|
||||
+export default class CustomMenuExtension extends Extension {
|
||||
+ enable() {
|
||||
+ BackgroundMenu.BackgroundMenu = CustomBackgroundMenu;
|
||||
+ Main.layoutManager._updateBackgrounds();
|
||||
+ }
|
||||
+
|
||||
+ disable() {
|
||||
+ BackgroundMenu.BackgroundMenu = OriginalBackgroundMenu;
|
||||
+ Main.layoutManager._updateBackgrounds();
|
||||
+ }
|
||||
+} /**/
|
||||
diff --git a/extensions/custom-menu/meson.build b/extensions/custom-menu/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..92450963
|
||||
--- /dev/null
|
||||
+++ b/extensions/custom-menu/meson.build
|
||||
@@ -0,0 +1,7 @@
|
||||
+extension_data += configure_file(
|
||||
+ input: metadata_name + '.in',
|
||||
+ output: metadata_name,
|
||||
+ configuration: metadata_conf
|
||||
+)
|
||||
+
|
||||
+extension_sources += files('config.js')
|
||||
diff --git a/extensions/custom-menu/metadata.json.in b/extensions/custom-menu/metadata.json.in
|
||||
new file mode 100644
|
||||
index 00000000..054f639b
|
||||
--- /dev/null
|
||||
+++ b/extensions/custom-menu/metadata.json.in
|
||||
@@ -0,0 +1,10 @@
|
||||
+{
|
||||
+"extension-id": "@extension_id@",
|
||||
+"uuid": "@uuid@",
|
||||
+"settings-schema": "@gschemaname@",
|
||||
+"gettext-domain": "@gettext_domain@",
|
||||
+"name": "Custom menu",
|
||||
+"description": "Quick custom menu for launching your favorite applications",
|
||||
+"shell-version": [ "@shell_current@" ],
|
||||
+"url": "@url@"
|
||||
+}
|
||||
diff --git a/meson.build b/meson.build
|
||||
index 82269ff5..dce1731c 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -53,6 +53,7 @@ all_extensions = default_extensions
|
||||
all_extensions += [
|
||||
'auto-move-windows',
|
||||
'classification-banner',
|
||||
+ 'custom-menu',
|
||||
'gesture-inhibitor',
|
||||
'native-window-placement',
|
||||
'user-theme'
|
||||
--
|
||||
2.45.2
|
||||
|
@ -1,72 +0,0 @@
|
||||
From d0f2273765ab61e55c5cf10e7283a545fcafa947 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 22 Aug 2024 13:24:20 +0200
|
||||
Subject: [PATCH] Add stub desktop-icons extension
|
||||
|
||||
---
|
||||
extensions/desktop-icons/extension.js | 5 +++++
|
||||
extensions/desktop-icons/meson.build | 9 +++++++++
|
||||
extensions/desktop-icons/metadata.json.in | 10 ++++++++++
|
||||
meson.build | 1 +
|
||||
4 files changed, 25 insertions(+)
|
||||
create mode 100644 extensions/desktop-icons/extension.js
|
||||
create mode 100644 extensions/desktop-icons/meson.build
|
||||
create mode 100644 extensions/desktop-icons/metadata.json.in
|
||||
|
||||
diff --git a/extensions/desktop-icons/extension.js b/extensions/desktop-icons/extension.js
|
||||
new file mode 100644
|
||||
index 00000000..bbc96ef2
|
||||
--- /dev/null
|
||||
+++ b/extensions/desktop-icons/extension.js
|
||||
@@ -0,0 +1,5 @@
|
||||
+// SPDX-FileCopyrightText: 2024 Florian Müllner <fmuellner@gnome.org>
|
||||
+//
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+export {Extension as default} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
diff --git a/extensions/desktop-icons/meson.build b/extensions/desktop-icons/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..7b28a2ef
|
||||
--- /dev/null
|
||||
+++ b/extensions/desktop-icons/meson.build
|
||||
@@ -0,0 +1,9 @@
|
||||
+# SPDX-FileCopyrightText: 2017 Florian Müllner <fmuellner@gnome.org>
|
||||
+#
|
||||
+# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+extension_data += configure_file(
|
||||
+ input: metadata_name + '.in',
|
||||
+ output: metadata_name,
|
||||
+ configuration: metadata_conf
|
||||
+)
|
||||
diff --git a/extensions/desktop-icons/metadata.json.in b/extensions/desktop-icons/metadata.json.in
|
||||
new file mode 100644
|
||||
index 00000000..78a55abb
|
||||
--- /dev/null
|
||||
+++ b/extensions/desktop-icons/metadata.json.in
|
||||
@@ -0,0 +1,10 @@
|
||||
+{
|
||||
+"extension-id": "@extension_id@",
|
||||
+"uuid": "@uuid@",
|
||||
+"settings-schema": "@gschemaname@",
|
||||
+"gettext-domain": "@gettext_domain@",
|
||||
+"name": "Desktop Icons",
|
||||
+"description": "Show icons on the desktop",
|
||||
+"shell-version": [ "@shell_current@" ],
|
||||
+"url": "@url@"
|
||||
+}
|
||||
diff --git a/meson.build b/meson.build
|
||||
index b915b68c..63a7432e 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -40,6 +40,7 @@ classic_extensions = [
|
||||
|
||||
default_extensions = classic_extensions
|
||||
default_extensions += [
|
||||
+ 'desktop-icons',
|
||||
'drive-menu',
|
||||
'heads-up-display',
|
||||
'light-style',
|
||||
--
|
||||
2.45.2
|
||||
|
@ -0,0 +1,158 @@
|
||||
From 1692d4a91d95fecd5642b0c92bc2c5b0dbcb4184 Mon Sep 17 00:00:00 2001
|
||||
From: Neal Gompa <ngompa@fedoraproject.org>
|
||||
Date: Fri, 29 Oct 2021 09:37:33 -0400
|
||||
Subject: [PATCH] classic: Install the session for Wayland and ship override
|
||||
sessions
|
||||
|
||||
The regular GNOME session ships with three options:
|
||||
|
||||
* GNOME
|
||||
* GNOME on Wayland (available when GDM starts in X11)
|
||||
* GNOME on Xorg (available when GDM starts in Wayland)
|
||||
|
||||
The main GNOME session is set up so it works to match how GDM starts,
|
||||
so GNOME is on Wayland if GDM is (or GNOME is on X11 if GDM is).
|
||||
|
||||
For GNOME Classic, we are missing this setup, so port this behavior
|
||||
over from the GNOME session setup.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/195>
|
||||
---
|
||||
data/gnome-classic-wayland.desktop.in | 8 ++++++
|
||||
data/gnome-classic-xorg.desktop.in | 8 ++++++
|
||||
data/meson.build | 40 +++++++++++++++++++++------
|
||||
meson.build | 5 ++++
|
||||
meson/session-post-install.py | 20 ++++++++++++++
|
||||
5 files changed, 72 insertions(+), 9 deletions(-)
|
||||
create mode 100644 data/gnome-classic-wayland.desktop.in
|
||||
create mode 100644 data/gnome-classic-xorg.desktop.in
|
||||
create mode 100755 meson/session-post-install.py
|
||||
|
||||
diff --git a/data/gnome-classic-wayland.desktop.in b/data/gnome-classic-wayland.desktop.in
|
||||
new file mode 100644
|
||||
index 00000000..7287c689
|
||||
--- /dev/null
|
||||
+++ b/data/gnome-classic-wayland.desktop.in
|
||||
@@ -0,0 +1,8 @@
|
||||
+[Desktop Entry]
|
||||
+Name=GNOME Classic on Wayland
|
||||
+Comment=This session logs you into GNOME Classic
|
||||
+Exec=env GNOME_SHELL_SESSION_MODE=classic gnome-session
|
||||
+TryExec=gnome-session
|
||||
+Type=Application
|
||||
+DesktopNames=GNOME-Classic;GNOME;
|
||||
+X-GDM-SessionRegisters=true
|
||||
diff --git a/data/gnome-classic-xorg.desktop.in b/data/gnome-classic-xorg.desktop.in
|
||||
new file mode 100644
|
||||
index 00000000..5fb338a1
|
||||
--- /dev/null
|
||||
+++ b/data/gnome-classic-xorg.desktop.in
|
||||
@@ -0,0 +1,8 @@
|
||||
+[Desktop Entry]
|
||||
+Name=GNOME Classic on Xorg
|
||||
+Comment=This session logs you into GNOME Classic
|
||||
+Exec=env GNOME_SHELL_SESSION_MODE=classic gnome-session
|
||||
+TryExec=gnome-session
|
||||
+Type=Application
|
||||
+DesktopNames=GNOME-Classic;GNOME;
|
||||
+X-GDM-SessionRegisters=true
|
||||
diff --git a/data/meson.build b/data/meson.build
|
||||
index 27f42872..47fe798e 100644
|
||||
--- a/data/meson.build
|
||||
+++ b/data/meson.build
|
||||
@@ -1,12 +1,34 @@
|
||||
-session_desktop = 'gnome-classic.desktop'
|
||||
-i18n.merge_file('',
|
||||
- input: session_desktop + '.in',
|
||||
- output: session_desktop,
|
||||
- po_dir: '../po',
|
||||
- install: true,
|
||||
- install_dir: xsessiondir,
|
||||
- type: 'desktop'
|
||||
-)
|
||||
+session_desktop_base = 'gnome-classic'
|
||||
+
|
||||
+session_desktops = [
|
||||
+ session_desktop_base,
|
||||
+ session_desktop_base + '-xorg',
|
||||
+ session_desktop_base + '-wayland',
|
||||
+]
|
||||
+
|
||||
+foreach name: session_desktops
|
||||
+ session_desktop = name + '.desktop'
|
||||
+ if name.endswith('-xorg')
|
||||
+ session_instdir = xsessiondir
|
||||
+ elif name.endswith('-wayland')
|
||||
+ session_instdir = wlsessiondir
|
||||
+ else
|
||||
+ # FIXME: The same target can not be copied into two directories.
|
||||
+ # There is a workaround in meson/session-post-install.py until proper
|
||||
+ # solution arises:
|
||||
+ # https://github.com/mesonbuild/meson/issues/2416
|
||||
+ session_instdir = xsessiondir
|
||||
+ #session_instdir = [ xesssiondir, wlsessiondir ]
|
||||
+ endif
|
||||
+ i18n.merge_file('',
|
||||
+ input: session_desktop + '.in',
|
||||
+ output: session_desktop,
|
||||
+ po_dir: '../po',
|
||||
+ install: true,
|
||||
+ install_dir: session_instdir,
|
||||
+ type: 'desktop'
|
||||
+ )
|
||||
+endforeach
|
||||
|
||||
classic_uuids = []
|
||||
foreach e : classic_extensions
|
||||
diff --git a/meson.build b/meson.build
|
||||
index dda3ddac..ea8a859d 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -20,6 +20,7 @@ themedir = join_paths(shelldir, 'theme')
|
||||
schemadir = join_paths(datadir, 'glib-2.0', 'schemas')
|
||||
sessiondir = join_paths(datadir, 'gnome-session', 'sessions')
|
||||
xsessiondir = join_paths(datadir, 'xsessions')
|
||||
+wlsessiondir = join_paths(datadir, 'wayland-sessions')
|
||||
|
||||
ver_arr = meson.project_version().split('.')
|
||||
shell_version = ver_arr[0]
|
||||
@@ -90,6 +91,10 @@ endforeach
|
||||
|
||||
if classic_mode_enabled
|
||||
subdir('data')
|
||||
+ meson.add_install_script(
|
||||
+ 'meson/session-post-install.py',
|
||||
+ join_paths(get_option('prefix'), datadir)
|
||||
+ )
|
||||
endif
|
||||
|
||||
subdir('extensions')
|
||||
diff --git a/meson/session-post-install.py b/meson/session-post-install.py
|
||||
new file mode 100755
|
||||
index 00000000..36abe5e4
|
||||
--- /dev/null
|
||||
+++ b/meson/session-post-install.py
|
||||
@@ -0,0 +1,20 @@
|
||||
+#!/usr/bin/env python3
|
||||
+
|
||||
+import os
|
||||
+import shutil
|
||||
+import sys
|
||||
+
|
||||
+if os.environ.get('DESTDIR'):
|
||||
+ install_root = os.environ.get('DESTDIR') + os.path.abspath(sys.argv[1])
|
||||
+else:
|
||||
+ install_root = sys.argv[1]
|
||||
+
|
||||
+# FIXME: Meson is unable to copy a generated target file:
|
||||
+# https://groups.google.com/forum/#!topic/mesonbuild/3iIoYPrN4P0
|
||||
+dst_dir = os.path.join(install_root, 'wayland-sessions')
|
||||
+if not os.path.exists(dst_dir):
|
||||
+ os.makedirs(dst_dir)
|
||||
+
|
||||
+src = os.path.join(install_root, 'xsessions', 'gnome-classic.desktop')
|
||||
+dst = os.path.join(dst_dir, 'gnome-classic.desktop')
|
||||
+shutil.copyfile(src, dst)
|
||||
--
|
||||
2.33.1
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,70 @@
|
||||
From 34d8854677513b016a08a04cdd9973b165146215 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 19 Mar 2024 13:16:50 +0100
|
||||
Subject: [PATCH 1/2] window-list: Use more appropriate fallback icon
|
||||
|
||||
'icon-missing' is not an actual icon name. It somewhat works
|
||||
because an invalid icon name will fallback to the correct
|
||||
'image-missing', however for apps the generic app icon is
|
||||
a better fallback.
|
||||
---
|
||||
extensions/window-list/extension.js | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
||||
index 91ee3e6b..1f112548 100644
|
||||
--- a/extensions/window-list/extension.js
|
||||
+++ b/extensions/window-list/extension.js
|
||||
@@ -188,7 +188,7 @@ class WindowTitle extends St.BoxLayout {
|
||||
this._icon.child = app.create_icon_texture(ICON_TEXTURE_SIZE);
|
||||
} else {
|
||||
this._icon.child = new St.Icon({
|
||||
- icon_name: 'icon-missing',
|
||||
+ icon_name: 'application-x-executable',
|
||||
icon_size: ICON_TEXTURE_SIZE,
|
||||
});
|
||||
}
|
||||
--
|
||||
2.44.0
|
||||
|
||||
|
||||
From 032cfb3593651c8344a59828a9c674f148329889 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 19 Mar 2024 14:07:12 +0100
|
||||
Subject: [PATCH 2/2] window-list: Override with window icon if available
|
||||
|
||||
---
|
||||
extensions/window-list/extension.js | 17 +++++++++++++++++
|
||||
1 file changed, 17 insertions(+)
|
||||
|
||||
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
||||
index 1f112548..0c28692d 100644
|
||||
--- a/extensions/window-list/extension.js
|
||||
+++ b/extensions/window-list/extension.js
|
||||
@@ -192,6 +192,23 @@ class WindowTitle extends St.BoxLayout {
|
||||
icon_size: ICON_TEXTURE_SIZE,
|
||||
});
|
||||
}
|
||||
+
|
||||
+ // Override with window icon if available
|
||||
+ if (this._hasWindowIcon()) {
|
||||
+ const textureCache = St.TextureCache.get_default();
|
||||
+ this._icon.child.gicon = textureCache.bind_cairo_surface_property(
|
||||
+ this._metaWindow, 'icon');
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _hasWindowIcon() {
|
||||
+ // HACK: GI cannot handle CairoSurface, so this
|
||||
+ // will throw if the icon property is null
|
||||
+ try {
|
||||
+ return this._metaWindow.icon !== null;
|
||||
+ } catch (e) {
|
||||
+ return true;
|
||||
+ }
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
--
|
||||
2.44.0
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,116 @@
|
||||
From 0d9210e9c19c1bd9535ffb75b4834c2ccd8db6c2 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 21 Apr 2022 16:34:50 +0200
|
||||
Subject: [PATCH 1/2] window-list: Fix primary button action on touch
|
||||
|
||||
If a click event was triggered via touch rather than a pointer
|
||||
device, the button parameter is 0 rather than a mouse button
|
||||
number.
|
||||
|
||||
Account for that to make sure that touch events are not misinterpreted
|
||||
as right clicks.
|
||||
|
||||
https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/issues/146
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/233>
|
||||
---
|
||||
extensions/window-list/extension.js | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
||||
index e122cf5f..43885378 100644
|
||||
--- a/extensions/window-list/extension.js
|
||||
+++ b/extensions/window-list/extension.js
|
||||
@@ -381,7 +381,7 @@ class WindowButton extends BaseButton {
|
||||
return;
|
||||
}
|
||||
|
||||
- if (button === 1)
|
||||
+ if (!button || button === 1)
|
||||
_minimizeOrActivateWindow(this.metaWindow);
|
||||
else
|
||||
_openMenu(this._contextMenu);
|
||||
@@ -623,7 +623,7 @@ class AppButton extends BaseButton {
|
||||
if (contextMenuWasOpen)
|
||||
this._contextMenu.close();
|
||||
|
||||
- if (button === 1) {
|
||||
+ if (!button || button === 1) {
|
||||
if (menuWasOpen)
|
||||
return;
|
||||
|
||||
--
|
||||
2.36.1
|
||||
|
||||
|
||||
From b080bb7ee88d0e5b35dc4a967d2e44eab7921b6f Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 5 May 2022 20:55:20 +0200
|
||||
Subject: [PATCH 2/2] window-list: Open menu on long press
|
||||
|
||||
Right-click isn't available on touch, so implement long-press as
|
||||
an alternative.
|
||||
|
||||
https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/issues/146
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/233>
|
||||
---
|
||||
extensions/window-list/extension.js | 42 +++++++++++++++++++++++++++++
|
||||
1 file changed, 42 insertions(+)
|
||||
|
||||
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
||||
index 43885378..3d1cd053 100644
|
||||
--- a/extensions/window-list/extension.js
|
||||
+++ b/extensions/window-list/extension.js
|
||||
@@ -266,6 +266,48 @@ const BaseButton = GObject.registerClass({
|
||||
this._updateVisibility();
|
||||
}
|
||||
|
||||
+ _setLongPressTimeout() {
|
||||
+ if (this._longPressTimeoutId)
|
||||
+ return;
|
||||
+
|
||||
+ const { longPressDuration } = Clutter.Settings.get_default();
|
||||
+ this._longPressTimeoutId =
|
||||
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, longPressDuration, () => {
|
||||
+ delete this._longPressTimeoutId;
|
||||
+
|
||||
+ if (this._canOpenPopupMenu() && !this._contextMenu.isOpen)
|
||||
+ _openMenu(this._contextMenu);
|
||||
+ return GLib.SOURCE_REMOVE;
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ _removeLongPressTimeout() {
|
||||
+ if (!this._longPressTimeoutId)
|
||||
+ return;
|
||||
+ GLib.source_remove(this._longPressTimeoutId);
|
||||
+ delete this._longPressTimeoutId;
|
||||
+ }
|
||||
+
|
||||
+ vfunc_button_press_event(buttonEvent) {
|
||||
+ if (buttonEvent.button === 1)
|
||||
+ this._setLongPressTimeout();
|
||||
+ return super.vfunc_button_press_event(buttonEvent);
|
||||
+ }
|
||||
+
|
||||
+ vfunc_button_release_event(buttonEvent) {
|
||||
+ this._removeLongPressTimeout();
|
||||
+
|
||||
+ return super.vfunc_button_release_event(buttonEvent);
|
||||
+ }
|
||||
+
|
||||
+ vfunc_touch_event(touchEvent) {
|
||||
+ if (touchEvent.type === Clutter.EventType.TOUCH_BEGIN)
|
||||
+ this._setLongPressTimeout();
|
||||
+ else if (touchEvent.type === Clutter.EventType.TOUCH_END)
|
||||
+ this._removeLongPressTimeout();
|
||||
+ return super.vfunc_touch_event(touchEvent);
|
||||
+ }
|
||||
+
|
||||
activate() {
|
||||
if (this.active)
|
||||
return;
|
||||
--
|
||||
2.36.1
|
||||
|
Loading…
Reference in new issue