Files
dash-to-panel/intellihide.js

401 lines
14 KiB
JavaScript
Raw Normal View History

2018-03-10 12:08:22 -06:00
/*
* 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 <http://www.gnu.org/licenses/>.
*/
const Lang = imports.lang;
const Clutter = imports.gi.Clutter;
2018-03-10 12:08:22 -06:00
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
2018-03-10 12:08:22 -06:00
2020-06-13 07:40:33 -04:00
var GrabHelper = imports.ui.grabHelper;
2018-03-10 12:08:22 -06:00
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const OverviewControls = imports.ui.overviewControls;
2018-03-10 12:08:22 -06:00
const PointerWatcher = imports.ui.pointerWatcher;
const Me = imports.misc.extensionUtils.getCurrentExtension();
const Panel = Me.imports.panel;
const Proximity = Me.imports.proximity;
const Utils = Me.imports.utils;
2018-03-10 12:08:22 -06:00
//timeout intervals
const CHECK_POINTER_MS = 200;
const CHECK_GRAB_MS = 400;
2018-03-10 12:08:22 -06:00
const POST_ANIMATE_MS = 50;
const MIN_UPDATE_MS = 250;
//timeout names
const T1 = 'checkGrabTimeout';
2018-03-10 12:08:22 -06:00
const T2 = 'limitUpdateTimeout';
const T3 = 'postAnimateTimeout';
const T4 = 'panelBoxClipTimeout';
2018-03-10 12:08:22 -06:00
2019-09-25 15:57:45 -04:00
var SIDE_CONTROLS_ANIMATION_TIME = OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / (OverviewControls.SIDE_CONTROLS_ANIMATION_TIME > 1 ? 1000 : 1);
var Hold = {
NONE: 0,
TEMPORARY: 1,
PERMANENT: 2
};
2019-02-12 21:49:20 -05:00
var Intellihide = Utils.defineClass({
2018-03-10 12:08:22 -06:00
Name: 'DashToPanel.Intellihide',
_init: function(dtpPanel) {
this._dtpPanel = dtpPanel;
this._panelBox = dtpPanel.panelBox;
this._panelManager = dtpPanel.panelManager;
this._proximityManager = this._panelManager.proximityManager;
this._holdStatus = Hold.NONE;
2018-03-10 12:08:22 -06:00
this._signalsHandler = new Utils.GlobalSignalsHandler();
this._timeoutsHandler = new Utils.TimeoutsHandler();
2018-03-10 12:08:22 -06:00
2019-08-31 15:50:53 -04:00
this._intellihideChangedId = Me.settings.connect('changed::intellihide', () => this._changeEnabledStatus());
this._intellihideOnlySecondaryChangedId = Me.settings.connect('changed::intellihide-only-secondary', () => this._changeEnabledStatus());
2018-03-10 12:08:22 -06:00
2019-08-08 17:54:38 -04:00
this.enabled = false;
this._changeEnabledStatus();
2018-03-10 12:08:22 -06:00
},
enable: function() {
2019-08-08 17:54:38 -04:00
this.enabled = true;
2018-10-02 21:13:52 -04:00
this._monitor = this._dtpPanel.monitor;
2018-03-10 12:08:22 -06:00
this._animationDestination = -1;
this._pendingUpdate = false;
this._hoveredOut = false;
2018-10-13 12:59:45 -04:00
this._windowOverlap = false;
2020-06-13 07:40:33 -04:00
this._translationProp = 'translation_' + (this._dtpPanel.checkIfVertical() ? 'x' : 'y');
2018-03-10 12:08:22 -06:00
this._panelBox.translation_y = 0;
this._panelBox.translation_x = 0;
2018-03-10 12:08:22 -06:00
this._setTrackPanel(true);
2018-03-10 12:08:22 -06:00
this._bindGeneralSignals();
2019-08-31 15:50:53 -04:00
if (Me.settings.get_boolean('intellihide-hide-from-windows')) {
this._proximityWatchId = this._proximityManager.createWatch(
this._panelBox.get_parent(),
2019-08-31 15:50:53 -04:00
Proximity.Mode[Me.settings.get_string('intellihide-behaviour')],
0, 0,
overlap => {
this._windowOverlap = overlap;
this._queueUpdatePanelPosition();
}
);
2018-03-10 12:08:22 -06:00
}
this._setRevealMechanism();
this._queueUpdatePanelPosition();
},
disable: function(reset) {
if (this._proximityWatchId) {
this._proximityManager.removeWatch(this._proximityWatchId);
}
this._setTrackPanel(false);
2018-03-10 12:08:22 -06:00
this._signalsHandler.destroy();
this._timeoutsHandler.destroy();
this._removeRevealMechanism();
this._revealPanel(!reset);
2019-08-08 17:54:38 -04:00
this.enabled = false;
2018-03-10 12:08:22 -06:00
},
destroy: function() {
2019-08-31 15:50:53 -04:00
Me.settings.disconnect(this._intellihideChangedId);
Me.settings.disconnect(this._intellihideOnlySecondaryChangedId);
2019-06-23 23:56:49 -04:00
2019-08-08 17:54:38 -04:00
if (this.enabled) {
2019-06-23 23:56:49 -04:00
this.disable();
}
2018-03-10 12:08:22 -06:00
},
toggle: function() {
this[this._holdStatus & Hold.PERMANENT ? 'release' : 'revealAndHold'](Hold.PERMANENT);
},
revealAndHold: function(holdStatus) {
2019-08-08 17:54:38 -04:00
if (this.enabled && !this._holdStatus) {
this._revealPanel();
}
this._holdStatus |= holdStatus;
},
release: function(holdStatus) {
this._holdStatus -= holdStatus;
2019-08-08 17:54:38 -04:00
if (this.enabled && !this._holdStatus) {
this._queueUpdatePanelPosition();
}
},
reset: function() {
2018-03-10 12:08:22 -06:00
this.disable(true);
this.enable();
2018-03-10 12:08:22 -06:00
},
_changeEnabledStatus: function() {
2019-08-31 15:50:53 -04:00
let intellihide = Me.settings.get_boolean('intellihide');
let onlySecondary = Me.settings.get_boolean('intellihide-only-secondary');
let enabled = intellihide && !(this._dtpPanel.isPrimary && onlySecondary);
2019-08-08 17:54:38 -04:00
if (this.enabled !== enabled) {
this[enabled ? 'enable' : 'disable']();
}
2018-03-10 12:08:22 -06:00
},
_bindGeneralSignals: function() {
this._signalsHandler.add(
[
this._dtpPanel.taskbar,
'menu-closed',
() => this._panelBox.sync_hover()
],
[
2019-08-31 15:50:53 -04:00
Me.settings,
2018-03-10 12:08:22 -06:00
[
'changed::intellihide-use-pressure',
'changed::intellihide-hide-from-windows',
'changed::intellihide-behaviour',
'changed::intellihide-pressure-threshold',
'changed::intellihide-pressure-time'
2018-03-10 12:08:22 -06:00
],
() => this.reset()
],
2018-03-10 12:08:22 -06:00
[
this._panelBox,
'notify::hover',
2019-05-17 15:40:00 -04:00
() => this._onHoverChanged()
],
[
this._dtpPanel.taskbar.previewMenu,
'open-state-changed',
() => this._queueUpdatePanelPosition()
2018-03-20 07:31:14 -06:00
],
[
Main.overview,
[
'showing',
'hiding'
2018-03-20 07:31:14 -06:00
],
() => this._queueUpdatePanelPosition()
2018-03-10 12:08:22 -06:00
]
);
},
2019-05-17 15:40:00 -04:00
_onHoverChanged: function() {
this._hoveredOut = !this._panelBox.hover;
2019-05-17 15:40:00 -04:00
this._queueUpdatePanelPosition();
},
_setTrackPanel: function(enable) {
let trackedIndex = Main.layoutManager._findActor(this._panelBox);
let actorData = Main.layoutManager._trackedActors[trackedIndex]
2019-06-23 23:56:49 -04:00
actorData.affectsStruts = !enable;
actorData.trackFullscreen = !enable;
this._panelBox.track_hover = enable;
this._panelBox.reactive = enable;
this._panelBox.visible = enable ? enable : this._panelBox.visible;
Main.layoutManager._queueUpdateRegions();
2018-03-10 12:08:22 -06:00
},
_setRevealMechanism: function() {
2019-08-31 15:50:53 -04:00
if (global.display.supports_extended_barriers() && Me.settings.get_boolean('intellihide-use-pressure')) {
this._edgeBarrier = this._createBarrier();
2018-03-10 12:08:22 -06:00
this._pressureBarrier = new Layout.PressureBarrier(
2019-08-31 15:50:53 -04:00
Me.settings.get_int('intellihide-pressure-threshold'),
Me.settings.get_int('intellihide-pressure-time'),
Shell.ActionMode.NORMAL
2018-03-10 12:08:22 -06:00
);
this._pressureBarrier.addBarrier(this._edgeBarrier);
2018-03-10 12:08:22 -06:00
this._signalsHandler.add([this._pressureBarrier, 'trigger', () => this._queueUpdatePanelPosition(true)]);
} else {
this._pointerWatch = PointerWatcher.getPointerWatcher()
.addWatch(CHECK_POINTER_MS, (x, y) => this._checkMousePointer(x, y));
}
},
_removeRevealMechanism: function() {
if (this._pointerWatch) {
PointerWatcher.getPointerWatcher()._removeWatch(this._pointerWatch);
}
if (this._pressureBarrier) {
this._pressureBarrier.destroy();
this._edgeBarrier.destroy();
2018-03-10 12:08:22 -06:00
}
},
_createBarrier: function() {
let position = this._dtpPanel.geom.position;
let opts = { display: global.display };
2018-03-10 12:08:22 -06:00
2020-06-13 07:40:33 -04:00
if (this._dtpPanel.checkIfVertical()) {
2018-10-02 21:13:52 -04:00
opts.y1 = this._monitor.y;
opts.y2 = this._monitor.y + this._monitor.height;
opts.x1 = opts.x2 = this._monitor.x;
2018-03-10 12:08:22 -06:00
} else {
opts.x1 = this._monitor.x;
opts.x2 = this._monitor.x + this._monitor.width;
opts.y1 = opts.y2 = this._monitor.y;
}
2018-03-10 12:08:22 -06:00
if (position == St.Side.TOP) {
opts.directions = Meta.BarrierDirection.POSITIVE_Y;
} else if (position == St.Side.BOTTOM) {
opts.y1 = opts.y2 = opts.y1 + this._monitor.height;
2018-03-10 12:08:22 -06:00
opts.directions = Meta.BarrierDirection.NEGATIVE_Y;
} else if (position == St.Side.LEFT) {
opts.directions = Meta.BarrierDirection.POSITIVE_X;
} else {
opts.x1 = opts.x2 = opts.x1 + this._monitor.width;
opts.directions = Meta.BarrierDirection.NEGATIVE_X;
2018-03-10 12:08:22 -06:00
}
return new Meta.Barrier(opts);
},
_checkMousePointer: function(x, y) {
let position = this._dtpPanel.geom.position;
if (!this._panelBox.hover && !Main.overview.visible &&
((position == St.Side.TOP && y <= this._monitor.y + 1) ||
(position == St.Side.BOTTOM && y >= this._monitor.y + this._monitor.height - 1) ||
(position == St.Side.LEFT && x <= this._monitor.x + 1) ||
(position == St.Side.RIGHT && x >= this._monitor.x + this._monitor.width - 1)) &&
((x >= this._monitor.x && x < this._monitor.x + this._monitor.width) &&
(y >= this._monitor.y && y < this._monitor.y + this._monitor.height))) {
2018-03-10 12:08:22 -06:00
this._queueUpdatePanelPosition(true);
}
},
_queueUpdatePanelPosition: function(fromRevealMechanism) {
2018-03-24 13:13:57 -06:00
if (!fromRevealMechanism && this._timeoutsHandler.getId(T2) && !Main.overview.visible) {
//unless this is a mouse interaction or entering/leaving the overview, limit the number
//of updates, but remember to update again when the limit timeout is reached
2018-03-10 12:08:22 -06:00
this._pendingUpdate = true;
} else if (!this._holdStatus) {
2018-03-10 12:08:22 -06:00
this._checkIfShouldBeVisible(fromRevealMechanism) ? this._revealPanel() : this._hidePanel();
this._timeoutsHandler.add([T2, MIN_UPDATE_MS, () => this._endLimitUpdate()]);
}
},
_endLimitUpdate: function() {
if (this._pendingUpdate) {
this._pendingUpdate = false;
this._queueUpdatePanelPosition();
}
},
_checkIfShouldBeVisible: function(fromRevealMechanism) {
if (Main.overview.visibleTarget || this._dtpPanel.taskbar.previewMenu.opened ||
this._panelBox.get_hover() || this._checkIfGrab()) {
return true;
}
2018-03-10 12:08:22 -06:00
if (fromRevealMechanism) {
let mouseBtnIsPressed = global.get_pointer()[2] & Clutter.ModifierType.BUTTON1_MASK;
2018-03-10 12:08:22 -06:00
//the user is trying to reveal the panel
if (this._monitor.inFullscreen && !mouseBtnIsPressed) {
2019-08-31 15:50:53 -04:00
return Me.settings.get_boolean('intellihide-show-in-fullscreen');
2018-03-10 12:08:22 -06:00
}
return !mouseBtnIsPressed;
2018-03-10 12:08:22 -06:00
}
2019-08-31 15:50:53 -04:00
if (!Me.settings.get_boolean('intellihide-hide-from-windows')) {
2018-03-10 12:08:22 -06:00
return this._panelBox.hover;
}
return !this._windowOverlap;
2018-03-10 12:08:22 -06:00
},
_checkIfGrab: function() {
if (GrabHelper._grabHelperStack && GrabHelper._grabHelperStack.some(gh => gh._owner == this._dtpPanel.panel.actor)) {
//there currently is a grab on a child of the panel, check again soon to catch its release
this._timeoutsHandler.add([T1, CHECK_GRAB_MS, () => this._queueUpdatePanelPosition()]);
return true;
}
},
2018-03-10 12:08:22 -06:00
_revealPanel: function(immediate) {
if (!this._panelBox.visible) {
this._panelBox.visible = true;
this._dtpPanel.taskbar._shownInitially = false;
}
this._animatePanel(0, immediate);
2018-03-10 12:08:22 -06:00
},
_hidePanel: function(immediate) {
let position = this._dtpPanel.geom.position;
let size = this._panelBox[position == St.Side.LEFT || position == St.Side.RIGHT ? 'width' : 'height'];
let coefficient = position == St.Side.TOP || position == St.Side.LEFT ? -1 : 1;
this._animatePanel(size * coefficient, immediate);
2018-03-10 12:08:22 -06:00
},
_animatePanel: function(destination, immediate) {
let animating = Utils.isAnimating(this._panelBox, this._translationProp);
2018-03-10 12:08:22 -06:00
if (!((animating && destination === this._animationDestination) ||
(!animating && destination === this._panelBox[this._translationProp]))) {
//the panel isn't already at, or animating to the asked destination
if (animating) {
Utils.stopAnimations(this._panelBox);
}
2018-03-10 12:08:22 -06:00
this._animationDestination = destination;
if (immediate) {
this._panelBox[this._translationProp] = destination;
this._panelBox.visible = !destination;
} else {
let tweenOpts = {
//when entering/leaving the overview, use its animation time instead of the one from the settings
time: Main.overview.visible ?
2019-09-25 15:57:45 -04:00
SIDE_CONTROLS_ANIMATION_TIME :
2019-08-31 15:50:53 -04:00
Me.settings.get_int('intellihide-animation-time') * 0.001,
//only delay the animation when hiding the panel after the user hovered out
2019-08-31 15:50:53 -04:00
delay: destination != 0 && this._hoveredOut ? Me.settings.get_int('intellihide-close-delay') * 0.001 : 0,
transition: 'easeOutQuad',
onComplete: () => {
this._panelBox.visible = !destination;
Main.layoutManager._queueUpdateRegions();
this._timeoutsHandler.add([T3, POST_ANIMATE_MS, () => this._queueUpdatePanelPosition()]);
}
};
tweenOpts[this._translationProp] = destination;
Utils.animate(this._panelBox, tweenOpts);
}
2018-03-10 12:08:22 -06:00
}
this._hoveredOut = false;
2018-03-10 12:08:22 -06:00
},
});