diff --git a/Settings.ui b/Settings.ui
index b2bdcb7..3c21cb2 100644
--- a/Settings.ui
+++ b/Settings.ui
@@ -14,6 +14,48 @@
0.01
0.10000000000000001
+
True
False
@@ -575,182 +617,6 @@
-
- True
- False
- vertical
-
-
- True
- False
- none
-
-
- True
- True
-
-
- True
- False
- 12
- 12
- 12
- 12
- 32
-
-
- True
- False
- True
- Show Desktop button width (px)
- 0
-
-
- 0
- 0
-
-
-
-
- True
- True
- 4
- 1
- show_showdesktop_width_adjustment
- True
- 1
-
-
- 1
- 0
-
-
-
-
-
-
-
-
- True
- True
-
-
- True
- False
- 12
- 12
- 12
- 12
- 12
- 32
-
-
- True
- False
- True
- Reveal the desktop when hovering the Show Desktop button
- True
- 0
-
-
- 0
- 0
-
-
-
-
- True
- True
- end
- center
-
-
- 1
- 0
-
-
-
-
- True
- False
- 4
-
-
- True
- False
- True
- Delay before revealing the desktop (ms)
- True
- 0
-
-
- 0
- 0
-
-
-
-
- True
- True
- 4
- 1
- show_showdesktop_delay_adjustment
- True
- 1
-
-
- 1
- 0
-
-
-
-
- True
- False
- True
- Fade duration (ms)
- True
- 0
-
-
- 0
- 1
-
-
-
-
- True
- True
- 4
- 1
- show_showdesktop_time_adjustment
- True
- 1
-
-
- 1
- 1
-
-
-
-
- 0
- 1
- 2
-
-
-
-
-
-
-
-
- False
- True
- 2
-
-
-
10
1
@@ -1458,6 +1324,7 @@
True
True
+ end
4
0
group_apps_label_font_size_adjustment
@@ -1607,6 +1474,7 @@
True
True
+ end
4
0
group_apps_label_max_width_adjustment
@@ -1966,6 +1834,7 @@
True
True
+ end
4
0
intellihide_pressure_threshold_adjustment
@@ -1994,6 +1863,7 @@
True
True
+ end
4
0
intellihide_pressure_time_adjustment
@@ -2194,6 +2064,7 @@
True
True
+ end
4
0
intellihide_animation_time_adjustment
@@ -2239,6 +2110,7 @@
True
True
+ end
4
10
intellihide_close_delay_adjustment
@@ -2269,96 +2141,6 @@
25
100
-
- True
- False
- vertical
-
-
- True
- False
- 0
- in
-
-
- True
- False
- none
-
-
- 100
- 80
- True
- True
-
-
- True
- False
- 12
- 12
- 12
- 12
- 32
-
-
- True
- False
- True
- Preview timeout on icon leave (ms)
- 0
-
-
- 0
- 0
-
-
-
-
- True
- False
- True
- If set too low, the window preview of running applications may seem to close too quickly when trying to enter the popup. If set too high, the preview may linger too long when moving to an adjacent icon.
- True
- 40
- 0
-
-
-
- 0
- 1
- 2
-
-
-
-
- True
- True
- 4
- 0
- leave_timeout_adjustment
- True
-
-
- 1
- 0
-
-
-
-
-
-
-
-
-
-
- False
- True
- 0
-
-
-
0.33000000000000002
1
@@ -2382,60 +2164,82 @@
10
25
+
+ 1000
+ 10
+ 50
+
50
500
10
50
+
+ 100
+ 5
+ 10
+
50
1
5
+
+ 100
+ 800
+ 10
+ 50
+
9999
25
100
-
- 50
- 500
- 10
- 50
+
+ 6
+ 24
+ 1
+ 100
-
+
True
- False
- vertical
+ True
+ in
+ 460
+ 480
-
+
True
False
- 0
- in
+ True
+ True
+ natural
+ natural
True
False
none
-
+
True
True
-
+
True
False
12
12
12
12
+ 12
32
True
True
+ end
4
0
preview_timeout_adjustment
@@ -2460,16 +2264,692 @@
0
+
+
+ True
+ False
+ True
+ Time (ms) before hiding (100 is default)
+ 0
+
+
+ 0
+ 1
+
+
+
+
+ True
+ True
+ end
+ 4
+ 25
+ leave_timeout_adjustment
+ True
+ 25
+
+
+ 1
+ 1
+
+
+
+
+ True
+ False
+ True
+ Animation time (ms)
+ 0
+
+
+ 0
+ 2
+
+
+
+
+ True
+ True
+ end
+ 4
+ 0
+ preview_animation_time_adjustment
+ True
+
+
+ 1
+ 2
+
+
+
+
+
+
+
+
+ True
+ True
+
+
+ True
+ False
+ 12
+ 12
+ 12
+ 12
+ 32
+
+
+ True
+ False
+ True
+ Middle click on the preview to close the window
+ True
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ end
+ center
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+
+ True
+ True
+
+
+ True
+ False
+ 12
+ 12
+ 12
+ 12
+ 12
+ 32
+
+
+ True
+ False
+ True
+ Window previews preferred size (px)
+ True
+ 0
+
+
+ 0
+ 0
+ 3
+
+
+
+
+ True
+ True
+ end
+ 4
+ 100
+ preview_size_adjustment
+ True
+ 100
+
+
+ 2
+ 0
+
+
+
+
+ True
+ False
+ True
+ Window previews aspect ratio Y (height)
+ True
+ 0
+
+
+ 0
+ 2
+ 2
+
+
+
+
+ True
+ False
+ True
+ Window previews padding (px)
+ True
+ 0
+
+
+ 0
+ 3
+ 2
+
+
+
+
+ True
+ True
+ end
+ 4
+ 50
+ preview_padding_adjustment
+ True
+ 50
+
+
+ 2
+ 3
+
+
+
+
+ True
+ False
+ end
+ 2
+ True
+
+
+ True
+ False
+ end
+ center
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+ - 11
+ - 12
+ - 13
+ - 14
+ - 15
+ - 16
+ - 17
+ - 18
+ - 19
+ - 20
+ - 21
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ Fixed
+ True
+ True
+ True
+ end
+
+
+ False
+ True
+ 2
+
+
+
+
+ 2
+ 1
+
+
+
+
+ True
+ False
+ True
+ Window previews aspect ratio X (width)
+ True
+ 0
+
+
+ 0
+ 1
+ 2
+
+
+
+
+ True
+ False
+ end
+ 2
+ True
+
+
+ True
+ False
+ end
+ center
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ - 10
+ - 11
+ - 12
+ - 13
+ - 14
+ - 15
+ - 16
+ - 17
+ - 18
+ - 19
+ - 20
+ - 21
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ Fixed
+ True
+ True
+ True
+ end
+
+
+ False
+ True
+ 1
+
+
+
+
+ 2
+ 2
+
+
+
+
+
+
+
+
+ True
+ True
+
+
+ True
+ False
+ 12
+ 12
+ 12
+ 12
+ 32
+
+
+ True
+ False
+ True
+ Use custom opacity for the previews background
+ True
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ True
+ False
+ True
+ If disabled, the previews background have the same opacity as the panel
+ True
+ 40
+ 0
+
+
+
+ 0
+ 1
+ 2
+
+
+
+
+ True
+ False
+ 8
+
+
+ True
+ True
+ end
+ center
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ True
+ end
+ center
+ 5
+ preview_opacity_adjustment
+ 5
+
+
+ False
+ True
+ 2
+
+
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+
+ True
+ True
+
+
+ True
+ False
+ 12
+ 12
+ 12
+ 12
+ 32
+
+
+ True
+ False
+ True
+ Close button and header position
+ 0
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ end
+ 32
+
+
+ Bottom
+ True
+ True
+ False
+ center
+ center
+ True
+ True
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ Top
+ True
+ True
+ False
+ center
+ center
+ bottom
+ True
+ preview_title_position_bottom_button
+
+
+
+ False
+ True
+ 2
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+
+
+
+
+ True
+ True
+
+
+ True
+ False
+ 12
+ 12
+ 12
+ 12
+ 32
+
+
+ True
+ False
+ True
+ Display window preview headers
+ True
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ end
+ center
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ 12
+ 32
+
+
+ True
+ True
+ end
+ 4
+ 6
+ preview_title_font_size_adjustment
+ True
+ 6
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ True
+ Font size (px) of the preview titles
+ True
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 1
+ 2
+
+
+
+
+ True
+ False
+ 12
+ 32
+
+
+ True
+ False
+ True
+ Font weight of the preview titles
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ True
+ False
+ end
+ center
+
+ - inherit from theme
+ - normal
+ - lighter
+ - bold
+ - bolder
+
+
+
+ 1
+ 0
+
+
+
+
+ 0
+ 2
+ 2
+
+
+
+
+ True
+ False
+ 12
+ 32
+
+
+ True
+ False
+ True
+ Font color of the preview titles
+ True
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ True
+ end
+
+
+ 1
+ 0
+
+
+
+
+ 0
+ 3
+ 2
+
+
- 100
- 80
True
True
+ 6
True
@@ -2524,381 +3004,127 @@
2
-
-
-
-
-
-
- 100
- 80
- True
- True
-
-
- True
- False
- 24
- 12
- 12
- 12
- 32
-
+
True
False
- True
- Enter window peeking mode timeout (ms)
- 0
+ 12
+ 32
+
+
+ True
+ False
+ True
+ Enter window peeking mode timeout (ms)
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ end
+ 4
+ 50
+ enter_peek_mode_timeout_adjustment
+ True
+ 50
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ True
+ Time of inactivity while hovering over a window preview needed to enter the window peeking mode.
+ True
+ 40
+ 0
+
+
+
+ 0
+ 1
+ 2
+
+
0
- 0
-
-
-
-
- True
- False
- True
- Time of inactivity while hovering over a window preview needed to enter the window peeking mode.
- True
- 40
- 0
-
-
-
- 0
- 1
+ 2
2
-
- True
- True
- 4
- enter_peek_mode_timeout_adjustment
- True
-
-
- 1
- 0
-
-
-
-
-
-
-
-
- 100
- 80
- True
- True
-
-
- True
- False
- 24
- 12
- 12
- 12
- 32
-
-
+
True
False
- True
- Window peeking mode opacity
- 0
+ 12
+ 32
+
+
+ True
+ False
+ True
+ Window peeking mode opacity
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ end
+ 4
+ 0
+ peek_mode_opacity_adjustment
+ True
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ True
+ All windows except for the peeked one have their opacity set to the same value.
+ True
+ 40
+ 0
+
+
+
+ 0
+ 1
+ 2
+
+
0
- 0
-
-
-
-
- True
- False
- True
- All windows except for the peeked one have their opacity set to the same value.
- True
- 40
- 0
-
-
-
- 0
- 1
+ 3
2
-
-
- True
- True
- 4
- peek_mode_opacity_adjustment
- True
-
-
- 1
- 0
-
-
-
-
-
-
-
-
- 100
- 80
- True
- True
-
-
- True
- False
- 12
- 12
- 12
- 12
- 32
-
-
- True
- False
- True
- Middle click to close window
- True
- 0
-
-
- 0
- 0
-
-
-
-
- True
- True
- end
- center
-
-
- 1
- 0
-
-
-
-
- True
- False
- True
- Middle click on the preview to close the window.
- True
- 40
- 0
-
-
-
- 0
- 1
- 2
-
-
-
-
-
-
-
-
- 100
- True
- True
-
-
- True
- False
- 12
- 12
- 12
- 12
- 32
-
-
- True
- False
- True
- Display window title in previews
- True
- 0
-
-
- 0
- 0
-
-
-
-
- True
- True
- end
- center
-
-
- 1
- 0
-
-
-
-
-
-
-
-
- 100
- True
- True
-
-
- True
- False
- 12
- 12
- 12
- 12
- 32
-
-
- True
- True
- 4
- 0
- preview_width_adjustment
- True
-
-
- 1
- 0
-
-
-
-
- True
- False
- True
- Width of the window previews (px)
- True
- 0
-
-
- 0
- 0
-
-
-
-
-
-
-
-
- 100
- True
- True
-
-
- True
- False
- 12
- 12
- 12
- 12
- 32
-
-
- True
- True
- 4
- 0
- preview_height_adjustment
- True
-
-
- 1
- 0
-
-
-
-
- True
- False
- True
- Height of the window previews (px)
- True
- 0
-
-
- 0
- 0
-
-
-
-
-
-
-
-
- 100
- True
- True
-
-
- True
- False
- 12
- 12
- 12
- 12
- 32
-
-
- True
- True
- 4
- 50
- preview_padding_adjustment
- True
- 50
-
-
- 1
- 0
-
-
-
-
- True
- False
- True
- Padding of the window previews (px)
- True
- 0
-
-
- 0
- 0
-
-
@@ -2906,11 +3132,6 @@
-
- False
- True
- 0
-
@@ -3399,6 +3620,185 @@
1
10
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ none
+
+
+ True
+ True
+
+
+ True
+ False
+ 12
+ 12
+ 12
+ 12
+ 32
+
+
+ True
+ False
+ True
+ Show Desktop button width (px)
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ end
+ 4
+ 1
+ show_showdesktop_width_adjustment
+ True
+ 1
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+
+ True
+ True
+
+
+ True
+ False
+ 12
+ 12
+ 12
+ 12
+ 12
+ 32
+
+
+ True
+ False
+ True
+ Reveal the desktop when hovering the Show Desktop button
+ True
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ end
+ center
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ 4
+
+
+ True
+ False
+ True
+ Delay before revealing the desktop (ms)
+ True
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ end
+ 4
+ 1
+ show_showdesktop_delay_adjustment
+ True
+ 1
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ True
+ Fade duration (ms)
+ True
+ 0
+
+
+ 0
+ 1
+
+
+
+
+ True
+ True
+ end
+ 4
+ 1
+ show_showdesktop_time_adjustment
+ True
+ 1
+
+
+ 1
+ 1
+
+
+
+
+ 0
+ 1
+ 2
+
+
+
+
+
+
+
+
+ False
+ True
+ 2
+
+
+
0.33000000000000002
1
@@ -3507,6 +3907,7 @@
True
True
+ end
4
50
trans_distance_adjustment
@@ -3567,6 +3968,7 @@
True
True
+ end
0
trans_opacity_min_adjustment
@@ -3598,6 +4000,7 @@
True
True
+ end
4
50
trans_anim_time_adjustment
diff --git a/appIcons.js b/appIcons.js
index b9d8e03..0865099 100644
--- a/appIcons.js
+++ b/appIcons.js
@@ -47,7 +47,6 @@ const Workspace = imports.ui.workspace;
const Me = imports.misc.extensionUtils.getCurrentExtension();
const Utils = Me.imports.utils;
-const WindowPreview = Me.imports.windowPreview;
const Taskbar = Me.imports.taskbar;
const _ = imports.gettext.domain(Utils.TRANSLATION_DOMAIN).gettext;
@@ -99,7 +98,7 @@ var taskbarAppIcon = Utils.defineClass({
Extends: AppDisplay.AppIcon,
ParentConstrParams: [[1, 'app'], [3]],
- _init: function(settings, appInfo, panelWrapper, iconParams) {
+ _init: function(settings, appInfo, panelWrapper, iconParams, previewMenu) {
// a prefix is required to avoid conflicting with the parent class variable
this._dtpSettings = settings;
@@ -107,6 +106,7 @@ var taskbarAppIcon = Utils.defineClass({
this._nWindows = 0;
this.window = appInfo.window;
this.isLauncher = appInfo.isLauncher;
+ this._previewMenu = previewMenu;
// Fix touchscreen issues before the listener is added by the parent constructor.
this._onTouchEvent = function(actor, event) {
@@ -183,11 +183,9 @@ var taskbarAppIcon = Utils.defineClass({
Lang.bind(this, this._onFocusAppChanged));
this._windowEnteredMonitorId = this._windowLeftMonitorId = 0;
+ this._stateChangedId = this.app.connect('windows-changed', Lang.bind(this, this.onWindowsChanged));
if (!this.window) {
- this._stateChangedId = this.app.connect('windows-changed',
- Lang.bind(this, this.onWindowsChanged));
-
if (this._dtpSettings.get_boolean('isolate-monitors')) {
this._windowEnteredMonitorId = Utils.DisplayWrapper.getScreen().connect('window-entered-monitor', this.onWindowEnteredOrLeft.bind(this));
this._windowLeftMonitorId = Utils.DisplayWrapper.getScreen().connect('window-left-monitor', this.onWindowEnteredOrLeft.bind(this));
@@ -204,6 +202,8 @@ var taskbarAppIcon = Utils.defineClass({
this._switchWorkspaceId = global.window_manager.connect('switch-workspace',
Lang.bind(this, this._onSwitchWorkspace));
+
+ this._hoverChangeId = this.actor.connect('notify::hover', () => this._onAppIconHoverChanged());
this._dtpSettingsSignalIds = [
this._dtpSettings.connect('changed::dot-position', Lang.bind(this, this._settingsChangeRefresh)),
@@ -236,93 +236,6 @@ var taskbarAppIcon = Utils.defineClass({
this.forcedOverview = false;
this._numberOverlay();
-
- this._signalsHandler = new Utils.GlobalSignalsHandler();
- },
-
- _createWindowPreview: function() {
- // Abort if already activated
- if (this.menuManagerWindowPreview)
- return;
-
- // Creating a new menu manager for window previews as adding it to the
- // using the secondary menu's menu manager (which uses the "ignoreRelease"
- // function) caused the extension to crash.
- this.menuManagerWindowPreview = new PopupMenu.PopupMenuManager(this);
-
- this.windowPreview = new WindowPreview.thumbnailPreviewMenu(this, this._dtpSettings, this.menuManagerWindowPreview);
-
- this.windowPreview.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) {
- if (!isPoppedUp)
- this._onMenuPoppedDown();
- }));
- this.menuManagerWindowPreview.addMenu(this.windowPreview);
-
- // grabHelper.grab() is usually called when the menu is opened. However, there seems to be a bug in the
- // underlying gnome-shell that causes all window contents to freeze if the grab and ungrab occur
- // in quick succession in timeouts from the Mainloop (for example, clicking the icon as the preview window is opening)
- // So, instead wait until the mouse is leaving the icon (and might be moving toward the open window) to trigger the grab
- // in windowPreview.js
- let windowPreviewMenuData = this.menuManagerWindowPreview._menus[this.menuManagerWindowPreview._findMenu(this.windowPreview)];
- this.windowPreview.disconnect(windowPreviewMenuData.openStateChangeId);
- windowPreviewMenuData.openStateChangeId = this.windowPreview.connect('open-state-changed', Lang.bind(this.menuManagerWindowPreview, function(menu, open) {
- if (open) {
- if (this.activeMenu)
- this.activeMenu.close(BoxPointer.PopupAnimation.FADE);
-
- // don't grab here, we are grabbing in onLeave in windowPreview.js
- //this._grabHelper.grab({ actor: menu.actor, focus: menu.sourceActor, onUngrab: Lang.bind(this, this._closeMenu, menu) });
- } else {
- this._grabHelper.ungrab({ actor: menu.actor });
- }
- }));
- },
-
- enableWindowPreview: function(appIcons) {
- this._createWindowPreview();
-
- // We first remove to ensure there are no duplicates
- this._signalsHandler.removeWithLabel('window-preview');
- this._signalsHandler.addWithLabel('window-preview', [
- this.windowPreview,
- 'menu-closed',
- // enter-event doesn't fire on an app icon when the popup menu from a previously
- // hovered app icon is still open, so when a preview menu closes we need to
- // see if a new app icon is hovered and open its preview menu now.
- // also, for some reason actor doesn't report being hovered by get_hover()
- // if the hover started when a popup was opened. So, look for the actor by mouse position.
- menu => this.syncWindowPreview(appIcons, menu)
- ]);
-
- this.windowPreview.enableWindowPreview();
- },
-
- syncWindowPreview: function(appIcons, menu) {
- let [x, y,] = global.get_pointer();
- let hoveredActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
- let appIconToOpen;
-
- appIcons.forEach(function (appIcon) {
- if(appIcon.actor == hoveredActor) {
- appIconToOpen = appIcon;
- } else if(appIcon.windowPreview && appIcon.windowPreview.isOpen) {
- appIcon.windowPreview.close();
- }
- });
-
- if(appIconToOpen) {
- appIconToOpen.actor.sync_hover();
- if(appIconToOpen.windowPreview && appIconToOpen.windowPreview != menu)
- appIconToOpen.windowPreview._onEnter();
- }
-
- return GLib.SOURCE_REMOVE;
- },
-
- disableWindowPreview: function() {
- this._signalsHandler.removeWithLabel('window-preview');
- if (this.windowPreview)
- this.windowPreview.disableWindowPreview();
},
shouldShowTooltip: function() {
@@ -333,7 +246,20 @@ var taskbarAppIcon = Utils.defineClass({
} else {
return this.actor.hover && !this.window &&
(!this._menu || !this._menu.isOpen) &&
- (!this.windowPreview || !this.windowPreview.isOpen);
+ (this._previewMenu.getCurrentAppIcon() !== this);
+ }
+ },
+
+ _onAppIconHoverChanged: function() {
+ if (!this._dtpSettings.get_boolean('show-window-previews') ||
+ (!this.window && !this._nWindows)) {
+ return;
+ }
+
+ if (this.actor.hover) {
+ this._previewMenu.requestOpen(this);
+ } else {
+ this._previewMenu.requestClose();
}
},
@@ -341,6 +267,8 @@ var taskbarAppIcon = Utils.defineClass({
this.callParent('_onDestroy');
this._destroyed = true;
+ this._previewMenu.close(true);
+
// Disconect global signals
// stateChangedId is already handled by parent)
@@ -364,19 +292,23 @@ var taskbarAppIcon = Utils.defineClass({
if(this._scaleFactorChangedId)
St.ThemeContext.get_for_stage(global.stage).disconnect(this._scaleFactorChangedId);
+ if (this._hoverChangeId) {
+ this.actor.disconnect(this._hoverChangeId);
+ }
+
for (let i = 0; i < this._dtpSettingsSignalIds.length; ++i) {
this._dtpSettings.disconnect(this._dtpSettingsSignalIds[i]);
}
},
onWindowsChanged: function() {
- this._updateCounterClass();
+ this._updateWindows();
this.updateIcon();
},
onWindowEnteredOrLeft: function() {
if (this._checkIfFocusedApp()) {
- this._updateCounterClass();
+ this._updateWindows();
this._displayProperIndicator();
}
},
@@ -405,7 +337,7 @@ var taskbarAppIcon = Utils.defineClass({
_showDots: function() {
// Just update style if dots already exist
if (this._focusedDots && this._unfocusedDots) {
- this._updateCounterClass();
+ this._updateWindows();
return;
}
@@ -449,7 +381,7 @@ var taskbarAppIcon = Utils.defineClass({
this._dotsContainer.add_child(this._unfocusedDots);
- this._updateCounterClass();
+ this._updateWindows();
}
this._dotsContainer.add_child(this._focusedDots);
@@ -465,7 +397,7 @@ var taskbarAppIcon = Utils.defineClass({
_settingsChangeRefresh: function() {
if (this._isGroupApps) {
- this._updateCounterClass();
+ this._updateWindows();
this._focusedDots.queue_repaint();
this._unfocusedDots.queue_repaint();
}
@@ -588,8 +520,7 @@ var taskbarAppIcon = Utils.defineClass({
this.emit('menu-state-changed', true);
- if (this.windowPreview)
- this.windowPreview.close();
+ this._previewMenu.close(true);
this.actor.set_hover(true);
this._menu.actor.add_style_class_name('dashtopanelSecondaryMenu');
@@ -612,10 +543,7 @@ var taskbarAppIcon = Utils.defineClass({
},
_onSwitchWorkspace: function(windowTracker) {
- Mainloop.timeout_add(0, Lang.bind(this, function () {
- this._displayProperIndicator();
- return GLib.SOURCE_REMOVE;
- }));
+ this._displayProperIndicator();
},
_displayProperIndicator: function (force) {
@@ -774,8 +702,8 @@ var taskbarAppIcon = Utils.defineClass({
}
let appCount = this.getAppIconInterestingWindows().length;
- if (this.windowPreview && (!(buttonAction == "TOGGLE-SHOWPREVIEW") || (appCount <= 1)))
- this.windowPreview.requestCloseMenu();
+ if (!(buttonAction == "TOGGLE-SHOWPREVIEW") || (appCount <= 1))
+ this._previewMenu.close(true);
// We check if the app is running, and that the # of windows is > 0 in
// case we use workspace isolation,
@@ -922,16 +850,24 @@ var taskbarAppIcon = Utils.defineClass({
}
},
- _updateCounterClass: function() {
- this._nWindows = this.getAppIconInterestingWindows().length;
-
- for (let i = 1; i <= MAX_INDICATORS; i++){
- let className = 'running'+i;
- if(i != this._nWindows)
- this.actor.remove_style_class_name(className);
- else
- this.actor.add_style_class_name(className);
+ _updateWindows: function() {
+ let windows = [this.window];
+
+ if (!this.window) {
+ windows = this.getAppIconInterestingWindows();
+
+ this._nWindows = windows.length;
+
+ for (let i = 1; i <= MAX_INDICATORS; i++){
+ let className = 'running'+i;
+ if(i != this._nWindows)
+ this.actor.remove_style_class_name(className);
+ else
+ this.actor.add_style_class_name(className);
+ }
}
+
+ this._previewMenu.update(this, windows);
},
_getRunningIndicatorCount: function() {
@@ -1149,7 +1085,7 @@ var taskbarAppIcon = Utils.defineClass({
handleDragOver: function(source, actor, x, y, time) {
if (source == Main.xdndHandler) {
- this.windowPreview.close();
+ this._previewMenu.close(true);
}
return DND.DragMotionResult.CONTINUE;
diff --git a/intellihide.js b/intellihide.js
index 4690500..e725715 100644
--- a/intellihide.js
+++ b/intellihide.js
@@ -187,10 +187,12 @@ var Intellihide = Utils.defineClass({
[
this._panelBox,
'notify::hover',
- () => {
- this._hoveredOut = !this._panelBox.hover;
- this._queueUpdatePanelPosition();
- }
+ () => this._onHoverChanged()
+ ],
+ [
+ this._dtpPanel.taskbar.previewMenu,
+ 'open-state-changed',
+ () => this._queueUpdatePanelPosition()
],
[
Main.overview,
@@ -203,6 +205,11 @@ var Intellihide = Utils.defineClass({
);
},
+ _onHoverChanged: function() {
+ this._hoveredOut = !this._panelBox.hover;
+ this._queueUpdatePanelPosition();
+ },
+
_setTrackPanel: function(reset, enable) {
if (!reset) {
Main.layoutManager._untrackActor(this._panelBox);
@@ -291,7 +298,8 @@ var Intellihide = Utils.defineClass({
},
_checkIfShouldBeVisible: function(fromRevealMechanism) {
- if (Main.overview.visibleTarget || this._checkIfGrab() || this._panelBox.get_hover()) {
+ if (Main.overview.visibleTarget || this._dtpPanel.taskbar.previewMenu.opened ||
+ this._panelBox.get_hover() || this._checkIfGrab()) {
return true;
}
diff --git a/overview.js b/overview.js
index 5255acc..384fc25 100644
--- a/overview.js
+++ b/overview.js
@@ -42,6 +42,7 @@ var dtpOverview = Utils.defineClass({
_init: function(settings) {
this._numHotkeys = 10;
+ this._currentHotkeyFocusIndex = -1;
this._dtpSettings = settings;
},
@@ -179,56 +180,34 @@ var dtpOverview = Utils.defineClass({
if (appIndex < apps.length) {
let appIcon = apps[appIndex];
- let windowCount = !appIcon.window ? appIcon._nWindows : seenApps[appIcon.app];
+ let seenAppCount = seenApps[appIcon.app];
+ let windowCount = appIcon.window || appIcon._hotkeysCycle ? seenAppCount : appIcon._nWindows;
if (this._dtpSettings.get_boolean('shortcut-previews') && windowCount > 1) {
- let windowIndex = 0;
- let currentWindow = appIcon.window;
- let hotkeyPrefix = this._dtpSettings.get_string('hotkey-prefix-text');
- let shortcutKeys = (hotkeyPrefix == 'Super' ? [Clutter.Super_L] : [Clutter.Super_L, Clutter.Alt_L]).concat([Clutter['KEY_' + (appIndex + 1)]]);
- let currentKeys = shortcutKeys.slice();
- let setFocus = windowIndex => global.stage.set_key_focus(appIcon.windowPreview._previewBox.box.get_children()[windowIndex]);
- let capturedEventId = global.stage.connect('captured-event', (actor, event) => {
- if (event.type() == Clutter.EventType.KEY_PRESS) {
- let pressedKey = event.get_key_symbol();
+ if (this._currentHotkeyFocusIndex < 0) {
+ let currentWindow = appIcon.window;
+ let keyFocusOutId = appIcon.actor.connect('key-focus-out', () => appIcon.actor.grab_key_focus());
+ let capturedEventId = global.stage.connect('captured-event', (actor, e) => {
+ if (e.type() == Clutter.EventType.KEY_RELEASE && e.get_key_symbol() == Clutter.Super_L) {
+ global.stage.disconnect(capturedEventId);
+ appIcon.actor.disconnect(keyFocusOutId);
- if (shortcutKeys.indexOf(pressedKey) >= 0 && currentKeys.indexOf(pressedKey) < 0) {
- currentKeys.push(pressedKey);
-
- if (shortcutKeys.length === currentKeys.length) {
- windowIndex = windowIndex < windowCount - 1 ? windowIndex + 1 : 0;
- setFocus(windowIndex);
- }
- }
- } else if (event.type() == Clutter.EventType.KEY_RELEASE) {
- let releasedKey = event.get_key_symbol();
- let keyIndex = currentKeys.indexOf(releasedKey);
-
- if (releasedKey == Clutter.Super_L) {
- let thumbnails = appIcon.windowPreview._previewBox.box.get_children();
- let focusedThumbnail = thumbnails.filter(c => c.has_key_focus())[0];
-
- focusedThumbnail._delegate.activate();
- } else if (keyIndex >= 0) {
- currentKeys.splice(keyIndex, 1);
+ appIcon._previewMenu.activateFocused();
+ appIcon.window = currentWindow;
+ delete appIcon._hotkeysCycle;
+ this._currentHotkeyFocusIndex = -1;
}
- }
+
+ return Clutter.EVENT_PROPAGATE;
+ });
- return Clutter.EVENT_PROPAGATE;
- });
- let hotkeyOpenStateChangedId = appIcon.windowPreview.connect('open-state-changed', (menu, isOpen) => {
- if (!isOpen) {
- global.stage.disconnect(capturedEventId);
- appIcon.windowPreview.disconnect(hotkeyOpenStateChangedId);
- appIcon.windowPreview._previewBox._resetPreviews();
- appIcon.window = currentWindow;
- }
- });
-
- appIcon.window = null;
- appIcon.windowPreview.popup();
- appIcon.menuManagerWindowPreview._onMenuOpenState(appIcon.windowPreview, true);
- setFocus(windowIndex);
+ appIcon._hotkeysCycle = appIcon.window;
+ appIcon.window = null;
+ appIcon._previewMenu.open(appIcon);
+ appIcon.actor.grab_key_focus();
+ }
+
+ this._currentHotkeyFocusIndex = appIcon._previewMenu.focusNext();
} else {
// Activate with button = 1, i.e. same as left click
let button = 1;
diff --git a/panel.js b/panel.js
index 20dc1f8..1c28cae 100644
--- a/panel.js
+++ b/panel.js
@@ -161,6 +161,7 @@ var dtpPanelWrapper = Utils.defineClass({
//the timeout makes sure the theme's styles are computed before initially applying the transparency
this.startDynamicTransparencyId = Mainloop.timeout_add(0, () => {
+ this.startDynamicTransparencyId = 0;
this.dynamicTransparency = new Transparency.DynamicTransparency(this);
});
@@ -269,7 +270,6 @@ var dtpPanelWrapper = Utils.defineClass({
this._setAppmenuVisible(false);
if(this.appMenu)
this.panel._leftBox.add_child(this.appMenu.container);
- this.taskbar.destroy();
if (this.startIntellihideId) {
Mainloop.source_remove(this.startIntellihideId);
@@ -290,6 +290,8 @@ var dtpPanelWrapper = Utils.defineClass({
this.dynamicTransparency.destroy();
}
+ this.taskbar.destroy();
+
// reset stored icon size to the default dash
Main.overview.dashIconSize = Main.overview._controls.dash.iconSize;
diff --git a/panelManager.js b/panelManager.js
index 8742e5b..5ad296c 100755
--- a/panelManager.js
+++ b/panelManager.js
@@ -283,7 +283,7 @@ var dtpPanelManager = Utils.defineClass({
},
_getBoxPointerPreferredHeight: function(boxPointer, alloc, monitor) {
- if (boxPointer._dtpInPanel && this._dtpSettings.get_boolean('intellihide')) {
+ if (boxPointer._dtpInPanel && boxPointer.sourceActor && this._dtpSettings.get_boolean('intellihide')) {
monitor = monitor || Main.layoutManager.findMonitorForActor(boxPointer.sourceActor);
let excess = alloc.natural_size + this._dtpSettings.get_int('panel-size') + 10 - monitor.height; // 10 is arbitrary
@@ -387,7 +387,7 @@ var dtpPanelManager = Utils.defineClass({
},
_newGetShowAppsButton: function() {
- let focusedMonitorIndex = Taskbar.findIndex(this.allPanels, p => this.checkIfFocusedMonitor(p.monitor));
+ let focusedMonitorIndex = Utils.findIndex(this.allPanels, p => this.checkIfFocusedMonitor(p.monitor));
return this.allPanels[focusedMonitorIndex].taskbar.showAppsButton;
},
diff --git a/prefs.js b/prefs.js
index 3260aae..b7db252 100644
--- a/prefs.js
+++ b/prefs.js
@@ -990,6 +990,22 @@ const Settings = new Lang.Class({
'active',
Gio.SettingsBindFlags.DEFAULT);
+ switch (this._settings.get_string('window-preview-title-position')) {
+ case 'BOTTOM':
+ this._builder.get_object('preview_title_position_bottom_button').set_active(true);
+ break;
+ case 'TOP':
+ this._builder.get_object('preview_title_position_top_button').set_active(true);
+ break;
+ }
+
+ this._builder.get_object('grid_preview_title_font_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('window-preview-title-font-color', hexString);
+ }));
+
this._builder.get_object('show_window_previews_button').connect('clicked', Lang.bind(this, function() {
let dialog = new Gtk.Dialog({ title: _('Window preview options'),
@@ -1001,55 +1017,109 @@ const Settings = new Lang.Class({
// Use +1 for the reset action
dialog.add_button(_('Reset to defaults'), 1);
- let box = this._builder.get_object('box_window_preview_options');
- dialog.get_content_area().add(box);
+ let scrolledWindow = this._builder.get_object('box_window_preview_options');
+
+ adjustScrollableHeight(this._builder.get_object('viewport_window_preview_options'), scrolledWindow);
+
+ dialog.get_content_area().add(scrolledWindow);
this._builder.get_object('preview_timeout_spinbutton').set_value(this._settings.get_int('show-window-previews-timeout'));
this._builder.get_object('preview_timeout_spinbutton').connect('value-changed', Lang.bind (this, function(widget) {
this._settings.set_int('show-window-previews-timeout', widget.get_value());
}));
- this._settings.bind('peek-mode',
- this._builder.get_object('peek_mode_switch'),
- 'active',
- Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('window-preview-show-title',
- this._builder.get_object('preview_show_title_switch'),
- 'active',
- Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('peek-mode',
- this._builder.get_object('listboxrow_enter_peek_mode_timeout'),
- 'sensitive',
- Gio.SettingsBindFlags.DEFAULT);
- this._settings.bind('peek-mode',
- this._builder.get_object('listboxrow_peek_mode_opacity'),
- 'sensitive',
- Gio.SettingsBindFlags.DEFAULT);
this._settings.bind('preview-middle-click-close',
this._builder.get_object('preview_middle_click_close_switch'),
'active',
Gio.SettingsBindFlags.DEFAULT);
- this._builder.get_object('enter_peek_mode_timeout_spinbutton').set_value(this._settings.get_int('enter-peek-mode-timeout'));
+ this._settings.bind('window-preview-fixed-x',
+ this._builder.get_object('preview_aspect_ratio_x_fixed_togglebutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('window-preview-fixed-y',
+ this._builder.get_object('preview_aspect_ratio_y_fixed_togglebutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._settings.bind('preview-use-custom-opacity',
+ this._builder.get_object('preview_custom_opacity_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('preview-use-custom-opacity',
+ this._builder.get_object('preview_custom_opacity_spinbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._builder.get_object('preview_custom_opacity_spinbutton').set_value(this._settings.get_int('preview-custom-opacity'));
+ this._builder.get_object('preview_custom_opacity_spinbutton').connect('value-changed', Lang.bind (this, function(widget) {
+ this._settings.set_int('preview-custom-opacity', widget.get_value());
+ }));
+
+ this._settings.bind('peek-mode',
+ this._builder.get_object('peek_mode_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('peek-mode',
+ this._builder.get_object('grid_enter_peek_mode_timeout'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('peek-mode',
+ this._builder.get_object('grid_peek_mode_opacity'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._settings.bind('window-preview-show-title',
+ this._builder.get_object('preview_show_title_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('window-preview-show-title',
+ this._builder.get_object('grid_preview_title_size'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('window-preview-show-title',
+ this._builder.get_object('grid_preview_title_weight'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('window-preview-show-title',
+ this._builder.get_object('grid_preview_title_font_color'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._builder.get_object('enter_peek_mode_timeout_spinbutton').set_value(this._settings.get_int('enter-peek-mode-timeout'));
this._builder.get_object('enter_peek_mode_timeout_spinbutton').connect('value-changed', Lang.bind (this, function(widget) {
this._settings.set_int('enter-peek-mode-timeout', widget.get_value());
}));
- this._builder.get_object('peek_mode_opacity_spinbutton').set_value(this._settings.get_int('peek-mode-opacity'));
+ this._builder.get_object('leave_timeout_spinbutton').set_value(this._settings.get_int('leave-timeout'));
+ this._builder.get_object('leave_timeout_spinbutton').connect('value-changed', Lang.bind (this, function(widget) {
+ this._settings.set_int('leave-timeout', widget.get_value());
+ }));
+ this._builder.get_object('animation_time_spinbutton').set_value(this._settings.get_int('window-preview-animation-time'));
+ this._builder.get_object('animation_time_spinbutton').connect('value-changed', Lang.bind (this, function(widget) {
+ this._settings.set_int('window-preview-animation-time', widget.get_value());
+ }));
+
+ this._builder.get_object('peek_mode_opacity_spinbutton').set_value(this._settings.get_int('peek-mode-opacity'));
this._builder.get_object('peek_mode_opacity_spinbutton').connect('value-changed', Lang.bind (this, function(widget) {
this._settings.set_int('peek-mode-opacity', widget.get_value());
}));
- this._builder.get_object('preview_width_spinbutton').set_value(this._settings.get_int('window-preview-width'));
- this._builder.get_object('preview_width_spinbutton').connect('value-changed', Lang.bind (this, function(widget) {
- this._settings.set_int('window-preview-width', widget.get_value());
+ this._builder.get_object('preview_size_spinbutton').set_value(this._settings.get_int('window-preview-size'));
+ this._builder.get_object('preview_size_spinbutton').connect('value-changed', Lang.bind (this, function(widget) {
+ this._settings.set_int('window-preview-size', widget.get_value());
}));
- this._builder.get_object('preview_height_spinbutton').set_value(this._settings.get_int('window-preview-height'));
- this._builder.get_object('preview_height_spinbutton').connect('value-changed', Lang.bind (this, function(widget) {
- this._settings.set_int('window-preview-height', widget.get_value());
+ this._builder.get_object('preview_aspect_ratio_x_combo').set_active_id(this._settings.get_int('window-preview-aspect-ratio-x').toString());
+ this._builder.get_object('preview_aspect_ratio_x_combo').connect('changed', Lang.bind (this, function(widget) {
+ this._settings.set_int('window-preview-aspect-ratio-x', parseInt(widget.get_active_id(), 10));
+ }));
+
+ this._builder.get_object('preview_aspect_ratio_y_combo').set_active_id(this._settings.get_int('window-preview-aspect-ratio-y').toString());
+ this._builder.get_object('preview_aspect_ratio_y_combo').connect('changed', Lang.bind (this, function(widget) {
+ this._settings.set_int('window-preview-aspect-ratio-y', parseInt(widget.get_active_id(), 10));
}));
this._builder.get_object('preview_padding_spinbutton').set_value(this._settings.get_int('window-preview-padding'));
@@ -1057,12 +1127,39 @@ const Settings = new Lang.Class({
this._settings.set_int('window-preview-padding', widget.get_value());
}));
+ this._builder.get_object('preview_title_size_spinbutton').set_value(this._settings.get_int('window-preview-title-font-size'));
+ this._builder.get_object('preview_title_size_spinbutton').connect('value-changed', Lang.bind (this, function(widget) {
+ this._settings.set_int('window-preview-title-font-size', widget.get_value());
+ }));
+
+ this._builder.get_object('grid_preview_title_weight_combo').set_active_id(this._settings.get_string('window-preview-title-font-weight'));
+ this._builder.get_object('grid_preview_title_weight_combo').connect('changed', Lang.bind (this, function(widget) {
+ this._settings.set_string('window-preview-title-font-weight', widget.get_active_id());
+ }));
+
+ (function() {
+ let rgba = new Gdk.RGBA();
+ rgba.parse(this._settings.get_string('window-preview-title-font-color'));
+ this._builder.get_object('grid_preview_title_font_color_colorbutton').set_rgba(rgba);
+ }).apply(this);
+
dialog.connect('response', Lang.bind(this, function(dialog, id) {
if (id == 1) {
// restore default settings
this._settings.set_value('show-window-previews-timeout', this._settings.get_default_value('show-window-previews-timeout'));
this._builder.get_object('preview_timeout_spinbutton').set_value(this._settings.get_int('show-window-previews-timeout'));
+ this._settings.set_value('leave-timeout', this._settings.get_default_value('leave-timeout'));
+ this._builder.get_object('leave_timeout_spinbutton').set_value(this._settings.get_int('leave-timeout'));
+
+ this._settings.set_value('window-preview-animation-time', this._settings.get_default_value('window-preview-animation-time'));
+ this._builder.get_object('animation_time_spinbutton').set_value(this._settings.get_int('window-preview-animation-time'));
+
+ this._settings.set_value('preview-use-custom-opacity', this._settings.get_default_value('preview-use-custom-opacity'));
+
+ this._settings.set_value('preview-custom-opacity', this._settings.get_default_value('preview-custom-opacity'));
+ this._builder.get_object('preview_custom_opacity_spinbutton').set_value(this._settings.get_int('preview-custom-opacity'));
+
this._settings.set_value('peek-mode', this._settings.get_default_value('peek-mode'));
this._settings.set_value('window-preview-show-title', this._settings.get_default_value('window-preview-show-title'));
this._settings.set_value('enter-peek-mode-timeout', this._settings.get_default_value('enter-peek-mode-timeout'));
@@ -1070,20 +1167,37 @@ const Settings = new Lang.Class({
this._settings.set_value('peek-mode-opacity', this._settings.get_default_value('peek-mode-opacity'));
this._builder.get_object('peek_mode_opacity_spinbutton').set_value(this._settings.get_int('peek-mode-opacity'));
- this._settings.set_value('window-preview-width', this._settings.get_default_value('window-preview-width'));
- this._builder.get_object('preview_width_spinbutton').set_value(this._settings.get_int('window-preview-width'));
-
- this._settings.set_value('window-preview-height', this._settings.get_default_value('window-preview-height'));
- this._builder.get_object('preview_height_spinbutton').set_value(this._settings.get_int('window-preview-height'));
+ this._settings.set_value('window-preview-size', this._settings.get_default_value('window-preview-size'));
+ this._builder.get_object('preview_size_spinbutton').set_value(this._settings.get_int('window-preview-size'));
+ this._settings.set_value('window-preview-fixed-x', this._settings.get_default_value('window-preview-fixed-x'));
+ this._settings.set_value('window-preview-fixed-y', this._settings.get_default_value('window-preview-fixed-y'));
+
+ this._settings.set_value('window-preview-aspect-ratio-x', this._settings.get_default_value('window-preview-aspect-ratio-x'));
+ this._builder.get_object('preview_aspect_ratio_x_combo').set_active_id(this._settings.get_int('window-preview-aspect-ratio-x').toString());
+
+ this._settings.set_value('window-preview-aspect-ratio-y', this._settings.get_default_value('window-preview-aspect-ratio-y'));
+ this._builder.get_object('preview_aspect_ratio_y_combo').set_active_id(this._settings.get_int('window-preview-aspect-ratio-y').toString());
+
this._settings.set_value('window-preview-padding', this._settings.get_default_value('window-preview-padding'));
this._builder.get_object('preview_padding_spinbutton').set_value(this._settings.get_int('window-preview-padding'));
this._settings.set_value('preview-middle-click-close', this._settings.get_default_value('preview-middle-click-close'));
+ this._settings.set_value('window-preview-title-font-size', this._settings.get_default_value('window-preview-title-font-size'));
+ this._builder.get_object('preview_title_size_spinbutton').set_value(this._settings.get_int('window-preview-title-font-size'));
+
+ this._settings.set_value('window-preview-title-font-weight', this._settings.get_default_value('window-preview-title-font-weight'));
+ this._builder.get_object('grid_preview_title_weight_combo').set_active_id(this._settings.get_string('window-preview-title-font-weight'));
+
+ this._settings.set_value('window-preview-title-font-color', this._settings.get_default_value('window-preview-title-font-color'));
+ let rgba = new Gdk.RGBA();
+ rgba.parse(this._settings.get_string('window-preview-title-font-color'));
+ this._builder.get_object('grid_preview_title_font_color_colorbutton').set_rgba(rgba);
+
} else {
// remove the settings box so it doesn't get destroyed;
- dialog.get_content_area().remove(box);
+ dialog.get_content_area().remove(scrolledWindow);
dialog.destroy();
}
return;
@@ -1414,17 +1528,10 @@ const Settings = new Lang.Class({
let box = this._builder.get_object('box_advanced_options');
dialog.get_content_area().add(box);
- this._builder.get_object('leave_timeout_spinbutton').set_value(this._settings.get_int('leave-timeout'));
-
- this._builder.get_object('leave_timeout_spinbutton').connect('value-changed', Lang.bind (this, function(widget) {
- this._settings.set_int('leave-timeout', widget.get_value());
- }));
-
dialog.connect('response', Lang.bind(this, function(dialog, id) {
if (id == 1) {
// restore default settings
- this._settings.set_value('leave-timeout', this._settings.get_default_value('leave-timeout'));
- this._builder.get_object('leave_timeout_spinbutton').set_value(this._settings.get_int('leave-timeout'));
+
} else {
// remove the settings box so it doesn't get destroyed;
dialog.get_content_area().remove(box);
@@ -1585,6 +1692,16 @@ const Settings = new Lang.Class({
this._settings.set_string('dot-position', "TOP");
},
+ preview_title_position_bottom_button_toggled_cb: function(button) {
+ if (button.get_active())
+ this._settings.set_string('window-preview-title-position', 'BOTTOM');
+ },
+
+ preview_title_position_top_button_toggled_cb: function(button) {
+ if (button.get_active())
+ this._settings.set_string('window-preview-title-position', 'TOP');
+ },
+
panel_size_scale_format_value_cb: function(scale, value) {
return value+ ' px';
},
@@ -1726,10 +1843,14 @@ function buildPrefsWidget() {
// I'd like the scrolled window to default to a size large enough to show all without scrolling, if it fits on the screen
// But, it doesn't seem possible, so I'm setting a minimum size if there seems to be enough screen real estate
widget.show_all();
- let viewportSize = settings.viewport.size_request();
- let screenHeight = widget.get_screen().get_height() - 120;
-
- widget.set_size_request(viewportSize.width, viewportSize.height > screenHeight ? screenHeight : viewportSize.height);
+ adjustScrollableHeight(settings.viewport, widget);
return widget;
}
+
+function adjustScrollableHeight(viewport, scrollableWindow) {
+ let viewportSize = viewport.size_request();
+ let screenHeight = scrollableWindow.get_screen().get_height() - 120;
+
+ scrollableWindow.set_size_request(viewportSize.width, viewportSize.height > screenHeight ? screenHeight : viewportSize.height);
+}
\ No newline at end of file
diff --git a/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml b/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml
index ddeaf68..fe51f6f 100644
--- a/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml
+++ b/schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml
@@ -44,11 +44,7 @@
-
-
-
-
-
+
@@ -65,7 +61,7 @@
-
+
'BOTTOM'
Panel position
Panel is shown on the Bottom or Top of the screen.
@@ -75,7 +71,7 @@
Panel size
Set the size of the panel.
-
+
'BOTTOM'
Dot position
Running indicators are shown on the Bottom or Top of the screen.
@@ -410,21 +406,61 @@
Display title in preview
Display window title in preview
-
- 350
- Window previews width
- The width of the window previews
+
+ 'TOP'
+ Title position
+ Position of the window title, close button and icon in preview.
-
- 200
- Window previews height
- The height of the window previews
+
+ "#dddddd"
+ Window previews title font color
+ This defines the window preview titles font color.
+
+
+ 14
+ Window previews title font size
+ This defines the window preview titles font size.
+
+
+ 260
+ Window previews animation time
+ This defines the window previews animation time.
+
+
+ 'inherit'
+ Font weight of window preview titles
+ This defines the font weight of window preview titles. Supported values: inherit (from theme), normal, lighter, bold and bolder.
+
+
+ 240
+ Window previews size
+ Preferred window previews size
+
+
+ false
+ Fixed aspect ratio X
+ This defines if the window previews use a fixed aspect ratio X.
+
+
+ true
+ Fixed aspect ratio Y
+ This defines if the window previews use a fixed aspect ratio Y.
- 20
+ 8
Window previews padding
The padding of the window previews
+
+ 16
+ Aspect ratio X
+ The window previews respected aspect ratio X.
+
+
+ 9
+ Aspect ratio Y
+ The window previews respected aspect ratio Y.
+
false
Provide workspace isolation
@@ -553,6 +589,16 @@
Middle click preview to close window
Middle click on the window preview to close that window
+
+ true
+ Window previews use custom opacity
+ Window previews background use a different opacity from the panel
+
+
+ 80
+ Window previews background opacity
+ Window previews use this custom background opacity.
+
0
Tray font size
diff --git a/stylesheet.css b/stylesheet.css
index 05cb091..6cbbf6a 100644
--- a/stylesheet.css
+++ b/stylesheet.css
@@ -41,10 +41,6 @@
padding-right: 8px;
}
-#dashtopanelThumbnailScrollview {
- -st-hfade-offset: 48px;
-}
-
#dashtopanelScrollview .app-well-app:hover .overview-icon,
#dashtopanelScrollview .app-well-app:focus .overview-icon {
background: none;
diff --git a/taskbar.js b/taskbar.js
index 4ba093f..de99965 100644
--- a/taskbar.js
+++ b/taskbar.js
@@ -41,7 +41,6 @@ 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();
@@ -84,21 +83,6 @@ function extendDashItemContainer(dashItemContainer) {
* - modified chldBox calculations for when 'show-apps-at-top' option is checked
* - handle horizontal dash
*/
-
-function findIndex(array, predicate) {
- if (Array.prototype.findIndex) {
- return array.findIndex(predicate);
- }
-
- for (let i = 0, l = array.length; i < l; ++i) {
- if (predicate(array[i])) {
- return i;
- }
- }
-
- return -1;
-};
-
var taskbarActor = Utils.defineClass({
Name: 'DashToPanel-TaskbarActor',
Extends: St.Widget,
@@ -239,6 +223,9 @@ var taskbar = Utils.defineClass({
coordinate: Clutter.BindCoordinate.HEIGHT
}));
+ this.previewMenu = new WindowPreview.PreviewMenu(settings, panelWrapper);
+ this.previewMenu.enable();
+
if (!this._dtpSettings.get_boolean('show-show-apps-button'))
this.hideShowAppsButton();
@@ -326,11 +313,6 @@ var taskbar = Utils.defineClass({
'notify::checked',
Lang.bind(this, this._syncShowAppsButtonToggled)
],
- [
- this._dtpSettings,
- 'changed::show-window-previews',
- Lang.bind(this, this._toggleWindowPreview)
- ],
[
this._dtpSettings,
'changed::show-show-apps-button',
@@ -380,6 +362,7 @@ var taskbar = Utils.defineClass({
this._showAppsIconWrapper.destroy();
this._container.destroy();
+ this.previewMenu.disable();
this._disconnectWorkspaceSignals();
},
@@ -553,20 +536,18 @@ var taskbar = Utils.defineClass({
setSizeManually: true,
showLabel: false,
isDraggable: !this._dtpSettings.get_boolean('taskbar-locked'),
- }
+ },
+ this.previewMenu
);
if (appIcon._draggable) {
appIcon._draggable.connect('drag-begin',
Lang.bind(this, function() {
appIcon.actor.opacity = 50;
- this._disableWindowPreview();
}));
appIcon._draggable.connect('drag-end',
Lang.bind(this, function() {
appIcon.actor.opacity = 255;
- this._enableWindowPreview();
- appIcon.syncWindowPreview(this._getAppIcons());
}));
}
@@ -585,7 +566,7 @@ var taskbar = Utils.defineClass({
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);
+ Utils.ensureActorVisibleInScrollView(this._scrollView, appIcon.actor, this._scrollView._dtpFadeSize);
this._ensureAppIconVisibilityTimeoutId = 0;
return GLib.SOURCE_REMOVE;
}));
@@ -599,11 +580,11 @@ var taskbar = Utils.defineClass({
appIcon.actor.connect('clicked',
Lang.bind(this, function(actor) {
- ensureActorVisibleInScrollView(this._scrollView, actor);
+ Utils.ensureActorVisibleInScrollView(this._scrollView, actor, this._scrollView._dtpFadeSize);
}));
appIcon.actor.connect('key-focus-in', Lang.bind(this, function(actor) {
- let [x_shift, y_shift] = ensureActorVisibleInScrollView(this._scrollView, actor);
+ let [x_shift, y_shift] = Utils.ensureActorVisibleInScrollView(this._scrollView, actor, this._scrollView._dtpFadeSize);
// 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.
@@ -624,29 +605,6 @@ var taskbar = Utils.defineClass({
return item;
},
- _toggleWindowPreview: function() {
- if (this._dtpSettings.get_boolean('show-window-previews'))
- this._enableWindowPreview();
- else
- this._disableWindowPreview();
- },
-
- _enableWindowPreview: function() {
- let appIcons = this._getAppIcons();
-
- appIcons.filter(appIcon => !appIcon.isLauncher)
- .forEach(function (appIcon) {
- appIcon.enableWindowPreview(appIcons);
- });
- },
-
- _disableWindowPreview: function() {
- let appIcons = this._getAppIcons();
- appIcons.forEach(function (appIcon) {
- appIcon.disableWindowPreview();
- });
- },
-
// Return an array with the "proper" appIcons currently in the taskbar
_getAppIcons: function() {
// Only consider children which are "proper" icons and which are not
@@ -783,10 +741,6 @@ var taskbar = Utils.defineClass({
getAppStableSequence(appB, this._dtpSettings, this.panelWrapper.monitor);
},
- sortWindowsCompareFunction: function(windowA, windowB) {
- return getWindowStableSequence(windowA) - getWindowStableSequence(windowB);
- },
-
_redisplay: function () {
if (!this._signalsHandler) {
return;
@@ -815,8 +769,8 @@ var taskbar = Utils.defineClass({
//remove the appIcons which are not in the expected apps list
for (let i = currentAppIcons.length - 1; i > -1; --i) {
let appIcon = currentAppIcons[i].child._delegate;
- let appIndex = findIndex(expectedAppInfos, appInfo => appInfo.app == appIcon.app &&
- appInfo.isLauncher == appIcon.isLauncher);
+ let appIndex = Utils.findIndex(expectedAppInfos, appInfo => appInfo.app == appIcon.app &&
+ appInfo.isLauncher == appIcon.isLauncher);
if (appIndex < 0 ||
(appIcon.window && (this.isGroupApps || expectedAppInfos[appIndex].windows.indexOf(appIcon.window) < 0)) ||
@@ -836,8 +790,8 @@ var taskbar = Utils.defineClass({
for (let j = 0, ll = neededAppIcons.length; j < ll; ++j) {
//check if the icon already exists
- let matchingAppIconIndex = findIndex(currentAppIcons, appIcon => appIcon.child._delegate.app == neededAppIcons[j].app &&
- appIcon.child._delegate.window == neededAppIcons[j].window);
+ let matchingAppIconIndex = Utils.findIndex(currentAppIcons, appIcon => appIcon.child._delegate.app == neededAppIcons[j].app &&
+ appIcon.child._delegate.window == neededAppIcons[j].window);
if (matchingAppIconIndex > 0 && matchingAppIconIndex != currentPosition) {
//moved icon, reposition it
@@ -873,9 +827,6 @@ var taskbar = Utils.defineClass({
this._updateNumberOverlay();
}
- // Connect windows previews to hover events
- this._toggleWindowPreview();
-
this._shownInitially = true;
},
@@ -905,7 +856,7 @@ var taskbar = Utils.defineClass({
app: app,
isLauncher: defaultIsLauncher || false,
windows: defaultWindows || AppIcons.getInterestingWindows(app, this._dtpSettings, this.panelWrapper.monitor)
- .sort(this.sortWindowsCompareFunction)
+ .sort(sortWindowsCompareFunction)
}));
},
@@ -984,9 +935,9 @@ var taskbar = Utils.defineClass({
let currentAppIcons = this._getAppIcons();
let sourceIndex = currentAppIcons.indexOf(source);
- let hoveredIndex = findIndex(currentAppIcons,
- appIcon => x >= appIcon._dashItemContainer.x &&
- x <= (appIcon._dashItemContainer.x + appIcon._dashItemContainer.width));
+ let hoveredIndex = Utils.findIndex(currentAppIcons,
+ appIcon => x >= appIcon._dashItemContainer.x &&
+ x <= (appIcon._dashItemContainer.x + appIcon._dashItemContainer.width));
if (!this._dragInfo) {
this._dragInfo = [sourceIndex, source];
@@ -1008,9 +959,9 @@ var taskbar = Utils.defineClass({
// Ensure the next and previous icon are visible when moving the icon
// (I assume there's room for both of them)
if (hoveredIndex > 1)
- ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[hoveredIndex-1]);
+ ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[hoveredIndex-1], this._scrollView._dtpFadeSize);
if (hoveredIndex < this._box.get_children().length-1)
- ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[hoveredIndex+1]);
+ ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[hoveredIndex+1], this._scrollView._dtpFadeSize);
}
}
@@ -1259,70 +1210,10 @@ function getAppStableSequence(app, settings, monitor) {
}, Infinity);
}
+function sortWindowsCompareFunction(windowA, windowB) {
+ return getWindowStableSequence(windowA) - getWindowStableSequence(windowB);
+}
+
function getWindowStableSequence(window) {
return ('_dtpPosition' in window ? window._dtpPosition : window.get_stable_sequence());
-}
-
-/*
- * 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 = scrollView._dtpFadeSize;
-
- 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];
-}
+}
\ No newline at end of file
diff --git a/transparency.js b/transparency.js
index 65389bb..84a7d4c 100644
--- a/transparency.js
+++ b/transparency.js
@@ -136,7 +136,7 @@ var DynamicTransparency = Utils.defineClass({
},
_updateAnimationDuration: function() {
- this._animationDuration = (this._dtpSettings.get_int('trans-dynamic-anim-time') * 0.001) + 's;';
+ this.animationDuration = (this._dtpSettings.get_int('trans-dynamic-anim-time') * 0.001) + 's;';
},
_updateAllAndSet: function() {
@@ -172,18 +172,18 @@ var DynamicTransparency = Utils.defineClass({
},
_updateColor: function(themeBackground) {
- this._backgroundColor = this._dtpSettings.get_boolean('trans-use-custom-bg') ?
- this._dtpSettings.get_string('trans-bg-color') :
- (themeBackground || this._getThemeBackground());
+ this.backgroundColorRgb = this._dtpSettings.get_boolean('trans-use-custom-bg') ?
+ this._dtpSettings.get_string('trans-bg-color') :
+ (themeBackground || this._getThemeBackground());
},
_updateAlpha: function(themeBackground) {
if (this._windowOverlap && !Main.overview.visibleTarget && this._dtpSettings.get_boolean('trans-use-dynamic-opacity')) {
- this._alpha = this._dtpSettings.get_double('trans-dynamic-anim-target');
+ this.alpha = this._dtpSettings.get_double('trans-dynamic-anim-target');
} else {
- this._alpha = this._dtpSettings.get_boolean('trans-use-custom-opacity') ?
- this._dtpSettings.get_double('trans-panel-opacity') :
- (themeBackground || this._getThemeBackground()).alpha * 0.003921569; // 1 / 255 = 0.003921569
+ this.alpha = this._dtpSettings.get_boolean('trans-use-custom-opacity') ?
+ this._dtpSettings.get_double('trans-panel-opacity') :
+ (themeBackground || this._getThemeBackground()).alpha * 0.003921569; // 1 / 255 = 0.003921569
}
},
@@ -192,17 +192,17 @@ var DynamicTransparency = Utils.defineClass({
if (this._dtpSettings.get_boolean('trans-use-custom-gradient')) {
this._gradientStyle += 'background-gradient-direction: vertical; ' +
- 'background-gradient-start: ' + this._getrgbaColor(this._dtpSettings.get_string('trans-gradient-top-color'),
+ 'background-gradient-start: ' + Utils.getrgbaColor(this._dtpSettings.get_string('trans-gradient-top-color'),
this._dtpSettings.get_double('trans-gradient-top-opacity')) +
- 'background-gradient-end: ' + this._getrgbaColor(this._dtpSettings.get_string('trans-gradient-bottom-color'),
+ 'background-gradient-end: ' + Utils.getrgbaColor(this._dtpSettings.get_string('trans-gradient-bottom-color'),
this._dtpSettings.get_double('trans-gradient-bottom-opacity'));
}
},
_setBackground: function() {
- this.currentBackgroundColor = this._getrgbaColor(this._backgroundColor, this._alpha);
+ this.currentBackgroundColor = Utils.getrgbaColor(this.backgroundColorRgb, this.alpha);
- let transition = 'transition-duration:' + this._animationDuration;
+ let transition = 'transition-duration:' + this.animationDuration;
let cornerStyle = '-panel-corner-background-color: ' + this.currentBackgroundColor + transition;
this._dtpPanel.panelBg.set_style('background-color: ' + this.currentBackgroundColor + transition + this._complementaryStyles);
@@ -216,22 +216,10 @@ var DynamicTransparency = Utils.defineClass({
'border-image: none; ' +
'background-image: none; ' +
this._gradientStyle +
- 'transition-duration:' + this._animationDuration
+ 'transition-duration:' + this.animationDuration
);
},
- _getrgbaColor: function(color, alpha) {
- if (alpha <= 0) {
- return 'transparent; ';
- }
-
- if (typeof color === 'string') {
- color = Clutter.color_from_string(color)[1];
- }
-
- return 'rgba(' + color.red + ',' + color.green + ',' + color.blue + ',' + (Math.floor(alpha * 100) * 0.01) + '); ' ;
- },
-
_getThemeBackground: function(reload) {
if (reload || !this._themeBackground) {
let fakePanel = new St.Bin({ name: 'panel' });
diff --git a/utils.js b/utils.js
index e83393f..6441d17 100644
--- a/utils.js
+++ b/utils.js
@@ -32,6 +32,8 @@ const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Main = imports.ui.main;
+const Tweener = imports.ui.tweener;
+const Util = imports.misc.util;
var TRANSLATION_DOMAIN = imports.misc.extensionUtils.getCurrentExtension().metadata['gettext-domain'];
@@ -94,7 +96,7 @@ var defineClass = function (classDef) {
.forEach(k => C.prototype[k] = classDef[k]);
if (isGObject) {
- C = GObject.registerClass(C);
+ C = GObject.registerClass({ Signals: classDef.Signals || {} }, C);
}
return C;
@@ -271,6 +273,50 @@ var DisplayWrapper = {
}
};
+var getCurrentWorkspace = function() {
+ return DisplayWrapper.getWorkspaceManager().get_active_workspace();
+};
+
+var getWorkspaceByIndex = function(index) {
+ return DisplayWrapper.getWorkspaceManager().get_workspace_by_index(index);
+};
+
+var getWorkspaceCount = function() {
+ return DisplayWrapper.getWorkspaceManager().n_workspaces;
+};
+
+var checkIfWindowHasTransient = function(window) {
+ let hasTransient;
+
+ window.foreach_transient(t => !(hasTransient = true));
+
+ return hasTransient;
+};
+
+var findIndex = function(array, predicate) {
+ if (Array.prototype.findIndex) {
+ return array.findIndex(predicate);
+ }
+
+ for (let i = 0, l = array.length; i < l; ++i) {
+ if (predicate(array[i])) {
+ return i;
+ }
+ }
+
+ return -1;
+};
+
+var mergeObjects = function(main, bck) {
+ for (var prop in bck) {
+ if (!main.hasOwnProperty(prop) && bck.hasOwnProperty(prop)) {
+ main[prop] = bck[prop];
+ }
+ }
+
+ return main;
+};
+
var hookVfunc = function(proto, symbol, func) {
if (Gi.hook_up_vfunc_symbol) {
//gjs > 1.53.3
@@ -305,6 +351,88 @@ var removeKeybinding = function(key) {
Main.wm.removeKeybinding(key);
}
};
+
+var getrgbaColor = function(color, alpha, offset) {
+ if (alpha <= 0) {
+ return 'transparent; ';
+ }
+
+ color = typeof color === 'string' ? Clutter.color_from_string(color)[1] : color;
+
+ let rgb = { red: color.red, green: color.green, blue: color.blue };
+
+ if (offset) {
+ ['red', 'green', 'blue'].forEach(k => {
+ rgb[k] = Math.min(255, Math.max(0, rgb[k] + offset));
+
+ if (rgb[k] == color[k]) {
+ rgb[k] = Math.min(255, Math.max(0, rgb[k] - offset));
+ }
+ });
+ }
+
+ return 'rgba(' + rgb.red + ',' + rgb.green + ',' + rgb.blue + ',' + (Math.floor(alpha * 100) * 0.01) + '); ' ;
+};
+
+/*
+ * 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
+*/
+var ensureActorVisibleInScrollView = function(scrollView, actor, fadeSize, onComplete) {
+ 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 = fadeSize;
+ let hoffset = fadeSize;
+
+ 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);
+
+ let tweenOpts = {
+ time: Util.SCROLL_TIME,
+ onComplete: onComplete || (() => {}),
+ transition: 'easeOutQuad'
+ };
+
+ if (vvalue !== vvalue0) {
+ Tweener.addTween(vadjustment, mergeObjects(tweenOpts, { value: vvalue }));
+ }
+
+ if (hvalue !== hvalue0) {
+ Tweener.addTween(hadjustment, mergeObjects(tweenOpts, { value: hvalue }));
+ }
+
+ return [hvalue- hvalue0, vvalue - vvalue0];
+}
/**
* ColorUtils is adapted from https://github.com/micheleg/dash-to-dock
diff --git a/windowPreview.js b/windowPreview.js
index 8b2376c..ce42944 100644
--- a/windowPreview.js
+++ b/windowPreview.js
@@ -12,22 +12,12 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- *
- * Credits:
- * This file is based on code from the Dash to Dock extension by micheleg
- * and code from the Taskbar extension by Zorin OS
- * Some code was also adapted from the upstream Gnome Shell source code.
+ * along with this program. If not, see .
*/
-
-const BoxPointer = imports.ui.boxpointer;
const Clutter = imports.gi.Clutter;
const Config = imports.misc.config;
-const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
-const Lang = imports.lang;
const Main = imports.ui.main;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@@ -36,1191 +26,1077 @@ const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tweener = imports.ui.tweener;
-const WindowMenu = imports.ui.windowMenu;
+const WindowManager = imports.ui.windowManager;
const Workspace = imports.ui.workspace;
const Me = imports.misc.extensionUtils.getCurrentExtension();
const Taskbar = Me.imports.taskbar;
-const AppIcons = Me.imports.appIcons;
const Utils = Me.imports.utils;
-let HOVER_APP_BLACKLIST = [
- "Oracle VM VirtualBox",
- "Virtual Machine Manager",
- "Remmina"
- ]
+//timeout intervals
+const ENSURE_VISIBLE_MS = 200;
-var thumbnailPreviewMenu = Utils.defineClass({
- Name: 'DashToPanel.ThumbnailPreviewMenu',
- Extends: PopupMenu.PopupMenu,
- ParentConstrParams: [[0, 'actor'], 0.5, Taskbar.getPosition()],
+//timeout names
+const T1 = 'openMenuTimeout';
+const T2 = 'closeMenuTimeout';
+const T3 = 'peekTimeout';
+const T4 = 'ensureVisibleTimeout';
- _init: function(source, settings) {
- this._dtpSettings = settings;
+const MAX_TRANSLATION = 40;
+const HEADER_HEIGHT = 38;
+const MAX_CLOSE_BUTTON_SIZE = 30;
+const MIN_DIMENSION = 100;
+const FOCUSED_COLOR_OFFSET = 24;
+const HEADER_COLOR_OFFSET = -12;
+const FADE_SIZE = 36;
+const PEEK_INDEX_PROP = '_dtpPeekInitialIndex';
- let side = Taskbar.getPosition();
+let headerHeight = 0;
+let alphaBg = 0;
+let isLeftButtons = false;
+let isTopHeader = true;
+let scaleFactor = 1;
+let animationTime = 0;
+let aspectRatio = {};
- this.callParent('_init', source.actor, 0.5, side);
+var PreviewMenu = Utils.defineClass({
+ Name: 'DashToPanel-PreviewMenu',
+ Extends: St.Widget,
+ Signals: { 'open-state-changed': {} },
- // We want to keep the item hovered while the menu is up
- this.blockSourceEvents = false;
+ _init: function(dtpSettings, panelWrapper) {
+ this.callParent('_init', { layout_manager: new Clutter.BinLayout() });
- this._source = source;
- this._app = this._source.app;
-
- this.actor.add_style_class_name('app-well-menu');
- this.actor.set_style("max-width: " + (this._source.panelWrapper.monitor.width - 22) + "px;");
- this.actor.hide();
-
- // Chain our visibility and lifecycle to that of the source
- this._mappedId = this._source.actor.connect('notify::mapped', Lang.bind(this, function () {
- if (!this._source.actor.mapped)
- this.close();
- }));
- this._destroyId = this._source.actor.connect('destroy', Lang.bind(this, this.destroy));
-
- Main.uiGroup.add_actor(this.actor);
-
- // Change the initialized side where required.
- this._arrowSide = side;
- this._boxPointer._arrowSide = side;
- this._boxPointer._userArrowSide = side;
-
- this._previewBox = new thumbnailPreviewList(this._app, source, this._dtpSettings);
- this.addMenuItem(this._previewBox);
-
- this._peekMode = false;
- this._peekModeEnterTimeoutId = 0;
- this._peekModeDisableTimeoutId = 0;
- this._DISABLE_PEEK_MODE_TIMEOUT = 50;
+ this._dtpSettings = dtpSettings;
+ this._panelWrapper = panelWrapper;
+ this.currentAppIcon = null;
+ this._focusedPreview = null;
this._peekedWindow = null;
- this._peekModeSavedWorkspaces = null;
- this._peekModeSavedOrder = null;
- this._trackOpenWindowsId = null;
- this._trackClosedWindowsIds = null;
- this._peekModeOriginalWorkspace = null;
- this._peekModeCurrentWorkspace = null;
- },
+ this.peekInitialWorkspaceIndex = -1;
+ this.opened = false;
+ this._position = Taskbar.getPosition();
+ this.isLeftOrRight = this._position == St.Side.LEFT || this._position == St.Side.RIGHT;
+ this._translationProp = 'translation_' + (this.isLeftOrRight ? 'x' : 'y');
+ this._translationDirection = (this._position == St.Side.TOP || this._position == St.Side.LEFT ? -1 : 1);
+ this._translationOffset = Math.min(this._dtpSettings.get_int('panel-size'), MAX_TRANSLATION) * this._translationDirection;
- enableWindowPreview: function() {
- // Show window previews on mouse hover
- this._enterSourceId = this._source.actor.connect('enter-event', Lang.bind(this, this._onEnter));
- this._leaveSourceId = this._source.actor.connect('leave-event', Lang.bind(this, this._onLeave));
-
- this._enterMenuId = this.actor.connect('enter-event', Lang.bind(this, this._onMenuEnter));
- this._leaveMenuId = this.actor.connect('leave-event', Lang.bind(this, this._onMenuLeave));
- },
-
- disableWindowPreview: function() {
- if (this._enterSourceId) {
- this._source.actor.disconnect(this._enterSourceId);
- this._enterSourceId = 0;
- }
- if (this._leaveSourceId) {
- this._source.actor.disconnect(this._leaveSourceId);
- this._leaveSourceId = 0;
- }
-
- if (this._enterMenuId) {
- this.actor.disconnect(this._enterMenuId);
- this._enterMenuId = 0;
- }
- if (this._leaveMenuId) {
- this.actor.disconnect(this._leaveMenuId);
- this._leaveMenuId = 0;
- }
-
- this.close();
- },
-
- requestCloseMenu: function() {
- // The "~0" argument makes the animation display.
- this.close(~0);
- },
-
- _redisplay: function() {
- this._previewBox._shownInitially = false;
- this._previewBox._redisplay();
- },
-
- popup: function() {
- let windows = AppIcons.getInterestingWindows(this._app, this._dtpSettings, this._source.panelWrapper.monitor);
- if (windows.length > 0) {
- this._redisplay();
- this.open();
- this._source.emit('sync-tooltip');
- }
- },
-
- _onMenuEnter: function () {
- this.cancelClose();
-
- this.hoverOpen();
- },
-
- _onMenuLeave: function () {
- this.cancelOpen();
- this.cancelClose();
-
- this._hoverCloseTimeoutId = Mainloop.timeout_add(Taskbar.DASH_ITEM_HOVER_TIMEOUT, Lang.bind(this, this.hoverClose));
- },
-
- _onEnter: function () {
- this.cancelOpen();
- this.cancelClose();
-
- this._hoverOpenTimeoutId = Mainloop.timeout_add(this._dtpSettings.get_int('show-window-previews-timeout'), Lang.bind(this, this.hoverOpen));
- },
-
- _onLeave: function () {
- this.cancelOpen();
- this.cancelClose();
-
- // grabHelper.grab() is usually called when the menu is opened. However, there seems to be a bug in the
- // underlying gnome-shell that causes all window contents to freeze if the grab and ungrab occur
- // in quick succession in timeouts from the Mainloop (for example, clicking the icon as the preview window is opening)
- // So, instead wait until the mouse is leaving the icon (and might be moving toward the open window) to trigger the grab
- if(this.isOpen)
- this._source.menuManagerWindowPreview._grabHelper.grab({ actor: this.actor, focus: this.sourceActor,
- onUngrab: Lang.bind(this, this.requestCloseMenu) });
-
- this._hoverCloseTimeoutId = Mainloop.timeout_add(this._dtpSettings.get_int('leave-timeout'), Lang.bind(this, this.hoverClose));
- },
-
- cancelOpen: function () {
- if(this._hoverOpenTimeoutId) {
- Mainloop.source_remove(this._hoverOpenTimeoutId);
- this._hoverOpenTimeoutId = null;
- }
- },
-
- cancelClose: function () {
- if(this._hoverCloseTimeoutId) {
- Mainloop.source_remove(this._hoverCloseTimeoutId);
- this._hoverCloseTimeoutId = null;
- }
- },
-
- _appInHoverBlacklist: function (appName) {
- for (let i = 0; i < HOVER_APP_BLACKLIST.length; i++) {
- if (appName === HOVER_APP_BLACKLIST[i])
- return true;
- }
-
- return false;
- },
-
- hoverOpen: function () {
- this._hoverOpenTimeoutId = null;
- if (!this.isOpen && this._dtpSettings.get_boolean("show-window-previews")) {
- this.popup();
- let focusedApp = Shell.WindowTracker.get_default().focus_app;
- if (focusedApp && this._appInHoverBlacklist(focusedApp.get_name())) {
- this.actor.grab_key_focus();
- }
- }
- },
-
- hoverClose: function () {
- this._hoverCloseTimeoutId = null;
- this.close(~0);
- },
-
- destroy: function () {
- this.cancelClose();
- this.cancelOpen();
-
- if (this._mappedId)
- this._source.actor.disconnect(this._mappedId);
-
- if (this._destroyId)
- this._source.actor.disconnect(this._destroyId);
-
- if (this._enterSourceId)
- this._source.actor.disconnect(this._enterSourceId);
- if (this._leaveSourceId)
- this._source.actor.disconnect(this._leaveSourceId);
-
- if (this._enterMenuId)
- this.actor.disconnect(this._enterMenuId);
- if (this._leaveMenuId)
- this.actor.disconnect(this._leaveMenuId);
-
- this.callParent('destroy');
- },
-
- close: function(animate) {
- this.cancelOpen();
-
- if (this.isOpen)
- this.emit('open-state-changed', false);
- if (this._activeMenuItem)
- this._activeMenuItem.setActive(false);
-
- if (this._boxPointer.actor.visible) {
- (this._boxPointer.close || this._boxPointer.hide).call(this._boxPointer, animate, Lang.bind(this, function() {
- this.emit('menu-closed', this);
- }));
- }
-
- if(this._peekMode)
- this._disablePeekMode();
-
- this.isOpen = false;
- },
-
- _emulateSwitchingWorkspaces: function(oldWorkspace, newWorkspace) {
- if(oldWorkspace == newWorkspace)
- return;
-
- oldWorkspace.list_windows().forEach(Lang.bind(this, function(window) {
- if(!window.is_on_all_workspaces() && !window.minimized) {
- let windowActor = window.get_compositor_private();
- Tweener.addTween(windowActor, {
- opacity: 0,
- time: Taskbar.DASH_ANIMATION_TIME,
- transition: 'easeOutQuad',
- onComplete: Lang.bind(this, function(windowActor) {
- windowActor.hide();
- windowActor.opacity = this._dtpSettings.get_int('peek-mode-opacity');
- }),
- onCompleteParams: [windowActor]
- });
- }
- }));
- newWorkspace.list_windows().forEach(Lang.bind(this, function(window) {
- if(!window.is_on_all_workspaces() && !window.minimized) {
- let windowActor = window.get_compositor_private();
- windowActor.show();
- windowActor.opacity = 0;
- Tweener.addTween(windowActor, {
- opacity: this._dtpSettings.get_int('peek-mode-opacity'),
- time: Taskbar.DASH_ANIMATION_TIME,
- transition: 'easeOutQuad'
- });
- }
- }));
-
- this._peekModeCurrentWorkspace = newWorkspace;
- },
-
- _disablePeekMode: function() {
- if(this._peekModeDisableTimeoutId) {
- Mainloop.source_remove(this._peekModeDisableTimeoutId);
- this._peekModeDisableTimeoutId = null;
- }
- //Restore windows' old state
- if(this._peekedWindow) {
- let peekedWindowActor = this._peekedWindow.get_compositor_private();
- let originalIndex = this._peekModeSavedOrder.indexOf(peekedWindowActor);
-
- let locatedOnOriginalWorkspace = this._peekModeCurrentWorkspace == this._peekModeOriginalWorkspace;
- if(!locatedOnOriginalWorkspace)
- this._emulateSwitchingWorkspaces(this._peekModeCurrentWorkspace, this._peekModeOriginalWorkspace);
-
- if(peekedWindowActor)
- global.window_group.set_child_at_index(peekedWindowActor, originalIndex);
- }
-
- this._peekModeSavedWorkspaces.forEach(Lang.bind(this, function(workspace) {
- workspace.forEach(Lang.bind(this, function(pairWindowOpacity) {
- let window = pairWindowOpacity[0];
- let initialOpacity = pairWindowOpacity[1];
- let windowActor = window.get_compositor_private();
- if(window && windowActor) {
- if(window.minimized || !window.located_on_workspace(this._peekModeOriginalWorkspace))
- Tweener.addTween(windowActor, {
- opacity: 0,
- time: Taskbar.DASH_ANIMATION_TIME,
- transition: 'easeOutQuad',
- onComplete: Lang.bind(windowActor, function() {
- windowActor.hide();
- windowActor.opacity = initialOpacity;
- })
- });
- else
- Tweener.addTween(windowActor, {
- opacity: initialOpacity,
- time: Taskbar.DASH_ANIMATION_TIME,
- transition: 'easeOutQuad'
- });
- }
- }));
- }));
- this._peekModeSavedWorkspaces = null;
- this._peekedWindow = null;
- this._peekModeSavedOrder = null;
- this._peekModeCurrentWorkspace = null;
- this._peekModeOriginalWorkspace = null;
-
- this._trackClosedWindowsIds.forEach(function(pairWindowSignalId) {
- if(pairWindowSignalId)
- pairWindowSignalId[0].disconnect(pairWindowSignalId[1]);
+ this.menu = new St.Widget({
+ name: 'preview-menu',
+ layout_manager: new Clutter.BinLayout(),
+ reactive: true,
+ track_hover: true,
+ y_expand: true,
+ y_align: Clutter.ActorAlign[this._translationDirection > 0 ? 'END' : 'START']
+ });
+ this._box = new St.BoxLayout({ vertical: this.isLeftOrRight });
+ this._scrollView = new St.ScrollView({
+ name: 'dashtopanelPreviewScrollview',
+ hscrollbar_policy: Gtk.PolicyType.NEVER,
+ vscrollbar_policy: Gtk.PolicyType.NEVER,
+ enable_mouse_scrolling: true,
+ y_expand: !this.isLeftOrRight,
+ x_expand: this.isLeftOrRight
});
- this._trackClosedWindowsIds = null;
- if(this._trackOpenWindowsId) {
- global.display.disconnect(this._trackOpenWindowsId);
- this._trackOpenWindowsId = null;
- }
-
- this._peekMode = false;
+ this._scrollView.add_actor(this._box);
+ this.menu.add_child(this._scrollView);
+ this.add_child(this.menu);
},
- _setPeekedWindow: function(newPeekedWindow) {
- if(this._peekedWindow == newPeekedWindow)
+ enable: function() {
+ this._timeoutsHandler = new Utils.TimeoutsHandler();
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+
+ Main.layoutManager.addChrome(this, { affectsInputRegion: false });
+ Main.layoutManager.trackChrome(this.menu, { affectsInputRegion: true });
+
+ this._resetHiddenState();
+ this._refreshGlobals();
+ this._updateClip();
+ this.menu.set_position(1, 1);
+
+ this._signalsHandler.add(
+ [
+ this.menu,
+ 'notify::hover',
+ () => this._onHoverChanged()
+ ],
+ [
+ this._scrollView,
+ 'scroll-event',
+ this._onScrollEvent.bind(this)
+ ],
+ [
+ this._panelWrapper.panelBox,
+ 'style-changed',
+ () => this._updateClip()
+ ],
+ [
+ this._dtpSettings,
+ [
+ 'changed::panel-size',
+ 'changed::window-preview-size',
+ 'changed::window-preview-padding',
+ 'changed::window-preview-show-title'
+ ],
+ () => {
+ this._refreshGlobals();
+ this._updateClip();
+ }
+ ]
+ );
+ },
+
+ disable: function() {
+ this._timeoutsHandler.destroy();
+ this._signalsHandler.destroy();
+
+ this.close(true);
+
+ Main.layoutManager._untrackActor(this);
+ Main.uiGroup.remove_child(this);
+
+ this.destroy();
+ },
+
+ requestOpen: function(appIcon) {
+ this._endOpenCloseTimeouts();
+ this._timeoutsHandler.add([T1, this._dtpSettings.get_int('show-window-previews-timeout'), () => this.open(appIcon)]);
+ },
+
+ requestClose: function() {
+ this._endOpenCloseTimeouts();
+ this._addCloseTimeout();
+ },
+
+ open: function(appIcon) {
+ if (this.currentAppIcon != appIcon) {
+ this.currentAppIcon = appIcon;
+
+ if (!this.opened) {
+ this._refreshGlobals();
+ this.menu.set_style('background: ' + Utils.getrgbaColor(this._panelWrapper.dynamicTransparency.backgroundColorRgb, alphaBg));
+
+ this.show();
+ }
+
+ this._mergeWindows(appIcon);
+ this._updatePosition();
+ this._animateOpenOrClose(true);
+
+ this._setReactive(true);
+ this._setOpenedState(true);
+ }
+ },
+
+ close: function(immediate) {
+ this._endOpenCloseTimeouts();
+ this._removeFocus();
+ this._endPeek();
+
+ if (immediate) {
+ this._resetHiddenState();
+ } else {
+ this._animateOpenOrClose(false, () => this._resetHiddenState());
+ }
+
+ this._setReactive(false);
+ this.currentAppIcon = null;
+ },
+
+ update: function(appIcon, windows) {
+ if (this.currentAppIcon == appIcon) {
+ if (windows && !windows.length) {
+ this.close();
+ } else {
+ this._addAndRemoveWindows(windows);
+ this._updatePosition();
+ }
+ }
+ },
+
+ updatePosition: function() {
+ this._updatePosition();
+ },
+
+ focusNext: function() {
+ let previews = this._box.get_children();
+ let currentIndex = this._focusedPreview ? previews.indexOf(this._focusedPreview) : -1;
+ let nextIndex = currentIndex + 1;
+
+ nextIndex = previews[nextIndex] ? nextIndex : 0;
+
+ if (previews[nextIndex]) {
+ this._removeFocus();
+ previews[nextIndex].setFocus(true);
+ this._focusedPreview = previews[nextIndex];
+ }
+
+ return nextIndex;
+ },
+
+ activateFocused: function() {
+ if (this.opened && this._focusedPreview) {
+ this._focusedPreview.activate();
+ }
+ },
+
+ requestPeek: function(window) {
+ this._timeoutsHandler.remove(T3);
+
+ if (this._dtpSettings.get_boolean('peek-mode')) {
+ if (this.peekInitialWorkspaceIndex < 0) {
+ this._timeoutsHandler.add([T3, this._dtpSettings.get_int('enter-peek-mode-timeout'), () => this._peek(window)]);
+ } else {
+ this._peek(window);
+ }
+ }
+ },
+
+ endPeekHere: function() {
+ this._endPeek(true);
+ },
+
+ ensureVisible: function(preview) {
+ let [ , upper, pageSize] = this._getScrollAdjustmentValues();
+
+ if (upper > pageSize) {
+ this._timeoutsHandler.add([
+ T4,
+ ENSURE_VISIBLE_MS,
+ () => Utils.ensureActorVisibleInScrollView(this._scrollView, preview, MIN_DIMENSION, () => this._updateScrollFade())
+ ]);
+ }
+ },
+
+ _setReactive: function(reactive) {
+ this._box.get_children().forEach(c => c.reactive = reactive);
+ this.menu.reactive = reactive;
+ },
+
+ _setOpenedState: function(opened) {
+ this.opened = opened;
+ this.emit('open-state-changed');
+ },
+
+ _removeFocus: function() {
+ if (this._focusedPreview) {
+ this._focusedPreview.setFocus(false);
+ this._focusedPreview = null;
+ }
+ },
+
+ _mergeWindows: function(appIcon, windows) {
+ windows = windows || (appIcon.window ? [appIcon.window] : appIcon.getAppIconInterestingWindows());
+ windows.sort(Taskbar.sortWindowsCompareFunction);
+
+ let currentPreviews = this._box.get_children();
+ let l = Math.max(windows.length, currentPreviews.length);
+
+ for (let i = 0; i < l; ++i) {
+ if (currentPreviews[i] && windows[i] && windows[i] != currentPreviews[i].window) {
+ currentPreviews[i].assignWindow(windows[i], this.opened);
+ } else if (!currentPreviews[i]) {
+ this._addNewPreview(windows[i]);
+ } else if (!windows[i]) {
+ currentPreviews[i][!this.opened ? 'destroy' : 'animateOut']();
+ }
+ }
+ },
+
+ _addAndRemoveWindows: function(windows) {
+ let currentPreviews = this._box.get_children();
+
+ windows.sort(Taskbar.sortWindowsCompareFunction);
+
+ for (let i = 0, l = windows.length; i < l; ++i) {
+ let currentIndex = Utils.findIndex(currentPreviews, c => c.window == windows[i]);
+
+ if (currentIndex < 0) {
+ this._addNewPreview(windows[i]);
+ } else {
+ currentPreviews[currentIndex].cancelAnimateOut();
+ currentPreviews[currentIndex].assignWindow(windows[i]);
+ currentPreviews.splice(currentIndex, 1);
+
+ if (this._peekedWindow && this._peekedWindow == windows[i]) {
+ this.requestPeek(windows[i]);
+ }
+ }
+ }
+
+ currentPreviews.forEach(c => c.animateOut());
+ },
+
+ _addNewPreview: function(window) {
+ let preview = new Preview(this);
+
+ this._box.add_child(preview);
+ preview.adjustOnStage();
+ preview.assignWindow(window, this.opened);
+ },
+
+ getCurrentAppIcon: function() {
+ return this.currentAppIcon;
+ },
+
+ _addCloseTimeout: function() {
+ this._timeoutsHandler.add([T2, this._dtpSettings.get_int('leave-timeout'), () => this.close()]);
+ },
+
+ _onHoverChanged: function() {
+ this._endOpenCloseTimeouts();
+
+ if (this.currentAppIcon && !this.menu.hover) {
+ this._addCloseTimeout();
+ this._endPeek();
+ }
+ },
+
+ _onScrollEvent: function(actor, event) {
+ if (!event.is_pointer_emulated()) {
+ let vOrh = this.isLeftOrRight ? 'v' : 'h';
+ let adjustment = this._scrollView['get_' + vOrh + 'scroll_bar']().get_adjustment();
+ let increment = adjustment.step_increment;
+ let delta = increment;
+
+ switch (event.get_scroll_direction()) {
+ case Clutter.ScrollDirection.UP:
+ delta = -increment;
+ break;
+ case Clutter.ScrollDirection.SMOOTH:
+ let [dx, dy] = event.get_scroll_delta();
+ delta = dy * increment;
+ delta += dx * increment;
+ break;
+ }
+
+ adjustment.set_value(adjustment.get_value() + delta);
+ this._updateScrollFade();
+ }
+
+ return Clutter.EVENT_STOP;
+ },
+
+ _endOpenCloseTimeouts: function() {
+ this._timeoutsHandler.remove(T1);
+ this._timeoutsHandler.remove(T2);
+ this._timeoutsHandler.remove(T4);
+ },
+
+ _refreshGlobals: function() {
+ isLeftButtons = Meta.prefs_get_button_layout().left_buttons.indexOf(Meta.ButtonFunction.CLOSE) >= 0;
+ isTopHeader = this._dtpSettings.get_string('window-preview-title-position') == 'TOP';
+ scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ headerHeight = this._dtpSettings.get_boolean('window-preview-show-title') ? HEADER_HEIGHT * scaleFactor : 0;
+ animationTime = this._dtpSettings.get_int('window-preview-animation-time') * .001;
+ aspectRatio.x = {
+ size: this._dtpSettings.get_int('window-preview-aspect-ratio-x'),
+ fixed: this._dtpSettings.get_boolean('window-preview-fixed-x')
+ };
+ aspectRatio.y = {
+ size: this._dtpSettings.get_int('window-preview-aspect-ratio-y'),
+ fixed: this._dtpSettings.get_boolean('window-preview-fixed-y')
+ };
+
+ if (this._panelWrapper.dynamicTransparency) {
+ alphaBg = this._dtpSettings.get_boolean('preview-use-custom-opacity') ?
+ this._dtpSettings.get_int('preview-custom-opacity') * .01 :
+ this._panelWrapper.dynamicTransparency.alpha;
+ }
+ },
+
+ _resetHiddenState: function() {
+ this.hide();
+ this._setOpenedState(false);
+ this.menu.opacity = 0;
+ this.menu[this._translationProp] = this._translationOffset;
+ this._box.get_children().forEach(c => c.destroy());
+ },
+
+ _updateClip: function() {
+ let x, y, w, h;
+ let panelBoxTheme = this._panelWrapper.panelBox.get_theme_node();
+ let panelSize = this._dtpSettings.get_int('panel-size') * scaleFactor;
+ let previewSize = (this._dtpSettings.get_int('window-preview-size') +
+ this._dtpSettings.get_int('window-preview-padding') * 2) * scaleFactor;
+
+ if (this.isLeftOrRight) {
+ w = previewSize;
+ h = this._panelWrapper.monitor.height;
+ y = this._panelWrapper.monitor.y;
+ } else {
+ w = this._panelWrapper.monitor.width;
+ h = (previewSize + headerHeight);
+ x = this._panelWrapper.monitor.x;
+ }
+
+ if (this._position == St.Side.LEFT) {
+ x = this._panelWrapper.monitor.x + panelSize + panelBoxTheme.get_padding(St.Side.LEFT);
+ } else if (this._position == St.Side.RIGHT) {
+ x = this._panelWrapper.monitor.x + this._panelWrapper.monitor.width - (panelSize + previewSize) - panelBoxTheme.get_padding(St.Side.RIGHT);
+ } else if (this._position == St.Side.TOP) {
+ y = this._panelWrapper.monitor.y + panelSize + panelBoxTheme.get_padding(St.Side.TOP);
+ } else { //St.Side.BOTTOM
+ y = this._panelWrapper.monitor.y + this._panelWrapper.monitor.height - (panelSize + panelBoxTheme.get_padding(St.Side.BOTTOM) + previewSize + headerHeight);
+ }
+
+ this.set_clip(0, 0, w, h);
+ this.set_position(x, y);
+ this.set_size(w, h);
+ },
+
+ _updatePosition: function() {
+ let sourceNode = this.currentAppIcon.actor.get_theme_node();
+ let sourceContentBox = sourceNode.get_content_box(this.currentAppIcon.actor.get_allocation_box());
+ let sourceAllocation = Shell.util_get_transformed_allocation(this.currentAppIcon.actor);
+ let [previewsWidth, previewsHeight] = this._getPreviewsSize();
+ let appIconMargin = this._dtpSettings.get_int('appicon-margin') / scaleFactor;
+ let x = 0, y = 0;
+
+ previewsWidth = Math.min(previewsWidth, this._panelWrapper.monitor.width);
+ previewsHeight = Math.min(previewsHeight, this._panelWrapper.monitor.height);
+ this._updateScrollFade(previewsWidth < this._panelWrapper.monitor.width && previewsHeight < this._panelWrapper.monitor.height);
+
+ if (this.isLeftOrRight) {
+ y = sourceAllocation.y1 + appIconMargin - this._panelWrapper.monitor.y + (sourceContentBox.y2 - sourceContentBox.y1 - previewsHeight) * .5;
+ y = Math.max(y, 0);
+ y = Math.min(y, this._panelWrapper.monitor.height - previewsHeight);
+ } else {
+ x = sourceAllocation.x1 + appIconMargin - this._panelWrapper.monitor.x + (sourceContentBox.x2 - sourceContentBox.x1 - previewsWidth) * .5;
+ x = Math.max(x, 0);
+ x = Math.min(x, this._panelWrapper.monitor.width - previewsWidth);
+ }
+
+ if (!this.opened) {
+ this.menu.set_position(x, y);
+ this.menu.set_size(previewsWidth, previewsHeight);
+ } else {
+ Tweener.addTween(this.menu, getTweenOpts({ x: x, y: y, width: previewsWidth, height: previewsHeight }));
+ }
+ },
+
+ _updateScrollFade: function(remove) {
+ let [value, upper, pageSize] = this._getScrollAdjustmentValues();
+ let needsFade = upper > pageSize;
+ let fadeWidgets = this.menu.get_children().filter(c => c != this._scrollView);
+
+ if (!remove && needsFade) {
+ if (!fadeWidgets.length) {
+ fadeWidgets.push(this._getFadeWidget());
+ fadeWidgets.push(this._getFadeWidget(true));
+
+ this.menu.add_child(fadeWidgets[0]);
+ this.menu.add_child(fadeWidgets[1]);
+ }
+
+ fadeWidgets[0].visible = value > 0;
+ fadeWidgets[1].visible = value + pageSize < upper;
+ } else if (remove || (!needsFade && fadeWidgets.length)) {
+ fadeWidgets.forEach(fw => fw.destroy());
+ }
+ },
+
+ _getScrollAdjustmentValues: function() {
+ let [value , , upper, , , pageSize] = this._scrollView[this.isLeftOrRight ? 'v' : 'h' + 'scroll'].adjustment.get_values();
+
+ return [value, upper, pageSize];
+ },
+
+ _getFadeWidget: function(end) {
+ let rotation = 0;
+ let size = 0;
+ let x = 0, y = 0;
+ let startBg = Utils.getrgbaColor(this._panelWrapper.dynamicTransparency.backgroundColorRgb, Math.min(alphaBg + .1, 1));
+ let endBg = Utils.getrgbaColor(this._panelWrapper.dynamicTransparency.backgroundColorRgb, 0)
+ let fadeStyle = 'background-gradient-start:' + startBg + 'background-gradient-end: ' + endBg + ' background-gradient-direction:';
+
+ if (this.isLeftOrRight) {
+ fadeStyle += 'vertical;'
+ rotation = end ? 270 : 90;
+ y = end ? this._panelWrapper.monitor.height - FADE_SIZE : 0;
+ size = this.width;
+ } else {
+ fadeStyle += 'horizontal;'
+ rotation = end ? 180 : 0;
+ x = end ? this._panelWrapper.monitor.width - FADE_SIZE : 0;
+ size = this.height;
+ }
+
+ let fadeWidget = new St.Widget({
+ reactive: false,
+ pivot_point: new Clutter.Point({ x: .5, y: .5 }),
+ rotation_angle_z: rotation,
+ style: fadeStyle,
+ x: x, y: y,
+ width: FADE_SIZE, height: size
+ });
+
+ return fadeWidget;
+ },
+
+ _getPreviewsSize: function() {
+ let previewsWidth = 0;
+ let previewsHeight = 0;
+
+ this._box.get_children().forEach(c => {
+ if (!c.animatingOut) {
+ let [width, height] = c.getSize();
+
+ if (this.isLeftOrRight) {
+ previewsWidth = Math.max(width, previewsWidth);
+ previewsHeight += height;
+ } else {
+ previewsWidth += width;
+ previewsHeight = Math.max(height, previewsHeight);
+ }
+ }
+ });
+
+ return [previewsWidth, previewsHeight];
+ },
+
+ _animateOpenOrClose: function(show, onComplete) {
+ let isTranslationAnimation = this.menu[this._translationProp] != 0;
+ let tweenOpts = {
+ opacity: show ? 255 : 0,
+ transition: show ? 'easeInOutQuad' : 'easeInCubic',
+ onComplete: () => {
+ if (isTranslationAnimation) {
+ Main.layoutManager._queueUpdateRegions();
+ }
+
+ (onComplete || (() => {}))();
+ }
+ };
+
+ tweenOpts[this._translationProp] = show ? this._translationDirection : this._translationOffset;
+
+ Tweener.addTween(this.menu, getTweenOpts(tweenOpts));
+ },
+
+ _peek: function(window) {
+ let currentWorkspace = Utils.getCurrentWorkspace();
+ let windowWorkspace = window.get_workspace();
+ let focusWindow = () => this._focusMetaWindow(this._dtpSettings.get_int('peek-mode-opacity'), window);
+
+ this._restorePeekedWindowStack();
+ this._peekedWindow = window;
+
+ if (currentWorkspace != windowWorkspace) {
+ this._switchToWorkspaceImmediate(windowWorkspace.index());
+ this._timeoutsHandler.add([T3, 100, focusWindow]);
+ } else {
+ focusWindow();
+ }
+
+ if (this.peekInitialWorkspaceIndex < 0) {
+ this.peekInitialWorkspaceIndex = currentWorkspace.index();
+ }
+ },
+
+ _endPeek: function(stayHere) {
+ this._timeoutsHandler.remove(T3);
+
+ if (this._peekedWindow) {
+ this._restorePeekedWindowStack();
+
+ this._focusMetaWindow(255);
+ this._peekedWindow = null;
+
+ if (!stayHere) {
+ this._switchToWorkspaceImmediate(this.peekInitialWorkspaceIndex);
+ }
+
+ this.peekInitialWorkspaceIndex = -1;
+ }
+ },
+
+ _switchToWorkspaceImmediate: function(workspaceIndex) {
+ let workspace = Utils.getWorkspaceByIndex(workspaceIndex);
+
+ if (!workspace || (!workspace.list_windows().length &&
+ workspaceIndex < Utils.getWorkspaceCount() -1)) {
+ workspace = Utils.getCurrentWorkspace();
+ }
+
+ Main.wm._blockAnimations = true;
+ workspace.activate(global.display.get_current_time_roundtrip());
+ Main.wm._blockAnimations = false;
+ },
+
+ _focusMetaWindow: function(dimOpacity, window) {
+ if (Main.overview.visibleTarget) {
return;
-
- //We don't need to bother with animating the workspace out and then in if the old peeked workspace is the same as the new one
- let needToChangeWorkspace = !newPeekedWindow.located_on_workspace(this._peekModeCurrentWorkspace) || (newPeekedWindow.is_on_all_workspaces() && this._peekModeCurrentWorkspace != this._peekModeOriginalWorkspace);
- if(needToChangeWorkspace) {
- //If the new peeked window is on every workspace, we get the original one
- //Otherwise, we get the workspace the window is exclusive to
- let newWorkspace = newPeekedWindow.get_workspace();
- this._emulateSwitchingWorkspaces(this._peekModeCurrentWorkspace, newWorkspace);
}
- //Hide currently peeked window and show the new one
- if(this._peekedWindow) {
- let peekedWindowActor = this._peekedWindow.get_compositor_private();
- let originalIndex = this._peekModeSavedOrder.indexOf(peekedWindowActor);
+ global.get_window_actors().forEach(wa => {
+ let mw = wa.meta_window;
+ let isFocused = mw == window;
- global.window_group.set_child_at_index(peekedWindowActor, originalIndex);
- if(this._peekedWindow.minimized || (needToChangeWorkspace && !this._peekedWindow.is_on_all_workspaces()))
- Tweener.addTween(peekedWindowActor, {
- opacity: 0,
- time: Taskbar.DASH_ANIMATION_TIME,
- transition: 'easeOutQuad',
- onComplete: Lang.bind(this, function(peekedWindowActor) {
- peekedWindowActor.hide();
- peekedWindowActor.opacity = this._dtpSettings.get_int('peek-mode-opacity');
- }),
- onCompleteParams: [peekedWindowActor]
- });
- else
- Tweener.addTween(peekedWindowActor, {
- opacity: this._dtpSettings.get_int('peek-mode-opacity'),
- time: Taskbar.DASH_ANIMATION_TIME,
- transition: 'easeOutQuad'
- });
- }
+ if (mw) {
+ if (isFocused) {
+ mw[PEEK_INDEX_PROP] = wa.get_parent().get_children().indexOf(wa);
+ wa.get_parent().set_child_above_sibling(wa, null);
+ }
- this._peekedWindow = newPeekedWindow;
- let peekedWindowActor = this._peekedWindow.get_compositor_private();
-
- if(this._peekedWindow.minimized) {
- peekedWindowActor.opacity = 0;
- peekedWindowActor.show();
- }
-
- global.window_group.set_child_above_sibling(peekedWindowActor, null);
- Tweener.addTween(peekedWindowActor, {
- opacity: 255,
- time: Taskbar.DASH_ANIMATION_TIME,
- transition: 'easeOutQuad'
- });
+ if (isFocused && mw.minimized) {
+ wa.show();
+ }
+
+ Tweener.addTween(wa, getTweenOpts({ opacity: isFocused ? 255 : dimOpacity }));
+ }
+ });
},
- _enterPeekMode: function(thumbnail) {
- let workspaceManager = Utils.DisplayWrapper.getWorkspaceManager();
+ _restorePeekedWindowStack: function() {
+ let windowActor = this._peekedWindow ? this._peekedWindow.get_compositor_private() : null;
- this._peekMode = true;
- //Remove the enter peek mode timeout
- if(this._peekModeEnterTimeoutId) {
- Mainloop.source_remove(this._peekModeEnterTimeoutId);
- this._peekModeEnterTimeoutId = null;
+ if (windowActor) {
+ if (this._peekedWindow.hasOwnProperty(PEEK_INDEX_PROP)) {
+ windowActor.get_parent().set_child_at_index(windowActor, this._peekedWindow[PEEK_INDEX_PROP]);
+ delete this._peekedWindow[PEEK_INDEX_PROP];
+ }
+
+ if(this._peekedWindow.minimized) {
+ windowActor.hide();
+ }
}
-
- //Save the visible windows in each workspace and lower their opacity
- this._peekModeSavedWorkspaces = [];
- this._peekModeOriginalWorkspace = workspaceManager.get_active_workspace();
- this._peekModeCurrentWorkspace = this._peekModeOriginalWorkspace;
-
- for ( let wks=0; wks,
- //there might appear St.Widget of type "tile preview" that is on top of the stack
- this._peekModeSavedOrder = global.window_group.get_children().slice();
-
- //Track closed windows - pairs (window, signal Id), null for backgrounds
- this._trackClosedWindowsIds = this._peekModeSavedOrder.map(Lang.bind(this, function(windowActor) {
- if(!(windowActor instanceof Meta.BackgroundGroup) && !(windowActor instanceof St.Widget))
- return [windowActor.meta_window,
- windowActor.meta_window.connect('unmanaged', Lang.bind(this, this._peekModeWindowClosed))];
- else
- return null;
- }));
-
- //Track newly opened windows
- if(this._trackOpenWindowsId)
- global.display.disconnect(this._trackOpenWindowsId);
- this._trackOpenWindowsId = global.display.connect('window-created', Lang.bind(this, this._peekModeWindowOpened));
-
- //Having lowered opacity of all the windows, show the peeked window
- this._setPeekedWindow(thumbnail.window);
},
-
- _peekModeWindowClosed: function(window) {
- if(this._peekMode && window == this._peekedWindow)
- this._disablePeekMode();
- },
-
- _windowOnTop: function(window) {
- //There can be St.Widgets "tile-preview" on top of the window stack.
- //The window is on top if there are no other window actors above it (Except for St.Widgets)
- let windowStack = global.window_group.get_children();
- let newWindowIndex = windowStack.indexOf(window.get_compositor_private());
-
- for(let index = newWindowIndex + 1; index < windowStack.length; ++index) {
- if(windowStack[index] instanceof Meta.WindowActor || windowStack[index] instanceof Meta.BackgroundGroup)
- return false;
- }
- return true;
- },
-
- _peekModeWindowOpened: function(display, window) {
- this._disablePeekMode();
- //If this new window is placed on the top then close the preview
- if(this._windowOnTop(window))
- this.requestCloseMenu();
- }
});
-var thumbnailPreview = Utils.defineClass({
- Name: 'DashToPanel.ThumbnailPreview',
- Extends: PopupMenu.PopupBaseMenuItem,
- ParentConstrParams: [{ reactive: true }],
+var Preview = Utils.defineClass({
+ Name: 'DashToPanel-Preview',
+ Extends: St.Widget,
- _init: function(window, settings) {
- this._dtpSettings = settings;
- this.window = window;
-
- let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
- if(!scaleFactor)
- scaleFactor = 1;
-
- this._thumbnailWidth = this._dtpSettings.get_int('window-preview-width')*scaleFactor;
- this._thumbnailHeight = this._dtpSettings.get_int('window-preview-height')*scaleFactor;
-
- this.callParent('_init', {reactive: true});
-
- this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._onResize));
-
- this.preview = this.getThumbnail();
-
- this.actor.remove_child(this._ornamentLabel);
- this.actor._delegate = this;
-
- this.actor.set_style('padding: ' + this._dtpSettings.get_int('window-preview-padding') + 'px;');
-
- this.animatingOut = false;
-
- this._windowBox = new St.BoxLayout({ style_class: 'window-box',
- x_expand: true,
- vertical: true });
-
- this._closeButton = new St.Button({ style_class: 'window-close', accessible_name: "Close window" });
-
- if (Config.PACKAGE_VERSION >= '3.31.9') {
- this._closeButton.add_actor(new St.Icon({ icon_name: 'window-close-symbolic' }));
- }
-
- this._closeButton.opacity = 0;
- this._closeButton.connect('clicked', Lang.bind(this, this._closeWindow));
- this._closeButton.connect('style-changed', () => this._isLeftButtons = null);
-
- this._previewBin = new Clutter.Actor({ width: this._thumbnailWidth, height: this._thumbnailHeight });
-
- if (this.preview)
- this._previewBin.add_actor(this.preview);
-
- this._previewBin.add_actor(this._closeButton);
-
- this.overlayGroup = new Clutter.Actor({ layout_manager: new Clutter.BinLayout()});
- this.overlayGroup.add_actor(this._previewBin);
-
- this._titleNotifyId = 0;
-
- this._windowBin = new St.Bin({
- child: this.overlayGroup,
- x_align: St.Align.MIDDLE,
- width: this._thumbnailWidth,
- height: this._thumbnailHeight
+ _init: function(previewMenu) {
+ this.callParent('_init', {
+ style_class: 'preview-container',
+ reactive: true,
+ track_hover: true,
+ layout_manager: new Clutter.BinLayout()
});
- this._windowBox.add_child(this._windowBin);
+ this.window = null;
+ this._needsCloseButton = true;
+ this.cloneWidth = this.cloneHeight = 0;
+ this._panelWrapper = previewMenu._panelWrapper;
+ this._previewMenu = previewMenu;
+ this._padding = previewMenu._dtpSettings.get_int('window-preview-padding') * scaleFactor;
+ this._previewDimensions = this._getPreviewDimensions();
+ this.animatingOut = false;
- if (this._dtpSettings.get_boolean('window-preview-show-title')) {
- this._title = new St.Label({ text: window.title });
- this._titleBin = new St.Bin({ child: this._title,
- x_align: St.Align.MIDDLE,
- width: this._thumbnailWidth
- });
- this._titleBin.add_style_class_name("preview-window-title");
+ let box = new St.Widget({ layout_manager: new Clutter.BoxLayout({ vertical: true }), y_expand: true });
+ let [previewBinWidth, previewBinHeight] = this._getBinSize();
+ let closeButton = new St.Button({ style_class: 'window-close', accessible_name: 'Close window' });
- this._windowBox.add_child(this._titleBin);
-
- this._titleNotifyId = this.window.connect('notify::title', Lang.bind(this, function() {
- this._title.set_text(this.window.title);
- }));
+ if (Config.PACKAGE_VERSION >= '3.31.9') {
+ closeButton.add_actor(new St.Icon({ icon_name: 'window-close-symbolic' }));
}
+
+ this._closeButtonBin = new St.Widget({
+ layout_manager: new Clutter.BinLayout(),
+ opacity: 0,
+ x_expand: true, y_expand: true,
+ x_align: Clutter.ActorAlign[isLeftButtons ? 'START' : 'END'],
+ y_align: Clutter.ActorAlign[isTopHeader ? 'START' : 'END']
+ });
+
+ this._closeButtonBin.add_child(closeButton);
+
+ this._previewBin = new St.Widget({
+ layout_manager: new Clutter.BinLayout(),
+ x_expand: true, y_expand: true,
+ style: 'padding: ' + this._padding / scaleFactor + 'px;'
+ });
+
+ this._previewBin.set_size(previewBinWidth, previewBinHeight);
+
+ box.add_child(this._previewBin);
- this.actor.add_child(this._windowBox);
-
- this.actor.connect('enter-event',
- Lang.bind(this, this._onEnter));
- this.actor.connect('leave-event',
- Lang.bind(this, this._onLeave));
- this.actor.connect('key-focus-in',
- Lang.bind(this, this._onEnter));
- this.actor.connect('key-focus-out',
- Lang.bind(this, this._onLeave));
- this.actor.connect('motion-event',
- Lang.bind(this, this._onMotionEvent));
-
- this._previewMenuPopupManager = new previewMenuPopupManager(window, this.actor);
- },
-
- _onEnter: function(actor, event) {
- this._repositionCloseButton();
- this._showCloseButton();
-
- let topMenu = this._getTopMenu();
- if(topMenu._dtpSettings.get_boolean('peek-mode')) {
- if(topMenu._peekMode) {
- if(topMenu._peekModeDisableTimeoutId) {
- Mainloop.source_remove(topMenu._peekModeDisableTimeoutId);
- topMenu._peekModeDisableTimeoutId = null;
- }
- //Hide the old peeked window and show the window in preview
- topMenu._setPeekedWindow(this.window);
- } else if(!this.animatingOut) {
- //Remove old timeout and set a new one
- if(topMenu._peekModeEnterTimeoutId)
- Mainloop.source_remove(topMenu._peekModeEnterTimeoutId);
- topMenu._peekModeEnterTimeoutId = Mainloop.timeout_add(topMenu._dtpSettings.get_int('enter-peek-mode-timeout'), Lang.bind(this, function() {
- topMenu._enterPeekMode(this);
- }));
- }
- }
-
- return Clutter.EVENT_PROPAGATE;
- },
-
- _onLeave: function(actor, event) {
- if (!this._previewBin.has_pointer &&
- !this._closeButton.has_pointer)
- this._hideCloseButton();
-
- let topMenu = this._getTopMenu();
- if(topMenu._peekMode) {
- if(topMenu._peekModeDisableTimeoutId){
- Mainloop.source_remove(topMenu._peekModeDisableTimeoutId);
- topMenu._peekModeDisableTimeoutId = null;
- }
- topMenu._peekModeDisableTimeoutId = Mainloop.timeout_add(topMenu._DISABLE_PEEK_MODE_TIMEOUT, function() {
- topMenu._disablePeekMode()
+ if (headerHeight) {
+ let headerBox = new St.Widget({
+ layout_manager: new Clutter.BoxLayout(),
+ x_expand: true,
+ y_align: Clutter.ActorAlign[isTopHeader ? 'START' : 'END'],
+ style: this._getBackgroundColor(HEADER_COLOR_OFFSET, 1)
});
- }
- if(topMenu._peekModeEnterTimeoutId) {
- Mainloop.source_remove(topMenu._peekModeEnterTimeoutId);
- topMenu._peekModeEnterTimeoutId = null;
+
+ this._workspaceIndicator = new St.Label({ y_align: Clutter.ActorAlign.CENTER });
+ this._windowTitle = new St.Label({ y_align: Clutter.ActorAlign.CENTER, x_expand: true });
+
+ this._iconBin = new St.Widget({ layout_manager: new Clutter.BinLayout() });
+ this._iconBin.set_size(headerHeight, headerHeight);
+
+ headerBox.add_child(this._iconBin);
+ headerBox.insert_child_at_index(this._workspaceIndicator, isLeftButtons ? 0 : 1);
+ headerBox.insert_child_at_index(this._windowTitle, isLeftButtons ? 1 : 2);
+
+ box.insert_child_at_index(headerBox, isTopHeader ? 0 : 1);
}
- return Clutter.EVENT_PROPAGATE;
+ this.add_child(box);
+ this.add_child(this._closeButtonBin);
+
+ closeButton.connect('clicked', () => this._onCloseBtnClick());
+ this.connect('notify::hover', () => this._onHoverChanged());
+ this.connect('button-release-event', (actor, e) => this._onButtonReleaseEvent(e));
+ this.connect('destroy', () => this._onDestroy());
},
- _idleToggleCloseButton: function() {
- this._idleToggleCloseId = 0;
+ adjustOnStage: function() {
+ let closeButton = this._closeButtonBin.get_first_child();
+ let closeButtonHeight = closeButton.height;
+ let closeButtonBorderRadius = '';
- if (!this._previewBin.has_pointer &&
- !this._closeButton.has_pointer)
- this._hideCloseButton();
-
- return GLib.SOURCE_REMOVE;
- },
-
- _showCloseButton: function() {
- if (this._windowCanClose()) {
- this._closeButton.show();
- Tweener.addTween(this._closeButton,
- { opacity: 255,
- time: Workspace.CLOSE_BUTTON_FADE_TIME,
- transition: 'easeOutQuad' });
- }
- },
-
- _windowCanClose: function() {
- return this.window.can_close() &&
- !this._hasAttachedDialogs();
- },
-
- _hasAttachedDialogs: function() {
- // count trasient windows
- let n = 0;
- this.window.foreach_transient(function() {n++;});
- return n > 0;
- },
-
- _hideCloseButton: function() {
- Tweener.addTween(this._closeButton,
- { opacity: 0,
- time: Workspace.CLOSE_BUTTON_FADE_TIME,
- transition: 'easeInQuad' });
- },
-
- getThumbnail: function() {
- let thumbnail = null;
- let mutterWindow = this.window.get_compositor_private();
- if (mutterWindow) {
- thumbnail = new Clutter.Clone ({ source: mutterWindow.get_texture(), reactive: true });
- this._resizePreview(thumbnail);
-
- this._resizeId = mutterWindow.meta_window.connect('size-changed',
- Lang.bind(this, this._queueResize));
-
- this._destroyId = mutterWindow.connect('destroy', () => this.animateOutAndDestroy());
+ if (closeButton.height > MAX_CLOSE_BUTTON_SIZE) {
+ closeButtonHeight = MAX_CLOSE_BUTTON_SIZE;
+ closeButton.set_size(closeButtonHeight, closeButtonHeight);
}
- return thumbnail;
- },
-
- _queueResize: function () {
- Main.queueDeferredWork(this._workId);
- },
-
- _resizePreview: function(preview) {
- let [width, height] = preview.get_source().get_size();
- let scale = Math.min(this._thumbnailWidth / width, this._thumbnailHeight / height);
-
- preview.set_size(width * scale, height * scale);
- preview.set_position((this._thumbnailWidth - preview.width) * .5, (this._thumbnailHeight - preview.height) * .5);
- },
-
- _onResize: function() {
- if (!this.preview) {
- return;
- }
-
- this._resizePreview(this.preview);
- },
-
- _repositionCloseButton: function() {
- let isLeftButtons = Meta.prefs_get_button_layout().left_buttons.indexOf(Meta.ButtonFunction.CLOSE) >= 0;
-
- if (this._isLeftButtons !== isLeftButtons) {
- let padding = this._dtpSettings.get_int('window-preview-padding');
- let halfButton = this._closeButton.width * .5; //button is a square
- let xInset = 4;
- let buttonX;
-
- if (isLeftButtons) {
- buttonX = Math.max(this.preview.x - halfButton + xInset, -padding);
+ if (!headerHeight) {
+ closeButtonBorderRadius = 'border-radius: ';
+
+ if (isTopHeader) {
+ closeButtonBorderRadius += (isLeftButtons ? '0 0 4px 0;' : '0 0 0 4px;');
} else {
- buttonX = this.preview.x + this.preview.width - halfButton - Math.max(xInset, halfButton - this.preview.x - padding);
+ closeButtonBorderRadius += (isLeftButtons ? '0 4px 0 0;' : '4px 0 0 0;');
}
-
- this._closeButton.set_position(buttonX, Math.max(this.preview.y - halfButton, -padding));
- this._isLeftButtons = isLeftButtons;
}
+
+ this._closeButtonBin.set_style(
+ 'padding: ' + (headerHeight ? Math.round((headerHeight - closeButtonHeight) * .5 / scaleFactor) : 4) + 'px;' +
+ this._getBackgroundColor(HEADER_COLOR_OFFSET, headerHeight ? 1 : .6) +
+ closeButtonBorderRadius
+ );
},
- _closeWindow: function() {
- let topMenu = this._getTopMenu();
- if(topMenu._peekModeEnterTimeoutId) {
- Mainloop.source_remove(topMenu._peekModeEnterTimeoutId);
- topMenu._peekModeEnterTimeoutId = null;
+ assignWindow: function(window, animateSize) {
+ if (this.window != window) {
+ let _assignWindowClone = () => {
+ if (window.get_compositor_private()) {
+ let cloneBin = this._getWindowCloneBin(window);
+
+ this._resizeClone(cloneBin, window);
+ this._addClone(cloneBin, animateSize);
+ this._previewMenu.updatePosition();
+ } else {
+ Mainloop.idle_add(() => _assignWindowClone());
+ }
+ };
+
+ _assignWindowClone();
}
-
- this.window.delete(global.get_current_time());
+
+ this._removeWindowSignals();
+ this.window = window;
+ this._needsCloseButton = window.can_close() && !Utils.checkIfWindowHasTransient(window);
+ this._updateHeader();
},
- show: function(animate) {
- let fullWidth = this.actor.get_width();
-
- this.actor.opacity = 0;
- this.actor.set_width(0);
-
- let time = animate ? Taskbar.DASH_ANIMATION_TIME : 0;
- Tweener.addTween(this.actor,
- { opacity: 255,
- width: fullWidth,
- time: time,
- transition: 'easeInOutQuad'
- });
- },
-
- animateOutAndDestroy: function() {
+ animateOut: function() {
if (!this.animatingOut) {
+ let tweenOpts = getTweenOpts({ opacity: 0, width: 0, height: 0, onComplete: () => this.destroy() });
+
this.animatingOut = true;
- this._hideCloseButton();
- Tweener.addTween(this.actor,
- { width: 0,
- opacity: 0,
- time: Taskbar.DASH_ANIMATION_TIME,
- transition: 'easeOutQuad',
- onComplete: Lang.bind(this, function() {
- this.destroy();
- })
- });
+
+ Tweener.removeTweens(this);
+ Tweener.addTween(this, tweenOpts);
+ }
+ },
+
+ cancelAnimateOut: function() {
+ if (this.animatingOut) {
+ this.animatingOut = false;
+
+ Tweener.removeTweens(this);
+ Tweener.addTween(this, getTweenOpts({ opacity: 255 }));
+ }
+ },
+
+ getSize: function() {
+ let [binWidth, binHeight] = this._getBinSize();
+
+ binWidth = Math.max(binWidth, this.cloneWidth + this._padding * 2);
+ binHeight = Math.max(binHeight, this.cloneHeight + this._padding * 2) + headerHeight;
+
+ return [binWidth, binHeight];
+ },
+
+ setFocus: function(focused) {
+ this._hideOrShowCloseButton(!focused);
+ this.set_style(this._getBackgroundColor(FOCUSED_COLOR_OFFSET, focused ? '-' : 0));
+
+ if (focused) {
+ this._previewMenu.ensureVisible(this);
+ this._previewMenu.requestPeek(this.window);
}
},
activate: function() {
- let topMenu = this._getTopMenu();
-
- if(topMenu._dtpSettings.get_boolean('peek-mode')) {
- if(topMenu._peekMode) {
- topMenu._disablePeekMode();
- }
- else if(topMenu._peekModeEnterTimeoutId) {
- Mainloop.source_remove(topMenu._peekModeEnterTimeoutId);
- topMenu._peekModeEnterTimeoutId = null;
- }
- }
-
- topMenu.close(~0);
-
+ this._previewMenu.endPeekHere();
+ this._previewMenu.close();
Main.activateWindow(this.window);
},
- _onMotionEvent: function() {
- //If in normal mode, then set new timeout for entering peek mode after removing the old one
- let topMenu = this._getTopMenu();
- if(topMenu._dtpSettings.get_boolean('peek-mode')) {
- if(!topMenu._peekMode && !this.animatingOut) {
- if(topMenu._peekModeEnterTimeoutId)
- Mainloop.source_remove(topMenu._peekModeEnterTimeoutId);
- topMenu._peekModeEnterTimeoutId = Mainloop.timeout_add(topMenu._dtpSettings.get_int('enter-peek-mode-timeout'), Lang.bind(this, function() {
- topMenu._enterPeekMode(this);
- }));
- }
+ _onDestroy: function() {
+ this._removeWindowSignals();
+ },
+
+ _onHoverChanged: function() {
+ this.setFocus(this.hover);
+ },
+
+ _onCloseBtnClick: function() {
+ this.window.delete(global.get_current_time());
+ this._hideOrShowCloseButton(true);
+ this.reactive = false;
+
+ if (!this._previewMenu._dtpSettings.get_boolean('group-apps')) {
+ this._previewMenu.close();
}
},
- _onButtonReleaseEvent: function(actor, event) {
- this.actor.remove_style_pseudo_class ('active');
- switch (event.get_button()) {
- case 1:
- // Left click
- this.activate(event);
+ _onButtonReleaseEvent: function(e) {
+ switch (e.get_button()) {
+ case 1: // Left click
+ this.activate();
break;
- case 2:
- // Middle click
- if (this._dtpSettings.get_boolean('preview-middle-click-close')) {
- this._closeWindow();
+ case 2: // Middle click
+ if (this._previewMenu._dtpSettings.get_boolean('preview-middle-click-close')) {
+ this._onCloseBtnClick();
}
break;
- case 3:
- // Right click
- this.showContextMenu(event);
+ case 3: // Right click
+ this._showContextMenu(e);
break;
}
+
return Clutter.EVENT_STOP;
},
- showContextMenu: function(event) {
- let coords = event.get_coords();
- this._previewMenuPopupManager.showWindowMenuForWindow({
+ _showContextMenu: function(e) {
+ let coords = e.get_coords();
+ let currentWorkspace = this._previewMenu.peekInitialWorkspaceIndex < 0 ?
+ Utils.getCurrentWorkspace() :
+ Utils.getWorkspaceByIndex(this._previewMenu.peekInitialWorkspaceIndex);
+
+ Main.wm._showWindowMenu(null, this.window, Meta.WindowMenuType.WM, {
x: coords[0],
y: coords[1],
width: 0,
height: 0
});
+
+ let ctxMenuData = Main.wm._windowMenuManager._manager._menus[0];
+
+ ctxMenuData.menu.connect('open-state-changed', () => this._previewMenu.menu.sync_hover());
+
+ if (this.window.get_workspace() != currentWorkspace) {
+ let menuItem = new PopupMenu.PopupMenuItem(_('Move to current Workspace') + ' [' + (currentWorkspace.index() + 1) + ']');
+ let menuItems = ctxMenuData.menu.box.get_children();
+ let insertIndex = Utils.findIndex(menuItems, c => c._delegate instanceof PopupMenu.PopupSeparatorMenuItem);
+
+ insertIndex = insertIndex >= 0 ? insertIndex : menuItems.length - 1;
+ ctxMenuData.menu.addMenuItem(menuItem, insertIndex);
+ menuItem.connect('activate', () => this.window.change_workspace(currentWorkspace));
+ }
},
- destroy: function() {
- if (this._titleNotifyId) {
- this.window.disconnect(this._titleNotifyId);
- this._titleNotifyId = 0;
+ _removeWindowSignals: function() {
+ if (this._titleWindowChangeId) {
+ this.window.disconnect(this._titleWindowChangeId);
+ this._titleWindowChangeId = 0;
+ }
+ },
+
+ _updateHeader: function() {
+ if (headerHeight) {
+ let iconTextureSize = headerHeight / scaleFactor * .6;
+ let icon = this._previewMenu.getCurrentAppIcon().app.create_icon_texture(iconTextureSize);
+ let workspaceIndex = '';
+ let workspaceStyle = null;
+ let commonTitleStyles = 'color: ' + this._previewMenu._dtpSettings.get_string('window-preview-title-font-color') + ';' +
+ 'font-size: ' + this._previewMenu._dtpSettings.get_int('window-preview-title-font-size') + 'px;' +
+ 'font-weight: ' + this._previewMenu._dtpSettings.get_string('window-preview-title-font-weight') + ';';
+
+ this._iconBin.destroy_all_children();
+ this._iconBin.add_child(icon);
+
+ if (!this._previewMenu._dtpSettings.get_boolean('isolate-workspaces')) {
+ workspaceIndex = (this.window.get_workspace().index() + 1).toString();
+ workspaceStyle = 'margin: 0 4px 0 ' + (isLeftButtons ? Math.round((headerHeight - icon.width) * .5) + 'px' : '0') + '; padding: 0 4px;' +
+ 'border: 2px solid ' + this._getRgbaColor(FOCUSED_COLOR_OFFSET, .8) + 'border-radius: 2px;' + commonTitleStyles;
+ }
+
+ this._workspaceIndicator.text = workspaceIndex;
+ this._workspaceIndicator.set_style(workspaceStyle);
+
+ this._titleWindowChangeId = this.window.connect('notify::title', () => this._updateWindowTitle());
+ this._windowTitle.set_style('max-width: 0px; padding-right: 4px;' + commonTitleStyles);
+ this._updateWindowTitle();
+ }
+ },
+
+ _updateWindowTitle: function() {
+ this._windowTitle.text = this.window.title;
+ },
+
+ _hideOrShowCloseButton: function(hide) {
+ if (this._needsCloseButton) {
+ Tweener.addTween(this._closeButtonBin, getTweenOpts({ opacity: hide ? 0 : 255 }));
+ }
+ },
+
+ _getBackgroundColor: function(offset, alpha) {
+ return 'background-color: ' + this._getRgbaColor(offset, alpha) +
+ 'transition-duration:' + this._panelWrapper.dynamicTransparency.animationDuration;
+ },
+
+ _getRgbaColor: function(offset, alpha) {
+ alpha = Math.abs(alpha);
+
+ if (isNaN(alpha)) {
+ alpha = alphaBg;
}
- let mutterWindow = this.window.get_compositor_private();
+ return Utils.getrgbaColor(this._panelWrapper.dynamicTransparency.backgroundColorRgb, alpha, offset);
+ },
- if (mutterWindow) {
- if(this._resizeId) {
- mutterWindow.meta_window.disconnect(this._resizeId);
+ _addClone: function(newCloneBin, animateSize) {
+ let currentClones = this._previewBin.get_children();
+ let newCloneOpts = getTweenOpts({ opacity: 255 });
+
+ this._previewBin.add_child(newCloneBin);
+
+ if (currentClones.length) {
+ let currentCloneBin = currentClones.pop();
+ let currentCloneOpts = getTweenOpts({ opacity: 0, onComplete: () => currentCloneBin.destroy() });
+
+ if (newCloneBin.width > currentCloneBin.width) {
+ newCloneOpts.width = newCloneBin.width;
+ newCloneBin.width = currentCloneBin.width;
+ } else {
+ currentCloneOpts.width = newCloneBin.width;
}
- if(this._destroyId) {
- mutterWindow.disconnect(this._destroyId);
+ if (newCloneBin.height > currentCloneBin.height) {
+ newCloneOpts.height = newCloneBin.height;
+ newCloneBin.height = currentCloneBin.height;
+ } else {
+ currentCloneOpts.height = newCloneBin.height;
}
+
+ currentClones.forEach(c => c.destroy());
+ Tweener.addTween(currentCloneBin, currentCloneOpts);
+ } else if (animateSize) {
+ newCloneBin.width = 0;
+ newCloneBin.height = 0;
+ newCloneOpts.width = this.cloneWidth;
+ newCloneOpts.height = this.cloneHeight;
}
- this.callParent('destroy');
+ Tweener.addTween(newCloneBin, newCloneOpts);
+ },
+
+ _getWindowCloneBin: function(window) {
+ let frameRect = window.get_frame_rect();
+ let bufferRect = window.get_buffer_rect();
+ let clone = new Clutter.Clone({ source: window.get_compositor_private() });
+ let cloneBin = new St.Widget({
+ opacity: 0,
+ layout_manager: frameRect.width != bufferRect.width ||
+ frameRect.height != bufferRect.height ?
+ new WindowCloneLayout(frameRect, bufferRect) :
+ new Clutter.BinLayout()
+ });
+
+ cloneBin.add_child(clone);
+
+ return cloneBin;
+ },
+
+ _getBinSize: function() {
+ let [fixedWidth, fixedHeight] = this._previewDimensions;
+
+ return [
+ aspectRatio.x.fixed ? fixedWidth + this._padding * 2 : -1,
+ aspectRatio.y.fixed ? fixedHeight + this._padding * 2 : -1
+ ];
+ },
+
+ _resizeClone: function(cloneBin, window) {
+ let frameRect = cloneBin.layout_manager.frameRect || window.get_frame_rect();
+ let [fixedWidth, fixedHeight] = this._previewDimensions;
+ let ratio = Math.min(fixedWidth / frameRect.width, fixedHeight / frameRect.height, 1);
+ let cloneWidth = frameRect.width * ratio;
+ let cloneHeight = frameRect.height * ratio;
+
+ let clonePaddingTB = cloneHeight < MIN_DIMENSION ? MIN_DIMENSION - cloneHeight : 0;
+ let clonePaddingLR = cloneWidth < MIN_DIMENSION ? MIN_DIMENSION - cloneWidth : 0;
+ let clonePaddingTop = clonePaddingTB * .5;
+ let clonePaddingLeft = clonePaddingLR * .5;
+
+ this.cloneWidth = cloneWidth + clonePaddingLR * scaleFactor;
+ this.cloneHeight = cloneHeight + clonePaddingTB * scaleFactor;
+
+ cloneBin.set_style('padding: ' + clonePaddingTop + 'px ' + clonePaddingLeft + 'px;');
+ cloneBin.layout_manager.ratio = ratio;
+ cloneBin.layout_manager.padding = [clonePaddingLeft * scaleFactor, clonePaddingTop * scaleFactor];
+
+ cloneBin.get_first_child().set_size(cloneWidth, cloneHeight);
+ },
+
+ _getPreviewDimensions: function() {
+ let size = this._previewMenu._dtpSettings.get_int('window-preview-size') * scaleFactor;
+ let w, h;
+
+ if (this._previewMenu.isLeftOrRight) {
+ w = size;
+ h = w * aspectRatio.y.size / aspectRatio.x.size;
+ } else {
+ h = size;
+ w = h * aspectRatio.x.size / aspectRatio.y.size;
+ }
+
+ return [w, h];
}
});
-var thumbnailPreviewList = Utils.defineClass({
- Name: 'DashToPanel.ThumbnailPreviewList',
- Extends: PopupMenu.PopupMenuSection,
-
- _init: function(app, source, settings) {
- this._dtpSettings = settings;
+var WindowCloneLayout = Utils.defineClass({
+ Name: 'DashToPanel-WindowCloneLayout',
+ Extends: Clutter.BinLayout,
+ _init: function(frameRect, bufferRect) {
this.callParent('_init');
- this._ensurePreviewVisibilityTimeoutId = 0;
-
- this.actor = new St.ScrollView({ name: 'dashtopanelThumbnailScrollview',
- hscrollbar_policy: Gtk.PolicyType.NEVER,
- vscrollbar_policy: Gtk.PolicyType.NEVER,
- enable_mouse_scrolling: true });
-
- this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent ));
-
- this.box.set_vertical(false);
- this.box.set_name("dashtopanelThumbnailList");
- this.actor.add_actor(this.box);
- this.actor._delegate = this;
-
- this._shownInitially = false;
-
- this.app = app;
- this._source = source;
-
- this._redisplayId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
- this._scrollbarId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._showHideScrollbar));
-
- this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
-
- this._dtpSettingsSignalIds = [
- this._dtpSettings.connect('changed::window-preview-width', () => this._resetPreviews()),
- this._dtpSettings.connect('changed::window-preview-height', () => this._resetPreviews()),
- this._dtpSettings.connect('changed::window-preview-show-title', () => this._resetPreviews()),
- this._dtpSettings.connect('changed::window-preview-padding', () => this._resetPreviews())
- ];
-
- this._stateChangedId = this._source.window ? 0 :
- this.app.connect('windows-changed', Lang.bind(this, this._queueRedisplay));
+ //the buffer_rect contains the transparent padding that must be removed
+ this.frameRect = frameRect;
+ this.bufferRect = bufferRect;
},
- _needsScrollbar: function() {
- let topMenu = this._getTopMenu();
- let [topMinWidth, topNaturalWidth] = topMenu.actor.get_preferred_width(-1);
- let topThemeNode = topMenu.actor.get_theme_node();
+ vfunc_allocate: function(actor, box, flags) {
+ let [width, height] = box.get_size();
- let topMaxWidth = topThemeNode.get_max_width();
- return topMaxWidth >= 0 && topNaturalWidth >= topMaxWidth;
- },
+ box.set_origin(
+ (this.bufferRect.x - this.frameRect.x) * this.ratio + this.padding[0],
+ (this.bufferRect.y - this.frameRect.y) * this.ratio + this.padding[1]
+ );
- _showHideScrollbar: function() {
- let needsScrollbar = this._needsScrollbar();
+ box.set_size(
+ width + (this.bufferRect.width - this.frameRect.width) * this.ratio,
+ height + (this.bufferRect.height - this.frameRect.height) * this.ratio
+ );
- // St.ScrollView always requests space vertically for a possible horizontal
- // scrollbar if in AUTOMATIC mode. This looks bad when we *don't* need it,
- // so turn off the scrollbar when that's true. Dynamic changes in whether
- // we need it aren't handled properly.
-
- this.actor.hscrollbar_policy =
- needsScrollbar ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER;
-
- if (needsScrollbar)
- this.actor.add_style_pseudo_class('scrolled');
- else
- this.actor.remove_style_pseudo_class('scrolled');
- },
-
- _queueScrollbar: function () {
- Main.queueDeferredWork(this._scrollbarId);
- },
-
- _queueRedisplay: function () {
- Main.queueDeferredWork(this._redisplayId);
- },
-
- _onScrollEvent: function(actor, event) {
- // 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 (event_y >= actor_h - 2)
- return Clutter.EVENT_PROPAGATE;
-
- // reset timeout to avid conflicts with the mousehover event
- if (this._ensurePreviewVisibilityTimeoutId>0) {
- Mainloop.source_remove(this._ensurePreviewVisibilityTimeoutId);
- this._ensurePreviewVisibilityTimeoutId = 0;
- }
-
- // Skip to avoid double events mouse
- if (event.is_pointer_emulated())
- return Clutter.EVENT_STOP;
-
- let adjustment, delta;
-
- adjustment = this.actor.get_hscroll_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;
- delta += dx*increment;
- break;
-
- }
-
- adjustment.set_value(adjustment.get_value() + delta);
-
- return Clutter.EVENT_STOP;
-
- },
-
- _onDestroy: function() {
- for (let i = 0; i < this._dtpSettingsSignalIds.length; ++i) {
- this._dtpSettings.disconnect(this._dtpSettingsSignalIds[i]);
- }
-
- if (this._stateChangedId) {
- this.app.disconnect(this._stateChangedId);
- this._stateChangedId = 0;
- }
- },
-
- _createPreviewItem: function(window) {
- let preview = new thumbnailPreview(window, this._dtpSettings);
-
- preview.actor.connect('notify::hover', Lang.bind(this, function() {
- if (preview.actor.hover){
- this._ensurePreviewVisibilityTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function(){
- Taskbar.ensureActorVisibleInScrollView(this.actor, preview.actor);
- this._ensurePreviewVisibilityTimeoutId = 0;
- return GLib.SOURCE_REMOVE;
- }));
- } else {
- if (this._ensurePreviewVisibilityTimeoutId>0) {
- Mainloop.source_remove(this._ensurePreviewVisibilityTimeoutId);
- this._ensurePreviewVisibilityTimeoutId = 0;
- }
- }
- }));
-
- preview.actor.connect('key-focus-in',
- Lang.bind(this, function(actor) {
-
- let [x_shift, y_shift] = Taskbar.ensureActorVisibleInScrollView(this.actor, actor);
- }));
-
- return preview;
- },
-
- _resetPreviews: function() {
- let previews = this._getPreviews();
- let l = previews.length;
-
- if (l > 0) {
- for (let i = 0; i < l; ++i) {
- previews[i]._delegate.animateOutAndDestroy();
- }
-
- this._queueRedisplay();
- }
- },
-
- _getPreviews: function() {
- return this.box.get_children().filter(function(actor) {
- return actor._delegate.window &&
- actor._delegate.preview &&
- !actor._delegate.animatingOut;
- });
- },
-
- _redisplay: function () {
- let windows = this._source.window ? [this._source.window] :
- AppIcons.getInterestingWindows(this.app, this._dtpSettings, this._source.panelWrapper.monitor).sort(this.sortWindowsCompareFunction);
- let children = this._getPreviews();
- // Apps currently in the taskbar
- let oldWin = children.map(function(actor) {
- return actor._delegate.window;
- });
- // Apps supposed to be in the taskbar
- let newWin = windows;
-
- let addedItems = [];
- let removedActors = [];
-
- let newIndex = 0;
- let oldIndex = 0;
-
- while (newIndex < newWin.length || oldIndex < oldWin.length) {
- // Window removed at oldIndex
- if (oldWin[oldIndex] &&
- newWin.indexOf(oldWin[oldIndex]) == -1) {
- removedActors.push(children[oldIndex]);
- oldIndex++;
- continue;
- }
-
- // Window added at newIndex
- if (newWin[newIndex] &&
- oldWin.indexOf(newWin[newIndex]) == -1) {
- addedItems.push({ item: this._createPreviewItem(newWin[newIndex]),
- pos: newIndex });
- newIndex++;
- continue;
- }
-
- // No change at oldIndex/newIndex
- if (oldWin[oldIndex] == newWin[newIndex]) {
- oldIndex++;
- newIndex++;
- continue;
- }
-
- // Window moved
- let insertHere = newWin[newIndex + 1] &&
- newWin[newIndex + 1] == oldWin[oldIndex];
- let alreadyRemoved = removedActors.reduce(function(result, actor) {
- let removedWin = actor.window;
- return result || removedWin == newWin[newIndex];
- }, false);
-
- if (insertHere || alreadyRemoved) {
- addedItems.push({ item: this._createPreviewItem(newWin[newIndex]),
- pos: newIndex + removedActors.length });
- newIndex++;
- } else {
- removedActors.push(children[oldIndex]);
- oldIndex++;
- }
- }
-
- for (let i = 0; i < addedItems.length; i++)
- this.addMenuItem(addedItems[i].item,
- addedItems[i].pos);
-
- for (let i = 0; i < removedActors.length; i++) {
- let item = removedActors[i];
- item._delegate.animateOutAndDestroy();
- }
-
- // Skip animations on first run when adding the initial set
- // of items, to avoid all items zooming in at once
-
- let animate = this._shownInitially;
-
- if (!this._shownInitially)
- this._shownInitially = true;
-
- for (let i = 0; i < addedItems.length; i++) {
- addedItems[i].item.show(animate);
- }
-
- this._queueScrollbar();
-
- // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
- // Without it, StBoxLayout may use a stale size cache
- this.box.queue_relayout();
-
- if (windows.length < 1) {
- this._getTopMenu().close(~0);
- }
- },
-
- isAnimatingOut: function() {
- return this.actor.get_children().reduce(function(result, actor) {
- return result || actor.animatingOut;
- }, false);
- },
-
- sortWindowsCompareFunction: function(windowA, windowB) {
- return windowA.get_stable_sequence() > windowB.get_stable_sequence();
+ actor.get_first_child().allocate(box, flags);
}
});
-var previewMenuPopup = Utils.defineClass({
- Name: 'previewMenuPopup',
- Extends: WindowMenu.WindowMenu,
- ParentConstrParams: [[0], [1]],
+function getTweenOpts(opts) {
+ let defaults = {
+ time: animationTime,
+ transition: 'easeInOutQuad'
+ };
- _init: function(window, sourceActor) {
- this.callParent('_init', window, sourceActor);
-
- let side = Taskbar.getPosition();
- this._arrowSide = side;
- this._boxPointer._arrowSide = side;
- this._boxPointer._userArrowSide = side;
- }
-
- // Otherwise, just let the parent do its thing?
-});
-
-var previewMenuPopupManager = Utils.defineClass({
- Name: 'previewMenuPopupManagerTest',
-
- _init: function(window, source) {
- this._manager = new PopupMenu.PopupMenuManager({ actor: Main.layoutManager.dummyCursor });
-
- this._sourceActor = new St.Widget({ reactive: true, visible: false });
- this._sourceActor.connect('button-press-event', Lang.bind(this,
- function() {
- this._manager.activeMenu.toggle();
- }));
- Main.uiGroup.add_actor(this._sourceActor);
-
- this.window = window;
- },
-
- showWindowMenuForWindow: function(rect) {
- let menu = new previewMenuPopup(this.window, this._sourceActor);
- let window = this.window;
-
- this._manager.addMenu(menu);
-
- menu.connect('activate', function() {
- window.check_alive(global.get_current_time());
- });
- let destroyId = window.connect('unmanaged',
- function() {
- menu.close();
- });
-
- this._sourceActor.set_size(Math.max(1, rect.width), Math.max(1, rect.height));
- this._sourceActor.set_position(rect.x, rect.y);
-
- this._sourceActor.show();
-
- menu.open(BoxPointer.PopupAnimation.NONE);
- menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
- menu.connect('open-state-changed', Lang.bind(this, function(menu_, isOpen) {
- if (isOpen)
- return;
-
- this._sourceActor.hide();
- menu.destroy();
- window.disconnect(destroyId);
- }));
- }
-});
+ return Utils.mergeObjects(opts || {}, defaults);
+}
\ No newline at end of file