Implement window menus in gnome-shell

https://bugzilla.gnome.org/show_bug.cgi?id=726352
This commit is contained in:
Jasper St. Pierre 2014-03-13 18:51:10 -04:00
parent c9190294bc
commit e7af257814
6 changed files with 180 additions and 1 deletions

View File

@ -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>

View 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
View 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();
}));
},
});

View File

@ -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)

View File

@ -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);

View File

@ -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,