diff --git a/AppKit/CPResponder.j b/AppKit/CPResponder.j index 0b7467dd7..26b542c5a 100644 --- a/AppKit/CPResponder.j +++ b/AppKit/CPResponder.j @@ -290,6 +290,16 @@ CPDeleteForwardKeyCode = 46; { } +/* + @ignore + Returns YES if the receiver handles the Tab key itself, as an editable text view does by + inserting a tab character. Otherwise the window uses Tab to move to the next key view. +*/ +- (BOOL)_wantsTabKey +{ + return NO; +} + - (void)insertBackTab:(id)sender { } diff --git a/AppKit/CPTextView/CPTextView.j b/AppKit/CPTextView/CPTextView.j index e947cec60..27d89eb4a 100644 --- a/AppKit/CPTextView/CPTextView.j +++ b/AppKit/CPTextView/CPTextView.j @@ -1839,6 +1839,16 @@ Sets the selection to a range of characters in response to user action. [self insertText:@"\t"]; } +/* + @ignore + As in Cocoa, an editable text view takes the Tab key and inserts a tab character. + Control-Tab still moves to the next key view. +*/ +- (BOOL)_wantsTabKey +{ + return [self isEditable]; +} + - (void)insertTabIgnoringFieldEditor:(id)sender { [self insertTab:sender]; diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index 79cc66198..3166906e9 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -2007,8 +2007,14 @@ CPTexturedBackgroundWindowMask return [[self firstResponder] keyUp:anEvent]; case CPKeyDown: + // Tab moves to the next key view, unless the first responder takes plain Tab itself, as an + // editable text view does to insert a tab character. Control-Tab always moves on, as in Cocoa. + var modifierFlags = [anEvent modifierFlags], + firstResponderTakesTab = !(modifierFlags & (CPShiftKeyMask | CPControlKeyMask)) && [[self firstResponder] _wantsTabKey]; + if ([anEvent charactersIgnoringModifiers] === CPTabCharacter && - !([anEvent modifierFlags] & (CPAlternateKeyMask | CPCommandKeyMask))) + !(modifierFlags & (CPAlternateKeyMask | CPCommandKeyMask)) && + !firstResponderTakesTab) { if ([anEvent modifierFlags] & CPShiftKeyMask) [self selectPreviousKeyView:self]; @@ -2044,6 +2050,10 @@ CPTexturedBackgroundWindowMask return; } + // A first responder that takes Tab gets the key, and the browser must not also move its own focus. + if (firstResponderTakesTab && [anEvent charactersIgnoringModifiers] === CPTabCharacter && CPDOMAvailable) + [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO]; + [[self firstResponder] keyDown:anEvent]; // Trigger the default button if needed