Recent

Author Topic: Dark Theme in my program?  (Read 5436 times)

Erik@T

  • New Member
  • *
  • Posts: 15
Re: Dark Theme in my program?
« Reply #15 on: March 20, 2023, 01:54:03 pm »
For your target application, call uWin32WidgetSetDark.ApplyDarkStyle on FormCreate;

ListView's color and scrollbar can be fixed by setting its theme to 'DarkMode_Explorer' (I cannot darken the header either, for now):


Code: Pascal  [Select][+][-]
  1. uses
  2.   UxTheme
  3. var
  4.   C: TComponent;
  5. begin
  6.   for C in AForm do
  7.     begin
  8.     if (C is TWinControl) then
  9.       begin
  10.       if (C is TListView) then
  11.         begin
  12.         TListView(C).Color := aDarkModeBgColor;
  13.         TListView(C).Font.Color:=aDarkModeTextColor;
  14.         //TListView(C).BorderStyle:=bsNone;
  15.         SetWindowTheme(TWinControl(C).Handle, 'DarkMode_Explorer', NIL);
  16.         end;
  17. ...

d7_2_laz

  • Sr. Member
  • ****
  • Posts: 362
Re: Dark Theme in my program?
« Reply #16 on: March 20, 2023, 02:48:30 pm »
Thank you very much Erik@T!
For the small test snippet i applied this (without a loop) direcly onto 'ListView1'.
And interpreted that aDarkModeBgColor and aDarkModeTextColor are thought at to be user provided values (coould not fond a dfinition). Here i did set 002A2A2A and clWhite instead.
Very good!
I think the column header is a thing one can live with.
Fascinating!

Yes. here we need to do it on unit level (not project level). If i let it  (cal of ApplyDarkStyle within uwin32widgetsetdark) on project level and as well in one single unit, there's a stack ov.
Ok, so it (meaning: the includes and the call of ApplyDarkStyle)  here need to be done in each unit that wants to have the dark style. Is this right understood?



Erik@T

  • New Member
  • *
  • Posts: 15
Re: Dark Theme in my program?
« Reply #17 on: March 20, 2023, 03:23:18 pm »
Hi d7_2_laz, you only have to call uWin32WidgetSetDark.ApplyDarkStyle in your main form; other forms will follow the dark theme setting. Remember to give the user a choice wether to use the normal (light) theme or dark theme; and/or check the system settings in Windows. There are muliple examples of how to do that.

For the header of the ListView maybe you can use a separate HeaderControl? This one is themed. So in your ListView set ShowColumnHeaders to false and drop a separate HeaderControl just above the ListView. You should create some code to align/match the width of the HeaderControl.Sections and ListView.Columns (e.g. also sorting of items by clicking on column of ListView).

d7_2_laz

  • Sr. Member
  • ****
  • Posts: 362
Re: Dark Theme in my program?
« Reply #18 on: March 20, 2023, 03:39:39 pm »
Hi Erik@T, about "within main form (only") - that's good to know for to avoid unnecessary comlexity. Thank you!

About the listview header: yes, would be solution. Later .. if it should really bother some day ..

About the choice: yes indeed. actually i spreaded (due to customdrawing etc.) the usage via a DEFINE USE_DARKMODE that controls some IFs.
Already thought about - when doing it evaluating an inifile parameter - how to do a conditional "use" of the units. Not quite sure yet how to do it best, but it is foreseen.

My big respect to the author of the coding!

d7_2_laz

  • Sr. Member
  • ****
  • Posts: 362
Re: Dark Theme in my program?
« Reply #19 on: March 21, 2023, 01:10:40 pm »
@Erik@T: i'm so amazed and astonished of the smart code you pointed out and guided, that i'd like to share an image of an app written once for own needs in a first draft.
Applying the code itself is quite easy. Mostly i was dealing with to branch (*) and adopt own customdrawings accordingly (ongoing).

The text of statusbar panel[0] is not painted yet; i'm still digging were the flaw is on my side (in the simple test snippet project  it works).

Yes, thr listview column headers don't look nice. But i don't really love the idea to imply a second column header into the code.
==> You know maybe if the column headers will be part of the code one day?

(*) Finally i'd like to say some words about conditional usage of the dark mode controlled by an ini key; i'll do it in a separate reply later.

Erik@T

  • New Member
  • *
  • Posts: 15
Re: Dark Theme in my program?
« Reply #20 on: March 22, 2023, 04:22:19 pm »
Hi d7_2_laz, yes it's fun to darken applications.  :)

I did create a dark-mode unit for my Delphi XE2 applications (including DPI scaling for DPI-per-monitor). It was a lot of work (lots of interposer / interceptor classes) and later I found the Double Commander unit uwin32widgetsetdark. The Lazarus LCL is quite different then the Delphi VCL though.

