fixed: table sizing

This commit is contained in:
daboe01
2026-06-17 07:58:33 +02:00
parent 50bac0a470
commit 5a3d6e30cb
2 changed files with 110 additions and 63 deletions
+60 -8
View File
@@ -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();
+50 -55
View File
@@ -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;