diff --git a/Makefile b/Makefile
index ab19ca194..32fa1ed78 100644
--- a/Makefile
+++ b/Makefile
@@ -2,9 +2,9 @@
UUID = dash-to-dock@micxgx.gmail.com
BASE_MODULES = extension.js stylesheet.css metadata.json COPYING README.md
-EXTRA_MODULES = dockedDash.js intellihide.js myDash.js convenience.js prefs.js Settings.ui
+EXTRA_MODULES = convenience.js dash.js docking.js icons.js intellihide.js prefs.js theming.js windows.js
EXTRA_MEDIA = logo.svg
-TOLOCALIZE = prefs.js
+TOLOCALIZE = prefs.js dash.js docking.js windows.js icons.js
MSGSRC = $(wildcard po/*.po)
INSTALLBASE = ~/.local/share/gnome-shell/extensions
INSTALLNAME = dash-to-dock@micxgx.gmail.com
diff --git a/Settings.ui b/Settings.ui
index 0368601e9..8da32c1f1 100644
--- a/Settings.ui
+++ b/Settings.ui
@@ -937,6 +937,48 @@
+
+
+
diff --git a/appIcons.js b/appIcons.js
new file mode 100644
index 000000000..b5677d90b
--- /dev/null
+++ b/appIcons.js
@@ -0,0 +1,698 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Signals = imports.signals;
+
+const Clutter = imports.gi.Clutter;
+const Meta = imports.gi.Meta;
+const Shell = imports.gi.Shell;
+const St = imports.gi.St;
+
+const AppDisplay = imports.ui.appDisplay;
+const Dash = imports.ui.dash;
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+const Tweener = imports.ui.tweener;
+
+const Util = imports.misc.util;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const Convenience = Me.imports.convenience;
+const Windows = Me.imports.windows;
+
+let tracker = Shell.WindowTracker.get_default();
+
+let DASH_ITEM_LABEL_SHOW_TIME = Dash.DASH_ITEM_LABEL_SHOW_TIME;
+
+const ClickAction = {
+ SKIP: 0,
+ MINIMIZE: 1,
+ LAUNCH: 2,
+ CYCLE_WINDOWS: 3
+};
+
+let recentlyClickedAppLoopId = 0;
+let recentlyClickedApp = null;
+let recentlyClickedAppWindows = null;
+let recentlyClickedAppIndex = 0;
+
+/**
+ * Extend AppIcon
+ *
+ * - Pass settings to the constructor and bind settings changes
+ * - Apply a css class based on the number of windows of each application (#N);
+ * - Draw a dot for each window of the application based on the default "dot" style which is hidden (#N);
+ * a class of the form "running#N" is applied to the AppWellIcon actor.
+ * like the original .running one.
+ * - Add a .focused style to the focused app
+ * - Customize click actions.
+ * - Update minimization animation target
+ * - Support configurable "window stealing"
+ */
+const MyAppIcon = new Lang.Class({
+ Name: 'DashToDock.AppIcon',
+ Extends: AppDisplay.AppIcon,
+
+ /**
+ * Settings are required inside.
+ */
+ _init: function(settings, app, iconParams, onActivateOverride) {
+ this._dtdSettings = settings;
+ this._nWindows = 0;
+
+ this.parent(app, iconParams, onActivateOverride);
+
+ // Monitor windows-changes instead of app state.
+ // Keep using the same Id and function callback (that is extended)
+ if (this._stateChangedId > 0) {
+ this.app.disconnect(this._stateChangedId);
+ this._stateChangedId = 0;
+ }
+
+ this._stateChangedId = this.app.connect('windows-changed', Lang.bind(this, this.onWindowsChanged));
+ this._focusAppChangeId = tracker.connect('notify::focus-app', Lang.bind(this, this._onFocusAppChanged));
+
+ this._dots = null;
+
+ let keys = ['apply-custom-theme',
+ 'custom-theme-running-dots',
+ 'custom-theme-customize-running-dots',
+ 'custom-theme-running-dots-color',
+ 'custom-theme-running-dots-border-color',
+ 'custom-theme-running-dots-border-width'];
+
+ keys.forEach(function(key) {
+ this._dtdSettings.connect('changed::' + key, Lang.bind(this, this._toggleDots));
+ }, this);
+
+ this._toggleDots();
+ },
+
+ _onDestroy: function() {
+ this.parent();
+
+ // Disconect global signals
+ // (stateChangedId is already handled by parent)
+ if (this._focusAppChangeId > 0)
+ tracker.disconnect(this._focusAppChangeId);
+ },
+
+ onWindowsChanged: function() {
+ this._updateRunningStyle();
+ this.updateIconGeometry();
+ },
+
+ /**
+ * Update taraget for minimization animation
+ */
+ updateIconGeometry: function() {
+ // If (for unknown reason) the actor is not on the stage the reported size
+ // and position are random values, which might exceeds the integer range
+ // resulting in an error when assigned to the a rect. This is a more like
+ // a workaround to prevent flooding the system with errors.
+ if (this.actor.get_stage() == null)
+ return;
+
+ let rect = new Meta.Rectangle();
+
+ if (Windows.isStolen(this.app, this._dtdSettings)) {
+ // Hide stolen app icon
+ if (this.actor.child) {
+ // TODO: if settings changed, how do we recreate the child?
+ this.actor.child.destroy();
+ this.actor.child = null;
+ }
+ }
+
+ [rect.x, rect.y] = this.actor.get_transformed_position();
+ [rect.width, rect.height] = this.actor.get_transformed_size();
+
+ let windows = Windows.getAllWindows(this.app, this._dtdSettings);
+ windows.forEach(function(w) {
+ w.set_icon_geometry(rect);
+ });
+ },
+
+ _toggleDots: function() {
+ if (this._dtdSettings.get_boolean('custom-theme-running-dots') || this._dtdSettings.get_boolean('apply-custom-theme'))
+ this._showDots();
+ else
+ this._hideDots();
+ },
+
+ _showDots: function() {
+ // I use opacity to hide the default dot because the show/hide function
+ // are used by the parent class.
+ this._dot.opacity = 0;
+
+ // Just update style if dots already exist
+ if (this._dots) {
+ this._updateCounterClass();
+ return;
+ }
+
+ this._dots = new St.DrawingArea({x_expand: true, y_expand: true});
+ this._dots.connect('repaint', Lang.bind(this, function() {
+ this._drawCircles(this._dots, Convenience.getPosition(this._dtdSettings));
+ }));
+ this._iconContainer.add_child(this._dots);
+ this._updateCounterClass();
+ },
+
+ _hideDots: function() {
+ this._dot.opacity = 255;
+ if (this._dots)
+ this._dots.destroy()
+ this._dots = null;
+ },
+
+ _updateRunningStyle: function() {
+ this.parent();
+ this._updateCounterClass();
+ },
+
+ popupMenu: function() {
+ this._removeMenuTimeout();
+ this.actor.fake_release();
+ this._draggable.fakeRelease();
+
+ if (!this._menu) {
+ this._menu = new MyAppIconMenu(this, this._dtdSettings);
+ this._menu.connect('activate-window', Lang.bind(this, function(menu, window) {
+ this.activateWindow(window);
+ }));
+ this._menu.connect('open-state-changed', Lang.bind(this, function(menu, isPoppedUp) {
+ if (!isPoppedUp)
+ this._onMenuPoppedDown();
+ }));
+ let id = Main.overview.connect('hiding', Lang.bind(this, function() {
+ this._menu.close();
+ }));
+ this._menu.actor.connect('destroy', function() {
+ Main.overview.disconnect(id);
+ });
+
+ this._menuManager.addMenu(this._menu);
+ }
+
+ this.emit('menu-state-changed', true);
+
+ this.actor.set_hover(true);
+ this._menu.popup();
+ this._menuManager.ignoreRelease();
+ this.emit('sync-tooltip');
+
+ return false;
+ },
+
+ _onFocusAppChanged: function() {
+ let focusedApp = tracker.focus_app;
+ let isFocused = Windows.isWindowStealer(this.app, this._dtdSettings) ?
+ Windows.isStealingFrom(this.app, focusedApp, this._dtdSettings) :
+ this.app == focusedApp;
+ if (isFocused)
+ this.actor.add_style_class_name('focused');
+ else
+ this.actor.remove_style_class_name('focused');
+ },
+
+ activate: function(button) {
+ if (!this._dtdSettings.get_boolean('customize-click')) {
+ this.parent(button);
+ return;
+ }
+
+ let isRunning = Windows.isWindowStealer(this.app, this._dtdSettings) ?
+ Windows.getAllWindows(this.app, this._dtdSettings).length > 0 :
+ this.app.state == Shell.AppState.RUNNING;
+
+ let event = Clutter.get_current_event();
+ let modifiers = event ? event.get_state() : 0;
+ let openNewWindow = (modifiers & Clutter.ModifierType.CONTROL_MASK) &&
+ isRunning ||
+ button && (button == 2);
+
+ let focusedApp = tracker.focus_app;
+ let isFocused = Windows.isWindowStealer(this.app, this._dtdSettings) ?
+ Windows.isStealingFrom(this.app, focusedApp, this._dtdSettings) :
+ this.app == focusedApp;
+
+ if (!isRunning || openNewWindow)
+ this.animateLaunch();
+
+ if (button && button == 1 && isRunning) {
+ if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
+ // Keep default behaviour: launch new window
+ // By calling the parent method I make it compatible
+ // with other extensions tweaking ctrl + click
+ this.parent(button);
+ return;
+
+ }
+ else if (this._dtdSettings.get_boolean('minimize-shift') && modifiers & Clutter.ModifierType.SHIFT_MASK) {
+ // On double click, minimize all windows in the current workspace
+ minimizeWindow(this.app, event.get_click_count() > 1, this._dtdSettings);
+ }
+ else if (isFocused && !Main.overview._shown) {
+ if (this._dtdSettings.get_enum('click-action') == ClickAction.CYCLE_WINDOWS)
+ cycleThroughWindows(this.app, this._dtdSettings);
+ else if (this._dtdSettings.get_enum('click-action') == ClickAction.MINIMIZE)
+ minimizeWindow(this.app, true, this._dtdSettings);
+ else if (this._dtdSettings.get_enum('click-action') == ClickAction.LAUNCH)
+ this.app.open_new_window(-1);
+ }
+ else {
+ // Activate all window of the app or only le last used
+ if (this._dtdSettings.get_enum('click-action') == ClickAction.CYCLE_WINDOWS && !Main.overview._shown) {
+ // If click cycles through windows I can activate one windows at a time
+ let windows = Windows.getInterestingWindows(this.app, this._dtdSettings);
+ let w = windows[0];
+ Main.activateWindow(w);
+ }
+ else if (this._dtdSettings.get_enum('click-action') == ClickAction.LAUNCH)
+ this.app.open_new_window(-1);
+ else if (this._dtdSettings.get_enum('click-action') == ClickAction.MINIMIZE) {
+ // If click minimizes all, then one expects all windows to be reshown
+ activateAllWindows(this.app, this._dtdSettings);
+ }
+ else
+ this.app.activate();
+ }
+ }
+ else {
+ // Default behaviour
+ if (openNewWindow)
+ this.app.open_new_window(-1);
+ else
+ this.app.activate();
+ }
+
+ Main.overview.hide();
+ },
+
+ _updateCounterClass: function() {
+ let maxN = 4;
+ this._nWindows = Math.min(Windows.getInterestingWindows(this.app, this._dtdSettings).length, maxN);
+
+ for (let i = 1; i <= maxN; i++) {
+ let className = 'running' + i;
+ if (i != this._nWindows)
+ this.actor.remove_style_class_name(className);
+ else
+ this.actor.add_style_class_name(className);
+ }
+
+ if (this._dots)
+ this._dots.queue_repaint();
+ },
+
+ _drawCircles: function(area, side) {
+ let borderColor, borderWidth, bodyColor;
+
+ if (!this._dtdSettings.get_boolean('apply-custom-theme')
+ && this._dtdSettings.get_boolean('custom-theme-running-dots')
+ && this._dtdSettings.get_boolean('custom-theme-customize-running-dots')) {
+ borderColor = Clutter.color_from_string(this._dtdSettings.get_string('custom-theme-running-dots-border-color'))[1];
+ borderWidth = this._dtdSettings.get_int('custom-theme-running-dots-border-width');
+ bodyColor = Clutter.color_from_string(this._dtdSettings.get_string('custom-theme-running-dots-color'))[1];
+ }
+ else {
+ // Re-use the style - background color, and border width and color -
+ // of the default dot
+ let themeNode = this._dot.get_theme_node();
+ borderColor = themeNode.get_border_color(side);
+ borderWidth = themeNode.get_border_width(side);
+ bodyColor = themeNode.get_background_color();
+ }
+
+ let [width, height] = area.get_surface_size();
+ let cr = area.get_context();
+
+ // Draw the required numbers of dots
+ let radius = width/22 - borderWidth/2;
+ radius = Math.max(radius, borderWidth/4+1);
+ let padding = 0; // distance from the margin
+ let spacing = radius + borderWidth; // separation between the dots
+ let n = this._nWindows;
+
+ cr.setLineWidth(borderWidth);
+ Clutter.cairo_set_source_color(cr, borderColor);
+
+ switch (side) {
+ case St.Side.TOP:
+ cr.translate((width - (2*n)*radius - (n-1)*spacing)/2, padding);
+ for (let i = 0; i < n; i++) {
+ cr.newSubPath();
+ cr.arc((2*i+1)*radius + i*spacing, radius + borderWidth/2, radius, 0, 2*Math.PI);
+ }
+ break;
+
+ case St.Side.BOTTOM:
+ cr.translate((width - (2*n)*radius - (n-1)*spacing)/2, height- padding- 2*radius);
+ for (let i = 0; i < n; i++) {
+ cr.newSubPath();
+ cr.arc((2*i+1)*radius + i*spacing, radius + borderWidth/2, radius, 0, 2*Math.PI);
+ }
+ break;
+
+ case St.Side.LEFT:
+ cr.translate(padding, (height - (2*n)*radius - (n-1)*spacing)/2);
+ for (let i = 0; i < n; i++) {
+ cr.newSubPath();
+ cr.arc(radius + borderWidth/2, (2*i+1)*radius + i*spacing, radius, 0, 2*Math.PI);
+ }
+ break;
+
+ case St.Side.RIGHT:
+ cr.translate(width - padding- 2*radius, (height - (2*n)*radius - (n-1)*spacing)/2);
+ for (let i = 0; i < n; i++) {
+ cr.newSubPath();
+ cr.arc(radius + borderWidth/2, (2*i+1)*radius + i*spacing, radius, 0, 2*Math.PI);
+ }
+ break;
+ }
+
+ cr.strokePreserve();
+
+ Clutter.cairo_set_source_color(cr, bodyColor);
+ cr.fill();
+ cr.$dispose();
+ }
+});
+
+function minimizeWindow(app, param, settings) {
+ // Param true make all app windows minimize
+ let windows = Windows.getInterestingWindows(app, settings);
+ let current_workspace = global.screen.get_active_workspace();
+ for (let i = windows.length - 1; i >= 0; i--) {
+ let w = windows[i];
+ if (w.get_workspace() == current_workspace && w.showing_on_its_workspace()) {
+ w.minimize();
+ // Just minimize one window. By specification it should be the
+ // focused window on the current workspace.
+ if (!param)
+ break;
+ }
+ }
+}
+
+/**
+ * By default only non minimized windows are activated.
+ * This activates all windows in the current workspace.
+ */
+function activateAllWindows(app, settings) {
+ // First activate first window so workspace is switched if needed.
+ if (!Windows.isWindowStealer(app, settings))
+ app.activate();
+
+ // then activate all other app windows in the current workspace
+ let windows = Windows.getInterestingWindows(app, settings);
+ let activeWorkspace = global.screen.get_active_workspace_index();
+
+ if (windows.length <= 0)
+ return;
+
+ let activatedWindows = 0;
+
+ for (let i = windows.length - 1; i >= 0; i--) {
+ if (windows[i].get_workspace().index() == activeWorkspace) {
+ Main.activateWindow(windows[i]);
+ activatedWindows++;
+ }
+ }
+}
+
+function cycleThroughWindows(app, settings) {
+ // Store for a little amount of time last clicked app and its windows
+ // since the order changes upon window interaction
+ const MEMORY_TIME = 3000;
+
+ let app_windows = Windows.getInterestingWindows(app, settings);
+
+ if (recentlyClickedAppLoopId > 0)
+ Mainloop.source_remove(recentlyClickedAppLoopId);
+ recentlyClickedAppLoopId = Mainloop.timeout_add(MEMORY_TIME, resetRecentlyClickedApp);
+
+ // If there isn't already a list of windows for the current app,
+ // or the stored list is outdated, use the current windows list.
+ if (!recentlyClickedApp ||
+ recentlyClickedApp.get_id() != app.get_id() ||
+ recentlyClickedAppWindows.length != app_windows.length) {
+ recentlyClickedApp = app;
+ recentlyClickedAppWindows = app_windows;
+ recentlyClickedAppIndex = 0;
+ }
+
+ recentlyClickedAppIndex++;
+ let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length;
+ let window = recentlyClickedAppWindows[index];
+
+ Main.activateWindow(window);
+}
+
+function resetRecentlyClickedApp() {
+ if (recentlyClickedAppLoopId > 0)
+ Mainloop.source_remove(recentlyClickedAppLoopId);
+ recentlyClickedAppLoopId = 0;
+ recentlyClickedApp = null;
+ recentlyClickedAppWindows = null;
+ recentlyClickedAppIndex = 0;
+
+ return false;
+}
+
+/**
+ * Extend AppIconMenu
+ *
+ * - Pass settings to the constructor
+ * - set popup arrow side based on dash orientation
+ * - Add close windows option based on quitfromdash extension
+ * (https://github.com/deuill/shell-extension-quitfromdash)
+ */
+const MyAppIconMenu = new Lang.Class({
+ Name: 'DashToDock.MyAppIconMenu',
+ Extends: AppDisplay.AppIconMenu,
+
+ _init: function(source, settings) {
+ let side = Convenience.getPosition(settings);
+
+ // Damm it, there has to be a proper way of doing this...
+ // As I can't call the parent parent constructor (?) passing the side
+ // parameter, I overwite what I need later
+ this.parent(source);
+
+ // Change the initialized side where required.
+ this._arrowSide = side;
+ this._boxPointer._arrowSide = side;
+ this._boxPointer._userArrowSide = side;
+
+ this._dtdSettings = settings;
+ },
+
+ /**
+ * Helper function for the quit windows abilities
+ */
+ _closeWindowInstance: function(metaWindow) {
+ metaWindow.delete(global.get_current_time());
+ },
+
+ _redisplay: function() {
+ this.parent();
+
+ // steal windows menu
+ if (this._dtdSettings.get_boolean('support-window-stealing')) {
+ this._appendSeparator();
+ this._stealWindowsMenuItem = this._appendMenuItem(_("Steal Windows"));
+ this._stealWindowsMenuItem.connect('activate', Lang.bind(this, function() {
+ let dialog = new Windows.WindowStealingSettings(this._source.app, this._dtdSettings);
+ dialog.open();
+ }));
+ }
+
+ // quit menu
+ let app = this._source.app;
+ let count = Windows.getInterestingWindows(app, this._dtdSettings).length;
+ if (count > 0) {
+ this._appendSeparator();
+ let quitFromDashMenuText = '';
+ if (count == 1)
+ quitFromDashMenuText = _("Quit");
+ else
+ quitFromDashMenuText = _("Quit") + ' ' + count + ' ' + _("Windows");
+
+ this._quitfromDashMenuItem = this._appendMenuItem(quitFromDashMenuText);
+ this._quitfromDashMenuItem.connect('activate', Lang.bind(this, function() {
+ let app = this._source.app;
+ let windows = Windows.getAllWindows(app, this._dtdSettings);
+ for (let i = 0; i < windows.length; i++) {
+ this._closeWindowInstance(windows[i])
+ }
+ }));
+ }
+ }
+});
+
+/**
+ * Extend ShowAppsIcon
+ *
+ * - Pass settings to the constructor
+ * - set label position based on dash orientation
+ * - implement a popupMenu based on the AppIcon code
+ *
+ * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973.
+ * thus use this ugly pattern.
+ */
+function extendShowAppsIcon(showAppsIcon, settings) {
+ showAppsIcon._dtdSettings = settings;
+ /* the variable equivalent to toggleButton has a different name in the appIcon class
+ (actor): duplicate reference to easily reuse appIcon methods */
+ showAppsIcon.actor = showAppsIcon.toggleButton;
+
+ // Re-use appIcon methods
+ showAppsIcon._removeMenuTimeout = AppDisplay.AppIcon.prototype._removeMenuTimeout;
+ showAppsIcon._setPopupTimeout = AppDisplay.AppIcon.prototype._setPopupTimeout;
+ showAppsIcon._onButtonPress = AppDisplay.AppIcon.prototype._onButtonPress;
+ showAppsIcon._onKeyboardPopupMenu = AppDisplay.AppIcon.prototype._onKeyboardPopupMenu;
+ showAppsIcon._onLeaveEvent = AppDisplay.AppIcon.prototype._onLeaveEvent;
+ showAppsIcon._onTouchEvent = AppDisplay.AppIcon.prototype._onTouchEvent;
+ showAppsIcon._onMenuPoppedDown = AppDisplay.AppIcon.prototype._onMenuPoppedDown;
+
+
+ // No action on clicked (showing of the appsview is controlled elsewhere)
+ showAppsIcon._onClicked = function(actor, button) {
+ showAppsIcon._removeMenuTimeout();
+ };
+
+ showAppsIcon.actor.connect('leave-event', Lang.bind(showAppsIcon, showAppsIcon._onLeaveEvent));
+ showAppsIcon.actor.connect('button-press-event', Lang.bind(showAppsIcon, showAppsIcon._onButtonPress));
+ showAppsIcon.actor.connect('touch-event', Lang.bind(showAppsIcon, showAppsIcon._onTouchEvent));
+ showAppsIcon.actor.connect('clicked', Lang.bind(showAppsIcon, showAppsIcon._onClicked));
+ showAppsIcon.actor.connect('popup-menu', Lang.bind(showAppsIcon, showAppsIcon._onKeyboardPopupMenu));
+
+ showAppsIcon._menu = null;
+ showAppsIcon._menuManager = new PopupMenu.PopupMenuManager(showAppsIcon);
+ showAppsIcon._menuTimeoutId = 0;
+
+ showAppsIcon.showLabel = itemShowLabel;
+
+ showAppsIcon.popupMenu = function() {
+ showAppsIcon._removeMenuTimeout();
+ showAppsIcon.actor.fake_release();
+
+ if (!showAppsIcon._menu) {
+ showAppsIcon._menu = new MyShowAppsIconMenu(showAppsIcon, showAppsIcon._dtdSettings);
+ showAppsIcon._menu.connect('open-state-changed', Lang.bind(showAppsIcon, function(menu, isPoppedUp) {
+ if (!isPoppedUp)
+ showAppsIcon._onMenuPoppedDown();
+ }));
+ let id = Main.overview.connect('hiding', Lang.bind(showAppsIcon, function() {
+ showAppsIcon._menu.close();
+ }));
+ showAppsIcon._menu.actor.connect('destroy', function() {
+ Main.overview.disconnect(id);
+ });
+ showAppsIcon._menuManager.addMenu(showAppsIcon._menu);
+ }
+
+ showAppsIcon.emit('menu-state-changed', true);
+
+ showAppsIcon.actor.set_hover(true);
+ showAppsIcon._menu.popup();
+ showAppsIcon._menuManager.ignoreRelease();
+ showAppsIcon.emit('sync-tooltip');
+
+ return false;
+ };
+
+ Signals.addSignalMethods(showAppsIcon);
+}
+
+/**
+ * A menu for the showAppsIcon
+ */
+const MyShowAppsIconMenu = new Lang.Class({
+ Name: 'DashToDock.ShowAppsIconMenu',
+ Extends: MyAppIconMenu,
+
+ _redisplay: function() {
+ this.removeAll();
+
+ let item = this._appendMenuItem('Dash to Dock ' + _("Settings"));
+
+ item.connect('activate', function () {
+ Util.spawn(["gnome-shell-extension-prefs", Me.metadata.uuid]);
+ });
+ }
+});
+
+/**
+ * This function is used for both extendShowAppsIcon and extendDashItemContainer
+ */
+function itemShowLabel() {
+ if (!this._labelText)
+ return;
+
+ this.label.set_text(this._labelText);
+ this.label.opacity = 0;
+ this.label.show();
+
+ let [stageX, stageY] = this.get_transformed_position();
+ let node = this.label.get_theme_node();
+
+ let itemWidth = this.allocation.x2 - this.allocation.x1;
+ let itemHeight = this.allocation.y2 - this.allocation.y1;
+
+ let labelWidth = this.label.get_width();
+ let labelHeight = this.label.get_height();
+
+ let x, y, xOffset, yOffset;
+
+ let position = Convenience.getPosition(this._dtdSettings);
+ this._isHorizontal = ((position == St.Side.TOP) || (position == St.Side.BOTTOM));
+ let labelOffset = node.get_length('-x-offset');
+
+ switch (position) {
+ case St.Side.LEFT:
+ yOffset = Math.floor((itemHeight - labelHeight) / 2);
+ y = stageY + yOffset;
+ xOffset = labelOffset;
+ x = stageX + this.get_width() + xOffset;
+ break;
+ case St.Side.RIGHT:
+ yOffset = Math.floor((itemHeight - labelHeight) / 2);
+ y = stageY + yOffset;
+ xOffset = labelOffset;
+ x = Math.round(stageX) - labelWidth - xOffset;
+ break;
+ case St.Side.TOP:
+ y = stageY + labelOffset + itemHeight;
+ xOffset = Math.floor((itemWidth - labelWidth) / 2);
+ x = stageX + xOffset;
+ break;
+ case St.Side.BOTTOM:
+ yOffset = labelOffset;
+ y = stageY - labelHeight - yOffset;
+ xOffset = Math.floor((itemWidth - labelWidth) / 2);
+ x = stageX + xOffset;
+ break;
+ }
+
+ // keep the label inside the screen border
+ // Only needed fot the x coordinate.
+
+ // Leave a few pixel gap
+ let gap = 5;
+ let monitor = Main.layoutManager.findMonitorForActor(this);
+ if (x - monitor.x < gap)
+ x += monitor.x - x + labelOffset;
+ else if (x + labelWidth > monitor.x + monitor.width - gap)
+ x -= x + labelWidth - (monitor.x + monitor.width) + gap;
+
+ this.label.set_position(x, y);
+ Tweener.addTween(this.label, {
+ opacity: 255,
+ time: DASH_ITEM_LABEL_SHOW_TIME,
+ transition: 'easeOutQuad',
+ });
+}
diff --git a/convenience.js b/convenience.js
index b4d336c03..16f939c40 100644
--- a/convenience.js
+++ b/convenience.js
@@ -3,17 +3,16 @@
/*
* Part of this file comes from gnome-shell-extensions:
* http://git.gnome.org/browse/gnome-shell-extensions/
- *
*/
-
const Gettext = imports.gettext;
-const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils;
+const Clutter = imports.gi.Clutter;
+const Gio = imports.gi.Gio;
/**
* initTranslations:
@@ -27,7 +26,7 @@ function initTranslations(domain) {
domain = domain || extension.metadata['gettext-domain'];
- // check if this extension was built with "make zip-file", and thus
+ // Check if this extension was built with "make zip-file", and thus
// has the locale files in a subfolder
// otherwise assume that extension has been installed in the
// same prefix as gnome-shell
@@ -53,7 +52,7 @@ function getSettings(schema) {
const GioSSS = Gio.SettingsSchemaSource;
- // check if this extension was built with "make zip-file", and thus
+ // Check if this extension was built with "make zip-file", and thus
// has the schema files in a subfolder
// otherwise assume that extension has been installed in the
// same prefix as gnome-shell (and therefore schemas are available
@@ -72,23 +71,26 @@ function getSettings(schema) {
throw new Error('Schema ' + schema + ' could not be found for extension '
+ extension.metadata.uuid + '. Please check your installation.');
- return new Gio.Settings({ settings_schema: schemaObj });
+ return new Gio.Settings({
+ settings_schema: schemaObj
+ });
}
-// simplify global signals and function injections handling
-// abstract class
+/**
+ * Simplify global signals and function injections handling
+ * abstract class
+ */
const BasicHandler = new Lang.Class({
- Name: 'dashToDock.BasicHandler',
+ Name: 'DashToDock.BasicHandler',
- _init: function(){
+ _init: function() {
this._storage = new Object();
},
- add: function(/*unlimited 3-long array arguments*/){
-
- // convert arguments object to array, concatenate with generic
+ add: function(/* unlimited 3-long array arguments */) {
+ // Convert arguments object to array, concatenate with generic
let args = Array.concat('generic', Array.slice(arguments));
- // call addWithLabel with ags as if they were passed arguments
+ // Call addWithLabel with ags as if they were passed arguments
this.addWithLabel.apply(this, args);
},
@@ -97,76 +99,79 @@ const BasicHandler = new Lang.Class({
this.removeWithLabel(label);
},
- addWithLabel: function( label /* plus unlimited 3-long array arguments*/) {
-
- if(this._storage[label] == undefined)
+ addWithLabel: function(label /* plus unlimited 3-long array arguments*/) {
+ if (this._storage[label] == undefined)
this._storage[label] = new Array();
- // skip first element of the arguments
- for( let i = 1; i < arguments.length; i++ ) {
- this._storage[label].push( this._create(arguments[i]) );
+ // Skip first element of the arguments
+ for (let i = 1; i < arguments.length; i++) {
+ this._storage[label].push( this._create(arguments[i]));
}
-
},
- removeWithLabel: function(label){
-
- if(this._storage[label]) {
- for( let i = 0; i < this._storage[label].length; i++ ) {
+ removeWithLabel: function(label) {
+ if (this._storage[label]) {
+ for (let i = 0; i < this._storage[label].length; i++)
this._remove(this._storage[label][i]);
- }
delete this._storage[label];
}
},
- /* Virtual methods to be implemented by subclass */
- // create single element to be stored in the storage structure
- _create: function(item){
- throw new Error('no implementation of _create in ' + this);
+ // Virtual methods to be implemented by subclass
+
+ /**
+ * Create single element to be stored in the storage structure
+ */
+ _create: function(item) {
+ throw new Error('no implementation of _create in ' + this);
},
- // correctly delete single element
- _remove: function(item){
- throw new Error('no implementation of _remove in ' + this);
+ /**
+ * Correctly delete single element
+ */
+ _remove: function(item) {
+ throw new Error('no implementation of _remove in ' + this);
}
});
-// Manage global signals
+/**
+ * Manage global signals
+ */
const GlobalSignalsHandler = new Lang.Class({
Name: 'DashToDock.GlobalSignalHandler',
Extends: BasicHandler,
_create: function(item) {
+ let object = item[0];
+ let event = item[1];
+ let callback = item[2]
+ let id = object.connect(event, callback);
- let object = item[0];
- let event = item[1];
- let callback = item[2]
- let id = object.connect(event, callback);
-
- return [object, id];
+ return [object, id];
},
- _remove: function(item){
- item[0].disconnect(item[1]);
+ _remove: function(item) {
+ item[0].disconnect(item[1]);
}
});
-// Manage function injection: both instances and prototype can be overridden
-// and restored
+/**
+ * Manage function injection: both instances and prototype can be overridden
+ * and restored
+ */
const InjectionsHandler = new Lang.Class({
Name: 'DashToDock.InjectionsHandler',
Extends: BasicHandler,
_create: function(item) {
+ let object = item[0];
+ let name = item[1];
+ let injectedFunction = item[2];
+ let original = object[name];
- let object = item[0];
- let name = item[1];
- let injectedFunction = item[2];
- let original = object[name];
-
- object[name] = injectedFunction;
- return [object, name, injectedFunction, original];
+ object[name] = injectedFunction;
+ return [object, name, injectedFunction, original];
},
_remove: function(item) {
@@ -176,3 +181,17 @@ const InjectionsHandler = new Lang.Class({
object[name] = original;
}
});
+
+/**
+ * Return the actual position reverseing left and right in rtl
+ */
+function getPosition(settings) {
+ let position = settings.get_enum('dock-position');
+ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
+ if (position == St.Side.LEFT)
+ position = St.Side.RIGHT;
+ else if (position == St.Side.RIGHT)
+ position = St.Side.LEFT;
+ }
+ return position;
+}
diff --git a/dash.js b/dash.js
new file mode 100644
index 000000000..9e8572cb5
--- /dev/null
+++ b/dash.js
@@ -0,0 +1,1147 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Signals = imports.signals;
+
+const Clutter = imports.gi.Clutter;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const Meta = imports.gi.Meta;
+const Shell = imports.gi.Shell;
+const St = imports.gi.St;
+
+const AppFavorites = imports.ui.appFavorites;
+const Dash = imports.ui.dash;
+const DND = imports.ui.dnd;
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+const Tweener = imports.ui.tweener;
+
+const Util = imports.misc.util;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const Convenience = Me.imports.convenience;
+const AppIcons = Me.imports.appIcons;
+const Windows = Me.imports.windows;
+
+let DASH_ANIMATION_TIME = Dash.DASH_ANIMATION_TIME;
+let DASH_ITEM_LABEL_HIDE_TIME = Dash.DASH_ITEM_LABEL_HIDE_TIME;
+let DASH_ITEM_HOVER_TIMEOUT = Dash.DASH_ITEM_HOVER_TIMEOUT;
+
+/**
+ * Extend DashItemContainer
+ *
+ * - Pass settings to the constructor
+ * - set label position based on dash orientation
+ *
+ * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973.
+ * thus use this ugly pattern.
+ */
+function extendDashItemContainer(dashItemContainer, settings) {
+ dashItemContainer._dtdSettings = settings;
+ dashItemContainer.showLabel = AppIcons.itemShowLabel;
+}
+
+/**
+ * This class is a fork of the upstream DashActor class (ui.dash.js)
+ *
+ * Summary of changes:
+ * - passed settings to class as parameter
+ * - modified chldBox calculations for when 'show-apps-at-top' option is checked
+ * - handle horizontal dash
+ */
+const MyDashActor = new Lang.Class({
+ Name: 'DashToDock.MyDashActor',
+
+ _init: function(settings) {
+ this._dtdSettings = settings;
+ this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
+
+ this._position = Convenience.getPosition(settings);
+ this._isHorizontal = ((this._position == St.Side.TOP) ||
+ (this._position == St.Side.BOTTOM));
+
+ let layout = new Clutter.BoxLayout({
+ orientation: this._isHorizontal ? Clutter.Orientation.HORIZONTAL : Clutter.Orientation.VERTICAL
+ });
+
+ this.actor = new Shell.GenericContainer({
+ name: 'dash',
+ layout_manager: layout,
+ clip_to_allocation: true
+ });
+ this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
+ this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
+ this.actor.connect('allocate', Lang.bind(this, this._allocate));
+
+ this.actor._delegate = this;
+
+ // Popup menu
+ let side = Convenience.getPosition(settings);
+ this.menu = new PopupMenu.PopupMenu(this.actor, 0.0, side, 0);
+ this.menu.actor.add_style_class_name('panel-menu');
+ let settingsMenuItem = new PopupMenu.PopupMenuItem('Dash to Dock ' + _("Settings"));
+ settingsMenuItem.connect('activate', Lang.bind(this, function() {
+ Util.spawn(['gnome-shell-extension-prefs', Me.metadata.uuid]);
+ }));
+ this.menu.addMenuItem(settingsMenuItem);
+
+ Main.uiGroup.add_actor(this.menu.actor);
+ this.menu.close();
+
+ // Menu manager is required
+ this._menuManager = new PopupMenu.PopupMenuManager(this);
+ this._menuManager.addMenu(this.menu);
+
+ // Right-click on dash to toggle popup menu
+ this.actor.reactive = true;
+ this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
+ if (event.get_state() & Clutter.ModifierType.BUTTON3_MASK) {
+ this.menu.toggle();
+ }
+ }));
+ },
+
+ _allocate: function(actor, box, flags) {
+ let contentBox = box;
+ let availWidth = contentBox.x2 - contentBox.x1;
+ let availHeight = contentBox.y2 - contentBox.y1;
+
+ let [appIcons, showAppsButton] = actor.get_children();
+ let [showAppsMinHeight, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth);
+ let [showAppsMinWidth, showAppsNatWidth] = showAppsButton.get_preferred_width(availHeight);
+
+ let offset_x = this._isHorizontal?showAppsNatWidth:0;
+ let offset_y = this._isHorizontal?0:showAppsNatHeight;
+
+ let childBox = new Clutter.ActorBox();
+ if ((this._dtdSettings.get_boolean('show-apps-at-top') && !this._isHorizontal)
+ || (this._dtdSettings.get_boolean('show-apps-at-top') && !this._rtl)
+ || (!this._dtdSettings.get_boolean('show-apps-at-top') && this._isHorizontal && this._rtl)) {
+ childBox.x1 = contentBox.x1 + offset_x;
+ childBox.y1 = contentBox.y1 + offset_y;
+ childBox.x2 = contentBox.x2;
+ childBox.y2 = contentBox.y2;
+ appIcons.allocate(childBox, flags);
+
+ childBox.y1 = contentBox.y1;
+ childBox.x1 = contentBox.x1;
+ childBox.x2 = contentBox.x1 + showAppsNatWidth;
+ childBox.y2 = contentBox.y1 + showAppsNatHeight;
+ showAppsButton.allocate(childBox, flags);
+ }
+ else {
+ childBox.x1 = contentBox.x1;
+ childBox.y1 = contentBox.y1;
+ childBox.x2 = contentBox.x2 - offset_x;
+ childBox.y2 = contentBox.y2 - offset_y;
+ appIcons.allocate(childBox, flags);
+
+ childBox.x2 = contentBox.x2;
+ childBox.y2 = contentBox.y2;
+ childBox.x1 = contentBox.x2 - showAppsNatWidth;
+ childBox.y1 = contentBox.y2 - showAppsNatHeight;
+ showAppsButton.allocate(childBox, flags);
+ }
+ },
+
+ _getPreferredWidth: function(actor, forHeight, alloc) {
+ // We want to request the natural height of all our children
+ // as our natural height, so we chain up to StWidget (which
+ // then calls BoxLayout), but we only request the showApps
+ // button as the minimum size
+
+ let [, natWidth] = this.actor.layout_manager.get_preferred_width(this.actor, forHeight);
+
+ let themeNode = this.actor.get_theme_node();
+ let [, showAppsButton] = this.actor.get_children();
+ let [minWidth, ] = showAppsButton.get_preferred_height(forHeight);
+
+ alloc.min_size = minWidth;
+ alloc.natural_size = natWidth;
+
+ },
+
+ _getPreferredHeight: function(actor, forWidth, alloc) {
+ // We want to request the natural height of all our children
+ // as our natural height, so we chain up to StWidget (which
+ // then calls BoxLayout), but we only request the showApps
+ // button as the minimum size
+
+ let [, natHeight] = this.actor.layout_manager.get_preferred_height(this.actor, forWidth);
+
+ let themeNode = this.actor.get_theme_node();
+ let [, showAppsButton] = this.actor.get_children();
+ let [minHeight, ] = showAppsButton.get_preferred_height(forWidth);
+
+ alloc.min_size = minHeight;
+ alloc.natural_size = natHeight;
+ }
+});
+
+const baseIconSizes = [16, 22, 24, 32, 48, 64, 96, 128];
+
+/**
+ * This class is a fork of the upstream dash class (ui.dash.js)
+ *
+ * Summary of changes:
+ * - disconnect global signals adding a destroy method;
+ * - play animations even when not in overview mode
+ * - set a maximum icon size
+ * - show running and/or favorite applications
+ * - emit a custom signal when an app icon is added
+ * - hide showApps label when the custom menu is shown.
+ * - add scrollview
+ * ensure actor is visible on keyfocus inseid the scrollview
+ * - add 128px icon size, might be usefull for hidpi display
+ * - sync minimization application target position.
+ * - support configurable "window stealing"
+ */
+const MyDash = new Lang.Class({
+ Name: 'DashToDock.MyDash',
+
+ _init: function(settings) {
+ this._maxHeight = -1;
+ this.iconSize = 64;
+ this._availableIconSizes = baseIconSizes;
+ this._shownInitially = false;
+
+ this._dtdSettings = settings;
+ this._position = Convenience.getPosition(settings);
+ this._isHorizontal = ((this._position == St.Side.TOP) ||
+ (this._position == St.Side.BOTTOM));
+ this._signalsHandler = new Convenience.GlobalSignalsHandler();
+
+ this._dragPlaceholder = null;
+ this._dragPlaceholderPos = -1;
+ this._animatingPlaceholdersCount = 0;
+ this._showLabelTimeoutId = 0;
+ this._resetHoverTimeoutId = 0;
+ this._ensureAppIconVisibilityTimeoutId = 0;
+ this._labelShowing = false;
+
+ this._containerObject = new MyDashActor(settings);
+ this._container = this._containerObject.actor;
+ this._scrollView = new St.ScrollView({
+ name: 'dashtodockDashScrollview',
+ hscrollbar_policy: Gtk.PolicyType.NEVER,
+ vscrollbar_policy: Gtk.PolicyType.NEVER,
+ enable_mouse_scrolling: false
+ });
+
+ this._scrollView.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
+
+ this._box = new St.BoxLayout({
+ vertical: !this._isHorizontal,
+ clip_to_allocation: false,
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.START
+ });
+ this._box._delegate = this;
+ this._container.add_actor(this._scrollView);
+ this._scrollView.add_actor(this._box);
+
+ this._showAppsIcon = new Dash.ShowAppsIcon();
+ AppIcons.extendShowAppsIcon(this._showAppsIcon, this._dtdSettings);
+ this._showAppsIcon.childScale = 1;
+ this._showAppsIcon.childOpacity = 255;
+ this._showAppsIcon.icon.setIconSize(this.iconSize);
+ this._hookUpLabel(this._showAppsIcon);
+
+ let appsIcon = this._showAppsIcon;
+ appsIcon.connect('menu-state-changed', Lang.bind(this, function(appsIcon, opened) {
+ this._itemMenuStateChanged(appsIcon, opened);
+ }));
+
+ this.showAppsButton = this._showAppsIcon.toggleButton;
+
+ this._container.add_actor(this._showAppsIcon);
+
+ let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
+ this.actor = new St.Bin({
+ child: this._container,
+ y_align: St.Align.START,
+ x_align: rtl ? St.Align.END : St.Align.START
+ });
+
+ if (this._isHorizontal) {
+ this.actor.connect('notify::width', Lang.bind(this, function() {
+ if (this._maxHeight != this.actor.width)
+ this._queueRedisplay();
+ this._maxHeight = this.actor.width;
+ }));
+ }
+ else {
+ this.actor.connect('notify::height', Lang.bind(this, function() {
+ if (this._maxHeight != this.actor.height)
+ this._queueRedisplay();
+ this._maxHeight = this.actor.height;
+ }));
+ }
+
+ // Update minimization animation target position on allocation of the
+ // container and on scrollview change.
+ this._box.connect('notify::allocation', Lang.bind(this, this._updateAppIcons));
+ let scrollViewAdjustment = this._isHorizontal ? this._scrollView.hscroll.adjustment : this._scrollView.vscroll.adjustment;
+ scrollViewAdjustment.connect('notify::value', Lang.bind(this, this._updateAppIcons));
+
+ this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
+
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.shell'
+ });
+
+ this._appSystem = Shell.AppSystem.get_default();
+
+ this._signalsHandler.add([
+ this._appSystem,
+ 'installed-changed',
+ Lang.bind(this, function() {
+ AppFavorites.getAppFavorites().reload();
+ this._queueRedisplay();
+ })
+ ], [
+ AppFavorites.getAppFavorites(),
+ 'changed',
+ Lang.bind(this, this._queueRedisplay)
+ ], [
+ this._appSystem,
+ 'app-state-changed',
+ Lang.bind(this, this._queueRedisplay)
+ ], [
+ Main.overview,
+ 'item-drag-begin',
+ Lang.bind(this, this._onDragBegin)
+ ], [
+ Main.overview,
+ 'item-drag-end',
+ Lang.bind(this, this._onDragEnd)
+ ], [
+ Main.overview,
+ 'item-drag-cancelled',
+ Lang.bind(this, this._onDragCancelled)
+ ]);
+
+ this._supportWindowStealingChangedId = this._dtdSettings.connect('changed::support-window-stealing', Lang.bind(this, this._redisplay));
+ this._windowStealingChangedId = this._dtdSettings.connect('changed::window-stealing', Lang.bind(this, this._redisplay));
+ },
+
+ destroy: function() {
+ this._signalsHandler.destroy();
+ },
+
+ _onScrollEvent: function(actor, event) {
+ // If scroll is not used because the icon is resized, let the scroll event propagate.
+ if (!this._dtdSettings.get_boolean('icon-size-fixed'))
+ return Clutter.EVENT_PROPAGATE;
+
+ // Event coordinates are relative to the stage but can be transformed
+ // as the actor will only receive events within his bounds.
+ let stage_x, stage_y, ok, event_x, event_y, actor_w, actor_h;
+ [stage_x, stage_y] = event.get_coords();
+ [ok, event_x, event_y] = actor.transform_stage_point(stage_x, stage_y);
+ [actor_w, actor_h] = actor.get_size();
+
+ // If the scroll event is within a 1px margin from
+ // the relevant edge of the actor, let the event propagate.
+ if ((this._position == St.Side.LEFT && event_x <= 1)
+ || (this._position == St.Side.RIGHT && event_x >= actor_w - 2)
+ || (this._position == St.Side.TOP && event_y <= 1)
+ || (this._position == St.Side.BOTTOM && event_y >= actor_h - 2))
+ return Clutter.EVENT_PROPAGATE;
+
+ // reset timeout to avid conflicts with the mousehover event
+ if (this._ensureAppIconVisibilityTimeoutId > 0) {
+ Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId);
+ this._ensureAppIconVisibilityTimeoutId = 0;
+ }
+
+ // Skip to avoid double events mouse
+ if (event.is_pointer_emulated())
+ return Clutter.EVENT_STOP;
+
+ let adjustment, delta;
+
+ if (this._isHorizontal)
+ adjustment = this._scrollView.get_hscroll_bar().get_adjustment();
+ else
+ adjustment = this._scrollView.get_vscroll_bar().get_adjustment();
+
+ let increment = adjustment.step_increment;
+
+ switch (event.get_scroll_direction()) {
+ case Clutter.ScrollDirection.UP:
+ delta = -increment;
+ break;
+ case Clutter.ScrollDirection.DOWN:
+ delta = +increment;
+ break;
+ case Clutter.ScrollDirection.SMOOTH:
+ let [dx, dy] = event.get_scroll_delta();
+ delta = dy * increment;
+ // Also consider horizontal component, for instance touchpad
+ if (this._isHorizontal)
+ delta += dx * increment;
+ break;
+ }
+
+ adjustment.set_value(adjustment.get_value() + delta);
+
+ return Clutter.EVENT_STOP;
+ },
+
+ _onDragBegin: function() {
+ this._dragCancelled = false;
+ this._dragMonitor = {
+ dragMotion: Lang.bind(this, this._onDragMotion)
+ };
+ DND.addDragMonitor(this._dragMonitor);
+
+ if (this._box.get_n_children() == 0) {
+ this._emptyDropTarget = new Dash.EmptyDropTargetItem();
+ this._box.insert_child_at_index(this._emptyDropTarget, 0);
+ this._emptyDropTarget.show(true);
+ }
+ },
+
+ _onDragCancelled: function() {
+ this._dragCancelled = true;
+ this._endDrag();
+ },
+
+ _onDragEnd: function() {
+ if (this._dragCancelled)
+ return;
+
+ this._endDrag();
+ },
+
+ _endDrag: function() {
+ this._clearDragPlaceholder();
+ this._clearEmptyDropTarget();
+ this._showAppsIcon.setDragApp(null);
+ DND.removeDragMonitor(this._dragMonitor);
+ },
+
+ _onDragMotion: function(dragEvent) {
+ let app = Dash.getAppFromSource(dragEvent.source);
+ if (app == null)
+ return DND.DragMotionResult.CONTINUE;
+
+ let showAppsHovered = this._showAppsIcon.contains(dragEvent.targetActor);
+
+ if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
+ this._clearDragPlaceholder();
+
+ if (showAppsHovered)
+ this._showAppsIcon.setDragApp(app);
+ else
+ this._showAppsIcon.setDragApp(null);
+
+ return DND.DragMotionResult.CONTINUE;
+ },
+
+ _appIdListToHash: function(apps) {
+ let ids = {};
+ for (let i = 0; i < apps.length; i++)
+ ids[apps[i].get_id()] = apps[i];
+ return ids;
+ },
+
+ _queueRedisplay: function() {
+ Main.queueDeferredWork(this._workId);
+ },
+
+ _hookUpLabel: function(item, appIcon) {
+ item.child.connect('notify::hover', Lang.bind(this, function() {
+ this._syncLabel(item, appIcon);
+ }));
+
+ let id = Main.overview.connect('hiding', Lang.bind(this, function() {
+ this._labelShowing = false;
+ item.hideLabel();
+ }));
+ item.child.connect('destroy', function() {
+ Main.overview.disconnect(id);
+ });
+
+ if (appIcon) {
+ appIcon.connect('sync-tooltip', Lang.bind(this, function() {
+ this._syncLabel(item, appIcon);
+ }));
+ }
+ },
+
+ _createAppItem: function(app) {
+ let appIcon = new AppIcons.MyAppIcon(this._dtdSettings, app,
+ { setSizeManually: true,
+ showLabel: false });
+ if (appIcon._draggable) {
+ appIcon._draggable.connect('drag-begin', Lang.bind(this, function() {
+ appIcon.actor.opacity = 50;
+ }));
+ appIcon._draggable.connect('drag-end', Lang.bind(this, function() {
+ appIcon.actor.opacity = 255;
+ }));
+ }
+
+ appIcon.connect('menu-state-changed', Lang.bind(this, function(appIcon, opened) {
+ this._itemMenuStateChanged(item, opened);
+ }));
+
+ let item = new Dash.DashItemContainer();
+
+ extendDashItemContainer(item, this._dtdSettings);
+ item.setChild(appIcon.actor);
+
+ item.setChild(appIcon.actor);
+ appIcon.actor.connect('notify::hover', Lang.bind(this, function() {
+ if (appIcon.actor.hover) {
+ this._ensureAppIconVisibilityTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function() {
+ ensureActorVisibleInScrollView(this._scrollView, appIcon.actor);
+ this._ensureAppIconVisibilityTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ }));
+ }
+ else {
+ if (this._ensureAppIconVisibilityTimeoutId > 0) {
+ Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId);
+ this._ensureAppIconVisibilityTimeoutId = 0;
+ }
+ }
+ }));
+
+ appIcon.actor.connect('clicked', Lang.bind(this, function(actor) {
+ ensureActorVisibleInScrollView(this._scrollView, actor);
+ }));
+
+ appIcon.actor.connect('key-focus-in', Lang.bind(this, function(actor) {
+ let [x_shift, y_shift] = ensureActorVisibleInScrollView(this._scrollView, actor);
+
+ // This signal is triggered also by mouse click. The popup menu is opened at the original
+ // coordinates. Thus correct for the shift which is going to be applied to the scrollview.
+ if (appIcon._menu) {
+ appIcon._menu._boxPointer.xOffset = -x_shift;
+ appIcon._menu._boxPointer.yOffset = -y_shift;
+ }
+ }));
+
+ // Override default AppIcon label_actor, now the
+ // accessible_name is set at DashItemContainer.setLabelText
+ appIcon.actor.label_actor = null;
+ item.setLabelText(app.get_name());
+
+ appIcon.icon.setIconSize(this.iconSize);
+ this._hookUpLabel(item, appIcon);
+
+ return item;
+ },
+
+ /**
+ * Return an array with the "proper" appIcons currently in the dash
+ */
+ _getAppIcons: function() {
+ // Only consider children which are "proper"
+ // icons (i.e. ignoring drag placeholders) and which are not
+ // animating out (which means they will be destroyed at the end of
+ // the animation)
+ let iconChildren = this._box.get_children().filter(function(actor) {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.icon &&
+ !actor.animatingOut;
+ });
+
+ let appIcons = iconChildren.map(function(actor) {
+ return actor.child._delegate;
+ });
+
+ return appIcons;
+ },
+
+ _updateAppIcons: function() {
+ let appIcons = this._getAppIcons();
+ appIcons.forEach(function(icon) {
+ icon.onWindowsChanged();
+ });
+ },
+
+ _itemMenuStateChanged: function(item, opened) {
+ // When the menu closes, it calls sync_hover, which means
+ // that the notify::hover handler does everything we need to.
+ if (opened) {
+ if (this._showLabelTimeoutId > 0) {
+ Mainloop.source_remove(this._showLabelTimeoutId);
+ this._showLabelTimeoutId = 0;
+ }
+
+ item.hideLabel();
+ }
+ else {
+ // I want to listen from outside when a menu is closed. I used to
+ // add a custom signal to the appIcon, since gnome 3.8 the signal
+ // calling this callback was added upstream.
+ this.emit('menu-closed');
+ }
+ },
+
+ _syncLabel: function(item, appIcon) {
+ let shouldShow = appIcon ? appIcon.shouldShowTooltip() : item.child.get_hover();
+
+ if (shouldShow) {
+ if (this._showLabelTimeoutId == 0) {
+ let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
+ this._showLabelTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, function() {
+ this._labelShowing = true;
+ item.showLabel();
+ this._showLabelTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ }));
+ GLib.Source.set_name_by_id(this._showLabelTimeoutId, '[gnome-shell] item.showLabel');
+ if (this._resetHoverTimeoutId > 0) {
+ Mainloop.source_remove(this._resetHoverTimeoutId);
+ this._resetHoverTimeoutId = 0;
+ }
+ }
+ }
+ else {
+ if (this._showLabelTimeoutId > 0)
+ Mainloop.source_remove(this._showLabelTimeoutId);
+ this._showLabelTimeoutId = 0;
+ item.hideLabel();
+ if (this._labelShowing) {
+ this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT, Lang.bind(this, function() {
+ this._labelShowing = false;
+ this._resetHoverTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ }));
+ GLib.Source.set_name_by_id(this._resetHoverTimeoutId, '[gnome-shell] this._labelShowing');
+ }
+ }
+ },
+
+ _adjustIconSize: function() {
+ // For the icon size, we only consider children which are "proper"
+ // icons (i.e. ignoring drag placeholders) and which are not
+ // animating out (which means they will be destroyed at the end of
+ // the animation)
+ let iconChildren = this._box.get_children().filter(function(actor) {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.icon &&
+ !actor.animatingOut;
+ });
+
+ iconChildren.push(this._showAppsIcon);
+
+ if (this._maxHeight == -1)
+ return;
+
+ let themeNode = this._container.get_theme_node();
+ let maxAllocation = new Clutter.ActorBox({
+ x1: 0,
+ y1: 0,
+ x2: this._isHorizontal ? this._maxHeight : 42 /* whatever */,
+ y2: this._isHorizontal ? 42 : this._maxHeight
+ });
+ let maxContent = themeNode.get_content_box(maxAllocation);
+ let availHeight;
+ if (this._isHorizontal)
+ availHeight = maxContent.x2 - maxContent.x1;
+ else
+ availHeight = maxContent.y2 - maxContent.y1;
+ let spacing = themeNode.get_length('spacing');
+
+ let firstButton = iconChildren[0].child;
+ let firstIcon = firstButton._delegate.icon;
+
+ let minHeight, natHeight, maxWidth, natWidth;
+
+ // Enforce the current icon size during the size request
+ firstIcon.setIconSize(this.iconSize);
+ [minHeight, natHeight] = firstButton.get_preferred_height(-1);
+ [minWidth, natWidth] = firstButton.get_preferred_width(-1);
+
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let iconSizes = this._availableIconSizes.map(function(s) {
+ return s * scaleFactor;
+ });
+
+ // Subtract icon padding and box spacing from the available height
+ if (this._isHorizontal)
+ availHeight -= iconChildren.length * (natWidth - this.iconSize * scaleFactor) +
+ (iconChildren.length - 1) * spacing;
+ else
+ availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) +
+ (iconChildren.length - 1) * spacing;
+
+ let availSize = availHeight / iconChildren.length;
+
+
+ let newIconSize = this._availableIconSizes[0];
+ for (let i = 0; i < iconSizes.length; i++) {
+ if (iconSizes[i] < availSize)
+ newIconSize = this._availableIconSizes[i];
+ }
+
+ if (newIconSize == this.iconSize)
+ return;
+
+ let oldIconSize = this.iconSize;
+ this.iconSize = newIconSize;
+ this.emit('icon-size-changed');
+
+ let scale = oldIconSize / newIconSize;
+ for (let i = 0; i < iconChildren.length; i++) {
+ let icon = iconChildren[i].child._delegate.icon;
+
+ // Set the new size immediately, to keep the icons' sizes
+ // in sync with this.iconSize
+ icon.setIconSize(this.iconSize);
+
+ // Don't animate the icon size change when the overview
+ // is transitioning, or when initially filling
+ // the dash
+ if (Main.overview.animationInProgress || !this._shownInitially)
+ continue;
+
+ let [targetWidth, targetHeight] = icon.icon.get_size();
+
+ // Scale the icon's texture to the previous size and
+ // tween to the new size
+ icon.icon.set_size(icon.icon.width * scale,
+ icon.icon.height * scale);
+
+ Tweener.addTween(icon.icon, {
+ width: targetWidth,
+ height: targetHeight,
+ time: DASH_ANIMATION_TIME,
+ transition: 'easeOutQuad',
+ });
+ }
+ },
+
+ _redisplay: function() {
+ let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
+
+ let running = this._appSystem.get_running();
+
+ let children = this._box.get_children().filter(function(actor) {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.app;
+ });
+ // Apps currently in the dash
+ let oldApps = children.map(function(actor) {
+ return actor.child._delegate.app;
+ });
+ // Apps supposed to be in the dash
+ let newApps = [];
+
+ if (this._dtdSettings.get_boolean('show-favorites')) {
+ for (let id in favorites)
+ newApps.push(favorites[id]);
+ }
+
+ if (this._dtdSettings.get_boolean('show-running')) {
+ for (let i = 0; i < running.length; i++) {
+ let app = running[i];
+ if (this._dtdSettings.get_boolean('show-favorites') && (app.get_id() in favorites))
+ continue;
+ newApps.push(app);
+ }
+ }
+
+ // Figure out the actual changes to the list of items; we iterate
+ // over both the list of items currently in the dash and the list
+ // of items expected there, and collect additions and removals.
+ // Moves are both an addition and a removal, where the order of
+ // the operations depends on whether we encounter the position
+ // where the item has been added first or the one from where it
+ // was removed.
+ // There is an assumption that only one item is moved at a given
+ // time; when moving several items at once, everything will still
+ // end up at the right position, but there might be additional
+ // additions/removals (e.g. it might remove all the launchers
+ // and add them back in the new order even if a smaller set of
+ // additions and removals is possible).
+ // If above assumptions turns out to be a problem, we might need
+ // to use a more sophisticated algorithm, e.g. Longest Common
+ // Subsequence as used by diff.
+
+ let addedItems = [];
+ let removedActors = [];
+
+ let newIndex = 0;
+ let oldIndex = 0;
+ while ((newIndex < newApps.length) || (oldIndex < oldApps.length)) {
+ // No change at oldIndex/newIndex
+ if (oldApps[oldIndex] == newApps[newIndex]) {
+ oldIndex++;
+ newIndex++;
+ continue;
+ }
+
+ // App removed at oldIndex
+ if (oldApps[oldIndex] && (newApps.indexOf(oldApps[oldIndex]) == -1)) {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ continue;
+ }
+
+ // App added at newIndex
+ if (newApps[newIndex] && (oldApps.indexOf(newApps[newIndex]) == -1)) {
+ let newItem = this._createAppItem(newApps[newIndex]);
+ addedItems.push({ app: newApps[newIndex],
+ item: newItem,
+ pos: newIndex });
+ newIndex++;
+ continue;
+ }
+
+ // App moved
+ let insertHere = newApps[newIndex + 1] && (newApps[newIndex + 1] == oldApps[oldIndex]);
+ let alreadyRemoved = removedActors.reduce(function(result, actor) {
+ let removedApp = actor.child._delegate.app;
+ return result || removedApp == newApps[newIndex];
+ }, false);
+
+ if (insertHere || alreadyRemoved) {
+ let newItem = this._createAppItem(newApps[newIndex]);
+ addedItems.push({
+ app: newApps[newIndex],
+ item: newItem,
+ pos: newIndex + removedActors.length
+ });
+ newIndex++;
+ }
+ else {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ }
+ }
+
+ for (let i = 0; i < addedItems.length; i++)
+ this._box.insert_child_at_index(addedItems[i].item,
+ addedItems[i].pos);
+
+ for (let i = 0; i < removedActors.length; i++) {
+ let item = removedActors[i];
+
+ // Don't animate item removal when the overview is transitioning
+ if (!Main.overview.animationInProgress)
+ item.animateOutAndDestroy();
+ else
+ item.destroy();
+ }
+
+ this._adjustIconSize();
+
+ for (let i = 0; i < addedItems.length; i++)
+ // Emit a custom signal notifying that a new item has been added
+ this.emit('item-added', addedItems[i]);
+
+ // Skip animations on first run when adding the initial set
+ // of items, to avoid all items zooming in at once
+
+ let animate = this._shownInitially &&
+ !Main.overview.animationInProgress;
+
+ if (!this._shownInitially)
+ this._shownInitially = true;
+
+ for (let i = 0; i < addedItems.length; i++)
+ addedItems[i].item.show(animate);
+
+ // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
+ // Without it, StBoxLayout may use a stale size cache
+ this._box.queue_relayout();
+
+ // This is required for icon reordering when the scrollview is used.
+ this._updateAppIcons();
+ },
+
+ setIconSize: function(max_size, doNotAnimate) {
+ let max_allowed = baseIconSizes[baseIconSizes.length-1];
+ max_size = Math.min(max_size, max_allowed);
+
+ if (this._dtdSettings.get_boolean('icon-size-fixed'))
+ this._availableIconSizes = [max_size];
+ else {
+ this._availableIconSizes = baseIconSizes.filter(function(val) {
+ return (val numChildren)
+ pos = numChildren;
+ }
+ else
+ pos = 0; // always insert at the top when dash is empty
+
+ // Take into account childredn position in rtl
+ if (this._isHorizontal && (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL))
+ pos = numChildren - pos;
+
+ if ((pos != this._dragPlaceholderPos) && (pos <= numFavorites) && (this._animatingPlaceholdersCount == 0)) {
+ this._dragPlaceholderPos = pos;
+
+ // Don't allow positioning before or after self
+ if ((favPos != -1) && (pos == favPos || pos == favPos + 1)) {
+ this._clearDragPlaceholder();
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ // If the placeholder already exists, we just move
+ // it, but if we are adding it, expand its size in
+ // an animation
+ let fadeIn;
+ if (this._dragPlaceholder) {
+ this._dragPlaceholder.destroy();
+ fadeIn = false;
+ }
+ else
+ fadeIn = true;
+
+ this._dragPlaceholder = new Dash.DragPlaceholderItem();
+ this._dragPlaceholder.child.set_width (this.iconSize);
+ this._dragPlaceholder.child.set_height (this.iconSize / 2);
+ this._box.insert_child_at_index(this._dragPlaceholder,
+ this._dragPlaceholderPos);
+ this._dragPlaceholder.show(fadeIn);
+ // Ensure the next and previous icon are visible when moving the placeholder
+ // (I assume there's room for both of them)
+ if (this._dragPlaceholderPos > 1)
+ ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[this._dragPlaceholderPos-1]);
+ if (this._dragPlaceholderPos < this._box.get_children().length-1)
+ ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[this._dragPlaceholderPos+1]);
+ }
+
+ // Remove the drag placeholder if we are not in the
+ // "favorites zone"
+ if (pos > numFavorites)
+ this._clearDragPlaceholder();
+
+ if (!this._dragPlaceholder)
+ return DND.DragMotionResult.NO_DROP;
+
+ let srcIsFavorite = (favPos != -1);
+
+ if (srcIsFavorite)
+ return DND.DragMotionResult.MOVE_DROP;
+
+ return DND.DragMotionResult.COPY_DROP;
+ },
+
+ /**
+ * Draggable target interface
+ */
+ acceptDrop: function(source, actor, x, y, time) {
+ let app = Dash.getAppFromSource(source);
+
+ // Don't allow favoriting of transient apps
+ if (app == null || app.is_window_backed())
+ return false;
+
+ if (!this._settings.is_writable('favorite-apps') || !this._dtdSettings.get_boolean('show-favorites'))
+ return false;
+
+ let id = app.get_id();
+
+ let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
+
+ let srcIsFavorite = (id in favorites);
+
+ let favPos = 0;
+ let children = this._box.get_children();
+ for (let i = 0; i < this._dragPlaceholderPos; i++) {
+ if (this._dragPlaceholder && (children[i] == this._dragPlaceholder))
+ continue;
+
+ let childId = children[i].child._delegate.app.get_id();
+ if (childId == id)
+ continue;
+ if (childId in favorites)
+ favPos++;
+ }
+
+ // No drag placeholder means we don't wan't to favorite the app
+ // and we are dragging it to its original position
+ if (!this._dragPlaceholder)
+ return true;
+
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
+ let appFavorites = AppFavorites.getAppFavorites();
+ if (srcIsFavorite)
+ appFavorites.moveFavoriteToPos(id, favPos);
+ else
+ appFavorites.addFavoriteAtPos(id, favPos);
+ return false;
+ }));
+
+ return true;
+ },
+
+ showShowAppsButton: function() {
+ this.showAppsButton.visible = true
+ this.showAppsButton.set_width(-1)
+ this.showAppsButton.set_height(-1)
+ },
+
+ hideShowAppsButton: function() {
+ this.showAppsButton.hide()
+ this.showAppsButton.set_width(0)
+ this.showAppsButton.set_height(0)
+ }
+});
+
+Signals.addSignalMethods(MyDash.prototype);
+
+/**
+ * This is a copy of the same function in utils.js, but also adjust horizontal scrolling
+ * and perform few further cheks on the current value to avoid changing the values when
+ * it would be clamp to the current one in any case.
+ * Return the amount of shift applied
+ */
+function ensureActorVisibleInScrollView(scrollView, actor) {
+ let adjust_v = true;
+ let adjust_h = true;
+
+ let vadjustment = scrollView.vscroll.adjustment;
+ let hadjustment = scrollView.hscroll.adjustment;
+ let [vvalue, vlower, vupper, vstepIncrement, vpageIncrement, vpageSize] = vadjustment.get_values();
+ let [hvalue, hlower, hupper, hstepIncrement, hpageIncrement, hpageSize] = hadjustment.get_values();
+
+ let [hvalue0, vvalue0] = [hvalue, vvalue];
+
+ let voffset = 0;
+ let hoffset = 0;
+ let fade = scrollView.get_effect('fade');
+ if (fade) {
+ voffset = fade.vfade_offset;
+ hoffset = fade.hfade_offset;
+ }
+
+ let box = actor.get_allocation_box();
+ let y1 = box.y1, y2 = box.y2, x1 = box.x1, x2 = box.x2;
+
+ let parent = actor.get_parent();
+ while (parent != scrollView) {
+ if (!parent)
+ throw new Error('Actor not in scroll view');
+
+ let box = parent.get_allocation_box();
+ y1 += box.y1;
+ y2 += box.y1;
+ x1 += box.x1;
+ x2 += box.x1;
+ parent = parent.get_parent();
+ }
+
+ if (y1 < vvalue + voffset)
+ vvalue = Math.max(0, y1 - voffset);
+ else if (vvalue < vupper - vpageSize && y2 > vvalue + vpageSize - voffset)
+ vvalue = Math.min(vupper -vpageSize, y2 + voffset - vpageSize);
+
+ if (x1 < hvalue + hoffset)
+ hvalue = Math.max(0, x1 - hoffset);
+ else if (hvalue < hupper - hpageSize && x2 > hvalue + hpageSize - hoffset)
+ hvalue = Math.min(hupper - hpageSize, x2 + hoffset - hpageSize);
+
+ if (vvalue !== vvalue0) {
+ Tweener.addTween(vadjustment, { value: vvalue,
+ time: Util.SCROLL_TIME,
+ transition: 'easeOutQuad'
+ });
+ }
+
+ if (hvalue !== hvalue0) {
+ Tweener.addTween(hadjustment,
+ { value: hvalue,
+ time: Util.SCROLL_TIME,
+ transition: 'easeOutQuad' });
+ }
+
+ return [hvalue- hvalue0, vvalue - vvalue0];
+}
diff --git a/dockedDash.js b/docking.js
similarity index 59%
rename from dockedDash.js
rename to docking.js
index 95e11e8af..a437bf1cd 100644
--- a/dockedDash.js
+++ b/docking.js
@@ -1,34 +1,34 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Signals = imports.signals;
+
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
-const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
-const Mainloop = imports.mainloop;
-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 Tweener = imports.ui.tweener;
-const Signals = imports.signals;
const ViewSelector = imports.ui.viewSelector;
-const WorkspaceSwitcherPopup= imports.ui.workspaceSwitcherPopup;
+const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
const Layout = imports.ui.layout;
-const LayoutManager = imports.ui.main.layoutManager;
+
+const Params = imports.misc.params;
const Me = imports.misc.extensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
const Intellihide = Me.imports.intellihide;
-const MyDash = Me.imports.myDash;
+const Theming = Me.imports.theming;
+const MyDash = Me.imports.dash;
-const DOCK_DWELL_CHECK_INTERVAL = 100; //TODO
+const DOCK_DWELL_CHECK_INTERVAL = 100; // TODO
const State = {
HIDDEN: 0,
@@ -37,19 +37,7 @@ const State = {
HIDING: 3
};
-/* Return the actual position reverseing left and right in rtl */
-function getPosition(settings) {
- let position = settings.get_enum('dock-position');
- if(Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
- if (position == St.Side.LEFT)
- position = St.Side.RIGHT;
- else if (position == St.Side.RIGHT)
- position = St.Side.LEFT;
- }
- return position;
-}
-
-/*
+/**
* A simple St.Widget with one child whose allocation takes into account the
* slide out of its child via the _slidex parameter ([0:1]).
*
@@ -65,13 +53,11 @@ function getPosition(settings) {
* It can't be an extended object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973.
* thus use the Shell.GenericContainer pattern.
*/
-
const DashSlideContainer = new Lang.Class({
- Name: 'DashSlideContainer',
+ Name: 'DashToDock.DashSlideContainer',
_init: function(params) {
-
- /* Default local params */
+ // Default local params
let localDefaults = {
side: St.Side.LEFT,
initialSlideValue: 1
@@ -79,9 +65,9 @@ const DashSlideContainer = new Lang.Class({
let localParams = Params.parse(params, localDefaults, true);
- if (params){
- /* Remove local params before passing the params to the parent
- constructor to avoid errors. */
+ if (params) {
+ // Remove local params before passing the params to the parent
+ // constructor to avoid errors.
let prop;
for (prop in localDefaults) {
if ((prop in params))
@@ -104,9 +90,7 @@ const DashSlideContainer = new Lang.Class({
this._slideoutSize = 0; // minimum size when slided out
},
-
_allocate: function(actor, box, flags) {
-
if (this._child == null)
return;
@@ -123,62 +107,64 @@ const DashSlideContainer = new Lang.Class({
let slideoutSize = this._slideoutSize;
if (this._side == St.Side.LEFT) {
- childBox.x1 = (this._slidex -1)*(childWidth - slideoutSize);
+ 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) {
+ }
+ 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) {
+ }
+ 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);
+ childBox.y1 = (this._slidex -1) * (childHeight - slideoutSize);
+ childBox.y2 = slideoutSize + this._slidex * (childHeight - slideoutSize);
}
this._child.allocate(childBox, flags);
this._child.set_clip(-childBox.x1, -childBox.y1,
- -childBox.x1+availWidth,-childBox.y1 + availHeight);
+ -childBox.x1+availWidth, -childBox.y1 + availHeight);
},
- /* Just the child width but taking into account the slided out part */
+ /**
+ * Just the child width but taking into account the slided out part
+ */
_getPreferredWidth: function(actor, forHeight, alloc) {
- let [minWidth, natWidth ] = this._child.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;
+ let [minWidth, natWidth] = this._child.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;
}
alloc.min_size = minWidth;
alloc.natural_size = natWidth;
},
- /* Just the child height but taking into account the slided out part */
+ /**
+ * Just the child height but taking into account the slided out part
+ */
_getPreferredHeight: function(actor, forWidth, alloc) {
let [minHeight, natHeight] = this._child.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;
+ 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;
}
alloc.min_size = minHeight;
alloc.natural_size = natHeight;
},
- /* I was expecting it to be a virtual function... stil I don't understand
- how things work.
- */
+ /**
+ * I was expecting it to be a virtual function... stil I don't understand
+ * how things work.
+ */
add_child: function(actor) {
-
- /* I'm supposed to have only on child */
- if(this._child !== null) {
+ // I'm supposed to have only on child
+ if (this._child !== null)
this.actor.remove_child(actor);
- }
this._child = actor;
this.actor.add_child(actor);
@@ -192,23 +178,20 @@ const DashSlideContainer = new Lang.Class({
get slidex() {
return this._slidex;
}
-
});
-const dockedDash = new Lang.Class({
- Name: 'dockedDash',
-
- _init: function(settings) {
+const DockedDash = new Lang.Class({
+ Name: 'DashToDock.DockedDash',
- this._rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
+ _init: function(settings) {
+ this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
// Load settings
- this._settings = settings;
+ this._dtdSettings = settings;
this._bindSettingsChanges();
- this._position = getPosition(settings);
- this._isHorizontal = ( this._position == St.Side.TOP ||
- this._position == St.Side.BOTTOM );
+ this._position = Convenience.getPosition(settings);
+ 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;
@@ -220,7 +203,7 @@ const dockedDash = new Lang.Class({
this._fixedIsEnabled = null;
// Create intellihide object to monitor windows overlapping
- this._intellihide = new Intellihide.intellihide(this._settings);
+ this._intellihide = new Intellihide.Intellihide(this._dtdSettings);
// initialize dock state
this._dockState = State.HIDDEN;
@@ -250,7 +233,7 @@ const dockedDash = new Lang.Class({
this._dockDwellTimeoutId = 0
// Create a new dash object
- this.dash = new MyDash.myDash(this._settings);
+ this.dash = new MyDash.MyDash(this._dtdSettings);
// set stored icon size to the new dash
Main.overview.dashIconSize = this.dash.iconSize;
@@ -258,7 +241,7 @@ const dockedDash = new Lang.Class({
// connect app icon into the view selector
this.dash.showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled));
- if (!this._settings.get_boolean('show-show-apps-button'))
+ if (!this._dtdSettings.get_boolean('show-show-apps-button'))
this.dash.hideShowAppsButton();
// Create the main actor and the containers for sliding in and out and
@@ -266,112 +249,110 @@ const dockedDash = new Lang.Class({
let positionStyleClass = ['top', 'right', 'bottom', 'left'];
// This is the centering actor
- this.actor = new St.Bin({ name: 'dashtodockContainer',reactive: false,
- style_class:positionStyleClass[this._position],
+ this.actor = new St.Bin({
+ name: 'dashtodockContainer',
+ reactive: false,
+ style_class: positionStyleClass[this._position],
x_align: this._isHorizontal?St.Align.MIDDLE:St.Align.START,
- y_align: this._isHorizontal?St.Align.START:St.Align.MIDDLE});
+ y_align: this._isHorizontal?St.Align.START:St.Align.MIDDLE
+ });
this.actor._delegate = this;
// This is the sliding actor whose allocation is to be tracked for input regions
- this._slider = new DashSlideContainer({side: this._position, initialSlideValue: 0});
+ this._slider = new DashSlideContainer({
+ side: this._position,
+ initialSlideValue: 0
+ });
// 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", Lang.bind(this, this._hoverChanged));
+ this._box = new St.BoxLayout({
+ name: 'dashtodockBox',
+ reactive: true,
+ track_hover: true
+ });
+ this._box.connect('notify::hover', Lang.bind(this, this._hoverChanged));
// Create and apply height constraint to the dash. It's controlled by this.actor height
- this.constrainSize = new Clutter.BindConstraint({ source: this.actor,
- coordinate: this._isHorizontal?Clutter.BindCoordinate.WIDTH:Clutter.BindCoordinate.HEIGHT });
+ this.constrainSize = new Clutter.BindConstraint({
+ source: this.actor,
+ coordinate: this._isHorizontal?Clutter.BindCoordinate.WIDTH:Clutter.BindCoordinate.HEIGHT
+ });
this.dash.actor.add_constraint(this.constrainSize);
// Connect global signals
this._signalsHandler = new Convenience.GlobalSignalsHandler();
- this._signalsHandler.add(
- [
- Main.overview,
- 'item-drag-begin',
- Lang.bind(this, this._onDragStart)
- ],
- [
- Main.overview,
- 'item-drag-end',
- Lang.bind(this, this._onDragEnd)
- ],
- [
- Main.overview,
- 'item-drag-cancelled',
- Lang.bind(this, this._onDragEnd)
- ],
+ this._signalsHandler.add([
+ Main.overview,
+ 'item-drag-begin',
+ Lang.bind(this, this._onDragStart)
+ ], [
+ Main.overview,
+ 'item-drag-end',
+ Lang.bind(this, this._onDragEnd)
+ ], [
+ Main.overview,
+ 'item-drag-cancelled',
+ Lang.bind(this, this._onDragEnd)
+ ], [
// update when monitor changes, for instance in multimonitor when monitor are attached
- [
- global.screen,
- 'monitors-changed',
- Lang.bind(this, this._resetPosition )
- ],
+ global.screen,
+ 'monitors-changed',
+ Lang.bind(this, this._resetPosition )
+ ], [
// update when workarea changes, for instance if other extensions modify the struts
//(like moving th panel at the bottom)
- [
- global.screen,
- 'workareas-changed',
- Lang.bind(this, this._resetPosition)
- ],
- [
- Main.overview,
- 'showing',
- Lang.bind(this, this._onOverviewShowing)
- ],
- [
- Main.overview,
- 'hiding',
- Lang.bind(this, this._onOverviewHiding)
- ],
+ global.screen,
+ 'workareas-changed',
+ Lang.bind(this, this._resetPosition)
+ ], [
+ Main.overview,
+ 'showing',
+ Lang.bind(this, this._onOverviewShowing)
+ ], [
+ Main.overview,
+ 'hiding',
+ Lang.bind(this, this._onOverviewHiding)
+ ], [
// Hide on appview
- [
- Main.overview.viewSelector,
- 'page-changed',
- Lang.bind(this, this._pageChanged)
- ],
- [
- Main.overview.viewSelector,
- 'page-empty',
- Lang.bind(this, this._onPageEmpty)
- ],
+ Main.overview.viewSelector,
+ 'page-changed',
+ Lang.bind(this, this._pageChanged)
+ ], [
+ Main.overview.viewSelector,
+ 'page-empty',
+ Lang.bind(this, this._onPageEmpty)
+ ], [
// Ensure the ShowAppsButton status is kept in sync
- [
- Main.overview.viewSelector._showAppsButton,
- 'notify::checked',
- Lang.bind(this, this._syncShowAppsButtonToggled)
- ],
+ Main.overview.viewSelector._showAppsButton,
+ 'notify::checked',
+ Lang.bind(this, this._syncShowAppsButtonToggled)
+ ], [
// Monitor windows overlapping
- [
- this._intellihide,
- 'status-changed',
- Lang.bind(this, this._updateDashVisibility)
- ],
+ this._intellihide,
+ 'status-changed',
+ Lang.bind(this, this._updateDashVisibility)
+ ], [
// Keep dragged icon consistent in size with this dash
- [
- this.dash,
- 'icon-size-changed',
- Lang.bind(this, function() {
- Main.overview.dashIconSize = this.dash.iconSize;
- })
- ],
+ this.dash,
+ 'icon-size-changed',
+ Lang.bind(this, function() {
+ Main.overview.dashIconSize = this.dash.iconSize;
+ })
+ ], [
// This duplicate the similar signal which is in owerview.js.
// Being connected and thus executed later this effectively
// overwrite any attempt to use the size of the default dash
//which given the customization is usually much smaller.
// I can't easily disconnect the original signal
- [
- Main.overview._controls.dash,
- 'icon-size-changed',
- Lang.bind(this, function() {
- Main.overview.dashIconSize = this.dash.iconSize;
- })
- ]
- );
+ Main.overview._controls.dash,
+ 'icon-size-changed',
+ Lang.bind(this, function() {
+ Main.overview.dashIconSize = this.dash.iconSize;
+ })
+ ]);
this._injectionsHandler = new Convenience.InjectionsHandler();
- this._themeManager = new themeManager(this._settings, this.actor, this.dash);
+ this._themeManager = new Theming.ThemeManager(this._dtdSettings, this.actor, this.dash);
// 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
@@ -380,14 +361,16 @@ const dockedDash = new Lang.Class({
Lang.bind(Main.layoutManager, Main.layoutManager._queueUpdateRegions));
this.dash._container.connect('allocation-changed', Lang.bind(this, this._updateStaticBox));
- this._slider.actor.connect(this._isHorizontal?'notify::x':'notify::y', Lang.bind(this, this._updateStaticBox));
+ this._slider.actor.connect(this._isHorizontal ? 'notify::x' : 'notify::y', Lang.bind(this, this._updateStaticBox));
// sync hover after a popupmenu is closed
- this.dash.connect('menu-closed', Lang.bind(this, function(){this._box.sync_hover();}));
+ this.dash.connect('menu-closed', Lang.bind(this, function() {
+ this._box.sync_hover();
+ }));
// Restore dash accessibility
Main.ctrlAltTabManager.addGroup(
- this.dash.actor, _("Dash"),'user-bookmarks-symbolic',
+ this.dash.actor, _("Dash"), 'user-bookmarks-symbolic',
{focusCallback: Lang.bind(this, this._onAccessibilityFocus)});
// Load optional features
@@ -396,7 +379,7 @@ const dockedDash = new Lang.Class({
// Delay operations that require the shell to be fully loaded and with
// user theme applied.
- this._paintId = this.actor.connect("paint", Lang.bind(this, this._initialize));
+ this._paintId = this.actor.connect('paint', Lang.bind(this, this._initialize));
// Hide usual Dash
Main.overview._controls.dash.actor.hide();
@@ -415,14 +398,14 @@ const dockedDash = new Lang.Class({
this._dashSpacer = new OverviewControls.DashSpacer();
this._dashSpacer.setDashActor(this._box);
- if (this._position == St.Side.LEFT)
- Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl?-1:0); // insert on first
+ if (this._position == St.Side.LEFT)
+ Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl ? -1 : 0); // insert on first
else if (this._position == St.Side.RIGHT)
- Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl?0:-1); // insert on last
- else if (this._position == St.Side.TOP)
+ Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl ? 0 : -1); // insert on last
+ else if (this._position == St.Side.TOP)
Main.overview._overview.insert_child_at_index(this._dashSpacer, 0);
- else if (this._position == St.Side.BOTTOM)
- Main.overview._overview.insert_child_at_index(this._dashSpacer, -1);
+ else if (this._position == St.Side.BOTTOM)
+ Main.overview._overview.insert_child_at_index(this._dashSpacer, -1);
// Add dash container actor and the container to the Chrome.
this.actor.set_child(this._slider.actor);
@@ -438,40 +421,38 @@ const dockedDash = new Lang.Class({
// Keep the dash below the modalDialogGroup
Main.layoutManager.uiGroup.set_child_below_sibling(this.actor,Main.layoutManager.modalDialogGroup);
- if ( this._settings.get_boolean('dock-fixed') )
- Main.layoutManager._trackActor(this._box, {affectsStruts: true, trackFullscreen: true});
+ if (this._dtdSettings.get_boolean('dock-fixed'))
+ Main.layoutManager._trackActor(this._box, {affectsStruts: true, trackFullscreen: true});
// pretend this._slider is isToplevel child so that fullscreen is actually tracked
let index = Main.layoutManager._findActor(this._slider.actor);
- Main.layoutManager._trackedActors[index].isToplevel = true ;
+ Main.layoutManager._trackedActors[index].isToplevel = true;
// Set initial position
this._resetPosition();
-
},
- _initialize: function(){
-
- if(this._paintId>0){
+ _initialize: function() {
+ if (this._paintId > 0) {
this.actor.disconnect(this._paintId);
this._paintId=0;
}
- this.dash.setIconSize(this._settings.get_int('dash-max-icon-size'), true);
+ this.dash.setIconSize(this._dtdSettings.get_int('dash-max-icon-size'), true);
// 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.viewSelector._activePage == null)
- Main.overview.viewSelector._activePage = Main.overview.viewSelector._workspacesPage;
+ if (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){
+ if (Main.overview.visibleTarget) {
this._onOverviewShowing();
this._pageChanged();
}
@@ -482,11 +463,9 @@ const dockedDash = new Lang.Class({
// setup dwelling system if pressure barriers are not available
this._setupDockDwellIfNeeded();
-
},
- destroy: function(){
-
+ destroy: function() {
// Disconnect global signals
this._signalsHandler.destroy();
// The dash and intellihide have global signals as well internally
@@ -507,7 +486,7 @@ const dockedDash = new Lang.Class({
this._removeBarrier();
// Remove pointer watcher
- if(this._dockWatch){
+ if (this._dockWatch) {
PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch);
this._dockWatch = null;
}
@@ -529,45 +508,43 @@ const dockedDash = new Lang.Class({
},
_bindSettingsChanges: function() {
-
- this._settings.connect('changed::scroll-switch-workspace', Lang.bind(this, function(){
- this._optionalScrollWorkspaceSwitch(this._settings.get_boolean('scroll-switch-workspace'));
+ this._dtdSettings.connect('changed::scroll-switch-workspace', Lang.bind(this, function() {
+ this._optionalScrollWorkspaceSwitch(this._dtdSettings.get_boolean('scroll-switch-workspace'));
}));
- this._settings.connect('changed::dash-max-icon-size', Lang.bind(this, function(){
- this.dash.setIconSize(this._settings.get_int('dash-max-icon-size'));
+ this._dtdSettings.connect('changed::dash-max-icon-size', Lang.bind(this, function() {
+ this.dash.setIconSize(this._dtdSettings.get_int('dash-max-icon-size'));
}));
- this._settings.connect('changed::icon-size-fixed', Lang.bind(this, function(){
- this.dash.setIconSize(this._settings.get_int('dash-max-icon-size'));
+ this._dtdSettings.connect('changed::icon-size-fixed', Lang.bind(this, function() {
+ this.dash.setIconSize(this._dtdSettings.get_int('dash-max-icon-size'));
}));
- this._settings.connect('changed::show-favorites', Lang.bind(this, function(){
+ this._dtdSettings.connect('changed::show-favorites', Lang.bind(this, function() {
this.dash.resetAppIcons();
}));
- this._settings.connect('changed::show-running', Lang.bind(this, function(){
+ this._dtdSettings.connect('changed::show-running', Lang.bind(this, function() {
this.dash.resetAppIcons();
}));
- this._settings.connect('changed::show-apps-at-top', Lang.bind(this, function(){
+ this._dtdSettings.connect('changed::show-apps-at-top', Lang.bind(this, function() {
this.dash.resetAppIcons();
}));
- this._settings.connect('changed::show-show-apps-button', Lang.bind(this, function(){
- if (this._settings.get_boolean('show-show-apps-button'))
+ this._dtdSettings.connect('changed::show-show-apps-button', Lang.bind(this, function() {
+ if (this._dtdSettings.get_boolean('show-show-apps-button'))
this.dash.showShowAppsButton();
else
this.dash.hideShowAppsButton();
}));
- this._settings.connect('changed::dock-fixed', Lang.bind(this, function(){
+ this._dtdSettings.connect('changed::dock-fixed', Lang.bind(this, function() {
- if(this._settings.get_boolean('dock-fixed')) {
+ if (this._dtdSettings.get_boolean('dock-fixed'))
Main.layoutManager._trackActor(this._box, {affectsStruts: true, trackFullscreen: true});
- } else {
+ else
Main.layoutManager._untrackActor(this._box);
- }
this._resetPosition();
@@ -577,46 +554,48 @@ const dockedDash = new Lang.Class({
this._updateVisibilityMode();
}));
- this._settings.connect('changed::intellihide', Lang.bind(this, this._updateVisibilityMode));
+ this._dtdSettings.connect('changed::intellihide', Lang.bind(this, this._updateVisibilityMode));
- this._settings.connect('changed::intellihide-mode', Lang.bind(this, function(){
+ this._dtdSettings.connect('changed::intellihide-mode', Lang.bind(this, function() {
this._intellihide.forceUpdate();
}));
- this._settings.connect('changed::autohide', Lang.bind(this, function(){
+ this._dtdSettings.connect('changed::autohide', Lang.bind(this, function() {
this._updateVisibilityMode();
this._updateBarrier();
}));
- this._settings.connect('changed::extend-height', Lang.bind(this,this._resetPosition));
- this._settings.connect('changed::preferred-monitor', Lang.bind(this,this._resetPosition));
- this._settings.connect('changed::height-fraction', Lang.bind(this,this._resetPosition));
- this._settings.connect('changed::require-pressure-to-show', Lang.bind(this,function(){
+ this._dtdSettings.connect('changed::extend-height', Lang.bind(this,this._resetPosition));
+ this._dtdSettings.connect('changed::preferred-monitor', Lang.bind(this,this._resetPosition));
+ this._dtdSettings.connect('changed::height-fraction', Lang.bind(this,this._resetPosition));
+ this._dtdSettings.connect('changed::require-pressure-to-show', Lang.bind(this,function() {
// Remove pointer watcher
- if(this._dockWatch){
+ if (this._dockWatch) {
PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch);
this._dockWatch = null;
}
this._setupDockDwellIfNeeded();
this._updateBarrier();
}));
- this._settings.connect('changed::pressure-threshold', Lang.bind(this,function() {
+ this._dtdSettings.connect('changed::pressure-threshold', Lang.bind(this,function() {
this._updatePressureBarrier();
this._updateBarrier();
}));
},
- // This is call when visibility settings change
+ /**
+ * This is call when visibility settings change
+ */
_updateVisibilityMode: function() {
-
- if (this._settings.get_boolean('dock-fixed')) {
+ if (this._dtdSettings.get_boolean('dock-fixed')) {
this._fixedIsEnabled = true;
this._autohideIsEnabled = false;
this._intellihideIsEnabled = false;
- } else {
+ }
+ else {
this._fixedIsEnabled = false;
- this._autohideIsEnabled = this._settings.get_boolean('autohide')
- this._intellihideIsEnabled = this._settings.get_boolean('intellihide')
+ this._autohideIsEnabled = this._dtdSettings.get_boolean('autohide')
+ this._intellihideIsEnabled = this._dtdSettings.get_boolean('intellihide')
}
if (this._intellihideIsEnabled)
@@ -627,7 +606,8 @@ const dockedDash = new Lang.Class({
this._updateDashVisibility();
},
- /* Show/hide dash based on, in order of priority:
+ /**
+ * Show/hide dash based on, in order of priority:
* overview visibility
* fixed mode
* intellihide
@@ -635,39 +615,38 @@ const dockedDash = new Lang.Class({
* overview visibility
*/
_updateDashVisibility: function() {
-
if (Main.overview.visibleTarget)
return;
- if ( this._fixedIsEnabled ) {
+ if (this._fixedIsEnabled) {
this._removeAnimations();
- this._animateIn(this._settings.get_double('animation-time'), 0);
- } else if (this._intellihideIsEnabled) {
- if ( this._intellihide.getOverlapStatus() ) {
+ this._animateIn(this._dtdSettings.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(this._settings.get_double('animation-time'), 0);
- }
- } else {
+ if (!this._box.hover || !this._autohideIsEnabled)
+ this._animateOut(this._dtdSettings.get_double('animation-time'), 0);
+ }
+ else {
this._ignoreHover = true;
this._removeAnimations();
- this._animateIn(this._settings.get_double('animation-time'), 0);
+ this._animateIn(this._dtdSettings.get_double('animation-time'), 0);
}
- } else {
+ }
+ else {
if (this._autohideIsEnabled) {
this._ignoreHover = false;
global.sync_pointer();
- if( this._box.hover ) {
- this._animateIn(this._settings.get_double('animation-time'), 0);
- } else {
- this._animateOut(this._settings.get_double('animation-time'), 0);
- }
-
- } else {
- this._animateOut(this._settings.get_double('animation-time'), 0);
+ if (this._box.hover)
+ this._animateIn(this._dtdSettings.get_double('animation-time'), 0);
+ else
+ this._animateOut(this._dtdSettings.get_double('animation-time'), 0);
}
+ else
+ this._animateOut(this._dtdSettings.get_double('animation-time'), 0);
}
},
@@ -675,7 +654,7 @@ const dockedDash = new Lang.Class({
this._ignoreHover = true;
this._intellihide.disable();
this._removeAnimations();
- this._animateIn(this._settings.get_double('animation-time'), 0);
+ this._animateIn(this._dtdSettings.get_double('animation-time'), 0);
},
_onOverviewHiding: function() {
@@ -685,100 +664,89 @@ const dockedDash = new Lang.Class({
},
_hoverChanged: function() {
-
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 ) {
+ if (this._autohideIsEnabled) {
+ if (this._box.hover)
this._show();
- } else {
+ else
this._hide();
- }
}
}
},
_show: function() {
-
- if ( this._dockState == State.HIDDEN || this._dockState == State.HIDING ) {
-
- if(this._dockState == State.HIDING){
+ if ((this._dockState == State.HIDDEN) || (this._dockState == State.HIDING)) {
+ if (this._dockState == State.HIDING)
// suppress all potential queued hiding animations - i.e. added to Tweener but not started,
// always give priority to show
this._removeAnimations();
- }
- this.emit("showing");
- this._animateIn(this._settings.get_double('animation-time'), 0);
+ this.emit('showing');
+ this._animateIn(this._dtdSettings.get_double('animation-time'), 0);
}
},
_hide: function() {
-
// If no hiding animation is running or queued
- if ( this._dockState == State.SHOWN || this._dockState == State.SHOWING ) {
-
+ if ((this._dockState == State.SHOWN) || (this._dockState == State.SHOWING)) {
let delay;
- if (this._dockState == State.SHOWING) {
+ if (this._dockState == State.SHOWING)
//if a show already started, let it finish; queue hide without removing the show.
- // to obtain this I increase the delay to avoid the overlap and interference
+ // to obtain this I increase the delay to avoid the overlap and interference
// between the animations
- delay = this._settings.get_double('hide-delay') + this._settings.get_double('animation-time');
- } else {
- delay = this._settings.get_double('hide-delay');
- }
-
- this.emit("hiding");
- this._animateOut(this._settings.get_double('animation-time'), delay);
+ delay = this._dtdSettings.get_double('hide-delay') + this._dtdSettings.get_double('animation-time');
+ else
+ delay = this._dtdSettings.get_double('hide-delay');
+ this.emit('hiding');
+ this._animateOut(this._dtdSettings.get_double('animation-time'), delay);
}
},
_animateIn: function(time, delay) {
-
this._dockState = State.SHOWING;
- Tweener.addTween(this._slider,{
+ Tweener.addTween(this._slider, {
slidex: 1,
time: time,
delay: delay,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
- 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) {
- Mainloop.source_remove(this._removeBarrierTimeoutId);
- }
- this._removeBarrierTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, this._removeBarrier));
- })
+ 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)
+ Mainloop.source_remove(this._removeBarrierTimeoutId);
+ this._removeBarrierTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, this._removeBarrier));
+ })
});
},
- _animateOut: function(time, delay){
-
+ _animateOut: function(time, delay) {
this._dockState = State.HIDING;
- Tweener.addTween(this._slider,{
+ Tweener.addTween(this._slider, {
slidex: 0,
time: time,
delay: delay ,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
- this._dockState = State.HIDDEN;
- this._updateBarrier();
+ this._dockState = State.HIDDEN;
+ this._updateBarrier();
})
});
},
- // Dwelling system based on the GNOME Shell 3.14 messageTray code.
+ /**
+ * Dwelling system based on the GNOME Shell 3.14 messageTray code.
+ */
_setupDockDwellIfNeeded: function() {
// If we don't have extended barrier features, then we need
// to support the old tray dwelling mechanism.
- if (!global.display.supports_extended_barriers() || !this._settings.get_boolean('require-pressure-to-show')) {
+ if (!global.display.supports_extended_barriers() || !this._dtdSettings.get_boolean('require-pressure-to-show')) {
let pointerWatcher = PointerWatcher.getPointerWatcher();
this._dockWatch = pointerWatcher.addWatch(DOCK_DWELL_CHECK_INTERVAL, Lang.bind(this, this._checkDockDwell));
this._dockDwelling = false;
@@ -795,15 +763,14 @@ const dockedDash = new Lang.Class({
// Check for the correct screen edge
// Position is approximated to the lower integer
- if(this._position==St.Side.LEFT){
- shouldDwell = shouldDwell && x == this._monitor.x;
- } else if(this._position==St.Side.RIGHT) {
- shouldDwell = shouldDwell && x == this._monitor.x + this._monitor.width - 1;
- } else if(this._position==St.Side.TOP) {
- shouldDwell = shouldDwell && y == this._monitor.y;
- } else if (this._position==St.Side.BOTTOM) {
- shouldDwell = shouldDwell && y == this._monitor.y + this._monitor.height - 1;
- }
+ if (this._position == St.Side.LEFT)
+ shouldDwell = shouldDwell && (x == this._monitor.x);
+ else if (this._position == St.Side.RIGHT)
+ shouldDwell = shouldDwell && (x == this._monitor.x + this._monitor.width - 1);
+ else if (this._position == St.Side.TOP)
+ shouldDwell = shouldDwell && (y == this._monitor.y);
+ else if (this._position == St.Side.BOTTOM)
+ shouldDwell = shouldDwell && (y == this._monitor.y + this._monitor.height - 1);
if (shouldDwell) {
// We only set up dwell timeout when the user is not hovering over the dock
@@ -811,17 +778,18 @@ const dockedDash = new Lang.Class({
// 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) {
+ 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 = Mainloop.timeout_add(this._settings.get_double('show-delay')*1000,
+ this._dockDwellTimeoutId = Mainloop.timeout_add(this._dtdSettings.get_double('show-delay') * 1000,
Lang.bind(this, this._dockDwellTimeout));
GLib.Source.set_name_by_id(this._dockDwellTimeoutId, '[dash-to-dock] this._dockDwellTimeout');
}
this._dockDwelling = true;
- } else {
+ }
+ else {
this._cancelDockDwell();
this._dockDwelling = false;
}
@@ -837,7 +805,7 @@ const dockedDash = new Lang.Class({
_dockDwellTimeout: function() {
this._dockDwellTimeoutId = 0;
- if (!this._settings.get_boolean('autohide-in-fullscreen') && this._monitor.inFullscreen)
+ if (!this._dtdSettings.get_boolean('autohide-in-fullscreen') && this._monitor.inFullscreen)
return GLib.SOURCE_REMOVE;
// We don't want to open the tray when a modal dialog
@@ -860,7 +828,7 @@ const dockedDash = new Lang.Class({
_updatePressureBarrier: function() {
this._canUsePressure = global.display.supports_extended_barriers();
- let pressureThreshold = this._settings.get_double('pressure-threshold');
+ let pressureThreshold = this._dtdSettings.get_double('pressure-threshold');
// Remove existing pressure barrier
if (this._pressureBarrier) {
@@ -868,47 +836,48 @@ const dockedDash = new Lang.Class({
this._pressureBarrier = null;
}
- if (this._barrier){
+ 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, this._settings.get_double('show-delay')*1000,
+ this._pressureBarrier = new Layout.PressureBarrier(pressureThreshold, this._dtdSettings.get_double('show-delay')*1000,
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW);
- this._pressureBarrier.connect('trigger', Lang.bind(this, function(barrier){
- if (!this._settings.get_boolean('autohide-in-fullscreen') && this._monitor.inFullscreen)
+ this._pressureBarrier.connect('trigger', Lang.bind(this, function(barrier) {
+ if (!this._dtdSettings.get_boolean('autohide-in-fullscreen') && this._monitor.inFullscreen)
return;
this._onPressureSensed();
}));
}
},
- // handler for mouse pressure sensed
+ /**
+ * handler for mouse pressure sensed
+ */
_onPressureSensed: function() {
-
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 = Mainloop.timeout_add(250,
- Lang.bind(this, function() {
- triggerTimeoutId = 0;
- this._hoverChanged();
- return GLib.SOURCE_REMOVE;
- }));
+ let triggerTimeoutId = Mainloop.timeout_add(250, Lang.bind(this, function() {
+ triggerTimeoutId = 0;
+ this._hoverChanged();
+ return GLib.SOURCE_REMOVE;
+ }));
this._show();
},
- // Remove pressure barrier
+ /**
+ * Remove pressure barrier
+ */
_removeBarrier: function() {
if (this._barrier) {
- if (this._pressureBarrier) {
+ if (this._pressureBarrier)
this._pressureBarrier.removeBarrier(this._barrier);
- }
this._barrier.destroy();
this._barrier = null;
}
@@ -916,7 +885,9 @@ const dockedDash = new Lang.Class({
return false;
},
- // Update pressure barrier size
+ /**
+ * Update pressure barrier size
+ */
_updateBarrier: function() {
// Remove existing barrier
this._removeBarrier();
@@ -930,28 +901,31 @@ const dockedDash = new Lang.Class({
// Create new barrier
// Note: dash in fixed position doesn't use pressure barrier
- if (this._canUsePressure && this._autohideIsEnabled && this._settings.get_boolean('require-pressure-to-show')) {
+ if (this._canUsePressure && this._autohideIsEnabled && this._dtdSettings.get_boolean('require-pressure-to-show')) {
let x1, x2, y1, y2, direction;
- if(this._position==St.Side.LEFT){
+ if (this._position == St.Side.LEFT) {
x1 = this.staticBox.x1;
x2 = this.staticBox.x1;
y1 = this.staticBox.y1;
y2 = this.staticBox.y2;
direction = Meta.BarrierDirection.POSITIVE_X;
- } else if(this._position==St.Side.RIGHT) {
+ }
+ else if (this._position == St.Side.RIGHT) {
x1 = this.staticBox.x2;
x2 = this.staticBox.x2;
y1 = this.staticBox.y1;
y2 = this.staticBox.y2;
direction = Meta.BarrierDirection.NEGATIVE_X;
- } else if(this._position==St.Side.TOP) {
+ }
+ else if (this._position == St.Side.TOP) {
x1 = this.staticBox.x1;
x2 = this.staticBox.x2;
y1 = this.staticBox.y1;
y2 = this.staticBox.y1;
direction = Meta.BarrierDirection.POSITIVE_Y;
- } else if (this._position==St.Side.BOTTOM) {
+ }
+ else if (this._position == St.Side.BOTTOM) {
x1 = this.staticBox.x1;
x2 = this.staticBox.x2;
y1 = this.staticBox.y2;
@@ -959,31 +933,32 @@ const dockedDash = new Lang.Class({
direction = Meta.BarrierDirection.NEGATIVE_Y;
}
- this._barrier = new Meta.Barrier({display: global.display,
- x1: x1, x2: x2,
- y1: y1, y2: y2,
- directions: direction});
- if (this._pressureBarrier) {
+ this._barrier = new Meta.Barrier({
+ display: global.display,
+ x1: x1,
+ x2: x2,
+ y1: y1,
+ y2: y2,
+ directions: direction
+ });
+ if (this._pressureBarrier)
this._pressureBarrier.addBarrier(this._barrier);
- }
}
-
},
_isPrimaryMonitor: function() {
- return (this._monitor.x == Main.layoutManager.primaryMonitor.x &&
- this._monitor.y == Main.layoutManager.primaryMonitor.y);
+ return ((this._monitor.x == Main.layoutManager.primaryMonitor.x) &&
+ (this._monitor.y == Main.layoutManager.primaryMonitor.y));
},
_resetPosition: function() {
-
// Ensure variables linked to settings are updated.
this._updateVisibilityMode();
- let monitorIndex = this._settings.get_int('preferred-monitor');
- let extendHeight = this._settings.get_boolean('extend-height');
+ let monitorIndex = this._dtdSettings.get_int('preferred-monitor');
+ let extendHeight = this._dtdSettings.get_boolean('extend-height');
- if (monitorIndex >0 && monitorIndex< Main.layoutManager.monitors.length)
+ if ((monitorIndex > 0) && (monitorIndex < Main.layoutManager.monitors.length))
this._monitor = Main.layoutManager.monitors[monitorIndex];
else {
monitorIndex = Main.layoutManager.primaryIndex
@@ -997,68 +972,69 @@ const dockedDash = new Lang.Class({
// Reserve space for the dash on the overview
// if the dock is on the primary monitor
- if (this._isPrimaryMonitor()){
+ if (this._isPrimaryMonitor())
this._dashSpacer.show();
- } else {
+ else
// No space is required in the overview of the dash
this._dashSpacer.hide();
- }
- let fraction = this._settings.get_double('height-fraction');
+ let fraction = this._dtdSettings.get_double('height-fraction');
- if(extendHeight)
+ if (extendHeight)
fraction = 1;
- else if(fraction<0 || fraction >1)
+ else if ((fraction < 0) || (fraction > 1))
fraction = 0.95;
let anchor_point;
- if(this._isHorizontal){
-
+ if (this._isHorizontal) {
this.actor.width = Math.round( fraction * workArea.width);
let pos_y;
- if( this._position == St.Side.BOTTOM) {
+ if (this._position == St.Side.BOTTOM) {
pos_y = this._monitor.y + this._monitor.height;
anchor_point = Clutter.Gravity.SOUTH_WEST;
- } else {
+ }
+ else {
pos_y = this._monitor.y;
anchor_point = Clutter.Gravity.NORTH_WEST;
}
this.actor.move_anchor_point_from_gravity(anchor_point);
- this.actor.x = workArea.x + Math.round( (1-fraction)/2 * workArea.width);
+ this.actor.x = workArea.x + Math.round((1 - fraction) / 2 * workArea.width);
this.actor.y = pos_y;
- if(extendHeight){
+ if (extendHeight) {
this.dash._container.set_width(this.actor.width);
this.actor.add_style_class_name('extended');
- } else {
+ }
+ else {
this.dash._container.set_width(-1);
this.actor.remove_style_class_name('extended');
}
-
- } else {
-
- this.actor.height = Math.round( fraction * workArea.height);
+ }
+ else {
+ this.actor.height = Math.round(fraction * workArea.height);
let pos_x;
- if( this._position == St.Side.RIGHT) {
+ if (this._position == St.Side.RIGHT) {
pos_x = this._monitor.x + this._monitor.width;
anchor_point = Clutter.Gravity.NORTH_EAST;
- } else {
+ }
+ else {
pos_x = this._monitor.x;
anchor_point = Clutter.Gravity.NORTH_WEST;
}
this.actor.move_anchor_point_from_gravity(anchor_point);
this.actor.x = pos_x;
- this.actor.y = workArea.y + Math.round( (1-fraction)/2 * workArea.height);
+ this.actor.y = workArea.y + Math.round((1 - fraction) / 2 * workArea.height);
- if(extendHeight){
+ if (extendHeight) {
this.dash._container.set_height(this.actor.height);
this.actor.add_style_class_name('extended');
- } else {
+ }
+ else {
this.dash._container.set_height(-1);
this.actor.remove_style_class_name('extended');
}
@@ -1071,36 +1047,35 @@ const dockedDash = new Lang.Class({
this._updateStaticBox();
},
- _adjustLegacyTray: function(){
-
+ _adjustLegacyTray: function() {
let use_work_area = true;
- if ( this._fixedIsEnabled && !this._settings.get_boolean('extend-height')
- && this._isPrimaryMonitor()
- && (this._position == St.Side.BOTTOM ||this._position == St.Side.LEFT )
- )
- {
+ if (this._fixedIsEnabled && !this._dtdSettings.get_boolean('extend-height')
+ && this._isPrimaryMonitor()
+ && ((this._position == St.Side.BOTTOM) || (this._position == St.Side.LEFT)))
use_work_area = false;
- }
Main.legacyTray.actor.clear_constraints();
- let constraint = new Layout.MonitorConstraint({ primary: true,
- work_area: use_work_area});
+ let constraint = new Layout.MonitorConstraint({
+ primary: true,
+ work_area: use_work_area
+ });
Main.legacyTray.actor.add_constraint(constraint);
},
_resetLegacyTray: function() {
Main.legacyTray.actor.clear_constraints();
- let constraint = new Layout.MonitorConstraint({ primary: true,
- work_area: true });
+ let constraint = new Layout.MonitorConstraint({
+ primary: true,
+ work_area: true
+ });
Main.legacyTray.actor.add_constraint(constraint);
},
_updateStaticBox: function() {
-
this.staticBox.init_rect(
- this.actor.x + this._slider.actor.x - (this._position==St.Side.RIGHT?this._box.width:0),
- this.actor.y + this._slider.actor.y - (this._position==St.Side.BOTTOM?this._box.height:0),
+ this.actor.x + this._slider.actor.x - (this._position == St.Side.RIGHT ? this._box.width : 0),
+ this.actor.y + this._slider.actor.y - (this._position == St.Side.BOTTOM ? this._box.height : 0),
this._box.width,
this._box.height
);
@@ -1108,15 +1083,17 @@ const dockedDash = new Lang.Class({
this._intellihide.updateTargetBox(this.staticBox);
},
- // Adjust Panel corners
+ /**
+ * Adjust Panel corners
+ */
_adjustPanelCorners: function() {
- let extendHeight = this._settings.get_boolean('extend-height');
+ let extendHeight = this._dtdSettings.get_boolean('extend-height');
if (!this._isHorizontal && this._isPrimaryMonitor() && extendHeight && this._fixedIsEnabled) {
Main.panel._rightCorner.actor.hide();
Main.panel._leftCorner.actor.hide();
- } else {
- this._revertPanelCorners();
}
+ else
+ this._revertPanelCorners();
},
_revertPanelCorners: function() {
@@ -1128,37 +1105,35 @@ const dockedDash = new Lang.Class({
Tweener.removeTweens(this._slider);
},
- _onDragStart: function(){
+ _onDragStart: function() {
// The dash need to be above the top_window_group, otherwise it doesn't
// accept dnd of app icons when not in overiew mode.
Main.layoutManager.uiGroup.set_child_above_sibling(this.actor, global.top_window_group);
this._oldignoreHover = this._ignoreHover;
this._ignoreHover = true;
- this._animateIn(this._settings.get_double('animation-time'), 0);
+ this._animateIn(this._dtdSettings.get_double('animation-time'), 0);
},
- _onDragEnd: function(){
+ _onDragEnd: function() {
// Restore drag default dash stack order
Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup);
if (this._oldignoreHover !== null)
this._ignoreHover = this._oldignoreHover;
this._oldignoreHover = null;
this._box.sync_hover();
- if(Main.overview._shown)
+ if (Main.overview._shown)
this._pageChanged();
},
_pageChanged: function() {
-
let activePage = Main.overview.viewSelector.getActivePage();
let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
activePage == ViewSelector.ViewPage.APPS);
- if(dashVisible){
- this._animateIn(this._settings.get_double('animation-time'), 0);
- } else {
- this._animateOut(this._settings.get_double('animation-time'), 0);
- }
+ if (dashVisible)
+ this._animateIn(this._dtdSettings.get_double('animation-time'), 0);
+ else
+ this._animateOut(this._dtdSettings.get_double('animation-time'), 0);
},
_onPageEmpty: function() {
@@ -1182,38 +1157,37 @@ const dockedDash = new Lang.Class({
this._dashSpacer.visible = (this._isHorizontal || activePage == ViewSelector.ViewPage.WINDOWS);
},
- // Show dock and give key focus to it
- _onAccessibilityFocus: function(){
+ /**
+ * Show dock and give key focus to it
+ */
+ _onAccessibilityFocus: function() {
this._box.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
- this._animateIn(this._settings.get_double('animation-time'), 0);
+ this._animateIn(this._dtdSettings.get_double('animation-time'), 0);
},
_onShowAppsButtonToggled: function() {
-
// 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 animate = this._dtdSettings.get_boolean('animate-show-apps');
let selector = Main.overview.viewSelector;
- if(selector._showAppsButton.checked !== this.dash.showAppsButton.checked){
-
+ if (selector._showAppsButton.checked !== this.dash.showAppsButton.checked) {
// find visible view
let visibleView;
Main.overview.viewSelector.appDisplay._views.every(function(v, index) {
if (v.view.actor.visible) {
visibleView = index;
return false;
- } else {
- return true;
}
+ else
+ return true;
});
- if(this.dash.showAppsButton.checked){
-
+ if (this.dash.showAppsButton.checked) {
// force spring animation triggering.By default the animation only
// runs if we are already inside the overview.
if (!Main.overview._shown) {
@@ -1232,27 +1206,26 @@ const dockedDash = new Lang.Class({
Main.overview.viewSelector._activePage.show();
grid.actor.opacity = 0;
-
// 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.
- let overviewShownId = Main.overview.connect('shown', Lang.bind(this, function(){
+ let overviewShownId = Main.overview.connect('shown', Lang.bind(this, function() {
Main.overview.disconnect(overviewShownId);
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
grid.actor.opacity = 255;
grid.animateSpring(IconGrid.AnimationDirection.IN, this.dash.showAppsButton);
}));
- }));
+ }));
}
}
// Finally show the overview
selector._showAppsButton.checked = true;
Main.overview.show();
-
- } else {
+ }
+ else {
if (this.forcedOverview) {
// force exiting overview if needed
@@ -1262,81 +1235,81 @@ const dockedDash = new Lang.Class({
// onComplete to avoid ugly flashing of original icons.
let view = Main.overview.viewSelector.appDisplay._views[visibleView].view;
let grid = view._grid;
- view.animate(IconGrid.AnimationDirection.OUT, Lang.bind(this, function(){
+ view.animate(IconGrid.AnimationDirection.OUT, Lang.bind(this, function() {
Main.overview.viewSelector._appsPage.hide();
Main.overview.hide();
selector._showAppsButton.checked = false;
this.forcedOverview = false;
}));
- } else {
+ }
+ else {
Main.overview.hide();
this.forcedOverview = false;
}
- } else {
+ }
+ 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( this.dash.showAppsButton.checked==false)
+ if (this.dash.showAppsButton.checked == false)
this.forcedOverview = false;
},
- // Keep ShowAppsButton status in sync with the overview status
+ /**
+ * Keep ShowAppsButton status in sync with the overview status
+ */
_syncShowAppsButtonToggled: function() {
let status = Main.overview.viewSelector._showAppsButton.checked;
- if(this.dash.showAppsButton.checked !== status)
+ if (this.dash.showAppsButton.checked !== status)
this.dash.showAppsButton.checked = status;
},
// Optional features enable/disable
- // Switch workspace by scrolling over the dock
+ /**
+ * Switch workspace by scrolling over the dock
+ */
_optionalScrollWorkspaceSwitch: function() {
-
let label = 'optionalScrollWorkspaceSwitch';
- this._settings.connect('changed::scroll-switch-workspace',Lang.bind(this, function(){
- if(this._settings.get_boolean('scroll-switch-workspace'))
+ this._dtdSettings.connect('changed::scroll-switch-workspace', Lang.bind(this, function() {
+ if (this._dtdSettings.get_boolean('scroll-switch-workspace'))
Lang.bind(this, enable)();
else
Lang.bind(this, disable)();
}));
- if(this._settings.get_boolean('scroll-switch-workspace'))
+ if (this._dtdSettings.get_boolean('scroll-switch-workspace'))
Lang.bind(this, enable)();
- function enable(){
-
+ function enable() {
this._signalsHandler.removeWithLabel(label);
- this._signalsHandler.addWithLabel(label,
- [
- this._box,
- 'scroll-event',
- Lang.bind(this, onScrollEvent)
- ]
- );
+ this._signalsHandler.addWithLabel(label, [
+ this._box,
+ 'scroll-event',
+ Lang.bind(this, onScrollEvent)
+ ]);
- this._optionalScrollWorkspaceSwitchDeadTimeId=0;
+ this._optionalScrollWorkspaceSwitchDeadTimeId = 0;
}
function disable() {
this._signalsHandler.removeWithLabel(label);
- if(this._optionalScrollWorkspaceSwitchDeadTimeId>0){
+ if (this._optionalScrollWorkspaceSwitchDeadTimeId > 0) {
Mainloop.source_remove(this._optionalScrollWorkspaceSwitchDeadTimeId);
- this._optionalScrollWorkspaceSwitchDeadTimeId=0;
+ 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;
@@ -1344,7 +1317,7 @@ const dockedDash = new Lang.Class({
let activeWs = global.screen.get_active_workspace();
let direction = null;
- switch ( event.get_scroll_direction() ) {
+ switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:
direction = Meta.MotionDirection.UP;
break;
@@ -1353,32 +1326,25 @@ const dockedDash = new Lang.Class({
break;
case Clutter.ScrollDirection.SMOOTH:
let [dx, dy] = event.get_scroll_delta();
- if(dy < 0){
+ if (dy < 0)
direction = Meta.MotionDirection.UP;
- } else if(dy > 0) {
+ else if (dy > 0)
direction = Meta.MotionDirection.DOWN;
- }
break;
}
- if(direction !==null ){
-
+ 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>0)
+ if (this._optionalScrollWorkspaceSwitchDeadTimeId > 0)
return false;
- else {
- this._optionalScrollWorkspaceSwitchDeadTimeId =
- Mainloop.timeout_add(250,
- Lang.bind(this, function() {
- this._optionalScrollWorkspaceSwitchDeadTimeId=0;
- }
- ));
- }
-
+ else
+ this._optionalScrollWorkspaceSwitchDeadTimeId = Mainloop.timeout_add(250, Lang.bind(this, function() {
+ this._optionalScrollWorkspaceSwitchDeadTimeId = 0;
+ }));
let ws;
@@ -1395,225 +1361,16 @@ const dockedDash = new Lang.Class({
});
// Do not show wokspaceSwithcer in overview
- if(!Main.overview.visible)
- Main.wm._workspaceSwitcherPopup.display(direction, ws.index());
+ if (!Main.overview.visible)
+ Main.wm._workspaceSwitcherPopup.display(direction, ws.index());
Main.wm.actionMoveWorkspace(ws);
return true;
-
- } else {
- return false;
}
+ else
+ return false;
}
-
}
});
-Signals.addSignalMethods(dockedDash.prototype);
-
-/*
- * Manage theme customization and custom theme support
-*/
-const themeManager = new Lang.Class({
- Name: 'ThemeManager',
-
- _init: function(settings, actor, dash) {
-
- this._settings = settings;
- this._bindSettingsChanges();
- this._actor = actor;
- this._dash = dash;
-
- // initialize colors with generic values
- this._defaultBackground = {red: 0, green:0, blue: 0, alpha:0};
- this._defaultBackgroundColor = {red: 0, green:0, blue: 0, alpha:0};
- this._customizedBackground = {red: 0, green:0, blue: 0, alpha:0};
-
- this._signalsHandler = new Convenience.GlobalSignalsHandler();
- this._signalsHandler.add(
- // When theme changes re-obtain default background color
- [
- St.ThemeContext.get_for_stage (global.stage),
- 'changed',
- Lang.bind(this, this.updateCustomTheme)
- ],
- // update :overview pseudoclass
- [
- Main.overview,
- 'showing',
- Lang.bind(this, this._onOverviewShowing)
- ],
- [
- Main.overview,
- 'hiding',
- Lang.bind(this, this._onOverviewHiding)
- ]
- );
-
- this._updateCustomStyleClasses();
-
- },
-
- destroy: function() {
- this._signalsHandler.destroy();
- },
- _onOverviewShowing: function() {
- this._actor.add_style_pseudo_class('overview');
- },
-
- _onOverviewHiding: function() {
- this._actor.remove_style_pseudo_class('overview');
- },
-
- _updateBackgroundOpacity: function() {
-
- let newAlpha = this._settings.get_double('background-opacity');
-
- this._defaultBackground = 'rgba('+
- this._defaultBackgroundColor.red + ','+
- this._defaultBackgroundColor.green + ','+
- this._defaultBackgroundColor.blue + ','+
- Math.round(this._defaultBackgroundColor.alpha/2.55)/100 + ')';
-
- this._customizedBackground = 'rgba('+
- this._defaultBackgroundColor.red + ','+
- this._defaultBackgroundColor.green + ','+
- this._defaultBackgroundColor.blue + ','+
- newAlpha + ')';
- },
-
- _getBackgroundColor: function() {
-
- // Prevent shell crash if the actor is not on the stage.
- // It happens enabling/disabling repeatedly the extension
- if(!this._dash._container.get_stage())
- return;
-
- // Remove custom style
- let oldStyle = this._dash._container.get_style();
- this._dash._container.set_style(null);
-
- let themeNode = this._dash._container.get_theme_node();
- this._dash._container.set_style(oldStyle);
-
- this._defaultBackgroundColor = themeNode.get_background_color();
- },
-
- _updateCustomStyleClasses: function(){
-
- if (this._settings.get_boolean('apply-custom-theme'))
- this._actor.add_style_class_name('dashtodock');
- else {
- this._actor.remove_style_class_name('dashtodock');
- }
-
- if (this._settings.get_boolean('custom-theme-shrink'))
- this._actor.add_style_class_name('shrink');
- else {
- this._actor.remove_style_class_name('shrink');
- }
-
- },
-
- updateCustomTheme: function() {
- this._updateCustomStyleClasses();
- this._getBackgroundColor();
- this._updateBackgroundOpacity();
- this._adjustTheme();
- this._dash._redisplay();
- },
-
- /* Reimported back and adapted from atomdock */
- _adjustTheme: function() {
- // Prevent shell crash if the actor is not on the stage.
- // It happens enabling/disabling repeatedly the extension
- if (!this._dash._container.get_stage()) {
- return;
- }
-
- // Remove prior style edits
- this._dash._container.set_style(null);
-
- /* If built-in theme is enabled do nothing else */
- if( this._settings.get_boolean('apply-custom-theme') )
- return;
-
- let newStyle = '';
- let position = getPosition(this._settings);
-
- if ( ! this._settings.get_boolean('custom-theme-shrink') ) {
-
- // obtain theme border settings
- let themeNode = this._dash._container.get_theme_node();
- let borderColor = themeNode.get_border_color(St.Side.TOP);
- let borderWidth = themeNode.get_border_width(St.Side.TOP);
- let borderRadius = themeNode.get_border_radius(St.Corner.TOPRIGHT);
-
- /* We're copying border and corner styles to left border and top-left
- * corner, also removing bottom border and bottom-right corner styles
- */
- let borderInner = '';
- let borderRadiusValue = '';
- let borderMissingStyle = '';
-
- if (this._rtl && position != St.Side.RIGHT) {
- borderMissingStyle = 'border-right: ' + borderWidth + 'px solid ' +
- borderColor.to_string() + ';';
- } else if (!this._rtl && position != St.Side.LEFT){
- borderMissingStyle = 'border-left: ' + borderWidth + 'px solid ' +
- borderColor.to_string() + ';';
- }
-
- switch(position) {
- case St.Side.LEFT:
- borderInner = 'border-left';
- borderRadiusValue = '0 ' + borderRadius + 'px ' + borderRadius + 'px 0;';
- break;
- case St.Side.RIGHT:
- borderInner = 'border-right';
- borderRadiusValue = borderRadius + 'px 0 0 ' + borderRadius + 'px;';
- break;
- case St.Side.TOP:
- borderInner = 'border-top';
- borderRadiusValue = '0 0 ' + borderRadius + 'px ' + borderRadius + 'px;';
- break;
- case St.Side.BOTTOM:
- borderInner = 'border-bottom';
- borderRadiusValue = borderRadius + 'px ' + borderRadius + 'px 0 0;';
- break;
- }
-
- newStyle = borderInner + ': none;' +
- 'border-radius: ' + borderRadiusValue +
- borderMissingStyle ;
-
- /* I do call set_style possibly twice so that only the background gets the transition.
- * The transition-property css rules seems to be unsupported
- */
- this._dash._container.set_style(newStyle);
- }
-
- /* Customize background */
- if ( this._settings.get_boolean('opaque-background') ) {
- newStyle = newStyle + 'background-color:'+ this._customizedBackground + '; ' +
- 'transition-delay: 0s; transition-duration: 0.250s;';
- this._dash._container.set_style(newStyle);
- }
- },
-
- _bindSettingsChanges: function() {
-
- let keys = ['opaque-background',
- 'background-opacity',
- 'apply-custom-theme',
- 'custom-theme-shrink',
- 'extend-height'];
-
- keys.forEach(function(key){
- this._settings.connect('changed::'+key,
- Lang.bind(this, this.updateCustomTheme)
- );
- }, this );
-
- }
-});
+Signals.addSignalMethods(DockedDash.prototype);
diff --git a/extension.js b/extension.js
index 4bd19fa4c..d14981d86 100644
--- a/extension.js
+++ b/extension.js
@@ -1,28 +1,24 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+const Main = imports.ui.main;
+
const Me = imports.misc.extensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
-const DockedDash = Me.imports.dockedDash;
-
-const Main = imports.ui.main;
+const Docking = Me.imports.docking;
let settings;
let dock;
-
let oldDash;
function init() {
-
}
function enable() {
-
settings = Convenience.getSettings('org.gnome.shell.extensions.dash-to-dock');
- dock = new DockedDash.dockedDash(settings);
+ dock = new Docking.DockedDash(settings);
- /* Pretend I'm the dash: meant to make appgrd swarm animation come from the
- * right position of the appShowButton.
- */
+ // Pretend I'm the dash: meant to make appgrd swarm animation come from the
+ // right position of the appShowButton.
oldDash = Main.overview._dash;
Main.overview._dash = dock.dash;
bindSettingsChanges();
@@ -42,10 +38,9 @@ function disable() {
function bindSettingsChanges() {
// This settings change require a full reload.
- /* It's easier to just reload the extension when the dock position changes
- * rather than working out all changes to the differen containers.
- */
- settings.connect('changed::dock-position', function(){
+ // It's easier to just reload the extension when the dock position changes
+ // rather than working out all changes to the differen containers.
+ settings.connect('changed::dock-position', function() {
disable();
enable();
});
diff --git a/intellihide.js b/intellihide.js
index 069b385c4..331b91e27 100644
--- a/intellihide.js
+++ b/intellihide.js
@@ -1,13 +1,14 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
+const Signals = imports.signals;
+
+const GLib = imports.gi.GLib;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
-const Signals = imports.signals;
const Me = imports.misc.extensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
@@ -30,35 +31,32 @@ const IntellihideMode = {
// List of windows type taken into account. Order is important (keep the original
// enum order).
const handledWindowTypes = [
- Meta.WindowType.NORMAL,
- Meta.WindowType.DOCK,
- Meta.WindowType.DIALOG,
- Meta.WindowType.MODAL_DIALOG,
- Meta.WindowType.TOOLBAR,
- Meta.WindowType.MENU,
- Meta.WindowType.UTILITY,
- Meta.WindowType.SPLASHSCREEN
+ Meta.WindowType.NORMAL,
+ Meta.WindowType.DOCK,
+ Meta.WindowType.DIALOG,
+ Meta.WindowType.MODAL_DIALOG,
+ Meta.WindowType.TOOLBAR,
+ Meta.WindowType.MENU,
+ Meta.WindowType.UTILITY,
+ Meta.WindowType.SPLASHSCREEN
];
-/*
+/**
* A rough and ugly implementation of the intellihide behaviour.
* Intallihide object: emit 'status-changed' signal when the overlap of windows
* with the provided targetBoxClutter.ActorBox changes;
- *
-*/
-
-const intellihide = new Lang.Class({
- Name: 'Intellihide',
+ */
+const Intellihide = new Lang.Class({
+ Name: 'DashToDock.Intellihide',
_init: function(settings) {
-
// Load settings
- this._settings = settings;
+ this._dtdSettings = settings;
this._signalsHandler = new Convenience.GlobalSignalsHandler();
this._tracker = Shell.WindowTracker.get_default();
this._focusApp = null; // The application whose window is focused.
- this._topApp = null; //The application whose window is on top on the monitor with the dock.
+ this._topApp = null; // The application whose window is on top on the monitor with the dock.
this._isEnabled = false;
this.status = OverlapStatus.UNDEFINED;
@@ -68,35 +66,29 @@ const intellihide = new Lang.Class({
this._checkOverlapTimeoutId = 0;
// Connect global signals
- this._signalsHandler.add (
+ this._signalsHandler.add([
// Add signals on windows created from now on
- [
- global.display,
- 'window-created',
- Lang.bind(this, this._windowCreated)
- ],
+ global.display,
+ 'window-created',
+ Lang.bind(this, this._windowCreated)
+ ], [
// triggered for instance when the window list order changes,
// included when the workspace is switched
- [
- global.screen,
- 'restacked',
- Lang.bind(this, this._checkOverlap)
- ],
- // when windows are alwasy on top, the focus window can change
+ global.screen,
+ 'restacked',
+ Lang.bind(this, this._checkOverlap)
+ ], [
+ // when windows are alwasy on top, the focus window can change
// without the windows being restacked. Thus monitor window focus change.
- [
- this._tracker,
- 'notify::focus-app',
- Lang.bind(this, this._checkOverlap)
- ],
+ this._tracker,
+ 'notify::focus-app',
+ Lang.bind(this, this._checkOverlap)
+ ], [
// update wne monitor changes, for instance in multimonitor when monitor are attached
- [
- global.screen,
- 'monitors-changed',
- Lang.bind(this, this._checkOverlap )
- ]
- );
-
+ global.screen,
+ 'monitors-changed',
+ Lang.bind(this, this._checkOverlap )
+ ]);
},
destroy: function() {
@@ -108,21 +100,21 @@ const intellihide = new Lang.Class({
},
enable: function() {
- this._isEnabled = true;
- this._status = OverlapStatus.UNDEFINED;
- global.get_window_actors().forEach(function(win){
- this._addWindowSignals(win.get_meta_window())
- }, this);
- this._doCheckOverlap();
+ this._isEnabled = true;
+ this._status = OverlapStatus.UNDEFINED;
+ global.get_window_actors().forEach(function(win) {
+ this._addWindowSignals(win.get_meta_window());
+ }, this);
+ this._doCheckOverlap();
},
disable: function() {
this._isEnabled = false;
- global.get_window_actors().forEach(function(win){
- this._removeWindowSignals(win.get_meta_window())
- }, this);
+ global.get_window_actors().forEach(function(win) {
+ this._removeWindowSignals(win.get_meta_window());
+ }, this);
- if(this._checkOverlapTimeoutId>0) {
+ if (this._checkOverlapTimeoutId > 0) {
Mainloop.source_remove(this._checkOverlapTimeoutId);
this._checkOverlapTimeoutId = 0;
}
@@ -133,27 +125,23 @@ const intellihide = new Lang.Class({
},
_addWindowSignals: function(meta_win) {
- if(!meta_win || !this._handledWindow(meta_win))
- return;
+ if (!meta_win || !this._handledWindow(meta_win))
+ return;
- meta_win.dtd_onPositionChanged = meta_win.connect(
- 'position-changed', Lang.bind(this, this._checkOverlap, meta_win)
- );
+ meta_win.dtd_onPositionChanged = meta_win.connect('position-changed', Lang.bind(this, this._checkOverlap, meta_win));
- meta_win.dtd_onSizeChanged = meta_win.connect(
- 'size-changed', Lang.bind(this, this._checkOverlap, meta_win)
- );
+ meta_win.dtd_onSizeChanged = meta_win.connect('size-changed', Lang.bind(this, this._checkOverlap, meta_win));
},
_removeWindowSignals: function(meta_win) {
- if( meta_win && meta_win.dtd_onSizeChanged ) {
- meta_win.disconnect(meta_win.dtd_onSizeChanged);
- delete meta_win.dtd_onSizeChanged;
+ if (meta_win && meta_win.dtd_onSizeChanged) {
+ meta_win.disconnect(meta_win.dtd_onSizeChanged);
+ delete meta_win.dtd_onSizeChanged;
}
- if( meta_win && meta_win.dtd_onPositionChanged ) {
- meta_win.disconnect(meta_win.dtd_onPositionChanged);
- delete meta_win.dtd_onPositionChanged;
+ if (meta_win && meta_win.dtd_onPositionChanged) {
+ meta_win.disconnect(meta_win.dtd_onPositionChanged);
+ delete meta_win.dtd_onPositionChanged;
}
},
@@ -167,52 +155,43 @@ const intellihide = new Lang.Class({
this._doCheckOverlap();
},
- getOverlapStatus: function(){
- if(this._status == OverlapStatus.TRUE)
- return true;
- else
- return false;
+ getOverlapStatus: function() {
+ return (this._status == OverlapStatus.TRUE);
},
_checkOverlap: function() {
-
- if( !this._isEnabled || this._targetBox == null)
+ if (!this._isEnabled || (this._targetBox == null))
return;
/* Limit the number of calls to the doCheckOverlap function */
- if(this._checkOverlapTimeoutId){
+ if (this._checkOverlapTimeoutId) {
this._checkOverlapTimeoutContinue = true;
return
}
this._doCheckOverlap();
- this._checkOverlapTimeoutId =
- Mainloop.timeout_add(INTELLIHIDE_CHECK_INTERVAL,
- Lang.bind(this, function() {
- this._doCheckOverlap();
- if (this._checkOverlapTimeoutContinue){
- this._checkOverlapTimeoutContinue = false;
- return GLib.SOURCE_CONTINUE;
- } else {
- this._checkOverlapTimeoutId = 0;
- return GLib.SOURCE_REMOVE;
- }
- }
- ));
+ this._checkOverlapTimeoutId = Mainloop.timeout_add(INTELLIHIDE_CHECK_INTERVAL, Lang.bind(this, function() {
+ this._doCheckOverlap();
+ if (this._checkOverlapTimeoutContinue) {
+ this._checkOverlapTimeoutContinue = false;
+ return GLib.SOURCE_CONTINUE;
+ } else {
+ this._checkOverlapTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ }
+ }));
},
_doCheckOverlap: function() {
- if( !this._isEnabled || this._targetBox == null)
+ if (!this._isEnabled || (this._targetBox == null))
return;
let overlaps = OverlapStatus.FALSE;
let windows = global.get_window_actors();
- if (windows.length>0){
-
-
+ if (windows.length > 0) {
/*
* Get the top window on the monitor where the dock is placed.
* The idea is that we dont want to overlap with the windows of the topmost application,
@@ -220,41 +199,38 @@ const intellihide = new Lang.Class({
* select a window in the secondary monitor.
*/
- let monitorIndex = this._settings.get_int('preferred-monitor');
+ let monitorIndex = this._dtdSettings.get_int('preferred-monitor');
- if (monitorIndex < 0 || monitorIndex > Main.layoutManager.monitors.length)
+ if ((monitorIndex < 0) || (monitorIndex > Main.layoutManager.monitors.length))
monitorIndex = Main.layoutManager.primaryIndex;
let topWindow = null;
- for (let i = windows.length-1; i>=0; i--) {
+ for (let i = windows.length - 1; i >= 0; i--) {
let meta_win = windows[i].get_meta_window();
- if (this._handledWindow(meta_win)
- && meta_win.get_monitor() == monitorIndex) {
+ if (this._handledWindow(meta_win) && (meta_win.get_monitor() == monitorIndex)) {
topWindow = meta_win;
break;
}
}
if (topWindow !== null) {
-
this._topApp = this._tracker.get_window_app(topWindow);
// If there isn't a focused app, use that of the window on top
this._focusApp = this._tracker.focus_app || this._topApp
windows = windows.filter(this._intellihideFilterInteresting, this);
- for(let i=0; i< windows.length; i++){
-
+ for (let i = 0; i < windows.length; i++) {
let win = windows[i].get_meta_window();
- if(win){
+ if (win) {
let rect = win.get_frame_rect();
- let test = ( rect.x < this._targetBox.x2) &&
- ( rect.x +rect.width > this._targetBox.x1 ) &&
- ( rect.y < this._targetBox.y2 ) &&
- ( rect.y +rect.height > this._targetBox.y1 );
+ let test = (rect.x < this._targetBox.x2) &&
+ (rect.x + rect.width > this._targetBox.x1) &&
+ (rect.y < this._targetBox.y2) &&
+ (rect.y + rect.height > this._targetBox.y1);
- if(test){
+ if (test) {
overlaps = OverlapStatus.TRUE;
break;
}
@@ -263,7 +239,7 @@ const intellihide = new Lang.Class({
}
}
- if ( this._status !== overlaps ) {
+ if (this._status !== overlaps) {
this._status = overlaps;
this.emit('status-changed', this._status);
}
@@ -273,26 +249,24 @@ const intellihide = new Lang.Class({
// Filter interesting windows to be considered for intellihide.
// Consider all windows visible on the current workspace.
// Optionally skip windows of other applications
- _intellihideFilterInteresting: function(wa){
-
+ _intellihideFilterInteresting: function(wa) {
let meta_win = wa.get_meta_window();
- if (!meta_win || !this._handledWindow(meta_win)) {
+ if (!meta_win || !this._handledWindow(meta_win))
return false;
- }
let currentWorkspace = global.screen.get_active_workspace_index();
let wksp = meta_win.get_workspace();
let wksp_index = wksp.index();
// Depending on the intellihide mode, exclude non-relevent windows
- switch (this._settings.get_enum('intellihide-mode')) {
+ switch (this._dtdSettings.get_enum('intellihide-mode')) {
case IntellihideMode.ALL_WINDOWS:
// Do nothing
break;
case IntellihideMode.FOCUS_APPLICATION_WINDOWS:
// Skip windows of other apps
- if (this._focusApp ) {
+ if (this._focusApp) {
// The DropDownTerminal extension is not an application per se
// so we match its window by wm class instead
if (meta_win.get_wm_class() == 'DropDownTerminalWindow')
@@ -303,21 +277,18 @@ const intellihide = new Lang.Class({
// Consider half maximized windows side by side
// and windows which are alwayson top
- if( currentApp != this._focusApp && currentApp != this._topApp
- && !( (focusWindow && focusWindow.maximized_vertically && !focusWindow.maximized_horizontally)
+ if((currentApp != this._focusApp) && (currentApp != this._topApp)
+ && !((focusWindow && focusWindow.maximized_vertically && !focusWindow.maximized_horizontally)
&& (meta_win.maximized_vertically && !meta_win.maximized_horizontally)
- && meta_win.get_monitor() == focusWindow.get_monitor()
- )
- && !meta_win.is_above()
- ) {
+ && meta_win.get_monitor() == focusWindow.get_monitor())
+ && !meta_win.is_above())
return false;
- }
}
break;
case IntellihideMode.MAXIMIZED_WINDOWS:
// Skip unmaximized windows
- if (!meta_win.maximized_vertically && !meta_win.maximized_horizontally)
+ if (!meta_win.maximized_vertically && !meta_win.maximized_horizontally)
return false;
break;
}
@@ -333,12 +304,11 @@ const intellihide = new Lang.Class({
start hiding the dock, which is readily reshown as soon as the window
position is updated.
*/
- if ( wksp_index == currentWorkspace && meta_win.showing_on_its_workspace()
- && (Main.overview.visible || wa.mapped)) {
+ if (wksp_index == currentWorkspace && meta_win.showing_on_its_workspace()
+ && (Main.overview.visible || wa.mapped))
return true;
- } else {
+ else
return false;
- }
},
@@ -350,19 +320,16 @@ const intellihide = new Lang.Class({
if (metaWindow.get_wm_class() == 'DropDownTerminalWindow')
return true;
- var wtype = metaWindow.get_window_type();
- for (var i = 0; i < handledWindowTypes.length; i++) {
+ let wtype = metaWindow.get_window_type();
+ for (let i in handledWindowTypes) {
var hwtype = handledWindowTypes[i];
- if (hwtype == wtype) {
+ if (hwtype == wtype)
return true;
- } else if (hwtype > wtype) {
+ else if (hwtype > wtype)
return false;
- }
}
return false;
-
}
-
});
-Signals.addSignalMethods(intellihide.prototype);
+Signals.addSignalMethods(Intellihide.prototype);
diff --git a/myDash.js b/myDash.js
deleted file mode 100644
index 61c8780a9..000000000
--- a/myDash.js
+++ /dev/null
@@ -1,1827 +0,0 @@
-// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-
-const Clutter = imports.gi.Clutter;
-const Gio = imports.gi.Gio;
-const GLib = imports.gi.GLib;
-const Gtk = imports.gi.Gtk;
-const Signals = imports.signals;
-const Lang = imports.lang;
-const Meta = imports.gi.Meta;
-const Shell = imports.gi.Shell;
-const St = imports.gi.St;
-const Mainloop = imports.mainloop;
-
-const AppDisplay = imports.ui.appDisplay;
-const AppFavorites = imports.ui.appFavorites;
-const Dash = imports.ui.dash;
-const DND = imports.ui.dnd;
-const IconGrid = imports.ui.iconGrid;
-const Main = imports.ui.main;
-const PopupMenu = imports.ui.popupMenu;
-const Tweener = imports.ui.tweener;
-const Util = imports.misc.util;
-const Workspace = imports.ui.workspace;
-
-const Me = imports.misc.extensionUtils.getCurrentExtension();
-const Convenience = Me.imports.convenience;
-
-let DASH_ANIMATION_TIME = Dash.DASH_ANIMATION_TIME;
-let DASH_ITEM_LABEL_SHOW_TIME = Dash.DASH_ITEM_LABEL_SHOW_TIME;
-let DASH_ITEM_LABEL_HIDE_TIME = Dash.DASH_ITEM_LABEL_HIDE_TIME;
-let DASH_ITEM_HOVER_TIMEOUT = Dash.DASH_ITEM_HOVER_TIMEOUT;
-
-/* Return the actual position reverseing left and right in rtl */
-function getPosition(settings) {
- let position = settings.get_enum('dock-position');
- if(Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
- if (position == St.Side.LEFT)
- position = St.Side.RIGHT;
- else if (position == St.Side.RIGHT)
- position = St.Side.LEFT;
- }
- return position;
-}
-
-/**
- * Extend AppIconMenu
- *
- * - Pass settings to the constructor
- * - set popup arrow side based on dash orientation
- * - Add close windows option based on quitfromdash extension
- * (https://github.com/deuill/shell-extension-quitfromdash)
- */
-
-const myAppIconMenu = new Lang.Class({
- Name: 'myAppIconMenu',
- Extends: AppDisplay.AppIconMenu,
-
- _init: function(source, settings) {
-
- let side = getPosition(settings);
-
- // Damm it, there has to be a proper way of doing this...
- // As I can't call the parent parent constructor (?) passing the side
- // parameter, I overwite what I need later
- this.parent(source);
-
- // Change the initialized side where required.
- this._arrowSide = side;
- this._boxPointer._arrowSide = side;
- this._boxPointer._userArrowSide = side;
- },
-
- // helper function for the quit windows abilities
- _closeWindowInstance: function(metaWindow) {
- metaWindow.delete(global.get_current_time());
- },
-
- _redisplay: function() {
-
- this.parent();
-
- // quit menu
- let app = this._source.app;
- let count = getAppInterestingWindows(app).length;
- if ( count > 0) {
- this._appendSeparator();
- let quitFromDashMenuText = "";
- if (count == 1)
- quitFromDashMenuText = _("Quit");
- else
- quitFromDashMenuText = _("Quit") + ' ' + count + ' ' + _("Windows");
-
- this._quitfromDashMenuItem = this._appendMenuItem(quitFromDashMenuText);
- this._quitfromDashMenuItem.connect('activate', Lang.bind(this, function() {
- let app = this._source.app;
- let windows = app.get_windows();
- for (let i = 0; i < windows.length; i++) {
- this._closeWindowInstance(windows[i])
- }
- }));
- }
- }
-});
-
-/**
- * Extend DashItemContainer
- *
- * - Pass settings to the constructor
- * - set label position based on dash orientation
- *
- * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973.
- * thus use this ugly pattern.
- */
-
-// define first this function to use it in both extendShowAppsIcon and extendDashItemContainer
-function ItemShowLabel() {
- if (!this._labelText) {
- return;
- }
-
- this.label.set_text(this._labelText);
- this.label.opacity = 0;
- this.label.show();
-
- let [stageX, stageY] = this.get_transformed_position();
- let node = this.label.get_theme_node();
-
- let itemWidth = this.allocation.x2 - this.allocation.x1;
- let itemHeight = this.allocation.y2 - this.allocation.y1;
-
-
- let labelWidth = this.label.get_width();
- let labelHeight = this.label.get_height();
-
- let x, y, xOffset, yOffset;
-
- let position = getPosition(this._dtdSettings);
- this._isHorizontal = ( position == St.Side.TOP ||
- position == St.Side.BOTTOM);
- let labelOffset = node.get_length('-x-offset');
-
- switch(position) {
- case St.Side.LEFT:
- yOffset = Math.floor((itemHeight - labelHeight) / 2);
- y = stageY + yOffset;
- xOffset = labelOffset;
- x = stageX + this.get_width() + xOffset;
- break;
- case St.Side.RIGHT:
- yOffset = Math.floor((itemHeight - labelHeight) / 2);
- y = stageY + yOffset;
- xOffset = labelOffset;
- x = Math.round(stageX) - labelWidth - xOffset;
- break;
- case St.Side.TOP:
- y = stageY + labelOffset + itemHeight;
- xOffset = Math.floor((itemWidth - labelWidth) / 2);
- x = stageX + xOffset;
- break;
- case St.Side.BOTTOM:
- yOffset = labelOffset;
- y = stageY - labelHeight - yOffset;
- xOffset = Math.floor((itemWidth - labelWidth) / 2);
- x = stageX + xOffset;
- break;
- }
-
- // keep the label inside the screen border
- // Only needed fot the x coordinate.
-
- // Leave a few pixel gap
- let gap = 5;
- let monitor = Main.layoutManager.findMonitorForActor(this);
- if ( x - monitor.x monitor.x + monitor.width - gap)
- x-= x + labelWidth -( monitor.x + monitor.width) + gap;
-
- this.label.set_position(x, y);
- Tweener.addTween(this.label,
- { opacity: 255,
- time: DASH_ITEM_LABEL_SHOW_TIME,
- transition: 'easeOutQuad',
- });
-};
-
-function extendDashItemContainer(dashItemContainer, settings) {
-
- dashItemContainer._dtdSettings = settings;
- dashItemContainer.showLabel = ItemShowLabel;
-};
-
-/*
- * A menu for the showAppsIcon
-*/
-const myShowAppsIconMenu = new Lang.Class({
-
- Name: 'dashToDockShowAppsIconMenu',
- Extends: myAppIconMenu,
-
- _redisplay: function() {
- this.removeAll();
-
- let item = this._appendMenuItem("Dash to Dock " + _("Settings"));
-
- item.connect('activate', function () {
- Util.spawn(["gnome-shell-extension-prefs", Me.metadata.uuid]);
- });
- }
-
-});
-
-/**
- * Extend ShowAppsIcon
- *
- * - Pass settings to the constructor
- * - set label position based on dash orientation
- * - implement a popupMenu based on the AppIcon code
- *
- * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973.
- * thus use this ugly pattern.
- */
-
-function extendShowAppsIcon(showAppsIcon, settings){
-
-
- showAppsIcon._dtdSettings = settings;
- /* the variable equivalent to toggleButton has a different name in the appIcon class
- (actor): duplicate reference to easily reuse appIcon methods */
- showAppsIcon.actor = showAppsIcon.toggleButton;
-
- // Re-use appIcon methods
- showAppsIcon._removeMenuTimeout = AppDisplay.AppIcon.prototype._removeMenuTimeout;
- showAppsIcon._setPopupTimeout = AppDisplay.AppIcon.prototype._setPopupTimeout;
- showAppsIcon._onButtonPress = AppDisplay.AppIcon.prototype._onButtonPress;
- showAppsIcon._onKeyboardPopupMenu = AppDisplay.AppIcon.prototype._onKeyboardPopupMenu;
- showAppsIcon._onLeaveEvent = AppDisplay.AppIcon.prototype._onLeaveEvent;
- showAppsIcon._onTouchEvent = AppDisplay.AppIcon.prototype._onTouchEvent;
- showAppsIcon._onMenuPoppedDown = AppDisplay.AppIcon.prototype._onMenuPoppedDown;
-
-
- // No action on clicked (showing of the appsview is controlled elsewhere)
- showAppsIcon._onClicked = function(actor, button) {
- showAppsIcon._removeMenuTimeout();
- };
-
-
- showAppsIcon.actor.connect('leave-event', Lang.bind( showAppsIcon, showAppsIcon._onLeaveEvent));
- showAppsIcon.actor.connect('button-press-event', Lang.bind( showAppsIcon, showAppsIcon._onButtonPress));
- showAppsIcon.actor.connect('touch-event', Lang.bind( showAppsIcon, showAppsIcon._onTouchEvent));
- showAppsIcon.actor.connect('clicked', Lang.bind( showAppsIcon, showAppsIcon._onClicked));
- showAppsIcon.actor.connect('popup-menu', Lang.bind( showAppsIcon, showAppsIcon._onKeyboardPopupMenu));
-
- showAppsIcon._menu = null;
- showAppsIcon._menuManager = new PopupMenu.PopupMenuManager(showAppsIcon);
- showAppsIcon._menuTimeoutId = 0;
-
-
- showAppsIcon.showLabel = ItemShowLabel;
-
-
- showAppsIcon.popupMenu = function() {
-
- showAppsIcon._removeMenuTimeout();
- showAppsIcon.actor.fake_release();
-
- if (!showAppsIcon._menu) {
- showAppsIcon._menu = new myShowAppsIconMenu(showAppsIcon, showAppsIcon._dtdSettings);
- showAppsIcon._menu.connect('open-state-changed', Lang.bind(showAppsIcon, function (menu, isPoppedUp) {
- if (!isPoppedUp)
- showAppsIcon._onMenuPoppedDown();
- }));
- let id = Main.overview.connect('hiding', Lang.bind(showAppsIcon, function () { showAppsIcon._menu.close(); }));
- showAppsIcon._menu.actor.connect('destroy', function() {
- Main.overview.disconnect(id);
- });
- showAppsIcon._menuManager.addMenu(showAppsIcon._menu);
- }
-
- showAppsIcon.emit('menu-state-changed', true);
-
- showAppsIcon.actor.set_hover(true);
- showAppsIcon._menu.popup();
- showAppsIcon._menuManager.ignoreRelease();
- showAppsIcon.emit('sync-tooltip');
-
- return false;
- };
-
- Signals.addSignalMethods(showAppsIcon);
-}
-
-/* This class is a fork of the upstream DashActor class (ui.dash.js)
- *
- * Summary of changes:
- * - passed settings to class as parameter
- * - modified chldBox calculations for when 'show-apps-at-top' option is checked
- * - handle horizontal dash
- */
-const myDashActor = new Lang.Class({
- Name: 'DashToDockmyDashActor',
-
- _init: function(settings) {
- this._dtdSettings = settings;
- this._rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
-
- this._position = getPosition(settings);
- this._isHorizontal = ( this._position == St.Side.TOP ||
- this._position == St.Side.BOTTOM );
-
- let layout = new Clutter.BoxLayout({ orientation:
- this._isHorizontal?Clutter.Orientation.HORIZONTAL:Clutter.Orientation.VERTICAL });
-
- this.actor = new Shell.GenericContainer({ name: 'dash',
- layout_manager: layout,
- clip_to_allocation: true });
- this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
- this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
- this.actor.connect('allocate', Lang.bind(this, this._allocate));
-
- this.actor._delegate = this;
-
- },
-
- _allocate: function(actor, box, flags) {
- let contentBox = box;
- let availWidth = contentBox.x2 - contentBox.x1;
- let availHeight = contentBox.y2 - contentBox.y1;
-
- let [appIcons, showAppsButton] = actor.get_children();
- let [showAppsMinHeight, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth);
- let [showAppsMinWidth, showAppsNatWidth] = showAppsButton.get_preferred_width(availHeight);
-
- let offset_x = this._isHorizontal?showAppsNatWidth:0;
- let offset_y = this._isHorizontal?0:showAppsNatHeight;
-
- let childBox = new Clutter.ActorBox();
- if( (this._dtdSettings.get_boolean('show-apps-at-top') && !this._isHorizontal)
- || (this._dtdSettings.get_boolean('show-apps-at-top') && !this._rtl)
- || (!this._dtdSettings.get_boolean('show-apps-at-top') && this._isHorizontal && this._rtl)
- ) {
- childBox.x1 = contentBox.x1 + offset_x;
- childBox.y1 = contentBox.y1 + offset_y;
- childBox.x2 = contentBox.x2;
- childBox.y2 = contentBox.y2;
- appIcons.allocate(childBox, flags);
-
- childBox.y1 = contentBox.y1;
- childBox.x1 = contentBox.x1;
- childBox.x2 = contentBox.x1 + showAppsNatWidth;
- childBox.y2 = contentBox.y1 + showAppsNatHeight;
- showAppsButton.allocate(childBox, flags);
- } else {
- childBox.x1 = contentBox.x1;
- childBox.y1 = contentBox.y1;
- childBox.x2 = contentBox.x2 - offset_x;
- childBox.y2 = contentBox.y2 - offset_y;
- appIcons.allocate(childBox, flags);
-
- childBox.x2 = contentBox.x2;
- childBox.y2 = contentBox.y2;
- childBox.x1 = contentBox.x2 - showAppsNatWidth;
- childBox.y1 = contentBox.y2 - showAppsNatHeight;
- showAppsButton.allocate(childBox, flags);
- }
- },
-
- _getPreferredWidth: function(actor, forHeight, alloc) {
- // We want to request the natural height of all our children
- // as our natural height, so we chain up to StWidget (which
- // then calls BoxLayout), but we only request the showApps
- // button as the minimum size
-
- let [, natWidth] = this.actor.layout_manager.get_preferred_width(this.actor, forHeight);
-
- let themeNode = this.actor.get_theme_node();
- let [, showAppsButton] = this.actor.get_children();
- let [minWidth, ] = showAppsButton.get_preferred_height(forHeight);
-
- alloc.min_size = minWidth;
- alloc.natural_size = natWidth;
-
- },
-
- _getPreferredHeight: function(actor, forWidth, alloc) {
- // We want to request the natural height of all our children
- // as our natural height, so we chain up to StWidget (which
- // then calls BoxLayout), but we only request the showApps
- // button as the minimum size
-
- let [, natHeight] = this.actor.layout_manager.get_preferred_height(this.actor, forWidth);
-
- let themeNode = this.actor.get_theme_node();
- let [, showAppsButton] = this.actor.get_children();
- let [minHeight, ] = showAppsButton.get_preferred_height(forWidth);
-
- alloc.min_size = minHeight;
- alloc.natural_size = natHeight;
- }
-});
-
-/* This class is a fork of the upstream dash class (ui.dash.js)
- *
- * Summary of changes:
- * - disconnect global signals adding a destroy method;
- * - play animations even when not in overview mode
- * - set a maximum icon size
- * - show running and/or favorite applications
- * - emit a custom signal when an app icon is added
- * - hide showApps label when the custom menu is shown.
- * - Add scrollview
- * Ensure actor is visible on keyfocus inseid the scrollview
- * - add 128px icon size, might be usefull for hidpi display
- * - Sync minimization application target position.
- */
-
-const baseIconSizes = [ 16, 22, 24, 32, 48, 64, 96, 128 ];
-
-const myDash = new Lang.Class({
- Name: 'dashToDock.myDash',
-
- _init : function(settings) {
- this._maxHeight = -1;
- this.iconSize = 64;
- this._availableIconSizes = baseIconSizes;
- this._shownInitially = false;
-
- this._dtdSettings = settings;
- this._position = getPosition(settings);
- this._isHorizontal = ( this._position == St.Side.TOP ||
- this._position == St.Side.BOTTOM );
- this._signalsHandler = new Convenience.GlobalSignalsHandler();
-
- this._dragPlaceholder = null;
- this._dragPlaceholderPos = -1;
- this._animatingPlaceholdersCount = 0;
- this._showLabelTimeoutId = 0;
- this._resetHoverTimeoutId = 0;
- this._ensureAppIconVisibilityTimeoutId = 0;
- this._labelShowing = false;
-
- this._containerObject = new myDashActor(settings);
- this._container = this._containerObject.actor;
- this._scrollView = new St.ScrollView({ name: 'dashtodockDashScrollview',
- hscrollbar_policy: Gtk.PolicyType.NEVER,
- vscrollbar_policy: Gtk.PolicyType.NEVER,
- enable_mouse_scrolling: false });
-
- this._scrollView.connect('scroll-event', Lang.bind(this, this._onScrollEvent ));
-
- this._box = new St.BoxLayout({ vertical: !this._isHorizontal,
- clip_to_allocation: false,
- x_align: Clutter.ActorAlign.START,
- y_align: Clutter.ActorAlign.START });
- this._box._delegate = this;
- this._container.add_actor(this._scrollView);
- this._scrollView.add_actor(this._box);
-
- this._showAppsIcon = new Dash.ShowAppsIcon();
- extendShowAppsIcon(this._showAppsIcon, this._dtdSettings);
- this._showAppsIcon.childScale = 1;
- this._showAppsIcon.childOpacity = 255;
- this._showAppsIcon.icon.setIconSize(this.iconSize);
- this._hookUpLabel(this._showAppsIcon);
-
-
- let appsIcon = this._showAppsIcon;
- appsIcon.connect('menu-state-changed',
- Lang.bind(this, function(appsIcon, opened) {
- this._itemMenuStateChanged(appsIcon, opened);
- }));
-
- this.showAppsButton = this._showAppsIcon.toggleButton;
-
- this._container.add_actor(this._showAppsIcon);
-
- let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
- this.actor = new St.Bin({ child: this._container,
- y_align: St.Align.START, x_align:rtl?St.Align.END:St.Align.START
- });
-
- if(this._isHorizontal) {
- this.actor.connect('notify::width', Lang.bind(this,
- function() {
- if (this._maxHeight != this.actor.width)
- this._queueRedisplay();
- this._maxHeight = this.actor.width;
- }));
- } else {
- this.actor.connect('notify::height', Lang.bind(this,
- function() {
- if (this._maxHeight != this.actor.height)
- this._queueRedisplay();
- this._maxHeight = this.actor.height;
- }));
- }
-
- // Update minimization animation target position on allocation of the
- // container and on scrollview change.
- this._box.connect('notify::allocation', Lang.bind(this, this._updateAppIconsGeometry));
- let scrollViewAdjustment = this._isHorizontal?this._scrollView.hscroll.adjustment:this._scrollView.vscroll.adjustment;
- scrollViewAdjustment.connect('notify::value', Lang.bind(this, this._updateAppIconsGeometry));
-
- this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
-
- this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
-
- this._appSystem = Shell.AppSystem.get_default();
-
- this._signalsHandler.add(
- [
- this._appSystem,
- 'installed-changed',
- Lang.bind(this, function() {
- AppFavorites.getAppFavorites().reload();
- this._queueRedisplay();
- })
- ],
- [
- AppFavorites.getAppFavorites(),
- 'changed',
- Lang.bind(this, this._queueRedisplay)
- ],
- [
- this._appSystem,
- 'app-state-changed',
- Lang.bind(this, this._queueRedisplay)
- ],
- [
- Main.overview,
- 'item-drag-begin',
- Lang.bind(this, this._onDragBegin)
- ],
- [
- Main.overview,
- 'item-drag-end',
- Lang.bind(this, this._onDragEnd)
- ],
- [
- Main.overview,
- 'item-drag-cancelled',
- Lang.bind(this, this._onDragCancelled)
- ]
- );
-
- },
-
- destroy: function() {
- this._signalsHandler.destroy();
- },
-
- _onScrollEvent: function(actor, event) {
-
- // If scroll is not used because the icon is resized, let the scroll event propagate.
- if (!this._dtdSettings.get_boolean('icon-size-fixed'))
- return Clutter.EVENT_PROPAGATE;
-
- // Event coordinates are relative to the stage but can be transformed
- // as the actor will only receive events within his bounds.
- let stage_x, stage_y, ok, event_x, event_y, actor_w, actor_h;
- [stage_x, stage_y] = event.get_coords();
- [ok, event_x, event_y] = actor.transform_stage_point(stage_x, stage_y);
- [actor_w, actor_h] = actor.get_size();
-
- // If the scroll event is within a 1px margin from
- // the relevant edge of the actor, let the event propagate.
- if ((this._position == St.Side.LEFT && event_x <= 1)
- || (this._position == St.Side.RIGHT && event_x >= actor_w - 2)
- || (this._position == St.Side.TOP && event_y <= 1)
- || (this._position == St.Side.BOTTOM && event_y >= actor_h - 2))
- return Clutter.EVENT_PROPAGATE;
-
- // reset timeout to avid conflicts with the mousehover event
- if (this._ensureAppIconVisibilityTimeoutId>0) {
- Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId);
- this._ensureAppIconVisibilityTimeoutId = 0;
- }
-
- // Skip to avoid double events mouse
- if (event.is_pointer_emulated())
- return Clutter.EVENT_STOP;
-
- let adjustment, delta;
-
- if (this._isHorizontal)
- adjustment = this._scrollView.get_hscroll_bar().get_adjustment();
- else
- adjustment = this._scrollView.get_vscroll_bar().get_adjustment();
-
- let increment = adjustment.step_increment;
-
- switch ( event.get_scroll_direction() ) {
- case Clutter.ScrollDirection.UP:
- delta = -increment;
- break;
- case Clutter.ScrollDirection.DOWN:
- delta = +increment;
- break;
- case Clutter.ScrollDirection.SMOOTH:
- let [dx, dy] = event.get_scroll_delta();
- delta = dy*increment;
- // Also consider horizontal component, for instance touchpad
- if (this._isHorizontal)
- delta += dx*increment;
- break;
-
- }
-
- adjustment.set_value(adjustment.get_value() + delta);
-
- return Clutter.EVENT_STOP;
-
- },
-
- _onDragBegin: function() {
- this._dragCancelled = false;
- this._dragMonitor = {
- dragMotion: Lang.bind(this, this._onDragMotion)
- };
- DND.addDragMonitor(this._dragMonitor);
-
- if (this._box.get_n_children() == 0) {
- this._emptyDropTarget = new Dash.EmptyDropTargetItem();
- this._box.insert_child_at_index(this._emptyDropTarget, 0);
- this._emptyDropTarget.show(true);
- }
- },
-
- _onDragCancelled: function() {
- this._dragCancelled = true;
- this._endDrag();
- },
-
- _onDragEnd: function() {
- if (this._dragCancelled)
- return;
-
- this._endDrag();
- },
-
- _endDrag: function() {
- this._clearDragPlaceholder();
- this._clearEmptyDropTarget();
- this._showAppsIcon.setDragApp(null);
- DND.removeDragMonitor(this._dragMonitor);
- },
-
- _onDragMotion: function(dragEvent) {
- let app = Dash.getAppFromSource(dragEvent.source);
- if (app == null)
- return DND.DragMotionResult.CONTINUE;
-
- let showAppsHovered =
- this._showAppsIcon.contains(dragEvent.targetActor);
-
- if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
- this._clearDragPlaceholder();
-
- if (showAppsHovered)
- this._showAppsIcon.setDragApp(app);
- else
- this._showAppsIcon.setDragApp(null);
-
- return DND.DragMotionResult.CONTINUE;
- },
-
- _appIdListToHash: function(apps) {
- let ids = {};
- for (let i = 0; i < apps.length; i++)
- ids[apps[i].get_id()] = apps[i];
- return ids;
- },
-
- _queueRedisplay: function () {
- Main.queueDeferredWork(this._workId);
- },
-
- _hookUpLabel: function(item, appIcon) {
- item.child.connect('notify::hover', Lang.bind(this, function() {
- this._syncLabel(item, appIcon);
- }));
-
- let id = Main.overview.connect('hiding', Lang.bind(this, function() {
- this._labelShowing = false;
- item.hideLabel();
- }));
- item.child.connect('destroy', function() {
- Main.overview.disconnect(id);
- });
-
- if (appIcon) {
- appIcon.connect('sync-tooltip', Lang.bind(this, function() {
- this._syncLabel(item, appIcon);
- }));
- }
- },
-
- _createAppItem: function(app) {
- let appIcon = new myAppIcon(this._dtdSettings, app,
- { setSizeManually: true,
- showLabel: false });
- if (appIcon._draggable) {
- appIcon._draggable.connect('drag-begin',
- Lang.bind(this, function() {
- appIcon.actor.opacity = 50;
- }));
- appIcon._draggable.connect('drag-end',
- Lang.bind(this, function() {
- appIcon.actor.opacity = 255;
- }));
- }
-
- appIcon.connect('menu-state-changed',
- Lang.bind(this, function(appIcon, opened) {
- this._itemMenuStateChanged(item, opened);
- }));
-
- let item = new Dash.DashItemContainer();
-
- extendDashItemContainer(item, this._dtdSettings);
- item.setChild(appIcon.actor);
-
-
- item.setChild(appIcon.actor);
- appIcon.actor.connect('notify::hover', Lang.bind(this, function() {
- if (appIcon.actor.hover){
- this._ensureAppIconVisibilityTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function(){
- ensureActorVisibleInScrollView(this._scrollView, appIcon.actor);
- this._ensureAppIconVisibilityTimeoutId = 0;
- return GLib.SOURCE_REMOVE;
- }));
- } else {
- if (this._ensureAppIconVisibilityTimeoutId>0) {
- Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId);
- this._ensureAppIconVisibilityTimeoutId = 0;
- }
- }
- }));
-
- appIcon.actor.connect('clicked',
- Lang.bind(this, function(actor) {
- ensureActorVisibleInScrollView(this._scrollView, actor);
- }));
-
- appIcon.actor.connect('key-focus-in',
- Lang.bind(this, function(actor) {
-
- let [x_shift, y_shift] = ensureActorVisibleInScrollView(this._scrollView, actor);
-
- // This signal is triggered also by mouse click. The popup menu is opened at the original
- // coordinates. Thus correct for the shift which is going to be applied to the scrollview.
- if (appIcon._menu) {
- appIcon._menu._boxPointer.xOffset = -x_shift;
- appIcon._menu._boxPointer.yOffset = -y_shift;
- }
- }));
-
- // Override default AppIcon label_actor, now the
- // accessible_name is set at DashItemContainer.setLabelText
- appIcon.actor.label_actor = null;
- item.setLabelText(app.get_name());
-
- appIcon.icon.setIconSize(this.iconSize);
- this._hookUpLabel(item, appIcon);
-
- return item;
- },
-
- // Return an array with the "proper" appIcons currently in the dash
- _getAppIcons: function() {
- // Only consider children which are "proper"
- // icons (i.e. ignoring drag placeholders) and which are not
- // animating out (which means they will be destroyed at the end of
- // the animation)
- let iconChildren = this._box.get_children().filter(function(actor) {
- return actor.child &&
- actor.child._delegate &&
- actor.child._delegate.icon &&
- !actor.animatingOut;
- });
-
- let appIcons = iconChildren.map(function(actor){
- return actor.child._delegate;
- });
-
- return appIcons;
- },
-
- _updateAppIconsGeometry: function() {
- let appIcons = this._getAppIcons();
- appIcons.forEach(function(icon){
- icon.updateIconGeometry();
- });
- },
-
- _itemMenuStateChanged: function(item, opened) {
- // When the menu closes, it calls sync_hover, which means
- // that the notify::hover handler does everything we need to.
- if (opened) {
- if (this._showLabelTimeoutId > 0) {
- Mainloop.source_remove(this._showLabelTimeoutId);
- this._showLabelTimeoutId = 0;
- }
-
- item.hideLabel();
- } else {
- // I want to listen from outside when a menu is closed. I used to
- // add a custom signal to the appIcon, since gnome 3.8 the signal
- // calling this callback was added upstream.
- this.emit('menu-closed');
- }
- },
-
- _syncLabel: function (item, appIcon) {
- let shouldShow = appIcon ? appIcon.shouldShowTooltip() : item.child.get_hover();
-
- if (shouldShow) {
- if (this._showLabelTimeoutId == 0) {
- let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
- this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
- Lang.bind(this, function() {
- this._labelShowing = true;
- item.showLabel();
- this._showLabelTimeoutId = 0;
- return GLib.SOURCE_REMOVE;
- }));
- GLib.Source.set_name_by_id(this._showLabelTimeoutId, '[gnome-shell] item.showLabel');
- if (this._resetHoverTimeoutId > 0) {
- Mainloop.source_remove(this._resetHoverTimeoutId);
- this._resetHoverTimeoutId = 0;
- }
- }
- } else {
- if (this._showLabelTimeoutId > 0)
- Mainloop.source_remove(this._showLabelTimeoutId);
- this._showLabelTimeoutId = 0;
- item.hideLabel();
- if (this._labelShowing) {
- this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
- Lang.bind(this, function() {
- this._labelShowing = false;
- this._resetHoverTimeoutId = 0;
- return GLib.SOURCE_REMOVE;
- }));
- GLib.Source.set_name_by_id(this._resetHoverTimeoutId, '[gnome-shell] this._labelShowing');
- }
- }
- },
-
- _adjustIconSize: function() {
- // For the icon size, we only consider children which are "proper"
- // icons (i.e. ignoring drag placeholders) and which are not
- // animating out (which means they will be destroyed at the end of
- // the animation)
- let iconChildren = this._box.get_children().filter(function(actor) {
- return actor.child &&
- actor.child._delegate &&
- actor.child._delegate.icon &&
- !actor.animatingOut;
- });
-
- iconChildren.push(this._showAppsIcon);
-
- if (this._maxHeight == -1)
- return;
-
- let themeNode = this._container.get_theme_node();
- let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0,
- x2: this._isHorizontal?this._maxHeight:42 /* whatever */,
- y2: this._isHorizontal?42:this._maxHeight });
- let maxContent = themeNode.get_content_box(maxAllocation);
- let availHeight;
- if (this._isHorizontal)
- availHeight = maxContent.x2 - maxContent.x1;
- else
- availHeight = maxContent.y2 - maxContent.y1;
- let spacing = themeNode.get_length('spacing');
-
- let firstButton = iconChildren[0].child;
- let firstIcon = firstButton._delegate.icon;
-
- let minHeight, natHeight, maxWidth, natWidth;
-
- // Enforce the current icon size during the size request
- firstIcon.setIconSize(this.iconSize);
- [minHeight, natHeight] = firstButton.get_preferred_height(-1);
- [minWidth, natWidth] = firstButton.get_preferred_width(-1);
-
- let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
- let iconSizes = this._availableIconSizes.map(function(s) {
- return s * scaleFactor;
- });
-
- // Subtract icon padding and box spacing from the available height
- if(this._isHorizontal){
- availHeight -= iconChildren.length * (natWidth - this.iconSize * scaleFactor) +
- (iconChildren.length - 1) * spacing;
- } else {
- availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) +
- (iconChildren.length - 1) * spacing;
- }
-
- let availSize = availHeight / iconChildren.length;
-
-
- let newIconSize = this._availableIconSizes[0];
- for (let i = 0; i < iconSizes.length; i++) {
- if (iconSizes[i] < availSize)
- newIconSize = this._availableIconSizes[i];
- }
-
- if (newIconSize == this.iconSize)
- return;
-
- let oldIconSize = this.iconSize;
- this.iconSize = newIconSize;
- this.emit('icon-size-changed');
-
- let scale = oldIconSize / newIconSize;
- for (let i = 0; i < iconChildren.length; i++) {
- let icon = iconChildren[i].child._delegate.icon;
-
- // Set the new size immediately, to keep the icons' sizes
- // in sync with this.iconSize
- icon.setIconSize(this.iconSize);
-
- // Don't animate the icon size change when the overview
- // is transitioning, or when initially filling
- // the dash
- if (Main.overview.animationInProgress ||
- !this._shownInitially)
- continue;
-
- let [targetWidth, targetHeight] = icon.icon.get_size();
-
- // Scale the icon's texture to the previous size and
- // tween to the new size
- icon.icon.set_size(icon.icon.width * scale,
- icon.icon.height * scale);
-
- Tweener.addTween(icon.icon,
- { width: targetWidth,
- height: targetHeight,
- time: DASH_ANIMATION_TIME,
- transition: 'easeOutQuad',
- });
- }
- },
-
- _redisplay: function () {
- let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
-
- let running = this._appSystem.get_running();
-
- let children = this._box.get_children().filter(function(actor) {
- return actor.child &&
- actor.child._delegate &&
- actor.child._delegate.app;
- });
- // Apps currently in the dash
- let oldApps = children.map(function(actor) {
- return actor.child._delegate.app;
- });
- // Apps supposed to be in the dash
- let newApps = [];
-
- if( this._dtdSettings.get_boolean('show-favorites') ) {
- for (let id in favorites)
- newApps.push(favorites[id]);
- }
-
- if( this._dtdSettings.get_boolean('show-running') ) {
- for (let i = 0; i < running.length; i++) {
- let app = running[i];
- if (this._dtdSettings.get_boolean('show-favorites') && (app.get_id() in favorites) )
- continue;
- newApps.push(app);
- }
- }
-
- // Figure out the actual changes to the list of items; we iterate
- // over both the list of items currently in the dash and the list
- // of items expected there, and collect additions and removals.
- // Moves are both an addition and a removal, where the order of
- // the operations depends on whether we encounter the position
- // where the item has been added first or the one from where it
- // was removed.
- // There is an assumption that only one item is moved at a given
- // time; when moving several items at once, everything will still
- // end up at the right position, but there might be additional
- // additions/removals (e.g. it might remove all the launchers
- // and add them back in the new order even if a smaller set of
- // additions and removals is possible).
- // If above assumptions turns out to be a problem, we might need
- // to use a more sophisticated algorithm, e.g. Longest Common
- // Subsequence as used by diff.
- let addedItems = [];
- let removedActors = [];
-
- let newIndex = 0;
- let oldIndex = 0;
- while (newIndex < newApps.length || oldIndex < oldApps.length) {
- // No change at oldIndex/newIndex
- if (oldApps[oldIndex] == newApps[newIndex]) {
- oldIndex++;
- newIndex++;
- continue;
- }
-
- // App removed at oldIndex
- if (oldApps[oldIndex] &&
- newApps.indexOf(oldApps[oldIndex]) == -1) {
- removedActors.push(children[oldIndex]);
- oldIndex++;
- continue;
- }
-
- // App added at newIndex
- if (newApps[newIndex] &&
- oldApps.indexOf(newApps[newIndex]) == -1) {
- addedItems.push({ app: newApps[newIndex],
- item: this._createAppItem(newApps[newIndex]),
- pos: newIndex });
- newIndex++;
- continue;
- }
-
- // App moved
- let insertHere = newApps[newIndex + 1] &&
- newApps[newIndex + 1] == oldApps[oldIndex];
- let alreadyRemoved = removedActors.reduce(function(result, actor) {
- let removedApp = actor.child._delegate.app;
- return result || removedApp == newApps[newIndex];
- }, false);
-
- if (insertHere || alreadyRemoved) {
- let newItem = this._createAppItem(newApps[newIndex]);
- addedItems.push({ app: newApps[newIndex],
- item: newItem,
- pos: newIndex + removedActors.length });
- newIndex++;
- } else {
- removedActors.push(children[oldIndex]);
- oldIndex++;
- }
- }
-
- for (let i = 0; i < addedItems.length; i++)
- this._box.insert_child_at_index(addedItems[i].item,
- addedItems[i].pos);
-
- for (let i = 0; i < removedActors.length; i++) {
- let item = removedActors[i];
-
- // Don't animate item removal when the overview is transitioning
- if (!Main.overview.animationInProgress)
- item.animateOutAndDestroy();
- else
- item.destroy();
- }
-
- this._adjustIconSize();
-
- for (let i = 0; i < addedItems.length; i++){
- // Emit a custom signal notifying that a new item has been added
- this.emit('item-added', addedItems[i]);
- }
-
- // Skip animations on first run when adding the initial set
- // of items, to avoid all items zooming in at once
-
- let animate = this._shownInitially &&
- !Main.overview.animationInProgress;
-
- if (!this._shownInitially)
- this._shownInitially = true;
-
- for (let i = 0; i < addedItems.length; i++) {
- addedItems[i].item.show(animate);
- }
-
- // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
- // Without it, StBoxLayout may use a stale size cache
- this._box.queue_relayout();
-
- // This is required for icon reordering when the scrollview is used.
- this._updateAppIconsGeometry();
- },
-
- setIconSize: function (max_size, doNotAnimate) {
-
- let max_allowed = baseIconSizes[baseIconSizes.length-1];
- max_size = Math.min(max_size, max_allowed);
-
- if (this._dtdSettings.get_boolean('icon-size-fixed')) {
- this._availableIconSizes = [ max_size ];
- } else {
- this._availableIconSizes = baseIconSizes.filter(
- function(val){
- return (val numChildren)
- pos = numChildren;
- } else
- pos = 0; // always insert at the top when dash is empty
-
- /* Take into account childredn position in rtl*/
- if (this._isHorizontal &&
- Clutter.get_default_text_direction() == Clutter.TextDirection.RTL
- )
- pos = numChildren - pos;
-
- if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
- this._dragPlaceholderPos = pos;
-
- // Don't allow positioning before or after self
- if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
- this._clearDragPlaceholder();
- return DND.DragMotionResult.CONTINUE;
- }
-
- // If the placeholder already exists, we just move
- // it, but if we are adding it, expand its size in
- // an animation
- let fadeIn;
- if (this._dragPlaceholder) {
- this._dragPlaceholder.destroy();
- fadeIn = false;
- } else {
- fadeIn = true;
- }
-
- this._dragPlaceholder = new Dash.DragPlaceholderItem();
- this._dragPlaceholder.child.set_width (this.iconSize);
- this._dragPlaceholder.child.set_height (this.iconSize / 2);
- this._box.insert_child_at_index(this._dragPlaceholder,
- this._dragPlaceholderPos);
- this._dragPlaceholder.show(fadeIn);
- // Ensure the next and previous icon are visible when moving the placeholder
- // (I assume there's room for both of them)
- if (this._dragPlaceholderPos > 1)
- ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[this._dragPlaceholderPos-1]);
- if (this._dragPlaceholderPos < this._box.get_children().length-1)
- ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[this._dragPlaceholderPos+1]);
- }
-
- // Remove the drag placeholder if we are not in the
- // "favorites zone"
- if (pos > numFavorites)
- this._clearDragPlaceholder();
-
- if (!this._dragPlaceholder)
- return DND.DragMotionResult.NO_DROP;
-
- let srcIsFavorite = (favPos != -1);
-
- if (srcIsFavorite)
- return DND.DragMotionResult.MOVE_DROP;
-
- return DND.DragMotionResult.COPY_DROP;
- },
-
- // Draggable target interface
- acceptDrop : function(source, actor, x, y, time) {
-
- let app = Dash.getAppFromSource(source);
-
- // Don't allow favoriting of transient apps
- if (app == null || app.is_window_backed()) {
- return false;
- }
-
- if (!this._settings.is_writable('favorite-apps') || !this._dtdSettings.get_boolean('show-favorites'))
- return false;
-
- let id = app.get_id();
-
- let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
-
- let srcIsFavorite = (id in favorites);
-
- let favPos = 0;
- let children = this._box.get_children();
- for (let i = 0; i < this._dragPlaceholderPos; i++) {
- if (this._dragPlaceholder &&
- children[i] == this._dragPlaceholder)
- continue;
-
- let childId = children[i].child._delegate.app.get_id();
- if (childId == id)
- continue;
- if (childId in favorites)
- favPos++;
- }
-
- // No drag placeholder means we don't wan't to favorite the app
- // and we are dragging it to its original position
- if (!this._dragPlaceholder)
- return true;
-
- Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
- function () {
- let appFavorites = AppFavorites.getAppFavorites();
- if (srcIsFavorite)
- appFavorites.moveFavoriteToPos(id, favPos);
- else
- appFavorites.addFavoriteAtPos(id, favPos);
- return false;
- }));
-
- return true;
- },
-
- showShowAppsButton: function() {
- this.showAppsButton.visible = true
- this.showAppsButton.set_width(-1)
- this.showAppsButton.set_height(-1)
- },
-
- hideShowAppsButton: function() {
- this.showAppsButton.hide()
- this.showAppsButton.set_width(0)
- this.showAppsButton.set_height(0)
- }
-
-});
-
-Signals.addSignalMethods(myDash.prototype);
-
-
-/**
- * Extend AppIcon
- *
- * - Pass settings to the constructor and bind settings changes
- * - Apply a css class based on the number of windows of each application (#N);
- * - Draw a dot for each window of the application based on the default "dot" style which is hidden (#N);
- * a class of the form "running#N" is applied to the AppWellIcon actor.
- * like the original .running one.
- * - add a .focused style to the focused app
- * - Customize click actions.
- * - Update minimization animation target
- *
- */
-
-let tracker = Shell.WindowTracker.get_default();
-
-const clickAction = {
- SKIP: 0,
- MINIMIZE: 1,
- LAUNCH: 2,
- CYCLE_WINDOWS: 3
-};
-
-let recentlyClickedAppLoopId = 0;
-let recentlyClickedApp = null;
-let recentlyClickedAppWindows = null;
-let recentlyClickedAppIndex = 0;
-
-const myAppIcon = new Lang.Class({
- Name: 'dashToDock.AppIcon',
- Extends: AppDisplay.AppIcon,
-
- // settings are required inside.
- _init: function(settings, app, iconParams, onActivateOverride) {
-
- this._dtdSettings = settings;
- this._nWindows = 0;
-
- this.parent(app, iconParams, onActivateOverride);
-
- // Monitor windows-changes instead of app state.
- // Keep using the same Id and function callback (that is extended)
- if(this._stateChangedId>0){
- this.app.disconnect(this._stateChangedId);
- this._stateChangedId=0;
- }
-
- this._stateChangedId = this.app.connect('windows-changed',
- Lang.bind(this,
- this.onWindowsChanged));
- this._focuseAppChangeId = tracker.connect('notify::focus-app',
- Lang.bind(this,
- this._onFocusAppChanged));
-
- this._dots = null;
-
- let keys = ['apply-custom-theme',
- 'custom-theme-running-dots',
- 'custom-theme-customize-running-dots',
- 'custom-theme-running-dots-color',
- 'custom-theme-running-dots-border-color',
- 'custom-theme-running-dots-border-width'];
-
- keys.forEach(function(key){
- this._dtdSettings.connect('changed::'+key,
- Lang.bind(this, this._toggleDots)
- );
- }, this );
-
- this._toggleDots();
- },
-
- _onDestroy: function() {
- this.parent();
-
- // Disconect global signals
- // stateChangedId is already handled by parent)
- if(this._focusAppId>0)
- tracker.disconnect(this._focusAppId);
- },
-
- onWindowsChanged: function() {
-
- this._updateRunningStyle();
- this.updateIconGeometry();
-
- },
-
- // Update taraget for minimization animation
- updateIconGeometry: function() {
-
- // If (for unknown reason) the actor is not on the stage the reported size
- // and position are random values, which might exceeds the integer range
- // resulting in an error when assigned to the a rect. This is a more like
- // a workaround to prevent flooding the system with errors.
- if (this.actor.get_stage() == null)
- return
-
- let rect = new Meta.Rectangle();
-
- [rect.x, rect.y] = this.actor.get_transformed_position();
- [rect.width, rect.height] = this.actor.get_transformed_size();
-
- let windows = this.app.get_windows();
- windows.forEach(function(w) {
- w.set_icon_geometry(rect);
- });
-
- },
-
- _toggleDots: function() {
-
- if ( this._dtdSettings.get_boolean('custom-theme-running-dots')
- || this._dtdSettings.get_boolean('apply-custom-theme') )
- this._showDots();
- else
- this._hideDots();
- },
-
- _showDots: function() {
- // I use opacity to hide the default dot because the show/hide function
- // are used by the parent class.
- this._dot.opacity = 0;
-
- // Just update style if dots already exist
- if (this._dots) {
- this._updateCounterClass();
- return;
- }
-
- this._dots = new St.DrawingArea({x_expand: true, y_expand: true});
- this._dots.connect('repaint', Lang.bind(this,
- function() {
- this._drawCircles(this._dots, getPosition(this._dtdSettings));
- }));
- this._iconContainer.add_child(this._dots);
- this._updateCounterClass();
-
- },
-
- _hideDots: function() {
- this._dot.opacity=255;
- if (this._dots)
- this._dots.destroy()
- this._dots = null;
- },
-
- _updateRunningStyle: function() {
- this.parent();
- this._updateCounterClass();
- },
-
- popupMenu: function() {
- this._removeMenuTimeout();
- this.actor.fake_release();
- this._draggable.fakeRelease();
-
- if (!this._menu) {
- this._menu = new myAppIconMenu(this, this._dtdSettings);
- this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
- this.activateWindow(window);
- }));
- this._menu.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) {
- if (!isPoppedUp)
- this._onMenuPoppedDown();
- }));
- let id = Main.overview.connect('hiding', Lang.bind(this, function () { this._menu.close(); }));
- this._menu.actor.connect('destroy', function() {
- Main.overview.disconnect(id);
- });
-
- this._menuManager.addMenu(this._menu);
- }
-
- this.emit('menu-state-changed', true);
-
- this.actor.set_hover(true);
- this._menu.popup();
- this._menuManager.ignoreRelease();
- this.emit('sync-tooltip');
-
- return false;
- },
-
- _onFocusAppChanged: function() {
- if(tracker.focus_app == this.app)
- this.actor.add_style_class_name('focused');
- else
- this.actor.remove_style_class_name('focused');
- },
-
- activate: function(button) {
-
- if ( !this._dtdSettings.get_boolean('customize-click') ){
- this.parent(button);
- return;
- }
-
- let event = Clutter.get_current_event();
- let modifiers = event ? event.get_state() : 0;
- let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK &&
- this.app.state == Shell.AppState.RUNNING ||
- button && button == 2;
- let focusedApp = tracker.focus_app;
-
- if (this.app.state == Shell.AppState.STOPPED || openNewWindow)
- this.animateLaunch();
-
- if(button && button == 1 && this.app.state == Shell.AppState.RUNNING) {
-
- if(modifiers & Clutter.ModifierType.CONTROL_MASK){
- // Keep default behaviour: launch new window
- // By calling the parent method I make it compatible
- // with other extensions tweaking ctrl + click
- this.parent(button);
- return;
-
- } else if (this._dtdSettings.get_boolean('minimize-shift') && modifiers & Clutter.ModifierType.SHIFT_MASK){
- // On double click, minimize all windows in the current workspace
- minimizeWindow(this.app, event.get_click_count() > 1);
-
- } else if(this.app == focusedApp && !Main.overview._shown){
-
- if(this._dtdSettings.get_enum('click-action') == clickAction.CYCLE_WINDOWS)
- cycleThroughWindows(this.app);
- else if(this._dtdSettings.get_enum('click-action') == clickAction.MINIMIZE)
- minimizeWindow(this.app, true);
- else if(this._dtdSettings.get_enum('click-action') == clickAction.LAUNCH)
- this.app.open_new_window(-1);
-
- } else {
- // Activate all window of the app or only le last used
- if (this._dtdSettings.get_enum('click-action') == clickAction.CYCLE_WINDOWS && !Main.overview._shown){
- // If click cycles through windows I can activate one windows at a time
- let windows = getAppInterestingWindows(this.app);
- let w = windows[0];
- Main.activateWindow(w);
- } else if(this._dtdSettings.get_enum('click-action') == clickAction.LAUNCH)
- this.app.open_new_window(-1);
- else if(this._dtdSettings.get_enum('click-action') == clickAction.MINIMIZE){
- // If click minimizes all, then one expects all windows to be reshown
- activateAllWindows(this.app);
- } else
- this.app.activate();
- }
- } else {
- // Default behaviour
- if (openNewWindow)
- this.app.open_new_window(-1);
- else
- this.app.activate();
- }
-
- Main.overview.hide();
- },
-
- _updateCounterClass: function() {
-
- let maxN = 4;
- this._nWindows = Math.min(getAppInterestingWindows(this.app).length, maxN);
-
- for(let i = 1; i<=maxN; i++){
- let className = 'running'+i;
- if(i!=this._nWindows)
- this.actor.remove_style_class_name(className);
- else
- this.actor.add_style_class_name(className);
- }
-
- if (this._dots)
- this._dots.queue_repaint();
- },
-
- _drawCircles: function(area, side) {
-
- let borderColor, borderWidth, bodyColor;
-
- if (!this._dtdSettings.get_boolean('apply-custom-theme')
- && this._dtdSettings.get_boolean('custom-theme-running-dots')
- && this._dtdSettings.get_boolean('custom-theme-customize-running-dots')) {
- borderColor = Clutter.color_from_string(this._dtdSettings.get_string('custom-theme-running-dots-border-color'))[1];
- borderWidth = this._dtdSettings.get_int('custom-theme-running-dots-border-width');
- bodyColor = Clutter.color_from_string(this._dtdSettings.get_string('custom-theme-running-dots-color'))[1];
- } else {
- // Re-use the style - background color, and border width and color -
- // of the default dot
- let themeNode = this._dot.get_theme_node();
- borderColor = themeNode.get_border_color(side);
- borderWidth = themeNode.get_border_width(side);
- bodyColor = themeNode.get_background_color();
- }
-
- let [width, height] = area.get_surface_size();
- let cr = area.get_context();
-
- // Draw the required numbers of dots
- let radius = width/22 - borderWidth/2;
- radius = Math.max(radius, borderWidth/4+1);
- let padding = 0; // distance from the margin
- let spacing = radius + borderWidth; // separation between the dots
- let n = this._nWindows;
-
- cr.setLineWidth(borderWidth);
- Clutter.cairo_set_source_color(cr, borderColor);
-
- switch (side) {
- case St.Side.TOP:
- cr.translate((width - (2*n)*radius - (n-1)*spacing)/2, padding);
- for (let i=0; i=0; i--){
- if(windows[i].get_workspace().index() == activeWorkspace){
- Main.activateWindow(windows[i]);
- activatedWindows++;
- }
- }
-}
-
-function cycleThroughWindows(app) {
-
- // Store for a little amount of time last clicked app and its windows
- // since the order changes upon window interaction
- let MEMORY_TIME=3000;
-
- let app_windows = getAppInterestingWindows(app);
-
- if(recentlyClickedAppLoopId>0)
- Mainloop.source_remove(recentlyClickedAppLoopId);
- recentlyClickedAppLoopId = Mainloop.timeout_add(MEMORY_TIME, resetRecentlyClickedApp);
-
- // If there isn't already a list of windows for the current app,
- // or the stored list is outdated, use the current windows list.
- if( !recentlyClickedApp ||
- recentlyClickedApp.get_id() != app.get_id() ||
- recentlyClickedAppWindows.length != app_windows.length
- ){
-
- recentlyClickedApp = app;
- recentlyClickedAppWindows = app_windows;
- recentlyClickedAppIndex = 0;
- }
-
- recentlyClickedAppIndex++;
- let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length;
- let window = recentlyClickedAppWindows[index];
-
- Main.activateWindow(window);
-}
-
-function resetRecentlyClickedApp() {
-
- if(recentlyClickedAppLoopId>0)
- Mainloop.source_remove(recentlyClickedAppLoopId);
- recentlyClickedAppLoopId=0;
- recentlyClickedApp =null;
- recentlyClickedAppWindows = null;
- recentlyClickedAppIndex = 0;
-
- return false;
-}
-
-function getAppInterestingWindows(app) {
- // Filter out unnecessary windows, for instance
- // nautilus desktop window.
- let windows = app.get_windows().filter(function(w) {
- return !w.skip_taskbar;
- });
-
- return windows;
-}
-
-
-/*
- * This is a copy of the same function in utils.js, but also adjust horizontal scrolling
- * and perform few further cheks on the current value to avoid changing the values when
- * it would be clamp to the current one in any case.
- * Return the amount of shift applied
-*/
-function ensureActorVisibleInScrollView(scrollView, actor) {
-
- let adjust_v = true;
- let adjust_h = true;
-
- let vadjustment = scrollView.vscroll.adjustment;
- let hadjustment = scrollView.hscroll.adjustment;
- let [vvalue, vlower, vupper, vstepIncrement, vpageIncrement, vpageSize] = vadjustment.get_values();
- let [hvalue, hlower, hupper, hstepIncrement, hpageIncrement, hpageSize] = hadjustment.get_values();
-
- let [hvalue0, vvalue0] = [hvalue, vvalue];
-
- let voffset = 0;
- let hoffset = 0;
- let fade = scrollView.get_effect("fade");
- if (fade){
- voffset = fade.vfade_offset;
- hoffset = fade.hfade_offset;
- }
-
- let box = actor.get_allocation_box();
- let y1 = box.y1, y2 = box.y2, x1 = box.x1, x2 = box.x2;
-
- let parent = actor.get_parent();
- while (parent != scrollView) {
- if (!parent)
- throw new Error("actor not in scroll view");
-
- let box = parent.get_allocation_box();
- y1 += box.y1;
- y2 += box.y1;
- x1 += box.x1;
- x2 += box.x1;
- parent = parent.get_parent();
- }
-
- if (y1 < vvalue + voffset)
- vvalue = Math.max(0, y1 - voffset);
- else if (vvalue < vupper - vpageSize && y2 > vvalue + vpageSize - voffset)
- vvalue = Math.min(vupper -vpageSize, y2 + voffset - vpageSize);
-
- if (x1 < hvalue + hoffset)
- hvalue = Math.max(0, x1 - hoffset);
- else if (hvalue < hupper - hpageSize && x2 > hvalue + hpageSize - hoffset)
- hvalue = Math.min(hupper - hpageSize, x2 + hoffset - hpageSize);
-
- if (vvalue !== vvalue0) {
- Tweener.addTween(vadjustment,
- { value: vvalue,
- time: Util.SCROLL_TIME,
- transition: 'easeOutQuad' });
- }
-
- if (hvalue !== hvalue0) {
- Tweener.addTween(hadjustment,
- { value: hvalue,
- time: Util.SCROLL_TIME,
- transition: 'easeOutQuad' });
- }
-
- return [hvalue- hvalue0, vvalue - vvalue0];
-}
diff --git a/prefs.js b/prefs.js
index 82ec7dd9d..3bb430598 100644
--- a/prefs.js
+++ b/prefs.js
@@ -1,13 +1,13 @@
-// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Gdk = imports.gi.Gdk;
-const Lang = imports.lang;
-const Mainloop = imports.mainloop;
-
const Gettext = imports.gettext.domain('dashtodock');
const _ = Gettext.gettext;
@@ -18,50 +18,47 @@ const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
const SCALE_UPDATE_TIMEOUT = 500;
-const DEFAULT_ICONS_SIZES = [ 128, 96, 64, 48, 32, 24, 16 ];
+const DEFAULT_ICONS_SIZES = [128, 96, 64, 48, 32, 24, 16];
-/*
-This function was copied from the activities-config extension
-https://github.com/nls1729/acme-code/tree/master/activities-config
-by Norman L. Smith.
-*/
+/**
+ * This function was copied from the activities-config extension
+ * https://github.com/nls1729/acme-code/tree/master/activities-config
+ * by Norman L. Smith.
+ */
function cssHexString(css) {
let rrggbb = '#';
let start;
- for(let loop = 0; loop < 3; loop++) {
+ for (let loop = 0; loop < 3; loop++) {
let end = 0;
let xx = '';
- for(let loop = 0; loop < 2; loop++) {
- while(true) {
+ for (let loop = 0; loop < 2; loop++) {
+ while (true) {
let x = css.slice(end, end + 1);
- if(x == '(' || x == ',' || x == ')')
+ if ((x == '(') || (x == ',') || (x == ')'))
break;
- end = end + 1;
+ end++;
}
- if(loop == 0) {
- end = end + 1;
+ if (loop == 0) {
+ end++;
start = end;
}
}
xx = parseInt(css.slice(start, end)).toString(16);
- if(xx.length == 1)
+ if (xx.length == 1)
xx = '0' + xx;
- rrggbb = rrggbb + xx;
+ rrggbb += xx;
css = css.slice(end);
}
return rrggbb;
}
-
const Settings = new Lang.Class({
- Name: 'DashToDockSettings',
-
+ Name: 'DashToDock.Settings',
_init: function() {
+ this._dtdSettings = Convenience.getSettings('org.gnome.shell.extensions.dash-to-dock');
- this._settings = Convenience.getSettings('org.gnome.shell.extensions.dash-to-dock');
-
- this._rtl = Gtk.Widget.get_default_direction()==Gtk.TextDirection.RTL;
+ this._rtl = (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL);
this._builder = new Gtk.Builder();
this._builder.set_translation_domain(Me.metadata['gettext-domain']);
@@ -77,17 +74,17 @@ const Settings = new Lang.Class({
this._bindSettings();
this._builder.connect_signals_full(Lang.bind(this, this._connector));
-
},
- // Connect signals
+ /**
+ * Connect signals
+ */
_connector: function(builder, object, signal, handler) {
object.connect(signal, Lang.bind(this, this._SignalHandler[handler]));
},
_bindSettings: function() {
-
- /* Position and size panel */
+ // Position and size panel
// Monitor options
@@ -96,7 +93,7 @@ const Settings = new Lang.Class({
let n_monitors = Gdk.Screen.get_default().get_n_monitors();
let primary_monitor = Gdk.Screen.get_default().get_primary_monitor();
- let monitor = this._settings.get_int('preferred-monitor');
+ let monitor = this._dtdSettings.get_int('preferred-monitor');
// Add primary monitor with index 0, because in GNOME Shell the primary monitor is always 0
this._builder.get_object('dock_monitor_combo').append_text(_("Primary monitor"));
@@ -105,23 +102,23 @@ const Settings = new Lang.Class({
// Add connected monitors
let ctr = 0;
for (let i = 0; i < n_monitors; i++) {
- if (i !== primary_monitor){
+ if (i !== primary_monitor) {
ctr++;
this._monitors.push(ctr);
- this._builder.get_object('dock_monitor_combo').append_text(_("Secondary monitor ") + ctr);
+ this._builder.get_object('dock_monitor_combo').append_text(_("Secondary monitor") + ' ' + ctr);
}
}
// If one of the external monitor is set as preferred, show it even if not attached
- if ( monitor >= n_monitors && monitor !== primary_monitor) {
+ if ((monitor >= n_monitors) && (monitor !== primary_monitor)) {
this._monitors.push(monitor)
- this._builder.get_object('dock_monitor_combo').append_text(_("Secondary monitor ") + ++ctr);
+ this._builder.get_object('dock_monitor_combo').append_text(_("Secondary monitor") + ' ' + ++ctr);
}
this._builder.get_object('dock_monitor_combo').set_active(this._monitors.indexOf(monitor));
// Position option
- let position = this._settings.get_enum('dock-position');
+ let position = this._dtdSettings.get_enum('dock-position');
switch (position) {
case 0:
@@ -145,65 +142,67 @@ const Settings = new Lang.Class({
}
// Intelligent autohide options
- this._settings.bind('dock-fixed',
+ this._dtdSettings.bind('dock-fixed',
this._builder.get_object('intelligent_autohide_switch'),
'active',
Gio.SettingsBindFlags.INVERT_BOOLEAN);
- this._settings.bind('dock-fixed',
+ this._dtdSettings.bind('dock-fixed',
this._builder.get_object('intelligent_autohide_button'),
'sensitive',
Gio.SettingsBindFlags.INVERT_BOOLEAN);
- this._settings.bind('autohide',
+ this._dtdSettings.bind('autohide',
this._builder.get_object('autohide_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('autohide-in-fullscreen',
+ this._dtdSettings.bind('autohide-in-fullscreen',
this._builder.get_object('autohide_enable_in_fullscreen_checkbutton'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('require-pressure-to-show',
+ this._dtdSettings.bind('require-pressure-to-show',
this._builder.get_object('require_pressure_checkbutton'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('intellihide',
+ this._dtdSettings.bind('intellihide',
this._builder.get_object('intellihide_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('animation-time',
+ this._dtdSettings.bind('animation-time',
this._builder.get_object('animation_duration_spinbutton'),
'value',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('hide-delay',
+ this._dtdSettings.bind('hide-delay',
this._builder.get_object('hide_timeout_spinbutton'),
'value',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('show-delay',
+ this._dtdSettings.bind('show-delay',
this._builder.get_object('show_timeout_spinbutton'),
'value',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('pressure-threshold',
+ this._dtdSettings.bind('pressure-threshold',
this._builder.get_object('pressure_threshold_spinbutton'),
'value',
Gio.SettingsBindFlags.DEFAULT);
- //this._builder.get_object('animation_duration_spinbutton').set_value(this._settings.get_double('animation-time'));
+ //this._builder.get_object('animation_duration_spinbutton').set_value(this._dtdSettings.get_double('animation-time'));
// Create dialog for intelligent autohide advanced settings
this._builder.get_object('intelligent_autohide_button').connect('clicked', Lang.bind(this, function() {
- let dialog = new Gtk.Dialog({ title: _("Intelligent autohide customization"),
- transient_for: this.widget.get_toplevel(),
- use_header_bar: true,
- modal: true });
+ let dialog = new Gtk.Dialog({
+ title: _("Intelligent autohide customization"),
+ transient_for: this.widget.get_toplevel(),
+ use_header_bar: true,
+ modal: true
+ });
// GTK+ leaves positive values for application-defined response ids.
- // Use +1 for the reset action
+ // Use +1 for the reset action
dialog.add_button(_("Reset to defaults"), 1);
let box = this._builder.get_object('intelligent_autohide_advanced_settings_box');
dialog.get_content_area().add(box);
- this._settings.bind('intellihide',
+ this._dtdSettings.bind('intellihide',
this._builder.get_object('intellihide_mode_box'),
'sensitive',
Gio.SettingsBindFlags.GET);
@@ -216,31 +215,31 @@ const Settings = new Lang.Class({
this._builder.get_object('maximized_windows_radio_button')
];
- intellihideModeRadioButtons[this._settings.get_enum('intellihide-mode')].set_active(true);
+ intellihideModeRadioButtons[this._dtdSettings.get_enum('intellihide-mode')].set_active(true);
- this._settings.bind('autohide',
+ this._dtdSettings.bind('autohide',
this._builder.get_object('require_pressure_checkbutton'),
'sensitive',
Gio.SettingsBindFlags.GET);
- this._settings.bind('autohide',
+ this._dtdSettings.bind('autohide',
this._builder.get_object('autohide_enable_in_fullscreen_checkbutton'),
'sensitive',
Gio.SettingsBindFlags.GET);
- this._settings.bind('require-pressure-to-show',
+ this._dtdSettings.bind('require-pressure-to-show',
this._builder.get_object('show_timeout_spinbutton'),
'sensitive',
Gio.SettingsBindFlags.INVERT_BOOLEAN);
- this._settings.bind('require-pressure-to-show',
+ this._dtdSettings.bind('require-pressure-to-show',
this._builder.get_object('show_timeout_label'),
'sensitive',
Gio.SettingsBindFlags.INVERT_BOOLEAN);
- this._settings.bind('require-pressure-to-show',
+ this._dtdSettings.bind('require-pressure-to-show',
this._builder.get_object('pressure_threshold_spinbutton'),
'sensitive',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('require-pressure-to-show',
+ this._dtdSettings.bind('require-pressure-to-show',
this._builder.get_object('pressure_threshold_label'),
'sensitive',
Gio.SettingsBindFlags.DEFAULT);
@@ -250,11 +249,12 @@ const Settings = new Lang.Class({
// restore default settings for the relevant keys
let keys = ['intellihide', 'autohide', 'intellihide-mode', 'autohide-in-fullscreen', 'require-pressure-to-show',
'animation-time', 'show-delay', 'hide-delay', 'pressure-threshold'];
- keys.forEach(function(val){
- this._settings.set_value(val, this._settings.get_default_value(val));
+ keys.forEach(function(val) {
+ this._dtdSettings.set_value(val, this._dtdSettings.get_default_value(val));
}, this);
- intellihideModeRadioButtons[this._settings.get_enum('intellihide-mode')].set_active(true);
- } else {
+ intellihideModeRadioButtons[this._dtdSettings.get_enum('intellihide-mode')].set_active(true);
+ }
+ else {
// remove the settings box so it doesn't get destroyed;
dialog.get_content_area().remove(box);
dialog.destroy();
@@ -267,13 +267,13 @@ const Settings = new Lang.Class({
}));
// size options
- this._builder.get_object('dock_size_scale').set_value(this._settings.get_double('height-fraction'));
+ this._builder.get_object('dock_size_scale').set_value(this._dtdSettings.get_double('height-fraction'));
this._builder.get_object('dock_size_scale').add_mark(0.9, Gtk.PositionType.TOP, null);
let icon_size_scale = this._builder.get_object('icon_size_scale');
- icon_size_scale.set_range(DEFAULT_ICONS_SIZES[DEFAULT_ICONS_SIZES.length -1], DEFAULT_ICONS_SIZES[0]);
- icon_size_scale.set_value(this._settings.get_int('dash-max-icon-size'));
- DEFAULT_ICONS_SIZES.forEach(function(val){
- icon_size_scale.add_mark(val, Gtk.PositionType.TOP, val.toString());
+ icon_size_scale.set_range(DEFAULT_ICONS_SIZES[DEFAULT_ICONS_SIZES.length - 1], DEFAULT_ICONS_SIZES[0]);
+ icon_size_scale.set_value(this._dtdSettings.get_int('dash-max-icon-size'));
+ DEFAULT_ICONS_SIZES.forEach(function(val) {
+ icon_size_scale.add_mark(val, Gtk.PositionType.TOP, val.toString());
});
// Corrent for rtl languages
@@ -281,74 +281,77 @@ const Settings = new Lang.Class({
// Flip value position: this is not done automatically
this._builder.get_object('dock_size_scale').set_value_pos(Gtk.PositionType.LEFT);
icon_size_scale.set_value_pos(Gtk.PositionType.LEFT);
- /* I suppose due to a bug, having a more than one mark and one above a value of 100
- * makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable
- * and then manually inverting it
- */
+ // I suppose due to a bug, having a more than one mark and one above a value of 100
+ // makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable
+ // and then manually inverting it
icon_size_scale.set_flippable(false);
icon_size_scale.set_inverted(true);
}
- this._settings.bind('icon-size-fixed', this._builder.get_object('icon_size_fixed_checkbutton'), 'active', Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('extend-height', this._builder.get_object('dock_size_extend_checkbutton'), 'active', Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('extend-height', this._builder.get_object('dock_size_scale'), 'sensitive', Gio.SettingsBindFlags.INVERT_BOOLEAN);
+ this._dtdSettings.bind('icon-size-fixed', this._builder.get_object('icon_size_fixed_checkbutton'), 'active', Gio.SettingsBindFlags.DEFAULT);
+ this._dtdSettings.bind('extend-height', this._builder.get_object('dock_size_extend_checkbutton'), 'active', Gio.SettingsBindFlags.DEFAULT);
+ this._dtdSettings.bind('extend-height', this._builder.get_object('dock_size_scale'), 'sensitive', Gio.SettingsBindFlags.INVERT_BOOLEAN);
- /* Behavior panel */
+ // Behavior panel
- this._settings.bind('show-running',
+ this._dtdSettings.bind('show-running',
this._builder.get_object('show_running_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('show-favorites',
+ this._dtdSettings.bind('show-favorites',
this._builder.get_object('show_favorite_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('show-show-apps-button',
+ this._dtdSettings.bind('show-show-apps-button',
this._builder.get_object('show_applications_button_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('show-apps-at-top',
+ this._dtdSettings.bind('show-apps-at-top',
this._builder.get_object('application_button_first_button'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('show-show-apps-button',
+ this._dtdSettings.bind('show-show-apps-button',
this._builder.get_object('application_button_first_button'),
'sensitive',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('animate-show-apps',
+ this._dtdSettings.bind('animate-show-apps',
this._builder.get_object('application_button_animation_button'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('show-show-apps-button',
+ this._dtdSettings.bind('show-show-apps-button',
this._builder.get_object('application_button_animation_button'),
'sensitive',
Gio.SettingsBindFlags.DEFAULT);
+ this._dtdSettings.bind('support-window-stealing',
+ this._builder.get_object('support_window_stealing_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
- this._builder.get_object('click_action_combo').set_active(this._settings.get_enum('click-action'));
+ this._builder.get_object('click_action_combo').set_active(this._dtdSettings.get_enum('click-action'));
this._builder.get_object('click_action_combo').connect('changed', Lang.bind (this, function(widget) {
- this._settings.set_enum('click-action', widget.get_active());
+ this._dtdSettings.set_enum('click-action', widget.get_active());
}));
- this._builder.get_object('shift_click_action_combo').set_active(this._settings.get_boolean('minimize-shift')?1:0);
+ this._builder.get_object('shift_click_action_combo').set_active(this._dtdSettings.get_boolean('minimize-shift') ? 1 : 0);
this._builder.get_object('shift_click_action_combo').connect('changed', Lang.bind (this, function(widget) {
- this._settings.set_boolean('minimize-shift', widget.get_active()==1);
+ this._dtdSettings.set_boolean('minimize-shift', widget.get_active() == 1);
}));
- this._settings.bind('scroll-switch-workspace', this._builder.get_object('switch_workspace_switch'), 'active', Gio.SettingsBindFlags.DEFAULT);
+ this._dtdSettings.bind('scroll-switch-workspace', this._builder.get_object('switch_workspace_switch'), 'active', Gio.SettingsBindFlags.DEFAULT);
- /* Appearance Panel */
+ // Appearance Panel
- this._settings.bind('apply-custom-theme', this._builder.get_object('customize_theme'), 'sensitive', Gio.SettingsBindFlags.INVERT_BOOLEAN | Gio.SettingsBindFlags.GET);
- this._settings.bind('apply-custom-theme', this._builder.get_object('builtin_theme_switch'), 'active', Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('custom-theme-shrink', this._builder.get_object('shrink_dash_switch'), 'active', Gio.SettingsBindFlags.DEFAULT);
+ this._dtdSettings.bind('apply-custom-theme', this._builder.get_object('customize_theme'), 'sensitive', Gio.SettingsBindFlags.INVERT_BOOLEAN | Gio.SettingsBindFlags.GET);
+ this._dtdSettings.bind('apply-custom-theme', this._builder.get_object('builtin_theme_switch'), 'active', Gio.SettingsBindFlags.DEFAULT);
+ this._dtdSettings.bind('custom-theme-shrink', this._builder.get_object('shrink_dash_switch'), 'active', Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('custom-theme-running-dots',
+ this._dtdSettings.bind('custom-theme-running-dots',
this._builder.get_object('running_dots_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('custom-theme-running-dots',
+ this._dtdSettings.bind('custom-theme-running-dots',
this._builder.get_object('running_dots_advance_settings_button'),
'sensitive',
Gio.SettingsBindFlags.DEFAULT);
@@ -356,44 +359,46 @@ const Settings = new Lang.Class({
// Create dialog for running dots advanced settings
this._builder.get_object('running_dots_advance_settings_button').connect('clicked', Lang.bind(this, function() {
- let dialog = new Gtk.Dialog({ title: _("Customize running indicators"),
- transient_for: this.widget.get_toplevel(),
- use_header_bar: true,
- modal: true });
+ let dialog = new Gtk.Dialog({
+ title: _("Customize running indicators"),
+ transient_for: this.widget.get_toplevel(),
+ use_header_bar: true,
+ modal: true
+ });
let box = this._builder.get_object('running_dots_advance_settings_box');
dialog.get_content_area().add(box);
- this._settings.bind('custom-theme-customize-running-dots',
+ this._dtdSettings.bind('custom-theme-customize-running-dots',
this._builder.get_object('dot_style_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('custom-theme-customize-running-dots',
+ this._dtdSettings.bind('custom-theme-customize-running-dots',
this._builder.get_object('dot_style_settings_box'),
'sensitive', Gio.SettingsBindFlags.DEFAULT);
let rgba = new Gdk.RGBA();
- rgba.parse(this._settings.get_string('custom-theme-running-dots-color'));
+ rgba.parse(this._dtdSettings.get_string('custom-theme-running-dots-color'));
this._builder.get_object('dot_color_colorbutton').set_rgba(rgba);
this._builder.get_object('dot_color_colorbutton').connect('notify::color', Lang.bind(this, function(button) {
let rgba = button.get_rgba();
let css = rgba.to_string();
let hexString = cssHexString(css);
- this._settings.set_string('custom-theme-running-dots-color', hexString);
+ this._dtdSettings.set_string('custom-theme-running-dots-color', hexString);
}));
- rgba.parse(this._settings.get_string('custom-theme-running-dots-border-color'));
+ rgba.parse(this._dtdSettings.get_string('custom-theme-running-dots-border-color'));
this._builder.get_object('dot_border_color_colorbutton').set_rgba(rgba);
this._builder.get_object('dot_border_color_colorbutton').connect('notify::color', Lang.bind(this, function(button) {
let rgba = button.get_rgba();
let css = rgba.to_string();
let hexString = cssHexString(css);
- this._settings.set_string('custom-theme-running-dots-border-color', hexString);
+ this._dtdSettings.set_string('custom-theme-running-dots-border-color', hexString);
}));
- this._settings.bind('custom-theme-running-dots-border-width',
+ this._dtdSettings.bind('custom-theme-running-dots-border-width',
this._builder.get_object('dot_border_width_spin_button'),
'value',
Gio.SettingsBindFlags.DEFAULT);
@@ -410,110 +415,109 @@ const Settings = new Lang.Class({
}));
- this._settings.bind('opaque-background', this._builder.get_object('customize_opacity_switch'), 'active', Gio.SettingsBindFlags.DEFAULT);
- this._builder.get_object('custom_opacity_scale').set_value(this._settings.get_double('background-opacity'));
- this._settings.bind('opaque-background', this._builder.get_object('custom_opacity'), 'sensitive', Gio.SettingsBindFlags.DEFAULT);
+ this._dtdSettings.bind('opaque-background', this._builder.get_object('customize_opacity_switch'), 'active', Gio.SettingsBindFlags.DEFAULT);
+ this._builder.get_object('custom_opacity_scale').set_value(this._dtdSettings.get_double('background-opacity'));
+ this._dtdSettings.bind('opaque-background', this._builder.get_object('custom_opacity'), 'sensitive', Gio.SettingsBindFlags.DEFAULT);
- /* About Panel */
+ // About Panel
this._builder.get_object('extension_version').set_label(Me.metadata.version.toString());
-
},
-
- // Object containing all signals defined in the glade file
+ /**
+ * Object containing all signals defined in the glade file
+ */
_SignalHandler: {
+ dock_display_combo_changed_cb: function(combo) {
+ this._dtdSettings.set_int('preferred-monitor', this._monitors[combo.get_active()]);
+ },
+
+ position_top_button_toggled_cb: function(button) {
+ if (button.get_active())
+ this._dtdSettings.set_enum('dock-position', 0);
+ },
+
+ position_right_button_toggled_cb: function(button) {
+ if (button.get_active())
+ this._dtdSettings.set_enum('dock-position', 1);
+ },
+
+ position_bottom_button_toggled_cb: function(button) {
+ if (button.get_active())
+ this._dtdSettings.set_enum('dock-position', 2);
+ },
+
+ position_left_button_toggled_cb: function(button) {
+ if (button.get_active())
+ this._dtdSettings.set_enum('dock-position', 3);
+ },
+
+ icon_size_combo_changed_cb: function(combo) {
+ this._dtdSettings.set_int('dash-max-icon-size', this._allIconSizes[combo.get_active()]);
+ },
+
+ dock_size_scale_format_value_cb: function(scale, value) {
+ return Math.round(value*100)+ ' %';
+ },
+
+ dock_size_scale_value_changed_cb: function(scale) {
+ // Avoid settings the size consinuosly
+ if (this._dock_size_timeout > 0)
+ Mainloop.source_remove(this._dock_size_timeout);
+
+ this._dock_size_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() {
+ this._dtdSettings.set_double('height-fraction', scale.get_value());
+ this._dock_size_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ }));
+ },
- dock_display_combo_changed_cb: function (combo) {
- this._settings.set_int('preferred-monitor', this._monitors[combo.get_active()]);
- },
-
- position_top_button_toggled_cb: function (button){
- if (button.get_active())
- this._settings.set_enum('dock-position', 0);
- },
-
- position_right_button_toggled_cb: function (button) {
- if (button.get_active())
- this._settings.set_enum('dock-position', 1);
- },
-
- position_bottom_button_toggled_cb: function (button) {
- if (button.get_active())
- this._settings.set_enum('dock-position', 2);
- },
-
- position_left_button_toggled_cb: function (button) {
- if (button.get_active())
- this._settings.set_enum('dock-position', 3);
- },
-
- icon_size_combo_changed_cb: function(combo) {
- this._settings.set_int('dash-max-icon-size', this._allIconSizes[combo.get_active()]);
- },
-
- dock_size_scale_format_value_cb: function(scale, value) {
- return Math.round(value*100)+ ' %';
- },
-
- dock_size_scale_value_changed_cb: function(scale){
- // Avoid settings the size consinuosly
- if (this._dock_size_timeout >0)
- Mainloop.source_remove(this._dock_size_timeout);
-
- this._dock_size_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function(){
- this._settings.set_double('height-fraction', scale.get_value());
- this._dock_size_timeout = 0;
- return GLib.SOURCE_REMOVE;
- }));
- },
-
- icon_size_scale_format_value_cb: function(scale, value) {
- return value+ ' px';
- },
-
- icon_size_scale_value_changed_cb: function(scale){
- // Avoid settings the size consinuosly
- if (this._icon_size_timeout >0)
- Mainloop.source_remove(this._icon_size_timeout);
-
- this._icon_size_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function(){
- this._settings.set_int('dash-max-icon-size', scale.get_value());
- this._icon_size_timeout = 0;
- return GLib.SOURCE_REMOVE;
- }));
- },
-
- custom_opacity_scale_value_changed_cb: function(scale){
- // Avoid settings the opacity consinuosly as it's change is animated
- if (this._opacity_timeout >0)
- Mainloop.source_remove(this._opacity_timeout);
-
- this._opacity_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function(){
- this._settings.set_double('background-opacity', scale.get_value());
- this._opacity_timeout = 0;
- return GLib.SOURCE_REMOVE;
- }));
- },
-
- custom_opacity_scale_format_value_cb: function(scale, value) {
- return Math.round(value*100) + ' %';
- },
-
- all_windows_radio_button_toggled_cb: function (button){
- if (button.get_active())
- this._settings.set_enum('intellihide-mode', 0);
- },
-
- focus_application_windows_radio_button_toggled_cb: function (button){
- if (button.get_active())
- this._settings.set_enum('intellihide-mode', 1);
- },
-
- maximized_windows_radio_button_toggled_cb: function (button){
- if (button.get_active())
- this._settings.set_enum('intellihide-mode', 2);
- }
+ icon_size_scale_format_value_cb: function(scale, value) {
+ return value+ ' px';
+ },
+
+ icon_size_scale_value_changed_cb: function(scale) {
+ // Avoid settings the size consinuosly
+ if (this._icon_size_timeout > 0)
+ Mainloop.source_remove(this._icon_size_timeout);
+
+ this._icon_size_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() {
+ this._dtdSettings.set_int('dash-max-icon-size', scale.get_value());
+ this._icon_size_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ }));
+ },
+
+ custom_opacity_scale_value_changed_cb: function(scale) {
+ // Avoid settings the opacity consinuosly as it's change is animated
+ if (this._opacity_timeout > 0)
+ Mainloop.source_remove(this._opacity_timeout);
+
+ this._opacity_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() {
+ this._dtdSettings.set_double('background-opacity', scale.get_value());
+ this._opacity_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ }));
+ },
+
+ custom_opacity_scale_format_value_cb: function(scale, value) {
+ return Math.round(value*100) + ' %';
+ },
+
+ all_windows_radio_button_toggled_cb: function(button) {
+ if (button.get_active())
+ this._dtdSettings.set_enum('intellihide-mode', 0);
+ },
+
+ focus_application_windows_radio_button_toggled_cb: function(button) {
+ if (button.get_active())
+ this._dtdSettings.set_enum('intellihide-mode', 1);
+ },
+
+ maximized_windows_radio_button_toggled_cb: function(button) {
+ if (button.get_active())
+ this._dtdSettings.set_enum('intellihide-mode', 2);
+ }
}
});
@@ -527,4 +531,3 @@ function buildPrefsWidget() {
widget.show_all();
return widget;
}
-
diff --git a/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml b/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml
index 8a3345dbb..ce35dcafc 100644
--- a/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml
+++ b/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml
@@ -191,6 +191,15 @@
true
Activate only one window
+
+ false
+ Support window stealing
+
+
+ []
+ Window stealing
+ Each string is a space-separated list, where the first item in the list is the app ID and the subsequent items are WM_CLASS names
+
'cycle-windows'
Action when clicking on a running app
diff --git a/stylesheet.css b/stylesheet.css
index 869840783..8e3399553 100644
--- a/stylesheet.css
+++ b/stylesheet.css
@@ -87,3 +87,29 @@
#dashtodockContainer.dashtodock #dash {
background: #2e3436;
}
+
+/*
+#dashtodockContainer2 .stolen {
+ border: 0px solid red;
+ margin: 0px;
+ padding: 0px;
+ width: 0px;
+ height: 0px;
+}
+
+#dashtodockContainer2 .stolen StBin {
+ border: 0px solid red;
+ margin: 0px;
+ padding: 0px;
+ width: 0px;
+ height: 0px;
+}
+
+#dashtodockContainer2 .stolen StWidget {
+ border: 0px solid red;
+ margin: 0px;
+ padding: 0px;
+ width: 0px;
+ height: 0px;
+}
+*/
diff --git a/theming.js b/theming.js
new file mode 100644
index 000000000..cbd3dd90f
--- /dev/null
+++ b/theming.js
@@ -0,0 +1,200 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Lang = imports.lang;
+
+const St = imports.gi.St;
+
+const Main = imports.ui.main;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const Convenience = Me.imports.convenience;
+
+/**
+ * Manage theme customization and custom theme support
+ */
+const ThemeManager = new Lang.Class({
+ Name: 'DashToDock.ThemeManager',
+
+ _init: function(settings, actor, dash) {
+ this._dtdSettings = settings;
+ this._bindSettingsChanges();
+ this._actor = actor;
+ this._dash = dash;
+
+ // initialize colors with generic values
+ this._defaultBackground = {red: 0, green: 0, blue: 0, alpha: 0};
+ this._defaultBackgroundColor = {red: 0, green: 0, blue: 0, alpha: 0};
+ this._customizedBackground = {red: 0, green: 0, blue: 0, alpha: 0};
+
+ this._signalsHandler = new Convenience.GlobalSignalsHandler();
+ this._signalsHandler.add([
+ // When theme changes re-obtain default background color
+ St.ThemeContext.get_for_stage (global.stage),
+ 'changed',
+ Lang.bind(this, this.updateCustomTheme)
+ ], [
+ // update :overview pseudoclass
+ Main.overview,
+ 'showing',
+ Lang.bind(this, this._onOverviewShowing)
+ ], [
+ Main.overview,
+ 'hiding',
+ Lang.bind(this, this._onOverviewHiding)
+ ]);
+
+ this._updateCustomStyleClasses();
+
+ },
+
+ destroy: function() {
+ this._signalsHandler.destroy();
+ },
+
+ _onOverviewShowing: function() {
+ this._actor.add_style_pseudo_class('overview');
+ },
+
+ _onOverviewHiding: function() {
+ this._actor.remove_style_pseudo_class('overview');
+ },
+
+ _updateBackgroundOpacity: function() {
+ let newAlpha = this._dtdSettings.get_double('background-opacity');
+
+ this._defaultBackground = 'rgba(' +
+ this._defaultBackgroundColor.red + ',' +
+ this._defaultBackgroundColor.green + ',' +
+ this._defaultBackgroundColor.blue + ',' +
+ Math.round(this._defaultBackgroundColor.alpha/2.55)/100 + ')';
+
+ this._customizedBackground = 'rgba(' +
+ this._defaultBackgroundColor.red + ',' +
+ this._defaultBackgroundColor.green + ',' +
+ this._defaultBackgroundColor.blue + ',' +
+ newAlpha + ')';
+ },
+
+ _getBackgroundColor: function() {
+ // Prevent shell crash if the actor is not on the stage.
+ // It happens enabling/disabling repeatedly the extension
+ if (!this._dash._container.get_stage())
+ return;
+
+ // Remove custom style
+ let oldStyle = this._dash._container.get_style();
+ this._dash._container.set_style(null);
+
+ let themeNode = this._dash._container.get_theme_node();
+ this._dash._container.set_style(oldStyle);
+
+ this._defaultBackgroundColor = themeNode.get_background_color();
+ },
+
+ _updateCustomStyleClasses: function() {
+ if (this._dtdSettings.get_boolean('apply-custom-theme'))
+ this._actor.add_style_class_name('dashtodock');
+ else
+ this._actor.remove_style_class_name('dashtodock');
+
+ if (this._dtdSettings.get_boolean('custom-theme-shrink'))
+ this._actor.add_style_class_name('shrink');
+ else
+ this._actor.remove_style_class_name('shrink');
+ },
+
+ updateCustomTheme: function() {
+ this._updateCustomStyleClasses();
+ this._getBackgroundColor();
+ this._updateBackgroundOpacity();
+ this._adjustTheme();
+ this._dash._redisplay();
+ },
+
+ /**
+ * Reimported back and adapted from atomdock
+ */
+ _adjustTheme: function() {
+ // Prevent shell crash if the actor is not on the stage.
+ // It happens enabling/disabling repeatedly the extension
+ if (!this._dash._container.get_stage())
+ return;
+
+ // Remove prior style edits
+ this._dash._container.set_style(null);
+
+ // If built-in theme is enabled do nothing else
+ if (this._dtdSettings.get_boolean('apply-custom-theme'))
+ return;
+
+ let newStyle = '';
+ let position = Convenience.getPosition(this._dtdSettings);
+
+ if (!this._dtdSettings.get_boolean('custom-theme-shrink')) {
+ // obtain theme border settings
+ let themeNode = this._dash._container.get_theme_node();
+ let borderColor = themeNode.get_border_color(St.Side.TOP);
+ let borderWidth = themeNode.get_border_width(St.Side.TOP);
+ let borderRadius = themeNode.get_border_radius(St.Corner.TOPRIGHT);
+
+ // We're copying border and corner styles to left border and top-left
+ // corner, also removing bottom border and bottom-right corner styles
+ let borderInner = '';
+ let borderRadiusValue = '';
+ let borderMissingStyle = '';
+
+ if (this._rtl && (position != St.Side.RIGHT))
+ borderMissingStyle = 'border-right: ' + borderWidth + 'px solid ' +
+ borderColor.to_string() + ';';
+ else if (!this._rtl && (position != St.Side.LEFT))
+ borderMissingStyle = 'border-left: ' + borderWidth + 'px solid ' +
+ borderColor.to_string() + ';';
+
+ switch (position) {
+ case St.Side.LEFT:
+ borderInner = 'border-left';
+ borderRadiusValue = '0 ' + borderRadius + 'px ' + borderRadius + 'px 0;';
+ break;
+ case St.Side.RIGHT:
+ borderInner = 'border-right';
+ borderRadiusValue = borderRadius + 'px 0 0 ' + borderRadius + 'px;';
+ break;
+ case St.Side.TOP:
+ borderInner = 'border-top';
+ borderRadiusValue = '0 0 ' + borderRadius + 'px ' + borderRadius + 'px;';
+ break;
+ case St.Side.BOTTOM:
+ borderInner = 'border-bottom';
+ borderRadiusValue = borderRadius + 'px ' + borderRadius + 'px 0 0;';
+ break;
+ }
+
+ newStyle = borderInner + ': none;' +
+ 'border-radius: ' + borderRadiusValue +
+ borderMissingStyle;
+
+ // I do call set_style possibly twice so that only the background gets the transition.
+ // The transition-property css rules seems to be unsupported
+ this._dash._container.set_style(newStyle);
+ }
+
+ // Customize background
+ if (this._dtdSettings.get_boolean('opaque-background')) {
+ newStyle = newStyle + 'background-color:'+ this._customizedBackground + '; ' +
+ 'transition-delay: 0s; transition-duration: 0.250s;';
+ this._dash._container.set_style(newStyle);
+ }
+ },
+
+ _bindSettingsChanges: function() {
+ let keys = ['opaque-background',
+ 'background-opacity',
+ 'apply-custom-theme',
+ 'custom-theme-shrink',
+ 'extend-height'];
+
+ keys.forEach(function(key) {
+ this._dtdSettings.connect('changed::' + key, Lang.bind(this, this.updateCustomTheme));
+ }, this);
+ }
+});
diff --git a/windows.js b/windows.js
new file mode 100644
index 000000000..8ef295c29
--- /dev/null
+++ b/windows.js
@@ -0,0 +1,290 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Lang = imports.lang;
+
+const Clutter = imports.gi.Clutter;
+const Gio = imports.gi.Gio;
+const Shell = imports.gi.Shell;
+const St = imports.gi.St;
+
+const ModalDialog = imports.ui.modalDialog;
+const ShellEntry = imports.ui.shellEntry;
+
+// Example settings:
+let exampleSettings = [
+ 'sapir-claws-mail.desktop Claws-mail',
+ 'sapir-pidgin.desktop Pidgin'
+];
+
+function getSettings(settings) {
+ if (!settings.get_boolean('support-window-stealing'))
+ return {};
+
+ let table = {};
+
+ let array = settings.get_strv('window-stealing') || [];
+
+ if ((array == null) || !array.length) {
+ array = exampleSettings;
+ settings.set_strv('window-stealing', array);
+ Gio.Settings.sync();
+ }
+
+ for (let a in array) {
+ let entry = array[a].split('|');
+ if (entry.length) {
+ let key = entry[0];
+ entry.splice(0, 1);
+ table[key] = entry;
+ }
+ }
+ return table;
+}
+
+function isWindowStealer(app, settings) {
+ settings = getSettings(settings);
+ return settings[app.id] != null;
+}
+
+function isStolen(app, settings) {
+ return hasStolenWindows(app, settings) && !getNonStolenWindows(app, settings).length;
+}
+
+function isStealingFrom(app, stolenApp, settings) {
+ if (stolenApp !== null) {
+ let windows = stolenApp.get_windows();
+ for (let w = windows.length - 1; w >= 0; w--) {
+ if (isStealingWindow(app, windows[w], settings)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+function isStolenWindow(window, settings) {
+ settings = getSettings(settings);
+ let clazz = window.wm_class;
+ for (let id in settings) {
+ let classesToSteal = settings[id];
+ for (let i in classesToSteal) {
+ if (clazz == classesToSteal[i]) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+function isStealingWindow(app, window, settings) {
+ settings = getSettings(settings);
+ let classesToSteal = settings[app.id];
+ if (classesToSteal) {
+ let clazz = window.wm_class;
+ for (let c in classesToSteal) {
+ if (classesToSteal[c] == clazz) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+function hasStolenWindows(app, settings) {
+ let windows = app.get_windows();
+ for (let w = windows.length - 1; w >= 0; w--) {
+ if (isStolenWindow(windows[w], settings)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+function getStolenWindows(app, settings) {
+ return app.get_windows().filter(function(w) {
+ return isStolenWindow(w, settings);
+ });
+}
+
+function getNonStolenWindows(app, settings) {
+ return app.get_windows().filter(function(w) {
+ return !isStolenWindow(w, settings);
+ });
+}
+
+/**
+ * Includes stolen windows
+ */
+function getAllWindows(app, settings) {
+ settings = getSettings(settings);
+ let windows = app.get_windows();
+ let classesToSteal = settings[app.id];
+ if (classesToSteal) {
+ let running = Shell.AppSystem.get_default().get_running();
+ running.forEach(function(r) {
+ r.get_windows().forEach(function(window) {
+ let clazz = window.wm_class;
+ for (let c in classesToSteal) {
+ if (classesToSteal[c] == clazz) {
+ windows.push(window);
+ }
+ }
+ });
+ });
+ }
+ return windows;
+}
+
+/**
+ * Filter out unnecessary windows, for instance
+ * nautilus desktop window.
+ */
+function getInterestingWindows(app, settings) {
+ return getAllWindows(app, settings).filter(function(w) {
+ return !w.skip_taskbar;
+ });
+}
+
+/**
+ * Window stealing settings
+ */
+const WindowStealingSettings = new Lang.Class({
+ Name: 'DashToDock.WindowStealingSettings',
+ Extends: ModalDialog.ModalDialog,
+
+ _init: function(app, settings) {
+ this.parent({styleClass: 'run-dialog'});
+
+ this._app = app;
+ this._dtdSettings = settings;
+
+ let value = '';
+
+ let array = this._dtdSettings.get_strv('window-stealing') || [];
+ for (let a in array) {
+ let c = array[a].indexOf('|');
+ if (c != -1) {
+ if (this._app.id == array[a].substring(0, c)) {
+ value = array[a].substring(c + 1);
+ break;
+ }
+ }
+ }
+
+ let mainContentBox = new St.BoxLayout({vertical: true});
+ this.contentLayout.add(mainContentBox, {
+ x_fill: true,
+ y_fill: true
+ });
+
+ // Title
+ let appIdLabel = new St.Label({
+ style_class: 'run-dialog-label',
+ text: _("Application ID") + ': ' + app.id
+ });
+ mainContentBox.add(appIdLabel, {
+ x_fill: true,
+ x_align: St.Align.MIDDLE
+ });
+
+ // Instructions
+ let wmClassLabel = new St.Label({
+ style_class: 'run-dialog-label',
+ text: _("Enter pipe-separated list of WM_CLASS names to steal")
+ });
+ mainContentBox.add(wmClassLabel, {
+ x_fill: false,
+ x_align: St.Align.START,
+ y_align: St.Align.START
+ });
+
+ // Entry
+ this._entry = new St.Entry({
+ style_class: 'run-dialog-entry',
+ can_focus: true
+ });
+ ShellEntry.addContextMenu(this._entry); // adds copy/paste context menu
+ this._entry.set_text(value);
+ mainContentBox.add(this._entry, {
+ y_align: St.Align.START
+ });
+ this.setInitialKeyFocus(this._entry.clutter_text);
+ this._entry.clutter_text.connect('key-press-event', Lang.bind(this, function(owner, event) {
+ let symbol = event.get_key_symbol();
+ if ((symbol == Clutter.Return) || (symbol == Clutter.KP_Enter)) {
+ this._onSave();
+ return Clutter.EVENT_STOP;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }));
+
+ // Buttons
+ this.setButtons([{
+ label: _("Cancel"),
+ action: Lang.bind(this, this._onCancel),
+ key: Clutter.Escape
+ }, {
+ label: _("Save"),
+ action: Lang.bind(this, this._onSave),
+ default: true
+ }]);
+ },
+
+ _onCancel: function() {
+ this.close();
+ },
+
+ _onSave: function() {
+ let value = this._entry.get_text();
+
+ // Cleanup
+ value = value.split('|');
+ for (let v in value)
+ value[v] = value[v].trim();
+ value = value.join('|');
+
+ let array = this._dtdSettings.get_strv('window-stealing') || [];
+
+ if (value.length) {
+ value = this._app.id + '|' + value;
+
+ // Change
+ let found = false;
+ for (let a in array) {
+ let entry = array[a].split('|', 2);
+ if (entry.length == 2) {
+ if (this._app.id == entry[0]) {
+ array[a] = value;
+ found = true;
+ global.log('Changing window stealing: ' + value);
+ break;
+ }
+ }
+ }
+
+ // Add
+ if (!found) {
+ array.push(value);
+ global.log('Adding window stealing: ' + value);
+ }
+ }
+ else {
+ // Remove
+ for (let a in array) {
+ let entry = array[a].split('|', 2);
+ if (entry.length == 2) {
+ if (this._app.id == entry[0]) {
+ array.splice(a, 1);
+ global.log('Removing window stealing: ' + this._app.id);
+ break;
+ }
+ }
+ }
+ }
+
+ this._dtdSettings.set_strv('window-stealing', array);
+ Gio.Settings.sync();
+
+ this.close();
+ }
+});