mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
New: non-editable but selectable text fields can become the first responder.
Without this change, text fields with setEditable:NO but setSelectable:YES could not become the first responder, and so would not listen for copy: events. This change allows selectable fields to become the first responder, taking advantage of the distinction between first responder and first key responder maintained internally in CPTextField - a merely selectable field does not become the first key responder. Refs #1972.
This commit is contained in:
+65
-43
@@ -351,8 +351,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateEditable];
|
||||
|
||||
// We only allow first responder status if the field is editable and enabled.
|
||||
if (!shouldBeEditable && [[self window] firstResponder] === self)
|
||||
// We only allow first responder status if the field is enable, and editable or selectable.
|
||||
if (!(shouldBeEditable && ![self isSelectable]) && [[self window] firstResponder] === self)
|
||||
[[self window] makeFirstResponder:nil];
|
||||
|
||||
if (shouldBeEditable)
|
||||
@@ -377,7 +377,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
{
|
||||
[super setEnabled:shouldBeEnabled];
|
||||
|
||||
// We only allow first responder status if the field is editable and enabled.
|
||||
// We only allow first responder status if the field is enabled.
|
||||
if (!shouldBeEnabled && [[self window] firstResponder] === self)
|
||||
[[self window] makeFirstResponder:nil];
|
||||
}
|
||||
@@ -531,7 +531,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
/*! @ignore */
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return [self isEditable] && [self isEnabled] && [self _isWithinUsablePlatformRect];
|
||||
return ([self isEditable] || [self isSelectable]) && [self isEnabled] && [self _isWithinUsablePlatformRect];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
@@ -542,7 +542,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
_isEditing = NO;
|
||||
|
||||
if ([[self window] isKeyWindow])
|
||||
if ([[self window] isKeyWindow] && [self isEditable])
|
||||
return [self _becomeFirstKeyResponder];
|
||||
|
||||
return YES;
|
||||
@@ -552,6 +552,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
A text field can be the first responder without necessarily being the focus of keyboard input. For example, it might be the first responder of window A but window B is the main and key window. It's important we don't put a focused input field into a text field in a non-key window, even if that field is the first responder, because the key window might also have a first responder text field which the user will expect to receive keyboard input.
|
||||
|
||||
Since a first responder but non-key window text field can't receive input it should not even look like an active text field (Cocoa has a "slightly active" text field look it uses when another window is the key window, but Cappuccino doesn't today.)
|
||||
|
||||
It's also possible for a text field to be non-editable but selectable in which case it can also become the first responder -
|
||||
this is what allows text to be copied from it.
|
||||
*/
|
||||
- (BOOL)_becomeFirstKeyResponder
|
||||
{
|
||||
@@ -560,6 +563,11 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
if (![self _isWithinUsablePlatformRect])
|
||||
return NO;
|
||||
|
||||
// A selectable but non-editable text field may be the first responder, but never the
|
||||
// first key responder (first key responder indicating editability.)
|
||||
if (![self isEditable])
|
||||
return NO;
|
||||
|
||||
[self setThemeState:CPThemeStateEditing];
|
||||
|
||||
[self _updatePlaceholderState];
|
||||
@@ -680,24 +688,26 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
- (BOOL)resignFirstResponder
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
var element = [self _inputElement],
|
||||
newValue = element.value,
|
||||
error = @"";
|
||||
|
||||
if (newValue !== _stringValue)
|
||||
// We might have been the first responder without actually editing.
|
||||
if (_isEditing)
|
||||
{
|
||||
[self _setStringValue:newValue];
|
||||
}
|
||||
var element = [self _inputElement],
|
||||
newValue = element.value,
|
||||
error = @"";
|
||||
|
||||
// If there is a formatter, always give it a chance to reject the resignation,
|
||||
// even if the value has not changed.
|
||||
if ([self _valueIsValid:newValue] === NO)
|
||||
{
|
||||
element.focus();
|
||||
return NO;
|
||||
}
|
||||
if (newValue !== _stringValue)
|
||||
{
|
||||
[self _setStringValue:newValue];
|
||||
}
|
||||
|
||||
// If there is a formatter, always give it a chance to reject the resignation,
|
||||
// even if the value has not changed.
|
||||
if ([self _valueIsValid:newValue] === NO)
|
||||
{
|
||||
element.focus();
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// When we are no longer the first responder we don't worry about the key status of our window anymore.
|
||||
@@ -706,10 +716,13 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[self _resignFirstKeyResponder];
|
||||
|
||||
_isEditing = NO;
|
||||
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
|
||||
if ([self isEditable])
|
||||
{
|
||||
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
|
||||
|
||||
if ([self sendsActionOnEndEditing])
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
if ([self sendsActionOnEndEditing])
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
}
|
||||
|
||||
[self textDidBlur:[CPNotification notificationWithName:CPTextFieldDidBlurNotification object:self userInfo:nil]];
|
||||
|
||||
@@ -782,6 +795,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
- (void)_windowDidBecomeKey:(CPNotification)aNotification
|
||||
{
|
||||
if (![self isEditable])
|
||||
return;
|
||||
|
||||
var wind = [self window];
|
||||
|
||||
if ([wind isKeyWindow] && [wind firstResponder] === self)
|
||||
@@ -828,6 +844,17 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
return [self acceptsFirstResponder];
|
||||
}
|
||||
|
||||
- (void)_didEdit
|
||||
{
|
||||
if (!_isEditing)
|
||||
{
|
||||
_isEditing = YES;
|
||||
[self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
|
||||
}
|
||||
|
||||
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
// Don't track! (ever?)
|
||||
@@ -880,26 +907,21 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
- (void)keyUp:(CPEvent)anEvent
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
if (![self isEditable])
|
||||
return;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
var newValue = [self _inputElement].value;
|
||||
|
||||
if (newValue !== _stringValue)
|
||||
{
|
||||
[self _setStringValue:newValue];
|
||||
|
||||
if (!_isEditing)
|
||||
{
|
||||
_isEditing = YES;
|
||||
[self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
|
||||
}
|
||||
|
||||
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
|
||||
[self _didEdit];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)keyDown:(CPEvent)anEvent
|
||||
@@ -940,6 +962,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
if (newValue !== _stringValue)
|
||||
{
|
||||
[self _setStringValue:newValue];
|
||||
[self _didEdit];
|
||||
}
|
||||
|
||||
if ([self _valueIsValid:_stringValue])
|
||||
@@ -993,13 +1016,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
// NOTE: _stringValue is now the current input element value
|
||||
if (oldValue !== _stringValue)
|
||||
{
|
||||
if (!_isEditing)
|
||||
{
|
||||
_isEditing = YES;
|
||||
[self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
|
||||
}
|
||||
|
||||
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
|
||||
[self _didEdit];
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1298,6 +1315,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
- (void)cut:(id)sender
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
[self copy:sender];
|
||||
|
||||
if (![self isEditable])
|
||||
@@ -1336,7 +1356,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
pasteString = [pasteboard stringForType:CPStringPboardType],
|
||||
newValue = [_stringValue stringByReplacingCharactersInRange:selectedRange withString:pasteString];
|
||||
|
||||
[self setStringValue:newValue];
|
||||
[self _setStringValue:newValue];
|
||||
[self _didEdit];
|
||||
[self setSelectedRange:CPMakeRange(selectedRange.location + pasteString.length, 0)];
|
||||
}
|
||||
// If we don't have an oninput listener, we won't detect the change made by the cut and need to fake a key up "soon".
|
||||
@@ -1447,8 +1468,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
var newValue = [_stringValue stringByReplacingCharactersInRange:selectedRange withString:""];
|
||||
|
||||
[self setStringValue:newValue];
|
||||
[self _setStringValue:newValue];
|
||||
[self setSelectedRange:CPMakeRange(selectedRange.location, 0)];
|
||||
[self _didEdit];
|
||||
}
|
||||
|
||||
#pragma mark Setting the Delegate
|
||||
@@ -1629,7 +1651,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
{
|
||||
[super viewDidUnhide];
|
||||
|
||||
if ([[self window] firstResponder] === self)
|
||||
if ([self isEditable] && [[self window] firstResponder] === self)
|
||||
[self _becomeFirstKeyResponder];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user