Page 1 of 2

Owner-drawn buttons

Posted: Wed Feb 15, 2012 9:32 am
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

Re: Owner-drawn buttons

Posted: Wed Feb 15, 2012 11:10 am
by kernadec
Hi, Kukulkan
very nice thank you for sharing
can be : yes = green, orange = cancel , No = red
goodbye

Re: Owner-drawn buttons

Posted: Wed Feb 15, 2012 11:39 am
by Kuron
Absolutely gorgeous, Kukulkan! Excellent job!

Thank you VERY much for sharing this.

Re: Owner-drawn buttons

Posted: Wed Feb 15, 2012 11:47 am
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

Re: Owner-drawn buttons

Posted: Wed Feb 15, 2012 12:00 pm
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

Re: Owner-drawn buttons

Posted: Thu Feb 16, 2012 9:26 am
by electrochrisso
I like it. :)

Re: Owner-drawn buttons

Posted: Thu Feb 16, 2012 12:05 pm
by captain_skank
Stoopid question - but could you give en example of handling clicking on a button ??

cheers

Re: Owner-drawn buttons

Posted: Thu Feb 16, 2012 3:04 pm
by Kwai chang caine
Nice job, thanks for sharing 8)

Re: Owner-drawn buttons

Posted: Thu Feb 16, 2012 3:25 pm
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

Re: Owner-drawn buttons

Posted: Fri Feb 17, 2012 3:00 pm
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!

Re: Owner-drawn buttons

Posted: Sat Apr 14, 2012 2:34 pm
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

Re: Owner-drawn buttons

Posted: Fri Apr 20, 2012 4:59 am
by A M S
very nice and excellent ,Please update if it can...merci :D

Re: Owner-drawn buttons

Posted: Mon May 14, 2012 3:04 am
by Amundo
Very nice, Kukulkan, thanks for sharing, this makes for a very slick-looking GUI.

Re: Owner-drawn buttons

Posted: Mon May 14, 2012 10:57 pm
by Andre
Looks very nice! :D
Any chance for a MacOS adaption?

Re: Owner-drawn buttons

Posted: Mon May 14, 2012 11:37 pm
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... ;)