KnobGadget() - Update March 2016 Rev 2.0

Share your advanced PureBasic knowledge/code with the community.
RichardL
Enthusiast
Enthusiast
Posts: 532
Joined: Sat Sep 11, 2004 11:54 am
Location: UK

KnobGadget() - Update March 2016 Rev 2.0

Post by RichardL »

Hi Folks,
See the next posting for more info.
Later versions will replace this posting.
RichardL

Code: Select all

; ====================================
; Name:    KnobGadget
; Version: 1.02
; Author:  RichardL
; Date:    5th June 2013
; OS:      Windows
; PB ver.: 5.11
; License: Free
; ====================================
;{ Release info...
; 0.90 First release version

; 0.91 Added poor man's anti-aliasing to backdrop
;      Improved Font specification
;      Improved radial markers... rather nice!
;      Added flag bit to control 'heat ring'
;      Demo includes slaving two knobs.

; 0.92 4th June 2013 
;      Added 'jazzy' knob top with control bit in flag
;      Added frame option with control bit in flag
;      Added frame border options
;      Improved scaling and caption positioning4

; 1.00 5th June 2013
;      Modified control range. Knob 300 degrees = 16383 'clicks'
;      Demo shows how to apply small steps with MouseWheel
;        Mouse wheel UP   - Fine adjust 1 clicks per mouse increment
;        Mouse wheel DOWN - COARSE adjust 20 clicks per mouse increment

;      Now code is an IncludeFile() (=.PBI) but the demo code is automatically
;      compiled if this file is compiled on its own for testing, thanks to
;                 <<< CompilerIf #PB_Compiler_IsMainFile >>>

; 1.01 23rd June 2013 (505)
;      Fixed benign bug with \KnobRate
;      Added flag bit and code to create a borderless version
;      Modified demo to show all flag options
;      Code version 05 as I'm changing the input syntax and other things 
;      to allow Max and Min like a ScrollBar().
;      Added #MultiTurn1 flag. Knob goes round with mouse but 'span'
;      is spread over 'N' revolutions.

; 1.02 8th July 2013 (542)
;     Added #SWITCHKNOB - Knob clicks to one of 'N' positions.
;     Added #PLAINKKNOB - No Graduations or RING_Heat... good for SWITCH version.

; Things to do someday...
;      Caption update so knob value can be shown... in user units.
;      MULTITURN2 style of knob
;}
;{ Useful references
; http://msdn.microsoft.com/en-us/library/windows/desktop/ms644931(v=vs.85).aspx
; http://www.purebasic.fr/english/viewtopic.php?f=12&T=54117&p=409390&hilit=Meter+Gadget#p409390
;}
;{ Procedure declarations
Declare SetKnobState(gadNum,Value.F=0) ; Value 0 to 16383
Declare GetKnobState(gadNum)
;}
;{ Structures, constants etc
Enumeration ; Flag constants
  
  #POTKNOB      = %000000001
  #MULTITURN1   = %000000010 ; ** See note
  #MULTITURN2   = %000000100 ; ** See note
  #SWITCHKNOB   = %000001000
  #PLAINKKNOB   = %000010000  
  
  #RING_Heat    = %000100000
  #FRAME_Switch = %001000000
  #JAZZ_top     = %010000000
  #BORDER       = %100000000

  
EndEnumeration
; Where N is the 'gearing rate'
; ** MULTITURN1 - Knob tracks mouse and value increase by 1/Number of turns
;    MULTITURN2 - Knob rotates at Rate/N

Structure KNOBTYPE
  width.i               ; Knob gadget width - INSIDE FRAME
  height.i              ; knob height
  KnobImage.i           ; Image that has the knob background.. NOT the pointer
  CanvasID.i            ; Gadget number
  CentreX.i             ; Knob co-ords within the gadget
  CentreY.i
  BackColour.i  
  FrameColourInner.i
  FrameColourOuter.i
  KnobRad.i             ; Radius of knob
  KnobColour.i
  ScaleColour.i         ; Radial lines around knob
  Caption.s             ; Knob caption
  CaptionColour.i       ; and the colour
  DotColour.i           ; The 'pointer' dot and line on top of knob
  LastDotX.i            ; Position of dot 
  LastDotY.i
  Position.F            ; Knob value, returned with GetKnobState(GadNum) or via callback
  Gate.i
  KnobMin.i
  KnobMax.i
  KnobRate.F
  KnobTurns.i
  LastF.F
  flags.i               ; Bitwise option flags...
  CaptionFont.s         ; Use this font for knob caption
  CaptionFontHt.i       ; with this height.
EndStructure
Structure KNOBDEFAULTS
  KnobMax.i 
  KnobMin.i
  KnobRate.F
  KnobTurns.i
  BackColour.i
  ScaleColour.i
  KnobColour.i
  DotColour.i
  CaptionColour.i
  CaptionFont.s
  CaptionFontHt.i
  Caption.s
  FrameColourInner.i
  FrameColourOuter.i
EndStructure

#PBM_KNOB = #WM_APP + 1 ; Application specific callback value
#Scale = 3              ; Scaling factor for the 'poor man's anti-aliasing)

Global KnobDefaults.KNOBDEFAULTS
Global Dim Knob.KNOBTYPE(1)
Global NewList KnobList.i()

; Default knob parameters... 
; SOME may be adjusted BEFORE a knob is created.

With KnobDefaults
  ; These values can be programmed directly or with the procedures
  ; provided.
  \BackColour    = GetSysColor_(#COLOR_BTNFACE) ; Background
  \ScaleColour   = #Black                       ; Scale
  \KnobColour    = #Black                       ; Knob body
  \DotColour     = #Red                         ; Dot and line
  \CaptionColour = #Cyan                        ; Caption
  \FrameColourInner = #Green                    ; Frame 1
  \FrameColourOuter = #White                    ; Frame 2
  \CaptionFont   = "CourierNew"                 ; Caption font
  \CaptionFontHt = 12                           ; Caption font size
  \KnobTurns     = 1                            ; Rotations for for multi-turn
  \Caption       = "Test Knob"
  
  ; Leave alone...
  \KnobMin       = 0
  \KnobMax       = 16383
  \KnobRate      = (\KnobMax - \KnobMin)/300
  
EndWith
;}
Procedure SetGadgetMouseXY(Win,Gadget,MX,MY)
  ; http://msdn.microsoft.com/en-us/library/windows/desktop/ms633516(v=vs.85).aspx
  
  ; Position mouse pointer at specified co-ordinated within a gadget
  Static OldWin = -1
  Static XFix
  Static YFix
   
  ; Get the corrections needed to take account of borders, titles and menus
  If  Win <> OldWin
    
    wi.WINDOWINFO\cbSize = SizeOf(wi)
    GetWindowInfo_(WindowID(Win),wi)
    
    ; Get width of border + title + Menu
    YFix = wi\rcClient\top - wi\rcWindow\top
    XFix = wi\cxWindowBorders
    
    OldWin = Win
  EndIf
  
  MX + WindowX(Win)+GadgetX(Gadget) + XFix
  MY + WindowY(Win)+GadgetY(Gadget) + YFix     
  SetCursorPos_(MX,MY)
  
EndProcedure

Procedure SetKnobBackgroundColour(C = $808080)
  KnobDefaults\BackColour = C
EndProcedure
Procedure SetKnobScaleColour(C = #Black)
  KnobDefaults\ScaleColour = C
EndProcedure
Procedure SetKnobColour(C = #Black)
  Shared KnobDefaults
  KnobDefaults\KnobColour = C
EndProcedure
Procedure SetKnobDotColour(C = #Red)
  KnobDefaults\DotColour = C 
EndProcedure
Procedure SetKnobCaptionColour(C = #Cyan)
  KnobDefaults\CaptionColour = C
EndProcedure
Procedure SetKnobCaptionFont(S.s,ch.i)
  KnobDefaults\CaptionFont   = S
  KnobDefaults\CaptionFontHt = ch
EndProcedure
Procedure SetKnobFrameColours(i.i,o.i)
  KnobDefaults\FrameColourInner  = i
  KnobDefaults\FrameColourOuter  = o
EndProcedure
Procedure SetKnobMultiTurn(i.i)
  KnobDefaults\KnobTurns = i
EndProcedure
Procedure SetKnobState(gadNum,Value.F=0) ; Value in range KnobMin to KnobMax
  Protected n,T,X,Y,Index.i = 0, Result.i = 0
  
  ; Locate specified knob in list
  n=1 : Index = 0
  ForEach KnobList()
    If KnobList() = gadNum
      Index = n    ; 1...
      Break
    EndIf
    n+1
  Next 
  
  If Index = 0
    ProcedureReturn #False
  EndIf
  
  
  With Knob(Index)
    ; Check for out-of-range values
    If Value < \KnobMin : Value = \KnobMin : EndIf
    If Value > \KnobMax : Value = \KnobMax : EndIf 
    
    ; Save the user provided value for this knob
    \Position = Value 
    
    ; Send callback message
    SendMessage_(WindowID(GetActiveWindow()),#PBM_KNOB,Value,GadgetID(gadNum))
    
    ; Re-draw the knob cap and pointer
    ; Convert user value to first circle degrees (This works for single
    ; and multi-turn pots.)
    Value - \KnobMin
    Value / \KnobRate                            ; 0..3x0 degrees
    While Value > 360 : Value - 360 : Wend       ; '%' cannot be used with FLOAT values
    
    ; Angle of pointer 
    If \flags & #MULTITURN1
      T = Value + 180
      T = 360-T
    Else
      T = -(Value + 30) 
    EndIf
     
    If StartDrawing(CanvasOutput(\CanvasID))
        DrawImage(ImageID(\KnobImage),0,0)       ; Draw the knob backdrop
        
        X = \CentreX + (\KnobRad*Sin(Radian(T))) ; End of pointer X
        Y = \CentreY + (\KnobRad*Cos(Radian(T))) ; End of pointer Y
        
        If \flags & #JAZZ_top 
          ; Draw jazzy knob top and pointer
          DrawingMode(#PB_2DDrawing_Gradient)      
          BackColor(\KnobColour)
          FrontColor(\DotColour)
          ConicalGradient(\CentreX,\CentreY, T-90)     
          Circle(\CentreX,\CentreY,\KnobRad)
          Circle(X,Y,\KnobRad>>3,\DotColour)     ; Pointer dot
        Else  
          ; Draw standard knob top with line and pointer dot
          LineXY(\CentreX,\CentreY,X,Y,\DotColour)
          Circle(X,Y,\KnobRad>>3,\DotColour)
        EndIf
        
        ; Draw dynamic caption on SWITCH... 
        ; (Will need FONT data)
        If \flags & #SWITCHKNOB
          If FindString(\Caption,"|")
            DrawingMode(#PB_2DDrawing_Transparent)
            k$ = StringField(\Caption,\Position-\KnobMin+1,"|")
            DrawText((\width-TextWidth(k$))/2,\width-8,k$,\CaptionColour)
          EndIf
        EndIf
        
        ; Keep pointer position etc for readout / next time
        \LastDotX = X : \LastDotY = Y
        
       
        
        Result = #True
      StopDrawing()
       
    EndIf
  EndWith
   
  ProcedureReturn Result
EndProcedure
Procedure GetKnobState(gadNum)
  Protected n,Result
  n=1
  ForEach KnobList()
    If KnobList() = gadNum
      Result = Knob(n)\Position
      Break
    EndIf
    n+1
  Next
  ProcedureReturn Result
EndProcedure

Procedure KnobService(Gad)
  ; Here because the user pressed left mouse button while over a KnobGadget()
  Protected n.i, Index.i, X.i, Y.i, F.F, dx.F, dy.F,T.i, dF.F,  Span.i, MyFont
  Static S.F 
  
  ; Search list for the KnobGadget() 
  n=1 : Index = 0
  ForEach KnobList()
    If KnobList() = Gad
      Index = n         ; 1...
      Break
    EndIf
    n+1
  Next
  If Index = 0
    ProcedureReturn #False
  EndIf
  
  ; Mini event manager - Just while dragging the knob pointer
  With Knob(Index)
    ; Move mouse pointer to last position used. (This prevents the knob
    ; from jumping to a random place when the mouse button is first pressed.)
    SetGadgetMouseXY(GetActiveWindow(),\CanvasID,\LastDotX,\LastDotY)
    
    Repeat
      WaitWindowEvent(20)
      
      ; Finish when user releases the mouse button
      If EventType() = #PB_EventType_LeftButtonUp
        Break 
      EndIf
      
      X = GetGadgetAttribute(Gad,#PB_Canvas_MouseX)
      Y = GetGadgetAttribute(Gad,#PB_Canvas_MouseY)
      
      If X 
        ; Correct X and Y for framed version of knob
        If (\flags & #BORDER )
          Y -2  : X - 2 
        EndIf
        
        ; Calculate angle of mouse relative to knob centre in Degrees
        dx = X - \CentreX 
        dy = Y - \CentreY
        F  = Degree(ATan2(dx,dy))
        If     \flags & (#POTKNOB | #SWITCHKNOB) ;{ Potentiometer and Switch knobs
          If F < 0 : F = 360 + F : EndIf
          F + 240                  ; Offset because pot's '0' is not at cardinal point
          If F>360 : F-360 : EndIf ; Circular wrap around?
          If F>302 : F=0   : EndIf ; Potentiometers have 300 degree travel.
          
          ; Convert degrees of rotation to User value
          F * \KnobRate
          F + \KnobMin
          
          ; Limit the range
          If F < \KnobMin : F = \KnobMin : EndIf
          If F > \KnobMax : F = \KnobMax : EndIf
         
          ; Limit steps size and also force dead-band at bottom. Neat :-)
          If Abs(F - \Position) < (60 * \KnobRate)
            SetKnobState(Gad, Round (F,#PB_Round_Nearest))
          EndIf
          ;}
        ElseIf \flags & #MULTITURN1  ;{ MULTITURN1 - Knob tracks mouse and value increase by 1/\Turns
          ; (There must be a neater way!!!)
          
          F + 90                                    ; Move '0' to top.
          If F < 0 : F=360+F : EndIf                ; Roll-over correction
          
          ; Calculate knob revolution... so far
          Span = (\KnobMax  - \KnobMin)/ \KnobTurns ; Clicks per revolution
          Turn = Int((\Position - \KnobMin)/Span)   ; Completed turns of knob 
          
          ; Detect mouse passing through 'Top Dead Centre' (=TDC) and adjust 'Turns'
          S = F -\LastF                             ; Movement since last time
          \LastF = F
          
          If Abs(S) > 320                           ; 0=>360 or 360=>0
            ; Manage TDC transitions for first, last and in-between turns.
            ; (\Gate: 0 = free to move up or down, -1 = Jammed at min, +1 = Jammed at max)
            Select \Gate
              Case 1 ; Can only decrease...
                If Sign(S) <> -1
                  Turn = \KnobTurns - 1
                  F    = 359
                  \Gate = 0
                EndIf
              Case -1 ; Can only increase...
                If Sign(S) <> 1
                  Turn = 0
                  F    = 1
                  \Gate = 0
                EndIf
              Case 0 ; Can move up or down...
                If Sign(S)=  1 And Turn = 0            :\Gate = -1: EndIf ; Reached min?
                If Sign(S)= -1 And Turn = \KnobTurns-1 :\Gate =  1: EndIf ; Reached max?
                If \Gate  = 0 : Turn - Sign(S) : EndIf                    ; Free to move...
            EndSelect
          EndIf 
          
          ; Convert knob angle to control clicks and update display
          If \Gate = 0
            F * \KnobRate                           ; Degrees => 'Clicks'
            F + (Turn * Span)                       ; Add 'Clicks' for turns 
            F + \KnobMin       
            SetKnobState(Gad,Round(F,#PB_Round_Nearest))
          EndIf
          ;}
        EndIf
        
      EndIf
    ForEver
  EndWith
EndProcedure
Procedure KnobGadget(gadNum.i, X.i, Y.i, Size.i,Caption.s = "", KnobMin.i= 0, KnobMax.i = 10000, flags.i = 0)
   
  Protected Result.i, w.i, h.i, dx.F, dy.F, cx.F, cy.F
  Static KnobCount = 1
  
  w = Size
  h = Size 
  If Caption
    h + KnobDefaults\CaptionFontHt+2
  EndIf
 
  T = #PB_Canvas_ClipMouse|#PB_Canvas_Keyboard|#PB_Canvas_DrawFocus
  If (flags & #BORDER) 
    T | #PB_Canvas_Border
  EndIf
  
  Result = CanvasGadget(gadNum, X, Y, w, h,T)
  
  If Result <> 0
    
    If gadNum = #PB_Any : gadNum = Result : EndIf
    If KnobCount > 1 : Redim Knob(KnobCount) : EndIf
     
    With Knob(KnobCount)
      \CanvasID = gadNum
      \width  = w 
      \height = h 
      If (flags & #BORDER)
        \width - 4  : \height - 4
      EndIf   
      
      \KnobImage = CreateImage(#PB_Any,\width*#Scale,\height*#Scale)
      \CentreX  = \width >> 1
      \CentreY  = \width >> 1  
      
      \KnobRad  = \width >> 2
      If flags & #PLAINKKNOB
        \KnobRad  = \width>>1 - \width>>3
      EndIf
       
      \Caption  = Caption
      \flags    = flags
      \KnobMin  = KnobMin 
      \KnobMax  = KnobMax
      \Position = KnobMin
      
      If \flags & #MULTITURN1
        \KnobTurns = KnobDefaults\KnobTurns
        \KnobRate  = (\KnobMax - \KnobMin) / (360 * \KnobTurns) ; 'Clicks' per degree
      ElseIf \flags & #SWITCHKNOB
        \KnobTurns = 1
        \KnobRate  = (\KnobMax - \KnobMin) / 300 ; 'Clicks' per SWITCH STEP
      Else
        \KnobTurns = 1 ; Pots and switches always 1
        \KnobRate   = (\KnobMax - \KnobMin) / 300
      EndIf
      
      \KnobColour       = KnobDefaults\KnobColour
      \BackColour       = KnobDefaults\BackColour
      \ScaleColour      = KnobDefaults\ScaleColour
      \CaptionColour    = KnobDefaults\CaptionColour
      \CaptionFont      = KnobDefaults\CaptionFont
      \CaptionFontHt    = KnobDefaults\CaptionFontHt
      \DotColour        = KnobDefaults\DotColour
      \FrameColourInner = KnobDefaults\FrameColourInner
      \FrameColourOuter = KnobDefaults\FrameColourOuter
      
      MyFont = LoadFont(#PB_Any,\CaptionFont,\CaptionFontHt * #Scale)
      
      StartDrawing(ImageOutput(\KnobImage))
        DrawingFont(FontID(MyFont))
        
        ; Backdrop
        Box(0,0,\width*#Scale,\height*#Scale,\BackColour)
        
        ; Optional border
        If flags & #FRAME_Switch
          n = 1 : R = 12
          RoundBox(n*#Scale, n*#Scale,(\width-(2*n))*#Scale,(\height-(2*n))*#Scale,R*#Scale,R*#Scale,\FrameColourInner)
          n = 4 : R = 09
          RoundBox(n*#Scale, n*#Scale,(\width-(2*n))*#Scale,(\height-(2*n))*#Scale,R*#Scale,R*#Scale,\FrameColourOuter)
          n = 7 : R = 06
          RoundBox(n*#Scale, n*#Scale,(\width-(2*n))*#Scale,(\height-(2*n))*#Scale,R*#Scale,R*#Scale,\BackColour)
        EndIf
        
        ; Radial markers 
        If Not(flags & #PLAINKKNOB)
          R = (\width>>1 - \width>>4 ) * #Scale          ; Radius of marker lines
          A = 30
          B = 330
          S = 30
          
          If flags & #MULTITURN1 : B = 360 : EndIf
          
          If flags & #SWITCHKNOB
            A = 30
            B = 330
            S = (B - A) / (\KnobMax - \KnobMin)
          EndIf
          
          T = A
          Repeat
            X     = \CentreX*#Scale + (R*Sin(Radian(T))) ; End of pointer X
            Y     = \CentreY*#Scale + (R*Cos(Radian(T))) ; End of pointer Y
            cx    = \CentreX*#Scale                     
            cy    = \CentreY*#Scale                     
            
            ; Draw several adjacent lines to create a single thick one.
            For n = -5 To 5
              dx = n * Cos(Radian(T)) : dy = n * Sin(Radian(T))
              LineXY(cx+dx, cy-dy, X+dx, Y-dy, \ScaleColour)
            Next
            
            T + S
          Until T > B
        EndIf
        
        
        ; Optional coloured ring around the knob
        If flags & #RING_Heat
          DrawingMode(#PB_2DDrawing_Gradient)  
          BackColor( $000000)
          GradientColor(0.2,#Black)
          GradientColor(0.5, $00FFFF)
          FrontColor($0000FF)
          
          ConicalGradient(\CentreX*#Scale,\CentreY*#Scale, 300)     
          Circle(\CentreX*#Scale,\CentreY*#Scale,(\KnobRad+\width>>4-1)*#Scale)
          DrawingMode(#PB_2DDrawing_Default )
        Else
          ; Normal - just clear the radial markers
          Circle(\CentreX*#Scale,\CentreY*#Scale,(\KnobRad+\width>>4-1)*#Scale,\BackColour)
        EndIf
        
        ; Draw the top of the knob
        Circle(\CentreX*#Scale,\CentreY*#Scale,\KnobRad*#Scale,\KnobColour)
        
        ; Draw the caption
        DrawingFont(FontID(MyFont))
        DrawingMode(#PB_2DDrawing_Transparent)
        If Not(\flags & #SWITCHKNOB And CountString(\Caption,"|"))
          X = (#Scale*\width - TextWidth(\Caption))/2
          DrawText(X,#Scale*(\width-12),\Caption,\CaptionColour)
        EndIf
        
      StopDrawing()
      FreeFont(MyFont)
      
      ResizeImage(\KnobImage,\width,\height)
      
      StartDrawing(CanvasOutput(\CanvasID))
        DrawImage(ImageID(\KnobImage),0,0)
      StopDrawing() 
      SetGadgetAttribute(\CanvasID,#PB_Canvas_Cursor,#PB_Cursor_Hand)
    
    EndWith
    
    AddElement(KnobList())
    KnobList() = gadNum
    SetKnobState(gadNum,0)
    
    KnobCount + 1
    
  EndIf
  
  ProcedureReturn gadNum
EndProcedure


CompilerIf #PB_Compiler_IsMainFile
  ; *************************************
  ;            Test code
  ;{ *************************************
  Enumeration 1000 ; Define Window, gadget numbers etc...
    #Win_Main
    
    #Gad_Knob1    ; It is helpful to have the knobes grouped
    #Gad_Knob2    ; together
    #Gad_Knob3
    
    #Gad_Knob4
    #Gad_Knob5
    #Gad_Knob6
    #Gad_Knob7
    #Gad_Knob8
    #Gad_Knob9
    #Gad_Knob10
    #Gad_Knob11
    #Gad_Knob12
    #Gad_Knob13
    #Gad_Knob14
    
    #Gad_Text1
    #Gad_Text2
    #Gad_Text3
    #Gad_Text4
    #Gad_Text5
    
    ; More here
  EndEnumeration
  Procedure WinCallback(hwnd, uMsg, wParam, lParam) ; Standard PB Windows callback...
    
    
    Select uMsg  
     
      Case #PBM_KNOB   ; Receiving Knob() messages in 'real time'
        Select lParam
          Case GadgetID(#Gad_Knob1) : SetGadgetText(#Gad_Text1, "Knob1 = "+Str(wParam))
          Case GadgetID(#Gad_Knob2) : SetGadgetText(#Gad_Text2, "Knob2 = "+Str(wParam))
          Case GadgetID(#Gad_Knob3) : SetGadgetText(#Gad_Text3, "Knob3 = "+Str(wParam))
          Case GadgetID(#Gad_Knob12): SetGadgetText(#Gad_Text4, "Knob4 = "+Str(wParam))
          Case GadgetID(#Gad_Knob14): SetGadgetText(#Gad_Text5, "Switch 14 = "+Str(wParam))
            
            
          ; Good place to check Get/SetKnobState()  
           Case GadgetID(#Gad_Knob4) : SetKnobState(#Gad_Knob8,GetKnobState(#Gad_Knob4)) ; Slave two knobs... one way, 4=>8
        EndSelect
        
        ; etc
        ; etc
        
    EndSelect
    
    ProcedureReturn #PB_ProcessPureBasicEvents 
    
  EndProcedure
  
  OpenWindow(#Win_Main,0,0,900,450,"Knob test Rev 1.02",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  
  ; User defined default values
  SetKnobScaleColour(#White)
  SetKnobColour(#Black)
  SetKnobDotColour(#Red)
  SetKnobCaptionColour(#White)
  SetKnobCaptionFont("CourierNew",18)
  
  ; *************** Create Knobs ****************
  ; Change values BEFORE creating a Knob
  SetKnobDotColour(#Red)
  KnobGadget(#Gad_Knob1,40,20,150,"TREBLE",0,1000,#POTKNOB | #RING_Heat | #FRAME_Switch) ; Frame and HeatRing
  SetKnobState(#Gad_Knob1,50)
  
  SetKnobScaleColour(#Black)
  SetKnobDotColour(#White)
  SetKnobCaptionColour(#Blue)
  KnobGadget(#Gad_Knob2,195,20,150,"BASS",0,10000,#POTKNOB | %110000000)
  
  SetKnobScaleColour(#Yellow)
  SetKnobDotColour(#Blue)
  SetKnobColour(#Cyan)
  SetKnobCaptionColour(#Red)
  KnobGadget(#Gad_Knob3,350,20,150,"VOLUME",1,42,#POTKNOB | %011100000) 
  
  SetKnobScaleColour(#Black)
  SetKnobDotColour(#White)
  SetKnobColour(#Black)
  SetKnobMultiTurn(4)
  KnobGadget(#Gad_Knob12,510,20,200,"MultiTurn (4T)",100,10000,#MULTITURN1 | %110000000)
  SetKnobState(#Gad_Knob12,1000)
  
  SetKnobDotColour(#Yellow)
  SetKnobCaptionColour(#Blue)
  KnobGadget(#Gad_Knob14,720,20,150,"160M|80M|40M|20M|15M|10M|6M|x7|x8|x9|",10,17,#SWITCHKNOB | #JAZZ_top)
  
  SetKnobDotColour(#White)
  SetKnobCaptionFont("CourierNew",12)
  SetKnobCaptionColour(#White)
  SetKnobBackgroundColour($FFAA55)
  KnobGadget(#Gad_Knob4, 10 ,280,100,"Channel 1",0,10000,#POTKNOB | 8<<5)
  KnobGadget(#Gad_Knob5, 110,280,100,"Channel 2",0,10000,#POTKNOB | 9<<5) 
  KnobGadget(#Gad_Knob6, 220,280,100,"Channel 3",0,10000,#POTKNOB | 2<<5) 
  KnobGadget(#Gad_Knob7, 320,280,100,"Channel 4",0,10000,#POTKNOB | 3<<5) 
  
  SetKnobScaleColour(#Black)
  
  KnobGadget(#Gad_Knob8, 430,280,100,"Channel 5",0,10000,#POTKNOB | 4<<5) 
  KnobGadget(#Gad_Knob9, 530,280,100,"Channel 6",0,10000,#POTKNOB | 5<<5)
  KnobGadget(#Gad_Knob10,640,280,100,"Channel 7",0,10000,#POTKNOB | 6<<5) 
  KnobGadget(#Gad_Knob11,740,280,100,"Channel 8",0,10000,#POTKNOB | 7<<5)
  
  DisableGadget(#Gad_Knob8,#True)
  ; ******************************************** 
  
  ; To show knob values returned from Window callback
  TextGadget(#Gad_Text1,40, 200,150,20,"",#PB_Text_Center)
  TextGadget(#Gad_Text2,190,200,150,20,"",#PB_Text_Center)
  TextGadget(#Gad_Text3,340,200,160,20,"",#PB_Text_Center)
  TextGadget(#Gad_Text4,510,250,200,20,"",#PB_Text_Center)
  TextGadget(#Gad_Text5,720,195,150,15,"",#PB_Text_Center)
  
  ; Optional: Callabck to receive 'real-time' knob messages
  SetWindowCallback(@WinCallback())    
  
  ; Dispatch
  WheelRate.i = 1
  Repeat
    Select WaitWindowEvent(10)
      Case #PB_Event_CloseWindow
        Break
        
      Case #PB_Event_Gadget
        Select EventGadget()
            
            ; KnobGadget() all grouped in order to make coding much easier
          Case #Gad_Knob1 To #Gad_Knob14 
           
            Select EventType()
                
              Case #PB_EventType_LeftButtonDown 
                KnobService(EventGadget())
                
              Case  #PB_EventType_MouseWheel          
                T = GetGadgetAttribute(EventGadget(),#PB_Canvas_WheelDelta)            ; Get mouse delta
                If EventGadget() = #Gad_Knob14 : WheelRate = 1 : EndIf
                SetKnobState(EventGadget(),GetKnobState(EventGadget())+(T * WheelRate))  ; Apply it to Knob
                
              Case #PB_EventType_MiddleButtonDown : WheelRate = 50
              Case #PB_EventType_MiddleButtonUp   : WheelRate = 1
                
            EndSelect
        EndSelect
    EndSelect
    
  ForEver
  ;}
CompilerEndIf

Last edited by RichardL on Fri Mar 11, 2016 6:09 pm, edited 7 times in total.
RichardL
Enthusiast
Enthusiast
Posts: 532
Joined: Sat Sep 11, 2004 11:54 am
Location: UK

Re: KnobGadget() - Rev 0.8 (Work in progress)

Post by RichardL »

KnobGadget()

I was looking at basically Pure's Meter gadget, with the intention of making an Ohms Law demo for a basic electronics training course...

http://www.purebasic.fr/english/viewtop ... et#p409390

I thought that a similar style of KnobGadget() would be nice, here is the first pass.
I have some upgrades in mind so keep an eye on the first posting.
Some of the code style follows BP's, so if you use one the other will be easy!

Comments, feedback and suggestions most welcome.
RichardL

Rev 0.90
First time out of the cage!
The size, font and colours are configurable.

Rev 0.91
Still the first day and a new revision already... it must be the sunshine that does it :D

- Added poor man's anti-aliasing to backdrop to improve appearance
- Improved Font specification, now the font name and size are specified.
- Improved radial markers... went back to first principles for this and it looks rather nice!
- Added flag bit to control 'heat ring'.. I have others in mind
- Demo includes slaving two knobs and that DisableGadget() works on my knobs... 'cos I could!

Rev 0.92 - 4th June 2013
Day two... the sun still shines, so time to cut the grass!
- Added optional 'jazzy' knob top using Conical Gradient
- Added optional border frame
- Added user interface to specify border colours
- Improved scaling to better track user specified knob size

Rev 1.0 - 5th June 2013
Finished cutting the grass... so... without much feedback I can only assume that
there are no problems or PB users have no interest in knobs.

- Modified the control range. Knob 300 degrees now reports 0..16383 'clicks'.
- For fine adjustment the mouse wheel can be used and the demo shows this:
Mouse wheel UP - FINE adjust 1 click per mouse wheel increment
Mouse wheel DOWN - COARSE adjust 20 clicks per mouse wheel increment
- Code format
Save code as KnobGadget.pbi and use XIncludeFile() to call it from your own code.
The demo code is automatically compiled if the .PBI file is compiled directly
but the demo code is left out when the KnobGadget.pbi file is referenced in your source,
thanks to:
<<< CompilerIf #PB_Compiler_IsMainFile >>>

Development has now reached a 'bug fix only' plateau until / if a new feature is needed for
my own projects.

Rev1.1 - 23rd June 2013
- I needed a multi-turn control to mimic a multi-turn potentiometer. This gives a smoother
and slower response rate.
- Now provides Min and Max limits like a Scrollbargadget(). Range limited to + Word values.
- Borderless version introduced with control bit in 'Flags'

Rev1.2 - 8th July 2013
- Added #SWITCHKNOB - Knob clicks to one of 'N' positions over range Min to Max.
Minimum range is currently FOUR clicks. (Anyway, two or three look stupid IMHO)
Works OK but if the caption area is to show a selected item the Font info will need to
be saved etc... For the time being you will need to provide your own reporting of the
selected item or accept the limitations of the font being used.
- Added #PLAINKNOB that has no scale markings, colour bands etc. Use this when you
would rather do it all yourself except for the knob twiddling.
- I used this little library with my Audio Signal Generator / Scope project and quite like
the results.
Last edited by RichardL on Mon Jul 08, 2013 10:24 am, edited 6 times in total.
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: KnobGadget() - Rev 0.8 (Work in progress)

Post by davido »

Richard:

Very nice, thanks.
DE AA EB
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: KnobGadget() - Rev1.00 Feedback please!

Post by Kwai chang caine »

Yes !!! great !!!
Thanks 8)
ImageThe happiness is a road...
Not a destination
User avatar
bobobo
Enthusiast
Enthusiast
Posts: 206
Joined: Mon Jun 09, 2003 8:30 am

Re: KnobGadget() - Rev1.00 Feedback please!

Post by bobobo »

cool

but i prefer prerendered images made by knobman
http://www.g200kg.com/en/software/knobman.html
사십 둘 .
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: KnobGadget() - Rev1.00 Feedback please!

Post by rsts »

Very nice gadget.

Thanks for sharing with us. :)
User avatar
em_uk
Enthusiast
Enthusiast
Posts: 366
Joined: Sun Aug 08, 2010 3:32 pm
Location: Manchester UK

Re: KnobGadget() - Rev1.00 Feedback please!

Post by em_uk »

Very nice code, like the inclusion of mousewheel.

Good use of windows callback also.

Well done.
----

R Tape loading error, 0:1
RichardL
Enthusiast
Enthusiast
Posts: 532
Joined: Sat Sep 11, 2004 11:54 am
Location: UK

Re: KnobGadget() - March'16 Rev 2.0 Feedback please!

Post by RichardL »

Good morning folks,

Thank you for the feedback.
I have added a few extra features, the revised code is in the first post and some info about the changes are in the second.

All the best...
Richard L
Last edited by RichardL on Fri Mar 11, 2016 6:06 pm, edited 1 time in total.
morosh
Enthusiast
Enthusiast
Posts: 329
Joined: Wed Aug 03, 2011 4:52 am
Location: Beirut, Lebanon

Re: KnobGadget() - Rev1.02 Feedback please!

Post by morosh »

Good job!!
thank you for sharing
PureBasic: Surprisingly simple, diabolically powerful
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: KnobGadget() - Rev1.02 Feedback please!

Post by Fred »

You can now use BindGadgetEvent() starting with 5.20 to get realtime events.
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: KnobGadget() - Rev1.02 Feedback please!

Post by BasicallyPure »

It's an impressive gadget.
I like the upgrades.

This gadget is complex enough that a couple of paragraphs explaining the proper syntax for the KnobGadget procedure and descriptions of each of the parameters would be helpful.
Something similar to the entries in PB's help doc.
Also, a list of all the support procedures and their purpose.
Having all of this information would allow use of the gadget without searching through the code to figure it out.

BP
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: KnobGadget() - Rev1.02 Feedback please!

Post by Joris »

RichardL very nice and thanks for sharing.
Fred wrote:You can now use BindGadgetEvent() starting with 5.20 to get realtime events.
@Fred is there any info on this (just looked in the PB helpfile nothing there...)
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
minimy
Enthusiast
Enthusiast
Posts: 556
Joined: Mon Jul 08, 2013 8:43 pm
Location: off world

Re: KnobGadget() - Rev1.02 Feedback please!

Post by minimy »

The first thing to say that my English is very bad ..
either way I'll get right to the point:
I found a code to make a vumeter with sound output, the bad is that it is in C and my C is very poor. I leave the link and maybe you can translate to PureBasic.
Thanks and by the way, magnificent demonstration of the power of PureBasic.
Thank you for your great contribution.

http://sourceforge.net/projects/windowsvumeter/
If translation=Error: reply="Sorry, Im Spanish": Endif
infratec
Always Here
Always Here
Posts: 7587
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: KnobGadget() - Rev1.02 Feedback please!

Post by infratec »

User avatar
minimy
Enthusiast
Enthusiast
Posts: 556
Joined: Mon Jul 08, 2013 8:43 pm
Location: off world

Re: KnobGadget() - Rev1.02 Feedback please!

Post by minimy »

Thank you very much Infratec!
If translation=Error: reply="Sorry, Im Spanish": Endif
Post Reply