SynEdit: refactor painting

git-svn-id: trunk@34863 -
This commit is contained in:
martin 2012-01-22 13:17:30 +00:00
parent 4065155e57
commit d90f4b44fe
8 changed files with 178 additions and 86 deletions

View File

@ -5,7 +5,7 @@ unit LazSynTextArea;
interface
uses
Classes, SysUtils, Graphics, LCLType, LCLIntf, LCLProc,
Classes, SysUtils, Graphics, Controls, LCLType, LCLIntf, LCLProc,
SynEditTypes, SynEditMiscProcs, SynEditMiscClasses, LazSynEditText,
SynEditMarkup, SynEditHighlighter, SynTextDrawer;
@ -57,9 +57,10 @@ type
FirstCol, LastCol: integer); virtual;
property Canvas: TCanvas read FCanvas;
public
constructor Create(ATextDrawer: TheTextDrawer);
//constructor Create(AOwner : TSynEditBase; ATextDrawer: TheTextDrawer);
constructor Create(AOwner: TWinControl; ATextDrawer: TheTextDrawer);
destructor Destroy; override;
procedure Assign(Src: TLazSynSurface); override;
procedure InvalidateLines(FirstLine, LastLine: TLineIdx); override;
function ScreenColumnToXValue(Col: integer): integer; // map screen column to screen pixel
function RowColumnToPixels(const RowCol: TPoint): TPoint;
@ -85,6 +86,7 @@ type
property DisplayView: TLazSynDisplayView read FDisplayView write FDisplayView;
property Highlighter: TSynCustomHighlighter read FHighlighter write FHighlighter;
property MarkupManager: TSynEditMarkupManager read FMarkupManager write FMarkupManager;
property TextDrawer: TheTextDrawer read FTextDrawer;
public
property TextBounds: TRect read FTextBounds;
@ -109,7 +111,8 @@ type
procedure DoPaint(ACanvas: TCanvas; AClip: TRect); override;
procedure BoundsChanged; override;
public
constructor Create;
constructor Create(AOwner: TWinControl);
procedure InvalidateLines(FirstLine, LastLine: TLineIdx); override;
property TextArea: TLazSynTextArea read FTextArea write FTextArea;
property LeftGutterArea: TLazSynSurface read FLeftGutterArea write FLeftGutterArea;
property RightGutterArea: TLazSynSurface read FRightGutterArea write FRightGutterArea;
@ -154,12 +157,30 @@ begin
FRightGutterArea.SetBounds(Top, r, Bottom, Right);
end;
constructor TLazSynSurfaceManager.Create;
constructor TLazSynSurfaceManager.Create(AOwner: TWinControl);
begin
inherited Create(AOwner);
FLeftGutterWidth := 0;
FRightGutterWidth := 0;
end;
procedure TLazSynSurfaceManager.InvalidateLines(FirstLine, LastLine: TLineIdx);
var
rcInval: TRect;
begin
rcInval := Bounds;
if (FirstLine >= 0) then
rcInval.Top := TextArea.TextBounds.Top + FirstLine * TextArea.LineHeight;
if (LastLine >= 0) then
rcInval.Bottom := TextArea.TextBounds.Top + LastLine * TextArea.LineHeight;
{$IFDEF VerboseSynEditInvalidate}
DebugLn(['TCustomSynEdit.InvalidateGutterLines ',DbgSName(self), ' FirstLine=',FirstLine, ' LastLine=',LastLine, ' rect=',dbgs(rcInval)]);
{$ENDIF}
if (rcInval.Top < rcInval.Bottom) and (rcInval.Left < rcInval.Right) then
InvalidateRect(Handle, @rcInval, FALSE);
end;
{ TLazSynTextArea }
function TLazSynTextArea.GetPadding(Side: TLazSynBorderSide): integer;
@ -252,10 +273,11 @@ begin
if Result.Y < 0 then Result.Y := 0;
end;
constructor TLazSynTextArea.Create(ATextDrawer: TheTextDrawer);
constructor TLazSynTextArea.Create(AOwner: TWinControl; ATextDrawer: TheTextDrawer);
var
i: TLazSynBorderSide;
begin
inherited Create(AOwner);
FTextDrawer := ATextDrawer;
FTextDrawer.RegisterOnFontChangeHandler(@DoDrawerFontChanged);
FPaintLineColor := TSynSelectedColor.Create;
@ -278,6 +300,53 @@ begin
inherited Destroy;
end;
procedure TLazSynTextArea.Assign(Src: TLazSynSurface);
var
i: TLazSynBorderSide;
begin
inherited Assign(Src);
FTextDrawer := TLazSynTextArea(Src).FTextDrawer;
FTheLinesView := TLazSynTextArea(Src).FTheLinesView;
FDisplayView := TLazSynTextArea(Src).FDisplayView;
FHighlighter := TLazSynTextArea(Src).FHighlighter;
FMarkupManager := TLazSynTextArea(Src).FMarkupManager;
FForegroundColor := TLazSynTextArea(Src).FForegroundColor;
FBackgroundColor := TLazSynTextArea(Src).FBackgroundColor;
FRightEdgeColor := TLazSynTextArea(Src).FRightEdgeColor;
FExtraCharSpacing := TLazSynTextArea(Src).FExtraCharSpacing;
FExtraLineSpacing := TLazSynTextArea(Src).FExtraLineSpacing;
FVisibleSpecialChars := TLazSynTextArea(Src).FVisibleSpecialChars;
FRightEdgeColumn := TLazSynTextArea(Src).FRightEdgeColumn;
FRightEdgeVisible := TLazSynTextArea(Src).FRightEdgeVisible;
for i := low(TLazSynBorderSide) to high(TLazSynBorderSide) do
FPadding[i] := TLazSynTextArea(Src).FPadding[i];
FTopLine := TLazSynTextArea(Src).FTopLine;
FLeftChar := TLazSynTextArea(Src).FLeftChar;
BoundsChanged;
end;
procedure TLazSynTextArea.InvalidateLines(FirstLine, LastLine: TLineIdx);
var
rcInval: TRect;
begin
rcInval := Bounds;
if (FirstLine >= 0) then
rcInval.Top := TextBounds.Top + FirstLine * LineHeight;
if (LastLine >= 0) then
rcInval.Bottom := TextBounds.Top + LastLine * LineHeight;
{$IFDEF VerboseSynEditInvalidate}
DebugLn(['TCustomSynEdit.InvalidateGutterLines ',DbgSName(self), ' FirstLine=',FirstLine, ' LastLine=',LastLine, ' rect=',dbgs(rcInval)]);
{$ENDIF}
if (rcInval.Top < rcInval.Bottom) and (rcInval.Left < rcInval.Right) then
InvalidateRect(Handle, @rcInval, FALSE);
end;
procedure TLazSynTextArea.FontChanged;
begin
// ToDo: wait for handle creation
@ -947,7 +1016,7 @@ var
// Initialize rcLine for drawing. Note that Top and Bottom are updated
// inside the loop. Get only the starting point for this.
rcLine := AClip;
rcLine.Bottom := FirstLine * fTextHeight;
rcLine.Bottom := TextBounds.Top + FirstLine * fTextHeight;
TV := TopLine - 1;

View File

@ -432,9 +432,6 @@ type
FStrings: TStrings; // External TStrings based interface to the Textbuffer
FTopLinesView: TSynEditStrings; // The linesview that holds the real line-buffer/FLines
FDisplayView: TLazSynDisplayView;
FTextArea: TLazSynTextArea;
FLeftGutterArea, FRightGutterArea: TLazSynGutterArea;
FPaintArea: TLazSynSurfaceManager;
fExtraCharSpacing: integer;
fMaxLeftChar: Integer; // 1024
@ -666,8 +663,11 @@ type
protected
{$IFDEF EnableDoubleBuf}
BufferBitmap: TBitmap; // the double buffer
{$ENDIF}
SavedCanvas: TCanvas; // the normal TCustomControl canvas during paint
{$ENDIF}
FTextArea: TLazSynTextArea;
FLeftGutterArea, FRightGutterArea: TLazSynGutterArea;
FPaintArea: TLazSynSurfaceManager;
procedure Paint; override;
procedure StartPaintBuffer(const ClipRect: TRect);
@ -1903,7 +1903,7 @@ begin
FLastMousePoint := Point(-1,-1);
fBlockIndent := 2;
FTextArea := TLazSynTextArea.Create(FTextDrawer);
FTextArea := TLazSynTextArea.Create(Self, FTextDrawer);
FTextArea.RightEdgeVisible := not(eoHideRightMargin in SYNEDIT_DEFAULT_OPTIONS);
FTextArea.ExtraCharSpacing := 0;
FTextArea.ExtraLineSpacing := 0;
@ -1912,14 +1912,15 @@ begin
FTextArea.DisplayView := FDisplayView;
FTextArea.Highlighter := nil;
FLeftGutterArea := TLazSynGutterArea.Create;
FLeftGutterArea := TLazSynGutterArea.Create(Self);
FLeftGutterArea.TextArea := FTextArea;
FLeftGutterArea.Gutter := FLeftGutter;
FRightGutterArea := TLazSynGutterArea.Create;
FRightGutterArea := TLazSynGutterArea.Create(Self);
FRightGutterArea.TextArea := FTextArea;
FRightGutterArea.Gutter := FRightGutter;
FPaintArea := TLazSynSurfaceManager.Create;
FPaintArea := TLazSynSurfaceManager.Create(Self);
FPaintArea.TextArea := FTextArea;
FPaintArea.LeftGutterArea := FLeftGutterArea;
FPaintArea.RightGutterArea := FRightGutterArea;
@ -2146,7 +2147,7 @@ end;
destructor TCustomSynEdit.Destroy;
var
i: integer;
q,i: integer;
begin
Application.RemoveOnIdleHandler(@IdleScanRanges);
SurrenderPrimarySelection;
@ -2466,22 +2467,8 @@ begin
if sfPainting in fStateFlags then exit;
if Visible and HandleAllocated then
if (FirstLine = -1) and (LastLine = -1) then begin
if FLeftGutter.Visible then begin;
rcInval := Rect(0, 0, FLeftGutter.Width, ClientHeight - ScrollBarWidth);
{$IFDEF VerboseSynEditInvalidate}
DebugLn(['TCustomSynEdit.InvalidateGutterLines ',DbgSName(self),' ALL ',dbgs(rcInval)]);
{$ENDIF}
InvalidateRect(Handle, @rcInval, FALSE);
end;
// right gutter
if FRightGutter.Visible then begin
rcInval := Rect(ClientWidth - FRightGutter.Width - ScrollBarWidth, 0,
ClientWidth - ScrollBarWidth, ClientHeight - ScrollBarWidth);
{$IFDEF VerboseSynEditInvalidate}
DebugLn(['TCustomSynEdit.InvalidateGutterLines ',DbgSName(self),' ALL ',dbgs(rcInval)]);
{$ENDIF}
InvalidateRect(Handle, @rcInval, FALSE);
end;
FLeftGutterArea.InvalidateLines(-1, -1);
FRightGutterArea.InvalidateLines(-1, -1);
end else begin
// pretend we haven't scrolled
TopFoldLine := FFoldedLinesView.TopLine;
@ -2498,26 +2485,9 @@ begin
LastLine := LinesInWindow + 1;
FirstLine := RowToScreenRow(FirstLine);
FirstLine := Max(0, FirstLine);
{ any line visible? }
if (LastLine >= FirstLine) then begin
if FLeftGutter.Visible then begin;
rcInval := Rect(0, LineHeight * FirstLine,
FLeftGutter.Width, LineHeight * LastLine);
{$IFDEF VerboseSynEditInvalidate}
DebugLn(['TCustomSynEdit.InvalidateGutterLines ',DbgSName(self),' PART ',dbgs(rcInval)]);
{$ENDIF}
InvalidateRect(Handle, @rcInval, FALSE);
end;
// right gutter
if FRightGutter.Visible then begin
rcInval.Left := ClientWidth - FRightGutter.Width - ScrollBarWidth;
rcInval.Right := ClientWidth - ScrollBarWidth;
{$IFDEF VerboseSynEditInvalidate}
DebugLn(['TCustomSynEdit.InvalidateGutterLines ',DbgSName(self),' PART ',dbgs(rcInval)]);
{$ENDIF}
InvalidateRect(Handle, @rcInval, FALSE);
end;
end;
FLeftGutterArea.InvalidateLines(FirstLine, LastLine);
FRightGutterArea.InvalidateLines(FirstLine, LastLine);
FFoldedLinesView.TopLine := TopFoldLine;
end;
@ -2532,13 +2502,7 @@ begin
if sfPainting in fStateFlags then exit;
if Visible and HandleAllocated then
if (FirstLine = -1) and (LastLine = -1) then begin
rcInval := ClientRect;
rcInval.Left := FTextArea.Bounds.Left;
rcInval.Right := FTextArea.Bounds.Right;
{$IFDEF VerboseSynEditInvalidate}
DebugLn(['TCustomSynEdit.InvalidateLines ',DbgSName(self),' ALL ',dbgs(rcInval)]);
{$ENDIF}
InvalidateRect(Handle, @rcInval, FALSE);
FTextArea.InvalidateLines(-1, -1);
end else begin
// pretend we haven't scrolled
TopFoldLine := FFoldedLinesView.TopLine;
@ -2555,15 +2519,8 @@ begin
l := LinesInWindow + 1;
f := RowToScreenRow(FirstLine);
f := Max(0, f);
{ any line visible? }
if (l >= f) then begin
rcInval := Rect(FTextArea.Bounds.Left, LineHeight * f,
FTextArea.Bounds.Right, LineHeight * l);
{$IFDEF VerboseSynEditInvalidate}
DebugLn(['TCustomSynEdit.InvalidateLines ',DbgSName(self),' PART ',dbgs(rcInval)]);
{$ENDIF}
InvalidateRect(Handle, @rcInval, FALSE);
end;
FTextArea.InvalidateLines(F, L);
FFoldedLinesView.TopLine := TopFoldLine;
end;
@ -3431,8 +3388,8 @@ var
NewBufferHeight: Integer;
{$ENDIF}
begin
if (SavedCanvas<>nil) then RaiseGDBException('');
{$IFDEF EnableDoubleBuf}
if (SavedCanvas<>nil) then RaiseGDBException('');
if BufferBitmap=nil then
BufferBitmap:=TBitmap.Create;
NewBufferWidth:=BufferBitmap.Width;
@ -7057,7 +7014,7 @@ end;
procedure TCustomSynEdit.MoveCaretHorz(DX: integer);
var
NewCaret: TPoint;
s: String;
s, a: String;
PhysicalLineLen: Integer;
begin
NewCaret:=Point(CaretX+DX,CaretY);

View File

@ -269,11 +269,17 @@ type
TLazSynSurface = class
private
FBounds: TRect;
FOwner: TWinControl;
function GetHandle: HWND;
protected
procedure BoundsChanged; virtual;
procedure DoPaint(ACanvas: TCanvas; AClip: TRect); virtual; abstract;
property Handle: HWND read GetHandle;
public
constructor Create(AOwner: TWinControl);
procedure Assign(Src: TLazSynSurface); virtual;
procedure Paint(ACanvas: TCanvas; AClip: TRect);
procedure InvalidateLines(FirstLine, LastLine: TLineIdx); virtual;
procedure SetBounds(ATop, ALeft, ABottom, ARight: Integer);
property Left: Integer read FBounds.Left;
@ -825,11 +831,26 @@ end;
{ TLazSynSurface }
function TLazSynSurface.GetHandle: HWND;
begin
Result := FOwner.Handle;
end;
procedure TLazSynSurface.BoundsChanged;
begin
//
end;
constructor TLazSynSurface.Create(AOwner: TWinControl);
begin
FOwner := AOwner;
end;
procedure TLazSynSurface.Assign(Src: TLazSynSurface);
begin
// do not assign the bounds
end;
procedure TLazSynSurface.Paint(ACanvas: TCanvas; AClip: TRect);
begin
if (AClip.Left >= Bounds.Right) or
@ -842,11 +863,16 @@ begin
if (AClip.Left < Bounds.Left) then AClip.Left := Bounds.Left;
if (AClip.Right > Bounds.Right) then AClip.Right := Bounds.Right;
if (AClip.Top < Bounds.Top) then AClip.Top := Bounds.Top;
if (AClip.Bottom < Bounds.Bottom) then AClip.Bottom := Bounds.Bottom;
if (AClip.Bottom > Bounds.Bottom) then AClip.Bottom := Bounds.Bottom;
DoPaint(ACanvas, AClip);
end;
procedure TLazSynSurface.InvalidateLines(FirstLine, LastLine: TLineIdx);
begin
//
end;
procedure TLazSynSurface.SetBounds(ATop, ALeft, ABottom, ARight: Integer);
begin
if (FBounds.Left = ALeft) and (FBounds.Top = ATop) and

