diff --git a/Makefile b/Makefile index 13ca75e..6b05729 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ UUID = dash-to-panel@jderose9.github.com BASE_MODULES = extension.js stylesheet.css metadata.json COPYING README.md -EXTRA_MODULES = appIcons.js convenience.js panel.js panelManager.js intellihide.js panelStyle.js overview.js taskbar.js windowPreview.js prefs.js utils.js Settings.ui +EXTRA_MODULES = appIcons.js convenience.js panel.js panelManager.js proximity.js intellihide.js panelStyle.js overview.js taskbar.js windowPreview.js prefs.js utils.js Settings.ui EXTRA_IMAGES = highlight_stacked_bg.svg TOLOCALIZE = prefs.js appIcons.js MSGSRC = $(wildcard po/*.po) diff --git a/Settings.ui b/Settings.ui index 6d50d03..a6e31fa 100644 --- a/Settings.ui +++ b/Settings.ui @@ -3950,6 +3950,29 @@ 1 + + + True + False + 0 + in + + + True + False + none + + + + + + + + False + True + 2 + + 1 diff --git a/intellihide.js b/intellihide.js index 3a609e2..a2e55e1 100644 --- a/intellihide.js +++ b/intellihide.js @@ -27,6 +27,7 @@ const PointerWatcher = imports.ui.pointerWatcher; const Tweener = imports.ui.tweener; const Me = imports.misc.extensionUtils.getCurrentExtension(); +const Proximity = Me.imports.proximity; const Utils = Me.imports.utils; //timeout intervals @@ -47,6 +48,7 @@ var Intellihide = new Lang.Class({ this._dtpPanel = dtpPanel; this._dtpSettings = dtpPanel._dtpSettings; this._panelBox = dtpPanel.panelBox; + this._proximityManager = dtpPanel.panelManager.proximityManager; this._signalsHandler = new Utils.GlobalSignalsHandler(); this._timeoutsHandler = new Utils.TimeoutsHandler(); @@ -61,7 +63,6 @@ var Intellihide = new Lang.Class({ enable: function(reset) { this._enabled = true; this._monitor = this._dtpPanel.monitor; - this._focusedWindowInfo = null; this._animationDestination = -1; this._pendingUpdate = false; this._dragging = false; @@ -79,8 +80,15 @@ var Intellihide = new Lang.Class({ this._bindGeneralSignals(); if (this._dtpSettings.get_boolean('intellihide-hide-from-windows')) { - this._bindWindowSignals(); - this._setFocusedWindow(); + this._proximityWatchId = this._proximityManager.createWatch( + this._panelBox, + Proximity.Mode[this._dtpSettings.get_string('intellihide-behaviour')], + 0, + overlap => { + this._windowOverlap = overlap; + this._queueUpdatePanelPosition(); + } + ); } this._setRevealMechanism(); @@ -92,8 +100,9 @@ var Intellihide = new Lang.Class({ this._dtpSettings.disconnect(this._intellihideChangedId); } + this._proximityManager.removeWatch(this._proximityWatchId); + this._setTrackPanel(reset, false); - this._disconnectFocusedWindow(); this._signalsHandler.destroy(); this._timeoutsHandler.destroy(); @@ -145,15 +154,11 @@ var Intellihide = new Lang.Class({ 'changed::panel-position', 'changed::panel-size', 'changed::intellihide-use-pressure', - 'changed::intellihide-hide-from-windows' + 'changed::intellihide-hide-from-windows', + 'changed::intellihide-behaviour' ], () => this._reset() ], - [ - Utils.DisplayWrapper.getScreen(), - 'restacked', - () => this._queueUpdatePanelPosition() - ], [ Main.layoutManager, 'monitors-changed', @@ -188,32 +193,6 @@ var Intellihide = new Lang.Class({ ); }, - _bindWindowSignals: function() { - this._signalsHandler.add( - [ - global.display, - 'notify::focus-window', - () => { - this._setFocusedWindow(); - this._queueUpdatePanelPosition(); - } - ], - [ - global.window_group, - [ - 'actor-added', - 'actor-removed' - ], - () => this._queueUpdatePanelPosition() - ], - [ - this._dtpSettings, - 'changed::intellihide-behaviour', - () => this._queueUpdatePanelPosition() - ] - ); - }, - _setTrackPanel: function(reset, enable) { if (!reset) { Main.layoutManager._untrackActor(this._panelBox); @@ -283,66 +262,6 @@ var Intellihide = new Lang.Class({ } }, - _setFocusedWindow: function() { - this._disconnectFocusedWindow(); - - let focusedWindow = global.display.focus_window; - - if (focusedWindow) { - let focusedWindowInfo = this._getFocusedWindowInfo(focusedWindow); - - if (this._checkIfHandledWindowType(focusedWindowInfo.metaWindow)) { - focusedWindowInfo.id = focusedWindowInfo.window.connect('allocation-changed', () => this._queueUpdatePanelPosition()) - this._focusedWindowInfo = focusedWindowInfo; - } - } - }, - - _getFocusedWindowInfo: function(focusedWindow) { - let focusedWindowInfo = { window: focusedWindow.get_compositor_private() }; - - focusedWindowInfo.metaWindow = focusedWindowInfo.window.get_meta_window(); - - if (focusedWindow.is_attached_dialog()) { - let mainMetaWindow = focusedWindow.get_transient_for(); - - if (focusedWindowInfo.metaWindow.get_frame_rect().height < mainMetaWindow.get_frame_rect().height) { - focusedWindowInfo.window = mainMetaWindow.get_compositor_private(); - focusedWindowInfo.metaWindow = mainMetaWindow; - } - } - - return focusedWindowInfo; - }, - - _disconnectFocusedWindow: function() { - if (this._focusedWindowInfo) { - this._focusedWindowInfo.window.disconnect(this._focusedWindowInfo.id); - this._focusedWindowInfo = null; - } - }, - - _getHandledWindows: function() { - return global.get_window_actors() - .map(w => w.get_meta_window()) - .filter(mw => this._checkIfHandledWindow(mw)); - }, - - _checkIfHandledWindow: function(metaWindow) { - return metaWindow && !metaWindow.minimized && - metaWindow.get_workspace().index() == Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace_index() && - metaWindow.get_monitor() == this._monitor.index && - this._checkIfHandledWindowType(metaWindow); - }, - - _checkIfHandledWindowType: function(metaWindow) { - let metaWindowType = metaWindow.get_window_type(); - - //https://www.roojs.org/seed/gir-1.2-gtk-3.0/seed/Meta.WindowType.html - return metaWindowType <= Meta.WindowType.SPLASHSCREEN && - metaWindowType != Meta.WindowType.DESKTOP; - }, - _queueUpdatePanelPosition: function(fromRevealMechanism) { if (!fromRevealMechanism && this._timeoutsHandler.getId(T2) && !Main.overview.visible) { //unless this is a mouse interaction or entering/leaving the overview, limit the number @@ -379,34 +298,7 @@ var Intellihide = new Lang.Class({ return this._panelBox.hover; } - let behaviour = this._dtpSettings.get_string('intellihide-behaviour'); - - if (behaviour === 'FOCUSED_WINDOWS') { - return !(this._focusedWindowInfo && - this._checkIfHandledWindow(this._focusedWindowInfo.metaWindow) && - this._checkIfWindowObstructs(this._focusedWindowInfo.metaWindow)); - } - - let metaWindows = this._getHandledWindows(); - - if (behaviour === 'MAXIMIZED_WINDOWS') { - return !metaWindows.some(mw => mw.maximized_vertically && mw.maximized_horizontally); - } else { //ALL_WINDOWS - return !metaWindows.some(mw => this._checkIfWindowObstructs(mw)); - } - }, - - _checkIfWindowObstructs: function(metaWindow) { - let windowRect = metaWindow.get_frame_rect(); - - if (this._panelAtTop) { - return windowRect.y <= this._monitor.y + this._panelBox.height; - } - - let windowBottom = windowRect.y + windowRect.height; - let panelTop = this._monitor.y + this._monitor.height - this._panelBox.height; - - return windowBottom >= panelTop; + return !this._windowOverlap; }, _checkIfGrab: function() { @@ -421,7 +313,8 @@ var Intellihide = new Lang.Class({ }, _adjustDynamicTransparency: function() { - this._invokeIfExists(this._dtpPanel.panel._updateSolidStyle); + if (this._dtpPanel.panel.hasOwnProperty('_updateSolidStyle')) + this._invokeIfExists(this._dtpPanel.panel._updateSolidStyle); }, _revealPanel: function(immediate) { diff --git a/panel.js b/panel.js index 721ff06..419e4eb 100644 --- a/panel.js +++ b/panel.js @@ -322,7 +322,6 @@ var dtpPanelWrapper = new Lang.Class({ if (!this.isSecondary) { this.panel.actor.set_height(this._oldPanelHeight); - this.panelBox.set_anchor_point(0, 0); Main.overview._panelGhost.set_height(this._oldPanelHeight); this._setActivitiesButtonVisible(true); @@ -506,7 +505,7 @@ var dtpPanelWrapper = new Lang.Class({ this._myPanelGhost.set_height(isTop ? 0 : size); if(isTop) { - this.panelBox.set_anchor_point(0, 0); + this.panelBox.set_position(this.monitor.x, this.monitor.y); this._removeTopLimit(); @@ -517,7 +516,7 @@ var dtpPanelWrapper = new Lang.Class({ if(!this.panel.actor.has_style_class_name('dashtopanelTop')) this.panel.actor.add_style_class_name('dashtopanelTop'); } else { - this.panelBox.set_anchor_point(0,(-1)*(this.monitor.height-this.panelBox.height)); + this.panelBox.set_position(this.monitor.x, this.monitor.y + this.monitor.height - this.panelBox.height); if (!this._topLimit) { this._topLimit = new St.BoxLayout({ name: 'topLimit', vertical: true }); @@ -780,12 +779,14 @@ var dtpSecondaryPanelBoxWrapper = new Lang.Class({ var dtpSecondaryPanel = new Lang.Class({ Name: 'DashToPanel.SecondaryPanel', - Extends: Panel.Panel, + Extends: St.Widget, _init : function(settings, monitor) { + this.parent({ name: 'panel', reactive: true }); + this._dtpSettings = settings; - this.actor = new Shell.GenericContainer({ name: 'panel', reactive: true }); + this.actor = this; this.actor._delegate = this; this._sessionStyle = null; diff --git a/panelManager.js b/panelManager.js index 3485140..9ebabdf 100644 --- a/panelManager.js +++ b/panelManager.js @@ -30,6 +30,7 @@ const Me = imports.misc.extensionUtils.getCurrentExtension(); const Overview = Me.imports.overview; const Panel = Me.imports.panel; +const Proximity = Me.imports.proximity; const Utils = Me.imports.utils; const Clutter = imports.gi.Clutter; @@ -53,10 +54,9 @@ var dtpPanelManager = new Lang.Class({ enable: function(reset) { let dtpPrimaryMonitor = Main.layoutManager.monitors[(Main.layoutManager.primaryIndex + this._dtpSettings.get_int('primary-monitor')) % Main.layoutManager.monitors.length]; - + this.proximityManager = new Proximity.ProximityManager(); + this.primaryPanel = new Panel.dtpPanelWrapper(this, dtpPrimaryMonitor, Main.panel, Main.layoutManager.panelBox); - Main.layoutManager.panelBox.set_position(dtpPrimaryMonitor.x, dtpPrimaryMonitor.y); - Main.layoutManager.panelBox.set_size(dtpPrimaryMonitor.width, -1); this.primaryPanel.enable(); this.allPanels = [ this.primaryPanel ]; @@ -73,8 +73,6 @@ var dtpPanelManager = new Lang.Class({ let panel = new Panel.dtpSecondaryPanel(this._dtpSettings, monitor); panelBox.add(panel.actor); - - panelBox.set_position(monitor.x, monitor.y); panelBox.set_size(monitor.width, -1); let panelWrapper = new Panel.dtpPanelWrapper(this, monitor, panel, panelBox, true); @@ -127,6 +125,7 @@ var dtpPanelManager = new Lang.Class({ disable: function(reset) { this._overview.disable(); + this.proximityManager.destroy(); this.allPanels.forEach(p => { p.disable(); diff --git a/proximity.js b/proximity.js new file mode 100644 index 0000000..5122ad8 --- /dev/null +++ b/proximity.js @@ -0,0 +1,251 @@ +/* + * This file is part of the Dash-To-Panel extension for Gnome 3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +const Lang = imports.lang; +const Meta = imports.gi.Meta; + +const Layout = imports.ui.layout; +const Main = imports.ui.main; + +const Me = imports.misc.extensionUtils.getCurrentExtension(); +const Utils = Me.imports.utils; + +//timeout intervals +const MIN_UPDATE_MS = 200; + +//timeout names +const T1 = 'limitUpdateTimeout'; + +var Mode = { + ALL_WINDOWS: 0, + FOCUSED_WINDOWS: 1, + MAXIMIZED_WINDOWS: 2 +}; + +var ProximityWatch = new Lang.Class({ + Name: 'DashToPanel.ProximityWatch', + + _init: function(actor, mode, threshold, handler) { + this.actor = actor; + this.mode = mode; + this.threshold = threshold; + this.handler = handler; + + this._allocationChangedId = actor.connect('allocation-changed', () => this._update()); + + this._update(); + }, + + destroy: function() { + this.actor.disconnect(this._allocationChangedId); + }, + + _update: function() { + let [actorX, actorY] = this.actor.get_position(); + + this.actorX = actorX; + this.actorY = actorY; + this.monitorIndex = Main.layoutManager.findIndexForActor(this.actor); + + this._updateWatchRect(); + }, + + _updateWatchRect: function() { + this.rect = new Meta.Rectangle({ + x: this.actorX - this.threshold, + y: this.actorY - this.threshold, + width: this.actor.width + this.threshold * 2, + height: this.actor.height + this.threshold * 2 + }); + }, +}); + +var ProximityManager = new Lang.Class({ + Name: 'DashToPanel.ProximityManager', + + _init: function() { + this._counter = 1; + this._watches = {}; + this._focusedWindowInfo = null; + + this._signalsHandler = new Utils.GlobalSignalsHandler(); + this._timeoutsHandler = new Utils.TimeoutsHandler(); + + this._bindSignals(); + this._setFocusedWindow(); + }, + + createWatch: function(actor, mode, threshold, handler) { + let watch = new ProximityWatch(actor, mode, threshold, handler); + + this._watches[this._counter] = watch; + this.update(); + + return this._counter++; + }, + + removeWatch: function(id) { + if (this._watches[id]) { + this._watches[id].destroy(); + delete this._watches[id]; + } + }, + + update() { + this._queueUpdate(true); + }, + + destroy: function() { + this._signalsHandler.destroy(); + this._timeoutsHandler.destroy(); + this._disconnectFocusedWindow(); + Object.keys(this._watches).forEach(id => this.removeWatch(id)); + }, + + _bindSignals: function() { + this._signalsHandler.add( + [ + Utils.DisplayWrapper.getScreen(), + 'restacked', + () => this._queueUpdate() + ], + [ + global.display, + 'notify::focus-window', + () => { + this._setFocusedWindow(); + this._queueUpdate(); + } + ], + [ + global.window_group, + [ + 'actor-added', + 'actor-removed' + ], + () => this._queueUpdate() + ] + ); + }, + + _setFocusedWindow: function() { + this._disconnectFocusedWindow(); + + let focusedWindow = global.display.focus_window; + + if (focusedWindow) { + let focusedWindowInfo = this._getFocusedWindowInfo(focusedWindow); + + if (this._checkIfHandledWindowType(focusedWindowInfo.metaWindow)) { + focusedWindowInfo.id = focusedWindowInfo.window.connect('allocation-changed', () => this._queueUpdate()) + this._focusedWindowInfo = focusedWindowInfo; + } + } + }, + + _getFocusedWindowInfo: function(focusedWindow) { + let focusedWindowInfo = { window: focusedWindow.get_compositor_private() }; + + focusedWindowInfo.metaWindow = focusedWindowInfo.window.get_meta_window(); + + if (focusedWindow.is_attached_dialog()) { + let mainMetaWindow = focusedWindow.get_transient_for(); + + if (focusedWindowInfo.metaWindow.get_frame_rect().height < mainMetaWindow.get_frame_rect().height) { + focusedWindowInfo.window = mainMetaWindow.get_compositor_private(); + focusedWindowInfo.metaWindow = mainMetaWindow; + } + } + + return focusedWindowInfo; + }, + + _disconnectFocusedWindow: function() { + if (this._focusedWindowInfo) { + this._focusedWindowInfo.window.disconnect(this._focusedWindowInfo.id); + this._focusedWindowInfo = null; + } + }, + + _getHandledWindows: function() { + return global.get_window_actors() + .map(w => w.get_meta_window()) + .filter(mw => this._checkIfHandledWindow(mw)); + }, + + _checkIfHandledWindow: function(metaWindow) { + return metaWindow && !metaWindow.minimized && + // metaWindow.get_workspace().index() == Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace_index() && + // metaWindow.get_monitor() == this._monitor.index && + this._checkIfHandledWindowType(metaWindow); + }, + + _checkIfHandledWindowType: function(metaWindow) { + let metaWindowType = metaWindow.get_window_type(); + + //https://www.roojs.org/seed/gir-1.2-gtk-3.0/seed/Meta.WindowType.html + return metaWindowType <= Meta.WindowType.SPLASHSCREEN && + metaWindowType != Meta.WindowType.DESKTOP; + }, + + _queueUpdate: function(noDelay) { + if (!noDelay && this._timeoutsHandler.getId(T1)) { + //limit the number of updates + return this._pendingUpdate = true; + } + + this._timeoutsHandler.add([T1, MIN_UPDATE_MS, () => this._endLimitUpdate()]); + + let metaWindows = this._getHandledWindows(); + + Object.keys(this._watches).forEach(id => { + let watch = this._watches[id]; + let overlap = this._update(watch, metaWindows); + + if (overlap !== watch.overlap) { + watch.handler(overlap); + watch.overlap = overlap; + } + }); + }, + + _endLimitUpdate: function() { + if (this._pendingUpdate) { + this._pendingUpdate = false; + this._queueUpdate(); + } + }, + + _update: function(watch, metaWindows) { + if (watch.mode === Mode.FOCUSED_WINDOWS) { + return (this._focusedWindowInfo && + this._checkIfHandledWindow(this._focusedWindowInfo.metaWindow) && + this._checkProximity(this._focusedWindowInfo.metaWindow, watch)); + } + + if (watch.mode === Mode.MAXIMIZED_WINDOWS) { + return metaWindows.some(mw => mw.maximized_vertically && mw.maximized_horizontally && + mw.get_monitor() == watch.monitorIndex); + } else if (watch.mode === Mode.ALL_WINDOWS) { + return metaWindows.some(mw => this._checkProximity(mw, watch)); + } + }, + + _checkProximity: function(metaWindow, watch) { + return metaWindow.get_frame_rect().overlap(watch.rect); + }, +}); \ No newline at end of file