Page 1 of 1

How to set background color in splitter gadget?

Posted: Wed Nov 12, 2014 3:55 pm
by Kukulkan
Hi,

I like to set the background of my program. Sadly, as soon as I start to use the splitter, the background colour is no longer set for the splitter content background.

Example:

Code: Select all

CompilerIf #PB_Compiler_Unicode
  #XmlEncoding = #PB_UTF8
CompilerElse
  #XmlEncoding = #PB_Ascii
CompilerEndIf

#Dialog = 0
#Xml = 0
#Image = 0

XML$ = "<window id='#PB_Any' name='test' text='test' minwidth='400' minheight='auto' flags='#PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget'>" +
       "<singlebox margin='10'>"+
       "  <splitter name='splitter' flags='#PB_Splitter_Separator'>"+
       "    <vbox align='left'>"+
       "      <button text='Some button' />"+
       "      <empty />"+
       "      <vbox>"+
       "        <button text='Example UI content' />"+
       "        <button text='Example UI content' />"+
       "      </vbox>"+
       "    </vbox>"+
       "    <vbox align='center'>"+
       "      <button text='Example UI content' />"+
       "      <button text='Example UI content' />"+
       "    </vbox>"+
       "  </splitter>"+
       "</singlebox>"+
       "</window>"


If CatchXML(#Xml, @XML$, StringByteLength(XML$), 0, #XmlEncoding) And XMLStatus(#Xml) = #PB_XML_Success
 
  If CreateDialog(#Dialog) And OpenXMLDialog(#Dialog, #Xml, "test")
    
    SetWindowColor(DialogWindow(#Dialog), RGB(200, 223, 205))

    Repeat
      Event = WaitWindowEvent()
    Until Event = #PB_Event_CloseWindow
   
  Else
    Debug "Error  -Dialog- : " + DialogError(#Dialog)
  EndIf
Else
  Debug "Error XML : " + XMLError(#Xml) + " (Line: " + XMLErrorLine(#Xml) + ")"
EndIf
I tried it with SetClassLong_(DialogGadget(#Dialog, "splitter"), #GCL_HBRBACKGROUND, CreateSolidBrush_(RGB(255,0,255))) but I've had no success.

BTW, I need this cross-platform.

Best,

Kukulkan

Re: How to set background color in splitter gadget?

Posted: Wed Nov 12, 2014 11:18 pm
by netmaestro
There are some containers and a splitter in this dialog and if all is to look right, they must all have colored backgrounds imho. So here's a thing, I'm pretty noobish when it comes to dialogs and while I could use DialogGadget() to retrieve the ID of the splitter, I had no luck with the containers. You probably know how to do that but I couldn't so I went to some lengths to get the IDs. Sorry for the extra code. Once I had those, It was a matter of subclassing the relevant controls and handling WM_ERASEBKGND with the appropriate hBrush and hPen. So this code is working on Windows. Unfortunately I know less about Linux and MacOS than I do about dialogs so someone else will have to help on those platforms. Wilbert can handle the MacOS without difficulty I'm sure, perhaps Idle for the Linux. But there are others. Anyway, here's Windows:

Code: Select all

Global hBrush = CreateSolidBrush_(RGB(200, 223, 205)), hPen = CreatePen_(#PS_SOLID, 1, RGB(200,223,205))
Global Dim containers(2), numcontainers=0

Procedure BackColorProc(hwnd, msg, wparam, lparam)
  oldproc = GetProp_(hwnd, "oldproc")
  gadget = GetDlgCtrlID_(hwnd)
  
  Select msg
    Case #WM_NCDESTROY
      RemoveProp_(hwnd, "oldproc")
      
    Case #WM_ERASEBKGND
      SelectObject_(wparam, hBrush)
      SelectObject_(wparam, hPen)
      GetClientRect_(hwnd, @r.RECT)
      Rectangle_(wparam, r\left, r\top, r\right, r\bottom)
      ProcedureReturn 1
      
  EndSelect
  
  ProcedureReturn CallWindowProc_(oldproc, hwnd, msg, wparam, lparam)
EndProcedure

Procedure Enummer(hwnd, lparam)
  cn$ = Space(100)
  GetClassName_(hwnd, @cn$, 100-SizeOf(Character))
  If cn$ = "PureContainer"
    containers(numcontainers) = GetDlgCtrlID_(hwnd)
    numcontainers+1
  EndIf
  ProcedureReturn 1
EndProcedure

CompilerIf #PB_Compiler_Unicode
  #XmlEncoding = #PB_UTF8
CompilerElse
  #XmlEncoding = #PB_Ascii
CompilerEndIf

#Dialog = 0
#Xml = 0
#Image = 0

XML$ = "<window id='#PB_Any' name='test' text='test' minwidth='400' minheight='auto' flags='#PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget'>" +
       "<singlebox name='singlebox' margin='10'>"+
       "  <splitter name='splitter' flags='#PB_Splitter_Separator'>"+
       "    <vbox align='left'>"+
       "      <button text='Some button' />"+
       "      <empty />"+
       "      <vbox>"+
       "        <button text='Example UI content' />"+
       "        <button text='Example UI content' />"+
       "      </vbox>"+
       "    </vbox>"+
       "    <vbox align='center'>"+
       "      <button text='Example UI content' />"+
       "      <button text='Example UI content' />"+
       "    </vbox>"+
       "  </splitter>"+
       "</singlebox>"+
       "</window>"

If CatchXML(#Xml, @XML$, StringByteLength(XML$), 0, #XmlEncoding) And XMLStatus(#Xml) = #PB_XML_Success
  
  If CreateDialog(#Dialog) And OpenXMLDialog(#Dialog, #Xml, "test")
    
    SetWindowColor(DialogWindow(#Dialog), RGB(200, 223, 205))
    
    ; Find the containers and enumerate them in an array
    EnumChildWindows_(WindowID((DialogWindow(#Dialog))), @Enummer(), 0) 
    
    ; Subclass the found containers
    For i=0 To numcontainers-1 
      SetProp_(GadgetID(containers(i)), "oldproc", SetWindowLongPtr_(GadgetID(containers(i)), #GWLP_WNDPROC, @BackColorProc()))
    Next
    
    ; and the splitter too. Now splitter and containers will
    ; have their backgrounds erased with the colored brush.
    hwndSplitter = GadgetID(DialogGadget(#dialog, "splitter"))
    SetProp_(hwndSplitter, "oldproc", SetWindowLongPtr_(hwndSplitter, #GWLP_WNDPROC, @BackColorProc())) 
    
    Repeat
      Event = WaitWindowEvent()
    Until Event = #PB_Event_CloseWindow
    
  Else
    Debug "Error  -Dialog- : " + DialogError(#Dialog)
  EndIf
Else
  Debug "Error XML : " + XMLError(#Xml) + " (Line: " + XMLErrorLine(#Xml) + ")"
EndIf

Re: How to set background color in splitter gadget?

Posted: Thu Nov 13, 2014 8:04 am
by Kukulkan
Hello netmaestro,

Wow, thank you for that great solution. If I run it here (Win7), it still has a grey one pixel border surrounding each button. Do you have the same or is it only my system?

Best,

Volker

Re: How to set background color in splitter gadget?

Posted: Thu Nov 13, 2014 8:15 am
by netmaestro
I have the same, it's probably part of the button.

Re: How to set background color in splitter gadget?

Posted: Thu Nov 13, 2014 10:43 am
by chi
If I run it here (Win7), it still has a grey one pixel border surrounding each button
Just return your desired brush on #WM_CTLCOLORBTN and you are good to go ;)

Code: Select all

Procedure BackColorProc(hwnd, msg, wparam, lparam)
  oldproc = GetProp_(hwnd, "oldproc")
  gadget = GetDlgCtrlID_(hwnd)
  
  Select msg
    Case #WM_NCDESTROY
      RemoveProp_(hwnd, "oldproc")
      
      ;------------------------------ added
    Case #WM_CTLCOLORBTN
      ProcedureReturn hBrush
      ;------------------------------
      
    Case #WM_ERASEBKGND
      SelectObject_(wparam, hBrush)
      SelectObject_(wparam, hPen)
      GetClientRect_(hwnd, @r.RECT)
      Rectangle_(wparam, r\left, r\top, r\right, r\bottom)
      ProcedureReturn 1
      
  EndSelect
  
  ProcedureReturn CallWindowProc_(oldproc, hwnd, msg, wparam, lparam)
EndProcedure

Re: How to set background color in splitter gadget?

Posted: Thu Nov 13, 2014 11:10 am
by Kukulkan
Hi,

thanks for the tipps. As I need it cross-platform, it turns out to be not that easy. I'll try to use my own drawn buttons. So I have full control over the colours.

Best,

Kukulkan

Re: How to set background color in splitter gadget?

Posted: Thu Nov 13, 2014 8:02 pm
by netmaestro
Windows will ignore brushes returned from WM_CTLCOLORBTN for all but ownerdrawn buttons. This is because there is just one WM_CTLCOLORBTN message to respond to but more than one color to draw depending upon the state of the button. So unless you've informed the OS that you're responsible for all drawing on the button, Windows will use its own brushes.

Re: How to set background color in splitter gadget?

Posted: Thu Nov 13, 2014 9:26 pm
by chi
Windows will ignore brushes returned from WM_CTLCOLORBTN for all but ownerdrawn buttons. This is because there is just one WM_CTLCOLORBTN message to respond to but more than one color to draw depending upon the state of the button. So unless you've informed the OS that you're responsible for all drawing on the button, Windows will use its own brushes.
Sounds like I'm doing something wrong her!? But, I'm pretty sure, I am not ;)

Code: Select all

Global oldProc, brushBG = CreateSolidBrush_(GetSysColor_(3))

Procedure WndCallback(hwnd, msg, wparam, lparam)
  Select msg
      
    Case #WM_CTLCOLORBTN
      ProcedureReturn brushBG
      
    Case #WM_CTLCOLORSTATIC
      SetBkMode_(wparam, #TRANSPARENT);
      ProcedureReturn brushBG   
      
  EndSelect
  ProcedureReturn CallWindowProc_(oldProc, hwnd, msg, wparam, lparam)
EndProcedure

OpenWindow(0, 0, 0, 320, 200, "", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget|#PB_Window_ScreenCentered)
SetClassLongPtr_(WindowID(0), #GCL_HBRBACKGROUND, brushBG)

oldProc = SetWindowLongPtr_(WindowID(0), #GWL_WNDPROC, @WndCallback())

ButtonGadget(0, 10, 10, 100, 40, "button")
ButtonGadget(1, 110, 10, 100, 40, "button")
TextGadget(2, 10, 60, 100, 40, "text")
TextGadget(3, 110, 60, 100, 40, "text")
OptionGadget(4, 10, 110, 100, 20, "option 1")
OptionGadget(5, 10, 130, 100, 20, "option 2")

While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend