Add a concept of "going modal" by grabbing the keyboard

shell-global.[ch]: Replace shell_global_focus_stage()
  with shell_global_grab_keyboard()/shell_global_ungrab_keyboard()
main.js: Add startModal()/endModal() functions to go modal and
  undo that.
run_dialog.js overlay.js main.js: Use startModal() for the overlay
  and for the run dialog.

http://bugzilla.gnome.org/show_bug.cgi?id=561880

svn path=/trunk/; revision=83
This commit is contained in:
Owen Taylor 2008-11-24 19:07:18 +00:00
parent 8a9b138c2e
commit c12de59864
5 changed files with 113 additions and 36 deletions

View File

@ -103,13 +103,14 @@ function start() {
// Make sure not more than one run dialog is shown. // Make sure not more than one run dialog is shown.
if (!run_dialog) { if (!run_dialog) {
run_dialog = new RunDialog.RunDialog(); run_dialog = new RunDialog.RunDialog();
let handler = function() { let end_handler = function() {
run_dialog.destroy(); run_dialog.destroy();
run_dialog = null; run_dialog = null;
}; };
run_dialog.connect('run', handler); run_dialog.connect('run', end_handler);
run_dialog.connect('cancel', handler); run_dialog.connect('cancel', end_handler);
run_dialog.show(); if (!run_dialog.show())
end_handler();
} }
}); });
@ -120,17 +121,33 @@ function start() {
wm = new WindowManager.WindowManager(); wm = new WindowManager.WindowManager();
} }
function show_overlay() { // 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
function startModal() {
let global = Shell.global_get(); let global = Shell.global_get();
overlay.show(); if (!global.grab_keyboard())
return false;
global.set_stage_input_area(0, 0, global.screen_width, global.screen_height); global.set_stage_input_area(0, 0, global.screen_width, global.screen_height);
return true;
}
function endModal() {
let global = Shell.global_get();
global.ungrab_keyboard();
global.set_stage_input_area(0, 0, global.screen_width, Panel.PANEL_HEIGHT);
}
function show_overlay() {
if (startModal())
overlay.show();
} }
function hide_overlay() { function hide_overlay() {
let global = Shell.global_get();
overlay.hide(); overlay.hide();
panel.overlayHidden(); endModal();
global.set_stage_input_area(0, 0, global.screen_width, Panel.PANEL_HEIGHT);
} }

View File

