Since PanelMenu.Button started to inherit from St.Widget, the custom _allocate() function isn't called anymore. Simply changing the function to vfunc_allocate() doesn't work as other changes happened in the meantime, so for now just remove it altogether. https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/124
393 lines
13 KiB
JavaScript
393 lines
13 KiB
JavaScript
/* exported WorkspaceIndicator */
|
|
const { Clutter, Gio, GObject, Meta, St } = imports.gi;
|
|
|
|
const DND = imports.ui.dnd;
|
|
const Main = imports.ui.main;
|
|
const PanelMenu = imports.ui.panelMenu;
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
|
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
|
const _ = Gettext.gettext;
|
|
|
|
let WindowPreview = GObject.registerClass(
|
|
class WindowPreview extends St.Button {
|
|
_init(window) {
|
|
super._init({
|
|
style_class: 'window-list-window-preview',
|
|
});
|
|
|
|
this._delegate = this;
|
|
DND.makeDraggable(this, { restoreOnSuccess: true });
|
|
|
|
this._window = window;
|
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
|
|
|
this._sizeChangedId = this._window.connect('size-changed',
|
|
this._relayout.bind(this));
|
|
this._positionChangedId = this._window.connect('position-changed',
|
|
this._relayout.bind(this));
|
|
this._minimizedChangedId = this._window.connect('notify::minimized',
|
|
this._relayout.bind(this));
|
|
this._monitorEnteredId = global.display.connect('window-entered-monitor',
|
|
this._relayout.bind(this));
|
|
this._monitorLeftId = global.display.connect('window-left-monitor',
|
|
this._relayout.bind(this));
|
|
|
|
// Do initial layout when we get a parent
|
|
let id = this.connect('parent-set', () => {
|
|
this.disconnect(id);
|
|
if (!this.get_parent())
|
|
return;
|
|
this._laterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
|
|
this._laterId = 0;
|
|
this._relayout();
|
|
return false;
|
|
});
|
|
});
|
|
|
|
this._focusChangedId = global.display.connect('notify::focus-window',
|
|
this._onFocusChanged.bind(this));
|
|
this._onFocusChanged();
|
|
}
|
|
|
|
// needed for DND
|
|
get realWindow() {
|
|
return this._window.get_compositor_private();
|
|
}
|
|
|
|
_onDestroy() {
|
|
this._window.disconnect(this._sizeChangedId);
|
|
this._window.disconnect(this._positionChangedId);
|
|
this._window.disconnect(this._minimizedChangedId);
|
|
global.display.disconnect(this._monitorEnteredId);
|
|
global.display.disconnect(this._monitorLeftId);
|
|
global.display.disconnect(this._focusChangedId);
|
|
if (this._laterId)
|
|
Meta.later_remove(this._laterId);
|
|
}
|
|
|
|
_onFocusChanged() {
|
|
if (global.display.focus_window === this._window)
|
|
this.add_style_class_name('active');
|
|
else
|
|
this.remove_style_class_name('active');
|
|
}
|
|
|
|
_relayout() {
|
|
let monitor = Main.layoutManager.findIndexForActor(this);
|
|
this.visible = monitor === this._window.get_monitor() &&
|
|
this._window.window_type !== Meta.WindowType.DESKTOP &&
|
|
this._window.showing_on_its_workspace();
|
|
|
|
if (!this.visible)
|
|
return;
|
|
|
|
let workArea = Main.layoutManager.getWorkAreaForMonitor(monitor);
|
|
let hscale = this.get_parent().allocation.get_width() / workArea.width;
|
|
let vscale = this.get_parent().allocation.get_height() / workArea.height;
|
|
|
|
let frameRect = this._window.get_frame_rect();
|
|
this.set_size(
|
|
Math.round(Math.min(frameRect.width, workArea.width) * hscale),
|
|
Math.round(Math.min(frameRect.height, workArea.height) * vscale));
|
|
this.set_position(
|
|
Math.round(frameRect.x * hscale),
|
|
Math.round(frameRect.y * vscale));
|
|
}
|
|
});
|
|
|
|
let WorkspaceThumbnail = GObject.registerClass(
|
|
class WorkspaceThumbnail extends St.Button {
|
|
_init(index) {
|
|
super._init({
|
|
style_class: 'workspace',
|
|
child: new Clutter.Actor({
|
|
layout_manager: new Clutter.BinLayout(),
|
|
clip_to_allocation: true,
|
|
}),
|
|
});
|
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
|
|
|
this._index = index;
|
|
this._delegate = this; // needed for DND
|
|
|
|
this._windowPreviews = new Map();
|
|
|
|
let workspaceManager = global.workspace_manager;
|
|
this._workspace = workspaceManager.get_workspace_by_index(index);
|
|
|
|
this._windowAddedId = this._workspace.connect('window-added',
|
|
(ws, window) => {
|
|
this._addWindow(window);
|
|
});
|
|
this._windowRemovedId = this._workspace.connect('window-removed',
|
|
(ws, window) => {
|
|
this._removeWindow(window);
|
|
});
|
|
this._restackedId = global.display.connect('restacked',
|
|
this._onRestacked.bind(this));
|
|
|
|
this._workspace.list_windows().forEach(w => this._addWindow(w));
|
|
this._onRestacked();
|
|
}
|
|
|
|
acceptDrop(source) {
|
|
if (!source.realWindow)
|
|
return false;
|
|
|
|
let window = source.realWindow.get_meta_window();
|
|
this._moveWindow(window);
|
|
return true;
|
|
}
|
|
|
|
handleDragOver(source) {
|
|
if (source.realWindow)
|
|
return DND.DragMotionResult.MOVE_DROP;
|
|
else
|
|
return DND.DragMotionResult.CONTINUE;
|
|
}
|
|
|
|
_addWindow(window) {
|
|
if (this._windowPreviews.has(window))
|
|
return;
|
|
|
|
let preview = new WindowPreview(window);
|
|
preview.connect('clicked', (a, btn) => this.emit('clicked', btn));
|
|
this._windowPreviews.set(window, preview);
|
|
this.child.add_child(preview);
|
|
}
|
|
|
|
_removeWindow(window) {
|
|
let preview = this._windowPreviews.get(window);
|
|
if (!preview)
|
|
return;
|
|
|
|
this._windowPreviews.delete(window);
|
|
preview.destroy();
|
|
}
|
|
|
|
_onRestacked() {
|
|
let lastPreview = null;
|
|
let windows = global.get_window_actors().map(a => a.meta_window);
|
|
for (let i = 0; i < windows.length; i++) {
|
|
let preview = this._windowPreviews.get(windows[i]);
|
|
if (!preview)
|
|
continue;
|
|
|
|
this.child.set_child_above_sibling(preview, lastPreview);
|
|
lastPreview = preview;
|
|
}
|
|
}
|
|
|
|
_moveWindow(window) {
|
|
let monitorIndex = Main.layoutManager.findIndexForActor(this);
|
|
if (monitorIndex !== window.get_monitor())
|
|
window.move_to_monitor(monitorIndex);
|
|
window.change_workspace_by_index(this._index, false);
|
|
}
|
|
|
|
on_clicked() {
|
|
let ws = global.workspace_manager.get_workspace_by_index(this._index);
|
|
if (ws)
|
|
ws.activate(global.get_current_time());
|
|
}
|
|
|
|
_onDestroy() {
|
|
this._workspace.disconnect(this._windowAddedId);
|
|
this._workspace.disconnect(this._windowRemovedId);
|
|
global.display.disconnect(this._restackedId);
|
|
}
|
|
});
|
|
|
|
var WorkspaceIndicator = GObject.registerClass(
|
|
class WorkspaceIndicator extends PanelMenu.Button {
|
|
_init() {
|
|
super._init(0.0, _('Workspace Indicator'), true);
|
|
this.setMenu(new PopupMenu.PopupMenu(this, 0.0, St.Side.BOTTOM));
|
|
this.add_style_class_name('window-list-workspace-indicator');
|
|
this.menu.actor.remove_style_class_name('panel-menu');
|
|
|
|
let container = new St.Widget({
|
|
layout_manager: new Clutter.BinLayout(),
|
|
x_expand: true,
|
|
y_expand: true,
|
|
});
|
|
this.add_actor(container);
|
|
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
|
this._statusLabel = new St.Label({ text: this._getStatusText() });
|
|
|
|
this._statusBin = new St.Bin({
|
|
style_class: 'status-label-bin',
|
|
x_expand: true,
|
|
y_expand: true,
|
|
child: this._statusLabel,
|
|
});
|
|
container.add_actor(this._statusBin);
|
|
|
|
this._thumbnailsBox = new St.BoxLayout({
|
|
style_class: 'workspaces-box',
|
|
y_expand: true,
|
|
reactive: true,
|
|
});
|
|
this._thumbnailsBox.connect('scroll-event',
|
|
this._onScrollEvent.bind(this));
|
|
container.add_actor(this._thumbnailsBox);
|
|
|
|
this._workspacesItems = [];
|
|
|
|
this._workspaceManagerSignals = [
|
|
workspaceManager.connect('notify::n-workspaces',
|
|
this._nWorkspacesChanged.bind(this)),
|
|
workspaceManager.connect_after('workspace-switched',
|
|
this._onWorkspaceSwitched.bind(this)),
|
|
workspaceManager.connect('notify::layout-rows',
|
|
this._onWorkspaceOrientationChanged.bind(this)),
|
|
];
|
|
|
|
this.connect('scroll-event', this._onScrollEvent.bind(this));
|
|
this._updateMenu();
|
|
this._updateThumbnails();
|
|
this._onWorkspaceOrientationChanged();
|
|
|
|
this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.wm.preferences' });
|
|
this._settingsChangedId = this._settings.connect(
|
|
'changed::workspace-names', this._updateMenuLabels.bind(this));
|
|
}
|
|
|
|
_onDestroy() {
|
|
for (let i = 0; i < this._workspaceManagerSignals.length; i++)
|
|
global.workspace_manager.disconnect(this._workspaceManagerSignals[i]);
|
|
|
|
if (this._settingsChangedId) {
|
|
this._settings.disconnect(this._settingsChangedId);
|
|
this._settingsChangedId = 0;
|
|
}
|
|
|
|
super._onDestroy();
|
|
}
|
|
|
|
_onWorkspaceOrientationChanged() {
|
|
let vertical = global.workspace_manager.layout_rows === -1;
|
|
this.reactive = vertical;
|
|
|
|
this._statusBin.visible = vertical;
|
|
this._thumbnailsBox.visible = !vertical;
|
|
}
|
|
|
|
_onWorkspaceSwitched() {
|
|
let workspaceManager = global.workspace_manager;
|
|
this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
|
|
|
this._updateMenuOrnament();
|
|
this._updateActiveThumbnail();
|
|
|
|
this._statusLabel.set_text(this._getStatusText());
|
|
}
|
|
|
|
_nWorkspacesChanged() {
|
|
this._updateMenu();
|
|
this._updateThumbnails();
|
|
}
|
|
|
|
_updateMenuOrnament() {
|
|
for (let i = 0; i < this._workspacesItems.length; i++) {
|
|
this._workspacesItems[i].setOrnament(i === this._currentWorkspace
|
|
? PopupMenu.Ornament.DOT
|
|
: PopupMenu.Ornament.NONE);
|
|
}
|
|
}
|
|
|
|
_updateActiveThumbnail() {
|
|
let thumbs = this._thumbnailsBox.get_children();
|
|
for (let i = 0; i < thumbs.length; i++) {
|
|
if (i === this._currentWorkspace)
|
|
thumbs[i].add_style_class_name('active');
|
|
else
|
|
thumbs[i].remove_style_class_name('active');
|
|
}
|
|
}
|
|
|
|
_getStatusText() {
|
|
let workspaceManager = global.workspace_manager;
|
|
let current = workspaceManager.get_active_workspace_index();
|
|
let total = workspaceManager.n_workspaces;
|
|
|
|
return '%d / %d'.format(current + 1, total);
|
|
}
|
|
|
|
_updateMenuLabels() {
|
|
for (let i = 0; i < this._workspacesItems.length; i++) {
|
|
let item = this._workspacesItems[i];
|
|
let name = Meta.prefs_get_workspace_name(i);
|
|
item.label.text = name;
|
|
}
|
|
}
|
|
|
|
_updateMenu() {
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
this.menu.removeAll();
|
|
this._workspacesItems = [];
|
|
this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
|
|
|
for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
|
let name = Meta.prefs_get_workspace_name(i);
|
|
let item = new PopupMenu.PopupMenuItem(name);
|
|
item.workspaceId = i;
|
|
|
|
item.connect('activate', () => {
|
|
this._activate(item.workspaceId);
|
|
});
|
|
|
|
if (i === this._currentWorkspace)
|
|
item.setOrnament(PopupMenu.Ornament.DOT);
|
|
|
|
this.menu.addMenuItem(item);
|
|
this._workspacesItems[i] = item;
|
|
}
|
|
|
|
this._statusLabel.set_text(this._getStatusText());
|
|
}
|
|
|
|
_updateThumbnails() {
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
this._thumbnailsBox.destroy_all_children();
|
|
|
|
for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
|
let thumb = new WorkspaceThumbnail(i);
|
|
this._thumbnailsBox.add_actor(thumb);
|
|
}
|
|
this._updateActiveThumbnail();
|
|
}
|
|
|
|
_activate(index) {
|
|
let workspaceManager = global.workspace_manager;
|
|
|
|
if (index >= 0 && index < workspaceManager.n_workspaces) {
|
|
let metaWorkspace = workspaceManager.get_workspace_by_index(index);
|
|
metaWorkspace.activate(global.get_current_time());
|
|
}
|
|
}
|
|
|
|
_onScrollEvent(actor, event) {
|
|
let direction = event.get_scroll_direction();
|
|
let diff = 0;
|
|
if (direction === Clutter.ScrollDirection.DOWN)
|
|
diff = 1;
|
|
else if (direction === Clutter.ScrollDirection.UP)
|
|
diff = -1;
|
|
else
|
|
return;
|
|
|
|
let newIndex = this._currentWorkspace + diff;
|
|
this._activate(newIndex);
|
|
}
|
|
});
|
|
|