import gnome-shell-extensions-3.32.1-33.el8
This commit is contained in:
parent
27629185ec
commit
ae47908807
@ -0,0 +1,68 @@
|
||||
From 3d32ab1848011a3a7af97255307b3541a7553b09 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 6c2fe007..1cf03b3f 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;
|
||||
+
|
||||
if (this._settings)
|
||||
this._settings.run_dispose();
|
||||
this._settings = null;
|
||||
@@ -95,6 +98,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',
|
||||
@@ -111,6 +119,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
|
||||
|
76
SOURCES/0001-desktop-icons-Don-t-use-blocking-IO.patch
Normal file
76
SOURCES/0001-desktop-icons-Don-t-use-blocking-IO.patch
Normal file
@ -0,0 +1,76 @@
|
||||
From 93e3e938b322433aff862bbc46f80c60ab7dc2ab 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 | 35 +++++++++++++++++-----
|
||||
1 file changed, 28 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/extensions/desktop-icons/desktopManager.js b/extensions/desktop-icons/desktopManager.js
|
||||
index 399aee03..2ce6eefb 100644
|
||||
--- a/extensions/desktop-icons/desktopManager.js
|
||||
+++ b/extensions/desktop-icons/desktopManager.js
|
||||
@@ -53,6 +53,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: {
|
||||
@@ -221,9 +236,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);
|
||||
}
|
||||
@@ -268,14 +281,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);
|
||||
--
|
||||
2.38.1
|
||||
|
31
SOURCES/0001-fileItem-Just-destroy-menus.patch
Normal file
31
SOURCES/0001-fileItem-Just-destroy-menus.patch
Normal file
@ -0,0 +1,31 @@
|
||||
From 506c6d69eaa5e056d9580a28e9c200586b0e1fb0 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 2 Dec 2022 15:20:40 +0100
|
||||
Subject: [PATCH] fileItem: Just destroy menus
|
||||
|
||||
The menu manager is smart enough to remove the menu automatically,
|
||||
and the actor will be destroyed alongside the menu. Not doing those
|
||||
actions explicitly allows the automatic handling to proceed without
|
||||
confusing the grab state.
|
||||
---
|
||||
extensions/desktop-icons/fileItem.js | 4 ----
|
||||
1 file changed, 4 deletions(-)
|
||||
|
||||
diff --git a/extensions/desktop-icons/fileItem.js b/extensions/desktop-icons/fileItem.js
|
||||
index 44a93352..f2f03440 100644
|
||||
--- a/extensions/desktop-icons/fileItem.js
|
||||
+++ b/extensions/desktop-icons/fileItem.js
|
||||
@@ -575,10 +575,6 @@ var FileItem = class {
|
||||
|
||||
_removeMenu() {
|
||||
if (this._menu != null) {
|
||||
- if (this._menuManager != null)
|
||||
- this._menuManager.removeMenu(this._menu);
|
||||
-
|
||||
- Main.layoutManager.uiGroup.remove_child(this._menu.actor);
|
||||
this._menu.destroy();
|
||||
this._menu = null;
|
||||
}
|
||||
--
|
||||
2.38.1
|
||||
|
147
SOURCES/0001-fileItem-Support-.desktop-files-of-type-Link.patch
Normal file
147
SOURCES/0001-fileItem-Support-.desktop-files-of-type-Link.patch
Normal file
@ -0,0 +1,147 @@
|
||||
From be4ab59a3f2bb9829dde390db3dd8868a08840eb Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 2 Dec 2022 19:28:54 +0100
|
||||
Subject: [PATCH] fileItem: Support .desktop files of type Link
|
||||
|
||||
Gio only has direct support for .desktop files of type Application.
|
||||
|
||||
However in the context of desktop icons (and file managers), shortcuts
|
||||
of URLs are useful as well, so add explicit support for .desktop files
|
||||
of type Link.
|
||||
---
|
||||
extensions/desktop-icons/fileItem.js | 71 +++++++++++++++++++++++-----
|
||||
1 file changed, 60 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/extensions/desktop-icons/fileItem.js b/extensions/desktop-icons/fileItem.js
|
||||
index f2f03440..1c9a1e55 100644
|
||||
--- a/extensions/desktop-icons/fileItem.js
|
||||
+++ b/extensions/desktop-icons/fileItem.js
|
||||
@@ -239,12 +239,32 @@ var FileItem = class {
|
||||
log(`desktop-icons: File ${this._displayName} is writable by others - will not allow launching`);
|
||||
|
||||
if (this._isDesktopFile) {
|
||||
- this._desktopFile = Gio.DesktopAppInfo.new_from_filename(this._file.get_path());
|
||||
- if (!this._desktopFile) {
|
||||
- log(`Couldn’t parse ${this._displayName} as a desktop file, will treat it as a regular file.`);
|
||||
+ try {
|
||||
+ const keyFile = new GLib.KeyFile();
|
||||
+ keyFile.load_from_file(this._file.get_path(), GLib.KeyFileFlags.NONE);
|
||||
+
|
||||
+ const type = keyFile.get_string(
|
||||
+ GLib.KEY_FILE_DESKTOP_GROUP, GLib.KEY_FILE_DESKTOP_KEY_TYPE);
|
||||
+ switch (type) {
|
||||
+ case GLib.KEY_FILE_DESKTOP_TYPE_APPLICATION:
|
||||
+ this._desktopFile = Gio.DesktopAppInfo.new_from_keyfile(keyFile);
|
||||
+ if (!this._desktopFile) {
|
||||
+ log(`Couldn’t parse ${this._displayName} as a desktop file, will treat it as a regular file.`);
|
||||
+ this._isValidDesktopFile = false;
|
||||
+ } else {
|
||||
+ this._isValidDesktopFile = true;
|
||||
+ }
|
||||
+ break;
|
||||
+ case GLib.KEY_FILE_DESKTOP_TYPE_LINK:
|
||||
+ const url = keyFile.get_string(
|
||||
+ GLib.KEY_FILE_DESKTOP_GROUP, GLib.KEY_FILE_DESKTOP_KEY_URL);
|
||||
+ if (url)
|
||||
+ this._linkFile = keyFile;
|
||||
+ default: // fall-through
|
||||
+ this._isValidDesktopFile = false;
|
||||
+ }
|
||||
+ } catch (e) {
|
||||
this._isValidDesktopFile = false;
|
||||
- } else {
|
||||
- this._isValidDesktopFile = true;
|
||||
}
|
||||
} else {
|
||||
this._isValidDesktopFile = false;
|
||||
@@ -356,8 +376,17 @@ var FileItem = class {
|
||||
if (this._isBrokenSymlink) {
|
||||
this._icon.child = this._createEmblemedStIcon(null, 'text-x-generic');
|
||||
} else {
|
||||
- if (this.trustedDesktopFile && this._desktopFile.has_key('Icon'))
|
||||
- this._icon.child = this._createEmblemedStIcon(null, this._desktopFile.get_string('Icon'));
|
||||
+ let iconName = null;
|
||||
+
|
||||
+ try {
|
||||
+ if (this.trustedDesktopFile)
|
||||
+ iconName = this._desktopFile.get_string('Icon');
|
||||
+ else if (this._linkFile)
|
||||
+ iconName = this._linkFile.get_string(GLib.KEY_FILE_DESKTOP_GROUP, GLib.KEY_FILE_DESKTOP_KEY_ICON);
|
||||
+ } catch (e) {}
|
||||
+
|
||||
+ if (iconName)
|
||||
+ this._icon.child = this._createEmblemedStIcon(null, iconName);
|
||||
else
|
||||
this._icon.child = this._createEmblemedStIcon(this._fileInfo.get_icon(), null);
|
||||
}
|
||||
@@ -411,7 +440,7 @@ var FileItem = class {
|
||||
itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-unreadable')));
|
||||
else
|
||||
itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-symbolic-link')));
|
||||
- } else if (this.trustedDesktopFile) {
|
||||
+ } else if (this.trustedDesktopFile || this._linkFile) {
|
||||
itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-symbolic-link')));
|
||||
}
|
||||
|
||||
@@ -440,6 +469,12 @@ var FileItem = class {
|
||||
return;
|
||||
}
|
||||
|
||||
+ if (this._linkFile) {
|
||||
+ this._openUri(this._linkFile.get_string(
|
||||
+ GLib.KEY_FILE_DESKTOP_GROUP, GLib.KEY_FILE_DESKTOP_KEY_URL));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (this._attributeCanExecute &&
|
||||
!this._isDirectory &&
|
||||
!this._isValidDesktopFile &&
|
||||
@@ -449,13 +484,17 @@ var FileItem = class {
|
||||
return;
|
||||
}
|
||||
|
||||
- Gio.AppInfo.launch_default_for_uri_async(this.file.get_uri(),
|
||||
+ this._openUri(this.file.get_uri());
|
||||
+ }
|
||||
+
|
||||
+ _openUri(uri) {
|
||||
+ Gio.AppInfo.launch_default_for_uri_async(uri,
|
||||
null, null,
|
||||
(source, result) => {
|
||||
try {
|
||||
Gio.AppInfo.launch_default_for_uri_finish(result);
|
||||
} catch (e) {
|
||||
- log('Error opening file ' + this.file.get_uri() + ': ' + e.message);
|
||||
+ log('Error opening file ' + uri + ': ' + e.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -555,7 +594,9 @@ var FileItem = class {
|
||||
}
|
||||
|
||||
canRename() {
|
||||
- return !this.trustedDesktopFile && this._fileExtra == Prefs.FileType.NONE;
|
||||
+ return !this.trustedDesktopFile &&
|
||||
+ !this._linkFile &&
|
||||
+ this._fileExtra == Prefs.FileType.NONE;
|
||||
}
|
||||
|
||||
_doOpenWith() {
|
||||
@@ -819,6 +860,14 @@ var FileItem = class {
|
||||
if (this.trustedDesktopFile)
|
||||
return this._desktopFile.get_name();
|
||||
|
||||
+ if (this._linkFile) {
|
||||
+ try {
|
||||
+ const name = this._linkFile.get_string(
|
||||
+ GLib.KEY_FILE_DESKTOP_GROUP, GLib.KEY_FILE_DESKTOP_KEY_NAME);
|
||||
+ return name;
|
||||
+ } catch (e) {}
|
||||
+ }
|
||||
+
|
||||
return this._displayName || null;
|
||||
}
|
||||
|
||||
--
|
||||
2.38.1
|
||||
|
@ -0,0 +1,51 @@
|
||||
From ce75829479b1e7bf99e74bf835174e91c8da2276 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 | 5 ++++-
|
||||
.../org.gnome.shell.extensions.gesture-inhibitor.gschema.xml | 4 ++++
|
||||
2 files changed, 8 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/gesture-inhibitor/extension.js b/extensions/gesture-inhibitor/extension.js
|
||||
index e74ede2f..bf02d075 100644
|
||||
--- a/extensions/gesture-inhibitor/extension.js
|
||||
+++ b/extensions/gesture-inhibitor/extension.js
|
||||
@@ -37,6 +37,8 @@ class Extension {
|
||||
this._showOverview = a;
|
||||
else if (a instanceof WindowManager.AppSwitchAction)
|
||||
this._appSwitch = a;
|
||||
+ else if (a instanceof WindowManager.WorkspaceSwitchAction)
|
||||
+ this._workspaceSwitch = a;
|
||||
else if (a instanceof EdgeDragAction.EdgeDragAction &&
|
||||
a._side == St.Side.BOTTOM)
|
||||
this._showOsk = a;
|
||||
@@ -52,7 +54,8 @@ class Extension {
|
||||
{ setting: 'app-switch', action: this._appSwitch },
|
||||
{ setting: 'show-osk', action: this._showOsk },
|
||||
{ setting: 'unfullscreen', action: this._unfullscreen },
|
||||
- { setting: 'show-app-grid', action: this._showAppGrid }
|
||||
+ { setting: 'show-app-grid', action: this._showAppGrid },
|
||||
+ { setting: 'workspace-switch', action: this._workspaceSwitch },
|
||||
];
|
||||
}
|
||||
|
||||
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..a5e97a3d 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
|
||||
@@ -16,6 +16,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
|
||||
|
@ -1,7 +1,7 @@
|
||||
From ed28c7abd7c324dc6071ff96309854b1f5d48761 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 01/10] Add top-icons extension
|
||||
Subject: [PATCH 01/11] Add top-icons extension
|
||||
|
||||
---
|
||||
extensions/top-icons/extension.js | 96 +++++++++++++++++++++++++++
|
||||
@ -164,13 +164,13 @@ index b987f2d4..6050c32f 100644
|
||||
]
|
||||
|
||||
--
|
||||
2.33.1
|
||||
2.38.1
|
||||
|
||||
|
||||
From b99f1a2ead84c4fe494a387a032715f2973fbfa7 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 20 May 2015 18:05:41 +0200
|
||||
Subject: [PATCH 02/10] Add dash-to-dock extension
|
||||
Subject: [PATCH 02/11] Add dash-to-dock extension
|
||||
|
||||
---
|
||||
extensions/dash-to-dock/Settings.ui | 3335 +++++++++++++++++
|
||||
@ -13413,13 +13413,13 @@ index 6050c32f..2909135a 100644
|
||||
'top-icons',
|
||||
'user-theme'
|
||||
--
|
||||
2.33.1
|
||||
2.38.1
|
||||
|
||||
|
||||
From 9ffe67c4d25f34fa6c3af5ee4ddbd0be3018ef14 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 20 May 2015 18:55:47 +0200
|
||||
Subject: [PATCH 03/10] Add panel-favorites extension
|
||||
Subject: [PATCH 03/11] Add panel-favorites extension
|
||||
|
||||
---
|
||||
extensions/panel-favorites/extension.js | 267 ++++++++++++++++++++
|
||||
@ -13766,13 +13766,13 @@ index 2909135a..e8e00dce 100644
|
||||
'user-theme'
|
||||
]
|
||||
--
|
||||
2.33.1
|
||||
2.38.1
|
||||
|
||||
|
||||
From 4bd1716e559af83795eec5b02025798b02c09fa4 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 4 Mar 2016 17:07:21 +0100
|
||||
Subject: [PATCH 04/10] Add updates-dialog extension
|
||||
Subject: [PATCH 04/11] Add updates-dialog extension
|
||||
|
||||
---
|
||||
extensions/updates-dialog/extension.js | 503 ++++++++++++++++++
|
||||
@ -14396,13 +14396,13 @@ index 9c1438ac..55f0e9aa 100644
|
||||
extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml
|
||||
extensions/window-list/extension.js
|
||||
--
|
||||
2.33.1
|
||||
2.38.1
|
||||
|
||||
|
||||
From 0ba4b86fa5f73bccd3ab1984d9deef0d39f656c6 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 1 Jun 2017 23:57:14 +0200
|
||||
Subject: [PATCH 05/10] Add no-hot-corner extension
|
||||
Subject: [PATCH 05/11] Add no-hot-corner extension
|
||||
|
||||
---
|
||||
extensions/no-hot-corner/extension.js | 31 +++++++++++++++++++++++
|
||||
@ -14499,13 +14499,13 @@ index d129e6cd..6f27f460 100644
|
||||
'top-icons',
|
||||
'updates-dialog',
|
||||
--
|
||||
2.33.1
|
||||
2.38.1
|
||||
|
||||
|
||||
From f56b4374904cdfd8e1790dc3cf5080b60f30ebea Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 26 Mar 2019 19:44:43 +0100
|
||||
Subject: [PATCH 06/10] Add window-grouper extension
|
||||
Subject: [PATCH 06/11] Add window-grouper extension
|
||||
|
||||
---
|
||||
extensions/window-grouper/extension.js | 109 ++++++++++
|
||||
@ -14903,13 +14903,13 @@ index 6f27f460..4b9d138c 100644
|
||||
|
||||
enabled_extensions = get_option('enable_extensions')
|
||||
--
|
||||
2.33.1
|
||||
2.38.1
|
||||
|
||||
|
||||
From 25c4999ff6adf19a32bab2a4d6cccae42520563b Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 26 Mar 2019 21:32:09 +0100
|
||||
Subject: [PATCH 07/10] Add disable-screenshield extension
|
||||
Subject: [PATCH 07/11] Add disable-screenshield extension
|
||||
|
||||
---
|
||||
extensions/disable-screenshield/extension.js | 27 +++++++++++++++++++
|
||||
@ -15002,13 +15002,13 @@ index 4b9d138c..cf855a01 100644
|
||||
'no-hot-corner',
|
||||
'panel-favorites',
|
||||
--
|
||||
2.33.1
|
||||
2.38.1
|
||||
|
||||
|
||||
From 59604979f6ba48b7ff8d1616ab9df739dcf46a20 Mon Sep 17 00:00:00 2001
|
||||
From: Carlos Soriano <csoriano@gnome.org>
|
||||
Date: Mon, 13 Aug 2018 17:28:41 +0200
|
||||
Subject: [PATCH 08/10] Add desktop icons extension
|
||||
Subject: [PATCH 08/11] Add desktop icons extension
|
||||
|
||||
---
|
||||
.../desktop-icons/createFolderDialog.js | 164 ++++
|
||||
@ -26738,13 +26738,13 @@ index 74a95f8a..fa5ba9b8 100644
|
||||
#~ msgstr "Dock 的位置"
|
||||
|
||||
--
|
||||
2.33.1
|
||||
2.38.1
|
||||
|
||||
|
||||
From dc47faaf827011e5dd7a53f9007ea618c6e88203 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 8 Oct 2021 19:36:18 +0200
|
||||
Subject: [PATCH 09/10] Add dash-to-panel
|
||||
Subject: [PATCH 09/11] Add dash-to-panel
|
||||
|
||||
---
|
||||
extensions/dash-to-panel/COPYING | 341 +
|
||||
@ -70770,13 +70770,13 @@ index fa5ba9b8..015d85a4 100644
|
||||
#~ msgstr "退出應用程式"
|
||||
|
||||
--
|
||||
2.33.1
|
||||
2.38.1
|
||||
|
||||
|
||||
From e8facafa1d16fc62a01c02bcee9e34a388f81572 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 10/10] Add classification-banner
|
||||
Subject: [PATCH 10/11] Add classification-banner
|
||||
|
||||
---
|
||||
extensions/classification-banner/extension.js | 170 ++++++++
|
||||
@ -71427,5 +71427,789 @@ index d4791263..75a2beaa 100644
|
||||
'dash-to-panel',
|
||||
'disable-screenshield',
|
||||
--
|
||||
2.33.1
|
||||
2.38.1
|
||||
|
||||
|
||||
From 214b5c2959ab3c0ab64d32b79fbff011302d1af8 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 11/11] Add custom-menu extension
|
||||
|
||||
---
|
||||
extensions/custom-menu/config.js | 484 ++++++++++++++++++++++++
|
||||
extensions/custom-menu/extension.js | 217 +++++++++++
|
||||
extensions/custom-menu/meson.build | 7 +
|
||||
extensions/custom-menu/metadata.json.in | 10 +
|
||||
extensions/custom-menu/stylesheet.css | 1 +
|
||||
meson.build | 1 +
|
||||
6 files changed, 720 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
|
||||
create mode 100644 extensions/custom-menu/stylesheet.css
|
||||
|
||||
diff --git a/extensions/custom-menu/config.js b/extensions/custom-menu/config.js
|
||||
new file mode 100644
|
||||
index 00000000..652c0223
|
||||
--- /dev/null
|
||||
+++ b/extensions/custom-menu/config.js
|
||||
@@ -0,0 +1,484 @@
|
||||
+const Gio = imports.gi.Gio;
|
||||
+const GLib = imports.gi.GLib;
|
||||
+const Json = imports.gi.Json;
|
||||
+const Lang = imports.lang;
|
||||
+const Main = imports.ui.main;
|
||||
+const PopupMenu = imports.ui.popupMenu;
|
||||
+const ByteArray = imports.byteArray;
|
||||
+
|
||||
+const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||
+const getLogger = Me.imports.extension.getLogger;
|
||||
+
|
||||
+const Entry = new Lang.Class({
|
||||
+ Name: 'Entry',
|
||||
+ Abstract: true,
|
||||
+
|
||||
+ _init: function(prop) {
|
||||
+ this.type = prop.type;
|
||||
+ this.title = prop.title || "";
|
||||
+
|
||||
+ this.__vars = prop.__vars || [];
|
||||
+ this.updateEnv(prop);
|
||||
+ },
|
||||
+
|
||||
+ setTitle: function(text) {
|
||||
+ this.item.label.get_clutter_text().set_text(text);
|
||||
+ },
|
||||
+
|
||||
+ updateEnv: function(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: function() { },
|
||||
+
|
||||
+ _try_destroy: function() {
|
||||
+ try {
|
||||
+ if(this.item && this.item.destroy)
|
||||
+ this.item.destroy();
|
||||
+ } catch(e) { /* Ignore all errors during destory*/ }
|
||||
+ },
|
||||
+});
|
||||
+
|
||||
+const DerivedEntry = new Lang.Class({
|
||||
+ Name: 'DerivedEntry',
|
||||
+
|
||||
+ _init: function(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: function(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;
|
||||
+ },
|
||||
+});
|
||||
+
|
||||
+/*
|
||||
+ * callback: function (stdout, stderr, exit_status) { }
|
||||
+ */
|
||||
+let __pipeOpenQueue = [];
|
||||
+let __pipeExecTimer = null;
|
||||
+
|
||||
+function pipeOpen(cmdline, env, callback) {
|
||||
+ let param = [cmdline, env, callback]
|
||||
+ __pipeOpenQueue.push(param);
|
||||
+ if(__pipeExecTimer === null) {
|
||||
+ __pipeExecTimer = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50,
|
||||
+ function() {
|
||||
+ let param = __pipeOpenQueue.shift();
|
||||
+ if(param === undefined) {
|
||||
+ __pipeExecTimer = null;
|
||||
+ return false;
|
||||
+ }
|
||||
+ if(realPipeOpen) realPipeOpen(param[0], param[1], param[2]);
|
||||
+ 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) {
|
||||
+ stdout_content = ByteArray.toString(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 = ByteArray.toString(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);
|
||||
+
|
||||
+ // log(`Spawning ${cmdline}`); TODO: BEN
|
||||
+ 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);
|
||||
+ }
|
||||
+ });
|
||||
+}
|
||||
+
|
||||
+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 = { };
|
||||
+
|
||||
+const TogglerEntry = new Lang.Class({
|
||||
+ Name: 'TogglerEntry',
|
||||
+ Extends: Entry,
|
||||
+
|
||||
+ _init: function(prop) {
|
||||
+ this.parent(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: function() {
|
||||
+ 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', Lang.bind(this, this._onManuallyToggled));
|
||||
+ this._loadState();
|
||||
+ return this.item;
|
||||
+ },
|
||||
+
|
||||
+ _onManuallyToggled: function(_, state) {
|
||||
+ // when switched on again, this flag will get cleared.
|
||||
+ this._manually_switched_off = !state;
|
||||
+ this._storeState(state);
|
||||
+ this._onToggled(state);
|
||||
+ },
|
||||
+
|
||||
+ _onToggled: function(state) {
|
||||
+ if(state)
|
||||
+ _generalSpawn(this.command_on, this.__env, this.title);
|
||||
+ else
|
||||
+ _generalSpawn(this.command_off, this.__env, this.title);
|
||||
+ },
|
||||
+
|
||||
+ _detect: function(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*$/)));
|
||||
+ });
|
||||
+ },
|
||||
+
|
||||
+ compareState: function(new_state) {
|
||||
+ // compare the new state with cached state
|
||||
+ // notify when state is different
|
||||
+ 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: function(state) {
|
||||
+ let hash = JSON.stringify({ env: this.__env, detector: this.detector });
|
||||
+ _toggler_state_cache[hash] = state;
|
||||
+ },
|
||||
+
|
||||
+ _loadState: function() {
|
||||
+ 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: function() {
|
||||
+ this._detect(Lang.bind(this, function(state) {
|
||||
+ this.compareState(state);
|
||||
+
|
||||
+ this._storeState(state);
|
||||
+ this._loadState();
|
||||
+ //global.log(this.title + ': ' + this._manually_switched_off);
|
||||
+
|
||||
+ 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: function() {
|
||||
+ this.item.toggle();
|
||||
+ },
|
||||
+});
|
||||
+
|
||||
+const LauncherEntry = new Lang.Class({
|
||||
+ Name: 'LauncherEntry',
|
||||
+ Extends: Entry,
|
||||
+
|
||||
+ _init: function(prop) {
|
||||
+ this.parent(prop);
|
||||
+
|
||||
+ this.command = prop.command || "";
|
||||
+ },
|
||||
+
|
||||
+ createItem: function() {
|
||||
+ this._try_destroy();
|
||||
+
|
||||
+ this.item = new PopupMenu.PopupMenuItem(this.title);
|
||||
+ this.item.label.get_clutter_text().set_use_markup(true);
|
||||
+ this.item.connect('activate', Lang.bind(this, this._onClicked));
|
||||
+
|
||||
+ return this.item;
|
||||
+ },
|
||||
+
|
||||
+ _onClicked: function(_) {
|
||||
+ _generalSpawn(this.command, this.__env, this.title);
|
||||
+ },
|
||||
+
|
||||
+ perform: function() {
|
||||
+ this.item.emit('activate');
|
||||
+ },
|
||||
+});
|
||||
+
|
||||
+const SubMenuEntry = new Lang.Class({
|
||||
+ Name: 'SubMenuEntry',
|
||||
+ Extends: Entry,
|
||||
+
|
||||
+ _init: function(prop) {
|
||||
+ this.parent(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: function() {
|
||||
+ 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: function() {
|
||||
+ for(let i in this.entries) {
|
||||
+ let entry = this.entries[i];
|
||||
+ entry.pulse();
|
||||
+ }
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
+const SeparatorEntry = new Lang.Class({
|
||||
+ Name: 'SeparatorEntry',
|
||||
+ Extends: Entry,
|
||||
+
|
||||
+ _init: function(prop) { },
|
||||
+
|
||||
+ createItem: function() {
|
||||
+ 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);
|
||||
+}
|
||||
+
|
||||
+var Loader = new Lang.Class({
|
||||
+ Name: 'ConfigLoader',
|
||||
+
|
||||
+ _init: function(filename) {
|
||||
+ if(filename)
|
||||
+ this.loadConfig(filename);
|
||||
+ },
|
||||
+
|
||||
+ loadConfig: function(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: function(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..9f3e3ce8
|
||||
--- /dev/null
|
||||
+++ b/extensions/custom-menu/extension.js
|
||||
@@ -0,0 +1,217 @@
|
||||
+/* 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
|
||||
+ */
|
||||
+
|
||||
+/* exported init */
|
||||
+
|
||||
+const GETTEXT_DOMAIN = 'custom-menu-panel';
|
||||
+const CONFIGURATION_FILE = '/.entries.json';
|
||||
+
|
||||
+const { GObject, St } = imports.gi;
|
||||
+
|
||||
+const Gettext = imports.gettext.domain(GETTEXT_DOMAIN);
|
||||
+const _ = Gettext.gettext;
|
||||
+
|
||||
+const ExtensionUtils = imports.misc.extensionUtils;
|
||||
+const Lang = imports.lang;
|
||||
+const GLib = imports.gi.GLib;
|
||||
+const Gio = imports.gi.Gio;
|
||||
+const BackgroundMenu = imports.ui.backgroundMenu;
|
||||
+const Main = imports.ui.main;
|
||||
+const PanelMenu = imports.ui.panelMenu;
|
||||
+const PopupMenu = imports.ui.popupMenu;
|
||||
+
|
||||
+const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||
+const Config = Me.imports.config
|
||||
+
|
||||
+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));
|
||||
+ }
|
||||
+ }
|
||||
+);
|
||||
+
|
||||
+
|
||||
+const Logger = new Lang.Class({
|
||||
+ Name: 'Logger',
|
||||
+
|
||||
+ _init: function(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: function() {
|
||||
+ this.log = function(_) { };
|
||||
+ },
|
||||
+
|
||||
+ _initGnomeLog: function() {
|
||||
+ this.log = function(s) {
|
||||
+ global.log("custom-menu-panel> " + s);
|
||||
+ };
|
||||
+ },
|
||||
+
|
||||
+ _initFileLog: function() {
|
||||
+ 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: function(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;
|
||||
+function getLogger() {
|
||||
+ if(logger === null)
|
||||
+ logger = new Logger("gnome-shell");
|
||||
+ return logger;
|
||||
+}
|
||||
+
|
||||
+class Extension {
|
||||
+ constructor(uuid) {
|
||||
+ this._uuid = uuid;
|
||||
+
|
||||
+ ExtensionUtils.initTranslations(GETTEXT_DOMAIN);
|
||||
+ }
|
||||
+
|
||||
+ enable() {
|
||||
+ BackgroundMenu.BackgroundMenu = CustomBackgroundMenu;
|
||||
+ Main.layoutManager._updateBackgrounds();
|
||||
+ }
|
||||
+
|
||||
+ disable() {
|
||||
+ BackgroundMenu.BackgroundMenu = OriginalBackgroundMenu;
|
||||
+ Main.layoutManager._updateBackgrounds();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+function init(meta) {
|
||||
+ return new Extension(meta.uuid);
|
||||
+}
|
||||
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/extensions/custom-menu/stylesheet.css b/extensions/custom-menu/stylesheet.css
|
||||
new file mode 100644
|
||||
index 00000000..25134b65
|
||||
--- /dev/null
|
||||
+++ b/extensions/custom-menu/stylesheet.css
|
||||
@@ -0,0 +1 @@
|
||||
+/* This extensions requires no special styling */
|
||||
diff --git a/meson.build b/meson.build
|
||||
index 75a2beaa..b0c94fe4 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -51,6 +51,7 @@ all_extensions = default_extensions
|
||||
all_extensions += [
|
||||
'auto-move-windows',
|
||||
'classification-banner',
|
||||
+ 'custom-menu',
|
||||
'dash-to-dock',
|
||||
'dash-to-panel',
|
||||
'disable-screenshield',
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
Name: gnome-shell-extensions
|
||||
Version: 3.32.1
|
||||
Release: 29%{?dist}
|
||||
Release: 33%{?dist}
|
||||
Summary: Modify and extend GNOME Shell functionality and behavior
|
||||
|
||||
Group: User Interface/Desktops
|
||||
@ -51,6 +51,11 @@ Patch0022: 0001-gesture-inhibitor-Put-a-foot-down-with-self-enabling.pat
|
||||
Patch0023: 0001-desktop-icons-Use-a-single-unique-name-to-access-nau.patch
|
||||
Patch0024: window-list-touch.patch
|
||||
Patch0025: 0001-auto-move-windows-Don-t-move-windows-already-on-all-.patch
|
||||
Patch0026: 0001-fileItem-Just-destroy-menus.patch
|
||||
Patch0027: 0001-fileItem-Support-.desktop-files-of-type-Link.patch
|
||||
Patch0028: 0001-classification-banner-Handle-fullscreen-monitors.patch
|
||||
Patch0029: 0001-gesture-inhibitor-Allow-inhibiting-workspace-switch-.patch
|
||||
Patch0030: 0001-desktop-icons-Don-t-use-blocking-IO.patch
|
||||
|
||||
%description
|
||||
GNOME Shell Extensions is a collection of extensions providing additional and
|
||||
@ -60,6 +65,7 @@ Enabled extensions:
|
||||
* apps-menu
|
||||
* auto-move-windows
|
||||
* classification-banner
|
||||
* custom-menu
|
||||
* dash-to-dock
|
||||
* dash-to-panel
|
||||
* disable-screenshield
|
||||
@ -165,6 +171,16 @@ Requires: %{pkg_prefix}-common = %{version}-%{release}
|
||||
This GNOME Shell extension adds a banner that displays the classification level.
|
||||
|
||||
|
||||
%package -n %{pkg_prefix}-custom-menu
|
||||
Summary: Add a custom menu to the desktop
|
||||
Group: User Interface/Desktops
|
||||
License: GPLv2+
|
||||
Requires: %{pkg_prefix}-common = %{version}-%{release}
|
||||
|
||||
%description -n %{pkg_prefix}-custom-menu
|
||||
This GNOME Shell extension adds a custom menu to the desktop background.
|
||||
|
||||
|
||||
%package -n %{pkg_prefix}-dash-to-dock
|
||||
Summary: Show the dash outside the activities overview
|
||||
Group: User Interface/Desktops
|
||||
@ -449,6 +465,10 @@ cp $RPM_SOURCE_DIR/gnome-classic.desktop $RPM_BUILD_ROOT%{_datadir}/xsessions
|
||||
%{_datadir}/gnome-shell/extensions/classification-banner*/
|
||||
|
||||
|
||||
%files -n %{pkg_prefix}-custom-menu
|
||||
%{_datadir}/gnome-shell/extensions/custom-menu*/
|
||||
|
||||
|
||||
%files -n %{pkg_prefix}-dash-to-dock
|
||||
%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml
|
||||
%{_datadir}/gnome-shell/extensions/dash-to-dock*/
|
||||
@ -549,6 +569,26 @@ cp $RPM_SOURCE_DIR/gnome-classic.desktop $RPM_BUILD_ROOT%{_datadir}/xsessions
|
||||
|
||||
|
||||
%changelog
|
||||
* Tue Jan 17 2023 Florian Müllner <fmuellner@redhat.com> - 3.32.1-33
|
||||
- Avoid blocking IO in desktop-icons
|
||||
Resolves: #2162017
|
||||
|
||||
* Thu Jan 12 2023 Florian Müllner <fmuellner@redhat.com> - 3.32.1-32
|
||||
- Add custom-menu extension
|
||||
Resolves: #2033572
|
||||
|
||||
* Wed Dec 14 2022 Florian Müllner <fmuellner@redhat.com> - 3.32.1-31
|
||||
- Adjust classification banner position in fullscreen
|
||||
Resolves: #2150107
|
||||
- Allow inhibiting workspace switch gesture
|
||||
Resolves: #2138109
|
||||
|
||||
* Fri Dec 09 2022 Florian Müllner <fmuellner@redhat.com> - 3.32.1-30
|
||||
- Fix stuck grab if disabled with open context menu
|
||||
Resolves: #2149670
|
||||
- Support .desktop files of type Link
|
||||
Resolves: #2143825
|
||||
|
||||
* Mon Aug 29 2022 Jonas Ådahl <jadahl@redhat.com> - 3.32.1-29
|
||||
- Avoid invalid window management in auto-move-windows
|
||||
Resolves: #2089311
|
||||
|
Loading…
Reference in New Issue
Block a user