For the TListView: maybe you can add it yourself to the uwin32widgetsetdark unit? Or you can ask for it in the Github of Double Commander (github.com/doublecmd/doublecmd). I think alexx2000 is doing most of the stuff for the dark LCL.

d7_2_laz

  • Sr. Member
  • ****
  • Posts: 362
Re: Dark Theme in my program?
« Reply #21 on: March 22, 2023, 05:52:40 pm »
Hi Erik@T, I appreciate your reply a lot, thanks!

Yes, it’s really a pleasure to work with the units and see the results.
The app with all the subforms and diverse controls is nearly ready. Only two things left.
1) The listview column headers (i had thought about for a short time to ownerdraw them, but hm, why that for each listview resp. specific listview component).
>> you can ask for it in the Github of DC  -> yes, definitely! I was just thinking about how to write, when you asked.

2) How to spread an ini-key information (dark or not?) best onto the layers?
For udarkstyle (for the form), as well as for uwin32widgetsetdark (for the widgets) - so that both optionally might exit just at the beginning -, as well as for the app itself.
Something like the "early config" (but as far as i can see, both units are not communicating with each other).
Reading the ini-key three times? Hm, un-nice
Reading it in the project file and using a global variable (never used such, intentionally)? Maybe, couldn't try it yet if it would work.
I'm waiting a bit for the case you should have any additional idea.

d7_2_laz

  • Sr. Member
  • ****
  • Posts: 362
