diff --git a/SOURCES/0001-general-launch-only-executable-files.patch b/SOURCES/0001-general-launch-only-executable-files.patch new file mode 100644 index 0000000..5e90b46 --- /dev/null +++ b/SOURCES/0001-general-launch-only-executable-files.patch @@ -0,0 +1,45 @@ +From ee89a91a9ac235b69ff3c47af14d702c0309e892 Mon Sep 17 00:00:00 2001 +From: Sergio Costas +Date: Thu, 25 Jul 2019 00:12:09 +0200 +Subject: [PATCH] general: launch only executable files + +Until now, if a file has the "execute" flag, clicking on it will try +to execute it, no matter if it is really an executable. This means +that a non-executable file (like a JPEG picture, or a text file) +won't be opened with its desired application if it has set the +executable flag. + +This patch fixes this, by ensuring that the only files that can be +executed when the "execute" flag is set, are the ones that makes +sense to execute. + +Fixes https://gitlab.gnome.org/World/ShellExtensions/desktop-icons/issues/144 +--- + extensions/desktop-icons/fileItem.js | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/extensions/desktop-icons/fileItem.js b/extensions/desktop-icons/fileItem.js +index d6d43c9f..44a93352 100644 +--- a/extensions/desktop-icons/fileItem.js ++++ b/extensions/desktop-icons/fileItem.js +@@ -440,10 +440,13 @@ var FileItem = class { + return; + } + +- if (this._attributeCanExecute && !this._isDirectory && !this._isValidDesktopFile) { +- if (this._execLine) +- Util.spawnCommandLine(this._execLine); +- return; ++ if (this._attributeCanExecute && ++ !this._isDirectory && ++ !this._isValidDesktopFile && ++ Gio.content_type_can_be_executable(this._attributeContentType)) { ++ if (this._execLine) ++ Util.spawnCommandLine(this._execLine); ++ return; + } + + Gio.AppInfo.launch_default_for_uri_async(this.file.get_uri(), +-- +2.31.1 + diff --git a/SOURCES/0001-heads-up-display-Add-extension-for-showing-persisten.patch b/SOURCES/0001-heads-up-display-Add-extension-for-showing-persisten.patch new file mode 100644 index 0000000..d426940 --- /dev/null +++ b/SOURCES/0001-heads-up-display-Add-extension-for-showing-persisten.patch @@ -0,0 +1,885 @@ +From 131b1b81f8f28bf57d080487e121d332546f1ab1 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 24 Aug 2021 15:03:57 -0400 +Subject: [PATCH] heads-up-display: Add extension for showing persistent heads + up display message + +--- + extensions/heads-up-display/extension.js | 320 ++++++++++++++++++ + extensions/heads-up-display/headsUpMessage.js | 150 ++++++++ + extensions/heads-up-display/meson.build | 8 + + extensions/heads-up-display/metadata.json.in | 11 + + ...ll.extensions.heads-up-display.gschema.xml | 54 +++ + extensions/heads-up-display/prefs.js | 175 ++++++++++ + extensions/heads-up-display/stylesheet.css | 32 ++ + meson.build | 1 + + 8 files changed, 751 insertions(+) + create mode 100644 extensions/heads-up-display/extension.js + create mode 100644 extensions/heads-up-display/headsUpMessage.js + create mode 100644 extensions/heads-up-display/meson.build + create mode 100644 extensions/heads-up-display/metadata.json.in + create mode 100644 extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml + create mode 100644 extensions/heads-up-display/prefs.js + create mode 100644 extensions/heads-up-display/stylesheet.css + +diff --git a/extensions/heads-up-display/extension.js b/extensions/heads-up-display/extension.js +new file mode 100644 +index 0000000..e4ef9e8 +--- /dev/null ++++ b/extensions/heads-up-display/extension.js +@@ -0,0 +1,320 @@ ++/* exported init enable disable */ ++ ++ ++const Signals = imports.signals; ++ ++const { ++ Atk, Clutter, Gio, GLib, GObject, Gtk, Meta, Shell, St, ++} = imports.gi; ++ ++const ExtensionUtils = imports.misc.extensionUtils; ++const Me = ExtensionUtils.getCurrentExtension(); ++ ++const Main = imports.ui.main; ++const HeadsUpMessage = Me.imports.headsUpMessage; ++ ++const Gettext = imports.gettext.domain('gnome-shell-extensions'); ++const _ = Gettext.gettext; ++ ++class Extension { ++ constructor() { ++ ExtensionUtils.initTranslations(); ++ } ++ ++ enable() { ++ this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.heads-up-display'); ++ this._idleTimeoutChangedId = this._settings.connect('changed::idle-timeout', this._onIdleTimeoutChanged.bind(this)); ++ this._settingsChangedId = this._settings.connect('changed', this._updateMessage.bind(this)); ++ ++ this._idleMonitor = Meta.IdleMonitor.get_core(); ++ this._messageInhibitedUntilIdle = false; ++ this._oldMapWindow = Main.wm._mapWindow; ++ Main.wm._mapWindow = this._mapWindow; ++ this._windowManagerMapId = global.window_manager.connect('map', this._onWindowMap.bind(this)); ++ ++ if (Main.layoutManager._startingUp) ++ this._startupCompleteId = Main.layoutManager.connect('startup-complete', this._onStartupComplete.bind(this)); ++ else ++ this._onStartupComplete(this); ++ } ++ ++ disable() { ++ this._dismissMessage(); ++ ++ if (this._idleWatchId) { ++ this._idleMonitor.remove_watch(this._idleWatchId); ++ this._idleWatchId = 0; ++ } ++ ++ if (this._sessionModeUpdatedId) { ++ Main.sessionMode.disconnect(this._sessionModeUpdatedId); ++ this._sessionModeUpdatedId = 0; ++ } ++ ++ if (this._overviewShowingId) { ++ Main.overview.disconnect(this._overviewShowingId); ++ this._overviewShowingId = 0; ++ } ++ ++ if (this._overviewHiddenId) { ++ Main.overview.disconnect(this._overviewHiddenId); ++ this._overviewHiddenId = 0; ++ } ++ ++ if (this._panelConnectionId) { ++ Main.layoutManager.panelBox.disconnect(this._panelConnectionId); ++ this._panelConnectionId = 0; ++ } ++ ++ if (this._oldMapWindow) { ++ Main.wm._mapWindow = this._oldMapWindow; ++ this._oldMapWindow = null; ++ } ++ ++ if (this._windowManagerMapId) { ++ global.window_manager.disconnect(this._windowManagerMapId); ++ this._windowManagerMapId = 0; ++ } ++ ++ if (this._startupCompleteId) { ++ Main.layoutManager.disconnect(this._startupCompleteId); ++ this._startupCompleteId = 0; ++ } ++ ++ if (this._settingsChangedId) { ++ this._settings.disconnect(this._settingsChangedId); ++ this._settingsChangedId = 0; ++ } ++ } ++ ++ _onWindowMap(shellwm, actor) { ++ let windowObject = actor.meta_window; ++ let windowType = windowObject.get_window_type(); ++ ++ if (windowType != Meta.WindowType.NORMAL) ++ return; ++ ++ if (!this._message || !this._message.visible) ++ return; ++ ++ let messageRect = new Meta.Rectangle({ x: this._message.x, y: this._message.y, width: this._message.width, height: this._message.height }); ++ let windowRect = windowObject.get_frame_rect(); ++ ++ if (windowRect.intersect(messageRect)) { ++ windowObject.move_frame(false, windowRect.x, this._message.y + this._message.height); ++ } ++ } ++ ++ _onStartupComplete() { ++ this._overviewShowingId = Main.overview.connect('showing', this._updateMessage.bind(this)); ++ this._overviewHiddenId = Main.overview.connect('hidden', this._updateMessage.bind(this)); ++ this._panelConnectionId = Main.layoutManager.panelBox.connect('notify::visible', this._updateMessage.bind(this)); ++ this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._onSessionModeUpdated.bind(this)); ++ ++ this._updateMessage(); ++ } ++ ++ _onSessionModeUpdated() { ++ if (!Main.sessionMode.hasWindows) ++ this._messageInhibitedUntilIdle = false; ++ this._updateMessage(); ++ } ++ ++ _onIdleTimeoutChanged() { ++ if (this._idleWatchId) { ++ this._idleMonitor.remove_watch(this._idleWatchId); ++ this._idleWatchId = 0; ++ } ++ this._messageInhibitedUntilIdle = false; ++ } ++ ++ _updateMessage() { ++ if (this._messageInhibitedUntilIdle) { ++ if (this._message) ++ this._dismissMessage(); ++ return; ++ } ++ ++ if (this._idleWatchId) { ++ this._idleMonitor.remove_watch(this._idleWatchId); ++ this._idleWatchId = 0; ++ } ++ ++ if (Main.sessionMode.hasOverview && Main.overview.visible) { ++ this._dismissMessage(); ++ return; ++ } ++ ++ if (!Main.layoutManager.panelBox.visible) { ++ this._dismissMessage(); ++ return; ++ } ++ ++ let supportedModes = []; ++ ++ if (this._settings.get_boolean('show-when-unlocked')) ++ supportedModes.push('user'); ++ ++ if (this._settings.get_boolean('show-when-unlocking')) ++ supportedModes.push('unlock-dialog'); ++ ++ if (this._settings.get_boolean('show-when-locked')) ++ supportedModes.push('lock-screen'); ++ ++ if (this._settings.get_boolean('show-on-login-screen')) ++ supportedModes.push('gdm'); ++ ++ if (!supportedModes.includes(Main.sessionMode.currentMode) && ++ !supportedModes.includes(Main.sessionMode.parentMode)) { ++ this._dismissMessage(); ++ return; ++ } ++ ++ let heading = this._settings.get_string('message-heading'); ++ let body = this._settings.get_string('message-body'); ++ ++ if (!heading && !body) { ++ this._dismissMessage(); ++ return; ++ } ++ ++ if (!this._message) { ++ this._message = new HeadsUpMessage.HeadsUpMessage(heading, body); ++ ++ this._message.connect('notify::allocation', this._adaptSessionForMessage.bind(this)); ++ this._message.connect('clicked', this._onMessageClicked.bind(this)); ++ } ++ ++ this._message.reactive = true; ++ this._message.track_hover = true; ++ ++ this._message.setHeading(heading); ++ this._message.setBody(body); ++ ++ if (!Main.sessionMode.hasWindows) { ++ this._message.track_hover = false; ++ this._message.reactive = false; ++ } ++ } ++ ++ _onMessageClicked() { ++ if (!Main.sessionMode.hasWindows) ++ return; ++ ++ if (this._idleWatchId) { ++ this._idleMonitor.remove_watch(this._idleWatchId); ++ this._idleWatchId = 0; ++ } ++ ++ let idleTimeout = this._settings.get_uint('idle-timeout'); ++ this._idleWatchId = this._idleMonitor.add_idle_watch(idleTimeout * 1000, this._onUserIdle.bind(this)); ++ this._messageInhibitedUntilIdle = true; ++ this._updateMessage(); ++ } ++ ++ _onUserIdle() { ++ this._messageInhibitedUntilIdle = false; ++ this._updateMessage(); ++ } ++ ++ _dismissMessage() { ++ if (!this._message) { ++ return; ++ } ++ ++ this._message.visible = false; ++ this._message.destroy(); ++ this._message = null; ++ this._resetMessageTray(); ++ this._resetLoginDialog(); ++ } ++ ++ _resetMessageTray() { ++ if (!Main.messageTray) ++ return; ++ ++ Main.messageTray.actor.set_translation(0, 0, 0); ++ } ++ ++ _alignMessageTray() { ++ if (!Main.messageTray) ++ return; ++ ++ if (!this._message || !this._message.visible) { ++ this._resetMessageTray() ++ return; ++ } ++ ++ let panelBottom = Main.layoutManager.panelBox.y + Main.layoutManager.panelBox.height; ++ let messageBottom = this._message.y + this._message.height; ++ ++ Main.messageTray.actor.set_translation(0, messageBottom - panelBottom, 0); ++ } ++ ++ _resetLoginDialog() { ++ if (!Main.sessionMode.isGreeter) ++ return; ++ ++ if (!Main.screenShield || !Main.screenShield._dialog) ++ return; ++ ++ let dialog = Main.screenShield._dialog; ++ ++ if (this._authPromptAllocatedId) { ++ dialog.disconnect(this._authPromptAllocatedId); ++ this._authPromptAllocatedId = 0; ++ } ++ ++ dialog.style = null; ++ dialog._bannerView.style = null; ++ } ++ ++ _adaptLoginDialogForMessage() { ++ if (!Main.sessionMode.isGreeter) ++ return; ++ ++ if (!Main.screenShield || !Main.screenShield._dialog) ++ return; ++ ++ if (!this._message || !this._message.visible) { ++ this._resetLoginDialog() ++ return; ++ } ++ ++ let dialog = Main.screenShield._dialog; ++ ++ let messageHeight = this._message.y + this._message.height; ++ if (dialog._logoBin.visible) ++ messageHeight -= dialog._logoBin.height; ++ ++ if (messageHeight <= 0) { ++ dialog.style = null; ++ dialog._bannerView.style = null; ++ } else { ++ dialog.style = `margin-top: ${messageHeight}px;`; ++ ++ let bannerOnSide = dialog._bannerView.x + dialog._bannerView.width < dialog._authPrompt.actor.x; ++ ++ if (bannerOnSide) ++ dialog._bannerView.style = `margin-bottom: ${messageHeight}px;`; ++ else ++ dialog._bannerView.style = `margin-top: ${messageHeight}px`; ++ } ++ } ++ ++ _adaptSessionForMessage() { ++ this._alignMessageTray(); ++ ++ if (Main.sessionMode.isGreeter) { ++ this._adaptLoginDialogForMessage(); ++ if (!this._authPromptAllocatedId) { ++ let dialog = Main.screenShield._dialog; ++ this._authPromptAllocatedId = dialog._authPrompt.actor.connect("notify::allocation", this._adaptLoginDialogForMessage.bind(this)); ++ } ++ } ++ } ++} ++ ++function init() { ++ return new Extension(); ++} +diff --git a/extensions/heads-up-display/headsUpMessage.js b/extensions/heads-up-display/headsUpMessage.js +new file mode 100644 +index 0000000..d828d8c +--- /dev/null ++++ b/extensions/heads-up-display/headsUpMessage.js +@@ -0,0 +1,150 @@ ++const { Atk, Clutter, GObject, Pango, St } = imports.gi; ++const Layout = imports.ui.layout; ++const Main = imports.ui.main; ++const Signals = imports.signals; ++ ++var HeadsUpMessageBodyLabel = GObject.registerClass({ ++}, class HeadsUpMessageBodyLabel extends St.Label { ++ _init(params) { ++ super._init(params); ++ ++ this.clutter_text.single_line_mode = false; ++ this.clutter_text.line_wrap = true; ++ } ++ ++ vfunc_get_preferred_width(forHeight) { ++ let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex); ++ ++ let [labelMinimumWidth, labelNaturalWidth] = super.vfunc_get_preferred_width(forHeight); ++ ++ labelMinimumWidth = Math.min(labelMinimumWidth, .75 * workArea.width); ++ labelNaturalWidth = Math.min(labelNaturalWidth, .75 * workArea.width); ++ ++ return [labelMinimumWidth, labelNaturalWidth]; ++ } ++ ++ vfunc_get_preferred_height(forWidth) { ++ let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex); ++ let labelHeightUpperBound = .25 * workArea.height; ++ ++ this.clutter_text.single_line_mode = true; ++ this.clutter_text.line_wrap = false; ++ let [lineHeight] = super.vfunc_get_preferred_height(-1); ++ let numberOfLines = Math.floor(labelHeightUpperBound / lineHeight); ++ numberOfLines = Math.max(numberOfLines, 1); ++ ++ let labelHeight = lineHeight * numberOfLines; ++ ++ this.clutter_text.single_line_mode = false; ++ this.clutter_text.line_wrap = true; ++ let [labelMinimumHeight, labelNaturalHeight] = super.vfunc_get_preferred_height(forWidth); ++ ++ labelMinimumHeight = Math.min(labelMinimumHeight, labelHeight); ++ labelNaturalHeight = Math.min(labelNaturalHeight, labelHeight); ++ ++ return [labelMinimumHeight, labelNaturalHeight]; ++ } ++ ++ vfunc_allocate(box, flags) { ++ if (!this.visible) ++ return; ++ ++ super.vfunc_allocate(box, flags); ++ } ++}); ++ ++var HeadsUpMessage = GObject.registerClass({ ++}, class HeadsUpMessage extends St.Button { ++ _init(heading, body) { ++ super._init({ ++ style_class: 'message', ++ accessible_role: Atk.Role.NOTIFICATION, ++ can_focus: false, ++ }); ++ ++ Main.layoutManager.addChrome(this, { affectsInputRegion: true }); ++ ++ this.add_style_class_name('heads-up-display-message'); ++ ++ this._panelAllocationId = Main.layoutManager.panelBox.connect ("notify::allocation", this._alignWithPanel.bind(this)); ++ this.connect("notify::allocation", this._alignWithPanel.bind(this)); ++ ++ this._messageTraySnappingId = Main.messageTray.connect ("notify::y", () => { ++ if (!this.visible) ++ return; ++ ++ if (!Main.messageTray.visible) ++ return; ++ ++ if (Main.messageTray.y >= this.y && Main.messageTray.y < this.y + this.height) ++ Main.messageTray.y = this.y + this.height; ++ }); ++ ++ ++ let contentsBox = new St.BoxLayout({ style_class: 'heads-up-message-content', ++ vertical: true, ++ x_align: Clutter.ActorAlign.CENTER }); ++ this.add_actor(contentsBox); ++ ++ this.headingLabel = new St.Label({ style_class: 'heads-up-message-heading', ++ x_expand: true, ++ x_align: Clutter.ActorAlign.CENTER }); ++ this.setHeading(heading); ++ contentsBox.add_actor(this.headingLabel); ++ this.contentsBox = contentsBox; ++ ++ this.bodyLabel = new HeadsUpMessageBodyLabel({ style_class: 'heads-up-message-body', ++ x_expand: true, ++ y_expand: true }); ++ contentsBox.add_actor(this.bodyLabel); ++ ++ this.setBody(body); ++ this.bodyLabel.clutter_text.label = this.bodyLabel; ++ } ++ ++ _alignWithPanel() { ++ if (!this.visible) ++ return; ++ ++ this.x = Main.panel.actor.x; ++ this.x += Main.panel.actor.width / 2; ++ this.x -= this.width / 2; ++ this.x = Math.floor(this.x); ++ this.y = Main.panel.actor.y + Main.panel.actor.height; ++ this.queue_relayout(); ++ } ++ ++ setHeading(text) { ++ if (text) { ++ let heading = text ? text.replace(/\n/g, ' ') : ''; ++ this.headingLabel.text = heading; ++ this.headingLabel.visible = true; ++ } else { ++ this.headingLabel.text = text; ++ this.headingLabel.visible = false; ++ } ++ } ++ ++ setBody(text) { ++ this.bodyLabel.text = text; ++ if (text) { ++ this.bodyLabel.visible = true; ++ } else { ++ this.bodyLabel.visible = false; ++ } ++ } ++ ++ destroy() { ++ if (this._panelAllocationId) { ++ Main.layoutManager.panelBox.disconnect(this._panelAllocationId); ++ this._panelAllocationId = 0; ++ } ++ ++ if (this._messageTraySnappingId) { ++ Main.messageTray.disconnect(this._messageTraySnappingId); ++ this._messageTraySnappingId = 0; ++ } ++ ++ super.destroy(); ++ } ++}); +diff --git a/extensions/heads-up-display/meson.build b/extensions/heads-up-display/meson.build +new file mode 100644 +index 0000000..40c3de0 +--- /dev/null ++++ b/extensions/heads-up-display/meson.build +@@ -0,0 +1,8 @@ ++extension_data += configure_file( ++ input: metadata_name + '.in', ++ output: metadata_name, ++ configuration: metadata_conf ++) ++ ++extension_sources += files('headsUpMessage.js', 'prefs.js') ++extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml') +diff --git a/extensions/heads-up-display/metadata.json.in b/extensions/heads-up-display/metadata.json.in +new file mode 100644 +index 0000000..e7ab71a +--- /dev/null ++++ b/extensions/heads-up-display/metadata.json.in +@@ -0,0 +1,11 @@ ++{ ++"extension-id": "@extension_id@", ++"uuid": "@uuid@", ++"gettext-domain": "@gettext_domain@", ++"name": "Heads-up Display Message", ++"description": "Add a message to be displayed on screen always above all windows and chrome.", ++"original-authors": [ "rstrode@redhat.com" ], ++"shell-version": [ "@shell_current@" ], ++"url": "@url@", ++"session-modes": [ "gdm", "lock-screen", "unlock-dialog", "user" ] ++} +diff --git a/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml b/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml +new file mode 100644 +index 0000000..ea1f377 +--- /dev/null ++++ b/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml +@@ -0,0 +1,54 @@ ++ ++ ++ ++ 30 ++ Idle Timeout ++ ++ Number of seconds until message is reshown after user goes idle. ++ ++ ++ ++ "" ++ Message to show at top of display ++ ++ The top line of the heads up display message. ++ ++ ++ ++ "" ++ Banner message ++ ++ A message to always show at the top of the screen. ++ ++ ++ ++ true ++ Show on login screen ++ ++ Whether or not the message should display on the login screen ++ ++ ++ ++ false ++ Show on screen shield ++ ++ Whether or not the message should display when the screen is locked ++ ++ ++ ++ false ++ Show on unlock screen ++ ++ Whether or not the message should display on the unlock screen. ++ ++ ++ ++ false ++ Show in user session ++ ++ Whether or not the message should display when the screen is unlocked. ++ ++ ++ ++ +diff --git a/extensions/heads-up-display/prefs.js b/extensions/heads-up-display/prefs.js +new file mode 100644 +index 0000000..b4b6f94 +--- /dev/null ++++ b/extensions/heads-up-display/prefs.js +@@ -0,0 +1,175 @@ ++ ++/* Desktop Icons GNOME Shell extension ++ * ++ * Copyright (C) 2017 Carlos Soriano ++ * ++ * 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 . ++ */ ++ ++const { Gio, GObject, Gdk, Gtk } = imports.gi; ++const ExtensionUtils = imports.misc.extensionUtils; ++const Gettext = imports.gettext.domain('gnome-shell-extensions'); ++const _ = Gettext.gettext; ++const N_ = e => e; ++const cssData = ` ++ .no-border { ++ border: none; ++ } ++ ++ .border { ++ border: 1px solid; ++ border-radius: 3px; ++ border-color: #b6b6b3; ++ box-shadow: inset 0 0 0 1px rgba(74, 144, 217, 0); ++ background-color: white; ++ } ++ ++ .margins { ++ padding-left: 8px; ++ padding-right: 8px; ++ padding-bottom: 8px; ++ } ++ ++ .message-label { ++ font-weight: bold; ++ } ++`; ++ ++var settings; ++ ++function init() { ++ settings = ExtensionUtils.getSettings("org.gnome.shell.extensions.heads-up-display"); ++ let cssProvider = new Gtk.CssProvider(); ++ cssProvider.load_from_data(cssData); ++ ++ let screen = Gdk.Screen.get_default(); ++ Gtk.StyleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); ++} ++ ++function buildPrefsWidget() { ++ ExtensionUtils.initTranslations(); ++ ++ let contents = new Gtk.Box({ ++ orientation: Gtk.Orientation.VERTICAL, ++ border_width: 20, ++ spacing: 10, ++ }); ++ ++ contents.add(buildSwitch('show-when-locked', _("Show message when screen is locked"))); ++ contents.add(buildSwitch('show-when-unlocking', _("Show message on unlock screen"))); ++ contents.add(buildSwitch('show-when-unlocked', _("Show message when screen is unlocked"))); ++ contents.add(buildSpinButton('idle-timeout', _("Seconds after user goes idle before reshowing message"))); ++ ++ let outerMessageBox = new Gtk.Box({ ++ orientation: Gtk.Orientation.VERTICAL, ++ border_width: 0, ++ spacing: 5, ++ }); ++ contents.add(outerMessageBox); ++ ++ let messageLabel = new Gtk.Label({ ++ label: 'Message', ++ halign: Gtk.Align.START, ++ }); ++ messageLabel.get_style_context().add_class("message-label"); ++ outerMessageBox.add(messageLabel); ++ ++ let innerMessageBox = new Gtk.Box({ ++ orientation: Gtk.Orientation.VERTICAL, ++ border_width: 0, ++ spacing: 0, ++ }); ++ innerMessageBox.get_style_context().add_class("border"); ++ outerMessageBox.add(innerMessageBox); ++ ++ innerMessageBox.add(buildEntry('message-heading', _("Message Heading"))); ++ innerMessageBox.add(buildTextView('message-body', _("Message Body"))); ++ contents.show_all(); ++ return contents; ++} ++ ++function buildTextView(key, labelText) { ++ let textView = new Gtk.TextView({ ++ accepts_tab: false, ++ wrap_mode: Gtk.WrapMode.WORD, ++ }); ++ settings.bind(key, textView.get_buffer(), 'text', Gio.SettingsBindFlags.DEFAULT); ++ ++ let scrolledWindow = new Gtk.ScrolledWindow({ ++ expand: true, ++ }); ++ let styleContext = scrolledWindow.get_style_context(); ++ styleContext.add_class("margins"); ++ ++ scrolledWindow.add(textView); ++ return scrolledWindow; ++} ++function buildEntry(key, labelText) { ++ let entry = new Gtk.Entry({ placeholder_text: labelText }); ++ let styleContext = entry.get_style_context(); ++ styleContext.add_class("no-border"); ++ settings.bind(key, entry, 'text', Gio.SettingsBindFlags.DEFAULT); ++ ++ entry.get_settings()['gtk-entry-select-on-focus'] = false; ++ ++ return entry; ++} ++ ++function buildSpinButton(key, labelText) { ++ let hbox = new Gtk.Box({ ++ orientation: Gtk.Orientation.HORIZONTAL, ++ spacing: 10, ++ }); ++ let label = new Gtk.Label({ ++ label: labelText, ++ xalign: 0, ++ }); ++ let adjustment = new Gtk.Adjustment({ ++ value: 0, ++ lower: 0, ++ upper: 2147483647, ++ step_increment: 1, ++ page_increment: 60, ++ page_size: 60, ++ }); ++ let spinButton = new Gtk.SpinButton({ ++ adjustment: adjustment, ++ climb_rate: 1.0, ++ digits: 0, ++ max_width_chars: 3, ++ width_chars: 3, ++ }); ++ settings.bind(key, spinButton, 'value', Gio.SettingsBindFlags.DEFAULT); ++ hbox.pack_start(label, true, true, 0); ++ hbox.add(spinButton); ++ return hbox; ++} ++ ++function buildSwitch(key, labelText) { ++ let hbox = new Gtk.Box({ ++ orientation: Gtk.Orientation.HORIZONTAL, ++ spacing: 10, ++ }); ++ let label = new Gtk.Label({ ++ label: labelText, ++ xalign: 0, ++ }); ++ let switcher = new Gtk.Switch({ ++ active: settings.get_boolean(key), ++ }); ++ settings.bind(key, switcher, 'active', Gio.SettingsBindFlags.DEFAULT); ++ hbox.pack_start(label, true, true, 0); ++ hbox.add(switcher); ++ return hbox; ++} +diff --git a/extensions/heads-up-display/stylesheet.css b/extensions/heads-up-display/stylesheet.css +new file mode 100644 +index 0000000..9303446 +--- /dev/null ++++ b/extensions/heads-up-display/stylesheet.css +@@ -0,0 +1,32 @@ ++.heads-up-display-message { ++ background-color: rgba(0.24, 0.24, 0.24, 0.80); ++ border: 1px solid black; ++ border-radius: 6px; ++ color: #eeeeec; ++ font-size: 11pt; ++ margin-top: 0.5em; ++ margin-bottom: 0.5em; ++ padding: 0.9em; ++} ++ ++.heads-up-display-message:insensitive { ++ background-color: rgba(0.24, 0.24, 0.24, 0.33); ++} ++ ++.heads-up-display-message:hover { ++ background-color: rgba(0.24, 0.24, 0.24, 0.2); ++ border: 1px solid rgba(0.0, 0.0, 0.0, 0.5); ++ color: #4d4d4d; ++ transition-duration: 250ms; ++} ++ ++.heads-up-message-heading { ++ height: 1.75em; ++ font-size: 1.25em; ++ font-weight: bold; ++ text-align: center; ++} ++ ++.heads-up-message-body { ++ text-align: center; ++} +diff --git a/meson.build b/meson.build +index 9e59729..84e161d 100644 +--- a/meson.build ++++ b/meson.build +@@ -17,60 +17,61 @@ modedir = join_paths(shelldir, 'modes') + 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') + + js60 = find_program('js60', required: false) + + ver_arr = meson.project_version().split('.') + if ver_arr[1].to_int().is_even() + shell_version = '@0@.@1@'.format(ver_arr[0], ver_arr[1]) + else + shell_version = '.'.join(ver_arr) + endif + + uuid_suffix = '@gnome-shell-extensions.gcampax.github.com' + + classic_extensions = [ + 'apps-menu', + 'desktop-icons', + 'horizontal-workspaces', + 'places-menu', + 'launch-new-instance', + 'top-icons', + 'window-list' + ] + + default_extensions = classic_extensions + default_extensions += [ + 'drive-menu', ++ 'heads-up-display', + 'screenshot-window-sizer', + 'windowsNavigator', + 'workspace-indicator' + ] + + all_extensions = default_extensions + all_extensions += [ + 'auto-move-windows', + 'dash-to-dock', + 'disable-screenshield', + 'gesture-inhibitor', + 'native-window-placement', + 'no-hot-corner', + 'panel-favorites', + 'systemMonitor', + 'updates-dialog', + 'user-theme', + 'window-grouper' + ] + + enabled_extensions = get_option('enable_extensions') + + if enabled_extensions.length() == 0 + set = get_option('extension_set') + + if set == 'classic' + enabled_extensions += classic_extensions + elif set == 'default' + enabled_extensions += default_extensions + elif set == 'all' +-- +2.27.0 + diff --git a/SOURCES/0001-top-icons-Don-t-use-wm_class-as-role.patch b/SOURCES/0001-top-icons-Don-t-use-wm_class-as-role.patch new file mode 100644 index 0000000..bc3b89b --- /dev/null +++ b/SOURCES/0001-top-icons-Don-t-use-wm_class-as-role.patch @@ -0,0 +1,27 @@ +From ce48dc2f4fba6a7084540df256cb5b3eb0da43da Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +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 79e2f423..3dfba469 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.31.1 + diff --git a/SOURCES/0001-window-list-Leave-fake-overview-when-destroyed.patch b/SOURCES/0001-window-list-Leave-fake-overview-when-destroyed.patch new file mode 100644 index 0000000..3c6c7e5 --- /dev/null +++ b/SOURCES/0001-window-list-Leave-fake-overview-when-destroyed.patch @@ -0,0 +1,30 @@ +From ee25c2aac70b86f31c91f6491dad4c67a59bc261 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 26 Jan 2021 21:14:47 +0100 +Subject: [PATCH] window-list: Leave "fake overview" when destroyed + +Otherwise we leave an incomplete overview-like state around, which +can cause issues later when the extension is re-enabled (for example +when coming back from screen lock). + +https://bugzilla.redhat.com/show_bug.cgi?id=1904371 +--- + extensions/window-list/windowPicker.js | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/extensions/window-list/windowPicker.js b/extensions/window-list/windowPicker.js +index 12a7627..afb5d27 100644 +--- a/extensions/window-list/windowPicker.js ++++ b/extensions/window-list/windowPicker.js +@@ -210,6 +210,8 @@ var WindowPicker = class { + } + + _onDestroy() { ++ this._fakeOverviewVisible(false); ++ + if (this._monitorsChangedId) + Main.layoutManager.disconnect(this._monitorsChangedId); + this._monitorsChangedId = 0; +-- +2.31.1 + diff --git a/SOURCES/add-workspace-tooltips.patch b/SOURCES/add-workspace-tooltips.patch new file mode 100644 index 0000000..c809a48 --- /dev/null +++ b/SOURCES/add-workspace-tooltips.patch @@ -0,0 +1,176 @@ +From 1f9f4af38f991b462ee5f872a697d88a9e115499 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 20 Jan 2021 20:18:39 +0100 +Subject: [PATCH 1/2] workspace-indicator: Add tooltips to workspace thumbnails + +When showing previews instead of the menu, the workspace names from +our preferences don't appear anywhere. Some users care strongly about +those, so expose them as tooltip on hover. +--- + extensions/workspace-indicator/extension.js | 40 +++++++++++++++++++++ + 1 file changed, 40 insertions(+) + +diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js +index 69eef88c..b10e37ff 100644 +--- a/extensions/workspace-indicator/extension.js ++++ b/extensions/workspace-indicator/extension.js +@@ -8,6 +8,7 @@ const ExtensionUtils = imports.misc.extensionUtils; + const Main = imports.ui.main; + const PanelMenu = imports.ui.panelMenu; + const PopupMenu = imports.ui.popupMenu; ++const Tweener = imports.ui.tweener; + + const Gettext = imports.gettext.domain('gnome-shell-extensions'); + const _ = Gettext.gettext; +@@ -15,6 +16,9 @@ const _ = Gettext.gettext; + const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences'; + const WORKSPACE_KEY = 'workspace-names'; + ++const TOOLTIP_OFFSET = 6; ++const TOOLTIP_ANIMATION_TIME = 150; ++ + let WindowPreview = GObject.registerClass({ + GTypeName: 'WorkspaceIndicatorWindowPreview' + }, class WindowPreview extends St.Button { +@@ -117,7 +121,14 @@ let WorkspaceThumbnail = GObject.registerClass({ + y_fill: true + }); + ++ this._tooltip = new St.Label({ ++ style_class: 'dash-label', ++ visible: false, ++ }); ++ Main.uiGroup.add_child(this._tooltip); ++ + this.connect('destroy', this._onDestroy.bind(this)); ++ this.connect('notify::hover', this._syncTooltip.bind(this)); + + this._index = index; + this._delegate = this; // needed for DND +@@ -204,7 +215,36 @@ let WorkspaceThumbnail = GObject.registerClass({ + ws.activate(global.get_current_time()); + } + ++ _syncTooltip() { ++ if (this.hover) { ++ this._tooltip.text = Meta.prefs_get_workspace_name(this._index); ++ this._tooltip.opacity = 0; ++ this._tooltip.show(); ++ ++ const [stageX, stageY] = this.get_transformed_position(); ++ const thumbWidth = this.allocation.get_width(); ++ const thumbHeight = this.allocation.get_height(); ++ const tipWidth = this._tooltip.width; ++ const xOffset = Math.floor((thumbWidth - tipWidth) / 2); ++ const monitor = Main.layoutManager.findMonitorForActor(this); ++ const x = Math.min( ++ Math.max(stageX + xOffset, monitor.x), ++ monitor.x + monitor.width - tipWidth); ++ const y = stageY + thumbHeight + TOOLTIP_OFFSET; ++ this._tooltip.set_position(x, y); ++ } ++ ++ Tweener.addTween(this._tooltip, { ++ opacity: this.hover ? 255 : 0, ++ time: TOOLTIP_ANIMATION_TIME * 1000, ++ transition: 'easeOutQuad', ++ onComplete: () => (this._tooltip.visible = this.hover), ++ }); ++ } ++ + _onDestroy() { ++ this._tooltip.destroy(); ++ + this._workspace.disconnect(this._windowAddedId); + this._workspace.disconnect(this._windowRemovedId); + global.display.disconnect(this._restackedId); +-- +2.31.1 + + +From 19e19e11214b6b9deae110cd6a4c9232d77c18cb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 20 Jan 2021 20:29:01 +0100 +Subject: [PATCH 2/2] window-list: Add tooltips to workspace thumbnails + +When showing previews instead of the menu, the workspace names +don't appear anywhere. Some users care strongly about those, so +expose them as tooltip on hover. +--- + extensions/window-list/workspaceIndicator.js | 40 ++++++++++++++++++++ + 1 file changed, 40 insertions(+) + +diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js +index ca476111..33ec9b0e 100644 +--- a/extensions/window-list/workspaceIndicator.js ++++ b/extensions/window-list/workspaceIndicator.js +@@ -5,10 +5,14 @@ const DND = imports.ui.dnd; + const Main = imports.ui.main; + const PanelMenu = imports.ui.panelMenu; + const PopupMenu = imports.ui.popupMenu; ++const Tweener = imports.ui.tweener; + + const Gettext = imports.gettext.domain('gnome-shell-extensions'); + const _ = Gettext.gettext; + ++const TOOLTIP_OFFSET = 6; ++const TOOLTIP_ANIMATION_TIME = 150; ++ + let WindowPreview = GObject.registerClass({ + GTypeName: 'WindowListWindowPreview' + }, class WindowPreview extends St.Button { +@@ -111,7 +115,14 @@ let WorkspaceThumbnail = GObject.registerClass({ + y_fill: true + }); + ++ this._tooltip = new St.Label({ ++ style_class: 'dash-label', ++ visible: false, ++ }); ++ Main.uiGroup.add_child(this._tooltip); ++ + this.connect('destroy', this._onDestroy.bind(this)); ++ this.connect('notify::hover', this._syncTooltip.bind(this)); + + this._index = index; + this._delegate = this; // needed for DND +@@ -198,7 +209,36 @@ let WorkspaceThumbnail = GObject.registerClass({ + ws.activate(global.get_current_time()); + } + ++ _syncTooltip() { ++ if (this.hover) { ++ this._tooltip.text = Meta.prefs_get_workspace_name(this._index); ++ this._tooltip.opacity = 0; ++ this._tooltip.show(); ++ ++ const [stageX, stageY] = this.get_transformed_position(); ++ const thumbWidth = this.allocation.get_width(); ++ const tipWidth = this._tooltip.width; ++ const tipHeight = this._tooltip.height; ++ const xOffset = Math.floor((thumbWidth - tipWidth) / 2); ++ const monitor = Main.layoutManager.findMonitorForActor(this); ++ const x = Math.min( ++ Math.max(stageX + xOffset, monitor.x), ++ monitor.x + monitor.width - tipWidth); ++ const y = stageY - tipHeight - TOOLTIP_OFFSET; ++ this._tooltip.set_position(x, y); ++ } ++ ++ Tweener.addTween(this._tooltip, { ++ opacity: this.hover ? 255 : 0, ++ time: TOOLTIP_ANIMATION_TIME * 1000, ++ transition: 'easeOutQuad', ++ onComplete: () => (this._tooltip.visible = this.hover), ++ }); ++ } ++ + _onDestroy() { ++ this._tooltip.destroy(); ++ + this._workspace.disconnect(this._windowAddedId); + this._workspace.disconnect(this._windowRemovedId); + global.display.disconnect(this._restackedId); +-- +2.31.1 + diff --git a/SOURCES/desktop-icons-touch-support.patch b/SOURCES/desktop-icons-touch-support.patch new file mode 100644 index 0000000..d596c7f --- /dev/null +++ b/SOURCES/desktop-icons-touch-support.patch @@ -0,0 +1,267 @@ +From bcbf9709802e7644c5911615dabdee7d8ca07719 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Mon, 31 May 2021 19:29:34 +0200 +Subject: [PATCH 1/3] desktopManager: Handle TOUCH_UPDATE/END events explicitly + for rubberband + +These events need specific handling for Wayland, as we do not get emulated +pointer events in that platform. Handle these for rubberband selection. +--- + extensions/desktop-icons/desktopManager.js | 67 ++++++++++++++++------ + 1 file changed, 48 insertions(+), 19 deletions(-) + +diff --git a/extensions/desktop-icons/desktopManager.js b/extensions/desktop-icons/desktopManager.js +index 399aee0..a70cd98 100644 +--- a/extensions/desktop-icons/desktopManager.js ++++ b/extensions/desktop-icons/desktopManager.js +@@ -130,26 +130,49 @@ var DesktopManager = GObject.registerClass({ + } + [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)); +- } ++ this._updateSelection(x, y); + }); ++ this._rubberBandTouchId = global.stage.connect('touch-event', (actor, event) => { ++ // Let x11 pointer emulation do the job on X11 ++ if (!Meta.is_wayland_compositor()) ++ return Clutter.EVENT_PROPAGATE; ++ if (!global.display.is_pointer_emulating_sequence(event.get_event_sequence())) ++ return Clutter.EVENT_PROPAGATE; ++ ++ if (event.type() == Clutter.EventType.TOUCH_END) { ++ this.endRubberBand(); ++ return Clutter.EVENT_STOP; ++ } else if (event.type() == Clutter.EventType.TOUCH_UPDATE) { ++ [x, y] = event.get_coords(); ++ this._updateRubberBand(x, y); ++ this._updateSelection(x, y); ++ return Clutter.EVENT_STOP; ++ } ++ ++ return Clutter.EVENT_PROPAGATE; ++ }); ++ } ++ ++ _updateSelection(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() { +@@ -157,8 +180,10 @@ var DesktopManager = GObject.registerClass({ + Extension.lockActivitiesButton = false; + this._grabHelper.ungrab(); + global.stage.disconnect(this._rubberBandId); ++ global.stage.disconnect(this._rubberBandTouchId); + global.stage.disconnect(this._stageReleaseEventId); + this._rubberBandId = 0; ++ this._rubberBandTouchId = 0; + this._stageReleaseEventId = 0; + + this._selection = new Set([...this._selection, ...this._currentSelection]); +@@ -739,6 +764,10 @@ var DesktopManager = GObject.registerClass({ + global.stage.disconnect(this._rubberBandId); + this._rubberBandId = 0; + ++ if (this._rubberBandTouchId) ++ global.stage.disconnect(this._rubberBandTouchId); ++ this._rubberBandTouchId = 0; ++ + this._rubberBand.destroy(); + + if (this._queryFileInfoCancellable) +-- +2.31.1 + + +From 0733004ffeb517f7a80ff41e7181027e8b92b17e Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Mon, 31 May 2021 19:31:03 +0200 +Subject: [PATCH 2/3] desktopGrid: Handle TOUCH_BEGIN events explicitly + +We do not get pointer emulated events on Wayland, so touch events should +be handled explicitly there. Handle starting rubberband selection via +touch. +--- + extensions/desktop-icons/desktopGrid.js | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/extensions/desktop-icons/desktopGrid.js b/extensions/desktop-icons/desktopGrid.js +index 94d2dfd..602fa7f 100644 +--- a/extensions/desktop-icons/desktopGrid.js ++++ b/extensions/desktop-icons/desktopGrid.js +@@ -21,6 +21,7 @@ const Clutter = imports.gi.Clutter; + const St = imports.gi.St; + const Gio = imports.gi.Gio; + const GLib = imports.gi.GLib; ++const Meta = imports.gi.Meta; + const Shell = imports.gi.Shell; + + const Signals = imports.signals; +@@ -123,6 +124,7 @@ var DesktopGrid = class { + () => this._backgroundDestroyed()); + + this._grid.connect('button-press-event', (actor, event) => this._onPressButton(actor, event)); ++ this._grid.connect('touch-event', (actor, event) => this._onTouchEvent(actor, event)); + + this._grid.connect('key-press-event', this._onKeyPress.bind(this)); + +@@ -506,6 +508,23 @@ var DesktopGrid = class { + return Clutter.EVENT_PROPAGATE; + } + ++ _onTouchEvent(actor, event) { ++ // Let x11 pointer emulation do the job on X11 ++ if (!Meta.is_wayland_compositor()) ++ return Clutter.EVENT_PROPAGATE; ++ ++ if (event.type() == Clutter.EventType.TOUCH_BEGIN && ++ global.display.is_pointer_emulating_sequence(event.get_event_sequence())) { ++ Extension.desktopManager.clearSelection(); ++ let [x, y] = event.get_coords(); ++ let [gridX, gridY] = this._grid.get_transformed_position(); ++ Extension.desktopManager.startRubberBand(x, y, gridX, gridY); ++ return Clutter.EVENT_STOP; ++ } ++ ++ return Clutter.EVENT_PROPAGATE; ++ } ++ + _addDesktopBackgroundMenu() { + this.actor._desktopBackgroundMenu = this._createDesktopBackgroundMenu(); + this.actor._desktopBackgroundManager = new PopupMenu.PopupMenuManager({ actor: this.actor }); +-- +2.31.1 + + +From 2d978ffc58562c4f4d00b1afb03da58be3102e29 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Mon, 31 May 2021 19:31:50 +0200 +Subject: [PATCH 3/3] fileItem: Handle (multi) touch explicitly via touch + events + +Wayland does not get pointer emulated events, so we must handle TOUCH_BEGIN/ +END here for file clicking/tapping to work there. +--- + extensions/desktop-icons/fileItem.js | 34 ++++++++++++++++++++++++---- + 1 file changed, 30 insertions(+), 4 deletions(-) + +diff --git a/extensions/desktop-icons/fileItem.js b/extensions/desktop-icons/fileItem.js +index 143cb9b..1cb47e8 100644 +--- a/extensions/desktop-icons/fileItem.js ++++ b/extensions/desktop-icons/fileItem.js +@@ -117,6 +117,7 @@ var FileItem = class { + this._container.connect('motion-event', (actor, event) => this._onMotion(actor, event)); + this._container.connect('leave-event', (actor, event) => this._onLeave(actor, event)); + this._container.connect('button-release-event', (actor, event) => this._onReleaseButton(actor, event)); ++ this._container.connect('touch-event', (actor, event) => this._onTouchEvent(actor, event)); + + /* Set the metadata and update relevant UI */ + this._updateMetadataFromFileInfo(fileInfo); +@@ -648,16 +649,26 @@ var FileItem = class { + DesktopIconsUtil.launchTerminal(this.file.get_path()); + } + ++ _eventButton(event) { ++ // Emulate button1 press on touch events ++ if (event.type() == Clutter.EventType.TOUCH_BEGIN || ++ event.type() == Clutter.EventType.TOUCH_END || ++ event.type() == Clutter.EventType.TOUCH_UPDATE) ++ return 1; ++ ++ return event.get_button(); ++ } ++ + _updateClickState(event) { + let settings = Clutter.Settings.get_default(); +- if ((event.get_button() == this._lastClickButton) && ++ if ((this._eventButton(event) == this._lastClickButton) && + ((event.get_time() - this._lastClickTime) < settings.double_click_time)) + this._clickCount++; + else + this._clickCount = 1; + + this._lastClickTime = event.get_time(); +- this._lastClickButton = event.get_button(); ++ this._lastClickButton = this._eventButton(event); + } + + _getClickCount() { +@@ -666,7 +677,7 @@ var FileItem = class { + + _onPressButton(actor, event) { + this._updateClickState(event); +- let button = event.get_button(); ++ let button = this._eventButton(event); + if (button == 3) { + if (!this.isSelected) + this.emit('selected', false, false, true); +@@ -725,7 +736,7 @@ var FileItem = class { + } + + _onReleaseButton(actor, event) { +- let button = event.get_button(); ++ let button = this._eventButton(event); + if (button == 1) { + // primaryButtonPressed is TRUE only if the user has pressed the button + // over an icon, and if (s)he has not started a drag&drop operation +@@ -744,6 +755,21 @@ var FileItem = class { + return Clutter.EVENT_PROPAGATE; + } + ++ _onTouchEvent(actor, event) { ++ // Let x11 pointer emulation do the job on X11 ++ if (!Meta.is_wayland_compositor()) ++ return Clutter.EVENT_PROPAGATE; ++ if (!global.display.is_pointer_emulating_sequence(event.get_event_sequence())) ++ return Clutter.EVENT_PROPAGATE; ++ ++ if (event.type() == Clutter.EventType.TOUCH_BEGIN) ++ this._onPressButton(actor, event); ++ else if (event.type() == Clutter.EventType.TOUCH_UPDATE) ++ this._onMotion(actor, event); ++ else if (event.type() == Clutter.EventType.TOUCH_END) ++ this._onReleaseButton(actor, event); ++ } ++ + get savedCoordinates() { + return this._savedCoordinates; + } +-- +2.31.1 + diff --git a/SPECS/gnome-shell-extensions.spec b/SPECS/gnome-shell-extensions.spec index aa2953e..105fbe9 100644 --- a/SPECS/gnome-shell-extensions.spec +++ b/SPECS/gnome-shell-extensions.spec @@ -6,7 +6,7 @@ Name: gnome-shell-extensions Version: 3.32.1 -Release: 15%{?dist} +Release: 20%{?dist} Summary: Modify and extend GNOME Shell functionality and behavior Group: User Interface/Desktops @@ -39,7 +39,13 @@ Patch0010: 0001-window-list-Invalid-current-mode-selected-in-Prefere.pat Patch0011: 0001-Update-desktop-icons-gettext-domain.patch Patch0012: 0001-desktop-icons-Update-Japanese-translation.patch Patch0013: 0001-fileItem-Ignore-double-click-distance-clicking-on-it.patch +Patch0014: 0001-window-list-Leave-fake-overview-when-destroyed.patch +Patch0015: add-workspace-tooltips.patch +Patch0016: 0001-general-launch-only-executable-files.patch +Patch0017: desktop-icons-touch-support.patch Patch0018: 0001-Add-gesture-inhibitor-extension.patch +Patch0019: 0001-top-icons-Don-t-use-wm_class-as-role.patch +Patch0020: 0001-heads-up-display-Add-extension-for-showing-persisten.patch %description GNOME Shell Extensions is a collection of extensions providing additional and @@ -52,6 +58,7 @@ Enabled extensions: * disable-screenshield * desktop-icons * gesture-inhibitor + * heads-up-display * horizontal-workspaces * drive-menu * launch-new-instance @@ -161,6 +168,17 @@ Requires: %{pkg_prefix}-common = %{version}-%{release} This GNOME Shell extension disabled the screen shield if screen locking is disabled. +%package -n %{pkg_prefix}-heads-up-display +Summary: Display persistent on-screen message +Group: User Interface/Desktops +License: GPLv3+ +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-heads-up-display +This GNOME Shell extension displays a persistent message in the top middle of the screen. +This message can appear on the login screen, lock screen, or regular user session. + + %package -n %{pkg_prefix}-horizontal-workspaces Summary: Desktop icons support for the classic experience Group: User Interface/Desktops @@ -408,6 +426,11 @@ cp $RPM_SOURCE_DIR/gnome-classic.desktop $RPM_BUILD_ROOT%{_datadir}/xsessions %{_datadir}/gnome-shell/extensions/disable-screenshield*/ +%files -n %{pkg_prefix}-heads-up-display +%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.heads-up-display.gschema.xml +%{_datadir}/gnome-shell/extensions/heads-up-display*/ + + %files -n %{pkg_prefix}-horizontal-workspaces %{_datadir}/gnome-shell/extensions/horizontal-workspaces*/ @@ -489,9 +512,31 @@ cp $RPM_SOURCE_DIR/gnome-classic.desktop $RPM_BUILD_ROOT%{_datadir}/xsessions %changelog -* Wed Jun 30 2021 Carlos Garnacho - 3.32.1-15 +* Thu Aug 26 2021 Ray Strode - 3.32.1-20 +- Add extension for displaying heads up message + Related: #1651378 + +* Wed Jun 02 2021 Florian Müllner - 3.32.1-19 +- Don't use status icon wm_class as top bar role + Resolves: #1897932 + +* Tue Jun 01 2021 Carlos Garnacho - 3.32.1-18 - Add gesture-inhibitor extension - Resolves: #1962169 + Resolves: #1854679 + +* Tue Jun 01 2021 Carlos Garnacho - 3.32.1-17 +- Handle touchscreens on Wayland in the desktop-icons extension + Resolves: #1924725 + +* Mon May 31 2021 Florian Müllner - 3.32.1-16 +- Fix opening files with (wrongly) set executable bit + Resolves: #1813727 + +* Tue Apr 13 2021 Florian Müllner - 3.32.1-15 +- Fix stuck window picker after screen lock + Resolves: #1905000 +- Add tooltips to workspace previews + Resolves: #1894613 * Wed Jan 27 2021 Carlos Garnacho - 3.32.1-14 - Use same logic than Nautilus for double click/tap in desktop-icons extension