Implement window menus in gnome-shell
https://bugzilla.gnome.org/show_bug.cgi?id=726352
This commit is contained in:
parent
c9190294bc
commit
e7af257814
@ -84,6 +84,7 @@
|
|||||||
<file>ui/userWidget.js</file>
|
<file>ui/userWidget.js</file>
|
||||||
<file>ui/viewSelector.js</file>
|
<file>ui/viewSelector.js</file>
|
||||||
<file>ui/windowAttentionHandler.js</file>
|
<file>ui/windowAttentionHandler.js</file>
|
||||||
|
<file>ui/windowMenu.js</file>
|
||||||
<file>ui/windowManager.js</file>
|
<file>ui/windowManager.js</file>
|
||||||
<file>ui/workspace.js</file>
|
<file>ui/workspace.js</file>
|
||||||
<file>ui/workspaceSwitcherPopup.js</file>
|
<file>ui/workspaceSwitcherPopup.js</file>
|
||||||
|
@ -15,6 +15,7 @@ const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
|
|||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
const WindowMenu = imports.ui.windowMenu;
|
||||||
|
|
||||||
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
||||||
const WINDOW_ANIMATION_TIME = 0.25;
|
const WINDOW_ANIMATION_TIME = 0.25;
|
||||||
@ -480,6 +481,7 @@ const WindowManager = new Lang.Class({
|
|||||||
this._shellwm.connect('switch-workspace', Lang.bind(this, this._switchWorkspace));
|
this._shellwm.connect('switch-workspace', Lang.bind(this, this._switchWorkspace));
|
||||||
this._shellwm.connect('show-tile-preview', Lang.bind(this, this._showTilePreview));
|
this._shellwm.connect('show-tile-preview', Lang.bind(this, this._showTilePreview));
|
||||||
this._shellwm.connect('hide-tile-preview', Lang.bind(this, this._hideTilePreview));
|
this._shellwm.connect('hide-tile-preview', Lang.bind(this, this._hideTilePreview));
|
||||||
|
this._shellwm.connect('show-window-menu', Lang.bind(this, this._showWindowMenu));
|
||||||
this._shellwm.connect('minimize', Lang.bind(this, this._minimizeWindow));
|
this._shellwm.connect('minimize', Lang.bind(this, this._minimizeWindow));
|
||||||
this._shellwm.connect('maximize', Lang.bind(this, this._maximizeWindow));
|
this._shellwm.connect('maximize', Lang.bind(this, this._maximizeWindow));
|
||||||
this._shellwm.connect('unmaximize', Lang.bind(this, this._unmaximizeWindow));
|
this._shellwm.connect('unmaximize', Lang.bind(this, this._unmaximizeWindow));
|
||||||
@ -669,6 +671,8 @@ const WindowManager = new Lang.Class({
|
|||||||
this._dimWindow(this._dimmedWindows[i]);
|
this._dimWindow(this._dimmedWindows[i]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this._windowMenuManager = new WindowMenu.WindowMenuManager();
|
||||||
|
|
||||||
if (Main.sessionMode.hasWorkspaces)
|
if (Main.sessionMode.hasWorkspaces)
|
||||||
this._workspaceTracker = new WorkspaceTracker(this);
|
this._workspaceTracker = new WorkspaceTracker(this);
|
||||||
|
|
||||||
@ -1159,6 +1163,10 @@ const WindowManager = new Lang.Class({
|
|||||||
this._tilePreview.hide();
|
this._tilePreview.hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_showWindowMenu: function(shellwm, window) {
|
||||||
|
this._windowMenuManager.showForWindow(window);
|
||||||
|
},
|
||||||
|
|
||||||
_startAppSwitcher : function(display, screen, window, binding) {
|
_startAppSwitcher : function(display, screen, window, binding) {
|
||||||
/* prevent a corner case where both popups show up at once */
|
/* prevent a corner case where both popups show up at once */
|
||||||
if (this._workspaceSwitcherPopup != null)
|
if (this._workspaceSwitcherPopup != null)
|
||||||
|
145
js/ui/windowMenu.js
Normal file
145
js/ui/windowMenu.js
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*
|
||||||
|
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
|
const WindowMenu = new Lang.Class({
|
||||||
|
Name: 'WindowMenu',
|
||||||
|
Extends: PopupMenu.PopupMenu,
|
||||||
|
|
||||||
|
_init: function(window) {
|
||||||
|
this.parent(Main.layoutManager.dummyCursor, 0, St.Side.TOP);
|
||||||
|
|
||||||
|
this.actor.add_style_class_name('window-menu');
|
||||||
|
|
||||||
|
Main.layoutManager.uiGroup.add_actor(this.actor);
|
||||||
|
this.actor.hide();
|
||||||
|
|
||||||
|
this._buildMenu(window);
|
||||||
|
},
|
||||||
|
|
||||||
|
_buildMenu: function(window) {
|
||||||
|
let type = window.get_window_type();
|
||||||
|
|
||||||
|
let item;
|
||||||
|
|
||||||
|
item = this.addAction(_("Minimize"), Lang.bind(this, function(event) {
|
||||||
|
window.minimize();
|
||||||
|
}));
|
||||||
|
if (!window.can_minimize())
|
||||||
|
item.setSensitive(false);
|
||||||
|
|
||||||
|
if (window.get_maximized()) {
|
||||||
|
item = this.addAction(_("Unmaximize"), Lang.bind(this, function() {
|
||||||
|
window.unmaximize(Meta.MaximizeFlags.BOTH);
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
item = this.addAction(_("Maximize"), Lang.bind(this, function() {
|
||||||
|
window.maximize(Meta.MaximizeFlags.BOTH);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (!window.can_maximize())
|
||||||
|
item.setSensitive(false);
|
||||||
|
|
||||||
|
item = this.addAction(_("Move"), Lang.bind(this, function(event) {
|
||||||
|
window.begin_grab_op(Meta.GrabOp.KEYBOARD_MOVING, true, event.get_time());
|
||||||
|
}));
|
||||||
|
if (!window.allows_move())
|
||||||
|
item.setSensitive(false);
|
||||||
|
|
||||||
|
item = this.addAction(_("Resize"), Lang.bind(this, function(event) {
|
||||||
|
window.begin_grab_op(Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN, true, event.get_time());
|
||||||
|
}));
|
||||||
|
if (!window.allows_resize())
|
||||||
|
item.setSensitive(false);
|
||||||
|
|
||||||
|
if (!window.titlebar_is_onscreen() && type != Meta.WindowType.DOCK && type != Meta.WindowType.DESKTOP) {
|
||||||
|
this.addAction(_("Move Titlebar Onscreen"), Lang.bind(this, function(event) {
|
||||||
|
window.shove_titlebar_onscreen();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
item = this.addAction("Always on Top", Lang.bind(this, function() {
|
||||||
|
if (window.is_above())
|
||||||
|
window.unmake_above();
|
||||||
|
else
|
||||||
|
window.make_above();
|
||||||
|
}));
|
||||||
|
if (window.is_above())
|
||||||
|
item.setOrnament(PopupMenu.Ornament.DOT);
|
||||||
|
if (window.get_maximized() ||
|
||||||
|
type == Meta.WindowType.DOCK ||
|
||||||
|
type == Meta.WindowType.DESKTOP ||
|
||||||
|
type == Meta.WindowType.SPLASHSCREEN)
|
||||||
|
item.setSensitive(false);
|
||||||
|
|
||||||
|
if (!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"), Lang.bind(this, function() {
|
||||||
|
if (isSticky)
|
||||||
|
window.unstick();
|
||||||
|
else
|
||||||
|
window.stick();
|
||||||
|
}));
|
||||||
|
if (isSticky)
|
||||||
|
item.setOrnament(PopupMenu.Ornament.DOT);
|
||||||
|
if (window.is_always_on_all_workspaces())
|
||||||
|
item.setSensitive(false);
|
||||||
|
|
||||||
|
let nWorkspaces = global.screen.n_workspaces;
|
||||||
|
|
||||||
|
if (!isSticky) {
|
||||||
|
let workspace = window.get_workspace();
|
||||||
|
let idx = workspace.index();
|
||||||
|
if (idx > 0) {
|
||||||
|
this.addAction(_("Move to Workspace Up"), Lang.bind(this, function(event) {
|
||||||
|
window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.UP));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (idx < nWorkspaces) {
|
||||||
|
this.addAction(_("Move to Workspace Down"), Lang.bind(this, function(event) {
|
||||||
|
window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.DOWN));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
|
||||||
|
item = this.addAction(_("Close"), Lang.bind(this, function(event) {
|
||||||
|
window.close(event.get_time());
|
||||||
|
}));
|
||||||
|
if (!window.can_close())
|
||||||
|
item.setSensitive(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const WindowMenuManager = new Lang.Class({
|
||||||
|
Name: 'WindowMenuManager',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._manager = new PopupMenu.PopupMenuManager({ actor: Main.layoutManager.dummyCursor });
|
||||||
|
},
|
||||||
|
|
||||||
|
showForWindow: function(window) {
|
||||||
|
let menu = new WindowMenu(window);
|
||||||
|
this._manager.addMenu(menu);
|
||||||
|
|
||||||
|
let [x, y] = global.get_pointer();
|
||||||
|
Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0);
|
||||||
|
menu.open(BoxPointer.PopupAnimation.NONE);
|
||||||
|
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||||
|
menu.connect('open-state-changed', Lang.bind(this, function(menu_, isOpen) {
|
||||||
|
if (!isOpen)
|
||||||
|
menu.destroy();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
@ -73,7 +73,8 @@ static void gnome_shell_plugin_show_tile_preview (MetaPlugin *plugin,
|
|||||||
MetaRectangle *tile_rect,
|
MetaRectangle *tile_rect,
|
||||||
int tile_monitor);
|
int tile_monitor);
|
||||||
static void gnome_shell_plugin_hide_tile_preview (MetaPlugin *plugin);
|
static void gnome_shell_plugin_hide_tile_preview (MetaPlugin *plugin);
|
||||||
|
static void gnome_shell_plugin_show_window_menu (MetaPlugin *plugin,
|
||||||
|
MetaWindow *window);
|
||||||
|
|
||||||
static gboolean gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
|
static gboolean gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
|
||||||
XEvent *event);
|
XEvent *event);
|
||||||
@ -140,6 +141,7 @@ gnome_shell_plugin_class_init (GnomeShellPluginClass *klass)
|
|||||||
|
|
||||||
plugin_class->show_tile_preview = gnome_shell_plugin_show_tile_preview;
|
plugin_class->show_tile_preview = gnome_shell_plugin_show_tile_preview;
|
||||||
plugin_class->hide_tile_preview = gnome_shell_plugin_hide_tile_preview;
|
plugin_class->hide_tile_preview = gnome_shell_plugin_hide_tile_preview;
|
||||||
|
plugin_class->show_window_menu = gnome_shell_plugin_show_window_menu;
|
||||||
|
|
||||||
plugin_class->xevent_filter = gnome_shell_plugin_xevent_filter;
|
plugin_class->xevent_filter = gnome_shell_plugin_xevent_filter;
|
||||||
plugin_class->keybinding_filter = gnome_shell_plugin_keybinding_filter;
|
plugin_class->keybinding_filter = gnome_shell_plugin_keybinding_filter;
|
||||||
@ -303,6 +305,13 @@ gnome_shell_plugin_hide_tile_preview (MetaPlugin *plugin)
|
|||||||
_shell_wm_hide_tile_preview (get_shell_wm ());
|
_shell_wm_hide_tile_preview (get_shell_wm ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gnome_shell_plugin_show_window_menu (MetaPlugin *plugin,
|
||||||
|
MetaWindow *window)
|
||||||
|
{
|
||||||
|
_shell_wm_show_window_menu (get_shell_wm (), window);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
|
gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
|
||||||
XEvent *xev)
|
XEvent *xev)
|
||||||
|
@ -40,6 +40,8 @@ void _shell_wm_show_tile_preview (ShellWM *wm,
|
|||||||
MetaRectangle *tile_rect,
|
MetaRectangle *tile_rect,
|
||||||
int tile_monitor);
|
int tile_monitor);
|
||||||
void _shell_wm_hide_tile_preview (ShellWM *wm);
|
void _shell_wm_hide_tile_preview (ShellWM *wm);
|
||||||
|
void _shell_wm_show_window_menu (ShellWM *wm,
|
||||||
|
MetaWindow *window);
|
||||||
|
|
||||||
gboolean _shell_wm_filter_keybinding (ShellWM *wm,
|
gboolean _shell_wm_filter_keybinding (ShellWM *wm,
|
||||||
MetaKeyBinding *binding);
|
MetaKeyBinding *binding);
|
||||||
|
@ -28,6 +28,7 @@ enum
|
|||||||
KILL_WINDOW_EFFECTS,
|
KILL_WINDOW_EFFECTS,
|
||||||
SHOW_TILE_PREVIEW,
|
SHOW_TILE_PREVIEW,
|
||||||
HIDE_TILE_PREVIEW,
|
HIDE_TILE_PREVIEW,
|
||||||
|
SHOW_WINDOW_MENU,
|
||||||
FILTER_KEYBINDING,
|
FILTER_KEYBINDING,
|
||||||
CONFIRM_DISPLAY_CHANGE,
|
CONFIRM_DISPLAY_CHANGE,
|
||||||
|
|
||||||
@ -135,6 +136,13 @@ shell_wm_class_init (ShellWMClass *klass)
|
|||||||
0,
|
0,
|
||||||
NULL, NULL, NULL,
|
NULL, NULL, NULL,
|
||||||
G_TYPE_NONE, 0);
|
G_TYPE_NONE, 0);
|
||||||
|
shell_wm_signals[SHOW_WINDOW_MENU] =
|
||||||
|
g_signal_new ("show-window-menu",
|
||||||
|
G_TYPE_FROM_CLASS (klass),
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
0, NULL, NULL, NULL,
|
||||||
|
G_TYPE_NONE, 1,
|
||||||
|
META_TYPE_WINDOW);
|
||||||
shell_wm_signals[FILTER_KEYBINDING] =
|
shell_wm_signals[FILTER_KEYBINDING] =
|
||||||
g_signal_new ("filter-keybinding",
|
g_signal_new ("filter-keybinding",
|
||||||
G_TYPE_FROM_CLASS (klass),
|
G_TYPE_FROM_CLASS (klass),
|
||||||
@ -288,6 +296,12 @@ _shell_wm_hide_tile_preview (ShellWM *wm)
|
|||||||
g_signal_emit (wm, shell_wm_signals[HIDE_TILE_PREVIEW], 0);
|
g_signal_emit (wm, shell_wm_signals[HIDE_TILE_PREVIEW], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_shell_wm_show_window_menu (ShellWM *wm,
|
||||||
|
MetaWindow *window)
|
||||||
|
{
|
||||||
|
g_signal_emit (wm, shell_wm_signals[SHOW_WINDOW_MENU], 0, window);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_shell_wm_minimize (ShellWM *wm,
|
_shell_wm_minimize (ShellWM *wm,
|
||||||
|
Loading…
Reference in New Issue
Block a user