zamtmn / metadarkstyle

76 stars 24 forks source link

Issue? TRichMemo - scrollbar not darkened / Workaround #36

Closed klaus101 closed 1 year ago

klaus101 commented 1 year ago

Another program learned smoothly the dark mode now, but, the ScrollBars for a TRichMemo stayed light, not darkened .... See screenshot and testcase attached. The debug output in the message console shows up that within “InterceptOpenThemeData” VSCLASS_SCROLLBAR is correctly detected in the classlist. And, so, “AllowDarkStyle” for the scrollbar's hwnd will be done. But it stays light.

I found a workaround (see the testcase attached) but I don't know what to do with this, if it is something that had to be handled in the uwin32widgetsetdark.pas itself.

scrollbar_prob

testcase_TRichMemo_Scrollbars__workaround.zip

The workaround is: to call a SetWindowTheme(RichMemo1.Handle, 'DarkMode_Explorer', nil); // Needs uses UXTheme within the user program.

A special thing here is that RichMemo's property "ScrollBars := " has to be set before, early! If done later (after the SetWindowTheme), the workaround won't have any effect (scrollbars stay light).

What do you mean, zamtmn? Should metadarkstyle handle it generically?

(Note, RichMemo needs the richmemo_package from the OPM). At the end TRichMemo is derived from a StdCrls items.

zamtmn commented 1 year ago

Yes, confimed with https://wiki.freepascal.org/RichMemo image I will try to solve it inside our package

klaus101 commented 1 year ago

(Seeing the image above:) yes, control's main window (and borders) were also light, but i didn't mention that -sorry!-, because i had sorted it out early via Color := clWindow and BorderStyle := bsNone.

klaus101 commented 1 year ago

Some thoughts .. Unfortunately the TRichMemo, which at the end is derived from a TCustomMemo, is not covered by the custommemo callback, but nevertheless. The workaround does work fine, but a "SetWindowTheme(Window, 'DarkMode_Explorer'" would be somehow not very intuitive to use for a TMemo. Whereas the proccedure "EnableDarkStyle" already does just the same. It's simply not exposed to be callable from outside yet. But it’s esay to define it as a public procedure ("uses Windows" must be shifted a bit upwards for that).

Better, if a similar proceedeure covers "Color" and "BorderStyle" too --- just as "TWin32WSCustomMemoDark.CreateHandle" already does. So, in sum, why not do something like:

interface

uses
  Windows,   // for to make HWND known 
  LCLVersion, uDarkStyleParams, uDarkStyleSchemes;

.....
procedure TryEnforceDarkStyle(Window: HWND);

implementation

uses
   //Classes, SysUtils, Win32Int, WSLCLClasses, Forms, Windows, Win32Proc, Menus,
     Classes, SysUtils, Win32Int, WSLCLClasses, Forms,           Win32Proc, Menus,

....

procedure EnableDarkStyle(Window: HWND);
begin
  AllowDarkModeForWindow(Window, True);
  SetWindowTheme(Window, 'DarkMode_Explorer', nil);
  SendMessageW(Window, WM_THEMECHANGED, 0, 0);
end;

// and then, placed directly beyond procedure EnableDarkStyle(Window: HWND);
procedure TryEnforceDarkStyle(Window: HWND);
var ctrl: TWinControl;
begin
  if (Window <> 0) then begin
     EnableDarkStyle(Window);
     ctrl := FindControl(Window);
     if ctrl = nil then exit;
     ctrl.Color := clWindow;
     //if (ctrl Is TCustomMemo) then          // This attempt won't work (= light scrollbars again)
     //    (ctrl As TCustomMemo).BorderStyle := bsNone;
  end;
end;

// In the user program:

  If IsDarkModeEnabled then begin
     //RichMemo1.Color := clWindow;       // No more needed
     //SetWindowTheme(RichMemo1.Handle, 'DarkMode_Explorer', nil);  // Appears roonter-intiutive
     RichMemo1.BorderStyle := bsNone;  // Still needed here
     TryEnforceDarkStyle(RichMemo1.Handle);
  end;

A kind of last resort that could be easily applied for similar cases ...

scrollbar_prob_2

zamtmn commented 1 year ago

Yes, it seems that nothing can be done from the inside((

klaus101 commented 1 year ago

I'd ended up in the same opinion, so the idea at least to encapsulate it a bit ...

klaus101 commented 1 year ago

Btw, maybe better to pass directly a "TWinControl" as parameter, for to save the need to do a "FindControl". Requires to move "Controls" (instead of "Windows") into the first "uses" clause. "BorderStyle" would work now too:

procedure TryEnforceDarkStyle(ctrl:TWinControl);
begin
  if (ctrl <> nil) then begin
     if (ctrl Is TCustomMemo) then     // Could be expanded for other component types later, if needed
        (ctrl As TCustomMemo).BorderStyle := bsNone;  // Must be coded here before "EnableDarkStyle"!
     EnableDarkStyle(ctrl.Handle);
     ctrl.Color := clWindow;
  end;
end;

// User program:

  If IsDarkModeEnabled then
     TryEnforceDarkStyle(RichMemo1);

Would it be, conceptually, anyway of interest for you to offer such an exposed procedure for such cases we have here?

klaus101 commented 1 year ago

Unless any better solution (using an internal hook) could be found (which would be very appreciated), the best i could do for to support an easier usage is to propose an exposure procedure via merge request.

klaus101 commented 1 year ago

Thank you! Can be revoked if an intrinsic solution is seen.

klaus101 commented 1 year ago

So far closing.