layout: merge chrome.js into layout.js
LayoutManager and Chrome are already somewhat intertwined and will be becoming more so. As a first step in merging them, move the Chrome object into layout.js (with no other code changes). https://bugzilla.gnome.org/show_bug.cgi?id=655813
This commit is contained in:
parent
bcd307066a
commit
99149f9c41
@ -20,7 +20,6 @@ nobase_dist_js_DATA = \
|
||||
ui/autorunManager.js \
|
||||
ui/boxpointer.js \
|
||||
ui/calendar.js \
|
||||
ui/chrome.js \
|
||||
ui/ctrlAltTab.js \
|
||||
ui/dash.js \
|
||||
ui/dateMenu.js \
|
||||
|
445
js/ui/chrome.js
445
js/ui/chrome.js
@ -1,445 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const ScreenSaver = imports.misc.screenSaver;
|
||||
|
||||
// This manages the shell "chrome"; the UI that's visible in the
|
||||
// normal mode (ie, outside the Overview), that surrounds the main
|
||||
// workspace content.
|
||||
|
||||
const defaultParams = {
|
||||
visibleInFullscreen: false,
|
||||
affectsStruts: false,
|
||||
affectsInputRegion: true
|
||||
};
|
||||
|
||||
function Chrome() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
Chrome.prototype = {
|
||||
_init: function() {
|
||||
// The group itself has zero size so it doesn't interfere with DND
|
||||
this.actor = new Shell.GenericContainer({ width: 0, height: 0 });
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocated));
|
||||
|
||||
this._monitors = [];
|
||||
this._inOverview = false;
|
||||
|
||||
this._trackedActors = [];
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._relayout));
|
||||
global.screen.connect('restacked',
|
||||
Lang.bind(this, this._windowsRestacked));
|
||||
|
||||
// Need to update struts on new workspaces when they are added
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, this._overviewShowing));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._overviewHidden));
|
||||
|
||||
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
|
||||
this._screenSaverProxy.connect('ActiveChanged', Lang.bind(this, this._onScreenSaverActiveChanged));
|
||||
this._screenSaverProxy.GetActiveRemote(Lang.bind(this,
|
||||
function(result, err) {
|
||||
if (!err)
|
||||
this._onScreenSaverActiveChanged(this._screenSaverProxy, result);
|
||||
}));
|
||||
|
||||
this._relayout();
|
||||
},
|
||||
|
||||
_allocated: function(actor, box, flags) {
|
||||
let children = this.actor.get_children();
|
||||
for (let i = 0; i < children.length; i++)
|
||||
children[i].allocate_preferred_size(flags);
|
||||
},
|
||||
|
||||
// addActor:
|
||||
// @actor: an actor to add to the chrome layer
|
||||
// @params: (optional) additional params
|
||||
//
|
||||
// Adds @actor to the chrome layer and (unless %affectsInputRegion
|
||||
// is %false) extends the input region to include it. Changes in
|
||||
// @actor's size, position, and visibility will automatically
|
||||
// result in appropriate changes to the input region.
|
||||
//
|
||||
// If %affectsStruts is %true (and @actor is along a screen edge),
|
||||
// then @actor's size and position will also affect the window
|
||||
// manager struts. Changes to @actor's visibility will NOT affect
|
||||
// whether or not the strut is present, however.
|
||||
//
|
||||
// If %visibleInFullscreen is %true, the actor will be visible
|
||||
// even when a fullscreen window should be covering it.
|
||||
addActor: function(actor, params) {
|
||||
this.actor.add_actor(actor);
|
||||
this._trackActor(actor, params);
|
||||
},
|
||||
|
||||
// trackActor:
|
||||
// @actor: a descendant of the chrome to begin tracking
|
||||
// @params: parameters describing how to track @actor
|
||||
//
|
||||
// Tells the chrome to track @actor, which must be a descendant
|
||||
// of an actor added via addActor(). This can be used to extend the
|
||||
// struts or input region to cover specific children.
|
||||
//
|
||||
// @params can have any of the same values as in addActor(), though
|
||||
// some possibilities don't make sense (eg, trying to have a
|
||||
// %visibleInFullscreen child of a non-%visibleInFullscreen parent).
|
||||
// By default, @actor has the same params as its chrome ancestor.
|
||||
trackActor: function(actor, params) {
|
||||
let ancestor = actor.get_parent();
|
||||
let index = this._findActor(ancestor);
|
||||
while (ancestor && index == -1) {
|
||||
ancestor = ancestor.get_parent();
|
||||
index = this._findActor(ancestor);
|
||||
}
|
||||
if (!ancestor)
|
||||
throw new Error('actor is not a descendent of the chrome layer');
|
||||
|
||||
let ancestorData = this._trackedActors[index];
|
||||
if (!params)
|
||||
params = {};
|
||||
// We can't use Params.parse here because we want to drop
|
||||
// the extra values like ancestorData.actor
|
||||
for (let prop in defaultParams) {
|
||||
if (!params[prop])
|
||||
params[prop] = ancestorData[prop];
|
||||
}
|
||||
|
||||
this._trackActor(actor, params);
|
||||
},
|
||||
|
||||
// untrackActor:
|
||||
// @actor: an actor previously tracked via trackActor()
|
||||
//
|
||||
// Undoes the effect of trackActor()
|
||||
untrackActor: function(actor) {
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
// removeActor:
|
||||
// @actor: a child of the chrome layer
|
||||
//
|
||||
// Removes @actor from the chrome layer
|
||||
removeActor: function(actor) {
|
||||
this.actor.remove_actor(actor);
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
_findActor: function(actor) {
|
||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (actorData.actor == actor)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
_trackActor: function(actor, params) {
|
||||
if (this._findActor(actor) != -1)
|
||||
throw new Error('trying to re-track existing chrome actor');
|
||||
|
||||
let actorData = Params.parse(params, defaultParams);
|
||||
actorData.actor = actor;
|
||||
actorData.visibleId = actor.connect('notify::visible',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
actorData.allocationId = actor.connect('notify::allocation',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
actorData.parentSetId = actor.connect('parent-set',
|
||||
Lang.bind(this, this._actorReparented));
|
||||
// Note that destroying actor will unset its parent, so we don't
|
||||
// need to connect to 'destroy' too.
|
||||
|
||||
this._trackedActors.push(actorData);
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_untrackActor: function(actor) {
|
||||
let i = this._findActor(actor);
|
||||
|
||||
if (i == -1)
|
||||
return;
|
||||
let actorData = this._trackedActors[i];
|
||||
|
||||
this._trackedActors.splice(i, 1);
|
||||
actor.disconnect(actorData.visibleId);
|
||||
actor.disconnect(actorData.allocationId);
|
||||
actor.disconnect(actorData.parentSetId);
|
||||
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_actorReparented: function(actor, oldParent) {
|
||||
if (!this.actor.contains(actor))
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (!this._inOverview && !actorData.visibleInFullscreen &&
|
||||
this._findMonitorForActor(actorData.actor).inFullscreen)
|
||||
this.actor.set_skip_paint(actorData.actor, true);
|
||||
else
|
||||
this.actor.set_skip_paint(actorData.actor, false);
|
||||
}
|
||||
},
|
||||
|
||||
_overviewShowing: function() {
|
||||
this._inOverview = true;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_overviewHidden: function() {
|
||||
this._inOverview = false;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_relayout: function() {
|
||||
this._monitors = Main.layoutManager.monitors;
|
||||
this._primaryMonitor = Main.layoutManager.primaryMonitor;
|
||||
|
||||
this._updateFullscreen();
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_onScreenSaverActiveChanged: function(proxy, screenSaverActive) {
|
||||
this.actor.visible = !screenSaverActive;
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_findMonitorForRect: function(x, y, w, h) {
|
||||
// First look at what monitor the center of the rectangle is at
|
||||
let cx = x + w/2;
|
||||
let cy = y + h/2;
|
||||
for (let i = 0; i < this._monitors.length; i++) {
|
||||
let monitor = this._monitors[i];
|
||||
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
|
||||
cy >= monitor.y && cy < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
}
|
||||
// If the center is not on a monitor, return the first overlapping monitor
|
||||
for (let i = 0; i < this._monitors.length; i++) {
|
||||
let monitor = this._monitors[i];
|
||||
if (x + w > monitor.x && x < monitor.x + monitor.width &&
|
||||
y + h > monitor.y && y < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
}
|
||||
// otherwise on no monitor
|
||||
return null;
|
||||
},
|
||||
|
||||
_findMonitorForWindow: function(window) {
|
||||
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
|
||||
},
|
||||
|
||||
// This call guarantees that we return some monitor to simplify usage of it
|
||||
// In practice all tracked actors should be visible on some monitor anyway
|
||||
_findMonitorForActor: function(actor) {
|
||||
let [x, y] = actor.get_transformed_position();
|
||||
let [w, h] = actor.get_transformed_size();
|
||||
let monitor = this._findMonitorForRect(x, y, w, h);
|
||||
if (monitor)
|
||||
return monitor;
|
||||
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
|
||||
},
|
||||
|
||||
_queueUpdateRegions: function() {
|
||||
if (!this._updateRegionIdle)
|
||||
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
|
||||
Meta.PRIORITY_BEFORE_REDRAW);
|
||||
},
|
||||
|
||||
_updateFullscreen: function() {
|
||||
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
|
||||
|
||||
// Reset all monitors to not fullscreen
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
this._monitors[i].inFullscreen = false;
|
||||
|
||||
// The chrome layer should be visible unless there is a window
|
||||
// with layer FULLSCREEN, or a window with layer
|
||||
// OVERRIDE_REDIRECT that covers the whole screen.
|
||||
// ('override_redirect' is not actually a layer above all
|
||||
// other windows, but this seems to be how mutter treats it
|
||||
// currently...) If we wanted to be extra clever, we could
|
||||
// figure out when an OVERRIDE_REDIRECT window was trying to
|
||||
// partially overlap us, and then adjust the input region and
|
||||
// our clip region accordingly...
|
||||
|
||||
// @windows is sorted bottom to top.
|
||||
|
||||
for (let i = windows.length - 1; i > -1; i--) {
|
||||
let window = windows[i];
|
||||
|
||||
// Skip minimized windows
|
||||
if (!window.showing_on_its_workspace())
|
||||
continue;
|
||||
|
||||
let layer = window.get_meta_window().get_layer();
|
||||
|
||||
if (layer == Meta.StackLayer.FULLSCREEN) {
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
if (monitor)
|
||||
monitor.inFullscreen = true;
|
||||
}
|
||||
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
if (monitor &&
|
||||
window.x <= monitor.x &&
|
||||
window.x + window.width >= monitor.x + monitor.width &&
|
||||
window.y <= monitor.y &&
|
||||
window.y + window.height >= monitor.y + monitor.height)
|
||||
monitor.inFullscreen = true;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_windowsRestacked: function() {
|
||||
let wasInFullscreen = [];
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
wasInFullscreen[i] = this._monitors[i].inFullscreen;
|
||||
|
||||
this._updateFullscreen();
|
||||
|
||||
let changed = false;
|
||||
for (let i = 0; i < wasInFullscreen.length; i++) {
|
||||
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
}
|
||||
},
|
||||
|
||||
_updateRegions: function() {
|
||||
let rects = [], struts = [], i;
|
||||
|
||||
delete this._updateRegionIdle;
|
||||
|
||||
for (i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
|
||||
continue;
|
||||
|
||||
let [x, y] = actorData.actor.get_transformed_position();
|
||||
let [w, h] = actorData.actor.get_transformed_size();
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
w = Math.round(w);
|
||||
h = Math.round(h);
|
||||
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
|
||||
|
||||
if (actorData.affectsInputRegion &&
|
||||
actorData.actor.get_paint_visibility() &&
|
||||
!this.actor.get_skip_paint(actorData.actor))
|
||||
rects.push(rect);
|
||||
|
||||
if (!actorData.affectsStruts)
|
||||
continue;
|
||||
|
||||
// Limit struts to the size of the screen
|
||||
let x1 = Math.max(x, 0);
|
||||
let x2 = Math.min(x + w, global.screen_width);
|
||||
let y1 = Math.max(y, 0);
|
||||
let y2 = Math.min(y + h, global.screen_height);
|
||||
|
||||
// NetWM struts are not really powerful enought to handle
|
||||
// a multi-monitor scenario, they only describe what happens
|
||||
// around the outer sides of the full display region. However
|
||||
// it can describe a partial region along each side, so
|
||||
// we can support having the struts only affect the
|
||||
// primary monitor. This should be enough as we only have
|
||||
// chrome affecting the struts on the primary monitor so
|
||||
// far.
|
||||
//
|
||||
// Metacity wants to know what side of the screen the
|
||||
// strut is considered to be attached to. If the actor is
|
||||
// only touching one edge, or is touching the entire
|
||||
// border of the primary monitor, then it's obvious which
|
||||
// side to call it. If it's in a corner, we pick a side
|
||||
// arbitrarily. If it doesn't touch any edges, or it spans
|
||||
// the width/height across the middle of the screen, then
|
||||
// we don't create a strut for it at all.
|
||||
let side;
|
||||
let primary = this._primaryMonitor;
|
||||
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
|
||||
if (y1 <= primary.y)
|
||||
side = Meta.Side.TOP;
|
||||
else if (y2 >= primary.y + primary.height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
|
||||
if (x1 <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (x2 >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else
|
||||
continue;
|
||||
} else if (x1 <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (y1 <= 0)
|
||||
side = Meta.Side.TOP;
|
||||
else if (x2 >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else if (y2 >= global.screen_height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
|
||||
// Ensure that the strut rects goes all the way to the screen edge,
|
||||
// as this really what mutter expects.
|
||||
switch (side) {
|
||||
case Meta.Side.TOP:
|
||||
y1 = 0;
|
||||
break;
|
||||
case Meta.Side.BOTTOM:
|
||||
y2 = global.screen_height;
|
||||
break;
|
||||
case Meta.Side.LEFT:
|
||||
x1 = 0;
|
||||
break;
|
||||
case Meta.Side.RIGHT:
|
||||
x2 = global.screen_width;
|
||||
break;
|
||||
}
|
||||
|
||||
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
|
||||
let strut = new Meta.Strut({ rect: strutRect, side: side });
|
||||
struts.push(strut);
|
||||
}
|
||||
|
||||
global.set_stage_input_region(rects);
|
||||
|
||||
let screen = global.screen;
|
||||
for (let w = 0; w < screen.n_workspaces; w++) {
|
||||
let workspace = screen.get_workspace_by_index(w);
|
||||
workspace.set_builtin_struts(struts);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(Chrome.prototype);
|
439
js/ui/layout.js
439
js/ui/layout.js
@ -2,10 +2,15 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const ScreenSaver = imports.misc.screenSaver;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||
@ -319,3 +324,437 @@ HotCorner.prototype = {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// This manages the shell "chrome"; the UI that's visible in the
|
||||
// normal mode (ie, outside the Overview), that surrounds the main
|
||||
// workspace content.
|
||||
|
||||
const defaultParams = {
|
||||
visibleInFullscreen: false,
|
||||
affectsStruts: false,
|
||||
affectsInputRegion: true
|
||||
};
|
||||
|
||||
function Chrome() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
Chrome.prototype = {
|
||||
_init: function() {
|
||||
// The group itself has zero size so it doesn't interfere with DND
|
||||
this.actor = new Shell.GenericContainer({ width: 0, height: 0 });
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocated));
|
||||
|
||||
this._monitors = [];
|
||||
this._inOverview = false;
|
||||
|
||||
this._trackedActors = [];
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._relayout));
|
||||
global.screen.connect('restacked',
|
||||
Lang.bind(this, this._windowsRestacked));
|
||||
|
||||
// Need to update struts on new workspaces when they are added
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, this._overviewShowing));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._overviewHidden));
|
||||
|
||||
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
|
||||
this._screenSaverProxy.connect('ActiveChanged', Lang.bind(this, this._onScreenSaverActiveChanged));
|
||||
this._screenSaverProxy.GetActiveRemote(Lang.bind(this,
|
||||
function(result, err) {
|
||||
if (!err)
|
||||
this._onScreenSaverActiveChanged(this._screenSaverProxy, result);
|
||||
}));
|
||||
|
||||
this._relayout();
|
||||
},
|
||||
|
||||
_allocated: function(actor, box, flags) {
|
||||
let children = this.actor.get_children();
|
||||
for (let i = 0; i < children.length; i++)
|
||||
children[i].allocate_preferred_size(flags);
|
||||
},
|
||||
|
||||
// addActor:
|
||||
// @actor: an actor to add to the chrome layer
|
||||
// @params: (optional) additional params
|
||||
//
|
||||
// Adds @actor to the chrome layer and (unless %affectsInputRegion
|
||||
// is %false) extends the input region to include it. Changes in
|
||||
// @actor's size, position, and visibility will automatically
|
||||
// result in appropriate changes to the input region.
|
||||
//
|
||||
// If %affectsStruts is %true (and @actor is along a screen edge),
|
||||
// then @actor's size and position will also affect the window
|
||||
// manager struts. Changes to @actor's visibility will NOT affect
|
||||
// whether or not the strut is present, however.
|
||||
//
|
||||
// If %visibleInFullscreen is %true, the actor will be visible
|
||||
// even when a fullscreen window should be covering it.
|
||||
addActor: function(actor, params) {
|
||||
this.actor.add_actor(actor);
|
||||
this._trackActor(actor, params);
|
||||
},
|
||||
|
||||
// trackActor:
|
||||
// @actor: a descendant of the chrome to begin tracking
|
||||
// @params: parameters describing how to track @actor
|
||||
//
|
||||
// Tells the chrome to track @actor, which must be a descendant
|
||||
// of an actor added via addActor(). This can be used to extend the
|
||||
// struts or input region to cover specific children.
|
||||
//
|
||||
// @params can have any of the same values as in addActor(), though
|
||||
// some possibilities don't make sense (eg, trying to have a
|
||||
// %visibleInFullscreen child of a non-%visibleInFullscreen parent).
|
||||
// By default, @actor has the same params as its chrome ancestor.
|
||||
trackActor: function(actor, params) {
|
||||
let ancestor = actor.get_parent();
|
||||
let index = this._findActor(ancestor);
|
||||
while (ancestor && index == -1) {
|
||||
ancestor = ancestor.get_parent();
|
||||
index = this._findActor(ancestor);
|
||||
}
|
||||
if (!ancestor)
|
||||
throw new Error('actor is not a descendent of the chrome layer');
|
||||
|
||||
let ancestorData = this._trackedActors[index];
|
||||
if (!params)
|
||||
params = {};
|
||||
// We can't use Params.parse here because we want to drop
|
||||
// the extra values like ancestorData.actor
|
||||
for (let prop in defaultParams) {
|
||||
if (!params[prop])
|
||||
params[prop] = ancestorData[prop];
|
||||
}
|
||||
|
||||
this._trackActor(actor, params);
|
||||
},
|
||||
|
||||
// untrackActor:
|
||||
// @actor: an actor previously tracked via trackActor()
|
||||
//
|
||||
// Undoes the effect of trackActor()
|
||||
untrackActor: function(actor) {
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
// removeActor:
|
||||
// @actor: a child of the chrome layer
|
||||
//
|
||||
// Removes @actor from the chrome layer
|
||||
removeActor: function(actor) {
|
||||
this.actor.remove_actor(actor);
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
_findActor: function(actor) {
|
||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (actorData.actor == actor)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
_trackActor: function(actor, params) {
|
||||
if (this._findActor(actor) != -1)
|
||||
throw new Error('trying to re-track existing chrome actor');
|
||||
|
||||
let actorData = Params.parse(params, defaultParams);
|
||||
actorData.actor = actor;
|
||||
actorData.visibleId = actor.connect('notify::visible',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
actorData.allocationId = actor.connect('notify::allocation',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
actorData.parentSetId = actor.connect('parent-set',
|
||||
Lang.bind(this, this._actorReparented));
|
||||
// Note that destroying actor will unset its parent, so we don't
|
||||
// need to connect to 'destroy' too.
|
||||
|
||||
this._trackedActors.push(actorData);
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_untrackActor: function(actor) {
|
||||
let i = this._findActor(actor);
|
||||
|
||||
if (i == -1)
|
||||
return;
|
||||
let actorData = this._trackedActors[i];
|
||||
|
||||
this._trackedActors.splice(i, 1);
|
||||
actor.disconnect(actorData.visibleId);
|
||||
actor.disconnect(actorData.allocationId);
|
||||
actor.disconnect(actorData.parentSetId);
|
||||
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_actorReparented: function(actor, oldParent) {
|
||||
if (!this.actor.contains(actor))
|
||||
this._untrackActor(actor);
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
for (let i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (!this._inOverview && !actorData.visibleInFullscreen &&
|
||||
this._findMonitorForActor(actorData.actor).inFullscreen)
|
||||
this.actor.set_skip_paint(actorData.actor, true);
|
||||
else
|
||||
this.actor.set_skip_paint(actorData.actor, false);
|
||||
}
|
||||
},
|
||||
|
||||
_overviewShowing: function() {
|
||||
this._inOverview = true;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_overviewHidden: function() {
|
||||
this._inOverview = false;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_relayout: function() {
|
||||
this._monitors = Main.layoutManager.monitors;
|
||||
this._primaryMonitor = Main.layoutManager.primaryMonitor;
|
||||
|
||||
this._updateFullscreen();
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_onScreenSaverActiveChanged: function(proxy, screenSaverActive) {
|
||||
this.actor.visible = !screenSaverActive;
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_findMonitorForRect: function(x, y, w, h) {
|
||||
// First look at what monitor the center of the rectangle is at
|
||||
let cx = x + w/2;
|
||||
let cy = y + h/2;
|
||||
for (let i = 0; i < this._monitors.length; i++) {
|
||||
let monitor = this._monitors[i];
|
||||
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
|
||||
cy >= monitor.y && cy < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
}
|
||||
// If the center is not on a monitor, return the first overlapping monitor
|
||||
for (let i = 0; i < this._monitors.length; i++) {
|
||||
let monitor = this._monitors[i];
|
||||
if (x + w > monitor.x && x < monitor.x + monitor.width &&
|
||||
y + h > monitor.y && y < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
}
|
||||
// otherwise on no monitor
|
||||
return null;
|
||||
},
|
||||
|
||||
_findMonitorForWindow: function(window) {
|
||||
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
|
||||
},
|
||||
|
||||
// This call guarantees that we return some monitor to simplify usage of it
|
||||
// In practice all tracked actors should be visible on some monitor anyway
|
||||
_findMonitorForActor: function(actor) {
|
||||
let [x, y] = actor.get_transformed_position();
|
||||
let [w, h] = actor.get_transformed_size();
|
||||
let monitor = this._findMonitorForRect(x, y, w, h);
|
||||
if (monitor)
|
||||
return monitor;
|
||||
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
|
||||
},
|
||||
|
||||
_queueUpdateRegions: function() {
|
||||
if (!this._updateRegionIdle)
|
||||
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
|
||||
Meta.PRIORITY_BEFORE_REDRAW);
|
||||
},
|
||||
|
||||
_updateFullscreen: function() {
|
||||
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
|
||||
|
||||
// Reset all monitors to not fullscreen
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
this._monitors[i].inFullscreen = false;
|
||||
|
||||
// The chrome layer should be visible unless there is a window
|
||||
// with layer FULLSCREEN, or a window with layer
|
||||
// OVERRIDE_REDIRECT that covers the whole screen.
|
||||
// ('override_redirect' is not actually a layer above all
|
||||
// other windows, but this seems to be how mutter treats it
|
||||
// currently...) If we wanted to be extra clever, we could
|
||||
// figure out when an OVERRIDE_REDIRECT window was trying to
|
||||
// partially overlap us, and then adjust the input region and
|
||||
// our clip region accordingly...
|
||||
|
||||
// @windows is sorted bottom to top.
|
||||
|
||||
for (let i = windows.length - 1; i > -1; i--) {
|
||||
let window = windows[i];
|
||||
let layer = window.get_meta_window().get_layer();
|
||||
|
||||
// Skip minimized windows
|
||||
if (!window.showing_on_its_workspace())
|
||||
continue;
|
||||
|
||||
if (layer == Meta.StackLayer.FULLSCREEN) {
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
if (monitor)
|
||||
monitor.inFullscreen = true;
|
||||
}
|
||||
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
if (monitor &&
|
||||
window.x <= monitor.x &&
|
||||
window.x + window.width >= monitor.x + monitor.width &&
|
||||
window.y <= monitor.y &&
|
||||
window.y + window.height >= monitor.y + monitor.height)
|
||||
monitor.inFullscreen = true;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_windowsRestacked: function() {
|
||||
let wasInFullscreen = [];
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
wasInFullscreen[i] = this._monitors[i].inFullscreen;
|
||||
|
||||
this._updateFullscreen();
|
||||
|
||||
let changed = false;
|
||||
for (let i = 0; i < wasInFullscreen.length; i++) {
|
||||
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
}
|
||||
},
|
||||
|
||||
_updateRegions: function() {
|
||||
let rects = [], struts = [], i;
|
||||
|
||||
delete this._updateRegionIdle;
|
||||
|
||||
for (i = 0; i < this._trackedActors.length; i++) {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
|
||||
continue;
|
||||
|
||||
let [x, y] = actorData.actor.get_transformed_position();
|
||||
let [w, h] = actorData.actor.get_transformed_size();
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
w = Math.round(w);
|
||||
h = Math.round(h);
|
||||
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
|
||||
|
||||
if (actorData.affectsInputRegion &&
|
||||
actorData.actor.get_paint_visibility() &&
|
||||
!this.actor.get_skip_paint(actorData.actor))
|
||||
rects.push(rect);
|
||||
|
||||
if (!actorData.affectsStruts)
|
||||
continue;
|
||||
|
||||
// Limit struts to the size of the screen
|
||||
let x1 = Math.max(x, 0);
|
||||
let x2 = Math.min(x + w, global.screen_width);
|
||||
let y1 = Math.max(y, 0);
|
||||
let y2 = Math.min(y + h, global.screen_height);
|
||||
|
||||
// NetWM struts are not really powerful enought to handle
|
||||
// a multi-monitor scenario, they only describe what happens
|
||||
// around the outer sides of the full display region. However
|
||||
// it can describe a partial region along each side, so
|
||||
// we can support having the struts only affect the
|
||||
// primary monitor. This should be enough as we only have
|
||||
// chrome affecting the struts on the primary monitor so
|
||||
// far.
|
||||
//
|
||||
// Metacity wants to know what side of the screen the
|
||||
// strut is considered to be attached to. If the actor is
|
||||
// only touching one edge, or is touching the entire
|
||||
// border of the primary monitor, then it's obvious which
|
||||
// side to call it. If it's in a corner, we pick a side
|
||||
// arbitrarily. If it doesn't touch any edges, or it spans
|
||||
// the width/height across the middle of the screen, then
|
||||
// we don't create a strut for it at all.
|
||||
let side;
|
||||
let primary = this._primaryMonitor;
|
||||
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
|
||||
if (y1 <= primary.y)
|
||||
side = Meta.Side.TOP;
|
||||
else if (y2 >= primary.y + primary.height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
|
||||
if (x1 <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (x2 >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else
|
||||
continue;
|
||||
} else if (x1 <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (y1 <= 0)
|
||||
side = Meta.Side.TOP;
|
||||
else if (x2 >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else if (y2 >= global.screen_height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
|
||||
// Ensure that the strut rects goes all the way to the screen edge,
|
||||
// as this really what mutter expects.
|
||||
switch (side) {
|
||||
case Meta.Side.TOP:
|
||||
y1 = 0;
|
||||
break;
|
||||
case Meta.Side.BOTTOM:
|
||||
y2 = global.screen_height;
|
||||
break;
|
||||
case Meta.Side.LEFT:
|
||||
x1 = 0;
|
||||
break;
|
||||
case Meta.Side.RIGHT:
|
||||
x2 = global.screen_width;
|
||||
break;
|
||||
}
|
||||
|
||||
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
|
||||
let strut = new Meta.Strut({ rect: strutRect, side: side });
|
||||
struts.push(strut);
|
||||
}
|
||||
|
||||
global.set_stage_input_region(rects);
|
||||
|
||||
let screen = global.screen;
|
||||
for (let w = 0; w < screen.n_workspaces; w++) {
|
||||
let workspace = screen.get_workspace_by_index(w);
|
||||
workspace.set_builtin_struts(struts);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(Chrome.prototype);
|
||||
|
@ -14,7 +14,6 @@ const St = imports.gi.St;
|
||||
|
||||
const AutomountManager = imports.ui.automountManager;
|
||||
const AutorunManager = imports.ui.autorunManager;
|
||||
const Chrome = imports.ui.chrome;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
||||
@ -137,7 +136,7 @@ function start() {
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
overview = new Overview.Overview();
|
||||
chrome = new Chrome.Chrome();
|
||||
chrome = new Layout.Chrome();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
|
||||
panel = new Panel.Panel();
|
||||
|
Loading…
Reference in New Issue
Block a user