reskinning an application (->color questions)

Windows specific forum
broozar
User
User
Posts: 61
Joined: Sat May 08, 2010 11:21 pm
Location: Berlin, Germany

reskinning an application (->color questions)

Post by broozar »

hi all,

i am currently in the process of reskinning/recoloring my application. i found a handy solution for linux earlier today (http://www.purebasic.fr/english/viewtop ... 15&t=58981), now it's on to windows. as i am not a windows expert, i would be thankful for any suggestion you could give me.

the problem, in a nutshell: i want to recolor gadgets to make a grey/dark theme. setting colors through PB is supported by many of them, but not all. i am looking at 2 options:
1. recolor every gadget manually through a windows api function call ---> please share code samples if you know any, thanks!
2. override the theme on a per-application basis ---> like i did with the gtkrc file on linux. is that even possible?

let me know what you think.

cheers
felix
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: reskinning an application (->color questions)

Post by IdeasVacuum »

For the odd gadget such as CheckBoxGadget(), there is no colour option and it does not respect the Window colour. There is an API method to fix the back colour, but you can draw your own CheckBoxGadget() on a tiny canvas and control everything. Same thing with OptionGadget, there are a few examples of 'DIY' gadgets on this forum.

To colour buttons, I suggest drawing on ButtonImageGadgets(), that is very easy and the Windows Theme does not interfere. Again, you will find examples on this forum.

If you prefer the API way, this is a code snippet published by Rashad:

Code: Select all

Procedure.i WindowCallBack(WindowId.i, Msg.i, wParam.i, lParam.i)
;----------------------------------------------------------------
Protected iReturn.i

   If Msg = #WM_CTLCOLORSTATIC

          Select GetDlgCtrlID_(lParam)   ;API call: allows choosing gadgets by their number
                  Case #MyOpt0, #MyOpt1, #MyOpt2, #MyChkBx0, #MyChkBx1, #MyChkBx2
                     ; uncomment if compiling without XP support
                     ; SetBkMode_(wParam,#TRANSPARENT)
                         SetBkColor_(wParam, RGB(0,0,0))
                       SetTextColor_(wParam, RGB(0,0,255))
                       iReturn = #False
          EndSelect
   EndIf

   ProcedureReturn iReturn

EndProcedure
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
broozar
User
User
Posts: 61
Joined: Sat May 08, 2010 11:21 pm
Location: Berlin, Germany

Re: reskinning an application (->color questions)

Post by broozar »

thanks! CheckBoxes, Buttons, Frames and Tabs are exactly what gives me trouble.
I would indeed prefer to do it all by API. it might take me longer, but i might learn something about windows along the way.

Please help me one more step. How do I use the callback correctly? i currently have

Code: Select all

SetWindowCallback(@WindowCallBack())
which leads to my buttons, checkboxes, frames and progress bars disappearing, and the window generally hangs (window close button does nothing, procedures that are supposed to load text from a txt file on the hard drive are not executed)
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: reskinning an application (->color questions)

Post by Danilo »

broozar wrote:Please help me one more step. How do I use the callback correctly? i currently have

Code: Select all

SetWindowCallback(@WindowCallBack())
which leads to my buttons, checkboxes, frames and progress bars disappearing, and the window generally hangs (window close button does nothing, procedures that are supposed to load text from a txt file on the hard drive are not executed)
Please see the help for SetWindowCallback() for an example. When returning other values than #PB_ProcessPureBasicEvents,
PureBasic does no further event handling. You need to return #PB_ProcessPureBasicEvents by default, and only return other
values (like a color, or #False/#True) to API directly, when no PB event processing is required for that message.
broozar
User
User
Posts: 61
Joined: Sat May 08, 2010 11:21 pm
Location: Berlin, Germany

Re: reskinning an application (->color questions)

Post by broozar »

thanks! i visited the help page before, but did not spot the #PB_ProcessPureBasicEvents line.
no more hangs this way, but also no coloring. May I just assume that checkboxes cannot be colored that way? I see the callback procedure firing since i added some debug calls, but the color remains unchanged.
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: reskinning an application (->color questions)

Post by IdeasVacuum »

It should work, it is working in one of my old apps, for Options and CheckBoxes, but I can't get it to work either. Must be something subtle that I'm overlooking. One of the forum experts will no doubt spot it immediately.
Sample code PB5.22LTS:

Code: Select all

Enumeration
#Win
#Font10
#Font14B
#MyOpt0
#MyOpt1
#MyOpt2
#MyChkBx0
#MyChkBx1
#MyChkBx2
#BtnImg
#BtnExit
EndEnumeration

LoadFont(#Font10,  "Arial", 10, #PB_Font_Bold | #PB_Font_HighQuality + 2)
LoadFont(#Font14B, "Arial Black", 14, #PB_Font_HighQuality + 2)

Global  igWinColour.i = RGB(036,036,036)
Global  igTxtColour.i = RGB(186,090,087)
;Global igBackColour.i = CreateSolidBrush_(igWinColour)

Procedure DrawBtn(iBtnID.i, sText.s, iW.i, iH.i)
;-----------------------------------------------
Protected iBtnImg.i, dX.d, dY.d, dW.d, dH.d

               CreateImage(#BtnImg, iW, iH, 24)

               If StartDrawing(ImageOutput(#BtnImg))

                             DrawingFont(FontID(#Font14B))

                             dH = TextHeight(sText)
                             dW = TextWidth(sText)
                             dX = ((iW - dW) / 2)
                             dY = ((iH - dH) / 2)

                             DrawingMode(#PB_2DDrawing_Default)
                                     Box(0,0,iW,iH,igWinColour)
                                DrawText(dX, dY, sText, igTxtColour, igWinColour)
                      SetGadgetAttribute(iBtnID, #PB_Button_Image, ImageID(#BtnImg))
                             StopDrawing()
               EndIf
EndProcedure

Procedure.i WindowCallBack(WindowId.i, iMsg.i, wParam.i, lParam.i)
;-----------------------------------------------------------------
Protected iReturn.i = #PB_ProcessPureBasicEvents

               If iMsg = #WM_CTLCOLORSTATIC

                      Select GetDlgCtrlID_(lParam)

                              ;API call: allows choosing gadgets by their number
                              ;Comment out Select to apply to all gadgets

                              Case #MyOpt0, #MyOpt1, #MyOpt2, #MyChkBx0, #MyChkBx1, #MyChkBx2
                                   ;uncomment if compiling without XP support
                                   ;SetBkMode_(wParam, #TRANSPARENT)
                                     SetBkColor_(wParam, igWinColour)
                                   SetTextColor_(wParam, igTxtColour)
                                   iReturn = #False
                      EndSelect
               EndIf

               ProcedureReturn iReturn
EndProcedure

Procedure Win()
;--------------
Protected iFlags.i = #PB_Window_Invisible | #PB_Window_SystemMenu | #PB_Window_ScreenCentered

               If OpenWindow(#Win, 0, 0, 300, 130, "Colour", iFlags)

                              SetWindowColor(#Win, igWinColour)
                               SetGadgetFont(#PB_All, FontID(#Font10))

                                OptionGadget(#MyOpt0,    20,  10, 120, 20, "Option 0")
                                OptionGadget(#MyOpt1,    20,  40, 120, 20, "Option 1")
                                OptionGadget(#MyOpt2,    20,  70, 120, 20, "Option 2")
                              CheckBoxGadget(#MyChkBx0, 150,  10, 120, 20, "Check Box 0")
                              CheckBoxGadget(#MyChkBx1, 150,  40, 120, 20, "Check Box 1")
                              CheckBoxGadget(#MyChkBx2, 150,  70, 120, 20, "Check Box 2")

                           ButtonImageGadget(#BtnExit,    0, 100, 300, 30, 0)
                                     DrawBtn(#BtnExit, "EXIT", 294, 24)

                           SetWindowCallback(@WindowCallBack(), #Win)
                                  HideWindow(#Win, #False)
               EndIf
EndProcedure

Procedure WaitForUser()
;----------------------
Protected iQuit.i = #False

               Repeat
                         Select WaitWindowEvent(1)

                               Case #PB_Event_CloseWindow: iQuit = #True
                               Case #PB_Event_Gadget

                                     Select EventGadget()

                                                 Case  #BtnExit: iQuit = #True
                                     EndSelect
                         EndSelect

                         Delay(1)

               Until iQuit = #True
EndProcedure

Win()
WaitForUser()

End
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
broozar
User
User
Posts: 61
Joined: Sat May 08, 2010 11:21 pm
Location: Berlin, Germany

Re: reskinning an application (->color questions)

Post by broozar »

thanks for the snippet!
IdeasVacuum wrote:it is working in one of my old apps
i tried to compile with 5.22, 5.00, as well as 4.51, no success. do you by any chance remember the version where it did still work, in case nobody comes up with a better solution?
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4954
Joined: Sun Apr 12, 2009 6:27 am

Re: reskinning an application (->color questions)

Post by RASHAD »

Code: Select all

Global Bkgcolor_1,Bkgcolor_2,Bkgcolor_3,Bkgcolor_4

Bkgcolor_1  = CreateSolidBrush_($88FCF9)
Bkgcolor_2  = CreateSolidBrush_($88FCA4)
Bkgcolor_3  = CreateSolidBrush_($F4FC88)
Bkgcolor_4  = CreateSolidBrush_($FB89DF)

CreateImage(0,80,22)
StartDrawing(ImageOutput(0))
    Box(0,0,80,22,#Yellow)
    DrawingMode(#PB_2DDrawing_Transparent )
    FrontColor(#Blue)
    DrawText(18,3,"TEST")
StopDrawing()

Procedure WindowProc(hwnd, uMsg, wParam, lParam) 
    Select uMSG
     Case #WM_CTLCOLORSTATIC
      Select GetProp_(lParam, "PB_ID")
         Case 1
               SetBkMode_(wParam,#TRANSPARENT	)
               SetTextColor_(wParam,#Black)
               ProcedureReturn Bkgcolor_1
           
         Case 2
                SetBkMode_(wParam,#TRANSPARENT	)
                SetTextColor_(wParam,#Red)
                ProcedureReturn Bkgcolor_2
            
          Case 3
                SetBkMode_(wParam,#TRANSPARENT	)
                SetTextColor_(wParam,#Blue)
                ProcedureReturn Bkgcolor_3
            
          Case 4
                SetBkMode_(wParam,#TRANSPARENT	)
                SetTextColor_(wParam,#Yellow)
                ProcedureReturn Bkgcolor_4
         
        EndSelect
    EndSelect    
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

OpenWindow(0,0,0,400,300,"test", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
SetWindowColor(0,#Gray)
SetWindowCallback(@WindowProc())

CheckBoxGadget(1,10,10,120,20,"CheckBox # 1")
CheckBoxGadget(2,10,40,120,20,"CheckBox # 2")
OptionGadget(3,140,10,120,20,"Option # 1")
OptionGadget(4,140,40,120,20,"Option # 2")

For Gadget = 1 To 4
  SetWindowTheme_(GadgetID(Gadget), @null.w, @null.w)
Next

ButtonImageGadget(5,10,70,80,22,ImageID(0))

Repeat
  Select WaitWindowEvent()
      
       Case #PB_Event_CloseWindow
            Quit = 1
            
       Case #PB_Event_Gadget
          Select EventGadget()

          EndSelect
  EndSelect
Until Quit = 1
Egypt my love
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: reskinning an application (->color questions)

Post by IdeasVacuum »

Hi broozar, it is working in my old app, coded with PB4.61. Tried my snippet in #PB4.61, does not work, though the debug shows that the gadget ID's are picked up correctly.

Hi Rashad - the code I used in my old app to set the back colour and text colour for option and check-box gadgets was posted by you, for me - it didn't use CreateSolidBrush though I have tried that (failed), and in any case that would not fix the text colour which is also not working. SetBkMode does not work either but as I understand it, cannot be used anyway on XP or Win7 if the User has set a fancy theme.

Edit: Just had a thought, is it pallet related? Can the colours only be chosen from a limited range?
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
broozar
User
User
Posts: 61
Joined: Sat May 08, 2010 11:21 pm
Location: Berlin, Germany

Re: reskinning an application (->color questions)

Post by broozar »

@RASHAD Thanks so much! Checkboxes, labels and buttons work perfectly now. 3 gadgets remain problematic, maybe you have some idea on how to solve these last issues:
1. Tabs do not seem to be affected at all, except for SetWindowTheme_, so i know i have the right gadget(s) targeted
2. Frames: labels are great, the lines around them are pretty white and wide. Any way to thin them out or recolor them?
3. StringGadgets/Inputs: like Frames, they have an ugly border. SetWindowTheme with @null diminishes the effect, but it still is not as pretty as it could be.

i also noticed the call to SetWindowTheme_ which looks very interesting. If i hypothetically made a Windows theme file, could i use this function to override the theme of my application all in one go?

cheers
felix
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4954
Joined: Sun Apr 12, 2009 6:27 am

Re: reskinning an application (->color questions)

Post by RASHAD »

Full color control Panel Gadget

Code: Select all

Global Bkgcolor

Bkgcolor  = CreateSolidBrush_($D5FEFD)

CreateImage(0,64,20)                           
StartDrawing(ImageOutput(0))
  DrawingMode(#PB_2DDrawing_Gradient)     
  BackColor($1EFDFD)
  FrontColor($7878FE)
  BoxedGradient(0, 0, 64, 20)               
  Box(0,0,64,20)
  DrawingMode(#PB_2DDrawing_Transparent)                         
  DrawText(4, 2, "Tab One",#Red)                     
StopDrawing()

CreateImage(1,64,20)                           
StartDrawing(ImageOutput(1))           
  Box(0,0,64,20,$BDFEBA)
  DrawingMode(#PB_2DDrawing_Transparent)                         
  DrawText(4, 2, "Tab Two",#Black)                           
StopDrawing()
                                     
Imlist = ImageList_Create_(64,20,#ILC_COLOR32,2,10)   
ImageList_Add_(Imlist,ImageID(0),0)                   
ImageList_Add_(Imlist,ImageID(1),0)                   

OpenWindow(0,0,0,400,300,"Full Control Color Panel Gadget",#PB_Window_SystemMenu| #PB_Window_ScreenCentered)
SetWindowColor(0,$FEFED1)
  PanelGadget(0,10,10,380,280)
    SendMessage_(GadgetID(0),#TCM_SETPADDING,0,1|3<<16)   
    SendMessage_(GadgetID(0),#TCM_SETIMAGELIST,0,Imlist)
    AddGadgetItem(0,0,"")
        ButtonGadget(2, 10, 15, 80, 24,"Tab 1 Button 1")
        ButtonGadget(3, 95, 15, 80, 24,"Tab 1 Button 2")
    AddGadgetItem(0,1,"")
        ButtonGadget(4, 10, 15, 80, 24,"Tab 2 Button 3")
        ButtonGadget(5, 95, 15, 80, 24,"Tab 2 Button 4")
  CloseGadgetList()

t.TC_ITEM
t\mask = #TCIF_IMAGE
t\iImage = 0
SendMessage_(GadgetID(0),#TCM_SETITEM, 0, @t)
t\iImage = 1
SendMessage_(GadgetID(0),#TCM_SETITEM, 1, @t)

;Comment for the next lines for normal background Color
SetWindowTheme_(GadgetID(0), @null.w, @null.w)
SetClassLongPtr_(GadgetID(0),#GCL_HBRBACKGROUND, Bkgcolor)

TextGadget(10,148,10,250,24,"")
SetGadgetColor(10,#PB_Gadget_BackColor,$FEFED1)
BringWindowToTop_(GadgetID(10))

Repeat
  Select WaitWindowEvent()
     
       Case #PB_Event_CloseWindow
            Quit = 1
  EndSelect
Until Quit = 1
Last edited by RASHAD on Sun Apr 13, 2014 11:04 pm, edited 1 time in total.
Egypt my love
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4954
Joined: Sun Apr 12, 2009 6:27 am

Re: reskinning an application (->color questions)

Post by RASHAD »

Full text & color control String Gadget
Text Val. & Hal centered (Work around :mrgreen: )

Code: Select all


CreateImage(0,300,24)
StartDrawing(ImageOutput(0))
Box(0,0,300,24,#Red)
Box(1,1,298,22,$D3FEFD)
StopDrawing()

OpenWindow(0,0,0,400,300,"", #PB_Window_SystemMenu|#PB_Window_SizeGadget| #PB_Window_ScreenCentered) 

ImageGadget(0,10,10,120,24,ImageID(0))
DisableGadget(0,1)
ContainerGadget(1, 10,10,300,24) 
    StringGadget(2,3,4,296,16,"",#PB_String_BorderLess| #ES_center) 
    SetGadgetColor(2,#PB_Gadget_BackColor,$D5FEFD)
    SetGadgetColor(2,#PB_Gadget_FrontColor,$FF3904)
CloseGadgetList()

SetClassLongPtr_(GadgetID(1),#GCL_HBRBACKGROUND, GetStockObject_(#NULL_BRUSH))

Repeat:Until WaitWindowEvent()=#WM_CLOSE 
Egypt my love
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: reskinning an application (->color questions)

Post by IdeasVacuum »

For the StringGadgets, why not use #PB_String_BorderLess?

Revised snippet using Rashad's code, Tested working fine on Win8:

Code: Select all

Enumeration
#Win
#Font10
#Font14B
#BtnImg
#MyOpt0
#MyOpt1
#MyOpt2
#MyChkBx0
#MyChkBx1
#MyChkBx2
#BtnExit
EndEnumeration

LoadFont(#Font10,  "Arial", 10, #PB_Font_Bold | #PB_Font_HighQuality + 2)
LoadFont(#Font14B, "Arial Black", 14, #PB_Font_HighQuality + 2)

Global  igWinColour.i = RGB(036,036,036)
Global  igTxtColour.i = RGB(186,090,087)
Global igBackColour.i = CreateSolidBrush_(igWinColour)

Procedure DrawBtn(iBtnID.i, sText.s, iW.i, iH.i)
;-----------------------------------------------
Protected iBtnImg.i, dX.d, dY.d, dW.d, dH.d

               CreateImage(#BtnImg, iW, iH, 24)

               If StartDrawing(ImageOutput(#BtnImg))

                             DrawingFont(FontID(#Font14B))

                             dH = TextHeight(sText)
                             dW = TextWidth(sText)
                             dX = ((iW - dW) / 2)
                             dY = ((iH - dH) / 2)

                             DrawingMode(#PB_2DDrawing_Default)
                                     Box(0,0,iW,iH,igWinColour)
                                DrawText(dX, dY, sText, igTxtColour, igWinColour)
                      SetGadgetAttribute(iBtnID, #PB_Button_Image, ImageID(#BtnImg))
                             StopDrawing()
               EndIf
EndProcedure

Procedure.i WindowCallBack(WindowId.i, iMsg.i, wParam.i, lParam.i)
;-----------------------------------------------------------------
Protected iReturn.i = #PB_ProcessPureBasicEvents

               If iMsg = #WM_CTLCOLORSTATIC

                      Select GetProp_(lParam, "PB_ID")

                              ;API call: allows choosing gadgets by their number
                              ;Comment out Select to apply to all gadgets

                              Case #MyOpt0, #MyOpt1, #MyOpt2, #MyChkBx0, #MyChkBx1, #MyChkBx2
                                   ;uncomment if compiling without XP support
                                      SetBkMode_(wParam, #TRANSPARENT)
                                     ;SetBkColor_(wParam,igWinColour)
                                    SetTextColor_(wParam,igTxtColour)
                                   iReturn = igBackColour
                      EndSelect
               EndIf

               ProcedureReturn iReturn
EndProcedure

Procedure Win()
;--------------
Protected iFlags.i = #PB_Window_Invisible | #PB_Window_SystemMenu | #PB_Window_ScreenCentered
Protected iGadget.i

               If OpenWindow(#Win, 0, 0, 300, 130, "Colour", iFlags)

                              SetWindowColor(#Win, igWinColour)
                               SetGadgetFont(#PB_All, FontID(#Font10))

                                OptionGadget(#MyOpt0,    20,  10, 120, 20, "Option 0")
                                OptionGadget(#MyOpt1,    20,  40, 120, 20, "Option 1")
                                OptionGadget(#MyOpt2,    20,  70, 120, 20, "Option 2")
                              CheckBoxGadget(#MyChkBx0, 150,  10, 120, 20, "Check Box 0")
                              CheckBoxGadget(#MyChkBx1, 150,  40, 120, 20, "Check Box 1")
                              CheckBoxGadget(#MyChkBx2, 150,  70, 120, 20, "Check Box 2")

                           ButtonImageGadget(#BtnExit,    0, 100, 300, 30, 0)
                                     DrawBtn(#BtnExit, "EXIT", 294, 24)

                           For iGadget = #MyOpt0 To #MyChkBx2

                                   SetWindowTheme_(GadgetID(iGadget), @null.w, @null.w)
                           Next

                           SetWindowCallback(@WindowCallBack(), #Win)
                                  HideWindow(#Win, #False)
               EndIf
EndProcedure

Procedure WaitForUser()
;----------------------
Protected iQuit.i = #False

               Repeat
                         Select WaitWindowEvent(1)

                               Case #PB_Event_CloseWindow: iQuit = #True
                               Case #PB_Event_Gadget

                                     Select EventGadget()

                                                 Case  #BtnExit: iQuit = #True
                                     EndSelect
                         EndSelect

                         Delay(1)

               Until iQuit = #True
EndProcedure

Win()
WaitForUser()

End
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
broozar
User
User
Posts: 61
Joined: Sat May 08, 2010 11:21 pm
Location: Berlin, Germany

Re: reskinning an application (->color questions)

Post by broozar »

sorry for being quiet for so long, i did not have a chance to check your posts until just a couple of minutes ago.

@ideas boderless puts the text in the top left corner without any padding, which looks meh. rashad's centered text is prettier.

@rashad thanks a lot for your time and effort! originally, i was hoping to get color into the thing without rewriting/custom designing gadgets. but oh well, there seems to be no way around it. however, even with your custom drawn tabs, i get a thin "white" (windows standard dialogue color) border. i will keep investigating.
Post Reply