
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