diff --git a/data/theme/gnome-shell-sass/widgets/_app-grid.scss b/data/theme/gnome-shell-sass/widgets/_app-grid.scss index 8d3ae62bd..c183cfeb0 100644 --- a/data/theme/gnome-shell-sass/widgets/_app-grid.scss +++ b/data/theme/gnome-shell-sass/widgets/_app-grid.scss @@ -67,6 +67,19 @@ $app_grid_fg_color: #fff; & > StIcon { icon-size: 16px } } } + + & .icon-grid { + row-spacing: $base_spacing * 2; + column-spacing: $base_spacing * 5; + } + + & .page-indicators { + margin-bottom: 18px; + + .page-indicator { + padding: 15px 12px; + } + } } .app-folder-dialog-container { padding: 12px; diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 76546c887..a2bfd3690 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -120,7 +120,7 @@ var BaseAppView = GObject.registerClass({ _init(params = {}) { super._init(params); - this._grid = new IconGrid.IconGrid(); + this._grid = this._createGrid(); // Standard hack for ClutterBinLayout this._grid.x_expand = true; @@ -138,6 +138,10 @@ var BaseAppView = GObject.registerClass({ }); } + _createGrid() { + return new IconGrid.IconGrid(); + } + _redisplay() { let oldApps = this._orderedItems.slice(); let oldAppIds = oldApps.map(icon => icon.id); @@ -945,6 +949,24 @@ var AppSearchProvider = class AppSearchProvider { } }; +var FolderGrid = GObject.registerClass( +class FolderGrid extends IconGrid.IconGrid { + _init() { + super._init({ + allow_incomplete_pages: false, + orientation: Clutter.Orientation.HORIZONTAL, + columns_per_page: 3, + rows_per_page: 3, + page_halign: Clutter.ActorAlign.CENTER, + page_valign: Clutter.ActorAlign.CENTER, + }); + } + + adaptToSize(width, height) { + this.layout_manager.adaptToSize(width, height); + } +}); + var FolderView = GObject.registerClass( class FolderView extends BaseAppView { _init(folder, id, parentView) { @@ -967,17 +989,37 @@ class FolderView extends BaseAppView { x_expand: true, y_expand: true, }); - this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL); - this.add_actor(this._scrollView); + this._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER); + this._scrollView.add_actor(this._grid); - let scrollableContainer = new St.BoxLayout({ + const box = new St.BoxLayout({ vertical: true, reactive: true, x_expand: true, y_expand: true, }); - scrollableContainer.add_actor(this._grid); - this._scrollView.add_actor(scrollableContainer); + box.add_child(this._scrollView); + + // Page Dots + this._adjustment = this._scrollView.hscroll.adjustment; + this._adjustment.connect('notify::value', adj => { + this._pageIndicators.setCurrentPosition(adj.value / adj.page_size); + }); + + this._pageIndicators = new PageIndicators.PageIndicators(Clutter.Orientation.HORIZONTAL); + this._pageIndicators.y_expand = false; + this._pageIndicators.connect('page-activated', + (indicators, pageIndex) => { + this._grid.goToPage(pageIndex); + }); + this._pageIndicators.connect('scroll-event', (actor, event) => { + this._scrollView.event(event, false); + }); + box.add_child(this._pageIndicators); + this.add_child(box); + + this._availWidth = 0; + this._availHeight = 0; let action = new Clutter.PanAction({ interpolate: true }); action.connect('pan', this._onPan.bind(this)); @@ -986,6 +1028,10 @@ class FolderView extends BaseAppView { this._redisplay(); } + _createGrid() { + return new FolderGrid(); + } + vfunc_allocate(box) { const node = this.get_theme_node(); const contentBox = node.get_content_box(box); @@ -1034,10 +1080,26 @@ class FolderView extends BaseAppView { } adaptToSize(width, height) { - this._parentAvailableWidth = width; - this._parentAvailableHeight = height; + const oldNPages = this._grid.nPages; + const [, indicatorHeight] = this._pageIndicators.get_preferred_height(-1); + height -= indicatorHeight; this._grid.adaptToSize(width, height); + + if (this._availWidth !== width || + this._availHeight !== height || + oldNPages !== this._grid.nPages) { + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { + this._adjustment.value = 0; + this._grid.currentPage = 0; + this._pageIndicators.setNPages(this._grid.nPages); + this._pageIndicators.setCurrentPosition(0); + return GLib.SOURCE_REMOVE; + }); + } + + this._availWidth = width; + this._availHeight = height; } _loadApps() {