From 4a62513b17d915afcae80b1adfb1b0cdcf86f748 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 5 Feb 2023 08:44:04 +0100 Subject: Adding upstream version 20230205. Signed-off-by: Daniel Baumann --- .../multi-monitors-add-on@spin83/mmoverview.js | 678 +++++++++++++++++++++ 1 file changed, 678 insertions(+) create mode 100644 extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmoverview.js (limited to 'extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmoverview.js') diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmoverview.js b/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmoverview.js new file mode 100644 index 0000000..b7555d2 --- /dev/null +++ b/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmoverview.js @@ -0,0 +1,678 @@ +/* +Copyright (C) 2014 spin83 + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, visit https://www.gnu.org/licenses/. +*/ + +const { Clutter, GObject, St, Shell, GLib, Gio, Meta } = imports.gi; + +const Main = imports.ui.main; +const Params = imports.misc.params; +const WorkspaceThumbnail = imports.ui.workspaceThumbnail; +const OverviewControls = imports.ui.overviewControls; +const Overview = imports.ui.overview; +const SearchController = imports.ui.searchController; +const LayoutManager = imports.ui.layout; +const Background = imports.ui.background; +const WorkspacesView = imports.ui.workspacesView; + +const ExtensionUtils = imports.misc.extensionUtils; +const CE = ExtensionUtils.getCurrentExtension(); +const MultiMonitors = CE.imports.extension; +const Convenience = CE.imports.convenience; + +const THUMBNAILS_SLIDER_POSITION_ID = 'thumbnails-slider-position'; + +var MultiMonitorsWorkspaceThumbnail = (() => { + let MultiMonitorsWorkspaceThumbnail = class MultiMonitorsWorkspaceThumbnail extends St.Widget { + _init(metaWorkspace, monitorIndex) { + super._init({ + clip_to_allocation: true, + style_class: 'workspace-thumbnail', + }); + this._delegate = this; + + this.metaWorkspace = metaWorkspace; + this.monitorIndex = monitorIndex; + + this._removed = false; + + this._contents = new Clutter.Actor(); + this.add_child(this._contents); + + this.connect('destroy', this._onDestroy.bind(this)); + + this._createBackground(); + + let workArea = Main.layoutManager.getWorkAreaForMonitor(this.monitorIndex); + this.setPorthole(workArea.x, workArea.y, workArea.width, workArea.height); + + let windows = global.get_window_actors().filter(actor => { + let win = actor.meta_window; + return win.located_on_workspace(metaWorkspace); + }); + + // Create clones for windows that should be visible in the Overview + this._windows = []; + this._allWindows = []; + this._minimizedChangedIds = []; + for (let i = 0; i < windows.length; i++) { + let minimizedChangedId = + windows[i].meta_window.connect('notify::minimized', + this._updateMinimized.bind(this)); + this._allWindows.push(windows[i].meta_window); + this._minimizedChangedIds.push(minimizedChangedId); + + if (this._isMyWindow(windows[i]) && this._isOverviewWindow(windows[i])) + this._addWindowClone(windows[i]); + } + + // Track window changes + this._windowAddedId = this.metaWorkspace.connect('window-added', + this._windowAdded.bind(this)); + this._windowRemovedId = this.metaWorkspace.connect('window-removed', + this._windowRemoved.bind(this)); + this._windowEnteredMonitorId = global.display.connect('window-entered-monitor', + this._windowEnteredMonitor.bind(this)); + this._windowLeftMonitorId = global.display.connect('window-left-monitor', + this._windowLeftMonitor.bind(this)); + + this.state = WorkspaceThumbnail.ThumbnailState.NORMAL; + this._slidePosition = 0; // Fully slid in + this._collapseFraction = 0; // Not collapsed + } + + _createBackground() { + this._bgManager = new Background.BackgroundManager({ monitorIndex: this.monitorIndex, + container: this._contents, + vignette: false }); + }}; + + MultiMonitors.copyClass(WorkspaceThumbnail.WorkspaceThumbnail, MultiMonitorsWorkspaceThumbnail); + return GObject.registerClass({ + Properties: { + 'collapse-fraction': GObject.ParamSpec.double( + 'collapse-fraction', 'collapse-fraction', 'collapse-fraction', + GObject.ParamFlags.READWRITE, + 0, 1, 0), + 'slide-position': GObject.ParamSpec.double( + 'slide-position', 'slide-position', 'slide-position', + GObject.ParamFlags.READWRITE, + 0, 1, 0), + }, + }, MultiMonitorsWorkspaceThumbnail); +})(); + +const MultiMonitorsThumbnailsBox = (() => { + let MultiMonitorsThumbnailsBox = class MultiMonitorsThumbnailsBox extends St.Widget { + _init(scrollAdjustment, monitorIndex) { + + super._init({ reactive: true, + style_class: 'workspace-thumbnails', + request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT }); + + this._delegate = this; + this._monitorIndex = monitorIndex; + + let indicator = new St.Bin({ style_class: 'workspace-thumbnail-indicator' }); + + // We don't want the indicator to affect drag-and-drop + Shell.util_set_hidden_from_pick(indicator, true); + + this._indicator = indicator; + this.add_actor(indicator); + + // The porthole is the part of the screen we're showing in the thumbnails + this._porthole = { width: global.stage.width, height: global.stage.height, + x: global.stage.x, y: global.stage.y }; + + this._dropWorkspace = -1; + this._dropPlaceholderPos = -1; + this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' }); + this.add_actor(this._dropPlaceholder); + this._spliceIndex = -1; + + this._targetScale = 0; + this._scale = 0; + this._pendingScaleUpdate = false; + this._stateUpdateQueued = false; + this._animatingIndicator = false; + + this._stateCounts = {}; + for (let key in WorkspaceThumbnail.ThumbnailState) + this._stateCounts[WorkspaceThumbnail.ThumbnailState[key]] = 0; + + this._thumbnails = []; + + this._showingId = Main.overview.connect('showing', + this._createThumbnails.bind(this)); + this._hiddenId = Main.overview.connect('hidden', + this._destroyThumbnails.bind(this)); + + this._itemDragBeginId = Main.overview.connect('item-drag-begin', + this._onDragBegin.bind(this)); + this._itemDragEndId = Main.overview.connect('item-drag-end', + this._onDragEnd.bind(this)); + this._itemDragCancelledId = Main.overview.connect('item-drag-cancelled', + this._onDragCancelled.bind(this)); + this._windowDragBeginId = Main.overview.connect('window-drag-begin', + this._onDragBegin.bind(this)); + this._windowDragEndId = Main.overview.connect('window-drag-end', + this._onDragEnd.bind(this)); + this._windowDragCancelledId = Main.overview.connect('window-drag-cancelled', + this._onDragCancelled.bind(this)); + + this._settings = new Gio.Settings({ schema_id: WorkspaceThumbnail.MUTTER_SCHEMA }); + this._changedDynamicWorkspacesId = this._settings.connect('changed::dynamic-workspaces', + this._updateSwitcherVisibility.bind(this)); + + this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', () => { + this._destroyThumbnails(); + if (Main.overview.visible) + this._createThumbnails(); + }); + + this._workareasChangedPortholeId = global.display.connect('workareas-changed', + this._updatePorthole.bind(this)); + + this._switchWorkspaceNotifyId = 0; + this._nWorkspacesNotifyId = 0; + this._syncStackingId = 0; + this._workareasChangedId = 0; + + this._scrollAdjustment = scrollAdjustment; + + this._scrollAdjustmentNotifyValueId = this._scrollAdjustment.connect('notify::value', adj => { + let workspaceManager = global.workspace_manager; + let activeIndex = workspaceManager.get_active_workspace_index(); + + this._animatingIndicator = adj.value !== activeIndex; + + if (!this._animatingIndicator) + this._queueUpdateStates(); + + this.queue_relayout(); + }); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + this._destroyThumbnails(); + this._scrollAdjustment.disconnect(this._scrollAdjustmentNotifyValueId); + Main.overview.disconnect(this._showingId); + Main.overview.disconnect(this._hiddenId); + + Main.overview.disconnect(this._itemDragBeginId); + Main.overview.disconnect(this._itemDragEndId); + Main.overview.disconnect(this._itemDragCancelledId); + Main.overview.disconnect(this._windowDragBeginId); + Main.overview.disconnect(this._windowDragEndId); + Main.overview.disconnect(this._windowDragCancelledId); + + this._settings.disconnect(this._changedDynamicWorkspacesId); + Main.layoutManager.disconnect(this._monitorsChangedId); + global.display.disconnect(this._workareasChangedPortholeId); + } + + addThumbnails(start, count) { + let workspaceManager = global.workspace_manager; + + for (let k = start; k < start + count; k++) { + let metaWorkspace = workspaceManager.get_workspace_by_index(k); + let thumbnail = new MultiMonitorsWorkspaceThumbnail(metaWorkspace, this._monitorIndex); + thumbnail.setPorthole(this._porthole.x, this._porthole.y, + this._porthole.width, this._porthole.height); + this._thumbnails.push(thumbnail); + this.add_actor(thumbnail); + + if (start > 0 && this._spliceIndex == -1) { + // not the initial fill, and not splicing via DND + thumbnail.state = WorkspaceThumbnail.ThumbnailState.NEW; + thumbnail.slide_position = 1; // start slid out + this._haveNewThumbnails = true; + } else { + thumbnail.state = WorkspaceThumbnail.ThumbnailState.NORMAL; + } + + this._stateCounts[thumbnail.state]++; + } + + this._queueUpdateStates(); + + // The thumbnails indicator actually needs to be on top of the thumbnails + this.set_child_above_sibling(this._indicator, null); + + // Clear the splice index, we got the message + this._spliceIndex = -1; + } + + _updatePorthole() { + this._porthole = Main.layoutManager.getWorkAreaForMonitor(this._monitorIndex); + this.queue_relayout(); + }}; + + MultiMonitors.copyClass(WorkspaceThumbnail.ThumbnailsBox, MultiMonitorsThumbnailsBox); + return GObject.registerClass({ + Properties: { + 'indicator-y': GObject.ParamSpec.double( + 'indicator-y', 'indicator-y', 'indicator-y', + GObject.ParamFlags.READWRITE, + 0, Infinity, 0), + 'scale': GObject.ParamSpec.double( + 'scale', 'scale', 'scale', + GObject.ParamFlags.READWRITE, + 0, Infinity, 0), + }, + }, MultiMonitorsThumbnailsBox); +})(); + +/* This isn't compatible with GNOME 40 and i don't know how to fix it -- TH +var MultiMonitorsSlidingControl = (() => { + let MultiMonitorsSlidingControl = class MultiMonitorsSlidingControl extends St.Widget { + _init(params) { + params = Params.parse(params, { slideDirection: OverviewControls.SlideDirection.LEFT }); + + this.layout = new OverviewControls.SlideLayout(); + this.layout.slideDirection = params.slideDirection; + super._init({ + layout_manager: this.layout, + style_class: 'overview-controls', + clip_to_allocation: true, + }); + + this._visible = true; + this._inDrag = false; + + this.connect('destroy', this._onDestroy.bind(this)); + this._hidingId = Main.overview.connect('hiding', this._onOverviewHiding.bind(this)); + + this._itemDragBeginId = Main.overview.connect('item-drag-begin', this._onDragBegin.bind(this)); + this._itemDragEndId = Main.overview.connect('item-drag-end', this._onDragEnd.bind(this)); + this._itemDragCancelledId = Main.overview.connect('item-drag-cancelled', this._onDragEnd.bind(this)); + + this._windowDragBeginId = Main.overview.connect('window-drag-begin', this._onWindowDragBegin.bind(this)); + this._windowDragCancelledId = Main.overview.connect('window-drag-cancelled', this._onWindowDragEnd.bind(this)); + this._windowDragEndId = Main.overview.connect('window-drag-end', this._onWindowDragEnd.bind(this)); + } + + _onDestroy() { + Main.overview.disconnect(this._hidingId); + + Main.overview.disconnect(this._itemDragBeginId); + Main.overview.disconnect(this._itemDragEndId); + Main.overview.disconnect(this._itemDragCancelledId); + + Main.overview.disconnect(this._windowDragBeginId); + Main.overview.disconnect(this._windowDragCancelledId); + Main.overview.disconnect(this._windowDragEndId); + }}; + + MultiMonitors.copyClass(OverviewControls.SlidingControl, MultiMonitorsSlidingControl); + return GObject.registerClass(MultiMonitorsSlidingControl); +})(); + +var MultiMonitorsThumbnailsSlider = (() => { + let MultiMonitorsThumbnailsSlider = class MultiMonitorsThumbnailsSlider extends MultiMonitorsSlidingControl { + _init(thumbnailsBox) { + super._init({ slideDirection: OverviewControls.SlideDirection.RIGHT }); + + this._thumbnailsBox = thumbnailsBox; + + this.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT; + this.reactive = true; + this.track_hover = true; + this.add_actor(this._thumbnailsBox); + + this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', this._updateSlide.bind(this)); + this._activeWorkspaceChangedId = global.workspace_manager.connect('active-workspace-changed', + this._updateSlide.bind(this)); + this._notifyNWorkspacesId = global.workspace_manager.connect('notify::n-workspaces', + this._updateSlide.bind(this)); + this.connect('notify::hover', this._updateSlide.bind(this)); + this._thumbnailsBox.bind_property('visible', this, 'visible', GObject.BindingFlags.SYNC_CREATE); + } + + _onDestroy() { + global.workspace_manager.disconnect(this._activeWorkspaceChangedId); + global.workspace_manager.disconnect(this._notifyNWorkspacesId); + Main.layoutManager.disconnect(this._monitorsChangedId); + super._onDestroy(); + }}; + + MultiMonitors.copyClass(OverviewControls.ThumbnailsSlider, MultiMonitorsThumbnailsSlider); + return GObject.registerClass(MultiMonitorsThumbnailsSlider); +})(); +*/ + +var MultiMonitorsControlsManager = GObject.registerClass( +class MultiMonitorsControlsManager extends St.Widget { + _init(index) { + this._monitorIndex = index; + this._workspacesViews = null; + this._spacer_height = 0; + this._fixGeometry = 0; + this._visible = false; + + let layout + if (OverviewControls.ControlsManagerLayout) { + layout = new OverviewControls.ControlsManagerLayout(); + } else { + layout = new OverviewControls.ControlsLayout(); + } + super._init({ + layout_manager: layout, + x_expand: true, + y_expand: true, + clip_to_allocation: true, + }); + + this._workspaceAdjustment = Main.overview._overview._controls._workspaceAdjustment; + + this._thumbnailsBox = + new MultiMonitorsThumbnailsBox(this._workspaceAdjustment, this._monitorIndex); + //this._thumbnailsSlider = new MultiMonitorsThumbnailsSlider(this._thumbnailsBox); + + this._searchController = new St.Widget({ visible: false, x_expand: true, y_expand: true, clip_to_allocation: true }); + this._pageChangedId = Main.overview.searchController.connect('page-changed', this._setVisibility.bind(this)); + this._pageEmptyId = Main.overview.searchController.connect('page-empty', this._onPageEmpty.bind(this)); + + this._group = new St.BoxLayout({ name: 'mm-overview-group-'+index, + x_expand: true, y_expand: true }); + this.add_actor(this._group); + + this._group.add_child(this._searchController); + //this._group.add_actor(this._thumbnailsSlider); + + this._settings = Convenience.getSettings(); + + this._monitorsChanged(); + //this._thumbnailsSlider.slideOut(); + this._thumbnailsBox._updatePorthole(); + + this.connect('notify::allocation', this._updateSpacerVisibility.bind(this)); + this.connect('destroy', this._onDestroy.bind(this)); + //this._thumbnailsSelectSideId = this._settings.connect('changed::'+THUMBNAILS_SLIDER_POSITION_ID, + // this._thumbnailsSelectSide.bind(this)); + this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', this._monitorsChanged.bind(this)); + } + + _onDestroy() { + Main.overview.searchController.disconnect(this._pageChangedId); + Main.overview.searchController.disconnect(this._pageEmptyId); + this._settings.disconnect(this._thumbnailsSelectSideId); + Main.layoutManager.disconnect(this._monitorsChangedId); + } + + _monitorsChanged() { + this._primaryMonitorOnTheLeft = Main.layoutManager.monitors[this._monitorIndex].x > Main.layoutManager.primaryMonitor.x; + this._thumbnailsSelectSide(); + } + + /* + _thumbnailsSelectSide() { + let thumbnailsSlider; + thumbnailsSlider = this._thumbnailsSlider; + + let sett = this._settings.get_string(THUMBNAILS_SLIDER_POSITION_ID); + let onLeftSide = sett === 'left' || (sett === 'auto' && this._primaryMonitorOnTheLeft); + + if (onLeftSide) { + let first = this._group.get_first_child(); + if (first != thumbnailsSlider) { + this._thumbnailsSlider.layout.slideDirection = OverviewControls.SlideDirection.LEFT; + this._thumbnailsBox.remove_style_class_name('workspace-thumbnails'); + this._thumbnailsBox.set_style_class_name('workspace-thumbnails workspace-thumbnails-left'); + this._group.set_child_below_sibling(thumbnailsSlider, first) + } + } + else { + let last = this._group.get_last_child(); + if (last != thumbnailsSlider) { + this._thumbnailsSlider.layout.slideDirection = OverviewControls.SlideDirection.RIGHT; + this._thumbnailsBox.remove_style_class_name('workspace-thumbnails workspace-thumbnails-left'); + this._thumbnailsBox.set_style_class_name('workspace-thumbnails'); + this._group.set_child_above_sibling(thumbnailsSlider, last); + } + } + this._fixGeometry = 3; + } + */ + + _updateSpacerVisibility() { + if (Main.layoutManager.monitors.length { + this._searchController.visible = false; + }, + }); + this._workspacesViews = null; + } +}); + +var MultiMonitorsOverviewActor = GObject.registerClass( +class MultiMonitorsOverviewActor extends St.BoxLayout { + _init(index) { + this._monitorIndex = index; + super._init({ + name: 'mm-overview-'+index, + /* Translators: This is the main view to select + activities. See also note for "Activities" string. */ + accessible_name: _("MMOverview@"+index), + vertical: true, + }); + + this.add_constraint(new LayoutManager.MonitorConstraint({ index: this._monitorIndex })); + + this._panelGhost = null; + if (Main.mmPanel) { + for (let idx in Main.mmPanel) { + if (Main.mmPanel[idx].monitorIndex !== this._monitorIndex) + continue + // Add a clone of the panel to the overview so spacing and such is + // automatic + this._panelGhost = new St.Bin({ + child: new Clutter.Clone({ source: Main.mmPanel[idx] }), + reactive: false, + opacity: 0, + }); + this.add_actor(this._panelGhost); + break; + } + } + + this._spacer = new St.Widget(); + this.add_actor(this._spacer); + + this._controls = new MultiMonitorsControlsManager(this._monitorIndex); + + // Add our same-line elements after the search entry + this.add_child(this._controls); + } +}); + + +var MultiMonitorsOverview = class MultiMonitorsOverview { + constructor(index) { + this.monitorIndex = index; + + this._initCalled = true; + this._overview = new MultiMonitorsOverviewActor(this.monitorIndex); + this._overview._delegate = this; + this._overview.connect('destroy', this._onDestroy.bind(this)); + Main.layoutManager.overviewGroup.add_child(this._overview); + + this._showingId = Main.overview.connect('showing', this._show.bind(this)); + this._hidingId = Main.overview.connect('hiding', this._hide.bind(this)); + } + + getWorkspacesActualGeometry() { + return this._overview._controls.getWorkspacesActualGeometry(); + } + + _onDestroy() { + Main.overview.disconnect(this._showingId); + Main.overview.disconnect(this._hidingId); + + Main.layoutManager.overviewGroup.remove_child(this._overview); + this._overview._delegate = null; + } + + _show() { + this._overview._controls.show(); + } + + _hide() { + this._overview._controls.hide(); + } + + destroy() { + this._overview.destroy(); + } + + addAction(action) { + this._overview.add_action(action); + } + + removeAction(action) { + if (action.get_actor()) + this._overview.remove_action(action); + } +}; -- cgit v1.2.3