Owner-drawn buttons

Share your advanced PureBasic knowledge/code with the community.
User avatar
Kukulkan
Addict
Addict
Posts: 1352
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Owner-drawn buttons

Post by Kukulkan »

Hi,

I just want to share my button include to make such buttons:
Image

It works on Windows and Linux in single-byte or unicode-mode. I did not test on x64 architecture.

Update on 14th April 2012: added multi window capabilities and horizontal icon (left from text) using rbutton_CreateH()

Update on 16th July 2012: added rbutton_Hide() function to effectively hide/unhide buttons

Code of rbutton_include.pbi:

Code: Select all

; Compiles on Windows and Linux (tested 32 Bit, 64bit, single-byte and unicode)
; works with EnableExplicit

; Usage example with loaded backgrounds (normal and hover):
; rbutton_SetFont("Verdana", 16, 0, RGB(255,255,255), -120)
; rbutton_Init(CatchImage(#PB_Any, ?Back), CatchImage(#PB_Any, ?BackHover))
; Define regButton1.i = rbutton_Create(#PB_Any, "display", 10, 10, LoadImage(#PB_Any, "display.png"))
;
; Usage example with self drawn background
; rbutton_SetFont("Verdana", 16, 0, RGB(255,255,255), -120)
; rbutton_Init_OwnerDrawn(120, 120, RGBA(255,255,255,255), RGBA(214,226,236,255), RGBA(120,158,191,255), RGBA(255,255,255,255), RGBA(90,167,255,255),15, 2)
; Define regButton1.i = rbutton_Create(#PB_Any, WindowID, "display", 10, 10, LoadImage(#PB_Any, "display.png"))
;
; Do a call to rbutton_CheckHover(WinID) in your event-loop to enable hover effect
;
; HINT: You can call rbutton_Init(), rbutton_Init_OwnerDrawn() and rbutton_SetFont() multiple
;       times to make different buttons on the same window/screen.
UsePNGImageDecoder()

#button_ShaddowWidth   = 5  ; how wide the shaddow will be around the button (default 5)
#button_ShaddowOpacity = 30 ; 'darkness' of the surrounding shaddow: 10 = light 100 = heavy (default 30)
#button_HoverDimm      = 20 ; hover-effect dimming of the background gradient (>0 = lighten, <0 = darken) (default 20)
#button_ScaleFactor    = 2  ; making smoother, do not change (default 2)

Structure structrButtonList
  Caption.s                 ; Gadget Caption
  Disabled.b                ; #True or #False
  Hidden.b                  ; #True or #False
  GadgetID.i                ; image-gadget ID
  ButtonImageID_draw.i      ; image-id of image to draw onto
  ButtonImageID_normal.i    ; image-id of background image normal
  ButtonImageID_hover.i     ; image-id of background image hover
  X.i                       ; Gadget X
  Y.i                       ; Gadget Y
  Width.i                   ; Gadget Width
  Height.i                  ; Gadget Height
  IconImageID.i             ; Icon image-id
  IconWidth.i               ; Icon Width
  IconHeight.i              ; Icon Height
  IconX.i                   ; Icon position X in Gadget
  IconY.i                   ; Icon position Y in Gadget
  TextX.i                   ; Text Position X in Gadget
  TextY.i                   ; Text Position Y in Gadget
  FontName.s
  FontSize.i
  FontStyle.i
  FontColor.i
  ShaddowDimm.i
  WindowID.i                ; ID of the window this button is drawn on
EndStructure

Structure structrButton
  rButtonImageID_normal.i
  rButtonImageID_hover.i
  FontName.s
  FontSize.i
  FontStyle.i
  FontColor.i
  ShaddowDimm.i
  LineThickness.i
  List ButtonList.structrButtonList()
EndStructure

Global rButton.structrButton

; Makes the cursor in the window (parent of given Gadget) to a hand
; Works both windows and linux
Procedure.b rbutton_MakeCursorHand(GadgetID.i)
  CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Windows
    
    ; WINDOWS
    Static *cursor
    If *cursor = 0
      *cursor = LoadCursor_(0, #IDC_HAND)
    EndIf
    SetCursor_(*cursor)
    
  CompilerCase #PB_OS_Linux
    
    ; LINUX
    Static *cursor.GdkCursor
    If *cursor = 0
      *cursor = gdk_cursor_new_(#GDK_HAND1);
    EndIf
    gdk_window_set_cursor_(gtk_widget_get_parent_window_(GadgetID(GadgetID.i)), *cursor)
    
  CompilerCase #PB_OS_MacOS
    ; MAC
    ; write me!
    
  CompilerEndSelect
EndProcedure

; Checks, if the current mouse position is over the given gadget.
; Returns #True in case of success, otherwise #False.
; Use this in your GUI loop (for example to change the cursor in
; conjunction with rbutton_MakeCursorHand()).
Procedure.b rbutton_MouseOverGadget(WindowID.i, GadgetID.i)
  
  Protected MX.i = WindowMouseX(WindowID.i)
  Protected MY.i = WindowMouseY(WindowID.i)
  Protected GX.i = GadgetX(GadgetID.i)
  Protected GY.i = GadgetY(GadgetID.i)
  Protected GW.i = GadgetWidth(GadgetID.i)
  Protected GH.i = GadgetHeight(GadgetID.i)
  
  If MX.i > GX.i And MX.i < (GX.i + GW.i) And MY.i > GY.i And MY.i < (GY.i + GH.i)
    ProcedureReturn #True
  EndIf
  ProcedureReturn #False
EndProcedure

; Helping function to dimm a color.
; Example:
; NewColor.i = rbutton_DimmColor(OriginalColor.i, -50); reduce by 50
Procedure.i rbutton_DimmColor(OriginalColor.i, Dimm.i)
  Protected R.i = Red(OriginalColor.i)
  Protected G.i = Green(OriginalColor.i)
  Protected B.i = Blue(OriginalColor.i)
  
  R.i = R.i + Dimm.i: If R.i < 0: R.i = 0: EndIf: If R.i > 255: R.i = 255: EndIf
  G.i = G.i + Dimm.i: If G.i < 0: G.i = 0: EndIf: If G.i > 255: G.i = 255: EndIf
  B.i = B.i + Dimm.i: If B.i < 0: B.i = 0: EndIf: If B.i > 255: B.i = 255: EndIf
  
  ProcedureReturn RGB(R.i, G.i, B.i)
EndProcedure

; Set the font for the r-style buttons.
; Need to be called before creating the first button.
; FontStyle.i follows the purebasic LoadFont() constants like #PB_Font_Bold.
Procedure.b rbutton_SetFont(FontName.s, FontSize.i, FontStyle.i = 0, FontColor.i = 0, ShaddowDimm.i = 0)
  With rButton
    \FontName     = FontName.s
    \FontSize     = FontSize.i
    \FontStyle    = FontStyle.i
    \FontColor    = FontColor.i
    \ShaddowDimm  = ShaddowDimm.i
  EndWith
  ProcedureReturn #True
EndProcedure

; Initialize main rbutton preferences with own drawn images
Procedure.b rbutton_Init_OwnerDrawn(Width.i, Height.i, BackgroundColor.i, TopColor.i, BottomColor.i, LineColor.i, LineColorSelected.i, EdgeRadius.f = 15, LineThickness.i = 2)
  Protected x.i, EdgeRadiusUse.f
  
  Width.i         = Width.i * #button_ScaleFactor
  Height.i        = Height.i * #button_ScaleFactor
  EdgeRadiusUse.f = EdgeRadius.f * #button_ScaleFactor
  LineThickness.i = LineThickness.i * #button_ScaleFactor
  
  Protected BackImage.i = CreateImage(#PB_Any, Width.i, Height.i, #PB_Image_Transparent  | 32)
  
  Protected ShaddowWidth.f = #button_ShaddowWidth
  
  Protected CenterX.f = Width.i / 2
  Protected CenterY.f = Height.i / 2
  
  Protected XRadius.f = Width.i / 2 + #button_ScaleFactor
  Protected YRadius.f = Height.i / 2 + #button_ScaleFactor
  
  StartDrawing(ImageOutput(BackImage.i))
  
  DrawingMode(#PB_2DDrawing_AlphaBlend)
  
  ; make background with background color
  Box(0, 0, Width.i, Height.i, RGBA(Red(BackgroundColor.i), Green(BackgroundColor.i), Blue(BackgroundColor.i), 255))
  LineColor.i = RGBA(Red(LineColor.i), Green(LineColor.i), Blue(LineColor.i), 255)
  LineColorSelected.i = RGBA(Red(LineColorSelected.i), Green(LineColorSelected.i), Blue(LineColorSelected.i), 255)
  
  ; make shaddows
  DrawingMode(#PB_2DDrawing_AlphaBlend)
  Protected ShaddowStep.f = #button_ShaddowOpacity / ShaddowWidth.f
  For x.i = 1 To ShaddowWidth.f
    XRadius.f = XRadius.f - #button_ScaleFactor
    YRadius.f = YRadius.f - #button_ScaleFactor
    EdgeRadiusUse.f = EdgeRadiusUse.f - #button_ScaleFactor / 2
    If EdgeRadiusUse.f < 0: EdgeRadiusUse.f = 0: EndIf
    RoundBox(CenterX.f - XRadius.f, CenterY.f - YRadius.f, XRadius.f * 2, YRadius.f * 2, EdgeRadiusUse.f, EdgeRadiusUse.f, RGBA(0,0,0, x.i * ShaddowStep.f))
  Next
  
  ; make border with LineColor
  DrawingMode(#PB_2DDrawing_AllChannels)
  RoundBox(CenterX.f - XRadius.f, CenterY.f - YRadius.f, XRadius.f * 2, YRadius.f * 2, EdgeRadiusUse.f, EdgeRadiusUse.f, LineColor.i)
  XRadius.f = XRadius.f - LineThickness.i
  YRadius.f = YRadius.f - LineThickness.i
  EdgeRadiusUse.f = EdgeRadiusUse.f - LineThickness.i
  If EdgeRadiusUse.f < 0: EdgeRadiusUse.f = 0: EndIf
  
  ; background gradient
  DrawingMode(#PB_2DDrawing_Gradient)
  BackColor(TopColor.i)
  FrontColor(BottomColor.i)
  Protected Rotate.f = YRadius.f / 2
  LinearGradient(CenterX.f, CenterY.f - YRadius.f, CenterX.f + Rotate.f, CenterY.f + YRadius.f)
  RoundBox(CenterX.f - XRadius.f, CenterY.f - YRadius.f, XRadius.f * 2, YRadius.f * 2, EdgeRadiusUse.f, EdgeRadiusUse.f)
  StopDrawing()
  
  ResizeImage(BackImage.i, Width.i / #button_ScaleFactor, Height.i / #button_ScaleFactor, #PB_Image_Smooth)
  
  Protected ButtonImageNormalID.i = BackImage.i
  
  ; HOVER IMAGE
  
  Protected BackImageHover.i = CreateImage(#PB_Any, Width.i, Height.i, #PB_Image_Transparent  | 32)
  
  EdgeRadiusUse.f = EdgeRadius.f * #button_ScaleFactor
  XRadius.f       = Width.i / 2 + #button_ScaleFactor
  YRadius.f       = Height.i / 2 + #button_ScaleFactor
  
  StartDrawing(ImageOutput(BackImageHover.i))
  
  DrawingMode(#PB_2DDrawing_AlphaBlend)
  
  ; make background with background color
  Box(0, 0, Width.i, Height.i, RGBA(Red(BackgroundColor.i), Green(BackgroundColor.i), Blue(BackgroundColor.i), 255))
  
  ; dimm colors for hover effect
  TopColor.i    = rbutton_DimmColor(TopColor.i, #button_HoverDimm)
  BottomColor.i = rbutton_DimmColor(BottomColor.i, #button_HoverDimm)
  
  ; make shaddows
  DrawingMode(#PB_2DDrawing_AlphaBlend)
  ShaddowStep.f = #button_ShaddowOpacity / ShaddowWidth.f
  For x.i = 1 To ShaddowWidth.f
    XRadius.f = XRadius.f - #button_ScaleFactor
    YRadius.f = YRadius.f - #button_ScaleFactor
    EdgeRadiusUse.f = EdgeRadiusUse.f - #button_ScaleFactor / 2
    If EdgeRadiusUse.f < 0: EdgeRadiusUse.f = 0: EndIf
    RoundBox(CenterX.f - XRadius.f, CenterY.f - YRadius.f, XRadius.f * 2, YRadius.f * 2, EdgeRadiusUse.f, EdgeRadiusUse.f, RGBA(0,0,0, x.i * ShaddowStep.f))
  Next
  
  ; make border with LineColor
  DrawingMode(#PB_2DDrawing_AllChannels)
  RoundBox(CenterX.f - XRadius.f, CenterY.f - YRadius.f, XRadius.f * 2, YRadius.f * 2, EdgeRadiusUse.f, EdgeRadiusUse.f, LineColorSelected.i)
  XRadius.f = XRadius.f - LineThickness.i
  YRadius.f = YRadius.f - LineThickness.i
  EdgeRadiusUse.f = EdgeRadiusUse.f - LineThickness.i
  If EdgeRadiusUse.f < 0: EdgeRadiusUse.f = 0: EndIf
  
  ; background gradient
  DrawingMode(#PB_2DDrawing_Gradient)
  BackColor(TopColor.i)
  FrontColor(BottomColor.i)
  Rotate.f = YRadius.f / 2
  LinearGradient(CenterX.f, CenterY.f - YRadius.f, CenterX.f + Rotate.f, CenterY.f + YRadius.f)
  RoundBox(CenterX.f - XRadius.f, CenterY.f - YRadius.f, XRadius.f * 2, YRadius.f * 2, EdgeRadiusUse.f, EdgeRadiusUse.f)
  StopDrawing()
  
  ResizeImage(BackImageHover.i, Width.i / #button_ScaleFactor, Height.i / #button_ScaleFactor, #PB_Image_Smooth)
  
  Protected ButtonImageHoverID.i  = BackImageHover.i
  
  With rButton
    \rButtonImageID_normal = ButtonImageNormalID.i
    \rButtonImageID_hover  = ButtonImageHoverID.i
    \LineThickness         = LineThickness.i ; for later use while drawing
  EndWith
EndProcedure

; Initialize main rbutton preferences:
; ButtonImageNormalID.i = ImageID of an image to use as button background
; ButtonImageHoverID.i  = ImageID of an image to use as button background in case of mouse-hover (same size!)
Procedure.b rbutton_Init(ButtonImageNormalID.i, ButtonImageHoverID.i)
  With rButton
    \rButtonImageID_normal = ButtonImageNormalID.i
    \rButtonImageID_hover  = ButtonImageHoverID.i
  EndWith
EndProcedure

; Filter-procedure for disabled icons in rbutton_Redraw-Function
Procedure rbutton_FilterCallback(x, y, SourceColor, DestinationColor)
  Protected AlphaVal = Alpha(SourceColor)
  Protected AlphaValNeg = 255-Alpha(SourceColor)
  
  Protected NewRed = (Green(SourceColor) * 0.7 * AlphaVal + Red(DestinationColor)   * AlphaValNeg) / 255
  Protected NewBlu = (Green(SourceColor) * 0.7 * AlphaVal + Blue(DestinationColor)  * AlphaValNeg) / 255
  Protected NewGre = (Green(SourceColor) * 0.7 * AlphaVal + Green(DestinationColor) * AlphaValNeg) / 255
  
  If NewRed > 255: NewRed = 255: EndIf
  If NewBlu > 255: NewBlu = 255: EndIf
  If NewGre > 255: NewGre = 255: EndIf
  
  Protected NewColor = RGBA(NewRed, NewGre, NewBlu, 255)
  
  ProcedureReturn NewColor
EndProcedure

; Redraws an existing r button
; state=0 -> normal
; state=1 -> hover / highlighted
Procedure.b rbutton_Redraw(rGadgetID.i, State.i)
  Protected Found.b = #False
  Protected TextColor.i
  Protected IconLift.i
  Protected FontName.s, FontSize.i, FontColor.i, FontStyle.i, ShaddowColor.i
  Protected XP.i, YP.i, Caption.s
  
  ForEach rButton\ButtonList()
    If rButton\ButtonList()\GadgetID = rGadgetID.i
      Found.b = #True
      Break
    EndIf
  Next
  If Found.b = #False
    ; Debug "rGadgetID " + Str(rGadgetID.i) + " not found (Redraw)!"
    ProcedureReturn #False
  EndIf
  
  If rButton\ButtonList()\Caption <> ""
    Protected MyFontID.i = LoadFont(#PB_Any, rButton\ButtonList()\FontName, rButton\ButtonList()\FontSize, rButton\ButtonList()\FontStyle) ; load font in desired size
  EndIf
  
  ; draw background
  StartDrawing(ImageOutput(rButton\ButtonList()\ButtonImageID_draw))
  Box(0, 0, rButton\ButtonList()\Width, rButton\ButtonList()\Height, RGBA(0,0,0,0)) ; init as transparent background
  If rButton\ButtonList()\Caption <> ""  
    DrawingFont(FontID(MyFontID.i))
  EndIf
  DrawingMode(#PB_2DDrawing_AlphaBlend)
  
  If rButton\ButtonList()\Disabled = #False
    ; -- ENABLED --
    If State.i = 0
      DrawImage(ImageID(rButton\ButtonList()\ButtonImageID_normal), 0, 0)
      FontColor.i    = rbutton_DimmColor(rButton\ButtonList()\FontColor, -10)
      ShaddowColor.i = rbutton_DimmColor(rButton\ButtonList()\FontColor, rButton\ButtonList()\ShaddowDimm - 10)
      IconLift.i = 0
    EndIf
    If State.i = 1
      DrawImage(ImageID(rButton\ButtonList()\ButtonImageID_hover), 0, 0)
      FontColor.i    = rButton\ButtonList()\FontColor
      ShaddowColor.i = rbutton_DimmColor(rButton\ButtonList()\FontColor, rButton\ButtonList()\ShaddowDimm)
      IconLift.i = -1
    EndIf
    
    ; draw icon
    If rButton\ButtonList()\IconImageID <> 0
      DrawImage(ImageID(rButton\ButtonList()\IconImageID), rButton\ButtonList()\IconX, rButton\ButtonList()\IconY + IconLift.i)
    EndIf
    
    If rButton\ButtonList()\Caption <> ""
      ; draw caption
      DrawingMode(#PB_2DDrawing_Transparent)
      Caption.s = rButton\ButtonList()\Caption
      If ShaddowColor.i <> FontColor.i
        DrawText(rButton\ButtonList()\TextX + 1, rButton\ButtonList()\TextY + 1 + IconLift.i, Caption.s, ShaddowColor.i); shaddow
      EndIf
      DrawText(rButton\ButtonList()\TextX, rButton\ButtonList()\TextY + IconLift.i, Caption.s, FontColor.i) ; real
    EndIf
  Else
    ; -- DISABLED --
    DrawImage(ImageID(rButton\ButtonList()\ButtonImageID_normal), 0, 0)
    ; draw icon
    If rButton\ButtonList()\IconImageID <> 0
      DrawingMode(#PB_2DDrawing_CustomFilter)
      CustomFilterCallback(@rbutton_FilterCallback())
      
      DrawImage(ImageID(rButton\ButtonList()\IconImageID), rButton\ButtonList()\IconX, rButton\ButtonList()\IconY)
    EndIf
    
    If rButton\ButtonList()\Caption <> ""
      ; draw caption
      FontColor.i    = rbutton_DimmColor(rButton\ButtonList()\FontColor, 120)
      ShaddowColor.i = rbutton_DimmColor(rButton\ButtonList()\FontColor, 180)
      IconLift.i = 0
      DrawingMode(#PB_2DDrawing_Transparent)
      Caption.s = rButton\ButtonList()\Caption
      If ShaddowColor.i <> FontColor.i
        DrawText(rButton\ButtonList()\TextX+1, rButton\ButtonList()\TextY + 1, Caption.s, ShaddowColor.i); shaddow
      EndIf
      DrawText(rButton\ButtonList()\TextX, rButton\ButtonList()\TextY, Caption.s, FontColor.i) ; real
    EndIf
    
  EndIf
  StopDrawing()
  
  If IsFont(MyFontID.i)
    FreeFont(MyFontID.i)
  EndIf
  
  ; set final image to ImageGadget
  SetGadgetState(rGadgetID.i, ImageID(rButton\ButtonList()\ButtonImageID_draw))
  
  ProcedureReturn #True
EndProcedure

; Creates a new r-style button in vertical mode (icon on top of text).
; The size depends on the used background-image.
Procedure.i rbutton_Create(Gadget.i, WindowID.i, Caption.s, PosX.i, PosY.i, IconImageID.i = 0)
  Protected FontName.s, FontSize.i, FontColor.i, FontStyle.i, ShaddowColor.i
  
  With rButton
    
    If \FontName = "" : FontName.s = "Verdana": Else : FontName.s = \FontName: EndIf ; use defaults
    If \FontSize = 0  : FontSize.i = 16       : Else : FontSize.i = \FontSize: EndIf ; use defaults
    
    FontStyle.i    = \FontStyle
    FontColor.i    = rbutton_DimmColor(\FontColor, -10)
    ShaddowColor.i = rbutton_DimmColor(\FontColor, \ShaddowDimm - 10)
    
    ; load images, if needed
    If \rButtonImageID_normal = 0
      Debug "Need to call rbutton_Init() at first!"
      ProcedureReturn
    EndIf
    If \rButtonImageID_hover = 0
      Debug "Need to call rbutton_Init() at first!"
      ProcedureReturn
    EndIf
    
    ; prepare button and load images
    AddElement(\ButtonList())
    
    \ButtonList()\ButtonImageID_normal = \rButtonImageID_normal
    \ButtonList()\ButtonImageID_hover  = \rButtonImageID_hover
    \ButtonList()\IconImageID          = IconImageID.i
    \ButtonList()\X                    = PosX.i
    \ButtonList()\Y                    = PosY.i
    \ButtonList()\Width                = ImageWidth(\rButtonImageID_normal)
    \ButtonList()\Height               = ImageHeight(\rButtonImageID_normal)
    \ButtonList()\Caption              = Caption.s
    \ButtonList()\ButtonImageID_draw   = CreateImage(#PB_Any, \ButtonList()\Width, \ButtonList()\Height, 32)
    \ButtonList()\FontName             = \FontName
    \ButtonList()\FontSize             = \FontSize
    \ButtonList()\FontStyle            = \FontStyle
    \ButtonList()\FontColor            = \FontColor
    \ButtonList()\ShaddowDimm          = \ShaddowDimm
    \ButtonList()\WindowID             = WindowID.i
    If IconImageID.i <> 0
      \ButtonList()\IconWidth          = ImageWidth(\ButtonList()\IconImageID)
      \ButtonList()\IconHeight         = ImageHeight(\ButtonList()\IconImageID)
    Else
      \ButtonList()\IconWidth          = 0
      \ButtonList()\IconHeight         = 0
    EndIf
    
    Protected XP.i
    Protected YP.i
    If \ButtonList()\Caption <> ""
      ; with caption (icon shifted up)
      XP.i = \ButtonList()\Width / 2 - \ButtonList()\IconWidth / 2
      YP.i = (\ButtonList()\Height * 0.8) / 2 - \ButtonList()\IconHeight / 2
    Else
      ; no caption (center icon)
      XP.i = \ButtonList()\Width / 2 - \ButtonList()\IconWidth / 2
      YP.i = \ButtonList()\Height / 2 - \ButtonList()\IconHeight / 2
    EndIf
    \ButtonList()\IconX      = XP.i
    \ButtonList()\IconY      = YP.i
    
    ; draw caption
    ; start drawing only for gathering the correct values for textwidth/height
    ; generate image values
    Protected MyFontID.i = LoadFont(#PB_Any, FontName.s, FontSize.i, FontStyle.i) ; load font in desired size
    StartDrawing(ImageOutput(\ButtonList()\ButtonImageID_draw))
    
      DrawingFont(FontID(MyFontID.i))
      If IconImageID.i <> 0
        ; text lowered
        XP.i = \ButtonList()\Width / 2 - TextWidth(Caption.s) / 2
        YP.i = \ButtonList()\Height * 0.75 - TextHeight(Caption.s) / 2
      Else
        ; text centered
        XP.i = \ButtonList()\Width / 2  - TextWidth(Caption.s) / 2
        YP.i = \ButtonList()\Height / 2 - TextHeight(Caption.s) / 2
      EndIf
      \ButtonList()\TextX = XP.i
      \ButtonList()\TextY = YP.i

    StopDrawing()
    FreeFont(MyFontID.i)
    
    ; create gadget
    Protected newGadgetID.i = ImageGadget(Gadget.i, PosX.i, PosY.i, \ButtonList()\Width, \ButtonList()\Height, ImageID(\ButtonList()\ButtonImageID_draw))
    If Gadget.i = #PB_Any
      \ButtonList()\GadgetID = newGadgetID.i
    Else
      \ButtonList()\GadgetID = Gadget.i
    EndIf
    
    rbutton_Redraw(\ButtonList()\GadgetID, 0)
    
  EndWith
  
  ProcedureReturn newGadgetID.i
  
EndProcedure

; Creates a new r-style button in horizontal mode (icon left from text).
; The size depends on the used background-image.
Procedure.i rbutton_CreateH(Gadget.i, WindowID.i, Caption.s, PosX.i, PosY.i, IconImageID.i = 0)
  Protected FontName.s, FontSize.i, FontColor.i, FontStyle.i, ShaddowColor.i
  
  With rButton
    
    If \FontName = "" : FontName.s = "Verdana": Else : FontName.s = \FontName: EndIf ; use defaults
    If \FontSize = 0  : FontSize.i = 16       : Else : FontSize.i = \FontSize: EndIf ; use defaults
    
    FontStyle.i    = \FontStyle
    FontColor.i    = rbutton_DimmColor(\FontColor, -10)
    ShaddowColor.i = rbutton_DimmColor(\FontColor, \ShaddowDimm - 10)
    
    ; load images, if needed
    If \rButtonImageID_normal = 0
      Debug "Need to call rbutton_Init() at first!"
      ProcedureReturn
    EndIf
    If \rButtonImageID_hover = 0
      Debug "Need to call rbutton_Init() at first!"
      ProcedureReturn
    EndIf
    
    ; prepare button and load images
    AddElement(\ButtonList())
    
    \ButtonList()\ButtonImageID_normal = \rButtonImageID_normal
    \ButtonList()\ButtonImageID_hover  = \rButtonImageID_hover
    \ButtonList()\IconImageID          = IconImageID.i
    \ButtonList()\X                    = PosX.i
    \ButtonList()\Y                    = PosY.i
    \ButtonList()\Width                = ImageWidth(\rButtonImageID_normal)
    \ButtonList()\Height               = ImageHeight(\rButtonImageID_normal)
    \ButtonList()\Caption              = Caption.s
    \ButtonList()\ButtonImageID_draw   = CreateImage(#PB_Any, \ButtonList()\Width, \ButtonList()\Height, 32)
    \ButtonList()\FontName             = \FontName
    \ButtonList()\FontSize             = \FontSize
    \ButtonList()\FontStyle            = \FontStyle
    \ButtonList()\FontColor            = \FontColor
    \ButtonList()\ShaddowDimm          = \ShaddowDimm
    \ButtonList()\WindowID             = WindowID.i
    If IconImageID.i <> 0
      \ButtonList()\IconWidth          = ImageWidth(\ButtonList()\IconImageID)
      \ButtonList()\IconHeight         = ImageHeight(\ButtonList()\IconImageID)
    Else
      \ButtonList()\IconWidth          = 0
      \ButtonList()\IconHeight         = 0
    EndIf
    
    Protected MyFontID.i = LoadFont(#PB_Any, FontName.s, FontSize.i, FontStyle.i) ; load font in desired size
    ; start drawing only for gathering the correct values for textwidth/height
    ; generate image values
    StartDrawing(ImageOutput(\ButtonList()\ButtonImageID_draw))
      DrawingFont(FontID(MyFontID.i))
    
      Protected XP.i
      Protected YP.i
      Protected IconSpace.i = \ButtonList()\IconWidth * 0.1
      If \ButtonList()\Caption <> ""
        ; with caption (icon shifted left)
        XP.i = \ButtonList()\Width / 2 - (\ButtonList()\IconWidth + TextWidth(Caption.s) + IconSpace.i) / 2
        YP.i = \ButtonList()\Height / 2 - \ButtonList()\IconHeight / 2
        ; take care about the left position of icon...
        If XP.i < #button_ShaddowWidth + \LineThickness
          XP.i = #button_ShaddowWidth + \LineThickness
        EndIf
      Else
        ; no caption (center icon)
        XP.i = \ButtonList()\Width / 2 - \ButtonList()\IconWidth / 2
        YP.i = \ButtonList()\Height / 2 - \ButtonList()\IconHeight / 2
      EndIf
      \ButtonList()\IconX      = XP.i
      \ButtonList()\IconY      = YP.i
      
      ; draw caption
      If IconImageID.i <> 0
        ; text right
        XP.i = \ButtonList()\IconX + \ButtonList()\IconWidth + IconSpace.i
        YP.i = \ButtonList()\Height / 2 - TextHeight(Caption.s) / 2
      Else
        ; text centered
        XP.i = \ButtonList()\Width / 2  - TextWidth(Caption.s) / 2
        YP.i = \ButtonList()\Height / 2 - TextHeight(Caption.s) / 2
      EndIf
      \ButtonList()\TextX = XP.i
      \ButtonList()\TextY = YP.i

    StopDrawing()
    FreeFont(MyFontID.i)
    
    ; create gadget
    Protected newGadgetID.i = ImageGadget(Gadget.i, PosX.i, PosY.i, \ButtonList()\Width, \ButtonList()\Height, ImageID(\ButtonList()\ButtonImageID_draw))
    If Gadget.i = #PB_Any
      \ButtonList()\GadgetID = newGadgetID.i
    Else
      \ButtonList()\GadgetID = Gadget.i
    EndIf
    
    rbutton_Redraw(\ButtonList()\GadgetID, 0)
    
  EndWith
  
  ProcedureReturn newGadgetID.i
  
EndProcedure

; Checks, if the mouse in the given window is over a r-style button.
; In that case, the button will get hover-effect.
; Simply add this to the event-loop of your window.
Procedure rbutton_CheckHover(WindowID.i)
  Static LastHighlightGadgetID.i
  
  Protected x.i
  Protected FoundSome.b = #False
  
  ; remove dead elements
  ForEach rButton\ButtonList()
    If Not IsGadget(rButton\ButtonList()\GadgetID) 
      DeleteElement(rButton\ButtonList())
    EndIf
  Next
    
  For x.i = 0 To ListSize(rButton\ButtonList()) - 1
    SelectElement(rButton\ButtonList(), x.i)
    
    With rButton\ButtonList()
      
      If \WindowID = WindowID.i
        ; actualize gadget position information
        \X      = GadgetX(\GadgetID)
        \Y      = GadgetY(\GadgetID)
        \Width  = GadgetWidth(\GadgetID)
        \Height = GadgetHeight(\GadgetID)
        
        If rbutton_MouseOverGadget(WindowID.i, \GadgetID) = #True
          ; matching
          FoundSome.b = #True
          ; enable cursor symbol on this gadget
          If \Disabled = #False And \Hidden = #False
            rbutton_MakeCursorHand(\GadgetID) ; change current cursor to a hand
          EndIf
          If LastHighlightGadgetID.i <> \GadgetID
            ; remove old hover gadget
            Protected CurrentGadget.i = \GadgetID
            ; set state of new gadget
            rbutton_Redraw(CurrentGadget.i, 1)
            ; remove state of previous gadget
            If LastHighlightGadgetID.i <> 0
              rbutton_Redraw(LastHighlightGadgetID.i, 0)
            EndIf
            LastHighlightGadgetID.i = CurrentGadget.i
            Break
          EndIf
        EndIf
      EndIf
    EndWith
  Next
  If FoundSome.b = #False
    If LastHighlightGadgetID.i <> 0
      rbutton_Redraw(LastHighlightGadgetID.i, 0)
    EndIf
    LastHighlightGadgetID.i = 0
  EndIf
  
EndProcedure

; disable/enable a rbutton (State = #False -> enabled, State = #True -> disabled)
Procedure rbutton_Disable(rGadgetID.i, State.b)
  Protected Found.b = #False
  
  ForEach rButton\ButtonList()
    If rButton\ButtonList()\GadgetID = rGadgetID.i
      Found.b = #True
      Break
    EndIf
  Next
  If Found.b = #False
    Debug "rGadgetID " + Str(rGadgetID.i) + " not found (Disable)!"
    ProcedureReturn #False
  EndIf
  
  rButton\ButtonList()\Disabled = State.b
  
  DisableGadget(rGadgetID.i, State.b)
  
  rbutton_Redraw(rButton\ButtonList()\GadgetID, 0)
  
EndProcedure

; Hide or unhide a given rbutton (use this instead
; of HideGadget() function for rbutton gadgets).
Procedure rbutton_Hide(rGadgetID.i, State.b)
  Protected Found.b = #False
  
  ForEach rButton\ButtonList()
    If rButton\ButtonList()\GadgetID = rGadgetID.i
      Found.b = #True
      Break
    EndIf
  Next
  If Found.b = #False
    Debug "rGadgetID " + Str(rGadgetID.i) + " not found (Hide)!"
    ProcedureReturn #False
  EndIf
  
  rButton\ButtonList()\Hidden = State.b
  
  HideGadget(rGadgetID.i, State.b)
  
EndProcedure

; Determine the maximum space needed for the buttons. You can give multiple captions
; to the routine, if you divide the captions with the PIPE character (|).
; You will get the maximum button width needed.
; This makes only sense, if you are using the rbutton_Init_OwnerDrawn() with this value as width.
Procedure.i rbutton_GetButtonWidth(Captions.s)
  Static TextImage
  Protected MaxWidth.i = 0
  Protected Width.i = 0
  Protected Caption.s = ""
  
  If rButton\FontName = ""
    Debug "You can not call rbutton_GetButtonWidth() before calling rbutton_SetFont()!"
    ProcedureReturn 0
  EndIf
  
  Protected MyFontID.i = LoadFont(#PB_Any, rButton\FontName, rButton\FontSize, rButton\FontStyle) ; load font in desired size
  If IsImage(TextImage) = 0
    TextImage = CreateImage(#PB_Any, 500, 30)
  EndIf
  
  StartDrawing(ImageOutput(TextImage))
  DrawingFont(FontID(MyFontID.i))
  Protected Idx = 0
  Repeat
    Idx = Idx + 1
    Caption.s = StringField(Captions.s, Idx, "|")
    Width.i = TextWidth(Caption.s) + (#button_ShaddowWidth * #button_ScaleFactor) + #button_ScaleFactor
    If Width.i > MaxWidth.i
      MaxWidth.i = Width.i
    EndIf
  Until Caption.s = ""
  
  StopDrawing()
  
  ProcedureReturn MaxWidth.i
EndProcedure
Example code:

Code: Select all

; TEST AND EXAMPLE CODE
XIncludeFile "rbutton_include.pbi"

OpenWindow(0, 100, 200, 300, 400, "rButton test window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
SetWindowColor(0, RGB(255, 255, 255))

; rbutton_Init(LoadImage(#PB_Any, "regButton_normal.png"), LoadImage(#PB_Any, "regButton_hover.png"))
rbutton_SetFont("Georgia", 9, 0, RGB(255,255,255), -120)
;Define ButtonWidth.i = rbutton_GetButtonWidth("display|print|disabled");
rbutton_Init_OwnerDrawn(120, 120, GetWindowColor(0), RGBA(214,226,236,255), RGBA(120,158,191,255), RGBA(255,255,255,255), RGBA(90,167,255,255),15, 2)

Define regDisplay.i  = rbutton_Create(#PB_Any, 0, "display", 10, 10, LoadImage(#PB_Any, "display.png"))
Define regPrint.i    = rbutton_Create(#PB_Any, 0, "print (disabled)", 160, 10, LoadImage(#PB_Any, "print.png"))
Define regDisplayD.i = rbutton_Create(#PB_Any, 0, "no image", 10, 150, 0)
Define regNoCaption.i= rbutton_Create(#PB_Any, 0, "", 160, 150, LoadImage(#PB_Any, "print.png"))

rbutton_SetFont("Arial", 9, #PB_Font_Bold | #PB_Font_Italic, RGB(255,255,255), -120)
rbutton_Init_OwnerDrawn(80, 30, GetWindowColor(0), RGBA(214,236,226,255), RGBA(120,191,158,255), RGBA(255,255,255,255), RGBA(60,225,137,255),5, 2)
Define regSmall1.i = rbutton_Create(#PB_Any, 0, "yes", 10, 280, 0)
Define regSmall2.i = rbutton_Create(#PB_Any, 0, "no", 105, 280, 0)
rbutton_Init_OwnerDrawn(80, 30, GetWindowColor(0), RGBA(236,214,214,255), RGBA(191,120,120,255), RGBA(255,255,255,255), RGBA(225,60,60,255),5, 2)
Define regSmall3.i = rbutton_Create(#PB_Any, 0, "cancel", 200, 280, 0)

rbutton_SetFont("Courier New", 16, #PB_Font_Bold, RGB(40,30,50), 150)
rbutton_Init_OwnerDrawn(130, 70, GetWindowColor(0), RGBA(206,200,216,255), RGBA(120,100,158,255), RGBA(255,255,255,255), RGBA(100,90,140,255),10, 2)
Define Printer.i = LoadImage(#PB_Any, "print.png")
ResizeImage(Printer.i, 28, 28)
Define regHoriz1.i = rbutton_CreateH(#PB_Any, 0, "print", 10, 320, Printer.i)
Define regHoriz1.i = rbutton_CreateH(#PB_Any, 0, "OK", 150, 320, Printer.i)

rbutton_Disable(regPrint.i, #True)

Define Quit.i = 0

Repeat
  Define Event.i = WaitWindowEvent()

  If Event.i = #PB_Event_CloseWindow  ; If the user has pressed on the close button
    Quit.i = 1
  EndIf
  
  If Event.i = #PB_Event_Gadget
    If EventGadget() = regSmall3.i
      Quit.i = 1
    EndIf
  EndIf
  
  rbutton_CheckHover(0)

Until Quit.i = 1

End
There are two images needed for the above example:
- display.png
- print.png

You may find some on iconfinder.com.

Best,

Kukulkan
Last edited by Kukulkan on Mon Jul 16, 2012 3:23 pm, edited 2 times in total.
User avatar
kernadec
Enthusiast
Enthusiast
Posts: 146
Joined: Tue Jan 05, 2010 10:35 am

Re: Owner-drawn buttons

Post by kernadec »

Hi, Kukulkan
very nice thank you for sharing
can be : yes = green, orange = cancel , No = red
goodbye
User avatar
Kuron
Addict
Addict
Posts: 1626
Joined: Sat Oct 17, 2009 10:51 pm
Location: Pacific Northwest

Re: Owner-drawn buttons

Post by Kuron »

Absolutely gorgeous, Kukulkan! Excellent job!

Thank you VERY much for sharing this.
Best wishes to the PB community. Thank you for the memories. ♥️
User avatar
Kukulkan
Addict
Addict
Posts: 1352
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Owner-drawn buttons

Post by Kukulkan »

kernadec wrote:can be : yes = green, orange = cancel , No = redgoodbye
Yes, no problem. Simply call rbutton_Init_OwnerDrawn() with orange colors before creating the "cancel" button. Theoretical, every button can have a new border-radius, color, font etc. I try'd to be very carefull about ressource usage. We have no problems with about 20 buttons on a window here.

Kukulkan
User avatar
Kukulkan
Addict
Addict
Posts: 1352
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Owner-drawn buttons

Post by Kukulkan »

How we use this on Windows and Linux:

Image

The optimal width for the buttons is calculated using rbutton_GetButtonWidth() provided with all captions. This works great with different languages (we currently support english, german and french).

Kukulkan
User avatar
electrochrisso
Addict
Addict
Posts: 980
Joined: Mon May 14, 2007 2:13 am
Location: Darling River

Re: Owner-drawn buttons

Post by electrochrisso »

I like it. :)
PureBasic! Purely one of the best 8)
User avatar
captain_skank
Enthusiast
Enthusiast
Posts: 636
Joined: Fri Oct 06, 2006 3:57 pm
Location: England

Re: Owner-drawn buttons

Post by captain_skank »

Stoopid question - but could you give en example of handling clicking on a button ??

cheers
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5353
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Owner-drawn buttons

Post by Kwai chang caine »

Nice job, thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
User avatar
Kukulkan
Addict
Addict
Posts: 1352
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Owner-drawn buttons

Post by Kukulkan »

captain_skank wrote:Stoopid question - but could you give en example of handling clicking on a button ??
You can use it like any other button. You can use #PB_Any while creation (like the example) or fixed gadget IDs. You can use all events that a image-gadget would do (in fact, this are simple image gadgets).

Kukulkan
Num3
PureBasic Expert
PureBasic Expert
Posts: 2810
Joined: Fri Apr 25, 2003 4:51 pm
Location: Portugal, Lisbon
Contact:

Re: Owner-drawn buttons

Post by Num3 »

Works on x64 with no problems.
The only thing missing is key navigation, but that can be achieved by extra code!

Thanks for this one!
User avatar
Kukulkan
Addict
Addict
Posts: 1352
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Owner-drawn buttons

Post by Kukulkan »

Hello,

I just updated the code on the first page.

New:
- rbutton_CreateH() makes a horizontal button (icon left beside the caption)
- now works on multiple windows
- reduced initial memory usage

Important: The rbutton_Create() syntax changed somewhat because you need to give the WindowID you are drawing on, too.

Kukulkan
A M S
User
User
Posts: 58
Joined: Fri Aug 14, 2009 2:26 pm
Location: Afghanistan

Re: Owner-drawn buttons

Post by A M S »

very nice and excellent ,Please update if it can...merci :D
Amundo
Enthusiast
Enthusiast
Posts: 191
Joined: Thu Feb 16, 2006 1:41 am
Location: New Zealand

Re: Owner-drawn buttons

Post by Amundo »

Very nice, Kukulkan, thanks for sharing, this makes for a very slick-looking GUI.
Win8.1, PB5.x, okayish CPU, onboard video card, fuzzy monitor (or is that my eyesight?)
"When the facts change, I change my mind" - John Maynard Keynes
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2064
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: Owner-drawn buttons

Post by Andre »

Looks very nice! :D
Any chance for a MacOS adaption?
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
User avatar
Bisonte
Addict
Addict
Posts: 1232
Joined: Tue Oct 09, 2007 2:15 am

Re: Owner-drawn buttons

Post by Bisonte »

The only thing to convert is the "rbutton_MakeCursorHand(GadgetID.i)" procedure...

If you know, how to change the mousepointer... so it's no problem... or let the pointer like it is... ;)
PureBasic 6.10 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
English is not my native language... (I often use DeepL to translate my texts.)
Post Reply