View File

@ -6,13 +6,14 @@ interface
uses
SysUtils, Classes, Controls, Graphics, LCLType, LCLIntf, Menus,
SynEditMarks, SynEditMiscClasses, SynEditMiscProcs, LazSynTextArea,
SynEditMarks, SynEditTypes, SynEditMiscClasses, SynEditMiscProcs, LazSynTextArea,
SynTextDrawer, SynGutterBase, SynGutterLineNumber, SynGutterCodeFolding,
SynGutterMarks, SynGutterChanges, SynEditMouseCmds;
type
TSynGutterSeparator = class;
TLazSynGutterArea = class;
{ TSynGutter }
@ -32,7 +33,7 @@ type
constructor Create(AOwner : TSynEditBase; ASide: TSynGutterSide;
ATextDrawer: TheTextDrawer);
destructor Destroy; override;
procedure Paint(Canvas: TCanvas; AClip: TRect; FirstLine, LastLine: integer);
procedure Paint(Canvas: TCanvas; Surface:TLazSynGutterArea; AClip: TRect; FirstLine, LastLine: integer);
function HasCustomPopupMenu(out PopMenu: TPopupMenu): Boolean;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure MouseMove(Shift: TShiftState; X, Y: Integer);
@ -98,11 +99,15 @@ type
private
FGutter: TSynGutter;
FTextArea: TLazSynTextArea;
function GetTextBounds: TRect;
protected
procedure DoPaint(ACanvas: TCanvas; AClip: TRect); override;
public
procedure InvalidateLines(FirstLine, LastLine: TLineIdx); override;
procedure Assign(Src: TLazSynSurface); override;
property TextArea: TLazSynTextArea read FTextArea write FTextArea;
property Gutter: TSynGutter read FGutter write FGutter;
property TextBounds: TRect read GetTextBounds;
end;
implementation
@ -111,6 +116,11 @@ uses
{ TLazSynGutterArea }
function TLazSynGutterArea.GetTextBounds: TRect;
begin
Result := TextArea.TextBounds;
end;
procedure TLazSynGutterArea.DoPaint(ACanvas: TCanvas; AClip: TRect);
var
ScreenRow1, ScreenRow2: integer;
@ -119,7 +129,31 @@ begin
ScreenRow1 := Max((AClip.Top - Bounds.Top) div TextArea.LineHeight, 0);
ScreenRow2 := Min((AClip.Bottom-1 - Bounds.Top) div TextArea.LineHeight, TextArea.LinesInWindow + 1);
FGutter.Paint(ACanvas, AClip, ScreenRow1, ScreenRow2);
FGutter.Paint(ACanvas, Self, AClip, ScreenRow1, ScreenRow2);
end;
procedure TLazSynGutterArea.InvalidateLines(FirstLine, LastLine: TLineIdx);
var
rcInval: TRect;
begin
rcInval := Bounds;
if (FirstLine >= 0) then
rcInval.Top := TextArea.TextBounds.Top + FirstLine * TextArea.LineHeight;
if (LastLine >= 0) then
rcInval.Bottom := TextArea.TextBounds.Top + LastLine * TextArea.LineHeight;
{$IFDEF VerboseSynEditInvalidate}
DebugLn(['TCustomSynEdit.InvalidateGutterLines ',DbgSName(self), ' FirstLine=',FirstLine, ' LastLine=',LastLine, ' rect=',dbgs(rcInval)]);
{$ENDIF}
if (rcInval.Top < rcInval.Bottom) and (rcInval.Left < rcInval.Right) then
InvalidateRect(Handle, @rcInval, FALSE);
end;
procedure TLazSynGutterArea.Assign(Src: TLazSynSurface);
begin
inherited Assign(Src);
FTextArea := TLazSynGutterArea(Src).FTextArea;
FGutter := TLazSynGutterArea(Src).FGutter;
end;
{ TSynGutter }
@ -203,7 +237,7 @@ begin
Parts[PixelToPartIndex(X)].DoOnGutterClick(X, Y);
end;
procedure TSynGutter.Paint(Canvas: TCanvas; AClip: TRect; FirstLine, LastLine: integer);
procedure TSynGutter.Paint(Canvas: TCanvas; Surface:TLazSynGutterArea; AClip: TRect; FirstLine, LastLine: integer);
var
i: integer;
rcLine: TRect;
@ -224,10 +258,8 @@ begin
TextDrawer.ExtTextOut(Left, Top, ETO_OPAQUE, AClip, nil, 0);
TextDrawer.EndDrawing;
if Side = gsLeft then
AClip.Left := LeftOffset
else
AClip.Left := SynEdit.ClientWidth - Width - ScrollBarWidth + LeftOffset;
AClip.Left := Surface.Left;
AClip.Top := Surface.TextBounds.Top + FirstLine * TCustomSynEdit(SynEdit).LineHeight;
rcLine := AClip;
rcLine.Right := rcLine.Left;

