Page 1 of 2

Why no SetGadgetColor for buttons?

Posted: Mon Aug 01, 2022 3:25 pm
by jacdelad
Hello,
is there a special reason, why SetGadgetColor doesn't work for normal buttons? I know there are some Modules and codes for colored buttons (and they work quite well), but I wich the normal button would be able to be colored too.

Re: Why no SetGadgetColor for buttons?

Posted: Mon Aug 01, 2022 10:24 pm
by mk-soft
Is a limitation from the Windows OS

Re: Why no SetGadgetColor for buttons?

Posted: Tue Aug 02, 2022 8:57 am
by jacdelad
I suspected that. Which is also strange, because other gadgets can easily be colored. Maybe because buttons were among the first controls introduced.

Re: Why no SetGadgetColor for buttons?

Posted: Tue Aug 02, 2022 10:47 am
by Caronte3D
I don't understand these things of Windows OS :? Another no-sense "feature" is when the text is not vertically centered, by example on on textgadgets :shock:
It's ridiculous!

Re: Why no SetGadgetColor for buttons?

Posted: Tue Aug 02, 2022 11:20 am
by AZJIO
Many elements support color initially, since the background is not a picture. The button is not originally a one-color plane. There is a style for the button when it is drawn by the user. It is difficult to set it just by color, since most likely you need to draw both the border of the button, this is already 3 colors, and the icon. And we also need the Callback function. If you have your own Callback, then it's probably more difficult to put it all together.

Re: Why no SetGadgetColor for buttons?

Posted: Tue Aug 02, 2022 12:23 pm
by BarryG
As mk-soft said, buttons don't support colors in Windows. And vertical alignment for text gadgets aren't supported by Windows, either. (I tested both in Visual Basic, and there's no property settings for either of these attributes).

Re: Why no SetGadgetColor for buttons?

Posted: Tue Aug 02, 2022 2:16 pm
by fryquez
Caronte3D wrote: Tue Aug 02, 2022 10:47 am I don't understand these things of Windows OS :? Another no-sense "feature" is when the text is not vertically centered, by example on on textgadgets :shock:

Code: Select all

Structure tagED0_Simple Align #PB_Structure_AlignC
  hText.i
  cchAlloc.l
  cchTextMax.l
  cch.l
  cLines.l
  ichMinSel.l
  ichMaxSel.l
  ichCaret.l
  iCaretLine.l
  ichScreenStart.l
  ichLinesOnScreen.l
  xOffset.l
  charPasswordChar.l
  cPasswordCharWidth.l
  *hwnd
  pwnd.q
  rcFmt.rect
  *hwndParent
  ptPrevMouse.POINT
  prevKeys.l
  fSingle.l
EndStructure

Procedure VCenter_SL_Edit(hWnd)
  
  Protected *tagED.tagED0_Simple = GetWindowLongPtr_(hWnd, 0)
  
  If *tagED And *tagED\fSingle & 1
    
    Protected rc.rect
    GetClientRect_(*tagED\hwnd, @rc)
    
    Protected h = *tagED\rcFmt\bottom - *tagED\rcFmt\top
    *tagED\rcFmt\top = ((rc\bottom - rc\top) - h) / 2
    *tagED\rcFmt\bottom = *tagED\rcFmt\top + h
    
  EndIf
  
  
EndProcedure

OpenWindow(0, 0, 0, 322, 205, "StringGadget Flags", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
StringGadget(0, 8,  10, 306, 40, "Normal StringGadget...")    
StringGadget(1, 8,  60, 306, 40, "Centered StringGadget...")    
VCenter_SL_Edit(GadgetID(1))
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow

Re: Why no SetGadgetColor for buttons?

Posted: Tue Aug 02, 2022 5:19 pm
by Caronte3D
thanks fryquez :wink: I already know the workarounds, but something as simple as that must be done directly, for example with a flag, I don't understand that laziness of Microsoft since years and years, no matter what language you use. :|

Re: Why no SetGadgetColor for buttons?

Posted: Wed Aug 03, 2022 5:21 am
by DeanH
For the Windows OS, not being able to set the colour of a pushbutton and not being able to vertically centre text easily has been a lack. I think it goes way back to Windows 2 when there were only a handful of basic controls. Buttons, static text (TextGadget) and Editbox are "old controls". For whatever reason, Microsoft has not added much to these old controls over time. There are plenty of workarounds as you are aware. I agree, though, it is something one would have thought would have been addressed by MS long ago but hasn't.

It is possible to have multi-line text in a pushbutton by adding #Pb_Button_Multiline to the style. It will automatically centre the text. Also applies to checkboxes and radiobuttons ... which MS oddly considers types of buttons.

It is possible to have multi-line text in a TextGadget and centre it horizontally. It is possible to vertically centre a single line of text in the same type of control but I have not found out how to do both together without resorting to a work-around. Same for string and editor gadgets.

Below is a simple example. I have not found the #SS_EDITCONTROL constant in PB6. It displays multi-line text in a TextGadget (static text control) horizontally centred. #SS_CENTERIMAGE will vertically centre the text but only a single line.

Code: Select all

#SS_EDITCONTROL=$2000
OpenWindow(1,#PB_Ignore,#PB_Ignore,320,240,"Test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ButtonGadget(10,12,12,70,64,"Click here to close",#PB_Button_MultiLine)
TextGadget(20,100,12,60,64,"Multi line text gadget", #WS_BORDER | #SS_CENTER | #SS_EDITCONTROL | #SS_NOTIFY)
TextGadget(30,180,12,90,64,"Single line", #PB_Text_Center | #WS_BORDER | #SS_NOTIFY | #SS_CENTERIMAGE)
StringGadget(40,12,100,80,64,"Multi line string gadget",#ES_CENTER|#ES_MULTILINE | #ESB_DISABLE_LEFT | #ESB_DISABLE_RIGHT | #ES_READONLY)
EditorGadget(50,100,100,70,64,#PB_Editor_WordWrap | #ES_CENTER|#ES_MULTILINE | #ESB_DISABLE_LEFT | #ESB_DISABLE_RIGHT | #ES_READONLY)
SetGadgetText(50,"Multi line editor gadget")
Repeat
	e=WaitWindowEvent()
	If e=#PB_Event_CloseWindow
		Break
	EndIf
	If e=#PB_Event_Gadget
		g=EventGadget()
		If g=10 Or g=20
			Break
		EndIf
	EndIf
ForEver
CloseWindow(1)

Re: Why no SetGadgetColor for buttons?

Posted: Wed Aug 03, 2022 7:20 am
by Denis
mk-soft wrote: Mon Aug 01, 2022 10:24 pm Is a limitation from the Windows OS
BarryG wrote: Tue Aug 02, 2022 12:23 pm As mk-soft said, buttons don't support colors in Windows. ).
It's possible to colourize true buttons in Windows, its a lot of work.
You have to use subclassing and also uxtheme functions such as
IsThemeBackgroundPartiallyTransparent()
DrawThemeParentBackground()
DrawThemeBackground()
GetThemeBackgroundContentRect()

You also have to manage the state of the button (normal, hovered etc.)

Here is a utility I wrote with coloured buttons.

Image

and the callback i use (written more than 10 years ago), i never get crash (i also use gdiplus to colorise buttons)

Code: Select all

Procedure.i Gdiplus_Window_Callback(Window, Message, wParam, lParam)
      ;///////////////////////////////////////////////////////////////////////////////////////////////////
      ;//
      ;// FONCTION: Gdiplus_Window_Callback()
      ;//
      ;// BUT: Callback dessinant les boutons couleur
      ;//
      ;// PARAMS: Window, Message, wParam, lParam
      ;//
      ;// RETOURNE: L'adresse de la procedure d'origine ou la procedure par défaut
      ;//
      ;///////////////////////////////////////////////////////////////////////////////////////////////////
      
      Protected stateID, partID, ButtonText.s
      Protected BoutonStyle, TexteStyle, textWidth, textHeight, Font
      Protected TextColor, Color1, Color2, SavedDC
      Protected rc_Interior.RECT, rc_InteriorF.RECTF, rc_ThemeContent.rect
      Protected *di.DRAWITEMSTRUCT
      Protected *token, *gfx, *brush, *OriginProc
      Protected Alpha_Value, hBmpTampon, hdcMem, solidBrush
      
      If ListSize(Gdiplus_Window_SubClassing_Infos()) = 0
            ProcedureReturn DefWindowProc_(window, message, wParam, lParam)
      EndIf
      
      ForEach (Gdiplus_Window_SubClassing_Infos())
            ;// on se positionne sur le bon élément
            If Gdiplus_Window_SubClassing_Infos()\Gdiplus_SubClassed_Window = Window
                  Break
            EndIf
      Next
      
      *OriginProc = GetProp_(Window, Gdiplus_Window_SubClassing_Infos()\Gdiplus_SubClassed_WindowString$)
      If *OriginProc = 0
            ProcedureReturn DefWindowProc_(window, message, wParam, lParam)
      EndIf
      *di = lParam
      If *di
            Select Message
                  Case #WM_DRAWITEM
                        If *di\CtlType = #ODT_BUTTON
                              hdcMem = CreateCompatibleDC_(*di\hDC)
                              If hdcMem = 0
                                    ProcedureReturn CallWindowProc_(*OriginProc,Window, Message, wParam, lParam)
                              EndIf
                              
                              hBmpTampon = CreateCompatibleBitmap_(*di\hDC, GetDeviceCaps_(*di\hDC, #HORZRES), GetDeviceCaps_(*di\hDC, #VERTRES))
                              If hBmpTampon = 0
                                    DeleteDC_(hdcMem)
                                    ProcedureReturn CallWindowProc_(*OriginProc,Window, Message, wParam, lParam)
                              EndIf
                              
                              ;// sélectionne l'élément de la Map correspondant au bouton à dessiner, + teste l'initialisation de GDI+ etc.
                              If ((*GLB_Gdiplus_token <> -1) And GLB_hTheme And MapSize(GLB_Bouton()) And FindMapElement(GLB_Bouton(), Str(GadgetID(*di\CtlID))) = #False)
                                    DeleteDC_(hdcMem)
                                    DeleteObject_(hBmpTampon)
                                    ProcedureReturn CallWindowProc_(*OriginProc,Window, Message, wParam, lParam)
                              EndIf
                              
                              ;// sélectionne le bmp dans le dc
                              SelectObject_(hdcMem, hBmpTampon)
                              
                              If *GdipCreateFromHDC(hdcMem, @*gfx) = #Ok
                                    
                                    ;// sauvegarde du dc
                                    SavedDC = SaveDC_(*di\hDC)
                                    
                                    If GLB_Bouton()\Gdiplus_ButtonStyle = #Button_oldStyle
                                          If *di\itemState & #ODS_DISABLED
                                                partID = #DFC_BUTTON
                                                stateID = #DFCS_BUTTONPUSH|#DFCS_FLAT|#DFCS_INACTIVE
                                                Alpha_Value = 255
                                                TextColor = GetSysColor_(#COLOR_GRAYTEXT)
                                                Color1 = GetSysColor_(#COLOR_BTNFACE)
                                                Color2 = GetSysColor_(#COLOR_BTNFACE)
                                                
                                          ElseIf *di\itemState & #ODS_SELECTED
                                                partID = #DFC_BUTTON
                                                stateID = #DFCS_BUTTONPUSH|#DFCS_FLAT|#DFCS_PUSHED
                                                Alpha_Value = GLB_Bouton()\Selected_Alpha
                                                TextColor = GLB_Bouton()\Selected_TextColor
                                                Color1 = GLB_Bouton()\Selected_BackGround_UpperColor
                                                Color2 = GLB_Bouton()\Selected_BackGround_LowerColor
                                                
                                          Else
                                                partID = #DFC_BUTTON
                                                stateID = #DFCS_BUTTONPUSH|#DFCS_FLAT
                                                If GLB_Bouton()\IsMouseOver
                                                      Alpha_Value = GLB_Bouton()\MouseOver_Alpha
                                                      TextColor = GLB_Bouton()\MouseOver_TextColor
                                                      Color1 = GLB_Bouton()\MouseOver_BackGround_UpperColor
                                                      Color2 = GLB_Bouton()\MouseOver_BackGround_LowerColor
                                                Else
                                                      Alpha_Value = GLB_Bouton()\Normal_Alpha
                                                      TextColor = GLB_Bouton()\Normal_TextColor
                                                      Color1 = GLB_Bouton()\Normal_BackGround_UpperColor
                                                      Color2 = GLB_Bouton()\Normal_BackGround_LowerColor
                                                EndIf
                                          EndIf
                                          
                                    Else   ;//  GLB_Bouton()\Gdiplus_ButtonStyle = #Button_XPStyle
                                          If *di\itemState & #ODS_DISABLED
                                                ;  Debug "#ODS_DISABLED"
                                                ;  Debug #PBS_DISABLED
                                                partID = #BP_PUSHBUTTON
                                                stateID = #PBS_DISABLED
                                                Alpha_Value = GLB_Bouton()\Normal_Alpha
                                                Alpha_Value = 80
                                                TextColor = GLB_Bouton()\Normal_TextColor
                                                
                                               ; TextColor = GetSysColor_(#COLOR_GRAYTEXT)
                                              ;  TextColor = $B1A2A2  ;$838383
                                                ;  Color1 = GetSysColor_(#COLOR_BTNFACE)
                                                ;  Color2 = GetSysColor_(#COLOR_BTNFACE)
                                              ;  Color1 = GLB_Bouton()\Normal_BackGround_UpperColor
                                                Color2 = GLB_Bouton()\Normal_BackGround_LowerColor
                                                Color1 = Color2
                                                
                                          ElseIf *di\itemState & #ODS_SELECTED
                                                ;  Debug #PBS_PRESSED
                                                partID = #BP_PUSHBUTTON
                                                stateID = #PBS_PRESSED
                                                Alpha_Value = GLB_Bouton()\Selected_Alpha
                                                TextColor = GLB_Bouton()\Selected_TextColor
                                                Color1 = GLB_Bouton()\Selected_BackGround_UpperColor
                                                Color2 = GLB_Bouton()\Selected_BackGround_LowerColor
                                                
                                          Else
                                                partID = #BP_PUSHBUTTON
                                                stateID = #PBS_NORMAL
                                                ;  Debug #PBS_NORMAL
                                                If GLB_Bouton()\IsMouseOver
                                                      Alpha_Value = GLB_Bouton()\MouseOver_Alpha
                                                      TextColor = GLB_Bouton()\MouseOver_TextColor
                                                      Color1 = GLB_Bouton()\MouseOver_BackGround_UpperColor
                                                      Color2 = GLB_Bouton()\MouseOver_BackGround_LowerColor
                                                Else
                                                      Alpha_Value = GLB_Bouton()\Normal_Alpha
                                                      TextColor = GLB_Bouton()\Normal_TextColor
                                                      Color1 = GLB_Bouton()\Normal_BackGround_UpperColor
                                                      Color2 = GLB_Bouton()\Normal_BackGround_LowerColor
                                                EndIf
                                          EndIf
                                    EndIf
                                    
                                    If GLB_Bouton()\Gdiplus_ButtonStyle = #Button_XPStyle
                                          ;// on redessine le bouton
                                          If IsThemeBackgroundPartiallyTransparent_(GLB_hTheme, partID, stateID)
                                                DrawThemeParentBackground_(*di\hwndItem, hdcMem, *di\rcItem)
                                          EndIf
                                          
                                          DrawThemeBackground_(GLB_hTheme, hdcMem, partID, stateID, *di\rcItem, 0)
                                          
                                          GetThemeBackgroundContentRect_(GLB_hTheme, *di\hDC, partID, stateID, *di\rcItem, @rc_ThemeContent)
                                          rc_Interior = rc_ThemeContent
                                          
                                    ElseIf GLB_Bouton()\Gdiplus_ButtonStyle = #Button_oldStyle
                                          DrawFrameControl_(hdcMem, *di\rcItem, partID, stateID)
                                          rc_Interior = *di\rcItem
                                          InflateRect_(rc_Interior, -GetSystemMetrics_(#SM_CXEDGE), -GetSystemMetrics_(#SM_CYEDGE))
                                          
                                    Else  ;//  GLB_Bouton()\Gdiplus_ButtonStyle = #Button_ThickFrame
                                          GetThemeBackgroundContentRect_(GLB_hTheme, hdcMem, partID, stateID, *di\rcItem, @rc_ThemeContent)
                                          rc_Interior = rc_ThemeContent
                                          solidBrush = CreateSolidBrush_(GLB_Bouton()\Gdiplus_ThickFrameColor)
                                          If solidBrush
                                                FillRect_(hdcMem, *di\rcItem, solidBrush)
                                                DeleteObject_(solidBrush)
                                          EndIf
                                          
                                    EndIf
                                    
                                    Select GLB_Bouton()\LinearGradientMode
                                          Case #LinearGradientModehorizontal
                                                rc_InteriorF\x = rc_Interior\left
                                                rc_InteriorF\y = rc_Interior\top+1
                                                rc_InteriorF\Width = rc_Interior\right - rc_Interior\left -2
                                                rc_InteriorF\Height =  rc_Interior\bottom - rc_Interior\top-2
                                                *GdipCreateLineBrushFromRect(@rc_InteriorF, Gdiplus_ARGB(Color1, Alpha_Value), Gdiplus_ARGB(Color2, Alpha_Value), GLB_Bouton()\LinearGradientMode, #WrapModeTile, @*brush)
                                                *GdipFillRectangle(*gfx, *brush, rc_InteriorF\x+1, rc_InteriorF\y, rc_InteriorF\Width, rc_InteriorF\Height)
                                                
                                          Default  ;/ #LinearGradientModeVertical, #LinearGradientModeForwardDiagonal, #LinearGradientModeBackwardDiagonal
                                                If GLB_Bouton()\Gdiplus_ButtonStyle = #Button_oldStyle
                                                      rc_InteriorF\x = rc_Interior\left+1
                                                      rc_InteriorF\y = rc_Interior\top+1
                                                      rc_InteriorF\Height =  rc_Interior\bottom - rc_Interior\top-1
                                                      rc_InteriorF\Width = rc_Interior\right - rc_Interior\left -2
                                                      *GdipCreateLineBrushFromRect(@rc_InteriorF, Gdiplus_ARGB(Color1, Alpha_Value), Gdiplus_ARGB(Color2, Alpha_Value), GLB_Bouton()\LinearGradientMode, #WrapModeTile, @*brush)
                                                      *GdipFillRectangle(*gfx, *brush, rc_InteriorF\x, rc_InteriorF\y, rc_InteriorF\Width, rc_InteriorF\Height-1)
                                                Else
                                                      ; Debug "Default"
                                                      rc_InteriorF\x = rc_Interior\left
                                                      rc_InteriorF\y = rc_Interior\top
                                                      rc_InteriorF\Height =  rc_Interior\bottom - rc_Interior\top
                                                      rc_InteriorF\Width = rc_Interior\right - rc_Interior\left
                                                      *GdipCreateLineBrushFromRect(@rc_InteriorF, Gdiplus_ARGB(Color1, Alpha_Value), Gdiplus_ARGB(Color2, Alpha_Value), GLB_Bouton()\LinearGradientMode, #WrapModeTile, @*brush)
                                                      rc_InteriorF\x = rc_Interior\left+1
                                                      rc_InteriorF\y = rc_Interior\top+1
                                                      rc_InteriorF\Height =  rc_Interior\bottom - rc_Interior\top-2
                                                      rc_InteriorF\Width = rc_Interior\right - rc_Interior\left -2
                                                      *GdipFillRectangle(*gfx, *brush, rc_InteriorF\x, rc_InteriorF\y, rc_InteriorF\Width, rc_InteriorF\Height)
                                                EndIf
                                                
                                    EndSelect
                                    ;// destruction de la brush
                                    Gdiplus_DelBrush(*brush)
                                    
                                    ;// écriture du texte du bouton
                                    ButtonText = GetGadgetText(*di\CtlID)
                                    
                                    If Len(ButtonText)
                                          ;// couleur texte , fond transparent
                                          SetTextColor_(hdcMem, TextColor)
                                          SetBkMode_(hdcMem, #TRANSPARENT)
                                          Font = GetGadgetFont(*di\CtlID)
                                          If Font
                                                SelectObject_(hdcMem, Font)
                                          EndIf
                                          ;// lecture du style du bouton
                                          BoutonStyle = GetWindowLongPtr_(*di\hwndItem, #GWL_STYLE)
                                          If BoutonStyle & #BS_RIGHT
                                                TexteStyle | #DT_RIGHT
                                          ElseIf BoutonStyle & #BS_LEFT
                                                TexteStyle | #DT_LEFT
                                          Else
                                                TexteStyle | #DT_CENTER
                                          EndIf
                                          
                                          If (Not(BoutonStyle & #BS_MULTILINE))
                                                TexteStyle | #DT_SINGLELINE | #DT_WORDBREAK | #DT_VCENTER
                                                rc_Interior\right-1
                                          Else
                                                TexteStyle | #DT_WORDBREAK
                                                ;// calcul du rectangle du texte à centrer et centrage
                                                DrawText_(hdcMem, @ButtonText, -1, @rc_Interior, #DT_CALCRECT | TexteStyle)
                                                textWidth = rc_Interior\right - rc_Interior\left
                                                textHeight = rc_Interior\bottom - rc_Interior\top
                                                rc_Interior\left = ((*di\rcItem\right - *di\rcItem\left) - (rc_Interior\right - rc_Interior\left)) / 2
                                                rc_Interior\right = rc_Interior\left + textWidth
                                                rc_Interior\top = (*di\rcItem\bottom - *di\rcItem\top - textheight) / 2
                                                rc_Interior\bottom = rc_Interior\top + textheight
                                          EndIf
                                          
                                          ;// on écrit le texte
                                          DrawTextEx_(hdcMem, ButtonText, -1, @rc_Interior, TexteStyle | #DT_EXPANDTABS , 0)
                                          ;// on copie le hDC de la mémoire vers celui du bouton
                                          BitBlt_(*di\hDC, 0, 0, *di\rcItem\right - *di\rcItem\left, *di\rcItem\bottom - *di\rcItem\top, hdcMem, 0, 0, #SRCCOPY)
                                    EndIf
                                    DeleteDC_(hdcMem)
                                    DeleteObject_(hBmpTampon)
                                    Gdiplus_DelGraphics(*gfx)
                                    If SavedDC
                                          RestoreDC_(*di\hDC, SavedDC)
                                    EndIf
                                    ProcedureReturn #True
                              EndIf
                        EndIf
            EndSelect
      EndIf
      
      ProcedureReturn CallWindowProc_(*OriginProc,Window, Message, wParam, lParam)
EndProcedure

Re: Why no SetGadgetColor for buttons?

Posted: Wed Aug 03, 2022 11:02 am
by Mindphazer
Hello Denis,

could you provide a small example to show the use of your callback procedure ?

Thanks a lot

Re: Why no SetGadgetColor for buttons?

Posted: Wed Aug 03, 2022 11:37 am
by Denis
Hello Mindphazer,

to do what you want, I should post the whole library which has a lot of functions, I have never done that and I don't think I will because I don't provide if I haven't written a doc.
It would quickly become incomprehensible.I would have to answer a lot of questions and I don't have the time.

So writing doc is very time consuming.


Sorry Mindphazer

Re: Why no SetGadgetColor for buttons?

Posted: Wed Aug 03, 2022 12:21 pm
by Mindphazer
It's okay, no problem.
I totally understand your point

Re: Why no SetGadgetColor for buttons?

Posted: Thu Aug 04, 2022 6:21 am
by Denis
Mindphazer,
If you want to see how the coloured buttons work, here are 2 links to my utility. Be careful, don't upload any images with it, as the tool may start changing its content.

x64 version
CleanUpImage_X64.exe
x86 version
CleanUpImage_X86.exe

Re: Why no SetGadgetColor for buttons?

Posted: Thu Aug 04, 2022 4:34 pm
by Mindphazer
Thanks Denis,

I'll have a look