Track Selection Gadget - Revised June 26 2011

Share your advanced PureBasic knowledge/code with the community.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Track Selection Gadget - Revised June 26 2011

Post by netmaestro »

I wanted to give the new CanvasGadget a run through its paces and I got this idea from a question in the coding questions forum. It's a selection tool that allows you to set an upper and lower selection point in a large set of <whatever>, say, movie frames for example. I obviously haven't tapped into every feature of the canvas gadget in this project but it's being put to a pretty reasonable test imho. So far it performs exactly as advertised with no disappointments. Beta 2, 1 really as this wasn't improved for 2 and the performance is rock-solid. Really impressive for something this far from its release date. Probably a few bugs in it, it's early. But here's the Canvas Gadget implemented as a TrackSelection tool:

Image

June 26 2011:
Code is updated to use CustomRegions.pbi (available in this forum) and is now nearly crossplatform. The only thing OS-specific in it is the GetSysColor_() command and if some kind Linux/MacOS coder posts or pms the equivalents I can modify this to be fully crossplatform.

Also fixed a small bug in the drawing where the selection box was one pixel too wide. This can be seen in the posted graphic, which I haven't changed.

Code: Select all

;=============================================================
; Library:            TrackSelectionGadget
; Author:             Lloyd Gallant (netmaestro)
; Date:               April 30, 2011
; Target compiler:    Purebasic 4.60 and later
; Target OS:          Microsoft Windows all
; License:            Free, unrestricted, no warranty
;=============================================================
;
;  How to use the gadget: 
;  
;  - Leftclick and drag the lower pointer to set the selection endpoint
;  - Leftclick and drag the upper pointer to set the selection startpoint
;  - Fine-tune the selections with the mousewheel while the mousepointer is over the appropriate pointer
;  - Move the selection endpoint one increment higher/lower with the Right/Left tArrow keys
;  - Move the selection startpoint one increment higher/lower with the Shift/Right and Shift/left arrow keys
;  - Leftclicks outside the thumbs affect the startpoint if the mousepointer is in the top half of the gadget,
;    and the endpoint if the mousepointer is in the bottom half.
;
;  - Note: keyboard control of fine-tuning is scrapped in favor of the mousewheel only.
;

XIncludeFile "CustomRegions.pbi"

Structure TrackData
  p_region1.l
  p_region2.l
  baseimage.l
  baseimagetop.l
  baseimageleft.l
  bclientwidth.l
  baseimagewidth.l
  pointer1.l
  pointer2.l
  selection_start.l
  selection_end.l
  selection_max.l
  tickmin.l
  tickmax.l
  tickleftx.l
  tickrightx.l
  pointer1top.l
  pointer2top.l
  pointer1x.l
  pointer2x.l
  pointermin.l
  pointermax.l
  startchanged.l
  endchanged.l
  displayfont.l
EndStructure

