From 29601b84d4b472530bd1725917478b5b49a73b81 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 8 Jun 2022 11:28:48 -0300 Subject: [PATCH 01/22] appDisplay: Don't update fade effect We'll have to dismantle parts of AppDisplay and BaseAppView in order to introduce navigation arrows in a way that won't drive people insane. Start this dismantling by removing the fade effect. It looks odd for now, since we still set padding to the app grid, but that will change soon too. Part-of: --- js/ui/appDisplay.js | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 0768dee34d..3bf9a27e20 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -207,7 +207,6 @@ var BaseAppView = GObject.registerClass({ const scroll = this._scrollView.hscroll; this._adjustment = scroll.adjustment; this._adjustment.connect('notify::value', adj => { - this._updateFade(); const value = adj.value / adj.page_size; this._pageIndicators.setCurrentPosition(value); @@ -386,40 +385,6 @@ var BaseAppView = GObject.registerClass({ effect.extend_fade_area = true; } - _updateFade() { - const { pagePadding } = this._grid.layout_manager; - - if (this._pagesShown) - return; - - if (pagePadding.top === 0 && - pagePadding.right === 0 && - pagePadding.bottom === 0 && - pagePadding.left === 0) - return; - - let hOffset = 0; - let vOffset = 0; - - if ((this._adjustment.value % this._adjustment.page_size) !== 0.0) { - const vertical = this._orientation === Clutter.Orientation.VERTICAL; - - hOffset = vertical ? 0 : Math.max(pagePadding.left, pagePadding.right); - vOffset = vertical ? Math.max(pagePadding.top, pagePadding.bottom) : 0; - - if (hOffset === 0 && vOffset === 0) - return; - } - - this._scrollView.update_fade_effect( - new Clutter.Margin({ - left: hOffset, - right: hOffset, - top: vOffset, - bottom: vOffset, - })); - } - _createGrid() { return new IconGrid.IconGrid({ allow_incomplete_pages: true }); } -- GitLab From 1fb570b415f88a97982b390c616fe553c7fe00d6 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 8 Jun 2022 11:32:58 -0300 Subject: [PATCH 02/22] appDisplay: Remove stack from AppDisplay The stack widget once was a ShellStack and had a prominent role in layouting AppDisplay, but after a series of reworks it's now effectively unused, and can be safely removed. Remove the '_stack' widget from AppDisplay. Part-of: --- js/ui/appDisplay.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 3bf9a27e20..b0f85f377f 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -1336,13 +1336,7 @@ class AppDisplay extends BaseAppView { this._pageManager = new PageManager(); this._pageManager.connect('layout-changed', () => this._redisplay()); - this._stack = new St.Widget({ - layout_manager: new Clutter.BinLayout(), - x_expand: true, - y_expand: true, - }); - this.add_actor(this._stack); - this._stack.add_child(this._box); + this.add_child(this._box); this._folderIcons = []; -- GitLab From 5cb94c526c0afc8d3985fa57068f78c3d8c9f4ad Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 8 Jun 2022 11:45:14 -0300 Subject: [PATCH 03/22] appDisplay: Give all available size to grid Another step in dismantling AppDisplay before reintroducing some of the elements there. Instead of adding a certain amount of padding, capped at 200px, always give the grid all available size. Part-of: --- js/ui/appDisplay.js | 47 +-------------------------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index b0f85f377f..7f6466d200 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -44,7 +44,6 @@ const PAGE_PREVIEW_ANIMATION_START_OFFSET = 100; const PAGE_PREVIEW_FADE_EFFECT_MAX_OFFSET = 300; const PAGE_PREVIEW_MAX_ARROW_OFFSET = 80; const PAGE_INDICATOR_FADE_TIME = 200; -const MAX_PAGE_PADDING = 200; const OVERSHOOT_THRESHOLD = 20; const OVERSHOOT_TIMEOUT = 1000; @@ -1051,54 +1050,10 @@ var BaseAppView = GObject.registerClass({ const availWidth = box.get_width(); const availHeight = box.get_height(); - const gridRatio = this._grid.layout_manager.columnsPerPage / - this._grid.layout_manager.rowsPerPage; - const spaceRatio = availWidth / availHeight; - let pageWidth, pageHeight; - - if (spaceRatio > gridRatio * 1.1) { - // Enough room for some preview - pageHeight = availHeight; - pageWidth = Math.ceil(availHeight * gridRatio); - - if (spaceRatio > gridRatio * 1.5) { - // Ultra-wide layout, give some extra space for - // the page area, but up to an extent. - const extraPageSpace = Math.min( - Math.floor((availWidth - pageWidth) / 2), MAX_PAGE_PADDING); - pageWidth += extraPageSpace; - this._grid.layout_manager.pagePadding.left = - Math.floor(extraPageSpace / 2); - this._grid.layout_manager.pagePadding.right = - Math.ceil(extraPageSpace / 2); - } - } else { - // Not enough room, needs to shrink horizontally - pageWidth = Math.ceil(availWidth * 0.8); - pageHeight = availHeight; - this._grid.layout_manager.pagePadding.left = - Math.floor(availWidth * 0.02); - this._grid.layout_manager.pagePadding.right = - Math.ceil(availWidth * 0.02); - } - - this._grid.adaptToSize(pageWidth, pageHeight); + this._grid.adaptToSize(availWidth, availHeight); const leftPadding = Math.floor( (availWidth - this._grid.layout_manager.pageWidth) / 2); - const rightPadding = Math.ceil( - (availWidth - this._grid.layout_manager.pageWidth) / 2); - const topPadding = Math.floor( - (availHeight - this._grid.layout_manager.pageHeight) / 2); - const bottomPadding = Math.ceil( - (availHeight - this._grid.layout_manager.pageHeight) / 2); - - this._scrollView.content_padding = new Clutter.Margin({ - left: leftPadding, - right: rightPadding, - top: topPadding, - bottom: bottomPadding, - }); this._availWidth = availWidth; this._availHeight = availHeight; -- GitLab From 5ba699e095073d704636080e4c74e5f22779bd51 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 8 Jun 2022 11:53:20 -0300 Subject: [PATCH 04/22] appDisplay: Stop updating fade effect Don't update the fade effect, since we have "infinite" borders now. Also remove _availWidth and _availHeight, since these variables are now unused. Part-of: --- js/ui/appDisplay.js | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 7f6466d200..1240caf046 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -41,7 +41,6 @@ const FOLDER_DIALOG_ANIMATION_TIME = 200; const PAGE_PREVIEW_ANIMATION_TIME = 150; const PAGE_PREVIEW_ANIMATION_START_OFFSET = 100; -const PAGE_PREVIEW_FADE_EFFECT_MAX_OFFSET = 300; const PAGE_PREVIEW_MAX_ARROW_OFFSET = 80; const PAGE_INDICATOR_FADE_TIME = 200; @@ -319,8 +318,6 @@ var BaseAppView = GObject.registerClass({ this._swipeTracker.connect('update', this._swipeUpdate.bind(this)); this._swipeTracker.connect('end', this._swipeEnd.bind(this)); - this._availWidth = 0; - this._availHeight = 0; this._orientation = Clutter.Orientation.HORIZONTAL; this._items = new Map(); @@ -360,30 +357,6 @@ var BaseAppView = GObject.registerClass({ this._disconnectDnD(); } - _updateFadeForNavigation() { - const fadeMargin = new Clutter.Margin(); - const rtl = this.get_text_direction() === Clutter.TextDirection.RTL; - const showingNextPage = this._pagesShown & SidePages.NEXT; - const showingPrevPage = this._pagesShown & SidePages.PREVIOUS; - - if ((showingNextPage && !rtl) || (showingPrevPage && rtl)) { - fadeMargin.right = Math.max( - -PAGE_PREVIEW_FADE_EFFECT_MAX_OFFSET, - -(this._availWidth - this._grid.layout_manager.pageWidth) / 2); - } - - if ((showingPrevPage && !rtl) || (showingNextPage && rtl)) { - fadeMargin.left = Math.max( - -PAGE_PREVIEW_FADE_EFFECT_MAX_OFFSET, - -(this._availWidth - this._grid.layout_manager.pageWidth) / 2); - } - - this._scrollView.update_fade_effect(fadeMargin); - const effect = this._scrollView.get_effect('fade'); - if (effect) - effect.extend_fade_area = true; - } - _createGrid() { return new IconGrid.IconGrid({ allow_incomplete_pages: true }); } @@ -1054,10 +1027,6 @@ var BaseAppView = GObject.registerClass({ const leftPadding = Math.floor( (availWidth - this._grid.layout_manager.pageWidth) / 2); - - this._availWidth = availWidth; - this._availHeight = availHeight; - this._pageIndicatorOffset = leftPadding; this._pageArrowOffset = Math.max( leftPadding - PAGE_PREVIEW_MAX_ARROW_OFFSET, 0); @@ -1174,7 +1143,6 @@ var BaseAppView = GObject.registerClass({ duration: PAGE_PREVIEW_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, }); - this._updateFadeForNavigation(); } else if (adjustment) { adjustment.ease(0, { duration: PAGE_PREVIEW_ANIMATION_TIME, @@ -1184,7 +1152,6 @@ var BaseAppView = GObject.registerClass({ this._syncClip(); this._nextPageArrow.visible = false; this._nextPageIndicator.visible = false; - this._updateFadeForNavigation(); }, }); } @@ -1197,7 +1164,6 @@ var BaseAppView = GObject.registerClass({ duration: PAGE_PREVIEW_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, }); - this._updateFadeForNavigation(); } else if (adjustment) { adjustment.ease(0, { duration: PAGE_PREVIEW_ANIMATION_TIME, @@ -1207,7 +1173,6 @@ var BaseAppView = GObject.registerClass({ this._syncClip(); this._prevPageArrow.visible = false; this._prevPageIndicator.visible = false; - this._updateFadeForNavigation(); }, }); } -- GitLab From 083a691a74ab1bcfa5f3f56919789fc8e6c79599 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 8 Jun 2022 12:35:30 -0300 Subject: [PATCH 05/22] appDisplay: Mostly remove adaptToSize Back in the day, adaptToSize() contained mad maths to figure out icon sizes. Over time, its scope was reduced, to the point where we are today where it mostly just redoes some quick maths to predict the grid size based on the CSS box model. Remove adaptToSize() from BaseAppView and child classes. It still is an internal detail of the IconGrid class, but it's not exposed as a "public" method anymore. This removal is not perfect, as it doesn't move or compensate for any of page indicators and arrows code. This will be done in follow up commits introducing a new layout manager for that. Part-of: --- js/ui/appDisplay.js | 49 --------------------------------------------- js/ui/iconGrid.js | 9 +++++---- 2 files changed, 5 insertions(+), 53 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 1240caf046..0eebbe9763 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -918,15 +918,6 @@ var BaseAppView = GObject.registerClass({ this._grid.moveItem(item, newPage, newPosition); } - vfunc_allocate(box) { - const width = box.get_width(); - const height = box.get_height(); - - this.adaptToSize(width, height); - - super.vfunc_allocate(box); - } - vfunc_map() { this._swipeTracker.enabled = true; this._connectDnD(); @@ -1011,27 +1002,6 @@ var BaseAppView = GObject.registerClass({ this._grid.goToPage(pageNumber, animate); } - adaptToSize(width, height) { - let box = new Clutter.ActorBox({ - x2: width, - y2: height, - }); - box = this.get_theme_node().get_content_box(box); - box = this._scrollView.get_theme_node().get_content_box(box); - box = this._grid.get_theme_node().get_content_box(box); - - const availWidth = box.get_width(); - const availHeight = box.get_height(); - - this._grid.adaptToSize(availWidth, availHeight); - - const leftPadding = Math.floor( - (availWidth - this._grid.layout_manager.pageWidth) / 2); - this._pageIndicatorOffset = leftPadding; - this._pageArrowOffset = Math.max( - leftPadding - PAGE_PREVIEW_MAX_ARROW_OFFSET, 0); - } - _getIndicatorOffset(page, progress, baseOffset) { const rtl = this.get_text_direction() === Clutter.TextDirection.RTL; const translationX = @@ -1311,14 +1281,6 @@ class AppDisplay extends BaseAppView { super._redisplay(); } - adaptToSize(width, height) { - const [, indicatorHeight] = this._pageIndicators.get_preferred_height(-1); - height -= indicatorHeight; - - this._grid.findBestModeForSize(width, height); - super.adaptToSize(width, height); - } - _savePages() { const pages = []; @@ -2013,10 +1975,6 @@ class FolderGrid extends IconGrid.IconGrid { page_valign: Clutter.ActorAlign.CENTER, }); } - - adaptToSize(width, height) { - this.layout_manager.adaptToSize(width, height); - } }); var FolderView = GObject.registerClass( @@ -2149,13 +2107,6 @@ class FolderView extends BaseAppView { return false; } - adaptToSize(width, height) { - const [, indicatorHeight] = this._pageIndicators.get_preferred_height(-1); - height -= indicatorHeight; - - super.adaptToSize(width, height); - } - _loadApps() { let apps = []; let appSys = Shell.AppSystem.get_default(); diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js index 599374fa35..a5f86fc64d 100644 --- a/js/ui/iconGrid.js +++ b/js/ui/iconGrid.js @@ -1207,6 +1207,11 @@ var IconGrid = GObject.registerClass({ delete child._iconGridKeyFocusInId; } + vfunc_allocate(box) { + this.layout_manager.adaptToSize(...box.get_size()); + super.vfunc_allocate(box); + } + vfunc_style_changed() { super.vfunc_style_changed(); @@ -1391,10 +1396,6 @@ var IconGrid = GObject.registerClass({ return this.layout_manager.nPages; } - adaptToSize(width, height) { - this.layout_manager.adaptToSize(width, height); - } - setGridModes(modes) { this._gridModes = modes ? modes : defaultGridModes; this.queue_relayout(); -- GitLab From 113130552f1d3104957a2b8441b3401ea1f6b8f4 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 9 Jun 2022 15:32:26 -0300 Subject: [PATCH 06/22] appDisplay: Set 3x3 grid mode for folders Folders have a fixed 3x3 grid, given that folders themselves have a fixed size. Make the code correspond to this invariant. Part-of: --- js/ui/appDisplay.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 0eebbe9763..23bff90c73 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -1974,6 +1974,13 @@ class FolderGrid extends IconGrid.IconGrid { page_halign: Clutter.ActorAlign.CENTER, page_valign: Clutter.ActorAlign.CENTER, }); + + this.setGridModes([ + { + rows: 3, + columns: 3, + }, + ]); } }); -- GitLab From b115e07b4a73fe6f3435b239fd0d1e5e1aac4026 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 9 Jun 2022 15:40:21 -0300 Subject: [PATCH 07/22] iconGrid: Always call findBestModeForSize() The API surface of IconGrid was originally meant to be only setGridModes(), however findBestModeForSize() ended up being called by AppDisplay code. Now that FolderGrid sets the modes correctly, we don't need to skip calling findBestModeForSize() anymore. Always call findBestModeForSize() during IconGrid's allocation. Add an underscore to findBestModeForSize() since it's now back to be a private method of IconGrid. Part-of: --- js/ui/iconGrid.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js index a5f86fc64d..ac8d3ecaaf 100644 --- a/js/ui/iconGrid.js +++ b/js/ui/iconGrid.js @@ -1180,7 +1180,7 @@ var IconGrid = GObject.registerClass({ } } - findBestModeForSize(width, height) { + _findBestModeForSize(width, height) { const { pagePadding } = this.layout_manager; width -= pagePadding.left + pagePadding.right; height -= pagePadding.top + pagePadding.bottom; @@ -1208,7 +1208,9 @@ var IconGrid = GObject.registerClass({ } vfunc_allocate(box) { - this.layout_manager.adaptToSize(...box.get_size()); + const [width, height] = box.get_size(); + this._findBestModeForSize(width, height); + this.layout_manager.adaptToSize(width, height); super.vfunc_allocate(box); } -- GitLab From b9a373c1ab79a1fb93463c49404c6921b37a9467 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 10 Jun 2022 15:25:05 -0300 Subject: [PATCH 08/22] appDisplay: Introduce custom IconGrid class for AppDisplay AppDisplay will require an extra padding applied on top of CSS page padding. This is specific to AppDisplay and FolderView. Add a new AppGrid class which subclasses IconGrid and adds this extra padding - here called 'indicators-padding'. Part-of: --- js/ui/appDisplay.js | 52 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 23bff90c73..480dd1fb5c 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -155,6 +155,54 @@ function _findBestFolderName(apps) { return null; } +const AppGrid = GObject.registerClass({ + Properties: { + 'indicators-padding': GObject.ParamSpec.boxed('indicators-padding', + 'Indicators padding', 'Indicators padding', + GObject.ParamFlags.READWRITE, + Clutter.Margin.$gtype), + }, +}, class AppGrid extends IconGrid.IconGrid { + _init(layoutParams) { + super._init(layoutParams); + + this._indicatorsPadding = new Clutter.Margin(); + } + + _updatePadding() { + const node = this.get_theme_node(); + const {rowSpacing, columnSpacing} = this.layoutManager; + + const padding = this._indicatorsPadding.copy(); + padding.left += rowSpacing; + padding.right += rowSpacing; + padding.top += columnSpacing; + padding.bottom += columnSpacing; + ['top', 'right', 'bottom', 'left'].forEach(side => { + padding[side] += node.get_length(`page-padding-${side}`); + }); + + this.layoutManager.pagePadding = padding; + } + + vfunc_style_changed() { + super.vfunc_style_changed(); + this._updatePadding(); + } + + get indicatorsPadding() { + return this._indicatorsPadding; + } + + set indicatorsPadding(v) { + if (this._indicatorsPadding === v) + return; + + this._indicatorsPadding = v ? v : new Clutter.Margin(); + this._updatePadding(); + } +}); + var BaseAppView = GObject.registerClass({ GTypeFlags: GObject.TypeFlags.ABSTRACT, Properties: { @@ -358,7 +406,7 @@ var BaseAppView = GObject.registerClass({ } _createGrid() { - return new IconGrid.IconGrid({ allow_incomplete_pages: true }); + return new AppGrid({allow_incomplete_pages: true}); } _onScroll(actor, event) { @@ -1965,7 +2013,7 @@ class AppViewItem extends St.Button { }); var FolderGrid = GObject.registerClass( -class FolderGrid extends IconGrid.IconGrid { +class FolderGrid extends AppGrid { _init() { super._init({ allow_incomplete_pages: false, -- GitLab From 6c00e0fda4d3f064810def9e4bb72f3aaf3c2bf0 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 10 Jun 2022 18:30:26 -0300 Subject: [PATCH 09/22] appDisplay: Use custom layout manager for page indicators This is a major departure to how page previews used to work. Add a new layout manager that handles showing and hiding page previews and navigation arrows. Move most of the code handling page previews to this new layout manager. The layout manager allocates at most 20% of the screen width for the previews, and at least the width of the arrow. The next and previous page peeking effect is temporarily lost with this commit, but will be reintroduced in later commits. Part-of: --- js/ui/appDisplay.js | 453 ++++++++++++++++++++------------------------ 1 file changed, 207 insertions(+), 246 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 480dd1fb5c..f3134d94a0 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -40,9 +40,8 @@ var APP_ICON_TITLE_COLLAPSE_TIME = 100; const FOLDER_DIALOG_ANIMATION_TIME = 200; const PAGE_PREVIEW_ANIMATION_TIME = 150; -const PAGE_PREVIEW_ANIMATION_START_OFFSET = 100; -const PAGE_PREVIEW_MAX_ARROW_OFFSET = 80; const PAGE_INDICATOR_FADE_TIME = 200; +const PAGE_PREVIEW_RATIO = 0.20; const OVERSHOOT_THRESHOLD = 20; const OVERSHOOT_TIMEOUT = 1000; @@ -203,6 +202,194 @@ const AppGrid = GObject.registerClass({ } }); +const BaseAppViewGridLayout = GObject.registerClass( +class BaseAppViewGridLayout extends Clutter.BinLayout { + _init(grid, scrollView, nextPageIndicator, nextPageArrow, + previousPageIndicator, previousPageArrow) { + if (!(grid instanceof AppGrid)) + throw new Error('Grid must be an AppGrid subclass'); + + super._init(); + + this._grid = grid; + this._scrollView = scrollView; + this._previousPageIndicator = previousPageIndicator; + this._previousPageArrow = previousPageArrow; + this._nextPageIndicator = nextPageIndicator; + this._nextPageArrow = nextPageArrow; + + grid.connect('pages-changed', () => this._syncPageIndicatorsVisibility()); + + this._pageIndicatorsAdjustment = new St.Adjustment({ + lower: 0, + upper: 1, + }); + this._pageIndicatorsAdjustment.connect( + 'notify::value', () => this._syncPageIndicators()); + + this._showIndicators = false; + this._currentPage = 0; + } + + _getIndicatorsWidth(box) { + const [width, height] = box.get_size(); + const arrows = [ + this._nextPageArrow, + this._previousPageArrow, + ]; + + const minArrowsWidth = arrows.reduce( + (previousWidth, accessory) => { + const [min] = accessory.get_preferred_width(height); + return Math.max(previousWidth, min); + }, 0); + + const idealIndicatorWidth = (width * PAGE_PREVIEW_RATIO) / 2; + + return Math.max(idealIndicatorWidth, minArrowsWidth); + } + + _syncPageIndicatorsVisibility(animate = true) { + const previousIndicatorsVisible = + this._currentPage > 0 && this._showIndicators; + + if (previousIndicatorsVisible) { + this._previousPageIndicator.show(); + this._previousPageArrow.show(); + } + this._previousPageIndicator.ease({ + opacity: previousIndicatorsVisible ? 255 : 0, + duration: animate ? PAGE_INDICATOR_FADE_TIME : 0, + onComplete: () => { + if (!previousIndicatorsVisible) { + this._previousPageIndicator.hide(); + this._previousPageArrow.hide(); + } + }, + }); + this._previousPageArrow.ease({ + opacity: previousIndicatorsVisible ? 255 : 0, + duration: animate ? PAGE_INDICATOR_FADE_TIME : 0, + }); + + // Always show the next page indicator to allow dropping + // icons into new pages + const {allowIncompletePages, nPages} = this._grid.layoutManager; + const nextIndicatorsVisible = this._showIndicators && + (allowIncompletePages ? true : this._currentPage < nPages - 1); + if (nextIndicatorsVisible) { + this._nextPageIndicator.show(); + this._nextPageArrow.show(); + } + this._nextPageIndicator.ease({ + opacity: nextIndicatorsVisible ? 255 : 0, + duration: animate ? PAGE_INDICATOR_FADE_TIME : 0, + onComplete: () => { + if (!nextIndicatorsVisible) { + this._nextPageIndicator.hide(); + this._nextPageArrow.hide(); + } + }, + }); + this._nextPageArrow.ease({ + opacity: nextIndicatorsVisible ? 255 : 0, + duration: animate ? PAGE_INDICATOR_FADE_TIME : 0, + }); + } + + _syncPageIndicators() { + if (!this._container) + return; + + const {value} = this._pageIndicatorsAdjustment; + + const ltr = this._container.get_text_direction() !== Clutter.TextDirection.RTL; + const {left, right} = this._grid.indicatorsPadding; + const leftOffset = -left * (1 - value); + const rightOffset = right * (1 - value); + + const leftArrowOffset = -left * value; + const rightArrowOffset = right * value; + + this._previousPageArrow.translationX = + ltr ? leftArrowOffset : rightArrowOffset; + this._nextPageArrow.translationX = + ltr ? rightArrowOffset : leftArrowOffset; + this._previousPageIndicator.translationX = ltr ? leftOffset : rightOffset; + this._previousPageArrow.translationX = ltr ? leftOffset : rightOffset; + this._nextPageIndicator.translationX = ltr ? rightOffset : leftOffset; + this._nextPageArrow.translationX = ltr ? rightOffset : leftOffset; + } + + vfunc_set_container(container) { + this._container = container; + this._pageIndicatorsAdjustment.actor = container; + this._syncPageIndicators(); + } + + vfunc_allocate(container, box) { + const ltr = container.get_text_direction() !== Clutter.TextDirection.RTL; + const indicatorsWidth = this._getIndicatorsWidth(box); + + this._grid.indicatorsPadding = new Clutter.Margin({ + left: indicatorsWidth, + right: indicatorsWidth, + }); + + this._scrollView.allocate(box); + + const leftBox = box.copy(); + leftBox.x2 = leftBox.x1 + indicatorsWidth; + + const rightBox = box.copy(); + rightBox.x1 = rightBox.x2 - indicatorsWidth; + + this._previousPageIndicator.allocate(ltr ? leftBox : rightBox); + this._previousPageArrow.allocate(ltr ? leftBox : rightBox); + this._nextPageIndicator.allocate(ltr ? rightBox : leftBox); + this._nextPageArrow.allocate(ltr ? rightBox : leftBox); + } + + goToPage(page, animate = true) { + if (this._currentPage === page) + return; + + this._currentPage = page; + this._syncPageIndicatorsVisibility(animate); + this._syncPageIndicators(); + } + + showPageIndicators() { + if (this._showIndicators) + return; + + this._pageIndicatorsAdjustment.ease(1, { + duration: PAGE_PREVIEW_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_CUBIC, + }); + + this._grid.clipToView = false; + this._showIndicators = true; + this._syncPageIndicatorsVisibility(); + } + + hidePageIndicators() { + if (!this._showIndicators) + return; + + this._pageIndicatorsAdjustment.ease(0, { + duration: PAGE_PREVIEW_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_CUBIC, + onComplete: () => { + this._grid.clipToView = true; + }, + }); + + this._showIndicators = false; + this._syncPageIndicatorsVisibility(); + } +}); + var BaseAppView = GObject.registerClass({ GTypeFlags: GObject.TypeFlags.ABSTRACT, Properties: { @@ -238,14 +425,10 @@ var BaseAppView = GObject.registerClass({ enable_mouse_scrolling: false, }); this._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER); - this._scrollView._delegate = this; this._canScroll = true; // limiting scrolling speed this._scrollTimeoutId = 0; this._scrollView.connect('scroll-event', this._onScroll.bind(this)); - this._scrollView.connect('motion-event', this._onMotion.bind(this)); - this._scrollView.connect('enter-event', this._onMotion.bind(this)); - this._scrollView.connect('leave-event', this._onLeave.bind(this)); this._scrollView.connect('button-press-event', this._onButtonPress.bind(this)); this._scrollView.add_actor(this._grid); @@ -255,20 +438,6 @@ var BaseAppView = GObject.registerClass({ this._adjustment.connect('notify::value', adj => { const value = adj.value / adj.page_size; this._pageIndicators.setCurrentPosition(value); - - const distanceToPage = Math.abs(Math.round(value) - value); - if (distanceToPage < 0.001) { - this._hintContainer.opacity = 255; - this._hintContainer.translationX = 0; - } else { - this._hintContainer.remove_transition('opacity'); - let opacity = Math.clamp( - 255 * (1 - (distanceToPage * 2)), - 0, 255); - - this._hintContainer.translationX = (Math.round(value) - value) * adj.page_size; - this._hintContainer.opacity = opacity; - } }); // Page Indicators @@ -332,23 +501,24 @@ var BaseAppView = GObject.registerClass({ x_align: Clutter.ActorAlign.START, }); - this._hintContainer = new St.Widget({ - layout_manager: new Clutter.BinLayout(), - x_expand: true, - y_expand: true, - }); - this._hintContainer.add_child(this._prevPageIndicator); - this._hintContainer.add_child(this._nextPageIndicator); - const scrollContainer = new St.Widget({ - layout_manager: new Clutter.BinLayout(), clip_to_allocation: true, y_expand: true, }); - scrollContainer.add_child(this._hintContainer); scrollContainer.add_child(this._scrollView); + scrollContainer.add_child(this._prevPageIndicator); + scrollContainer.add_child(this._nextPageIndicator); scrollContainer.add_child(this._nextPageArrow); scrollContainer.add_child(this._prevPageArrow); + scrollContainer.layoutManager = new BaseAppViewGridLayout( + this._grid, + this._scrollView, + this._nextPageIndicator, + this._nextPageArrow, + this._prevPageIndicator, + this._prevPageArrow); + this._appGridLayout = scrollContainer.layoutManager; + scrollContainer._delegate = this; this._box = new St.BoxLayout({ vertical: true, @@ -460,27 +630,20 @@ var BaseAppView = GObject.registerClass({ _pageForCoords(x, y) { const rtl = this.get_text_direction() === Clutter.TextDirection.RTL; - const { allocation } = this._grid; + const {pagePadding} = this._grid.layoutManager; const [success, pointerX] = this._scrollView.transform_stage_point(x, y); if (!success) return SidePages.NONE; - if (pointerX < allocation.x1) + if (pointerX < pagePadding.left) return rtl ? SidePages.NEXT : SidePages.PREVIOUS; - else if (pointerX > allocation.x2) + else if (pointerX > this._scrollView.width - pagePadding.right) return rtl ? SidePages.PREVIOUS : SidePages.NEXT; return SidePages.NONE; } - _onMotion(actor, event) { - const page = this._pageForCoords(...event.get_coords()); - this._slideSidePages(page); - - return Clutter.EVENT_PROPAGATE; - } - _onButtonPress(actor, event) { const page = this._pageForCoords(...event.get_coords()); if (page === SidePages.NEXT) @@ -489,10 +652,6 @@ var BaseAppView = GObject.registerClass({ this.goToPage(this._grid.currentPage - 1); } - _onLeave() { - this._slideSidePages(SidePages.NONE); - } - _swipeBegin(tracker, monitor) { if (monitor !== Main.layoutManager.primaryIndex) return; @@ -522,8 +681,6 @@ var BaseAppView = GObject.registerClass({ const adjustment = this._adjustment; const value = endProgress * adjustment.page_size; - this._syncPageHints(endProgress); - adjustment.ease(value, { mode: Clutter.AnimationMode.EASE_OUT_CUBIC, duration, @@ -683,7 +840,7 @@ var BaseAppView = GObject.registerClass({ dragMotion: this._onDragMotion.bind(this), }; DND.addDragMonitor(this._dragMonitor); - this._slideSidePages(SidePages.PREVIOUS | SidePages.NEXT | SidePages.DND); + this._appGridLayout.showPageIndicators(); this._dragFocus = null; this._swipeTracker.enabled = false; } @@ -694,14 +851,6 @@ var BaseAppView = GObject.registerClass({ const appIcon = dragEvent.source; - this._dropPage = this._pageForCoords(dragEvent.x, dragEvent.y); - if (this._dropPage && - this._dropPage === SidePages.PREVIOUS && - this._grid.currentPage === 0) { - delete this._dropPage; - return DND.DragMotionResult.NO_DROP; - } - // Handle the drag overshoot. When dragging to above the // icon grid, move to the page above; when dragging below, // move to the page below. @@ -720,8 +869,7 @@ var BaseAppView = GObject.registerClass({ } this._resetOvershoot(); - this._slideSidePages(SidePages.NONE); - delete this._dropPage; + this._appGridLayout.hidePageIndicators(); this._swipeTracker.enabled = true; } @@ -729,7 +877,7 @@ var BaseAppView = GObject.registerClass({ // At this point, the positions aren't stored yet, thus _redisplay() // will move all items to their original positions this._redisplay(); - this._slideSidePages(SidePages.NONE); + this._appGridLayout.hidePageIndicators(); this._swipeTracker.enabled = true; } @@ -999,203 +1147,16 @@ var BaseAppView = GObject.registerClass({ this._grid.ease(params); } - _syncPageHints(pageNumber, animate = true) { - const showingNextPage = this._pagesShown & SidePages.NEXT; - const showingPrevPage = this._pagesShown & SidePages.PREVIOUS; - const dnd = this._pagesShown & SidePages.DND; - const duration = animate ? PAGE_INDICATOR_FADE_TIME : 0; - - if (showingPrevPage) { - const opacity = pageNumber === 0 ? 0 : 255; - this._prevPageIndicator.visible = true; - this._prevPageIndicator.ease({ - opacity, - duration, - }); - - if (!dnd) { - this._prevPageArrow.visible = true; - this._prevPageArrow.ease({ - opacity, - duration, - }); - } - } - - if (showingNextPage) { - const opacity = pageNumber === this._grid.nPages - 1 ? 0 : 255; - this._nextPageIndicator.visible = true; - this._nextPageIndicator.ease({ - opacity, - duration, - }); - - if (!dnd) { - this._nextPageArrow.visible = true; - this._nextPageArrow.ease({ - opacity, - duration, - }); - } - } - } - goToPage(pageNumber, animate = true) { pageNumber = Math.clamp(pageNumber, 0, this._grid.nPages - 1); if (this._grid.currentPage === pageNumber) return; - this._syncPageHints(pageNumber, animate); + this._appGridLayout.goToPage(pageNumber, animate); this._grid.goToPage(pageNumber, animate); } - _getIndicatorOffset(page, progress, baseOffset) { - const rtl = this.get_text_direction() === Clutter.TextDirection.RTL; - const translationX = - (1 - progress) * PAGE_PREVIEW_ANIMATION_START_OFFSET; - - page = rtl ? -page : page; - - return (translationX - baseOffset) * page; - } - - _syncClip() { - const nextPageAdjustment = this._previewedPages.get(1); - const prevPageAdjustment = this._previewedPages.get(-1); - this._grid.clip_to_view = - (!prevPageAdjustment || prevPageAdjustment.value === 0) && - (!nextPageAdjustment || nextPageAdjustment.value === 0); - } - - _setupPagePreview(page, state) { - if (this._previewedPages.has(page)) - return this._previewedPages.get(page); - - const adjustment = new St.Adjustment({ - actor: this, - lower: 0, - upper: 1, - }); - - const indicator = page > 0 - ? this._nextPageIndicator : this._prevPageIndicator; - - adjustment.connectObject('notify::value', () => { - const nextPage = this._grid.currentPage + page; - const hasFollowingPage = nextPage >= 0 && - nextPage < this._grid.nPages; - - if (hasFollowingPage) { - const items = this._grid.getItemsAtPage(nextPage); - items.forEach(item => { - item.translation_x = - this._getIndicatorOffset(page, adjustment.value, 0); - }); - - if (!(state & SidePages.DND)) { - const pageArrow = page > 0 - ? this._nextPageArrow - : this._prevPageArrow; - pageArrow.set({ - visible: true, - opacity: adjustment.value * 255, - translation_x: this._getIndicatorOffset( - page, adjustment.value, - this._pageArrowOffset), - }); - } - } - if (hasFollowingPage || - (page > 0 && - this._grid.layout_manager.allow_incomplete_pages && - (state & SidePages.DND) !== 0)) { - indicator.set({ - visible: true, - opacity: adjustment.value * 255, - translation_x: this._getIndicatorOffset( - page, adjustment.value, - this._pageIndicatorOffset - indicator.width), - }); - } - this._syncClip(); - }, this); - - this._previewedPages.set(page, adjustment); - - return adjustment; - } - - _teardownPagePreview(page) { - const adjustment = this._previewedPages.get(page); - if (!adjustment) - return; - - adjustment.value = 1; - adjustment.disconnectObject(this); - this._previewedPages.delete(page); - } - - _slideSidePages(state) { - if (this._pagesShown === state) - return; - this._pagesShown = state; - const showingNextPage = state & SidePages.NEXT; - const showingPrevPage = state & SidePages.PREVIOUS; - const dnd = state & SidePages.DND; - let adjustment; - - if (dnd) { - this._nextPageIndicator.add_style_class_name('dnd'); - this._prevPageIndicator.add_style_class_name('dnd'); - } else { - this._nextPageIndicator.remove_style_class_name('dnd'); - this._prevPageIndicator.remove_style_class_name('dnd'); - } - - adjustment = this._previewedPages.get(1); - if (showingNextPage) { - adjustment = this._setupPagePreview(1, state); - - adjustment.ease(1, { - duration: PAGE_PREVIEW_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } else if (adjustment) { - adjustment.ease(0, { - duration: PAGE_PREVIEW_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - this._teardownPagePreview(1); - this._syncClip(); - this._nextPageArrow.visible = false; - this._nextPageIndicator.visible = false; - }, - }); - } - - adjustment = this._previewedPages.get(-1); - if (showingPrevPage) { - adjustment = this._setupPagePreview(-1, state); - - adjustment.ease(1, { - duration: PAGE_PREVIEW_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } else if (adjustment) { - adjustment.ease(0, { - duration: PAGE_PREVIEW_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - this._teardownPagePreview(-1); - this._syncClip(); - this._prevPageArrow.visible = false; - this._prevPageIndicator.visible = false; - }, - }); - } - } - updateDragFocus(dragFocus) { this._dragFocus = dragFocus; } -- GitLab From 3281c03aeaf5e0a34527d8d48de7ced45ada95de Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 14 Jun 2022 16:58:19 -0300 Subject: [PATCH 10/22] appDisplay: Make page previews fill allocation Instead of hardcoding the width of the page previous, allow them to fill the allocated space. Part-of: --- data/theme/gnome-shell-sass/widgets/_app-grid.scss | 2 -- js/ui/appDisplay.js | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/data/theme/gnome-shell-sass/widgets/_app-grid.scss b/data/theme/gnome-shell-sass/widgets/_app-grid.scss index 91cf023f19..0fcf62edd3 100644 --- a/data/theme/gnome-shell-sass/widgets/_app-grid.scss +++ b/data/theme/gnome-shell-sass/widgets/_app-grid.scss @@ -125,8 +125,6 @@ $app_icon_size: 96px; } .page-navigation-hint { - width: 300px; - &.dnd { background: rgba(255, 255, 255, 0.1); } diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index f3134d94a0..cb90ffa61c 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -461,7 +461,7 @@ var BaseAppView = GObject.registerClass({ reactive: false, x_expand: true, y_expand: true, - x_align: Clutter.ActorAlign.END, + x_align: Clutter.ActorAlign.FILL, y_align: Clutter.ActorAlign.FILL, }); @@ -472,7 +472,7 @@ var BaseAppView = GObject.registerClass({ reactive: false, x_expand: true, y_expand: true, - x_align: Clutter.ActorAlign.START, + x_align: Clutter.ActorAlign.FILL, y_align: Clutter.ActorAlign.FILL, }); -- GitLab From 48bcc8f0e293f6e12b72d4b49c294a055bc21ee4 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 14 Jun 2022 17:40:30 -0300 Subject: [PATCH 11/22] appDisplay: Make arrows always visible Always show navigation arrows in the app grid. Make the arrows and page indicators multually exclusive, hide one whenever the other is visible. Part-of: --- js/ui/appDisplay.js | 65 ++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index cb90ffa61c..c29991900d 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -253,23 +253,31 @@ class BaseAppViewGridLayout extends Clutter.BinLayout { const previousIndicatorsVisible = this._currentPage > 0 && this._showIndicators; - if (previousIndicatorsVisible) { + if (previousIndicatorsVisible) this._previousPageIndicator.show(); - this._previousPageArrow.show(); - } + this._previousPageIndicator.ease({ opacity: previousIndicatorsVisible ? 255 : 0, duration: animate ? PAGE_INDICATOR_FADE_TIME : 0, onComplete: () => { - if (!previousIndicatorsVisible) { + if (!previousIndicatorsVisible) this._previousPageIndicator.hide(); - this._previousPageArrow.hide(); - } }, }); + + const previousArrowVisible = + this._currentPage > 0 && !previousIndicatorsVisible; + + if (previousArrowVisible) + this._previousPageArrow.show(); + this._previousPageArrow.ease({ - opacity: previousIndicatorsVisible ? 255 : 0, + opacity: previousArrowVisible ? 255 : 0, duration: animate ? PAGE_INDICATOR_FADE_TIME : 0, + onComplete: () => { + if (!previousArrowVisible) + this._previousPageArrow.hide(); + }, }); // Always show the next page indicator to allow dropping @@ -277,23 +285,33 @@ class BaseAppViewGridLayout extends Clutter.BinLayout { const {allowIncompletePages, nPages} = this._grid.layoutManager; const nextIndicatorsVisible = this._showIndicators && (allowIncompletePages ? true : this._currentPage < nPages - 1); - if (nextIndicatorsVisible) { + + if (nextIndicatorsVisible) this._nextPageIndicator.show(); - this._nextPageArrow.show(); - } + this._nextPageIndicator.ease({ opacity: nextIndicatorsVisible ? 255 : 0, duration: animate ? PAGE_INDICATOR_FADE_TIME : 0, onComplete: () => { - if (!nextIndicatorsVisible) { + if (!nextIndicatorsVisible) this._nextPageIndicator.hide(); - this._nextPageArrow.hide(); - } }, }); + + const nextArrowVisible = + this._currentPage < nPages - 1 && + !nextIndicatorsVisible; + + if (nextArrowVisible) + this._nextPageArrow.show(); + this._nextPageArrow.ease({ - opacity: nextIndicatorsVisible ? 255 : 0, + opacity: nextArrowVisible ? 255 : 0, duration: animate ? PAGE_INDICATOR_FADE_TIME : 0, + onComplete: () => { + if (!nextArrowVisible) + this._nextPageArrow.hide(); + }, }); } @@ -305,8 +323,13 @@ class BaseAppViewGridLayout extends Clutter.BinLayout { const ltr = this._container.get_text_direction() !== Clutter.TextDirection.RTL; const {left, right} = this._grid.indicatorsPadding; - const leftOffset = -left * (1 - value); - const rightOffset = right * (1 - value); + const leftIndicatorOffset = -left * (1 - value); + const rightIndicatorOffset = right * (1 - value); + + this._previousPageIndicator.translationX = + ltr ? leftIndicatorOffset : rightIndicatorOffset; + this._nextPageIndicator.translationX = + ltr ? rightIndicatorOffset : leftIndicatorOffset; const leftArrowOffset = -left * value; const rightArrowOffset = right * value; @@ -315,10 +338,6 @@ class BaseAppViewGridLayout extends Clutter.BinLayout { ltr ? leftArrowOffset : rightArrowOffset; this._nextPageArrow.translationX = ltr ? rightArrowOffset : leftArrowOffset; - this._previousPageIndicator.translationX = ltr ? leftOffset : rightOffset; - this._previousPageArrow.translationX = ltr ? leftOffset : rightOffset; - this._nextPageIndicator.translationX = ltr ? rightOffset : leftOffset; - this._nextPageArrow.translationX = ltr ? rightOffset : leftOffset; } vfunc_set_container(container) { @@ -483,11 +502,9 @@ var BaseAppView = GObject.registerClass({ icon_name: rtl ? 'carousel-arrow-previous-symbolic' : 'carousel-arrow-next-symbolic', - opacity: 0, reactive: false, - visible: false, x_expand: true, - x_align: Clutter.ActorAlign.END, + x_align: Clutter.ActorAlign.CENTER, }); this._prevPageArrow = new St.Icon({ style_class: 'page-navigation-arrow', @@ -498,7 +515,7 @@ var BaseAppView = GObject.registerClass({ reactive: false, visible: false, x_expand: true, - x_align: Clutter.ActorAlign.START, + x_align: Clutter.ActorAlign.CENTER, }); const scrollContainer = new St.Widget({ -- GitLab From f22a5c5a911ef08b958670a84138315e5b7f0da2 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 23 Jun 2022 16:41:42 -0300 Subject: [PATCH 12/22] appDisplay: Readd next and previous page icons previews This was lost 2 commits ago, but now we reimplement this in a different way. There is some jesting with allocations, since we cannot use transformed positions while changing translation of the icons. This new implementation works regardless of the screen resolution. Part-of: --- js/ui/appDisplay.js | 72 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index c29991900d..77fac9e1e7 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -229,6 +229,7 @@ class BaseAppViewGridLayout extends Clutter.BinLayout { this._showIndicators = false; this._currentPage = 0; + this._pageWidth = 0; } _getIndicatorsWidth(box) { @@ -315,6 +316,65 @@ class BaseAppViewGridLayout extends Clutter.BinLayout { }); } + _getEndIcon(icons) { + const {columnsPerPage} = this._grid.layoutManager; + const index = Math.min(icons.length, columnsPerPage); + return icons[Math.max(index - 1, 0)]; + } + + _translatePreviousPageIcons(value, ltr) { + if (this._currentPage === 0) + return; + + const previousPage = this._currentPage - 1; + const icons = this._grid.getItemsAtPage(previousPage).filter(i => i.visible); + if (icons.length === 0) + return; + + const {left, right} = this._grid.indicatorsPadding; + const {columnSpacing} = this._grid.layoutManager; + const endIcon = this._getEndIcon(icons); + let iconOffset; + + if (ltr) { + const currentPageOffset = this._pageWidth * this._currentPage; + iconOffset = currentPageOffset - endIcon.allocation.x2 + left - columnSpacing; + } else { + const rtlPage = this._grid.nPages - previousPage - 1; + const pageOffset = this._pageWidth * rtlPage; + iconOffset = pageOffset - endIcon.allocation.x1 - right + columnSpacing; + } + + for (const icon of icons) + icon.translationX = iconOffset * value; + } + + _translateNextPageIcons(value, ltr) { + if (this._currentPage >= this._grid.nPages - 1) + return; + + const nextPage = this._currentPage + 1; + const icons = this._grid.getItemsAtPage(nextPage).filter(i => i.visible); + if (icons.length === 0) + return; + + const {left, right} = this._grid.indicatorsPadding; + const {columnSpacing} = this._grid.layoutManager; + let iconOffset; + + if (ltr) { + const pageOffset = this._pageWidth * nextPage; + iconOffset = pageOffset - icons[0].allocation.x1 - right + columnSpacing; + } else { + const rtlPage = this._grid.nPages - this._currentPage - 1; + const currentPageOffset = this._pageWidth * rtlPage; + iconOffset = currentPageOffset - icons[0].allocation.x2 + left - columnSpacing; + } + + for (const icon of icons) + icon.translationX = iconOffset * value; + } + _syncPageIndicators() { if (!this._container) return; @@ -338,6 +398,16 @@ class BaseAppViewGridLayout extends Clutter.BinLayout { ltr ? leftArrowOffset : rightArrowOffset; this._nextPageArrow.translationX = ltr ? rightArrowOffset : leftArrowOffset; + + // Page icons + this._translatePreviousPageIcons(value, ltr); + this._translateNextPageIcons(value, ltr); + + if (this._grid.nPages > 0) { + this._grid.getItemsAtPage(this._currentPage).forEach(icon => { + icon.translationX = 0; + }); + } } vfunc_set_container(container) { @@ -367,6 +437,8 @@ class BaseAppViewGridLayout extends Clutter.BinLayout { this._previousPageArrow.allocate(ltr ? leftBox : rightBox); this._nextPageIndicator.allocate(ltr ? rightBox : leftBox); this._nextPageArrow.allocate(ltr ? rightBox : leftBox); + + this._pageWidth = box.get_width(); } goToPage(page, animate = true) { -- GitLab From 892fa6581ce63d79a1edfbc98598af91811ca3fa Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 28 Jun 2022 12:39:50 -0300 Subject: [PATCH 13/22] appDisplay: Turn navigation arrows into StButtons Make the next and previous page arrows be StButtons, with their 'icon-name' property matching the current StIcon icon name, and use the 'clicked' signal to switch pages. Remove the 'button-press' callback the scroll view, since the buttons take over this functionality. Part-of: --- .../gnome-shell-sass/widgets/_app-grid.scss | 2 +- js/ui/appDisplay.js | 38 ++++--------------- 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/data/theme/gnome-shell-sass/widgets/_app-grid.scss b/data/theme/gnome-shell-sass/widgets/_app-grid.scss index 0fcf62edd3..6add597ea2 100644 --- a/data/theme/gnome-shell-sass/widgets/_app-grid.scss +++ b/data/theme/gnome-shell-sass/widgets/_app-grid.scss @@ -146,7 +146,7 @@ $app_icon_size: 96px; } } -.page-navigation-arrow { +.page-navigation-arrow > StIcon { margin: 6px; width: 24px; height: 24px; diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 77fac9e1e7..8689e62a26 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -520,7 +520,6 @@ var BaseAppView = GObject.registerClass({ this._canScroll = true; // limiting scrolling speed this._scrollTimeoutId = 0; this._scrollView.connect('scroll-event', this._onScroll.bind(this)); - this._scrollView.connect('button-press-event', this._onButtonPress.bind(this)); this._scrollView.add_actor(this._grid); @@ -569,26 +568,27 @@ var BaseAppView = GObject.registerClass({ // Next/prev page arrows const rtl = this.get_text_direction() === Clutter.TextDirection.RTL; - this._nextPageArrow = new St.Icon({ + this._nextPageArrow = new St.Button({ style_class: 'page-navigation-arrow', icon_name: rtl ? 'carousel-arrow-previous-symbolic' : 'carousel-arrow-next-symbolic', - reactive: false, x_expand: true, - x_align: Clutter.ActorAlign.CENTER, }); - this._prevPageArrow = new St.Icon({ + this._nextPageArrow.connect('clicked', + () => this.goToPage(this._grid.currentPage + 1)); + + this._prevPageArrow = new St.Button({ style_class: 'page-navigation-arrow', icon_name: rtl ? 'carousel-arrow-next-symbolic' : 'carousel-arrow-previous-symbolic', opacity: 0, - reactive: false, visible: false, x_expand: true, - x_align: Clutter.ActorAlign.CENTER, }); + this._prevPageArrow.connect('clicked', + () => this.goToPage(this._grid.currentPage - 1)); const scrollContainer = new St.Widget({ clip_to_allocation: true, @@ -717,30 +717,6 @@ var BaseAppView = GObject.registerClass({ return Clutter.EVENT_STOP; } - _pageForCoords(x, y) { - const rtl = this.get_text_direction() === Clutter.TextDirection.RTL; - const {pagePadding} = this._grid.layoutManager; - - const [success, pointerX] = this._scrollView.transform_stage_point(x, y); - if (!success) - return SidePages.NONE; - - if (pointerX < pagePadding.left) - return rtl ? SidePages.NEXT : SidePages.PREVIOUS; - else if (pointerX > this._scrollView.width - pagePadding.right) - return rtl ? SidePages.PREVIOUS : SidePages.NEXT; - - return SidePages.NONE; - } - - _onButtonPress(actor, event) { - const page = this._pageForCoords(...event.get_coords()); - if (page === SidePages.NEXT) - this.goToPage(this._grid.currentPage + 1); - else if (page === SidePages.PREVIOUS) - this.goToPage(this._grid.currentPage - 1); - } - _swipeBegin(tracker, monitor) { if (monitor !== Main.layoutManager.primaryIndex) return; -- GitLab From ca9a19dfbe4b89cd299528406e1df84e64e2ea87 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 28 Jun 2022 13:19:55 -0300 Subject: [PATCH 14/22] style: Theme pagination arrows as per mockups The style is essentially a copy of %osd_button, but adapted to style the inner icon instead of the whole button. Part-of: --- .../gnome-shell-sass/widgets/_app-grid.scss | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/data/theme/gnome-shell-sass/widgets/_app-grid.scss b/data/theme/gnome-shell-sass/widgets/_app-grid.scss index 6add597ea2..abea753353 100644 --- a/data/theme/gnome-shell-sass/widgets/_app-grid.scss +++ b/data/theme/gnome-shell-sass/widgets/_app-grid.scss @@ -146,8 +146,16 @@ $app_icon_size: 96px; } } -.page-navigation-arrow > StIcon { - margin: 6px; - width: 24px; - height: 24px; +.page-navigation-arrow { + & > StIcon { + margin: 6px; + padding: 18px; + width: 24px; + height: 24px; + border-radius: 99px; + } + + &:insensitive > StIcon { @include button(undecorated, $osd_fg_color, transparentize($osd_bg_color, 0.5));} + &:hover > StIcon { @include button(hover, $osd_fg_color, transparentize($osd_bg_color, 0.5));} + &:active > StIcon { @include button(active, $osd_fg_color, transparentize($osd_bg_color, 0.5));} } -- GitLab From 09b975fabf8e05ca2c777a498364672701808dcc Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 28 Jun 2022 19:47:06 -0300 Subject: [PATCH 15/22] appDisplay: Reimplement drag motion using page indicators This simplified the _handleDragOvershoot() callback quite a lot. Instead of transforming point positions and checking them against grid coordinates, merely check if the drop target is one of the page indicators. Part-of: --- js/ui/appDisplay.js | 47 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 8689e62a26..cc7881c6e1 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -43,7 +43,6 @@ const PAGE_PREVIEW_ANIMATION_TIME = 150; const PAGE_INDICATOR_FADE_TIME = 200; const PAGE_PREVIEW_RATIO = 0.20; -const OVERSHOOT_THRESHOLD = 20; const OVERSHOOT_TIMEOUT = 1000; const DELAYED_MOVE_TIMEOUT = 200; @@ -548,7 +547,7 @@ var BaseAppView = GObject.registerClass({ style_class: 'page-navigation-hint next', opacity: 0, visible: false, - reactive: false, + reactive: true, x_expand: true, y_expand: true, x_align: Clutter.ActorAlign.FILL, @@ -559,7 +558,7 @@ var BaseAppView = GObject.registerClass({ style_class: 'page-navigation-hint previous', opacity: 0, visible: false, - reactive: false, + reactive: true, x_expand: true, y_expand: true, x_align: Clutter.ActorAlign.FILL, @@ -641,7 +640,6 @@ var BaseAppView = GObject.registerClass({ () => this._redisplay(), this); // Drag n' Drop - this._lastOvershoot = -1; this._lastOvershootTimeoutId = 0; this._delayedMoveData = null; @@ -846,54 +844,37 @@ var BaseAppView = GObject.registerClass({ if (this._lastOvershootTimeoutId) GLib.source_remove(this._lastOvershootTimeoutId); this._lastOvershootTimeoutId = 0; - this._lastOvershoot = -1; } _handleDragOvershoot(dragEvent) { - const [gridX, gridY] = this.get_transformed_position(); - const [gridWidth, gridHeight] = this.get_transformed_size(); - - const vertical = this._orientation === Clutter.Orientation.VERTICAL; - const gridStart = vertical ? gridY : gridX; - const gridEnd = vertical - ? gridY + gridHeight - OVERSHOOT_THRESHOLD - : gridX + gridWidth - OVERSHOOT_THRESHOLD; - // Already animating if (this._adjustment.get_transition('value') !== null) return; - // Within the grid boundaries - const dragPosition = vertical ? dragEvent.y : dragEvent.x; - if (dragPosition > gridStart && dragPosition < gridEnd) { - // Check whether we moved out the area of the last switch - if (Math.abs(this._lastOvershoot - dragPosition) > OVERSHOOT_THRESHOLD) - this._resetOvershoot(); + const {targetActor} = dragEvent; + if (targetActor !== this._prevPageIndicator && + targetActor !== this._nextPageIndicator) { + this._resetOvershoot(); return; } - // Still in the area of the previous page switch - if (this._lastOvershoot >= 0) + if (this._lastOvershootTimeoutId > 0) return; - const rtl = this.get_text_direction() === Clutter.TextDirection.RTL; - if (dragPosition <= gridStart) - this.goToPage(this._grid.currentPage + (rtl ? 1 : -1)); - else if (dragPosition >= gridEnd) - this.goToPage(this._grid.currentPage + (rtl ? -1 : 1)); + let targetPage; + if (dragEvent.targetActor === this._prevPageIndicator) + targetPage = this._grid.currentPage - 1; else - return; // don't go beyond first/last page - - this._lastOvershoot = dragPosition; + targetPage = this._grid.currentPage + 1; - if (this._lastOvershootTimeoutId > 0) - GLib.source_remove(this._lastOvershootTimeoutId); + if (targetPage < 0 || targetPage >= this._grid.nPages) + return; // don't go beyond first/last page this._lastOvershootTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, OVERSHOOT_TIMEOUT, () => { this._resetOvershoot(); - this._handleDragOvershoot(dragEvent); + this.goToPage(targetPage); return GLib.SOURCE_REMOVE; }); GLib.Source.set_name_by_id(this._lastOvershootTimeoutId, -- GitLab From 8f247971eb47e153df896ee173fa148c8b48d411 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 29 Jun 2022 14:25:47 -0300 Subject: [PATCH 16/22] appDisplay: Special-case dropping over page indicators This brings back the ability to drop an icon beyong the last page. Because the acceptDrop() method does not have access to the target drop actor, to avoid an extra pick or manually calculating it from allocations, simply store it during DragMonitor.dragDrop(), and use the target actor in acceptDrop(). This commit also removes the last usage of SidePages, so drop the enum too. Part-of: --- js/ui/appDisplay.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index cc7881c6e1..1b21f14dc8 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -79,13 +79,6 @@ const DEFAULT_FOLDERS = { }, }; -var SidePages = { - NONE: 0, - PREVIOUS: 1 << 0, - NEXT: 1 << 1, - DND: 1 << 2, -}; - function _getCategories(info) { let categoriesStr = info.get_categories(); if (!categoriesStr) @@ -884,6 +877,7 @@ var BaseAppView = GObject.registerClass({ _onDragBegin() { this._dragMonitor = { dragMotion: this._onDragMotion.bind(this), + dragDrop: this._onDragDrop.bind(this), }; DND.addDragMonitor(this._dragMonitor); this._appGridLayout.showPageIndicators(); @@ -908,6 +902,13 @@ var BaseAppView = GObject.registerClass({ return DND.DragMotionResult.CONTINUE; } + _onDragDrop(dropEvent) { + // Because acceptDrop() does not receive the target actor, store it + // here and use this value in the acceptDrop() implementation below. + this._dropTarget = dropEvent.targetActor; + return DND.DragMotionResult.CONTINUE; + } + _onDragEnd() { if (this._dragMonitor) { DND.removeDragMonitor(this._dragMonitor); @@ -939,11 +940,15 @@ var BaseAppView = GObject.registerClass({ } acceptDrop(source) { + const dropTarget = this._dropTarget; + delete this._dropTarget; + if (!this._canAccept(source)) return false; - if (this._dropPage) { - const increment = this._dropPage === SidePages.NEXT ? 1 : -1; + if (dropTarget === this._prevPageIndicator || + dropTarget === this._nextPageIndicator) { + const increment = dropTarget === this._prevPageIndicator ? -1 : 1; const { currentPage, nPages } = this._grid; const page = Math.min(currentPage + increment, nPages); const position = page < nPages ? -1 : 0; -- GitLab From 078aca220d3cc094186d9715874007de0b2e4062 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 29 Jun 2022 14:32:47 -0300 Subject: [PATCH 17/22] Revert "st/scrollview: Add ::content-padding property to StScrollView" This reverts commit 0d62dadfbc4532953668a976a2585369d753ae01. The only consumer of such API is now gone. Part-of: --- src/st/st-scroll-view-fade.c | 8 -------- src/st/st-scroll-view.c | 39 ------------------------------------ 2 files changed, 47 deletions(-) diff --git a/src/st/st-scroll-view-fade.c b/src/st/st-scroll-view-fade.c index 4cf45af1f2..77b1d0b120 100644 --- a/src/st/st-scroll-view-fade.c +++ b/src/st/st-scroll-view-fade.c @@ -99,7 +99,6 @@ st_scroll_view_fade_paint_target (ClutterOffscreenEffect *effect, gboolean h_scroll_visible, v_scroll_visible, rtl; ClutterActorBox allocation, content_box, paint_box; - ClutterMargin *content_padding; float fade_area_topleft[2]; float fade_area_bottomright[2]; @@ -111,13 +110,6 @@ st_scroll_view_fade_paint_target (ClutterOffscreenEffect *effect, clutter_actor_get_allocation_box (self->actor, &allocation); st_theme_node_get_content_box (st_widget_get_theme_node (ST_WIDGET (self->actor)), (const ClutterActorBox *)&allocation, &content_box); - g_object_get (self->actor, "content-padding", &content_padding, NULL); - - content_box.x1 += content_padding->left; - content_box.x2 -= content_padding->right; - content_box.y1 += content_padding->top; - content_box.y2 -= content_padding->bottom; - clutter_margin_free (content_padding); /* * The FBO is based on the paint_volume's size which can be larger then the actual diff --git a/src/st/st-scroll-view.c b/src/st/st-scroll-view.c index cf13ccee0f..50de481944 100644 --- a/src/st/st-scroll-view.c +++ b/src/st/st-scroll-view.c @@ -84,8 +84,6 @@ struct _StScrollViewPrivate StAdjustment *vadjustment; ClutterActor *vscroll; - ClutterMargin content_padding; - StPolicyType hscrollbar_policy; StPolicyType vscrollbar_policy; @@ -118,7 +116,6 @@ enum { PROP_VSCROLLBAR_VISIBLE, PROP_MOUSE_SCROLL, PROP_OVERLAY_SCROLLBARS, - PROP_CONTENT_PADDING, N_PROPS }; @@ -159,9 +156,6 @@ st_scroll_view_get_property (GObject *object, case PROP_OVERLAY_SCROLLBARS: g_value_set_boolean (value, priv->overlay_scrollbars); break; - case PROP_CONTENT_PADDING: - g_value_set_boxed (value, &priv->content_padding); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -208,23 +202,6 @@ st_scroll_view_update_fade_effect (StScrollView *scroll, } } -static void -st_scroll_view_set_content_padding (StScrollView *scroll, - ClutterMargin *content_padding) -{ - StScrollViewPrivate *priv = ST_SCROLL_VIEW (scroll)->priv; - - if (priv->content_padding.left == content_padding->left && - priv->content_padding.right == content_padding->right && - priv->content_padding.top == content_padding->top && - priv->content_padding.bottom == content_padding->bottom) - return; - - priv->content_padding = *content_padding; - - g_object_notify_by_pspec (G_OBJECT (scroll), props[PROP_CONTENT_PADDING]); -} - static void st_scroll_view_set_property (GObject *object, guint property_id, @@ -254,10 +231,6 @@ st_scroll_view_set_property (GObject *object, priv->hscrollbar_policy, g_value_get_enum (value)); break; - case PROP_CONTENT_PADDING: - st_scroll_view_set_content_padding (self, - (ClutterMargin *)g_value_get_boxed (value)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -570,11 +543,6 @@ st_scroll_view_allocate (ClutterActor *actor, st_theme_node_get_content_box (theme_node, box, &content_box); - content_box.x1 += priv->content_padding.left; - content_box.x2 -= priv->content_padding.right; - content_box.y1 += priv->content_padding.top; - content_box.y2 += priv->content_padding.bottom; - avail_width = content_box.x2 - content_box.x1; avail_height = content_box.y2 - content_box.y1; @@ -966,13 +934,6 @@ st_scroll_view_class_init (StScrollViewClass *klass) FALSE, ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); - props[PROP_CONTENT_PADDING] = - g_param_spec_boxed ("content-padding", - "Content padding", - "Content padding", - CLUTTER_TYPE_MARGIN, - ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); - g_object_class_install_properties (object_class, N_PROPS, props); } -- GitLab From 88c3945ae1f4988be8dabfd3b3c4f5d99075a82b Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 9 Aug 2022 14:26:24 -0300 Subject: [PATCH 18/22] appDisplay: Center-align arrows As per feedback, center align the navigation arrows since they are big enough of click targets. Part-of: --- js/ui/appDisplay.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 1b21f14dc8..b4c00f824e 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -426,9 +426,11 @@ class BaseAppViewGridLayout extends Clutter.BinLayout { rightBox.x1 = rightBox.x2 - indicatorsWidth; this._previousPageIndicator.allocate(ltr ? leftBox : rightBox); - this._previousPageArrow.allocate(ltr ? leftBox : rightBox); + this._previousPageArrow.allocate_align_fill(ltr ? leftBox : rightBox, + 0.5, 0.5, false, false); this._nextPageIndicator.allocate(ltr ? rightBox : leftBox); - this._nextPageArrow.allocate(ltr ? rightBox : leftBox); + this._nextPageArrow.allocate_align_fill(ltr ? rightBox : leftBox, + 0.5, 0.5, false, false); this._pageWidth = box.get_width(); } @@ -565,7 +567,8 @@ var BaseAppView = GObject.registerClass({ icon_name: rtl ? 'carousel-arrow-previous-symbolic' : 'carousel-arrow-next-symbolic', - x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, }); this._nextPageArrow.connect('clicked', () => this.goToPage(this._grid.currentPage + 1)); @@ -577,7 +580,8 @@ var BaseAppView = GObject.registerClass({ : 'carousel-arrow-previous-symbolic', opacity: 0, visible: false, - x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, }); this._prevPageArrow.connect('clicked', () => this.goToPage(this._grid.currentPage - 1)); -- GitLab From 341cad764e1bb0ac36f9c711ba9cd600efdb71db Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 9 Aug 2022 15:42:18 -0300 Subject: [PATCH 19/22] appDisplay: Remove style of page indicators As per feedback, remove the visible styling of the page indicators. They're still used to layout and detect drag hovering, so the actors are still in place, but they're completely transparent now. Part-of: --- .../gnome-shell-sass/widgets/_app-grid.scss | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/data/theme/gnome-shell-sass/widgets/_app-grid.scss b/data/theme/gnome-shell-sass/widgets/_app-grid.scss index abea753353..49d3086a4f 100644 --- a/data/theme/gnome-shell-sass/widgets/_app-grid.scss +++ b/data/theme/gnome-shell-sass/widgets/_app-grid.scss @@ -124,28 +124,6 @@ $app_icon_size: 96px; icon-size: $app_icon_size * 0.5; } -.page-navigation-hint { - &.dnd { - background: rgba(255, 255, 255, 0.1); - } - - &.next:ltr, - &.previous:rtl { - background-gradient-start: rgba(255, 255, 255, 0.05); - background-gradient-end: transparent; - background-gradient-direction: horizontal; - border-radius: $modal_radius*1.5 0px 0px $modal_radius*1.5; - } - - &.previous:ltr, - &.next:rtl { - background-gradient-start: transparent; - background-gradient-end: rgba(255, 255, 255, 0.05); - background-gradient-direction: horizontal; - border-radius: 0px $modal_radius*1.5 $modal_radius*1.5 0px; - } -} - .page-navigation-arrow { & > StIcon { margin: 6px; -- GitLab From 01e43969e812e42c7218727506474f9f1b626fa7 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 9 Aug 2022 18:07:39 -0300 Subject: [PATCH 20/22] appDisplay: Reduce drag overshoot timeout As per design feedback, reduce it to 300 ms. Part-of: --- js/ui/appDisplay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index b4c00f824e..3b43f0d339 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -43,7 +43,7 @@ const PAGE_PREVIEW_ANIMATION_TIME = 150; const PAGE_INDICATOR_FADE_TIME = 200; const PAGE_PREVIEW_RATIO = 0.20; -const OVERSHOOT_TIMEOUT = 1000; +const OVERSHOOT_TIMEOUT = 300; const DELAYED_MOVE_TIMEOUT = 200; -- GitLab From 692de0eb952beabe1ef484dbf341516152378f54 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 9 Aug 2022 18:31:46 -0300 Subject: [PATCH 21/22] appDisplay: Rename _lastOvershootTimeoutId Trivial renaming to _overshootTimeoutId. No functional changes. Part-of: --- js/ui/appDisplay.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 3b43f0d339..9ede9a6900 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -637,7 +637,7 @@ var BaseAppView = GObject.registerClass({ () => this._redisplay(), this); // Drag n' Drop - this._lastOvershootTimeoutId = 0; + this._overshootTimeoutId = 0; this._delayedMoveData = null; this._dragBeginId = 0; @@ -838,9 +838,9 @@ var BaseAppView = GObject.registerClass({ } _resetOvershoot() { - if (this._lastOvershootTimeoutId) - GLib.source_remove(this._lastOvershootTimeoutId); - this._lastOvershootTimeoutId = 0; + if (this._overshootTimeoutId) + GLib.source_remove(this._overshootTimeoutId); + this._overshootTimeoutId = 0; } _handleDragOvershoot(dragEvent) { @@ -856,7 +856,7 @@ var BaseAppView = GObject.registerClass({ return; } - if (this._lastOvershootTimeoutId > 0) + if (this._overshootTimeoutId > 0) return; let targetPage; @@ -868,14 +868,14 @@ var BaseAppView = GObject.registerClass({ if (targetPage < 0 || targetPage >= this._grid.nPages) return; // don't go beyond first/last page - this._lastOvershootTimeoutId = + this._overshootTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, OVERSHOOT_TIMEOUT, () => { this._resetOvershoot(); this.goToPage(targetPage); return GLib.SOURCE_REMOVE; }); - GLib.Source.set_name_by_id(this._lastOvershootTimeoutId, - '[gnome-shell] this._lastOvershootTimeoutId'); + GLib.Source.set_name_by_id(this._overshootTimeoutId, + '[gnome-shell] this._overshootTimeoutId'); } _onDragBegin() { -- GitLab From 4dcae8ddd2637384b82fcbaa5ebd26c3dc2b77cd Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 9 Aug 2022 19:39:41 -0300 Subject: [PATCH 22/22] appDisplay: Bring back drag overshoot region This is a region where, if hovered while dragging, immediately goes to the previous or next page. This behavior was lost during the transition to the new app grid layout manager. Bring back the drag overshoot region. Part-of: --- js/ui/appDisplay.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 9ede9a6900..92ed05b72f 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -43,6 +43,7 @@ const PAGE_PREVIEW_ANIMATION_TIME = 150; const PAGE_INDICATOR_FADE_TIME = 200; const PAGE_PREVIEW_RATIO = 0.20; +const OVERSHOOT_THRESHOLD = 20; const OVERSHOOT_TIMEOUT = 300; const DELAYED_MOVE_TIMEOUT = 200; @@ -843,6 +844,23 @@ var BaseAppView = GObject.registerClass({ this._overshootTimeoutId = 0; } + _dragWithinOvershootRegion(dragEvent) { + const rtl = this.get_text_direction() === Clutter.TextDirection.RTL; + const {x, y, targetActor: indicator} = dragEvent; + const [indicatorX, indicatorY] = indicator.get_transformed_position(); + const [indicatorWidth, indicatorHeight] = indicator.get_transformed_size(); + + let overshootX = indicatorX; + if (indicator === this._nextPageIndicator || rtl) + overshootX += indicatorWidth - OVERSHOOT_THRESHOLD; + + const overshootBox = new Clutter.ActorBox(); + overshootBox.set_origin(overshootX, indicatorY); + overshootBox.set_size(OVERSHOOT_THRESHOLD, indicatorHeight); + + return overshootBox.contains(x, y); + } + _handleDragOvershoot(dragEvent) { // Already animating if (this._adjustment.get_transition('value') !== null) @@ -868,6 +886,13 @@ var BaseAppView = GObject.registerClass({ if (targetPage < 0 || targetPage >= this._grid.nPages) return; // don't go beyond first/last page + // If dragging over the drag overshoot threshold region, immediately + // switch pages + if (this._dragWithinOvershootRegion(dragEvent)) { + this._resetOvershoot(); + this.goToPage(targetPage); + } + this._overshootTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, OVERSHOOT_TIMEOUT, () => { this._resetOvershoot(); -- GitLab