Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
2026-07-12 Todd White <todd.white@thalion.global>

* Headers/AppKit/NSTextTable.h: Give the NSTextBlockLayer constants
their AppKit values (NSTextBlockPadding is -1, not 0).
* Source/NSTextBlock.m: Index the width arrays by the layer offset from
NSTextBlockPadding so the padding layer maps to the first slot.
* Tests/gui/NSTextTable/basic.m: Check the layer constant values.

2026-07-12 Todd White <todd.white@thalion.global>

* Headers/AppKit/NSTextTable.h: Give the NSTextBlockDimension constants
their AppKit values; the height dimensions were one short because
AppKit leaves the value 3 unused.
* Tests/gui/NSTextTable/basic.m: Check the dimension constant values.

2026-07-12 Todd White <todd.white@thalion.global>

* Tests/gui/NSTextTable/basic.m:
* Tests/gui/NSTextTable/TestInfo:
Add tests for NSTextBlock, NSTextTable and NSTextTableBlock: the
defaults, the dimension, width, colour and vertical alignment
accessors, the table defaults and flags, and the table block position
accessors.

2026-07-12 Todd White <todd.white@thalion.global>

* Tests/gui/NSTextFieldCell/attributes.m:
Expand Down
22 changes: 11 additions & 11 deletions Headers/AppKit/NSTextTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,19 @@ typedef enum _NSTextBlockValueType

typedef enum _NSTextBlockDimension
{
NSTextBlockWidth,
NSTextBlockMinimumWidth,
NSTextBlockMaximumWidth,
NSTextBlockHeight,
NSTextBlockMinimumHeight,
NSTextBlockMaximumHeight
NSTextBlockWidth = 0,
NSTextBlockMinimumWidth = 1,
NSTextBlockMaximumWidth = 2,
NSTextBlockHeight = 4,
NSTextBlockMinimumHeight = 5,
NSTextBlockMaximumHeight = 6
} NSTextBlockDimension;

typedef enum _NSTextBlockLayer
{
NSTextBlockPadding,
NSTextBlockBorder,
NSTextBlockMargin
NSTextBlockPadding = -1,
NSTextBlockBorder = 0,
NSTextBlockMargin = 1
} NSTextBlockLayer;

typedef enum _NSTextBlockVerticalAlignment
Expand All @@ -79,8 +79,8 @@ APPKIT_EXPORT_CLASS
// The following ivars come in pairs
CGFloat _value[NSTextBlockMaximumHeight + 1];
NSTextBlockValueType _valueType[NSTextBlockMaximumHeight + 1];
CGFloat _width[NSTextBlockMargin + 1][NSMaxYEdge + 1];
NSTextBlockValueType _widthType[NSTextBlockMargin + 1][NSMaxYEdge + 1];
CGFloat _width[NSTextBlockMargin - NSTextBlockPadding + 1][NSMaxYEdge + 1];
NSTextBlockValueType _widthType[NSTextBlockMargin - NSTextBlockPadding + 1][NSMaxYEdge + 1];
}

- (NSColor *) backgroundColor;
Expand Down
54 changes: 32 additions & 22 deletions Source/NSTextBlock.m
Original file line number Diff line number Diff line change
Expand Up @@ -162,65 +162,75 @@ - (void) setValue: (CGFloat)val

- (CGFloat) widthForLayer: (NSTextBlockLayer)layer edge: (NSRectEdge)edge
{
if (layer >= sizeof(_width) / sizeof(_width[0]))
NSInteger l = layer - NSTextBlockPadding;

if (l < 0 || l >= (NSInteger)(sizeof(_width) / sizeof(_width[0])))
[NSException raise: NSInvalidArgumentException
format: @"invalid layer %d", layer];
if (edge >= sizeof(_width[0]) / sizeof(_width[0][0]))
[NSException raise: NSInvalidArgumentException
format: @"invalid edge %lu", (unsigned long) edge];
return _width[layer][edge];
return _width[l][edge];
}

- (NSTextBlockValueType) widthValueTypeForLayer: (NSTextBlockLayer)layer
edge: (NSRectEdge)edge
{
if (layer >= sizeof(_width) / sizeof(_width[0]))
NSInteger l = layer - NSTextBlockPadding;

if (l < 0 || l >= (NSInteger)(sizeof(_width) / sizeof(_width[0])))
[NSException raise: NSInvalidArgumentException
format: @"invalid layer %d", layer];
if (edge >= sizeof(_width[0]) / sizeof(_width[0][0]))
[NSException raise: NSInvalidArgumentException
format: @"invalid edge %lu", (unsigned long) edge];
return _widthType[layer][edge];
return _widthType[l][edge];
}

- (void) setWidth: (CGFloat)val
type: (NSTextBlockValueType)type
forLayer: (NSTextBlockLayer)layer
edge: (NSRectEdge)edge
{
if (layer >= sizeof(_width) / sizeof(_width[0]))
NSInteger l = layer - NSTextBlockPadding;

if (l < 0 || l >= (NSInteger)(sizeof(_width) / sizeof(_width[0])))
[NSException raise: NSInvalidArgumentException
format: @"invalid layer %d", layer];
if (edge >= sizeof(_width[0]) / sizeof(_width[0][0]))
[NSException raise: NSInvalidArgumentException
format: @"invalid edge %lu", (unsigned long) edge];
_width[layer][edge] = val;
_widthType[layer][edge] = type;
_width[l][edge] = val;
_widthType[l][edge] = type;
}

