From 6f5ba4263b7c2e5e7d989b87919aaecaeaded4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 12 Feb 2019 13:04:55 +0100 Subject: [PATCH 1/8] workspace: Continue overlay idle hide timeout when pointing at close button While it makes sense to remove the timeout in every other case, restart it if the pointer is hovering over the close button to make sure the overlay will be hidden afterwards. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/136 --- js/ui/workspace.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 9c58861449..50fabdbe57 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -673,10 +673,12 @@ var WindowOverlay = class { } _idleHideOverlay() { + if (this.closeButton['has-pointer']) + return GLib.SOURCE_CONTINUE; + this._idleHideOverlayId = 0; - if (!this._windowClone.actor['has-pointer'] && - !this.closeButton['has-pointer']) + if (!this._windowClone.actor['has-pointer']) this._animateInvisible(); return GLib.SOURCE_REMOVE; -- GitLab From a4bebaf4946e17c8c56ae13d67801b3c0aff6c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 12 Feb 2019 13:04:55 +0100 Subject: [PATCH 2/8] workspace: Refactor hiding and showing of window overlays Right now the window overlay is shown and hidden using the hide() and show() functions of the overlay elements together with manipulating the opacities. This causes some bugs: For example the close button will also react to clicks if the overlay isn't visible and reentering window clones will flash the overlay. Instead, always use hide() and show() for the overlay elements and add a forceHidden property to hide the overlay during dragging. We can't initially hide the overlays because we have to read the border size and more values right after the overlays are initialized since they are needed in chromeHeights() and chromeWidths(). This is only possible if the overlay elements are on the stage before they are visible. Also fade in and fade out the overlays on hover. Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/413 Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/58 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/136 --- js/ui/workspace.js | 191 +++++++++++++++++++++++++-------------------- 1 file changed, 106 insertions(+), 85 deletions(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 50fabdbe57..17d7fe647f 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -443,6 +443,8 @@ var WindowOverlay = class { this._windowClone = windowClone; this._parentActor = parentActor; this._hidden = false; + this._forceHidden = false; + this._forceHiddenDragging = false; this._idleHideOverlayId = 0; @@ -468,12 +470,9 @@ var WindowOverlay = class { this.closeButton.connect('clicked', () => this._windowClone.deleteAll()); windowClone.actor.connect('destroy', this._onDestroy.bind(this)); - windowClone.connect('show-chrome', this._onShowChrome.bind(this)); + windowClone.connect('show-chrome', () => this.show(true)); windowClone.connect('hide-chrome', this._onHideChrome.bind(this)); - this.title.hide(); - this.closeButton.hide(); - // Don't block drop targets Shell.util_set_hidden_from_pick(this.title, true); Shell.util_set_hidden_from_pick(this.border, true); @@ -494,17 +493,86 @@ var WindowOverlay = class { this._onStyleChanged(); } - hide() { - this._hidden = true; + show(animate) { + if (!this._hidden || this._forceHidden || this._forceHiddenDragging) + return; - this.hideOverlay(); - } + this._parentActor.raise_top(); + + let toShow = [this.border, this.title]; + if (this._windowCanClose()) + toShow.push(this.closeButton); - show() { this._hidden = false; - if (this._windowClone.actor['has-pointer']) - this._animateVisible(); + if (animate) { + toShow.forEach(e => { + e.opacity = 0; + e.show(); + Tweener.addTween(e, + { opacity: 255, + time: WINDOW_OVERLAY_FADE_TIME, + transition: 'easeOutQuad' }); + }); + } else { + toShow.forEach(e => { + e.opacity = 255; + e.show(); + }); + } + + this.emit('chrome-visible'); + } + + hide(animate) { + if (this._hidden) + return; + + if (this._idleHideOverlayId > 0) { + Mainloop.source_remove(this._idleHideOverlayId); + this._idleHideOverlayId = 0; + } + + let toHide = [this.closeButton, this.border, this.title]; + + if (animate) { + toHide.forEach(e => { + e.opacity = 255; + Tweener.addTween(e, + { opacity: 0, + time: WINDOW_OVERLAY_FADE_TIME, + transition: 'easeInQuad', + onComplete: () => { + e.hide(); + this._hidden = true; + } + }); + }); + } else { + toHide.forEach(e => { + e.opacity = 0; + e.hide(); + }); + this._hidden = true; + } + } + + forceHide(hide) { + if (hide) { + this._forceHidden = true; + this.hide(); + } else { + this._forceHidden = false; + } + } + + forceHideDragging(hide) { + if (hide) { + this._forceHiddenDragging = true; + this.hide(); + } else { + this._forceHiddenDragging = false; + } } chromeHeights() { @@ -626,72 +694,22 @@ var WindowOverlay = class { this.border.destroy(); } - _animateVisible() { - this._parentActor.raise_top(); - - let toAnimate = [this.border, this.title]; - if (this._windowCanClose()) - toAnimate.push(this.closeButton); - - toAnimate.forEach(a => { - a.show(); - a.opacity = 0; - Tweener.addTween(a, - { opacity: 255, - time: WINDOW_OVERLAY_FADE_TIME, - transition: 'easeOutQuad' }); - }); - } - - _animateInvisible() { - [this.closeButton, this.border, this.title].forEach(a => { - a.opacity = 255; - Tweener.addTween(a, - { opacity: 0, - time: WINDOW_OVERLAY_FADE_TIME, - transition: 'easeInQuad' }); - }); - } - - _onShowChrome() { - // We might get enter events on the clone while the overlay is - // hidden, e.g. during animations, we ignore these events, - // as the close button will be shown as needed when the overlays - // are shown again - if (this._hidden) - return; - - this._animateVisible(); - this.emit('chrome-visible'); - } - _onHideChrome() { if (this._idleHideOverlayId == 0) { - this._idleHideOverlayId = Mainloop.timeout_add(WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT, this._idleHideOverlay.bind(this)); - GLib.Source.set_name_by_id(this._idleHideOverlayId, '[gnome-shell] this._idleHideOverlay'); - } - } + this._idleHideOverlayId = Mainloop.timeout_add(WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT, () => { + if (this.closeButton['has-pointer']) + return GLib.SOURCE_CONTINUE; - _idleHideOverlay() { - if (this.closeButton['has-pointer']) - return GLib.SOURCE_CONTINUE; + this._idleHideOverlayId = 0; - this._idleHideOverlayId = 0; + if (!this._windowClone.actor['has-pointer']) + this.hide(true); - if (!this._windowClone.actor['has-pointer']) - this._animateInvisible(); - - return GLib.SOURCE_REMOVE; - } + return GLib.SOURCE_REMOVE; + }); - hideOverlay() { - if (this._idleHideOverlayId > 0) { - Mainloop.source_remove(this._idleHideOverlayId); - this._idleHideOverlayId = 0; + GLib.Source.set_name_by_id(this._idleHideOverlayId, '[gnome-shell] this._idleHideOverlay'); } - this.closeButton.hide(); - this.border.hide(); - this.title.hide(); } _onStyleChanged() { @@ -1144,6 +1162,15 @@ var Workspace = class { this._windowEnteredMonitor.bind(this)); this._windowLeftMonitorId = global.display.connect('window-left-monitor', this._windowLeftMonitor.bind(this)); + + let disableOverlays = () => this._windowOverlays.forEach(o => o.forceHideDragging(true)); + let enableOverlays = () => this._windowOverlays.forEach(o => o.forceHideDragging(false)); + + this._windowDragBeginId = Main.overview.connect('window-drag-begin', disableOverlays); + this._windowDragEndId = Main.overview.connect('window-drag-end', enableOverlays); + this._itemDragBeginId = Main.overview.connect('item-drag-begin', disableOverlays); + this._itemDragEndId = Main.overview.connect('item-drag-end', enableOverlays); + this._repositionWindowsId = 0; this.leavingOverview = false; @@ -1310,7 +1337,7 @@ var Workspace = class { clone.overlay.setMaxChromeWidth(Math.round(maxChromeWidth)); if (clone.overlay && (initialPositioning || !clone.positioned)) - clone.overlay.hide(); + clone.overlay.forceHide(true); if (!clone.positioned) { // This window appeared after the overview was already up @@ -1350,7 +1377,7 @@ var Workspace = class { clone.actor.set_scale(scale, scale); clone.actor.set_opacity(255); clone.overlay.relayout(false); - this._showWindowOverlay(clone, clone.overlay); + clone.overlay.forceHide(false); } } } @@ -1383,22 +1410,12 @@ var Workspace = class { scale_y: scale, time: Overview.ANIMATION_TIME, transition: 'easeOutQuad', - onComplete: () => { - this._showWindowOverlay(clone, overlay); - } + onComplete: () => overlay.forceHide(false) }); clone.overlay.relayout(true); } - _showWindowOverlay(clone, overlay) { - if (clone.inDrag) - return; - - if (overlay && overlay._hidden) - overlay.show(); - } - _delayedWindowRepositioning() { let [x, y, mask] = global.get_pointer(); @@ -1775,6 +1792,11 @@ var Workspace = class { global.display.disconnect(this._windowEnteredMonitorId); global.display.disconnect(this._windowLeftMonitorId); + Main.overview.disconnect(this._windowDragBeginId); + Main.overview.disconnect(this._windowDragEndId); + Main.overview.disconnect(this._itemDragBeginId); + Main.overview.disconnect(this._itemDragEndId); + if (this._repositionWindowsId > 0) { Mainloop.source_remove(this._repositionWindowsId); this._repositionWindowsId = 0; @@ -1826,7 +1848,6 @@ var Workspace = class { this._onCloneSelected.bind(this)); clone.connect('drag-begin', () => { Main.overview.beginWindowDrag(clone.metaWindow); - overlay.hide(); }); clone.connect('drag-cancelled', () => { Main.overview.cancelledWindowDrag(clone.metaWindow); @@ -1851,7 +1872,7 @@ var Workspace = class { this._windowOverlays.forEach(o => { if (o != overlay) - o.hideOverlay(); + o.hide(); }); }); -- GitLab From 3e4df1f89221b9eb54d899af83cc0a579dc015fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 12 Feb 2019 13:04:55 +0100 Subject: [PATCH 3/8] workspace: Don't show the overlay again after dragging There's no reason to show the overlay again if a drag ended and the mouse isn't hovering over the clone. If it is hovering over the clone, an enter-event will be fired anyway and the overlay will be shown. This is a leftover of the old window overlays where the titles were always shown except during dragging and reordering windows. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/136 --- js/ui/workspace.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 17d7fe647f..63d14322e0 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -1854,7 +1854,6 @@ var Workspace = class { }); clone.connect('drag-end', () => { Main.overview.endWindowDrag(clone.metaWindow); - overlay.show(); }); clone.connect('size-changed', () => { this._recalculateWindowPositions(WindowPositionFlags.NONE); -- GitLab From ff4fb1202ad9275b598efb1136139cb7b849e466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 12 Feb 2019 13:04:55 +0100 Subject: [PATCH 4/8] workspace: Don't show window overlays during any animations Hide the window overlay of a clone while it gets animated to a new position. This gives us less cases where we have to deal with the overlays and fixes small visual issues appearing during the animation of moving the overlay. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/136 --- js/ui/workspace.js | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 63d14322e0..6efc241e3f 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -443,7 +443,7 @@ var WindowOverlay = class { this._windowClone = windowClone; this._parentActor = parentActor; this._hidden = false; - this._forceHidden = false; + this._forceHiddenAnimating = false; this._forceHiddenDragging = false; this._idleHideOverlayId = 0; @@ -460,7 +460,7 @@ var WindowOverlay = class { this._updateCaptionId = metaWindow.connect('notify::title', w => { this.title.text = this._getCaption(); - this.relayout(false); + this.relayout(); }); this.closeButton = new St.Button({ style_class: 'window-close' }); @@ -494,7 +494,7 @@ var WindowOverlay = class { } show(animate) { - if (!this._hidden || this._forceHidden || this._forceHiddenDragging) + if (!this._hidden || this._forceHiddenAnimating || this._forceHiddenDragging) return; this._parentActor.raise_top(); @@ -557,12 +557,12 @@ var WindowOverlay = class { } } - forceHide(hide) { + forceHideAnimating(hide) { if (hide) { - this._forceHidden = true; + this._forceHiddenAnimating = true; this.hide(); } else { - this._forceHidden = false; + this._forceHiddenAnimating = false; } } @@ -1336,9 +1336,6 @@ var Workspace = class { area.x + area.width - cloneCenter); clone.overlay.setMaxChromeWidth(Math.round(maxChromeWidth)); - if (clone.overlay && (initialPositioning || !clone.positioned)) - clone.overlay.forceHide(true); - if (!clone.positioned) { // This window appeared after the overview was already up // Grow the clone from the center of the slot @@ -1371,13 +1368,15 @@ var Workspace = class { this._animateClone(clone, clone.overlay, x, y, scale); } else { + clone.overlay.forceHideAnimating(true); + // cancel any active tweens (otherwise they might override our changes) Tweener.removeTweens(clone.actor); clone.actor.set_position(x, y); clone.actor.set_scale(scale, scale); clone.actor.set_opacity(255); - clone.overlay.relayout(false); - clone.overlay.forceHide(false); + clone.overlay.relayout(); + clone.overlay.forceHideAnimating(false); } } } @@ -1403,6 +1402,8 @@ var Workspace = class { } _animateClone(clone, overlay, x, y, scale) { + overlay.forceHideAnimating(true); + Tweener.addTween(clone.actor, { x: x, y: y, @@ -1410,10 +1411,10 @@ var Workspace = class { scale_y: scale, time: Overview.ANIMATION_TIME, transition: 'easeOutQuad', - onComplete: () => overlay.forceHide(false) + onComplete: () => overlay.forceHideAnimating(false) }); - clone.overlay.relayout(true); + clone.overlay.relayout(); } _delayedWindowRepositioning() { @@ -1544,7 +1545,7 @@ var Workspace = class { clone.actor.set_position(x, y); clone.actor.set_scale(scale, scale); - clone.overlay.relayout(false); + clone.overlay.relayout(); } this._currentLayout = null; -- GitLab From 77e953f9ddef688c0b14b26a2e0d9ed0fea8dc03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 12 Feb 2019 13:04:55 +0100 Subject: [PATCH 5/8] workspace: Check hover after overlays have been forced hidden Since the pointer might have entered a clone while the overlays were forced hidden, the overlay would not be visible even though the pointer is hovering over a clone. Fix this by checking whether the clone has the pointer (and showing the overlay if it does) after setting force hidden to false. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/136 --- js/ui/workspace.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 6efc241e3f..0b4c02988b 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -563,6 +563,8 @@ var WindowOverlay = class { this.hide(); } else { this._forceHiddenAnimating = false; + if (this._windowClone.actor['has-pointer']) + this.show(); } } @@ -572,6 +574,8 @@ var WindowOverlay = class { this.hide(); } else { this._forceHiddenDragging = false; + if (this._windowClone.actor['has-pointer']) + this.show(); } } -- GitLab From d58c15ba268fb4675f8763a347983f5b84339d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 12 Feb 2019 13:04:55 +0100 Subject: [PATCH 6/8] workspace: Reset idle hide timeout for overlays on reentry Instead of returning and waiting until the old timeout is finished, start a new idle hide timeout for the overlay when the pointer enters a window clone. This makes sure the timeout for hiding the overlay after the pointer left the clone always stays the same. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/136 --- js/ui/workspace.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 0b4c02988b..3ee327f27d 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -699,21 +699,22 @@ var WindowOverlay = class { } _onHideChrome() { - if (this._idleHideOverlayId == 0) { - this._idleHideOverlayId = Mainloop.timeout_add(WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT, () => { - if (this.closeButton['has-pointer']) - return GLib.SOURCE_CONTINUE; + if (this._idleHideOverlayId > 0) + Mainloop.source_remove(this._idleHideOverlayId); - this._idleHideOverlayId = 0; + this._idleHideOverlayId = Mainloop.timeout_add(WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT, () => { + if (this.closeButton['has-pointer']) + return GLib.SOURCE_CONTINUE; - if (!this._windowClone.actor['has-pointer']) - this.hide(true); + this._idleHideOverlayId = 0; - return GLib.SOURCE_REMOVE; - }); + if (!this._windowClone.actor['has-pointer']) + this.hide(true); - GLib.Source.set_name_by_id(this._idleHideOverlayId, '[gnome-shell] this._idleHideOverlay'); - } + return GLib.SOURCE_REMOVE; + }); + + GLib.Source.set_name_by_id(this._idleHideOverlayId, '[gnome-shell] this._idleHideOverlay'); } _onStyleChanged() { -- GitLab From 5f30ba26105f1397f165a2996e62c28e8ad36557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 12 Feb 2019 13:04:55 +0100 Subject: [PATCH 7/8] workspace: Make title of overlay reactive Also make the title of the overlay reactive so we can keep the overlay visible if the mouse is hovering over the title. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/136 --- js/ui/workspace.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 3ee327f27d..c1a3eee55c 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -452,7 +452,8 @@ var WindowOverlay = class { this.border = new St.Bin({ style_class: 'window-clone-border' }); this.title = new St.Label({ style_class: 'window-caption', - text: this._getCaption() }); + text: this._getCaption(), + reactive: true }); this.title.clutter_text.ellipsize = Pango.EllipsizeMode.END; windowClone.actor.label_actor = this.title; @@ -474,7 +475,6 @@ var WindowOverlay = class { windowClone.connect('hide-chrome', this._onHideChrome.bind(this)); // Don't block drop targets - Shell.util_set_hidden_from_pick(this.title, true); Shell.util_set_hidden_from_pick(this.border, true); parentActor.add_actor(this.border); @@ -703,7 +703,8 @@ var WindowOverlay = class { Mainloop.source_remove(this._idleHideOverlayId); this._idleHideOverlayId = Mainloop.timeout_add(WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT, () => { - if (this.closeButton['has-pointer']) + if (this.closeButton['has-pointer'] || + this.title['has-pointer']) return GLib.SOURCE_CONTINUE; this._idleHideOverlayId = 0; -- GitLab From 39c8c9ac2b007e359e34d77f98beb9e180da023c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 12 Feb 2019 13:04:55 +0100 Subject: [PATCH 8/8] workspace: Only remove relevant tweens when animating relayout Only remove the tweens we change while doing a relayout and don't touch the ones for fading in and out the overlay. This makes sure the opacity of the overlay animation won't get stopped somewhere between 0 and 255. Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/420 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/136 --- js/ui/workspace.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index c1a3eee55c..bf444203bf 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -601,9 +601,10 @@ var WindowOverlay = class { let title = this.title; let border = this.border; - Tweener.removeTweens(button); - Tweener.removeTweens(border); - Tweener.removeTweens(title); + // Only remove relayout tweens, not ones for fading + Tweener.removeTweens(button, "x", "y", "width", "height"); + Tweener.removeTweens(title, "x", "y", "width", "height"); + Tweener.removeTweens(border, "x", "y", "width", "height"); let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot; -- GitLab