diff options
author | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-21 17:08:55 +0000 |
---|---|---|
committer | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-21 17:08:55 +0000 |
commit | 9d61b3b27d9151d0856d42f95a06c82b9dbc1187 (patch) | |
tree | 18f7665d2b15d5bd68999563efa16c1e66e979d5 /chrome | |
parent | f1f73abc4122e51c7c3c74f58c9f62d43cdc7171 (diff) | |
download | chromium_src-9d61b3b27d9151d0856d42f95a06c82b9dbc1187.zip chromium_src-9d61b3b27d9151d0856d42f95a06c82b9dbc1187.tar.gz chromium_src-9d61b3b27d9151d0856d42f95a06c82b9dbc1187.tar.bz2 |
Forward edit commands to be executed for shortcuts which aren't handled by
javascript.
As a result, cocoa keybindings work.
The approach:
We instantiate a vanilla NSResponder, call interpretKeyEvents on it, and
record all of the selectors passed into doCommandBySelector while
interpreting the key event. The selectors are converted into edit commands
which can be passed to the render process.
Caveats:
- Shortcuts involving a sequence of key combinations (chords) don't work,
because we instantiate a new responder for each event.
- We ignore key combinations that don't include a modifier (ctrl, cmd, alt)
because this was causing strange behavior (e.g. tab always inserted a tab
rather than moving to the next field on the page).
BUG=12538
TEST=go to a page with a text field. make sure that ctrl-a moves cursor to beginning of line, ctrl-e goes to the end, ctrl-d deletes to the right, ctrl-h to the left, etc
Review URL: http://codereview.chromium.org/209046
Patch from matt@tolton.com.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@26694 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/cocoa/rwhvm_editcommand_helper.h | 4 | ||||
-rw-r--r-- | chrome/browser/cocoa/rwhvm_editcommand_helper.mm | 62 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view_mac.mm | 71 |
3 files changed, 103 insertions, 34 deletions
diff --git a/chrome/browser/cocoa/rwhvm_editcommand_helper.h b/chrome/browser/cocoa/rwhvm_editcommand_helper.h index 25a8a73..458b58c 100644 --- a/chrome/browser/cocoa/rwhvm_editcommand_helper.h +++ b/chrome/browser/cocoa/rwhvm_editcommand_helper.h @@ -51,6 +51,10 @@ class RWHVMEditCommandHelper { bool IsMenuItemEnabled(SEL item_action, id<RenderWidgetHostViewMacOwner> owner); + // Converts an editing selector into a command name that can be sent to + // webkit. + static NSString* CommandNameForSelector(SEL selector); + protected: // Gets a list of all the selectors that AddEditingSelectorsToClass adds to // the aforementioned class. diff --git a/chrome/browser/cocoa/rwhvm_editcommand_helper.mm b/chrome/browser/cocoa/rwhvm_editcommand_helper.mm index c4b1efd..fe02a88 100644 --- a/chrome/browser/cocoa/rwhvm_editcommand_helper.mm +++ b/chrome/browser/cocoa/rwhvm_editcommand_helper.mm @@ -105,35 +105,6 @@ const char* kEditCommands[] = { "yank", "yankAndSelect"}; -// Maps an objc-selector to a core command name. -// -// Returns the core command name (which is the selector name with the trailing -// ':' stripped in most cases). -// -// Adapted from a function by the same name in -// WebKit/mac/WebView/WebHTMLView.mm . -// Capitalized names are returned from this function, but that's simply -// matching WebHTMLView.mm. -NSString* CommandNameForSelector(SEL selector) { - if (selector == @selector(insertParagraphSeparator:) || - selector == @selector(insertNewlineIgnoringFieldEditor:)) - return @"InsertNewline"; - if (selector == @selector(insertTabIgnoringFieldEditor:)) - return @"InsertTab"; - if (selector == @selector(pageDown:)) - return @"MovePageDown"; - if (selector == @selector(pageDownAndModifySelection:)) - return @"MovePageDownAndModifySelection"; - if (selector == @selector(pageUp:)) - return @"MovePageUp"; - if (selector == @selector(pageUpAndModifySelection:)) - return @"MovePageUpAndModifySelection"; - - // Remove the trailing colon. - NSString* selector_str = NSStringFromSelector(selector); - int selector_len = [selector_str length]; - return [selector_str substringToIndex:selector_len - 1]; -} // This function is installed via the objc runtime as the implementation of all // the various editing selectors. @@ -157,7 +128,8 @@ void EditCommandImp(id self, SEL _cmd, id sender) { DCHECK([self conformsToProtocol:@protocol(RenderWidgetHostViewMacOwner)]); // SEL -> command name string. - NSString* command_name_ns = CommandNameForSelector(_cmd); + NSString* command_name_ns = + RWHVMEditCommandHelper::CommandNameForSelector(_cmd); std::string edit_command([command_name_ns UTF8String]); // Forward the edit command string down the pipeline. @@ -171,6 +143,36 @@ void EditCommandImp(id self, SEL _cmd, id sender) { } // namespace +// Maps an objc-selector to a core command name. +// +// Returns the core command name (which is the selector name with the trailing +// ':' stripped in most cases). +// +// Adapted from a function by the same name in +// WebKit/mac/WebView/WebHTMLView.mm . +// Capitalized names are returned from this function, but that's simply +// matching WebHTMLView.mm. +NSString* RWHVMEditCommandHelper::CommandNameForSelector(SEL selector) { + if (selector == @selector(insertParagraphSeparator:) || + selector == @selector(insertNewlineIgnoringFieldEditor:)) + return @"InsertNewline"; + if (selector == @selector(insertTabIgnoringFieldEditor:)) + return @"InsertTab"; + if (selector == @selector(pageDown:)) + return @"MovePageDown"; + if (selector == @selector(pageDownAndModifySelection:)) + return @"MovePageDownAndModifySelection"; + if (selector == @selector(pageUp:)) + return @"MovePageUp"; + if (selector == @selector(pageUpAndModifySelection:)) + return @"MovePageUpAndModifySelection"; + + // Remove the trailing colon. + NSString* selector_str = NSStringFromSelector(selector); + int selector_len = [selector_str length]; + return [selector_str substringToIndex:selector_len - 1]; +} + RWHVMEditCommandHelper::RWHVMEditCommandHelper() { for (size_t i = 0; i < arraysize(kEditCommands); ++i) { edit_command_set_.insert(kEditCommands[i]); diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm index 21fce5f..82145d0 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm +++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm @@ -5,6 +5,7 @@ #include "chrome/browser/renderer_host/render_widget_host_view_mac.h" #include "base/histogram.h" +#include "base/scoped_nsobject.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" #include "chrome/browser/browser_trial.h" @@ -14,6 +15,7 @@ #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/browser/spellchecker_platform_engine.h" #include "chrome/common/native_web_keyboard_event.h" +#include "chrome/common/edit_command.h" #include "chrome/common/render_messages.h" #include "skia/ext/platform_canvas.h" #include "webkit/api/public/mac/WebInputEventFactory.h" @@ -430,6 +432,60 @@ void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) { render_widget_host_->routing_id(), background)); } +// EditCommandMatcher --------------------------------------------------------- + +// This class is used to capture the shortcuts that a given key event maps to. +// We instantiate a vanilla NSResponder, call interpretKeyEvents on it, and +// record all of the selectors passed into doCommandBySelector while +// interpreting the key event. The selectors are converted into edit commands +// which can be passed to the render process. +// +// Caveats: +// - Shortcuts involving a sequence of key combinations (chords) don't work, +// because we instantiate a new responder for each event. +// - We ignore key combinations that don't include a modifier (ctrl, cmd, alt) +// because this was causing strange behavior (e.g. tab always inserted a tab +// rather than moving to the next field on the page). + +@interface EditCommandMatcher : NSResponder { + EditCommands* edit_commands_; +} +@end + +@implementation EditCommandMatcher + +- (id)initWithEditCommands:(EditCommands*)edit_commands { + if ((self = [super init]) != nil) { + edit_commands_ = edit_commands; + } + return self; +} + +- (void)doCommandBySelector:(SEL)selector { + NSString* editCommand = + RWHVMEditCommandHelper::CommandNameForSelector(selector); + edit_commands_->push_back( + EditCommand(base::SysNSStringToUTF8(editCommand), "")); +} + +- (void)insertText:(id)string { + // If we don't ignore this, then sometimes we get a bell. +} + ++ (void)matchEditCommands:(EditCommands*)edit_commands + forEvent:(NSEvent*)theEvent { + if ([theEvent type] != NSKeyDown) return; + // Don't interpret plain key presses. This screws up things like <Tab>. + NSUInteger flags = [theEvent modifierFlags]; + flags &= (NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask); + if (flags == 0) return; + scoped_nsobject<EditCommandMatcher> matcher( + [[EditCommandMatcher alloc] initWithEditCommands:edit_commands]); + [matcher.get() interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; +} + +@end + // RenderWidgetHostViewCocoa --------------------------------------------------- @implementation RenderWidgetHostViewCocoa @@ -465,7 +521,7 @@ void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) { closeOnDeactivate_ = b; } -- (void)mouseEvent:(NSEvent *)theEvent { +- (void)mouseEvent:(NSEvent*)theEvent { // Don't cancel child popups; killing them on a mouse click would prevent the // user from positioning the insertion point in the text field spawning the // popup. A click outside the text field would cause the text field to drop @@ -478,7 +534,7 @@ void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) { renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(event); } -- (void)keyEvent:(NSEvent *)theEvent { +- (void)keyEvent:(NSEvent*)theEvent { // TODO(avi): Possibly kill self? See RenderWidgetHostViewWin::OnKeyEvent and // http://b/issue?id=1192881 . @@ -499,8 +555,15 @@ void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) { event.windowsKeyCode = 0xE5; // Dispatch this keyboard event to the renderer. - if (renderWidgetHostView_->render_widget_host_) - renderWidgetHostView_->render_widget_host_->ForwardKeyboardEvent(event); + if (renderWidgetHostView_->render_widget_host_) { + RenderWidgetHost* widgetHost = renderWidgetHostView_->render_widget_host_; + // Look up shortcut, if any, for this key combination. + EditCommands editCommands; + [EditCommandMatcher matchEditCommands:&editCommands forEvent:theEvent]; + if (!editCommands.empty()) + widgetHost->ForwardEditCommandsForNextKeyEvent(editCommands); + widgetHost->ForwardKeyboardEvent(event); + } // Dispatch a NSKeyDown event to an input method. // To send an onkeydown() event before an onkeypress() event, we should |