@ -178,8 +178,6 @@ Overlay.prototype = {
let global = Shell.global_get(); let global = Shell.global_get();
global.focus_stage();
let windows = global.get_windows(); let windows = global.get_windows();
let desktopWindow = null; let desktopWindow = null;

View File

@ -26,8 +26,9 @@ RunDialog.prototype = {
_init : function() { _init : function() {
let global = Shell.global_get(); let global = Shell.global_get();
// All actors are inside _group. // All actors are inside _group. We create it initially
this._group = new Clutter.Group(); // hidden then show it in show()
this._group = new Clutter.Group({ visible: false });
global.stage.add_actor(this._group); global.stage.add_actor(this._group);
this._overlay = new Clutter.Rectangle({ color: OVERLAY_COLOR, this._overlay = new Clutter.Rectangle({ color: OVERLAY_COLOR,
@ -74,19 +75,6 @@ RunDialog.prototype = {
return false; return false;
}); });
// TODO: Detect escape key and make it cancel the operation.
// Use me.on_cancel() if it exists. Something like this:
// this._entry.connect('key-press-event', function(o, e) {
// if (the pressed key is the escape key) {
// me.hide();
// me.emit('cancel');
// return false;
// } else
// return true;
// });
global.focus_stage();
global.stage.set_key_focus(this._entry);
}, },
_run : function(command) { _run : function(command) {
@ -104,14 +92,41 @@ RunDialog.prototype = {
}, },
show : function() { show : function() {
if (this._group.visible) // Already shown
return false;
if (!Main.startModal())
return false;
this._group.show_all(); this._group.show_all();
// TODO: Detect escape key and make it cancel the operation.
// Use me.on_cancel() if it exists. Something like this:
// this._entry.connect('key-press-event', function(o, e) {
// if (the pressed key is the escape key) {
// me.hide();
// me.emit('cancel');
// return false;
// } else
// return true;
// });
let global = Shell.global_get();
global.stage.set_key_focus(this._entry);
return true;
}, },
hide : function() { hide : function() {
if (!this._group.visible)
return;
this._group.hide(); this._group.hide();
Main.endModal();
}, },
destroy : function(){ destroy : function(){
this.hide();
this._group.destroy(); this._group.destroy();
} }
}; };

View File

@ -11,6 +11,7 @@ struct _ShellGlobal {
MutterPlugin *plugin; MutterPlugin *plugin;
ShellWM *wm; ShellWM *wm;
gboolean keyboard_grabbed;
}; };
enum { enum {
@ -273,19 +274,64 @@ _shell_global_set_plugin (ShellGlobal *global,
} }
/** /**
* shell_global_focus_stage: * shell_global_grab_keyboard:
* @global: a #ShellGlobal
* *
* Set the keyboard focus to the Clutter stage window. This function * Grab the keyboard to the stage window. The stage will receive
* is best used in combination with some sort of visual notification * all keyboard events until shell_global_ungrab_keyboard() is called.
* that the shell has taken over input. * This is appropriate to do when the desktop goes into a special
* mode where no normal global key shortcuts or application keyboard
* processing should happen.
*/ */
void gboolean
shell_global_focus_stage (ShellGlobal *global) shell_global_grab_keyboard (ShellGlobal *global)
{ {
MetaScreen *screen = mutter_plugin_get_screen (global->plugin); MetaScreen *screen = mutter_plugin_get_screen (global->plugin);
MetaDisplay *display = meta_screen_get_display (screen); MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display); Display *xdisplay = meta_display_get_xdisplay (display);
ClutterStage *stage = CLUTTER_STAGE (mutter_plugin_get_stage (global->plugin)); ClutterStage *stage = CLUTTER_STAGE (mutter_plugin_get_stage (global->plugin));
Window stagewin = clutter_x11_get_stage_window (stage); Window stagewin = clutter_x11_get_stage_window (stage);
XSetInputFocus (xdisplay, stagewin, RevertToParent, CurrentTime);
/* FIXME: we need to coordinate with the rest of Metacity or we
* may grab the keyboard away from other portions of Metacity
* and leave Metacity in a confused state. An X client is allowed
* to overgrab itself, though not allowed to grab they keyboard
* away from another applications.
*/
if (global->keyboard_grabbed)
return FALSE;
if (XGrabKeyboard (xdisplay, stagewin,
False, /* owner_events - steal events from the rest of metacity */
GrabModeAsync, GrabModeAsync,
CurrentTime) != Success)
return FALSE; /* probably AlreadyGrabbed, some other app has a keyboard grab */
global->keyboard_grabbed = TRUE;
return TRUE;
}
/**
* shell_global_ungrab_keyboard:
* @global: a #ShellGlobal
*
* Undoes the effect of shell_global_grab_keyboard
*/
void
shell_global_ungrab_keyboard (ShellGlobal *global)
{
MetaScreen *screen;
MetaDisplay *display;
Display *xdisplay;
g_return_if_fail (global->keyboard_grabbed);
screen = mutter_plugin_get_screen (global->plugin);
display = meta_screen_get_display (screen);
xdisplay = meta_display_get_xdisplay (display);
XUngrabKeyboard (xdisplay, CurrentTime);
global->keyboard_grabbed = FALSE;
} }

View File

@ -49,7 +49,8 @@ void _shell_global_set_plugin (ShellGlobal *global,
MetaScreen * shell_global_get_screen (ShellGlobal *global); MetaScreen * shell_global_get_screen (ShellGlobal *global);
void shell_global_focus_stage (ShellGlobal *global); gboolean shell_global_grab_keyboard (ShellGlobal *global);
void shell_global_ungrab_keyboard (ShellGlobal *global);
G_END_DECLS G_END_DECLS