Commit 3d9bea97 authored by tapted's avatar tapted Committed by Commit bot

MacViews: Merge single-character edits, map (and validate) undo and redo

Undo and redo are mapped in a similar way to cut/copy/paste on Mac, via
the mainMenu's performKeyEquivalent, and the Cocoa responder chain.

This CL adds responders for undo: and redo:

Single-character editing commands were not being merged into a single
edit. For example, typing {'c', 'a', 't', Cmd+Z} would only undo the 't'
not the entire 'cat'. This CL fixes by detecting single-character
inserts in response to a keyDown event.

Then, for "redo", some of the textfield unittests would send both Ctrl+Y
and Ctrl+Shift+Z. Cmd+Y for undo isn't supported on Mac (it's mapped to
History -> Show Full History). So a wrapper is added that just sends
Cmd+Shfit+Z again.

Gets the following tests passing:
     TextfieldTest.DragAndDrop_ToTheLeft
     TextfieldTest.DragAndDrop_ToTheRight
     TextfieldTest.UndoRedoTest

BUG=454353

Review URL: https://codereview.chromium.org/923903002

Cr-Commit-Position: refs/heads/master@{#316992}
parent d88d2844
......@@ -32,6 +32,9 @@ class View;
// A tracking area installed to enable mouseMoved events.
ui::ScopedCrTrackingArea trackingArea_;
// Whether the view is reacting to a keyDown event on the view.
BOOL inKeyDown_;
}
@property(readonly, nonatomic) views::View* hostedView;
......
......@@ -67,6 +67,8 @@ gfx::Point MovePointToWindow(const NSPoint& point,
eventFlags:(int)eventFlags;
// Menu action handlers.
- (void)undo:(id)sender;
- (void)redo:(id)sender;
- (void)cut:(id)sender;
- (void)copy:(id)sender;
- (void)paste:(id)sender;
......@@ -165,6 +167,27 @@ gfx::Point MovePointToWindow(const NSPoint& point,
hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event);
}
- (void)undo:(id)sender {
// This DCHECK is more strict than a similar check in handleAction:. It can be
// done here because the actors sending these actions should be calling
// validateUserInterfaceItem: before enabling UI that allows these messages to
// be sent. Checking it here would be too late to provide correct UI feedback
// (e.g. there will be no "beep").
DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO));
[self handleAction:IDS_APP_UNDO
keyCode:ui::VKEY_Z
domCode:ui::DomCode::KEY_Z
eventFlags:ui::EF_CONTROL_DOWN];
}
- (void)redo:(id)sender {
DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_REDO));
[self handleAction:IDS_APP_REDO
keyCode:ui::VKEY_Z
domCode:ui::DomCode::KEY_Z
eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
}
- (void)cut:(id)sender {
DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_CUT));
[self handleAction:IDS_APP_CUT
......@@ -243,7 +266,9 @@ gfx::Point MovePointToWindow(const NSPoint& point,
- (void)keyDown:(NSEvent*)theEvent {
// Convert the event into an action message, according to OSX key mappings.
inKeyDown_ = YES;
[self interpretKeyEvents:@[ theEvent ]];
inKeyDown_ = NO;
}
- (void)mouseDown:(NSEvent*)theEvent {
......@@ -568,7 +593,17 @@ gfx::Point MovePointToWindow(const NSPoint& point,
return;
textInputClient_->DeleteRange(gfx::Range(replacementRange));
textInputClient_->InsertText(base::SysNSStringToUTF16(text));
// If a single character is inserted by keyDown's call to interpretKeyEvents:
// then use InsertChar() to allow editing events to be merged. The second
// argument is the key modifier, which interpretKeyEvents: will have already
// processed, so don't send it to InsertChar() as well. E.g. Alt+S puts 'ß' in
// |text| but sending 'Alt' to InsertChar would filter it out since it thinks
// it's a command. Actual commands (e.g. Cmd+S) won't go through insertText:.
if (inKeyDown_ && [text length] == 1)
textInputClient_->InsertChar([text characterAtIndex:0], 0);
else
textInputClient_->InsertText(base::SysNSStringToUTF16(text));
}
- (NSRange)markedRange {
......@@ -620,6 +655,10 @@ gfx::Point MovePointToWindow(const NSPoint& point,
SEL action = [item action];
if (action == @selector(undo:))
return textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO);
if (action == @selector(redo:))
return textInputClient_->IsEditCommandEnabled(IDS_APP_REDO);
if (action == @selector(cut:))
return textInputClient_->IsEditCommandEnabled(IDS_APP_CUT);
if (action == @selector(copy:))
......
......@@ -1087,11 +1087,11 @@ TEST_F(TextfieldTest, DragAndDrop_ToTheRight) {
EXPECT_STR_EQ("", textfield_->text());
SendKeyEvent(ui::VKEY_Z, false, true);
EXPECT_STR_EQ("", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("hello world", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("h welloorld", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("h welloorld", textfield_->text());
}
......@@ -1141,11 +1141,11 @@ TEST_F(TextfieldTest, DragAndDrop_ToTheLeft) {
EXPECT_STR_EQ("", textfield_->text());
SendKeyEvent(ui::VKEY_Z, false, true);
EXPECT_STR_EQ("", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("hello world", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("h worlellod", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("h worlellod", textfield_->text());
}
......@@ -1364,9 +1364,9 @@ TEST_F(TextfieldTest, UndoRedoTest) {
EXPECT_STR_EQ("", textfield_->text());
SendKeyEvent(ui::VKEY_Z, false, true);
EXPECT_STR_EQ("", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("a", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("a", textfield_->text());
// AppendText
......@@ -1375,7 +1375,7 @@ TEST_F(TextfieldTest, UndoRedoTest) {
EXPECT_STR_EQ("ab", textfield_->text());
SendKeyEvent(ui::VKEY_Z, false, true);
EXPECT_STR_EQ("a", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("ab", textfield_->text());
// SetText
......@@ -1387,9 +1387,9 @@ TEST_F(TextfieldTest, UndoRedoTest) {
EXPECT_STR_EQ("abc", textfield_->text());
SendKeyEvent(ui::VKEY_Z, false, true);
EXPECT_STR_EQ("ab", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("abc", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("abc", textfield_->text());
textfield_->SetText(ASCIIToUTF16("123"));
textfield_->SetText(ASCIIToUTF16("123"));
......@@ -1406,11 +1406,11 @@ TEST_F(TextfieldTest, UndoRedoTest) {
EXPECT_STR_EQ("ab", textfield_->text());
SendKeyEvent(ui::VKEY_Z, false, true);
EXPECT_STR_EQ("a", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("ab", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("123", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("1234", textfield_->text());
// Undoing to the same text shouldn't call ContentsChanged.
......@@ -1422,13 +1422,13 @@ TEST_F(TextfieldTest, UndoRedoTest) {
EXPECT_STR_EQ("abc", textfield_->text());
SendKeyEvent(ui::VKEY_Z, false, true);
EXPECT_STR_EQ("1234", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("abc", textfield_->text());
// Delete/Backspace
SendKeyEvent(ui::VKEY_BACK);
EXPECT_STR_EQ("ab", textfield_->text());
SendKeyEvent(ui::VKEY_HOME);
SendHomeEvent(false);
SendKeyEvent(ui::VKEY_DELETE);
EXPECT_STR_EQ("b", textfield_->text());
SendKeyEvent(ui::VKEY_A, false, true);
......@@ -1440,16 +1440,38 @@ TEST_F(TextfieldTest, UndoRedoTest) {
EXPECT_STR_EQ("ab", textfield_->text());
SendKeyEvent(ui::VKEY_Z, false, true);
EXPECT_STR_EQ("abc", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("ab", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("b", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("", textfield_->text());
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("", textfield_->text());
}
// Most platforms support Ctrl+Y as an alternative to Ctrl+Shift+Z, but on Mac
// that is bound to "Show full history", so is not mapped as an editing
// command. So, on Mac, send Cmd+Shift+Z.
#if !defined(OS_MACOSX)
// Test that Ctrl+Y works for Redo, as well as Ctrl+Shift+Z.
TEST_F(TextfieldTest, RedoWithCtrlY) {
InitTextfield();
SendKeyEvent(ui::VKEY_A);
EXPECT_STR_EQ("a", textfield_->text());
SendKeyEvent(ui::VKEY_Z, false, true);
EXPECT_STR_EQ("", textfield_->text());
SendKeyEvent(ui::VKEY_Y, false, true);
EXPECT_STR_EQ("a", textfield_->text());
SendKeyEvent(ui::VKEY_Z, false, true);
EXPECT_STR_EQ("", textfield_->text());
SendKeyEvent(ui::VKEY_Z, true, true);
EXPECT_STR_EQ("a", textfield_->text());
}
#endif // !defined(OS_MACOSX)
TEST_F(TextfieldTest, CutCopyPaste) {
InitTextfield();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment