SynEdit: Markup and more paint-token-breaker for future bidi support

git-svn-id: trunk@38996 -
This commit is contained in:
martin 2012-10-08 11:16:06 +00:00
parent b8dc7e13eb
commit 3bb19f7f9a
13 changed files with 147 additions and 84 deletions

View File

@ -50,7 +50,7 @@ type
FCurTxtLineIdx : Integer;
FCurViewToken: TLazSynDisplayTokenInfoEx;
FCurViewTokenViewPhysStart: Integer;
FCurViewTokenViewPhysStart: TLazSynDisplayTokenBound;
FCurViewinRTL: Boolean;
FCurViewRtlPhysStart: integer;
FCurViewRtlPhysEnd: integer;
@ -377,8 +377,8 @@ begin
FCurMarkupNextRtlInfo := ATokenInfo.NextRtlInfo;
FMarkupTokenAttr.Assign(ATokenInfo.Attr);
FMarkupTokenAttr.CurrentStartX := ATokenInfo.StartPos.Physical; // current sub-token
FMarkupTokenAttr.CurrentEndX := ATokenInfo.EndPos.Physical-1;
FMarkupTokenAttr.CurrentStartX := ATokenInfo.StartPos; // current sub-token
FMarkupTokenAttr.CurrentEndX := ATokenInfo.EndPos;
end;
fMarkupManager.MergeMarkupAttributeAtRowCol(FCurTxtLineIdx + 1,
@ -402,8 +402,10 @@ end;
function TLazSynPaintTokenBreaker.GetNextHighlighterTokenFromView(out
ATokenInfo: TLazSynDisplayTokenInfoEx; APhysEnd: Integer; ALogEnd: Integer): Boolean;
procedure InitSynAttr(var ATarget: TSynSelectedColor; ASource: TSynHighlighterAttributes;
AnAttrStartX: Integer);
procedure InitSynAttr(var ATarget: TSynSelectedColor; const ASource: TSynHighlighterAttributes;
const AnAttrStartX: TLazSynDisplayTokenBound);
const
NoEnd: TLazSynDisplayTokenBound = (Physical: -1; Logical: -1; Offset: 0);
begin
ATarget.Clear;
if Assigned(ASource) then begin
@ -420,15 +422,15 @@ function TLazSynPaintTokenBreaker.GetNextHighlighterTokenFromView(out
end;
ATarget.MergeFinalStyle := True;
ATarget.StyleMask := [];
FCurViewToken.Attr.StartX := AnAttrStartX;
ATarget.EndX := -1; //PhysicalStartPos + TokenCharLen - 1;
ATarget.StartX := AnAttrStartX;
ATarget.EndX := NoEnd;
end;
function MaybeFetchToken: Boolean; inline;
begin
Result := FCurViewToken.Tk.TokenLength > 0;
if Result or (FCurViewToken.Tk.TokenLength < 0) then exit;
FCurViewTokenViewPhysStart := FCurViewToken.PhysicalCharStart;
FCurViewTokenViewPhysStart := FCurViewToken.StartPos;
while FCurViewToken.Tk.TokenLength = 0 do begin // Todo: is SyncroEd-test a zero size token is returned
Result := FDisplayView.GetNextHighlighterToken(FCurViewToken.Tk);
if not Result then begin
@ -723,7 +725,7 @@ begin
InitSynAttr(FCurViewToken.Attr, FCurViewToken.Tk.TokenAttr, FCurViewTokenViewPhysStart);
if FCurViewToken.Tk.TokenLength = 0 then
ATokenInfo.Attr.EndX := PhysPos-1;
ATokenInfo.Attr.EndX := ATokenInfo.EndPos; // PhysPos-1;
MaybeFetchToken;
if MaybeChangeToRtl(LogicIdx, LogicEnd) then begin // get NextTokenPhysStart
@ -841,7 +843,7 @@ begin
InitSynAttr(FCurViewToken.Attr, FCurViewToken.Tk.TokenAttr, FCurViewTokenViewPhysStart);
if FCurViewToken.Tk.TokenLength = 0 then
ATokenInfo.Attr.EndX := PhysPos-1;
ATokenInfo.Attr.EndX := ATokenInfo.EndPos; // PhysPos-1;
MaybeFetchToken;
SkipRtlOffScreen(LogicIdx, LogicEnd);

View File

@ -30,12 +30,6 @@ uses
SynEditMiscClasses, Controls, SynEditHighlighter, LCLProc;
type
TLazSynDisplayTokenBound = record
Physical: Integer; // 1 based - May be in middle of char
Logical: Integer; // 1 based
Offset: Integer; // default 0. MultiWidth (e.g. Tab), if token starts in the middle of char
end;
TLazSynDisplayRtlInfo = record
IsRtl: Boolean;
PhysLeft: integer; // 1-based
@ -392,7 +386,7 @@ var
begin
c := GetMarkupAttributeAtRowCol(aRow, aStartCol, AnRtlInfo);
if assigned(c) then
AMarkup.Merge(c, aStartCol.Physical, AEndCol.Physical - 1);
AMarkup.Merge(c, aStartCol, AEndCol);
end;
procedure TSynEditMarkup.TextChanged(aFirstCodeLine, aLastCodeLine: Integer);

View File

@ -234,8 +234,7 @@ begin
or ((FBracketHighlightAntiPos.y = aRow) and (FBracketHighlightAntiPos.x = aStartCol.Logical))
then begin
Result := MarkupInfo;
MarkupInfo.StartX := aStartCol.Logical;
MarkupInfo.EndX := aStartCol.Logical;
MarkupInfo.SetFrameBoundsLog(aStartCol.Logical, aStartCol.Logical + 1); // bracket is alvays 1 byte
end;
end;

View File

@ -237,8 +237,7 @@ begin
if (aRow = FCtrlMouseLine) and FCtrlLinkable then begin
FCurX1 := LogicalToPhysicalPos(Point(FCtrlMouseX1, FCtrlMouseLine)).x;
FCurX2 := LogicalToPhysicalPos(Point(FCtrlMouseX2, FCtrlMouseLine)).x;
MarkupInfo.StartX := FCurX1;
MarkupInfo.EndX := FCurX2;
MarkupInfo.SetFrameBoundsPhys(FCurX1, FCurX2);
end;
end;
@ -250,8 +249,7 @@ begin
((aStartCol.Physical < FCurX1) or (aStartCol.Physical >= FCurX2))
then exit;
Result := MarkupInfo;
MarkupInfo.StartX := FCurX1;
MarkupInfo.EndX := FCurX2;
MarkupInfo.SetFrameBoundsPhys(FCurX1, FCurX2);
end;
procedure TSynEditMarkupCtrlMouseLink.GetNextMarkupColAfterRowCol(const aRow: Integer;

View File

@ -138,8 +138,7 @@ begin
( (FRowData[i].Priority < FoundPri) or (i = 0) )
then begin
Result := FRowData[i].Markup;
Result.StartX := FRowData[i].StartX;
Result.EndX := FRowData[i].EndX-1;
MarkupInfo.SetFrameBoundsPhys(FRowData[i].StartX, FRowData[i].EndX);
FoundPri := FRowData[i].Priority;
end;
end;

View File

@ -480,7 +480,7 @@ end;
function TSynEditMarkupHighlightAll.GetMarkupAttributeAtRowCol(const aRow: Integer;
const aStartCol: TLazSynDisplayTokenBound; const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
var
Pos: Integer;
Pos, s, e: Integer;
begin
result := nil;
if (fSearchString = '') then
@ -502,13 +502,14 @@ begin
//debugLN('+++>MARUP *ON* ',dbgs(@result),' / ',dbgs(ARow), ' at index ', dbgs(Pos));
if fMatches.Point[Pos-1].y < aRow then
MarkupInfo.StartX := -1
s := -1
else
MarkupInfo.StartX := fMatches.Point[Pos-1].x;
s := fMatches.Point[Pos-1].x;
if fMatches.Point[Pos].y > aRow then
MarkupInfo.EndX := -1
e := -1
else
MarkupInfo.EndX := fMatches.Point[Pos].x-1;
e := fMatches.Point[Pos].x;
MarkupInfo.SetFrameBoundsPhys(s, e);
result := MarkupInfo;
end;

View File

@ -138,8 +138,7 @@ begin
end;
end;
end;
MarkupInfo.StartX := nSelStart;
MarkupInfo.EndX := nSelEnd-1;
MarkupInfo.SetFrameBoundsPhys(nSelStart, nSelEnd);
end;
function TSynEditMarkupSelection.GetMarkupAttributeAtRowCol(const aRow: Integer;

View File

@ -114,8 +114,7 @@ begin
if (aStartCol.Physical >= FCurStart) and (aStartCol.Physical < FCurEnd) then begin
Result := MarkupInfo;
Result.StartX := FCurStart;
Result.EndX := FCurEnd - 1;
MarkupInfo.SetFrameBoundsPhys(FCurStart, FCurEnd);
end;
end;

View File

@ -180,8 +180,7 @@ function TSynEditMarkupSpecialLine.GetMarkupAttributeAtRowCol(const aRow: Intege
const aStartCol: TLazSynDisplayTokenBound; const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
begin
Result := nil;
MarkupInfo.StartX := 1;
MarkupInfo.EndX := MaxInt;
MarkupInfo.SetFrameBoundsPhys(1, MaxInt);
if FSpecialLine then
Result := MarkupInfo;
end;

View File

@ -400,24 +400,21 @@ begin
(aStartCol.Physical >= FHighlightPos1.x) and (aStartCol.Physical < FHighlightPos1.X2) then
begin
Result := MarkupInfo;
MarkupInfo.StartX := FHighlightPos1.x;
MarkupInfo.EndX := FHighlightPos1.X2 - 1;
MarkupInfo.SetFrameBoundsPhys(FHighlightPos1.x, FHighlightPos1.x2);
end
else
if (FHighlightPos3.y = aRow) and
(aStartCol.Physical >= FHighlightPos3.x) and (aStartCol.Physical < FHighlightPos3.X2) then
begin
Result := MarkupInfo;
MarkupInfo.StartX := FHighlightPos3.x;
MarkupInfo.EndX := FHighlightPos3.X2 - 1;
MarkupInfo.SetFrameBoundsPhys(FHighlightPos3.x, FHighlightPos3.x2);
end
else
if (FHighlightPos2.y = aRow) and
(aStartCol.Physical >= FHighlightPos2.x) and (aStartCol.Physical < FHighlightPos2.X2) then
begin
Result := MarkupInfo;
MarkupInfo.StartX := FHighlightPos2.x;
MarkupInfo.EndX := FHighlightPos2.X2 - 1;
MarkupInfo.SetFrameBoundsPhys(FHighlightPos2.x, FHighlightPos2.x2);
end;
end;

View File

@ -201,15 +201,21 @@ type
TSynObjectListItemClass = class of TSynObjectListItem;
TLazSynDisplayTokenBound = record
Physical: Integer; // 1 based - May be in middle of char
Logical: Integer; // 1 based
Offset: Integer; // default 0. MultiWidth (e.g. Tab), if token starts in the middle of char
end;
{ TSynSelectedColor }
TSynSelectedColor = class(TLazSynCustomTextAttributes)
private
FCurrentEndX: Integer;
FCurrentStartX: Integer;
FCurrentStartX: TLazSynDisplayTokenBound;
FCurrentEndX: TLazSynDisplayTokenBound;
FOnChange: TNotifyEvent;
// 0 or -1 start/end before/after line // 1 first char
FStartX, FEndX: Integer;
FStartX, FEndX: TLazSynDisplayTokenBound;
FFrameSidesInitialized: Boolean;
FFrameSideColors: array[TLazSynBorderSide] of TColor;
FFrameSideStyles: array[TLazSynBorderSide] of TSynLineStyle;
@ -219,6 +225,7 @@ type
function GetFrameSideOrigin(Side: TLazSynBorderSide): TSynFrameEdges;
function GetFrameSidePriority(Side: TLazSynBorderSide): integer;
function GetFrameSideStyles(Side: TLazSynBorderSide): TSynLineStyle;
function IsMatching(ABound1, ABound2: TLazSynDisplayTokenBound): Boolean;
protected
procedure DoChange; override;
procedure AssignFrom(Src: TLazSynCustomTextAttributes); override;
@ -229,14 +236,18 @@ type
// but PaintLines creates an instance that contains an actual style (without mask)
// Todo: always start with actual style
MergeFinalStyle: Boolean;
procedure Merge(Other: TSynSelectedColor; LeftCol, RightCol: Integer); deprecated;
procedure MergeFrames(Other: TSynSelectedColor; LeftCol, RightCol: Integer); deprecated;
procedure Merge(Other: TSynSelectedColor; LeftCol, RightCol: TLazSynDisplayTokenBound); deprecated;
procedure MergeFrames(Other: TSynSelectedColor; LeftCol, RightCol: TLazSynDisplayTokenBound); deprecated;
property FrameSideColors[Side: TLazSynBorderSide]: TColor read GetFrameSideColors;
property FrameSideStyles[Side: TLazSynBorderSide]: TSynLineStyle read GetFrameSideStyles;
property StartX: Integer read FStartX write FStartX;
property EndX: Integer read FEndX write FEndX;
property CurrentStartX: Integer read FCurrentStartX write FCurrentStartX;
property CurrentEndX: Integer read FCurrentEndX write FCurrentEndX;
// boundaries of the frame
procedure SetFrameBoundsPhys(AStart, AEnd: Integer);
procedure SetFrameBoundsLog(AStart, AEnd: Integer; AStartOffs: Integer = 0; AEndOffs: Integer = 0);
property StartX: TLazSynDisplayTokenBound read FStartX write FStartX;
property EndX: TLazSynDisplayTokenBound read FEndX write FEndX;
// boundaries for current paint
property CurrentStartX: TLazSynDisplayTokenBound read FCurrentStartX write FCurrentStartX;
property CurrentEndX: TLazSynDisplayTokenBound read FCurrentEndX write FCurrentEndX;
public
constructor Create;
procedure Clear; override;
@ -599,6 +610,7 @@ end;
constructor TSynSelectedColor.Create;
begin
inherited Create;
Clear;
MergeFinalStyle := False;
Background := clHighLight;
Foreground := clHighLightText;
@ -632,8 +644,8 @@ begin
else
if (Side in SynFrameEdgeToSides[FrameEdges]) and (
(Side in [bsTop, bsBottom]) or
( (Side = bsLeft) and (FCurrentStartX = FStartX) ) or
( (Side = bsRight) and (FCurrentEndX = FEndX) )
( (Side = bsLeft) and IsMatching(FCurrentStartX, FStartX) ) or
( (Side = bsRight) and IsMatching(FCurrentEndX, FEndX) )
)
then Result := FrameColor
else Result := clNone;
@ -655,8 +667,8 @@ begin
else
if (Side in SynFrameEdgeToSides[FrameEdges]) and (
(Side in [bsTop, bsBottom]) or
( (Side = bsLeft) and (FCurrentStartX = FStartX) ) or
( (Side = bsRight) and (FCurrentEndX = FEndX) )
( (Side = bsLeft) and IsMatching(FCurrentStartX, FStartX) ) or
( (Side = bsRight) and IsMatching(FCurrentEndX, FEndX) )
)
then Result := FramePriority
else Result := 0;
@ -672,6 +684,16 @@ begin
else Result := slsSolid;
end;
function TSynSelectedColor.IsMatching(ABound1, ABound2: TLazSynDisplayTokenBound): Boolean;
begin
Result := ( (ABound1.Physical > 0) and
(ABound1.Physical = ABound2.Physical)
) or
( (ABound1.Logical > 0) and
(ABound1.Logical = ABound2.Logical) and (ABound1.Offset = ABound2.Offset)
);
end;
procedure TSynSelectedColor.DoChange;
begin
if Assigned(FOnChange) then
@ -700,7 +722,8 @@ begin
Changed; {TODO: only if really changed}
end;
procedure TSynSelectedColor.Merge(Other: TSynSelectedColor; LeftCol, RightCol: Integer);
procedure TSynSelectedColor.Merge(Other: TSynSelectedColor; LeftCol,
RightCol: TLazSynDisplayTokenBound);
var
sKeep, sSet, sClr, sInv, sInvInv: TFontStyles;
j: TFontStyle;
@ -751,7 +774,8 @@ begin
EndUpdate;
end;
procedure TSynSelectedColor.MergeFrames(Other: TSynSelectedColor; LeftCol, RightCol: Integer);
procedure TSynSelectedColor.MergeFrames(Other: TSynSelectedColor; LeftCol,
RightCol: TLazSynDisplayTokenBound);
procedure SetSide(ASide: TLazSynBorderSide; ASrc: TSynSelectedColor);
begin
@ -767,9 +791,9 @@ procedure TSynSelectedColor.MergeFrames(Other: TSynSelectedColor; LeftCol, Right
FFrameSidePriority[ASide] := ASrc.FramePriority;
FFrameSideOrigin[ASide] := ASrc.FrameEdges;
if ASide = bsLeft then
FStartX := ASrc.FStartX;
FStartX := LeftCol; // LeftCol has Phys and log ; // ASrc.FStartX;
if ASide = bsRight then
FEndX := ASrc.FEndX;
FEndX := RightCol; // ASrc.FEndX;
end;
var
@ -792,8 +816,8 @@ begin
case Other.FrameEdges of
sfeAround: begin
// UpdateOnly, frame keeps behind individual sites
if (Other.StartX = LeftCol) then SetSide(bsLeft, Other);
if (Other.EndX = RightCol) then SetSide(bsRight, Other);
if IsMatching(Other.StartX, LeftCol) then SetSide(bsLeft, Other);
if IsMatching(Other.EndX, RightCol) then SetSide(bsRight, Other);
SetSide(bsBottom, Other);
SetSide(bsTop, Other);
//FrameColor := Other.FrameColor;
@ -810,6 +834,27 @@ begin
end;
end;
procedure TSynSelectedColor.SetFrameBoundsPhys(AStart, AEnd: Integer);
begin
FStartX.Physical := AStart;
FEndX.Physical := AEnd;
FStartX.Logical := -1;
FEndX.Logical := -1;
FStartX.Offset := 0;
FEndX.Offset := 0;
end;
procedure TSynSelectedColor.SetFrameBoundsLog(AStart, AEnd: Integer; AStartOffs: Integer;
AEndOffs: Integer);
begin
FStartX.Physical := -1;
FEndX.Physical := -1;
FStartX.Logical := AStart;
FEndX.Logical := AEnd;
FStartX.Offset := AStartOffs;
FEndX.Offset := AEndOffs;
end;
procedure TSynSelectedColor.Clear;
var
i: TLazSynBorderSide;
@ -822,10 +867,18 @@ begin
FFrameSideStyles[i] := slsSolid;
FFrameSideOrigin[i] := sfeNone;
end;
FStartX := -1;
FEndX := -1;
FCurrentStartX := -1;
FCurrentEndX := -1;
FStartX.Physical := -1;
FEndX.Physical := -1;
FStartX.Logical := -1;
FEndX.Logical := -1;
FStartX.Offset := 0;
FEndX.Offset := 0;
FCurrentStartX.Physical := -1;
FCurrentEndX.Physical := -1;
FCurrentStartX.Logical := -1;
FCurrentEndX.Logical := -1;
FCurrentStartX.Offset := 0;
FCurrentEndX.Offset := 0;
EndUpdate;
end;

View File

@ -530,14 +530,14 @@ end;
function TSynPluginSyncronizedEditMarkup.GetMarkupAttributeAtRowCol(const aRow: Integer;
const aStartCol: TLazSynDisplayTokenBound; const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
var
i, col: Integer;
i: Integer;
s, e: Integer;
begin
col := PhysicalToLogicalPos(Point(aStartCol.Physical, aRow)).x;
Result := nil;
for i := FPreparedCellFrom to FPreparedCellTo do begin
if ( ((Cells[i].LogStart.y = aRow) and (Cells[i].LogStart.x <= Col)) or
if ( ((Cells[i].LogStart.y = aRow) and (Cells[i].LogStart.x <= aStartCol.Logical)) or
(Cells[i].LogStart.y < aRow) ) and
( ((Cells[i].LogEnd.y = aRow) and (Cells[i].LogEnd.x > Col)) or
( ((Cells[i].LogEnd.y = aRow) and (Cells[i].LogEnd.x > aStartCol.Logical)) or
(Cells[i].LogEnd.y > aRow) ) and
(Cells[i].Group >= 0) // do not't display negative groups
then begin
@ -549,12 +549,13 @@ begin
else
Result := MarkupInfo;
Result.StartX := LogicalToPhysicalPos(Cells[i].LogStart).x;
s := Cells[i].LogStart.x;
if Cells[i].LogStart.y < aRow then
Result.StartX := -1;
Result.EndX := LogicalToPhysicalPos(Cells[i].LogEnd).x - 1;
s := -1;
e := Cells[i].LogEnd.x;
if Cells[i].LogEnd.y > aRow then
Result.EndX := -1;
e := -1;
Result.SetFrameBoundsLog(s, e);
break;
end;
end;
@ -564,24 +565,21 @@ procedure TSynPluginSyncronizedEditMarkup.GetNextMarkupColAfterRowCol(const aRow
const aStartCol: TLazSynDisplayTokenBound; const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys,
ANextLog: Integer);
var
i, col: Integer;
i: Integer;
begin
col := PhysicalToLogicalPos(Point(aStartCol.Physical, aRow)).x;
ANextLog := -1;
ANextPhys := -1;
for i := FPreparedCellFrom to FPreparedCellTo do begin
if Cells[i].Group < 0 then continue;
if (Cells[i].LogStart.y = aRow) and (Cells[i].LogStart.x > Col) and
( (Cells[i].LogStart.x < ANextPhys) or (ANextPhys < 0) )
if (Cells[i].LogStart.y = aRow) and (Cells[i].LogStart.x > aStartCol.Logical) and
( (Cells[i].LogStart.x < ANextLog) or (ANextLog < 0) )
then
ANextPhys := Cells[i].LogStart.x;
if (Cells[i].LogEnd.y = aRow) and (Cells[i].LogEnd.x > Col) and
( (Cells[i].LogEnd.x < ANextPhys) or (ANextPhys < 0) )
ANextLog := Cells[i].LogStart.x;
if (Cells[i].LogEnd.y = aRow) and (Cells[i].LogEnd.x > aStartCol.Logical) and
( (Cells[i].LogEnd.x < ANextLog) or (ANextLog < 0) )
then
ANextPhys := Cells[i].LogEnd.x;
ANextLog := Cells[i].LogEnd.x;
end;
if ANextPhys >= 0 then
ANextPhys := LogicalToPhysicalPos(Point(ANextPhys, aRow)).x;
end;
procedure TSynPluginSyncronizedEditMarkup.PrepareMarkupForRow(aRow: Integer);

View File

@ -7,7 +7,8 @@ interface
uses
Classes, SysUtils, fpcunit, testregistry, TestBase, LazSynTextArea, SynEditTypes,
SynEditMarkupBracket, SynEdit, SynHighlighterPosition, SynEditMarkup, Graphics, Forms;
SynEditMarkupBracket, SynEdit, SynHighlighterPosition, SynEditMarkup, SynEditMiscClasses,
Graphics, Forms;
type
@ -89,6 +90,7 @@ begin
'ABشس يCD',
#9'i'#9'12'#9,
'ي'#9'شس', // 15
#9'('#9')',
''
]);
end;
@ -727,6 +729,29 @@ type
TestEnd;
{%endregion}
{%region}
SynEdit.Options :=SynEdit.Options + [eoBracketHighlight];
SynEdit.BracketHighlightStyle := sbhsBoth;
SynEdit.BracketMatchColor.Foreground := clMaroon;
SynEdit.BracketMatchColor.FrameColor := clBlack;
SynEdit.BracketMatchColor.FrameEdges := sfeAround;
SynEdit.BracketMatchColor.FrameStyle := slsSolid;
SynEdit.LogicalCaretXY := point(2, 16);
TestStart('Scan full line / hl-token', 16, 1, 10, 16);
TestNext([], b( 1, 1, 0), b( 5, 2, 0), 1, 5, 1, 5, False, #9, clBlack);
AssertEquals(Name + ' #9 No-Frame l', clNone, Token.Attr.FrameSideColors[bsLeft]);
AssertEquals(Name + ' #9 No-Frame r', clNone, Token.Attr.FrameSideColors[bsRight]);
TestNext([], b( 5, 2, 0), b( 6, 3, 0), 5, 6, 5, 6, False, '(', clMaroon);
AssertEquals(Name + ' "(" Frame t', clBlack, Token.Attr.FrameSideColors[bsTop]);
AssertEquals(Name + ' "(" Frame l', clBlack, Token.Attr.FrameSideColors[bsLeft]);
AssertEquals(Name + ' "(" Frame r', clBlack, Token.Attr.FrameSideColors[bsRight]);
TestNext([], b( 6, 3, 0), b( 9, 4, 0), 6, 9, 6, 9, False, #9, clBlack);
{%endregion}
{%endregion}
{%region RTL only }