Create a more extensible/organized strut/input_area management system.
Now code can call Main.addShellActor(actor) to declare that that actor is part of the shell, and so it should (a) be protected by wm struts, and (b) be part of the stage input area, and then that code automatically deals with updating if the actor changes size or visibility.
This commit is contained in:
parent
63821f1ae7
commit
4a5873dd22
106
js/ui/main.js
106
js/ui/main.js
@ -3,6 +3,7 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
@ -60,8 +61,6 @@ function start() {
|
||||
});
|
||||
|
||||
panel = new Panel.Panel();
|
||||
panel.actor.connect('notify::visible', _panelVisibilityChanged);
|
||||
_panelVisibilityChanged();
|
||||
|
||||
overlay = new Overlay.Overlay();
|
||||
wm = new WindowManager.WindowManager();
|
||||
@ -95,6 +94,9 @@ function start() {
|
||||
display.connect('overlay-key', toggleOverlay);
|
||||
global.connect('panel-main-menu', toggleOverlay);
|
||||
|
||||
// Need to update struts on new workspaces when they are added
|
||||
global.screen.connect('notify::n-workspaces', _setStageArea);
|
||||
|
||||
Mainloop.idle_add(_removeUnusedWorkspaces);
|
||||
}
|
||||
|
||||
@ -128,18 +130,6 @@ function _removeUnusedWorkspaces() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function _panelVisibilityChanged() {
|
||||
if (!inModal) {
|
||||
let global = Shell.Global.get();
|
||||
|
||||
if (panel.actor.visible) {
|
||||
global.set_stage_input_area(0, 0,
|
||||
global.screen_width, Panel.PANEL_HEIGHT);
|
||||
} else
|
||||
global.set_stage_input_area(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Used to go into a mode where all keyboard and mouse input goes to
|
||||
// the stage. Returns true if we successfully grabbed the keyboard and
|
||||
// went modal, false otherwise
|
||||
@ -148,9 +138,9 @@ function startModal() {
|
||||
|
||||
if (!global.grab_keyboard())
|
||||
return false;
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
|
||||
|
||||
inModal = true;
|
||||
global.set_stage_input_area(0, 0, global.screen_width, global.screen_height);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -159,8 +149,8 @@ function endModal() {
|
||||
let global = Shell.Global.get();
|
||||
|
||||
global.ungrab_keyboard();
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
inModal = false;
|
||||
_panelVisibilityChanged();
|
||||
}
|
||||
|
||||
function show_overlay() {
|
||||
@ -175,3 +165,87 @@ function hide_overlay() {
|
||||
overlayActive = false;
|
||||
endModal();
|
||||
}
|
||||
|
||||
let _shellActors = [];
|
||||
|
||||
// For adding an actor that is part of the shell in the normal desktop view
|
||||
function addShellActor(actor) {
|
||||
let global = Shell.Global.get();
|
||||
|
||||
_shellActors.push(actor);
|
||||
|
||||
actor.connect('notify::visible', _setStageArea);
|
||||
actor.connect('destroy', function(actor) {
|
||||
let i = _shellActors.indexOf(actor);
|
||||
if (i != -1)
|
||||
_shellActors.splice(i, 1);
|
||||
_setStageArea();
|
||||
});
|
||||
|
||||
while (actor != global.stage) {
|
||||
actor.connect('notify::allocation', _setStageArea);
|
||||
actor = actor.get_parent();
|
||||
}
|
||||
|
||||
_setStageArea();
|
||||
}
|
||||
|
||||
function _setStageArea() {
|
||||
let global = Shell.Global.get();
|
||||
let rects = [], struts = [];
|
||||
|
||||
for (let i = 0; i < _shellActors.length; i++) {
|
||||
if (!_shellActors[i].visible)
|
||||
continue;
|
||||
|
||||
let [x, y] = _shellActors[i].get_transformed_position();
|
||||
let [w, h] = _shellActors[i].get_transformed_size();
|
||||
|
||||
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
|
||||
rects.push(rect);
|
||||
|
||||
// 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 width/height of one
|
||||
// edge, 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;
|
||||
if (w >= global.screen_width) {
|
||||
if (y <= 0)
|
||||
side = Meta.Side.TOP;
|
||||
else if (y + h >= global.screen_height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
} else if (h >= global.screen_height) {
|
||||
if (x <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (x + w >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else
|
||||
continue;
|
||||
} else if (x <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (y <= 0)
|
||||
side = Meta.Side.TOP;
|
||||
else if (x + w >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else if (y + h >= global.screen_height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
|
||||
let strut = new Meta.Strut({ rect: rect, side: side });
|
||||
struts.push(strut);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
global.set_stage_input_region(rects);
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ Panel.prototype = {
|
||||
PANEL_BOTTOM_COLOR);
|
||||
let shadow = global.create_vertical_gradient(SHADOW_COLOR,
|
||||
TRANSPARENT_COLOR);
|
||||
shadow.set_height(SHADOW_HEIGHT);
|
||||
shadow.set_height(SHADOW_HEIGHT + 1);
|
||||
backBox.append(backUpper, Big.BoxPackFlags.EXPAND);
|
||||
backBox.append(backLower, Big.BoxPackFlags.EXPAND);
|
||||
backBox.append(shadow, Big.BoxPackFlags.NONE);
|
||||
@ -159,15 +159,11 @@ Panel.prototype = {
|
||||
return true;
|
||||
});
|
||||
|
||||
this._setStruts();
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
function() {
|
||||
me._setStruts();
|
||||
});
|
||||
|
||||
this.actor.add_actor(box);
|
||||
|
||||
global.stage.add_actor(this.actor);
|
||||
// Declare just "box" (ie, not the drop shadow) as a shell actor
|
||||
Main.addShellActor(box);
|
||||
|
||||
global.screen.connect('restacked',
|
||||
function() {
|
||||
@ -179,30 +175,6 @@ Panel.prototype = {
|
||||
this._updateClock();
|
||||
},
|
||||
|
||||
// Struts determine the area along each side of the screen that is reserved
|
||||
// and not available to applications
|
||||
_setStruts: function() {
|
||||
let global = Shell.Global.get();
|
||||
|
||||
let struts = [
|
||||
new Meta.Strut({
|
||||
rect: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: global.screen_width,
|
||||
height: PANEL_HEIGHT
|
||||
},
|
||||
side: Meta.Side.TOP
|
||||
})
|
||||
];
|
||||
|
||||
let screen = global.screen;
|
||||
for (let i = 0; i < screen.n_workspaces; i++) {
|
||||
let workspace = screen.get_workspace_by_index(i);
|
||||
workspace.set_builtin_struts(struts);
|
||||
}
|
||||
},
|
||||
|
||||
_restacked: function() {
|
||||
let global = Shell.Global.get();
|
||||
let windows = global.get_windows();
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <dbus/dbus-glib.h>
|
||||
#include <libgnomeui/gnome-thumbnail.h>
|
||||
#include <math.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
|
||||
#define SHELL_DBUS_SERVICE "org.gnome.Shell"
|
||||
|
||||
@ -31,12 +32,10 @@ struct _ShellGlobal {
|
||||
* This window is never mapped or shown.
|
||||
*/
|
||||
GtkWindow *grab_notifier;
|
||||
gboolean grab_active;
|
||||
/* See shell_global_set_stage_input_area */
|
||||
int input_x;
|
||||
int input_y;
|
||||
int input_width;
|
||||
int input_height;
|
||||
gboolean gtk_grab_active;
|
||||
|
||||
ShellStageInputMode input_mode;
|
||||
XserverRegion input_region;
|
||||
|
||||
MutterPlugin *plugin;
|
||||
ShellWM *wm;
|
||||
@ -172,9 +171,11 @@ shell_global_init (ShellGlobal *global)
|
||||
|
||||
global->grab_notifier = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
|
||||
g_signal_connect (global->grab_notifier, "grab-notify", G_CALLBACK (grab_notify), global);
|
||||
global->grab_active = FALSE;
|
||||
global->gtk_grab_active = FALSE;
|
||||
|
||||
global->root_pixmap = NULL;
|
||||
|
||||
global->input_mode = SHELL_STAGE_INPUT_MODE_NORMAL;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -682,32 +683,85 @@ shell_global_get (void)
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_set_stage_input_area:
|
||||
* x: X coordinate of rectangle
|
||||
* y: Y coordinate of rectangle
|
||||
* width: width of rectangle
|
||||
* height: height of rectangle
|
||||
* shell_global_set_stage_input_mode:
|
||||
* @global: the #ShellGlobal
|
||||
* @mode: the stage input mode
|
||||
*
|
||||
* Sets the area of the stage that is responsive to mouse clicks as
|
||||
* a rectangle.
|
||||
* Sets the input mode of the stage; when @mode is
|
||||
* %SHELL_STAGE_INPUT_MODE_NONREACTIVE, then the stage does not absorb
|
||||
* any clicks, but just passes them through to underlying windows.
|
||||
* When it is %SHELL_STAGE_INPUT_MODE_NORMAL, then the stage accepts
|
||||
* clicks in the region defined by
|
||||
* shell_global_set_stage_input_region() but passes through clicks
|
||||
* outside that region. When it is %SHELL_STAGE_INPUT_MODE_FULLSCREEN,
|
||||
* the stage absorbs all input.
|
||||
*
|
||||
* Note that whenever a mutter-internal Gtk widget has a pointer grab,
|
||||
* the shell behaves as though it was in
|
||||
* %SHELL_STAGE_INPUT_MODE_NONREACTIVE, to ensure that the widget gets
|
||||
* any clicks it is expecting.
|
||||
*/
|
||||
void
|
||||
shell_global_set_stage_input_area (ShellGlobal *global,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height)
|
||||
shell_global_set_stage_input_mode (ShellGlobal *global,
|
||||
ShellStageInputMode mode)
|
||||
{
|
||||
g_return_if_fail (SHELL_IS_GLOBAL (global));
|
||||
|
||||
/* Cache these so we can save/restore across grabs */
|
||||
global->input_x = x;
|
||||
global->input_y = y;
|
||||
global->input_width = width;
|
||||
global->input_height = height;
|
||||
/* If we have a grab active, we'll set the input area when we ungrab. */
|
||||
if (!global->grab_active)
|
||||
mutter_plugin_set_stage_input_area (global->plugin, x, y, width, height);
|
||||
if (mode == SHELL_STAGE_INPUT_MODE_NONREACTIVE || global->gtk_grab_active)
|
||||
mutter_plugin_set_stage_reactive (global->plugin, FALSE);
|
||||
else if (mode == SHELL_STAGE_INPUT_MODE_FULLSCREEN || !global->input_region)
|
||||
mutter_plugin_set_stage_reactive (global->plugin, TRUE);
|
||||
else
|
||||
mutter_plugin_set_stage_input_region (global->plugin, global->input_region);
|
||||
|
||||
global->input_mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_set_stage_input_region:
|
||||
* @global: the #ShellGlobal
|
||||
* @rectangles: (element-type Meta.Rectangle): a list of #MetaRectangle
|
||||
* describing the input region.
|
||||
*
|
||||
* Sets the area of the stage that is responsive to mouse clicks when
|
||||
* the stage mode is %SHELL_STAGE_INPUT_MODE_NORMAL (but does not change the
|
||||
* current stage mode).
|
||||
*/
|
||||
void
|
||||
shell_global_set_stage_input_region (ShellGlobal *global,
|
||||
GSList *rectangles)
|
||||
{
|
||||
MetaScreen *screen = mutter_plugin_get_screen (global->plugin);
|
||||
MetaDisplay *display = meta_screen_get_display (screen);
|
||||
Display *xdpy = meta_display_get_xdisplay (display);
|
||||
MetaRectangle *rect;
|
||||
XRectangle *rects;
|
||||
int nrects, i;
|
||||
GSList *r;
|
||||
|
||||
g_return_if_fail (SHELL_IS_GLOBAL (global));
|
||||
|
||||
nrects = g_slist_length (rectangles);
|
||||
rects = g_new (XRectangle, nrects);
|
||||
for (r = rectangles, i = 0; r; r = r->next, i++)
|
||||
{
|
||||
rect = (MetaRectangle *)r->data;
|
||||
rects[i].x = rect->x;
|
||||
rects[i].y = rect->y;
|
||||
rects[i].width = rect->width;
|
||||
rects[i].height = rect->height;
|
||||
}
|
||||
|
||||
if (global->input_region)
|
||||
XFixesDestroyRegion (xdpy, global->input_region);
|
||||
|
||||
global->input_region = XFixesCreateRegion (xdpy, rects, nrects);
|
||||
g_free (rects);
|
||||
|
||||
/* set_stage_input_mode() will figure out whether or not we
|
||||
* should actually change the input region right now.
|
||||
*/
|
||||
shell_global_set_stage_input_mode (global, global->input_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -982,15 +1036,10 @@ grab_notify (GtkWidget *widget, gboolean was_grabbed, gpointer user_data)
|
||||
{
|
||||
ShellGlobal *global = SHELL_GLOBAL (user_data);
|
||||
|
||||
if (!was_grabbed)
|
||||
{
|
||||
mutter_plugin_set_stage_input_area (global->plugin, 0, 0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
mutter_plugin_set_stage_input_area (global->plugin, global->input_x, global->input_y,
|
||||
global->input_width, global->input_height);
|
||||
}
|
||||
global->gtk_grab_active = !was_grabbed;
|
||||
|
||||
/* Update for the new setting of gtk_grab_active */
|
||||
shell_global_set_stage_input_mode (global, global->input_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,11 +54,16 @@ void shell_global_grab_dbus_service (ShellGlobal *global);
|
||||
|
||||
void shell_global_start_task_panel (ShellGlobal *global);
|
||||
|
||||
void shell_global_set_stage_input_area (ShellGlobal *global,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height);
|
||||
typedef enum {
|
||||
SHELL_STAGE_INPUT_MODE_NONREACTIVE,
|
||||
SHELL_STAGE_INPUT_MODE_NORMAL,
|
||||
SHELL_STAGE_INPUT_MODE_FULLSCREEN
|
||||
} ShellStageInputMode;
|
||||
|
||||
void shell_global_set_stage_input_mode (ShellGlobal *global,
|
||||
ShellStageInputMode mode);
|
||||
void shell_global_set_stage_input_region (ShellGlobal *global,
|
||||
GSList *rectangles);
|
||||
|
||||
GList *shell_global_get_windows (ShellGlobal *global);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user