diff --git a/configure.ac b/configure.ac index 5a012ef5..6ef5eb40 100644 --- a/configure.ac +++ b/configure.ac @@ -19,7 +19,7 @@ PKG_PROG_PKG_CONFIG([0.22]) ADDITIONAL_PACKAGES= dnl keep this in sync with extensions/Makefile.am -ALL_EXTENSIONS="example alternate-tab xrandr-indicator" +ALL_EXTENSIONS="example alternate-tab xrandr-indicator windowsNavigator" AC_ARG_ENABLE([extensions], [AS_HELP_STRING([--enable-extensions],[Space separated list of extensions to enable. Default is that all extensions are built.])], [], @@ -54,6 +54,7 @@ AC_CONFIG_FILES([ extensions/Makefile extensions/example/Makefile extensions/alternate-tab/Makefile + extensions/windowsNavigator/Makefile extensions/xrandr-indicator/Makefile po/Makefile.in ]) diff --git a/extensions/Makefile.am b/extensions/Makefile.am index b42f5d7d..e2c5fbac 100644 --- a/extensions/Makefile.am +++ b/extensions/Makefile.am @@ -1,3 +1,3 @@ -DIST_SUBDIRS = example alternate-tab xrandr-indicator +DIST_SUBDIRS = example alternate-tab xrandr-indicator windowsNavigator SUBDIRS = $(ENABLED_EXTENSIONS) diff --git a/extensions/windowsNavigator/Makefile.am b/extensions/windowsNavigator/Makefile.am new file mode 100644 index 00000000..adeb886b --- /dev/null +++ b/extensions/windowsNavigator/Makefile.am @@ -0,0 +1,3 @@ +EXTENSION_ID = windowsNavigator + +include ../../extension.mk diff --git a/extensions/windowsNavigator/extension.js b/extensions/windowsNavigator/extension.js new file mode 100644 index 00000000..7801e536 --- /dev/null +++ b/extensions/windowsNavigator/extension.js @@ -0,0 +1,203 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ +const Clutter = imports.gi.Clutter; +const Lang = imports.lang; +const Mainloop = imports.mainloop; +const St = imports.gi.St; + +const Main = imports.ui.main; +const Workspace = imports.ui.workspace; +const WorkspacesView = imports.ui.workspacesView; + +function injectToFunction(parent, name, func) { + let origin = parent[name]; + parent[name] = function() { + let ret; + ret = origin.apply(this, arguments); + if (ret === undefined) + ret = func.apply(this, arguments); + return ret; + } +} + +function main() { + Workspace.WindowOverlay.prototype.setId = function(id) { + if (this._text.visible && id == null) + this._text.hide(); + this._id = id; + if (id != null) + this._text.text = this._id.toString(); + } + Workspace.WindowOverlay.prototype.getId = function() { + return this._id; + } + Workspace.WindowOverlay.prototype.showTooltip = function() { + if (this._id === null) + return; + this._text.raise_top(); + this._text.show(); + this._text.text = this._id.toString(); + } + Workspace.WindowOverlay.prototype.hideTooltip = function() { + if (this._text.visible) + this._text.hide(); + } + + Workspace.Workspace.prototype.showTooltip = function() { + if (this._tip == null) + return; + if (this.parent) + return; + this.actor.add_actor(this._tip); + this._tip.raise_top(); + } + Workspace.Workspace.prototype.hideTooltip = function() { + if (this._tip == null) + return; + if (!this._tip.get_parent()) + return; + this.actor.remove_actor(this._tip); + } + Workspace.Workspace.prototype.getWindowWithTooltip = function(id) { + for (let i in this._windowOverlays) { + if (this._windowOverlays[i] == null) + continue; + if (this._windowOverlays[i].getId() === id) + return this._windowOverlays[i]._windowClone.metaWindow; + } + return null; + } + Workspace.Workspace.prototype.showWindowsTooltips = function() { + for (let i in this._windowOverlays) { + if (this._windowOverlays[i] != null) + this._windowOverlays[i].showTooltip(); + } + } + Workspace.Workspace.prototype.hideWindowsTooltips = function() { + for (let i in this._windowOverlays) { + if (this._windowOverlays[i] != null) + this._windowOverlays[i].hideTooltip(); + } + } + + WorkspacesView.WorkspacesView.prototype._hideTooltips = function() { + global.stage.set_key_focus(this._prevFocusActor); + this._pickWindow = false; + for (let i = 0; i < this._workspaces.length; i++) + this._workspaces[i].hideWindowsTooltips(); + } + + WorkspacesView.WorkspacesView.prototype._hideWorkspacesTooltips = function() { + global.stage.set_key_focus(this._prevFocusActor); + this._pickWorkspace = false; + for (let i = 0; i < this._workspaces.length; i++) + this._workspaces[i].hideTooltip(); + } + + WorkspacesView.WorkspacesView.prototype._onKeyRelease = function(s, o) { + if (this._pickWindow && o.get_key_symbol() == Clutter.Alt_L) + this._hideTooltips(); + if (this._pickWorkspace && o.get_key_symbol() == Clutter.Control_L) + this._hideWorkspacesTooltips(); + } + WorkspacesView.WorkspacesView.prototype._onKeyPress = function(s, o) { + if (o.get_key_symbol() == Clutter.Alt_L && !this._pickWorkspace) { + this._prevFocusActor = global.stage.get_key_focus(); + global.stage.set_key_focus(null); + this._active = global.screen.get_active_workspace_index(); + this._pickWindow = true; + this._workspaces[global.screen.get_active_workspace_index()].showWindowsTooltips(); + return true; + } + if (o.get_key_symbol() == Clutter.Control_L && !this._pickWindow) { + this._prevFocusActor = global.stage.get_key_focus(); + global.stage.set_key_focus(null); + this._pickWorkspace = true; + for (let i = 0; i < this._workspaces.length; i++) + this._workspaces[i].showTooltip(); + return true; + } + + if (global.stage.get_key_focus() != global.stage) + return false; + + if (this._pickWindow) { + if (this._active != global.screen.get_active_workspace_index()) { + this._hideTooltips(); + return false; + } + let c = o.get_key_unicode(); + if (c > '9'.charCodeAt(0) || c < '0'.charCodeAt(0)) { + this._hideTooltips(); + return false; + } + let win = this._workspaces[this._active].getWindowWithTooltip(c - '0'.charCodeAt(0)); + this._hideTooltips(); + if (win) + Main.activateWindow(win, global.get_current_time()); + return true; + } + if (this._pickWorkspace) { + let c = o.get_key_unicode(); + if (c > '9'.charCodeAt(0) || c < '0'.charCodeAt(0)) { + this._hideWorkspacesTooltips(); + return false; + } + let workspace = this._workspaces[c - '0'.charCodeAt(0) - 1]; + if (workspace !== undefined) + workspace.metaWorkspace.activate(global.get_current_time()); + this._hideWorkspacesTooltips(); + return true; + } + return false; + } + + injectToFunction(Workspace.WindowOverlay.prototype, '_init', function(windowClone, parentActor) { + this._id = null; + this._text = new St.Label({ style_class: 'window-tooltip' }); + this._text.hide(); + parentActor.add_actor(this._text); + }); + injectToFunction(Workspace.WindowOverlay.prototype, 'updatePositions', function(cloneX, cloneY, cloneWidth, cloneHeight) { + let textX = cloneX - 2; + let textY = cloneY - 2; + this._text.set_position(Math.floor(textX), Math.floor(textY)); + this._text.raise_top(); + }); + injectToFunction(Workspace.Workspace.prototype, '_init', function(metaWorkspace) { + if (metaWorkspace.index() < 9) { + this._tip = new St.Label({ style_class: 'window-tooltip', + text: (metaWorkspace.index() + 1).toString() }); + this.actor.connect('notify::scale-x', Lang.bind(this, function() { + this._tip.set_scale(1 / this.actor.scale_x, 1 / this.actor.scale_x); + })); + } else + this._tip = null; + }); + injectToFunction(Workspace.Workspace.prototype, 'positionWindows', function(flags) { + let visibleClones = this._getVisibleClones(); + if (this._reservedSlot) + visibleClones.push(this._reservedSlot); + + let slots = this._computeAllWindowSlots(visibleClones.length); + visibleClones = this._orderWindowsByMotionAndStartup(visibleClones, slots); + for (let i = 0; i < visibleClones.length; i++) { + let clone = visibleClones[i]; + let metaWindow = clone.metaWindow; + let mainIndex = this._lookupIndex(metaWindow); + let overlay = this._windowOverlays[mainIndex]; + if (overlay) + overlay.setId(i < 9 ? i + 1 : null); + } + }); + + injectToFunction(WorkspacesView.WorkspacesView.prototype, '_init', function(width, height, x, y, workspaces) { + this._pickWorkspace = false; + this._pickWindow = false; + this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._onKeyPress)); + this._keyReleaseEventId = global.stage.connect('key-release-event', Lang.bind(this, this._onKeyRelease)); + }); + injectToFunction(WorkspacesView.WorkspacesView.prototype, '_onDestroy', function() { + global.stage.disconnect(this._keyPressEventId); + global.stage.disconnect(this._keyReleaseEventId); + }); +} diff --git a/extensions/windowsNavigator/metadata.json b/extensions/windowsNavigator/metadata.json new file mode 100644 index 00000000..50150ea5 --- /dev/null +++ b/extensions/windowsNavigator/metadata.json @@ -0,0 +1,7 @@ +{ + "shell-version": ["2.91.5"], + "uuid": "windowsNavigator@gnome-shell-extensions.gnome.org", + "original-author": "zaspire@rambler.ru", + "name": "windowNavigator", + "description": "Allow keyboard selection of windows and workspaces in overlay mode" +} \ No newline at end of file diff --git a/extensions/windowsNavigator/stylesheet.css b/extensions/windowsNavigator/stylesheet.css new file mode 100644 index 00000000..64066522 --- /dev/null +++ b/extensions/windowsNavigator/stylesheet.css @@ -0,0 +1,9 @@ +.window-tooltip { + color: #ff0000; + background: rgba(0,0,0,0.8); + border: 1px solid rgba(128,128,128,0.40); + border-radius: 10px; + font-size: 16px; + padding: 2px 8px; + -shell-caption-spacing: 4px; +} \ No newline at end of file