Merge remote branch 'origin/master' into zeitgeist
This commit is contained in:
@ -2,22 +2,40 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const DEFAULT_LIMIT = 512;
|
||||
|
||||
function HistoryManager(settings_key) {
|
||||
this._init(settings_key);
|
||||
function HistoryManager(params) {
|
||||
this._init(params);
|
||||
}
|
||||
|
||||
HistoryManager.prototype = {
|
||||
_init: function(settings_key, limit) {
|
||||
this._limit = limit || DEFAULT_LIMIT;
|
||||
this._key = settings_key;
|
||||
this._history = global.settings.get_strv(settings_key);
|
||||
this._historyIndex = -1;
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { gsettingsKey: null,
|
||||
limit: DEFAULT_LIMIT,
|
||||
entry: null });
|
||||
|
||||
global.settings.connect('changed::' + settings_key,
|
||||
Lang.bind(this, this._historyChanged));
|
||||
this._key = params.gsettingsKey;
|
||||
this._limit = params.limit;
|
||||
|
||||
this._historyIndex = 0;
|
||||
if (this._key) {
|
||||
this._history = global.settings.get_strv(this._key);
|
||||
global.settings.connect('changed::' + this._key,
|
||||
Lang.bind(this, this._historyChanged));
|
||||
|
||||
} else {
|
||||
this._history = [];
|
||||
}
|
||||
|
||||
this._entry = params.entry;
|
||||
|
||||
if (this._entry) {
|
||||
this._entry.connect('key-press-event',
|
||||
Lang.bind(this, this._onEntryKeyPress));
|
||||
}
|
||||
},
|
||||
|
||||
_historyChanged: function() {
|
||||
@ -26,18 +44,32 @@ HistoryManager.prototype = {
|
||||
},
|
||||
|
||||
prevItem: function(text) {
|
||||
this._setHistory(this._historyIndex--, text);
|
||||
if (this._historyIndex <= 0)
|
||||
return text;
|
||||
|
||||
if (text)
|
||||
this._history[this._historyIndex] = text;
|
||||
this._historyIndex--;
|
||||
return this._indexChanged();
|
||||
},
|
||||
|
||||
nextItem: function(text) {
|
||||
this._setHistory(this._historyIndex++, text);
|
||||
if (this._historyIndex >= this._history.length)
|
||||
return text;
|
||||
|
||||
if (text)
|
||||
this._history[this._historyIndex] = text;
|
||||
this._historyIndex++;
|
||||
return this._indexChanged();
|
||||
},
|
||||
|
||||
lastItem: function() {
|
||||
this._historyIndex = this._history.length;
|
||||
return this._indexChanged();
|
||||
if (this._historyIndex != this._history.length) {
|
||||
this._historyIndex = this._history.length;
|
||||
this._indexChanged();
|
||||
}
|
||||
|
||||
return this._historyIndex[this._history.length];
|
||||
},
|
||||
|
||||
addItem: function(input) {
|
||||
@ -45,28 +77,39 @@ HistoryManager.prototype = {
|
||||
this._history[this._history.length - 1] != input) {
|
||||
|
||||
this._history.push(input);
|
||||
this._historyIndex = this._history.length;
|
||||
this._save();
|
||||
}
|
||||
},
|
||||
|
||||
_onEntryKeyPress: function(entry, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_Up) {
|
||||
this.prevItem(entry.get_text());
|
||||
return true;
|
||||
} else if (symbol == Clutter.KEY_Down) {
|
||||
this.nextItem(entry.get_text());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_indexChanged: function() {
|
||||
let current = this._history[this._historyIndex] || '';
|
||||
this.emit('changed', current);
|
||||
|
||||
if (this._entry)
|
||||
this._entry.set_text(current);
|
||||
|
||||
return current;
|
||||
},
|
||||
|
||||
_setHistory: function(index, text) {
|
||||
this._historyIndex = Math.max(this._historyIndex, 0);
|
||||
this._historyIndex = Math.min(this._historyIndex, this._history.length);
|
||||
|
||||
if (text)
|
||||
this._history[index] = text;
|
||||
},
|
||||
|
||||
_save: function() {
|
||||
if (this._history.length > this._limit)
|
||||
this._history.splice(0, this._history.length - this._key);
|
||||
global.settings.set_strv(this._key, this._history);
|
||||
this._history.splice(0, this._history.length - this._limit);
|
||||
|
||||
if (this._key)
|
||||
global.settings.set_strv(this._key, this._history);
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(HistoryManager.prototype);
|
||||
|
@ -87,7 +87,7 @@ AltTabPopup.prototype = {
|
||||
let [childMinHeight, childNaturalHeight] = this._appSwitcher.actor.get_preferred_height(primary.width - hPadding);
|
||||
let [childMinWidth, childNaturalWidth] = this._appSwitcher.actor.get_preferred_width(childNaturalHeight);
|
||||
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
|
||||
childBox.x2 = Math.min(childBox.x1 + primary.width - hPadding, childBox.x1 + childNaturalWidth);
|
||||
childBox.x2 = Math.min(primary.x + primary.width - hPadding, childBox.x1 + childNaturalWidth);
|
||||
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
|
||||
childBox.y2 = childBox.y1 + childNaturalHeight;
|
||||
this._appSwitcher.actor.allocate(childBox, flags);
|
||||
|
@ -23,6 +23,7 @@ const Workspace = imports.ui.workspace;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const MENU_POPUP_TIMEOUT = 600;
|
||||
const SCROLL_TIME = 0.1;
|
||||
|
||||
function AlphabeticalView() {
|
||||
this._init();
|
||||
@ -67,6 +68,7 @@ AlphabeticalView.prototype = {
|
||||
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
|
||||
|
||||
this._grid.addItem(appIcon.actor);
|
||||
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
|
||||
|
||||
appIcon._appInfo = appInfo;
|
||||
if (this._filterApp && !this._filterApp(appInfo))
|
||||
@ -75,6 +77,28 @@ AlphabeticalView.prototype = {
|
||||
this._apps.push(appIcon);
|
||||
},
|
||||
|
||||
_ensureIconVisible: function(icon) {
|
||||
let adjustment = this.actor.vscroll.adjustment;
|
||||
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
|
||||
|
||||
let offset = 0;
|
||||
let vfade = this.actor.get_effect("vfade");
|
||||
if (vfade)
|
||||
offset = vfade.fade_offset;
|
||||
|
||||
if (icon.y < value + offset)
|
||||
value = Math.max(0, icon.y - offset);
|
||||
else if (icon.y + icon.height > value + pageSize - offset)
|
||||
value = Math.min(upper, icon.y + icon.height + offset - pageSize);
|
||||
else
|
||||
return;
|
||||
|
||||
Tweener.addTween(adjustment,
|
||||
{ value: value,
|
||||
time: SCROLL_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
setFilter: function(filter) {
|
||||
this._filterApp = filter;
|
||||
for (let i = 0; i < this._apps.length; i++)
|
||||
@ -128,6 +152,12 @@ ViewByCategories.prototype = {
|
||||
}));
|
||||
|
||||
this._sections = [];
|
||||
|
||||
// We need a dummy actor to catch the keyboard focus if the
|
||||
// user Ctrl-Alt-Tabs here before the deferred work creates
|
||||
// our real contents
|
||||
this._focusDummy = new St.Bin({ can_focus: true });
|
||||
this.actor.add(this._focusDummy);
|
||||
},
|
||||
|
||||
_scrollFilter: function(actor, event) {
|
||||
@ -166,7 +196,8 @@ ViewByCategories.prototype = {
|
||||
_addFilter: function(name, num) {
|
||||
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
||||
style_class: 'app-filter',
|
||||
x_align: St.Align.START });
|
||||
x_align: St.Align.START,
|
||||
can_focus: true });
|
||||
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
this._selectCategory(num);
|
||||
@ -201,6 +232,14 @@ ViewByCategories.prototype = {
|
||||
this._addFilter(sections[i], i);
|
||||
|
||||
this._selectCategory(-1);
|
||||
|
||||
if (this._focusDummy) {
|
||||
let focused = this._focusDummy.has_key_focus();
|
||||
this._focusDummy.destroy();
|
||||
this._focusDummy = null;
|
||||
if (focused)
|
||||
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -352,6 +391,7 @@ AppWellIcon.prototype = {
|
||||
this.actor = new St.Button({ style_class: 'app-well-app',
|
||||
reactive: true,
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
|
||||
can_focus: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
@ -361,6 +401,7 @@ AppWellIcon.prototype = {
|
||||
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
|
||||
|
||||
this._menu = null;
|
||||
this._menuManager = new PopupMenu.PopupMenuManager(this);
|
||||
@ -437,6 +478,11 @@ AppWellIcon.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
_onKeyboardPopupMenu: function() {
|
||||
this.popupMenu();
|
||||
this._menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
getId: function() {
|
||||
return this.app.get_id();
|
||||
},
|
||||
@ -454,6 +500,7 @@ AppWellIcon.prototype = {
|
||||
if (!isPoppedUp)
|
||||
this._onMenuPoppedDown();
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function () { this._menu.close(); }));
|
||||
|
||||
this._menuManager.addMenu(this._menu);
|
||||
}
|
||||
|
@ -10,11 +10,18 @@ const St = imports.gi.St;
|
||||
|
||||
const AltTab = imports.ui.altTab;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const POPUP_APPICON_SIZE = 96;
|
||||
const POPUP_FADE_TIME = 0.1; // seconds
|
||||
|
||||
const SortGroup = {
|
||||
TOP: 0,
|
||||
MIDDLE: 1,
|
||||
BOTTOM: 2
|
||||
};
|
||||
|
||||
function CtrlAltTabManager() {
|
||||
this._init();
|
||||
}
|
||||
@ -23,14 +30,18 @@ CtrlAltTabManager.prototype = {
|
||||
_init: function() {
|
||||
this._items = [];
|
||||
this._focusManager = St.FocusManager.get_for_stage(global.stage);
|
||||
Main.wm.setKeybindingHandler('switch_panels', Lang.bind(this,
|
||||
function (shellwm, binding, window, backwards) {
|
||||
this.popup(backwards);
|
||||
}));
|
||||
},
|
||||
|
||||
addGroup: function(root, name, icon) {
|
||||
this._items.push({ root: root, name: name, iconName: icon });
|
||||
addGroup: function(root, name, icon, params) {
|
||||
let item = Params.parse(params, { sortGroup: SortGroup.MIDDLE,
|
||||
proxy: root,
|
||||
focusCallback: null });
|
||||
|
||||
item.root = root;
|
||||
item.name = name;
|
||||
item.iconName = icon;
|
||||
|
||||
this._items.push(item);
|
||||
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
|
||||
this._focusManager.add_group(root);
|
||||
},
|
||||
@ -45,38 +56,73 @@ CtrlAltTabManager.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
focusGroup: function(root) {
|
||||
focusGroup: function(item) {
|
||||
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
|
||||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
|
||||
if (item.window)
|
||||
Main.activateWindow(item.window);
|
||||
else if (item.focusCallback)
|
||||
item.focusCallback();
|
||||
else
|
||||
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
// Sort the items into a consistent order; panel first, tray last,
|
||||
// and everything else in between, sorted by X coordinate, so that
|
||||
// they will have the same left-to-right ordering in the
|
||||
// Ctrl-Alt-Tab dialog as they do onscreen.
|
||||
_sortItems: function(a, b) {
|
||||
if (a.sortGroup != b.sortGroup)
|
||||
return a.sortGroup - b.sortGroup;
|
||||
|
||||
let y;
|
||||
if (a.x == undefined) {
|
||||
if (a.window)
|
||||
a.x = a.window.get_compositor_private().x;
|
||||
else
|
||||
[a.x, y] = a.proxy.get_transformed_position();
|
||||
}
|
||||
if (b.x == undefined) {
|
||||
if (b.window)
|
||||
b.x = b.window.get_compositor_private().x;
|
||||
else
|
||||
[b.x, y] = b.proxy.get_transformed_position();
|
||||
}
|
||||
|
||||
return a.x - b.x;
|
||||
},
|
||||
|
||||
popup: function(backwards) {
|
||||
// Start with the set of focus groups that are currently mapped
|
||||
let items = this._items.filter(function (item) { return item.root.mapped; });
|
||||
let items = this._items.filter(function (item) { return item.proxy.mapped; });
|
||||
|
||||
// And add the windows metacity would show in its Ctrl-Alt-Tab list
|
||||
let screen = global.screen;
|
||||
let display = screen.get_display();
|
||||
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
|
||||
let windowTracker = Shell.WindowTracker.get_default();
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let icon;
|
||||
let app = windowTracker.get_window_app(windows[i]);
|
||||
if (app)
|
||||
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
||||
else
|
||||
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
||||
items.push({ window: windows[i],
|
||||
name: windows[i].title,
|
||||
iconActor: icon });
|
||||
if (!Main.overview.visible) {
|
||||
let screen = global.screen;
|
||||
let display = screen.get_display();
|
||||
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
|
||||
let windowTracker = Shell.WindowTracker.get_default();
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let icon;
|
||||
let app = windowTracker.get_window_app(windows[i]);
|
||||
if (app)
|
||||
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
||||
else
|
||||
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
||||
items.push({ window: windows[i],
|
||||
name: windows[i].title,
|
||||
iconActor: icon,
|
||||
sortGroup: SortGroup.MIDDLE });
|
||||
}
|
||||
}
|
||||
|
||||
if (!items.length)
|
||||
return;
|
||||
|
||||
items.sort(Lang.bind(this, this._sortItems));
|
||||
new CtrlAltTabPopup().show(items, backwards);
|
||||
}
|
||||
};
|
||||
@ -91,12 +137,12 @@ function CtrlAltTabPopup() {
|
||||
|
||||
CtrlAltTabPopup.prototype = {
|
||||
_init : function() {
|
||||
let primary = global.get_primary_monitor();
|
||||
this.actor = new St.BoxLayout({ name: 'ctrlAltTabPopup',
|
||||
reactive: true,
|
||||
x: primary.x + primary.width / 2,
|
||||
y: primary.y + primary.height / 2,
|
||||
anchor_gravity: Clutter.Gravity.CENTER });
|
||||
this.actor = new Shell.GenericContainer({ name: 'ctrlAltTabPopup',
|
||||
reactive: true });
|
||||
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
@ -106,6 +152,37 @@ CtrlAltTabPopup.prototype = {
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
alloc.min_size = primary.width;
|
||||
alloc.natural_size = primary.width;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
alloc.min_size = primary.height;
|
||||
alloc.natural_size = primary.height;
|
||||
},
|
||||
|
||||
_allocate: function (actor, box, flags) {
|
||||
let childBox = new Clutter.ActorBox();
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
|
||||
let vPadding = this.actor.get_theme_node().get_vertical_padding();
|
||||
let hPadding = this.actor.get_theme_node().get_horizontal_padding();
|
||||
|
||||
let [childMinHeight, childNaturalHeight] = this._switcher.actor.get_preferred_height(primary.width - hPadding);
|
||||
let [childMinWidth, childNaturalWidth] = this._switcher.actor.get_preferred_width(childNaturalHeight);
|
||||
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
|
||||
childBox.x2 = Math.min(primary.width - hPadding, childBox.x1 + childNaturalWidth);
|
||||
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
|
||||
childBox.y2 = childBox.y1 + childNaturalHeight;
|
||||
this._switcher.actor.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
show : function(items, startBackwards) {
|
||||
if (!Main.pushModal(this.actor))
|
||||
return false;
|
||||
@ -180,11 +257,7 @@ CtrlAltTabPopup.prototype = {
|
||||
_finish : function() {
|
||||
this.destroy();
|
||||
|
||||
let item = this._items[this._selection];
|
||||
if (item.root)
|
||||
Main.ctrlAltTabManager.focusGroup(item.root);
|
||||
else
|
||||
Main.activateWindow(item.window);
|
||||
Main.ctrlAltTabManager.focusGroup(this._items[this._selection]);
|
||||
},
|
||||
|
||||
_popModal: function() {
|
||||
|
@ -262,11 +262,6 @@ Dash.prototype = {
|
||||
clip_to_allocation: true });
|
||||
this._box._delegate = this;
|
||||
|
||||
// This will eventually be automatic, see
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=584662
|
||||
if (St.Widget.get_default_direction () == St.TextDirection.RTL)
|
||||
this._box.add_style_pseudo_class('rtl');
|
||||
|
||||
this.actor = new St.Bin({ y_align: St.Align.START, child: this._box });
|
||||
this.actor.connect('notify::height', Lang.bind(this,
|
||||
function() {
|
||||
|
@ -90,6 +90,7 @@ DateMenuButton.prototype = {
|
||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
item.actor.can_focus = false;
|
||||
vbox.add(item.actor);
|
||||
|
||||
// Add vertical separator
|
||||
@ -109,6 +110,7 @@ DateMenuButton.prototype = {
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
item.actor.can_focus = false;
|
||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
// Whenever the menu is opened, select today
|
||||
|
50
js/ui/dnd.js
50
js/ui/dnd.js
@ -87,6 +87,10 @@ _Draggable.prototype = {
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, function() {
|
||||
this._actorDestroyed = true;
|
||||
// If the drag actor is destroyed and we were going to fix
|
||||
// up its hover state, fix up the parent hover state instead
|
||||
if (this.actor == this._firstLeaveActor)
|
||||
this._firstLeaveActor = this._dragOrigParent;
|
||||
if (this._dragInProgress)
|
||||
this._cancelDrag(global.get_current_time());
|
||||
this.disconnectAll();
|
||||
@ -101,6 +105,12 @@ _Draggable.prototype = {
|
||||
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
|
||||
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
|
||||
|
||||
// During the drag, we eat enter/leave events so that actors don't prelight or show
|
||||
// tooltips. But we remember the actors that we first left/last entered so we can
|
||||
// fix up the hover state after the drag ends.
|
||||
this._firstLeaveActor = null;
|
||||
this._lastEnterActor = null;
|
||||
|
||||
this._eventsGrabbed = false;
|
||||
},
|
||||
|
||||
@ -200,6 +210,11 @@ _Draggable.prototype = {
|
||||
this._cancelDrag(event.get_time());
|
||||
return true;
|
||||
}
|
||||
} else if (event.type() == Clutter.EventType.LEAVE) {
|
||||
if (this._firstLeaveActor == null)
|
||||
this._firstLeaveActor = event.get_source();
|
||||
} else if (event.type() == Clutter.EventType.ENTER) {
|
||||
this._lastEnterActor = event.get_source();
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -465,7 +480,9 @@ _Draggable.prototype = {
|
||||
let [parentX, parentY] = this._dragOrigParent.get_transformed_position();
|
||||
let [parentWidth, parentHeight] = this._dragOrigParent.get_size();
|
||||
let [parentScaledWidth, parentScaledHeight] = this._dragOrigParent.get_transformed_size();
|
||||
let parentScale = parentScaledWidth / parentWidth;
|
||||
let parentScale = 1.0;
|
||||
if (parentWidth != 0)
|
||||
parentScale = parentScaledWidth / parentWidth;
|
||||
|
||||
x = parentX + parentScale * this._dragOrigX;
|
||||
y = parentY + parentScale * this._dragOrigY;
|
||||
@ -481,13 +498,14 @@ _Draggable.prototype = {
|
||||
},
|
||||
|
||||
_cancelDrag: function(eventTime) {
|
||||
this.emit('drag-cancelled', eventTime);
|
||||
this._dragInProgress = false;
|
||||
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
|
||||
|
||||
if (this._actorDestroyed) {
|
||||
global.unset_cursor();
|
||||
if (!this._buttonDown)
|
||||
this._ungrabEvents();
|
||||
this._dragComplete();
|
||||
this.emit('drag-end', eventTime, false);
|
||||
return;
|
||||
}
|
||||
@ -544,12 +562,36 @@ _Draggable.prototype = {
|
||||
this._dragComplete();
|
||||
},
|
||||
|
||||
// Actor is an actor we have entered or left during the drag; call
|
||||
// st_widget_sync_hover on all StWidget ancestors
|
||||
_syncHover: function(actor) {
|
||||
while (actor) {
|
||||
let parent = actor.get_parent();
|
||||
if (actor instanceof St.Widget)
|
||||
actor.sync_hover();
|
||||
|
||||
actor = parent;
|
||||
}
|
||||
},
|
||||
|
||||
_dragComplete: function() {
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, false);
|
||||
if (!this._actorDestroyed)
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, false);
|
||||
|
||||
this._ungrabEvents();
|
||||
|
||||
if (this._firstLeaveActor) {
|
||||
this._syncHover(this._firstLeaveActor);
|
||||
this._firstLeaveActor = null;
|
||||
}
|
||||
|
||||
if (this._lastEnterActor) {
|
||||
this._syncHover(this._lastEnterActor);
|
||||
this._lastEnterActor = null;
|
||||
}
|
||||
|
||||
this._dragActor = undefined;
|
||||
currentDraggable = null;
|
||||
this._ungrabEvents();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -28,6 +28,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
'const Meta = imports.gi.Meta; ' +
|
||||
'const Semantic = imports.misc.semantic' +
|
||||
'const Shell = imports.gi.Shell; ' +
|
||||
'const Tp = imports.gi.TelepathyGLib; ' +
|
||||
'const Main = imports.ui.main; ' +
|
||||
'const Lang = imports.lang; ' +
|
||||
'const Tweener = imports.ui.tweener; ' +
|
||||
@ -779,23 +780,8 @@ LookingGlass.prototype = {
|
||||
return true;
|
||||
}));
|
||||
|
||||
this._history = new History.HistoryManager(HISTORY_KEY);
|
||||
this._history.connect('changed', Lang.bind(this, function(history, text) {
|
||||
this._entry.text = text;
|
||||
}));
|
||||
|
||||
this._entry.clutter_text.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Up) {
|
||||
this._history.prevItem(o.get_text());
|
||||
return true;
|
||||
} else if (symbol == Clutter.Down) {
|
||||
this._history.nextItem(o.get_text());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
|
||||
entry: this._entry.clutter_text });
|
||||
},
|
||||
|
||||
_updateFont: function() {
|
||||
|
@ -139,6 +139,7 @@ function start() {
|
||||
|
||||
placesManager = new PlaceDisplay.PlacesManager();
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
overview = new Overview.Overview();
|
||||
chrome = new Chrome.Chrome();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
@ -153,9 +154,6 @@ function start() {
|
||||
overview.init();
|
||||
statusIconDispatcher.start(messageTray.actor);
|
||||
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
ctrlAltTabManager.addGroup(panel.actor, _("Panel"), 'gnome-panel');
|
||||
|
||||
_startDate = new Date();
|
||||
|
||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
@ -264,12 +262,28 @@ function _checkWorkspaces() {
|
||||
emptyWorkspaces.push(false);
|
||||
}
|
||||
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let currentWorkspaceEmpty = emptyWorkspaces[activeWorkspaceIndex];
|
||||
|
||||
if (currentWorkspaceEmpty) {
|
||||
// "Merge" the empty workspace we are removing with the one at the end
|
||||
wm.blockAnimations();
|
||||
}
|
||||
|
||||
// Delete other empty workspaces; do it from the end to avoid index changes
|
||||
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
|
||||
if (emptyWorkspaces[i])
|
||||
global.screen.remove_workspace(_workspaces[i], global.get_current_time());
|
||||
}
|
||||
|
||||
if (currentWorkspaceEmpty) {
|
||||
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
|
||||
wm.unblockAnimations();
|
||||
|
||||
if (!overview.visible)
|
||||
overview.show();
|
||||
}
|
||||
|
||||
_checkWorkspacesId = 0;
|
||||
return false;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ const St = imports.gi.St;
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const Main = imports.ui.main;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Util = imports.misc.util;
|
||||
@ -517,25 +518,28 @@ Notification.prototype = {
|
||||
this._updated();
|
||||
},
|
||||
|
||||
_createScrollArea: function() {
|
||||
this.actor.add_style_class_name('multi-line-notification');
|
||||
this._scrollArea = new St.ScrollView({ name: 'notification-scrollview',
|
||||
vscrollbar_policy: Gtk.PolicyType.AUTOMATIC,
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||
vfade: true });
|
||||
this.actor.add(this._scrollArea, { row: 1, col: 1 });
|
||||
this._contentArea = new St.BoxLayout({ name: 'notification-body',
|
||||
vertical: true });
|
||||
this._scrollArea.add_actor(this._contentArea);
|
||||
// If we know the notification will be expandable, we need to add
|
||||
// the banner text to the body as the first element.
|
||||
this._addBannerBody();
|
||||
},
|
||||
|
||||
// addActor:
|
||||
// @actor: actor to add to the body of the notification
|
||||
//
|
||||
// Appends @actor to the notification's body
|
||||
addActor: function(actor, style) {
|
||||
if (!this._scrollArea) {
|
||||
this.actor.add_style_class_name('multi-line-notification');
|
||||
this._scrollArea = new St.ScrollView({ name: 'notification-scrollview',
|
||||
vscrollbar_policy: Gtk.PolicyType.AUTOMATIC,
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||
vfade: true });
|
||||
this.actor.add(this._scrollArea, { row: 1,
|
||||
col: 1 });
|
||||
this._contentArea = new St.BoxLayout({ name: 'notification-body',
|
||||
vertical: true });
|
||||
this._scrollArea.add_actor(this._contentArea);
|
||||
// If we know the notification will be expandable, we need to add
|
||||
// the banner text to the body as the first element.
|
||||
this._addBannerBody();
|
||||
this._createScrollArea();
|
||||
}
|
||||
|
||||
this._contentArea.add(actor, style ? style : {});
|
||||
@ -570,11 +574,6 @@ Notification.prototype = {
|
||||
//
|
||||
// Scrolls the content area (if scrollable) to the indicated edge
|
||||
scrollTo: function(side) {
|
||||
// Hack to force a relayout, since the caller probably
|
||||
// just added or removed something to scrollArea, and
|
||||
// the adjustment needs to reflect that.
|
||||
global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, 0, 0);
|
||||
|
||||
let adjustment = this._scrollArea.vscroll.adjustment;
|
||||
if (side == St.Side.TOP)
|
||||
adjustment.value = adjustment.lower;
|
||||
@ -853,7 +852,7 @@ Source.prototype = {
|
||||
|
||||
this.notification = notification;
|
||||
|
||||
this._notificationClickedId = notification.connect('clicked', Lang.bind(this, this._notificationClicked));
|
||||
this._notificationClickedId = notification.connect('clicked', Lang.bind(this, this.open));
|
||||
this._notificationDestroyedId = notification.connect('destroy', Lang.bind(this,
|
||||
function () {
|
||||
if (this.notification == notification) {
|
||||
@ -881,7 +880,7 @@ Source.prototype = {
|
||||
},
|
||||
|
||||
// Default implementation is to do nothing, but subclasses can override
|
||||
_notificationClicked: function(notification) {
|
||||
open: function(notification) {
|
||||
},
|
||||
|
||||
// Default implementation is to destroy this source, but subclasses can override
|
||||
@ -917,6 +916,27 @@ SummaryItem.prototype = {
|
||||
this._sourceBox.add_actor(this._sourceIcon);
|
||||
this._sourceBox.add_actor(this._sourceTitleBin, { expand: true });
|
||||
this.actor.child = this._sourceBox;
|
||||
this.rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
|
||||
vertical: true });
|
||||
|
||||
let item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Open"));
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
source.open();
|
||||
this.emit('right-click-menu-done-displaying');
|
||||
}));
|
||||
this.rightClickMenu.add(item.actor);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Remove"));
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
source.destroy();
|
||||
this.emit('right-click-menu-done-displaying');
|
||||
}));
|
||||
this.rightClickMenu.add(item.actor);
|
||||
|
||||
let focusManager = St.FocusManager.get_for_stage(global.stage);
|
||||
focusManager.add_group(this.rightClickMenu);
|
||||
},
|
||||
|
||||
// getTitleNaturalWidth, getTitleWidth, and setTitleWidth include
|
||||
@ -943,6 +963,7 @@ SummaryItem.prototype = {
|
||||
this._sourceTitle.clutter_text.ellipsize = mode;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(SummaryItem.prototype);
|
||||
|
||||
function MessageTray() {
|
||||
this._init();
|
||||
@ -980,17 +1001,19 @@ MessageTray.prototype = {
|
||||
|
||||
this._summaryMotionId = 0;
|
||||
|
||||
this._summaryNotificationBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||
{ reactive: true,
|
||||
track_hover: true });
|
||||
this._summaryNotificationBoxPointer.actor.style_class = 'summary-notification-boxpointer';
|
||||
this.actor.add_actor(this._summaryNotificationBoxPointer.actor);
|
||||
this._summaryNotificationBoxPointer.actor.lower_bottom();
|
||||
this._summaryNotificationBoxPointer.actor.hide();
|
||||
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||
{ reactive: true,
|
||||
track_hover: true });
|
||||
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
|
||||
this.actor.add_actor(this._summaryBoxPointer.actor);
|
||||
this._summaryBoxPointer.actor.lower_bottom();
|
||||
this._summaryBoxPointer.actor.hide();
|
||||
|
||||
this._summaryNotification = null;
|
||||
this._summaryNotificationClickedId = 0;
|
||||
this._summaryRightClickMenuClickedId = 0;
|
||||
this._clickedSummaryItem = null;
|
||||
this._clickedSummaryItemMouseButton = -1;
|
||||
this._clickedSummaryItemAllocationChangedId = 0;
|
||||
this._expandedSummaryItem = null;
|
||||
this._summaryItemTitleWidth = 0;
|
||||
@ -1004,7 +1027,7 @@ MessageTray.prototype = {
|
||||
this._focusGrabber = new FocusGrabber();
|
||||
this._focusGrabber.connect('focus-grabbed', Lang.bind(this,
|
||||
function() {
|
||||
if (this._summaryNotification)
|
||||
if (this._summaryBoxPointer.bin.child)
|
||||
this._lock();
|
||||
}));
|
||||
this._focusGrabber.connect('focus-ungrabbed', Lang.bind(this, this._unlock));
|
||||
@ -1027,7 +1050,7 @@ MessageTray.prototype = {
|
||||
this._notificationState = State.HIDDEN;
|
||||
this._notificationTimeoutId = 0;
|
||||
this._notificationExpandedId = 0;
|
||||
this._summaryNotificationState = State.HIDDEN;
|
||||
this._summaryBoxPointerState = State.HIDDEN;
|
||||
this._summaryNotificationTimeoutId = 0;
|
||||
this._summaryNotificationExpandedId = 0;
|
||||
this._overviewVisible = Main.overview.visible;
|
||||
@ -1037,7 +1060,7 @@ MessageTray.prototype = {
|
||||
Main.chrome.addActor(this.actor, { affectsStruts: false,
|
||||
visibleInOverview: true });
|
||||
Main.chrome.trackActor(this._notificationBin);
|
||||
Main.chrome.trackActor(this._summaryNotificationBoxPointer.actor);
|
||||
Main.chrome.trackActor(this._summaryBoxPointer.actor);
|
||||
|
||||
global.gdk_screen.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
|
||||
|
||||
@ -1138,9 +1161,9 @@ MessageTray.prototype = {
|
||||
this._onSummaryItemHoverChanged(summaryItem);
|
||||
}));
|
||||
|
||||
summaryItem.actor.connect('clicked', Lang.bind(this,
|
||||
function () {
|
||||
this._onSummaryItemClicked(summaryItem);
|
||||
summaryItem.actor.connect('button-press-event', Lang.bind(this,
|
||||
function (actor, event) {
|
||||
this._onSummaryItemClicked(summaryItem, event);
|
||||
}));
|
||||
|
||||
source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
|
||||
@ -1157,7 +1180,7 @@ MessageTray.prototype = {
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
this._summaryItems[index].actor.destroy();
|
||||
let summaryItemToRemove = this._summaryItems[index];
|
||||
|
||||
let newSummaryItemsIndex = this._newSummaryItems.indexOf(this._summaryItems[index]);
|
||||
if (newSummaryItemsIndex != -1)
|
||||
@ -1168,6 +1191,9 @@ MessageTray.prototype = {
|
||||
if (source.isChat)
|
||||
this._chatSummaryItemsCount--;
|
||||
|
||||
if (this._expandedSummaryItem == summaryItemToRemove)
|
||||
this._expandedSummaryItem = null;
|
||||
|
||||
if (this._longestSummaryItem.source == source) {
|
||||
let newTitleWidth = 0;
|
||||
this._longestSummaryItem = null;
|
||||
@ -1192,11 +1218,13 @@ MessageTray.prototype = {
|
||||
this._notificationRemoved = true;
|
||||
needUpdate = true;
|
||||
}
|
||||
if (this._clickedSummaryItem && this._clickedSummaryItem.source == source) {
|
||||
if (this._clickedSummaryItem == summaryItemToRemove) {
|
||||
this._unsetClickedSummaryItem();
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
summaryItemToRemove.actor.destroy();
|
||||
|
||||
if (needUpdate);
|
||||
this._updateState();
|
||||
|
||||
@ -1349,11 +1377,16 @@ MessageTray.prototype = {
|
||||
this._expandedSummaryItem.setEllipsization(Pango.EllipsizeMode.END);
|
||||
},
|
||||
|
||||
_onSummaryItemClicked: function(summaryItem) {
|
||||
if (!this._clickedSummaryItem || this._clickedSummaryItem != summaryItem)
|
||||
_onSummaryItemClicked: function(summaryItem, event) {
|
||||
let clickedButton = event.get_button();
|
||||
if (!this._clickedSummaryItem ||
|
||||
this._clickedSummaryItem != summaryItem ||
|
||||
this._clickedSummaryItemMouseButton != clickedButton) {
|
||||
this._clickedSummaryItem = summaryItem;
|
||||
else
|
||||
this._clickedSummaryItemMouseButton = clickedButton;
|
||||
} else {
|
||||
this._unsetClickedSummaryItem();
|
||||
}
|
||||
|
||||
this._updateState();
|
||||
},
|
||||
@ -1380,7 +1413,7 @@ MessageTray.prototype = {
|
||||
// leaving the tray. The tray is locked when the summary notification is visible anyway, but we
|
||||
// should treat the mouse being over the summary notification as the tray being left for collapsing
|
||||
// any expanded summary item other than the one related to the notification.
|
||||
if (this._summaryNotificationBoxPointer.bin.hover)
|
||||
if (this._summaryBoxPointer.bin.hover)
|
||||
return;
|
||||
|
||||
this._useLongerTrayLeftTimeout = false;
|
||||
@ -1538,19 +1571,23 @@ MessageTray.prototype = {
|
||||
}
|
||||
|
||||
// Summary notification
|
||||
let haveSummaryNotification = this._clickedSummaryItem != null;
|
||||
let summaryNotificationIsMainNotification = (haveSummaryNotification &&
|
||||
this._clickedSummaryItem.source.notification == this._notification);
|
||||
let canShowSummaryNotification = this._summaryState == State.SHOWN;
|
||||
let wrongSummaryNotification = (haveSummaryNotification &&
|
||||
let haveClickedSummaryItem = this._clickedSummaryItem != null;
|
||||
let summarySourceIsMainNotificationSource = (haveClickedSummaryItem && this._notification &&
|
||||
this._clickedSummaryItem.source == this._notification.source);
|
||||
let canShowSummaryBoxPointer = this._summaryState == State.SHOWN;
|
||||
let wrongSummaryNotification = (this._clickedSummaryItemMouseButton == 1 &&
|
||||
this._summaryNotification != this._clickedSummaryItem.source.notification);
|
||||
let wrongSummaryRightClickMenu = (this._clickedSummaryItemMouseButton == 3 &&
|
||||
this._summaryBoxPointer.bin.child != this._clickedSummaryItem.rightClickMenu);
|
||||
let wrongSummaryBoxPointer = (haveClickedSummaryItem &&
|
||||
(wrongSummaryNotification || wrongSummaryRightClickMenu));
|
||||
|
||||
if (this._summaryNotificationState == State.HIDDEN) {
|
||||
if (haveSummaryNotification && !summaryNotificationIsMainNotification && canShowSummaryNotification)
|
||||
this._showSummaryNotification();
|
||||
} else if (this._summaryNotificationState == State.SHOWN) {
|
||||
if (!haveSummaryNotification || !canShowSummaryNotification || wrongSummaryNotification)
|
||||
this._hideSummaryNotification();
|
||||
if (this._summaryBoxPointerState == State.HIDDEN) {
|
||||
if (haveClickedSummaryItem && !summarySourceIsMainNotificationSource && canShowSummaryBoxPointer)
|
||||
this._showSummaryBoxPointer();
|
||||
} else if (this._summaryBoxPointerState == State.SHOWN) {
|
||||
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer)
|
||||
this._hideSummaryBoxPointer();
|
||||
}
|
||||
|
||||
// Tray itself
|
||||
@ -1808,44 +1845,53 @@ MessageTray.prototype = {
|
||||
this._expandedSummaryItemTitleWidth = this._summaryItemTitleWidth;
|
||||
},
|
||||
|
||||
_showSummaryNotification: function() {
|
||||
this._summaryNotification = this._clickedSummaryItem.source.notification;
|
||||
this._summaryNotificationClickedId = this._summaryNotification.connect('done-displaying',
|
||||
Lang.bind(this, this._escapeTray));
|
||||
let index = this._notificationQueue.indexOf(this._summaryNotification);
|
||||
if (index != -1)
|
||||
this._notificationQueue.splice(index, 1);
|
||||
_showSummaryBoxPointer: function() {
|
||||
if (this._clickedSummaryItemMouseButton == 1) {
|
||||
let clickedSummaryItemNotification = this._clickedSummaryItem.source.notification;
|
||||
let index = this._notificationQueue.indexOf(clickedSummaryItemNotification);
|
||||
if (index != -1)
|
||||
this._notificationQueue.splice(index, 1);
|
||||
|
||||
this._summaryNotificationBoxPointer.bin.child = this._summaryNotification.actor;
|
||||
this._focusGrabber.grabFocus(this._summaryNotification.actor);
|
||||
this._summaryNotification = clickedSummaryItemNotification;
|
||||
this._summaryNotificationClickedId = this._summaryNotification.connect('done-displaying',
|
||||
Lang.bind(this, this._escapeTray));
|
||||
this._summaryBoxPointer.bin.child = this._summaryNotification.actor;
|
||||
if (!this._summaryNotificationExpandedId)
|
||||
this._summaryNotificationExpandedId = this._summaryNotification.connect('expanded',
|
||||
Lang.bind(this, this._onSummaryBoxPointerExpanded));
|
||||
this._summaryNotification.expand(false);
|
||||
} else if (this._clickedSummaryItemMouseButton == 3) {
|
||||
this._summaryRightClickMenuClickedId = this._clickedSummaryItem.connect('right-click-menu-done-displaying',
|
||||
Lang.bind(this, this._escapeTray));
|
||||
this._summaryBoxPointer.bin.child = this._clickedSummaryItem.rightClickMenu;
|
||||
}
|
||||
|
||||
this._focusGrabber.grabFocus(this._summaryBoxPointer.bin.child);
|
||||
|
||||
if (!this._summaryNotificationExpandedId)
|
||||
this._summaryNotificationExpandedId = this._summaryNotification.connect('expanded', Lang.bind(this, this._onSummaryNotificationExpanded));
|
||||
this._summaryNotification.expand(false);
|
||||
|
||||
this._clickedSummaryItemAllocationChangedId =
|
||||
this._clickedSummaryItem.actor.connect('allocation-changed',
|
||||
Lang.bind(this, this._adjustNotificationBoxPointerPosition));
|
||||
Lang.bind(this, this._adjustSummaryBoxPointerPosition));
|
||||
// _clickedSummaryItem.actor can change absolute postiion without changing allocation
|
||||
this._summaryMotionId = this._summary.connect('allocation-changed',
|
||||
Lang.bind(this, this._adjustNotificationBoxPointerPosition));
|
||||
Lang.bind(this, this._adjustSummaryBoxPointerPosition));
|
||||
|
||||
this._summaryNotificationBoxPointer.actor.opacity = 0;
|
||||
this._summaryNotificationBoxPointer.actor.show();
|
||||
this._adjustNotificationBoxPointerPosition();
|
||||
this._summaryBoxPointer.actor.opacity = 0;
|
||||
this._summaryBoxPointer.actor.show();
|
||||
this._adjustSummaryBoxPointerPosition();
|
||||
|
||||
this._summaryNotificationState = State.SHOWING;
|
||||
this._summaryNotificationBoxPointer.show(true, Lang.bind(this, function() {
|
||||
this._summaryNotificationState = State.SHOWN;
|
||||
this._summaryBoxPointerState = State.SHOWING;
|
||||
this._summaryBoxPointer.show(true, Lang.bind(this, function() {
|
||||
this._summaryBoxPointerState = State.SHOWN;
|
||||
}));
|
||||
},
|
||||
|
||||
_adjustNotificationBoxPointerPosition: function() {
|
||||
_adjustSummaryBoxPointerPosition: function() {
|
||||
// The position of the arrow origin should be the same as center of this._clickedSummaryItem.actor
|
||||
if (!this._clickedSummaryItem)
|
||||
return;
|
||||
|
||||
this._summaryNotificationBoxPointer.setPosition(this._clickedSummaryItem.actor, 0, 0.5);
|
||||
this._summaryBoxPointer.setPosition(this._clickedSummaryItem.actor, 0, 0.5);
|
||||
},
|
||||
|
||||
_unsetClickedSummaryItem: function() {
|
||||
@ -1856,14 +1902,20 @@ MessageTray.prototype = {
|
||||
this._summaryMotionId = 0;
|
||||
}
|
||||
|
||||
if (this._summaryRightClickMenuClickedId) {
|
||||
this._clickedSummaryItem.disconnect(this._summaryRightClickMenuClickedId);
|
||||
this._summaryRightClickMenuClickedId = 0;
|
||||
}
|
||||
|
||||
this._clickedSummaryItem = null;
|
||||
this._clickedSummaryItemMouseButton = -1;
|
||||
},
|
||||
|
||||
_onSummaryNotificationExpanded: function() {
|
||||
this._adjustNotificationBoxPointerPosition();
|
||||
_onSummaryBoxPointerExpanded: function() {
|
||||
this._adjustSummaryBoxPointerPosition();
|
||||
},
|
||||
|
||||
_hideSummaryNotification: function() {
|
||||
_hideSummaryBoxPointer: function() {
|
||||
if (this._summaryNotificationExpandedId) {
|
||||
this._summaryNotification.disconnect(this._summaryNotificationExpandedId);
|
||||
this._summaryNotificationExpandedId = 0;
|
||||
@ -1873,23 +1925,25 @@ MessageTray.prototype = {
|
||||
this._unsetClickedSummaryItem();
|
||||
|
||||
this._focusGrabber.ungrabFocus();
|
||||
this._summaryNotificationState = State.HIDING;
|
||||
this._summaryNotificationBoxPointer.hide(true, Lang.bind(this, this._hideSummaryNotificationCompleted));
|
||||
this._summaryBoxPointerState = State.HIDING;
|
||||
this._summaryBoxPointer.hide(true, Lang.bind(this, this._hideSummaryBoxPointerCompleted));
|
||||
},
|
||||
|
||||
_hideSummaryNotificationCompleted: function() {
|
||||
this._summaryNotificationState = State.HIDDEN;
|
||||
this._summaryNotificationBoxPointer.bin.child = null;
|
||||
this._summaryNotification.collapseCompleted();
|
||||
this._summaryNotification.disconnect(this._summaryNotificationClickedId);
|
||||
this._summaryNotificationClickedId = 0;
|
||||
let summaryNotification = this._summaryNotification;
|
||||
this._summaryNotification = null;
|
||||
if (summaryNotification.isTransient && !this._reNotifyWithSummaryNotificationAfterHide)
|
||||
summaryNotification.destroy(NotificationDestroyedReason.EXPIRED);
|
||||
if (this._reNotifyWithSummaryNotificationAfterHide) {
|
||||
this._onNotify(summaryNotification.source, summaryNotification);
|
||||
this._reNotifyWithSummaryNotificationAfterHide = false;
|
||||
_hideSummaryBoxPointerCompleted: function() {
|
||||
this._summaryBoxPointerState = State.HIDDEN;
|
||||
this._summaryBoxPointer.bin.child = null;
|
||||
if (this._summaryNotification != null) {
|
||||
this._summaryNotification.collapseCompleted();
|
||||
this._summaryNotification.disconnect(this._summaryNotificationClickedId);
|
||||
this._summaryNotificationClickedId = 0;
|
||||
let summaryNotification = this._summaryNotification;
|
||||
this._summaryNotification = null;
|
||||
if (summaryNotification.isTransient && !this._reNotifyWithSummaryNotificationAfterHide)
|
||||
summaryNotification.destroy(NotificationDestroyedReason.EXPIRED);
|
||||
if (this._reNotifyWithSummaryNotificationAfterHide) {
|
||||
this._onNotify(summaryNotification.source, summaryNotification);
|
||||
this._reNotifyWithSummaryNotificationAfterHide = false;
|
||||
}
|
||||
}
|
||||
if (this._clickedSummaryItem)
|
||||
this._updateState();
|
||||
@ -1915,7 +1969,7 @@ SystemNotificationSource.prototype = {
|
||||
icon_size: this.ICON_SIZE });
|
||||
},
|
||||
|
||||
_notificationClicked: function() {
|
||||
open: function() {
|
||||
this.destroy();
|
||||
}
|
||||
};
|
||||
|
@ -435,6 +435,7 @@ Source.prototype = {
|
||||
MessageTray.Source.prototype._init.call(this, title);
|
||||
|
||||
this._pid = pid;
|
||||
this._appStateChangedId = 0;
|
||||
this._setApp();
|
||||
if (this.app)
|
||||
this.title = this.app.get_name();
|
||||
@ -459,6 +460,10 @@ Source.prototype = {
|
||||
if (!this.app)
|
||||
return;
|
||||
|
||||
// We only update the app if this.app is null, so we can't disconnect the old this._appStateChangedId
|
||||
// even if it were non-zero for some reason.
|
||||
this._appStateChangedId = this.app.connect('notify::state', Lang.bind(this, this._appStateChanged));
|
||||
|
||||
// Only override the icon if we were previously using
|
||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
||||
if (!this._isTrayIcon) {
|
||||
@ -473,7 +478,7 @@ Source.prototype = {
|
||||
this._isTrayIcon = true;
|
||||
},
|
||||
|
||||
_notificationClicked: function(notification) {
|
||||
open: function(notification) {
|
||||
this.openApp();
|
||||
},
|
||||
|
||||
@ -482,6 +487,16 @@ Source.prototype = {
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_appStateChanged: function() {
|
||||
// Destroy notification sources when their apps exit.
|
||||
// The app exiting would normally result in a tray icon being removed,
|
||||
// so it should be ok to destroy the source associated with a tray icon
|
||||
// here too, however we just let that happen through the code path
|
||||
// associated with the tray icon being removed.
|
||||
if (!this._isTrayIcon && this.app.get_state() == Shell.AppState.STOPPED)
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
openApp: function() {
|
||||
if (this.app == null)
|
||||
return;
|
||||
@ -491,5 +506,13 @@ Source.prototype = {
|
||||
let mostRecentWindow = windows[0];
|
||||
Main.activateWindow(mostRecentWindow);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.app && this._appStateChangedId) {
|
||||
this.app.disconnect(this._appStateChangedId);
|
||||
this._appStateChangedId = 0;
|
||||
}
|
||||
MessageTray.Source.prototype.destroy.call(this);
|
||||
}
|
||||
};
|
||||
|
@ -180,10 +180,10 @@ Overview.prototype = {
|
||||
this._group.add_actor(this.viewSelector.actor);
|
||||
|
||||
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
||||
this.viewSelector.addViewTab(_("Windows"), this._workspacesDisplay.actor);
|
||||
this.viewSelector.addViewTab(_("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
|
||||
|
||||
let appView = new AppDisplay.AllAppDisplay();
|
||||
this.viewSelector.addViewTab(_("Applications"), appView.actor);
|
||||
this.viewSelector.addViewTab(_("Applications"), appView.actor, 'system-run');
|
||||
|
||||
// Default search providers
|
||||
this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||
@ -201,6 +201,10 @@ Overview.prototype = {
|
||||
this.dash.actor.add_constraint(this.viewSelector.constrainY);
|
||||
this.dash.actor.add_constraint(this.viewSelector.constrainHeight);
|
||||
|
||||
// Translators: this is the name of the dock/favorites area on
|
||||
// the left of the overview
|
||||
Main.ctrlAltTabManager.addGroup(this.dash.actor, _("Dash"), 'user-bookmarks');
|
||||
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
@ -481,35 +485,14 @@ Overview.prototype = {
|
||||
this.emit('window-drag-begin');
|
||||
},
|
||||
|
||||
cancelledWindowDrag: function(source) {
|
||||
this.emit('window-drag-cancelled');
|
||||
},
|
||||
|
||||
endWindowDrag: function(source) {
|
||||
this.emit('window-drag-end');
|
||||
},
|
||||
|
||||
// Returns the scale the Overview has when we just start zooming out
|
||||
// to overview mode. That is, when just the active workspace is showing.
|
||||
getZoomedInScale : function() {
|
||||
return 1 / this.workspaces.getScale();
|
||||
},
|
||||
|
||||
// Returns the position the Overview has when we just start zooming out
|
||||
// to overview mode. That is, when just the active workspace is showing.
|
||||
getZoomedInPosition : function() {
|
||||
let [posX, posY] = this.workspaces.getActiveWorkspacePosition();
|
||||
let scale = this.getZoomedInScale();
|
||||
|
||||
return [- posX * scale, - posY * scale];
|
||||
},
|
||||
|
||||
// Returns the current scale of the Overview.
|
||||
getScale : function() {
|
||||
return this.workspaces.actor.scaleX;
|
||||
},
|
||||
|
||||
// Returns the current position of the Overview.
|
||||
getPosition : function() {
|
||||
return [this.workspaces.actor.x, this.workspaces.actor.y];
|
||||
},
|
||||
|
||||
// show:
|
||||
//
|
||||
// Animates the overview visible and grabs mouse and keyboard input
|
||||
@ -563,30 +546,13 @@ Overview.prototype = {
|
||||
});
|
||||
}
|
||||
|
||||
// Create a zoom out effect. First scale the workspaces view up and
|
||||
// position it so that the active workspace fills up the whole screen,
|
||||
// then transform it to its normal dimensions and position.
|
||||
// The opposite transition is used in hide().
|
||||
this.workspaces.actor.scaleX = this.workspaces.actor.scaleY = this.getZoomedInScale();
|
||||
[this.workspaces.actor.x, this.workspaces.actor.y] = this.getZoomedInPosition();
|
||||
let primary = global.get_primary_monitor();
|
||||
Tweener.addTween(this.workspaces.actor,
|
||||
{ x: primary.x - this._group.x,
|
||||
y: primary.y - this._group.y,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
onComplete: this._showDone,
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
// Make the other elements fade in.
|
||||
this._group.opacity = 0;
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 255,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME
|
||||
time: ANIMATION_TIME,
|
||||
onComplete: this._showDone,
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
this._coverPane.raise_top();
|
||||
@ -698,27 +664,13 @@ Overview.prototype = {
|
||||
|
||||
this.workspaces.hide();
|
||||
|
||||
// Create a zoom in effect by transforming the workspaces view so that
|
||||
// the active workspace fills up the whole screen. The opposite
|
||||
// transition is used in show().
|
||||
let scale = this.getZoomedInScale();
|
||||
let [posX, posY] = this.getZoomedInPosition();
|
||||
Tweener.addTween(this.workspaces.actor,
|
||||
{ x: posX,
|
||||
y: posY,
|
||||
scaleX: scale,
|
||||
scaleY: scale,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
onComplete: this._hideDone,
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME
|
||||
time: ANIMATION_TIME,
|
||||
onComplete: this._hideDone,
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
this._coverPane.raise_top();
|
||||
|
@ -13,6 +13,7 @@ const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const Overview = imports.ui.overview;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
@ -243,6 +244,10 @@ AppMenuButton.prototype = {
|
||||
|
||||
let bin = new St.Bin({ name: 'appMenu' });
|
||||
this.actor.set_child(bin);
|
||||
|
||||
this.actor.reactive = false;
|
||||
this._targetIsCurrent = false;
|
||||
|
||||
this._container = new Shell.GenericContainer();
|
||||
bin.set_child(this._container);
|
||||
this._container.connect('get-preferred-width', Lang.bind(this, this._getContentPreferredWidth));
|
||||
@ -275,7 +280,7 @@ AppMenuButton.prototype = {
|
||||
this._clipWidth = PANEL_ICON_SIZE;
|
||||
this._direction = SPINNER_SPEED;
|
||||
|
||||
this._spinner = new AnimatedIcon('process-working.png',
|
||||
this._spinner = new AnimatedIcon('process-working.svg',
|
||||
PANEL_ICON_SIZE);
|
||||
this._container.add_actor(this._spinner.actor);
|
||||
this._spinner.actor.lower_bottom();
|
||||
@ -500,13 +505,6 @@ AppMenuButton.prototype = {
|
||||
lastStartedApp = this._startingApps[i];
|
||||
|
||||
let focusedApp = tracker.focus_app;
|
||||
let targetApp = focusedApp != null ? focusedApp : lastStartedApp;
|
||||
if (targetApp == this._targetApp) {
|
||||
if (targetApp && targetApp.get_state() != Shell.AppState.STARTING)
|
||||
this.stopAnimation();
|
||||
return;
|
||||
}
|
||||
this._stopAnimation();
|
||||
|
||||
if (!focusedApp) {
|
||||
// If the app has just lost focus to the panel, pretend
|
||||
@ -516,27 +514,56 @@ AppMenuButton.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let targetApp = focusedApp != null ? focusedApp : lastStartedApp;
|
||||
|
||||
if (targetApp == null) {
|
||||
if (!this._targetIsCurrent)
|
||||
return;
|
||||
|
||||
this.actor.reactive = false;
|
||||
this._targetIsCurrent = false;
|
||||
|
||||
Tweener.removeTweens(this.actor);
|
||||
Tweener.addTween(this.actor, { opacity: 0,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._targetIsCurrent) {
|
||||
this.actor.reactive = true;
|
||||
this._targetIsCurrent = true;
|
||||
|
||||
Tweener.removeTweens(this.actor);
|
||||
Tweener.addTween(this.actor, { opacity: 255,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
}
|
||||
|
||||
if (targetApp == this._targetApp) {
|
||||
if (targetApp && targetApp.get_state() != Shell.AppState.STARTING)
|
||||
this.stopAnimation();
|
||||
return;
|
||||
}
|
||||
this._stopAnimation();
|
||||
|
||||
if (this._iconBox.child != null)
|
||||
this._iconBox.child.destroy();
|
||||
this._iconBox.hide();
|
||||
this._label.setText('');
|
||||
this.actor.reactive = false;
|
||||
|
||||
this._targetApp = targetApp;
|
||||
if (targetApp != null) {
|
||||
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
||||
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
||||
|
||||
this._label.setText(targetApp.get_name());
|
||||
// TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
this._quitMenu.label.set_text(_("Quit %s").format(targetApp.get_name()));
|
||||
this._label.setText(targetApp.get_name());
|
||||
// TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
this._quitMenu.label.set_text(_("Quit %s").format(targetApp.get_name()));
|
||||
|
||||
this.actor.reactive = true;
|
||||
this._iconBox.set_child(icon);
|
||||
this._iconBox.show();
|
||||
this._iconBox.set_child(icon);
|
||||
this._iconBox.show();
|
||||
|
||||
if (targetApp.get_state() == Shell.AppState.STARTING)
|
||||
this.startAnimation();
|
||||
}
|
||||
if (targetApp.get_state() == Shell.AppState.STARTING)
|
||||
this.startAnimation();
|
||||
|
||||
this.emit('changed');
|
||||
}
|
||||
@ -813,13 +840,6 @@ Panel.prototype = {
|
||||
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
|
||||
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
||||
|
||||
// This will eventually be automatic, see
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=584662
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
|
||||
this._leftBox.add_style_pseudo_class('rtl');
|
||||
this._rightBox.add_style_pseudo_class('rtl');
|
||||
}
|
||||
|
||||
this._leftCorner = new PanelCorner(St.Side.LEFT);
|
||||
this._rightCorner = new PanelCorner(St.Side.RIGHT);
|
||||
|
||||
@ -996,6 +1016,9 @@ Panel.prototype = {
|
||||
Main.chrome.addActor(this._rightCorner.actor, { visibleInOverview: true,
|
||||
affectsStruts: false,
|
||||
affectsInputRegion: false });
|
||||
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Panel"), 'start-here',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||
},
|
||||
|
||||
_xdndShowOverview: function (actor) {
|
||||
|
@ -1,7 +1,9 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Lang = imports.lang;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Main = imports.ui.main;
|
||||
@ -20,9 +22,10 @@ Button.prototype = {
|
||||
track_hover: true });
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
|
||||
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, /* FIXME */ 0);
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
|
||||
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0);
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
|
||||
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
|
||||
Main.chrome.addActor(this.menu.actor, { visibleInOverview: true,
|
||||
affectsStruts: false });
|
||||
this.menu.actor.hide();
|
||||
@ -32,20 +35,37 @@ Button.prototype = {
|
||||
this.menu.toggle();
|
||||
},
|
||||
|
||||
_onKeyPress: function(actor, event) {
|
||||
_onSourceKeyPress: function(actor, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
|
||||
this.menu.toggle();
|
||||
return true;
|
||||
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
|
||||
this.menu.close();
|
||||
return true;
|
||||
} else if (symbol == Clutter.KEY_Down) {
|
||||
if (!this.menu.isOpen)
|
||||
this.menu.toggle();
|
||||
this.menu.activateFirst();
|
||||
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
},
|
||||
|
||||
_onMenuKeyPress: function(actor, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
||||
let focusManager = St.FocusManager.get_for_stage(global.stage);
|
||||
let group = focusManager.get_group(this.actor);
|
||||
if (group) {
|
||||
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
|
||||
group.navigate_focus(this.actor, direction, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_onOpenStateChanged: function(menu, open) {
|
||||
if (open)
|
||||
this.actor.add_style_pseudo_class('active');
|
||||
|
@ -54,8 +54,10 @@ PopupBaseMenuItem.prototype = {
|
||||
}
|
||||
if (params.reactive && params.hover)
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChanged));
|
||||
if (params.reactive)
|
||||
if (params.reactive) {
|
||||
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
|
||||
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
|
||||
}
|
||||
},
|
||||
|
||||
_onStyleChanged: function (actor) {
|
||||
@ -81,6 +83,10 @@ PopupBaseMenuItem.prototype = {
|
||||
this.setActive(true);
|
||||
},
|
||||
|
||||
_onKeyFocusOut: function (actor) {
|
||||
this.setActive(false);
|
||||
},
|
||||
|
||||
_onHoverChanged: function (actor) {
|
||||
this.setActive(actor.hover);
|
||||
},
|
||||
@ -684,28 +690,6 @@ PopupImageMenuItem.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function mod(a, b) {
|
||||
return (a + b) % b;
|
||||
}
|
||||
|
||||
function findNextInCycle(items, current, direction) {
|
||||
let cur;
|
||||
|
||||
if (items.length == 0)
|
||||
return current;
|
||||
else if (items.length == 1)
|
||||
return items[0];
|
||||
|
||||
if (current)
|
||||
cur = items.indexOf(current);
|
||||
else if (direction == 1)
|
||||
cur = items.length - 1;
|
||||
else
|
||||
cur = 0;
|
||||
|
||||
return items[mod(cur + direction, items.length)];
|
||||
}
|
||||
|
||||
function PopupMenuBase() {
|
||||
throw new TypeError('Trying to instantiate abstract class PopupMenuBase');
|
||||
}
|
||||
@ -861,17 +845,6 @@ PopupMenuBase.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
activateFirst: function() {
|
||||
let children = this.box.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let actor = children[i];
|
||||
if (actor._delegate && actor._delegate instanceof PopupBaseMenuItem && actor.visible && actor.reactive) {
|
||||
actor._delegate.setActive(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this.isOpen)
|
||||
this.close(true);
|
||||
@ -909,6 +882,8 @@ PopupMenu.prototype = {
|
||||
this.actor = this._boxPointer.actor;
|
||||
this.actor._delegate = this;
|
||||
this.actor.style_class = 'popup-menu-boxpointer';
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
|
||||
this._boxWrapper = new Shell.GenericContainer();
|
||||
this._boxWrapper.connect('get-preferred-width', Lang.bind(this, this._boxGetPreferredWidth));
|
||||
this._boxWrapper.connect('get-preferred-height', Lang.bind(this, this._boxGetPreferredHeight));
|
||||
@ -937,6 +912,15 @@ PopupMenu.prototype = {
|
||||
this.box.allocate(box, flags);
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape) {
|
||||
this.close(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
setArrowOrigin: function(origin) {
|
||||
this._boxPointer.setArrowOrigin(origin);
|
||||
},
|
||||
@ -1127,11 +1111,17 @@ PopupSubMenuMenuItem.prototype = {
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.KEY_Right) {
|
||||
let symbol = event.get_key_symbol();
|
||||
|
||||
if (symbol == Clutter.KEY_Right) {
|
||||
this.menu.open(true);
|
||||
this.menu.activateFirst();
|
||||
this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
|
||||
return true;
|
||||
} else if (symbol == Clutter.KEY_Left && this.menu.isOpen) {
|
||||
this.menu.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
return PopupBaseMenuItem.prototype._onKeyPressEvent.call(this, actor, event);
|
||||
},
|
||||
|
||||
@ -1158,12 +1148,13 @@ PopupMenuManager.prototype = {
|
||||
this.grabbed = false;
|
||||
|
||||
this._eventCaptureId = 0;
|
||||
this._keyPressEventId = 0;
|
||||
this._enterEventId = 0;
|
||||
this._leaveEventId = 0;
|
||||
this._keyFocusNotifyId = 0;
|
||||
this._activeMenu = null;
|
||||
this._menus = [];
|
||||
this._preGrabInputMode = null;
|
||||
this._grabbedFromKeynav = false;
|
||||
},
|
||||
|
||||
addMenu: function(menu, position) {
|
||||
@ -1172,15 +1163,13 @@ PopupMenuManager.prototype = {
|
||||
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
|
||||
destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
|
||||
enterId: 0,
|
||||
focusInId: 0,
|
||||
focusOutId: 0
|
||||
focusInId: 0
|
||||
};
|
||||
|
||||
let source = menu.sourceActor;
|
||||
if (source) {
|
||||
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
|
||||
menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
|
||||
menudata.focusOutId = source.connect('key-focus-out', Lang.bind(this, function() { this._onKeyFocusOut(menu); }));
|
||||
}
|
||||
|
||||
if (position == undefined)
|
||||
@ -1205,8 +1194,6 @@ PopupMenuManager.prototype = {
|
||||
menu.sourceActor.disconnect(menudata.enterId);
|
||||
if (menudata.focusInId)
|
||||
menu.sourceActor.disconnect(menudata.focusInId);
|
||||
if (menudata.focusOutId)
|
||||
menu.sourceActor.disconnect(menudata.focusOutId);
|
||||
|
||||
this._menus.splice(position, 1);
|
||||
},
|
||||
@ -1215,10 +1202,10 @@ PopupMenuManager.prototype = {
|
||||
Main.pushModal(this._owner.actor);
|
||||
|
||||
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
|
||||
this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
// captured-event doesn't see enter/leave events
|
||||
this._enterEventId = global.stage.connect('enter-event', Lang.bind(this, this._onEventCapture));
|
||||
this._leaveEventId = global.stage.connect('leave-event', Lang.bind(this, this._onEventCapture));
|
||||
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||
|
||||
this.grabbed = true;
|
||||
},
|
||||
@ -1226,49 +1213,47 @@ PopupMenuManager.prototype = {
|
||||
_ungrab: function() {
|
||||
global.stage.disconnect(this._eventCaptureId);
|
||||
this._eventCaptureId = 0;
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
this._keyPressEventId = 0;
|
||||
global.stage.disconnect(this._enterEventId);
|
||||
this._enterEventId = 0;
|
||||
global.stage.disconnect(this._leaveEventId);
|
||||
this._leaveEventId = 0;
|
||||
global.stage.disconnect(this._keyFocusNotifyId);
|
||||
this._keyFocusNotifyId = 0;
|
||||
|
||||
this.grabbed = false;
|
||||
Main.popModal(this._owner.actor);
|
||||
},
|
||||
|
||||
_onMenuOpenState: function(menu, open) {
|
||||
if (open)
|
||||
this._activeMenu = menu;
|
||||
|
||||
// Check what the focus was before calling pushModal/popModal
|
||||
let focus = global.stage.key_focus;
|
||||
let hadFocus = focus && this._activeMenuContains(focus);
|
||||
|
||||
if (open) {
|
||||
if (!this.grabbed) {
|
||||
this._preGrabInputMode = global.stage_input_mode;
|
||||
this._grabbedFromKeynav = hadFocus;
|
||||
this._grab();
|
||||
}
|
||||
this._activeMenu = menu;
|
||||
|
||||
// if the focus is not already associated with the menu,
|
||||
// then focus the menu
|
||||
let focus = global.stage.key_focus;
|
||||
if (!this._activeMenuContains(focus))
|
||||
menu.sourceActor.grab_key_focus();
|
||||
if (hadFocus)
|
||||
focus.grab_key_focus();
|
||||
else
|
||||
menu.actor.grab_key_focus();
|
||||
} else if (menu == this._activeMenu) {
|
||||
let focus = global.stage.key_focus;
|
||||
let fromActive = focus && this._activeMenuContains(focus);
|
||||
|
||||
if (this.grabbed)
|
||||
this._ungrab();
|
||||
this._activeMenu = null;
|
||||
|
||||
// If keynav was in effect before we grabbed, then we need
|
||||
// to properly re-establish it after we ungrab. (popModal
|
||||
// will have unset the focus.) If some part of the menu
|
||||
// was focused at the time of the ungrab then focus its
|
||||
// sourceActor. Otherwise just reset the focus to where it
|
||||
// was right before the ungrab.
|
||||
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED) {
|
||||
global.stage_input_mode = Shell.StageInputMode.FOCUSED;
|
||||
if (fromActive)
|
||||
if (this._grabbedFromKeynav) {
|
||||
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED)
|
||||
global.stage_input_mode = Shell.StageInputMode.FOCUSED;
|
||||
if (hadFocus && menu.sourceActor)
|
||||
menu.sourceActor.grab_key_focus();
|
||||
else
|
||||
else if (focus)
|
||||
focus.grab_key_focus();
|
||||
}
|
||||
}
|
||||
@ -1296,29 +1281,20 @@ PopupMenuManager.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
_onKeyFocusOut: function(menu) {
|
||||
if (!this.grabbed || menu != this._activeMenu)
|
||||
_onKeyFocusChanged: function() {
|
||||
if (!this.grabbed || !this._activeMenu)
|
||||
return;
|
||||
|
||||
// We want to close the menu if the focus has moved somewhere
|
||||
// other than inside the menu or to another menu's sourceActor.
|
||||
// Unfortunately, when key-focus-out is emitted,
|
||||
// stage.key_focus will be null. So we have to wait until
|
||||
// after it emits the key-focus-in as well.
|
||||
let id = global.stage.connect('notify::key-focus', Lang.bind(this,
|
||||
function () {
|
||||
global.stage.disconnect(id);
|
||||
let focus = global.stage.key_focus;
|
||||
if (focus) {
|
||||
if (this._activeMenuContains(focus))
|
||||
return;
|
||||
if (focus._delegate && focus._delegate.menu &&
|
||||
this._findMenu(focus._delegate.menu) != -1)
|
||||
return;
|
||||
}
|
||||
|
||||
if (menu != this._activeMenu)
|
||||
return;
|
||||
|
||||
let focus = global.stage.key_focus;
|
||||
if (!focus || this._activeMenuContains(focus))
|
||||
return;
|
||||
if (focus._delegate && this._findMenu(focus._delegate.menu) != -1)
|
||||
return;
|
||||
menu.close(true);
|
||||
}));
|
||||
this._closeMenu();
|
||||
},
|
||||
|
||||
_onMenuDestroy: function(menu) {
|
||||
@ -1354,18 +1330,6 @@ PopupMenuManager.prototype = {
|
||||
return -1;
|
||||
},
|
||||
|
||||
_nextMenu: function(pos, direction) {
|
||||
for (let i = 1; i < this._menus.length; i++) {
|
||||
let candidate = mod(pos + i * direction, this._menus.length);
|
||||
let menu = this._menus[candidate].menu;
|
||||
if (!menu.sourceActor || menu.sourceActor.visible)
|
||||
return menu;
|
||||
}
|
||||
// no menu is found? this should not happen
|
||||
// anyway stay on current menu
|
||||
return this._menus[pos];
|
||||
},
|
||||
|
||||
_onEventCapture: function(actor, event) {
|
||||
if (!this.grabbed)
|
||||
return false;
|
||||
@ -1383,8 +1347,7 @@ PopupMenuManager.prototype = {
|
||||
this._closeMenu();
|
||||
return true;
|
||||
}
|
||||
} else if ((eventType == Clutter.EventType.BUTTON_PRESS && !activeMenuContains)
|
||||
|| (eventType == Clutter.EventType.KEY_PRESS && event.get_key_symbol() == Clutter.Escape)) {
|
||||
} else if (eventType == Clutter.EventType.BUTTON_PRESS && !activeMenuContains) {
|
||||
this._closeMenu();
|
||||
return true;
|
||||
} else if (activeMenuContains || this._eventIsOnAnyMenuSource(event)) {
|
||||
@ -1394,27 +1357,6 @@ PopupMenuManager.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(actor, event) {
|
||||
if (!this.grabbed || !this._activeMenu)
|
||||
return false;
|
||||
if (!this._eventIsOnActiveMenu(event))
|
||||
return false;
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.Left || symbol == Clutter.Right) {
|
||||
let direction = symbol == Clutter.Right ? 1 : -1;
|
||||
let pos = this._findMenu(this._activeMenu);
|
||||
let next = this._nextMenu(pos, direction);
|
||||
if (next != this._activeMenu) {
|
||||
this._changeMenu(next);
|
||||
next.activateFirst();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_closeMenu: function() {
|
||||
if (this._activeMenu != null)
|
||||
this._activeMenu.close(true);
|
||||
|
@ -234,21 +234,10 @@ __proto__: ModalDialog.ModalDialog.prototype,
|
||||
this._commandCompleter = new CommandCompleter();
|
||||
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
|
||||
|
||||
this._history = new History.HistoryManager(HISTORY_KEY);
|
||||
this._history.connect('changed', Lang.bind(this, function(history, text) {
|
||||
this._entryText.set_text(text);
|
||||
}));
|
||||
|
||||
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
|
||||
entry: this._entryText });
|
||||
this._entryText.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Down) {
|
||||
this._history.nextItem(o.get_text());
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.Up) {
|
||||
this._history.prevItem(o.get_text());
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
|
||||
if (Shell.get_event_state(e) & Clutter.ModifierType.CONTROL_MASK)
|
||||
this._run(o.get_text(), true);
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const DBus = imports.dbus;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
@ -16,6 +17,16 @@ const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const BUS_NAME = 'org.gnome.ScreenSaver';
|
||||
const OBJECT_PATH = '/org/gnome/ScreenSaver';
|
||||
|
||||
const ScreenSaverInterface = {
|
||||
name: BUS_NAME,
|
||||
methods: [ { name: 'Lock', inSignature: '' } ]
|
||||
};
|
||||
|
||||
let ScreenSaverProxy = DBus.makeProxyClass(ScreenSaverInterface);
|
||||
|
||||
// Adapted from gdm/gui/user-switch-applet/applet.c
|
||||
//
|
||||
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
|
||||
@ -43,7 +54,7 @@ StatusMenuButton.prototype = {
|
||||
this._account_mgr = Tp.AccountManager.dup()
|
||||
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
|
||||
this._screenSaverProxy = new ScreenSaverProxy(DBus.session, BUS_NAME, OBJECT_PATH);
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._iconBox = new St.Bin();
|
||||
@ -188,7 +199,7 @@ StatusMenuButton.prototype = {
|
||||
|
||||
_onLockScreenActivate: function() {
|
||||
Main.overview.hide();
|
||||
Util.spawn(['gnome-screensaver-command', '--lock']);
|
||||
this._screenSaverProxy.LockRemote();
|
||||
},
|
||||
|
||||
_onLoginScreenActivate: function() {
|
||||
@ -207,7 +218,9 @@ StatusMenuButton.prototype = {
|
||||
|
||||
if (this._haveSuspend &&
|
||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
||||
this._upClient.suspend_sync(null);
|
||||
this._screenSaverProxy.LockRemote(Lang.bind(this, function() {
|
||||
this._upClient.suspend_sync(null);
|
||||
}));
|
||||
} else {
|
||||
Util.spawn(['gnome-session-quit', '--power-off']);
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ Source.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_notificationClicked: function(notification) {
|
||||
open: function(notification) {
|
||||
let props = {};
|
||||
props[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT;
|
||||
[props[Tp.PROP_CHANNEL_TARGET_HANDLE], props[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE]] = this._channel.get_handle();
|
||||
@ -279,6 +279,16 @@ Notification.prototype = {
|
||||
this._responseEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated));
|
||||
this.setActionArea(this._responseEntry);
|
||||
|
||||
this._oldMaxScrollAdjustment = 0;
|
||||
this._createScrollArea();
|
||||
|
||||
this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
|
||||
let currentValue = adjustment.value + adjustment.page_size;
|
||||
if (currentValue == this._oldMaxScrollAdjustment)
|
||||
this.scrollTo(St.Side.BOTTOM);
|
||||
this._oldMaxScrollAdjustment = adjustment.upper;
|
||||
}));
|
||||
|
||||
this._history = [];
|
||||
this._timestampTimeoutId = 0;
|
||||
},
|
||||
@ -305,7 +315,6 @@ Notification.prototype = {
|
||||
|
||||
let body = this.addBody(text);
|
||||
body.add_style_class_name(style);
|
||||
this.scrollTo(St.Side.BOTTOM);
|
||||
|
||||
this._history.unshift({ actor: body, time: timestamp, realMessage: true });
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
@ -15,12 +16,12 @@ const Search = imports.ui.search;
|
||||
const SearchDisplay = imports.ui.searchDisplay;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
function BaseTab(titleActor, pageActor) {
|
||||
this._init(titleActor, pageActor);
|
||||
function BaseTab(titleActor, pageActor, name, a11yIcon) {
|
||||
this._init(titleActor, pageActor, name, a11yIcon);
|
||||
}
|
||||
|
||||
BaseTab.prototype = {
|
||||
_init: function(titleActor, pageActor) {
|
||||
_init: function(titleActor, pageActor, name, a11yIcon) {
|
||||
this.title = titleActor;
|
||||
this.page = new St.Bin({ child: pageActor,
|
||||
x_align: St.Align.START,
|
||||
@ -29,6 +30,14 @@ BaseTab.prototype = {
|
||||
y_fill: true,
|
||||
style_class: 'view-tab-page' });
|
||||
|
||||
if (this.title.can_focus) {
|
||||
Main.ctrlAltTabManager.addGroup(this.title, name, a11yIcon);
|
||||
} else {
|
||||
Main.ctrlAltTabManager.addGroup(this.page, name, a11yIcon,
|
||||
{ proxy: this.title,
|
||||
focusCallback: Lang.bind(this, this._a11yFocus) });
|
||||
}
|
||||
|
||||
this.visible = false;
|
||||
},
|
||||
|
||||
@ -56,6 +65,11 @@ BaseTab.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
_a11yFocus: function() {
|
||||
this._activate();
|
||||
this.page.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
_activate: function() {
|
||||
this.emit('activated');
|
||||
}
|
||||
@ -63,19 +77,19 @@ BaseTab.prototype = {
|
||||
Signals.addSignalMethods(BaseTab.prototype);
|
||||
|
||||
|
||||
function ViewTab(label, pageActor) {
|
||||
this._init(label, pageActor);
|
||||
function ViewTab(label, pageActor, a11yIcon) {
|
||||
this._init(label, pageActor, a11yIcon);
|
||||
}
|
||||
|
||||
ViewTab.prototype = {
|
||||
__proto__: BaseTab.prototype,
|
||||
|
||||
_init: function(label, pageActor) {
|
||||
_init: function(label, pageActor, a11yIcon) {
|
||||
let titleActor = new St.Button({ label: label,
|
||||
style_class: 'view-tab-title' });
|
||||
titleActor.connect('clicked', Lang.bind(this, this._activate));
|
||||
|
||||
BaseTab.prototype._init.call(this, titleActor, pageActor);
|
||||
BaseTab.prototype._init.call(this, titleActor, pageActor, label, a11yIcon);
|
||||
}
|
||||
};
|
||||
|
||||
@ -101,7 +115,8 @@ SearchTab.prototype = {
|
||||
active; it should not exceed ~30
|
||||
characters. */
|
||||
hint_text: _("Type to search..."),
|
||||
track_hover: true });
|
||||
track_hover: true,
|
||||
can_focus: true });
|
||||
this._text = this._entry.clutter_text;
|
||||
this._text.connect('key-press-event', Lang.bind(this, this._onKeyPress));
|
||||
|
||||
@ -118,7 +133,9 @@ SearchTab.prototype = {
|
||||
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem, this._openSearchSystem);
|
||||
BaseTab.prototype._init.call(this,
|
||||
this._entry,
|
||||
this._searchResults.actor);
|
||||
this._searchResults.actor,
|
||||
_("Search"),
|
||||
'edit-find');
|
||||
|
||||
this._text.connect('text-changed', Lang.bind(this, this._onTextChanged));
|
||||
this._text.connect('activate', Lang.bind(this, function (se) {
|
||||
@ -366,8 +383,8 @@ ViewSelector.prototype = {
|
||||
}));
|
||||
},
|
||||
|
||||
addViewTab: function(title, pageActor) {
|
||||
let viewTab = new ViewTab(title, pageActor);
|
||||
addViewTab: function(title, pageActor, a11yIcon) {
|
||||
let viewTab = new ViewTab(title, pageActor, a11yIcon);
|
||||
this._tabs.push(viewTab);
|
||||
this._tabBox.add(viewTab.title);
|
||||
this._addTab(viewTab);
|
||||
|
@ -15,12 +15,11 @@ function WindowAttentionHandler() {
|
||||
WindowAttentionHandler.prototype = {
|
||||
_init : function() {
|
||||
this._startupIds = {};
|
||||
this._sources = {};
|
||||
this._tracker = Shell.WindowTracker.get_default();
|
||||
this._tracker.connect('startup-sequence-changed', Lang.bind(this, this._onStartupSequenceChanged));
|
||||
|
||||
let display = global.screen.get_display();
|
||||
display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
tracker.connect('startup-sequence-changed', Lang.bind(this, this._onStartupSequenceChanged));
|
||||
},
|
||||
|
||||
_onStartupSequenceChanged : function(tracker) {
|
||||
@ -57,28 +56,16 @@ WindowAttentionHandler.prototype = {
|
||||
if (!window || window.has_focus() || window.is_skip_taskbar())
|
||||
return;
|
||||
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
let app = tracker.get_window_app(window);
|
||||
let appId = app.get_id();
|
||||
|
||||
let source = this._sources[appId];
|
||||
if (source == null) {
|
||||
source = new Source(app, window);
|
||||
this._sources[appId] = source;
|
||||
Main.messageTray.add(source);
|
||||
source.connect('destroy', Lang.bind(this, function() { delete this._sources[appId]; }));
|
||||
}
|
||||
let app = this._tracker.get_window_app(window);
|
||||
let source = new Source(app, window);
|
||||
Main.messageTray.add(source);
|
||||
|
||||
let notification = new MessageTray.Notification(source, this._getTitle(app, window), this._getBanner(app, window));
|
||||
source.notify(notification);
|
||||
|
||||
window.connect('notify::title', Lang.bind(this, function(win) {
|
||||
notification.update(this._getTitle(app, win), this._getBanner(app, win));
|
||||
}));
|
||||
window.connect('notify::demands-attention', Lang.bind(this, function() { source.destroy(); }));
|
||||
window.connect('focus', Lang.bind(this, function() { source.destroy(); }));
|
||||
window.connect('unmanaged', Lang.bind(this, function() { source.destroy(); }));
|
||||
|
||||
source.signalIDs.push(window.connect('notify::title', Lang.bind(this, function(win) {
|
||||
notification.update(this._getTitle(app, win), this._getBanner(app, win));
|
||||
})));
|
||||
}
|
||||
};
|
||||
|
||||
@ -94,13 +81,27 @@ Source.prototype = {
|
||||
this._window = window;
|
||||
this._app = app;
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
|
||||
this.signalIDs = [];
|
||||
this.signalIDs.push(this._window.connect('notify::demands-attention', Lang.bind(this, function() { this.destroy(); })));
|
||||
this.signalIDs.push(this._window.connect('focus', Lang.bind(this, function() { this.destroy(); })));
|
||||
this.signalIDs.push(this._window.connect('unmanaged', Lang.bind(this, function() { this.destroy(); })));
|
||||
|
||||
this.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
for(let i = 0; i < this.signalIDs.length; i++) {
|
||||
this._window.disconnect(this.signalIDs[i]);
|
||||
}
|
||||
this.signalIDs = [];
|
||||
},
|
||||
|
||||
createNotificationIcon : function() {
|
||||
return this._app.create_icon_texture(this.ICON_SIZE);
|
||||
},
|
||||
|
||||
_notificationClicked : function(notification) {
|
||||
open : function(notification) {
|
||||
Main.activateWindow(this._window);
|
||||
this.destroy();
|
||||
}
|
||||
|
@ -93,6 +93,8 @@ WindowManager.prototype = {
|
||||
|
||||
this._dimmedWindows = [];
|
||||
|
||||
this._animationBlockCount = 0;
|
||||
|
||||
this._switchData = null;
|
||||
this._shellwm.connect('kill-switch-workspace', Lang.bind(this, this._switchWorkspaceDone));
|
||||
this._shellwm.connect('kill-window-effects', Lang.bind(this, function (shellwm, actor) {
|
||||
@ -117,6 +119,7 @@ WindowManager.prototype = {
|
||||
this.setKeybindingHandler('switch_to_workspace_down', Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setKeybindingHandler('switch_windows', Lang.bind(this, this._startAppSwitcher));
|
||||
this.setKeybindingHandler('switch_group', Lang.bind(this, this._startAppSwitcher));
|
||||
this.setKeybindingHandler('switch_panels', Lang.bind(this, this._startA11ySwitcher));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
for (let i = 0; i < this._dimmedWindows.length; i++)
|
||||
@ -138,8 +141,16 @@ WindowManager.prototype = {
|
||||
this._shellwm.connect('keybinding::' + keybinding, handler);
|
||||
},
|
||||
|
||||
blockAnimations: function() {
|
||||
this._animationBlockCount++;
|
||||
},
|
||||
|
||||
unblockAnimations: function() {
|
||||
this._animationBlockCount = Math.max(0, this._animationBlockCount - 1);
|
||||
},
|
||||
|
||||
_shouldAnimate : function(actor) {
|
||||
if (Main.overview.visible)
|
||||
if (Main.overview.visible || this._animationsBlocked > 0)
|
||||
return false;
|
||||
if (actor && (actor.meta_window.get_window_type() != Meta.WindowType.NORMAL))
|
||||
return false;
|
||||
@ -525,6 +536,10 @@ WindowManager.prototype = {
|
||||
tabPopup.destroy();
|
||||
},
|
||||
|
||||
_startA11ySwitcher : function(shellwm, binding, window, backwards) {
|
||||
Main.ctrlAltTabManager.popup(backwards);
|
||||
},
|
||||
|
||||
_showWorkspaceSwitcher : function(shellwm, binding, window, backwards) {
|
||||
if (global.screen.n_workspaces == 1)
|
||||
return;
|
||||
|
@ -125,6 +125,7 @@ WindowClone.prototype = {
|
||||
dragActorMaxSize: WINDOW_DND_SIZE,
|
||||
dragActorOpacity: DRAGGING_WINDOW_OPACITY });
|
||||
this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled));
|
||||
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
this.inDrag = false;
|
||||
|
||||
@ -288,10 +289,16 @@ WindowClone.prototype = {
|
||||
},
|
||||
|
||||
_onDragBegin : function (draggable, time) {
|
||||
[this.dragOrigX, this.dragOrigY] = this.actor.get_position();
|
||||
this.dragOrigScale = this.actor.scale_x;
|
||||
this.inDrag = true;
|
||||
this.emit('drag-begin');
|
||||
},
|
||||
|
||||
_onDragCancelled : function (draggable, time) {
|
||||
this.emit('drag-cancelled');
|
||||
},
|
||||
|
||||
_onDragEnd : function (draggable, time, snapback) {
|
||||
this.inDrag = false;
|
||||
|
||||
@ -327,6 +334,7 @@ WindowOverlay.prototype = {
|
||||
|
||||
this._windowClone = windowClone;
|
||||
this._parentActor = parentActor;
|
||||
this._hidden = false;
|
||||
|
||||
let title = new St.Label({ style_class: 'window-caption',
|
||||
text: metaWindow.title });
|
||||
@ -372,11 +380,13 @@ WindowOverlay.prototype = {
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this._hidden = true;
|
||||
this.closeButton.hide();
|
||||
this.title.hide();
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this._hidden = false;
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
|
||||
x, y);
|
||||
@ -387,8 +397,8 @@ WindowOverlay.prototype = {
|
||||
},
|
||||
|
||||
fadeIn: function() {
|
||||
this.show();
|
||||
this.title.opacity = 0;
|
||||
this.title.show();
|
||||
this._parentActor.raise_top();
|
||||
Tweener.addTween(this.title,
|
||||
{ opacity: 255,
|
||||
@ -419,8 +429,13 @@ WindowOverlay.prototype = {
|
||||
let button = this.closeButton;
|
||||
let title = this.title;
|
||||
|
||||
let buttonX = cloneX + cloneWidth - button._overlap;
|
||||
let buttonY = cloneY - button.height + button._overlap;
|
||||
let buttonX;
|
||||
let buttonY = cloneY - (button.height - button._overlap);
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
buttonX = cloneX - (button.width - button._overlap);
|
||||
else
|
||||
buttonX = cloneX + (cloneWidth - button._overlap);
|
||||
|
||||
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
|
||||
|
||||
if (!title.fullWidth)
|
||||
@ -475,6 +490,12 @@ WindowOverlay.prototype = {
|
||||
},
|
||||
|
||||
_onEnter: function() {
|
||||
// We might get enter events on the clone while the overlay is
|
||||
// hidden, e.g. during animations, we ignore these events,
|
||||
// as the close button will be shown as needed when the overlays
|
||||
// are shown again
|
||||
if (this._hidden)
|
||||
return;
|
||||
this._parentActor.raise_top();
|
||||
this.closeButton.show();
|
||||
this.emit('show-close-button');
|
||||
@ -515,7 +536,7 @@ WindowOverlay.prototype = {
|
||||
Signals.addSignalMethods(WindowOverlay.prototype);
|
||||
|
||||
const WindowPositionFlags = {
|
||||
ZOOM: 1 << 0,
|
||||
INITIAL: 1 << 0,
|
||||
ANIMATE: 1 << 1
|
||||
};
|
||||
|
||||
@ -531,27 +552,26 @@ Workspace.prototype = {
|
||||
// When dragging a window, we use this slot for reserve space.
|
||||
this._reservedSlot = null;
|
||||
this.metaWorkspace = metaWorkspace;
|
||||
this._x = 0;
|
||||
this._y = 0;
|
||||
this._width = 0;
|
||||
this._height = 0;
|
||||
|
||||
this._windowOverlaysGroup = new Clutter.Group();
|
||||
// Without this the drop area will be overlapped.
|
||||
this._windowOverlaysGroup.set_size(0, 0);
|
||||
|
||||
this.actor = new Clutter.Group();
|
||||
this.actor._delegate = this;
|
||||
this.actor.set_size(0, 0);
|
||||
|
||||
this._dropRect = new Clutter.Rectangle({ opacity: 0 });
|
||||
this._dropRect._delegate = this;
|
||||
|
||||
this.actor.add_actor(this._dropRect);
|
||||
this.actor.add_actor(this._windowOverlaysGroup);
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
// Items in _windowOverlaysGroup should not be scaled, so we don't
|
||||
// add them to this.actor, but to its parent whenever it changes
|
||||
this.actor.connect('parent-set', Lang.bind(this, this._onParentSet));
|
||||
|
||||
// Auto-sizing is unreliable in the presence of ClutterClone, so rather than
|
||||
// implicitly counting on the workspace actor to be sized to the size of the
|
||||
// included desktop actor clone, set the size explicitly to the screen size.
|
||||
// See http://bugzilla.openedhand.com/show_bug.cgi?id=1755
|
||||
this.actor.width = global.screen_width;
|
||||
this.actor.height = global.screen_height;
|
||||
this.scale = 1.0;
|
||||
|
||||
let windows = Main.getWindowActorsForWorkspace(this.metaWorkspace.index());
|
||||
|
||||
// Create clones for windows that should be
|
||||
@ -574,6 +594,22 @@ Workspace.prototype = {
|
||||
this.leavingOverview = false;
|
||||
},
|
||||
|
||||
setGeometry: function(x, y, width, height) {
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
|
||||
// This is sometimes called during allocation, so we do this later
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
|
||||
function () {
|
||||
this._dropRect.set_position(x, y);
|
||||
this._dropRect.set_size(width, height);
|
||||
return false;
|
||||
}));
|
||||
|
||||
},
|
||||
|
||||
_lookupIndex: function (metaWindow) {
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
if (this._windows[i].metaWindow == metaWindow) {
|
||||
@ -583,17 +619,6 @@ Workspace.prototype = {
|
||||
return -1;
|
||||
},
|
||||
|
||||
_onParentSet: function(actor, old_parent) {
|
||||
let new_parent = this.actor.get_parent();
|
||||
if (new_parent == null)
|
||||
return;
|
||||
|
||||
if (old_parent)
|
||||
this._windowOverlaysGroup.reparent(new_parent);
|
||||
else
|
||||
new_parent.add_actor(this._windowOverlaysGroup);
|
||||
},
|
||||
|
||||
containsMetaWindow: function (metaWindow) {
|
||||
return this._lookupIndex(metaWindow) >= 0;
|
||||
},
|
||||
@ -672,10 +697,20 @@ Workspace.prototype = {
|
||||
let xDelta, yDelta, distanceSquared;
|
||||
let actorWidth, actorHeight;
|
||||
|
||||
actorWidth = actor.width * actor.scale_x;
|
||||
actorHeight = actor.height * actor.scale_y;
|
||||
xDelta = actor.x + actorWidth / 2.0 - xCenter * global.screen_width;
|
||||
yDelta = actor.y + actorHeight / 2.0 - yCenter * global.screen_height;
|
||||
let x = actor.x;
|
||||
let y = actor.y;
|
||||
let scale = actor.scale_x;
|
||||
|
||||
if (actor._delegate.inDrag) {
|
||||
x = actor._delegate.dragOrigX;
|
||||
y = actor._delegate.dragOrigY;
|
||||
scale = actor._delegate.dragOrigScale;
|
||||
}
|
||||
|
||||
actorWidth = actor.width * scale;
|
||||
actorHeight = actor.height * scale;
|
||||
xDelta = x + actorWidth / 2.0 - xCenter * this._width - this._x;
|
||||
yDelta = y + actorHeight / 2.0 - yCenter * this._height - this._y;
|
||||
distanceSquared = xDelta * xDelta + yDelta * yDelta;
|
||||
|
||||
return distanceSquared;
|
||||
@ -700,6 +735,12 @@ Workspace.prototype = {
|
||||
let delta = this._computeWindowMotion(cloneActor, slot);
|
||||
|
||||
motion += delta;
|
||||
|
||||
// Bail out early if we're already larger than the
|
||||
// previous best
|
||||
if (minimumMotionPermutation != null &&
|
||||
motion > minimumMotion)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (minimumMotionPermutation == null || motion < minimumMotion) {
|
||||
@ -760,38 +801,35 @@ Workspace.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* _getSlotRelativeGeometry:
|
||||
* _getSlotGeometry:
|
||||
* @slot: A layout slot
|
||||
*
|
||||
* Returns: the workspace-relative [x, y, width, height]
|
||||
* Returns: the screen-relative [x, y, width, height]
|
||||
* of a given window layout slot.
|
||||
*/
|
||||
_getSlotRelativeGeometry: function(slot) {
|
||||
_getSlotGeometry: function(slot) {
|
||||
let [xCenter, yCenter, fraction] = slot;
|
||||
|
||||
let width = global.screen_width * fraction;
|
||||
let height = global.screen_height * fraction;
|
||||
let width = this._width * fraction;
|
||||
let height = this._height * fraction;
|
||||
|
||||
let x = xCenter * global.screen_width - width / 2;
|
||||
let y = yCenter * global.screen_height - height / 2;
|
||||
let x = this._x + xCenter * this._width - width / 2 ;
|
||||
let y = this._y + yCenter * this._height - height / 2;
|
||||
|
||||
return [x, y, width, height];
|
||||
},
|
||||
|
||||
/**
|
||||
* _computeWindowRelativeLayout:
|
||||
* _computeWindowLayout:
|
||||
* @metaWindow: A #MetaWindow
|
||||
* @slot: A layout slot
|
||||
*
|
||||
* Given a window and slot to fit it in, compute its
|
||||
* workspace-relative [x, y, scale] where scale applies
|
||||
* screen-relative [x, y, scale] where scale applies
|
||||
* to both X and Y directions.
|
||||
*/
|
||||
_computeWindowRelativeLayout: function(metaWindow, slot) {
|
||||
let [xCenter, yCenter, fraction] = slot;
|
||||
let [x, y, width, height] = this._getSlotRelativeGeometry(slot);
|
||||
|
||||
xCenter = xCenter * global.screen_width;
|
||||
_computeWindowLayout: function(metaWindow, slot) {
|
||||
let [x, y, width, height] = this._getSlotGeometry(slot);
|
||||
|
||||
let rect = metaWindow.get_outer_rect();
|
||||
let buttonOuterHeight, captionHeight;
|
||||
@ -799,23 +837,19 @@ Workspace.prototype = {
|
||||
|
||||
if (this._windowOverlays[0]) {
|
||||
[buttonOuterHeight, captionHeight] = this._windowOverlays[0].chromeHeights();
|
||||
buttonOuterWidth = this._windowOverlays[0].chromeWidth() / this.scale;
|
||||
buttonOuterWidth = this._windowOverlays[0].chromeWidth();
|
||||
} else
|
||||
[buttonOuterHeight, captionHeight] = [0, 0];
|
||||
buttonOuterHeight /= this.scale;
|
||||
captionHeight /= this.scale;
|
||||
|
||||
let desiredWidth = global.screen_width * fraction;
|
||||
let desiredHeight = global.screen_height * fraction;
|
||||
let scale = Math.min((desiredWidth - buttonOuterWidth) / rect.width,
|
||||
(desiredHeight - buttonOuterHeight - captionHeight) / rect.height,
|
||||
1.0 / this.scale);
|
||||
let scale = Math.min((width - buttonOuterWidth) / rect.width,
|
||||
(height - buttonOuterHeight - captionHeight) / rect.height,
|
||||
1.0);
|
||||
|
||||
x = Math.floor(xCenter - 0.5 * scale * rect.width);
|
||||
x = Math.floor(x + (width - scale * rect.width) / 2);
|
||||
|
||||
// We want to center the window in case we have just one
|
||||
if (metaWindow.get_workspace().n_windows == 1)
|
||||
y = Math.floor(yCenter * global.screen_height - 0.5 * scale * rect.height);
|
||||
y = Math.floor(y + (height - scale * rect.height) / 2);
|
||||
else
|
||||
y = Math.floor(y + height - rect.height * scale - captionHeight);
|
||||
|
||||
@ -841,7 +875,7 @@ Workspace.prototype = {
|
||||
/**
|
||||
* positionWindows:
|
||||
* @flags:
|
||||
* ZOOM - workspace is moving at the same time and we need to take that into account.
|
||||
* INITIAL - this is the initial positioning of the windows.
|
||||
* ANIMATE - Indicates that we need animate changing position.
|
||||
*/
|
||||
positionWindows : function(flags) {
|
||||
@ -854,7 +888,7 @@ Workspace.prototype = {
|
||||
if (this._reservedSlot)
|
||||
clones.push(this._reservedSlot);
|
||||
|
||||
let workspaceZooming = flags & WindowPositionFlags.ZOOM;
|
||||
let initialPositioning = flags & WindowPositionFlags.INITIAL;
|
||||
let animate = flags & WindowPositionFlags.ANIMATE;
|
||||
|
||||
// Start the animations
|
||||
@ -876,7 +910,7 @@ Workspace.prototype = {
|
||||
if (clone.inDrag)
|
||||
continue;
|
||||
|
||||
let [x, y, scale] = this._computeWindowRelativeLayout(metaWindow, slot);
|
||||
let [x, y, scale] = this._computeWindowLayout(metaWindow, slot);
|
||||
|
||||
if (overlay)
|
||||
overlay.hide();
|
||||
@ -885,7 +919,7 @@ Workspace.prototype = {
|
||||
/* Hidden windows should fade in and grow
|
||||
* therefore we need to resize them now so they
|
||||
* can be scaled up later */
|
||||
if (workspaceZooming) {
|
||||
if (initialPositioning) {
|
||||
clone.actor.opacity = 0;
|
||||
clone.actor.scale_x = 0;
|
||||
clone.actor.scale_y = 0;
|
||||
@ -906,7 +940,6 @@ Workspace.prototype = {
|
||||
y: y,
|
||||
scale_x: scale,
|
||||
scale_y: scale,
|
||||
workspace_relative: workspaceZooming ? this : null,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
@ -929,7 +962,7 @@ Workspace.prototype = {
|
||||
let clone = clones[i];
|
||||
let metaWindow = clone.metaWindow;
|
||||
if (i == 0) {
|
||||
clone.setStackAbove(null);
|
||||
clone.setStackAbove(this._dropRect);
|
||||
} else {
|
||||
let previousClone = clones[i - 1];
|
||||
clone.setStackAbove(previousClone.actor);
|
||||
@ -949,10 +982,8 @@ Workspace.prototype = {
|
||||
// be after the workspace animation finishes.
|
||||
let [cloneX, cloneY] = clone.actor.get_position();
|
||||
let [cloneWidth, cloneHeight] = clone.actor.get_size();
|
||||
cloneX = this.x + this.scale * cloneX;
|
||||
cloneY = this.y + this.scale * cloneY;
|
||||
cloneWidth = this.scale * clone.actor.scale_x * cloneWidth;
|
||||
cloneHeight = this.scale * clone.actor.scale_y * cloneHeight;
|
||||
cloneWidth = clone.actor.scale_x * cloneWidth;
|
||||
cloneHeight = clone.actor.scale_y * cloneHeight;
|
||||
|
||||
if (overlay) {
|
||||
overlay.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight);
|
||||
@ -977,12 +1008,10 @@ Workspace.prototype = {
|
||||
return true;
|
||||
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
let wsWidth = this.actor.width * this.scale;
|
||||
let wsHeight = this.actor.height * this.scale;
|
||||
|
||||
let pointerHasMoved = (this._cursorX != x && this._cursorY != y);
|
||||
let inWorkspace = (this.x < x && x < this.x + wsWidth &&
|
||||
this.y < y && y < this.y + wsHeight);
|
||||
let inWorkspace = (this._x < x && x < this._x + this._width &&
|
||||
this._y < y && y < this._y + this._height);
|
||||
|
||||
if (pointerHasMoved && inWorkspace) {
|
||||
// store current cursor position
|
||||
@ -1083,13 +1112,20 @@ Workspace.prototype = {
|
||||
let clone = this._addWindowClone(win);
|
||||
|
||||
if (win._overviewHint) {
|
||||
let x = (win._overviewHint.x - this.actor.x) / this.scale;
|
||||
let y = (win._overviewHint.y - this.actor.y) / this.scale;
|
||||
let scale = win._overviewHint.scale / this.scale;
|
||||
let x = win._overviewHint.x - this.actor.x;
|
||||
let y = win._overviewHint.y - this.actor.y;
|
||||
let scale = win._overviewHint.scale;
|
||||
delete win._overviewHint;
|
||||
|
||||
clone.actor.set_position (x, y);
|
||||
clone.actor.set_scale (scale, scale);
|
||||
} else {
|
||||
// Position new windows at the top corner of the workspace rather
|
||||
// than where they were placed for real to avoid the window
|
||||
// being clipped to the workspaceView. Its not really more
|
||||
// natural for the window to suddenly appear in the overview
|
||||
// on some seemingly random location anyway.
|
||||
clone.actor.set_position (this._x, this._y);
|
||||
}
|
||||
|
||||
this.positionWindows(WindowPositionFlags.ANIMATE);
|
||||
@ -1109,14 +1145,11 @@ Workspace.prototype = {
|
||||
|
||||
// Animate the full-screen to Overview transition.
|
||||
zoomToOverview : function() {
|
||||
this.actor.set_position(this.x, this.y);
|
||||
this.actor.set_scale(this.scale, this.scale);
|
||||
|
||||
// Position and scale the windows.
|
||||
if (Main.overview.animationInProgress)
|
||||
this.positionWindows(WindowPositionFlags.ANIMATE | WindowPositionFlags.ZOOM);
|
||||
this.positionWindows(WindowPositionFlags.ANIMATE | WindowPositionFlags.INITIAL);
|
||||
else
|
||||
this.positionWindows(WindowPositionFlags.ZOOM);
|
||||
this.positionWindows(WindowPositionFlags.INITIAL);
|
||||
},
|
||||
|
||||
// Animates the return from Overview mode
|
||||
@ -1134,7 +1167,7 @@ Workspace.prototype = {
|
||||
this._overviewHiddenId = Main.overview.connect('hidden', Lang.bind(this,
|
||||
this._doneLeavingOverview));
|
||||
|
||||
if (this._metaWorkspace == currentWorkspace)
|
||||
if (this.metaWorkspace != currentWorkspace)
|
||||
return;
|
||||
|
||||
// Position and scale the windows.
|
||||
@ -1149,7 +1182,6 @@ Workspace.prototype = {
|
||||
y: clone.origY,
|
||||
scale_x: 1.0,
|
||||
scale_y: 1.0,
|
||||
workspace_relative: this,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
opacity: 255,
|
||||
transition: 'easeOutQuad'
|
||||
@ -1160,7 +1192,6 @@ Workspace.prototype = {
|
||||
{ scale_x: 0,
|
||||
scale_y: 0,
|
||||
opacity: 0,
|
||||
workspace_relative: this,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
@ -1223,6 +1254,10 @@ Workspace.prototype = {
|
||||
Main.overview.beginWindowDrag();
|
||||
overlay.hide();
|
||||
}));
|
||||
clone.connect('drag-cancelled',
|
||||
Lang.bind(this, function(clone) {
|
||||
Main.overview.cancelledWindowDrag();
|
||||
}));
|
||||
clone.connect('drag-end',
|
||||
Lang.bind(this, function(clone) {
|
||||
Main.overview.endWindowDrag();
|
||||
@ -1330,56 +1365,3 @@ Workspace.prototype = {
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(Workspace.prototype);
|
||||
|
||||
// Create a SpecialPropertyModifier to let us move windows in a
|
||||
// straight line on the screen even though their containing workspace
|
||||
// is also moving.
|
||||
Tweener.registerSpecialPropertyModifier('workspace_relative', _workspaceRelativeModifier, _workspaceRelativeGet);
|
||||
|
||||
function _workspaceRelativeModifier(workspace) {
|
||||
let [startX, startY] = Main.overview.getPosition();
|
||||
let overviewPosX, overviewPosY, overviewScale;
|
||||
|
||||
if (!workspace)
|
||||
return [];
|
||||
|
||||
if (workspace.leavingOverview) {
|
||||
let [zoomedInX, zoomedInY] = Main.overview.getZoomedInPosition();
|
||||
overviewPosX = { begin: startX, end: zoomedInX };
|
||||
overviewPosY = { begin: startY, end: zoomedInY };
|
||||
overviewScale = { begin: Main.overview.getScale(),
|
||||
end: Main.overview.getZoomedInScale() };
|
||||
} else {
|
||||
overviewPosX = { begin: startX, end: 0 };
|
||||
overviewPosY = { begin: startY, end: 0 };
|
||||
overviewScale = { begin: Main.overview.getScale(), end: 1 };
|
||||
}
|
||||
|
||||
return [ { name: 'x',
|
||||
parameters: { workspacePos: workspace.x,
|
||||
overviewPos: overviewPosX,
|
||||
overviewScale: overviewScale } },
|
||||
{ name: 'y',
|
||||
parameters: { workspacePos: workspace.y,
|
||||
overviewPos: overviewPosY,
|
||||
overviewScale: overviewScale } }
|
||||
];
|
||||
}
|
||||
|
||||
function _workspaceRelativeGet(begin, end, time, params) {
|
||||
let curOverviewPos = (1 - time) * params.overviewPos.begin +
|
||||
time * params.overviewPos.end;
|
||||
let curOverviewScale = (1 - time) * params.overviewScale.begin +
|
||||
time * params.overviewScale.end;
|
||||
|
||||
// Calculate the screen position of the window.
|
||||
let screen = (1 - time) *
|
||||
((begin + params.workspacePos) * params.overviewScale.begin +
|
||||
params.overviewPos.begin) +
|
||||
time *
|
||||
((end + params.workspacePos) * params.overviewScale.end +
|
||||
params.overviewPos.end);
|
||||
|
||||
// Return the workspace coordinates.
|
||||
return (screen - curOverviewPos) / curOverviewScale - params.workspacePos;
|
||||
}
|
||||
|
@ -402,10 +402,6 @@ ThumbnailsBox.prototype = {
|
||||
// for the border and padding of the background actor.
|
||||
this._background = new St.Bin({ style_class: 'workspace-thumbnails-background' });
|
||||
|
||||
// This will eventually be automatic, see https://bugzilla.gnome.org/show_bug.cgi?id=584662
|
||||
if (St.Widget.get_default_direction () == St.TextDirection.RTL)
|
||||
this._background.add_style_pseudo_class('rtl');
|
||||
|
||||
this.actor.add_actor(this._background);
|
||||
|
||||
let indicator = new St.Bin({ style_class: 'workspace-thumbnail-indicator' });
|
||||
|
@ -25,14 +25,13 @@ const MAX_WORKSPACES = 16;
|
||||
const CONTROLS_POP_IN_TIME = 0.1;
|
||||
|
||||
|
||||
function WorkspacesView(width, height, x, y, workspaces) {
|
||||
this._init(width, height, x, y, workspaces);
|
||||
function WorkspacesView(workspaces) {
|
||||
this._init(workspaces);
|
||||
}
|
||||
|
||||
WorkspacesView.prototype = {
|
||||
_init: function(width, height, x, y, workspaces) {
|
||||
_init: function(workspaces) {
|
||||
this.actor = new St.Group({ style_class: 'workspaces-view' });
|
||||
this.actor.set_clip(x, y, width, height);
|
||||
|
||||
// The actor itself isn't a drop target, so we don't want to pick on its area
|
||||
this.actor.set_size(0, 0);
|
||||
@ -43,19 +42,16 @@ WorkspacesView.prototype = {
|
||||
function() {
|
||||
let node = this.actor.get_theme_node();
|
||||
this._spacing = node.get_length('spacing');
|
||||
this._computeWorkspacePositions();
|
||||
this._updateWorkspaceActors(false);
|
||||
}));
|
||||
this.actor.connect('notify::mapped',
|
||||
Lang.bind(this, this._onMappedChanged));
|
||||
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._zoomScale = 1.0;
|
||||
this._width = 0;
|
||||
this._height = 0;
|
||||
this._x = 0;
|
||||
this._y = 0;
|
||||
this._spacing = 0;
|
||||
this._activeWorkspaceX = 0; // x offset of active ws while dragging
|
||||
this._activeWorkspaceY = 0; // y offset of active ws while dragging
|
||||
this._lostWorkspaces = [];
|
||||
this._animating = false; // tweening
|
||||
this._scrolling = false; // swipe-scrolling
|
||||
@ -81,6 +77,11 @@ WorkspacesView.prototype = {
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].zoomToOverview();
|
||||
}));
|
||||
this._overviewShownId =
|
||||
Main.overview.connect('shown',
|
||||
Lang.bind(this, function() {
|
||||
this.actor.set_clip(this._x, this._y, this._width, this._height);
|
||||
}));
|
||||
|
||||
this._scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
|
||||
lower: 0,
|
||||
@ -109,30 +110,17 @@ WorkspacesView.prototype = {
|
||||
this._swipeScrollEndId = 0;
|
||||
},
|
||||
|
||||
setZoomScale: function(zoomScale) {
|
||||
if (zoomScale == this._zoomScale)
|
||||
return;
|
||||
setGeometry: function(x, y, width, height) {
|
||||
if (this._x == x && this._y == y &&
|
||||
this._width == width && this._height == height)
|
||||
return;
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
|
||||
this._zoomScale = zoomScale;
|
||||
if (this._zoomOut) {
|
||||
// If we are already zoomed out, then we have to reposition.
|
||||
// Note that when shown initially zoomOut is false, so we
|
||||
// won't trigger this.
|
||||
|
||||
// setZoomScale can be invoked when the workspaces view is
|
||||
// reallocated. Since we just want to animate things to the
|
||||
// new position it seems OK to call updateWorkspaceActors
|
||||
// immediately - adding a tween doesn't immediately cause
|
||||
// a new allocation. But hide/show of the window overlays we
|
||||
// do around animation does, so we need to do it later.
|
||||
// This can be removed when we fix things to not hide/show
|
||||
// the window overlay.
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||||
Lang.bind(this, function() {
|
||||
this._computeWorkspacePositions();
|
||||
this._updateWorkspaceActors(true);
|
||||
}));
|
||||
}
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].setGeometry(x, y, width, height);
|
||||
},
|
||||
|
||||
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
||||
@ -154,6 +142,8 @@ WorkspacesView.prototype = {
|
||||
|
||||
activeWorkspace.actor.raise_top();
|
||||
|
||||
this.actor.remove_clip(this._x, this._y, this._width, this._height);
|
||||
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].zoomFromOverview();
|
||||
},
|
||||
@ -162,93 +152,27 @@ WorkspacesView.prototype = {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
getScale: function() {
|
||||
return this._workspaces[0].scale;
|
||||
},
|
||||
|
||||
syncStacking: function(stackIndices) {
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].syncStacking(stackIndices);
|
||||
},
|
||||
|
||||
// Get the grid position of the active workspace.
|
||||
getActiveWorkspacePosition: function() {
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
|
||||
|
||||
return [activeWorkspace.x, activeWorkspace.y];
|
||||
},
|
||||
|
||||
zoomOut: function() {
|
||||
if (this._zoomOut)
|
||||
return;
|
||||
|
||||
this._zoomOut = true;
|
||||
this._computeWorkspacePositions();
|
||||
this._updateWorkspaceActors(true);
|
||||
},
|
||||
|
||||
zoomIn: function() {
|
||||
if (!this._zoomOut)
|
||||
return;
|
||||
|
||||
this._zoomOut = false;
|
||||
this._computeWorkspacePositions();
|
||||
this._updateWorkspaceActors(true);
|
||||
},
|
||||
|
||||
// Compute the position, scale and opacity of the workspaces, but don't
|
||||
// actually change the actors to match
|
||||
_computeWorkspacePositions: function() {
|
||||
let active = global.screen.get_active_workspace_index();
|
||||
let zoomScale = this._zoomOut ? this._zoomScale : 1;
|
||||
let scale = zoomScale * this._width / global.screen_width;
|
||||
|
||||
let _width = this._workspaces[0].actor.width * scale;
|
||||
let _height = this._workspaces[0].actor.height * scale;
|
||||
|
||||
this._activeWorkspaceX = (this._width - _width) / 2;
|
||||
this._activeWorkspaceY = (this._height - _height) / 2;
|
||||
|
||||
for (let w = 0; w < this._workspaces.length; w++) {
|
||||
let workspace = this._workspaces[w];
|
||||
|
||||
workspace.opacity = (this._inDrag && w != active) ? 200 : 255;
|
||||
|
||||
workspace.scale = scale;
|
||||
workspace.x = this._x + this._activeWorkspaceX;
|
||||
|
||||
// We adjust the center because the zoomScale is to leave space for
|
||||
// the expanded workspace control so we want to zoom to either the
|
||||
// left part of the area or the right part of the area
|
||||
let offset = 0.5 * (1 - this._zoomScale) * this._width;
|
||||
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
|
||||
if (this._zoomOut)
|
||||
workspace.x += rtl ? offset : - offset;
|
||||
|
||||
// We divide by zoomScale so that adjacent workspaces are always offscreen
|
||||
// except when we are switching between workspaces
|
||||
workspace.y = this._y + this._activeWorkspaceY
|
||||
+ (w - active) * (_height + this._spacing) / zoomScale;
|
||||
}
|
||||
updateWindowPositions: function() {
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].positionWindows(Workspace.WindowPositionFlags.ANIMATE);
|
||||
},
|
||||
|
||||
_scrollToActive: function(showAnimation) {
|
||||
let active = global.screen.get_active_workspace_index();
|
||||
|
||||
this._computeWorkspacePositions();
|
||||
this._updateWorkspaceActors(showAnimation);
|
||||
this._updateScrollAdjustment(active, showAnimation);
|
||||
},
|
||||
|
||||
// Update workspace actors parameters to the values calculated in
|
||||
// _computeWorkspacePositions()
|
||||
// Update workspace actors parameters
|
||||
// @showAnimation: iff %true, transition between states
|
||||
_updateWorkspaceActors: function(showAnimation) {
|
||||
let active = global.screen.get_active_workspace_index();
|
||||
let targetWorkspaceNewY = this._y + this._activeWorkspaceY;
|
||||
let targetWorkspaceCurrentY = this._workspaces[active].y;
|
||||
let dy = targetWorkspaceNewY - targetWorkspaceCurrentY;
|
||||
|
||||
this._animating = showAnimation;
|
||||
|
||||
@ -257,14 +181,12 @@ WorkspacesView.prototype = {
|
||||
|
||||
Tweener.removeTweens(workspace.actor);
|
||||
|
||||
workspace.y += dy;
|
||||
let opacity = (this._inDrag && w != active) ? 200 : 255;
|
||||
let y = (w - active) * (this._height + this._spacing);
|
||||
|
||||
if (showAnimation) {
|
||||
let params = { x: workspace.x,
|
||||
y: workspace.y,
|
||||
scale_x: workspace.scale,
|
||||
scale_y: workspace.scale,
|
||||
opacity: workspace.opacity,
|
||||
let params = { y: y,
|
||||
opacity: opacity,
|
||||
time: WORKSPACE_SWITCH_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
};
|
||||
@ -281,9 +203,8 @@ WorkspacesView.prototype = {
|
||||
}
|
||||
Tweener.addTween(workspace.actor, params);
|
||||
} else {
|
||||
workspace.actor.set_scale(workspace.scale, workspace.scale);
|
||||
workspace.actor.set_position(workspace.x, workspace.y);
|
||||
workspace.actor.opacity = workspace.opacity;
|
||||
workspace.actor.set_position(0, y);
|
||||
workspace.actor.opacity = opacity;
|
||||
if (w == 0)
|
||||
this._updateVisibility();
|
||||
}
|
||||
@ -294,7 +215,6 @@ WorkspacesView.prototype = {
|
||||
|
||||
Tweener.removeTweens(workspace.actor);
|
||||
|
||||
workspace.y += dy;
|
||||
workspace.actor.show();
|
||||
workspace.hideWindowsOverlays();
|
||||
|
||||
@ -338,7 +258,6 @@ WorkspacesView.prototype = {
|
||||
this._lostWorkspaces[l].destroy();
|
||||
this._lostWorkspaces = [];
|
||||
|
||||
this._computeWorkspacePositions();
|
||||
this._updateWorkspaceActors(false);
|
||||
},
|
||||
|
||||
@ -380,7 +299,6 @@ WorkspacesView.prototype = {
|
||||
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
||||
this.actor.add_actor(this._workspaces[w].actor);
|
||||
|
||||
this._computeWorkspacePositions();
|
||||
this._updateWorkspaceActors(false);
|
||||
} else {
|
||||
this._lostWorkspaces = lostWorkspaces;
|
||||
@ -399,6 +317,7 @@ WorkspacesView.prototype = {
|
||||
_onDestroy: function() {
|
||||
this._scrollAdjustment.run_dispose();
|
||||
Main.overview.disconnect(this._overviewShowingId);
|
||||
Main.overview.disconnect(this._overviewShownId);
|
||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||
|
||||
if (this._timeoutId) {
|
||||
@ -557,12 +476,6 @@ WorkspacesView.prototype = {
|
||||
Main.overview.hide();
|
||||
}
|
||||
|
||||
if (result == Overview.SwipeScrollResult.SWIPE)
|
||||
// The active workspace has changed; while swipe-scrolling
|
||||
// has already taken care of the positioning, the cached
|
||||
// positions need to be updated
|
||||
this._computeWorkspacePositions();
|
||||
|
||||
// Make sure title captions etc are shown as necessary
|
||||
this._updateVisibility();
|
||||
},
|
||||
@ -590,7 +503,7 @@ WorkspacesView.prototype = {
|
||||
return;
|
||||
|
||||
let currentY = firstWorkspaceY;
|
||||
let newY = this._y - adj.value / (adj.upper - 1) * workspacesHeight;
|
||||
let newY = - adj.value / (adj.upper - 1) * workspacesHeight;
|
||||
|
||||
let dy = newY - currentY;
|
||||
|
||||
@ -640,6 +553,7 @@ WorkspacesDisplay.prototype = {
|
||||
this.workspacesView = null;
|
||||
|
||||
this._inDrag = false;
|
||||
this._cancelledDrag = false;
|
||||
this._zoomOut = false;
|
||||
this._zoomFraction = 0;
|
||||
|
||||
@ -649,6 +563,7 @@ WorkspacesDisplay.prototype = {
|
||||
this._itemDragBeginId = 0;
|
||||
this._itemDragEndId = 0;
|
||||
this._windowDragBeginId = 0;
|
||||
this._windowDragCancelledId = 0;
|
||||
this._windowDragEndId = 0;
|
||||
},
|
||||
|
||||
@ -662,43 +577,10 @@ WorkspacesDisplay.prototype = {
|
||||
this._workspaces[i] = new Workspace.Workspace(metaWorkspace);
|
||||
}
|
||||
|
||||
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
|
||||
|
||||
let totalAllocation = this.actor.allocation;
|
||||
let totalWidth = totalAllocation.x2 - totalAllocation.x1;
|
||||
let totalHeight = totalAllocation.y2 - totalAllocation.y1;
|
||||
|
||||
let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
|
||||
|
||||
totalWidth -= controlsVisible;
|
||||
|
||||
// Workspaces expect to have the same ratio as the screen, so take
|
||||
// this into account when fitting the workspace into the available space
|
||||
let width, height;
|
||||
let totalRatio = totalWidth / totalHeight;
|
||||
let wsRatio = global.screen_width / global.screen_height;
|
||||
if (wsRatio > totalRatio) {
|
||||
width = totalWidth;
|
||||
height = Math.floor(totalWidth / wsRatio);
|
||||
} else {
|
||||
width = Math.floor(totalHeight * wsRatio);
|
||||
height = totalHeight;
|
||||
}
|
||||
|
||||
// Position workspaces in the available space
|
||||
let [x, y] = this.actor.get_transformed_position();
|
||||
x = Math.floor(x + Math.abs(totalWidth - width) / 2);
|
||||
y = Math.floor(y + Math.abs(totalHeight - height) / 2);
|
||||
|
||||
if (rtl)
|
||||
x += controlsVisible;
|
||||
|
||||
let newView = new WorkspacesView(width, height, x, y, this._workspaces);
|
||||
this._updateZoomScale();
|
||||
|
||||
if (this.workspacesView)
|
||||
this.workspacesView.destroy();
|
||||
this.workspacesView = newView;
|
||||
this.workspacesView = new WorkspacesView(this._workspaces);
|
||||
this._updateWorkspacesGeometry();
|
||||
|
||||
this._nWorkspacesNotifyId =
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
@ -717,6 +599,9 @@ WorkspacesDisplay.prototype = {
|
||||
if (this._windowDragBeginId == 0)
|
||||
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
|
||||
Lang.bind(this, this._dragBegin));
|
||||
if (this._windowDragCancelledId == 0)
|
||||
this._windowDragCancelledId = Main.overview.connect('window-drag-cancelled',
|
||||
Lang.bind(this, this._dragCancelled));
|
||||
if (this._windowDragEndId == 0)
|
||||
this._windowDragEndId = Main.overview.connect('window-drag-end',
|
||||
Lang.bind(this, this._dragEnd));
|
||||
@ -751,6 +636,10 @@ WorkspacesDisplay.prototype = {
|
||||
Main.overview.disconnect(this._windowDragBeginId);
|
||||
this._windowDragBeginId = 0;
|
||||
}
|
||||
if (this._windowDragCancelledId > 0) {
|
||||
Main.overview.disconnect(this._windowDragCancelledId);
|
||||
this._windowDragCancelledId = 0;
|
||||
}
|
||||
if (this._windowDragEndId > 0) {
|
||||
Main.overview.disconnect(this._windowDragEndId);
|
||||
this._windowDragEndId = 0;
|
||||
@ -809,22 +698,34 @@ WorkspacesDisplay.prototype = {
|
||||
childBox.y2 = box.y2- box.y1;
|
||||
this._controls.allocate(childBox, flags);
|
||||
|
||||
this._updateZoomScale();
|
||||
this._updateWorkspacesGeometry();
|
||||
},
|
||||
|
||||
_updateZoomScale: function() {
|
||||
_updateWorkspacesGeometry: function() {
|
||||
if (!this.workspacesView)
|
||||
return;
|
||||
|
||||
let totalAllocation = this.actor.allocation;
|
||||
let totalWidth = totalAllocation.x2 - totalAllocation.x1;
|
||||
let totalHeight = totalAllocation.y2 - totalAllocation.y1;
|
||||
let width = this.actor.allocation.x2 - this.actor.allocation.x1;
|
||||
let height = this.actor.allocation.y2 - this.actor.allocation.y1;
|
||||
|
||||
let [controlsMin, controlsNatural] = this._controls.get_preferred_width(totalHeight);
|
||||
let [controlsMin, controlsNatural] = this._controls.get_preferred_width(height);
|
||||
let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
|
||||
|
||||
let zoomScale = (totalWidth - controlsNatural) / (totalWidth - controlsVisible);
|
||||
this.workspacesView.setZoomScale(zoomScale);
|
||||
let [x, y] = this.actor.get_transformed_position();
|
||||
|
||||
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
|
||||
|
||||
if (this._zoomOut) {
|
||||
width -= controlsNatural;
|
||||
if (rtl)
|
||||
x += controlsNatural;
|
||||
} else {
|
||||
width -= controlsVisible;
|
||||
if (rtl)
|
||||
x += controlsVisible;
|
||||
}
|
||||
|
||||
this.workspacesView.setGeometry(x, y, width, height);
|
||||
},
|
||||
|
||||
_onRestacked: function() {
|
||||
@ -890,9 +791,10 @@ WorkspacesDisplay.prototype = {
|
||||
if (Main.overview.animationInProgress)
|
||||
return;
|
||||
|
||||
let shouldZoom = this._controls.hover || this._inDrag;
|
||||
let shouldZoom = this._controls.hover || (this._inDrag && !this._cancelledDrag);
|
||||
if (shouldZoom != this._zoomOut) {
|
||||
this._zoomOut = shouldZoom;
|
||||
this._updateWorkspacesGeometry();
|
||||
|
||||
if (!this.workspacesView)
|
||||
return;
|
||||
@ -902,10 +804,7 @@ WorkspacesDisplay.prototype = {
|
||||
time: WORKSPACE_SWITCH_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
|
||||
if (shouldZoom)
|
||||
this.workspacesView.zoomOut();
|
||||
else
|
||||
this.workspacesView.zoomIn();
|
||||
this.workspacesView.updateWindowPositions();
|
||||
}
|
||||
},
|
||||
|
||||
@ -915,6 +814,12 @@ WorkspacesDisplay.prototype = {
|
||||
|
||||
_dragBegin: function() {
|
||||
this._inDrag = true;
|
||||
this._cancelledDrag = false;
|
||||
this._updateZoom();
|
||||
},
|
||||
|
||||
_dragCancelled: function() {
|
||||
this._cancelledDrag = true;
|
||||
this._updateZoom();
|
||||
},
|
||||
|
||||
|
@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Mainloop = imports.mainloop;
|
||||
const DND = imports.ui.dnd;
|
||||
|
||||
function XdndHandler() {
|
||||
@ -41,9 +40,14 @@ XdndHandler.prototype = {
|
||||
// Called when the user cancels the drag (i.e release the button)
|
||||
_onLeave: function() {
|
||||
if (this._windowGroupVisibilityHandlerId != 0) {
|
||||
Mainloop.source_remove(this._windowGroupVisibilityHandlerId);
|
||||
global.window_group.disconnect(this._windowGroupVisibilityHandlerId);
|
||||
this._windowGroupVisibilityHandlerId = 0;
|
||||
}
|
||||
if (this._cursorWindowClone) {
|
||||
this._cursorWindowClone.destroy();
|
||||
this._cursorWindowClone = null;
|
||||
}
|
||||
|
||||
this.emit('drag-end');
|
||||
},
|
||||
|
||||
@ -77,8 +81,7 @@ XdndHandler.prototype = {
|
||||
// Make sure that the clone has the same position as the source
|
||||
this._cursorWindowClone.add_constraint(constraint_position);
|
||||
} else {
|
||||
if (this._cursorWindowClone)
|
||||
{
|
||||
if (this._cursorWindowClone) {
|
||||
this._cursorWindowClone.destroy();
|
||||
this._cursorWindowClone = null;
|
||||
}
|
||||
|
Reference in New Issue
Block a user