- (void) setWidth: (CGFloat)val
type: (NSTextBlockValueType)type
type: (NSTextBlockValueType)type
forLayer: (NSTextBlockLayer)layer
{
if (layer >= sizeof(_width) / sizeof(_width[0]))
NSInteger l = layer - NSTextBlockPadding;

if (l < 0 || l >= (NSInteger)(sizeof(_width) / sizeof(_width[0])))
[NSException raise: NSInvalidArgumentException
format: @"invalid layer %d", layer];
_width[layer][NSMinXEdge] = val;
_widthType[layer][NSMinXEdge] = type;
_width[layer][NSMinYEdge] = val;
_widthType[layer][NSMinYEdge] = type;
_width[layer][NSMaxXEdge] = val;
_widthType[layer][NSMaxXEdge] = type;
_width[layer][NSMaxYEdge] = val;
_widthType[layer][NSMaxYEdge] = type;
_width[l][NSMinXEdge] = val;
_widthType[l][NSMinXEdge] = type;
_width[l][NSMinYEdge] = val;
_widthType[l][NSMinYEdge] = type;
_width[l][NSMaxXEdge] = val;
_widthType[l][NSMaxXEdge] = type;
_width[l][NSMaxYEdge] = val;
_widthType[l][NSMaxYEdge] = type;
}

