mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
initial display of the table
This commit is contained in:
@@ -292,7 +292,6 @@ _oncontextmenuhandler = function () { return false; };
|
||||
// We erased all lines
|
||||
if (!startIndex)
|
||||
[self setExtraLineFragmentRect:CGRectMake(0, 0) usedRect:CGRectMake(0, 0) textContainer:nil];
|
||||
// document.title=startIndex;
|
||||
|
||||
[_typesetter layoutGlyphsInLayoutManager:self startingAtGlyphIndex:startIndex maxNumberOfLineFragments:-1 nextGlyphIndex:nil];
|
||||
|
||||
@@ -532,20 +531,26 @@ _oncontextmenuhandler = function () { return false; };
|
||||
var frames = [fragment glyphFrames],
|
||||
len = fragment._range.length;
|
||||
|
||||
for (var j = 0; j < len; j++)
|
||||
if (frames)
|
||||
{
|
||||
if (CGRectContainsPoint(frames[j], point))
|
||||
{
|
||||
if (partialFraction)
|
||||
partialFraction[0] = (point.x - frames[j].origin.x) / frames[j].size.width;
|
||||
var maxLen = MIN(len, frames.length);
|
||||
|
||||
return fragment._range.location + j;
|
||||
for (var j = 0; j < maxLen; j++)
|
||||
{
|
||||
var frame = frames[j];
|
||||
|
||||
if (frame && CGRectContainsPoint(frame, point))
|
||||
{
|
||||
if (partialFraction)
|
||||
partialFraction[0] = (point.x - frame.origin.x) / frame.size.width;
|
||||
|
||||
return fragment._range.location + j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, maybe a point left to the last character was clicked -> search again with broader constraints
|
||||
if ([[_textStorage string] length])
|
||||
{
|
||||
for (var i = 0; i < c; i++)
|
||||
@@ -554,30 +559,33 @@ _oncontextmenuhandler = function () { return false; };
|
||||
|
||||
if (fragment._textContainer === container)
|
||||
{
|
||||
// Within the horizontal territory of the current (not-empty) line?
|
||||
if (fragment._range.length > 0 && point.y > fragment._fragmentRect.origin.y &&
|
||||
point.y <= fragment._fragmentRect.origin.y + fragment._fragmentRect.size.height)
|
||||
if (fragment._range.length > 0 && point.y > fragment._fragmentRect.origin.y &&
|
||||
point.y <= fragment._fragmentRect.origin.y + fragment._fragmentRect.size.height)
|
||||
{
|
||||
if (i < c - 1 && _lineFragments[i + 1]._fragmentRect.origin.y === fragment._fragmentRect.origin.y)
|
||||
continue;
|
||||
|
||||
var nlLoc = CPMaxRange(fragment._range),
|
||||
frames = [fragment glyphFrames];
|
||||
|
||||
if (frames && frames.length > 0)
|
||||
{
|
||||
// Skip tabs and move on the last fragment in this line
|
||||
if (i < c - 1 && _lineFragments[i + 1]._fragmentRect.origin.y === fragment._fragmentRect.origin.y)
|
||||
continue;
|
||||
var lastFrame = frames[frames.length - 1],
|
||||
firstFrame = frames[0];
|
||||
|
||||
var nlLoc = CPMaxRange(fragment._range),
|
||||
lastFrame = [fragment glyphFrames][fragment._range.length - 1],
|
||||
firstFrame = [fragment glyphFrames][0];
|
||||
if (lastFrame && firstFrame)
|
||||
{
|
||||
if (_isNewlineCharacter([[_textStorage string] characterAtIndex:nlLoc > 0 ? nlLoc - 1 : 0]))
|
||||
nlLoc--;
|
||||
|
||||
// stay on the line the newline character belongs to
|
||||
if (_isNewlineCharacter([[_textStorage string] characterAtIndex:nlLoc > 0 ? nlLoc - 1 : 0]))
|
||||
nlLoc--;
|
||||
|
||||
// Clicked right to the last character
|
||||
if (point.x > CGRectGetMaxX(lastFrame))
|
||||
return nlLoc;
|
||||
// Clicked left to the last character
|
||||
else if (point.x <= CGRectGetMinX(firstFrame))
|
||||
return fragment._range.location;
|
||||
else
|
||||
return nlLoc;
|
||||
if (point.x > CGRectGetMaxX(lastFrame))
|
||||
return nlLoc;
|
||||
else if (point.x <= CGRectGetMinX(firstFrame))
|
||||
return fragment._range.location;
|
||||
else
|
||||
return nlLoc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -707,7 +715,11 @@ _oncontextmenuhandler = function () { return false; };
|
||||
|
||||
var index = location - lineFragment._range.location;
|
||||
|
||||
return lineFragment._glyphsOffsets[index];
|
||||
if (index < 0 || !lineFragment._glyphsOffsets || index >= lineFragment._glyphsOffsets.length)
|
||||
return 0.0;
|
||||
|
||||
var offset = lineFragment._glyphsOffsets[index];
|
||||
return (offset === undefined) ? 0.0 : offset;
|
||||
}
|
||||
|
||||
- (double)_descentAtLocation:(unsigned)location
|
||||
@@ -719,7 +731,11 @@ _oncontextmenuhandler = function () { return false; };
|
||||
|
||||
var index = location - lineFragment._range.location;
|
||||
|
||||
return lineFragment._glyphsFrames[index]._descent;
|
||||
if (index < 0 || !lineFragment._glyphsFrames || index >= lineFragment._glyphsFrames.length)
|
||||
return 0.0;
|
||||
|
||||
var frame = lineFragment._glyphsFrames[index];
|
||||
return (frame && frame._descent !== undefined) ? frame._descent : 0.0;
|
||||
}
|
||||
|
||||
- (void)setLineFragmentRect:(CGRect)fragmentRect forGlyphRange:(CPRange)glyphRange usedRect:(CGRect)usedRect
|
||||
@@ -843,11 +859,16 @@ _oncontextmenuhandler = function () { return false; };
|
||||
{
|
||||
if (_lineFragments.length > 0 && index >= [self numberOfGlyphs] - 1)
|
||||
{
|
||||
var lineFragment= _lineFragments[_lineFragments.length - 1],
|
||||
var lineFragment = _lineFragments[_lineFragments.length - 1],
|
||||
glyphFrames = [lineFragment glyphFrames];
|
||||
|
||||
if (glyphFrames.length > 0)
|
||||
return CGPointCreateCopy(glyphFrames[glyphFrames.length - 1].origin);
|
||||
if (glyphFrames && glyphFrames.length > 0)
|
||||
{
|
||||
var frame = glyphFrames[glyphFrames.length - 1];
|
||||
|
||||
if (frame)
|
||||
return CGPointCreateCopy(frame.origin);
|
||||
}
|
||||
}
|
||||
|
||||
var lineFragment = _objectWithLocationInRange(_lineFragments, index);
|
||||
@@ -858,8 +879,17 @@ _oncontextmenuhandler = function () { return false; };
|
||||
return CGPointCreateCopy(lineFragment._location);
|
||||
|
||||
var glyphFrames = [lineFragment glyphFrames];
|
||||
var relativeIndex = index - lineFragment._range.location;
|
||||
|
||||
return CGPointCreateCopy(glyphFrames[index - lineFragment._range.location].origin);
|
||||
if (glyphFrames && relativeIndex >= 0 && relativeIndex < glyphFrames.length)
|
||||
{
|
||||
var frame = glyphFrames[relativeIndex];
|
||||
|
||||
if (frame)
|
||||
return CGPointCreateCopy(frame.origin);
|
||||
}
|
||||
|
||||
return CGPointCreateCopy(lineFragment._location);
|
||||
}
|
||||
|
||||
return CGPointMakeZero();
|
||||
@@ -905,7 +935,6 @@ _oncontextmenuhandler = function () { return false; };
|
||||
inTextContainer:(CPTextContainer)container
|
||||
rectCount:(CGRectPointer)rectCount
|
||||
{
|
||||
|
||||
var rectArray = [],
|
||||
lineFragments = _objectsInRange(_lineFragments, selectedCharRange);
|
||||
|
||||
@@ -924,21 +953,29 @@ _oncontextmenuhandler = function () { return false; };
|
||||
rect = nil,
|
||||
len = fragment._range.length;
|
||||
|
||||
for (var j = 0; j < len; j++)
|
||||
if (frames)
|
||||
{
|
||||
if (CPLocationInRange(fragment._range.location + j, selectedCharRange))
|
||||
for (var j = 0; j < len; j++)
|
||||
{
|
||||
var correctedRect = CGRectCreateCopy(frames[j]);
|
||||
correctedRect.size.height -= frames[j]._descent;
|
||||
correctedRect.origin.y -= frames[j]._descent;
|
||||
if (j < frames.length && CPLocationInRange(fragment._range.location + j, selectedCharRange))
|
||||
{
|
||||
var frame = frames[j];
|
||||
|
||||
if (!rect)
|
||||
rect = CGRectCreateCopy(correctedRect);
|
||||
else
|
||||
rect = CGRectUnion(rect, correctedRect);
|
||||
if (frame)
|
||||
{
|
||||
var correctedRect = CGRectCreateCopy(frame);
|
||||
correctedRect.size.height -= frame._descent;
|
||||
correctedRect.origin.y -= frame._descent;
|
||||
|
||||
if (_isNewlineCharacter([[_textStorage string] characterAtIndex:MAX(0, CPMaxRange(selectedCharRange) - 1)]))
|
||||
rect.size.width = containerSize.width - rect.origin.x;
|
||||
if (!rect)
|
||||
rect = CGRectCreateCopy(correctedRect);
|
||||
else
|
||||
rect = CGRectUnion(rect, correctedRect);
|
||||
|
||||
if (_isNewlineCharacter([[_textStorage string] characterAtIndex:MAX(0, CPMaxRange(selectedCharRange) - 1)]))
|
||||
rect.size.width = containerSize.width - rect.origin.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -949,7 +986,7 @@ _oncontextmenuhandler = function () { return false; };
|
||||
|
||||
var len = rectArray.length;
|
||||
|
||||
for (var i = 0; i < len - 1; i++) // extend the width of all but the last one
|
||||
for (var i = 0; i < len - 1; i++)
|
||||
{
|
||||
if (FLOOR(CGRectGetMaxY(rectArray[i])) == FLOOR(CGRectGetMaxY(rectArray[i + 1])))
|
||||
continue;
|
||||
@@ -1182,13 +1219,9 @@ var _objectsInRange = function(aList, aRange)
|
||||
{
|
||||
if (![attributes objectForKey:_CPAttachmentInvisible])
|
||||
{
|
||||
var view = [attributes objectForKey:_CPAttachmentView],
|
||||
viewCopy = [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:view]],
|
||||
elem = viewCopy._DOMElement,
|
||||
run = {_range:CPMakeRangeCopy(effectiveRange), color:nil, font:nil, elem:elem, string:nil, view:viewCopy};
|
||||
|
||||
var view = [attributes objectForKey:_CPAttachmentView];
|
||||
var run = {_range:CPMakeRangeCopy(effectiveRange), color:nil, font:nil, elem:nil, string:nil, view:view}; }
|
||||
_runs.push(run);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1269,13 +1302,11 @@ var _objectsInRange = function(aList, aRange)
|
||||
|
||||
for (var i = 0; i < l; i++)
|
||||
{
|
||||
if (_runs[i].view && _runs[i].DOMactive)
|
||||
[_runs[i].view removeFromSuperview];
|
||||
|
||||
if (_runs[i].elem && _runs[i].DOMactive)
|
||||
{
|
||||
if (_runs[i].view)
|
||||
[_runs[i].view removeFromSuperview];
|
||||
else
|
||||
_textContainer._textView._DOMElement.removeChild(_runs[i].elem);
|
||||
}
|
||||
_textContainer._textView._DOMElement.removeChild(_runs[i].elem);
|
||||
|
||||
_runs[i].elem = nil;
|
||||
_runs[i].DOMactive = NO;
|
||||
@@ -1288,6 +1319,9 @@ var _objectsInRange = function(aList, aRange)
|
||||
c = runs.length,
|
||||
orig = CGPointMake(_fragmentRect.origin.x, _fragmentRect.origin.y);
|
||||
|
||||
if (_runs.length === 0)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < c; i++)
|
||||
{
|
||||
var run = runs[i];
|
||||
@@ -1302,13 +1336,21 @@ var _objectsInRange = function(aList, aRange)
|
||||
continue;
|
||||
|
||||
var loc = run._range.location - _runs[0]._range.location;
|
||||
|
||||
// Safety bounds check to protect against uninitialized/empty glyph frames or offsets
|
||||
if (loc < 0 || loc >= _glyphsFrames.length || !_glyphsFrames[loc] || !_glyphsOffsets || loc >= _glyphsOffsets.length)
|
||||
continue;
|
||||
|
||||
orig.x = _glyphsFrames[loc].origin.x + aPoint.x;
|
||||
orig.y = _glyphsFrames[loc].origin.y + aPoint.y + _glyphsOffsets[loc];
|
||||
|
||||
if(run.elem)
|
||||
if(run.elem || run.view)
|
||||
{
|
||||
run.elem.style.left = (orig.x) + "px";
|
||||
run.elem.style.top = (orig.y) + "px";
|
||||
if (run.elem)
|
||||
{
|
||||
run.elem.style.left = (orig.x) + "px";
|
||||
run.elem.style.top = (orig.y) + "px";
|
||||
}
|
||||
|
||||
if (run.view)
|
||||
[run.view setFrameOrigin:orig];
|
||||
@@ -1316,8 +1358,9 @@ var _objectsInRange = function(aList, aRange)
|
||||
if (!run.DOMactive)
|
||||
{
|
||||
if (run.view)
|
||||
[self._textContainer._textView addSubview:run.view];
|
||||
else
|
||||
[_textContainer._textView addSubview:run.view];
|
||||
|
||||
if (run.elem)
|
||||
_textContainer._textView._DOMElement.appendChild(run.elem);
|
||||
}
|
||||
|
||||
@@ -1373,12 +1416,14 @@ var _objectsInRange = function(aList, aRange)
|
||||
{
|
||||
_runs[i]._range.location += rangeOffset;
|
||||
|
||||
if (verticalOffset && _runs[i].elem)
|
||||
if (verticalOffset)
|
||||
{
|
||||
if (_runs[i].view)
|
||||
_runs[i].view._frame.origin.y += verticalOffset;
|
||||
|
||||
_runs[i].elem.top = (_runs[i].elem.top + verticalOffset) + 'px';
|
||||
if (_runs[i].elem)
|
||||
_runs[i].elem.top = (_runs[i].elem.top + verticalOffset) + 'px';
|
||||
|
||||
_runs[i].DOMpatched = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +401,7 @@ var kDelegateRespondsTo_textShouldBeginEditing
|
||||
- (void)superviewFrameChanged:(CPNotification)aNotification
|
||||
{
|
||||
_exposedRect = nil;
|
||||
[self sizeToFit];
|
||||
}
|
||||
|
||||
- (void)viewWillMoveToSuperview:(CPView)aView
|
||||
@@ -1397,7 +1398,10 @@ Sets the selection to a range of characters in response to user action.
|
||||
- (void)moveLeftAndModifySelection:(id)sender
|
||||
{
|
||||
if ([self isSelectable])
|
||||
{
|
||||
[self _extendSelectionIntoDirection:-1 granularity:CPSelectByCharacter];
|
||||
[self scrollRangeToVisible:_selectionRange];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)moveBackward:(id)sender
|
||||
@@ -1419,7 +1423,10 @@ Sets the selection to a range of characters in response to user action.
|
||||
- (void)moveLeft:(id)sender
|
||||
{
|
||||
if ([self isSelectable])
|
||||
{
|
||||
[self _establishSelection:CPMakeRange(_selectionRange.location - (_selectionRange.length ? 0 : 1), 0) byExtending:NO];
|
||||
[self scrollRangeToVisible:_selectionRange];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)moveToEndOfParagraph:(id)sender
|
||||
@@ -2134,9 +2141,15 @@ Sets the selection to a range of characters in response to user action.
|
||||
if (CPEmptyRange(aRange))
|
||||
{
|
||||
if (aRange.location >= [_layoutManager numberOfCharacters])
|
||||
rect = [_layoutManager extraLineFragmentRect];
|
||||
{
|
||||
rect = CGRectCreateCopy([_layoutManager extraLineFragmentRect]);
|
||||
rect.size.width = 1.0;
|
||||
}
|
||||
else
|
||||
rect = [_layoutManager lineFragmentRectForGlyphAtIndex:aRange.location effectiveRange:nil];
|
||||
{
|
||||
rect = CGRectCreateCopy([_layoutManager boundingRectForGlyphRange:CPMakeRange(aRange.location, 1) inTextContainer:_textContainer]);
|
||||
rect.size.width = 1.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2851,11 +2864,16 @@ var CPTextViewAllowsUndoKey = @"CPTextViewAllowsUndoKey",
|
||||
|
||||
_typingAttributes = [[_textStorage attributesAtIndex:0 effectiveRange:nil] copy];
|
||||
|
||||
if (!_typingAttributes)
|
||||
_typingAttributes = [CPMutableDictionary dictionary];
|
||||
|
||||
if (![_typingAttributes valueForKey:CPForegroundColorAttributeName])
|
||||
[_typingAttributes setObject:[CPColor blackColor] forKey:CPForegroundColorAttributeName];
|
||||
|
||||
_textColor = [_typingAttributes valueForKey:CPForegroundColorAttributeName];
|
||||
[self setFont:[_typingAttributes valueForKey:CPFontAttributeName]];
|
||||
|
||||
var decodedFont = [_typingAttributes valueForKey:CPFontAttributeName] || [CPFont systemFontOfSize:12.0];
|
||||
[self setFont:decodedFont];
|
||||
|
||||
[self setString:[_textStorage string]];
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
@import "CPView.j"
|
||||
@import "CPTextView.j"
|
||||
@@ -28,6 +27,7 @@
|
||||
{
|
||||
CPArray _headers;
|
||||
CPArray _rows;
|
||||
BOOL _isResizing;
|
||||
}
|
||||
|
||||
- (id)initWithHeaders:(CPArray)headers rows:(CPArray)rows
|
||||
@@ -42,30 +42,41 @@
|
||||
{
|
||||
_headers = headers;
|
||||
_rows = rows;
|
||||
_isResizing = NO;
|
||||
|
||||
var numCols = [headers count];
|
||||
[self _rebuildTableWithWidth:totalWidth];
|
||||
}
|
||||
|
||||
if (numCols == 0 && [rows count] > 0)
|
||||
numCols = [[rows objectAtIndex:0] count];
|
||||
return self;
|
||||
}
|
||||
|
||||
// Apply borders on the outer left and top container edges
|
||||
if (self._DOMElement) {
|
||||
self._DOMElement.style.borderTop = "1px solid #e0e0e0";
|
||||
self._DOMElement.style.borderLeft = "1px solid #e0e0e0";
|
||||
- (void)_rebuildTableWithWidth:(float)totalWidth
|
||||
{
|
||||
var numCols = _headers ? [_headers count] : 0;
|
||||
|
||||
if (numCols == 0 && _rows && [_rows count] > 0)
|
||||
numCols = [[_rows objectAtIndex:0] count];
|
||||
|
||||
// Apply borders on the outer left and top container edges
|
||||
if (self._DOMElement) {
|
||||
self._DOMElement.style.borderTop = "1px solid #e0e0e0";
|
||||
self._DOMElement.style.borderLeft = "1px solid #e0e0e0";
|
||||
self._DOMElement.style.boxSizing = "border-box";
|
||||
}
|
||||
|
||||
// Initialize header cells
|
||||
if (_headers && [_headers count] > 0) {
|
||||
for (var c = 0; c < numCols; c++) {
|
||||
var headerText = [_headers objectAtIndex:c];
|
||||
var cellView = [self createCellWithText:headerText frame:CGRectMakeZero() isHeader:YES];
|
||||
[self addSubview:cellView];
|
||||
}
|
||||
|
||||
// Initialize header cells
|
||||
if ([headers count] > 0) {
|
||||
for (var c = 0; c < numCols; c++) {
|
||||
var headerText = [headers objectAtIndex:c];
|
||||
var cellView = [self createCellWithText:headerText frame:CGRectMakeZero() isHeader:YES];
|
||||
[self addSubview:cellView];
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize body rows
|
||||
for (var r = 0; r < [rows count]; r++) {
|
||||
var rowData = [rows objectAtIndex:r];
|
||||
}
|
||||
|
||||
// Initialize body rows
|
||||
if (_rows) {
|
||||
for (var r = 0; r < [_rows count]; r++) {
|
||||
var rowData = [_rows objectAtIndex:r];
|
||||
for (var c = 0; c < numCols; c++) {
|
||||
var cellText = @"";
|
||||
|
||||
@@ -76,10 +87,53 @@
|
||||
[self addSubview:cellView];
|
||||
}
|
||||
}
|
||||
|
||||
[self resizeToWidth:totalWidth];
|
||||
}
|
||||
return self;
|
||||
|
||||
[self resizeToWidth:totalWidth];
|
||||
|
||||
// Trigger a parent layout manager re-layout once the table is fully reconstructed and sized.
|
||||
var textView = [self superview];
|
||||
if (textView && [textView isKindOfClass:[CPTextView class]])
|
||||
{
|
||||
var layoutManager = [textView layoutManager];
|
||||
if (layoutManager)
|
||||
{
|
||||
var charRange = [self _findCharacterRangeInLayoutManager:layoutManager];
|
||||
if (charRange && charRange.location !== CPNotFound)
|
||||
{
|
||||
[layoutManager invalidateLayoutForCharacterRange:charRange isSoft:NO actualCharacterRange:nil];
|
||||
[layoutManager invalidateDisplayForGlyphRange:charRange];
|
||||
[layoutManager _validateLayoutAndGlyphs];
|
||||
[textView sizeToFit];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CPRange)_findCharacterRangeInLayoutManager:(CPLayoutManager)layoutManager
|
||||
{
|
||||
var lineFragments = layoutManager._lineFragments;
|
||||
if (lineFragments)
|
||||
{
|
||||
var l = lineFragments.length;
|
||||
for (var i = 0; i < l; i++)
|
||||
{
|
||||
var fragment = lineFragments[i];
|
||||
var runs = fragment._runs;
|
||||
if (runs)
|
||||
{
|
||||
var rc = runs.length;
|
||||
for (var j = 0; j < rc; j++)
|
||||
{
|
||||
if (runs[j].view === self)
|
||||
{
|
||||
return runs[j]._range;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return CPMakeRange(CPNotFound, 0);
|
||||
}
|
||||
|
||||
- (CPArray)headers
|
||||
@@ -94,6 +148,7 @@
|
||||
|
||||
- (CPView)viewForWidth:(float)width
|
||||
{
|
||||
// Always call resize to ensure cell views are constructed, but do not guard here
|
||||
[self resizeToWidth:width];
|
||||
return self;
|
||||
}
|
||||
@@ -110,7 +165,9 @@
|
||||
var borderView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, initialWidth, initialHeight)];
|
||||
[borderView setBackgroundColor:[CPColor clearColor]];
|
||||
[borderView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
if (borderView._DOMElement) {
|
||||
|
||||
if (borderView._DOMElement)
|
||||
{
|
||||
borderView._DOMElement.style.borderBottom = "1px solid #e0e0e0";
|
||||
borderView._DOMElement.style.borderRight = "1px solid #e0e0e0";
|
||||
borderView._DOMElement.style.boxSizing = "border-box";
|
||||
@@ -119,29 +176,18 @@
|
||||
|
||||
var textContainer = [[CPTextContainer alloc] initWithContainerSize:CGSizeMake(initialWidth - 8, 1e7)];
|
||||
var textView = [[CPTextView alloc] initWithFrame:CGRectMake(4, 2, initialWidth - 8, initialHeight - 4) textContainer:textContainer];
|
||||
[textView setEditable:NO];
|
||||
[textView setEditable:YES];
|
||||
[textView setSelectable:YES];
|
||||
[textView setBackgroundColor:[CPColor clearColor]];
|
||||
[textView setVerticallyResizable:YES];
|
||||
[textView setHorizontallyResizable:NO];
|
||||
[[textView textContainer] setWidthTracksTextView:YES];
|
||||
|
||||
// Construct cell text using standard fonts
|
||||
// Configure cell text style using standard CPTextView APIs
|
||||
var cellFont = isHeader ? [CPFont boldSystemFontOfSize:11.0] : [CPFont systemFontOfSize:11.0];
|
||||
var parsedText = [[CPAttributedString alloc] initWithString:text attributes:@{
|
||||
CPFontAttributeName: cellFont,
|
||||
CPForegroundColorAttributeName: [CPColor blackColor]
|
||||
}];
|
||||
|
||||
var storage = [textView textStorage];
|
||||
if (storage && [storage respondsToSelector:@selector(setAttributedString:)]) {
|
||||
[storage setAttributedString:parsedText];
|
||||
} else {
|
||||
[textView setEditable:YES];
|
||||
[textView setString:@""];
|
||||
[textView insertText:parsedText];
|
||||
[textView setEditable:NO];
|
||||
}
|
||||
[textView setFont:cellFont];
|
||||
[textView setTextColor:[CPColor blackColor]];
|
||||
[textView setString:text];
|
||||
|
||||
[cellContainer addSubview:textView];
|
||||
return cellContainer;
|
||||
@@ -161,11 +207,19 @@
|
||||
|
||||
- (void)resizeToWidth:(float)newWidth
|
||||
{
|
||||
var numCols = [_headers count];
|
||||
if (numCols == 0 && [_rows count] > 0) {
|
||||
if (_isResizing)
|
||||
return;
|
||||
|
||||
_isResizing = YES;
|
||||
|
||||
var numCols = _headers ? [_headers count] : 0;
|
||||
if (numCols == 0 && _rows && [_rows count] > 0) {
|
||||
numCols = [[_rows objectAtIndex:0] count];
|
||||
}
|
||||
if (numCols == 0) return;
|
||||
if (numCols == 0) {
|
||||
_isResizing = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
var subviews = [self subviews];
|
||||
var colNaturalWidths = [];
|
||||
@@ -206,18 +260,22 @@
|
||||
}
|
||||
};
|
||||
|
||||
for (var c = 0; c < [_headers count]; c++) {
|
||||
measureCell([_headers objectAtIndex:c], YES, c);
|
||||
if (_headers) {
|
||||
for (var c = 0; c < [_headers count]; c++) {
|
||||
measureCell([_headers objectAtIndex:c], YES, c);
|
||||
}
|
||||
}
|
||||
|
||||
for (var r = 0; r < [_rows count]; r++) {
|
||||
var rowData = [_rows objectAtIndex:r];
|
||||
for (var c = 0; c < numCols; c++) {
|
||||
var cellText = @"";
|
||||
if (c < [rowData count]) {
|
||||
cellText = [rowData objectAtIndex:c];
|
||||
if (_rows) {
|
||||
for (var r = 0; r < [_rows count]; r++) {
|
||||
var rowData = [_rows objectAtIndex:r];
|
||||
for (var c = 0; c < numCols; c++) {
|
||||
var cellText = @"";
|
||||
if (c < [rowData count]) {
|
||||
cellText = [rowData objectAtIndex:c];
|
||||
}
|
||||
measureCell(cellText, NO, c);
|
||||
}
|
||||
measureCell(cellText, NO, c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,8 +337,9 @@
|
||||
var targetWidth = Math.max(10.0, colWidths[c] - 8);
|
||||
[[textView textContainer] setContainerSize:CGSizeMake(targetWidth, 1e7)];
|
||||
|
||||
var usedRect = [[textView layoutManager] usedRectForTextContainer:[textView textContainer]];
|
||||
var wrappedHeight = CGRectGetHeight(usedRect) + 12.0;
|
||||
var layoutManager = [textView layoutManager];
|
||||
var usedRect = layoutManager ? [layoutManager usedRectForTextContainer:[textView textContainer]] : nil;
|
||||
var wrappedHeight = (usedRect ? CGRectGetHeight(usedRect) : 0.0) + 12.0;
|
||||
if (wrappedHeight > maxCellHeight) {
|
||||
maxCellHeight = wrappedHeight;
|
||||
}
|
||||
@@ -314,19 +373,29 @@
|
||||
return maxCellHeight;
|
||||
};
|
||||
|
||||
if ([_headers count] > 0) {
|
||||
if (_headers && [_headers count] > 0) {
|
||||
var headerHeight = layoutRow(cellIndex);
|
||||
cellIndex += numCols;
|
||||
currentY += headerHeight;
|
||||
}
|
||||
|
||||
for (var r = 0; r < [_rows count]; r++) {
|
||||
var rowHeight = layoutRow(cellIndex);
|
||||
cellIndex += numCols;
|
||||
currentY += rowHeight;
|
||||
if (_rows) {
|
||||
for (var r = 0; r < [_rows count]; r++) {
|
||||
var rowHeight = layoutRow(cellIndex);
|
||||
cellIndex += numCols;
|
||||
currentY += rowHeight;
|
||||
}
|
||||
}
|
||||
|
||||
[self setFrameSize:CGSizeMake(newWidth, currentY)];
|
||||
// GUARD FRAME SIZE MUTATIONS: Only execute setFrameSize if dimensions actually change.
|
||||
// This stops infinite layout passes while ensuring subviews are laid out.
|
||||
var currentSize = [self frame].size;
|
||||
if (ABS(currentSize.width - newWidth) > 0.1 || ABS(currentSize.height - currentY) > 0.1)
|
||||
{
|
||||
[self setFrameSize:CGSizeMake(newWidth, currentY)];
|
||||
}
|
||||
|
||||
_isResizing = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
@import <AppKit/CPTextField.j>
|
||||
@import <AppKit/CPScrollView.j>
|
||||
@import <AppKit/CPParagraphStyle.j>
|
||||
@import <AppKit/CPTextStorage.j>
|
||||
@import <AppKit/_CPTableTextAttachment.j>
|
||||
|
||||
@implementation AppController : CPObject
|
||||
{
|
||||
@@ -90,6 +92,25 @@
|
||||
[_textView insertText:@" "];
|
||||
}
|
||||
|
||||
- (void)insertTable:(id)sender
|
||||
{
|
||||
var headers = [@"Item Description", @"Quantity", @"Unit Price"];
|
||||
var rows = [
|
||||
[@"Cappuccino Web Framework Lic.", @"2", @"$199.00"],
|
||||
[@"Objective-J Development Support", @"5", @"$150.00"],
|
||||
[@"Cloud Compilation VM Server", @"1", @"$49.00"]
|
||||
];
|
||||
|
||||
var tableAttachment = [[_CPTableTextAttachment alloc] initWithHeaders:headers rows:rows width:500.0];
|
||||
|
||||
// Insert single-character atomic text attachment
|
||||
var tableAttrStr = [CPTextStorage attributedStringWithAttachment:tableAttachment];
|
||||
|
||||
[_textView insertText:@"\ntable (own line)\n"];
|
||||
[_textView insertText:tableAttrStr];
|
||||
[_textView insertText:@"\nend table"];
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
|
||||
{
|
||||
var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
|
||||
@@ -145,6 +166,14 @@
|
||||
[toolbarView addSubview:attachButton];
|
||||
currentX += 150;
|
||||
|
||||
// Add Table Trigger
|
||||
var tableButton = [[CPButton alloc] initWithFrame:CGRectMake(currentX, 15, 100, 30)];
|
||||
[tableButton setTitle:@"Add Table"];
|
||||
[tableButton setTarget:self];
|
||||
[tableButton setAction:@selector(insertTable:)];
|
||||
[toolbarView addSubview:tableButton];
|
||||
currentX += 110;
|
||||
|
||||
// Text Alignment Group
|
||||
var labelAlign = [[CPTextField alloc] initWithFrame:CGRectMake(currentX, 22, 45, 20)];
|
||||
[labelAlign setStringValue:@"Align:"];
|
||||
@@ -210,9 +239,10 @@
|
||||
[leftLabel setAutoresizingMask:CPViewWidthSizable];
|
||||
[leftContainer addSubview:leftLabel];
|
||||
|
||||
_textView = [[CPTextView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([leftContainer bounds]) - 30, CGRectGetHeight([leftContainer bounds]) - 70)];
|
||||
_textView = [[CPTextView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([leftContainer bounds]) - 30, 0)];
|
||||
[_textView setRichText:YES];
|
||||
[_textView setBackgroundColor:[CPColor whiteColor]];
|
||||
[[_textView textContainer] setWidthTracksTextView:YES];
|
||||
|
||||
_scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(15, 40, CGRectGetWidth([leftContainer bounds]) - 30, CGRectGetHeight([leftContainer bounds]) - 65)];
|
||||
[_scrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
@@ -321,6 +351,8 @@
|
||||
[_textView insertText:[[CPAttributedString alloc] initWithString:@"This paragraph has a first-line indent of 30pt, a head indent of 50pt, and a tail indent of -30pt. Check the horizontal ruler above to see how the indent markers align with this paragraph, and adjust them directly!\n"
|
||||
attributes:[CPDictionary dictionaryWithObject:indentParagraph forKey:CPParagraphStyleAttributeName]]];
|
||||
|
||||
[_textView insertText:@"\n"];
|
||||
|
||||
[theWindow orderFront:self];
|
||||
[CPMenu setMenuBarVisible:YES];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user