mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-24 11:20:42 +00:00
Allow the editor to be placed anywhere based on a selection range.
This commit is contained in:
+63
-52
@@ -61,6 +61,8 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
||||
CPScrollView _tokenScrollView;
|
||||
BOOL _scrollToLastToken;
|
||||
|
||||
CPRange _selectedRange;
|
||||
|
||||
CPView _autocompleteContainer;
|
||||
CPScrollView _autocompleteScrollView;
|
||||
CPTableView _autocompleteView;
|
||||
@@ -88,6 +90,8 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
||||
{
|
||||
if (self = [super initWithFrame:frame])
|
||||
{
|
||||
_selectedRange = CPMakeRange(0, 0);
|
||||
|
||||
_tokenScrollView = [[CPScrollView alloc] initWithFrame:CGRectMakeZero()];
|
||||
[_tokenScrollView setHasHorizontalScroller:NO];
|
||||
[_tokenScrollView setHasVerticalScroller:NO];
|
||||
@@ -469,6 +473,9 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
||||
*/
|
||||
_value = objectValue;
|
||||
|
||||
// Place the cursor at the end
|
||||
_selectedRange = CPMakeRange([[self _tokens] count], 0);
|
||||
|
||||
[self _updatePlaceholderState];
|
||||
|
||||
_scrollToLastToken = YES;
|
||||
@@ -827,14 +834,63 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
||||
contentOrigin = contentRect.origin,
|
||||
contentSize = contentRect.size,
|
||||
offset = CPPointMake(contentOrigin.x, contentOrigin.y),
|
||||
spaceBetweenTokens = CPSizeMake(2.0, 2.0);
|
||||
spaceBetweenTokens = CPSizeMake(2.0, 2.0),
|
||||
isEditing = [[self window] firstResponder] == self;
|
||||
|
||||
// Hack to make sure we are handling an array
|
||||
if (![[self _tokens] isKindOfClass:[CPArray class]])
|
||||
return;
|
||||
|
||||
var fitAndFrame = function(width, height)
|
||||
{
|
||||
var r = CGRectMake(0, 0, width, height);
|
||||
|
||||
if (offset.x + width >= contentSize.width && offset.x > contentOrigin.x)
|
||||
{
|
||||
offset.x = contentOrigin.x;
|
||||
offset.y += height + spaceBetweenTokens.height;
|
||||
}
|
||||
|
||||
r.origin.x = offset.x;
|
||||
r.origin.y = offset.y;
|
||||
|
||||
// Make sure the frame fits.
|
||||
[contentView setFrame:CGRectMake(0, 0, CGRectGetWidth([_tokenScrollView bounds]), offset.y + height)];
|
||||
|
||||
offset.x += width + spaceBetweenTokens.width;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
var placeEditor = function()
|
||||
{
|
||||
var element = [self _inputElement],
|
||||
tokenToken = [_CPTokenFieldToken new];
|
||||
|
||||
// Get the height of a typical token, or a token token if you will.
|
||||
[tokenToken sizeToFit];
|
||||
|
||||
// XXX The "X" here is used to estimate the space needed for one more character,
|
||||
// so that we can wrap before we're out of bounds. Since different fonts
|
||||
// might have different sizes of "X" this solution is not ideal, but it works.
|
||||
var textWidth = [(element.value || @"") + "X" sizeWithFont:[self font]].width,
|
||||
tokenHeight = CGRectGetHeight([tokenToken bounds]),
|
||||
inputFrame = fitAndFrame(textWidth, tokenHeight);
|
||||
|
||||
element.style.left = inputFrame.origin.x + "px";
|
||||
element.style.top = inputFrame.origin.y + "px";
|
||||
element.style.width = inputFrame.size.width + "px";
|
||||
element.style.height = inputFrame.size.height + "px";
|
||||
|
||||
// When editing, always show the cursor.
|
||||
[[_tokenScrollView documentView] scrollRectToVisible:inputFrame];
|
||||
}
|
||||
|
||||
for (var i = 0, count = [[self _tokens] count]; i < count; i++)
|
||||
{
|
||||
if (isEditing && i == _selectedRange.location)
|
||||
placeEditor(i);
|
||||
|
||||
var tokenView = [[self _tokens] objectAtIndex:i];
|
||||
|
||||
// Make sure we are only changing completed tokens
|
||||
@@ -845,24 +901,15 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
||||
[tokenView sizeToFit];
|
||||
|
||||
var size = [contentView bounds].size,
|
||||
tokenViewSize = [tokenView bounds].size;
|
||||
tokenViewSize = [tokenView bounds].size,
|
||||
tokenFrame = fitAndFrame(tokenViewSize.width, tokenViewSize.height);
|
||||
|
||||
if (contentSize.width < offset.x + tokenViewSize.width)
|
||||
{
|
||||
// Reset the x coordinate to the beginnning of the field
|
||||
offset.x = contentOrigin.x;
|
||||
|
||||
// Increase the y offset to fall below the current tokens
|
||||
offset.y += tokenViewSize.height + spaceBetweenTokens.height;
|
||||
}
|
||||
|
||||
// Make sure there is enough space to hold the new token.
|
||||
[contentView setFrame:CGRectMake(0, 0, CGRectGetWidth([_tokenScrollView bounds]), offset.y + tokenViewSize.height)];
|
||||
|
||||
[tokenView setFrameOrigin:offset];
|
||||
offset.x += [tokenView bounds].size.width + spaceBetweenTokens.width;
|
||||
[tokenView setFrame:tokenFrame];
|
||||
}
|
||||
|
||||
if (isEditing && _selectedRange.location >= [[self _tokens] count])
|
||||
placeEditor();
|
||||
|
||||
if (_scrollToLastToken)
|
||||
{
|
||||
// This code is responsible for showing the last line of tokens
|
||||
@@ -871,42 +918,6 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
||||
[self _scrollTokenViewToVisible:[[self _tokens] lastObject]];
|
||||
_scrollToLastToken = NO;
|
||||
}
|
||||
|
||||
if ([[self window] firstResponder] != self)
|
||||
return;
|
||||
|
||||
var element = [self _inputElement],
|
||||
tokenToken = [_CPTokenFieldToken new];
|
||||
|
||||
// Get the height of a typical token, or a token token if you will.
|
||||
[tokenToken sizeToFit];
|
||||
|
||||
var tokenHeight = CGRectGetHeight([tokenToken bounds]);
|
||||
|
||||
// Do we need to line wrap the editor element?
|
||||
if (offset.x > contentOrigin.x)
|
||||
{
|
||||
// XXX The "X" here is used to estimate the space needed for one more character,
|
||||
// so that we can wrap before we're out of bounds. Since different fonts
|
||||
// might have different sizes of "X" this solution is not ideal, but it works.
|
||||
var textWidth = [(element.value || @" ") + "X" sizeWithFont:[self font]].width;
|
||||
|
||||
if (offset.x + textWidth >= contentSize.width)
|
||||
{
|
||||
offset.x = contentOrigin.x;
|
||||
offset.y += tokenHeight + spaceBetweenTokens.height;
|
||||
[contentView setFrame:CGRectMake(0, 0, CGRectGetWidth([_tokenScrollView bounds]), offset.y + tokenHeight)];
|
||||
}
|
||||
}
|
||||
|
||||
var inputFrame = CGRectMake(offset.x, offset.y, [contentView bounds].size.width - offset.x, tokenHeight);
|
||||
element.style.left = inputFrame.origin.x + "px";
|
||||
element.style.top = inputFrame.origin.y + "px";
|
||||
element.style.width = inputFrame.size.width + "px";
|
||||
element.style.height = inputFrame.size.height + "px";
|
||||
|
||||
// When editing, always show the cursor.
|
||||
[[_tokenScrollView documentView] scrollRectToVisible:inputFrame];
|
||||
}
|
||||
|
||||
- (BOOL)_scrollTokenViewToVisible:(_CPTokenFieldToken)aToken
|
||||
|
||||
Reference in New Issue
Block a user