- (CGFloat) _scaledWidthValue: (NSTextBlockLayer) layer : (NSRectEdge) edge : (NSSize) size
{
if (_widthType[layer][edge] == NSTextBlockAbsoluteValueType)
NSInteger l = layer - NSTextBlockPadding;

if (_widthType[l][edge] == NSTextBlockAbsoluteValueType)
{
// absolute
return _width[layer][edge];
return _width[l][edge];
}
else
{
Expand All @@ -229,13 +239,13 @@ - (CGFloat) _scaledWidthValue: (NSTextBlockLayer) layer : (NSRectEdge) edge : (N
{
case NSMinXEdge:
case NSMaxXEdge:
return _widthType[layer][edge]*size.width;
return _widthType[l][edge]*size.width;
case NSMinYEdge:
case NSMaxYEdge:
return _widthType[layer][edge]*size.height;
return _widthType[l][edge]*size.height;
}
}
return 0.0;
return 0.0;
}

- (NSRect) boundsRectForContentRect: (NSRect)cont
Expand Down
Empty file.
140 changes: 140 additions & 0 deletions Tests/gui/NSTextTable/basic.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/* Coverage for NSTextBlock, NSTextTable and NSTextTableBlock: the defaults,
* the dimension, width, colour and vertical alignment accessors, the table
* defaults and flags, and the table block position accessors. These are
* plain value objects and need no backend.
*/
#include "Testing.h"
#include <math.h>
#include <Foundation/Foundation.h>
#include <AppKit/NSColor.h>
#include <AppKit/NSTextTable.h>

#define EQ(a, b) (fabs((double)(a) - (double)(b)) < 0.001)

int
main(int argc, char **argv)
{
CREATE_AUTORELEASE_POOL(arp);

START_SET("dimension constant values")
PASS(NSTextBlockWidth == 0 && NSTextBlockMinimumWidth == 1
&& NSTextBlockMaximumWidth == 2 && NSTextBlockHeight == 4
&& NSTextBlockMinimumHeight == 5 && NSTextBlockMaximumHeight == 6,
"the NSTextBlockDimension values match AppKit");
PASS(NSTextBlockPadding == -1 && NSTextBlockBorder == 0
&& NSTextBlockMargin == 1,
"the NSTextBlockLayer values match AppKit");
END_SET("dimension constant values")

START_SET("NSTextBlock defaults")
NSTextBlock *b = AUTORELEASE([[NSTextBlock alloc] init]);

PASS([b verticalAlignment] == NSTextBlockTopAlignment,
"the default vertical alignment is top");
PASS(EQ([b valueForDimension: NSTextBlockWidth], 0.0),
"a dimension defaults to zero");
PASS(EQ([b widthForLayer: NSTextBlockBorder edge: NSMinXEdge], 0.0),
"a layer width defaults to zero");
PASS([b backgroundColor] == nil, "the default background colour is nil");
PASS([b borderColorForEdge: NSMinXEdge] == nil,
"the default border colour is nil");
END_SET("NSTextBlock defaults")

START_SET("NSTextBlock dimensions")
NSTextBlock *b = AUTORELEASE([[NSTextBlock alloc] init]);

[b setValue: 50.0 type: NSTextBlockPercentageValueType
forDimension: NSTextBlockMinimumWidth];
PASS(EQ([b valueForDimension: NSTextBlockMinimumWidth], 50.0),
"setValue:type:forDimension: stores the value");
PASS([b valueTypeForDimension: NSTextBlockMinimumWidth]
== NSTextBlockPercentageValueType,
"setValue:type:forDimension: stores the value type");

[b setContentWidth: 100.0 type: NSTextBlockAbsoluteValueType];
PASS(EQ([b contentWidth], 100.0), "setContentWidth: sets the width dimension");
PASS([b contentWidthValueType] == NSTextBlockAbsoluteValueType,
"setContentWidth: sets the width value type");
END_SET("NSTextBlock dimensions")

START_SET("NSTextBlock widths")
NSTextBlock *b = AUTORELEASE([[NSTextBlock alloc] init]);

[b setWidth: 3.0 type: NSTextBlockAbsoluteValueType
forLayer: NSTextBlockBorder edge: NSMaxYEdge];
PASS(EQ([b widthForLayer: NSTextBlockBorder edge: NSMaxYEdge], 3.0),
"setWidth:type:forLayer:edge: stores the width for one edge");
PASS([b widthValueTypeForLayer: NSTextBlockBorder edge: NSMaxYEdge]
== NSTextBlockAbsoluteValueType,
"setWidth:type:forLayer:edge: stores the width type");

[b setWidth: 5.0 type: NSTextBlockAbsoluteValueType
forLayer: NSTextBlockMargin];
PASS(EQ([b widthForLayer: NSTextBlockMargin edge: NSMinXEdge], 5.0)
&& EQ([b widthForLayer: NSTextBlockMargin edge: NSMinYEdge], 5.0)
&& EQ([b widthForLayer: NSTextBlockMargin edge: NSMaxXEdge], 5.0)
&& EQ([b widthForLayer: NSTextBlockMargin edge: NSMaxYEdge], 5.0),
"setWidth:type:forLayer: sets every edge");
END_SET("NSTextBlock widths")

START_SET("NSTextBlock colours and alignment")
NSTextBlock *b = AUTORELEASE([[NSTextBlock alloc] init]);

[b setVerticalAlignment: NSTextBlockMiddleAlignment];
PASS([b verticalAlignment] == NSTextBlockMiddleAlignment,
"setVerticalAlignment: round trips");

[b setBackgroundColor: [NSColor redColor]];
PASS([[b backgroundColor] isEqual: [NSColor redColor]],
"setBackgroundColor: round trips");

[b setBorderColor: [NSColor blueColor] forEdge: NSMinXEdge];
PASS([[b borderColorForEdge: NSMinXEdge] isEqual: [NSColor blueColor]],
"setBorderColor:forEdge: sets one edge");
PASS([b borderColorForEdge: NSMaxXEdge] == nil,
"setBorderColor:forEdge: leaves the other edges alone");

[b setBorderColor: [NSColor greenColor]];
PASS([[b borderColorForEdge: NSMinXEdge] isEqual: [NSColor greenColor]]
&& [[b borderColorForEdge: NSMinYEdge] isEqual: [NSColor greenColor]]
&& [[b borderColorForEdge: NSMaxXEdge] isEqual: [NSColor greenColor]]
&& [[b borderColorForEdge: NSMaxYEdge] isEqual: [NSColor greenColor]],
"setBorderColor: sets every edge");
END_SET("NSTextBlock colours and alignment")

START_SET("NSTextTable defaults and flags")
NSTextTable *t = AUTORELEASE([[NSTextTable alloc] init]);

PASS([t numberOfColumns] == 0, "the default column count is zero");
PASS([t layoutAlgorithm] == NSTextTableAutomaticLayoutAlgorithm,
"the default layout algorithm is automatic");
PASS([t collapsesBorders] == NO, "borders are not collapsed by default");
PASS([t hidesEmptyCells] == NO, "empty cells are not hidden by default");

[t setNumberOfColumns: 3];
PASS([t numberOfColumns] == 3, "setNumberOfColumns: round trips");
[t setLayoutAlgorithm: NSTextTableFixedLayoutAlgorithm];
PASS([t layoutAlgorithm] == NSTextTableFixedLayoutAlgorithm,
"setLayoutAlgorithm: round trips");
[t setCollapsesBorders: YES];
PASS([t collapsesBorders] == YES, "setCollapsesBorders: round trips");
[t setHidesEmptyCells: YES];
PASS([t hidesEmptyCells] == YES, "setHidesEmptyCells: round trips");
END_SET("NSTextTable defaults and flags")

START_SET("NSTextTableBlock position")
NSTextTable *t = AUTORELEASE([[NSTextTable alloc] init]);
NSTextTableBlock *tb = AUTORELEASE([[NSTextTableBlock alloc]
initWithTable: t startingRow: 1 rowSpan: 2
startingColumn: 3 columnSpan: 4]);

PASS([tb startingRow] == 1, "the starting row is stored");
PASS([tb rowSpan] == 2, "the row span is stored");
PASS([tb startingColumn] == 3, "the starting column is stored");
PASS([tb columnSpan] == 4, "the column span is stored");
PASS([tb table] == t, "the table is stored");
END_SET("NSTextTableBlock position")

DESTROY(arp);
return 0;
}
Loading