mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-20 01:13:29 +00:00
fixed: table sizing
This commit is contained in:
@@ -1076,6 +1076,25 @@ var kRgsymRtf = {
|
||||
if (numCols == 0 && [rows count] > 0) {
|
||||
numCols = [[rows objectAtIndex:0] count];
|
||||
}
|
||||
|
||||
// Convert raw header strings into CPAttributedStrings
|
||||
var parsedHeaders = [CPMutableArray array];
|
||||
for (var c = 0; c < [headers count]; c++) {
|
||||
var cellText = [headers objectAtIndex:c];
|
||||
[parsedHeaders addObject:[self parseTableCellMarkdown:cellText isHeader:YES]];
|
||||
}
|
||||
|
||||
// Convert raw row strings into CPAttributedStrings
|
||||
var parsedRows = [CPMutableArray array];
|
||||
for (var r = 0; r < [rows count]; r++) {
|
||||
var rowData = [rows objectAtIndex:r];
|
||||
var parsedRow = [CPMutableArray array];
|
||||
for (var c = 0; c < [rowData count]; c++) {
|
||||
var cellText = [rowData objectAtIndex:c];
|
||||
[parsedRow addObject:[self parseTableCellMarkdown:cellText isHeader:NO]];
|
||||
}
|
||||
[parsedRows addObject:parsedRow];
|
||||
}
|
||||
|
||||
var totalNaturalW = 0.0;
|
||||
var colNaturalWidths = [];
|
||||
@@ -1085,18 +1104,18 @@ var kRgsymRtf = {
|
||||
for (var c = 0; c < numCols; c++) {
|
||||
var cellW = 80.0;
|
||||
|
||||
if (c < headers.length) {
|
||||
var parsedText = [self parseInlineMarkdown:headers[c] isHeader:YES headerLevel:3];
|
||||
[measureTextField setStringValue:[parsedText string]];
|
||||
if (c < [parsedHeaders count]) {
|
||||
var parsedText = [parsedHeaders objectAtIndex:c];
|
||||
[measureTextField setStringValue:parsedText._string];
|
||||
[measureTextField sizeToFit];
|
||||
cellW = Math.max(cellW, CGRectGetWidth([measureTextField frame]) + 24.0);
|
||||
}
|
||||
|
||||
for (var r = 0; r < [rows count]; r++) {
|
||||
var rowData = [rows objectAtIndex:r];
|
||||
for (var r = 0; r < [parsedRows count]; r++) {
|
||||
var rowData = [parsedRows objectAtIndex:r];
|
||||
if (c < [rowData count]) {
|
||||
var parsedText = [self parseInlineMarkdown:rowData[c] isHeader:NO headerLevel:3];
|
||||
[measureTextField setStringValue:[parsedText string]];
|
||||
var parsedText = [rowData objectAtIndex:c];
|
||||
[measureTextField setStringValue:parsedText._string];
|
||||
[measureTextField sizeToFit];
|
||||
cellW = Math.max(cellW, CGRectGetWidth([measureTextField frame]) + 24.0);
|
||||
}
|
||||
@@ -1105,7 +1124,8 @@ var kRgsymRtf = {
|
||||
totalNaturalW += cellW;
|
||||
}
|
||||
|
||||
var matrixView = [[_CPTableTextAttachment alloc] initWithHeaders:headers rows:rows width:500.0];
|
||||
// Pass the parsed attributed strings to the attachment
|
||||
var matrixView = [[_CPTableTextAttachment alloc] initWithHeaders:parsedHeaders rows:parsedRows width:500.0];
|
||||
|
||||
// Render utilizing the correct atomic attachment character string
|
||||
var tableAttrStr = [CPTextStorage attributedStringWithAttachment:matrixView];
|
||||
@@ -1143,6 +1163,38 @@ var kRgsymRtf = {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parses a single table cell's Markdown, converting HTML breaks and handling list items
|
||||
+ (CPAttributedString)parseTableCellMarkdown:(CPString)cellText isHeader:(BOOL)isHeader
|
||||
{
|
||||
if (!cellText) {
|
||||
return [[CPAttributedString alloc] initWithString:@""];
|
||||
}
|
||||
|
||||
// Convert HTML line breaks to standard newlines
|
||||
var cleanedText = cellText.replace(/<br\s*\/?>/gi, "\n");
|
||||
var lines = cleanedText.split(/\r?\n/);
|
||||
var cellResult = [[CPMutableAttributedString alloc] initWithString:@""];
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i].trim();
|
||||
|
||||
// Convert dash or asterisk lists inside the cell to bullets
|
||||
var listMatch = line.match(/^(\*|-)\s+(.*)$/);
|
||||
if (listMatch) {
|
||||
line = " • " + listMatch[2];
|
||||
}
|
||||
|
||||
var parsedLine = [self parseInlineMarkdown:line isHeader:isHeader headerLevel:3];
|
||||
[cellResult appendAttributedString:parsedLine];
|
||||
|
||||
if (i < lines.length - 1) {
|
||||
[cellResult appendAttributedString:[[CPAttributedString alloc] initWithString:@"\n"]];
|
||||
}
|
||||
}
|
||||
|
||||
return cellResult;
|
||||
}
|
||||
|
||||
+ (BOOL)isTableHeaderLine:(CPString)line
|
||||
{
|
||||
var trimmed = line.trim();
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
- (id)initWithHeaders:(CPArray)headers rows:(CPArray)rows width:(float)totalWidth
|
||||
{
|
||||
return [self initWithHeaders:headers rows:rows width:totalWidth isEditable:YES acceptsRichText:NO];
|
||||
return [self initWithHeaders:headers rows:rows width:totalWidth isEditable:YES acceptsRichText:YES];
|
||||
}
|
||||
|
||||
- (id)initWithHeaders:(CPArray)headers rows:(CPArray)rows width:(float)totalWidth isEditable:(BOOL)isEditable acceptsRichText:(BOOL)acceptsRichText
|
||||
@@ -246,6 +246,10 @@
|
||||
|
||||
var textContainer = [[CPTextContainer alloc] initWithContainerSize:CGSizeMake(initialWidth - 8, 1e7)];
|
||||
var textView = [[CPTextView alloc] initWithFrame:CGRectMake(4, 2, initialWidth - 8, initialHeight - 4) textContainer:textContainer];
|
||||
|
||||
// Set explicit zero margins inside the text view to keep height measurements aligned with usedRect
|
||||
[textView setTextContainerInset:CGSizeMake(0, 0)];
|
||||
|
||||
[textView setEditable:_isEditable];
|
||||
[textView setSelectable:YES];
|
||||
[textView setBackgroundColor:[CPColor clearColor]];
|
||||
@@ -264,21 +268,11 @@
|
||||
[textView setFont:cellFont];
|
||||
[textView setTextColor:[CPColor blackColor]];
|
||||
|
||||
// Populate either rich text or plain text safely
|
||||
if (text && [text isKindOfClass:[CPAttributedString class]])
|
||||
{
|
||||
[[textView textStorage] setAttributedString:text];
|
||||
}
|
||||
else if (text)
|
||||
{
|
||||
[textView setString:String(text)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[textView setString:@""];
|
||||
}
|
||||
|
||||
if (text)
|
||||
[textView insertText:text];
|
||||
|
||||
[cellContainer addSubview:textView];
|
||||
|
||||
return cellContainer;
|
||||
}
|
||||
|
||||
@@ -441,32 +435,55 @@
|
||||
|
||||
for (var c = 0; c < numCols; c++) {
|
||||
var idx = startIndex + c;
|
||||
|
||||
if (idx < [subviews count]) {
|
||||
var cellView = [subviews objectAtIndex:idx];
|
||||
var textView = [self getTextViewFromCell:cellView];
|
||||
if (textView) {
|
||||
|
||||
if (textView)
|
||||
{
|
||||
var targetWidth = Math.max(10.0, colWidths[c] - 8);
|
||||
[[textView textContainer] setContainerSize:CGSizeMake(targetWidth, 1e7)];
|
||||
|
||||
// Update frame size directly so that textContainer auto-resizes.
|
||||
// Set a large temporary height to allow accurate wrapping measurements.
|
||||
[textView setFrameSize:CGSizeMake(targetWidth, 1e7)];
|
||||
|
||||
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;
|
||||
if (layoutManager)
|
||||
{
|
||||
// FORCE LAYOUT RECALCULATION:
|
||||
// Because the width changed, we must force the lazy layout manager
|
||||
// to synchronously calculate glyphs and wraps at the new width.
|
||||
[layoutManager glyphRangeForTextContainer:[textView textContainer]];
|
||||
}
|
||||
|
||||
var usedRect = layoutManager ? [layoutManager usedRectForTextContainer:[textView textContainer]] : nil;
|
||||
var textHeight = usedRect ? CGRectGetHeight(usedRect) : 0.0;
|
||||
|
||||
// Exact height + 8.0px padding (4.0px top, 4.0px bottom margin) inside the cell
|
||||
var wrappedHeight = textHeight + 8.0;
|
||||
|
||||
if (wrappedHeight > maxCellHeight)
|
||||
maxCellHeight = wrappedHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var currentX = 0;
|
||||
for (var c = 0; c < numCols; c++) {
|
||||
|
||||
for (var c = 0; c < numCols; c++)
|
||||
{
|
||||
var idx = startIndex + c;
|
||||
if (idx < [subviews count]) {
|
||||
|
||||
if (idx < [subviews count])
|
||||
{
|
||||
var cellView = [subviews objectAtIndex:idx];
|
||||
[cellView setFrame:CGRectMake(currentX, currentY, colWidths[c], maxCellHeight)];
|
||||
|
||||
var textView = [self getTextViewFromCell:cellView];
|
||||
if (textView) {
|
||||
|
||||
if (textView)
|
||||
{
|
||||
var targetWidth = Math.max(10.0, colWidths[c] - 8);
|
||||
var textY = 4.0;
|
||||
var finalTextViewHeight = maxCellHeight - 8.0;
|
||||
@@ -474,9 +491,9 @@
|
||||
}
|
||||
|
||||
var cellSubviews = [cellView subviews];
|
||||
if ([cellSubviews count] > 0) {
|
||||
|
||||
if ([cellSubviews count] > 0)
|
||||
[[cellSubviews objectAtIndex:0] setFrame:CGRectMake(0, 0, colWidths[c], maxCellHeight)];
|
||||
}
|
||||
}
|
||||
currentX += colWidths[c];
|
||||
}
|
||||
@@ -484,14 +501,17 @@
|
||||
return maxCellHeight;
|
||||
};
|
||||
|
||||
if (currentHeaders && [currentHeaders count] > 0) {
|
||||
if (currentHeaders && [currentHeaders count] > 0)
|
||||
{
|
||||
var headerHeight = layoutRow(cellIndex);
|
||||
cellIndex += numCols;
|
||||
currentY += headerHeight;
|
||||
}
|
||||
|
||||
if (currentRows) {
|
||||
for (var r = 0; r < [currentRows count]; r++) {
|
||||
if (currentRows)
|
||||
{
|
||||
for (var r = 0; r < [currentRows count]; r++)
|
||||
{
|
||||
var rowHeight = layoutRow(cellIndex);
|
||||
cellIndex += numCols;
|
||||
currentY += rowHeight;
|
||||
@@ -500,35 +520,10 @@
|
||||
|
||||
// GUARD FRAME SIZE MUTATIONS
|
||||
var currentSize = [self frame].size;
|
||||
|
||||
if (ABS(currentSize.width - newWidth) > 0.1 || ABS(currentSize.height - currentY) > 0.1)
|
||||
{
|
||||
[self setFrameSize:CGSizeMake(newWidth, currentY)];
|
||||
|
||||
// we need to re-layout the textview here.
|
||||
// but this is not easy as the layout engine is not re-entrant
|
||||
// this does not work: (delay does not matter), layout is always off
|
||||
// setTimeout(function() {
|
||||
// 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];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }, 0);
|
||||
|
||||
}
|
||||
|
||||
_isResizing = NO;
|
||||
|
||||
Reference in New Issue
Block a user