
The string "Take Screenshot" is used both by the action in the window menu and the screenshot/recording toggle in the UI. However in some languages, it would be best to use different strings for the two use cases. Address this by adding context to the window menu string, so it can be differentiated from the string in the screenshot UI. Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/7979 Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3503>
260 lines
9.4 KiB
JavaScript
260 lines
9.4 KiB
JavaScript
import Clutter from 'gi://Clutter';
|
|
import Meta from 'gi://Meta';
|
|
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 Screenshot from './screenshot.js';
|
|
|
|
export class WindowMenu extends PopupMenu.PopupMenu {
|
|
constructor(window, sourceActor) {
|
|
super(sourceActor, 0, St.Side.TOP);
|
|
|
|
this.actor.add_style_class_name('window-menu');
|
|
|
|
Main.layoutManager.uiGroup.add_child(this.actor);
|
|
this.actor.hide();
|
|
|
|
this._buildMenu(window);
|
|
}
|
|
|
|
_buildMenu(window) {
|
|
let type = window.get_window_type();
|
|
|
|
let item;
|
|
|
|
// Translators: entry in the window right click menu.
|
|
item = this.addAction(C_('window-menu', 'Take Screenshot'), async () => {
|
|
try {
|
|
const actor = window.get_compositor_private();
|
|
const content = actor.paint_to_content(null);
|
|
const texture = content.get_texture();
|
|
|
|
await Screenshot.captureScreenshot(texture, null, 1, null);
|
|
} catch (e) {
|
|
logError(e, 'Error capturing screenshot');
|
|
}
|
|
});
|
|
|
|
item = this.addAction(_('Hide'), () => {
|
|
window.minimize();
|
|
});
|
|
if (!window.can_minimize())
|
|
item.setSensitive(false);
|
|
|
|
if (window.get_maximized()) {
|
|
item = this.addAction(_('Restore'), () => {
|
|
window.unmaximize(Meta.MaximizeFlags.BOTH);
|
|
});
|
|
} else {
|
|
item = this.addAction(_('Maximize'), () => {
|
|
window.maximize(Meta.MaximizeFlags.BOTH);
|
|
});
|
|
}
|
|
if (!window.can_maximize())
|
|
item.setSensitive(false);
|
|
|
|
item = this.addAction(_('Move'), event => {
|
|
const device = event.get_device();
|
|
const seat = device.get_seat();
|
|
const deviceType = device.get_device_type();
|
|
const pointer =
|
|
deviceType === Clutter.InputDeviceType.POINTER_DEVICE ||
|
|
deviceType === Clutter.InputDeviceType.TABLET_DEVICE ||
|
|
deviceType === Clutter.InputDeviceType.PEN_DEVICE ||
|
|
deviceType === Clutter.InputDeviceType.ERASER_DEVICE
|
|
? device : seat.get_pointer();
|
|
|
|
window.begin_grab_op(
|
|
Meta.GrabOp.KEYBOARD_MOVING,
|
|
pointer, null,
|
|
event.get_time(),
|
|
null);
|
|
});
|
|
if (!window.allows_move())
|
|
item.setSensitive(false);
|
|
|
|
item = this.addAction(_('Resize'), event => {
|
|
const device = event.get_device();
|
|
const seat = device.get_seat();
|
|
const deviceType = device.get_device_type();
|
|
const pointer =
|
|
deviceType === Clutter.InputDeviceType.POINTER_DEVICE ||
|
|
deviceType === Clutter.InputDeviceType.TABLET_DEVICE ||
|
|
deviceType === Clutter.InputDeviceType.PEN_DEVICE ||
|
|
deviceType === Clutter.InputDeviceType.ERASER_DEVICE
|
|
? device : seat.get_pointer();
|
|
|
|
window.begin_grab_op(
|
|
Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN,
|
|
pointer, null,
|
|
event.get_time(),
|
|
null);
|
|
});
|
|
if (!window.allows_resize())
|
|
item.setSensitive(false);
|
|
|
|
item = this.addAction(_('Always on Top'), () => {
|
|
if (window.is_above())
|
|
window.unmake_above();
|
|
else
|
|
window.make_above();
|
|
});
|
|
if (window.is_above())
|
|
item.setOrnament(PopupMenu.Ornament.CHECK);
|
|
if (window.get_maximized() === Meta.MaximizeFlags.BOTH ||
|
|
type === Meta.WindowType.DOCK ||
|
|
type === Meta.WindowType.DESKTOP ||
|
|
type === Meta.WindowType.SPLASHSCREEN)
|
|
item.setSensitive(false);
|
|
|
|
if (Main.sessionMode.hasWorkspaces &&
|
|
(!Meta.prefs_get_workspaces_only_on_primary() ||
|
|
window.is_on_primary_monitor())) {
|
|
let isSticky = window.is_on_all_workspaces();
|
|
|
|
item = this.addAction(_('Always on Visible Workspace'), () => {
|
|
if (isSticky)
|
|
window.unstick();
|
|
else
|
|
window.stick();
|
|
});
|
|
if (isSticky)
|
|
item.setOrnament(PopupMenu.Ornament.CHECK);
|
|
if (window.is_always_on_all_workspaces())
|
|
item.setSensitive(false);
|
|
|
|
if (!isSticky) {
|
|
let workspace = window.get_workspace();
|
|
if (workspace !== workspace.get_neighbor(Meta.MotionDirection.LEFT)) {
|
|
this.addAction(_('Move to Workspace Left'), () => {
|
|
let dir = Meta.MotionDirection.LEFT;
|
|
window.change_workspace(workspace.get_neighbor(dir));
|
|
});
|
|
}
|
|
if (workspace !== workspace.get_neighbor(Meta.MotionDirection.RIGHT)) {
|
|
this.addAction(_('Move to Workspace Right'), () => {
|
|
let dir = Meta.MotionDirection.RIGHT;
|
|
window.change_workspace(workspace.get_neighbor(dir));
|
|
});
|
|
}
|
|
if (workspace !== workspace.get_neighbor(Meta.MotionDirection.UP)) {
|
|
this.addAction(_('Move to Workspace Up'), () => {
|
|
let dir = Meta.MotionDirection.UP;
|
|
window.change_workspace(workspace.get_neighbor(dir));
|
|
});
|
|
}
|
|
if (workspace !== workspace.get_neighbor(Meta.MotionDirection.DOWN)) {
|
|
this.addAction(_('Move to Workspace Down'), () => {
|
|
let dir = Meta.MotionDirection.DOWN;
|
|
window.change_workspace(workspace.get_neighbor(dir));
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
let display = global.display;
|
|
let nMonitors = display.get_n_monitors();
|
|
let monitorIndex = window.get_monitor();
|
|
if (nMonitors > 1 && monitorIndex >= 0) {
|
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
|
|
let dir = Meta.DisplayDirection.UP;
|
|
let upMonitorIndex =
|
|
display.get_monitor_neighbor_index(monitorIndex, dir);
|
|
if (upMonitorIndex !== -1) {
|
|
this.addAction(_('Move to Monitor Up'), () => {
|
|
window.move_to_monitor(upMonitorIndex);
|
|
});
|
|
}
|
|
|
|
dir = Meta.DisplayDirection.DOWN;
|
|
let downMonitorIndex =
|
|
display.get_monitor_neighbor_index(monitorIndex, dir);
|
|
if (downMonitorIndex !== -1) {
|
|
this.addAction(_('Move to Monitor Down'), () => {
|
|
window.move_to_monitor(downMonitorIndex);
|
|
});
|
|
}
|
|
|
|
dir = Meta.DisplayDirection.LEFT;
|
|
let leftMonitorIndex =
|
|
display.get_monitor_neighbor_index(monitorIndex, dir);
|
|
if (leftMonitorIndex !== -1) {
|
|
this.addAction(_('Move to Monitor Left'), () => {
|
|
window.move_to_monitor(leftMonitorIndex);
|
|
});
|
|
}
|
|
|
|
dir = Meta.DisplayDirection.RIGHT;
|
|
let rightMonitorIndex =
|
|
display.get_monitor_neighbor_index(monitorIndex, dir);
|
|
if (rightMonitorIndex !== -1) {
|
|
this.addAction(_('Move to Monitor Right'), () => {
|
|
window.move_to_monitor(rightMonitorIndex);
|
|
});
|
|
}
|
|
}
|
|
|
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
|
|
item = this.addAction(_('Close'), event => {
|
|
window.delete(event.get_time());
|
|
});
|
|
if (!window.can_close())
|
|
item.setSensitive(false);
|
|
}
|
|
|
|
addAction(label, callback) {
|
|
const item = super.addAction(label, callback);
|
|
item.setOrnament(PopupMenu.Ornament.NONE);
|
|
return item;
|
|
}
|
|
}
|
|
|
|
export class WindowMenuManager {
|
|
constructor() {
|
|
this._manager = new PopupMenu.PopupMenuManager(Main.layoutManager.dummyCursor);
|
|
|
|
this._sourceActor = new St.Widget({reactive: true, visible: false});
|
|
this._sourceActor.connect('button-press-event', () => {
|
|
this._manager.activeMenu.toggle();
|
|
});
|
|
Main.uiGroup.add_child(this._sourceActor);
|
|
}
|
|
|
|
showWindowMenuForWindow(window, type, rect) {
|
|
if (!Main.sessionMode.hasWmMenus)
|
|
return;
|
|
|
|
if (type !== Meta.WindowMenuType.WM)
|
|
throw new Error('Unsupported window menu type');
|
|
let menu = new WindowMenu(window, this._sourceActor);
|
|
|
|
this._manager.addMenu(menu);
|
|
|
|
menu.connect('activate', () => {
|
|
window.check_alive(global.get_current_time());
|
|
});
|
|
let destroyId = window.connect('unmanaged', () => {
|
|
menu.close();
|
|
});
|
|
|
|
this._sourceActor.set_size(Math.max(1, rect.width), Math.max(1, rect.height));
|
|
this._sourceActor.set_position(rect.x, rect.y);
|
|
this._sourceActor.show();
|
|
|
|
menu.open(BoxPointer.PopupAnimation.FADE);
|
|
menu.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
|
|
menu.connect('open-state-changed', (menu_, isOpen) => {
|
|
if (isOpen)
|
|
return;
|
|
|
|
this._sourceActor.hide();
|
|
menu.destroy();
|
|
window.disconnect(destroyId);
|
|
});
|
|
}
|
|
}
|