Re: Dark Theme in my program?
« Reply #22 on: March 25, 2023, 02:47:29 pm »
I had posted both topics there, but not in the issues area (faily. it's not an issue neither of the DC itself, nor of the dark theme classes, which do not specifically target that), but within "discussions".
No reply yet, so no news here.

But - unfortunately - very late i saw a caveat (i had noticed it already before, but hadn't taken it as serious) that appears to me like a showstopper:
when the listview does  gets unfocused (eg. by clicking another control), then:
- the selection will dissppear (listview property "HideSelection := True"
- or the selection will appear painted white resp. lightgray (with "HideSelection := False").
Both it's absolutely not wanted within the use case.
Attached a picure and a code snippet project for to show.

I'm aware that for an unfocused listview intentionally the selection color changes. That's by design.
But it might get a problem when one is not able to define which color for that. See also:
https://forum.lazarus.freepascal.org/index.php/topic,46467.msg331269.html?PHPSESSID=07cc016cj0g9r3nqtqav8i4171#msg331269

It's not an issue of the dark theme files (and of the LCL definitively not of course).
It's more a howto thing whether it is possible to act around this listview restriction.
I'll open a separate topic here later, for the case that anybody should remember a good clue for to make this excellent tool fully useable.

Remark: it's not possible to handle that via customdraw, because when having lsot the focus, the control will not receive any messages.

paweld

  • Hero Member
  • *****
  • Posts: 670
Re: Dark Theme in my program?
« Reply #23 on: March 25, 2023, 03:27:25 pm »
Code: Pascal  [Select][+][-]
  1. procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView; Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
  2. var
  3.   r: TRect;
  4.   i: Integer;
  5. begin
  6.   if (cdsSelected in State) and (Sender.SelCount > 0) and (Item.Index = Sender.Selected.Index) then
  7.   begin
  8.     Sender.Canvas.Brush.Color := clHighlight;
  9.     r := Item.DisplayRect(drBounds);
  10.     Sender.Canvas.FillRect(r);
  11.     r.Right := r.Left + TListView(Sender).Columns[0].Width;
  12.     Sender.Canvas.TextRect(r, r.Left + 6, r.Top + 2, Item.Caption);
  13.     if TListView(Sender).ViewStyle = vsReport then
  14.     begin
  15.       for i := 0 to Item.SubItems.Count - 1 do
  16.       begin
  17.         r.Left := r.Left + TListView(Sender).Columns.Items[i].Width;
  18.         r.Right := r.Left + TListView(Sender).Columns.Items[i + 1].Width;
  19.         Sender.Canvas.TextRect(r, r.Left + 6, r.Top + 2, Item.SubItems[i]);
  20.       end;
  21.     end;
  22.     DefaultDraw := False;
  23.   end;
  24. end;    
Best regards / Pozdrawiam
paweld

d7_2_laz

  • Sr. Member
  • ****
  • Posts: 362
Re: Dark Theme in my program?
« Reply #24 on: March 25, 2023, 04:30:02 pm »
@paweld, oh, that looks perfect ..
... honestly, i had traced OnCustomDrawItem & Co extensively within the component (not ListView1 itself - my bad!), but here all the customdraw events no more took place when action is done on other controls / unfocused listview.
Ouch. 1000 Kudos!  Shows that basically it can work. Need to find out why not in my app. Resp. why the OnCustomDrawItem here is not reached resp. blocked by  some other layer involved. Thank you!


d7_2_laz

  • Sr. Member
  • ****
  • Posts: 362
Re: Dark Theme in my program?
« Reply #25 on: March 26, 2023, 01:30:15 pm »
Found the culprit within my own context, so all clear. Thankx paweld having pushed me back to it.

d7_2_laz

  • Sr. Member
  • ****
  • Posts: 362
Re: Dark Theme in my program?
« Reply #26 on: March 31, 2023, 09:10:37 am »
So back to the last remaining listview's dark painting puzzle.
After some sporadical digging i could achieve the Listview header's dark background with only a very few lines to change.
The key is: SetWindowTheme 'ItemsView::Header' for the header.
Un-nice caveat: the font color stays black, so hard to read.

The challenge would be to modify the font color within uwin32widgetsetdark.pas. So that not any using class depends on own subclassing, CNNotify ownerdraw & Co.
(Erik@T, if cou should read that, do you have any idea?)

Erik@T

  • New Member
  • *
  • Posts: 15
Re: Dark Theme in my program?
« Reply #27 on: March 31, 2023, 06:02:06 pm »
Hi d7_2_laz, I also did manage to color the headers of the ListView (in another way you did) but also not the header text color. But if you set the header color a little less dark, then the black text is visible. I will post the code later, I have to clean up the code first. I also have a few other tips for the ListView:

    ListView_SetTextBkColor(hwnd, SysColor[COLOR_WINDOW]);
    ListView_SetTextColor(hwnd, SysColor[COLOR_WINDOWTEXT]);

So you don't have to color each ListView in your unit.

d7_2_laz

  • Sr. Member
  • ****
  • Posts: 362
Re: Dark Theme in my program?
« Reply #28 on: March 31, 2023, 07:25:51 pm »
Thank your Erik@T!  Yes, i see …
Quote
if you set the (...) color a little less dark, then the black text is visible
Yes, that's exactly the way i went for controls with having access to, eg. the virtual stringtree where the "disabled" color was not good readable in dark mode, Needed only to be adapted a bit for the phase "before" themeing.
But regarding the header control itself,  doing something like:
        ListView_SetTextColor(hColHdr, <some value>)
won't be applicable, sad.
So i fear that the "dark classes" woould need the full stuff of subclassing ListView, evaluating WM_NOTIFY,  ownerdraw routines etc.   ...exactly what i hoped to avoid.
So i'd really be interested how you did that  ;)

Erik@T

  • New Member
  • *
  • Posts: 15
Re: Dark Theme in my program?
« Reply #29 on: March 31, 2023, 10:22:55 pm »
Hi d7_2_laz, I did some modifications and adds to "your" (now "ours") uwin32widgetsetdark.pas (also noted in top of file):

  - 20230330 E@T: Mod TStatusBar
  - 20230330 E@T: Add DoLog
  - 20230330 E@T: Mod All RGBToColor(255, 255, 255) to RGBToColor(245, 245, 245)
  - 20230330 E@T: Add TP_SPLITBUTTON, TP_SPLITBUTTONDROPDOWN (and DrawUpDownArrow)
  - 20230331 d7_2_laz: Add ListView in InterceptOpenThemeData
  - 20230331 E@T: Add TWin32WSCustomListViewDark
  - 20230331 E@T: Add teListView in DrawThemeBackgroundDark
  - 20230331 E@T: Add DrawListViewHeader in InterceptDrawThemeBackground

If you do a file compare then you see it all.
Functional:
- StatusBar did not draw the separators.
- I think the light texts are to light, I modified them from clear white to a little less white.
- The ToolBar DropDown button did not have the DropDown and arrow; added those.
- ListView is now completely "darkened" from within uwin32widgetsetdark.pas. You don't have to call SetWindowTheme(ListView1.Handle, 'DarkMode_Explorer', NIL) and colors in your unit anymore.
- ListView's header is now not white, not dark, but a little dark. I did not find a correct way to modify the header text color, but black on 'a little dark' is readable.

It's fun to modify uwin32widgetsetdark. I'm beginning to understand the code. Maybe we should post it to github.com/doublecmd but I've never put something on GitHub...
I tried this code on the Lazarus IDE (like a post in this form about darkening the IDE with qt5). It works but the problem is that all applications made in Lazarus now default have the dark colors. That's not what I want so I'm back on Lazarus+qt5. It looks good.

I've attached the code (zip) and an image, let's see how this works to show them in this post.

Regards, Erik

PS, maybe these links are usefull (ListView):
https://github.com/ysc3839/win32-darkmode/blob/master/win32-darkmode/ListViewUtil.h
https://osdn.net/projects/tortoisesvn/scm/svn/blobs/28786/trunk/src/Utils/Theme.cpp
« Last Edit: March 31, 2023, 10:40:45 pm by Erik@T »

 

TinyPortal © 2005-2018