Mobile-inspired Toggle Switches

Developed or developing a new product in PureBasic? Tell the world about it.
missile69
User
User
Posts: 25
Joined: Mon Feb 21, 2011 12:15 pm

Mobile-inspired Toggle Switches

Post by missile69 »

A toggle switch I made using the awesome canvas gadget. Was made as a way of trying to learn PB's 2D drawing commands, which I had never really used before. Should be cross-platform as I only used PB's commands but I don't have a Mac or Linux machine for testing. If users of those systems could check it out and let me know what changes/tweaks need to be made, that'd be great! Thanks! :)

Edit: Aug 14, 2015
Bug with use of #PB_Any fixed thanks to dige

Edit: Dec 11, 2014
Added some window loading code to demo to reduce loading flicker as suggested by Electrochrisso
Commented out GetGadgetFont() line as it was not working on Macs. Thanks, Shardik.

Edit: Code for trackbar not included. Still working on it.

Image

Code: Select all

; Project     : Slick GUI Elements
; This Element: Switch/toggle
; Author      : missile69 - http://www.purebasic.fr/english/memberlist.php?mode=viewprofile&u=7421
; OS          : Cross-platform, I think :)
; PB version  : 5.31
; License     : http://www.gnu.org/licenses/gpl.html

;XIncludeFile  "SlickSlider.pbi"

EnableExplicit

Structure _slk_switch
  cnvColor.i
  fontNumber.i
  gadNumber.i
  height.i
  hover.b
  ONcolor.i
  style.i
  state.i
  text.s
  textColor.i
  width.i
EndStructure

