Skip to content
Open
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
130 changes: 87 additions & 43 deletions Source/VirtualTrees.BaseTree.pas
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,11 @@ TVTVirtualNodeEnumeration = record
function GetNext(Node: PVirtualNode): PVirtualNode;
end;

TVTPreparedBackground = record
Bitmap: TBitmap;
BackgroundColor: TColor;
Transparent: Boolean;
end;

// ----- TBaseVirtualTree
TBaseVirtualTree = class abstract(TVTBaseAncestor)
Expand Down Expand Up @@ -468,6 +473,7 @@ TBaseVirtualTree = class abstract(TVTBaseAncestor)
FTempNodeCache: TNodeArray; // used at various places to hold temporarily a bunch of node refs.
FTempNodeCount: Cardinal; // number of nodes in FTempNodeCache
FBackground: TVTBackground; // A background image loadable at design and runtime.
FBackgroundPrepared: TVTPreparedBackground; // Prepared background image and settings used when it was created.
FBackgroundImageTransparent: Boolean; // By default, this is off. When switched on, will try to draw the image
// transparent by using the color of the component as transparent color

Expand Down Expand Up @@ -748,6 +754,7 @@ TBaseVirtualTree = class abstract(TVTBaseAncestor)
procedure CMParentDoubleBufferedChange(var Message: TMessage); message CM_PARENTDOUBLEBUFFEREDCHANGED;

procedure AdjustTotalCount(Node: PVirtualNode; Value: Integer; relative: Boolean = False);
procedure BackgroundPictureChanged(Sender: TObject);
function CalculateCacheEntryCount: Integer;
procedure CalculateVerticalAlignments(var PaintInfo: TVTPaintInfo; var VButtonAlign: TDimension);
function ChangeCheckState(Node: PVirtualNode; Value: TCheckState): Boolean;
Expand All @@ -762,6 +769,7 @@ TBaseVirtualTree = class abstract(TVTBaseAncestor)
function FindInPositionCache(Position: TDimension; var CurrentPos: TNodeHeight): PVirtualNode; overload;
procedure FixupTotalCount(Node: PVirtualNode);
procedure FixupTotalHeight(Node: PVirtualNode);
function GetBackgroundBitmap(Source: TVTBackground; aBkgColor: TColor): TBitmap;
function GetBottomNode: PVirtualNode;
function GetCheckState(Node: PVirtualNode): TCheckState;
function GetCheckType(Node: PVirtualNode): TCheckType;
Expand Down Expand Up @@ -2293,6 +2301,8 @@ constructor TBaseVirtualTree.Create(AOwner: TComponent);
FAutoScrollInterval := 1;

FBackground := TVTBackground.Create;
FBackground.OnChange := BackgroundPictureChanged;

// Similar to the Transparent property of TImage,
// this flag is Off by default.
FBackGroundImageTransparent := False;
Expand Down Expand Up @@ -2354,6 +2364,7 @@ destructor TBaseVirtualTree.Destroy();
Clear;
FColors.Free;
FBackground.Free;
FreeAndNil(FBackgroundPrepared.Bitmap);

if CheckImageKind = ckSystemDefault then
FCheckImages.Free;
Expand Down Expand Up @@ -2460,6 +2471,21 @@ procedure TBaseVirtualTree.AdjustTotalHeight(Node: PVirtualNode; Value: TNodeHei

//----------------------------------------------------------------------------------------------------------------------

procedure TBaseVirtualTree.BackgroundPictureChanged(Sender: TObject);

// Invalidates the prepared background image whenever the background picture changes.

// Note: If Background's bitmap pixels are modified directly (ScanLine, a raw HBITMAP/HDC, or any
// GDI call that bypasses TCanvas), OnChange is not called. The cached FBackgroundPrepared image
// will keep showing the previously prepared image. In such cases, reassign the Background to
// force an OnChange so it can be re-prepared.

begin
FreeAndNil(FBackgroundPrepared.Bitmap);
end;

//----------------------------------------------------------------------------------------------------------------------

function TBaseVirtualTree.CalculateCacheEntryCount: Integer;

// Calculates the size of the position cache.
Expand Down Expand Up @@ -3337,6 +3363,32 @@ procedure TBaseVirtualTree.FixupTotalHeight(Node: PVirtualNode);

//----------------------------------------------------------------------------------------------------------------------

function TBaseVirtualTree.GetBackgroundBitmap(Source: TVTBackground; aBkgColor: TColor): TBitmap;

// Prepares and returns the background bitmap ready to be drawn. Creates a new bitmap if it
// hasn't been created yet or if preparation settings have changed.

var
bkgColor: TColor;

begin
bkgColor := ColorToRGB(aBkgColor);
if Assigned(FBackgroundPrepared.Bitmap) and
(FBackgroundPrepared.BackgroundColor = bkgColor) and
(FBackgroundPrepared.Transparent = FBackGroundImageTransparent) then
Exit(FBackgroundPrepared.Bitmap);

FreeAndNil(FBackgroundPrepared.Bitmap);
FBackgroundPrepared.Bitmap := TBitmap.Create;

PrepareBackGroundPicture(Source, FBackgroundPrepared.Bitmap, Source.Width, Source.Height, bkgColor);
FBackgroundPrepared.BackgroundColor := bkgColor;
FBackgroundPrepared.Transparent := FBackGroundImageTransparent;
Result := FBackgroundPrepared.Bitmap;
end;

//----------------------------------------------------------------------------------------------------------------------

function TBaseVirtualTree.GetBottomNode: PVirtualNode;

begin
Expand Down Expand Up @@ -5777,11 +5829,9 @@ procedure TBaseVirtualTree.StaticBackground(Source: TVTBackground; Target: TCanv
DrawRect: TRect;
DrawingBitmap: TBitmap;
begin
DrawingBitmap := TBitmap.Create;
try
// clear background
Target.Brush.Color := aBkgColor;
Target.FillRect(R);
// clear background
Target.Brush.Color := aBkgColor;
Target.FillRect(R);

// Picture rect in relation to client viewscreen.
PicRect := Rect(FBackgroundOffsetX, FBackgroundOffsetY, FBackgroundOffsetX + Source.Width, FBackgroundOffsetY + Source.Height);
Expand All @@ -5792,14 +5842,12 @@ procedure TBaseVirtualTree.StaticBackground(Source: TVTBackground; Target: TCanv
// If picture falls in AreaRect, return intersection (DrawRect).
if IntersectRect(DrawRect, PicRect, AreaRect) then
begin
PrepareBackGroundPicture(Source, DrawingBitmap, Source.Width, Source.Height, aBkgColor);
// copy image to destination
BitBlt(Target.Handle, DrawRect.Left - OffsetPosition.X, DrawRect.Top - OffsetPosition.Y, (DrawRect.Right - OffsetPosition.X) - (DrawRect.Left - OffsetPosition.X),
DrawingBitmap := GetBackgroundBitmap(Source, aBkgColor);

// copy image to destination
BitBlt(Target.Handle, DrawRect.Left - OffsetPosition.X, DrawRect.Top - OffsetPosition.Y, (DrawRect.Right - OffsetPosition.X) - (DrawRect.Left - OffsetPosition.X),
(DrawRect.Bottom - OffsetPosition.Y) - (DrawRect.Top - OffsetPosition.Y) + R.Top, DrawingBitmap.Canvas.Handle, DrawRect.Left - PicRect.Left, DrawRect.Top - PicRect.Top,
SRCCOPY);
end;
finally
DrawingBitmap.Free;
SRCCOPY);
end;
end;

Expand Down Expand Up @@ -5845,42 +5893,38 @@ procedure TBaseVirtualTree.TileBackground(Source: TVTBackground; Target: TCanvas
DeltaY: TDimension;
DrawingBitmap: TBitmap;
begin
DrawingBitmap := TBitmap.Create;
try
PrepareBackGroundPicture(Source, DrawingBitmap, Source.Width, Source.Height, aBkgColor);
with Target do
begin
SourceY := (R.Top + Offset.Y + FBackgroundOffsetY) mod Source.Height;
// Always wrap the source coordinates into positive range.
if SourceY < 0 then
SourceY := Source.Height + SourceY;
DrawingBitmap := GetBackgroundBitmap(Source, aBkgColor);

// Tile image vertically until target rect is filled.
while R.Top < R.Bottom do
begin
SourceX := (R.Left + Offset.X + FBackgroundOffsetX) mod Source.Width;
// always wrap the source coordinates into positive range
if SourceX < 0 then
SourceX := Source.Width + SourceX;
with Target do
begin
SourceY := (R.Top + Offset.Y + FBackgroundOffsetY) mod Source.Height;
// Always wrap the source coordinates into positive range.
if SourceY < 0 then
SourceY := Source.Height + SourceY;

TargetX := R.Left;
// height of strip to draw
DeltaY := Min(R.Bottom - R.Top, Source.Height - SourceY);
// Tile image vertically until target rect is filled.
while R.Top < R.Bottom do
begin
SourceX := (R.Left + Offset.X + FBackgroundOffsetX) mod Source.Width;
// always wrap the source coordinates into positive range
if SourceX < 0 then
SourceX := Source.Width + SourceX;

// tile the image horizontally
while TargetX < R.Right do
begin
BitBlt(Handle, TargetX, R.Top, Min(R.Right - TargetX, Source.Width - SourceX), DeltaY,
DrawingBitmap.Canvas.Handle, SourceX, SourceY, SRCCOPY);
Inc(TargetX, Source.Width - SourceX);
SourceX := 0;
end;
Inc(R.Top, Source.Height - SourceY);
SourceY := 0;
TargetX := R.Left;
// height of strip to draw
DeltaY := Min(R.Bottom - R.Top, Source.Height - SourceY);

// tile the image horizontally
while TargetX < R.Right do
begin
BitBlt(Handle, TargetX, R.Top, Min(R.Right - TargetX, Source.Width - SourceX), DeltaY,
DrawingBitmap.Canvas.Handle, SourceX, SourceY, SRCCOPY);
Inc(TargetX, Source.Width - SourceX);
SourceX := 0;
end;
Inc(R.Top, Source.Height - SourceY);
SourceY := 0;
end;
finally
DrawingBitmap.Free;
end;
end;

Expand Down