From 1155edadcd514ce053beab5e258df13fd2a79dd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Mon, 27 May 2013 23:41:56 +0200
Subject: [PATCH 1/2] window-list: Add context menu
gnome-panel's window list had context menus on buttons, that gave
easy access to common operations like close, minimize and maximize.
Add something similar to the window-list.
https://bugzilla.gnome.org/show_bug.cgi?id=699251
---
extensions/window-list/extension.js | 255 ++++++++++++++++++++++++++++++++----
1 file changed, 232 insertions(+), 23 deletions(-)
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
index 1c46791..7a9d392 100644
--- a/extensions/window-list/extension.js
+++ b/extensions/window-list/extension.js
@@ -37,6 +37,85 @@ function _minimizeOrActivateWindow(window) {
window.activate(global.get_current_time());
}
+function _openMenu(menu) {
+ menu.open();
+
+ let event = Clutter.get_current_event();
+ if (event && event.type() == Clutter.EventType.KEY_RELEASE)
+ menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
+}
+
+
+const WindowContextMenu = new Lang.Class({
+ Name: 'WindowContextMenu',
+ Extends: PopupMenu.PopupMenu,
+
+ _init: function(source, metaWindow) {
+ this.parent(source, 0.5, St.Side.BOTTOM);
+
+ this._metaWindow = metaWindow;
+
+ this._minimizeItem = new PopupMenu.PopupMenuItem('');
+ this._minimizeItem.connect('activate', Lang.bind(this, function() {
+ if (this._metaWindow.minimized)
+ this._metaWindow.unminimize();
+ else
+ this._metaWindow.minimize();
+ }));
+ this.addMenuItem(this._minimizeItem);
+
+ this._notifyMinimizedId =
+ this._metaWindow.connect('notify::minimized',
+ Lang.bind(this, this._updateMinimizeItem));
+ this._updateMinimizeItem();
+
+ this._maximizeItem = new PopupMenu.PopupMenuItem('');
+ this._maximizeItem.connect('activate', Lang.bind(this, function() {
+ if (this._metaWindow.maximized_vertically &&
+ this._metaWindow.maximized_horizontally)
+ this._metaWindow.unmaximize(Meta.MaximizeFlags.HORIZONTAL |
+ Meta.MaximizeFlags.VERTICAL);
+ else
+ this._metaWindow.maximize(Meta.MaximizeFlags.HORIZONTAL |
+ Meta.MaximizeFlags.VERTICAL);
+ }));
+ this.addMenuItem(this._maximizeItem);
+
+ this._notifyMaximizedHId =
+ this._metaWindow.connect('notify::maximized-horizontally',
+ Lang.bind(this, this._updateMaximizeItem));
+ this._notifyMaximizedVId =
+ this._metaWindow.connect('notify::maximized-vertically',
+ Lang.bind(this, this._updateMaximizeItem));
+ this._updateMaximizeItem();
+
+ let item = new PopupMenu.PopupMenuItem(_("Close"));
+ item.connect('activate', Lang.bind(this, function() {
+ this._metaWindow.delete(global.get_current_time());
+ }));
+ this.addMenuItem(item);
+
+ this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+ },
+
+ _updateMinimizeItem: function() {
+ this._minimizeItem.label.text = this._metaWindow.minimized ? _("Unminimize")
+ : _("Minimize");
+ },
+
+ _updateMaximizeItem: function() {
+ let maximized = this._metaWindow.maximized_vertically &&
+ this._metaWindow.maximized_horizontally;
+ this._maximizeItem.label.text = maximized ? _("Unmaximize")
+ : _("Maximize");
+ },
+
+ _onDestroy: function() {
+ this._metaWindow.disconnect(this._notifyMinimizedId);
+ this._metaWindow.disconnect(this._notifyMaximizedHId);
+ this._metaWindow.disconnect(this._notifyMaximizedVId);
+ }
+});
const WindowTitle = new Lang.Class({
Name: 'WindowTitle',
@@ -99,13 +178,22 @@ const WindowButton = new Lang.Class({
this.actor = new St.Button({ style_class: 'window-button',
x_fill: true,
can_focus: true,
+ button_mask: St.ButtonMask.ONE |
+ St.ButtonMask.THREE,
child: this._windowTitle.actor });
this.actor._delegate = this;
+ this._menuManager = new PopupMenu.PopupMenuManager(this);
+ this._contextMenu = new WindowContextMenu(this.actor, this.metaWindow);
+ this._contextMenu.actor.hide();
+ this._menuManager.addMenu(this._contextMenu);
+ Main.uiGroup.add_actor(this._contextMenu.actor);
+
this.actor.connect('allocation-changed',
Lang.bind(this, this._updateIconGeometry));
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+ this.actor.connect('popup-menu', Lang.bind(this, this._onPopupMenu));
this._switchWorkspaceId =
global.window_manager.connect('switch-workspace',
@@ -118,8 +206,22 @@ const WindowButton = new Lang.Class({
this._updateStyle();
},
- _onClicked: function() {
- _minimizeOrActivateWindow(this.metaWindow);
+ _onClicked: function(actor, button) {
+ if (this._contextMenu.isOpen) {
+ this._contextMenu.close();
+ return;
+ }
+
+ if (button == 1)
+ _minimizeOrActivateWindow(this.metaWindow);
+ else
+ _openMenu(this._contextMenu);
+ },
+
+ _onPopupMenu: function(actor) {
+ if (this._contextMenu.isOpen)
+ return;
+ _openMenu(this._contextMenu);
},
_updateStyle: function() {
@@ -151,10 +253,82 @@ const WindowButton = new Lang.Class({
_onDestroy: function() {
global.window_manager.disconnect(this._switchWorkspaceId);
global.display.disconnect(this._notifyFocusId);
+ this._contextMenu.actor.destroy();
}
});
+const AppContextMenu = new Lang.Class({
+ Name: 'AppContextMenu',
+ Extends: PopupMenu.PopupMenu,
+
+ _init: function(source, app) {
+ this.parent(source, 0.5, St.Side.BOTTOM);
+
+ this._app = app;
+
+ this._minimizeItem = new PopupMenu.PopupMenuItem(_("Minimize all"));
+ this._minimizeItem.connect('activate', Lang.bind(this, function() {
+ this._app.get_windows().forEach(function(w) {
+ w.minimize();
+ });
+ }));
+ this.addMenuItem(this._minimizeItem);
+
+ this._unminimizeItem = new PopupMenu.PopupMenuItem(_("Unminimize all"));
+ this._unminimizeItem.connect('activate', Lang.bind(this, function() {
+ this._app.get_windows().forEach(function(w) {
+ w.unminimize();
+ });
+ }));
+ this.addMenuItem(this._unminimizeItem);
+
+ this._maximizeItem = new PopupMenu.PopupMenuItem(_("Maximize all"));
+ this._maximizeItem.connect('activate', Lang.bind(this, function() {
+ this._app.get_windows().forEach(function(w) {
+ w.maximize(Meta.MaximizeFlags.HORIZONTAL |
+ Meta.MaximizeFlags.VERTICAL);
+ });
+ }));
+ this.addMenuItem(this._maximizeItem);
+
+ this._unmaximizeItem = new PopupMenu.PopupMenuItem(_("Unmaximize all"));
+ this._unmaximizeItem.connect('activate', Lang.bind(this, function() {
+ this._app.get_windows().forEach(function(w) {
+ w.unmaximize(Meta.MaximizeFlags.HORIZONTAL |
+ Meta.MaximizeFlags.VERTICAL);
+ });
+ }));
+ this.addMenuItem(this._unmaximizeItem);
+
+ let item = new PopupMenu.PopupMenuItem(_("Close all"));
+ item.connect('activate', Lang.bind(this, function() {
+ this._app.get_windows().forEach(function(w) {
+ w.delete(global.get_current_time());
+ });
+ }));
+ this.addMenuItem(item);
+ },
+
+ open: function(animate) {
+ let windows = this._app.get_windows();
+ this._minimizeItem.actor.visible = windows.some(function(w) {
+ return !w.minimized;
+ });
+ this._unminimizeItem.actor.visible = windows.some(function(w) {
+ return w.minimized;
+ });
+ this._maximizeItem.actor.visible = windows.some(function(w) {
+ return !(w.maximized_horizontally && w.maximized_vertically);
+ });
+ this._unmaximizeItem.actor.visible = windows.some(function(w) {
+ return w.maximized_horizontally && w.maximized_vertically;
+ });
+
+ this.parent(animate);
+ }
+});
+
const AppButton = new Lang.Class({
Name: 'AppButton',
@@ -165,6 +339,8 @@ const AppButton = new Lang.Class({
this.actor = new St.Button({ style_class: 'window-button',
x_fill: true,
can_focus: true,
+ button_mask: St.ButtonMask.ONE |
+ St.ButtonMask.THREE,
child: stack });
this.actor._delegate = this;
@@ -190,6 +366,12 @@ const AppButton = new Lang.Class({
this._menuManager.addMenu(this._menu);
Main.uiGroup.add_actor(this._menu.actor);
+ this._contextMenuManager = new PopupMenu.PopupMenuManager(this);
+ this._appContextMenu = new AppContextMenu(this.actor, this.app);
+ this._appContextMenu.actor.hide();
+ this._contextMenuManager.addMenu(this._appContextMenu);
+ Main.uiGroup.add_actor(this._appContextMenu.actor);
+
this._textureCache = St.TextureCache.get_default();
this._iconThemeChangedId =
this._textureCache.connect('icon-theme-changed', Lang.bind(this,
@@ -198,6 +380,7 @@ const AppButton = new Lang.Class({
}));
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+ this.actor.connect('popup-menu', Lang.bind(this, this._onPopupMenu));
this._switchWorkspaceId =
global.window_manager.connect('switch-workspace',
@@ -258,43 +441,69 @@ const AppButton = new Lang.Class({
this.metaWindow = windows[0];
this._windowTitle = new WindowTitle(this.metaWindow);
this._singleWindowTitle.child = this._windowTitle.actor;
+ this._windowContextMenu = new WindowContextMenu(this.actor, this.metaWindow);
+ Main.uiGroup.add_actor(this._windowContextMenu.actor);
+ this._windowContextMenu.actor.hide();
+ this._contextMenuManager.addMenu(this._windowContextMenu);
}
+ this._contextMenu = this._windowContextMenu;
} else {
if (this._windowTitle) {
this.metaWindow = null;
this._singleWindowTitle.child = null;
this._windowTitle = null;
+ this._windowContextMenu.actor.destroy();
+ this._windowContextMenu = null;
}
+ this._contextMenu = this._appContextMenu;
}
+
},
- _onClicked: function() {
- if (this._menu.isOpen) {
+ _onClicked: function(actor, button) {
+ let menuWasOpen = this._menu.isOpen;
+ if (menuWasOpen)
this._menu.close();
- return;
- }
- let windows = this._getWindowList();
- if (windows.length == 1) {
- _minimizeOrActivateWindow(windows[0]);
- } else {
- this._menu.removeAll();
-
- for (let i = 0; i < windows.length; i++) {
- let windowTitle = new WindowTitle(windows[i]);
- let item = new PopupMenu.PopupBaseMenuItem();
- item.addActor(windowTitle.actor);
- item._window = windows[i];
- this._menu.addMenuItem(item);
- }
- this._menu.open();
+ let contextMenuWasOpen = this._contextMenu.isOpen;
+ if (contextMenuWasOpen)
+ this._contextMenu.close();
+
+ if (button == 1) {
+ if (menuWasOpen)
+ return;
- let event = Clutter.get_current_event();
- if (event && event.type() == Clutter.EventType.KEY_RELEASE)
- this._menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
+ let windows = this._getWindowList();
+ if (windows.length == 1) {
+ if (contextMenuWasOpen)
+ return;
+ _minimizeOrActivateWindow(windows[0]);
+ } else {
+ this._menu.removeAll();
+
+ for (let i = 0; i < windows.length; i++) {
+ let windowTitle = new WindowTitle(windows[i]);
+ let item = new PopupMenu.PopupBaseMenuItem();
+ item.actor.add_actor(windowTitle.actor);
+ item._window = windows[i];
+ this._menu.addMenuItem(item);
+ }
+ _openMenu(this._menu);
+ }
+ } else {
+ if (contextMenuWasOpen)
+ return;
+ _openMenu(this._contextMenu);
}
},
+ _onPopupMenu: function(actor) {
+ if (this._menu.isOpen || this._contextMenu.isOpen)
+ return;
+ _openMenu(this._contextMenu);
+ },
+
+
_onMenuActivate: function(menu, child) {
child._window.activate(global.get_current_time());
},
--
1.8.3.1