Skip to content

Commit 2bc0624

Browse files
Merge pull request #1225 from livius2/systemImages_theme
CreateSystemImageSet, SetWindowTheme and small cleanup
2 parents d3752aa + 7c4f9b7 commit 2bc0624

File tree

3 files changed

+170
-144
lines changed

3 files changed

+170
-144
lines changed

Source/VirtualTrees.BaseAncestorFMX.pas

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ TVTBaseAncestorFMX = class abstract(TRectangle)
6060
procedure DragCanceled; virtual; abstract;
6161

6262
procedure Resize; override;
63+
function CreateSystemImageSet(): TImageList;
64+
procedure SetWindowTheme(const Theme: string); virtual;
6365

6466
procedure ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; isDpiChange: Boolean{$ifend}); virtual; abstract;
6567
function GetControlsAlignment: TAlignment; virtual; abstract;
@@ -68,7 +70,7 @@ TVTBaseAncestorFMX = class abstract(TRectangle)
6870
procedure MarkCutCopyNodes; virtual; abstract;
6971
function GetSortedCutCopySet(Resolve: Boolean): TNodeArray; virtual; abstract;
7072
function GetSortedSelection(Resolve: Boolean): TNodeArray; virtual; abstract;
71-
procedure WriteNode(Stream: TStream; Node: PVirtualNode); virtual; abstract;
73+
procedure WriteNode(Stream: TStream; Node: PVirtualNode); virtual; abstract;
7274
protected //properties
7375
property DottedBrushTreeLines: TStrokeBrush read FDottedBrushTreeLines write FDottedBrushTreeLines;
7476
property DottedBrushGridLines: TStrokeBrush read FDottedBrushGridLines write FDottedBrushGridLines;
@@ -606,4 +608,19 @@ function TVTBaseAncestorFMX.GetSystemMetrics(nIndex: Integer): Integer;
606608
{$ENDIF}
607609
end;
608610

611+
//----------------------------------------------------------------------------------------------------------------------
612+
613+
function TVTBaseAncestorFMX.CreateSystemImageSet(): TImageList;
614+
begin
615+
Result:= TImageList.Create(Self);
616+
FillSystemCheckImages(Self, Result);
617+
end;
618+
619+
//----------------------------------------------------------------------------------------------------------------------
620+
621+
procedure TVTBaseAncestorFMX.SetWindowTheme(const Theme: string);
622+
begin
623+
//nothing
624+
end;
625+
609626
end.

Source/VirtualTrees.BaseAncestorVcl.pas

Lines changed: 148 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ TVTBaseAncestorVcl = class abstract(TCustomControl)
3737
function RenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium; ForClipboard: Boolean): HResult; virtual;
3838
procedure NotifyAccessibleEvent(pEvent: DWord = EVENT_OBJECT_STATECHANGE);
3939
function PrepareDottedBrush(CurrentDottedBrush: TBrush; Bits: Pointer; const BitsLinesCount: Word): TBrush; virtual;
40+
function CreateSystemImageSet(): TImageList;
41+
procedure SetWindowTheme(const Theme: string); virtual;
4042
//// Abtract method that are implemented in TBaseVirtualTree, keep in sync with TVTBaseAncestorFMX
4143
function GetSelectedCount(): Integer; virtual; abstract;
4244
procedure MarkCutCopyNodes; virtual; abstract;
@@ -104,10 +106,17 @@ implementation
104106

105107
uses
106108
System.SyncObjs,
109+
System.SysUtils,
107110
Vcl.AxCtrls,
111+
Vcl.Forms,
112+
Vcl.Themes,
113+
Winapi.CommCtrl,
114+
Winapi.ShlObj,
115+
Winapi.UxTheme,
108116
VirtualTrees.DataObject,
109117
VirtualTrees.Clipboard,
110-
VirtualTrees.AccessibilityFactory;
118+
VirtualTrees.AccessibilityFactory,
119+
VirtualTrees.StyleHooks;
111120

112121
//----------------------------------------------------------------------------------------------------------------------
113122

@@ -283,6 +292,137 @@ procedure TVTBaseAncestorVcl.CopyToClipboard;
283292
end;
284293
end;
285294

