2025-02-09 23:13:53 +01:00
/ * *
* V - Shell ( Vertical Workspaces )
* appDisplay . js
*
* @ author GdH < G - dH @ github . com >
* @ copyright 2022 - 2023
* @ license GPL - 3.0
*
* /
'use strict' ;
2025-02-09 23:16:18 +01:00
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 ;
2025-02-09 23:13:53 +01:00
const AppDisplay = imports . ui . appDisplay ;
2025-02-09 23:16:18 +01:00
const DND = imports . ui . dnd ;
2025-02-09 23:13:53 +01:00
const IconGrid = imports . ui . iconGrid ;
2025-02-09 23:16:18 +01:00
const Main = imports . ui . main ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
let Me ;
let opt ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
let _timeouts ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
// DIALOG_SHADE_NORMAL = Clutter.Color.from_pixel(0x00000022);
// DIALOG_SHADE_HIGHLIGHT = Clutter.Color.from_pixel(0x00000000);
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
var AppDisplayModule = class {
constructor ( me ) {
Me = me ;
opt = Me . opt ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
this . _firstActivation = true ;
this . moduleEnabled = false ;
this . _overrides = null ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
this . _appGridLayoutSettings = null ;
this . _appDisplayScrollConId = 0 ;
this . _appSystemStateConId = 0 ;
this . _appGridLayoutConId = 0 ;
this . _origAppViewItemAcceptDrop = null ;
this . _updateFolderIcons = 0 ;
}
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
cleanGlobals ( ) {
Me = null ;
opt = null ;
}
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
update ( reset ) {
this . _removeTimeouts ( ) ;
this . moduleEnabled = opt . get ( 'appDisplayModule' ) ;
const conflict = false ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
reset = reset || ! this . moduleEnabled || conflict ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
// 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' ) ;
}
}
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
_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' ) ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
_disableModule ( ) {
Me . Modules . iconGridModule . update ( true ) ;
if ( this . _overrides )
this . _overrides . removeAll ( ) ;
this . _overrides = null ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
const reset = true ;
this . _setAppDisplayOrientation ( false ) ;
this . _updateAppDisplayProperties ( reset ) ;
this . _updateDND ( reset ) ;
this . _restoreOverviewGroup ( ) ;
this . _removeStatusMessage ( ) ;
console . debug ( ' AppDisplayModule - Disabled' ) ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
_removeTimeouts ( ) {
if ( _timeouts ) {
Object . values ( _timeouts ) . forEach ( t => {
if ( t )
GLib . source _remove ( t ) ;
} ) ;
_timeouts = null ;
}
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
_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 ;
2025-02-09 23:13:53 +01:00
} else {
2025-02-09 23:16:18 +01:00
appDisplay . _scrollView . set _policy ( St . PolicyType . EXTERNAL , St . PolicyType . NEVER ) ;
if ( this . _appDisplayScrollConId ) {
appDisplay . _adjustment . disconnect ( this . _appDisplayScrollConId ) ;
this . _appDisplayScrollConId = 0 ;
}
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
// 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' ) ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
// 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 ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
// 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 ( ) ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
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 ;
} ) ;
}
}
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
_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 ;
}
}
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
_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 ;
2025-02-09 23:13:53 +01:00
Main . overview . _overview . _controls . _appDisplay . opacity = 255 ;
}
2025-02-09 23:16:18 +01:00
// 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 ( ) ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
appDisplay . _folderIcons = [ ] ;
}
_removeStatusMessage ( ) {
if ( Me . _vShellStatusMessage ) {
if ( Me . _vShellMessageTimeoutId ) {
GLib . source _remove ( Me . _vShellMessageTimeoutId ) ;
Me . _vShellMessageTimeoutId = 0 ;
}
Me . _vShellStatusMessage . destroy ( ) ;
Me . _vShellStatusMessage = null ;
}
}
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
_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 ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
// 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 ) ;
}
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
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 ;
} ) ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
_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 ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
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 ) ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
_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 ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
_getAppRecentWorkspace ( app ) {
const recentWin = this . _getAppLastUsedWindow ( app ) ;
if ( recentWin )
return recentWin . get _workspace ( ) ;
return null ;
}
} ;
2025-02-09 23:13:53 +01:00
const AppDisplayVertical = {
// correction of the appGrid size when page indicators were moved from the bottom to the right
adaptToSize ( width , height ) {
const [ , indicatorWidth ] = this . _pageIndicators . get _preferred _width ( - 1 ) ;
width -= indicatorWidth ;
this . _grid . findBestModeForSize ( width , height ) ;
const adaptToSize = AppDisplay . BaseAppView . prototype . adaptToSize . bind ( this ) ;
adaptToSize ( width , height ) ;
} ,
} ;
const AppDisplayCommon = {
_ensureDefaultFolders ( ) {
// disable creation of default folders if user deleted them
} ,
_redisplay ( ) {
this . _folderIcons . forEach ( icon => {
icon . view . _redisplay ( ) ;
} ) ;
BaseAppViewCommon . _redisplay . bind ( this ) ( ) ;
} ,
// apps load adapted for custom sorting and including dash items
_loadApps ( ) {
let appIcons = [ ] ;
const runningApps = Shell . AppSystem . get _default ( ) . get _running ( ) . map ( a => a . id ) ;
this . _appInfoList = Shell . AppSystem . get _default ( ) . get _installed ( ) . filter ( appInfo => {
try {
appInfo . get _id ( ) ; // catch invalid file encodings
} catch ( e ) {
return false ;
}
const appIsRunning = runningApps . includes ( appInfo . get _id ( ) ) ;
const appIsFavorite = this . _appFavorites . isFavorite ( appInfo . get _id ( ) ) ;
const excludeApp = ( opt . APP _GRID _EXCLUDE _RUNNING && appIsRunning ) || ( opt . APP _GRID _EXCLUDE _FAVORITES && appIsFavorite ) ;
return this . _parentalControlsManager . shouldShowApp ( appInfo ) && ! excludeApp ;
} ) ;
let apps = this . _appInfoList . map ( app => app . get _id ( ) ) ;
let appSys = Shell . AppSystem . get _default ( ) ;
const appsInsideFolders = new Set ( ) ;
this . _folderIcons = [ ] ;
2025-02-09 23:16:18 +01:00
if ( ! opt . APP _GRID _USAGE ) {
2025-02-09 23:13:53 +01:00
let folders = this . _folderSettings . get _strv ( 'folder-children' ) ;
folders . forEach ( id => {
let path = ` ${ this . _folderSettings . path } folders/ ${ id } / ` ;
let icon = this . _items . get ( id ) ;
if ( ! icon ) {
icon = new AppDisplay . FolderIcon ( id , path , this ) ;
icon . connect ( 'apps-changed' , ( ) => {
this . _redisplay ( ) ;
this . _savePages ( ) ;
} ) ;
icon . connect ( 'notify::pressed' , ( ) => {
if ( icon . pressed )
this . updateDragFocus ( icon ) ;
} ) ;
2025-02-09 23:16:18 +01:00
} else if ( this . _updateFolderIcons && opt . APP _GRID _EXCLUDE _RUNNING ) {
// if any app changed its running state, update folder icon
2025-02-09 23:13:53 +01:00
icon . icon . update ( ) ;
}
// remove empty folder icons
if ( ! icon . visible ) {
icon . destroy ( ) ;
return ;
}
appIcons . push ( icon ) ;
this . _folderIcons . push ( icon ) ;
icon . getAppIds ( ) . forEach ( appId => appsInsideFolders . add ( appId ) ) ;
} ) ;
}
2025-02-09 23:16:18 +01:00
2025-02-09 23:13:53 +01:00
// reset request to update active icon
2025-02-09 23:16:18 +01:00
this . _updateFolderIcons = false ;
2025-02-09 23:13:53 +01:00
// 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
// the app picker, so there's no other need for a drag to start,
// at least on single-monitor setups.
// This also disables drag-to-launch on multi-monitor setups,
// but we hope that is not used much.
const isDraggable =
global . settings . is _writable ( 'favorite-apps' ) ||
global . settings . is _writable ( 'app-picker-layout' ) ;
apps . forEach ( appId => {
2025-02-09 23:16:18 +01:00
if ( ! opt . APP _GRID _USAGE && appsInsideFolders . has ( appId ) )
2025-02-09 23:13:53 +01:00
return ;
let icon = this . _items . get ( appId ) ;
if ( ! icon ) {
let app = appSys . lookup _app ( appId ) ;
icon = new AppDisplay . AppIcon ( app , { isDraggable } ) ;
icon . connect ( 'notify::pressed' , ( ) => {
if ( icon . pressed )
this . updateDragFocus ( icon ) ;
} ) ;
}
appIcons . push ( icon ) ;
} ) ;
// At last, if there's a placeholder available, add it
if ( this . _placeholder )
appIcons . push ( this . _placeholder ) ;
return appIcons ;
} ,
// support active preview icons
_onDragBegin ( overview , source ) {
if ( source . _sourceItem )
source = source . _sourceItem ;
this . _dragMonitor = {
dragMotion : this . _onDragMotion . bind ( this ) ,
} ;
DND . addDragMonitor ( this . _dragMonitor ) ;
2025-02-09 23:16:18 +01:00
if ( Me . shellVersion < 43 )
2025-02-09 23:13:53 +01:00
this . _slideSidePages ( AppDisplay . SidePages . PREVIOUS | AppDisplay . SidePages . NEXT | AppDisplay . SidePages . DND ) ;
else
this . _appGridLayout . showPageIndicators ( ) ;
this . _dragFocus = null ;
this . _swipeTracker . enabled = false ;
// When dragging from a folder dialog, the dragged app icon doesn't
// exist in AppDisplay. We work around that by adding a placeholder
// icon that is either destroyed on cancel, or becomes the effective
// new icon when dropped.
if ( AppDisplay . _getViewFromIcon ( source ) instanceof AppDisplay . FolderView ||
( opt . APP _GRID _EXCLUDE _FAVORITES && this . _appFavorites . isFavorite ( source . id ) ) )
this . _ensurePlaceholder ( source ) ;
} ,
_ensurePlaceholder ( source ) {
if ( this . _placeholder )
return ;
if ( source . _sourceItem )
source = source . _sourceItem ;
const appSys = Shell . AppSystem . get _default ( ) ;
const app = appSys . lookup _app ( source . id ) ;
const isDraggable =
global . settings . is _writable ( 'favorite-apps' ) ||
global . settings . is _writable ( 'app-picker-layout' ) ;
this . _placeholder = new AppDisplay . AppIcon ( app , { isDraggable } ) ;
this . _placeholder . connect ( 'notify::pressed' , ( ) => {
if ( this . _placeholder ? . pressed )
this . updateDragFocus ( this . _placeholder ) ;
} ) ;
this . _placeholder . scaleAndFade ( ) ;
this . _redisplay ( ) ;
} ,
2025-02-09 23:16:18 +01:00
// accept source from active folder preview
2025-02-09 23:13:53 +01:00
acceptDrop ( source ) {
2025-02-09 23:16:18 +01:00
if ( opt . APP _GRID _USAGE )
2025-02-09 23:13:53 +01:00
return false ;
if ( source . _sourceItem )
source = source . _sourceItem ;
2025-02-09 23:16:18 +01:00
if ( ! BaseAppViewCommon . acceptDrop . bind ( this ) ( source ) )
2025-02-09 23:13:53 +01:00
return false ;
this . _savePages ( ) ;
let view = AppDisplay . _getViewFromIcon ( source ) ;
if ( view instanceof AppDisplay . FolderView )
view . removeApp ( source . app ) ;
if ( this . _currentDialog )
this . _currentDialog . popdown ( ) ;
if ( opt . APP _GRID _EXCLUDE _FAVORITES && this . _appFavorites . isFavorite ( source . id ) )
this . _appFavorites . removeFavorite ( source . id ) ;
return true ;
} ,
} ;
const BaseAppViewVertical = {
after _ _init ( ) {
this . _grid . layoutManager . _orientation = Clutter . Orientation . VERTICAL ;
this . _scrollView . set _policy ( St . PolicyType . NEVER , St . PolicyType . EXTERNAL ) ;
this . _orientation = Clutter . Orientation . VERTICAL ;
this . _swipeTracker . orientation = Clutter . Orientation . VERTICAL ;
this . _swipeTracker . _reset ( ) ;
this . _pageIndicators . vertical = true ;
this . _box . vertical = false ;
this . _pageIndicators . x _expand = false ;
this . _pageIndicators . y _align = Clutter . ActorAlign . CENTER ;
this . _pageIndicators . x _align = Clutter . ActorAlign . START ;
this . _pageIndicators . set _style ( 'margin-right: 10px;' ) ;
const scrollContainer = this . _scrollView . get _parent ( ) ;
2025-02-09 23:16:18 +01:00
if ( Me . shellVersion < 43 ) {
2025-02-09 23:13:53 +01:00
// remove touch friendly side navigation bars / arrows
if ( this . _hintContainer && this . _hintContainer . get _parent ( ) )
scrollContainer . remove _child ( this . _hintContainer ) ;
} else {
// moving these bars needs more patching of the this's code
// for now we just change bars style to be more like vertically oriented arrows indicating direction to prev/next page
this . _nextPageIndicator . add _style _class _name ( 'nextPageIndicator' ) ;
this . _prevPageIndicator . add _style _class _name ( 'prevPageIndicator' ) ;
}
// setting their x_scale to 0 removes the arrows and avoid allocation issues compared to .hide() them
this . _nextPageArrow . scale _x = 0 ;
this . _prevPageArrow . scale _x = 0 ;
this . _adjustment = this . _scrollView . vscroll . adjustment ;
this . _adjustment . connect ( 'notify::value' , adj => {
const value = adj . value / adj . page _size ;
this . _pageIndicators . setCurrentPosition ( value ) ;
} ) ;
} ,
// <= 42 only, this fixes dnd from appDisplay to the workspace thumbnail on the left if appDisplay is on page 1 because of appgrid left overshoot
_pageForCoords ( ) {
return AppDisplay . SidePages . NONE ;
} ,
} ;
const BaseAppViewCommon = {
_sortOrderedItemsAlphabetically ( icons = null ) {
if ( ! icons )
icons = this . _orderedItems ;
icons . sort ( ( a , b ) => a . name . toLowerCase ( ) . localeCompare ( b . name . toLowerCase ( ) ) ) ;
} ,
_setLinearPositions ( icons ) {
const { itemsPerPage } = this . _grid ;
icons . forEach ( ( icon , i ) => {
const page = Math . floor ( i / itemsPerPage ) ;
const position = i % itemsPerPage ;
try {
this . _moveItem ( icon , page , position ) ;
} catch ( e ) {
2025-02-09 23:16:18 +01:00
console . warn ( ` Warning: ${ e } ` ) ;
2025-02-09 23:13:53 +01:00
}
} ) ;
} ,
// adds sorting options and option to add favorites and running apps
_redisplay ( ) {
let oldApps = this . _orderedItems . slice ( ) ;
let oldAppIds = oldApps . map ( icon => icon . id ) ;
let newApps = this . _loadApps ( ) . sort ( this . _compareItems . bind ( this ) ) ;
let newAppIds = newApps . map ( icon => icon . id ) ;
let addedApps = newApps . filter ( icon => ! oldAppIds . includes ( icon . id ) ) ;
let removedApps = oldApps . filter ( icon => ! newAppIds . includes ( icon . id ) ) ;
// Remove old app icons
removedApps . forEach ( icon => {
this . _removeItem ( icon ) ;
icon . destroy ( ) ;
} ) ;
// Add new app icons, or move existing ones
newApps . forEach ( icon => {
const [ page , position ] = this . _getItemPosition ( icon ) ;
if ( addedApps . includes ( icon ) ) {
this . _addItem ( icon , page , position ) ;
} else if ( page !== - 1 && position !== - 1 ) {
this . _moveItem ( icon , page , position ) ;
} else {
// App is part of a folder
}
} ) ;
2025-02-09 23:16:18 +01:00
// 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 ) ) {
2025-02-09 23:13:53 +01:00
// const { itemsPerPage } = this._grid;
let appIcons = this . _orderedItems ;
2025-02-09 23:16:18 +01:00
// sort all alphabetically
2025-02-09 23:13:53 +01:00
this . _sortOrderedItemsAlphabetically ( appIcons ) ;
// appIcons.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
// then sort used apps by usage
2025-02-09 23:16:18 +01:00
if ( ( opt . APP _GRID _USAGE && thisIsAppDisplay ) ||
( opt . APP _FOLDER _USAGE && thisIsFolder ) )
2025-02-09 23:13:53 +01:00
appIcons . sort ( ( a , b ) => Shell . AppUsage . get _default ( ) . compare ( a . app . id , b . app . id ) ) ;
// sort favorites first
if ( opt . APP _GRID _DASH _FIRST ) {
const fav = Object . keys ( this . _appFavorites . _favorites ) ;
appIcons . sort ( ( a , b ) => {
let aFav = fav . indexOf ( a . id ) ;
if ( aFav < 0 )
aFav = 999 ;
let bFav = fav . indexOf ( b . id ) ;
if ( bFav < 0 )
bFav = 999 ;
return bFav < aFav ;
} ) ;
}
// sort running first
2025-02-09 23:16:18 +01:00
if ( opt . APP _GRID _DASH _FIRST && thisIsAppDisplay )
2025-02-09 23:13:53 +01:00
appIcons . sort ( ( a , b ) => a . app . get _state ( ) !== Shell . AppState . RUNNING && b . app . get _state ( ) === Shell . AppState . RUNNING ) ;
2025-02-09 23:16:18 +01:00
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 ) ;
2025-02-09 23:13:53 +01:00
this . _setLinearPositions ( appIcons ) ;
this . _orderedItems = appIcons ;
}
this . emit ( 'view-loaded' ) ;
if ( ! opt . APP _GRID _ALLOW _INCOMPLETE _PAGES ) {
for ( let i = 0 ; i < this . _grid . nPages ; i ++ )
this . _grid . layoutManager . _fillItemVacancies ( i ) ;
}
} ,
_canAccept ( source ) {
2025-02-09 23:16:18 +01:00
return source instanceof AppDisplay . AppViewItem ;
2025-02-09 23:13:53 +01:00
} ,
// support active preview icons
acceptDrop ( source ) {
if ( ! this . _canAccept ( source ) )
return false ;
2025-02-09 23:16:18 +01:00
let dropTarget = null ;
if ( Me . shellVersion >= 43 ) {
dropTarget = this . _dropTarget ;
delete this . _dropTarget ;
}
if ( ! this . _canAccept ( source ) )
return false ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
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 ;
2025-02-09 23:13:53 +01:00
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 ;
2025-02-09 23:16:18 +01:00
try {
this . _moveItem ( source , page , position ) ;
} catch ( e ) {
console . warn ( ` Warning: ${ e } ` ) ;
}
2025-02-09 23:13:53 +01:00
this . _removeDelayedMove ( ) ;
}
return true ;
} ,
// support active preview icons
_onDragMotion ( dragEvent ) {
if ( ! ( dragEvent . source instanceof AppDisplay . AppViewItem ) )
return DND . DragMotionResult . CONTINUE ;
if ( dragEvent . source . _sourceItem )
dragEvent . source = dragEvent . source . _sourceItem ;
const appIcon = dragEvent . source ;
2025-02-09 23:16:18 +01:00
if ( Me . shellVersion < 43 ) {
2025-02-09 23:13:53 +01:00
this . _dropPage = this . _pageForCoords ( dragEvent . x , dragEvent . y ) ;
if ( this . _dropPage &&
this . _dropPage === AppDisplay . SidePages . PREVIOUS &&
this . _grid . currentPage === 0 ) {
delete this . _dropPage ;
return DND . DragMotionResult . NO _DROP ;
}
}
if ( appIcon instanceof AppDisplay . AppViewItem ) {
2025-02-09 23:16:18 +01:00
if ( Me . shellVersion < 44 ) {
2025-02-09 23:13:53 +01:00
// Handle the drag overshoot. When dragging to above the
// icon grid, move to the page above; when dragging below,
// move to the page below.
this . _handleDragOvershoot ( dragEvent ) ;
} else if ( ! this . _dragMaybeSwitchPageImmediately ( dragEvent ) ) {
// Two ways of switching pages during DND:
// 1) When "bumping" the cursor against the monitor edge, we switch
// page immediately.
// 2) When hovering over the next-page indicator for a certain time,
// we also switch page.
const { targetActor } = dragEvent ;
if ( targetActor === this . _prevPageIndicator ||
targetActor === this . _nextPageIndicator )
this . _maybeSetupDragPageSwitchInitialTimeout ( dragEvent ) ;
else
this . _resetDragPageSwitch ( ) ;
}
}
2025-02-09 23:16:18 +01:00
const thisIsFolder = this instanceof AppDisplay . FolderView ;
const thisIsAppDisplay = ! thisIsFolder ;
if ( ( ! opt . APP _GRID _ORDER && thisIsAppDisplay ) || ( ! opt . APP _FOLDER _ORDER && thisIsFolder ) )
this . _maybeMoveItem ( dragEvent ) ;
2025-02-09 23:13:53 +01:00
return DND . DragMotionResult . CONTINUE ;
} ,
// adjustable page width for GS <= 42
adaptToSize ( width , height , isFolder = false ) {
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 ( ) ;
let pageWidth , pageHeight ;
pageHeight = availHeight ;
pageWidth = Math . ceil ( availWidth * ( isFolder ? 1 : opt . APP _GRID _PAGE _WIDTH _SCALE ) ) ;
// subtract space for navigation arrows in horizontal mode
pageWidth -= opt . ORIENTATION ? 0 : 128 ;
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 ) ;
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 ;
this . _pageIndicatorOffset = leftPadding ;
this . _pageArrowOffset = Math . max (
leftPadding - 80 , 0 ) ; // 80 is AppDisplay.PAGE_PREVIEW_MAX_ARROW_OFFSET
} ,
} ;
const BaseAppViewGridLayout = {
_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 * 0.1 /* PAGE_PREVIEW_RATIO*/ ) / 2 ;
return Math . max ( idealIndicatorWidth , minArrowsWidth ) ;
} ,
} ;
const FolderIcon = {
after _ _init ( ) {
/* / / If folder preview icons are clickable ,
// disable opening the folder with primary mouse button and enable the secondary one
const buttonMask = opt . APP _GRID _ACTIVE _PREVIEW
? St . ButtonMask . TWO | St . ButtonMask . THREE
: St . ButtonMask . ONE | St . ButtonMask . TWO ;
this . button _mask = buttonMask ; * /
this . button _mask = St . ButtonMask . ONE | St . ButtonMask . TWO ;
} ,
open ( ) {
this . _ensureFolderDialog ( ) ;
2025-02-09 23:16:18 +01:00
// if (this._dialog._designCapacity !== this.view._orderedItems.length)
this . _dialog . _updateFolderSize ( ) ;
2025-02-09 23:13:53 +01:00
this . view . _scrollView . vscroll . adjustment . value = 0 ;
this . _dialog . popup ( ) ;
} ,
} ;
const FolderView = {
_createGrid ( ) {
2025-02-09 23:16:18 +01:00
let grid = new AppDisplay . FolderGrid ( ) ;
2025-02-09 23:13:53 +01:00
return grid ;
} ,
createFolderIcon ( size ) {
const layout = new Clutter . GridLayout ( {
row _homogeneous : true ,
column _homogeneous : true ,
} ) ;
let icon = new St . Widget ( {
layout _manager : layout ,
x _align : Clutter . ActorAlign . CENTER ,
style : ` width: ${ size } px; height: ${ size } px; ` ,
} ) ;
const numItems = this . _orderedItems . length ;
// decide what number of icons switch to 3x3 grid
// APP_GRID_FOLDER_ICON_GRID: 3 -> more than 4
// : 4 -> more than 8
const threshold = opt . APP _GRID _FOLDER _ICON _GRID % 3 ? 8 : 4 ;
const gridSize = opt . APP _GRID _FOLDER _ICON _GRID > 2 && numItems > threshold ? 3 : 2 ;
const FOLDER _SUBICON _FRACTION = gridSize === 2 ? 0.4 : 0.27 ;
let subSize = Math . floor ( FOLDER _SUBICON _FRACTION * size ) ;
let rtl = icon . get _text _direction ( ) === Clutter . TextDirection . RTL ;
for ( let i = 0 ; i < gridSize * gridSize ; i ++ ) {
const style = ` width: ${ subSize } px; height: ${ subSize } px; ` ;
let bin = new St . Bin ( { style , reactive : true } ) ;
bin . pivot _point = new Graphene . Point ( { x : 0.5 , y : 0.5 } ) ;
if ( i < numItems ) {
if ( ! opt . APP _GRID _ACTIVE _PREVIEW ) {
bin . child = this . _orderedItems [ i ] . app . create _icon _texture ( subSize ) ;
} else {
const app = this . _orderedItems [ i ] . app ;
2025-02-09 23:16:18 +01:00
const child = new AppDisplay . AppIcon ( app , {
setSizeManually : true ,
showLabel : false ,
} ) ;
2025-02-09 23:13:53 +01:00
child . _sourceItem = this . _orderedItems [ i ] ;
child . _sourceFolder = this ;
child . icon . style _class = '' ;
child . icon . set _style ( 'margin: 0; padding: 0;' ) ;
2025-02-09 23:16:18 +01:00
child . _dot . set _style ( 'margin-bottom: 1px;' ) ;
2025-02-09 23:13:53 +01:00
child . icon . setIconSize ( subSize ) ;
bin . child = child ;
bin . connect ( 'enter-event' , ( ) => {
bin . ease ( {
duration : 100 ,
scale _x : 1.14 ,
scale _y : 1.14 ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
} ) ;
} ) ;
bin . connect ( 'leave-event' , ( ) => {
bin . ease ( {
duration : 100 ,
scale _x : 1 ,
scale _y : 1 ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
} ) ;
} ) ;
}
}
layout . attach ( bin , rtl ? ( i + 1 ) % gridSize : i % gridSize , Math . floor ( i / gridSize ) , 1 , 1 ) ;
}
2025-02-09 23:16:18 +01:00
// if folder content changed, update folder size, but not if it's empty
/ * i f ( t h i s . _ d i a l o g & & t h i s . _ d i a l o g . _ d e s i g n C a p a c i t y ! = = t h i s . _ o r d e r e d I t e m s . l e n g t h & & t h i s . _ o r d e r e d I t e m s . l e n g t h )
this . _dialog . _updateFolderSize ( ) ; * /
2025-02-09 23:13:53 +01:00
return icon ;
} ,
// this just overrides _redisplay() for GS < 44
_redisplay ( ) {
// super._redisplay(); - super doesn't work in my overrides
AppDisplay . BaseAppView . prototype . _redisplay . bind ( this ) ( ) ;
} ,
_loadApps ( ) {
this . _apps = [ ] ;
const excludedApps = this . _folder . get _strv ( 'excluded-apps' ) ;
const appSys = Shell . AppSystem . get _default ( ) ;
const addAppId = appId => {
if ( excludedApps . includes ( appId ) )
return ;
if ( opt . APP _GRID _EXCLUDE _FAVORITES && this . _appFavorites . isFavorite ( appId ) )
return ;
const app = appSys . lookup _app ( appId ) ;
if ( ! app )
return ;
if ( opt . APP _GRID _EXCLUDE _RUNNING ) {
const runningApps = Shell . AppSystem . get _default ( ) . get _running ( ) . map ( a => a . id ) ;
if ( runningApps . includes ( appId ) )
return ;
}
if ( ! this . _parentalControlsManager . shouldShowApp ( app . get _app _info ( ) ) )
return ;
if ( this . _apps . indexOf ( app ) !== - 1 )
return ;
this . _apps . push ( app ) ;
} ;
const folderApps = this . _folder . get _strv ( 'apps' ) ;
folderApps . forEach ( addAppId ) ;
const folderCategories = this . _folder . get _strv ( 'categories' ) ;
const appInfos = this . _parentView . getAppInfos ( ) ;
appInfos . forEach ( appInfo => {
let appCategories = AppDisplay . _getCategories ( appInfo ) ;
if ( ! AppDisplay . _listsIntersect ( folderCategories , appCategories ) )
return ;
addAppId ( appInfo . get _id ( ) ) ;
} ) ;
let items = [ ] ;
this . _apps . forEach ( app => {
let icon = this . _items . get ( app . get _id ( ) ) ;
if ( ! icon )
icon = new AppDisplay . AppIcon ( app ) ;
items . push ( icon ) ;
} ) ;
2025-02-09 23:16:18 +01:00
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 ) ) ;
2025-02-09 23:13:53 +01:00
this . _appIds = this . _apps . map ( app => app . get _id ( ) ) ;
return items ;
} ,
// 42 only - don't apply appGrid scale on folders
adaptToSize ( width , height ) {
if ( ! opt . ORIENTATION ) {
const [ , indicatorHeight ] = this . _pageIndicators . get _preferred _height ( - 1 ) ;
height -= indicatorHeight ;
}
BaseAppViewCommon . adaptToSize . bind ( this ) ( width , height , true ) ;
} ,
} ;
// folder columns and rows
2025-02-09 23:16:18 +01:00
const FolderGridLegacy = {
2025-02-09 23:13:53 +01:00
_init ( ) {
2025-02-09 23:16:18 +01:00
IconGrid . IconGrid . prototype . _init . bind ( this ) ( {
2025-02-09 23:13:53 +01:00
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
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 ,
} ) ;
2025-02-09 23:16:18 +01:00
this . layout _manager . _isFolder = true ;
2025-02-09 23:13:53 +01:00
// if (!opt.APP_GRID_FOLDER_DEFAULT)
const spacing = opt . APP _GRID _SPACING ;
this . set _style ( ` column-spacing: ${ spacing } px; row-spacing: ${ spacing } px; ` ) ;
2025-02-09 23:16:18 +01:00
this . layoutManager . fixedIconSize = opt . APP _GRID _FOLDER _ICON _SIZE ;
} ,
2025-02-09 23:13:53 +01:00
adaptToSize ( width , height ) {
this . layout _manager . adaptToSize ( width , height ) ;
2025-02-09 23:16:18 +01:00
} ,
} ;
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 ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
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 ,
} ,
] ) ;
} ,
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
adaptToSize ( width , height ) {
this . layout _manager . adaptToSize ( width , height ) ;
} ,
} ;
2025-02-09 23:13:53 +01:00
const FOLDER _DIALOG _ANIMATION _TIME = 200 ; // AppDisplay.FOLDER_DIALOG_ANIMATION_TIME
const AppFolderDialog = {
// injection to _init()
after _ _init ( ) {
2025-02-09 23:16:18 +01:00
this . _viewBox . add _style _class _name ( 'app-folder-dialog-vshell' ) ;
2025-02-09 23:13:53 +01:00
// delegate this dialog to the FolderIcon._view
// so its _createFolderIcon function can update the dialog if folder content changed
this . _view . _dialog = this ;
// right click into the folder popup should close it
this . child . reactive = true ;
const clickAction = new Clutter . ClickAction ( ) ;
clickAction . connect ( 'clicked' , act => {
if ( act . get _button ( ) === Clutter . BUTTON _PRIMARY )
return Clutter . EVENT _STOP ;
const [ x , y ] = clickAction . get _coords ( ) ;
const actor = global . stage . get _actor _at _pos ( Clutter . PickMode . ALL , x , y ) ;
// if it's not entry for editing folder title
if ( actor !== this . _entry )
this . popdown ( ) ;
return Clutter . EVENT _STOP ;
} ) ;
this . child . add _action ( clickAction ) ;
2025-02-09 23:16:18 +01:00
// 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...)
/ * c o n s t a p p F o l d e r s = t h i s . _ a p p D i s p l a y . _ f o l d e r I c o n s . m a p ( i c o n = > i c o n . _ i d ) ;
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 ) ;
2025-02-09 23:13:53 +01:00
} ,
popup ( ) {
if ( this . _isOpen )
return ;
this . _isOpen = this . _grabHelper . grab ( {
actor : this ,
onUngrab : ( ) => this . popdown ( ) ,
} ) ;
if ( ! this . _isOpen )
return ;
this . get _parent ( ) . set _child _above _sibling ( this , null ) ;
this . _needsZoomAndFade = true ;
2025-02-09 23:16:18 +01:00
// 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 ( ) ;
2025-02-09 23:13:53 +01:00
this . emit ( 'open-state-changed' , true ) ;
} ,
_updateFolderSize ( ) {
const view = this . _view ;
2025-02-09 23:16:18 +01:00
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 ;
}
2025-02-09 23:13:53 +01:00
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 ) ;
2025-02-09 23:16:18 +01:00
const itemPadding = 55 ; // default icon item padding on Fedora 44
// const dialogMargin = 30;
2025-02-09 23:13:53 +01:00
const nItems = view . _orderedItems . length ;
let columns = opt . APP _GRID _FOLDER _COLUMNS ;
let rows = opt . APP _GRID _FOLDER _ROWS ;
2025-02-09 23:16:18 +01:00
const fullAdaptiveGrid = ! columns && ! rows ;
2025-02-09 23:13:53 +01:00
let spacing = opt . APP _GRID _SPACING ;
2025-02-09 23:16:18 +01:00
const minItemSize = 48 + itemPadding ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
if ( fullAdaptiveGrid ) {
2025-02-09 23:13:53 +01:00
columns = Math . ceil ( Math . sqrt ( nItems ) ) ;
rows = columns ;
if ( columns * ( columns - 1 ) >= nItems ) {
rows = columns - 1 ;
} else if ( ( columns + 1 ) * ( columns - 1 ) >= nItems ) {
rows = columns - 1 ;
columns += 1 ;
}
} else if ( ! columns && rows ) {
columns = Math . ceil ( nItems / rows ) ;
} else if ( columns && ! rows ) {
rows = Math . ceil ( nItems / columns ) ;
}
const iconSize = opt . APP _GRID _FOLDER _ICON _SIZE < 0 ? opt . APP _GRID _FOLDER _ICON _SIZE _DEFAULT : opt . APP _GRID _FOLDER _ICON _SIZE ;
2025-02-09 23:16:18 +01:00
view . _grid . layoutManager . fixedIconSize = iconSize ;
let itemSize = iconSize + 55 ; // icon padding
2025-02-09 23:13:53 +01:00
// 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
2025-02-09 23:16:18 +01:00
if ( this . realized ) {
2025-02-09 23:13:53 +01:00
firstItem . icon . setIconSize ( iconSize ) ;
const [ firstItemWidth ] = firstItem . get _preferred _size ( ) ;
const realSize = firstItemWidth / scaleFactor ;
2025-02-09 23:16:18 +01:00
// 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 ) )
2025-02-09 23:13:53 +01:00
itemSize = realSize ;
}
let width = columns * ( itemSize + spacing ) + /* padding for nav arrows*/ 64 ;
2025-02-09 23:16:18 +01:00
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 ) ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
// folder must fit the appDisplay area
2025-02-09 23:13:53 +01:00
// reduce columns/rows if needed and count with the scaled values
2025-02-09 23:16:18 +01:00
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 ;
}
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
// 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 ) ) ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
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 ) ;
2025-02-09 23:13:53 +01:00
const layoutManager = view . _grid . layoutManager ;
layoutManager . rows _per _page = rows ;
layoutManager . columns _per _page = columns ;
// this line is required by GS 43
view . _grid . setGridModes ( [ { columns , rows } ] ) ;
this . child . set _style ( `
width : $ { width } px ;
height : $ { height } px ;
padding : 30 px ;
` );
2025-02-09 23:16:18 +01:00
view . _grid . layoutManager . _pageWidth += 1 ;
view . _grid . layoutManager . adaptToSize ( view . _grid . layoutManager . _pageWidth - 1 , view . _grid . layoutManager . _pageHeight ) ;
2025-02-09 23:13:53 +01:00
view . _redisplay ( ) ;
// store original item count
this . _designCapacity = nItems ;
} ,
_zoomAndFadeIn ( ) {
let [ sourceX , sourceY ] =
this . _source . get _transformed _position ( ) ;
let [ dialogX , dialogY ] =
this . child . get _transformed _position ( ) ;
const sourceCenterX = sourceX + this . _source . width / 2 ;
const sourceCenterY = sourceY + this . _source . height / 2 ;
// this. covers the whole screen
let dialogTargetX = dialogX ;
let dialogTargetY = dialogY ;
2025-02-09 23:16:18 +01:00
const appDisplay = this . _source . _parentView ;
const [ appDisplayX , appDisplayY ] = this . _source . _parentView . get _transformed _position ( ) ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
if ( ! opt . APP _GRID _FOLDER _CENTER ) {
dialogTargetX = sourceCenterX - this . child . width / 2 ;
dialogTargetY = sourceCenterY - this . child . height / 2 ;
2025-02-09 23:13:53 +01:00
2025-02-09 23:16:18 +01:00
// keep the dialog in appDisplay area if possible
2025-02-09 23:13:53 +01:00
dialogTargetX = Math . clamp (
dialogTargetX ,
2025-02-09 23:16:18 +01:00
appDisplayX ,
appDisplayX + appDisplay . width - this . child . width
2025-02-09 23:13:53 +01:00
) ;
dialogTargetY = Math . clamp (
dialogTargetY ,
2025-02-09 23:16:18 +01:00
appDisplayY ,
appDisplayY + appDisplay . height - this . child . height
2025-02-09 23:13:53 +01:00
) ;
2025-02-09 23:16:18 +01:00
} 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 ;
2025-02-09 23:13:53 +01:00
}
2025-02-09 23:16:18 +01:00
const dialogOffsetX = Math . round ( dialogTargetX - dialogX ) ;
const dialogOffsetY = Math . round ( dialogTargetY - dialogY ) ;
2025-02-09 23:13:53 +01:00
this . child . set ( {
translation _x : sourceX - dialogX ,
translation _y : sourceY - dialogY ,
scale _x : this . _source . width / this . child . width ,
scale _y : this . _source . height / this . child . height ,
opacity : 0 ,
} ) ;
this . child . ease ( {
translation _x : dialogOffsetX ,
translation _y : dialogOffsetY ,
scale _x : 1 ,
scale _y : 1 ,
opacity : 255 ,
duration : FOLDER _DIALOG _ANIMATION _TIME ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
} ) ;
2025-02-09 23:16:18 +01:00
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 ,
} ) ;
}
2025-02-09 23:13:53 +01:00
this . _needsZoomAndFade = false ;
if ( this . _sourceMappedId === 0 ) {
this . _sourceMappedId = this . _source . connect (
'notify::mapped' , this . _zoomAndFadeOut . bind ( this ) ) ;
}
} ,
_zoomAndFadeOut ( ) {
if ( ! this . _isOpen )
return ;
if ( ! this . _source . mapped ) {
this . hide ( ) ;
return ;
}
2025-02-09 23:16:18 +01:00
// 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 ;
}
2025-02-09 23:13:53 +01:00
let [ sourceX , sourceY ] =
this . _source . get _transformed _position ( ) ;
let [ dialogX , dialogY ] =
this . child . get _transformed _position ( ) ;
this . child . ease ( {
translation _x : sourceX - dialogX + this . child . translation _x ,
translation _y : sourceY - dialogY + this . child . translation _y ,
scale _x : this . _source . width / this . child . width ,
scale _y : this . _source . height / this . child . height ,
opacity : 0 ,
duration : FOLDER _DIALOG _ANIMATION _TIME ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
onComplete : ( ) => {
this . child . set ( {
translation _x : 0 ,
translation _y : 0 ,
scale _x : 1 ,
scale _y : 1 ,
opacity : 255 ,
} ) ;
this . hide ( ) ;
this . _popdownCallbacks . forEach ( func => func ( ) ) ;
this . _popdownCallbacks = [ ] ;
} ,
} ) ;
2025-02-09 23:16:18 +01:00
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 ,
} ) ;
}
2025-02-09 23:13:53 +01:00
this . _needsZoomAndFade = false ;
} ,
_setLighterBackground ( lighter ) {
2025-02-09 23:16:18 +01:00
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 ,
} ) ;
}
/ * c o n s t b a c k g r o u n d C o l o r = l i g h t e r
2025-02-09 23:13:53 +01:00
? DIALOG _SHADE _HIGHLIGHT
: DIALOG _SHADE _NORMAL ;
this . ease ( {
backgroundColor ,
duration : FOLDER _DIALOG _ANIMATION _TIME ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
2025-02-09 23:16:18 +01:00
} ) ; * /
2025-02-09 23:13:53 +01:00
} ,
} ;
const AppIcon = {
after _ _init ( ) {
// update the app label behavior
this . _updateMultiline ( ) ;
} ,
// avoid accepting by placeholder when dragging active preview
// and also by icon if alphabet or usage sorting are used
_canAccept ( source ) {
if ( source . _sourceItem )
source = source . _sourceItem ;
let view = AppDisplay . _getViewFromIcon ( source ) ;
return source !== this &&
( source instanceof this . constructor ) &&
( view instanceof AppDisplay . AppDisplay &&
2025-02-09 23:16:18 +01:00
! opt . APP _GRID _USAGE ) ;
2025-02-09 23:13:53 +01:00
} ,
} ;
const AppViewItemCommon = {
_updateMultiline ( ) {
const { label } = this . icon ;
if ( label )
label . opacity = 255 ;
if ( ! this . _expandTitleOnHover || ! this . icon . label )
return ;
const { clutterText } = label ;
const isHighlighted = this . has _key _focus ( ) || this . hover || this . _forcedHighlight ;
if ( opt . APP _GRID _NAMES _MODE === 2 && this . _expandTitleOnHover ) { // !_expandTitleOnHover indicates search result icon
label . opacity = isHighlighted || ! this . app ? 255 : 0 ;
}
if ( isHighlighted )
this . get _parent ( ) ? . set _child _above _sibling ( this , null ) ;
if ( ! opt . APP _GRID _NAMES _MODE ) {
const layout = clutterText . get _layout ( ) ;
if ( ! layout . is _wrapped ( ) && ! layout . is _ellipsized ( ) )
return ;
}
label . remove _transition ( 'allocation' ) ;
const id = label . connect ( 'notify::allocation' , ( ) => {
label . restore _easing _state ( ) ;
label . disconnect ( id ) ;
} ) ;
const expand = opt . APP _GRID _NAMES _MODE === 1 || this . _forcedHighlight || this . hover || this . has _key _focus ( ) ;
label . save _easing _state ( ) ;
label . set _easing _duration ( expand
? AppDisplay . APP _ICON _TITLE _EXPAND _TIME
: AppDisplay . APP _ICON _TITLE _COLLAPSE _TIME ) ;
clutterText . set ( {
line _wrap : expand ,
line _wrap _mode : expand ? Pango . WrapMode . WORD _CHAR : Pango . WrapMode . NONE ,
ellipsize : expand ? Pango . EllipsizeMode . NONE : Pango . EllipsizeMode . END ,
} ) ;
} ,
// support active preview icons
acceptDrop ( source , _actor , x ) {
2025-02-09 23:16:18 +01:00
if ( opt . APP _GRID _USAGE )
2025-02-09 23:13:53 +01:00
return DND . DragMotionResult . NO _DROP ;
this . _setHoveringByDnd ( false ) ;
if ( ! this . _canAccept ( source ) )
return false ;
if ( this . _withinLeeways ( x ) )
return false ;
// added - remove app from the source folder after dnd to other folder
if ( source . _sourceItem ) {
const app = source . _sourceItem . app ;
source . _sourceFolder . removeApp ( app ) ;
}
return true ;
} ,
} ;
2025-02-09 23:16:18 +01:00
const ActiveFolderIcon = {
2025-02-09 23:13:53 +01:00
handleDragOver ( ) {
return DND . DragMotionResult . CONTINUE ;
2025-02-09 23:16:18 +01:00
} ,
2025-02-09 23:13:53 +01:00
acceptDrop ( ) {
return false ;
2025-02-09 23:16:18 +01:00
} ,
2025-02-09 23:13:53 +01:00
_onDragEnd ( ) {
this . _dragging = false ;
this . undoScaleAndFade ( ) ;
Main . overview . endItemDrag ( this . _sourceItem . icon ) ;
2025-02-09 23:16:18 +01:00
} ,
} ;