ObjectColor (Dark Mode)

Share your advanced PureBasic knowledge/code with the community.
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: ObjectColor (Dark Mode)

Post by ChrisR »

BarryG wrote: Sun May 08, 2022 11:51 am That's what I'm currently doing. All windows are created invisibly at first, with all gadgets, and then I'm using SetObjectColor() on them - similar to the code I posted above.
If it is done so, with All gadgets created before SetObjectColor(), a single call to SetObjectColor() should probably be enough.
I don't know for your unpainted ListIconGadget in your pop-up window, ListIconProc() callback should be called normally.
BarryG wrote: Sun May 08, 2022 5:17 am may I suggest that "SetWindowColor(w,darkcolor)" be done as part of SetObjectColor()?
Done and uploaded to GitHub, it's better this way, thanks :)
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: ObjectColor (Dark Mode)

Post by ChrisR »

BarryG wrote: Sun May 08, 2022 11:51 am In my case, there's only a ListIconGadget() that doesn't get colored dark in that pop-up window, but its other two gadgets become dark.
Can you try adding this in WinCallback()

Code: Select all

Case #WM_ACTIVATE
  RedrawWindow_(hWnd, #Null, #Null, #RDW_INVALIDATE | #RDW_ERASE | #RDW_ALLCHILDREN | #RDW_UPDATENOW)
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: ObjectColor (Dark Mode)

Post by BarryG »

Testing for #WM_ACTIVATE didn't make any difference, ChrisR. Below is a (partial) screenshot of the affected window, where you can see the problems.

The ComboBox at top is dark, but its editable text is not white.
The ListIcon is not dark at all.
The Editor background is dark, but its text is not white.

I was doing this to the window:

Code: Select all

colordarkmode=RGB(50,50,50)
SetDarkTheme()
SetWindowColor(win,colordarkmode)
SetObjectColor(win,#PB_All,colordarkmode)
Image

Sorry for all the hassle!
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: ObjectColor (Dark Mode)

Post by ChrisR »

Sorry for the late reply, I was not available yesterday.

Good point BarryG,
I had not tested the SplitterGadget. It was not managed as a container and so the colors were not passed on to its children.

It is done now and it is also painted according to its parent background color.
Based on chi's SplitterExt Module :)
There are 3 constants for the Splitter that we can play with:

Code: Select all

#UseUxGripper   = #False   ; #False = Custom, #True = Uxtheme
#LargeGripper   = #True    ; #False
#SplitterBorder = #False   ; #True
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: ObjectColor (Dark Mode)

Post by ChrisR »

For the SplitterGadget Test, like yours:

Code: Select all

EnableExplicit

Enumeration Window
  #Window
EndEnumeration

Enumeration Gadgets
  #Combo
  #ListIcon
  #Editor
  #Splitter
EndEnumeration

XIncludeFile "ObjectColor.pbi"

Procedure Open_Window(X = 0, Y = 0, Width = 320, Height = 260)
  Protected I 
  If OpenWindow(#Window, X, Y, Width, Height, "Title", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    ComboBoxGadget(#Combo, 10, 10, 300, 28, #CBS_HASSTRINGS|#CBS_OWNERDRAWFIXED)
    For I = 1 To 5 : AddGadgetItem(#Combo, -1,"ComboBox Element " + Str(I)) : Next
    SetGadgetState(#Combo, 1)

    ListIconGadget(#ListIcon, 0, 0, 0, 0, "ListIcon Column 1", 180)
    AddGadgetColumn(#ListIcon, 1, "Column 2", 120)
    For I = 1 To 5 : AddGadgetItem(#ListIcon, -1,"Column 1 Element " + Str(I) +Chr(10)+ "Column 2 Element " + Str(I)) : Next
    EditorGadget(#Editor, 0, 0, 0, 0)
    For I = 0 To 5 : AddGadgetItem(#Editor, I, "Editor Line " + Str(I)) : Next 
    
    SplitterGadget(#Splitter, 10, 50, 300, 200, #ListIcon, #Editor, #PB_Splitter_Separator)
    SetGadgetState(#Splitter, 110)
  EndIf
EndProcedure

Open_Window()

SetDarkTheme()
SetObjectColor(#Window, #PB_All, RGB(50,50,50))

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow

Image
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: ObjectColor (Dark Mode)

Post by BarryG »

Hi ChrisR! Thanks for the update! I appreciate your help. Hope you're not taking offense to all these issues - I just want your code to be rock-solid so it can be dropped into any app and work flawlessly. <Wink>. Only three small issues left at the moment.

Issue 1

I can confirm the latest code works better with my window now except for one bit: the background color of an editable ComboBox is still white instead of dark, like in the screenshot and test code below.

Image

My test code:

Code: Select all

XIncludeFile "ObjectColor.pbi"

Enumeration Window
  #Window_1
  #Window_2
EndEnumeration

Enumeration Gadgets
  #Combo
  #ListIcon
  #Editor
  #Splitter
EndEnumeration

If OpenWindow(#Window_1, 200, 200, 320, 60, "Window 1", #PB_Window_SystemMenu)
  button = ButtonGadget(#PB_Any, 10, 20, 300, 25, "Click to open second window")
EndIf

If OpenWindow(#Window_2, 600, 200, 320, 260, "Window 2", #PB_Window_SystemMenu | #PB_Window_Invisible)
  ComboBoxGadget(#Combo, 10, 10, 300, 28, #PB_ComboBox_Editable | #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED)
  For I = 1 To 5 : AddGadgetItem(#Combo, -1,"ComboBox Element " + Str(I)) : Next
  SetGadgetState(#Combo, 1)
  
  ListIconGadget(#ListIcon, 0, 0, 0, 0, "ListIcon Column 1", 180)
  AddGadgetColumn(#ListIcon, 1, "Column 2", 120)
  For I = 1 To 5 : AddGadgetItem(#ListIcon, -1,"Column 1 Element " + Str(I) +Chr(10)+ "Column 2 Element " + Str(I)) : Next
  EditorGadget(#Editor, 0, 0, 0, 0)
  For I = 0 To 5 : AddGadgetItem(#Editor, I, "Editor Line " + Str(I)) : Next 
  
  SplitterGadget(#Splitter, 10, 50, 300, 200, #ListIcon, #Editor, #PB_Splitter_Separator)
  SetGadgetState(#Splitter, 110)
EndIf

SetDarkTheme()
SetObjectColor(#PB_All, #PB_All, RGB(50,50,50))

Repeat
  ev = WaitWindowEvent()
  If ev = #PB_Event_Gadget And EventGadget() = button
    HideWindow(#Window_2, #False)
  EndIf
Until ev = #PB_Event_CloseWindow
Issue 2

I have another window that gets opened on demand by the app (after selecting a menu item on the main window to open it), and only its background color goes dark. Its TextGadget and ListIcon don't go dark at all. See the below screenshot of its top-right corner (for privacy) and the code I'm using to open it and make it dark (not compilable, but surely it shows if I'm doing something wrong?).

Image

Code: Select all

win=OpenWindow(#PB_Any,0,0,w,h,"Window",#PB_Window_Invisible|#PB_Window_SystemMenu|#PB_Window_WindowCentered,app) ; app = Main app window.
If win
  txt=TextGadget(#PB_Any,6,5,w-(15),20,"text text",#PB_Text_Center)
  lig=ListIconGadget(#PB_Any,0,29,w,h-(29),n$,w-sw,#LVS_NOCOLUMNHEADER|#PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection|#PB_ListIcon_MultiSelect|#PB_ListIcon_GridLines)
  AddGadgetColumn(lig,1,"Header",0)
  For i=1 To ws
    AddGadgetItem(lig,-1,w$(i))
  Next
  SetObjectColor(win,#PB_All,colordarkmode) ; Doesn't make TXT or LIG gadgets go dark.
  SetActiveGadget(lig)
  HideWindow(win,0)
  Repeat
    ev=WaitWindowEvent()
    ...
Issue 3

When my main app's window is hidden (HideWindow) and then shown, or unminimized from a minimized state, the ListIcon backgrounds show white briefly before changing back to dark. A noticeable flash. Any tips on how to stop that? Thanks.
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: ObjectColor (Dark Mode)

Post by ChrisR »

BarryG wrote: Wed May 11, 2022 2:08 am Hi ChrisR! Thanks for the update! I appreciate your help. Hope you're not taking offense to all these issues - I just want your code to be rock-solid so it can be dropped into any app and work flawlessly. <Wink>. Only three small issues left at the moment.
You're welcome, no worries at all, it allows to improve the tools :)
I would also like it to be as complete as possible and there are still a few things missing but it's progressing, slowly, slowly
For some stuff, like the icons on the tabs, I would need examples as a starting point, I don't know how to do.

For your 1st issue, for the Editable ComboBox, I have updated to also paint the Edit part with the colors now.
And on Windows 10 with the DarkTheme, the ComboBox Image is now partially supported by applying the theme on its chid.
Do not add #CBS_HASSTRINGS|#CBS_OWNERDRAWFIXED for ComboBox Image, only ComboBoxGadget(#Combo,X,Y,W,H,#PB_ComboBox_Image)

For the 3rd, I don't know, I added the code below but not sure if it changes anything:

Code: Select all

    Case #WM_SIZE
      If wParam = #SIZE_RESTORED   ; Not sure if there is a need
        For I = 0 To CountWindow - 1
          If Window(1, I) = hWnd
            RedrawWindow_(hWnd, #Null, #Null, #RDW_INVALIDATE | #RDW_ERASE | #RDW_ALLCHILDREN | #RDW_UPDATENOW)
            Break
          EndIf
        Next
      EndIf
It's uploaded on Github
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: ObjectColor (Dark Mode)

Post by ChrisR »

BarryG wrote: Wed May 11, 2022 2:08 am I have another window that gets opened on demand by the app (after selecting a menu item on the main window to open it), and only its background color goes dark. Its TextGadget and ListIcon don't go dark at all. See the below screenshot of its top-right corner (for privacy) and the code I'm using to open it and make it dark (not compilable, but surely it shows if I'm doing something wrong?).
Hi BarryG,
I don't really know why, I don't reproduce with the code below.
Maybe you have several calls to SetObjectColor.
For info, unless the color changes, it should only be called once.
The list (enumerations) of Windows and Gadgets is made only once, at the first use.
And so, to works, all windows and gadgets must be created before the 1st call with #PB_Window_Invisible if needed.

Code: Select all

XIncludeFile "ObjectColor.pbi"

Enumeration Window
  #Window_1
  #Window_2
EndEnumeration

Enumeration Gadgets
  #Combo
  #ListIcon
  #Editor
  #Splitter
EndEnumeration

Global Win, Button, Event, I

If OpenWindow(#Window_1, 200, 200, 320, 260, "Window 1", #PB_Window_SystemMenu)
  Button = ButtonGadget(#PB_Any, 10, 15, 300, 25, "Click to open second window")
EndIf

Win = OpenWindow(#PB_Any, 200, 300, 320, 100, "Window", #PB_Window_Invisible | #PB_Window_SystemMenu | #PB_Window_WindowCentered, WindowID(#Window_1)) ; app = Main app window.
If Win
  Define Txt = TextGadget(#PB_Any, 6, 5, 300, 20, "text text", #PB_Text_Center)
  Define Lig = ListIconGadget(#PB_Any, 0, 29, 320, 60, "ListIcon", 180, #LVS_NOCOLUMNHEADER | #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection | #PB_ListIcon_MultiSelect | #PB_ListIcon_GridLines)
  AddGadgetColumn(Lig, 1, "Header", 0)
  For i = 1 To 5
    AddGadgetItem(Lig,  -1, "Element " + Str(i))
  Next
  ; SetObjectColor(Win, #PB_All, RGB(50,50,50))
  ; If SetObjectColor() is done here, the Windows And Gadgets created after are Not enumerated.
  ; Even If SetObjectColor() is called afterwards For all windows (#PB_All). The Windows And Gadgets Enumeration is done only once.
  SetActiveGadget(Lig)
EndIf

If OpenWindow(#Window_2, 600, 200, 320, 260, "Window 2", #PB_Window_SystemMenu | #PB_Window_Invisible)
  ComboBoxGadget(#Combo, 10, 10, 300, 28, #PB_ComboBox_Editable | #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED)
  For I = 1 To 5 : AddGadgetItem(#Combo, -1, "ComboBox Element " + Str(I)) : Next
  SetGadgetState(#Combo, 1)
  
  ListIconGadget(#ListIcon, 0, 0, 0, 0, "ListIcon Column 1", 180)
  AddGadgetColumn(#ListIcon, 1, "Column 2", 120)
  For I = 1 To 5 : AddGadgetItem(#ListIcon, -1, "Column 1 Element " + Str(I) + Chr(10) + "Column 2 Element " + Str(I)) : Next
  EditorGadget(#Editor, 0, 0, 0, 0)
  For I = 0 To 5 : AddGadgetItem(#Editor, I, "Editor Line " + Str(I)) : Next
  
  SplitterGadget(#Splitter, 10, 50, 300, 200, #ListIcon, #Editor, #PB_Splitter_Separator)
  SetGadgetState(#Splitter, 110)
EndIf

SetDarkTheme()
SetObjectColor(#PB_All, #PB_All, RGB(50, 50, 50))

Repeat
  Event = WaitWindowEvent()
  Select Event
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventGadget() = Button
        HideWindow(#Window_2, #False)
        HideWindow(Win, #False)
      EndIf
  EndSelect
ForEver
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: ObjectColor (Dark Mode)

Post by BarryG »

ChrisR wrote:Maybe you have several calls to SetObjectColor.
The list (enumerations) of Windows and Gadgets is made only once, at the first use.
And so, to works, all windows and gadgets must be created before the 1st call with #PB_Window_Invisible if needed.
Oh... I think that's my problem. Yes, I've been using "SetObjectColor(win,#PB_All,colordarkmode" after every OpenWindow command. So I need to create all the windows first, then just one single call of SetObjectColor() to cover them all? Dang, that won't work for me as a lot of my windows are created on demand because they're not needed until the user chooses, and there's no way for my app to know how many windows the user will want to open. So I don't think this is going to work for me after all. <Sad>. It'd be much better if dark mode could somehow be applied to a window after it's been created.

Sorry for wasting your time due to my misunderstanding. I fear you may have made bug fixes or workarounds where none were actually needed.
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: ObjectColor (Dark Mode)

Post by ChrisR »

@BarryG, In relation to your (previous!) message on JellyButton disabled, badly displayed.
I handle the WM_ENABLE message now with an InvalidateRect(). It seems to fix the JellyButton display.
And in addition to the grey text, I added an image disabled, greyed, to better see its state.
JellyButton is available in IceDesign.
Last edited by ChrisR on Fri May 13, 2022 4:07 pm, edited 1 time in total.
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: ObjectColor (Dark Mode)

Post by BarryG »

Thanks, downloaded. I did try the original JellyButton code in this forum without using your ObjectColor code, and it also showed the problem. And the reason I removed my original JellyButton bug report was because I assumed it was due to my incorrect use of ObjectColor, so I didn't want you to chase a possible non-bug.

I'll try it tomorrow as I'm leaving the house in a few minutes. I have no doubts it'll work, though.

[Edit] Yep, I'm happy to report that your new version works correctly.
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: ObjectColor (Dark Mode)

Post by ChrisR »

Update ObjectColor v1.3.0

  • Supports as many SetObjectColor() calls as desired, even with dynamic Windows.
  • The theme Light: "Explorer" or Dark:" DarkMode_Explorer" (Windows 10 and up) is automatically applied according to the OS version and Background Color of each Gadget.

    About Theme, after calling SetObjectColor(), you can change the applied theme by using one of the 2 macros: SetDarkTheme(Gadget) or SetExplorerTheme(Gadget)
    For example, if you have a Window with a Dark Background Color and a ScrollArea with a Light Color,
    By default, the Light Theme will be applied for the ScollArea and its ScrollBars related to the ScrollArea Background Color.
    But you may want to change it to a Dark Theme to match the Window which is in Dark Color. It would be like this

    Code: Select all

    SetObjectColor(#PB_All, #PB_All, #Black)
    SetObjectColor(#Windows, #ScrollArea, #Gray)
    SetDarkTheme(#ScrollArea)
Fred
Administrator
Administrator
Posts: 16617
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: ObjectColor (Dark Mode)

Post by Fred »

It's really a great piece a work ! Dark mode on Windows is a pain to achieve
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: ObjectColor (Dark Mode)

Post by ChrisR »

Thanks Fred :)
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: ObjectColor (Dark Mode)

Post by BarryG »

ChrisR wrote: Fri Jan 27, 2023 3:40 pmSupports as many SetObjectColor() calls as desired, even with dynamic Windows.
Does this mean I can use it on windows that are created on-the-fly now? Like for my app where the number of windows is indeterminate and are opened/closed during runtime of my app?
Post Reply