; Check to see if the values shared among my custom gadgets already exist
CompilerIf Not Defined(imgSlickBG, #PB_Constant)
  Enumeration _Slk
    ;Drawn Images
    #imgSlickBG
    #imgSlickLayer1
  EndEnumeration
  
  ;Drawing Style constants
  #SlickMetal       = 4         ; Brushed aluminum look
  #SlickClean       = 8         ; Clean white look
  ;- Color Constants
  #clrSlickText     = $FF000000 ; Default text color
  #clrSlickBlue     = $FFFFB300 ; Default color for ON position
  #clrSlickWhite    = $FFFFFFFF ; 
  #clrSlickExLtGrey = $FFEBEBEB ; Main knob color 1
  #clrSlickLtGrey   = $FFE2E2E2 ; Default OFF color 
  #clrSlickDkGrey   = $FF575757 ; Main knob color 2. Also for bevel.
  #clrSlickMdGrey   = $FFAEAEAE ; Used for bevel edge
CompilerEndIf


;- Size Constants
#switchMinWidth    = 32
#switchMinHeight   = 20

NewList slk_Switches._slk_switch()

Procedure.i _slk_Select_Switch(gadNum.i)
  
  Shared slk_Switches()
  ForEach slk_Switches()
    If slk_Switches()\gadNumber = gadNum
      ProcedureReturn @slk_Switches()
    EndIf
  Next
  
  ProcedureReturn 0
EndProcedure

Procedure _slk_UpdateSwitchDrawing()
  ;Main drawing procedure. Called for any visible changes like drawing On/off state 
  ;and when the text changes.
  
  ;State : Zero/#false is OFF state. Non-zero is ON state

  Shared slk_Switches()
  
  If CreateImage(#imgSlickBG, 370, 230, 32, slk_Switches()\cnvColor) And CreateImage(#imgSlickLayer1, 230, 230, 32)
    With slk_Switches()
      ;-===================== Draw knob ==========================
      ;Common parts
      StartDrawing(ImageOutput(#imgSlickLayer1))
      ;Make circle's BG transparent
      DrawingMode(#PB_2DDrawing_AlphaChannel)
      Box(0, 0, 230, 230, $00000000)
      ;Circle Shadow
      DrawingMode(#PB_2DDrawing_AlphaBlend)
      Circle(115, 121, 106, #clrSlickDkGrey)
      ;Bevel
      DrawingMode(#PB_2DDrawing_Gradient|#PB_2DDrawing_AlphaBlend)
      BackColor (#clrSlickMdGrey)
      FrontColor(#clrSlickMdGrey)
      GradientColor(0.5, #clrSlickDkGrey)
      LinearGradient(115, 5, 115, 230) 
      Circle(115, 115, 106)
      
      If \style = #SlickMetal  
        ;Main circle
        ResetGradientColors()
        BackColor (#clrSlickDkGrey)
        FrontColor(#clrSlickDkGrey)
        GradientColor  (3/6, #clrSlickDkGrey  )
        If \hover
          GradientColor(1/6, #clrSlickWhite   )
          GradientColor(4/6, #clrSlickWhite   )
        Else
          GradientColor(1/6, #clrSlickExLtGrey)
          GradientColor(4/6, #clrSlickExLtGrey)
        EndIf
        ConicalGradient(115, 115, 30)     
        Circle(115, 115, 100)
        StopDrawing() 
      ElseIf \style = #SlickClean
        ;Main circle
        If \hover
          ResetGradientColors()
          BackColor (#clrSlickExLtGrey)
          FrontColor(#clrSlickWhite)
          Circle(115, 115, 100)
        Else
          DrawingMode(#PB_2DDrawing_Default)
          Circle(115, 115, 100, #clrSlickWhite)
        EndIf
        StopDrawing()
      EndIf
      
      ;-=============== Draw the pill-shaped BG ==================
      ;Box is common to both ON and OFF states
      StartDrawing(ImageOutput(#imgSlickBG))
      DrawingMode(#PB_2DDrawing_AlphaBlend)
      Box(115, 12, 170, 200, #clrSlickDkGrey)
      Box(115, 18, 170, 200, #clrSlickWhite )
      
      If \state = #False ;0 Gadget OFF - Draw grey pill
        Circle(265, 112, 100, #clrSlickDkGrey)
        Circle(265, 118, 100, #clrSlickWhite )
        Circle(265, 115, 100, #clrSlickLtGrey)
        Box(115, 15, 170, 200, #clrSlickLtGrey)
        ;place the knob
        DrawingMode(#PB_2DDrawing_AlphaBlend)
        DrawAlphaImage(ImageID(#imgSlickLayer1), 2, 0)
        StopDrawing()
        ResizeImage(#imgSlickBG, 32, 20)
      Else ;non-zero is ON - Draw colored pill
        Circle(115, 112, 100, #clrSlickDkGrey)
        Circle(115, 118, 100, #clrSlickWhite )
        Circle(115, 115, 100, \ONcolor)
        Box   (115, 15, 170, 200, \ONcolor)
        ;place the knob
        DrawingMode(#PB_2DDrawing_AlphaBlend)
        DrawAlphaImage(ImageID(#imgSlickLayer1), 138, 0)
        StopDrawing()
        ResizeImage(#imgSlickBG, 32, 20)
      EndIf
      
      ;assign image to canvas gadget and set BG color
      StartDrawing(CanvasOutput(\gadNumber))
      Box(0, 0, \width, \height, \cnvColor)
      DrawImage(ImageID(#imgSlickBG), 0, (\height-ImageHeight(#imgSlickBG))/2)
      
      ;-====================== Draw Text =========================
      If IsFont(\fontNumber)
        DrawingFont(FontID(\fontNumber))
      Else
        CompilerIf #PB_Compiler_OS = #PB_OS_Windows Or #PB_Compiler_OS = #PB_OS_Linux
          DrawingFont(GetGadgetFont(#PB_Default))
        CompilerElse 
          ;Not working on Macs
          ;DrawingFont(GetGadgetFont(#PB_Default))
        CompilerEndIf
        
      EndIf
      DrawingMode(#PB_2DDrawing_Transparent)
      DrawText(38, (\height-TextHeight(\text))/2, \text, \textColor)
      StopDrawing()
    EndWith
  EndIf
  
EndProcedure

Procedure.i GetSlickSwitchState(GadNum.i)
  ;Returns 0 if in OFF state and non-zero if ON
  Shared slk_Switches()
  
  If _slk_Select_Switch(GadNum)
    ProcedureReturn slk_Switches()\state
  EndIf
    
    ProcedureReturn 0
EndProcedure

Procedure SetSlickSwitchState(GadNum.i, State.i)
  ;Set the switch's state. Zero = Off, Non-zero = ON
  
  Shared slk_Switches()
  If _slk_Select_Switch(GadNum)
    slk_Switches()\state = State
    _slk_UpdateSwitchDrawing()
  EndIf
  
EndProcedure

Procedure SetSlickSwitchColor(GadNum.i, Color.i)
  ;Change the track/bar color
  
  Shared slk_Switches()
  If _slk_Select_Switch(GadNum)
    slk_Switches()\ONcolor = Color
    _slk_UpdateSwitchDrawing()
  EndIf
  
EndProcedure

Procedure.s GetSlickSwitchText(GadNum.i)
  ;Retrieve gadget's text
  
  Shared slk_Switches()
  If _slk_Select_Switch(GadNum)
    ProcedureReturn slk_Switches()\text
  EndIf
  
  ProcedureReturn #NULL$
EndProcedure

Procedure SetSlickSwitchText(GadNum.i, Text.s)
  ;Set gadget text
  
  Shared slk_Switches()
  If _slk_Select_Switch(GadNum)
    slk_Switches()\text = Text
    _slk_UpdateSwitchDrawing()
  EndIf
  
EndProcedure

Procedure.i IsSlickSwitch(GadNum.i) 
  ;Checks to see if given gadget number is a SlickSwitch
  ;Returns non-zero if it is and 0 if it's not.
  
  ProcedureReturn _slk_Select_Switch(GadNum)
  
EndProcedure

Procedure FreeSlickSwitch(GadNum.i)
  ;Free/remove given switch gadget
  
  Shared slk_Switches()
  If _slk_Select_Switch(GadNum)
    DeleteElement(slk_Switches())
    FreeGadget(GadNum)
  EndIf
  
EndProcedure

Procedure _slk_Switch_Events()
  ;Callback for all the switch gadgets.
  
  Shared slk_Switches()
  Protected GadNum.i = EventGadget()
  _slk_Select_Switch(GadNum)
  
  With slk_Switches()
    Select EventType()
      Case #PB_EventType_LeftClick
        If \state = #False
          \state  = #True 
          _slk_UpdateSwitchDrawing()
        Else
          \state  = #False
          _slk_UpdateSwitchDrawing()
        EndIf
        ;Send Change event to gadget's window
        PostEvent(#PB_Event_Gadget, EventWindow(), GadNum, #PB_EventType_Change)
        
      Case #PB_EventType_RightClick 
        Debug "Right Click"
        
      Case #PB_EventType_MouseEnter, #PB_EventType_Focus
        \hover = #True
        _slk_UpdateSwitchDrawing()
        
      Case #PB_EventType_MouseLeave, #PB_EventType_LostFocus
        \hover = #False
        _slk_UpdateSwitchDrawing()
        
      Case #PB_EventType_KeyUp 
        ;Enable turning on/off via space key like with checkbox
        If GetGadgetAttribute(\gadNumber, #PB_Canvas_Key) = #PB_Shortcut_Space
          If \state
            \state = #False
          Else
            \state = #True
          EndIf
          _slk_UpdateSwitchDrawing()
          PostEvent(#PB_Event_Gadget, EventWindow(), GadNum, #PB_EventType_Change)
        EndIf
        
      Case #PB_EventType_MouseWheel
        If GetGadgetAttribute(\gadNumber, #PB_Canvas_WheelDelta) < 0 ;Mouse wheel down
          If \state = #False
            \state  = #True
            _slk_UpdateSwitchDrawing()
            PostEvent(#PB_Event_Gadget, EventWindow(), GadNum, #PB_EventType_Change)
          EndIf
        Else                                                         ;Mouse wheel up
          If \state
            \state = #False
            _slk_UpdateSwitchDrawing()
            PostEvent(#PB_Event_Gadget, EventWindow(), GadNum, #PB_EventType_Change)
          EndIf
        EndIf
        ;Comment the above IF statement and uncomment the one below to change which
        ;mouse wheel directions turns the gadget on or off. 
;         If GetGadgetAttribute(\gadNumber, #PB_Canvas_WheelDelta) < 0 ;Mouse wheel down
;           If \state 
;             \state = #False
;             _slk_UpdateSwitchDrawing()
;           EndIf
;         Else                                                         ;Mouse wheel up
;           If \state = #False
;             \state  = #True
;             _slk_UpdateSwitchDrawing()
;           EndIf
;         EndIf
        
    EndSelect
  EndWith
  
EndProcedure

Procedure.i SlickSwitchGadget(GadNum.i, x.i, y.i, 
                              w.i = 0, 
                              h.i = 0,
                              Text.s = "", 
                              Style.i = #SlickMetal, 
                              ONColor.i = #clrSlickBlue, 
                              CnvColor.i = #PB_Default,
                              TextColor.i = #clrSlickText, 
                              FontNum.i = #PB_Default)
  
  Shared slk_Switches()
  Protected r.i
  
  ;Set minimums and defaults
  If w < #switchMinWidth:  w = #switchMinWidth:  EndIf
  If h < #switchMinHeight: h = #switchMinHeight: EndIf
  If CnvColor = #PB_Default
    CompilerIf #PB_Compiler_OS  = #PB_OS_Windows
      CnvColor = GetSysColor_(#COLOR_BTNFACE)
    CompilerElse
      CnvColor = $D0D0D0
    CompilerEndIf
  EndIf
  
  If IsGadget(GadNum) = #False  
    If AddElement(slk_Switches())
      With slk_Switches()
        \cnvColor = CnvColor
        \fontNumber  = FontNum
        ;\gadNumber   = CanvasGadget(GadNum, x, y, w, h, #PB_Canvas_Keyboard|#PB_Canvas_DrawFocus)
        \gadNumber   = CanvasGadget(GadNum, x, y, w, h, #PB_Canvas_Keyboard)
        If gadNum <> #PB_Any: \gadNumber = gadNum: EndIf
        \height      = h
        \ONcolor     = ONColor
        \state       = #False
        \style       = Style
        \text        = Text
        \textColor   = TextColor
        \width       = w
        _slk_UpdateSwitchDrawing() ;Initial drawing
        BindGadgetEvent(\gadNumber, @_slk_Switch_Events())
        r = \gadNumber
      EndWith
    EndIf
    
  EndIf
  
  ProcedureReturn r
EndProcedure
DisableExplicit

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
  If OpenWindow(0, 0, 0, 275, 175, "Slick Switch Test", #PB_Window_ScreenCentered|#PB_Window_SystemMenu|#PB_Window_Invisible)
    Define e.i, Switch1.i, Slider1.i, winColor.i = $FFFFFF
    SetWindowColor(0, winColor)
    
    Switch1 = SlickSwitchGadget(#PB_Any, 10, 10, 125, 0, "With sample text", #SlickClean, #clrSlickBlue, winColor)
    SlickSwitchGadget(#PB_Any, 10, 40, 0, 0, "", #SlickClean, $FFc900ff, winColor)
    SlickSwitchGadget(#PB_Any, 10, 70, 0, 0, "", #SlickClean, #clrSlickDkGrey, winColor)
    ;CheckBoxGadget(#PB_Any, 10, 100, 125, 20, "Normal checkbox")
    
    SlickSwitchGadget(#PB_Any, 140, 10, 125, 0, "With sample text", #SlickMetal, #clrSlickBlue, winColor)
    SlickSwitchGadget(#PB_Any, 140, 40, 0, 0, "", #SlickMetal, $FFc900ff, winColor)
    SlickSwitchGadget(#PB_Any, 140, 70, 0, 0, "", #SlickMetal, #clrSlickDkGrey, winColor)
    
    ;Slider1 = SlickSliderGadget(#PB_Any, 10, 140, 255, 0, 0, 500, #clrSlickBlue, #SliderStateOnHover|#SlickClean, winColor)
    ;SetSlickSliderState(Slider1, 250)
    
    HideWindow(0, #False)
    Repeat
      e = WaitWindowEvent()
      
      If e = #PB_Event_Gadget
        Select EventGadget()
          Case Switch1
            If EventType() = #PB_EventType_Change
              Debug "Switch 1 changed"
              
            EndIf
            
        EndSelect
        
      EndIf
      
    Until e = #PB_Event_CloseWindow
    
  EndIf
  
CompilerEndIf
Last edited by missile69 on Sat Aug 15, 2015 7:03 am, edited 7 times in total.
User avatar
electrochrisso
Addict
Addict
Posts: 989
Joined: Mon May 14, 2007 2:13 am
Location: Darling River

Re: Mobile-inspired Toggle Switches

Post by electrochrisso »

8) Thanks for sharing.
PureBasic! Purely the best 8)
User avatar
VB6_to_PBx
Enthusiast
Enthusiast
Posts: 627
Joined: Mon May 09, 2011 9:36 am

Re: Mobile-inspired Toggle Switches

Post by VB6_to_PBx »

very nice !
works great
thanks
 
PureBasic .... making tiny electrons do what you want !

"With every mistake we must surely be learning" - George Harrison
User avatar
idle
Always Here
Always Here
Posts: 5836
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Mobile-inspired Toggle Switches

Post by idle »

very slick click, thanks for sharing
Windows 11, Manjaro, Raspberry Pi OS
Image
User avatar
Tenaja
Addict
Addict
Posts: 1959
Joined: Tue Nov 09, 2010 10:15 pm

Re: Mobile-inspired Toggle Switches

Post by Tenaja »

Thank you very much for sharing.
missile69
User
User
Posts: 25
Joined: Mon Feb 21, 2011 12:15 pm

Re: Mobile-inspired Toggle Switches

Post by missile69 »

Thanks guys! Code updated to post a #pb_eventtype_change event when gadget state is changed via keyboard or mouse wheel also, not just click.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Mobile-inspired Toggle Switches

Post by Kwai chang caine »

Splendid :shock:
The chrome button is more real that the true :shock:
Thanks a lot for sharing 8)
ImageThe happiness is a road...
Not a destination
RichardL
Enthusiast
Enthusiast
Posts: 532
Joined: Sat Sep 11, 2004 11:54 am
Location: UK

Re: Mobile-inspired Toggle Switches

Post by RichardL »

Hi,
Looks good... how about a flag to have a vertical toggle switch?
Richard
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Mobile-inspired Toggle Switches

Post by Shardik »

Thank you for sharing your nice example!
missile69 wrote:Should be cross-platform as I only used PB's commands but I don't have a Mac or Linux machine for testing. If users of those systems could check it out and let me know what changes/tweaks need to be made, that'd be great! Thanks! :)
Works fine on Linux (andLinux/Kubuntu 9.04 x86 with KDE) but not on MacOS because GetGadgetFont(#PB_Default) doesn't work:
PB help for GetGadgetFont() wrote:Note: On Mac OSX, this function returns 0 if the gadget does not have a specific font associated with it.
User avatar
electrochrisso
Addict
Addict
Posts: 989
Joined: Mon May 14, 2007 2:13 am
Location: Darling River

Re: Mobile-inspired Toggle Switches

Post by electrochrisso »

@missile69, you probably know this but if you use #PB_Window_Invisible, then use HideWindow(0,#False) before the loop prevents window initialization flicker on slower computers. :)
PureBasic! Purely the best 8)
missile69
User
User
Posts: 25
Joined: Mon Feb 21, 2011 12:15 pm

Re: Mobile-inspired Toggle Switches

Post by missile69 »

electrochrisso wrote:@missile69, you probably know this but if you use #PB_Window_Invisible, then use HideWindow(0,#False) before the loop prevents window initialization flicker on slower computers. :)
I hadn't taken slower computers into consideration. I've updated the code with your suggestion, thanks!
Shardik wrote:Works fine on Linux (andLinux/Kubuntu 9.04 x86 with KDE) but not on MacOS because GetGadgetFont(#PB_Default) doesn't work:
PB help for GetGadgetFont() wrote:Note: On Mac OSX, this function returns 0 if the gadget does not have a specific font associated with it.
Oops! I read that page of the manual too, but maybe I was already half asleep when I got to that sentence! I've commented out the GetGadgetFont() line. On Windows if no font is loaded, PB just uses another default font to draw text. Does it work this way on Macs as well? Or should I try to assign another font like maybe Helvetica? I believe that comes pre-installed on Macs.
dige
Addict
Addict
Posts: 1391
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: Mobile-inspired Toggle Switches

Post by dige »

Very nice! Thank you missile69.

Are there any news on this? And please add the following line:

Code: Select all

        If gadNum <> #PB_Any : \gadNumber = gadNum : EndIf
after:

Code: Select all

\gadNumber   = CanvasGadget(GadNum, x, y, w, h, #PB_Canvas_Keyboard)
"Daddy, I'll run faster, then it is not so far..."
missile69
User
User
Posts: 25
Joined: Mon Feb 21, 2011 12:15 pm

Re: Mobile-inspired Toggle Switches

Post by missile69 »

Thanks, dige. I totally overlooked that. I updated the code with the fix you pointed out, but no, there is no other update to report as it did everything I needed it to do at the time. After these, I moved on to working on some new code to make nice looking pie charts, doughnut charts, etc, but I haven't had enough time to work on it due to some big changes in my life :(. Here's a screenshot of it so far:

Image
Post Reply