Page 1 of 2

KnobGadget() - Update March 2016 Rev 2.0

Posted: Mon Jun 03, 2013 12:28 pm
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


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

Posted: Mon Jun 03, 2013 12:29 pm
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.

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

Posted: Mon Jun 03, 2013 1:54 pm
by davido
Richard:

Very nice, thanks.

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

Posted: Thu Jun 13, 2013 10:17 am
by Kwai chang caine
Yes !!! great !!!
Thanks 8)

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

Posted: Fri Jun 14, 2013 4:23 pm
by bobobo
cool

but i prefer prerendered images made by knobman
http://www.g200kg.com/en/software/knobman.html

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

Posted: Fri Jun 14, 2013 6:46 pm
by rsts
Very nice gadget.

Thanks for sharing with us. :)

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

Posted: Mon Jun 17, 2013 10:05 pm
by em_uk
Very nice code, like the inclusion of mousewheel.

Good use of windows callback also.

Well done.

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

Posted: Mon Jul 08, 2013 10:32 am
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

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

Posted: Mon Jul 08, 2013 2:15 pm
by morosh
Good job!!
thank you for sharing

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

Posted: Mon Jul 08, 2013 3:07 pm
by Fred
You can now use BindGadgetEvent() starting with 5.20 to get realtime events.

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

Posted: Mon Jul 08, 2013 5:32 pm
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

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

Posted: Mon Jul 08, 2013 5:48 pm
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...)

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

Posted: Mon Jul 08, 2013 8:58 pm
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/

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

Posted: Mon Jul 08, 2013 9:22 pm
by infratec

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

Posted: Sun Aug 25, 2013 8:56 pm
by minimy
Thank you very much Infratec!