View File

@ -96,8 +96,8 @@ begin
Canvas.AntialiasingMode:=amOff;
rcLine := AClip;
rcLine.Bottom := AClip.Top;
rcLine.Left := rcLine.Left + Width div 2;
rcLine.Bottom := FirstLine * LineHeight;
for i := FirstLine to LastLine do
begin
iLine := FoldView.TextIndex[i];

View File

@ -679,7 +679,7 @@ begin
Pen.Color := MarkupInfo.Foreground;
Pen.Width := 1;
rcLine.Bottom := FirstLine * LineHeight;
rcLine.Bottom := AClip.Top;
for iLine := FirstLine to LastLine do
begin
// next line rect

View File

@ -253,7 +253,7 @@ begin
fTextDrawer.Style := MarkupInfo.Style;
// prepare the rect initially
rcLine := AClip;
rcLine.Bottom := FirstLine * LineHeight;
rcLine.Bottom := AClip.Top;
for i := FirstLine to LastLine do
begin
iLine := FoldView.DisplayNumber[i];

View File

@ -138,9 +138,9 @@ begin
LineHeight := TCustomSynEdit(SynEdit).LineHeight;
//Gutter.Paint always supplies AClip.Left = GutterPart.Left
MarkRect := Rect(AClip.Left + FBookMarkOpt.LeftMargin,
aScreenLine * LineHeight,
AClip.Top,
AClip.Left + FColumnWidth,
(aScreenLine+1) * LineHeight);
AClip.Top + LineHeight);
LastMarkIsBookmark := FBookMarkOpt.DrawBookmarksFirst;
@ -184,6 +184,8 @@ end;
procedure TSynGutterMarks.Paint(Canvas : TCanvas; AClip : TRect; FirstLine, LastLine : integer);
var
i: integer;
LineHeight: Integer;
rcLine: TRect;
begin
if not Visible then exit;
if MarkupInfo.Background <> clNone then
@ -198,10 +200,16 @@ begin
FColumnWidth := Width;
FColumnCount := Max((Width+1) div FColumnWidth, 1); // full columns
rcLine := AClip;
rcLine.Bottom := rcLine.Top;
if FBookMarkOpt.GlyphsVisible and (LastLine >= FirstLine) then
begin
for i := FirstLine to LastLine do
PaintLine(i, Canvas, AClip);
LineHeight := TCustomSynEdit(SynEdit).LineHeight;
for i := FirstLine to LastLine do begin
rcLine.Top := rcLine.Bottom;
rcLine.Bottom := Min(AClip.Bottom, rcLine.Top + LineHeight);
PaintLine(i, Canvas, rcLine);
end;
end;
end;