Citadel changes to gnome-shell
This commit is contained in:
@ -22,7 +22,7 @@ foreach service, dir : dbus_services
|
||||
|
||||
serviceconf = configuration_data()
|
||||
serviceconf.set('service', service)
|
||||
serviceconf.set('gjs', gjs.full_path())
|
||||
serviceconf.set('gjs', gjs)
|
||||
serviceconf.set('pkgdatadir', pkgdatadir)
|
||||
|
||||
configure_file(
|
||||
|
@ -157,5 +157,12 @@
|
||||
<file>ui/status/system.js</file>
|
||||
<file>ui/status/thunderbolt.js</file>
|
||||
<file>ui/status/volume.js</file>
|
||||
|
||||
<file>ui/realms/realmIndicator.js</file>
|
||||
<file>ui/realms/realmManager.js</file>
|
||||
<file>ui/realms/realmSearchProvider.js</file>
|
||||
<file>ui/realms/realmSwitcher.js</file>
|
||||
<file>ui/realms/realmWindowFrame.js</file>
|
||||
<file>ui/realms/realmWindowMenu.js</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
@ -47,6 +47,7 @@ import * as PointerA11yTimeout from './pointerA11yTimeout.js';
|
||||
import {formatError} from '../misc/errorUtils.js';
|
||||
import * as ParentalControlsManager from '../misc/parentalControlsManager.js';
|
||||
import * as Util from '../misc/util.js';
|
||||
import * as RealmManager from './realms/realmManager.js'
|
||||
|
||||
const WELCOME_DIALOG_LAST_SHOWN_VERSION = 'welcome-dialog-last-shown-version';
|
||||
// Make sure to mention the point release, otherwise it will show every time
|
||||
@ -96,6 +97,7 @@ export let screenTimeDBus = null;
|
||||
export let breakManagerDispatcher = null;
|
||||
export let timeLimitsManager = null;
|
||||
export let timeLimitsDispatcher = null;
|
||||
export let realmManager = null;
|
||||
|
||||
let _startDate;
|
||||
let _defaultCssStylesheet = null;
|
||||
@ -328,6 +330,8 @@ async function _initializeUI() {
|
||||
extensionManager = new ExtensionSystem.ExtensionManager();
|
||||
extensionManager.init();
|
||||
|
||||
realmManager = new RealmManager.RealmManager();
|
||||
|
||||
if (sessionMode.isGreeter && screenShield) {
|
||||
layoutManager.connect('startup-prepared', () => {
|
||||
screenShield.showDialog();
|
||||
|
@ -263,7 +263,7 @@ export class Overview extends Signals.EventEmitter {
|
||||
DND.addDragMonitor(this._dragMonitor);
|
||||
// Remember the workspace we started from
|
||||
let workspaceManager = global.workspace_manager;
|
||||
this._lastActiveWorkspaceIndex = workspaceManager.get_active_workspace_index();
|
||||
this._lastActiveWorkspaceIndex = workspaceManager.get_active_workspace_id();
|
||||
}
|
||||
|
||||
_onDragEnd() {
|
||||
|
37
js/ui/realms/realmIndicator.js
Normal file
37
js/ui/realms/realmIndicator.js
Normal file
@ -0,0 +1,37 @@
|
||||
const { Clutter, GObject, Shell, St } = imports.gi;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
|
||||
var RealmPanelIndicator = GObject.registerClass(
|
||||
class RealmPanelIndicator extends PanelMenu.Button {
|
||||
_init() {
|
||||
super._init(0.5, "Current Realm");
|
||||
|
||||
this._label = new St.Label({
|
||||
style_class: 'current-realm-label',
|
||||
y_align: Clutter.ActorAlign.CENTER
|
||||
});
|
||||
this.add_child(this._label);
|
||||
|
||||
this.update();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._label.set_text('');
|
||||
}
|
||||
|
||||
update() {
|
||||
let realm_name = '';
|
||||
const realms = Shell.Realms.get_default();
|
||||
let current = realms.current_realm;
|
||||
if (current) {
|
||||
realm_name = current.realm_name;
|
||||
this._label.set_text(`realm-${realm_name}`);
|
||||
}
|
||||
|
||||
if (realm_name.length > 0) {
|
||||
this._label.set_text(`realm-${realm_name}`);
|
||||
} else {
|
||||
this._label.set_text('');
|
||||
}
|
||||
}
|
||||
});
|
55
js/ui/realms/realmManager.js
Normal file
55
js/ui/realms/realmManager.js
Normal file
@ -0,0 +1,55 @@
|
||||
const { Clutter, Gio, Meta, Shell, St } = imports.gi;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const RealmIndicator = imports.ui.realms.realmIndicator;
|
||||
const RealmSwitcher = imports.ui.realms.realmSwitcher;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const RealmSearchProvider = imports.ui.realms.realmSearchProvider;
|
||||
const RealmWindowFrame = imports.ui.realms.realmWindowFrame;
|
||||
|
||||
var RealmManager = class {
|
||||
constructor() {
|
||||
|
||||
this._realmIndicator = new RealmIndicator.RealmPanelIndicator();
|
||||
Main.panel.addToStatusArea('RealmIndicator', this._realmIndicator);
|
||||
|
||||
this._switchAction = Main.wm.addKeybinding('switch-realm',
|
||||
new Gio.Settings({ schema_id: "org.gnome.shell.keybindings"}),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.ActionMode.NORMAL,
|
||||
this._switchRealms.bind(this));
|
||||
|
||||
this._switchActionBackward = Main.wm.addKeybinding('switch-realm-backward',
|
||||
new Gio.Settings({ schema_id: "org.gnome.shell.keybindings"}),
|
||||
Meta.KeyBindingFlags.IS_REVERSED,
|
||||
Shell.ActionMode.NORMAL,
|
||||
this._switchRealms.bind(this));
|
||||
|
||||
const realms = Shell.Realms.get_default();
|
||||
realms.connect('realm-context-switched', () => {
|
||||
Main.overview.dash._queueRedisplay();
|
||||
this._realmIndicator.update();
|
||||
});
|
||||
|
||||
this._switchAnimation = new RealmSwitcher.ContextSwitchAnimationController(this._realmIndicator);
|
||||
|
||||
this._searchResults = Main.overview._overview.controls._searchController._searchResults;
|
||||
this._searchProvider = new RealmSearchProvider.RealmSearchProvider();
|
||||
this._searchProvider.createResultDisplay(this._searchResults);
|
||||
this._searchResults._registerProvider(this._searchProvider);
|
||||
|
||||
this._frameManager = new RealmWindowFrame.WindowFrameManager();
|
||||
}
|
||||
|
||||
animateSwitch(from, to, onComplete) {
|
||||
this._switchAnimation.animateSwitch(from, to, onComplete);
|
||||
}
|
||||
|
||||
_switchRealms(display, window, binding) {
|
||||
let popup = new RealmSwitcher.SwitchRealmPopup(this._switchAction, this._switchActionBackward);
|
||||
if (!popup.show(binding.is_reversed(), binding.get_name(), binding.get_mask()))
|
||||
popup.fadeAndDestroy();
|
||||
}
|
||||
|
||||
};
|
||||
|
326
js/ui/realms/realmSearchProvider.js
Normal file
326
js/ui/realms/realmSearchProvider.js
Normal file
@ -0,0 +1,326 @@
|
||||
const { Clutter, GObject, Pango, Shell, St } = imports.gi;
|
||||
|
||||
const Search = imports.ui.search;
|
||||
const Main = imports.ui.main;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
// Based on ProviderInfo in search.js
|
||||
var RealmProviderInfo = GObject.registerClass(
|
||||
class RealmProviderInfo extends St.Button {
|
||||
_init() {
|
||||
super._init({
|
||||
style_class: 'search-provider-icon',
|
||||
reactive: false,
|
||||
can_focus: false,
|
||||
accessible_name: "Realms",
|
||||
track_hover: false,
|
||||
y_align: Clutter.ActorAlign.START,
|
||||
});
|
||||
|
||||
this._content = new St.BoxLayout({ vertical: false,
|
||||
style_class: 'list-search-provider-content' });
|
||||
this.set_child(this._content);
|
||||
|
||||
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
|
||||
icon_name: 'computer' });
|
||||
|
||||
let detailsBox = new St.BoxLayout({ style_class: 'list-search-provider-details',
|
||||
vertical: true,
|
||||
x_expand: true });
|
||||
|
||||
let nameLabel = new St.Label({
|
||||
text: "Realms",
|
||||
x_align: Clutter.ActorAlign.START
|
||||
});
|
||||
|
||||
this._moreLabel = new St.Label({ x_align: Clutter.ActorAlign.START });
|
||||
|
||||
detailsBox.add_actor(nameLabel);
|
||||
detailsBox.add_actor(this._moreLabel);
|
||||
|
||||
|
||||
this._content.add_actor(icon);
|
||||
this._content.add_actor(detailsBox);
|
||||
}
|
||||
|
||||
get PROVIDER_ICON_SIZE() {
|
||||
return 48;
|
||||
}
|
||||
|
||||
setMoreCount(count) {
|
||||
this._moreLabel.text = ngettext("%d more", "%d more", count).format(count);
|
||||
this._moreLabel.visible = count > 0;
|
||||
}
|
||||
});
|
||||
|
||||
var MAX_LIST_SEARCH_RESULTS_ROWS = 10;
|
||||
|
||||
// Based on ListSearchResult in search.js
|
||||
var RealmSearchResult = GObject.registerClass(
|
||||
class ListSearchResult extends Search.SearchResult {
|
||||
_init(provider, metaInfo, resultsView) {
|
||||
super._init(provider, metaInfo, resultsView);
|
||||
|
||||
this.style_class = 'list-search-result';
|
||||
|
||||
let content = new St.BoxLayout({
|
||||
style_class: 'list-search-result-content',
|
||||
vertical: false,
|
||||
x_align: Clutter.ActorAlign.FILL,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
});
|
||||
this.set_child(content);
|
||||
|
||||
this._termsChangedId = 0;
|
||||
|
||||
let titleBox = new St.BoxLayout({
|
||||
style_class: 'list-search-result-title',
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
|
||||
content.add_child(titleBox);
|
||||
|
||||
// An icon for, or thumbnail of, content
|
||||
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
|
||||
if (icon)
|
||||
titleBox.add(icon);
|
||||
|
||||
let title = new St.Label({
|
||||
text: this.metaInfo['name'],
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
titleBox.add_child(title);
|
||||
|
||||
this.label_actor = title;
|
||||
|
||||
if (this.metaInfo['description']) {
|
||||
this._descriptionLabel = new St.Label({
|
||||
style_class: 'list-search-result-description',
|
||||
x_expand: true,
|
||||
x_align: Clutter.ActorAlign.START,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
content.add_child(this._descriptionLabel);
|
||||
|
||||
this._termsChangedId =
|
||||
this._resultsView.connect('terms-changed',
|
||||
this._highlightTerms.bind(this));
|
||||
|
||||
this._highlightTerms();
|
||||
}
|
||||
|
||||
let id = this.metaInfo['id'];
|
||||
|
||||
if (id != ':new:') {
|
||||
this.configButton = new St.Button({
|
||||
style_class: 'button',
|
||||
track_hover: true,
|
||||
can_focus: true,
|
||||
child: new St.Icon({
|
||||
style_class: 'realm-config-icon',
|
||||
icon_name: 'emblem-system-symbolic',
|
||||
icon_size: 24,
|
||||
}),
|
||||
});
|
||||
|
||||
this.configButton.connect('clicked', () => {
|
||||
Main.overview.toggle();
|
||||
Util.spawn(['/usr/libexec/realm-config-ui', id]);
|
||||
});
|
||||
content.add_child(this.configButton);
|
||||
}
|
||||
|
||||
this.connect('destroy', this._onDestroy.bind(this));
|
||||
}
|
||||
|
||||
get ICON_SIZE() {
|
||||
return 24;
|
||||
}
|
||||
|
||||
_highlightTerms() {
|
||||
let markup = this._resultsView.highlightTerms(this.metaInfo['description'].split('\n')[0]);
|
||||
this._descriptionLabel.clutter_text.set_markup(markup);
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
if (this._termsChangedId)
|
||||
this._resultsView.disconnect(this._termsChangedId);
|
||||
this._termsChangedId = 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Based on ListSearchResults in search.js
|
||||
var RealmSearchResults = GObject.registerClass(
|
||||
class RealmSearchResults extends Search.SearchResultsBase {
|
||||
_init(provider, resultsView) {
|
||||
super._init(provider, resultsView);
|
||||
|
||||
this._container = new St.BoxLayout({ style_class: 'search-section-content' });
|
||||
this.providerInfo = new RealmProviderInfo();
|
||||
this.providerInfo.connect('key-focus-in', this._keyFocusIn.bind(this));
|
||||
this.providerInfo.connect('clicked', () => {
|
||||
Main.overview.toggle();
|
||||
});
|
||||
|
||||
this._container.add_child(this.providerInfo);
|
||||
|
||||
this._content = new St.BoxLayout({
|
||||
style_class: 'list-search-results',
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
});
|
||||
this._container.add_child(this._content);
|
||||
|
||||
this._resultDisplayBin.set_child(this._container);
|
||||
}
|
||||
|
||||
_setMoreCount(count) {
|
||||
this.providerInfo.setMoreCount(count);
|
||||
}
|
||||
|
||||
_getMaxDisplayedResults() {
|
||||
return MAX_LIST_SEARCH_RESULTS_ROWS;
|
||||
}
|
||||
|
||||
_clearResultDisplay() {
|
||||
this._content.remove_all_children();
|
||||
}
|
||||
|
||||
_createResultDisplay(meta) {
|
||||
return super._createResultDisplay(meta) ||
|
||||
new RealmSearchResult(this.provider, meta, this._resultsView);
|
||||
}
|
||||
|
||||
_addItem(display) {
|
||||
this._content.add_actor(display);
|
||||
}
|
||||
|
||||
getFirstResult() {
|
||||
if (this._content.get_n_children() > 0)
|
||||
return this._content.get_child_at_index(0);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var RealmSearchProvider = class RealmSearchProvider {
|
||||
constructor() {
|
||||
this._shellRealms = Shell.Realms.get_default();
|
||||
this.id = 'realms';
|
||||
this.isRemoteProvider = false;
|
||||
this.canLaunchSearch = false;
|
||||
this.display = null;
|
||||
}
|
||||
|
||||
createResultDisplay(resultsView) {
|
||||
this.display = new RealmSearchResults(this, resultsView);
|
||||
this.display.connect('notify::focus-child', resultsView._focusChildChanged.bind(resultsView));
|
||||
this.display.hide();
|
||||
resultsView._content.add(this.display);
|
||||
}
|
||||
|
||||
createIcon(size, realm) {
|
||||
if (realm.is_running()) {
|
||||
return new St.Icon({ icon_name: 'emblem-synchronizing', icon_size: size });
|
||||
} else {
|
||||
return new St.Icon({ icon_name: 'exaile', icon_size: size });
|
||||
}
|
||||
}
|
||||
|
||||
createRealmMeta(realm) {
|
||||
let id = realm.get_realm_name();
|
||||
let description = '';
|
||||
if (realm.is_running()) {
|
||||
description = `Set realm-${id} as current realm`;
|
||||
} else {
|
||||
description = `Start realm-${id}`;
|
||||
}
|
||||
|
||||
return {
|
||||
id: id,
|
||||
name: `realm-${id}`,
|
||||
description: description,
|
||||
createIcon: size => {
|
||||
return this.createIcon(size, realm);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
createNewRealmMeta() {
|
||||
return {
|
||||
id: ':new:',
|
||||
name: 'New Realm',
|
||||
description: 'Create a new realm',
|
||||
createIcon: size => {
|
||||
return new St.Icon({
|
||||
icon_name: 'computer',
|
||||
icon_size: size,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getResultMetas (ids, callback) {
|
||||
let metas = [];
|
||||
|
||||
for (let id of ids) {
|
||||
if (id == ":new:") {
|
||||
metas.push(this.createNewRealmMeta());
|
||||
} else {
|
||||
let realm = this._shellRealms.realm_by_name(id);
|
||||
if (realm && !realm.is_current()) {
|
||||
metas.push(this.createRealmMeta(realm));
|
||||
} else {
|
||||
log(`No realm found for ${id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(metas);
|
||||
}
|
||||
|
||||
activateResult (resultId, _terms) {
|
||||
|
||||
if (resultId == ':new:') {
|
||||
Main.overview.toggle();
|
||||
Util.spawn(['/usr/libexec/realm-config-ui', '--new']);
|
||||
return;
|
||||
}
|
||||
|
||||
let realm = this._shellRealms.realm_by_name(resultId);
|
||||
if (realm) {
|
||||
realm.set_current();
|
||||
} else {
|
||||
log(`No realm found for ${resultId}`);
|
||||
}
|
||||
}
|
||||
|
||||
filterResults(results, maxNumber) {
|
||||
return results.slice(0, maxNumber)
|
||||
}
|
||||
|
||||
getInitialResultSet(terms, callback, _cancellable) {
|
||||
let realms = this._shellRealms.get_all_realms();
|
||||
let matches = [];
|
||||
|
||||
if (terms.length == 1 && "new".startsWith(terms[0])) {
|
||||
matches.push(":new:");
|
||||
}
|
||||
|
||||
for (let realm of realms) {
|
||||
if (!realm.is_current()) {
|
||||
let name = realm.get_realm_name();
|
||||
if (terms.every(t => name.indexOf(t) != -1)) {
|
||||
matches.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(matches);
|
||||
}
|
||||
|
||||
getSubsearchResultSet(previousResults, terms, callback, cancellable) {
|
||||
this.getInitialResultSet(terms, callback, cancellable)
|
||||
}
|
||||
}
|
325
js/ui/realms/realmSwitcher.js
Normal file
325
js/ui/realms/realmSwitcher.js
Normal file
@ -0,0 +1,325 @@
|
||||
|
||||
const { Clutter, GObject, Meta, Shell, St } = imports.gi;
|
||||
|
||||
const Background = imports.ui.background;
|
||||
const SwitcherPopup = imports.ui.switcherPopup;
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const WINDOW_ANIMATION_TIME = 2000;
|
||||
var APP_ICON_SIZE = 96;
|
||||
|
||||
var RealmItem = GObject.registerClass(
|
||||
class RealmItem extends St.BoxLayout {
|
||||
_init(realm, workspace_group) {
|
||||
super._init({ vertical: true });
|
||||
this.realm = realm;
|
||||
|
||||
this.add_child(workspace_group);
|
||||
|
||||
this.label = new St.Label({
|
||||
text: `realm-${this.realm.realm_name}`,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
|
||||
this.add_child(this.label);
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.realm.set_current();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function getRealmItems() {
|
||||
const monitor = Main.layoutManager.primaryMonitor;
|
||||
const realms = Shell.Realms.get_default();
|
||||
let realm_list = realms.get_running_realms();
|
||||
let items = [];
|
||||
realm_list.forEach(realm => {
|
||||
let ws = realm.get_active_workspace();
|
||||
if (ws) {
|
||||
let size = 256; // default thumbnail size
|
||||
let scale = Math.min(1.0, size / monitor.width, size / monitor.height);
|
||||
let wsgroup = new WorkspaceGroup(ws, monitor, scale);
|
||||
items.push(new RealmItem(realm, wsgroup));
|
||||
}
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
var SwitchRealmList = GObject.registerClass(
|
||||
class SwitchRealmList extends SwitcherPopup.SwitcherList {
|
||||
_init(items) {
|
||||
super._init(false);
|
||||
|
||||
items.forEach(item => {
|
||||
this.addItem(item, item.label);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var SwitchRealmPopup = GObject.registerClass(
|
||||
class SwitchRealmPopup extends SwitcherPopup.SwitcherPopup {
|
||||
_init(action, actionBackward) {
|
||||
super._init();
|
||||
this._action = action;
|
||||
this._actionBackward = actionBackward;
|
||||
this._items = getRealmItems();
|
||||
this._switcherList = new SwitchRealmList(this._items);
|
||||
}
|
||||
|
||||
_keyPressHandler(keysym, action) {
|
||||
if (action == this._action)
|
||||
this._select(this._next());
|
||||
else if (action == this._actionBackward)
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.KEY_Left)
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.KEY_Right)
|
||||
this._select(this._next());
|
||||
else
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
_finish() {
|
||||
super._finish();
|
||||
this._items[this._selectedIndex].activate();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
const WorkspaceGroup = GObject.registerClass(
|
||||
class WorkspaceGroup extends Clutter.Actor {
|
||||
_init(workspace, monitor, scale = 1.0) {
|
||||
super._init();
|
||||
this._workspace = workspace;
|
||||
this._monitor = monitor;
|
||||
this._scale = scale;
|
||||
this._windowRecords = [];
|
||||
this.width = monitor.width * scale;
|
||||
this.height = monitor.height * scale;
|
||||
this._background = new Meta.BackgroundGroup({
|
||||
width: this.width * this._scale,
|
||||
height: this.height * this._scale,
|
||||
});
|
||||
this.add_actor(this._background);
|
||||
this._bgManager = new Background.BackgroundManager({
|
||||
container: this._background,
|
||||
monitorIndex: this._monitor.index,
|
||||
controlPosition: false,
|
||||
});
|
||||
this.clip_to_allocation = true;
|
||||
|
||||
this._createWindows();
|
||||
this.connect('destroy', this._onDestroy.bind(this));
|
||||
this._restackedId = global.display.connect('restacked', this._syncStacking.bind(this));
|
||||
}
|
||||
|
||||
get workspace() {
|
||||
return this._workspace;
|
||||
}
|
||||
|
||||
_shouldShowWindow(window) {
|
||||
if (!window.showing_on_its_workspace())
|
||||
return false;
|
||||
|
||||
const geometry = global.display.get_monitor_geometry(this._monitor.index);
|
||||
const [intersects] = window.get_frame_rect().intersect(geometry);
|
||||
if (!intersects)
|
||||
return false;
|
||||
|
||||
const isSticky = window.is_on_all_workspaces();
|
||||
|
||||
return !isSticky && window.located_on_workspace(this._workspace);
|
||||
}
|
||||
|
||||
_syncStacking() {
|
||||
const windowActors = global.get_window_actors().filter(w =>
|
||||
this._shouldShowWindow(w.meta_window));
|
||||
|
||||
let lastRecord;
|
||||
|
||||
for (const windowActor of windowActors) {
|
||||
const record = this._windowRecords.find(r => r.windowActor === windowActor);
|
||||
this.set_child_above_sibling(record.clone, lastRecord ? lastRecord.clone : this._background);
|
||||
lastRecord = record;
|
||||
}
|
||||
}
|
||||
|
||||
_createWindows() {
|
||||
const windowActors = global.get_window_actors().filter(w =>
|
||||
this._shouldShowWindow(w.meta_window));
|
||||
for (const windowActor of windowActors) {
|
||||
let [width,height] = windowActor.get_size();
|
||||
const clone = new Clutter.Clone({
|
||||
source: windowActor,
|
||||
width: width * this._scale,
|
||||
height: height * this._scale,
|
||||
x: (windowActor.x - this._monitor.x) * this._scale,
|
||||
y: (windowActor.y - this._monitor.y) * this._scale,
|
||||
});
|
||||
this.add_child(clone);
|
||||
const record = {windowActor, clone };
|
||||
record.windowDestroyId = windowActor.connect('destroy', () => {
|
||||
clone.destroy();
|
||||
this._windowRecords.splice(this._windowRecords.indexOf(record), 1);
|
||||
});
|
||||
this._windowRecords.push(record);
|
||||
}
|
||||
}
|
||||
|
||||
_removeWindows() {
|
||||
for (const record of this._windowRecords) {
|
||||
record.windowActor.disconnect(record.windowDestroyId);
|
||||
record.clone.destroy();
|
||||
}
|
||||
this._windowRecords = [];
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
global.display.disconnect(this._restackedId);
|
||||
this._removeWindows();
|
||||
this._bgManager.destroy();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
const MonitorGroup = GObject.registerClass({
|
||||
Properties: {
|
||||
'progress': GObject.ParamSpec.double(
|
||||
'progress', 'progress', 'progress',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
-Infinity, Infinity, 0),
|
||||
},
|
||||
}, class MonitorGroup extends St.Widget {
|
||||
_init(monitor, fromIndex, toIndex) {
|
||||
super._init({
|
||||
clip_to_allocation: true,
|
||||
style_class: 'workspace-animation',
|
||||
});
|
||||
this._monitor = monitor;
|
||||
const constraint = new Layout.MonitorConstraint({ index: monitor.index });
|
||||
this.add_constraint(constraint);
|
||||
|
||||
this._container = new Clutter.Actor();
|
||||
this.add_child(this._container);
|
||||
|
||||
this._progress = 0;
|
||||
this._fadeOut = true;
|
||||
|
||||
this._workspaceGroups = [];
|
||||
this._blackBackground = new Clutter.Actor();
|
||||
this._blackBackground.width = monitor.width;
|
||||
this._blackBackground.height = monitor.height;
|
||||
let [_res, color] = Clutter.Color.from_string("#000000ff");
|
||||
this._blackBackground.background_color = color;
|
||||
|
||||
|
||||
|
||||
this.addWorkspaceByIndex(toIndex, monitor);
|
||||
// add opaque black actor
|
||||
this._container.add_child(this._blackBackground);
|
||||
this.addWorkspaceByIndex(fromIndex, monitor);
|
||||
|
||||
// tween 'from' WorkspaceGroup opacity from 255 to 0 fading workspace to black background
|
||||
// tween 'block' actor opacity from 255 to 0 revealing 'to' WorkspaceGroup
|
||||
}
|
||||
|
||||
addWorkspaceByIndex(idx, monitor) {
|
||||
const workspaceManager = global.workspace_manager;
|
||||
const ws = workspaceManager.get_workspace_by_index(idx);
|
||||
if (ws) {
|
||||
const fullscreen = ws.list_windows().some(w => w.get_monitor() === monitor.index && w.is_fullscreen());
|
||||
const group = new WorkspaceGroup(ws, monitor);
|
||||
this._workspaceGroups.push(group);
|
||||
this._container.add_child(group);
|
||||
}
|
||||
}
|
||||
|
||||
get progress() {
|
||||
return this._progress;
|
||||
}
|
||||
|
||||
// Interpolate opacity from 0 (full opaque) to 50 (full transparent)
|
||||
calculateOpacity(progress) {
|
||||
return 255 - (255 * (progress / 50.0));
|
||||
}
|
||||
|
||||
set progress(p) {
|
||||
const fromGroup = this._workspaceGroups[this._workspaceGroups.length - 1];
|
||||
this._progress = p;
|
||||
// 0 - 50
|
||||
if (p < 50) {
|
||||
this._blackBackground.opacity = 255;
|
||||
fromGroup.opacity = this.calculateOpacity(p);
|
||||
} else if (p < 100) {
|
||||
if (this._fadeOut) {
|
||||
this._fadeOut = false;
|
||||
}
|
||||
fromGroup.opacity = 0;
|
||||
this._blackBackground.opacity = this.calculateOpacity(p - 50);
|
||||
} else {
|
||||
fromGroup.opacity = 0;
|
||||
this._blackBackground.opacity = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var ContextSwitchAnimationController = class {
|
||||
constructor(indicator) {
|
||||
this._switchData = null;
|
||||
this._indicator = indicator;
|
||||
}
|
||||
|
||||
_prepareContextSwitch(fromIdx, toIdx) {
|
||||
if (this._switchData) {
|
||||
this._switchData.monitors[0].remove_all_transitions();
|
||||
this._finishContextSwitch(this._switchData);
|
||||
}
|
||||
|
||||
const switchData = {};
|
||||
this._switchData = switchData;
|
||||
switchData.monitors = [];
|
||||
switchData.inProgress = false;
|
||||
|
||||
const monitor = Main.layoutManager.primaryMonitor;
|
||||
|
||||
const group = new MonitorGroup(monitor, fromIdx, toIdx);
|
||||
|
||||
Main.uiGroup.insert_child_above(group, global.window_group);
|
||||
|
||||
switchData.monitors.push(group);
|
||||
|
||||
Meta.disable_unredirect_for_display(global.display);
|
||||
}
|
||||
|
||||
_finishContextSwitch(switchData) {
|
||||
Meta.enable_unredirect_for_display(global.display);
|
||||
this._indicator.update();
|
||||
this._switchData = null;
|
||||
switchData.monitors.forEach(m => m.destroy());
|
||||
if (switchData.onComplete) {
|
||||
switchData.onComplete();
|
||||
}
|
||||
}
|
||||
|
||||
animateSwitch(fromIdx, toIdx, onComplete) {
|
||||
|
||||
this._prepareContextSwitch(fromIdx, toIdx);
|
||||
this._switchData.inProgress = true;
|
||||
this._switchData.onComplete = onComplete;
|
||||
|
||||
const params = {
|
||||
duration: WINDOW_ANIMATION_TIME,
|
||||
mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
|
||||
};
|
||||
params.onComplete = () => {
|
||||
this._finishContextSwitch(this._switchData);
|
||||
};
|
||||
this._indicator.clear();
|
||||
this._switchData.monitors[0].ease_property('progress', 100, params);
|
||||
}
|
||||
}
|
308
js/ui/realms/realmWindowFrame.js
Normal file
308
js/ui/realms/realmWindowFrame.js
Normal file
@ -0,0 +1,308 @@
|
||||
const { Clutter, Cogl, GObject, Meta, Shell, St } = imports.gi;
|
||||
|
||||
var WindowFrameManager = class WindowFrameManager {
|
||||
constructor() {
|
||||
this._realms = Shell.Realms.get_default();
|
||||
let frames = this._realms.window_frames();
|
||||
this._frame_effects = [];
|
||||
|
||||
global.window_manager.connect('map', this._handleWindowMap.bind(this));
|
||||
global.workspace_manager.connect('context-window-moved', this._onContextWindowMoved.bind(this));
|
||||
global.workspace_manager.connect('context-removed', this._onContextRemoved.bind(this));
|
||||
frames.connect('realm-frame-colors-changed', this._onFrameColorsChanged.bind(this));
|
||||
|
||||
this.trackWindows();
|
||||
}
|
||||
|
||||
_onContextWindowMoved(workspaceManager, window) {
|
||||
let actor = window.get_compositor_private();
|
||||
if (actor) {
|
||||
this.handleWindow(actor);
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
_handleWindowMap(shellwm, actor) {
|
||||
this.handleWindow(actor);
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
_onContextRemoved(workspaceManager, id) {
|
||||
this.trackWindows();
|
||||
}
|
||||
|
||||
_onFrameColorsChanged(realms) {
|
||||
this.trackWindows();
|
||||
}
|
||||
|
||||
trackWindows() {
|
||||
var actors = global.get_window_actors();
|
||||
actors.forEach(a => this.handleWindow(a));
|
||||
}
|
||||
|
||||
handleWindow(actor) {
|
||||
let win = actor.metaWindow;
|
||||
let win_id = win.get_stable_sequence();
|
||||
let effect = this._frame_effects[win_id];
|
||||
|
||||
let frames = this._realms.window_frames();
|
||||
|
||||
if (frames.has_frame(win) && frames.is_frame_enabled(win)) {
|
||||
let color = frames.color_for_window(win);
|
||||
|
||||
if (effect) {
|
||||
effect.setColor(color);
|
||||
} else {
|
||||
let label = frames.label_for_window(win);
|
||||
effect = new RealmFrameEffect(actor, color, label);
|
||||
this._frame_effects[win_id] = effect;
|
||||
}
|
||||
} else if (effect) {
|
||||
effect.removeEffect(actor);
|
||||
this._frame_effects[win_id] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var RealmFrameEffect = GObject.registerClass(
|
||||
class RealmFrameEffect extends Clutter.Effect {
|
||||
_init(actor, color, label_text) {
|
||||
super._init();
|
||||
this._frame_width = 2;
|
||||
this._pipeline = null;
|
||||
this._color = color;
|
||||
this._label_on_top = true;
|
||||
this._label = null;
|
||||
|
||||
if (label_text) {
|
||||
this._label = this._createLabel(actor, label_text);
|
||||
this._updateLabel(actor.metaWindow);
|
||||
}
|
||||
|
||||
this._sizeChangedId = actor.metaWindow.connect('size-changed', window => {
|
||||
this._updateLabel(window);
|
||||
});
|
||||
|
||||
actor.add_effect(this);
|
||||
}
|
||||
|
||||
removeEffect(actor) {
|
||||
if (this._label) {
|
||||
actor.remove_child(this._label);
|
||||
this._label = null;
|
||||
}
|
||||
if (this._sizeChangedId) {
|
||||
let win = actor.metaWindow;
|
||||
win.disconnect(this._sizeChangedId);
|
||||
this._sizeChangedId = 0;
|
||||
}
|
||||
actor.remove_effect(this);
|
||||
}
|
||||
|
||||
_createLabel(actor, label_text) {
|
||||
let label = new St.Label({
|
||||
style_class: 'realm-frame-label',
|
||||
z_position: 1.0,
|
||||
|
||||
});
|
||||
label.set_text(' '+label_text+' ');
|
||||
actor.add_child(label);
|
||||
return label;
|
||||
}
|
||||
|
||||
_updateLabel(window) {
|
||||
if (this._label) {
|
||||
this._updateLabelPosition(window);
|
||||
this._updateLabelColor();
|
||||
}
|
||||
}
|
||||
|
||||
_updateLabelPosition(window) {
|
||||
|
||||
if (!this._label_height) {
|
||||
// If we scale the text, the reported size of the label will not be the value we need so
|
||||
// save the initial value.
|
||||
this._label_height = this._label.get_height();
|
||||
}
|
||||
|
||||
|
||||
let maximized = window.is_fullscreen() === true || // Fullscreen
|
||||
[Meta.MaximizeFlags.BOTH, Meta.MaximizeFlags.VERTICAL].includes(window.get_maximized()); // Maximized
|
||||
|
||||
this._label_on_top = !maximized;
|
||||
|
||||
let frame_rect = window.get_frame_rect();
|
||||
let buffer_rect = window.get_buffer_rect();
|
||||
|
||||
let offsetX = frame_rect.x - buffer_rect.x;
|
||||
let offsetY = frame_rect.y - buffer_rect.y;
|
||||
|
||||
|
||||
if (window.get_client_type() === Meta.WindowClientType.WAYLAND) {
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
if (scaleFactor !== 1) {
|
||||
offsetX = offsetX / scaleFactor;
|
||||
this._label.set_style(`font-size: ${12 / scaleFactor}pt;`);
|
||||
}
|
||||
offsetX -= 1;
|
||||
offsetY -= 4;
|
||||
}
|
||||
|
||||
// If label is on top and there is enough space above title bar move position up by label height
|
||||
if (this._label_on_top && this._label_height <= offsetY) {
|
||||
offsetY -= this._label_height;
|
||||
} else if (maximized) {
|
||||
offsetX = 0;
|
||||
offsetY = 0;
|
||||
}
|
||||
this._label.set_position(offsetX, offsetY);
|
||||
}
|
||||
|
||||
_updateLabelColor() {
|
||||
let fg = new Clutter.Color({
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0,
|
||||
alpha: 96,
|
||||
});
|
||||
|
||||
let bg = this._color.copy();
|
||||
|
||||
if (this._label_on_top) {
|
||||
bg.alpha = 100;
|
||||
} else {
|
||||
bg.alpha = 200;
|
||||
}
|
||||
|
||||
let clutter_text = this._label.get_clutter_text();
|
||||
clutter_text.set_color(fg);
|
||||
clutter_text.set_background_color(bg);
|
||||
}
|
||||
|
||||
|
||||
setColor(color) {
|
||||
if (this._color && this._color.equal(color)) {
|
||||
return;
|
||||
}
|
||||
this._color = color;
|
||||
this.setPipelineColor();
|
||||
if (this._label) {
|
||||
this._updateLabelColor();
|
||||
}
|
||||
}
|
||||
|
||||
setPipelineColor() {
|
||||
if (!this._color || !this._pipeline) {
|
||||
return;
|
||||
}
|
||||
let s = this._color.to_string();
|
||||
|
||||
let cogl_color = new Cogl.Color();
|
||||
cogl_color.init_from_4ub(this._color.red, this._color.green, this._color.blue, 0xc4);
|
||||
this._pipeline.set_color(cogl_color);
|
||||
}
|
||||
|
||||
_calculate_frame_box(window, allocation) {
|
||||
let frame_rect = window.get_frame_rect();
|
||||
let buffer_rect = window.get_buffer_rect();
|
||||
|
||||
let offsetX = frame_rect.x - buffer_rect.x;
|
||||
let offsetY = frame_rect.y - buffer_rect.y;
|
||||
|
||||
let top = offsetY - 3;
|
||||
let bottom = offsetY - 1;
|
||||
let left = offsetX - 3;
|
||||
let right = offsetX - 1;
|
||||
|
||||
let wayland = window.get_client_type() == Meta.WindowClientType.WAYLAND;
|
||||
|
||||
if (wayland) {
|
||||
bottom += 4;
|
||||
}
|
||||
|
||||
let fw = this._frame_width;
|
||||
|
||||
|
||||
switch (window.get_maximized()) {
|
||||
case Meta.MaximizeFlags.BOTH:
|
||||
top += fw;
|
||||
right += fw;
|
||||
bottom += fw - (wayland ? 5 : 0);
|
||||
left += fw;
|
||||
break;
|
||||
case Meta.MaximizeFlags.HORIZONTAL:
|
||||
right += fw;
|
||||
left += fw;
|
||||
break;
|
||||
case Meta.MaximizeFlags.VERTICAL:
|
||||
top += fw;
|
||||
bottom += fw;
|
||||
break;
|
||||
}
|
||||
|
||||
if (window.is_fullscreen()) {
|
||||
top += 3;
|
||||
right += 2;
|
||||
bottom -= (wayland ? 3 : 0);
|
||||
left += 3;
|
||||
}
|
||||
|
||||
if (!wayland && !window.decorated && !window.is_fullscreen() && (window.get_maximized() !== Meta.MaximizeFlags.BOTH)) {
|
||||
bottom += 4;
|
||||
}
|
||||
|
||||
let x = left;
|
||||
let y = top + fw;
|
||||
let w = allocation.get_width() - (right + left);
|
||||
let h = allocation.get_height() - (bottom + top + fw);
|
||||
|
||||
return [x, y, w, h];
|
||||
}
|
||||
|
||||
draw_rect(node, x, y, width, height) {
|
||||
const box = new Clutter.ActorBox();
|
||||
box.set_origin(x, y);
|
||||
box.set_size(width, height);
|
||||
node.add_rectangle(box);
|
||||
}
|
||||
|
||||
draw_hline(node, x, y, width, width_factor = 1) {
|
||||
this.draw_rect(node, x, y, width, this._frame_width * width_factor);
|
||||
}
|
||||
|
||||
draw_vline(node, x, y, height, width_factor = 1) {
|
||||
this.draw_rect(node, x, y, this._frame_width * width_factor, height);
|
||||
}
|
||||
|
||||
vfunc_paint_node(node, ctx) {
|
||||
let actor = this.get_actor();
|
||||
|
||||
const actorNode = new Clutter.ActorNode(actor, -1);
|
||||
node.add_child(actorNode);
|
||||
|
||||
if (!this._pipeline) {
|
||||
let framebuffer = ctx.get_framebuffer();
|
||||
let coglContext = framebuffer.get_context();
|
||||
this._pipeline = new Cogl.Pipeline(coglContext);
|
||||
this.setPipelineColor();
|
||||
}
|
||||
|
||||
const pipelineNode = new Clutter.PipelineNode(this._pipeline);
|
||||
pipelineNode.set_name('Realm Frame');
|
||||
node.add_child(pipelineNode);
|
||||
|
||||
let [x, y, width, height] = this._calculate_frame_box(actor.metaWindow, actor.get_allocation_box());
|
||||
|
||||
// Top
|
||||
this.draw_hline(pipelineNode, x, y, width, 2);
|
||||
|
||||
// Right
|
||||
this.draw_vline(pipelineNode, x + width, y, height);
|
||||
|
||||
// Bottom
|
||||
this.draw_hline(pipelineNode, x, y + height, width);
|
||||
|
||||
// Left
|
||||
this.draw_vline(pipelineNode, x, y, height);
|
||||
}
|
||||
});
|
133
js/ui/realms/realmWindowMenu.js
Normal file
133
js/ui/realms/realmWindowMenu.js
Normal file
@ -0,0 +1,133 @@
|
||||
|
||||
const { Shell, GObject } = imports.gi;
|
||||
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
function _windowAppId(window) {
|
||||
const tracker = Shell.WindowTracker.get_default();
|
||||
const app = tracker.get_window_app(window);
|
||||
if (app) {
|
||||
return app.get_id();
|
||||
} else {
|
||||
log(`No app found for window ${window.get_description()}`)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function windowMenuDebugString(window) {
|
||||
const id = _windowAppId(window);
|
||||
const realm_name = windowRealmName(window);
|
||||
|
||||
if (!realm_name) {
|
||||
return id;
|
||||
} else if (window.is_on_foreign_workspace_context()) {
|
||||
return `${id} [${realm_name}]`;
|
||||
} else {
|
||||
return `${id} (${realm_name})`;
|
||||
}
|
||||
}
|
||||
|
||||
function _createMoveWindowItem(label, realm_name, window) {
|
||||
let item = new PopupMenu.PopupMenuItem(label);
|
||||
item.connect('activate', () => {
|
||||
let realms = Shell.Realms.get_default();
|
||||
let realm = realms.realm_by_name(realm_name);
|
||||
|
||||
if (realm) {
|
||||
realm.move_window_to_context(window);
|
||||
}
|
||||
});
|
||||
return item;
|
||||
}
|
||||
|
||||
// Return name of the realm the application this window belongs to is running in.
|
||||
function windowRealmName(window) {
|
||||
const realms = Shell.Realms.get_default();
|
||||
|
||||
if (realms.is_citadel_window(window)) {
|
||||
return "Citadel"
|
||||
}
|
||||
|
||||
let realm = realms.realm_by_window(window);
|
||||
|
||||
if (realm) {
|
||||
return realm.realm_name;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Return name of realm the context this window is currently located on belongs to
|
||||
function windowContextRealmName(window) {
|
||||
if (window.on_all_workspaces) {
|
||||
return windowRealmName(window);
|
||||
}
|
||||
|
||||
let ws = window.get_workspace();
|
||||
|
||||
if (!ws) {
|
||||
return null;
|
||||
}
|
||||
const realms = Shell.Realms.get_default();
|
||||
let realm = realms.realm_by_context_id(ws.get_context_id());
|
||||
|
||||
if (realm) {
|
||||
return realm.realm_name;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function enableFrameItem(window) {
|
||||
const realms = Shell.Realms.get_default();
|
||||
const frames = realms.window_frames();
|
||||
if (!frames.has_frame(window)) {
|
||||
return null;
|
||||
}
|
||||
let enabled = frames.is_frame_enabled(window);
|
||||
let item = new PopupMenu.PopupMenuItem("Display colored window frame");
|
||||
if (enabled) {
|
||||
item.setOrnament(PopupMenu.Ornament.CHECK);
|
||||
}
|
||||
|
||||
item.connect('activate', () => {
|
||||
let realms = Shell.Realms.get_default();
|
||||
const frames = realms.window_frames();
|
||||
frames.set_frame_enabled(window, !enabled);
|
||||
});
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
function realmWindowMenu(window) {
|
||||
|
||||
const realm_name = windowContextRealmName(window);
|
||||
|
||||
if (!realm_name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const realms = Shell.Realms.get_default();
|
||||
let other_realms = [];
|
||||
|
||||
let running_realms = realms.get_running_realms();
|
||||
running_realms.forEach(realm => {
|
||||
if (realm.realm_name != realm_name) {
|
||||
other_realms.push(realm.realm_name);
|
||||
}
|
||||
});
|
||||
|
||||
if (other_realms.length == 0) {
|
||||
return null;
|
||||
} else if (other_realms.length == 1) {
|
||||
let name = other_realms[0];
|
||||
return _createMoveWindowItem(`Move to realm-${name}`, name, window);
|
||||
}
|
||||
|
||||
let subMenu = new PopupMenu.PopupSubMenuMenuItem('Move to Realm...', true);
|
||||
|
||||
other_realms.forEach(name => {
|
||||
let item = _createMoveWindowItem(`realm-${name}`, name, window);
|
||||
subMenu.menu.addMenuItem(item);
|
||||
});
|
||||
return subMenu;
|
||||
}
|
@ -78,7 +78,7 @@ const _modes = {
|
||||
|
||||
'user': {
|
||||
hasOverview: true,
|
||||
showCalendarEvents: true,
|
||||
showCalendarEvents: false,
|
||||
showWelcomeDialog: true,
|
||||
allowSettings: true,
|
||||
allowScreencast: true,
|
||||
|
@ -204,6 +204,8 @@ class WorkspaceTracker {
|
||||
workspaceManager.connect('workspaces-reordered', () => {
|
||||
this._workspaces.sort((a, b) => a.index() - b.index());
|
||||
});
|
||||
workspaceManager.connect('context-switched',
|
||||
this._workspaceContextSwitched.bind(this));
|
||||
global.window_manager.connect('switch-workspace',
|
||||
this._queueCheckWorkspaces.bind(this));
|
||||
|
||||
@ -259,6 +261,8 @@ class WorkspaceTracker {
|
||||
emptyWorkspaces[index] = false;
|
||||
}
|
||||
|
||||
let current_context_id = workspaceManager.active_context_id();
|
||||
|
||||
let windows = global.get_window_actors();
|
||||
for (i = 0; i < windows.length; i++) {
|
||||
let actor = windows[i];
|
||||
@ -267,7 +271,12 @@ class WorkspaceTracker {
|
||||
if (win.is_on_all_workspaces())
|
||||
continue;
|
||||
|
||||
let workspaceIndex = win.get_workspace().index();
|
||||
let workspace = win.get_workspace();
|
||||
|
||||
if (workspace.get_context_id() != current_context_id)
|
||||
continue;
|
||||
|
||||
let workspaceIndex = workspace.index();
|
||||
emptyWorkspaces[workspaceIndex] = false;
|
||||
}
|
||||
|
||||
@ -348,6 +357,28 @@ class WorkspaceTracker {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_workspaceContextSwitched() {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
let numWorkspaces = workspaceManager.n_workspaces;
|
||||
|
||||
this._workspaces.forEach(workspace => {
|
||||
workspace.disconnect(workspace._windowAddedId);
|
||||
workspace.disconnect(workspace._windowRemovedId);
|
||||
});
|
||||
|
||||
this._workspaces = [];
|
||||
|
||||
for (let w = 0; w < numWorkspaces; w++) {
|
||||
let workspace = workspaceManager.get_workspace_by_index(w);
|
||||
workspace._windowAddedId = workspace.connect('window-added', this._queueCheckWorkspaces.bind(this));
|
||||
workspace._windowRemovedId = workspace.connect('window-removed', this._windowRemoved.bind(this));
|
||||
this._workspaces[w] = workspace;
|
||||
}
|
||||
this._queueCheckWorkspaces();
|
||||
return false;
|
||||
}
|
||||
|
||||
_nWorkspacesChanged() {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
let oldNumWorkspaces = this._workspaces.length;
|
||||
@ -1635,6 +1666,14 @@ export class WindowManager {
|
||||
|
||||
this._switchInProgress = true;
|
||||
|
||||
if (direction == Meta.MotionDirection.CONTEXT_SWITCH) {
|
||||
Main.realmManager.animateSwitch(from, to, () => {
|
||||
this._shellwm.completed_switch_workspace();
|
||||
this._switchInProgress = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._workspaceAnimation.animateSwitch(from, to, direction, () => {
|
||||
this._shellwm.completed_switch_workspace();
|
||||
this._switchInProgress = false;
|
||||
|
@ -5,6 +5,7 @@ import St from 'gi://St';
|
||||
import * as BoxPointer from './boxpointer.js';
|
||||
import * as Main from './main.js';
|
||||
import * as PopupMenu from './popupMenu.js';
|
||||
import * as RealmWindowMenu from './realms/realmWindowMenu.js'
|
||||
import * as Screenshot from './screenshot.js';
|
||||
|
||||
export class WindowMenu extends PopupMenu.PopupMenu {
|
||||
@ -24,6 +25,23 @@ export class WindowMenu extends PopupMenu.PopupMenu {
|
||||
|
||||
let item;
|
||||
|
||||
let s = RealmWindowMenu.windowMenuDebugString(window);
|
||||
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem(s));
|
||||
|
||||
item = RealmWindowMenu.enableFrameItem(window);
|
||||
if (item) {
|
||||
this.addMenuItem(item);
|
||||
}
|
||||
|
||||
if (!window.is_on_all_workspaces()) {
|
||||
let realmSubmenu = RealmWindowMenu.realmWindowMenu(window);
|
||||
if (realmSubmenu) {
|
||||
this.addMenuItem(realmSubmenu);
|
||||
}
|
||||
}
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
// Translators: entry in the window right click menu.
|
||||
item = this.addAction(C_('window-menu', 'Take Screenshot'), async () => {
|
||||
try {
|
||||
|
@ -113,6 +113,7 @@ class WorkspacesView extends WorkspacesViewBase {
|
||||
this._updateWorkspaces();
|
||||
workspaceManager.connectObject(
|
||||
'notify::n-workspaces', this._updateWorkspaces.bind(this),
|
||||
'context-switched', this._refreshWorkspaces.bind(this),
|
||||
'workspaces-reordered', () => {
|
||||
this._workspaces.sort((a, b) => {
|
||||
return a.metaWorkspace.index() - b.metaWorkspace.index();
|
||||
@ -443,6 +444,13 @@ class WorkspacesView extends WorkspacesViewBase {
|
||||
this.set_child_above_sibling(activeWorkspace, null);
|
||||
}
|
||||
|
||||
_refreshWorkspaces() {
|
||||
for (let ws = this._workspaces.pop(); ws; ws = this._workspaces.pop()) {
|
||||
ws.destroy();
|
||||
}
|
||||
this._updateWorkspaces();
|
||||
}
|
||||
|
||||
_updateWorkspaces() {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
let newNumWorkspaces = workspaceManager.n_workspaces;
|
||||
|
Reference in New Issue
Block a user