2025-02-09 23:08:39 +01:00
/ * *
2025-02-09 23:10:52 +01:00
* V - Shell ( Vertical Workspaces )
2025-02-09 23:08:39 +01:00
* dash . js
*
* @ author GdH < G - dH @ github . com >
* @ copyright 2022 - 2023
* @ license GPL - 3.0
* modified dash module of https : //github.com/RensAlthuis/vertical-overview extension
* /
2025-02-09 23:10:52 +01:00
const { Clutter , GObject , St , Shell , Meta } = imports . gi ;
2025-02-09 23:08:39 +01:00
const AppDisplay = imports . ui . appDisplay ;
const AppFavorites = imports . ui . appFavorites ;
const DND = imports . ui . dnd ;
const IconGrid = imports . ui . iconGrid ;
const Main = imports . ui . main ;
const Overview = imports . ui . overview ;
const Dash = imports . ui . dash ;
2025-02-09 23:10:52 +01:00
const PopupMenu = imports . ui . popupMenu ;
const { AppMenu } = imports . ui . appMenu ;
const BoxPointer = imports . ui . boxpointer ;
const AltTab = imports . ui . altTab ;
2025-02-09 23:08:39 +01:00
const Me = imports . misc . extensionUtils . getCurrentExtension ( ) ;
2025-02-09 23:10:52 +01:00
const Util = Me . imports . lib . util ;
const _ = Me . imports . lib . settings . _ ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
const shellVersion = Util . shellVersion ;
2025-02-09 23:08:39 +01:00
let _origWorkId ;
let _newWorkId ;
let _showAppsIconBtnPressId ;
2025-02-09 23:10:52 +01:00
// added values to achieve a better ability to scale down according to available space
2025-02-09 23:08:39 +01:00
var BaseIconSizes = [ 16 , 24 , 32 , 40 , 44 , 48 , 56 , 64 , 72 , 80 , 96 , 112 , 128 ] ;
2025-02-09 23:10:52 +01:00
const RecentFilesSearchProviderPrefix = Me . imports . lib . recentFilesSearchProvider . prefix ;
const WindowSearchProviderPrefix = Me . imports . lib . windowSearchProvider . prefix ;
2025-02-09 23:08:39 +01:00
let _overrides ;
2025-02-09 23:10:52 +01:00
let opt ;
let _firstRun = true ;
2025-02-09 23:08:39 +01:00
const DASH _ITEM _LABEL _SHOW _TIME = 150 ;
function update ( reset = false ) {
2025-02-09 23:10:52 +01:00
opt = Me . imports . lib . settings . opt ;
const moduleEnabled = opt . get ( 'dashModule' , true ) ;
reset = reset || ! moduleEnabled ;
// don't even touch this module if disabled
if ( _firstRun && reset )
return ;
_firstRun = false ;
if ( _overrides )
2025-02-09 23:08:39 +01:00
_overrides . removeAll ( ) ;
2025-02-09 23:10:52 +01:00
2025-02-09 23:08:39 +01:00
const dash = Main . overview . _overview . _controls . layoutManager . _dash ;
setToHorizontal ( ) ;
2025-02-09 23:10:52 +01:00
dash . remove _style _class _name ( 'vertical' ) ;
dash . remove _style _class _name ( 'vertical-left' ) ;
dash . remove _style _class _name ( 'vertical-right' ) ;
2025-02-09 23:08:39 +01:00
if ( reset ) {
_moveDashAppGridIcon ( reset ) ;
_connectShowAppsIcon ( reset ) ;
_updateSearchWindowsIcon ( false ) ;
_updateRecentFilesIcon ( false ) ;
dash . visible = true ;
dash . _background . opacity = 255 ;
2025-02-09 23:10:52 +01:00
dash . _background . remove _style _class _name ( 'v-shell-dash-background' ) ;
2025-02-09 23:08:39 +01:00
_overrides = null ;
opt = null ;
return ;
}
_overrides = new Util . Overrides ( ) ;
2025-02-09 23:10:52 +01:00
_overrides . addOverride ( 'DashItemContainer' , Dash . DashItemContainer . prototype , DashItemContainerCommon ) ;
_overrides . addOverride ( 'DashCommon' , Dash . Dash . prototype , DashCommon ) ;
_overrides . addOverride ( 'AppIcon' , AppDisplay . AppIcon . prototype , AppIconCommon ) ;
_overrides . addOverride ( 'DashIcon' , Dash . DashIcon . prototype , DashIconCommon ) ;
2025-02-09 23:08:39 +01:00
if ( opt . DASH _VERTICAL ) {
_overrides . addOverride ( 'Dash' , Dash . Dash . prototype , DashOverride ) ;
setToVertical ( ) ;
2025-02-09 23:10:52 +01:00
dash . add _style _class _name ( 'vertical' ) ;
2025-02-09 23:08:39 +01:00
if ( ! _newWorkId ) {
_origWorkId = dash . _workId ;
dash . _workId = Main . initializeDeferredWork ( dash . _box , dash . _redisplay . bind ( dash ) ) ;
_newWorkId = dash . _workId ;
} else {
dash . _workId = _newWorkId ;
}
} else {
setToHorizontal ( ) ;
if ( _origWorkId )
dash . _workId = _origWorkId ;
}
_updateSearchWindowsIcon ( ) ;
_updateRecentFilesIcon ( ) ;
_moveDashAppGridIcon ( ) ;
_connectShowAppsIcon ( ) ;
2025-02-09 23:10:52 +01:00
if ( dash . _showWindowsIcon && ! dash . _showWindowsIconClickedId )
2025-02-09 23:08:39 +01:00
dash . _showWindowsIconClickedId = dash . _showWindowsIcon . toggleButton . connect ( 'clicked' , ( a , c ) => c && Util . activateSearchProvider ( WindowSearchProviderPrefix ) ) ;
2025-02-09 23:10:52 +01:00
if ( dash . _recentFilesIcon && ! dash . _recentFilesIconClickedId )
2025-02-09 23:08:39 +01:00
dash . _recentFilesIconClickedId = dash . _recentFilesIcon . toggleButton . connect ( 'clicked' , ( a , c ) => c && Util . activateSearchProvider ( RecentFilesSearchProviderPrefix ) ) ;
2025-02-09 23:10:52 +01:00
dash . visible = opt . DASH _VISIBLE ;
dash . _background . add _style _class _name ( 'v-shell-dash-background' ) ;
dash . _redisplay ( ) ;
2025-02-09 23:08:39 +01:00
}
function setToVertical ( ) {
let dash = Main . overview . _overview . _controls . layoutManager . _dash ;
dash . _box . layout _manager . orientation = Clutter . Orientation . VERTICAL ;
dash . _dashContainer . layout _manager . orientation = Clutter . Orientation . VERTICAL ;
dash . _dashContainer . y _expand = false ;
dash . _dashContainer . x _expand = true ;
dash . x _align = Clutter . ActorAlign . START ;
dash . y _align = Clutter . ActorAlign . CENTER ;
let sizerBox = dash . _background . get _children ( ) [ 0 ] ;
sizerBox . clear _constraints ( ) ;
sizerBox . add _constraint ( new Clutter . BindConstraint ( {
source : dash . _showAppsIcon . icon ,
coordinate : Clutter . BindCoordinate . WIDTH ,
} ) ) ;
sizerBox . add _constraint ( new Clutter . BindConstraint ( {
source : dash . _dashContainer ,
coordinate : Clutter . BindCoordinate . HEIGHT ,
} ) ) ;
dash . _box . remove _all _children ( ) ;
dash . _separator = null ;
dash . _queueRedisplay ( ) ;
dash . _adjustIconSize ( ) ;
2025-02-09 23:10:52 +01:00
dash . add _style _class _name ( opt . DASH _LEFT ? 'vertical-left' : 'vertical-right' ) ;
2025-02-09 23:08:39 +01:00
}
function setToHorizontal ( ) {
let dash = Main . overview . _overview . _controls . layoutManager . _dash ;
if ( _origWorkId )
2025-02-09 23:10:52 +01:00
dash . _workId = _origWorkId ; // pretty sure this is a leak, but there no provided way to disconnect these...
2025-02-09 23:08:39 +01:00
dash . _box . layout _manager . orientation = Clutter . Orientation . HORIZONTAL ;
dash . _dashContainer . layout _manager . orientation = Clutter . Orientation . HORIZONTAL ;
dash . _dashContainer . y _expand = true ;
dash . _dashContainer . x _expand = false ;
dash . x _align = Clutter . ActorAlign . CENTER ;
dash . y _align = 0 ;
let sizerBox = dash . _background . get _children ( ) [ 0 ] ;
sizerBox . clear _constraints ( ) ;
sizerBox . add _constraint ( new Clutter . BindConstraint ( {
source : dash . _showAppsIcon . icon ,
coordinate : Clutter . BindCoordinate . HEIGHT ,
} ) ) ;
sizerBox . add _constraint ( new Clutter . BindConstraint ( {
source : dash . _dashContainer ,
coordinate : Clutter . BindCoordinate . WIDTH ,
} ) ) ;
dash . _box . remove _all _children ( ) ;
dash . _separator = null ;
dash . _queueRedisplay ( ) ;
dash . _adjustIconSize ( ) ;
}
function _moveDashAppGridIcon ( reset = false ) {
// move dash app grid icon to the front
const dash = Main . overview . _overview . _controls . layoutManager . _dash ;
const appIconPosition = opt . get ( 'showAppsIconPosition' , true ) ;
dash . _showAppsIcon . remove _style _class _name ( 'show-apps-icon-vertical-hide' ) ;
dash . _showAppsIcon . remove _style _class _name ( 'show-apps-icon-horizontal-hide' ) ;
dash . _showAppsIcon . opacity = 255 ;
if ( ! reset && appIconPosition === 0 ) // 0 - start
dash . _dashContainer . set _child _at _index ( dash . _showAppsIcon , 0 ) ;
if ( reset || appIconPosition === 1 ) { // 1 - end
const index = dash . _dashContainer . get _children ( ) . length - 1 ;
dash . _dashContainer . set _child _at _index ( dash . _showAppsIcon , index ) ;
}
2025-02-09 23:10:52 +01:00
if ( ! reset && appIconPosition === 2 ) { // 2 - hide
2025-02-09 23:08:39 +01:00
const style = opt . DASH _VERTICAL ? 'show-apps-icon-vertical-hide' : 'show-apps-icon-horizontal-hide' ;
dash . _showAppsIcon . add _style _class _name ( style ) ;
// for some reason even if the icon height in vertical mode should be set to 0 by the style, it stays visible in full size returning height 1px
dash . _showAppsIcon . opacity = 0 ;
}
}
function _connectShowAppsIcon ( reset = false ) {
if ( ! reset ) {
if ( _showAppsIconBtnPressId || Util . dashIsDashToDock ( ) ) {
// button is already connected || dash is Dash to Dock
return ;
}
Main . overview . dash . _showAppsIcon . reactive = true ;
_showAppsIconBtnPressId = Main . overview . dash . _showAppsIcon . connect ( 'button-press-event' , ( actor , event ) => {
const button = event . get _button ( ) ;
2025-02-09 23:10:52 +01:00
if ( button === Clutter . BUTTON _MIDDLE )
2025-02-09 23:08:39 +01:00
Util . openPreferences ( ) ;
2025-02-09 23:10:52 +01:00
else if ( button === Clutter . BUTTON _SECONDARY )
2025-02-09 23:08:39 +01:00
Util . activateSearchProvider ( WindowSearchProviderPrefix ) ;
2025-02-09 23:10:52 +01:00
else
2025-02-09 23:08:39 +01:00
return Clutter . EVENT _PROPAGATE ;
2025-02-09 23:10:52 +01:00
return Clutter . EVENT _STOP ;
2025-02-09 23:08:39 +01:00
} ) ;
2025-02-09 23:10:52 +01:00
} else if ( _showAppsIconBtnPressId ) {
Main . overview . dash . _showAppsIcon . disconnect ( _showAppsIconBtnPressId ) ;
_showAppsIconBtnPressId = 0 ;
Main . overview . dash . _showAppsIcon . reactive = false ;
2025-02-09 23:08:39 +01:00
}
}
2025-02-09 23:10:52 +01:00
const DashOverride = {
handleDragOver ( source , actor , _x , y , _time ) {
let app = Dash . getAppFromSource ( source ) ;
2025-02-09 23:08:39 +01:00
// Don't allow favoriting of transient apps
2025-02-09 23:10:52 +01:00
if ( app === null || app . is _window _backed ( ) )
2025-02-09 23:08:39 +01:00
return DND . DragMotionResult . NO _DROP ;
if ( ! global . settings . is _writable ( 'favorite-apps' ) )
return DND . DragMotionResult . NO _DROP ;
let favorites = AppFavorites . getAppFavorites ( ) . getFavorites ( ) ;
let numFavorites = favorites . length ;
let favPos = favorites . indexOf ( app ) ;
let children = this . _box . get _children ( ) ;
let numChildren = children . length ;
let boxHeight = this . _box . height ;
// Keep the placeholder out of the index calculation; assuming that
// the remove target has the same size as "normal" items, we don't
// need to do the same adjustment there.
if ( this . _dragPlaceholder ) {
boxHeight -= this . _dragPlaceholder . height ;
numChildren -- ;
}
// Same with the separator
if ( this . _separator ) {
boxHeight -= this . _separator . height ;
numChildren -- ;
}
let pos ;
if ( ! this . _emptyDropTarget )
pos = Math . floor ( y * numChildren / boxHeight ) ;
else
pos = 0 ; // always insert at the top when dash is empty
// Put the placeholder after the last favorite if we are not
// in the favorites zone
if ( pos > numFavorites )
pos = numFavorites ;
if ( pos !== this . _dragPlaceholderPos && this . _animatingPlaceholdersCount === 0 ) {
this . _dragPlaceholderPos = pos ;
// Don't allow positioning before or after self
2025-02-09 23:10:52 +01:00
if ( favPos !== - 1 && ( pos === favPos || pos === favPos + 1 ) ) {
2025-02-09 23:08:39 +01:00
this . _clearDragPlaceholder ( ) ;
return DND . DragMotionResult . CONTINUE ;
}
// If the placeholder already exists, we just move
// it, but if we are adding it, expand its size in
// an animation
let fadeIn ;
if ( this . _dragPlaceholder ) {
this . _dragPlaceholder . destroy ( ) ;
fadeIn = false ;
} else {
fadeIn = true ;
}
2025-02-09 23:10:52 +01:00
this . _dragPlaceholder = new Dash . DragPlaceholderItem ( ) ;
2025-02-09 23:08:39 +01:00
this . _dragPlaceholder . child . set _width ( this . iconSize / 2 ) ;
this . _dragPlaceholder . child . set _height ( this . iconSize ) ;
this . _box . insert _child _at _index ( this . _dragPlaceholder ,
this . _dragPlaceholderPos ) ;
this . _dragPlaceholder . show ( fadeIn ) ;
}
if ( ! this . _dragPlaceholder )
return DND . DragMotionResult . NO _DROP ;
2025-02-09 23:10:52 +01:00
let srcIsFavorite = favPos !== - 1 ;
2025-02-09 23:08:39 +01:00
if ( srcIsFavorite )
return DND . DragMotionResult . MOVE _DROP ;
return DND . DragMotionResult . COPY _DROP ;
} ,
2025-02-09 23:10:52 +01:00
_redisplay ( ) {
2025-02-09 23:08:39 +01:00
let favorites = AppFavorites . getAppFavorites ( ) . getFavoriteMap ( ) ;
let running = this . _appSystem . get _running ( ) ;
let children = this . _box . get _children ( ) . filter ( actor => {
return actor . child &&
actor . child . _delegate &&
actor . child . _delegate . app ;
} ) ;
// Apps currently in the dash
let oldApps = children . map ( actor => actor . child . _delegate . app ) ;
// Apps supposed to be in the dash
let newApps = [ ] ;
for ( let id in favorites )
newApps . push ( favorites [ id ] ) ;
for ( let i = 0 ; i < running . length ; i ++ ) {
let app = running [ i ] ;
if ( app . get _id ( ) in favorites )
continue ;
newApps . push ( app ) ;
}
// Figure out the actual changes to the list of items; we iterate
// over both the list of items currently in the dash and the list
// of items expected there, and collect additions and removals.
// Moves are both an addition and a removal, where the order of
// the operations depends on whether we encounter the position
// where the item has been added first or the one from where it
// was removed.
// There is an assumption that only one item is moved at a given
// time; when moving several items at once, everything will still
// end up at the right position, but there might be additional
// additions/removals (e.g. it might remove all the launchers
// and add them back in the new order even if a smaller set of
// additions and removals is possible).
// If above assumptions turns out to be a problem, we might need
// to use a more sophisticated algorithm, e.g. Longest Common
// Subsequence as used by diff.
let addedItems = [ ] ;
let removedActors = [ ] ;
let newIndex = 0 ;
let oldIndex = 0 ;
while ( newIndex < newApps . length || oldIndex < oldApps . length ) {
let oldApp = oldApps . length > oldIndex ? oldApps [ oldIndex ] : null ;
let newApp = newApps . length > newIndex ? newApps [ newIndex ] : null ;
// No change at oldIndex/newIndex
2025-02-09 23:10:52 +01:00
if ( oldApp === newApp ) {
2025-02-09 23:08:39 +01:00
oldIndex ++ ;
newIndex ++ ;
continue ;
}
// App removed at oldIndex
if ( oldApp && ! newApps . includes ( oldApp ) ) {
removedActors . push ( children [ oldIndex ] ) ;
oldIndex ++ ;
continue ;
}
// App added at newIndex
if ( newApp && ! oldApps . includes ( newApp ) ) {
addedItems . push ( {
app : newApp ,
item : this . _createAppItem ( newApp ) ,
2025-02-09 23:10:52 +01:00
pos : newIndex ,
2025-02-09 23:08:39 +01:00
} ) ;
newIndex ++ ;
continue ;
}
// App moved
let nextApp = newApps . length > newIndex + 1
? newApps [ newIndex + 1 ] : null ;
2025-02-09 23:10:52 +01:00
let insertHere = nextApp && nextApp === oldApp ;
2025-02-09 23:08:39 +01:00
let alreadyRemoved = removedActors . reduce ( ( result , actor ) => {
let removedApp = actor . child . _delegate . app ;
2025-02-09 23:10:52 +01:00
return result || removedApp === newApp ;
2025-02-09 23:08:39 +01:00
} , false ) ;
if ( insertHere || alreadyRemoved ) {
let newItem = this . _createAppItem ( newApp ) ;
addedItems . push ( {
app : newApp ,
item : newItem ,
2025-02-09 23:10:52 +01:00
pos : newIndex + removedActors . length ,
2025-02-09 23:08:39 +01:00
} ) ;
newIndex ++ ;
} else {
removedActors . push ( children [ oldIndex ] ) ;
oldIndex ++ ;
}
}
for ( let i = 0 ; i < addedItems . length ; i ++ ) {
this . _box . insert _child _at _index ( addedItems [ i ] . item ,
addedItems [ i ] . pos ) ;
}
for ( let i = 0 ; i < removedActors . length ; i ++ ) {
let item = removedActors [ i ] ;
// Don't animate item removal when the overview is transitioning
// or hidden
if ( Main . overview . visible && ! Main . overview . animationInProgress )
item . animateOutAndDestroy ( ) ;
else
item . destroy ( ) ;
}
this . _adjustIconSize ( ) ;
// Skip animations on first run when adding the initial set
// of items, to avoid all items zooming in at once
let animate = this . _shownInitially && Main . overview . visible &&
! Main . overview . animationInProgress ;
if ( ! this . _shownInitially )
this . _shownInitially = true ;
for ( let i = 0 ; i < addedItems . length ; i ++ )
addedItems [ i ] . item . show ( animate ) ;
// Update separator
const nFavorites = Object . keys ( favorites ) . length ;
const nIcons = children . length + addedItems . length - removedActors . length ;
if ( nFavorites > 0 && nFavorites < nIcons ) {
// destroy the horizontal separator if it exists.
// this is incredibly janky, but I can't think of a better way atm.
if ( this . _separator && this . _separator . height !== 1 ) {
this . _separator . destroy ( ) ;
this . _separator = null ;
}
if ( ! this . _separator ) {
this . _separator = new St . Widget ( {
style _class : 'dash-separator' ,
x _align : Clutter . ActorAlign . CENTER ,
y _align : Clutter . ActorAlign . CENTER ,
width : this . iconSize ,
height : 1 ,
} ) ;
2025-02-09 23:10:52 +01:00
this . _box . add _child ( this . _separator ) ;
2025-02-09 23:08:39 +01:00
}
2025-02-09 23:10:52 +01:00
// FIXME: separator placement is broken (also in original dash)
2025-02-09 23:08:39 +01:00
let pos = nFavorites ;
if ( this . _dragPlaceholder )
pos ++ ;
this . _box . set _child _at _index ( this . _separator , pos ) ;
} else if ( this . _separator ) {
this . _separator . destroy ( ) ;
this . _separator = null ;
}
// Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
// Without it, StBoxLayout may use a stale size cache
this . _box . queue _relayout ( ) ;
} ,
2025-02-09 23:10:52 +01:00
_createAppItem ( app ) {
let appIcon = new Dash . DashIcon ( app ) ;
2025-02-09 23:08:39 +01:00
let indicator = appIcon . _dot ;
indicator . x _align = opt . DASH _LEFT ? Clutter . ActorAlign . START : Clutter . ActorAlign . END ;
indicator . y _align = Clutter . ActorAlign . CENTER ;
appIcon . connect ( 'menu-state-changed' ,
( o , opened ) => {
this . _itemMenuStateChanged ( item , opened ) ;
} ) ;
2025-02-09 23:10:52 +01:00
let item = new Dash . DashItemContainer ( ) ;
2025-02-09 23:08:39 +01:00
item . setChild ( appIcon ) ;
// Override default AppIcon label_actor, now the
// accessible_name is set at DashItemContainer.setLabelText
appIcon . label _actor = null ;
item . setLabelText ( app . get _name ( ) ) ;
appIcon . icon . setIconSize ( this . iconSize ) ;
this . _hookUpLabel ( item , appIcon ) ;
return item ;
2025-02-09 23:10:52 +01:00
} ,
} ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
const DashItemContainerCommon = {
2025-02-09 23:08:39 +01:00
// move labels according dash position
2025-02-09 23:10:52 +01:00
showLabel ( ) {
2025-02-09 23:08:39 +01:00
if ( ! this . _labelText )
return ;
this . label . set _text ( this . _labelText ) ;
this . label . opacity = 0 ;
this . label . show ( ) ;
let [ stageX , stageY ] = this . get _transformed _position ( ) ;
const itemWidth = this . allocation . get _width ( ) ;
const itemHeight = this . allocation . get _height ( ) ;
const labelWidth = this . label . get _width ( ) ;
const labelHeight = this . label . get _height ( ) ;
2025-02-09 23:10:52 +01:00
let xOffset = Math . floor ( ( itemWidth - labelWidth ) / 2 ) ;
2025-02-09 23:08:39 +01:00
let x = Math . clamp ( stageX + xOffset , 0 , global . stage . width - labelWidth ) ;
let node = this . label . get _theme _node ( ) ;
let y ;
if ( opt . DASH _TOP ) {
const yOffset = itemHeight - labelHeight + 3 * node . get _length ( '-y-offset' ) ;
y = stageY + yOffset ;
} else if ( opt . DASH _BOTTOM ) {
const yOffset = node . get _length ( '-y-offset' ) ;
y = stageY - this . label . height - yOffset ;
} else if ( opt . DASH _RIGHT ) {
const yOffset = Math . floor ( ( itemHeight - labelHeight ) / 2 ) ;
2025-02-09 23:10:52 +01:00
xOffset = 4 ;
2025-02-09 23:08:39 +01:00
x = stageX - xOffset - this . label . width ;
y = Math . clamp ( stageY + yOffset , 0 , global . stage . height - labelHeight ) ;
2025-02-09 23:10:52 +01:00
} else if ( opt . DASH _LEFT ) {
2025-02-09 23:08:39 +01:00
const yOffset = Math . floor ( ( itemHeight - labelHeight ) / 2 ) ;
2025-02-09 23:10:52 +01:00
xOffset = 4 ;
2025-02-09 23:08:39 +01:00
x = stageX + this . width + xOffset ;
y = Math . clamp ( stageY + yOffset , 0 , global . stage . height - labelHeight ) ;
}
this . label . set _position ( x , y ) ;
this . label . ease ( {
opacity : 255 ,
duration : DASH _ITEM _LABEL _SHOW _TIME ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
} ) ;
this . label . set _position ( x , y ) ;
this . label . ease ( {
opacity : 255 ,
duration : DASH _ITEM _LABEL _SHOW _TIME ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
} ) ;
2025-02-09 23:10:52 +01:00
} ,
} ;
const DashCommon = {
// use custom BaseIconSizes and add support for custom icons
_adjustIconSize ( ) {
// if a user launches multiple apps at once, this function may be called again before the previous call has finished
// as a result, new icons will not reach their full size, or will be missing, if adding a new icon and changing the dash size due to lack of space at the same time
if ( this . _adjustingInProgress )
return ;
2025-02-09 23:08:39 +01:00
// For the icon size, we only consider children which are "proper"
// icons (i.e. ignoring drag placeholders) and which are not
// animating out (which means they will be destroyed at the end of
// the animation)
let iconChildren = this . _box . get _children ( ) . filter ( actor => {
return actor . child &&
actor . child . _delegate &&
actor . child . _delegate . icon &&
! actor . animatingOut ;
} ) ;
2025-02-09 23:10:52 +01:00
// add new custom icons to the list
if ( this . _showAppsIcon . visible )
2025-02-09 23:08:39 +01:00
iconChildren . push ( this . _showAppsIcon ) ;
2025-02-09 23:10:52 +01:00
if ( this . _showWindowsIcon )
2025-02-09 23:08:39 +01:00
iconChildren . push ( this . _showWindowsIcon ) ;
2025-02-09 23:10:52 +01:00
if ( this . _recentFilesIcon )
2025-02-09 23:08:39 +01:00
iconChildren . push ( this . _recentFilesIcon ) ;
2025-02-09 23:10:52 +01:00
if ( ! iconChildren . length )
return ;
2025-02-09 23:08:39 +01:00
if ( this . _maxWidth === - 1 || this . _maxHeight === - 1 )
return ;
const dashHorizontal = ! opt . DASH _VERTICAL ;
const themeNode = this . get _theme _node ( ) ;
const maxAllocation = new Clutter . ActorBox ( {
x1 : 0 ,
y1 : 0 ,
x2 : dashHorizontal ? this . _maxWidth : 42 , // not whatever
y2 : dashHorizontal ? 42 : this . _maxHeight ,
} ) ;
let maxContent = themeNode . get _content _box ( maxAllocation ) ;
let spacing = themeNode . get _length ( 'spacing' ) ;
let firstButton = iconChildren [ 0 ] . child ;
let firstIcon = firstButton . _delegate . icon ;
2025-02-09 23:10:52 +01:00
if ( ! firstIcon . icon )
return ;
2025-02-09 23:08:39 +01:00
// Enforce valid spacings during the size request
firstIcon . icon . ensure _style ( ) ;
const [ , , iconWidth , iconHeight ] = firstIcon . icon . get _preferred _size ( ) ;
const [ , , buttonWidth , buttonHeight ] = firstButton . get _preferred _size ( ) ;
2025-02-09 23:10:52 +01:00
let scaleFactor = St . ThemeContext . get _for _stage ( global . stage ) . scale _factor ;
2025-02-09 23:08:39 +01:00
let availWidth , availHeight , maxIconSize ;
if ( dashHorizontal ) {
availWidth = maxContent . x2 - maxContent . x1 ;
// Subtract icon padding and box spacing from the available width
availWidth -= iconChildren . length * ( buttonWidth - iconWidth ) +
( iconChildren . length - 1 ) * spacing +
2 * this . _background . get _theme _node ( ) . get _horizontal _padding ( ) ;
availHeight = this . _maxHeight ;
availHeight -= this . margin _top + this . margin _bottom ;
availHeight -= this . _background . get _theme _node ( ) . get _vertical _padding ( ) ;
availHeight -= themeNode . get _vertical _padding ( ) ;
availHeight -= buttonHeight - iconHeight ;
2025-02-09 23:10:52 +01:00
maxIconSize = Math . min ( availWidth / iconChildren . length , availHeight , opt . MAX _ICON _SIZE * scaleFactor ) ;
2025-02-09 23:08:39 +01:00
} else {
availWidth = this . _maxWidth ;
availWidth -= this . _background . get _theme _node ( ) . get _horizontal _padding ( ) ;
availWidth -= themeNode . get _horizontal _padding ( ) ;
availWidth -= buttonWidth - iconWidth ;
availHeight = maxContent . y2 - maxContent . y1 ;
availHeight -= iconChildren . length * ( buttonHeight - iconHeight ) +
( iconChildren . length - 1 ) * spacing +
2 * this . _background . get _theme _node ( ) . get _vertical _padding ( ) ;
2025-02-09 23:10:52 +01:00
maxIconSize = Math . min ( availWidth , availHeight / iconChildren . length , opt . MAX _ICON _SIZE * scaleFactor ) ;
2025-02-09 23:08:39 +01:00
}
let iconSizes = BaseIconSizes . map ( s => s * scaleFactor ) ;
let newIconSize = BaseIconSizes [ 0 ] ;
for ( let i = 0 ; i < iconSizes . length ; i ++ ) {
if ( iconSizes [ i ] <= maxIconSize )
newIconSize = BaseIconSizes [ i ] ;
}
2025-02-09 23:10:52 +01:00
if ( newIconSize === this . iconSize )
return ;
// set the in-progress state here after all the possible cancels
this . _adjustingInProgress = true ;
2025-02-09 23:08:39 +01:00
let oldIconSize = this . iconSize ;
this . iconSize = newIconSize ;
this . emit ( 'icon-size-changed' ) ;
let scale = oldIconSize / newIconSize ;
for ( let i = 0 ; i < iconChildren . length ; i ++ ) {
let icon = iconChildren [ i ] . child . _delegate . icon ;
// Set the new size immediately, to keep the icons' sizes
// in sync with this.iconSize
icon . setIconSize ( this . iconSize ) ;
// Don't animate the icon size change when the overview
// is transitioning, not visible or when initially filling
// the dash
if ( ! Main . overview . visible || Main . overview . animationInProgress ||
! this . _shownInitially )
continue ;
let [ targetWidth , targetHeight ] = icon . icon . get _size ( ) ;
// Scale the icon's texture to the previous size and
// tween to the new size
icon . icon . set _size ( icon . icon . width * scale ,
2025-02-09 23:10:52 +01:00
icon . icon . height * scale ) ;
2025-02-09 23:08:39 +01:00
icon . icon . ease ( {
width : targetWidth ,
height : targetHeight ,
duration : Dash . DASH _ANIMATION _TIME ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
} ) ;
}
if ( this . _separator ) {
this . _separator . ease ( {
width : dashHorizontal ? 1 : this . iconSize ,
height : dashHorizontal ? this . iconSize : 1 ,
duration : Dash . DASH _ANIMATION _TIME ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
} ) ;
}
2025-02-09 23:10:52 +01:00
this . _adjustingInProgress = false ;
} ,
} ;
const DashIconCommon = {
after _ _init ( ) {
if ( opt . DASH _ICON _SCROLL ) {
this . _scrollConId = this . connect ( 'scroll-event' , _onScrollEvent . bind ( this ) ) ;
this . _leaveConId = this . connect ( 'leave-event' , _onLeaveEvent . bind ( this ) ) ;
}
2025-02-09 23:08:39 +01:00
} ,
2025-02-09 23:10:52 +01:00
} ;
function _onScrollEvent ( source , event ) {
if ( ( this . app && ! opt . DASH _ICON _SCROLL ) || ( this . _isSearchWindowsIcon && ! opt . SEARCH _WINDOWS _ICON _SCROLL ) ) {
if ( this . _scrollConId )
this . disconnect ( this . _scrollConId ) ;
if ( this . _leaveConId )
this . disconnect ( this . _leaveConId ) ;
return Clutter . EVENT _PROPAGATE ;
}
let direction = Util . getScrollDirection ( event ) ;
if ( direction === Clutter . ScrollDirection . UP )
direction = 1 ;
else if ( direction === Clutter . ScrollDirection . DOWN )
direction = - 1 ;
else
return Clutter . EVENT _STOP ;
// avoid uncontrollable switching if smooth scroll wheel or trackpad is used
if ( this . _lastScroll && Date . now ( ) - this . _lastScroll < 160 )
return Clutter . EVENT _STOP ;
this . _lastScroll = Date . now ( ) ;
_switchWindow . bind ( this ) ( direction ) ;
return Clutter . EVENT _STOP ;
2025-02-09 23:08:39 +01:00
}
2025-02-09 23:10:52 +01:00
function _onLeaveEvent ( ) {
if ( ! this . _selectedMetaWin || this . has _pointer || this . toggleButton ? . has _pointer )
return ;
this . _selectedPreview . _activateSelected = false ;
this . _selectedMetaWin = null ;
this . _scrolledWindows = null ;
_showWindowPreview . bind ( this ) ( null ) ;
}
function _switchWindow ( direction ) {
if ( ! this . _scrolledWindows ) {
// source is app icon
if ( this . app ) {
this . _scrolledWindows = this . app . get _windows ( ) ;
const wsList = [ ] ;
this . _scrolledWindows . forEach ( w => {
const ws = w . get _workspace ( ) ;
if ( ! wsList . includes ( ws ) )
wsList . push ( ws ) ;
} ) ;
// sort windows by workspaces in MRU order
this . _scrolledWindows . sort ( ( a , b ) => wsList . indexOf ( a . get _workspace ( ) ) > wsList . indexOf ( b . get _workspace ( ) ) ) ;
// source is Search Windows icon
} else if ( this . _isSearchWindowsIcon ) {
if ( opt . SEARCH _WINDOWS _ICON _SCROLL === 1 ) // all windows
this . _scrolledWindows = AltTab . getWindows ( null ) ;
else
this . _scrolledWindows = AltTab . getWindows ( global . workspace _manager . get _active _workspace ( ) ) ;
}
}
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
let windows = this . _scrolledWindows ;
if ( ! windows . length )
return ;
// if window selection is in the process, the previewed window must be the current one
let currentWin = this . _selectedMetaWin ? this . _selectedMetaWin : windows [ 0 ] ;
const currentIdx = windows . indexOf ( currentWin ) ;
let targetIdx = currentIdx + direction ;
if ( targetIdx > windows . length - 1 )
targetIdx = 0 ;
else if ( targetIdx < 0 )
targetIdx = windows . length - 1 ;
const metaWin = windows [ targetIdx ] ;
_showWindowPreview . bind ( this ) ( metaWin ) ;
this . _selectedMetaWin = metaWin ;
}
function _showWindowPreview ( metaWin ) {
const views = Main . overview . _overview . controls . _workspacesDisplay . _workspacesViews ;
const viewsIter = [ views [ 0 ] ] ;
// secondary monitors use different structure
views . forEach ( v => {
if ( v . _workspacesView )
viewsIter . push ( v . _workspacesView ) ;
} ) ;
viewsIter . forEach ( view => {
// if workspaces are on primary monitor only
if ( ! view || ! view . _workspaces )
return ;
view . _workspaces . forEach ( ws => {
ws . _windows . forEach ( windowPreview => {
// metaWin === null resets opacity
let opacity = metaWin ? 50 : 255 ;
windowPreview . _activateSelected = false ;
// minimized windows are invisible if windows are not exposed (WORKSPACE_MODE === 0)
if ( ! windowPreview . opacity )
windowPreview . opacity = 255 ;
// app windows set to lower opacity, so they can be recognized
if ( this . _scrolledWindows && this . _scrolledWindows . includes ( windowPreview . metaWindow ) ) {
if ( opt . DASH _ICON _SCROLL === 2 )
opacity = 254 ;
}
if ( windowPreview . metaWindow === metaWin ) {
if ( metaWin && metaWin . get _workspace ( ) !== global . workspace _manager . get _active _workspace ( ) )
Main . wm . actionMoveWorkspace ( metaWin . get _workspace ( ) ) ;
windowPreview . get _parent ( ) . set _child _above _sibling ( windowPreview , null ) ;
opacity = 255 ;
this . _selectedPreview = windowPreview ;
windowPreview . _activateSelected = true ;
}
// if windows are exposed, highlight selected using opacity
if ( ( opt . OVERVIEW _MODE && opt . WORKSPACE _MODE ) || ! opt . OVERVIEW _MODE ) {
if ( metaWin && opacity === 255 )
windowPreview . showOverlay ( true ) ;
else
windowPreview . hideOverlay ( true ) ;
windowPreview . ease ( {
duration : 200 ,
opacity ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
} ) ;
}
} ) ;
} ) ;
} ) ;
}
const AppIconCommon = {
activate ( button ) {
const event = Clutter . get _current _event ( ) ;
const state = event ? event . get _state ( ) : 0 ;
const isMiddleButton = button && button === Clutter . BUTTON _MIDDLE ;
const isCtrlPressed = Util . isCtrlPressed ( state ) ;
const isShiftPressed = Util . isShiftPressed ( state ) ;
const openNewWindow = ( this . app . can _open _new _window ( ) &&
this . app . state === Shell . AppState . RUNNING &&
( isCtrlPressed || isMiddleButton ) && ! opt . DASH _CLICK _ACTION === 2 ) ||
( opt . DASH _CLICK _ACTION === 2 && ! this . _selectedMetaWin && ! isMiddleButton ) ;
const currentWS = global . workspace _manager . get _active _workspace ( ) ;
const appRecentWorkspace = _getAppRecentWorkspace ( this . app ) ;
// this feature shouldn't affect search results, dash icons don't have labels, so we use them as a condition
const showWidowsBeforeActivation = opt . DASH _CLICK _ACTION === 1 && ! this . icon . label ;
let targetWindowOnCurrentWs = false ;
if ( opt . DASH _FOLLOW _RECENT _WIN ) {
targetWindowOnCurrentWs = appRecentWorkspace === currentWS ;
} else {
this . app . get _windows ( ) . forEach (
w => {
targetWindowOnCurrentWs = targetWindowOnCurrentWs || ( w . get _workspace ( ) === currentWS ) ;
}
) ;
}
if ( ( this . app . state === Shell . AppState . STOPPED || openNewWindow ) && ! isShiftPressed )
this . animateLaunch ( ) ;
if ( openNewWindow ) {
this . app . open _new _window ( - 1 ) ;
// if DASH_CLICK_ACTION == "SHOW_WINS_BEFORE", the app has more than one window and has no window on the current workspace,
// don't activate the app immediately, only move the overview to the workspace with the app's recent window
} else if ( showWidowsBeforeActivation && ! isShiftPressed && this . app . get _n _windows ( ) > 1 && ! targetWindowOnCurrentWs /* && !(opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE)*/ ) {
// this._scroll = true;
// this._scrollTime = Date.now();
Main . wm . actionMoveWorkspace ( appRecentWorkspace ) ;
Main . overview . dash . showAppsButton . checked = false ;
return ;
} else if ( this . _selectedMetaWin ) {
this . _selectedMetaWin . activate ( global . get _current _time ( ) ) ;
} else if ( showWidowsBeforeActivation && opt . OVERVIEW _MODE && ! opt . WORKSPACE _MODE && ! isShiftPressed && this . app . get _n _windows ( ) > 1 ) {
// expose windows
Main . overview . _overview . _controls . _thumbnailsBox . _activateThumbnailAtPoint ( 0 , 0 , global . get _current _time ( ) , true ) ;
return ;
} else if ( opt . DASH _SHIFT _CLICK _MV && isShiftPressed && this . app . get _windows ( ) . length ) {
this . _moveAppToCurrentWorkspace ( ) ;
return ;
} else if ( isShiftPressed ) {
return ;
} else {
this . app . activate ( ) ;
}
Main . overview . hide ( ) ;
} ,
_moveAppToCurrentWorkspace ( ) {
this . app . get _windows ( ) . forEach ( w => w . change _workspace ( global . workspace _manager . get _active _workspace ( ) ) ) ;
} ,
popupMenu ( side = St . Side . LEFT ) {
if ( shellVersion >= 42 )
this . setForcedHighlight ( true ) ;
this . _removeMenuTimeout ( ) ;
this . fake _release ( ) ;
if ( ! this . _getWindowsOnCurrentWs ) {
this . _getWindowsOnCurrentWs = function ( ) {
const winList = [ ] ;
this . app . get _windows ( ) . forEach ( w => {
if ( w . get _workspace ( ) === global . workspace _manager . get _active _workspace ( ) )
winList . push ( w ) ;
} ) ;
return winList ;
} ;
this . _windowsOnOtherWs = function ( ) {
return ( this . app . get _windows ( ) . length - this . _getWindowsOnCurrentWs ( ) . length ) > 0 ;
} ;
}
if ( ! this . _menu ) {
this . _menu = new AppMenu ( this , side , {
favoritesSection : true ,
showSingleWindows : true ,
} ) ;
this . _menu . setApp ( this . app ) ;
this . _openSigId = this . _menu . connect ( 'open-state-changed' , ( menu , isPoppedUp ) => {
if ( ! isPoppedUp )
this . _onMenuPoppedDown ( ) ;
} ) ;
// Main.overview.connectObject('hiding',
this . _hidingSigId = Main . overview . connect ( 'hiding' ,
( ) => this . _menu . close ( ) , this ) ;
Main . uiGroup . add _actor ( this . _menu . actor ) ;
this . _menuManager . addMenu ( this . _menu ) ;
}
// once the menu is created, it stays unchanged and we need to modify our items based on current situation
if ( this . _addedMenuItems && this . _addedMenuItems . length )
this . _addedMenuItems . forEach ( i => i . destroy ( ) ) ;
const popupItems = [ ] ;
const separator = new PopupMenu . PopupSeparatorMenuItem ( ) ;
this . _menu . addMenuItem ( separator ) ;
if ( this . app . get _n _windows ( ) ) {
// if (/* opt.APP_MENU_FORCE_QUIT*/true) {}
popupItems . push ( [ _ ( 'Force Quit' ) , ( ) => {
this . app . get _windows ( ) [ 0 ] . kill ( ) ;
} ] ) ;
// if (opt.APP_MENU_CLOSE_WS) {}
const nWin = this . _getWindowsOnCurrentWs ( ) . length ;
if ( nWin ) {
popupItems . push ( [ _ ( ` Close ${ nWin } Windows on Current Workspace ` ) , ( ) => {
const windows = this . _getWindowsOnCurrentWs ( ) ;
let time = global . get _current _time ( ) ;
for ( let win of windows ) {
// increase time by 1 ms for each window to avoid errors from GS
win . delete ( time ++ ) ;
}
} ] ) ;
}
if ( /* opt.APP_MENU_MV_TO_WS && */ this . _windowsOnOtherWs ( ) )
popupItems . push ( [ _ ( 'Move App to Current Workspace ( Shift + Click )' ) , this . _moveAppToCurrentWorkspace ] ) ;
}
this . _addedMenuItems = [ ] ;
this . _addedMenuItems . push ( separator ) ;
popupItems . forEach ( i => {
let item = new PopupMenu . PopupMenuItem ( i [ 0 ] ) ;
this . _menu . addMenuItem ( item ) ;
item . connect ( 'activate' , i [ 1 ] . bind ( this ) ) ;
this . _addedMenuItems . push ( item ) ;
} ) ;
this . emit ( 'menu-state-changed' , true ) ;
this . _menu . open ( BoxPointer . PopupAnimation . FULL ) ;
this . _menuManager . ignoreRelease ( ) ;
this . emit ( 'sync-tooltip' ) ;
return false ;
} ,
} ;
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 ;
}
function _updateSearchWindowsIcon ( show = opt . SHOW _WINDOWS _ICON ) {
2025-02-09 23:08:39 +01:00
const dash = Main . overview . _overview . _controls . layoutManager . _dash ;
const dashContainer = dash . _dashContainer ;
if ( dash . _showWindowsIcon ) {
dashContainer . remove _child ( dash . _showWindowsIcon ) ;
2025-02-09 23:10:52 +01:00
if ( dash . _showWindowsIconClickedId )
dash . _showWindowsIcon . toggleButton . disconnect ( dash . _showWindowsIconClickedId ) ;
2025-02-09 23:08:39 +01:00
dash . _showWindowsIconClickedId = undefined ;
2025-02-09 23:10:52 +01:00
if ( dash . _showWindowsIcon )
dash . _showWindowsIcon . destroy ( ) ;
2025-02-09 23:08:39 +01:00
dash . _showWindowsIcon = undefined ;
}
2025-02-09 23:10:52 +01:00
if ( ! show || ! opt . WINDOW _SEARCH _PROVIDER _ENABLED )
return ;
2025-02-09 23:08:39 +01:00
if ( ! dash . _showWindowsIcon ) {
dash . _showWindowsIcon = new ShowWindowsIcon ( ) ;
dash . _showWindowsIcon . show ( false ) ;
dashContainer . add _child ( dash . _showWindowsIcon ) ;
dash . _hookUpLabel ( dash . _showWindowsIcon ) ;
}
2025-02-09 23:10:52 +01:00
dash . _showWindowsIcon . icon . setIconSize ( dash . iconSize ) ;
2025-02-09 23:08:39 +01:00
if ( opt . SHOW _WINDOWS _ICON === 1 ) {
dashContainer . set _child _at _index ( dash . _showWindowsIcon , 0 ) ;
} else if ( opt . SHOW _WINDOWS _ICON === 2 ) {
2025-02-09 23:10:52 +01:00
const index = dashContainer . get _children ( ) . length - 1 ;
2025-02-09 23:08:39 +01:00
dashContainer . set _child _at _index ( dash . _showWindowsIcon , index ) ;
}
Main . overview . _overview . _controls . layoutManager . _dash . _adjustIconSize ( ) ;
}
2025-02-09 23:10:52 +01:00
const ShowWindowsIcon = GObject . registerClass (
2025-02-09 23:08:39 +01:00
class ShowWindowsIcon extends Dash . DashItemContainer {
_init ( ) {
super . _init ( ) ;
2025-02-09 23:10:52 +01:00
this . _isSearchWindowsIcon = true ;
2025-02-09 23:08:39 +01:00
this . _labelText = _ ( 'Search Open Windows (Hotkey: Space)' ) ;
this . toggleButton = new St . Button ( {
style _class : 'show-apps' ,
track _hover : true ,
can _focus : true ,
toggle _mode : false ,
} ) ;
this . _iconActor = null ;
this . icon = new IconGrid . BaseIcon ( this . labelText , {
setSizeManually : true ,
showLabel : false ,
createIcon : this . _createIcon . bind ( this ) ,
} ) ;
this . icon . y _align = Clutter . ActorAlign . CENTER ;
this . toggleButton . add _actor ( this . icon ) ;
this . toggleButton . _delegate = this ;
this . setChild ( this . toggleButton ) ;
2025-02-09 23:10:52 +01:00
if ( opt . SEARCH _WINDOWS _ICON _SCROLL ) {
this . reactive = true ;
this . _scrollConId = this . connect ( 'scroll-event' , _onScrollEvent . bind ( this ) ) ;
this . _leaveConId = this . connect ( 'leave-event' , _onLeaveEvent . bind ( this ) ) ;
}
2025-02-09 23:08:39 +01:00
}
_createIcon ( size ) {
this . _iconActor = new St . Icon ( {
icon _name : 'focus-windows-symbolic' ,
icon _size : size ,
style _class : 'show-apps-icon' ,
track _hover : true ,
} ) ;
return this . _iconActor ;
}
} ) ;
function _updateRecentFilesIcon ( show = opt . SHOW _RECENT _FILES _ICON ) {
const dash = Main . overview . _overview . _controls . layoutManager . _dash ;
const dashContainer = dash . _dashContainer ;
if ( dash . _recentFilesIcon ) {
dashContainer . remove _child ( dash . _recentFilesIcon ) ;
2025-02-09 23:10:52 +01:00
if ( dash . _recentFilesIconClickedId )
dash . _recentFilesIcon . toggleButton . disconnect ( dash . _recentFilesIconClickedId ) ;
2025-02-09 23:08:39 +01:00
dash . _recentFilesIconClickedId = undefined ;
2025-02-09 23:10:52 +01:00
if ( dash . _recentFilesIcon )
dash . _recentFilesIcon . destroy ( ) ;
2025-02-09 23:08:39 +01:00
dash . _recentFilesIcon = undefined ;
}
2025-02-09 23:10:52 +01:00
if ( ! show || ! opt . RECENT _FILES _SEARCH _PROVIDER _ENABLED )
return ;
2025-02-09 23:08:39 +01:00
if ( ! dash . _recentFilesIcon ) {
dash . _recentFilesIcon = new ShowRecentFilesIcon ( ) ;
dash . _recentFilesIcon . show ( false ) ;
dashContainer . add _child ( dash . _recentFilesIcon ) ;
dash . _hookUpLabel ( dash . _recentFilesIcon ) ;
}
2025-02-09 23:10:52 +01:00
dash . _recentFilesIcon . icon . setIconSize ( dash . iconSize ) ;
2025-02-09 23:08:39 +01:00
if ( opt . SHOW _RECENT _FILES _ICON === 1 ) {
dashContainer . set _child _at _index ( dash . _recentFilesIcon , 0 ) ;
} else if ( opt . SHOW _RECENT _FILES _ICON === 2 ) {
2025-02-09 23:10:52 +01:00
const index = dashContainer . get _children ( ) . length - 1 ;
2025-02-09 23:08:39 +01:00
dashContainer . set _child _at _index ( dash . _recentFilesIcon , index ) ;
}
Main . overview . _overview . _controls . layoutManager . _dash . _adjustIconSize ( ) ;
}
2025-02-09 23:10:52 +01:00
const ShowRecentFilesIcon = GObject . registerClass (
2025-02-09 23:08:39 +01:00
class ShowRecentFilesIcon extends Dash . DashItemContainer {
_init ( ) {
super . _init ( ) ;
this . _labelText = _ ( 'Search Recent Files (Hotkey: Ctrl + Space)' ) ;
this . toggleButton = new St . Button ( {
style _class : 'show-apps' ,
track _hover : true ,
can _focus : true ,
toggle _mode : false ,
} ) ;
this . _iconActor = null ;
this . icon = new IconGrid . BaseIcon ( this . labelText , {
setSizeManually : true ,
showLabel : false ,
createIcon : this . _createIcon . bind ( this ) ,
} ) ;
this . icon . y _align = Clutter . ActorAlign . CENTER ;
this . toggleButton . add _actor ( this . icon ) ;
this . toggleButton . _delegate = this ;
this . setChild ( this . toggleButton ) ;
}
_createIcon ( size ) {
this . _iconActor = new St . Icon ( {
icon _name : 'document-open-recent-symbolic' ,
icon _size : size ,
style _class : 'show-apps-icon' ,
track _hover : true ,
} ) ;
return this . _iconActor ;
}
} ) ;