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:
;=============================================================
; 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