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
* workspaceThumbnail . js
2025-02-09 23:10:52 +01:00
*
2025-02-09 23:08:39 +01:00
* @ author GdH < G - dH @ github . com >
* @ copyright 2022 - 2023
* @ license GPL - 3.0
*
* /
'use strict' ;
2025-02-09 23:10:52 +01:00
const { GLib , Clutter , Graphene , Meta , Shell , St } = imports . gi ;
2025-02-09 23:08:39 +01:00
const DND = imports . ui . dnd ;
const Main = imports . ui . main ;
const Background = imports . ui . background ;
const WorkspaceThumbnail = imports . ui . workspaceThumbnail ;
const ThumbnailState = WorkspaceThumbnail . ThumbnailState ;
const ControlsState = imports . ui . overviewControls . ControlsState ;
const ExtensionUtils = imports . misc . extensionUtils ;
const Me = ExtensionUtils . getCurrentExtension ( ) ;
2025-02-09 23:10:52 +01:00
// gettext
const _ = Me . imports . lib . settings . _ ;
const _Util = Me . imports . lib . util ;
const shellVersion = _Util . shellVersion ;
2025-02-09 23:08:39 +01:00
let _overrides ;
const WORKSPACE _CUT _SIZE = 10 ;
2025-02-09 23:10:52 +01:00
const _originalMaxThumbnailScale = WorkspaceThumbnail . MAX _THUMBNAIL _SCALE ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
let opt = null ;
2025-02-09 23:08:39 +01:00
function update ( reset = false ) {
2025-02-09 23:10:52 +01:00
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
if ( reset ) {
2025-02-09 23:10:52 +01:00
if ( _originalMaxThumbnailScale )
WorkspaceThumbnail . MAX _THUMBNAIL _SCALE = _originalMaxThumbnailScale ;
2025-02-09 23:08:39 +01:00
_overrides = null ;
opt = null ;
return ;
}
2025-02-09 23:10:52 +01:00
opt = Me . imports . lib . settings . opt ;
2025-02-09 23:08:39 +01:00
_overrides = new _Util . Overrides ( ) ;
2025-02-09 23:10:52 +01:00
// don't limit max thumbnail scale for other clients than overview, for example AATWS.
WorkspaceThumbnail . MAX _THUMBNAIL _SCALE = 1 ;
2025-02-09 23:08:39 +01:00
_overrides . addOverride ( 'WorkspaceThumbnail' , WorkspaceThumbnail . WorkspaceThumbnail . prototype , WorkspaceThumbnailCommon ) ;
2025-02-09 23:10:52 +01:00
_overrides . addOverride ( 'ThumbnailsBoxCommon' , WorkspaceThumbnail . ThumbnailsBox . prototype , ThumbnailsBoxCommon ) ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
// replacing opt.ORIENTATION local constant with boxOrientation internal variable allows external customers such as the AATWS extension to control the box orientation.
Main . overview . _overview . controls . _thumbnailsBox . _boxOrientation = opt . ORIENTATION ;
2025-02-09 23:08:39 +01:00
}
2025-02-09 23:10:52 +01:00
const WorkspaceThumbnailCommon = {
// injection to _init()
after _ _init ( ) {
// layout manager allows aligning widget children
this . layout _manager = new Clutter . BinLayout ( ) ;
// adding layout manager to tmb widget breaks wallpaper background aligning and rounded corners
// unless border is removed
if ( opt . SHOW _WS _TMB _BG )
this . add _style _class _name ( 'ws-tmb-labeled' ) ;
2025-02-09 23:08:39 +01:00
// add workspace thumbnails labels if enabled
if ( opt . SHOW _WST _LABELS ) { // 0 - disable
2025-02-09 23:10:52 +01:00
const getLabel = function ( ) {
2025-02-09 23:08:39 +01:00
const wsIndex = this . metaWorkspace . index ( ) ;
let label = ` ${ wsIndex + 1 } ` ;
if ( opt . SHOW _WST _LABELS === 2 ) { // 2 - index + workspace name
const settings = ExtensionUtils . getSettings ( 'org.gnome.desktop.wm.preferences' ) ;
const wsLabels = settings . get _strv ( 'workspace-names' ) ;
2025-02-09 23:10:52 +01:00
if ( wsLabels . length > wsIndex && wsLabels [ wsIndex ] )
2025-02-09 23:08:39 +01:00
label += ` : ${ wsLabels [ wsIndex ] } ` ;
} else if ( opt . SHOW _WST _LABELS === 3 ) { // 3- index + app name
// global.display.get_tab_list offers workspace filtering using the second argument, but...
// ... it sometimes includes windows from other workspaces, like minimized VBox machines, after Shell restarts
const metaWin = global . display . get _tab _list ( 0 , null ) . filter (
w => w . get _monitor ( ) === this . monitorIndex && w . get _workspace ( ) . index ( ) === wsIndex ) [ 0 ] ;
if ( metaWin ) {
2025-02-09 23:10:52 +01:00
const tracker = Shell . WindowTracker . get _default ( ) ;
const app = tracker . get _window _app ( metaWin ) ;
label += ` : ${ app ? app . get _name ( ) : '' } ` ;
2025-02-09 23:08:39 +01:00
}
} else if ( opt . SHOW _WST _LABELS === 4 ) {
const metaWin = global . display . get _tab _list ( 0 , null ) . filter (
w => w . get _monitor ( ) === this . monitorIndex && w . get _workspace ( ) . index ( ) === wsIndex ) [ 0 ] ;
2025-02-09 23:10:52 +01:00
if ( metaWin )
2025-02-09 23:08:39 +01:00
label += ` : ${ metaWin . title } ` ;
}
return label ;
} . bind ( this ) ;
const label = getLabel ( ) ;
this . _wsLabel = new St . Label ( {
text : label ,
style _class : 'ws-tmb-label' ,
x _align : Clutter . ActorAlign . FILL ,
y _align : Clutter . ActorAlign . END ,
x _expand : true ,
y _expand : true ,
} ) ;
this . _wsLabel . _maxOpacity = 255 ;
this . _wsLabel . opacity = this . _wsLabel . _maxOpacity ;
this . add _child ( this . _wsLabel ) ;
this . set _child _above _sibling ( this . _wsLabel , null ) ;
2025-02-09 23:10:52 +01:00
this . _wsIndexConId = this . metaWorkspace . connect ( 'notify::workspace-index' , ( ) => {
2025-02-09 23:08:39 +01:00
const newLabel = getLabel ( ) ;
this . _wsLabel . text = newLabel ;
2025-02-09 23:10:52 +01:00
// avoid possibility of accessing non existing ws
if ( this . _updateLabelTimeout ) {
GLib . source _remove ( this . _updateLabelTimeout ) ;
this . _updateLabelTimeout = 0 ;
}
} ) ;
this . _nWindowsConId = this . metaWorkspace . connect ( 'notify::n-windows' , ( ) => {
// wait for new information
this . _updateLabelTimeout = GLib . timeout _add ( GLib . PRIORITY _DEFAULT , 250 , ( ) => {
const newLabel = getLabel ( ) ;
this . _wsLabel . text = newLabel ;
this . _updateLabelTimeout = 0 ;
return GLib . SOURCE _REMOVE ;
} ) ;
2025-02-09 23:08:39 +01:00
} ) ;
2025-02-09 23:10:52 +01:00
}
if ( opt . CLOSE _WS _BUTTON _MODE ) {
const closeButton = new St . Icon ( {
style _class : 'workspace-close-button' ,
icon _name : 'window-close-symbolic' ,
x _align : Clutter . ActorAlign . END ,
y _align : Clutter . ActorAlign . START ,
x _expand : true ,
y _expand : true ,
reactive : true ,
opacity : 0 ,
} ) ;
closeButton . connect ( 'button-release-event' , ( ) => {
if ( opt . CLOSE _WS _BUTTON _MODE ) {
this . _closeWorkspace ( ) ;
return Clutter . EVENT _STOP ;
} else {
return Clutter . EVENT _PROPAGATE ;
}
} ) ;
closeButton . connect ( 'button-press-event' , ( ) => {
return Clutter . EVENT _STOP ;
} ) ;
closeButton . connect ( 'enter-event' , ( ) => {
closeButton . opacity = 255 ;
if ( ! Meta . prefs _get _dynamic _workspaces ( ) || ( Meta . prefs _get _dynamic _workspaces ( ) && global . workspace _manager . get _n _workspaces ( ) - 1 !== this . metaWorkspace . index ( ) ) ) {
// color the button red if ready to react on clicks
if ( opt . CLOSE _WS _BUTTON _MODE < 3 || ( opt . CLOSE _WS _BUTTON _MODE === 3 && _Util . isCtrlPressed ( ) ) )
closeButton . add _style _class _name ( 'workspace-close-button-hover' ) ;
}
} ) ;
closeButton . connect ( 'leave-event' , ( ) => {
closeButton . remove _style _class _name ( 'workspace-close-button-hover' ) ;
} ) ;
this . add _child ( closeButton ) ;
this . _closeButton = closeButton ;
this . reactive = true ;
this . _lastCloseClickTime = 0 ;
}
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
if ( opt . SHOW _WST _LABELS _ON _HOVER )
this . _wsLabel . opacity = 0 ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
this . connect ( 'enter-event' , ( ) => {
if ( opt . CLOSE _WS _BUTTON _MODE && ( ! Meta . prefs _get _dynamic _workspaces ( ) || ( Meta . prefs _get _dynamic _workspaces ( ) && global . workspace _manager . get _n _workspaces ( ) - 1 !== this . metaWorkspace . index ( ) ) ) )
this . _closeButton . opacity = 200 ;
2025-02-09 23:08:39 +01:00
if ( opt . SHOW _WST _LABELS _ON _HOVER ) {
2025-02-09 23:10:52 +01:00
this . _wsLabel . ease ( {
2025-02-09 23:08:39 +01:00
duration : 100 ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
2025-02-09 23:10:52 +01:00
opacity : this . _wsLabel . _maxOpacity ,
} ) ;
}
} ) ;
this . connect ( 'leave-event' , ( ) => {
this . _closeButton . opacity = 0 ;
if ( opt . SHOW _WST _LABELS _ON _HOVER ) {
this . _wsLabel . ease ( {
2025-02-09 23:08:39 +01:00
duration : 100 ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
2025-02-09 23:10:52 +01:00
opacity : 0 ,
} ) ;
2025-02-09 23:08:39 +01:00
}
2025-02-09 23:10:52 +01:00
} ) ;
2025-02-09 23:08:39 +01:00
if ( opt . SHOW _WS _TMB _BG ) {
this . _bgManager = new Background . BackgroundManager ( {
monitorIndex : this . monitorIndex ,
container : this . _viewport ,
vignette : false ,
controlPosition : false ,
} ) ;
this . _viewport . set _child _below _sibling ( this . _bgManager . backgroundActor , null ) ;
2025-02-09 23:10:52 +01:00
this . connect ( 'destroy' , ( ) => {
2025-02-09 23:08:39 +01:00
if ( this . _bgManager )
this . _bgManager . destroy ( ) ;
this . _bgManager = null ;
2025-02-09 23:10:52 +01:00
} ) ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
// full brightness of the thumbnail bg draws unnecessary attention
// there is a grey bg under the wallpaper
2025-02-09 23:08:39 +01:00
this . _bgManager . backgroundActor . opacity = 220 ;
2025-02-09 23:10:52 +01:00
}
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
this . connect ( 'destroy' , ( ) => {
if ( this . _wsIndexConId )
this . metaWorkspace . disconnect ( this . _wsIndexConId ) ;
if ( this . _nWindowsConId )
this . metaWorkspace . disconnect ( this . _nWindowsConId ) ;
if ( this . _updateLabelTimeout )
GLib . source _remove ( this . _updateLabelTimeout ) ;
if ( this . _bgManager )
this . _bgManager . destroy ( ) ;
} ) ;
} ,
_closeWorkspace ( ) {
// CLOSE_WS_BUTTON_MODE 1: single click, 2: double-click, 3: Ctrl
if ( opt . CLOSE _WS _BUTTON _MODE === 2 ) {
const doubleClickTime = Clutter . Settings . get _default ( ) . double _click _time ;
const clickDelay = Date . now ( ) - this . _lastCloseClickTime ;
if ( clickDelay > doubleClickTime ) {
this . _lastCloseClickTime = Date . now ( ) ;
return ;
}
} else if ( opt . CLOSE _WS _BUTTON _MODE === 3 && ! _Util . isCtrlPressed ( ) ) {
return ;
}
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
// close windows on this monitor
const windows = global . display . get _tab _list ( 0 , null ) . filter (
w => w . get _monitor ( ) === this . monitorIndex && w . get _workspace ( ) === this . metaWorkspace
) ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
for ( let i = 0 ; i < windows . length ; i ++ ) {
if ( ! windows [ i ] . is _on _all _workspaces ( ) )
windows [ i ] . delete ( global . get _current _time ( ) + i ) ;
2025-02-09 23:08:39 +01:00
}
} ,
2025-02-09 23:10:52 +01:00
activate ( time ) {
2025-02-09 23:08:39 +01:00
if ( this . state > ThumbnailState . NORMAL )
return ;
// if Static Workspace overview mode active, a click on the already active workspace should activate the window picker mode
const wsIndex = this . metaWorkspace . index ( ) ;
const lastWsIndex = global . display . get _workspace _manager ( ) . get _n _workspaces ( ) - 1 ;
const stateAdjustment = Main . overview . _overview . controls . _stateAdjustment ;
if ( stateAdjustment . value === ControlsState . APP _GRID ) {
if ( this . metaWorkspace . active ) {
Main . overview . _overview . controls . _shiftState ( Meta . MotionDirection . DOWN ) ;
// if searchActive, hide it immediately
Main . overview . searchEntry . set _text ( '' ) ;
} else {
this . metaWorkspace . activate ( time ) ;
}
2025-02-09 23:10:52 +01:00
} else if ( opt . OVERVIEW _MODE2 && ! opt . WORKSPACE _MODE && wsIndex < lastWsIndex ) {
if ( stateAdjustment . value > 1 )
stateAdjustment . value = 1 ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
// spread windows
// in OVERVIEW MODE 2 windows are not spread and workspace is not scaled
// we need to repeat transition to the overview state 1 (window picker), but with spreading windows animation
if ( this . metaWorkspace . active ) {
Main . overview . _overview . controls . _searchController . _setSearchActive ( false ) ;
opt . WORKSPACE _MODE = 1 ;
// setting value to 0 would reset WORKSPACE_MODE
stateAdjustment . value = 0.01 ;
stateAdjustment . ease ( 1 , {
duration : 200 ,
mode : Clutter . AnimationMode . EASE _OUT _QUAD ,
} ) ;
2025-02-09 23:08:39 +01:00
} else {
2025-02-09 23:10:52 +01:00
// switch ws
2025-02-09 23:08:39 +01:00
this . metaWorkspace . activate ( time ) ;
}
2025-02-09 23:10:52 +01:00
// a click on the current workspace should go back to the main view
} else if ( this . metaWorkspace . active ) {
Main . overview . hide ( ) ;
} else {
this . metaWorkspace . activate ( time ) ;
2025-02-09 23:08:39 +01:00
}
2025-02-09 23:10:52 +01:00
} ,
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
// Draggable target interface used only by ThumbnailsBox
handleDragOverInternal ( source , actor , time ) {
if ( source === Main . xdndHandler ) {
this . metaWorkspace . activate ( time ) ;
return DND . DragMotionResult . CONTINUE ;
}
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
if ( this . state > ThumbnailState . NORMAL )
return DND . DragMotionResult . CONTINUE ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
if ( source . metaWindow &&
! this . _isMyWindow ( source . metaWindow . get _compositor _private ( ) ) )
return DND . DragMotionResult . MOVE _DROP ;
if ( source . app && source . app . can _open _new _window ( ) )
return DND . DragMotionResult . COPY _DROP ;
if ( ! source . app && source . shellWorkspaceLaunch )
return DND . DragMotionResult . COPY _DROP ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
if ( source instanceof imports . ui . appDisplay . FolderIcon )
return DND . DragMotionResult . COPY _DROP ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
return DND . DragMotionResult . CONTINUE ;
} ,
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
acceptDropInternal ( source , actor , time ) {
if ( this . state > ThumbnailState . NORMAL )
return false ;
if ( source . metaWindow ) {
let win = source . metaWindow . get _compositor _private ( ) ;
if ( this . _isMyWindow ( win ) )
return false ;
let metaWindow = win . get _meta _window ( ) ;
Main . moveWindowToMonitorAndWorkspace ( metaWindow ,
this . monitorIndex , this . metaWorkspace . index ( ) ) ;
return true ;
} else if ( source . app && source . app . can _open _new _window ( ) ) {
if ( source . animateLaunchAtPos )
source . animateLaunchAtPos ( actor . x , actor . y ) ;
source . app . open _new _window ( this . metaWorkspace . index ( ) ) ;
return true ;
} else if ( ! source . app && source . shellWorkspaceLaunch ) {
// While unused in our own drag sources, shellWorkspaceLaunch allows
// extensions to define custom actions for their drag sources.
source . shellWorkspaceLaunch ( {
workspace : this . metaWorkspace . index ( ) ,
timestamp : time ,
} ) ;
return true ;
} else if ( source instanceof imports . ui . appDisplay . FolderIcon ) {
if ( shellVersion >= 44 ) {
for ( let app of source . view . _apps ) {
// const app = Shell.AppSystem.get_default().lookup_app(id);
app . open _new _window ( this . metaWorkspace . index ( ) ) ;
}
} else {
for ( let id of source . view . _appIds ) {
const app = Shell . AppSystem . get _default ( ) . lookup _app ( id ) ;
app . open _new _window ( this . metaWorkspace . index ( ) ) ;
}
}
2025-02-09 23:08:39 +01:00
}
2025-02-09 23:10:52 +01:00
return false ;
} ,
} ;
const ThumbnailsBoxCommon = {
after _ _init ( scrollAdjustment , monitorIndex , orientation = opt . ORIENTATION ) {
this . _boxOrientation = orientation ;
} ,
_activateThumbnailAtPoint ( stageX , stageY , time , activateCurrent = false ) {
if ( activateCurrent ) {
const thumbnail = this . _thumbnails . find ( t => t . metaWorkspace . active ) ;
if ( thumbnail )
thumbnail . activate ( time ) ;
return ;
2025-02-09 23:08:39 +01:00
}
2025-02-09 23:10:52 +01:00
const [ r _ , x , y ] = this . transform _stage _point ( stageX , stageY ) ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
let thumbnail ;
if ( this . _boxOrientation )
thumbnail = this . _thumbnails . find ( t => y >= t . y && y <= t . y + t . height ) ;
else
thumbnail = this . _thumbnails . find ( t => x >= t . x && x <= t . x + t . width ) ;
if ( thumbnail )
thumbnail . activate ( time ) ;
2025-02-09 23:08:39 +01:00
} ,
2025-02-09 23:10:52 +01:00
acceptDrop ( source , actor , x , y , time ) {
if ( this . _dropWorkspace !== - 1 ) {
return this . _thumbnails [ this . _dropWorkspace ] . acceptDropInternal ( source , actor , time ) ;
} else if ( this . _dropPlaceholderPos !== - 1 ) {
if ( ! source . metaWindow &&
( ! source . app || ! source . app . can _open _new _window ( ) ) &&
( source . app || ! source . shellWorkspaceLaunch ) &&
! ( source instanceof imports . ui . appDisplay . FolderIcon ) )
return false ;
let isWindow = ! ! source . metaWindow ;
let newWorkspaceIndex ;
[ newWorkspaceIndex , this . _dropPlaceholderPos ] = [ this . _dropPlaceholderPos , - 1 ] ;
this . _spliceIndex = newWorkspaceIndex ;
Main . wm . insertWorkspace ( newWorkspaceIndex ) ;
if ( isWindow ) {
// Move the window to our monitor first if necessary.
let thumbMonitor = this . _thumbnails [ newWorkspaceIndex ] . monitorIndex ;
Main . moveWindowToMonitorAndWorkspace ( source . metaWindow ,
thumbMonitor , newWorkspaceIndex , true ) ;
} else if ( source . app && source . app . can _open _new _window ( ) ) {
if ( source . animateLaunchAtPos )
source . animateLaunchAtPos ( actor . x , actor . y ) ;
source . app . open _new _window ( newWorkspaceIndex ) ;
} else if ( ! source . app && source . shellWorkspaceLaunch ) {
// While unused in our own drag sources, shellWorkspaceLaunch allows
// extensions to define custom actions for their drag sources.
source . shellWorkspaceLaunch ( {
workspace : newWorkspaceIndex ,
timestamp : time ,
} ) ;
} else if ( source instanceof imports . ui . appDisplay . FolderIcon ) {
if ( shellVersion >= 44 ) {
for ( let app of source . view . _apps ) {
// const app = Shell.AppSystem.get_default().lookup_app(id);
app . open _new _window ( newWorkspaceIndex ) ;
}
} else {
for ( let id of source . view . _appIds ) {
const app = Shell . AppSystem . get _default ( ) . lookup _app ( id ) ;
app . open _new _window ( newWorkspaceIndex ) ;
}
}
}
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
if ( source . app || ( ! source . app && source . shellWorkspaceLaunch ) ) {
// This new workspace will be automatically removed if the application fails
// to open its first window within some time, as tracked by Shell.WindowTracker.
// Here, we only add a very brief timeout to avoid the _immediate_ removal of the
// workspace while we wait for the startup sequence to load.
let workspaceManager = global . workspace _manager ;
Main . wm . keepWorkspaceAlive ( workspaceManager . get _workspace _by _index ( newWorkspaceIndex ) ,
WorkspaceThumbnail . WORKSPACE _KEEP _ALIVE _TIME ) ;
}
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
// Start the animation on the workspace (which is actually
// an old one which just became empty)
let thumbnail = this . _thumbnails [ newWorkspaceIndex ] ;
this . _setThumbnailState ( thumbnail , ThumbnailState . NEW ) ;
thumbnail . slide _position = 1 ;
thumbnail . collapse _fraction = 1 ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
this . _queueUpdateStates ( ) ;
return true ;
} else {
return false ;
}
2025-02-09 23:08:39 +01:00
} ,
2025-02-09 23:10:52 +01:00
handleDragOver ( source , actor , x , y , time ) {
// switch axis for vertical orientation
if ( this . _boxOrientation )
x = y ;
2025-02-09 23:08:39 +01:00
if ( ! source . metaWindow &&
( ! source . app || ! source . app . can _open _new _window ( ) ) &&
( source . app || ! source . shellWorkspaceLaunch ) &&
2025-02-09 23:10:52 +01:00
source !== Main . xdndHandler && ! ( source instanceof imports . ui . appDisplay . FolderIcon ) )
2025-02-09 23:08:39 +01:00
return DND . DragMotionResult . CONTINUE ;
const rtl = Clutter . get _default _text _direction ( ) === Clutter . TextDirection . RTL ;
let canCreateWorkspaces = Meta . prefs _get _dynamic _workspaces ( ) ;
let spacing = this . get _theme _node ( ) . get _length ( 'spacing' ) ;
this . _dropWorkspace = - 1 ;
let placeholderPos = - 1 ;
let length = this . _thumbnails . length ;
for ( let i = 0 ; i < length ; i ++ ) {
const index = rtl ? length - i - 1 : i ;
if ( canCreateWorkspaces && source !== Main . xdndHandler ) {
const [ targetStart , targetEnd ] =
this . _getPlaceholderTarget ( index , spacing , rtl ) ;
2025-02-09 23:10:52 +01:00
if ( x > targetStart && x <= targetEnd ) {
2025-02-09 23:08:39 +01:00
placeholderPos = index ;
break ;
}
}
2025-02-09 23:10:52 +01:00
if ( this . _withinWorkspace ( x , index , rtl ) ) {
2025-02-09 23:08:39 +01:00
this . _dropWorkspace = index ;
break ;
}
}
2025-02-09 23:10:52 +01:00
if ( this . _dropPlaceholderPos !== placeholderPos ) {
2025-02-09 23:08:39 +01:00
this . _dropPlaceholderPos = placeholderPos ;
this . queue _relayout ( ) ;
}
2025-02-09 23:10:52 +01:00
if ( this . _dropWorkspace !== - 1 )
2025-02-09 23:08:39 +01:00
return this . _thumbnails [ this . _dropWorkspace ] . handleDragOverInternal ( source , actor , time ) ;
2025-02-09 23:10:52 +01:00
else if ( this . _dropPlaceholderPos !== - 1 )
2025-02-09 23:08:39 +01:00
return source . metaWindow ? DND . DragMotionResult . MOVE _DROP : DND . DragMotionResult . COPY _DROP ;
else
return DND . DragMotionResult . CONTINUE ;
} ,
2025-02-09 23:10:52 +01:00
_getPlaceholderTarget ( ... args ) {
if ( this . _boxOrientation )
return ThumbnailsBoxVertical . _getPlaceholderTarget . bind ( this ) ( ... args ) ;
else
return ThumbnailsBoxHorizontal . _getPlaceholderTarget . bind ( this ) ( ... args ) ;
} ,
_withinWorkspace ( ... args ) {
if ( this . _boxOrientation )
return ThumbnailsBoxVertical . _withinWorkspace . bind ( this ) ( ... args ) ;
else
return ThumbnailsBoxHorizontal . _withinWorkspace . bind ( this ) ( ... args ) ;
} ,
get _preferred _custom _width ( ... args ) {
if ( this . _boxOrientation )
return ThumbnailsBoxVertical . get _preferred _custom _width . bind ( this ) ( ... args ) ;
else
return ThumbnailsBoxHorizontal . get _preferred _custom _width . bind ( this ) ( ... args ) ;
} ,
get _preferred _custom _height ( ... args ) {
if ( this . _boxOrientation )
return ThumbnailsBoxVertical . get _preferred _custom _height . bind ( this ) ( ... args ) ;
else
return ThumbnailsBoxHorizontal . get _preferred _custom _height . bind ( this ) ( ... args ) ;
} ,
vfunc _allocate ( ... args ) {
if ( this . _boxOrientation )
return ThumbnailsBoxVertical . vfunc _allocate . bind ( this ) ( ... args ) ;
else
return ThumbnailsBoxHorizontal . vfunc _allocate . bind ( this ) ( ... args ) ;
} ,
_updateShouldShow ( ... args ) {
if ( this . _boxOrientation )
return ThumbnailsBoxVertical . _updateShouldShow . bind ( this ) ( ... args ) ;
else
return ThumbnailsBoxHorizontal . _updateShouldShow . bind ( this ) ( ... args ) ;
} ,
} ;
const ThumbnailsBoxVertical = {
_getPlaceholderTarget ( index , spacing , rtl ) {
this . _dropPlaceholder . add _style _class _name ( 'placeholder-vertical' ) ;
const workspace = this . _thumbnails [ index ] ;
let targetY1 ;
let targetY2 ;
if ( rtl ) {
const baseY = workspace . y + workspace . height ;
targetY1 = baseY - WORKSPACE _CUT _SIZE ;
targetY2 = baseY + spacing + WORKSPACE _CUT _SIZE ;
} else {
targetY1 = workspace . y - spacing - WORKSPACE _CUT _SIZE ;
targetY2 = workspace . y + WORKSPACE _CUT _SIZE ;
}
if ( index === 0 ) {
if ( rtl )
targetY2 -= spacing + WORKSPACE _CUT _SIZE ;
else
targetY1 += spacing + WORKSPACE _CUT _SIZE ;
}
if ( index === this . _dropPlaceholderPos ) {
const placeholderHeight = this . _dropPlaceholder . get _height ( ) + spacing ;
if ( rtl )
targetY2 += placeholderHeight ;
else
targetY1 -= placeholderHeight ;
}
return [ targetY1 , targetY2 ] ;
} ,
_withinWorkspace ( y , index , rtl ) {
const length = this . _thumbnails . length ;
const workspace = this . _thumbnails [ index ] ;
let workspaceY1 = workspace . y + WORKSPACE _CUT _SIZE ;
let workspaceY2 = workspace . y + workspace . height - WORKSPACE _CUT _SIZE ;
if ( index === length - 1 ) {
if ( rtl )
workspaceY1 -= WORKSPACE _CUT _SIZE ;
else
workspaceY2 += WORKSPACE _CUT _SIZE ;
}
return y > workspaceY1 && y <= workspaceY2 ;
} ,
// vfunc_get_preferred_width: function(forHeight) {
2025-02-09 23:08:39 +01:00
// override of this vfunc doesn't work for some reason (tested on Ubuntu and Fedora), it's not reachable
2025-02-09 23:10:52 +01:00
get _preferred _custom _width ( forHeight ) {
if ( ! this . visible )
return [ 0 , 0 ] ;
2025-02-09 23:08:39 +01:00
if ( forHeight === - 1 )
return this . get _preferred _custom _height ( forHeight ) ;
let themeNode = this . get _theme _node ( ) ;
forHeight = themeNode . adjust _for _width ( forHeight ) ;
let spacing = themeNode . get _length ( 'spacing' ) ;
let nWorkspaces = this . _thumbnails . length ;
let totalSpacing = ( nWorkspaces - 1 ) * spacing ;
const avail = forHeight - totalSpacing ;
let scale = ( avail / nWorkspaces ) / this . _porthole . height ;
2025-02-09 23:10:52 +01:00
// scale = Math.min(scale, opt.MAX_THUMBNAIL_SCALE);
2025-02-09 23:08:39 +01:00
const width = Math . round ( this . _porthole . width * scale ) ;
2025-02-09 23:10:52 +01:00
return themeNode . adjust _preferred _height ( width , width ) ;
2025-02-09 23:08:39 +01:00
} ,
2025-02-09 23:10:52 +01:00
get _preferred _custom _height ( _forWidth ) {
if ( ! this . visible )
return [ 0 , 0 ] ;
2025-02-09 23:08:39 +01:00
// Note that for getPreferredHeight/Width we cheat a bit and skip propagating
// the size request to our children because we know how big they are and know
// that the actors aren't depending on the virtual functions being called.
let themeNode = this . get _theme _node ( ) ;
let spacing = themeNode . get _length ( 'spacing' ) ;
let nWorkspaces = this . _thumbnails . length ;
2025-02-09 23:10:52 +01:00
// remove also top/bottom box padding
let totalSpacing = ( nWorkspaces - 3 ) * spacing ;
2025-02-09 23:08:39 +01:00
const ratio = this . _porthole . width / this . _porthole . height ;
2025-02-09 23:10:52 +01:00
const tmbHeight = themeNode . adjust _for _width ( _forWidth ) / ratio ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
const naturalheight = this . _thumbnails . reduce ( ( accumulator , thumbnail /* , index*/ ) => {
2025-02-09 23:08:39 +01:00
const progress = 1 - thumbnail . collapse _fraction ;
2025-02-09 23:10:52 +01:00
const height = tmbHeight * progress ;
2025-02-09 23:08:39 +01:00
return accumulator + height ;
} , 0 ) ;
2025-02-09 23:10:52 +01:00
return themeNode . adjust _preferred _width ( totalSpacing , naturalheight ) ;
2025-02-09 23:08:39 +01:00
} ,
// removes extra space (extraWidth in the original function), we need the box as accurate as possible
// for precise app grid transition animation
2025-02-09 23:10:52 +01:00
vfunc _allocate ( box ) {
2025-02-09 23:08:39 +01:00
this . set _allocation ( box ) ;
2025-02-09 23:10:52 +01:00
let rtl = Clutter . get _default _text _direction ( ) === Clutter . TextDirection . RTL ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
if ( this . _thumbnails . length === 0 ) // not visible
2025-02-09 23:08:39 +01:00
return ;
let themeNode = this . get _theme _node ( ) ;
box = themeNode . get _content _box ( box ) ;
const portholeWidth = this . _porthole . width ;
const portholeHeight = this . _porthole . height ;
const spacing = themeNode . get _length ( 'spacing' ) ;
2025-02-09 23:10:52 +01:00
/* const nWorkspaces = this._thumbnails.length;*/
2025-02-09 23:08:39 +01:00
// Compute the scale we'll need once everything is updated,
// unless we are currently transitioning
if ( this . _expandFraction === 1 ) {
2025-02-09 23:10:52 +01:00
// remove size "breathing" during adding/removing workspaces
/ * c o n s t t o t a l S p a c i n g = ( n W o r k s p a c e s - 1 ) * s p a c i n g ;
const availableHeight = ( box . get _height ( ) - totalSpacing ) / nWorkspaces ; * /
2025-02-09 23:08:39 +01:00
const hScale = box . get _width ( ) / portholeWidth ;
2025-02-09 23:10:52 +01:00
/* const vScale = availableHeight / portholeHeight;*/
const vScale = box . get _height ( ) / portholeHeight ;
2025-02-09 23:08:39 +01:00
const newScale = Math . min ( hScale , vScale ) ;
if ( newScale !== this . _targetScale ) {
if ( this . _targetScale > 0 ) {
// We don't ease immediately because we need to observe the
// ordering in queueUpdateStates - if workspaces have been
// removed we need to slide them out as the first thing.
this . _targetScale = newScale ;
this . _pendingScaleUpdate = true ;
} else {
this . _targetScale = this . _scale = newScale ;
}
this . _queueUpdateStates ( ) ;
}
}
const ratio = portholeWidth / portholeHeight ;
const thumbnailFullHeight = Math . round ( portholeHeight * this . _scale ) ;
const thumbnailWidth = Math . round ( thumbnailFullHeight * ratio ) ;
const thumbnailHeight = thumbnailFullHeight * this . _expandFraction ;
const roundedVScale = thumbnailHeight / portholeHeight ;
let indicatorValue = this . _scrollAdjustment . value ;
let indicatorUpperWs = Math . ceil ( indicatorValue ) ;
let indicatorLowerWs = Math . floor ( indicatorValue ) ;
let indicatorLowerY1 = 0 ;
let indicatorLowerY2 = 0 ;
let indicatorUpperY1 = 0 ;
let indicatorUpperY2 = 0 ;
let indicatorThemeNode = this . _indicator . get _theme _node ( ) ;
let indicatorTopFullBorder = indicatorThemeNode . get _padding ( St . Side . TOP ) + indicatorThemeNode . get _border _width ( St . Side . TOP ) ;
let indicatorBottomFullBorder = indicatorThemeNode . get _padding ( St . Side . BOTTOM ) + indicatorThemeNode . get _border _width ( St . Side . BOTTOM ) ;
let indicatorLeftFullBorder = indicatorThemeNode . get _padding ( St . Side . LEFT ) + indicatorThemeNode . get _border _width ( St . Side . LEFT ) ;
let indicatorRightFullBorder = indicatorThemeNode . get _padding ( St . Side . RIGHT ) + indicatorThemeNode . get _border _width ( St . Side . RIGHT ) ;
let y = box . y1 ;
2025-02-09 23:10:52 +01:00
if ( this . _dropPlaceholderPos === - 1 ) {
2025-02-09 23:08:39 +01:00
this . _dropPlaceholder . allocate _preferred _size (
... this . _dropPlaceholder . get _position ( ) ) ;
2025-02-09 23:10:52 +01:00
if ( shellVersion >= 44 ) {
const laters = global . compositor . get _laters ( ) ;
laters . add ( Meta . LaterType . BEFORE _REDRAW , ( ) => {
this . _dropPlaceholder . hide ( ) ;
} ) ;
} else {
Meta . later _add ( Meta . LaterType . BEFORE _REDRAW , ( ) => {
this . _dropPlaceholder . hide ( ) ;
} ) ;
}
2025-02-09 23:08:39 +01:00
}
let childBox = new Clutter . ActorBox ( ) ;
for ( let i = 0 ; i < this . _thumbnails . length ; i ++ ) {
const thumbnail = this . _thumbnails [ i ] ;
if ( i > 0 )
y += spacing - Math . round ( thumbnail . collapse _fraction * spacing ) ;
const x1 = box . x1 ;
const x2 = x1 + thumbnailWidth ;
if ( i === this . _dropPlaceholderPos ) {
2025-02-09 23:10:52 +01:00
let [ , placeholderHeight ] = this . _dropPlaceholder . get _preferred _width ( - 1 ) ;
2025-02-09 23:08:39 +01:00
childBox . x1 = x1 ;
childBox . x2 = x2 ;
if ( rtl ) {
childBox . y2 = box . y2 - Math . round ( y ) ;
childBox . y1 = box . y2 - Math . round ( y + placeholderHeight ) ;
} else {
childBox . y1 = Math . round ( y ) ;
childBox . y2 = Math . round ( y + placeholderHeight ) ;
}
this . _dropPlaceholder . allocate ( childBox ) ;
2025-02-09 23:10:52 +01:00
if ( shellVersion >= 44 ) {
const laters = global . compositor . get _laters ( ) ;
laters . add ( Meta . LaterType . BEFORE _REDRAW , ( ) => {
this . _dropPlaceholder . show ( ) ;
} ) ;
} else {
Meta . later _add ( Meta . LaterType . BEFORE _REDRAW , ( ) => {
this . _dropPlaceholder . show ( ) ;
} ) ;
}
2025-02-09 23:08:39 +01:00
y += placeholderHeight + spacing ;
}
// We might end up with thumbnailWidth being something like 99.33
// pixels. To make this work and not end up with a gap at the end,
// we need some thumbnails to be 99 pixels and some 100 pixels width;
// we compute an actual scale separately for each thumbnail.
const y1 = Math . round ( y ) ;
const y2 = Math . round ( y + thumbnailHeight ) ;
const roundedHScale = ( y2 - y1 ) / portholeHeight ;
// Allocating a scaled actor is funny - x1/y1 correspond to the origin
// of the actor, but x2/y2 are increased by the *unscaled* size.
if ( rtl ) {
childBox . y2 = box . y2 - y1 ;
childBox . y1 = box . y2 - ( y1 + thumbnailHeight ) ;
} else {
childBox . y1 = y1 ;
childBox . y2 = y1 + thumbnailHeight ;
}
childBox . x1 = x1 ;
childBox . x2 = x1 + thumbnailWidth ;
thumbnail . setScale ( roundedHScale , roundedVScale ) ;
thumbnail . allocate ( childBox ) ;
if ( i === indicatorUpperWs ) {
indicatorUpperY1 = childBox . y1 ;
indicatorUpperY2 = childBox . y2 ;
}
if ( i === indicatorLowerWs ) {
indicatorLowerY1 = childBox . y1 ;
indicatorLowerY2 = childBox . y2 ;
}
// We round the collapsing portion so that we don't get thumbnails resizing
// during an animation due to differences in rounded, but leave the uncollapsed
// portion unrounded so that non-animating we end up with the right total
y += thumbnailHeight - Math . round ( thumbnailHeight * thumbnail . collapse _fraction ) ;
}
childBox . x1 = box . x1 ;
childBox . x2 = box . x1 + thumbnailWidth ;
const indicatorY1 = indicatorLowerY1 +
( indicatorUpperY1 - indicatorLowerY1 ) * ( indicatorValue % 1 ) ;
const indicatorY2 = indicatorLowerY2 +
( indicatorUpperY2 - indicatorLowerY2 ) * ( indicatorValue % 1 ) ;
childBox . y1 = indicatorY1 - indicatorTopFullBorder ;
childBox . y2 = indicatorY2 + indicatorBottomFullBorder ;
childBox . x1 -= indicatorLeftFullBorder ;
childBox . x2 += indicatorRightFullBorder ;
this . _indicator . allocate ( childBox ) ;
} ,
2025-02-09 23:10:52 +01:00
_updateShouldShow ( ) {
2025-02-09 23:08:39 +01:00
const shouldShow = opt . SHOW _WS _TMB ;
if ( this . _shouldShow === shouldShow )
return ;
this . _shouldShow = shouldShow ;
this . notify ( 'should-show' ) ;
2025-02-09 23:10:52 +01:00
} ,
} ;
2025-02-09 23:08:39 +01:00
// ThumbnailsBox Horizontal
2025-02-09 23:10:52 +01:00
const ThumbnailsBoxHorizontal = {
_getPlaceholderTarget ( index , spacing , rtl ) {
const workspace = this . _thumbnails [ index ] ;
let targetX1 ;
let targetX2 ;
if ( rtl ) {
const baseX = workspace . x + workspace . width ;
targetX1 = baseX - WORKSPACE _CUT _SIZE ;
targetX2 = baseX + spacing + WORKSPACE _CUT _SIZE ;
} else {
targetX1 = workspace . x - spacing - WORKSPACE _CUT _SIZE ;
targetX2 = workspace . x + WORKSPACE _CUT _SIZE ;
}
if ( index === 0 ) {
if ( rtl )
targetX2 -= spacing + WORKSPACE _CUT _SIZE ;
else
targetX1 += spacing + WORKSPACE _CUT _SIZE ;
}
if ( index === this . _dropPlaceholderPos ) {
const placeholderWidth = this . _dropPlaceholder . get _width ( ) + spacing ;
if ( rtl )
targetX2 += placeholderWidth ;
else
targetX1 -= placeholderWidth ;
}
return [ targetX1 , targetX2 ] ;
} ,
_withinWorkspace ( x , index , rtl ) {
const length = this . _thumbnails . length ;
const workspace = this . _thumbnails [ index ] ;
let workspaceX1 = workspace . x + WORKSPACE _CUT _SIZE ;
let workspaceX2 = workspace . x + workspace . width - WORKSPACE _CUT _SIZE ;
if ( index === length - 1 ) {
if ( rtl )
workspaceX1 -= WORKSPACE _CUT _SIZE ;
else
workspaceX2 += WORKSPACE _CUT _SIZE ;
}
return x > workspaceX1 && x <= workspaceX2 ;
} ,
get _preferred _custom _height ( forWidth ) {
let themeNode = this . get _theme _node ( ) ;
forWidth = themeNode . adjust _for _width ( forWidth ) ;
let spacing = themeNode . get _length ( 'spacing' ) ;
let nWorkspaces = this . _thumbnails . length ;
let totalSpacing = ( nWorkspaces - 1 ) * spacing ;
const avail = forWidth - totalSpacing ;
let scale = ( avail / nWorkspaces ) / this . _porthole . width ;
// scale = Math.min(scale, opt.MAX_THUMBNAIL_SCALE);
const height = Math . round ( this . _porthole . height * scale ) ;
return themeNode . adjust _preferred _height ( height , height ) ;
} ,
get _preferred _custom _width ( _forHeight ) {
2025-02-09 23:08:39 +01:00
// Note that for getPreferredHeight/Width we cheat a bit and skip propagating
// the size request to our children because we know how big they are and know
// that the actors aren't depending on the virtual functions being called.
2025-02-09 23:10:52 +01:00
if ( ! this . visible )
return [ 0 , 0 ] ;
2025-02-09 23:08:39 +01:00
let themeNode = this . get _theme _node ( ) ;
let spacing = themeNode . get _length ( 'spacing' ) ;
let nWorkspaces = this . _thumbnails . length ;
2025-02-09 23:10:52 +01:00
// remove also left/right box padding from the total spacing
let totalSpacing = ( nWorkspaces - 3 ) * spacing ;
2025-02-09 23:08:39 +01:00
const ratio = this . _porthole . height / this . _porthole . width ;
2025-02-09 23:10:52 +01:00
const tmbWidth = themeNode . adjust _for _height ( _forHeight ) / ratio ;
const naturalWidth = this . _thumbnails . reduce ( ( accumulator , thumbnail ) => {
2025-02-09 23:08:39 +01:00
const progress = 1 - thumbnail . collapse _fraction ;
const width = tmbWidth * progress ;
return accumulator + width ;
} , 0 ) ;
return themeNode . adjust _preferred _width ( totalSpacing , naturalWidth ) ;
} ,
vfunc _allocate ( box ) {
this . set _allocation ( box ) ;
2025-02-09 23:10:52 +01:00
let rtl = Clutter . get _default _text _direction ( ) === Clutter . TextDirection . RTL ;
2025-02-09 23:08:39 +01:00
2025-02-09 23:10:52 +01:00
if ( this . _thumbnails . length === 0 ) // not visible
2025-02-09 23:08:39 +01:00
return ;
let themeNode = this . get _theme _node ( ) ;
box = themeNode . get _content _box ( box ) ;
const portholeWidth = this . _porthole . width ;
const portholeHeight = this . _porthole . height ;
const spacing = themeNode . get _length ( 'spacing' ) ;
2025-02-09 23:10:52 +01:00
/* const nWorkspaces = this._thumbnails.length; */
2025-02-09 23:08:39 +01:00
// Compute the scale we'll need once everything is updated,
// unless we are currently transitioning
if ( this . _expandFraction === 1 ) {
2025-02-09 23:10:52 +01:00
// remove size "breathing" during adding/removing workspaces
/ * c o n s t t o t a l S p a c i n g = ( n W o r k s p a c e s - 1 ) * s p a c i n g ;
2025-02-09 23:08:39 +01:00
const availableWidth = ( box . get _width ( ) - totalSpacing ) / nWorkspaces ;
2025-02-09 23:10:52 +01:00
const hScale = availableWidth / portholeWidth ; * /
const hScale = box . get _width ( ) / portholeWidth ;
2025-02-09 23:08:39 +01:00
const vScale = box . get _height ( ) / portholeHeight ;
const newScale = Math . min ( hScale , vScale ) ;
if ( newScale !== this . _targetScale ) {
if ( this . _targetScale > 0 ) {
// We don't ease immediately because we need to observe the
// ordering in queueUpdateStates - if workspaces have been
// removed we need to slide them out as the first thing.
this . _targetScale = newScale ;
this . _pendingScaleUpdate = true ;
} else {
this . _targetScale = this . _scale = newScale ;
}
this . _queueUpdateStates ( ) ;
}
}
const ratio = portholeWidth / portholeHeight ;
const thumbnailFullHeight = Math . round ( portholeHeight * this . _scale ) ;
const thumbnailWidth = Math . round ( thumbnailFullHeight * ratio ) ;
const thumbnailHeight = thumbnailFullHeight * this . _expandFraction ;
const roundedVScale = thumbnailHeight / portholeHeight ;
let indicatorValue = this . _scrollAdjustment . value ;
let indicatorUpperWs = Math . ceil ( indicatorValue ) ;
let indicatorLowerWs = Math . floor ( indicatorValue ) ;
let indicatorLowerX1 = 0 ;
let indicatorLowerX2 = 0 ;
let indicatorUpperX1 = 0 ;
let indicatorUpperX2 = 0 ;
let indicatorThemeNode = this . _indicator . get _theme _node ( ) ;
let indicatorTopFullBorder = indicatorThemeNode . get _padding ( St . Side . TOP ) + indicatorThemeNode . get _border _width ( St . Side . TOP ) ;
let indicatorBottomFullBorder = indicatorThemeNode . get _padding ( St . Side . BOTTOM ) + indicatorThemeNode . get _border _width ( St . Side . BOTTOM ) ;
let indicatorLeftFullBorder = indicatorThemeNode . get _padding ( St . Side . LEFT ) + indicatorThemeNode . get _border _width ( St . Side . LEFT ) ;
let indicatorRightFullBorder = indicatorThemeNode . get _padding ( St . Side . RIGHT ) + indicatorThemeNode . get _border _width ( St . Side . RIGHT ) ;
let x = box . x1 ;
2025-02-09 23:10:52 +01:00
if ( this . _dropPlaceholderPos === - 1 ) {
2025-02-09 23:08:39 +01:00
this . _dropPlaceholder . allocate _preferred _size (
... this . _dropPlaceholder . get _position ( ) ) ;
2025-02-09 23:10:52 +01:00
if ( shellVersion >= 44 ) {
const laters = global . compositor . get _laters ( ) ;
laters . add ( Meta . LaterType . BEFORE _REDRAW , ( ) => {
this . _dropPlaceholder . hide ( ) ;
} ) ;
} else {
Meta . later _add ( Meta . LaterType . BEFORE _REDRAW , ( ) => {
this . _dropPlaceholder . hide ( ) ;
} ) ;
}
2025-02-09 23:08:39 +01:00
}
let childBox = new Clutter . ActorBox ( ) ;
for ( let i = 0 ; i < this . _thumbnails . length ; i ++ ) {
const thumbnail = this . _thumbnails [ i ] ;
if ( i > 0 )
x += spacing - Math . round ( thumbnail . collapse _fraction * spacing ) ;
const y1 = box . y1 ;
const y2 = y1 + thumbnailHeight ;
if ( i === this . _dropPlaceholderPos ) {
const [ , placeholderWidth ] = this . _dropPlaceholder . get _preferred _width ( - 1 ) ;
childBox . y1 = y1 ;
childBox . y2 = y2 ;
if ( rtl ) {
childBox . x2 = box . x2 - Math . round ( x ) ;
childBox . x1 = box . x2 - Math . round ( x + placeholderWidth ) ;
} else {
childBox . x1 = Math . round ( x ) ;
childBox . x2 = Math . round ( x + placeholderWidth ) ;
}
this . _dropPlaceholder . allocate ( childBox ) ;
2025-02-09 23:10:52 +01:00
if ( shellVersion >= 44 ) {
const laters = global . compositor . get _laters ( ) ;
laters . add ( Meta . LaterType . BEFORE _REDRAW , ( ) => {
this . _dropPlaceholder . show ( ) ;
} ) ;
} else {
Meta . later _add ( Meta . LaterType . BEFORE _REDRAW , ( ) => {
this . _dropPlaceholder . show ( ) ;
} ) ;
}
2025-02-09 23:08:39 +01:00
x += placeholderWidth + spacing ;
}
// We might end up with thumbnailWidth being something like 99.33
// pixels. To make this work and not end up with a gap at the end,
// we need some thumbnails to be 99 pixels and some 100 pixels width;
// we compute an actual scale separately for each thumbnail.
const x1 = Math . round ( x ) ;
const x2 = Math . round ( x + thumbnailWidth ) ;
const roundedHScale = ( x2 - x1 ) / portholeWidth ;
// Allocating a scaled actor is funny - x1/y1 correspond to the origin
// of the actor, but x2/y2 are increased by the *unscaled* size.
if ( rtl ) {
childBox . x2 = box . x2 - x1 ;
childBox . x1 = box . x2 - ( x1 + thumbnailWidth ) ;
} else {
childBox . x1 = x1 ;
childBox . x2 = x1 + thumbnailWidth ;
}
childBox . y1 = y1 ;
childBox . y2 = y1 + thumbnailHeight ;
thumbnail . setScale ( roundedHScale , roundedVScale ) ;
thumbnail . allocate ( childBox ) ;
if ( i === indicatorUpperWs ) {
indicatorUpperX1 = childBox . x1 ;
indicatorUpperX2 = childBox . x2 ;
}
if ( i === indicatorLowerWs ) {
indicatorLowerX1 = childBox . x1 ;
indicatorLowerX2 = childBox . x2 ;
}
// We round the collapsing portion so that we don't get thumbnails resizing
// during an animation due to differences in rounded, but leave the uncollapsed
// portion unrounded so that non-animating we end up with the right total
x += thumbnailWidth - Math . round ( thumbnailWidth * thumbnail . collapse _fraction ) ;
}
childBox . y1 = box . y1 ;
childBox . y2 = box . y1 + thumbnailHeight ;
const indicatorX1 = indicatorLowerX1 +
( indicatorUpperX1 - indicatorLowerX1 ) * ( indicatorValue % 1 ) ;
const indicatorX2 = indicatorLowerX2 +
( indicatorUpperX2 - indicatorLowerX2 ) * ( indicatorValue % 1 ) ;
childBox . x1 = indicatorX1 - indicatorLeftFullBorder ;
childBox . x2 = indicatorX2 + indicatorRightFullBorder ;
childBox . y1 -= indicatorTopFullBorder ;
childBox . y2 += indicatorBottomFullBorder ;
this . _indicator . allocate ( childBox ) ;
} ,
2025-02-09 23:10:52 +01:00
_updateShouldShow : ThumbnailsBoxVertical . _updateShouldShow ,
} ;