From 207cc5777207a76a97b4da7fa5b064f43d37c5ce Mon Sep 17 00:00:00 2001 From: Charles Gagnon Date: Tue, 14 May 2019 17:08:08 -0400 Subject: [PATCH] Add initial window clones --- stylesheet.css | 3 +- windowPreview.js | 217 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 176 insertions(+), 44 deletions(-) diff --git a/stylesheet.css b/stylesheet.css index 05cb091..94981b7 100644 --- a/stylesheet.css +++ b/stylesheet.css @@ -41,8 +41,9 @@ padding-right: 8px; } -#dashtopanelThumbnailScrollview { +#dashtopanelPreviewScrollview { -st-hfade-offset: 48px; + -st-vfade-offset: 48px; } #dashtopanelScrollview .app-well-app:hover .overview-icon, diff --git a/windowPreview.js b/windowPreview.js index 2843c17..e08b6d0 100644 --- a/windowPreview.js +++ b/windowPreview.js @@ -15,6 +15,8 @@ * along with this program. If not, see . */ +const Clutter = imports.gi.Clutter; +const Gtk = imports.gi.Gtk; const Main = imports.ui.main; const Signals = imports.signals; const Shell = imports.gi.Shell; @@ -25,7 +27,6 @@ const Me = imports.misc.extensionUtils.getCurrentExtension(); const Taskbar = Me.imports.taskbar; const Utils = Me.imports.utils; -const TRANSLATION_OFFSET = 50; const MONITOR_PADDING = 10; //timeout intervals @@ -39,87 +40,140 @@ var PreviewMenu = Utils.defineClass({ Extends: St.Widget, _init: function(dtpSettings, panelWrapper) { - this.callParent('_init', { name: 'preview-menu', reactive: true }); + this.callParent('_init', { name: 'preview-menu', layout_manager: new Clutter.BinLayout(), reactive: true, track_hover: true }); this._dtpSettings = dtpSettings; this._panelWrapper = panelWrapper; this._currentAppIcon = null; this._position = Taskbar.getPosition(); - this._translationProp = 'translation_' + (this._position == St.Side.LEFT || this._position == St.Side.RIGHT ? 'x' : 'y'); - this[this._translationProp] = TRANSLATION_OFFSET; + let isLeftOrRight = this._position == St.Side.LEFT || this._position == St.Side.RIGHT; + this._translationProp = 'translation_' + (isLeftOrRight ? 'x' : 'y'); + this._translationOffset = Math.min(this._dtpSettings.get_int('panel-size'), 40); + + this._box = new St.BoxLayout({ + vertical: isLeftOrRight, + clip_to_allocation: false, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.START + }); + + this._scrollView = new St.ScrollView({ + name: 'dashtopanelPreviewScrollview', + hscrollbar_policy: Gtk.PolicyType.NEVER, + vscrollbar_policy: Gtk.PolicyType.NEVER, + enable_mouse_scrolling: true + }); + + this._scrollView.add_actor(this._box); + this.add_child(this._scrollView); + //testing - this.set_style('background: #ff0000;') + //this._scrollView.set_style('padding: 10px;'); //TODO //'open-state-changed' //'menu-closed' //'sync-tooltip' //this.add_style_class_name('app-well-menu'); + + // this._titleWindowChangeId = this.window.connect('notify::title', + // Lang.bind(this, this._updateWindowTitle)); }, enable: function() { - this._signalsHandler = new Utils.GlobalSignalsHandler(); this._timeoutsHandler = new Utils.TimeoutsHandler(); - - this.visible = false; - this.opacity = 0; + this._signalsHandler = new Utils.GlobalSignalsHandler(); Main.uiGroup.insert_child_below(this, this._panelWrapper.panelBox); - - // this._signalsHandler.add( - // [ - - // ], - // ); + Main.layoutManager._trackActor(this, { affectsStruts: false, trackFullscreen: true }); + this._resetHiddenState(); + + this._signalsHandler.add( + [ + this, + 'notify::hover', + () => this._onHoverChanged() + ], + ); }, disable: function() { - this._signalsHandler.destroy(); this._timeoutsHandler.destroy(); + this._signalsHandler.destroy(); this.close(true); + + Main.layoutManager._untrackActor(this); Main.uiGroup.remove_child(this); }, requestOpen: function(appIcon) { this._endOpenCloseTimeouts(); - - if (this._currentAppIcon) { - return this.open(appIcon); - } - this._timeoutsHandler.add([T1, this._dtpSettings.get_int('show-window-previews-timeout'), () => this.open(appIcon)]); }, requestClose: function() { this._endOpenCloseTimeouts(); - this._timeoutsHandler.add([T2, this._dtpSettings.get_int('leave-timeout'), () => this.close()]); + this._addCloseTimeout(); }, open: function(appIcon) { this._currentAppIcon = appIcon; + this.updateWindows(appIcon); this._updatePosition(); - this.show(); + this.visible = true; this._animateOpenOrClose(true); }, close: function(immediate) { this._currentAppIcon = null; - if (this.visible) { - this._animateOpenOrClose(false, immediate, () => { - this.hide(); - }); + if (immediate) { + this._resetHiddenState(); + } else { + this._animateOpenOrClose(false, () => this._resetHiddenState()); } }, updateWindows: function(appIcon, windows) { if (this._currentAppIcon == appIcon) { + windows = windows || (appIcon.window ? [appIcon.window] : appIcon.getAppIconInterestingWindows()); + let currentPreviews = this._box.get_children(); + + for (let i = 0, l = currentPreviews.length; i < l; ++i) { + if (Taskbar.findIndex(windows, w => w == currentPreviews[i].window) < 0) { + this._box.remove_child(currentPreviews[i]); + // currentPreviews[i]._animatingOut = 1; + // Tweener.addTween(currentPreviews[i], { + // width: 0, + // opacity: 0, + // time: Taskbar.DASH_ANIMATION_TIME, + // transition: 'easeInOutQuad', + // onComplete: () => this._box.remove_child(currentPreviews[i]) + // }) + } + } + + for (let i = 0, l = windows.length; i < l; ++i) { + let currentPosition = Taskbar.findIndex(currentPreviews, cp => cp.window == windows[i]); + let preview; + + if (currentPosition == i) { + continue; + } else if (currentPosition < 0) { + preview = new Preview(windows[i]); + preview.set_style('background: ' + this._panelWrapper.dynamicTransparency.currentBackgroundColor + 'padding: 10px;'); + } else { + preview = currentPreviews[currentPosition]; + } + + this._box.insert_child_at_index(preview, i); + } } }, @@ -127,16 +181,35 @@ var PreviewMenu = Utils.defineClass({ return this._currentAppIcon; }, - vfunc_allocate: function(box, flags) { - this.set_allocation(box, flags); - }, + // vfunc_allocate: function(box, flags) { + // this.callParent('vfunc_allocate', box, flags); + // this._scrollView.set_clip(0, 0, box.x2 - box.x1, box.y2 - box.y1 - this.translation_y); + // }, vfunc_get_preferred_width: function(forHeight) { - return [0, 300]; + let [, width] = St.Widget.prototype.vfunc_get_preferred_width.call(this, forHeight); + let maxWidth = this._panelWrapper.monitor.width - MONITOR_PADDING * 2; + + return [0, Math.min(width, maxWidth)]; }, vfunc_get_preferred_height: function(forWidth) { - return [0, 200]; + let [, height] = St.Widget.prototype.vfunc_get_preferred_height.call(this, forWidth); + let maxHeight = this._panelWrapper.monitor.height - MONITOR_PADDING * 2; + + return [0, Math.min(height, maxHeight)]; + }, + + _addCloseTimeout: function() { + this._timeoutsHandler.add([T2, this._dtpSettings.get_int('leave-timeout'), () => this.close()]); + }, + + _onHoverChanged: function() { + this._endOpenCloseTimeouts(); + + if (!this.hover) { + this._addCloseTimeout(); + } }, _endOpenCloseTimeouts: function() { @@ -144,15 +217,26 @@ var PreviewMenu = Utils.defineClass({ this._timeoutsHandler.remove(T2); }, + _resetHiddenState: function() { + this.visible = false; + this.opacity = 0; + this[this._translationProp] = this._translationOffset; + //this._box.remove_all_children(); + }, + _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 [minWidth, minHeight, natWidth, natHeight] = this.get_preferred_size(); + //let removedChildren = this._box.get_children().filter(c => c._animatingOut); + //let excessWidth = 0; let x, y; + + //removedChildren.forEach(rc => excessWidth += rc.width); if (this._position == St.Side.TOP || this._position == St.Side.BOTTOM) { - x = sourceAllocation.x1 + (sourceContentBox.x2 - sourceContentBox.x1) * .5 - natWidth * .5; + x = sourceAllocation.x1 + (sourceContentBox.x2 - sourceContentBox.x1) * .5 - natWidth * .5 /*+ excessWidth * .5*/; } else if (this._position == St.Side.LEFT) { x = sourceAllocation.x2; } else { //St.Side.RIGHT @@ -170,9 +254,9 @@ var PreviewMenu = Utils.defineClass({ x = Math.max(x, this._panelWrapper.monitor.x + MONITOR_PADDING); y = Math.max(y, this._panelWrapper.monitor.y + MONITOR_PADDING); - if (this[this._translationProp] > 0) { + if (this[this._translationProp] != 0) { this.set_position(x, y); - this[this._translationProp] = TRANSLATION_OFFSET; + this[this._translationProp] = this._translationOffset; } else { Tweener.addTween(this, { x: x, y: y, @@ -182,19 +266,66 @@ var PreviewMenu = Utils.defineClass({ } }, - _animateOpenOrClose: function(show, immediate, onComplete) { + _animateOpenOrClose: function(show, onComplete) { + let isTranslationAnimation = this[this._translationProp] !=0; let tweenOpts = { opacity: show ? 255 : 0, - time: immediate ? 0 : Taskbar.DASH_ANIMATION_TIME, - transition: show ? 'easeInOutQuad' : 'easeInCubic' + time: Taskbar.DASH_ANIMATION_TIME, + transition: show ? 'easeInOutQuad' : 'easeInCubic', + onComplete: () => { + if (isTranslationAnimation) { + Main.layoutManager._queueUpdateRegions(); + } + + (onComplete || (() => {}))(); + } }; - tweenOpts[this._translationProp] = show ? 0 : TRANSLATION_OFFSET; - - if (onComplete) { - tweenOpts.onComplete = onComplete; - } + tweenOpts[this._translationProp] = show ? 0 : this._translationOffset; Tweener.addTween(this, tweenOpts); }, +}); + +var Preview = Utils.defineClass({ + Name: 'DashToPanel.Preview', + Extends: St.Widget, + + _init: function(window) { + this.callParent('_init', { name: 'preview-menu', reactive: true }); + + this.window = window; + this.add_actor(this.getThumbnail()); + + // this._windowTitle = new St.Label({ + // y_align: Clutter.ActorAlign.CENTER, + // x_align: Clutter.ActorAlign.START, + // style_class: 'overview-label' + // }); + }, + + getThumbnail: function() { + let clone = null; + let mutterWindow = this.window.get_compositor_private(); + + if (mutterWindow) { + clone = new Clutter.Clone ({ source: mutterWindow.get_texture(), reactive: true }); + this._resize(clone); + + // this._resizeId = mutterWindow.meta_window.connect('size-changed', + // Lang.bind(this, this._queueResize)); + + // this._destroyId = mutterWindow.connect('destroy', () => this.animateOutAndDestroy()); + } + + return clone; + }, + + _resize: function(clone) { + let [width, height] = clone.get_source().get_size(); + //let scale = Math.min(this._thumbnailWidth / width, this._thumbnailHeight / height); + + clone.set_size(200, 150); + clone.set_position(10, 10); + }, }); \ No newline at end of file