295+
function TVTBaseAncestorVcl.CreateSystemImageSet: TImageList;
296+
297+
// Creates a system check image set.
298+
// Note: the DarkCheckImages and FlatImages image lists must already be filled, as some images from them are copied here.
299+
300+
const
301+
MaskColor: TColor = clRed;
302+
cFlags = ILC_COLOR32 or ILC_MASK;
303+
304+
var
305+
BM: TBitmap;
306+
Theme: HTHEME;
307+
Details: TThemedElementDetails;
308+
309+
//---------------------------------------------------------------------------
310+
311+
// Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style)
312+
function StyleServices: TCustomStyleServices;
313+
begin
314+
Result := VTStyleServices(Self);
315+
end;
316+
317+
procedure AddSystemImage(IL: TImageList; Index: Integer);
318+
const
319+
States: array [0..19] of Integer = (
320+
RBS_UNCHECKEDNORMAL, RBS_UNCHECKEDHOT, RBS_UNCHECKEDPRESSED, RBS_UNCHECKEDDISABLED,
321+
RBS_CHECKEDNORMAL, RBS_CHECKEDHOT, RBS_CHECKEDPRESSED, RBS_CHECKEDDISABLED,
322+
CBS_UNCHECKEDNORMAL, CBS_UNCHECKEDHOT, CBS_UNCHECKEDPRESSED, CBS_UNCHECKEDDISABLED,
323+
CBS_CHECKEDNORMAL, CBS_CHECKEDHOT, CBS_CHECKEDPRESSED, CBS_CHECKEDDISABLED,
324+
CBS_MIXEDNORMAL, CBS_MIXEDHOT, CBS_MIXEDPRESSED, CBS_MIXEDDISABLED);
325+
var
326+
ButtonState: Cardinal;
327+
ButtonType: Cardinal;
328+
329+
begin
330+
BM.Canvas.FillRect(Rect(0, 0, BM.Width, BM.Height));
331+
if StyleServices.Enabled and StyleServices.IsSystemStyle then
332+
begin
333+
if Index < 8 then
334+
Details.Part := BP_RADIOBUTTON
335+
else
336+
Details.Part := BP_CHECKBOX;
337+
Details.State := States[Index];
338+
DrawThemeBackground(Theme, BM.Canvas.Handle, Details.Part, Details.State, Rect(0, 0, BM.Width, BM.Height), nil);
339+
end
340+
else
341+
begin
342+
if Index < 8 then
343+
ButtonType := DFCS_BUTTONRADIO
344+
else
345+
ButtonType := DFCS_BUTTONCHECK;
346+
if Index >= 16 then
347+
ButtonType := ButtonType or DFCS_BUTTON3STATE;
348+
349+
case Index mod 4 of
350+
0:
351+
ButtonState := 0;
352+
1:
353+
ButtonState := DFCS_HOT;
354+
2:
355+
ButtonState := DFCS_PUSHED;
356+
else
357+
ButtonState := DFCS_INACTIVE;
358+
end;
359+
if Index in [4..7, 12..19] then
360+
ButtonState := ButtonState or DFCS_CHECKED;
361+
// if Flat then
362+
// ButtonState := ButtonState or DFCS_FLAT;
363+
DrawFrameControl(BM.Canvas.Handle, Rect(0, 0, BM.Width, BM.Height), DFC_BUTTON, ButtonType or ButtonState);
364+
end;
365+
IL.AddMasked(BM, MaskColor);
366+
end;
367+
368+
//--------------- end local functions ---------------------------------------
369+
370+
const
371+
cDefaultCheckboxSize = 13;// Used when no other value is available
372+
var
373+
I: Integer;
374+
lSize: TSize;
375+
Res: Boolean;
376+
begin
377+
BM := TBitmap.Create; // Create a temporary bitmap, which holds the intermediate images.
378+
try
379+
Res := False;
380+
// Retrieve the checkbox image size, prefer theme if available, fall back to GetSystemMetrics() otherwise, but this returns odd results on Windows 8 and higher in high-dpi scenarios.
381+
if StyleServices.Enabled then
382+
if StyleServices.IsSystemStyle then
383+
begin
384+
{$if CompilerVersion >= 33}
385+
if TOSVersion.Check(10) and (TOSVersion.Build >= 15063) then
386+
Theme := OpenThemeDataForDPI(Handle, 'BUTTON', CurrentPPI)
387+
else
388+
{$ifend}
389+
Theme := OpenThemeData(Self.Handle, 'BUTTON');
390+
Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal);
391+
Res := GetThemePartSize(Theme, BM.Canvas.Handle, Details.Part, Details.State, nil, TS_TRUE, lSize) = S_OK;
392+
end
393+
else
394+
Res := StyleServices.GetElementSize(BM.Canvas.Handle, StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal), TElementSize.esActual, lSize {$IF CompilerVersion >= 34}, Self.CurrentPPI{$IFEND});
395+
if not Res then begin
396+
lSize := TSize.Create(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK));
397+
if lSize.cx = 0 then begin // error? (Should happen rarely only)
398+
lSize.cx := MulDiv(cDefaultCheckboxSize, Screen.PixelsPerInch, USER_DEFAULT_SCREEN_DPI);
399+
lSize.cy := lSize.cx;
400+
end;// if
401+
end;//if
402+
403+
Result := TImageList.CreateSize(lSize.cx, lSize.cy);
404+
Result.Handle := ImageList_Create(Result.Width, Result.Height, cFlags, 0, Result.AllocBy);
405+
Result.Masked := True;
406+
Result.BkColor := clWhite;
407+
408+
// Make the bitmap the same size as the image list is to avoid problems when adding.
409+
BM.SetSize(Result.Width, Result.Height);
410+
BM.Canvas.Brush.Color := MaskColor;
411+
BM.Canvas.Brush.Style := bsSolid;
412+
BM.Canvas.FillRect(Rect(0, 0, BM.Width, BM.Height));
413+
Result.AddMasked(BM, MaskColor);
414+
415+
// Add the 20 system checkbox and radiobutton images.
416+
for I := 0 to 19 do
417+
AddSystemImage(Result, I);
418+
if StyleServices.Enabled and StyleServices.IsSystemStyle then
419+
CloseThemeData(Theme);
420+
421+
finally
422+
BM.Free;
423+
end;
424+
end;
425+
286426
procedure TVTBaseAncestorVcl.CutToClipboard;
287427
var
288428
lDataObject: IDataObject;
@@ -409,6 +549,13 @@ function TVTBaseAncestorVcl.SetScrollInfo(Bar: Integer; const ScrollInfo: TScrol
409549

410550
//----------------------------------------------------------------------------------------------------------------------
411551

552+
procedure TVTBaseAncestorVcl.SetWindowTheme(const Theme: string);
553+
begin
554+
Winapi.UxTheme.SetWindowTheme(Handle, PWideChar(Theme), nil);
555+
end;
556+
557+
//----------------------------------------------------------------------------------------------------------------------
558+
412559
function TVTBaseAncestorVcl.GetScrollInfo(Bar: Integer; var ScrollInfo: TScrollInfo): Boolean;
413560
begin
414561
Result:= WinApi.Windows.GetScrollInfo(Handle, Bar, ScrollInfo);

0 commit comments

Comments
 (0)