diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-08 16:02:53 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-08 16:02:53 +0000 |
commit | 4d8b071804d73b7a733f2b7696fde40caf8800bb (patch) | |
tree | 86aefc1c0ea9c596bab0ecbc0c054a2378d6cd13 /extensions/44/vertical-workspaces/lib/appDisplay.js | |
parent | Updating 44/no-overview to version 44 [68db01d]. (diff) | |
download | gnome-shell-extensions-extra-4d8b071804d73b7a733f2b7696fde40caf8800bb.tar.xz gnome-shell-extensions-extra-4d8b071804d73b7a733f2b7696fde40caf8800bb.zip |
Updating 44/vertical-workspaces to version 37+20231208 [0d82192].
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'extensions/44/vertical-workspaces/lib/appDisplay.js')
-rw-r--r-- | extensions/44/vertical-workspaces/lib/appDisplay.js | 1243 |
1 files changed, 760 insertions, 483 deletions
diff --git a/extensions/44/vertical-workspaces/lib/appDisplay.js b/extensions/44/vertical-workspaces/lib/appDisplay.js index 2ac70b1..aeb2808 100644 --- a/extensions/44/vertical-workspaces/lib/appDisplay.js +++ b/extensions/44/vertical-workspaces/lib/appDisplay.js @@ -10,246 +10,501 @@ 'use strict'; -const { Clutter, GLib, GObject, Meta, Shell, St, Graphene, Pango } = imports.gi; +const Clutter = imports.gi.Clutter; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Graphene = imports.gi.Graphene; +const Meta = imports.gi.Meta; +const Pango = imports.gi.Pango; +const Shell = imports.gi.Shell; +const St = imports.gi.St; -const DND = imports.ui.dnd; -const Main = imports.ui.main; const AppDisplay = imports.ui.appDisplay; +const DND = imports.ui.dnd; const IconGrid = imports.ui.iconGrid; +const Main = imports.ui.main; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const IconGridOverride = Me.imports.lib.iconGrid; -const _Util = Me.imports.lib.util; +let Me; +let opt; -const DIALOG_SHADE_NORMAL = Clutter.Color.from_pixel(0x00000022); -const DIALOG_SHADE_HIGHLIGHT = Clutter.Color.from_pixel(0x00000000); +let _timeouts; -// gettext -const _ = Me.imports.lib.settings._; +// DIALOG_SHADE_NORMAL = Clutter.Color.from_pixel(0x00000022); +// DIALOG_SHADE_HIGHLIGHT = Clutter.Color.from_pixel(0x00000000); -let _overrides; +var AppDisplayModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -let _appGridLayoutSettings; -let _appDisplayScrollConId; -let _appSystemStateConId; -let _appGridLayoutConId; -let _origAppViewItemAcceptDrop; -let _updateFolderIcons; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; -let opt; -let shellVersion = _Util.shellVersion; -let _firstRun = true; + this._appGridLayoutSettings = null; + this._appDisplayScrollConId = 0; + this._appSystemStateConId = 0; + this._appGridLayoutConId = 0; + this._origAppViewItemAcceptDrop = null; + this._updateFolderIcons = 0; + } -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('appDisplayModule', true); - reset = reset || !moduleEnabled; + cleanGlobals() { + Me = null; + opt = null; + } - // don't even touch this module if disabled - if (_firstRun && reset) - return; + update(reset) { + this._removeTimeouts(); + this.moduleEnabled = opt.get('appDisplayModule'); + const conflict = false; - _firstRun = false; + reset = reset || !this.moduleEnabled || conflict; - if (_overrides) - _overrides.removeAll(); + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + this.moduleEnabled = false; + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) { + this.moduleEnabled = false; + console.debug(' AppDisplayModule - Keeping untouched'); + } + } - if (reset) { - _setAppDisplayOrientation(false); - _updateAppGridProperties(reset); - _updateAppGridDND(reset); - _restoreOverviewGroup(); - _overrides = null; - opt = null; - return; + _activateModule() { + Me.Modules.iconGridModule.update(); + + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + _timeouts = {}; + + // Common + this._overrides.addOverride('FolderView', AppDisplay.FolderView.prototype, FolderView); + this._overrides.addOverride('FolderIcon', AppDisplay.FolderIcon.prototype, FolderIcon); + if (opt.APP_GRID_ACTIVE_PREVIEW) + this._overrides.addOverride('ActiveFolderIcon', AppDisplay.FolderIcon, ActiveFolderIcon); + this._overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIcon); + this._overrides.addOverride('AppDisplay', AppDisplay.AppDisplay.prototype, AppDisplayCommon); + this._overrides.addOverride('AppViewItem', AppDisplay.AppViewItem.prototype, AppViewItemCommon); + this._overrides.addOverride('BaseAppViewCommon', AppDisplay.BaseAppView.prototype, BaseAppViewCommon); + + if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) { + this._overrides.addOverride('AppDisplayVertical', AppDisplay.AppDisplay.prototype, AppDisplayVertical); + this._overrides.addOverride('BaseAppViewVertical', AppDisplay.BaseAppView.prototype, BaseAppViewVertical); + } + + // Custom App Grid + this._overrides.addOverride('AppFolderDialog', AppDisplay.AppFolderDialog.prototype, AppFolderDialog); + if (Me.shellVersion >= 43) { + // const defined class needs to be touched before real access + this._dummy = AppDisplay.AppGrid; + delete this._dummy; + // BaseAppViewGridLayout is not exported, we can only access current instance + this._overrides.addOverride('BaseAppViewGridLayout', Main.overview._overview.controls._appDisplay._appGridLayout, BaseAppViewGridLayout); + this._overrides.addOverride('FolderGrid', AppDisplay.FolderGrid.prototype, FolderGrid); + } else { + this._overrides.addOverride('FolderGrid', AppDisplay.FolderGrid.prototype, FolderGridLegacy); + } + + this._setAppDisplayOrientation(opt.ORIENTATION === Clutter.Orientation.VERTICAL); + this._updateDND(); + if (!Main.sessionMode.isGreeter) + this._updateAppDisplayProperties(); + + console.debug(' AppDisplayModule - Activated'); } - _overrides = new _Util.Overrides(); + _disableModule() { + Me.Modules.iconGridModule.update(true); + + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; - if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) { - _overrides.addOverride('AppDisplayVertical', AppDisplay.AppDisplay.prototype, AppDisplayVertical); - _overrides.addOverride('BaseAppViewVertical', AppDisplay.BaseAppView.prototype, BaseAppViewVertical); + const reset = true; + this._setAppDisplayOrientation(false); + this._updateAppDisplayProperties(reset); + this._updateDND(reset); + this._restoreOverviewGroup(); + this._removeStatusMessage(); + + console.debug(' AppDisplayModule - Disabled'); } - // Custom App Grid - _overrides.addOverride('AppFolderDialog', AppDisplay.AppFolderDialog.prototype, AppFolderDialog); - if (shellVersion >= 43) { - // const defined class needs to be touched before real access - AppDisplay.BaseAppViewGridLayout; - _overrides.addOverride('BaseAppViewGridLayout', AppDisplay.BaseAppViewGridLayout.prototype, BaseAppViewGridLayout); + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } } - _overrides.addOverride('FolderView', AppDisplay.FolderView.prototype, FolderView); - _overrides.addOverride('FolderIcon', AppDisplay.FolderIcon.prototype, FolderIcon); - _overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIcon); - _overrides.addOverride('AppDisplay', AppDisplay.AppDisplay.prototype, AppDisplayCommon); - _overrides.addOverride('AppViewItem', AppDisplay.AppViewItem.prototype, AppViewItemCommon); - _overrides.addOverride('BaseAppViewCommon', AppDisplay.BaseAppView.prototype, BaseAppViewCommon); - - _setAppDisplayOrientation(opt.ORIENTATION === Clutter.Orientation.VERTICAL); - _updateAppGridProperties(); - _updateAppGridDND(); - opt._appGridNeedsRedisplay = true; -} - -function _setAppDisplayOrientation(vertical = false) { - const CLUTTER_ORIENTATION = vertical ? Clutter.Orientation.VERTICAL : Clutter.Orientation.HORIZONTAL; - const scroll = vertical ? 'vscroll' : 'hscroll'; - // app display to vertical has issues - page indicator not working - // global appDisplay orientation switch is not built-in - let appDisplay = Main.overview._overview._controls._appDisplay; - // following line itself only changes in which axis will operate overshoot detection which switches appDisplay pages while dragging app icon to vertical - appDisplay._orientation = CLUTTER_ORIENTATION; - appDisplay._grid.layoutManager._orientation = CLUTTER_ORIENTATION; - appDisplay._swipeTracker.orientation = CLUTTER_ORIENTATION; - appDisplay._swipeTracker._reset(); - if (vertical) { - appDisplay._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL); - - // move and change orientation of page indicators - // needs corrections in appgrid page calculations, e.g. appDisplay.adaptToSize() fnc, - // which complicates use of super call inside the function - const pageIndicators = appDisplay._pageIndicators; - pageIndicators.vertical = true; - appDisplay._box.vertical = false; - pageIndicators.x_expand = false; - pageIndicators.y_align = Clutter.ActorAlign.CENTER; - pageIndicators.x_align = Clutter.ActorAlign.START; - - const scrollContainer = appDisplay._scrollView.get_parent(); - if (shellVersion < 43) { - // remove touch friendly side navigation bars / arrows - if (appDisplay._hintContainer && appDisplay._hintContainer.get_parent()) - scrollContainer.remove_child(appDisplay._hintContainer); + + _setAppDisplayOrientation(vertical = false) { + const CLUTTER_ORIENTATION = vertical ? Clutter.Orientation.VERTICAL : Clutter.Orientation.HORIZONTAL; + const scroll = vertical ? 'vscroll' : 'hscroll'; + // app display to vertical has issues - page indicator not working + // global appDisplay orientation switch is not built-in + let appDisplay = Main.overview._overview._controls._appDisplay; + // following line itself only changes in which axis will operate overshoot detection which switches appDisplay pages while dragging app icon to vertical + appDisplay._orientation = CLUTTER_ORIENTATION; + appDisplay._grid.layoutManager._orientation = CLUTTER_ORIENTATION; + appDisplay._swipeTracker.orientation = CLUTTER_ORIENTATION; + appDisplay._swipeTracker._reset(); + if (vertical) { + appDisplay._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL); + + // move and change orientation of page indicators + const pageIndicators = appDisplay._pageIndicators; + pageIndicators.vertical = true; + appDisplay._box.vertical = false; + pageIndicators.x_expand = false; + pageIndicators.y_align = Clutter.ActorAlign.CENTER; + pageIndicators.x_align = Clutter.ActorAlign.START; + + const scrollContainer = appDisplay._scrollView.get_parent(); + if (Me.shellVersion < 43) { + // remove touch friendly side navigation bars / arrows + if (appDisplay._hintContainer && appDisplay._hintContainer.get_parent()) + scrollContainer.remove_child(appDisplay._hintContainer); + } else { + // moving these bars needs more patching of the appDisplay's code + // for now we just change bars style to be more like vertically oriented arrows indicating direction to prev/next page + appDisplay._nextPageIndicator.add_style_class_name('nextPageIndicator'); + appDisplay._prevPageIndicator.add_style_class_name('prevPageIndicator'); + } + + // setting their x_scale to 0 removes the arrows and avoid allocation issues compared to .hide() them + appDisplay._nextPageArrow.scale_x = 0; + appDisplay._prevPageArrow.scale_x = 0; } else { - // moving these bars needs more patching of the appDisplay's code - // for now we just change bars style to be more like vertically oriented arrows indicating direction to prev/next page - appDisplay._nextPageIndicator.add_style_class_name('nextPageIndicator'); - appDisplay._prevPageIndicator.add_style_class_name('prevPageIndicator'); - } + appDisplay._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER); + if (this._appDisplayScrollConId) { + appDisplay._adjustment.disconnect(this._appDisplayScrollConId); + this._appDisplayScrollConId = 0; + } - // setting their x_scale to 0 removes the arrows and avoid allocation issues compared to .hide() them - appDisplay._nextPageArrow.scale_x = 0; - appDisplay._prevPageArrow.scale_x = 0; - } else { - appDisplay._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER); - if (_appDisplayScrollConId) { - appDisplay._adjustment.disconnect(_appDisplayScrollConId); - _appDisplayScrollConId = 0; + // restore original page indicators + const pageIndicators = appDisplay._pageIndicators; + pageIndicators.vertical = false; + appDisplay._box.vertical = true; + pageIndicators.x_expand = true; + pageIndicators.y_align = Clutter.ActorAlign.END; + pageIndicators.x_align = Clutter.ActorAlign.CENTER; + + // put back touch friendly navigation bars/buttons + const scrollContainer = appDisplay._scrollView.get_parent(); + if (appDisplay._hintContainer && !appDisplay._hintContainer.get_parent()) { + scrollContainer.add_child(appDisplay._hintContainer); + // the hit container covers the entire app grid and added at the top of the stack blocks DND drops + // so it needs to be pushed below + scrollContainer.set_child_below_sibling(appDisplay._hintContainer, null); + } + + appDisplay._nextPageArrow.scale_x = 1; + appDisplay._prevPageArrow.scale_x = 1; + + appDisplay._nextPageIndicator.remove_style_class_name('nextPageIndicator'); + appDisplay._prevPageIndicator.remove_style_class_name('prevPageIndicator'); } - // restore original page indicators - const pageIndicators = appDisplay._pageIndicators; - pageIndicators.vertical = false; - appDisplay._box.vertical = true; - pageIndicators.x_expand = true; - pageIndicators.y_align = Clutter.ActorAlign.END; - pageIndicators.x_align = Clutter.ActorAlign.CENTER; - - // put back touch friendly navigation bars/buttons - const scrollContainer = appDisplay._scrollView.get_parent(); - if (appDisplay._hintContainer && !appDisplay._hintContainer.get_parent()) { - scrollContainer.add_child(appDisplay._hintContainer); - // the hit container covers the entire app grid and added at the top of the stack blocks DND drops - // so it needs to be pushed below - scrollContainer.set_child_below_sibling(appDisplay._hintContainer, null); + // value for page indicator is calculated from scroll adjustment, horizontal needs to be replaced by vertical + appDisplay._adjustment = appDisplay._scrollView[scroll].adjustment; + + // no need to connect already connected signal (wasn't removed the original one before) + if (!vertical) { + // reset used appDisplay properties + Main.overview._overview._controls._appDisplay.scale_y = 1; + Main.overview._overview._controls._appDisplay.scale_x = 1; + Main.overview._overview._controls._appDisplay.opacity = 255; + return; } - appDisplay._nextPageArrow.scale_x = 1; - appDisplay._prevPageArrow.scale_x = 1; + // update appGrid dot pages indicators + this._appDisplayScrollConId = appDisplay._adjustment.connect('notify::value', adj => { + const value = adj.value / adj.page_size; + appDisplay._pageIndicators.setCurrentPosition(value); + }); + } + + // Set App Grid columns, rows, icon size, incomplete pages + _updateAppDisplayProperties(reset = false) { + opt._appGridNeedsRedisplay = false; + // columns, rows, icon size + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay.visible = true; + if (reset) { + appDisplay._grid.layoutManager.fixedIconSize = -1; + appDisplay._grid.layoutManager.allow_incomplete_pages = true; + appDisplay._grid._currentMode = -1; + appDisplay._grid.setGridModes(); + if (this._appGridLayoutSettings) { + this._appGridLayoutSettings.disconnect(this._appGridLayoutConId); + this._appGridLayoutConId = 0; + this._appGridLayoutSettings = null; + } + appDisplay._redisplay(); - appDisplay._nextPageIndicator.remove_style_class_name('nextPageIndicator'); - appDisplay._prevPageIndicator.remove_style_class_name('prevPageIndicator'); + appDisplay._grid.set_style(''); + this._updateAppGrid(reset); + } else { + // update grid on layout reset + if (!this._appGridLayoutSettings) { + this._appGridLayoutSettings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); + this._appGridLayoutConId = this._appGridLayoutSettings.connect('changed::app-picker-layout', this._updateLayout); + } + + appDisplay._grid.layoutManager.allow_incomplete_pages = opt.APP_GRID_ALLOW_INCOMPLETE_PAGES; + // appDisplay._grid.set_style(`column-spacing: ${opt.APP_GRID_SPACING}px; row-spacing: ${opt.APP_GRID_SPACING}px;`); + // APP_GRID_SPACING constant is used for grid dimensions calculation + // but sometimes the actual grid spacing properties affect/change the calculated size, therefore we set it lower to avoid this problem + // main app grid always use available space and the spacing is optimized for the grid dimensions + appDisplay._grid.set_style('column-spacing: 5px; row-spacing: 5px;'); + + // force redisplay + appDisplay._grid._currentMode = -1; + appDisplay._grid.setGridModes(); + appDisplay._grid.layoutManager.fixedIconSize = opt.APP_GRID_ICON_SIZE; + // avoid resetting appDisplay before startup animation + // x11 shell restart skips startup animation + if (!Main.layoutManager._startingUp) { + this._updateAppGrid(); + } else if (Main.layoutManager._startingUp && (Meta.is_restart() || Me.Util.dashIsDashToDock())) { + _timeouts.three = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._updateAppGrid(); + _timeouts.three = 0; + return GLib.SOURCE_REMOVE; + }); + } + } } - // value for page indicator is calculated from scroll adjustment, horizontal needs to be replaced by vertical - appDisplay._adjustment = appDisplay._scrollView[scroll].adjustment; + _updateDND(reset) { + if (!reset) { + if (!this._appSystemStateConId && opt.APP_GRID_INCLUDE_DASH >= 3) { + this._appSystemStateConId = Shell.AppSystem.get_default().connect( + 'app-state-changed', + () => { + this._updateFolderIcons = true; + Main.overview._overview.controls._appDisplay._redisplay(); + } + ); + } + } else if (this._appSystemStateConId) { + Shell.AppSystem.get_default().disconnect(this._appSystemStateConId); + this._appSystemStateConId = 0; + } + } - // no need to connect already connected signal (wasn't removed the original one before) - if (!vertical) { - // reset used appDisplay properties - Main.overview._overview._controls._appDisplay.scale_y = 1; - Main.overview._overview._controls._appDisplay.scale_x = 1; + _restoreOverviewGroup() { + Main.overview.dash.showAppsButton.checked = false; + Main.layoutManager.overviewGroup.opacity = 255; + Main.layoutManager.overviewGroup.scale_x = 1; + Main.layoutManager.overviewGroup.scale_y = 1; + Main.layoutManager.overviewGroup.hide(); + Main.overview._overview._controls._appDisplay.translation_x = 0; + Main.overview._overview._controls._appDisplay.translation_y = 0; + Main.overview._overview._controls._appDisplay.visible = true; Main.overview._overview._controls._appDisplay.opacity = 255; - return; } - // update appGrid dot pages indicators - _appDisplayScrollConId = appDisplay._adjustment.connect('notify::value', adj => { - const value = adj.value / adj.page_size; - appDisplay._pageIndicators.setCurrentPosition(value); - }); -} - -// Set App Grid columns, rows, icon size, incomplete pages -function _updateAppGridProperties(reset = false) { - opt._appGridNeedsRedisplay = false; - // columns, rows, icon size - const appDisplay = Main.overview._overview._controls._appDisplay; - appDisplay.visible = true; - - if (reset) { - appDisplay._grid.layoutManager.fixedIconSize = -1; - appDisplay._grid.layoutManager.allow_incomplete_pages = true; - appDisplay._grid.setGridModes(); - if (_appGridLayoutSettings) { - _appGridLayoutSettings.disconnect(_appGridLayoutConId); - _appGridLayoutConId = 0; - _appGridLayoutSettings = null; + // update all invalid positions that may be result of grid/icon size change + _updateIconPositions() { + const appDisplay = Main.overview._overview._controls._appDisplay; + const layout = JSON.stringify(global.settings.get_value('app-picker-layout').recursiveUnpack()); + // if app grid layout is empty, sort source alphabetically to avoid misplacing + if (layout === JSON.stringify([]) && appDisplay._sortOrderedItemsAlphabetically) + appDisplay._sortOrderedItemsAlphabetically(); + const icons = [...appDisplay._orderedItems]; + for (let i = 0; i < icons.length; i++) + appDisplay._moveItem(icons[i], -1, -1); + } + + _removeIcons() { + const appDisplay = Main.overview._overview._controls._appDisplay; + const icons = [...appDisplay._orderedItems]; + for (let i = 0; i < icons.length; i++) { + const icon = icons[i]; + if (icon._dialog) + Main.layoutManager.overviewGroup.remove_child(icon._dialog); + appDisplay._removeItem(icon); + icon.destroy(); } - appDisplay._redisplay(); + appDisplay._folderIcons = []; + } + + _removeStatusMessage() { + if (Me._vShellStatusMessage) { + if (Me._vShellMessageTimeoutId) { + GLib.source_remove(Me._vShellMessageTimeoutId); + Me._vShellMessageTimeoutId = 0; + } + Me._vShellStatusMessage.destroy(); + Me._vShellStatusMessage = null; + } + } - appDisplay._grid.set_style(''); - _resetAppGrid(); - } else { - // update grid on layout reset - if (!_appGridLayoutSettings) { - _appGridLayoutSettings = ExtensionUtils.getSettings('org.gnome.shell'); - _appGridLayoutConId = _appGridLayoutSettings.connect('changed::app-picker-layout', _resetAppGrid); + _updateLayout(settings, key) { + const currentValue = JSON.stringify(settings.get_value(key).deep_unpack()); + const emptyValue = JSON.stringify([]); + const customLayout = currentValue !== emptyValue; + if (!customLayout) { + this._updateAppGrid(); + } + } + + _updateAppGrid(reset = false, callback) { + const appDisplay = Main.overview._overview._controls._appDisplay; + // reset the grid only if called directly without args or if all folders where removed by using reset button in Settings window + // otherwise this function is called every time a user moves icon to another position as a settings callback + + // force update icon size using adaptToSize(), the page size cannot be the same as the current one + appDisplay._grid.layoutManager._pageWidth += 1; + appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight); + + // don't delay the first screen lock on GS < 44, removing icons takes a time and with other 15 enabled extensions it can be multiplied by 15 + if (!Main.sessionMode.isLocked) + this._removeIcons(); + + appDisplay._redisplay(); + // don't realize appDisplay on disable, or at startup if disabled + // always realize appDisplay otherwise to avoid errors while opening folders (that I was unable to trace) + if (reset || (!opt.APP_GRID_PERFORMANCE && callback)) { + this._removeStatusMessage(); + if (callback) + callback(); + return; } - appDisplay._grid.layoutManager.allow_incomplete_pages = opt.APP_GRID_ALLOW_INCOMPLETE_PAGES; - appDisplay._grid.set_style(`column-spacing: ${opt.APP_GRID_SPACING}px; row-spacing: ${opt.APP_GRID_SPACING}px;`); + // workaround - silently realize appDisplay + // appDisplay and its content must be "visible" (opacity > 0) on the screen (within monitor geometry) + // to realize its objects + // this action takes some time and affects animations during the first use + // if we do it invisibly before user needs it, it can improve the user's experience + + this._exposeAppGrid(); + + // let the main loop process our changes before continuing + _timeouts.one = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._updateIconPositions(); + if (appDisplay._sortOrderedItemsAlphabetically) { + appDisplay._sortOrderedItemsAlphabetically(); + appDisplay._grid.layoutManager._pageWidth += 1; + appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight); + appDisplay._setLinearPositions(appDisplay._orderedItems); + } - // force redisplay - appDisplay._grid._currentMode = -1; - appDisplay._grid.setGridModes(); - appDisplay._grid.layoutManager.fixedIconSize = opt.APP_GRID_ICON_SIZE; - // appDisplay._folderIcons.forEach(folder => folder._dialog?._updateFolderSize()); - _resetAppGrid(); + appDisplay._redisplay(); + // realize also all app folders (by opening them) so the first popup is as smooth as the second one + // let the main loop process our changes before continuing + _timeouts.two = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._restoreAppGrid(); + Me._resetInProgress = false; + this._removeStatusMessage(); + + if (callback) + callback(); + + _timeouts.two = 0; + return GLib.SOURCE_REMOVE; + }); + _timeouts.one = 0; + return GLib.SOURCE_REMOVE; + }); } -} -function _updateAppGridDND(reset) { - if (!reset) { - if (!_appSystemStateConId && opt.APP_GRID_INCLUDE_DASH >= 3) { - _appSystemStateConId = Shell.AppSystem.get_default().connect( - 'app-state-changed', - () => { - _updateFolderIcons = true; - Main.overview._overview._controls._appDisplay._redisplay(); - } - ); + _exposeAppGrid() { + const overviewGroup = Main.layoutManager.overviewGroup; + if (!overviewGroup.visible) { + // scale down the overviewGroup so it don't cover uiGroup + overviewGroup.scale_y = 0.001; + // make it invisible to the eye, but visible for the renderer + overviewGroup.opacity = 1; + // if overview is hidden, show it + overviewGroup.visible = true; } - } else if (_appSystemStateConId) { - Shell.AppSystem.get_default().disconnect(_appSystemStateConId); - _appSystemStateConId = 0; + + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay.opacity = 1; + + // find usable value, sometimes it's one, sometime the other... + let [x, y] = appDisplay.get_position(); + let { x1, y1 } = appDisplay.allocation; + x = x === Infinity ? 0 : x; + y = y === Infinity ? 0 : y; + x1 = x1 === Infinity ? 0 : x1; + y1 = y1 === Infinity ? 0 : y1; + appDisplay.translation_x = -(x ? x : x1); + appDisplay.translation_y = -(y ? y : y1); + this._exposeAppFolders(); + } + + _exposeAppFolders() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay._folderIcons.forEach(d => { + d._ensureFolderDialog(); + d._dialog._updateFolderSize(); + d._dialog.scale_y = 0.0001; + d._dialog.show(); + }); + } + + _restoreAppGrid() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay.translation_x = 0; + appDisplay.translation_y = 0; + // appDisplay.opacity = 0; + this._hideAppFolders(); + + const overviewGroup = Main.layoutManager.overviewGroup; + if (!Main.overview._shown) + overviewGroup.hide(); + overviewGroup.scale_y = 1; + overviewGroup.opacity = 255; + + this._removeStatusMessage(); + } + + _hideAppFolders() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay._folderIcons.forEach(d => { + if (d._dialog) { + d._dialog._updateFolderSize(); + d._dialog.hide(); + d._dialog.scale_y = 1; + } + }); + } + + _getWindowApp(metaWin) { + const tracker = Shell.WindowTracker.get_default(); + return tracker.get_window_app(metaWin); } - if (opt.APP_GRID_ORDER && !reset) { - if (!_origAppViewItemAcceptDrop) - _origAppViewItemAcceptDrop = AppDisplay.AppViewItem.prototype.acceptDrop; - AppDisplay.AppViewItem.prototype.acceptDrop = () => false; - } else if (_origAppViewItemAcceptDrop) { - AppDisplay.AppViewItem.prototype.acceptDrop = _origAppViewItemAcceptDrop; + + _getAppLastUsedWindow(app) { + let recentWin; + global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => { + const winApp = this._getWindowApp(metaWin); + if (!recentWin && winApp === app) + recentWin = metaWin; + }); + return recentWin; } -} -function _restoreOverviewGroup() { - Main.overview.dash.showAppsButton.checked = false; - Main.layoutManager.overviewGroup.opacity = 255; - Main.layoutManager.overviewGroup.scale_x = 1; - Main.layoutManager.overviewGroup.hide(); -} + _getAppRecentWorkspace(app) { + const recentWin = this._getAppLastUsedWindow(app); + if (recentWin) + return recentWin.get_workspace(); + + return null; + } +}; const AppDisplayVertical = { // correction of the appGrid size when page indicators were moved from the bottom to the right @@ -302,7 +557,7 @@ const AppDisplayCommon = { const appsInsideFolders = new Set(); this._folderIcons = []; - if (!opt.APP_GRID_ORDER) { + if (!opt.APP_GRID_USAGE) { let folders = this._folderSettings.get_strv('folder-children'); folders.forEach(id => { let path = `${this._folderSettings.path}folders/${id}/`; @@ -317,8 +572,8 @@ const AppDisplayCommon = { if (icon.pressed) this.updateDragFocus(icon); }); - } else if (_updateFolderIcons && opt.APP_GRID_EXCLUDE_RUNNING) { - // if any app changed its running state, update folder icon + } else if (this._updateFolderIcons && opt.APP_GRID_EXCLUDE_RUNNING) { + // if any app changed its running state, update folder icon icon.icon.update(); } @@ -334,8 +589,9 @@ const AppDisplayCommon = { icon.getAppIds().forEach(appId => appsInsideFolders.add(appId)); }); } + // reset request to update active icon - _updateFolderIcons = false; + this._updateFolderIcons = false; // Allow dragging of the icon only if the Dash would accept a drop to // change favorite-apps. There are no other possible drop targets from @@ -348,7 +604,7 @@ const AppDisplayCommon = { global.settings.is_writable('app-picker-layout'); apps.forEach(appId => { - if (!opt.APP_GRID_ORDER && appsInsideFolders.has(appId)) + if (!opt.APP_GRID_USAGE && appsInsideFolders.has(appId)) return; let icon = this._items.get(appId); @@ -380,7 +636,7 @@ const AppDisplayCommon = { dragMotion: this._onDragMotion.bind(this), }; DND.addDragMonitor(this._dragMonitor); - if (shellVersion < 43) + if (Me.shellVersion < 43) this._slideSidePages(AppDisplay.SidePages.PREVIOUS | AppDisplay.SidePages.NEXT | AppDisplay.SidePages.DND); else this._appGridLayout.showPageIndicators(); @@ -419,50 +675,16 @@ const AppDisplayCommon = { this._redisplay(); }, - // accept source from active preview + // accept source from active folder preview acceptDrop(source) { - if (opt.APP_GRID_ORDER) + if (opt.APP_GRID_USAGE) return false; if (source._sourceItem) source = source._sourceItem; - let dropTarget = null; - if (shellVersion >= 43) { - dropTarget = this._dropTarget; - delete this._dropTarget; - } - - if (!this._canAccept(source)) + if (!BaseAppViewCommon.acceptDrop.bind(this)(source)) return false; - if ((shellVersion < 43 && this._dropPage) || - (shellVersion >= 43 && (dropTarget === this._prevPageIndicator || - dropTarget === this._nextPageIndicator))) { - let increment; - - if (shellVersion < 43) - increment = this._dropPage === AppDisplay.SidePages.NEXT ? 1 : -1; - else - increment = dropTarget === this._prevPageIndicator ? -1 : 1; - - const { currentPage, nPages } = this._grid; - const page = Math.min(currentPage + increment, nPages); - const position = page < nPages ? -1 : 0; - - this._moveItem(source, page, position); - this.goToPage(page); - } else if (this._delayedMoveData) { - // Dropped before the icon was moved - const { page, position } = this._delayedMoveData; - - try { - this._moveItem(source, page, position); - } catch (e) { - log(`Warning:${e}`); - } - this._removeDelayedMove(); - } - this._savePages(); let view = AppDisplay._getViewFromIcon(source); @@ -493,7 +715,7 @@ const BaseAppViewVertical = { this._pageIndicators.x_align = Clutter.ActorAlign.START; this._pageIndicators.set_style('margin-right: 10px;'); const scrollContainer = this._scrollView.get_parent(); - if (shellVersion < 43) { + if (Me.shellVersion < 43) { // remove touch friendly side navigation bars / arrows if (this._hintContainer && this._hintContainer.get_parent()) scrollContainer.remove_child(this._hintContainer); @@ -536,7 +758,7 @@ const BaseAppViewCommon = { try { this._moveItem(icon, page, position); } catch (e) { - log(`Warning:${e}`); + console.warn(`Warning:${e}`); } }); }, @@ -570,14 +792,19 @@ const BaseAppViewCommon = { } }); - // sort all alphabetically - if (opt.APP_GRID_ORDER > 0) { + // different options for root app grid and app folders + const thisIsFolder = this instanceof AppDisplay.FolderView; + const thisIsAppDisplay = !thisIsFolder; + if ((opt.APP_GRID_ORDER && thisIsAppDisplay) || + (opt.APP_FOLDER_ORDER && thisIsFolder)) { // const { itemsPerPage } = this._grid; let appIcons = this._orderedItems; + // sort all alphabetically this._sortOrderedItemsAlphabetically(appIcons); // appIcons.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())); // then sort used apps by usage - if (opt.APP_GRID_ORDER === 2) + if ((opt.APP_GRID_USAGE && thisIsAppDisplay) || + (opt.APP_FOLDER_USAGE && thisIsFolder)) appIcons.sort((a, b) => Shell.AppUsage.get_default().compare(a.app.id, b.app.id)); // sort favorites first @@ -595,9 +822,14 @@ const BaseAppViewCommon = { } // sort running first - if (opt.APP_GRID_DASH_FIRST) + if (opt.APP_GRID_DASH_FIRST && thisIsAppDisplay) appIcons.sort((a, b) => a.app.get_state() !== Shell.AppState.RUNNING && b.app.get_state() === Shell.AppState.RUNNING); + if (opt.APP_GRID_FOLDERS_FIRST) + appIcons.sort((a, b) => b._folder && !a._folder); + else if (opt.APP_GRID_FOLDERS_LAST) + appIcons.sort((a, b) => a._folder && !b._folder); + this._setLinearPositions(appIcons); this._orderedItems = appIcons; @@ -611,7 +843,7 @@ const BaseAppViewCommon = { }, _canAccept(source) { - return opt.APP_GRID_ORDER ? false : source instanceof AppDisplay.AppViewItem; + return source instanceof AppDisplay.AppViewItem; }, // support active preview icons @@ -619,12 +851,25 @@ const BaseAppViewCommon = { if (!this._canAccept(source)) return false; - if (source._sourceItem) - source = source._sourceItem; + let dropTarget = null; + if (Me.shellVersion >= 43) { + dropTarget = this._dropTarget; + delete this._dropTarget; + } + + if (!this._canAccept(source)) + return false; + if ((Me.shellVersion < 43 && this._dropPage) || + (Me.shellVersion >= 43 && (dropTarget === this._prevPageIndicator || + dropTarget === this._nextPageIndicator))) { + let increment; + + if (Me.shellVersion < 43) + increment = this._dropPage === AppDisplay.SidePages.NEXT ? 1 : -1; + else + increment = dropTarget === this._prevPageIndicator ? -1 : 1; - if (this._dropPage) { - const increment = this._dropPage === AppDisplay.SidePages.NEXT ? 1 : -1; const { currentPage, nPages } = this._grid; const page = Math.min(currentPage + increment, nPages); const position = page < nPages ? -1 : 0; @@ -635,7 +880,11 @@ const BaseAppViewCommon = { // Dropped before the icon was moved const { page, position } = this._delayedMoveData; - this._moveItem(source, page, position); + try { + this._moveItem(source, page, position); + } catch (e) { + console.warn(`Warning:${e}`); + } this._removeDelayedMove(); } @@ -652,7 +901,7 @@ const BaseAppViewCommon = { const appIcon = dragEvent.source; - if (shellVersion < 43) { + if (Me.shellVersion < 43) { this._dropPage = this._pageForCoords(dragEvent.x, dragEvent.y); if (this._dropPage && this._dropPage === AppDisplay.SidePages.PREVIOUS && @@ -663,7 +912,7 @@ const BaseAppViewCommon = { } if (appIcon instanceof AppDisplay.AppViewItem) { - if (shellVersion < 44) { + if (Me.shellVersion < 44) { // Handle the drag overshoot. When dragging to above the // icon grid, move to the page above; when dragging below, // move to the page below. @@ -685,7 +934,10 @@ const BaseAppViewCommon = { } } - this._maybeMoveItem(dragEvent); + const thisIsFolder = this instanceof AppDisplay.FolderView; + const thisIsAppDisplay = !thisIsFolder; + if ((!opt.APP_GRID_ORDER && thisIsAppDisplay) || (!opt.APP_FOLDER_ORDER && thisIsFolder)) + this._maybeMoveItem(dragEvent); return DND.DragMotionResult.CONTINUE; }, @@ -771,16 +1023,12 @@ const FolderIcon = { : St.ButtonMask.ONE | St.ButtonMask.TWO; this.button_mask = buttonMask;*/ this.button_mask = St.ButtonMask.ONE | St.ButtonMask.TWO; - - // build the folders now to avoid node errors when dragging active folder preview icons - if (this.visible && opt.APP_GRID_ACTIVE_PREVIEW) - this._ensureFolderDialog(); }, open() { this._ensureFolderDialog(); - if (this._dialog._designCapacity !== this.view._orderedItems.length) - this._dialog._updateFolderSize(); + // if (this._dialog._designCapacity !== this.view._orderedItems.length) + this._dialog._updateFolderSize(); this.view._scrollView.vscroll.adjustment.value = 0; this._dialog.popup(); @@ -789,15 +1037,7 @@ const FolderIcon = { const FolderView = { _createGrid() { - let grid; - if (shellVersion < 43) - grid = new FolderGrid(); - else - grid = new FolderGrid43(); - - // IconGrid algorithm for adaptive icon size - // counts with different default(max) size for folders - grid.layoutManager._isFolder = true; + let grid = new AppDisplay.FolderGrid(); return grid; }, @@ -832,11 +1072,15 @@ const FolderView = { bin.child = this._orderedItems[i].app.create_icon_texture(subSize); } else { const app = this._orderedItems[i].app; - const child = new ActiveFolderIcon(app); + const child = new AppDisplay.AppIcon(app, { + setSizeManually: true, + showLabel: false, + }); child._sourceItem = this._orderedItems[i]; child._sourceFolder = this; child.icon.style_class = ''; child.icon.set_style('margin: 0; padding: 0;'); + child._dot.set_style('margin-bottom: 1px;'); child.icon.setIconSize(subSize); bin.child = child; @@ -863,9 +1107,9 @@ const FolderView = { layout.attach(bin, rtl ? (i + 1) % gridSize : i % gridSize, Math.floor(i / gridSize), 1, 1); } - // if folder content changed, update folder size - if (this._dialog && this._dialog._designCapacity !== this._orderedItems.length) - this._dialog._updateFolderSize(); + // if folder content changed, update folder size, but not if it's empty + /* if (this._dialog && this._dialog._designCapacity !== this._orderedItems.length && this._orderedItems.length) + this._dialog._updateFolderSize();*/ return icon; }, @@ -927,6 +1171,13 @@ const FolderView = { items.push(icon); }); + + if (opt.APP_FOLDER_ORDER) + Main.overview._overview.controls._appDisplay._sortOrderedItemsAlphabetically(items); + + if (opt.APP_FOLDER_USAGE) + items.sort((a, b) => Shell.AppUsage.get_default().compare(a.app.id, b.app.id)); + this._appIds = this._apps.map(app => app.get_id()); return items; }, @@ -942,10 +1193,9 @@ const FolderView = { }; // folder columns and rows -const FolderGrid = GObject.registerClass( -class FolderGrid extends IconGrid.IconGrid { +const FolderGridLegacy = { _init() { - super._init({ + IconGrid.IconGrid.prototype._init.bind(this)({ allow_incomplete_pages: false, // For adaptive size (0), set the numbers high enough to fit all the icons // to avoid splitting the icons to pages @@ -954,56 +1204,52 @@ class FolderGrid extends IconGrid.IconGrid { page_halign: Clutter.ActorAlign.CENTER, page_valign: Clutter.ActorAlign.CENTER, }); - + this.layout_manager._isFolder = true; // if (!opt.APP_GRID_FOLDER_DEFAULT) const spacing = opt.APP_GRID_SPACING; this.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`); - this.layout_manager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; - } + this.layoutManager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; + }, adaptToSize(width, height) { this.layout_manager.adaptToSize(width, height); - } -}); - - -let FolderGrid43; -// first reference to constant defined using const in other module returns undefined, the AppGrid const will remain empty and unused -const AppGrid = AppDisplay.AppGrid; -if (AppDisplay.AppGrid) { - FolderGrid43 = GObject.registerClass( - class FolderGrid43 extends AppDisplay.AppGrid { - _init() { - super._init({ - allow_incomplete_pages: false, - columns_per_page: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 20, - rows_per_page: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 20, - page_halign: Clutter.ActorAlign.CENTER, - page_valign: Clutter.ActorAlign.CENTER, - }); + }, +}; + +const FolderGrid = { + _init() { + AppDisplay.AppGrid.prototype._init.bind(this)({ + allow_incomplete_pages: false, + columns_per_page: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 20, + rows_per_page: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 20, + page_halign: Clutter.ActorAlign.CENTER, + page_valign: Clutter.ActorAlign.CENTER, + }); + this.layout_manager._isFolder = true; + const spacing = opt.APP_GRID_SPACING; + this.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`); + this.layoutManager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; - const spacing = opt.APP_GRID_SPACING; - this.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`); - this.layout_manager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; + this.setGridModes([ + { + columns: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 3, + rows: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 3, + }, + ]); + }, - this.setGridModes([ - { - columns: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 3, - rows: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 3, - }, - ]); - } + adaptToSize(width, height) { + this.layout_manager.adaptToSize(width, height); + }, +}; - adaptToSize(width, height) { - this.layout_manager.adaptToSize(width, height); - } - }); -} const FOLDER_DIALOG_ANIMATION_TIME = 200; // AppDisplay.FOLDER_DIALOG_ANIMATION_TIME const AppFolderDialog = { // injection to _init() after__init() { + this._viewBox.add_style_class_name('app-folder-dialog-vshell'); + // delegate this dialog to the FolderIcon._view // so its _createFolderIcon function can update the dialog if folder content changed this._view._dialog = this; @@ -1023,18 +1269,65 @@ const AppFolderDialog = { }); this.child.add_action(clickAction); + + // Adjust empty actor to center the title + this._entryBox.get_first_child().width = 82; + }, + + after__addFolderNameEntry() { + // Edit button + this._removeButton = new St.Button({ + style_class: 'edit-folder-button', + button_mask: St.ButtonMask.ONE, + toggle_mode: false, + reactive: true, + can_focus: true, + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.CENTER, + child: new St.Icon({ + icon_name: 'user-trash-symbolic', + icon_size: 16, + }), + }); + + this._removeButton.connect('clicked', () => { + if (Date.now() - this._removeButton._lastClick < Clutter.Settings.get_default().double_click_time) { + this._grabHelper.ungrab({ actor: this }); + // without hiding the dialog, Shell crashes (at least on X11) + this.hide(); + this._view._deletingFolder = true; + + // Resetting all keys deletes the relocatable schema + let keys = this._folder.settings_schema.list_keys(); + for (const key of keys) + this._folder.reset(key); + + let settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' }); + let folders = settings.get_strv('folder-children'); + folders.splice(folders.indexOf(this._view._id), 1); + + // remove all abandoned folders (usually my own garbage and unwanted default folders...) + /* const appFolders = this._appDisplay._folderIcons.map(icon => icon._id); + folders.forEach(folder => { + if (!appFolders.includes(folder)) { + folders.splice(folders.indexOf(folder._id), 1); + } + });*/ + settings.set_strv('folder-children', folders); + + this._view._deletingFolder = false; + return; + } + this._removeButton._lastClick = Date.now(); + }); + + this._entryBox.add_child(this._removeButton); }, popup() { if (this._isOpen) return; - /* if (!this._correctSize) { - // update folder with the precise app item size when the dialog is realized - GLib.idle_add(0, () => this._updateFolderSize(true)); - this._correctSize = true; - }*/ - this._isOpen = this._grabHelper.grab({ actor: this, onUngrab: () => this.popdown(), @@ -1046,26 +1339,48 @@ const AppFolderDialog = { this.get_parent().set_child_above_sibling(this, null); this._needsZoomAndFade = true; - this.show(); + // the first folder dialog realization needs size correction + // so set the folder size, let it realize and then update the folder content + if (!this.realized) { + this._updateFolderSize(); + GLib.idle_add( + GLib.PRIORITY_DEFAULT, + () => { + this._updateFolderSize(); + } + ); + } + + this.show(); this.emit('open-state-changed', true); }, _updateFolderSize() { - // adapt folder size according to the settings and number of icons const view = this._view; + const [firstItem] = view._grid.layoutManager._container; + if (!firstItem) + return; + // adapt folder size according to the settings and number of icons + const appDisplay = this._source._parentView; + if (!appDisplay.width || appDisplay.allocation.x2 === Infinity || appDisplay.allocation.x2 === -Infinity) { + return; + } + view._grid.layoutManager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; view._grid.set_style(`column-spacing: ${opt.APP_GRID_SPACING}px; row-spacing: ${opt.APP_GRID_SPACING}px;`); const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); - const dialogMargin = 30; + const itemPadding = 55; // default icon item padding on Fedora 44 + // const dialogMargin = 30; const nItems = view._orderedItems.length; let columns = opt.APP_GRID_FOLDER_COLUMNS; let rows = opt.APP_GRID_FOLDER_ROWS; + const fullAdaptiveGrid = !columns && !rows; let spacing = opt.APP_GRID_SPACING; - const monitor = global.display.get_monitor_geometry(global.display.get_primary_monitor()); + const minItemSize = 48 + itemPadding; - if (!columns && !rows) { + if (fullAdaptiveGrid) { columns = Math.ceil(Math.sqrt(nItems)); rows = columns; if (columns * (columns - 1) >= nItems) { @@ -1081,38 +1396,67 @@ const AppFolderDialog = { } const iconSize = opt.APP_GRID_FOLDER_ICON_SIZE < 0 ? opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT : opt.APP_GRID_FOLDER_ICON_SIZE; - let itemSize = iconSize + 53; // icon padding + view._grid.layoutManager.fixedIconSize = iconSize; + + let itemSize = iconSize + 55; // icon padding // first run sets the grid before we can read the real icon size // so we estimate the size from default properties // and correct it in the second run - if (this._notFirstRun) { - const [firstItem] = view._grid.layoutManager._container; + if (this.realized) { firstItem.icon.setIconSize(iconSize); const [firstItemWidth] = firstItem.get_preferred_size(); const realSize = firstItemWidth / scaleFactor; - if (realSize > iconSize) + // if the preferred item size is smaller than icon plus some padding, ignore it + // (icons that are not yet realized are returning sizes like 45 or 53) + if (realSize > (iconSize + 24)) itemSize = realSize; - } else { - this._needsUpdateSize = true; - this._notFirstRun = true; } - let width = columns * (itemSize + spacing) + /* padding for nav arrows*/64; - width = Math.round(width + (opt.ORIENTATION || !opt.APP_GRID_FOLDER_COLUMNS ? 100 : 160/* space for navigation arrows*/)); - let height = rows * (itemSize + spacing) + /* header*/75 + /* padding*/100; + width = Math.round(width + (opt.ORIENTATION ? 100 : 160/* space for navigation arrows*/)); + let height = rows * (itemSize + spacing) + /* header*/75 + /* padding*/ 2 * 30 + /* padding + ?page indicator*/(!opt.ORIENTATION || !opt.APP_GRID_FOLDER_COLUMNS ? 100 : 70); + + // allocation is more reliable than appDisplay width/height properties + const appDisplayWidth = appDisplay.allocation.x2 - appDisplay.allocation.x1; + const appDisplayHeight = appDisplay.allocation.y2 - appDisplay.allocation.y1 + (opt.SHOW_SEARCH_ENTRY ? Main.overview._overview.controls._searchEntryBin.height : 0); - // folder must fit the primary monitor + // folder must fit the appDisplay area // reduce columns/rows if needed and count with the scaled values - while (width * scaleFactor > monitor.width - 2 * dialogMargin) { - width -= itemSize + spacing; - columns -= 1; + if (!opt.APP_GRID_FOLDER_ROWS) { + while ((height * scaleFactor) > appDisplayHeight) { + height -= itemSize + spacing; + rows -= 1; + } + } + + if (!opt.APP_GRID_FOLDER_COLUMNS) { + while ((width * scaleFactor) > appDisplayWidth) { + width -= itemSize + spacing; + columns -= 1; + } } - while (height * scaleFactor > monitor.height - 2 * dialogMargin) { - height -= itemSize + spacing; - rows -= 1; + // try to compensate for the previous reduction if there is a space + if (!opt.APP_GRID_FOLDER_COLUMNS) { + while ((nItems > columns * rows) && ((width * scaleFactor + itemSize + spacing) <= appDisplayWidth)) { + width += itemSize + spacing; + columns += 1; + } + // remove columns that cannot be displayed + if ((columns * minItemSize + (columns - 1) * spacing) > appDisplayWidth) + columns = Math.floor(appDisplayWidth / (minItemSize + spacing)); } - width = Math.max(540, width); + if (!opt.APP_GRID_FOLDER_ROWS) { + while ((nItems > columns * rows) && ((height * scaleFactor + itemSize + spacing) <= appDisplayHeight)) { + height += itemSize + spacing; + rows += 1; + } + // remove rows that cannot be displayed + if ((rows * minItemSize + (rows - 1) * spacing) > appDisplayHeight) + rows = Math.floor(appDisplayWidth / (minItemSize + spacing)); + } + + width = Math.clamp(width, 640, appDisplayWidth); + height = Math.min(height, appDisplayHeight); const layoutManager = view._grid.layoutManager; layoutManager.rows_per_page = rows; @@ -1127,6 +1471,8 @@ const AppFolderDialog = { padding: 30px; `); + view._grid.layoutManager._pageWidth += 1; + view._grid.layoutManager.adaptToSize(view._grid.layoutManager._pageWidth - 1, view._grid.layoutManager._pageHeight); view._redisplay(); // store original item count @@ -1145,40 +1491,34 @@ const AppFolderDialog = { // this. covers the whole screen let dialogTargetX = dialogX; let dialogTargetY = dialogY; - if (!opt.APP_GRID_FOLDER_CENTER) { - const appDisplay = this._source._parentView; - dialogTargetX = Math.round(sourceCenterX - this.child.width / 2); - dialogTargetY = Math.round(sourceCenterY - this.child.height / 2); + const appDisplay = this._source._parentView; + const [appDisplayX, appDisplayY] = this._source._parentView.get_transformed_position(); - // keep the dialog in appDisplay area if possible - dialogTargetX = Math.clamp( - dialogTargetX, - this.x + appDisplay.x, - this.x + appDisplay.x + appDisplay.width - this.child.width - ); + if (!opt.APP_GRID_FOLDER_CENTER) { + dialogTargetX = sourceCenterX - this.child.width / 2; + dialogTargetY = sourceCenterY - this.child.height / 2; - dialogTargetY = Math.clamp( - dialogTargetY, - this.y + appDisplay.y, - this.y + appDisplay.y + appDisplay.height - this.child.height - ); - // or at least in the monitor area - const monitor = global.display.get_monitor_geometry(global.display.get_primary_monitor()); + // keep the dialog in appDisplay area if possible dialogTargetX = Math.clamp( dialogTargetX, - this.x + monitor.x, - this.x + monitor.x + monitor.width - this.child.width + appDisplayX, + appDisplayX + appDisplay.width - this.child.width ); dialogTargetY = Math.clamp( dialogTargetY, - this.y + monitor.y, - this.y + monitor.y + monitor.height - this.child.height + appDisplayY, + appDisplayY + appDisplay.height - this.child.height ); + } else { + const searchEntryHeight = opt.SHOW_SEARCH_ENTRY ? Main.overview._overview.controls._searchEntryBin.height : 0; + dialogTargetX = appDisplayX + appDisplay.width / 2 - this.child.width / 2; + dialogTargetY = appDisplayY - searchEntryHeight + ((appDisplay.height + searchEntryHeight) / 2 - this.child.height / 2) / 2; } - const dialogOffsetX = -dialogX + dialogTargetX; - const dialogOffsetY = -dialogY + dialogTargetY; + + const dialogOffsetX = Math.round(dialogTargetX - dialogX); + const dialogOffsetY = Math.round(dialogTargetY - dialogY); this.child.set({ translation_x: sourceX - dialogX, @@ -1188,12 +1528,6 @@ const AppFolderDialog = { opacity: 0, }); - this.ease({ - background_color: DIALOG_SHADE_NORMAL, - duration: FOLDER_DIALOG_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - this.child.ease({ translation_x: dialogOffsetX, translation_y: dialogOffsetY, @@ -1202,17 +1536,22 @@ const AppFolderDialog = { opacity: 255, duration: FOLDER_DIALOG_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - // if the folder grid was build with the estimated icon item size because the real size wasn't available - // rebuild it with the real size now, after the folder was realized - if (this._needsUpdateSize) { - this._updateFolderSize(); - this._view._redisplay(); - this._needsUpdateSize = false; - } - }, }); + appDisplay.ease({ + opacity: 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + if (opt.SHOW_SEARCH_ENTRY) { + Main.overview.searchEntry.ease({ + opacity: 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + this._needsZoomAndFade = false; if (this._sourceMappedId === 0) { @@ -1230,17 +1569,20 @@ const AppFolderDialog = { return; } + // if the dialog was shown silently, skip animation + if (this.scale_y < 1) { + this._needsZoomAndFade = false; + this.hide(); + this._popdownCallbacks.forEach(func => func()); + this._popdownCallbacks = []; + return; + } + let [sourceX, sourceY] = this._source.get_transformed_position(); let [dialogX, dialogY] = this.child.get_transformed_position(); - this.ease({ - background_color: Clutter.Color.from_pixel(0x00000000), - duration: FOLDER_DIALOG_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - this.child.ease({ translation_x: sourceX - dialogX + this.child.translation_x, translation_y: sourceY - dialogY + this.child.translation_y, @@ -1264,11 +1606,35 @@ const AppFolderDialog = { }, }); + const appDisplay = this._source._parentView; + appDisplay.ease({ + opacity: 255, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + if (opt.SHOW_SEARCH_ENTRY) { + Main.overview.searchEntry.ease({ + opacity: 255, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + this._needsZoomAndFade = false; }, _setLighterBackground(lighter) { - const backgroundColor = lighter + if (this._isOpen) { + const appDisplay = this._source._parentView; + appDisplay.ease({ + opacity: lighter ? 20 : 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + /* const backgroundColor = lighter ? DIALOG_SHADE_HIGHLIGHT : DIALOG_SHADE_NORMAL; @@ -1276,91 +1642,10 @@ const AppFolderDialog = { backgroundColor, duration: FOLDER_DIALOG_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); + }); */ }, }; -// just make app grid to update all invalid positions that may be result of grid/icon size change -function _updateIconPositions() { - const appDisplay = Main.overview._overview._controls._appDisplay; - const icons = [...appDisplay._orderedItems]; - for (let i = 0; i < icons.length; i++) - appDisplay._moveItem(icons[i], -1, -1); -} - -function _removeIcons() { - const appDisplay = Main.overview._overview._controls._appDisplay; - const icons = [...appDisplay._orderedItems]; - for (let i = 0; i < icons.length; i++) { - const icon = icons[i]; - if (icon._dialog) - Main.layoutManager.overviewGroup.remove_child(icon._dialog); - appDisplay._removeItem(icon); - icon.destroy(); - } - appDisplay._folderIcons = []; -} - -function _resetAppGrid(settings) { - const appDisplay = Main.overview._overview._controls._appDisplay; - // reset the grid only if called directly without args or if all folders where removed by using reset button in Settings window - // otherwise this function is called every time a user moves icon to another position as a settings callback - if (settings) { - const currentValue = JSON.stringify(global.settings.get_value('app-picker-layout').deep_unpack()); - const emptyValue = JSON.stringify([]); - const customLayout = currentValue !== emptyValue; - // appDisplay._customLayout = customLayout; - if (customLayout) - return; - else - opt._appGridNeedsRedisplay = true; - } - - // force update icon size using adaptToSize(). the page size cannot be the same as the current one - appDisplay._grid.layoutManager._pageWidth += 1; - appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight); - _removeIcons(); - appDisplay._redisplay(); - // force appDisplay to move all icons to proper positions and update all properties - GLib.idle_add(0, () => { - _updateIconPositions(); - if (appDisplay._sortOrderedItemsAlphabetically) { - appDisplay._sortOrderedItemsAlphabetically(); - appDisplay._grid.layoutManager._pageWidth += 1; - appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight); - appDisplay._setLinearPositions(appDisplay._orderedItems); - } else { - appDisplay._removeItem(appDisplay._orderedItems[0]); - appDisplay._redisplay(); - } - - appDisplay._redisplay(); - }); -} - -function _getWindowApp(metaWin) { - const tracker = Shell.WindowTracker.get_default(); - return tracker.get_window_app(metaWin); -} - -function _getAppLastUsedWindow(app) { - let recentWin; - global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => { - const winApp = _getWindowApp(metaWin); - if (!recentWin && winApp === app) - recentWin = metaWin; - }); - return recentWin; -} - -function _getAppRecentWorkspace(app) { - const recentWin = _getAppLastUsedWindow(app); - if (recentWin) - return recentWin.get_workspace(); - - return null; -} - const AppIcon = { after__init() { // update the app label behavior @@ -1377,7 +1662,7 @@ const AppIcon = { return source !== this && (source instanceof this.constructor) && (view instanceof AppDisplay.AppDisplay && - !opt.APP_GRID_ORDER); + !opt.APP_GRID_USAGE); }, }; @@ -1427,7 +1712,7 @@ const AppViewItemCommon = { // support active preview icons acceptDrop(source, _actor, x) { - if (opt.APP_GRID_ORDER) + if (opt.APP_GRID_USAGE) return DND.DragMotionResult.NO_DROP; this._setHoveringByDnd(false); @@ -1449,26 +1734,18 @@ const AppViewItemCommon = { }; -const ActiveFolderIcon = GObject.registerClass( -class ActiveFolderIcon extends AppDisplay.AppIcon { - _init(app) { - super._init(app, { - setSizeManually: true, - showLabel: false, - }); - } - +const ActiveFolderIcon = { handleDragOver() { return DND.DragMotionResult.CONTINUE; - } + }, acceptDrop() { return false; - } + }, _onDragEnd() { this._dragging = false; this.undoScaleAndFade(); Main.overview.endItemDrag(this._sourceItem.icon); - } -}); + }, +}; |