Procedure CreatePointers()
  Protected ptr, i, j
  ptr = CreateImage(#PB_Any, 11,30,32|#PB_Image_Transparent)                 
  
  Protected Dim pcolors(4)
  pcolors(0) = RGBA(0,0,0,0)
  pcolors(1) = GetSysColor_(#COLOR_3DDKSHADOW) |$FFFFFFFFFF000000
  pcolors(2) = GetSysColor_(#COLOR_3DSHADOW)   |$FFFFFFFFFF000000
  pcolors(3) = GetSysColor_(#COLOR_3DFACE)     |$FFFFFFFFFF000000
  pcolors(4) = GetSysColor_(#COLOR_3DHIGHLIGHT)|$FFFFFFFFFF000000                
  
  StartDrawing(ImageOutput(ptr))
    DrawingMode(#PB_2DDrawing_AllChannels)
    Restore base   : For i=0 To 10  : Read.i index  : Plot(i,0,pcolors(index)) : Next
    For j=1 To 9   : Restore body   : For i=0 To 10 : Read.i index : Plot(i,j,pcolors(index)) : Next : Next
    Restore pdn    : For j=10 To 14 : For i=0 To 10 : Read.i index : Plot(i,j,pcolors(index)) : Next : Next
    Restore pup    : For j=15 To 19 : For i=0 To 10 : Read.i index : Plot(i,j,pcolors(index)) : Next : Next    
    For j=20 To 28 : Restore body   : For i=0 To 10 : Read.i index : Plot(i,j,pcolors(index)) : Next : Next    
    Restore base   : For i=0 To 10  : Read.i index  : Plot(i,29,pcolors(index)) : Next
  StopDrawing()
  
  ProcedureReturn ptr
  
  DataSection
    base: Data.i 4,4,4,4,4,4,4,4,4,4,1
    body: Data.i 4,3,3,3,3,3,3,3,3,2,1
    pdn:  Data.i 0,4,3,3,3,3,3,3,2,1,0,0,0,4,3,3,3,3,2,1,0,0,0,0,0,4,3,3
          Data.i 2,1,0,0,0,0,0,0,0,4,2,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0
    pup:  Data.i 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,4,2,1,0,0,0,0,0,0,0,4,3,3 
          Data.i 2,1,0,0,0,0,0,4,3,3,3,3,2,1,0,0,0,4,3,3,3,3,3,3,2,1,0
  EndDataSection
  
EndProcedure

Procedure RedrawTrackSelectionGadget(gadget)
  *p.TrackData = GetGadgetData(gadget)
  StartDrawing(CanvasOutput(Gadget))
    Box(0,0,GadgetWidth(Gadget),GadgetHeight(Gadget),GetSysColor_(#COLOR_3DFACE))
    DrawImage(ImageID(*p\baseimage),*p\baseimageleft,*p\baseimagetop)
    Box(*p\tickleftx,*p\baseimagetop+2,*p\tickrightx-*p\tickleftx,11,GetSysColor_(#COLOR_MENUHILIGHT))
    DrawAlphaImage(ImageID(*p\pointer1),*p\pointer1x,*p\pointer1top)    
    DrawAlphaImage(ImageID(*p\pointer2),*p\pointer2x,*p\pointer2top)
    
    Box(*p\tickmin-17,*p\pointer1top-11,35,10, GetSysColor_(#COLOR_INFOBK))
    DrawingMode(#PB_2DDrawing_Outlined)
    Box(*p\tickmin-17,*p\pointer1top-11,35,10,GetSysColor_(#COLOR_3DDKSHADOW))
    DrawingFont(*p\displayfont)
    DrawingMode(#PB_2DDrawing_Transparent)
    txt$ = Str(*p\selection_start)
    w = TextWidth(txt$)/2
    DrawText(*p\tickmin-w,*p\pointer1top-11,txt$,GetSysColor_(#COLOR_INFOTEXT))
    
    DrawingMode(#PB_2DDrawing_Default)
    Box(*p\tickmin-17,*p\pointer2top+16,35,10,GetSysColor_(#COLOR_INFOBK))
    DrawingMode(#PB_2DDrawing_Outlined)
    Box(*p\tickmin-17,*p\pointer2top+16,35,10,GetSysColor_(#COLOR_3DDKSHADOW))
    DrawingFont(*p\displayfont)
    DrawingMode(#PB_2DDrawing_Transparent)
    txt$ = Str(*p\selection_end)
    w = TextWidth(txt$)/2
    DrawText(*p\tickmin-w,*p\pointer2top+16,txt$,GetSysColor_(#COLOR_INFOTEXT))    
    
  StopDrawing() 
EndProcedure

ProcedureDLL TrackSelectGadget(gadgetnumber, x, y, width, min=0, max=100, drawfocus=0)
  tmp = CreatePointers()
  pointer1 = GrabImage(tmp, #PB_Any,0,0,11,15)
  pointer2 = GrabImage(tmp, #PB_Any,0,15,11,15)
  FreeImage(tmp)
  
  If drawfocus
    flags = #PB_Canvas_Keyboard|#PB_Canvas_GrabMouse|#PB_Canvas_DrawFocus
  Else
    flags = #PB_Canvas_Keyboard|#PB_Canvas_GrabMouse
  EndIf
  
  Protected height = 60
  If gadgetnumber = #PB_Any
    result = CanvasGadget(#PB_Any, x, y, width, height, flags)
    gadgetnumber = result
  Else
    result = CanvasGadget(gadgetnumber, x, y, width, height, flags)
  EndIf
  
  h1 = CreateBitmapRegion(pointer1)
  h2 = CreateBitmapRegion(pointer2)
  
  *gadgetdata.TrackData = AllocateMemory(SizeOf(TrackData))
  With *gadgetdata
    \p_region1       = h1
    \p_region2       = h2 
    \baseimage       = baseimage
    \baseimagetop    = 23
    \baseimageleft   = 20
    \baseimagewidth  = width-36
    \bclientwidth    = \baseimagewidth-5
    \pointer1        = pointer1
    \pointer2        = pointer2
    \selection_start = 0
    \selection_end   = 0
    \selection_max   = max
    \tickmin         = 22
    \tickmax         = width-19
    \pointer1top     = 15
    \pointer2top     = 30
    \tickleftx       = 22
    \tickrightx      = 22
    \pointer1x       = 17
    \pointer2x       = 17
    \pointermin      = 17
    \pointermax      = width-24
    \displayfont     = LoadFont(0,"verdana", 6)
  EndWith
  
  SetGadgetData(gadgetnumber, *gadgetdata)
  *gadgetdata\baseimage = CreateImage(#PB_Any, *gadgetdata\baseimagewidth,15)
  
  StartDrawing(ImageOutput(*gadgetdata\baseimage))
    Box(0,0,*gadgetdata\baseimagewidth,15,GetSysColor_(#COLOR_3DFACE))
    Box(0,0,*gadgetdata\baseimagewidth-1,1,GetSysColor_(#COLOR_3DSHADOW))
    Box(0,0,1,14,GetSysColor_(#COLOR_3DSHADOW))
    Box(1,1,1,12,GetSysColor_(#COLOR_3DDKSHADOW))
    Box(1,1,*gadgetdata\baseimagewidth-3,1,GetSysColor_(#COLOR_3DDKSHADOW))
    Box(2,2,*gadgetdata\baseimagewidth-5,11,GetSysColor_(#COLOR_3DHIGHLIGHT))
    Box(1,13,*gadgetdata\baseimagewidth-4,1,GetSysColor_(#COLOR_3DFACE))
    Box(0,14,*gadgetdata\baseimagewidth-2,1,GetSysColor_(#COLOR_3DHIGHLIGHT))
    Box(*gadgetdata\baseimagewidth-3,1,1,13,GetSysColor_(#COLOR_3DFACE))
    Box(*gadgetdata\baseimagewidth-2,0,1,15,GetSysColor_(#COLOR_3DHIGHLIGHT))
  StopDrawing()
  
  RedrawTrackSelectionGadget(gadgetnumber)
  ProcedureReturn result
EndProcedure

ProcedureDLL GetTrackSelectionStart(gadget)
  *p.TrackData = GetGadgetData(gadget)
  ProcedureReturn *p\selection_start
EndProcedure

ProcedureDLL GetTrackSelectionEnd(gadget)
  *p.TrackData = GetGadgetData(gadget)
  ProcedureReturn *p\selection_end
EndProcedure

ProcedureDLL CheckSelectionStartChanged(gadget)
  *p.TrackData = GetGadgetData(gadget)
  If *p\startchanged
    *p\startchanged=0
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

ProcedureDLL CheckSelectionEndChanged(gadget)
  *p.TrackData = GetGadgetData(gadget)
  If *p\endchanged
    *p\endchanged=0
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

ProcedureDLL HandleTrackEvents(EventGadget,EventType)
  Static hot1, hot2, xoffset
  *p.TrackData = GetGadgetData(EventGadget)
  mx = GetGadgetAttribute(EventGadget,#PB_Canvas_MouseX)
  my = GetGadgetAttribute(EventGadget,#PB_Canvas_MouseY)
  
  Select EventType
      ;       
    Case #PB_EventType_KeyDown
      key = GetGadgetAttribute(EventGadget, #PB_Canvas_Key)
      mods = GetGadgetAttribute(EventGadget, #PB_Canvas_Modifiers)
      Select key
        Case #PB_Shortcut_Right
          If mods & #PB_Canvas_Shift
            *p\pointer1x = *p\tickleftx-5
            If *P\tickleftx < *p\tickmax
              *p\tickleftx+1
              *p\pointer1x+1
              If *p\tickrightx < *p\tickleftx
                *p\tickrightx = *p\tickleftx
                *p\pointer2x = *p\tickrightx-5
              EndIf
            EndIf
            s.d = Round(((*p\tickleftx-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
            *p\selection_start = Int(s)
            *P\startchanged = 1
            If *p\selection_start > *p\selection_end
              *p\selection_end = *p\selection_start
              *p\endchanged = 1
            EndIf                  
            RedrawTrackSelectionGadget(EventGadget) 
          Else
            *p\pointer2x = *p\tickrightx-5
            If *p\tickrightx < *p\tickmax
              *p\tickrightx+1
              *p\pointer2x+1
            EndIf
            e.d = Round(((*p\tickrightx-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
            *p\selection_end = Int(e)
            *p\endchanged = 1
            RedrawTrackSelectionGadget(EventGadget) 
          EndIf
          
        Case #PB_Shortcut_Left
          If mods & #PB_Canvas_Shift
            *p\pointer1x = *p\tickleftx-5
            If *p\tickleftx > *p\tickmin
              *p\tickleftx-1
              *p\pointer1x-1
            EndIf
            s.d = Round(((*p\tickleftx-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
            *p\selection_start = Int(s)
            *P\startchanged = 1
            RedrawTrackSelectionGadget(EventGadget) 
          Else
            *p\pointer2x = *p\tickrightx-5
            If *p\tickrightx > *p\tickmin
              *p\tickrightx-1
              *p\pointer2x-1
              If *p\tickleftx > *p\tickrightx
                *p\tickleftx = *p\tickrightx
                *p\pointer1x = *p\tickleftx-5
              EndIf
            EndIf
            e.d = Round(((*p\tickrightx-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
            *p\selection_end = Int(e)
            *p\endchanged = 1
            If *p\selection_end < *p\selection_start
              *p\selection_start = *p\selection_end
              *p\startchanged = 1
            EndIf                  
            RedrawTrackSelectionGadget(EventGadget) 
          EndIf
          
      EndSelect
      
    Case #PB_EventType_LeftButtonDown
      
      If mx <= *p\tickrightx And mx >= *p\tickmin And (my <= 26)
        *p\pointer1x = mx-5
        If *p\pointer1x < *p\pointermin
          *p\pointer1x = *p\pointermin
        EndIf
        *p\tickleftx = *p\pointer1x+5
        s.d = Round(((*p\tickleftx-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
        *p\selection_start = Int(s)
        *P\startchanged = 1
        hot1 = #True : #hot2 = #False
        RedrawTrackSelectionGadget(EventGadget) 
      ElseIf mx >= *p\tickleftx And mx <=*p\tickmax And (my >= 28)
        *p\pointer2x = mx-5
        If *p\pointer2x > *p\pointermax
          *p\pointer2x = *p\pointermax
        EndIf
        *p\tickrightx = *p\pointer2x+5
        e.d = Round(((*p\tickrightx-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
        *p\selection_end = Int(e)
        *p\endchanged = 1
        hot2 = #True : hot1 = #False
        RedrawTrackSelectionGadget(EventGadget) 
      EndIf
      
    Case #PB_EventType_MouseWheel
      delta = GetGadgetAttribute(EventGadget, #PB_Canvas_WheelDelta)
      If PtinRegion(*p\p_region2, mx-*p\pointer2x, my-*p\pointer2top)
        If delta>0
          thisone = *p\selection_end
          e.d = Round(((*p\tickrightx+1-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
          nextone = Int(e)
          testone = thisone+1
          *p\pointer2x = *p\tickrightx-5
          If testone<nextone 
            If *p\selection_end < nextone And *p\selection_end < *p\selection_max
              *p\selection_end+1
              *p\endchanged = 1
              RedrawTrackSelectionGadget(EventGadget)
            EndIf
          EndIf
        ElseIf delta < 0
          If *p\pointer2x = *p\tickrightx-5
            thisone = *p\selection_end
            e.d = Round(((*p\tickrightx-1-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
            nextone = Int(e)
            testone = thisone-1
            *p\pointer2x = *p\tickrightx-5
            If testone>nextone
              If *p\selection_end > nextone And *p\selection_end > *p\selection_start
                *p\selection_end-1
                *p\endchanged = 1
                RedrawTrackSelectionGadget(EventGadget)
              EndIf
            EndIf
          EndIf
        EndIf
      ElseIf PtinRegion(*p\p_region1, mx-*p\pointer1x, my-*p\pointer1top)
        If delta > 0
          thisone = *p\selection_start
          s.d = Round(((*p\tickleftx+1-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
          nextone = Int(s)
          testone = thisone+1
          *p\pointer1x = *p\tickleftx-5
          If testone>0
            If testone<nextone 
              If *p\selection_start < nextone And *p\selection_start < *p\selection_end
                *p\selection_start+1
                *P\startchanged = 1
                RedrawTrackSelectionGadget(EventGadget)
              EndIf
            EndIf
          EndIf
        ElseIf delta < 0
          thisone = *p\selection_start
          s.d = Round(((*p\tickleftx-1-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
          nextone = Int(s)
          testone = thisone-1
          *p\pointer1x = *p\tickleftx-5
          If testone>=0
            If testone>nextone
              If *p\selection_start > nextone And *p\selection_start > 0
                *p\selection_start-1
                *P\startchanged = 1
                RedrawTrackSelectionGadget(EventGadget)
              EndIf
            EndIf
          EndIf
        EndIf          
      EndIf
      
    Case #PB_EventType_LeftButtonUp
      hot1 = #False
      hot2 = #False
      
    Case #PB_EventType_MouseMove
      buttonstate = GetGadgetAttribute(EventGadget, #PB_Canvas_Buttons)
      lbutton = buttonstate & #PB_Canvas_LeftButton
      If lbutton 
        If hot1
          *p\pointer1x = mx-xoffset
          If *p\pointer1x < *p\pointermin
            *p\pointer1x = *p\pointermin
          ElseIf *p\pointer1x > *p\pointermax
            *p\pointer1x = *p\pointermax
          EndIf
          *p\tickleftx = *p\pointer1x+5
          If *p\tickrightx < *p\tickleftx
            *p\tickrightx = *p\tickleftx
            *p\pointer2x = *p\tickrightx-5
          EndIf
          s.d = Round(((*p\tickleftx-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
          *p\selection_start = Int(s)
          *P\startchanged = 1
          If *p\selection_start > *p\selection_end
            *p\selection_end = *p\selection_start
            *p\endchanged = 1
          EndIf
          RedrawTrackSelectionGadget(EventGadget) 
          
        ElseIf hot2
          *p\pointer2x = mx-xoffset
          If *p\pointer2x < *p\pointermin
            *p\pointer2x = *p\pointermin
          ElseIf *p\pointer2x > *p\pointermax
            *p\pointer2x = *p\pointermax
          EndIf          
          *p\tickrightx = *p\pointer2x+5
          If *p\tickleftx > *p\tickrightx
            *p\tickleftx = *p\tickrightx
            *p\pointer1x = *p\tickleftx-5
          EndIf
          e.d = Round(((*p\tickrightx-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
          *p\selection_end   = Int(e)
          *p\endchanged = 1
          If *p\selection_end < *p\selection_start
            *p\selection_start = *p\selection_end
            *p\startchanged = 1
          EndIf
        EndIf
        RedrawTrackSelectionGadget(EventGadget) 
      Else
        If PtinRegion(*p\p_region1, mx-*p\pointer1x, my-*p\pointer1top)
          SetGadgetAttribute(EventGadget, #PB_Canvas_Cursor,#PB_Cursor_Hand)
          hot1 = #True : hot2 = #False
          xoffset = mx-*p\pointer1x
        ElseIf PtinRegion(*p\p_region2, mx-*p\pointer2x, my-*p\pointer2top)
          SetGadgetAttribute(EventGadget, #PB_Canvas_Cursor,#PB_Cursor_Hand)
          hot2 = #True : hot1 = #False
          xoffset = mx-*p\pointer2x
        Else
          hot1=#False : hot2 = #False
          SetGadgetAttribute(EventGadget, #PB_Canvas_Cursor,#PB_Cursor_Default)
        EndIf
      EndIf
      
  EndSelect
  
EndProcedure

;========================================================
;                    End of Library Code
;========================================================

; Test program

OpenWindow(0,0,0,640,480,"",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ButtonGadget(0,20,20,100,20,"Button")
TrackSelectGadget(1,20,360,600,0,20129,1)

Repeat
  ev = WaitWindowEvent()
  Select ev
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 1
          HandleTrackEvents(EventGadget(), EventType())
      EndSelect
  EndSelect
  
Until ev = #PB_Event_CloseWindow
Last edited by netmaestro on Sun Jun 26, 2011 5:30 pm, edited 9 times in total.
BERESHEIT
User avatar
kenmo
Addict
Addict
Posts: 2033
Joined: Tue Dec 23, 2003 3:54 am

Re: Track Selection Gadget

Post by kenmo »

Brilliant :shock:

I don't have an immediate use for this, but it's a very nice gadget to have handy!
(Looks just like a native gadget, as well - clever.)

A cool variation would be: the pointer only appears when the mouse is over the control, and automatically jumps to whichever end you're hovering closest to. You could then drag the start and end points with just left-clicks, no right-clicks needed. (Although it could be tricky to use when the endpoints are too close and overlap.) Normally I'd try to implement this myself, but it's 2 AM here, I'm exhausted :)

Nice work!
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: Track Selection Gadget

Post by Trond »

It looks very good, but it doesn't seem to act naturally. I just don't feel that I'm in control of the control (pun!).

Consider:
- The lower bound starts at 0, but once you set it to anything else, sometimes it won't go back to 0, only 1.
- Dragging the lower bound past the upper bound moves the upper bound, but left clicking past the upper bound does nothing (I'd expect it to behave like dragging the lower bound there).
- And vice versa for the upper bound.
- When clicking on the band outside the thumb to set the bound (instead of dragging the thumb) I'd find it natural if it started a drag operation.
- Why is there only one thumb? Wouldn't it be natural to have two thumbs, both operated by the left mouse button?
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Track Selection Gadget

Post by netmaestro »

I agree, I don't like it either. It isn't intuitive and it takes too long to learn to use it without making a lot of mistakes. I took the single-thumb idea from the posted picture in Coding Questions, it just had the one thumb. What do you think of this idea?

Image
BERESHEIT
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Track Selection Gadget - Revised May 1/11

Post by netmaestro »

Consider:
- The lower bound starts at 0, but once you set it to anything else, sometimes it won't go back to 0, only 1.
I can't reproduce that no matter how hard I try
- Dragging the lower bound past the upper bound moves the upper bound, but left clicking past the upper bound does nothing (I'd expect it to behave like dragging the lower bound there).
Agreed, done.
- And vice versa for the upper bound.
Agreed, done.
- When clicking on the band outside the thumb to set the bound (instead of dragging the thumb) I'd find it natural if it started a drag operation.
Agreed, done.
- Why is there only one thumb? Wouldn't it be natural to have two thumbs, both operated by the left mouse button?
Agreed, done.

Thanks for your comments, they resulted in a much more intuitive control.
BERESHEIT
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Track Selection Gadget - Revised May 1/11

Post by netmaestro »

Gadget is modified to always show the start/end selection points on the gadget itself, so no need to use statusbartext or textgadgets etc. Code in first post is updated.
BERESHEIT
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: Track Selection Gadget - Revised May 8/11

Post by c4s »

Well, now it would be cool if the "value preview" would (optionally) move with the slider (and hide when cursor isn't over the gadget?) - similar to the #TBS_TOOLTIPS style. And you could use #COLOR_INFOBK (Tooltip background), #COLOR_INFOTEXT (Tooltip text) for it. ;)
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Track Selection Gadget - Revised May 8/11

Post by netmaestro »

And you could use #COLOR_INFOBK (Tooltip background), #COLOR_INFOTEXT (Tooltip text) for it.
Yes, good idea. On the moving info displays though there is a problem with the lower one. You can't see it under the cursor. You can move the cursor down out of the way while dragging because of #PB_Canvas_GrabMouse but for mousewheel finetuning it has to be in the way. Also, I thought it best that you can always read the settings whether the gadget is in focus or not.
BERESHEIT
User avatar
Tomi
Enthusiast
Enthusiast
Posts: 270
Joined: Wed Sep 03, 2008 9:29 am

Re: Track Selection Gadget - Revised May 8/11

Post by Tomi »

Thanks for share this code :)
User avatar
GeBonet
Enthusiast
Enthusiast
Posts: 135
Joined: Fri Apr 04, 2008 6:20 pm
Location: Belgium

Re: Track Selection Gadget - Revised May 8/11

Post by GeBonet »

Hi !, Tank for this code...
I speak French, and in gratitude, here's my contribution ...

Code: Select all

; I wanted To give the new CanvasGadget a run through its paces And I got this idea from a question 
; in the coding questions forum. It's a selection tool that allows you to set an upper and lower selection 
; point in a large set of <whatever>, say, movie frames For example. I obviously haven't tapped into every 
; feature of the canvas gadget in this project but it's being put To a pretty reasonable test imho. 
; So far it performs exactly As advertised With no disappointments. Beta 2, 1 really As this wasn't 
; improved For 2 And the performance is rock-solid. Really impressive For something this far from its 
; release date. Probably a few bugs in it, it's early. But here's the Canvas Gadget implemented As a 
; TrackSelection tool.
; ---------------------------
; Je voulais donner au CanvasGadget une nouvelle forme d'usage et j'ai eu cette idée suite à une 
; question sur le forum. C'est un outil de sélection qui vous permet de définir un Point de sélection 
; supérieure et inférieure dans un grand ensemble, disons, une image vidéo par exemple ou page editeur de texte. 
; Je n'ai évidemment pas exploité toutes les fonctionnalités du Gadget "Canvas" dans ce projet mais il est 
; mis soumis à un test raisonnable dans cet exemple. Jusqu'à présent, il se comporte exactement comme annoncé
; sans déceptions. Cela da,s les version Bêta 2, 1 vraiment et comme cela n'a pas été améliorée pour la Beta 3 
; et la performance semble très stable. Vraiment impressionnant pour une version aussi loin de sa date de 
; libération. Il y a/aura probablement quelques bugs dedans ? Il est tôt pour le dire. mais voici le Gadget 
; "CanvasGadget" mis en œuvre comme un outil de "TrackSelection".
;
;  How to use the gadget: 
;  
;  - Leftclick and drag the lower pointer to set the selection endpoint
;  - Leftclick and drag the upper pointer to set the selection startpoint
;  - Fine-tune the selections with the mousewheel while the mousepointer is over the appropriate pointer
;  - Move the selection endpoint one increment higher/lower with the Right/Left tArrow keys
;  - Move the selection startpoint one increment higher/lower with the Shift/Right and Shift/left arrow keys
;  - Leftclicks outside the thumbs affect the startpoint if the mousepointer is in the top half of the gadget,
;    and the endpoint if the mousepointer is in the bottom half.
;
;  - Note: keyboard control of fine-tuning is scrapped in favor of the mousewheel only.
; -------------------------------------------------------------------------------------------
; Comment utiliser le Gadget ! 
; 
; - Click Gauche : et faites glisser le curseur du bas pour regler la fin de zone
; - Click Gauche : et faites glisser le curseur du dessus pour régler le début de la zone
; - Ajustez finement le pointage des curseurs en plaçant la souris sur le curseur approprié
;   et actionner la molette de la souris... 
; - Déplacer le curseur de début d'un point avec la Maj+ Flèches Gauche/droite
; - Déplacer le curseur de fin de zone d'un point avec la Ctrl + Flèches Gauche/droite
; - Leftclicks dans la zone sans les curseurs déplace l'un ou l'autre curseurs vers l'endroit 
;   ou se trouve le pointeur de la souris
;
Bye :wink:
Sorry for my english :wink: ! (Windows Xp, Vista and Windows 7, Windows 10)
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Re: Track Selection Gadget - Revised May 8/11

Post by DoubleDutch »

Great stuff. :)
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Track Selection Gadget - Revised May 8/11

Post by netmaestro »

Updated today to move close to crossplatform (see first post). Also fixed a minor drawing bug.
BERESHEIT
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Re: Track Selection Gadget - Revised June 26 2011

Post by DoubleDutch »

GetSysColor_()
If there is something like this for Linux/OSX then it would be great if Fred/Freak could add it as a built-in command...
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
User avatar
bembulak
Enthusiast
Enthusiast
Posts: 575
Joined: Mon Mar 06, 2006 3:53 pm
Location: Austria

Re: Track Selection Gadget - Revised June 26 2011

Post by bembulak »

If there is something like this for Linux/OSX then it would be great if Fred/Freak could add it as a built-in command...
Yes, there are such commands.
http://developer.gnome.org/gtk/2.24/GtkWidget.html
http://developer.gnome.org/gtk/2.24/GtkStyle.html
http://developer.gnome.org/gtk/2.24/Gtk ... -style-get

You can "ask" a widget for it's colorset or ask for a complete style, but normally you would just parse the .gtkrc file which is a flat text file that holds the user's (or the system's) theme (and other information).
http://www.gtk.org/api/2.6/gtk/gtk-Resource-Files.html
cheers,

bembulak
WilliamL
Addict
Addict
Posts: 1252
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: Track Selection Gadget - Revised June 26 2011

Post by WilliamL »

The Track Selection Gadget works great! I wanted to see if the code would run on a Mac and the only obstacle with the code was the windows API colors. I replaced the window color themes with my guess of RGBA colors to match. The shortcoming to using RGBA colors is that each platform has theme colors and those should be the colors that are used. On-the-other-hand, if you define the colors you can get creative. This is my offering to the Mac users (and I hope it runs on Linux too) of one method to be able to use netmaestro's gadget.

Code: Select all

;=============================================================
; Library:            TrackSelectionGadget
; Author:             Lloyd Gallant (netmaestro)
; Date:               April 30, 2011
; Target compiler:    Purebasic 4.60 and later
; Target OS:          Microsoft Windows all
; License:            Free, unrestricted, no warranty
;=============================================================
;
;  How to use the gadget: 
;  
;  - Leftclick and drag the lower pointer to set the selection endpoint
;  - Leftclick and drag the upper pointer to set the selection startpoint
;  - Fine-tune the selections with the mousewheel while the mousepointer is over the appropriate pointer
;  - Move the selection endpoint one increment higher/lower with the Right/Left tArrow keys
;  - Move the selection startpoint one increment higher/lower with the Shift/Right and Shift/left arrow keys
;  - Leftclicks outside the thumbs affect the startpoint if the mousepointer is in the top half of the gadget,
;    and the endpoint if the mousepointer is in the bottom half.
;
;  - Note: keyboard control of fine-tuning is scrapped in favor of the mousewheel only.
;

XIncludeFile "CustomRegions.pbi"

Structure TrackData
  p_region1.l
  p_region2.l
  baseimage.l
  baseimagetop.l
  baseimageleft.l
  bclientwidth.l
  baseimagewidth.l
  pointer1.l
  pointer2.l
  selection_start.l
  selection_end.l
  selection_max.l
  tickmin.l
  tickmax.l
  tickleftx.l
  tickrightx.l
  pointer1top.l
  pointer2top.l
  pointer1x.l
  pointer2x.l
  pointermin.l
  pointermax.l
  startchanged.l
  endchanged.l
  displayfont.l
  c_transparent.l
  c_textshadow.l
  c_boxshadow.l
  c_background.l
  c_barbckgrnd.l
  c_textbckgrnd.l
  c_menuhighlight.l
  c_textcolor.l
EndStructure

Procedure CreatePointers()
  Protected ptr, i, j
  ptr = CreateImage(#PB_Any, 11,30,32); |#PB_Image_Transparent)
  
  Protected Dim pcolors(4)
  pcolors(0) = RGBA(0,0,0,0) ; \c_transparent
  pcolors(1) = RGBA(0,0,0,255) ; \c_textshadow ; GetSysColor_(#COLOR_3DDKSHADOW) |$FFFFFFFFFF000000
  pcolors(2) = RGBA(0,0,0,255) ; \c_boxshadow ; GetSysColor_(#COLOR_3DSHADOW)   |$FFFFFFFFFF000000
  pcolors(3) = RGBA(203,198,186,255) ; \c_background ; GetSysColor_(#COLOR_3DFACE)     |$FFFFFFFFFF000000
  pcolors(4) = RGBA(255,255,255,255) ; \c_barbckgrnd ; GetSysColor_(#COLOR_3DHIGHLIGHT)|$FFFFFFFFFF000000 

  StartDrawing(ImageOutput(ptr))
    DrawingMode(#PB_2DDrawing_AllChannels)
    Restore base   : For i=0 To 10  : Read.i index  : Plot(i,0,pcolors(index)) : Next
    For j=1 To 9   : Restore body   : For i=0 To 10 : Read.i index : Plot(i,j,pcolors(index)) : Next : Next
    Restore pdn    : For j=10 To 14 : For i=0 To 10 : Read.i index : Plot(i,j,pcolors(index)) : Next : Next
    Restore pup    : For j=15 To 19 : For i=0 To 10 : Read.i index : Plot(i,j,pcolors(index)) : Next : Next    
    For j=20 To 28 : Restore body   : For i=0 To 10 : Read.i index : Plot(i,j,pcolors(index)) : Next : Next    
    Restore base   : For i=0 To 10  : Read.i index  : Plot(i,29,pcolors(index)) : Next
  StopDrawing()
  
  ProcedureReturn ptr
  
  DataSection
    base: Data.i 4,4,4,4,4,4,4,4,4,4,1
    body: Data.i 4,3,3,3,3,3,3,3,3,2,1
    pdn:  Data.i 0,4,3,3,3,3,3,3,2,1,0,0,0,4,3,3,3,3,2,1,0,0,0,0,0,4,3,3
          Data.i 2,1,0,0,0,0,0,0,0,4,2,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0
    pup:  Data.i 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,4,2,1,0,0,0,0,0,0,0,4,3,3 
          Data.i 2,1,0,0,0,0,0,4,3,3,3,3,2,1,0,0,0,4,3,3,3,3,3,3,2,1,0
  EndDataSection
  
EndProcedure

Procedure RedrawTrackSelectionGadget(gadget)
  *p.TrackData = GetGadgetData(gadget)
  StartDrawing(CanvasOutput(Gadget))
    Box(0,0,GadgetWidth(Gadget),GadgetHeight(Gadget),*p\c_background)
    DrawImage(ImageID(*p\baseimage),*p\baseimageleft,*p\baseimagetop)
    Box(*p\tickleftx,*p\baseimagetop+2,*p\tickrightx-*p\tickleftx,11,*p\c_menuhighlight) ; GetSysColor_(#COLOR_MENUHILIGHT))
    DrawAlphaImage(ImageID(*p\pointer1),*p\pointer1x,*p\pointer1top)    
    DrawAlphaImage(ImageID(*p\pointer2),*p\pointer2x,*p\pointer2top)
    
    Box(*p\tickmin-17,*p\pointer1top-11,35,10,*p\c_textbckgrnd) ; GetSysColor_(#COLOR_INFOBK))
    DrawingMode(#PB_2DDrawing_Outlined)
    Box(*p\tickmin-17,*p\pointer1top-11,35,10,*p\c_textshadow) ; GetSysColor_(#COLOR_3DDKSHADOW))
    DrawingFont(*p\displayfont)
    DrawingMode(#PB_2DDrawing_Transparent)
    txt$ = Str(*p\selection_start)
    w = TextWidth(txt$)/2
    DrawText(*p\tickmin-w,*p\pointer1top-11,txt$,*p\c_textcolor) ; GetSysColor_(#COLOR_INFOTEXT))
    
    DrawingMode(#PB_2DDrawing_Default)
    Box(*p\tickmin-17,*p\pointer2top+16,35,10,*p\c_textbckgrnd) ; GetSysColor_(#COLOR_INFOBK))
    DrawingMode(#PB_2DDrawing_Outlined)
    Box(*p\tickmin-17,*p\pointer2top+16,35,10,*p\c_textshadow) ; GetSysColor_(#COLOR_3DDKSHADOW))
    DrawingFont(*p\displayfont)
    DrawingMode(#PB_2DDrawing_Transparent)
    txt$ = Str(*p\selection_end)
    w = TextWidth(txt$)/2
    DrawText(*p\tickmin-w,*p\pointer2top+16,txt$,*p\c_textcolor) ; GetSysColor_(#COLOR_INFOTEXT))    
    
  StopDrawing() 
EndProcedure

ProcedureDLL TrackSelectGadget(gadgetnumber, x, y, width, min=0, max=100, drawfocus=0)
  tmp = CreatePointers()
  pointer1 = GrabImage(tmp, #PB_Any,0,0,11,15)
  pointer2 = GrabImage(tmp, #PB_Any,0,15,11,15)
  FreeImage(tmp)
  
  If drawfocus
    flags = #PB_Canvas_Keyboard|#PB_Canvas_GrabMouse|#PB_Canvas_DrawFocus
  Else
    flags = #PB_Canvas_Keyboard|#PB_Canvas_GrabMouse
  EndIf
  
  Protected height = 60
  If gadgetnumber = #PB_Any
    result = CanvasGadget(#PB_Any, x, y, width, height, flags)
    gadgetnumber = result
  Else
    result = CanvasGadget(gadgetnumber, x, y, width, height, flags)
  EndIf
  
  h1 = CreateBitmapRegion(pointer1)
  h2 = CreateBitmapRegion(pointer2)
  
  *gadgetdata.TrackData = AllocateMemory(SizeOf(TrackData))
  With *gadgetdata
    \p_region1       = h1
    \p_region2       = h2 
    \baseimage       = baseimage
    \baseimagetop    = 23
    \baseimageleft   = 20
    \baseimagewidth  = width-36
    \bclientwidth    = \baseimagewidth-5
    \pointer1        = pointer1
    \pointer2        = pointer2
    \selection_start = 0
    \selection_end   = 0
    \selection_max   = max
    \tickmin         = 22
    \tickmax         = width-19
    \pointer1top     = 15
    \pointer2top     = 30
    \tickleftx       = 22
    \tickrightx      = 22
    \pointer1x       = 17
    \pointer2x       = 17
    \pointermin      = 17
    \pointermax      = width-24
    If LoadFont(0,"verdana", 6)
        \displayfont = FontID(0)
    ElseIf LoadFont(0,"Arial",9) ; for Mac platform
        \displayfont=FontID(0)
    Else
        MessageRequester("Loading font..","Font not found")
    EndIf
    *gadgetdata\c_transparent = RGBA(0,0,0,0) ; transparent
    *gadgetdata\c_textshadow = RGBA(0,0,0,255) ; shadow color of text box - GetSysColor_(#COLOR_3DDKSHADOW) |$FFFFFFFFFF000000
    *gadgetdata\c_boxshadow = RGBA(0,0,0,255) ; right shadow color of bar box & pointers - GetSysColor_(#COLOR_3DSHADOW)   |$FFFFFFFFFF000000
    *gadgetdata\c_background = RGBA(203,198,186,255) ; gadget background -  GetSysColor_(#COLOR_3DFACE)     |$FFFFFFFFFF000000
    *gadgetdata\c_barbckgrnd = RGBA(255,255,255,255) ; bar background - GetSysColor_(#COLOR_3DHIGHLIGHT)|$FFFFFFFFFF000000
    *gadgetdata\c_textbckgrnd = RGBA(255,255,255,255) ; text background - GetSysColor_(#COLOR_INFOBK))
    *gadgetdata\c_menuhighlight = RGBA(6,21,93,255) ; unkn - GetSysColor_(#COLOR_MENUHILIGHT))
    *gadgetdata\c_textcolor = RGBA(0,0,0,255) ; text color -  = GetSysColor_(#COLOR_INFOTEXT))
        
  EndWith
  
  SetGadgetData(gadgetnumber, *gadgetdata)
  *gadgetdata\baseimage = CreateImage(#PB_Any, *gadgetdata\baseimagewidth,15)
  
  StartDrawing(ImageOutput(*gadgetdata\baseimage))
    Box(0,0,*gadgetdata\baseimagewidth,15,*gadgetdata\c_background) ; GetSysColor_(#COLOR_3DFACE))
    Box(0,0,*gadgetdata\baseimagewidth-1,1,*gadgetdata\c_boxshadow) ; GetSysColor_(#COLOR_3DSHADOW))
    Box(0,0,1,14,*gadgetdata\c_boxshadow) ; GetSysColor_(#COLOR_3DSHADOW))
    Box(1,1,1,12,*gadgetdata\c_textshadow) ; GetSysColor_(#COLOR_3DDKSHADOW))
    Box(1,1,*gadgetdata\baseimagewidth-3,1,*gadgetdata\c_textshadow) ; GetSysColor_(#COLOR_3DDKSHADOW))
    Box(2,2,*gadgetdata\baseimagewidth-5,11,*gadgetdata\c_barbckgrnd) ; GetSysColor_(#COLOR_3DHIGHLIGHT))
    Box(1,13,*gadgetdata\baseimagewidth-4,1,*gadgetdata\c_background) ; GetSysColor_(#COLOR_3DFACE))
    Box(0,14,*gadgetdata\baseimagewidth-2,1,*gadgetdata\c_barbckgrnd) ; GetSysColor_(#COLOR_3DHIGHLIGHT))
    Box(*gadgetdata\baseimagewidth-3,1,1,13,*gadgetdata\c_background) ; GetSysColor_(#COLOR_3DFACE))
    Box(*gadgetdata\baseimagewidth-2,0,1,15,*gadgetdata\c_barbckgrnd) ;GetSysColor_(#COLOR_3DHIGHLIGHT))
  StopDrawing()
  
  RedrawTrackSelectionGadget(gadgetnumber)
  ProcedureReturn result
EndProcedure

ProcedureDLL GetTrackSelectionStart(gadget)
  *p.TrackData = GetGadgetData(gadget)
  ProcedureReturn *p\selection_start
EndProcedure

ProcedureDLL GetTrackSelectionEnd(gadget)
  *p.TrackData = GetGadgetData(gadget)
  ProcedureReturn *p\selection_end
EndProcedure

ProcedureDLL CheckSelectionStartChanged(gadget)
  *p.TrackData = GetGadgetData(gadget)
  If *p\startchanged
    *p\startchanged=0
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

ProcedureDLL CheckSelectionEndChanged(gadget)
  *p.TrackData = GetGadgetData(gadget)
  If *p\endchanged
    *p\endchanged=0
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

ProcedureDLL HandleTrackEvents(EventGadget,EventType)
  Static hot1, hot2, xoffset
  *p.TrackData = GetGadgetData(EventGadget)
  mx = GetGadgetAttribute(EventGadget,#PB_Canvas_MouseX)
  my = GetGadgetAttribute(EventGadget,#PB_Canvas_MouseY)
  
  Select EventType
      ;       
    Case #PB_EventType_KeyDown
      key = GetGadgetAttribute(EventGadget, #PB_Canvas_Key)
      mods = GetGadgetAttribute(EventGadget, #PB_Canvas_Modifiers)
      Select key
        Case #PB_Shortcut_Right
          If mods & #PB_Canvas_Shift
            *p\pointer1x = *p\tickleftx-5
            If *P\tickleftx < *p\tickmax
              *p\tickleftx+1
              *p\pointer1x+1
              If *p\tickrightx < *p\tickleftx
                *p\tickrightx = *p\tickleftx
                *p\pointer2x = *p\tickrightx-5
              EndIf
            EndIf
            s.d = Round(((*p\tickleftx-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
            *p\selection_start = Int(s)
            *P\startchanged = 1
            If *p\selection_start > *p\selection_end
              *p\selection_end = *p\selection_start
              *p\endchanged = 1
            EndIf                  
            RedrawTrackSelectionGadget(EventGadget) 
          Else
            *p\pointer2x = *p\tickrightx-5
            If *p\tickrightx < *p\tickmax
              *p\tickrightx+1
              *p\pointer2x+1
            EndIf
            e.d = Round(((*p\tickrightx-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
            *p\selection_end = Int(e)
            *p\endchanged = 1
            RedrawTrackSelectionGadget(EventGadget) 
          EndIf
          
        Case #PB_Shortcut_Left
          If mods & #PB_Canvas_Shift
            *p\pointer1x = *p\tickleftx-5
            If *p\tickleftx > *p\tickmin
              *p\tickleftx-1
              *p\pointer1x-1
            EndIf
            s.d = Round(((*p\tickleftx-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
            *p\selection_start = Int(s)
            *P\startchanged = 1
            RedrawTrackSelectionGadget(EventGadget) 
          Else
            *p\pointer2x = *p\tickrightx-5
            If *p\tickrightx > *p\tickmin
              *p\tickrightx-1
              *p\pointer2x-1
              If *p\tickleftx > *p\tickrightx
                *p\tickleftx = *p\tickrightx
                *p\pointer1x = *p\tickleftx-5
              EndIf
            EndIf
            e.d = Round(((*p\tickrightx-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
            *p\selection_end = Int(e)
            *p\endchanged = 1
            If *p\selection_end < *p\selection_start
              *p\selection_start = *p\selection_end
              *p\startchanged = 1
            EndIf                  
            RedrawTrackSelectionGadget(EventGadget) 
          EndIf
          
      EndSelect
      
    Case #PB_EventType_LeftButtonDown
      
      If mx <= *p\tickrightx And mx >= *p\tickmin And (my <= 26)
        *p\pointer1x = mx-5
        If *p\pointer1x < *p\pointermin
          *p\pointer1x = *p\pointermin
        EndIf
        *p\tickleftx = *p\pointer1x+5
        s.d = Round(((*p\tickleftx-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
        *p\selection_start = Int(s)
        *P\startchanged = 1
        hot1 = #True : #hot2 = #False
        RedrawTrackSelectionGadget(EventGadget) 
      ElseIf mx >= *p\tickleftx And mx <=*p\tickmax And (my >= 28)
        *p\pointer2x = mx-5
        If *p\pointer2x > *p\pointermax
          *p\pointer2x = *p\pointermax
        EndIf
        *p\tickrightx = *p\pointer2x+5
        e.d = Round(((*p\tickrightx-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
        *p\selection_end = Int(e)
        *p\endchanged = 1
        hot2 = #True : hot1 = #False
        RedrawTrackSelectionGadget(EventGadget) 
      EndIf
      
    Case #PB_EventType_MouseWheel
      delta = GetGadgetAttribute(EventGadget, #PB_Canvas_WheelDelta)
      If PtinRegion(*p\p_region2, mx-*p\pointer2x, my-*p\pointer2top)
        If delta>0
          thisone = *p\selection_end
          e.d = Round(((*p\tickrightx+1-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
          nextone = Int(e)
          testone = thisone+1
          *p\pointer2x = *p\tickrightx-5
          If testone<nextone 
            If *p\selection_end < nextone And *p\selection_end < *p\selection_max
              *p\selection_end+1
              *p\endchanged = 1
              RedrawTrackSelectionGadget(EventGadget)
            EndIf
          EndIf
        ElseIf delta < 0
          If *p\pointer2x = *p\tickrightx-5
            thisone = *p\selection_end
            e.d = Round(((*p\tickrightx-1-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
            nextone = Int(e)
            testone = thisone-1
            *p\pointer2x = *p\tickrightx-5
            If testone>nextone
              If *p\selection_end > nextone And *p\selection_end > *p\selection_start
                *p\selection_end-1
                *p\endchanged = 1
                RedrawTrackSelectionGadget(EventGadget)
              EndIf
            EndIf
          EndIf
        EndIf
      ElseIf PtinRegion(*p\p_region1, mx-*p\pointer1x, my-*p\pointer1top)
        If delta > 0
          thisone = *p\selection_start
          s.d = Round(((*p\tickleftx+1-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
          nextone = Int(s)
          testone = thisone+1
          *p\pointer1x = *p\tickleftx-5
          If testone>0
            If testone<nextone 
              If *p\selection_start < nextone And *p\selection_start < *p\selection_end
                *p\selection_start+1
                *P\startchanged = 1
                RedrawTrackSelectionGadget(EventGadget)
              EndIf
            EndIf
          EndIf
        ElseIf delta < 0
          thisone = *p\selection_start
          s.d = Round(((*p\tickleftx-1-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
          nextone = Int(s)
          testone = thisone-1
          *p\pointer1x = *p\tickleftx-5
          If testone>=0
            If testone>nextone
              If *p\selection_start > nextone And *p\selection_start > 0
                *p\selection_start-1
                *P\startchanged = 1
                RedrawTrackSelectionGadget(EventGadget)
              EndIf
            EndIf
          EndIf
        EndIf          
      EndIf
      
    Case #PB_EventType_LeftButtonUp
      hot1 = #False
      hot2 = #False
      
    Case #PB_EventType_MouseMove
      buttonstate = GetGadgetAttribute(EventGadget, #PB_Canvas_Buttons)
      lbutton = buttonstate & #PB_Canvas_LeftButton
      If lbutton 
        If hot1
          *p\pointer1x = mx-xoffset
          If *p\pointer1x < *p\pointermin
            *p\pointer1x = *p\pointermin
          ElseIf *p\pointer1x > *p\pointermax
            *p\pointer1x = *p\pointermax
          EndIf
          *p\tickleftx = *p\pointer1x+5
          If *p\tickrightx < *p\tickleftx
            *p\tickrightx = *p\tickleftx
            *p\pointer2x = *p\tickrightx-5
          EndIf
          s.d = Round(((*p\tickleftx-*p\tickmin)/*p\bclientwidth)  * *p\selection_max, #PB_Round_Nearest)
          *p\selection_start = Int(s)
          *P\startchanged = 1
          If *p\selection_start > *p\selection_end
            *p\selection_end = *p\selection_start
            *p\endchanged = 1
          EndIf
          RedrawTrackSelectionGadget(EventGadget) 
          
        ElseIf hot2
          *p\pointer2x = mx-xoffset
          If *p\pointer2x < *p\pointermin
            *p\pointer2x = *p\pointermin
          ElseIf *p\pointer2x > *p\pointermax
            *p\pointer2x = *p\pointermax
          EndIf          
          *p\tickrightx = *p\pointer2x+5
          If *p\tickleftx > *p\tickrightx
            *p\tickleftx = *p\tickrightx
            *p\pointer1x = *p\tickleftx-5
          EndIf
          e.d = Round(((*p\tickrightx-*p\tickmin)/*p\bclientwidth) * *p\selection_max, #PB_Round_Nearest)
          *p\selection_end   = Int(e)
          *p\endchanged = 1
          If *p\selection_end < *p\selection_start
            *p\selection_start = *p\selection_end
            *p\startchanged = 1
          EndIf
        EndIf
        RedrawTrackSelectionGadget(EventGadget) 
      Else
        If PtinRegion(*p\p_region1, mx-*p\pointer1x, my-*p\pointer1top)
          SetGadgetAttribute(EventGadget, #PB_Canvas_Cursor,#PB_Cursor_Hand)
          hot1 = #True : hot2 = #False
          xoffset = mx-*p\pointer1x
        ElseIf PtinRegion(*p\p_region2, mx-*p\pointer2x, my-*p\pointer2top)
          SetGadgetAttribute(EventGadget, #PB_Canvas_Cursor,#PB_Cursor_Hand)
          hot2 = #True : hot1 = #False
          xoffset = mx-*p\pointer2x
        Else
          hot1=#False : hot2 = #False
          SetGadgetAttribute(EventGadget, #PB_Canvas_Cursor,#PB_Cursor_Default)
        EndIf
      EndIf
      
  EndSelect
  
EndProcedure

;========================================================
;                    End of Library Code
;========================================================

; Test program

OpenWindow(0,0,0,640,480,"",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ButtonGadget(0,20,20,100,20,"Button")
TrackSelectGadget(1,20,360,600,0,20129,1)

Repeat
  ev = WaitWindowEvent()
  Select ev
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 1
          HandleTrackEvents(EventGadget(), EventType())
      EndSelect
  EndSelect
  
Until ev = #PB_Event_CloseWindow
MacBook Pro-M1 (2021), Sequoia 15.4, PB 6.20
Post Reply