1 Star 1 Fork 1

道尘/dash-to-dock

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
docking.js 75.58 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Params = imports.misc.params;
const Main = imports.ui.main;
const Dash = imports.ui.dash;
const IconGrid = imports.ui.iconGrid;
const Overview = imports.ui.overview;
const OverviewControls = imports.ui.overviewControls;
const PointerWatcher = imports.ui.pointerWatcher;
const Signals = imports.signals;
const ViewSelector = imports.ui.viewSelector;
const WorkspaceSwitcherPopup= imports.ui.workspaceSwitcherPopup;
const Layout = imports.ui.layout;
const LayoutManager = imports.ui.main.layoutManager;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Utils = Me.imports.utils;
const Intellihide = Me.imports.intellihide;
const Theming = Me.imports.theming;
const MyDash = Me.imports.dash;
const LauncherAPI = Me.imports.launcherAPI;
const FileManager1API = Me.imports.fileManager1API;
const DOCK_DWELL_CHECK_INTERVAL = 100;
let USE_NEW_ALLOCATION;
var State = {
HIDDEN: 0,
SHOWING: 1,
SHOWN: 2,
HIDING: 3
};
const scrollAction = {
DO_NOTHING: 0,
CYCLE_WINDOWS: 1,
SWITCH_WORKSPACE: 2
};
/**
* A simple St.Widget with one child whose allocation takes into account the
* slide out of its child via the _slidex parameter ([0:1]).
*
* Required since I want to track the input region of this container which is
* based on its allocation even if the child overlows the parent actor. By doing
* this the region of the dash that is slideout is not steling anymore the input
* regions making the extesion usable when the primary monitor is the right one.
*
* The slidex parameter can be used to directly animate the sliding. The parent
* must have a WEST (SOUTH) anchor_point to achieve the sliding to the RIGHT (BOTTOM)
* side.
*/
var DashSlideContainer = GObject.registerClass({
Properties: {
'side': GObject.ParamSpec.enum(
'side', 'side', 'side',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
St.Side, St.Side.LEFT),
'slidex': GObject.ParamSpec.double(
'slidex', 'slidex', 'slidex',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
0, 1, 1),
}
}, class DashToDock_DashSlideContainer extends St.Bin {
_init(params = {}) {
super._init(params);
// slide parameter: 1 = visible, 0 = hidden.
this._slidex = params.slidex || 1;
this._slideoutSize = 0; // minimum size when slided out
}
vfunc_allocate(box, flags) {
let contentBox = this.get_theme_node().get_content_box(box);
DockManager.useNewAllocation ?
this.set_allocation(box) : this.set_allocation(box, flags);
if (this.child == null)
return;
let availWidth = contentBox.x2 - contentBox.x1;
let availHeight = contentBox.y2 - contentBox.y1;
let [, , natChildWidth, natChildHeight] =
this.child.get_preferred_size();
let childWidth = natChildWidth;
let childHeight = natChildHeight;
let childBox = new Clutter.ActorBox();
let slideoutSize = this._slideoutSize;
if (this.side == St.Side.LEFT) {
childBox.x1 = (this._slidex -1) * (childWidth - slideoutSize);
childBox.x2 = slideoutSize + this._slidex*(childWidth - slideoutSize);
childBox.y1 = 0;
childBox.y2 = childBox.y1 + childHeight;
}
else if ((this.side == St.Side.RIGHT) || (this.side == St.Side.BOTTOM)) {
childBox.x1 = 0;
childBox.x2 = childWidth;
childBox.y1 = 0;
childBox.y2 = childBox.y1 + childHeight;
}
else if (this.side == St.Side.TOP) {
childBox.x1 = 0;
childBox.x2 = childWidth;
childBox.y1 = (this._slidex -1) * (childHeight - slideoutSize);
childBox.y2 = slideoutSize + this._slidex * (childHeight - slideoutSize);
}
DockManager.useNewAllocation ?
this.child.allocate(childBox) : this.child.allocate(childBox, flags);
this.child.set_clip(-childBox.x1, -childBox.y1,
-childBox.x1+availWidth, -childBox.y1 + availHeight);
}
/**
* Just the child width but taking into account the slided out part
*/
vfunc_get_preferred_width(forHeight) {
let [minWidth, natWidth] = super.vfunc_get_preferred_width(forHeight);
if ((this.side == St.Side.LEFT) || (this.side == St.Side.RIGHT)) {
minWidth = (minWidth - this._slideoutSize) * this._slidex + this._slideoutSize;
natWidth = (natWidth - this._slideoutSize) * this._slidex + this._slideoutSize;
}
return [minWidth, natWidth];
}
/**
* Just the child height but taking into account the slided out part
*/
vfunc_get_preferred_height(forWidth) {
let [minHeight, natHeight] = super.vfunc_get_preferred_height(forWidth);
if ((this.side == St.Side.TOP) || (this.side == St.Side.BOTTOM)) {
minHeight = (minHeight - this._slideoutSize) * this._slidex + this._slideoutSize;
natHeight = (natHeight - this._slideoutSize) * this._slidex + this._slideoutSize;
}
return [minHeight, natHeight];
}
set slidex(value) {
if (value == this._slidex)
return;
this._slidex = value;
this.notify('slidex');
this.queue_relayout();
}
get slidex() {
return this._slidex;
}
});
var DockedDash = GObject.registerClass({
Signals: {
'showing': {},
'hiding': {},
}
}, class DashToDock extends St.Bin {
_init(remoteModel, monitorIndex) {
this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
// Load settings
let settings = DockManager.settings;
this._remoteModel = remoteModel;
this._monitorIndex = monitorIndex;
// Connect global signals
this._signalsHandler = new Utils.GlobalSignalsHandler();
this._bindSettingsChanges();
this._position = Utils.getPosition();
this._isHorizontal = ((this._position == St.Side.TOP) || (this._position == St.Side.BOTTOM));
// Temporary ignore hover events linked to autohide for whatever reason
this._ignoreHover = false;
this._oldignoreHover = null;
// This variables are linked to the settings regardles of autohide or intellihide
// being temporary disable. Get set by _updateVisibilityMode;
this._autohideIsEnabled = null;
this._intellihideIsEnabled = null;
this._fixedIsEnabled = null;
// Create intellihide object to monitor windows overlapping
this._intellihide = new Intellihide.Intellihide(this._monitorIndex);
// initialize dock state
this._dockState = State.HIDDEN;
// Put dock on the required monitor
this._monitor = Main.layoutManager.monitors[this._monitorIndex];
// this store size and the position where the dash is shown;
// used by intellihide module to check window overlap.
this.staticBox = new Clutter.ActorBox();
// Initialize pressure barrier variables
this._canUsePressure = false;
this._pressureBarrier = null;
this._barrier = null;
this._removeBarrierTimeoutId = 0;
// Initialize dwelling system variables
this._dockDwelling = false;
this._dockWatch = null;
this._dockDwellUserTime = 0;
this._dockDwellTimeoutId = 0
// Create a new dash object
this.dash = new MyDash.MyDash(this._remoteModel, this._monitorIndex);
if (Main.overview.isDummy || !settings.get_boolean('show-show-apps-button'))
this.dash.hideShowAppsButton();
// Create the main actor and the containers for sliding in and out and
// centering, turn on track hover
let positionStyleClass = ['top', 'right', 'bottom', 'left'];
// This is the centering actor
super._init({
name: 'dashtodockContainer',
reactive: false,
style_class: positionStyleClass[this._position],
});
// This is the sliding actor whose allocation is to be tracked for input regions
this._slider = new DashSlideContainer({
side: this._position,
slidex: 0,
...(this._isHorizontal ? {
x_align: Clutter.ActorAlign.CENTER,
} : {
y_align: Clutter.ActorAlign.CENTER,
})
});
// This is the actor whose hover status us tracked for autohide
this._box = new St.BoxLayout({
name: 'dashtodockBox',
reactive: true,
track_hover: true
});
this._box.connect('notify::hover', this._hoverChanged.bind(this));
this._signalsHandler.add([
// update when workarea changes, for instance if other extensions modify the struts
//(like moving th panel at the bottom)
global.display,
'workareas-changed',
this._resetPosition.bind(this)
], [
global.display,
'in-fullscreen-changed',
this._updateBarrier.bind(this)
], [
// Monitor windows overlapping
this._intellihide,
'status-changed',
this._updateDashVisibility.bind(this)
], [
// sync hover after a popupmenu is closed
this.dash,
'menu-closed',
() => { this._box.sync_hover() }
]);
if (!Main.overview.isDummy) {
this._signalsHandler.add([
Main.overview,
'item-drag-begin',
this._onDragStart.bind(this)
], [
Main.overview,
'item-drag-end',
this._onDragEnd.bind(this)
], [
Main.overview,
'item-drag-cancelled',
this._onDragEnd.bind(this)
], [
Main.overview,
'showing',
this._onOverviewShowing.bind(this)
], [
Main.overview,
'hiding',
this._onOverviewHiding.bind(this)
], [
// Hide on appview
Main.overview.viewSelector,
'page-changed',
this._pageChanged.bind(this)
], [
// Ensure the ShowAppsButton status is kept in sync
Main.overview.viewSelector._showAppsButton,
'notify::checked',
this._syncShowAppsButtonToggled.bind(this)
]);
}
this._injectionsHandler = new Utils.InjectionsHandler();
this._themeManager = new Theming.ThemeManager(this);
// Since the actor is not a topLevel child and its parent is now not added to the Chrome,
// the allocation change of the parent container (slide in and slideout) doesn't trigger
// anymore an update of the input regions. Force the update manually.
this.connect('notify::allocation',
Main.layoutManager._queueUpdateRegions.bind(Main.layoutManager));
// Since Clutter has no longer ClutterAllocationFlags,
// "allocation-changed" signal has been removed. MR !1245
this.dash._container.connect('notify::allocation', this._updateStaticBox.bind(this));
this._slider.connect(this._isHorizontal ? 'notify::x' : 'notify::y', this._updateStaticBox.bind(this));
// Load optional features that need to be activated for one dock only
if (this._monitorIndex == settings.get_int('preferred-monitor'))
this._enableExtraFeatures();
// Load optional features that need to be activated once per dock
this._optionalScrollWorkspaceSwitch();
// Delay operations that require the shell to be fully loaded and with
// user theme applied.
this._paintId = this.connect('paint', this._initialize.bind(this));
// Manage the which is used to reserve space in the overview for the dock
// Add and additional dashSpacer positioned according to the dash positioning.
// It gets restored on extension unload.
this._dashSpacer = new OverviewControls.DashSpacer();
this._dashSpacer.setDashActor(this._box);
if (!Main.overview.isDummy) {
const overviewActor = Main.overview._overview;
if (this._position == St.Side.LEFT)
overviewActor._controls._group.insert_child_at_index(this._dashSpacer, this._rtl ? -1 : 0); // insert on first
else if (this._position == St.Side.RIGHT)
overviewActor._controls._group.insert_child_at_index(this._dashSpacer, this._rtl ? 0 : -1); // insert on last
else if (this._position == St.Side.TOP)
overviewActor.insert_child_at_index(this._dashSpacer, 0);
else if (this._position == St.Side.BOTTOM)
overviewActor.insert_child_at_index(this._dashSpacer, -1);
if (this._isHorizontal)
this._dashSpacer.visible = true;
}
// Add dash container actor and the container to the Chrome.
this.set_child(this._slider);
this._slider.set_child(this._box);
this._box.add_actor(this.dash);
// Add aligning container without tracking it for input region
Main.uiGroup.add_child(this);
if (Main.uiGroup.contains(global.top_window_group))
Main.uiGroup.set_child_below_sibling(this, global.top_window_group);
if (settings.get_boolean('dock-fixed')) {
// Note: tracking the fullscreen directly on the slider actor causes some hiccups when fullscreening
// windows of certain applications
Main.layoutManager._trackActor(this, {affectsInputRegion: false, trackFullscreen: true});
Main.layoutManager._trackActor(this._slider, {affectsStruts: true});
}
else
Main.layoutManager._trackActor(this._slider);
// Create and apply height/width constraint to the dash.
if (this._isHorizontal) {
this.connect('notify::width', () => {
this.dash.setMaxHeight(this.width);
});
}
else {
this.connect('notify::height', () => {
this.dash.setMaxHeight(this.height)
});
}
if (this._position == St.Side.RIGHT)
this.connect('notify::width', () => this.translation_x = -this.width);
else if (this._position == St.Side.BOTTOM)
this.connect('notify::height', () => this.translation_y = -this.height);
// Set initial position
this._resetPosition();
this.connect('destroy', this._onDestroy.bind(this));
}
_initialize() {
if (this._paintId > 0) {
this.disconnect(this._paintId);
this._paintId=0;
}
// Apply custome css class according to the settings
this._themeManager.updateCustomTheme();
// Since Gnome 3.8 dragging an app without having opened the overview before cause the attemp to
//animate a null target since some variables are not initialized when the viewSelector is created
if (!Main.overview.isDummy && Main.overview.viewSelector._activePage == null)
Main.overview.viewSelector._activePage = Main.overview.viewSelector._workspacesPage;
this._updateVisibilityMode();
// In case we are already inside the overview when the extension is loaded,
// for instance on unlocking the screen if it was locked with the overview open.
if (Main.overview.visibleTarget) {
this._onOverviewShowing();
this._pageChanged();
}
// Setup pressure barrier (GS38+ only)
this._updatePressureBarrier();
this._updateBarrier();
// setup dwelling system if pressure barriers are not available
this._setupDockDwellIfNeeded();
}
_onDestroy() {
// Disconnect global signals
this._signalsHandler.destroy();
// The dash, intellihide and themeManager have global signals as well internally
this.dash.destroy();
this._intellihide.destroy();
this._themeManager.destroy();
this._injectionsHandler.destroy();
if (this._marginLater) {
Meta.later_remove(this._marginLater);
delete this._marginLater;
}
// Remove barrier timeout
if (this._removeBarrierTimeoutId > 0)
GLib.source_remove(this._removeBarrierTimeoutId);
// Remove existing barrier
this._removeBarrier();
// Remove pointer watcher
if (this._dockWatch) {
PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch);
this._dockWatch = null;
}
// Remove the dashSpacer
this._dashSpacer.destroy();
}
_bindSettingsChanges() {
let settings = DockManager.settings;
this._signalsHandler.add([
settings,
'changed::scroll-action',
() => { this._optionalScrollWorkspaceSwitch(); }
], [
settings,
'changed::dash-max-icon-size',
() => { this.dash.setIconSize(settings.get_int('dash-max-icon-size')); }
], [
settings,
'changed::icon-size-fixed',
() => { this.dash.setIconSize(settings.get_int('dash-max-icon-size')); }
], [
settings,
'changed::show-favorites',
() => { this.dash.resetAppIcons(); }
], [
settings,
'changed::show-trash',
() => { this.dash.resetAppIcons(); },
Utils.SignalsHandlerFlags.CONNECT_AFTER,
], [
settings,
'changed::show-mounts',
() => { this.dash.resetAppIcons(); },
Utils.SignalsHandlerFlags.CONNECT_AFTER
], [
settings,
'changed::show-running',
() => { this.dash.resetAppIcons(); }
], [
settings,
'changed::show-apps-at-top',
() => { this.dash.resetAppIcons(); }
], [
settings,
'changed::show-show-apps-button',
() => {
if (!Main.overview.isDummy &&
settings.get_boolean('show-show-apps-button'))
this.dash.showShowAppsButton();
else
this.dash.hideShowAppsButton();
}
], [
settings,
'changed::dock-fixed',
() => {
if (settings.get_boolean('dock-fixed')) {
Main.layoutManager._untrackActor(this);
Main.layoutManager._trackActor(this, {affectsInputRegion: false, trackFullscreen: true});
Main.layoutManager._untrackActor(this._slider);
Main.layoutManager._trackActor(this._slider, {affectsStruts: true});
} else {
Main.layoutManager._untrackActor(this);
Main.layoutManager._untrackActor(this._slider);
Main.layoutManager._trackActor(this._slider);
}
this._resetPosition();
// Add or remove barrier depending on if dock-fixed
this._updateBarrier();
this._updateVisibilityMode();
}
], [
settings,
'changed::intellihide',
this._updateVisibilityMode.bind(this)
], [
settings,
'changed::intellihide-mode',
() => { this._intellihide.forceUpdate(); }
], [
settings,
'changed::autohide',
() => {
this._updateVisibilityMode();
this._updateBarrier();
}
], [
settings,
'changed::autohide-in-fullscreen',
this._updateBarrier.bind(this)
],
[
settings,
'changed::extend-height',
this._resetPosition.bind(this)
], [
settings,
'changed::height-fraction',
this._resetPosition.bind(this)
], [
settings,
'changed::require-pressure-to-show',
() => {
// Remove pointer watcher
if (this._dockWatch) {
PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch);
this._dockWatch = null;
}
this._setupDockDwellIfNeeded();
this._updateBarrier();
}
], [
settings,
'changed::pressure-threshold',
() => {
this._updatePressureBarrier();
this._updateBarrier();
}
]);
}
/**
* This is call when visibility settings change
*/
_updateVisibilityMode() {
let settings = DockManager.settings;
if (settings.get_boolean('dock-fixed')) {
this._fixedIsEnabled = true;
this._autohideIsEnabled = false;
this._intellihideIsEnabled = false;
}
else {
this._fixedIsEnabled = false;
this._autohideIsEnabled = settings.get_boolean('autohide')
this._intellihideIsEnabled = settings.get_boolean('intellihide')
}
if (this._intellihideIsEnabled)
this._intellihide.enable();
else
this._intellihide.disable();
this._updateDashVisibility();
}
/**
* Show/hide dash based on, in order of priority:
* overview visibility
* fixed mode
* intellihide
* autohide
* overview visibility
*/
_updateDashVisibility() {
if (Main.overview.visibleTarget)
return;
let settings = DockManager.settings;
if (this._fixedIsEnabled) {
this._removeAnimations();
this._animateIn(settings.get_double('animation-time'), 0);
}
else if (this._intellihideIsEnabled) {
if (this._intellihide.getOverlapStatus()) {
this._ignoreHover = false;
// Do not hide if autohide is enabled and mouse is hover
if (!this._box.hover || !this._autohideIsEnabled)
this._animateOut(settings.get_double('animation-time'), 0);
}
else {
this._ignoreHover = true;
this._removeAnimations();
this._animateIn(settings.get_double('animation-time'), 0);
}
}
else {
if (this._autohideIsEnabled) {
this._ignoreHover = false;
global.sync_pointer();
if (this._box.hover)
this._animateIn(settings.get_double('animation-time'), 0);
else
this._animateOut(settings.get_double('animation-time'), 0);
}
else
this._animateOut(settings.get_double('animation-time'), 0);
}
}
_onOverviewShowing() {
this._ignoreHover = true;
this._intellihide.disable();
this._removeAnimations();
this._animateIn(DockManager.settings.get_double('animation-time'), 0);
}
_onOverviewHiding() {
this._ignoreHover = false;
this._intellihide.enable();
this._updateDashVisibility();
}
_hoverChanged() {
if (!this._ignoreHover) {
// Skip if dock is not in autohide mode for instance because it is shown
// by intellihide.
if (this._autohideIsEnabled) {
if (this._box.hover)
this._show();
else
this._hide();
}
}
}
getDockState() {
return this._dockState;
}
_show() {
this._delayedHide = false;
if ((this._dockState == State.HIDDEN) || (this._dockState == State.HIDING)) {
if (this._dockState == State.HIDING)
// suppress all potential queued transitions - i.e. added but not started,
// always give priority to show
this._removeAnimations();
this.emit('showing');
this._animateIn(DockManager.settings.get_double('animation-time'), 0);
}
}
_hide() {
// If no hiding animation is running or queued
if ((this._dockState == State.SHOWN) || (this._dockState == State.SHOWING)) {
let settings = DockManager.settings;
let delay = settings.get_double('hide-delay');
if (this._dockState == State.SHOWING) {
// if a show already started, let it finish; queue hide without removing the show.
// to obtain this, we wait for the animateIn animation to be completed
this._delayedHide = true;
return;
}
this.emit('hiding');
this._animateOut(settings.get_double('animation-time'), delay);
}
}
_animateIn(time, delay) {
this._dockState = State.SHOWING;
this.dash.iconAnimator.start();
this._delayedHide = false;
this._slider.ease_property('slidex', 1, {
duration: time * 1000,
delay: delay * 1000,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this._dockState = State.SHOWN;
// Remove barrier so that mouse pointer is released and can access monitors on other side of dock
// NOTE: Delay needed to keep mouse from moving past dock and re-hiding dock immediately. This
// gives users an opportunity to hover over the dock
if (this._removeBarrierTimeoutId > 0)
GLib.source_remove(this._removeBarrierTimeoutId);
if (!this._delayedHide) {
this._removeBarrierTimeoutId = GLib.timeout_add(
GLib.PRIORITY_DEFAULT, 100, this._removeBarrier.bind(this));
} else {
this._hide();
}
}
});
}
_animateOut(time, delay) {
this._dockState = State.HIDING;
this._slider.ease_property('slidex', 0, {
duration: time * 1000,
delay: delay * 1000,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this._dockState = State.HIDDEN;
// Remove queued barried removal if any
if (this._removeBarrierTimeoutId > 0)
GLib.source_remove(this._removeBarrierTimeoutId);
this._updateBarrier();
this.dash.iconAnimator.pause();
}
});
}
/**
* Dwelling system based on the GNOME Shell 3.14 messageTray code.
*/
_setupDockDwellIfNeeded() {
// If we don't have extended barrier features, then we need
// to support the old tray dwelling mechanism.
if (!global.display.supports_extended_barriers() ||
!DockManager.settings.get_boolean('require-pressure-to-show')) {
let pointerWatcher = PointerWatcher.getPointerWatcher();
this._dockWatch = pointerWatcher.addWatch(DOCK_DWELL_CHECK_INTERVAL, this._checkDockDwell.bind(this));
this._dockDwelling = false;
this._dockDwellUserTime = 0;
}
}
_checkDockDwell(x, y) {
let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor.index)
let shouldDwell;
// Check for the correct screen edge, extending the sensitive area to the whole workarea,
// minus 1 px to avoid conflicting with other active corners.
if (this._position == St.Side.LEFT)
shouldDwell = (x == this._monitor.x) && (y > workArea.y) && (y < workArea.y + workArea.height);
else if (this._position == St.Side.RIGHT)
shouldDwell = (x == this._monitor.x + this._monitor.width - 1) && (y > workArea.y) && (y < workArea.y + workArea.height);
else if (this._position == St.Side.TOP)
shouldDwell = (y == this._monitor.y) && (x > workArea.x) && (x < workArea.x + workArea.width);
else if (this._position == St.Side.BOTTOM)
shouldDwell = (y == this._monitor.y + this._monitor.height - 1) && (x > workArea.x) && (x < workArea.x + workArea.width);
if (shouldDwell) {
// We only set up dwell timeout when the user is not hovering over the dock
// already (!this._box.hover).
// The _dockDwelling variable is used so that we only try to
// fire off one dock dwell - if it fails (because, say, the user has the mouse down),
// we don't try again until the user moves the mouse up and down again.
if (!this._dockDwelling && !this._box.hover && (this._dockDwellTimeoutId == 0)) {
// Save the interaction timestamp so we can detect user input
let focusWindow = global.display.focus_window;
this._dockDwellUserTime = focusWindow ? focusWindow.user_time : 0;
this._dockDwellTimeoutId = GLib.timeout_add(
GLib.PRIORITY_DEFAULT,
DockManager.settings.get_double('show-delay') * 1000,
this._dockDwellTimeout.bind(this));
GLib.Source.set_name_by_id(this._dockDwellTimeoutId, '[dash-to-dock] this._dockDwellTimeout');
}
this._dockDwelling = true;
}
else {
this._cancelDockDwell();
this._dockDwelling = false;
}
}
_cancelDockDwell() {
if (this._dockDwellTimeoutId != 0) {
GLib.source_remove(this._dockDwellTimeoutId);
this._dockDwellTimeoutId = 0;
}
}
_dockDwellTimeout() {
this._dockDwellTimeoutId = 0;
if (!DockManager.settings.get_boolean('autohide-in-fullscreen') &&
this._monitor.inFullscreen)
return GLib.SOURCE_REMOVE;
// We don't want to open the tray when a modal dialog
// is up, so we check the modal count for that. When we are in the
// overview we have to take the overview's modal push into account
if (Main.modalCount > (Main.overview.visible ? 1 : 0))
return GLib.SOURCE_REMOVE;
// If the user interacted with the focus window since we started the tray
// dwell (by clicking or typing), don't activate the message tray
let focusWindow = global.display.focus_window;
let currentUserTime = focusWindow ? focusWindow.user_time : 0;
if (currentUserTime != this._dockDwellUserTime)
return GLib.SOURCE_REMOVE;
// Reuse the pressure version function, the logic is the same
this._onPressureSensed();
return GLib.SOURCE_REMOVE;
}
_updatePressureBarrier() {
let settings = DockManager.settings;
this._canUsePressure = global.display.supports_extended_barriers();
let pressureThreshold = settings.get_double('pressure-threshold');
// Remove existing pressure barrier
if (this._pressureBarrier) {
this._pressureBarrier.destroy();
this._pressureBarrier = null;
}
if (this._barrier) {
this._barrier.destroy();
this._barrier = null;
}
// Create new pressure barrier based on pressure threshold setting
if (this._canUsePressure) {
this._pressureBarrier = new Layout.PressureBarrier(pressureThreshold, settings.get_double('show-delay')*1000,
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW);
this._pressureBarrier.connect('trigger', (barrier) => {
if (!settings.get_boolean('autohide-in-fullscreen') && this._monitor.inFullscreen)
return;
this._onPressureSensed();
});
}
}
/**
* handler for mouse pressure sensed
*/
_onPressureSensed() {
if (Main.overview.visibleTarget)
return;
// In case the mouse move away from the dock area before hovering it, in such case the leave event
// would never be triggered and the dock would stay visible forever.
let triggerTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => {
triggerTimeoutId = 0;
let [x, y, mods] = global.get_pointer();
let shouldHide = true;
switch (this._position) {
case St.Side.LEFT:
if (x <= this.staticBox.x2 &&
x >= this._monitor.x &&
y >= this._monitor.y &&
y <= this._monitor.y + this._monitor.height) {
shouldHide = false;
}
break;
case St.Side.RIGHT:
if (x >= this.staticBox.x1 &&
x <= this._monitor.x + this._monitor.width &&
y >= this._monitor.y &&
y <= this._monitor.y + this._monitor.height) {
shouldHide = false;
}
break;
case St.Side.TOP:
if (x >= this._monitor.x &&
x <= this._monitor.x + this._monitor.width &&
y <= this.staticBox.y2 &&
y >= this._monitor.y) {
shouldHide = false;
}
break;
case St.Side.BOTTOM:
if (x >= this._monitor.x &&
x <= this._monitor.x + this._monitor.width &&
y >= this.staticBox.y1 &&
y <= this._monitor.y + this._monitor.height) {
shouldHide = false;
}
}
if (shouldHide) {
this._hoverChanged();
return GLib.SOURCE_REMOVE;
}
else {
return GLib.SOURCE_CONTINUE;
}
});
this._show();
}
/**
* Remove pressure barrier
*/
_removeBarrier() {
if (this._barrier) {
if (this._pressureBarrier)
this._pressureBarrier.removeBarrier(this._barrier);
this._barrier.destroy();
this._barrier = null;
}
this._removeBarrierTimeoutId = 0;
return false;
}
/**
* Update pressure barrier size
*/
_updateBarrier() {
// Remove existing barrier
this._removeBarrier();
// The barrier needs to be removed in fullscreen with autohide disabled, otherwise the mouse can
// get trapped on monitor.
if (this._monitor.inFullscreen &&
!DockManager.settings.get_boolean('autohide-in-fullscreen'))
return
// Manually reset pressure barrier
// This is necessary because we remove the pressure barrier when it is triggered to show the dock
if (this._pressureBarrier) {
this._pressureBarrier._reset();
this._pressureBarrier._isTriggered = false;
}
// Create new barrier
// The barrier extends to the whole workarea, minus 1 px to avoid conflicting with other active corners
// Note: dash in fixed position doesn't use pressure barrier.
if (this._canUsePressure && this._autohideIsEnabled &&
DockManager.settings.get_boolean('require-pressure-to-show')) {
let x1, x2, y1, y2, direction;
let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor.index)
if (this._position == St.Side.LEFT) {
x1 = this._monitor.x + 1;
x2 = x1;
y1 = workArea.y + 1;
y2 = workArea.y + workArea.height - 1;
direction = Meta.BarrierDirection.POSITIVE_X;
}
else if (this._position == St.Side.RIGHT) {
x1 = this._monitor.x + this._monitor.width - 1;
x2 = x1;
y1 = workArea.y + 1;
y2 = workArea.y + workArea.height - 1;
direction = Meta.BarrierDirection.NEGATIVE_X;
}
else if (this._position == St.Side.TOP) {
x1 = workArea.x + 1;
x2 = workArea.x + workArea.width - 1;
y1 = this._monitor.y;
y2 = y1;
direction = Meta.BarrierDirection.POSITIVE_Y;
}
else if (this._position == St.Side.BOTTOM) {
x1 = workArea.x + 1;
x2 = workArea.x + workArea.width - 1;
y1 = this._monitor.y + this._monitor.height;
y2 = y1;
direction = Meta.BarrierDirection.NEGATIVE_Y;
}
if (this._pressureBarrier && this._dockState == State.HIDDEN) {
this._barrier = new Meta.Barrier({
display: global.display,
x1: x1,
x2: x2,
y1: y1,
y2: y2,
directions: direction
});
this._pressureBarrier.addBarrier(this._barrier);
}
}
}
_isPrimaryMonitor() {
return (this._monitorIndex == Main.layoutManager.primaryIndex);
}
_resetPosition() {
// Ensure variables linked to settings are updated.
this._updateVisibilityMode();
let extendHeight = DockManager.settings.get_boolean('extend-height');
// Note: do not use the workarea coordinates in the direction on which the dock is placed,
// to avoid a loop [position change -> workArea change -> position change] with
// fixed dock.
let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitorIndex);
// Reserve space for the dash on the overview
// if the dock is on the primary monitor
if (this._isPrimaryMonitor())
this._dashSpacer.show();
else
// No space is required in the overview of the dash
this._dashSpacer.hide();
let fraction = DockManager.settings.get_double('height-fraction');
if (extendHeight)
fraction = 1;
else if ((fraction < 0) || (fraction > 1))
fraction = 0.95;
if (this._isHorizontal) {
this.width = Math.round(fraction * workArea.width);
let pos_y = this._monitor.y;
if (this._position == St.Side.BOTTOM)
pos_y += this._monitor.height;
this.x = workArea.x + Math.round((1 - fraction) / 2 * workArea.width);
this.y = pos_y;
if (extendHeight) {
this.dash._container.set_width(this.width);
this.add_style_class_name('extended');
}
else {
this.dash._container.set_width(-1);
this.remove_style_class_name('extended');
}
}
else {
this.height = Math.round(fraction * workArea.height);
let pos_x = this._monitor.x;
if (this._position == St.Side.RIGHT)
pos_x += this._monitor.width;
this.x = pos_x;
this.y = workArea.y + Math.round((1 - fraction) / 2 * workArea.height);
let overviewControls = null;
if (!Main.overview.isDummy && this._isPrimaryMonitor()) {
overviewControls = Main.overview._overview._controls;
if (this._oldSelectorMargin)
overviewControls.margin_bottom = this._oldSelectorMargin;
else
this._oldSelectorMargin = overviewControls.margin_bottom;
}
this._signalsHandler.removeWithLabel('verticalOffsetChecker');
if (extendHeight) {
if (overviewControls && !DockManager.useNewAllocation) {
// This is a workaround for bug #1007, only in versions before 3.38
this._signalsHandler.addWithLabel('verticalOffsetChecker', [
overviewControls.layout_manager,
'notify::allocation',
() => {
let [, y] = overviewControls.get_transformed_position();
let [, height] = overviewControls.get_transformed_size();
let monitor = Main.layoutManager.primaryMonitor;
let contentY2 = y + height;
let offset = Math.max(0, contentY2 - (monitor.y + monitor.height));
if (this._marginLater)
Meta.later_remove(this._marginLater);
this._marginLater = Meta.later_add(
Meta.LaterType.BEFORE_REDRAW, () => {
Main.overview.viewSelector.margin_bottom = offset;
});
}]);
}
this.dash._container.set_height(this.height);
this.add_style_class_name('extended');
}
else {
this.dash._container.set_height(-1);
this.remove_style_class_name('extended');
}
}
}
_updateStaticBox() {
this.staticBox.init_rect(
this.x + this._slider.x - (this._position == St.Side.RIGHT ? this._box.width : 0),
this.y + this._slider.y - (this._position == St.Side.BOTTOM ? this._box.height : 0),
this._box.width,
this._box.height
);
this._intellihide.updateTargetBox(this.staticBox);
}
_removeAnimations() {
this._slider.remove_all_transitions();
}
_onDragStart() {
this._oldignoreHover = this._ignoreHover;
this._ignoreHover = true;
this._animateIn(DockManager.settings.get_double('animation-time'), 0);
}
_onDragEnd() {
if (this._oldignoreHover !== null)
this._ignoreHover = this._oldignoreHover;
this._oldignoreHover = null;
this._box.sync_hover();
if (Main.overview._shown)
this._pageChanged();
}
_pageChanged() {
let activePage = Main.overview.viewSelector.getActivePage();
let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
activePage == ViewSelector.ViewPage.APPS);
if (dashVisible)
this._animateIn(DockManager.settings.get_double('animation-time'), 0);
else
this._animateOut(DockManager.settings.get_double('animation-time'), 0);
}
/**
* Show dock and give key focus to it
*/
_onAccessibilityFocus() {
this._box.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
this._animateIn(DockManager.settings.get_double('animation-time'), 0);
}
/**
* Keep ShowAppsButton status in sync with the overview status
*/
_syncShowAppsButtonToggled() {
let status = Main.overview.viewSelector._showAppsButton.checked;
if (this.dash.showAppsButton.checked !== status)
this.dash.showAppsButton.checked = status;
}
// Optional features to be enabled only for the main Dock
_enableExtraFeatures() {
// Restore dash accessibility
Main.ctrlAltTabManager.addGroup(
this.dash, _('Dash'), 'user-bookmarks-symbolic',
{focusCallback: this._onAccessibilityFocus.bind(this)});
}
/**
* Switch workspace by scrolling over the dock
*/
_optionalScrollWorkspaceSwitch() {
let label = 'optionalScrollWorkspaceSwitch';
function isEnabled() {
return DockManager.settings.get_enum('scroll-action') === scrollAction.SWITCH_WORKSPACE;
}
DockManager.settings.connect('changed::scroll-action', () => {
if (isEnabled.bind(this)())
enable.bind(this)();
else
disable.bind(this)();
});
if (isEnabled.bind(this)())
enable.bind(this)();
function enable() {
this._signalsHandler.removeWithLabel(label);
this._signalsHandler.addWithLabel(label, [
this._box,
'scroll-event',
onScrollEvent.bind(this)
]);
}
function disable() {
this._signalsHandler.removeWithLabel(label);
if (this._optionalScrollWorkspaceSwitchDeadTimeId) {
GLib.source_remove(this._optionalScrollWorkspaceSwitchDeadTimeId);
this._optionalScrollWorkspaceSwitchDeadTimeId = 0;
}
}
// This was inspired to desktop-scroller@obsidien.github.com
function onScrollEvent(actor, event) {
// When in overview change workscape only in windows view
if (Main.overview.visible && Main.overview.viewSelector.getActivePage() !== ViewSelector.ViewPage.WINDOWS)
return false;
let activeWs = global.workspace_manager.get_active_workspace();
let direction = null;
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:
direction = Meta.MotionDirection.UP;
break;
case Clutter.ScrollDirection.DOWN:
direction = Meta.MotionDirection.DOWN;
break;
case Clutter.ScrollDirection.SMOOTH:
let [dx, dy] = event.get_scroll_delta();
if (dy < 0)
direction = Meta.MotionDirection.UP;
else if (dy > 0)
direction = Meta.MotionDirection.DOWN;
break;
}
if (direction !== null) {
// Prevent scroll events from triggering too many workspace switches
// by adding a 250ms deadtime between each scroll event.
// Usefull on laptops when using a touchpad.
// During the deadtime do nothing
if (this._optionalScrollWorkspaceSwitchDeadTimeId)
return false;
else
this._optionalScrollWorkspaceSwitchDeadTimeId = GLib.timeout_add(
GLib.PRIORITY_DEFAULT, 250, () => {
this._optionalScrollWorkspaceSwitchDeadTimeId = 0;
});
let ws;
ws = activeWs.get_neighbor(direction)
if (Main.wm._workspaceSwitcherPopup == null)
// Support Workspace Grid extension showing their custom Grid Workspace Switcher
if (global.workspace_manager.workspace_grid !== undefined) {
Main.wm._workspaceSwitcherPopup =
global.workspace_manager.workspace_grid.getWorkspaceSwitcherPopup();
} else {
Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
}
// Set the actor non reactive, so that it doesn't prevent the
// clicks events from reaching the dash actor. I can't see a reason
// why it should be reactive.
Main.wm._workspaceSwitcherPopup.reactive = false;
Main.wm._workspaceSwitcherPopup.connect('destroy', function() {
Main.wm._workspaceSwitcherPopup = null;
});
// If Workspace Grid is installed, let them handle the scroll behaviour.
if (global.workspace_manager.workspace_grid !== undefined)
ws = global.workspace_manager.workspace_grid.actionMoveWorkspace(direction);
else
Main.wm.actionMoveWorkspace(ws);
// Do not show wokspaceSwithcer in overview
if (!Main.overview.visible)
Main.wm._workspaceSwitcherPopup.display(direction, ws.index());
return true;
}
else
return false;
}
}
_activateApp(appIndex) {
let children = this.dash._box.get_children().filter(function(actor) {
return actor.child &&
actor.child.app;
});
// Apps currently in the dash
let apps = children.map(function(actor) {
return actor.child;
});
// Activate with button = 1, i.e. same as left click
let button = 1;
if (appIndex < apps.length)
apps[appIndex].activate(button);
}
});
/*
* Handle keybaord shortcuts
*/
const DashToDock_KeyboardShortcuts_NUM_HOTKEYS = 10;
var KeyboardShortcuts = class DashToDock_KeyboardShortcuts {
constructor() {
this._signalsHandler = new Utils.GlobalSignalsHandler();
this._hotKeysEnabled = false;
if (DockManager.settings.get_boolean('hot-keys'))
this._enableHotKeys();
this._signalsHandler.add([
DockManager.settings,
'changed::hot-keys',
() => {
if (DockManager.settings.get_boolean('hot-keys'))
this._enableHotKeys.bind(this)();
else
this._disableHotKeys.bind(this)();
}
]);
this._optionalNumberOverlay();
}
destroy() {
// Remove keybindings
this._disableHotKeys();
this._disableExtraShortcut();
this._signalsHandler.destroy();
}
_enableHotKeys() {
if (this._hotKeysEnabled)
return;
// Setup keyboard bindings for dash elements
let keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-'];
keys.forEach( function(key) {
for (let i = 0; i < DashToDock_KeyboardShortcuts_NUM_HOTKEYS; i++) {
let appNum = i;
Main.wm.addKeybinding(key + (i + 1), DockManager.settings,
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
() => {
DockManager.getDefault().mainDock._activateApp(appNum);
this._showOverlay();
});
}
}, this);
this._hotKeysEnabled = true;
}
_disableHotKeys() {
if (!this._hotKeysEnabled)
return;
let keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-'];
keys.forEach( function(key) {
for (let i = 0; i < DashToDock_KeyboardShortcuts_NUM_HOTKEYS; i++)
Main.wm.removeKeybinding(key + (i + 1));
}, this);
this._hotKeysEnabled = false;
}
_optionalNumberOverlay() {
let settings = DockManager.settings;
this._shortcutIsSet = false;
// Enable extra shortcut if either 'overlay' or 'show-dock' are true
if (settings.get_boolean('hot-keys') &&
(settings.get_boolean('hotkeys-overlay') || settings.get_boolean('hotkeys-show-dock')))
this._enableExtraShortcut();
this._signalsHandler.add([
settings,
'changed::hot-keys',
this._checkHotkeysOptions.bind(this)
], [
settings,
'changed::hotkeys-overlay',
this._checkHotkeysOptions.bind(this)
], [
settings,
'changed::hotkeys-show-dock',
this._checkHotkeysOptions.bind(this)
]);
}
_checkHotkeysOptions() {
let settings = DockManager.settings;
if (settings.get_boolean('hot-keys') &&
(settings.get_boolean('hotkeys-overlay') || settings.get_boolean('hotkeys-show-dock')))
this._enableExtraShortcut();
else
this._disableExtraShortcut();
}
_enableExtraShortcut() {
if (!this._shortcutIsSet) {
Main.wm.addKeybinding('shortcut', DockManager.settings,
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
this._showOverlay.bind(this));
this._shortcutIsSet = true;
}
}
_disableExtraShortcut() {
if (this._shortcutIsSet) {
Main.wm.removeKeybinding('shortcut');
this._shortcutIsSet = false;
}
}
_showOverlay() {
for (let dock of DockManager.allDocks) {
if (DockManager.settings.get_boolean('hotkeys-overlay'))
dock.dash.toggleNumberOverlay(true);
// Restart the counting if the shortcut is pressed again
if (dock._numberOverlayTimeoutId) {
GLib.source_remove(dock._numberOverlayTimeoutId);
dock._numberOverlayTimeoutId = 0;
}
// Hide the overlay/dock after the timeout
let timeout = DockManager.settings.get_double('shortcut-timeout') * 1000;
dock._numberOverlayTimeoutId = GLib.timeout_add(
GLib.PRIORITY_DEFAULT, timeout, () => {
dock._numberOverlayTimeoutId = 0;
dock.dash.toggleNumberOverlay(false);
// Hide the dock again if necessary
dock._updateDashVisibility();
});
// Show the dock if it is hidden
if (DockManager.settings.get_boolean('hotkeys-show-dock')) {
let showDock = (dock._intellihideIsEnabled || dock._autohideIsEnabled);
if (showDock)
dock._show();
}
}
}
};
/**
* Isolate overview to open new windows for inactive apps
* Note: the future implementaion is not fully contained here. Some bits are around in other methods of other classes.
* This class just take care of enabling/disabling the option.
*/
var WorkspaceIsolation = class DashToDock_WorkspaceIsolation {
constructor() {
let settings = DockManager.settings;
this._signalsHandler = new Utils.GlobalSignalsHandler();
this._injectionsHandler = new Utils.InjectionsHandler();
this._signalsHandler.add([
settings,
'changed::isolate-workspaces',
() => {
DockManager.allDocks.forEach((dock) =>
dock.dash.resetAppIcons());
if (settings.get_boolean('isolate-workspaces') ||
settings.get_boolean('isolate-monitors'))
this._enable.bind(this)();
else
this._disable.bind(this)();
}
],[
settings,
'changed::isolate-monitors',
() => {
DockManager.allDocks.forEach((dock) =>
dock.dash.resetAppIcons());
if (settings.get_boolean('isolate-workspaces') ||
settings.get_boolean('isolate-monitors'))
this._enable.bind(this)();
else
this._disable.bind(this)();
}
]);
if (settings.get_boolean('isolate-workspaces') ||
settings.get_boolean('isolate-monitors'))
this._enable();
}
_enable() {
// ensure I never double-register/inject
// although it should never happen
this._disable();
DockManager.allDocks.forEach((dock) => {
this._signalsHandler.addWithLabel('isolation', [
global.display,
'restacked',
dock.dash._queueRedisplay.bind(dock.dash)
], [
global.window_manager,
'switch-workspace',
dock.dash._queueRedisplay.bind(dock.dash)
]);
// This last signal is only needed for monitor isolation, as windows
// might migrate from one monitor to another without triggering 'restacked'
if (DockManager.settings.get_boolean('isolate-monitors'))
this._signalsHandler.addWithLabel('isolation', [
global.display,
'window-entered-monitor',
dock.dash._queueRedisplay.bind(dock.dash)
]);
}, this);
// here this is the Shell.App
function IsolatedOverview() {
// These lines take care of Nautilus for icons on Desktop
let windows = this.get_windows().filter(function(w) {
return w.get_workspace().index() == global.workspace_manager.get_active_workspace_index();
});
if (windows.length == 1)
if (windows[0].skip_taskbar)
return this.open_new_window(-1);
if (this.is_on_workspace(global.workspace_manager.get_active_workspace()))
return Main.activateWindow(windows[0]);
return this.open_new_window(-1);
}
this._injectionsHandler.addWithLabel('isolation', [
Shell.App.prototype,
'activate',
IsolatedOverview
]);
}
_disable () {
this._signalsHandler.removeWithLabel('isolation');
this._injectionsHandler.removeWithLabel('isolation');
}
destroy() {
this._signalsHandler.destroy();
this._injectionsHandler.destroy();
}
};
var DockManager = class DashToDock_DockManager {
constructor() {
if (Me.imports.extension.dockManager)
throw new Error('DashToDock has been already initialized');
Me.imports.extension.dockManager = this;
this._remoteModel = new LauncherAPI.LauncherEntryRemoteModel();
this._signalsHandler = new Utils.GlobalSignalsHandler();
this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.dash-to-dock');
this._oldDash = Main.overview.isDummy ? null : Main.overview.dash;
this._ensureFileManagerClient();
/* Array of all the docks created */
this._allDocks = [];
this._createDocks();
// status variable: true when the overview is shown through the dash
// applications button.
this._forcedOverview = false;
// Connect relevant signals to the toggling function
this._bindSettingsChanges();
}
static getDefault() {
return Me.imports.extension.dockManager
}
static get allDocks() {
return DockManager.getDefault()._allDocks;
}
static get settings() {
return DockManager.getDefault()._settings;
}
static get useNewAllocation() {
/* Remove this when version prior to 3.38 are not supported anymore */
if (USE_NEW_ALLOCATION === undefined) {
/* We only support 3.36 and 3.38 right now, so no much to check */
USE_NEW_ALLOCATION = ExtensionUtils.versionCheck(
['3.37.91', '3.37.92', '3.38'],
imports.misc.config.PACKAGE_VERSION);
}
return USE_NEW_ALLOCATION;
}
get fm1Client() {
return this._fm1Client;
}
get mainDock() {
return this._allDocks.length ? this._allDocks[0] : null;
}
_ensureFileManagerClient() {
let supportsLocations = ['show-trash', 'show-mounts'].some((s) => {
return this._settings.get_boolean(s);
});
if (supportsLocations) {
if (!this._fm1Client)
this._fm1Client = new FileManager1API.FileManager1Client();
} else if (this._fm1Client) {
this._fm1Client.destroy();
this._fm1Client = null;
}
}
_toggle() {
if (this._toggleLater)
Meta.later_remove(this._toggleLater);
this._toggleLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
delete this._toggleLater;
this._restoreDash();
this._deleteDocks();
this._createDocks();
this.emit('toggled');
});
}
_bindSettingsChanges() {
// Connect relevant signals to the toggling function
this._signalsHandler.add([
Meta.MonitorManager.get(),
'monitors-changed',
this._toggle.bind(this)
], [
Main.sessionMode,
'updated',
this._toggle.bind(this)
], [
this._settings,
'changed::multi-monitor',
this._toggle.bind(this)
], [
this._settings,
'changed::preferred-monitor',
this._toggle.bind(this)
], [
this._settings,
'changed::dock-position',
this._toggle.bind(this)
], [
this._settings,
'changed::extend-height',
this._adjustPanelCorners.bind(this)
], [
this._settings,
'changed::dock-fixed',
this._adjustPanelCorners.bind(this)
], [
this._settings,
'changed::show-trash',
() => this._ensureFileManagerClient()
], [
this._settings,
'changed::show-mounts',
() => this._ensureFileManagerClient()
], );
}
_createDocks() {
// If there are no monitors (headless configurations, but it can also happen temporary while disconnecting
// and reconnecting monitors), just do nothing. When a monitor will be connected we we'll be notified and
// and thus create the docks. This prevents pointing trying to access monitors throughout the code, were we
// are assuming that at least the primary monitor is present.
if (Main.layoutManager.monitors.length <= 0) {
return;
}
this._preferredMonitorIndex = this._settings.get_int('preferred-monitor');
// In case of multi-monitor, we consider the dock on the primary monitor to be the preferred (main) one
// regardless of the settings
// The dock goes on the primary monitor also if the settings are incosistent (e.g. desired monitor not connected).
if (this._settings.get_boolean('multi-monitor') ||
this._preferredMonitorIndex < 0 || this._preferredMonitorIndex > Main.layoutManager.monitors.length - 1
) {
this._preferredMonitorIndex = Main.layoutManager.primaryIndex;
} else {
// Gdk and shell monitors numbering differ at least under wayland:
// While the primary monitor appears to be always index 0 in Gdk,
// the shell can assign a different number (Main.layoutManager.primaryMonitor)
// This ensure the indexing in the settings (Gdk) and in the shell are matched,
// i.e. that we start counting from the primaryMonitorIndex
this._preferredMonitorIndex = (Main.layoutManager.primaryIndex + this._preferredMonitorIndex) % Main.layoutManager.monitors.length ;
}
// First we create the main Dock, to get the extra features to bind to this one
let dock = new DockedDash(this._remoteModel, this._preferredMonitorIndex);
this._allDocks.push(dock);
// connect app icon into the view selector
dock.dash.showAppsButton.connect('notify::checked', this._onShowAppsButtonToggled.bind(this));
// Make the necessary changes to Main.overview.dash
this._prepareMainDash();
// Adjust corners if necessary
this._adjustPanelCorners();
if (this._settings.get_boolean('multi-monitor')) {
let nMon = Main.layoutManager.monitors.length;
for (let iMon = 0; iMon < nMon; iMon++) {
if (iMon == this._preferredMonitorIndex)
continue;
let dock = new DockedDash(this._remoteModel, iMon);
this._allDocks.push(dock);
// connect app icon into the view selector
dock.dash.showAppsButton.connect('notify::checked', this._onShowAppsButtonToggled.bind(this));
}
}
// Load optional features. We load *after* the docks are created, since
// we need to connect the signals to all dock instances.
this._workspaceIsolation = new WorkspaceIsolation();
this._keyboardShortcuts = new KeyboardShortcuts();
}
_prepareMainDash() {
// Ensure Main.overview.dash is set to our dash in dummy mode
// while just use the default getter otherwise.
// The getter must be dynamic and not set only when we've a dummy
// overview because the mode can change dynamically.
let defaultDashGetter = Object.getOwnPropertyDescriptor(
Main.overview.constructor.prototype, 'dash').get;
Object.defineProperty(Main.overview, 'dash', {
configurable: true,
get: () => Main.overview.isDummy ?
this.mainDock.dash : defaultDashGetter.call(Main.overview),
});
if (Main.overview.isDummy)
return;
this._signalsHandler.removeWithLabel('old-dash-changes');
// Hide usual Dash
this._oldDash.hide();
// Also set dash width to 1, so it's almost not taken into account by code
// calculaing the reserved space in the overview. The reason to keep it at 1 is
// to allow its visibility change to trigger an allocaion of the appGrid which
// in turn is triggergin the appsIcon spring animation, required when no other
// actors has this effect, i.e in horizontal mode and without the workspaceThumnails
// 1 static workspace only)
this._oldDash.set_width(1);
this._signalsHandler.addWithLabel('old-dash-changes', [
this._oldDash,
'notify::visible',
() => this._prepareMainDash()
], [
this._oldDash,
'notify::width',
() => this._prepareMainDash()
]);
// Pretend I'm the dash: meant to make appgrid swarm animation come from
// the right position of the appShowButton.
let overviewControls = Main.overview._overview._controls;
overviewControls.dash = this.mainDock.dash;
if (!this.mainDock.dash._isHorizontal && !this._oldDashSpacer) {
this._oldDashSpacer = overviewControls._dashSpacer;
overviewControls._group.remove_child(this._oldDashSpacer);
overviewControls._dashSpacer = this.mainDock._dashSpacer;
}
}
_deleteDocks() {
if (!this._allDocks.length)
return;
// Remove extra features
this._workspaceIsolation.destroy();
this._keyboardShortcuts.destroy();
// Delete all docks
this._allDocks.forEach(d => d.destroy());
this._allDocks = [];
}
_restoreDash() {
Object.defineProperty(Main.overview, 'dash',
Object.getOwnPropertyDescriptor(
Main.overview.constructor.prototype, 'dash'));
if (!this._oldDash)
return;
this._signalsHandler.removeWithLabel('old-dash-changes');
let overviewControls = Main.overview._overview._controls;
if (!!this._oldDashSpacer) {
overviewControls._dashSpacer = this._oldDashSpacer;
overviewControls._group.insert_child_at_index(this._oldDashSpacer, 0);
this._oldDashSpacer = null;
}
overviewControls.dash = this._oldDash;
Main.overview.dash.show();
Main.overview.dash.set_width(-1); //reset default dash size
// This force the recalculation of the icon size
Main.overview.dash._maxHeight = -1;
}
_onShowAppsButtonToggled(button) {
// Sync the status of the default appButtons. Only if the two statuses are
// different, that means the user interacted with the extension provided
// application button, cutomize the behaviour. Otherwise the shell has changed the
// status (due to the _syncShowAppsButtonToggled function below) and it
// has already performed the desired action.
let animate = this._settings.get_boolean('animate-show-apps');
let selector = Main.overview.viewSelector;
if (selector._showAppsButton.checked !== button.checked) {
let visibleView;
let overviewViews = Main.overview.viewSelector.appDisplay._views;
if (overviewViews) {
// find visible view in gnome-shell pre 3.38
visibleView = overviewViews.find(v => v.view.visible);
visibleView = visibleView ? visibleView.view : null;
} else {
visibleView = Main.overview.viewSelector.appDisplay;
}
if (button.checked) {
// force spring animation triggering.By default the animation only
// runs if we are already inside the overview.
if (!Main.overview._shown) {
this._forcedOverview = true;
let grid = visibleView._grid;
if (animate) {
// The animation has to be trigered manually because the AppDisplay.animate
// method is waiting for an allocation not happening, as we skip the workspace view
// and the appgrid could already be allocated from previous shown.
// It has to be triggered after the overview is shown as wrong coordinates are obtained
// otherwise.
// There was another issue in gnome 3.38 causing flickering every time using the previous
// workaround. Because it is no longer needed, only the part that prevents it from freezing
// on first opening is used.
Main.overview.viewSelector._activePage = Main.overview.viewSelector._appsPage;
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
grid.opacity = 255;
grid.animateSpring(IconGrid.AnimationDirection.IN, this.mainDock.dash.showAppsButton);
});
}
else {
Main.overview.viewSelector._activePage = Main.overview.viewSelector._appsPage;
Main.overview.viewSelector._activePage.show();
grid.opacity = 255;
}
}
// Finally show the overview
selector._showAppsButton.checked = true;
Main.overview.show();
}
else {
if (this._forcedOverview) {
// force exiting overview if needed
if (animate) {
// Manually trigger springout animation without activating the
// workspaceView to avoid the zoomout animation. Hide the appPage
// onComplete to avoid ugly flashing of original icons.
visibleView.animate(IconGrid.AnimationDirection.OUT, () => {
Main.overview.viewSelector._appsPage.hide();
Main.overview.hide();
selector._showAppsButton.checked = false;
this._forcedOverview = false;
});
}
else {
Main.overview.hide();
this._forcedOverview = false;
}
}
else {
selector._showAppsButton.checked = false;
this._forcedOverview = false;
}
}
}
// whenever the button is unactivated even if not by the user still reset the
// forcedOverview flag
if (button.checked == false)
this._forcedOverview = false;
}
destroy() {
this._signalsHandler.destroy();
if (this._toggleLater) {
Meta.later_remove(this._toggleLater);
delete this._toggleLater;
}
this._restoreDash();
this._deleteDocks();
this._revertPanelCorners();
if (this._oldSelectorMargin)
Main.overview.viewSelector.margin_bottom = this._oldSelectorMargin;
if (this._fm1Client) {
this._fm1Client.destroy();
this._fm1Client = null;
}
this._remoteModel.destroy();
this._settings.run_dispose();
this._settings = null;
this._oldDash = null;
Me.imports.extension.dockManager = null;
}
/**
* Adjust Panel corners
*/
_adjustPanelCorners() {
let position = Utils.getPosition();
let isHorizontal = ((position == St.Side.TOP) || (position == St.Side.BOTTOM));
let extendHeight = this._settings.get_boolean('extend-height');
let fixedIsEnabled = this._settings.get_boolean('dock-fixed');
let dockOnPrimary = this._settings.get_boolean('multi-monitor') ||
this._preferredMonitorIndex == Main.layoutManager.primaryIndex;
if (!isHorizontal && dockOnPrimary && extendHeight && fixedIsEnabled) {
Main.panel._rightCorner.hide();
Main.panel._leftCorner.hide();
}
else
this._revertPanelCorners();
}
_revertPanelCorners() {
Main.panel._leftCorner.show();
Main.panel._rightCorner.show();
}
};
Signals.addSignalMethods(DockManager.prototype);
// This class drives long-running icon animations, to keep them running in sync
// with each other, and to save CPU by pausing them when the dock is hidden.
var IconAnimator = class DashToDock_IconAnimator {
constructor(actor) {
this._count = 0;
this._started = false;
this._animations = {
dance: [],
};
this._timeline = new Clutter.Timeline({
duration: 3000,
repeat_count: -1,
});
/* Just use the construction property when no need to support 3.36 */
if (this._timeline.set_actor)
this._timeline.set_actor(actor);
this._timeline.connect('new-frame', () => {
const progress = this._timeline.get_progress();
const danceRotation = progress < 1/6 ? 15*Math.sin(progress*24*Math.PI) : 0;
const dancers = this._animations.dance;
for (let i = 0, iMax = dancers.length; i < iMax; i++) {
dancers[i].target.rotation_angle_z = danceRotation;
}
});
}
destroy() {
this._timeline.stop();
this._timeline = null;
for (const name in this._animations) {
const pairs = this._animations[name];
for (let i = 0, iMax = pairs.length; i < iMax; i++) {
const pair = pairs[i];
pair.target.disconnect(pair.targetDestroyId);
}
}
this._animations = null;
}
pause() {
if (this._started && this._count > 0) {
this._timeline.stop();
}
this._started = false;
}
start() {
if (!this._started && this._count > 0) {
this._timeline.start();
}
this._started = true;
}
addAnimation(target, name) {
const targetDestroyId = target.connect('destroy', () => this.removeAnimation(target, name));
this._animations[name].push({ target, targetDestroyId });
if (this._started && this._count === 0) {
this._timeline.start();
}
this._count++;
}
removeAnimation(target, name) {
const pairs = this._animations[name];
for (let i = 0, iMax = pairs.length; i < iMax; i++) {
const pair = pairs[i];
if (pair.target === target) {
target.disconnect(pair.targetDestroyId);
pairs.splice(i, 1);
this._count--;
if (this._started && this._count === 0) {
this._timeline.stop();
}
return;
}
}
}
};
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/devopssec/dash-to-dock.git
git@gitee.com:devopssec/dash-to-dock.git
devopssec
dash-to-dock
dash-to-dock
master

搜索帮助