diff options
Diffstat (limited to 'extensions/45/vertical-workspaces/lib/windowPreview.js')
-rw-r--r-- | extensions/45/vertical-workspaces/lib/windowPreview.js | 619 |
1 files changed, 0 insertions, 619 deletions
diff --git a/extensions/45/vertical-workspaces/lib/windowPreview.js b/extensions/45/vertical-workspaces/lib/windowPreview.js deleted file mode 100644 index 3253aa5..0000000 --- a/extensions/45/vertical-workspaces/lib/windowPreview.js +++ /dev/null @@ -1,619 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * windowPreview.js - * - * @author GdH <G-dH@github.com> - * @copyright 2022 - 2024 - * @license GPL-3.0 - * - */ - -'use strict'; - -import GLib from 'gi://GLib'; -import Clutter from 'gi://Clutter'; -import St from 'gi://St'; -import Meta from 'gi://Meta'; -import Shell from 'gi://Shell'; -import Pango from 'gi://Pango'; -import Graphene from 'gi://Graphene'; -import Atk from 'gi://Atk'; - -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; -import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js'; -import * as WindowPreview from 'resource:///org/gnome/shell/ui/windowPreview.js'; - -let Me; -let opt; - -const WINDOW_SCALE_TIME = 200; -const WINDOW_ACTIVE_SIZE_INC = 5; -const WINDOW_OVERLAY_FADE_TIME = 200; -const WINDOW_DND_SIZE = 256; -const DRAGGING_WINDOW_OPACITY = 100; - -const ControlsState = OverviewControls.ControlsState; - -export const WindowPreviewModule = class { - constructor(me) { - Me = me; - opt = Me.opt; - - this._firstActivation = true; - this.moduleEnabled = false; - this._overrides = null; - } - - cleanGlobals() { - Me = null; - opt = null; - } - - update(reset) { - this.moduleEnabled = opt.get('windowPreviewModule'); - const conflict = false; - - reset = reset || !this.moduleEnabled || conflict; - - // don't touch the original code if module disabled - if (reset && !this._firstActivation) { - this._disableModule(); - } else if (!reset) { - this._firstActivation = false; - this._activateModule(); - } - if (reset && this._firstActivation) - console.debug(' WindowPreviewModule - Keeping untouched'); - } - - _activateModule() { - if (!this._overrides) - this._overrides = new Me.Util.Overrides(); - - this._overrides.addOverride('WindowPreview', WindowPreview.WindowPreview.prototype, WindowPreviewCommon); - // A shorter timeout allows user to quickly cancel the selection by leaving the preview with the mouse pointer - // if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW) - // WindowPreview.WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT = 150; // incompatible - console.debug(' WindowPreviewModule - Activated'); - } - - _disableModule() { - // If WindowPreview._init was injected by another extension (like Burn My Windows) - // which enables/disables before V-Shell - // don't restore the original if it's not injected, - // because it would restore injected _init and recursion would freeze GS when extensions are enabled again. - // This can happen when all extension re-enabled, not only when screen is locked/unlocked - // If _init doesn't include "fn.apply(this, args)" when reset === true, some extension already restored the original - const skipReset = WindowPreview.WindowPreview.prototype._init.toString().includes('fn.apply(this, args)'); - if (this._overrides && skipReset) { - // skip restoring original _init() - this._overrides['_init'] = null; - } - - if (this._overrides) - this._overrides.removeAll(); - - this._overrides = null; - - console.debug(' WindowPreviewModule - Disabled'); - } -}; - -const WindowPreviewCommon = { - _init(metaWindow, workspace, overviewAdjustment) { - this.metaWindow = metaWindow; - this.metaWindow._delegate = this; - this._windowActor = metaWindow.get_compositor_private(); - this._workspace = workspace; - this._overviewAdjustment = overviewAdjustment; - - const ICON_SIZE = opt.WIN_PREVIEW_ICON_SIZE; - const ICON_OVERLAP = 0.7; - - Shell.WindowPreview.prototype._init.bind(this)({ - reactive: true, - can_focus: true, - accessible_role: Atk.Role.PUSH_BUTTON, - offscreen_redirect: Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY, - }); - - const windowContainer = new Clutter.Actor({ - pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), - }); - this.window_container = windowContainer; - - windowContainer.connect('notify::scale-x', - () => this._adjustOverlayOffsets()); - // gjs currently can't handle setting an actors layout manager during - // the initialization of the actor if that layout manager keeps track - // of its container, so set the layout manager after creating the - // container - windowContainer.layout_manager = new Shell.WindowPreviewLayout(); - this.add_child(windowContainer); - - this._addWindow(metaWindow); - - this._delegate = this; - - this._stackAbove = null; - - this._cachedBoundingBox = { - x: windowContainer.layout_manager.bounding_box.x1, - y: windowContainer.layout_manager.bounding_box.y1, - width: windowContainer.layout_manager.bounding_box.get_width(), - height: windowContainer.layout_manager.bounding_box.get_height(), - }; - - windowContainer.layout_manager.connect( - 'notify::bounding-box', layout => { - this._cachedBoundingBox = { - x: layout.bounding_box.x1, - y: layout.bounding_box.y1, - width: layout.bounding_box.get_width(), - height: layout.bounding_box.get_height(), - }; - - // A bounding box of 0x0 means all windows were removed - if (layout.bounding_box.get_area() > 0) - this.emit('size-changed'); - }); - - this._windowActor.connectObject('destroy', () => this.destroy(), this); - - this._updateAttachedDialogs(); - - let clickAction = new Clutter.ClickAction(); - clickAction.connect('clicked', act => { - const button = act.get_button(); - if (button === Clutter.BUTTON_SECONDARY) { - if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 1) { - this._closeWinAction(); - return Clutter.EVENT_STOP; - } else if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 2) { - this._searchAppWindowsAction(); - return Clutter.EVENT_STOP; - } else if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 3 && global.windowThumbnails) { - this._removeLaters(); - global.windowThumbnails?.createThumbnail(metaWindow); - return Clutter.EVENT_STOP; - } - } else if (button === Clutter.BUTTON_MIDDLE) { - if (opt.WIN_PREVIEW_MID_BTN_ACTION === 1) { - this._closeWinAction(); - return Clutter.EVENT_STOP; - } else if (opt.WIN_PREVIEW_MID_BTN_ACTION === 2) { - this._searchAppWindowsAction(); - return Clutter.EVENT_STOP; - } else if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 3 && global.windowThumbnails) { - this._removeLaters(); - global.windowThumbnails?.createThumbnail(metaWindow); - return Clutter.EVENT_STOP; - } - } - return this._activate(); - }); - - - if (this._onLongPress) { - clickAction.connect('long-press', this._onLongPress.bind(this)); - } else { - clickAction.connect('long-press', (action, actor, state) => { - if (state === Clutter.LongPressState.ACTIVATE) - this.showOverlay(true); - return true; - }); - } - - this.connect('destroy', this._onDestroy.bind(this)); - - this._draggable = DND.makeDraggable(this, { - restoreOnSuccess: true, - manualMode: !!this._onLongPress, - dragActorMaxSize: WINDOW_DND_SIZE, - dragActorOpacity: DRAGGING_WINDOW_OPACITY, - }); - - // _draggable.addClickAction is new in GS45 - if (this._draggable.addClickAction) - this._draggable.addClickAction(clickAction); - else - this.add_action(clickAction); - - this._draggable.connect('drag-begin', this._onDragBegin.bind(this)); - this._draggable.connect('drag-cancelled', this._onDragCancelled.bind(this)); - this._draggable.connect('drag-end', this._onDragEnd.bind(this)); - this.inDrag = false; - - this._selected = false; - this._overlayEnabled = true; - this._overlayShown = false; - this._closeRequested = false; - this._idleHideOverlayId = 0; - - const tracker = Shell.WindowTracker.get_default(); - const app = tracker.get_window_app(this.metaWindow); - this._icon = app.create_icon_texture(ICON_SIZE); - this._icon.add_style_class_name('icon-dropshadow'); - this._icon.set({ - reactive: true, - pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), - }); - this._icon.add_constraint(new Clutter.BindConstraint({ - source: windowContainer, - coordinate: Clutter.BindCoordinate.POSITION, - })); - this._icon.add_constraint(new Clutter.AlignConstraint({ - source: windowContainer, - align_axis: Clutter.AlignAxis.X_AXIS, - factor: 0.5, - })); - this._icon.add_constraint(new Clutter.AlignConstraint({ - source: windowContainer, - align_axis: Clutter.AlignAxis.Y_AXIS, - pivot_point: new Graphene.Point({ x: -1, y: ICON_OVERLAP }), - factor: 1, - })); - - if (opt.WINDOW_ICON_CLICK_ACTION) { - const iconClickAction = new Clutter.ClickAction(); - iconClickAction.connect('clicked', act => { - if (act.get_button() === Clutter.BUTTON_PRIMARY) { - if (opt.WINDOW_ICON_CLICK_ACTION === 1) { - this._searchAppWindowsAction(); - return Clutter.EVENT_STOP; - } else if (opt.WINDOW_ICON_CLICK_ACTION === 2 && global.windowThumbnails) { - this._removeLaters(); - global.windowThumbnails?.createThumbnail(metaWindow); - return Clutter.EVENT_STOP; - } - } /* else if (act.get_button() === Clutter.BUTTON_SECONDARY) { - return Clutter.EVENT_STOP; - }*/ - return Clutter.EVENT_PROPAGATE; - }); - this._icon.add_action(iconClickAction); - } - const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); - this._title = new St.Label({ - visible: false, - style_class: 'window-caption', - text: this._getCaption(), - reactive: true, - }); - this._title.clutter_text.single_line_mode = true; - this._title.add_constraint(new Clutter.BindConstraint({ - source: windowContainer, - coordinate: Clutter.BindCoordinate.X, - })); - - let offset; - if (opt.WIN_TITLES_POSITION < 2) { - // we cannot get proper title height before it gets to the stage, so 35 is estimated height + spacing - offset = -scaleFactor * (ICON_SIZE * ICON_OVERLAP + 35); - } else { - offset = scaleFactor * (ICON_SIZE * (1 - ICON_OVERLAP) + 4); - } - this._title.add_constraint(new Clutter.BindConstraint({ - source: windowContainer, - coordinate: Clutter.BindCoordinate.Y, - offset, - })); - this._title.add_constraint(new Clutter.AlignConstraint({ - source: windowContainer, - align_axis: Clutter.AlignAxis.X_AXIS, - factor: 0.5, - })); - this._title.add_constraint(new Clutter.AlignConstraint({ - source: windowContainer, - align_axis: Clutter.AlignAxis.Y_AXIS, - pivot_point: new Graphene.Point({ x: -1, y: 0 }), - factor: 1, - })); - this._title.clutter_text.ellipsize = Pango.EllipsizeMode.END; - this.label_actor = this._title; - this.metaWindow.connectObject( - 'notify::title', () => (this._title.text = this._getCaption()), - this); - - const layout = Meta.prefs_get_button_layout(); - this._closeButtonSide = - layout.left_buttons.includes(Meta.ButtonFunction.CLOSE) - ? St.Side.LEFT : St.Side.RIGHT; - this._closeButton = new St.Button({ - visible: false, - style_class: 'window-close', - icon_name: 'preview-close-symbolic', - }); - this._closeButton.add_constraint(new Clutter.BindConstraint({ - source: windowContainer, - coordinate: Clutter.BindCoordinate.POSITION, - })); - this._closeButton.add_constraint(new Clutter.AlignConstraint({ - source: windowContainer, - align_axis: Clutter.AlignAxis.X_AXIS, - pivot_point: new Graphene.Point({ x: 0.5, y: -1 }), - factor: this._closeButtonSide === St.Side.LEFT ? 0 : 1, - })); - this._closeButton.add_constraint(new Clutter.AlignConstraint({ - source: windowContainer, - align_axis: Clutter.AlignAxis.Y_AXIS, - pivot_point: new Graphene.Point({ x: -1, y: 0.5 }), - factor: 0, - })); - this._closeButton.connect('clicked', () => this._deleteAll()); - - this.add_child(this._title); - this.add_child(this._icon); - this.add_child(this._closeButton); - - this._overviewAdjustment.connectObject( - 'notify::value', () => this._updateIconScale(), this); - this._updateIconScale(); - - this.connect('notify::realized', () => { - if (!this.realized) - return; - - this._title.ensure_style(); - this._icon.ensure_style(); - }); - - if (ICON_SIZE < 22) { - // disable app icon - this._icon.hide(); - } else { - this._updateIconScale(); - } - - - - // if window is created while the overview is shown, icon and title should be visible immediately - if (Main.overview._overview._controls._stateAdjustment.value < 1) { - this._icon.scale_x = 0; - this._icon.scale_y = 0; - this._title.opacity = 0; - } - - if (opt.ALWAYS_SHOW_WIN_TITLES) - this._title.show(); - - if (opt.OVERVIEW_MODE === 1) { - // spread windows on hover - this._wsStateConId = this.connect('enter-event', () => { - // don't spread windows if user don't use pointer device at this moment - if (global.get_pointer()[0] === opt.showingPointerX || Main.overview._overview._controls._stateAdjustment.value < 1) - return; - - opt.WORKSPACE_MODE = 1; - const view = this._workspace.get_parent(); - view.exposeWindows(this._workspace.metaWorkspace.index()); - this.disconnect(this._wsStateConId); - }); - } - - if (opt.OVERVIEW_MODE) { - // show window icon and title on ws windows spread - this._stateAdjustmentSigId = this._workspace.stateAdjustment.connect('notify::value', this._updateIconScale.bind(this)); - } - - const metaWin = this.metaWindow; - if (opt.DASH_ISOLATE_WS && !metaWin._wsChangedConId) { - metaWin._wsChangedConId = metaWin.connect('workspace-changed', - () => Main.overview.dash._queueRedisplay()); - } else if (!opt.DASH_ISOLATE_WS && metaWin._wsChangedConId) { - metaWin.disconnect(metaWin._wsChangedConId); - } - }, - - _closeWinAction() { - this.hide(); - this._deleteAll(); - }, - - _removeLaters() { - if (this._longPressLater) { - const laters = global.compositor.get_laters(); - laters.remove(this._longPressLater); - delete this._longPressLater; - } - }, - - _searchAppWindowsAction() { - // this action cancels long-press event and the 'long-press-cancel' event is used by the Shell to actually initiate DnD - // so the dnd initiation needs to be removed - this._removeLaters(); - const tracker = Shell.WindowTracker.get_default(); - const appName = tracker.get_window_app(this.metaWindow).get_name(); - Me.Util.activateSearchProvider(`${Me.WSP_PREFIX} ${appName}`); - }, - - _updateIconScale() { - let { currentState, initialState, finalState } = - this._overviewAdjustment.getStateTransitionParams(); - - // Current state - 0 - HIDDEN, 1 - WINDOW_PICKER, 2 - APP_GRID - const primaryMonitor = this.metaWindow.get_monitor() === global.display.get_primary_monitor(); - - const visible = - (initialState > ControlsState.HIDDEN || finalState > ControlsState.HIDDEN) && - !(finalState === ControlsState.APP_GRID && opt.WS_ANIMATION && primaryMonitor); - - let scale = 0; - if (visible) - scale = currentState >= 1 ? 1 : currentState % 1; - - if (!primaryMonitor && opt.WORKSPACE_MODE && - ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || - (initialState === ControlsState.APP_GRID && finalState === ControlsState.WINDOW_PICKER)) - ) - scale = 1; - else if (!primaryMonitor && opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE) - scale = 0; - /* } else if (primaryMonitor && ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || - initialState === ControlsState.APP_GRID && finalState === ControlsState.HIDDEN)) {*/ - else if (primaryMonitor && currentState > ControlsState.WINDOW_PICKER) - scale = 0; - - // in static workspace mode show icon and title on windows expose - if (opt.OVERVIEW_MODE) { - if (currentState === 1) - scale = this._workspace._background._stateAdjustment.value; - else if ((finalState === 1 && !opt.WORKSPACE_MODE) || (finalState === 0 && !opt.WORKSPACE_MODE)) - return; - } - - if (!opt.WS_ANIMATION && (Main.overview.searchController.searchActive || - ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || - (initialState === ControlsState.APP_GRID && finalState === ControlsState.WINDOW_PICKER))) - ) - return; - - // if titles are in 'always show' mode, we need to add transition between visible/invisible state - // but the transition is quite expensive, - // showing the titles at the end of the transition is good enough and workspace preview transition is much smoother - if (scale === 1) { - this._icon.set({ - scale_x: 1, - scale_y: 1, - }); - this._title.ease({ - duration: 100, - opacity: 255, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } else { - this._title.opacity = 0; - this._icon.set({ - scale_x: scale, - scale_y: scale, - }); - } - }, - - showOverlay(animate) { - if (!this._overlayEnabled) - return; - - if (this._overlayShown) - return; - - this._overlayShown = true; - if (opt.WIN_TITLES_POSITION === 2) - this._restack(); - - // If we're supposed to animate and an animation in our direction - // is already happening, let that one continue - const ongoingTransition = this._title.get_transition('opacity'); - if (animate && - ongoingTransition && - ongoingTransition.get_interval().peek_final_value() === 255) - return; - - const toShow = this._windowCanClose() && opt.SHOW_CLOSE_BUTTON - ? [this._closeButton] - : []; - - if (!opt.ALWAYS_SHOW_WIN_TITLES) - toShow.push(this._title); - - - toShow.forEach(a => { - a.opacity = 0; - a.show(); - a.ease({ - opacity: 255, - duration: animate ? WINDOW_OVERLAY_FADE_TIME : 0, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - }); - - const [width, height] = this.window_container.get_size(); - const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); - const activeExtraSize = WINDOW_ACTIVE_SIZE_INC * 2 * scaleFactor; - const origSize = Math.max(width, height); - const scale = (origSize + activeExtraSize) / origSize; - - this.window_container.ease({ - scale_x: scale, - scale_y: scale, - duration: animate ? WINDOW_SCALE_TIME : 0, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - - this.emit('show-chrome'); - }, - - hideOverlay(animate) { - if (!this._overlayShown) - return; - this._overlayShown = false; - - if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW && Main.overview._overview.controls._stateAdjustment.value < 1) - this._activateSelected = true; - - - if (opt.WIN_TITLES_POSITION === 2) - this._restack(); - - // If we're supposed to animate and an animation in our direction - // is already happening, let that one continue - const ongoingTransition = this._title.get_transition('opacity'); - if (animate && - ongoingTransition && - ongoingTransition.get_interval().peek_final_value() === 0) - return; - - const toHide = [this._closeButton]; - - if (!opt.ALWAYS_SHOW_WIN_TITLES) - toHide.push(this._title); - - toHide.forEach(a => { - a.opacity = 255; - a.ease({ - opacity: 0, - duration: animate ? WINDOW_OVERLAY_FADE_TIME : 0, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => a.hide(), - }); - }); - - if (this.window_container) { - this.window_container.ease({ - scale_x: 1, - scale_y: 1, - duration: animate ? WINDOW_SCALE_TIME : 0, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } - }, - - _onDestroy() { - if (this._activateSelected) - this._activate(); - - this.metaWindow._delegate = null; - this._delegate = null; - this._destroyed = true; - - if (this._longPressLater) { - const laters = global.compositor.get_laters(); - laters.remove(this._longPressLater); - delete this._longPressLater; - } - - if (this._idleHideOverlayId > 0) { - GLib.source_remove(this._idleHideOverlayId); - this._idleHideOverlayId = 0; - } - - if (this.inDrag) { - this.emit('drag-end'); - this.inDrag = false; - } - - if (this._stateAdjustmentSigId) - this._workspace.stateAdjustment.disconnect(this._stateAdjustmentSigId); - }, -}; |