LCL/TreeView: Improved custom drawing.

This commit is contained in:
wp_xyz 2025-02-09 15:56:16 +01:00
parent 19ed12106d
commit 8f5c0d7288

View File

@ -5058,6 +5058,7 @@ var
SpaceRect, DrawRect: TRect; SpaceRect, DrawRect: TRect;
Node: TTreeNode; Node: TTreeNode;
InsertMarkRect: TRect; InsertMarkRect: TRect;
bkColor: TColor;
begin begin
if [tvsPainting] * FStates <> [] then Exit; if [tvsPainting] * FStates <> [] then Exit;
Include(FStates, tvsPainting); Include(FStates, tvsPainting);
@ -5071,10 +5072,13 @@ begin
//UpdateScrollbars; //UpdateScrollbars;
with Canvas do with Canvas do
begin begin
bkColor := Self.Color;
Canvas.Brush.Color := bkColor;
if IsCustomDrawn(dtControl, cdPrePaint) then if IsCustomDrawn(dtControl, cdPrePaint) then
begin begin
DrawRect := ClientRect; DrawRect := ClientRect;
if not CustomDraw(DrawRect, cdPrePaint) then exit; if not CustomDraw(DrawRect, cdPrePaint) then exit;
bkColor := Canvas.Brush.Color;
end; end;
// draw nodes // draw nodes
Node := TopItem; Node := TopItem;
@ -5107,10 +5111,12 @@ begin
SpaceRect.Top := Node.Top + Node.Height - FScrolledTop + BorderWidth; SpaceRect.Top := Node.Top + Node.Height - FScrolledTop + BorderWidth;
//if Node<>nil then DebugLn('BottomItem=',BottomItem.text) else DebugLn('NO BOTTOMITEM!!!!!!!!!'); //if Node<>nil then DebugLn('BottomItem=',BottomItem.text) else DebugLn('NO BOTTOMITEM!!!!!!!!!');
// TWinControl(Parent).InvalidateRect(Self,SpaceRect,true); // TWinControl(Parent).InvalidateRect(Self,SpaceRect,true);
if (Color <> clNone) and (SpaceRect.Top < SpaceRect.Bottom) then Brush.Color := bkColor;
if (Brush.Color <> clNone) and (SpaceRect.Top < SpaceRect.Bottom) then
// if (Color <> clNone) and (SpaceRect.Top < SpaceRect.Bottom) then
begin begin
//DebugLn(' SpaceRect=',SpaceRect.Left,',',SpaceRect.Top,',',SpaceRect.Right,',',SpaceRect.Bottom); //DebugLn(' SpaceRect=',SpaceRect.Left,',',SpaceRect.Top,',',SpaceRect.Right,',',SpaceRect.Bottom);
Brush.Color := Color; //Brush.Color := Color;
FillRect(SpaceRect); FillRect(SpaceRect);
end; end;
// draw border // draw border
@ -5165,7 +5171,7 @@ procedure TCustomTreeView.DoPaintNode(Node: TTreeNode);
var var
NodeRect: TRect; NodeRect: TRect;
VertMid, VertDelta, RealExpandSignSize, RealIndent: integer; VertMid, VertDelta, RealExpandSignSize, RealIndent: integer;
NodeSelected, HasExpandSign: boolean; NodeSelected, NodeHot, NodeDisabled, HasExpandSign, CustomDrawn: boolean;
procedure DrawVertLine(X, Y1, Y2: Integer); procedure DrawVertLine(X, Y1, Y2: Integer);
begin begin
@ -5289,6 +5295,7 @@ var
Details: TThemedElementDetails; Details: TThemedElementDetails;
R: TRect; R: TRect;
PrevColor: TColor; PrevColor: TColor;
PrevStyle: TBrushStyle;
const const
cShiftHorzArrow = 2; //paint horz arrow N pixels upper than MidY cShiftHorzArrow = 2; //paint horz arrow N pixels upper than MidY
begin begin
@ -5308,6 +5315,9 @@ var
with Canvas do with Canvas do
begin begin
PrevStyle := Brush.Style;
PrevColor := Brush.Color;
Pen.Color := FExpandSignColor; Pen.Color := FExpandSignColor;
Pen.Style := psSolid; Pen.Style := psSolid;
case ExpandSignType of case ExpandSignType of
@ -5315,6 +5325,7 @@ var
begin begin
// draw a themed expand sign. Todo: track hot // draw a themed expand sign. Todo: track hot
R := Rect(ALeft, ATop, ARight, ABottom); R := Rect(ALeft, ATop, ARight, ABottom);
Canvas.Brush.Color := Self.Color;
Details := ThemeServices.GetElementDetails(PlusMinusDetail[False, CollapseSign]); Details := ThemeServices.GetElementDetails(PlusMinusDetail[False, CollapseSign]);
ThemeServices.DrawElement(Canvas.Handle, Details, R, nil); ThemeServices.DrawElement(Canvas.Handle, Details, R, nil);
end; end;
@ -5322,6 +5333,7 @@ var
begin begin
// draw a plus or a minus sign // draw a plus or a minus sign
R := Rect(ALeft, ATop, ARight+1, ABottom+1); //+1 for centering of line in square R := Rect(ALeft, ATop, ARight+1, ABottom+1); //+1 for centering of line in square
Canvas.Brush.Color := Self.Color;
Rectangle(R); Rectangle(R);
SmallIndent := Scale96ToFont(2); SmallIndent := Scale96ToFont(2);
MoveTo(R.Left + SmallIndent, MidY); MoveTo(R.Left + SmallIndent, MidY);
@ -5352,15 +5364,8 @@ var
end; end;
if ExpandSignType = tvestArrowFill then if ExpandSignType = tvestArrowFill then
begin
PrevColor := Brush.Color;
Brush.Color := ExpandSignColor; Brush.Color := ExpandSignColor;
end;
Polygon(Points, 3, False); Polygon(Points, 3, False);
if ExpandSignType = tvestArrowFill then
begin
Brush.Color := PrevColor;
end;
end; end;
tvestAngleBracket: tvestAngleBracket:
begin begin
@ -5400,6 +5405,8 @@ var
end; end;
end; end;
end; end;
Brush.Color := PrevColor;
Brush.Style := PrevStyle;
end; end;
end; end;
@ -5481,88 +5488,74 @@ var
end; end;
end; end;
procedure DrawBackground(IsSelected: Boolean; ARect: TRect); { Draws the default normal node background }
var procedure DrawNormalBackground(ARect: TRect);
Details: TThemedElementDetails;
CurBackgroundColor,bclr: TColor;
begin begin
bclr:=Canvas.Brush.Color; if Canvas.Brush.Color <> clNone then
try Canvas.FillRect(ARect);
if (tvoRowSelect in Options) and IsSelected then
if tvoThemedDraw in Options then
begin
if tvoFocusedPainting in FStates then
Details := ThemeServices.GetElementDetails(ttItemSelected)
else
Details := ThemeServices.GetElementDetails(ttItemSelectedNotFocus);
if ThemeServices.HasTransparentParts(Details) then
begin
Canvas.Brush.Color := Color;
Canvas.FillRect(ARect);
end;
ThemeServices.DrawElement(Canvas.Handle, Details, ARect, nil);
Exit;
end
else
CurBackgroundColor := FSelectedColor
else
CurBackgroundColor := Color;
if CurBackgroundColor <> clNone then
begin
Canvas.Brush.Color := CurBackgroundColor;
Canvas.FillRect(ARect);
end;
finally
Canvas.Brush.Color := bclr;
end;
end; end;
procedure DrawNodeText(IsSelected: Boolean; NdRect: TRect; AText: String); { Default-draws the background of selected and hot-tracked nodes over the full
client width. This does not occur when the tree is not in RowSelect mode,
or when the preceding OnAdvancedCustomDrawItem event handler has been
exited with DefaultDraw = fals. }
procedure DrawSpecialBackground(IsSelected, IsHot: Boolean; ARect: TRect);
var var
Details: TThemedElementDetails; Details: TThemedElementDetails;
NeedUnderline: Boolean;
PrevFontStyle: TFontStyles;
PrevFontColor: TColor;
begin begin
if IsSelected then if not RowSelect then
exit;
if not (IsSelected or IsHot) then
exit;
if tvoThemedDraw in Options then
begin
if (tvoFocusedPainting in FStates) then
begin
if IsSelected then
Details := ThemeServices.GetElementDetails(ttItemSelected)
else
if IsHot then
Details := ThemeServices.GetElementDetails(ttItemHot);
ThemeServices.DrawElement(Canvas.Handle, Details, ARect, nil);
end;
end else
if (IsSelected or IsHot) and (Canvas.Brush.Color <> clNone) then
Canvas.FillRect(ARect);
end;
{ If not deactivated by user selection in the custom-draw events, draws the
node text background. Then the node text. }
procedure DrawNodeText(IsSelected, IsHot: Boolean; NdRect: TRect; AText: String);
var
Details: TThemedElementDetails;
begin
if IsSelected or IsHot then
begin begin
if tvoFocusedPainting in FStates then if tvoFocusedPainting in FStates then
Details := ThemeServices.GetElementDetails(ttItemSelected) begin
else if IsSelected then
Details := ThemeServices.GetElementDetails(ttItemSelected)
else
Details := ThemeServices.GetElementDetails(ttItemHot);
end else
Details := ThemeServices.GetElementDetails(ttItemSelectedNotFocus); Details := ThemeServices.GetElementDetails(ttItemSelectedNotFocus);
if not (tvoRowSelect in Options) then if not (tvoRowSelect in Options) then
begin
if (tvoThemedDraw in Options) then if (tvoThemedDraw in Options) then
ThemeServices.DrawElement(Canvas.Handle, Details, NdRect, nil) ThemeServices.DrawElement(Canvas.Handle, Details, NdRect, nil)
else else
begin if Canvas.Brush.Color <> clNone then
Canvas.Brush.Color := FSelectedColor; Canvas.FillRect(NdRect)
Canvas.Font.Color := IfThen(FSelectedFontColorUsed, end else
FSelectedFontColor, InvertNdColor(FSelectedColor)); if not (tvoThemedDraw in Options) and (Canvas.Brush.Color <> clNone) then
Canvas.FillRect(NdRect);
end
else
if not (tvoThemedDraw in Options) then
begin
Canvas.Brush.Color := FSelectedColor;
Canvas.Font.Color := IfThen(FSelectedFontColorUsed,
FSelectedFontColor, InvertNdColor(FSelectedColor));
Canvas.FillRect(NdRect); Canvas.FillRect(NdRect);
end;
end end
else else
Details := ThemeServices.GetElementDetails(ttItemNormal); Details := ThemeServices.GetElementDetails(ttItemNormal);
NeedUnderline := (tvoHotTrack in FOptions) and (Node=FNodeUnderCursor);
if NeedUnderline then
begin
PrevFontStyle := Canvas.Font.Style;
PrevFontColor := Canvas.Font.Color;
Canvas.Font.Style := [fsUnderline];
if FHotTrackColor<>clNone then
Canvas.Font.Color := FHotTrackColor;
end;
NdRect.Offset(ScaleX(2, 96), 0); NdRect.Offset(ScaleX(2, 96), 0);
SetBkMode(Canvas.Handle, TRANSPARENT);
if (tvoThemedDraw in Options) then if (tvoThemedDraw in Options) then
begin begin
if not (Enabled and Node.Enabled) then if not (Enabled and Node.Enabled) then
@ -5571,19 +5564,10 @@ var
end end
else else
begin begin
if not (Enabled and Node.Enabled) and (FDisabledFontColor<>clNone) then
Canvas.Font.Color := FDisabledFontColor;
DrawText(Canvas.Handle, PChar(AText), -1, NdRect, DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_NOPREFIX); DrawText(Canvas.Handle, PChar(AText), -1, NdRect, DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_NOPREFIX);
end; end;
if NeedUnderline then
begin
Canvas.Font.Style := PrevFontStyle;
Canvas.Font.Color := PrevFontColor;
end;
end; end;
var var
x, ImgIndex: integer; x, ImgIndex: integer;
CurTextRect, ImgRect: TRect; CurTextRect, ImgRect: TRect;
@ -5591,6 +5575,7 @@ var
PaintImages: boolean; PaintImages: boolean;
OverlayIndex: Integer; OverlayIndex: Integer;
ImageRes, StateImageRes: TScaledImageListResolution; ImageRes, StateImageRes: TScaledImageListResolution;
savedBrushColor: TColor;
begin begin
if Assigned(FImages) then if Assigned(FImages) then
ImageRes := Images.ResolutionForPPI[ImagesWidth, Font.PixelsPerInch, GetCanvasScaleFactor]; ImageRes := Images.ResolutionForPPI[ImagesWidth, Font.PixelsPerInch, GetCanvasScaleFactor];
@ -5602,10 +5587,29 @@ begin
if (NodeRect.Bottom < 0) or (NodeRect.Top >= ClientHeight) then if (NodeRect.Bottom < 0) or (NodeRect.Top >= ClientHeight) then
Exit; Exit;
NodeSelected := (Node.Selected) or (Node.MultiSelected); NodeSelected := (Node.Selected) or (Node.MultiSelected);
Canvas.Font.Color := Font.Color; NodeHot := (tvoHotTrack in FOptions) and (Node = FNodeUnderCursor) and Assigned(FNodeUnderCursor);
Canvas.Brush.Color := Color; NodeDisabled := not (Enabled and Node.Enabled);
Canvas.Font.Assign(Self.Font);
if NodeSelected and not (tvoThemedDraw in Options) then
begin
Canvas.Brush.Color := FSelectedColor;
Canvas.Font.Color := FSelectedFontColor;
end else
begin
Canvas.Font.Color := Font.Color;
Canvas.Brush.Color := Color;
if NodeHot and not (tvoThemedDraw in FOptions) then
begin
Canvas.Font.Style := [fsUnderline];
if FHotTrackColor <> clNone then
Canvas.Font.Color := FHotTrackColor;
end;
end;
if NodeDisabled and (FDisabledFontColor <> clNone) then
Canvas.Font.Color := FDisabledFontColor;
PaintImages := True; PaintImages := True;
if IsCustomDrawn(dtItem, cdPrePaint) then customdrawn := IsCustomDrawn(dtItem, cdPrePaint);
if customDrawn then
begin begin
DrawState := []; DrawState := [];
if NodeSelected then if NodeSelected then
@ -5614,6 +5618,10 @@ begin
Include(DrawState, cdsFocused); Include(DrawState, cdsFocused);
if Node.MultiSelected then if Node.MultiSelected then
Include(DrawState, cdsMarked); Include(DrawState, cdsMarked);
if NodeHot then
Include(DrawState, cdsHot);
if NodeDisabled then
Include(DrawState, cdsDisabled);
if not CustomDrawItem(Node, DrawState, cdPrePaint, PaintImages) then Exit; if not CustomDrawItem(Node, DrawState, cdPrePaint, PaintImages) then Exit;
end; end;
@ -5623,10 +5631,21 @@ begin
//DebugLn(['[TCustomTreeView.DoPaintNode] Node=',DbgS(Node),' Node.Text=',Node.Text,' NodeRect=',NodeRect.Left,',',NodeRect.Top,',',NodeRect.Right,',',NodeRect.Bottom,' VertMid=',VertMid]); //DebugLn(['[TCustomTreeView.DoPaintNode] Node=',DbgS(Node),' Node.Text=',Node.Text,' NodeRect=',NodeRect.Left,',',NodeRect.Top,',',NodeRect.Right,',',NodeRect.Bottom,' VertMid=',VertMid]);
with Canvas do with Canvas do
begin begin
// draw background // Draw the normal node background, no selected color, no hot-track color
DrawBackground(NodeSelected, NodeRect); savedBrushColor := Canvas.Brush.Color;
Canvas.Brush.Color := Color;
customDrawn := IsCustomDrawn(dtItem, cdPreErase);
if not IsCustomDrawn(dtItem, cdPreErase) or CustomDrawItem(Node, [], cdPreErase, PaintImages) then
DrawNormalBackground(NodeRect);
Canvas.Brush.Color := savedBrushColor;
// draw tree lines // Background of selected or hot-tracked nodes
customDrawn := IsCustomDrawn(dtItem, cdPostErase);
if not IsCustomDrawn(dtItem, cdPostErase) or CustomDrawItem(Node, DrawState, cdPostErase, PaintImages) then
// If not custom-painted draw it over the full tree client width
DrawSpecialBackground(NodeSelected, NodeHot, NodeRect);
// Draw tree lines
Pen.Color := TreeLineColor; Pen.Color := TreeLineColor;
Pen.Style := TreeLinePenStyle; Pen.Style := TreeLinePenStyle;
if Pen.Style = psPattern then if Pen.Style = psPattern then
@ -5698,12 +5717,12 @@ begin
end; end;
// draw text // draw text
if (Node.Text <> '') and (Node<>FEditingItem) then if (Node.Text <> '') and (Node <> FEditingItem) then
begin begin
CurTextRect := NodeRect; CurTextRect := NodeRect;
CurTextRect.Left := x; CurTextRect.Left := x;
CurTextRect.Right := x + TextWidth(Node.Text) + (FDefItemSpace * 2); CurTextRect.Right := x + TextWidth(Node.Text) + (FDefItemSpace * 2);
DrawNodeText(NodeSelected, CurTextRect, Node.Text); DrawNodeText(NodeSelected, NodeHot, CurTextRect, Node.Text);
end; end;
// draw separator // draw separator
@ -5727,6 +5746,8 @@ begin
Include(DrawState,cdsFocused); Include(DrawState,cdsFocused);
if Node.MultiSelected then if Node.MultiSelected then
Include(DrawState,cdsMarked); Include(DrawState,cdsMarked);
if NodeHot then
Include(DrawState, cdsHot);
if not CustomDrawItem(Node,DrawState,cdPostPaint,PaintImages) then exit; if not CustomDrawItem(Node,DrawState,cdPostPaint,PaintImages) then exit;
end; end;
end; end;