[appDisplay] port from ShellMenu to PopupMenu
This fixes the style, and also makes it keyboard navigable https://bugzilla.gnome.org/show_bug.cgi?id=619541
This commit is contained in:
parent
7fbf8ae4c9
commit
748739ed9c
@ -586,28 +586,7 @@ StTooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-well-menu {
|
.app-well-menu {
|
||||||
border: 1px solid #5f5f5f;
|
font-size: 12px
|
||||||
border-radius: 4px;
|
|
||||||
padding: 4px;
|
|
||||||
background-color: rgba(0,0,0,0.9);
|
|
||||||
color: #ffffff;
|
|
||||||
spacing: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-well-menu-arrow {
|
|
||||||
border-color: #5f5f5f;
|
|
||||||
color: rgba(0,0,0,0.9);
|
|
||||||
width: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-well-menu-item:hover {
|
|
||||||
background-color: #1e1e1e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-well-menu-separator {
|
|
||||||
padding-top: 1px;
|
|
||||||
border-bottom: 1px solid #5f5f5f;
|
|
||||||
height: 1px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Places */
|
/* Places */
|
||||||
|
@ -18,6 +18,7 @@ const DND = imports.ui.dnd;
|
|||||||
const GenericDisplay = imports.ui.genericDisplay;
|
const GenericDisplay = imports.ui.genericDisplay;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Overview = imports.ui.overview;
|
const Overview = imports.ui.overview;
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const Search = imports.ui.search;
|
const Search = imports.ui.search;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const Workspace = imports.ui.workspace;
|
const Workspace = imports.ui.workspace;
|
||||||
@ -477,7 +478,9 @@ AppWellIcon.prototype = {
|
|||||||
this.actor.set_child(this._icon.actor);
|
this.actor.set_child(this._icon.actor);
|
||||||
|
|
||||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||||
|
|
||||||
this._menu = null;
|
this._menu = null;
|
||||||
|
this._menuManager = new PopupMenu.PopupMenuManager(this);
|
||||||
|
|
||||||
this._draggable = DND.makeDraggable(this.actor);
|
this._draggable = DND.makeDraggable(this.actor);
|
||||||
this._draggable.connect('drag-begin', Lang.bind(this,
|
this._draggable.connect('drag-begin', Lang.bind(this,
|
||||||
@ -527,7 +530,7 @@ AppWellIcon.prototype = {
|
|||||||
this._removeMenuTimeout();
|
this._removeMenuTimeout();
|
||||||
this._menuTimeoutId = Mainloop.timeout_add(MENU_POPUP_TIMEOUT,
|
this._menuTimeoutId = Mainloop.timeout_add(MENU_POPUP_TIMEOUT,
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this.popupMenu(button);
|
this.popupMenu();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -547,9 +550,7 @@ AppWellIcon.prototype = {
|
|||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
}
|
}
|
||||||
} else if (button == 3) {
|
} else if (button == 3) {
|
||||||
// Don't bind to the right click here; we want left click outside the
|
this.popupMenu();
|
||||||
// area to deactivate as well.
|
|
||||||
this.popupMenu(0);
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -558,7 +559,7 @@ AppWellIcon.prototype = {
|
|||||||
return this.app.get_id();
|
return this.app.get_id();
|
||||||
},
|
},
|
||||||
|
|
||||||
popupMenu: function(activatingButton) {
|
popupMenu: function() {
|
||||||
this._removeMenuTimeout();
|
this._removeMenuTimeout();
|
||||||
this.actor.fake_release();
|
this.actor.fake_release();
|
||||||
|
|
||||||
@ -577,9 +578,12 @@ AppWellIcon.prototype = {
|
|||||||
this._onMenuPoppedDown();
|
this._onMenuPoppedDown();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this._menuManager.addMenu(this._menu, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._menu.popup(activatingButton);
|
this._menu.popup();
|
||||||
|
this._menuManager.grab();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -646,6 +650,11 @@ AppWellIcon.prototype = {
|
|||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// called by this._menuManager when it has the grab
|
||||||
|
menuEventFilter: function(event) {
|
||||||
|
return this._menu.menuEventFilter(event);
|
||||||
|
},
|
||||||
|
|
||||||
shellWorkspaceLaunch : function() {
|
shellWorkspaceLaunch : function() {
|
||||||
this.app.open_new_window();
|
this.app.open_new_window();
|
||||||
},
|
},
|
||||||
@ -667,103 +676,34 @@ function AppIconMenu(source) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AppIconMenu.prototype = {
|
AppIconMenu.prototype = {
|
||||||
|
__proto__: PopupMenu.PopupMenu.prototype,
|
||||||
|
|
||||||
_init: function(source) {
|
_init: function(source) {
|
||||||
|
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, St.Align.MIDDLE, St.Side.LEFT, 0);
|
||||||
|
|
||||||
this._source = source;
|
this._source = source;
|
||||||
|
|
||||||
this.actor = new Shell.GenericContainer({ reactive: true });
|
this.connect('active-changed', Lang.bind(this, this._onActiveChanged));
|
||||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
this.connect('activate', Lang.bind(this, this._onActivate));
|
||||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
this.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
|
||||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
|
||||||
|
|
||||||
this._windowContainer = new Shell.Menu({ style_class: 'app-well-menu',
|
this.actor.add_style_class_name('app-well-menu');
|
||||||
vertical: true,
|
|
||||||
width: Main.overview._dash.actor.width });
|
|
||||||
this._windowContainer.connect('unselected', Lang.bind(this, this._onItemUnselected));
|
|
||||||
this._windowContainer.connect('selected', Lang.bind(this, this._onItemSelected));
|
|
||||||
this._windowContainer.connect('cancelled', Lang.bind(this, this._onWindowSelectionCancelled));
|
|
||||||
this._windowContainer.connect('activate', Lang.bind(this, this._onItemActivate));
|
|
||||||
this.actor.add_actor(this._windowContainer);
|
|
||||||
|
|
||||||
// Stay popped up on release over application icon
|
|
||||||
this._windowContainer.set_persistent_source(this._source.actor);
|
|
||||||
|
|
||||||
// Intercept events while the menu has the pointer grab to do window-related effects
|
|
||||||
this._windowContainer.connect('enter-event', Lang.bind(this, this._onMenuEnter));
|
|
||||||
this._windowContainer.connect('leave-event', Lang.bind(this, this._onMenuLeave));
|
|
||||||
this._windowContainer.connect('button-release-event', Lang.bind(this, this._onMenuButtonRelease));
|
|
||||||
|
|
||||||
this._arrow = new St.DrawingArea({ style_class: 'app-well-menu-arrow' });
|
|
||||||
this._arrow.connect('repaint', Lang.bind(this, function (area) {
|
|
||||||
Shell.draw_box_pointer(area, Shell.PointerDirection.LEFT);
|
|
||||||
}));
|
|
||||||
this.actor.add_actor(this._arrow);
|
|
||||||
|
|
||||||
// Chain our visibility and lifecycle to that of the source
|
// Chain our visibility and lifecycle to that of the source
|
||||||
source.actor.connect('notify::mapped', Lang.bind(this, function () {
|
source.actor.connect('notify::mapped', Lang.bind(this, function () {
|
||||||
if (!source.actor.mapped)
|
if (!source.actor.mapped)
|
||||||
this._windowContainer.popdown();
|
this.close();
|
||||||
}));
|
}));
|
||||||
source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));
|
source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));
|
||||||
|
|
||||||
Main.uiGroup.add_actor(this.actor);
|
Main.uiGroup.add_actor(this.actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
|
||||||
let [menuMin, menuNatural] = this._windowContainer.get_preferred_width(forHeight);
|
|
||||||
let [arrowMin, arrowNatural] = this._arrow.get_preferred_width(forHeight);
|
|
||||||
alloc.min_size = menuMin + arrowMin;
|
|
||||||
alloc.natural_size = menuNatural + arrowNatural;
|
|
||||||
},
|
|
||||||
|
|
||||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
|
||||||
let [min, natural] = this._windowContainer.get_preferred_height(forWidth);
|
|
||||||
alloc.min_size = min;
|
|
||||||
alloc.natural_size = natural;
|
|
||||||
},
|
|
||||||
|
|
||||||
_allocate: function(actor, box, flags) {
|
|
||||||
let childBox = new Clutter.ActorBox();
|
|
||||||
let themeNode = this._windowContainer.get_theme_node();
|
|
||||||
|
|
||||||
let width = box.x2 - box.x1;
|
|
||||||
let height = box.y2 - box.y1;
|
|
||||||
|
|
||||||
let [arrowMinWidth, arrowWidth] = this._arrow.get_preferred_width(height);
|
|
||||||
|
|
||||||
childBox.x1 = 0;
|
|
||||||
childBox.x2 = arrowWidth;
|
|
||||||
childBox.y1 = Math.floor((height / 2) - (arrowWidth / 2));
|
|
||||||
childBox.y2 = childBox.y1 + arrowWidth;
|
|
||||||
this._arrow.allocate(childBox, flags);
|
|
||||||
|
|
||||||
// Ensure the arrow is above the border area
|
|
||||||
let border = themeNode.get_border_width(St.Side.LEFT);
|
|
||||||
childBox.x1 = arrowWidth - border;
|
|
||||||
childBox.x2 = width;
|
|
||||||
childBox.y1 = 0;
|
|
||||||
childBox.y2 = height;
|
|
||||||
this._windowContainer.allocate(childBox, flags);
|
|
||||||
},
|
|
||||||
|
|
||||||
_redisplay: function() {
|
_redisplay: function() {
|
||||||
this._windowContainer.remove_all();
|
this.removeAll();
|
||||||
|
|
||||||
let windows = this._source.app.get_windows();
|
let windows = this._source.app.get_windows();
|
||||||
|
|
||||||
this._windowContainer.show();
|
|
||||||
|
|
||||||
let iconsDiffer = false;
|
|
||||||
let texCache = St.TextureCache.get_default();
|
|
||||||
if (windows.length > 0) {
|
|
||||||
let firstIcon = windows[0].mini_icon;
|
|
||||||
for (let i = 1; i < windows.length; i++) {
|
|
||||||
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
|
|
||||||
iconsDiffer = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display the app windows menu items and the separator between windows
|
// Display the app windows menu items and the separator between windows
|
||||||
// of the current desktop and other windows.
|
// of the current desktop and other windows.
|
||||||
let activeWorkspace = global.screen.get_active_workspace();
|
let activeWorkspace = global.screen.get_active_workspace();
|
||||||
@ -774,8 +714,8 @@ AppIconMenu.prototype = {
|
|||||||
this._appendSeparator();
|
this._appendSeparator();
|
||||||
separatorShown = true;
|
separatorShown = true;
|
||||||
}
|
}
|
||||||
let box = this._appendMenuItem(windows[i].title);
|
let item = this._appendMenuItem(windows[i].title);
|
||||||
box._window = windows[i];
|
item._window = windows[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (windows.length > 0)
|
if (windows.length > 0)
|
||||||
@ -794,45 +734,53 @@ AppIconMenu.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_appendSeparator: function () {
|
_appendSeparator: function () {
|
||||||
let bin = new St.Bin({ style_class: 'app-well-menu-separator' });
|
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
||||||
this._windowContainer.add_actor(bin);
|
this.addMenuItem(separator);
|
||||||
},
|
},
|
||||||
|
|
||||||
_appendMenuItem: function(labelText) {
|
_appendMenuItem: function(labelText) {
|
||||||
let box = new St.BoxLayout({ style_class: 'app-well-menu-item',
|
// FIXME: app-well-menu-item style
|
||||||
reactive: true });
|
let item = new PopupMenu.PopupMenuItem(labelText);
|
||||||
let label = new St.Label({ text: labelText });
|
this.addMenuItem(item);
|
||||||
box.add(label);
|
return item;
|
||||||
this._windowContainer.add_actor(box);
|
|
||||||
return box;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
popup: function(activatingButton) {
|
popup: function(activatingButton) {
|
||||||
let [stageX, stageY] = this._source.actor.get_transformed_position();
|
|
||||||
let [stageWidth, stageHeight] = this._source.actor.get_transformed_size();
|
|
||||||
|
|
||||||
this._redisplay();
|
this._redisplay();
|
||||||
|
this.open();
|
||||||
this._windowContainer.popup(activatingButton, global.get_current_time());
|
|
||||||
|
|
||||||
this.emit('popup', true);
|
|
||||||
|
|
||||||
let x, y;
|
|
||||||
x = Math.floor(stageX + stageWidth);
|
|
||||||
y = Math.floor(stageY + (stageHeight / 2) - (this.actor.height / 2));
|
|
||||||
|
|
||||||
this.actor.set_position(x, y);
|
|
||||||
this.actor.show();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
popdown: function() {
|
_onOpenStateChanged: function (menu, open) {
|
||||||
this._windowContainer.popdown();
|
if (open) {
|
||||||
this.emit('popup', false);
|
this.emit('popup', true);
|
||||||
this.actor.hide();
|
} else {
|
||||||
|
this._updateHighlight(null);
|
||||||
|
this.emit('popup', false);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
selectWindow: function(metaWindow) {
|
// called by this._menuManager when it has the grab
|
||||||
this._selectMenuItemForWindow(metaWindow);
|
menuEventFilter: function(event) {
|
||||||
|
let eventType = event.type();
|
||||||
|
|
||||||
|
// Check if the user is interacting with a window representation
|
||||||
|
// rather than interacting with the menu
|
||||||
|
|
||||||
|
if (eventType == Clutter.EventType.BUTTON_RELEASE) {
|
||||||
|
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
||||||
|
if (metaWindow)
|
||||||
|
this.emit('activate-window', metaWindow);
|
||||||
|
} else if (eventType == Clutter.EventType.ENTER) {
|
||||||
|
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
||||||
|
if (metaWindow)
|
||||||
|
this._selectMenuItemForWindow(metaWindow, true);
|
||||||
|
} else if (eventType == Clutter.EventType.LEAVE) {
|
||||||
|
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
||||||
|
if (metaWindow)
|
||||||
|
this._selectMenuItemForWindow(metaWindow, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
_findMetaWindowForActor: function (actor) {
|
_findMetaWindowForActor: function (actor) {
|
||||||
@ -843,64 +791,32 @@ AppIconMenu.prototype = {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
// This function is called while the menu has a pointer grab; what we want
|
|
||||||
// to do is see if the mouse was released over a window representation
|
|
||||||
_onMenuButtonRelease: function (actor, event) {
|
|
||||||
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
|
||||||
if (metaWindow) {
|
|
||||||
this.emit('activate-window', metaWindow);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateHighlight: function (item) {
|
_updateHighlight: function (item) {
|
||||||
if (this._highlightedItem) {
|
if (this._highlightedItem)
|
||||||
this._highlightedItem.remove_style_pseudo_class('hover');
|
|
||||||
this.emit('highlight-window', null);
|
this.emit('highlight-window', null);
|
||||||
}
|
|
||||||
this._highlightedItem = item;
|
this._highlightedItem = item;
|
||||||
if (this._highlightedItem) {
|
if (this._highlightedItem) {
|
||||||
item.add_style_pseudo_class('hover');
|
|
||||||
let window = this._highlightedItem._window;
|
let window = this._highlightedItem._window;
|
||||||
if (window)
|
if (window)
|
||||||
this.emit('highlight-window', window);
|
this.emit('highlight-window', window);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_selectMenuItemForWindow: function (metaWindow) {
|
_selectMenuItemForWindow: function (metaWindow, selected) {
|
||||||
let children = this._windowContainer.get_children();
|
let items = this.getMenuItems();
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
let child = children[i];
|
let item = items[i];
|
||||||
let menuMetaWindow = child._window;
|
let menuMetaWindow = item._window;
|
||||||
if (menuMetaWindow == metaWindow)
|
if (menuMetaWindow == metaWindow)
|
||||||
this._updateHighlight(child);
|
item.setActive(selected);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Called while menu has a pointer grab
|
_onActiveChanged: function (menu, child) {
|
||||||
_onMenuEnter: function (actor, event) {
|
|
||||||
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
|
||||||
if (metaWindow) {
|
|
||||||
this._selectMenuItemForWindow(metaWindow);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Called while menu has a pointer grab
|
|
||||||
_onMenuLeave: function (actor, event) {
|
|
||||||
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
|
||||||
if (metaWindow) {
|
|
||||||
this._updateHighlight(null);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onItemUnselected: function (actor, child) {
|
|
||||||
this._updateHighlight(null);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onItemSelected: function (actor, child) {
|
|
||||||
this._updateHighlight(child);
|
this._updateHighlight(child);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onItemActivate: function (actor, child) {
|
_onActivate: function (actor, child) {
|
||||||
if (child._window) {
|
if (child._window) {
|
||||||
let metaWindow = child._window;
|
let metaWindow = child._window;
|
||||||
this.emit('activate-window', metaWindow);
|
this.emit('activate-window', metaWindow);
|
||||||
@ -915,12 +831,7 @@ AppIconMenu.prototype = {
|
|||||||
else
|
else
|
||||||
favs.addFavorite(this._source.app.get_id());
|
favs.addFavorite(this._source.app.get_id());
|
||||||
}
|
}
|
||||||
this.popdown();
|
this.close();
|
||||||
},
|
|
||||||
|
|
||||||
_onWindowSelectionCancelled: function () {
|
|
||||||
this.emit('highlight-window', null);
|
|
||||||
this.popdown();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Signals.addSignalMethods(AppIconMenu.prototype);
|
Signals.addSignalMethods(AppIconMenu.prototype);
|
||||||
|
@ -736,6 +736,9 @@ Workspace.prototype = {
|
|||||||
* Draw the user's attention to the given window @metaWindow.
|
* Draw the user's attention to the given window @metaWindow.
|
||||||
*/
|
*/
|
||||||
setHighlightWindow: function (metaWindow) {
|
setHighlightWindow: function (metaWindow) {
|
||||||
|
if (!this._lightbox)
|
||||||
|
return;
|
||||||
|
|
||||||
let actor;
|
let actor;
|
||||||
if (metaWindow != null) {
|
if (metaWindow != null) {
|
||||||
let clone = this.lookupCloneForMetaWindow(metaWindow);
|
let clone = this.lookupCloneForMetaWindow(metaWindow);
|
||||||
|
Loading…
Reference in New Issue
Block a user