Mit paar Optimierungen auch bei 10.000+ Items noch recht schnell. Wobei es eher für kleinere Einsatzgebiete gedacht ist. (verwende es final in einer Grafiksoftware)
Code: Alles auswählen
; HyperTreeGadget.pb
;
; Version: 1.0
; Constants
#HyperTree_Selected = 0 ; Is Item selected? (#True/#False)
#HyperTree_Node = 1 ; Is Item a Node? (#True/False)
#HyperTree_Font = 2 ; Set Item Font. (FontNumber)
; Flags
#HyperTree_MultiSelect = 1<<0
#HyperTree_FullRowSelect = 1<<1
Structure HYPERTREE_FIELD_INFO
img.i
w.i
h.i
EndStructure
Structure HYPERTREE_ITEM_INFO
txt.s
pos.i
sel.i
lvl.i
exp.i
node.i
rect.RECT
h.i
cBkgnd.i
cTitle.i
cBoxSel.i
cBoxSelHi.i
cBoxBkgnd.i
cBoxBrd.i
gdata.i
font.i
List field.HYPERTREE_FIELD_INFO()
EndStructure
Structure HYPERTREE_INFO
id.l
flags.l
ml.i
pw.f
ph.f
sx.f
sy.f
sw.f
rsx.f
rsy.f
sh.f
bx.i
by.i
scroll.i
scrollx.i
indent.i
spacing.i
font.i
cBkgnd.i
cDkBrd.i
cBrd.i
List item.HYPERTREE_ITEM_INFO()
EndStructure
Procedure HyperTreeEvent(GadgetID.i)
Protected *g.HYPERTREE_INFO = GetGadgetData(GadgetID)
Protected w.i, h.i, mx.i, my.i
Protected ch.i=4, cw.i=4, ci.i= *g\indent, cl.i, exp.i=#True, sel.i=-1, xw.i
mx = GetGadgetAttribute(GadgetID, #PB_Canvas_MouseX)
my = GetGadgetAttribute(GadgetID, #PB_Canvas_MouseY)
Select EventType()
Case #PB_EventType_LeftButtonDown
*g\ml=#True
*g\bx=mx-*g\sx
*g\by=my-*g\sy
Case #PB_EventType_LeftButtonUp
*g\scroll=#False
*g\scrollx=#False
*g\ml=#False
EndSelect
StartDrawing(CanvasOutput(GadgetID))
w=OutputWidth()
h=OutputHeight()
Box(0, 0, w, h, *g\cBkgnd)
; Items
With *g\item()
ForEach *g\item()
If \lvl<=expl
exp=#True
ElseIf \lvl>expl And exp=#False
Continue
EndIf
If \lvl>cl And exp=#True
; Stop stop stoooop! We're a Child now so go back and tell your Parent!
PreviousElement(*g\item())
\node=#True
DrawingMode(#PB_2DDrawing_Default)
Box(-*g\rsx+\rect\left-12, -*g\sy+\rect\top+\h/2-5, 10, 10, \cBoxBkgnd)
DrawingMode(#PB_2DDrawing_Outlined)
Box(-*g\rsx+\rect\left-12, -*g\rsy+\rect\top+\h/2-5, 10, 10, \cBoxBrd)
If \exp=#False
LineXY(-*g\rsx+\rect\left-10, -*g\rsy+\rect\top+\h/2, -*g\rsx+\rect\left-5, -*g\rsy+\rect\top+\h/2, \cBoxBrd)
LineXY(-*g\rsx+\rect\left-8, -*g\rsy+\rect\top+\h/2-3, -*g\rsx+\rect\left-8, -*g\rsy+\rect\top+\h/2+2, \cBoxBrd)
Else
LineXY(-*g\rsx+\rect\left-10, -*g\rsy+\rect\top+\h/2, -*g\rsx+\rect\left-5, -*g\rsy+\rect\top+\h/2, \cBoxBrd)
EndIf
If exp=#True
exp=\exp
expl=\lvl
EndIf
NextElement(*g\item())
cl=\lvl
If exp=#False
Continue
EndIf
ElseIf \lvl<cl
exp=#True
EndIf
; Check Item
xw = 0
cl = \lvl
\rect\left=*g\indent*(\lvl+1)
\rect\top=ch
\rect\bottom=ch+\h
\rect\right=\rect\left+TextWidth(\txt)
If \rect\bottom-*g\rsy>0 And \rect\top-*g\rsy<h ;/
If EventType()=#PB_EventType_LeftClick And EventGadget()=GadgetID
If mx>\rect\left-12-*g\rsx And mx<\rect\left-*g\rsx And my>\rect\top-*g\rsy And my<\rect\bottom-*g\rsy And \node=#True
\exp=\exp!1
ElseIf mx>\rect\left-*g\rsx And mx<\rect\right-*g\rsx And my>\rect\top-*g\rsy And my<\rect\bottom-*g\rsy
\sel=#True
ElseIf Not *g\flags & #HyperTree_MultiSelect And GetAsyncKeyState_(#VK_CONTROL)=#False
\sel=#False
EndIf
EndIf
EndIf
If \rect\bottom-*g\rsy>0 And \rect\top-*g\rsy<h ;/
DrawingMode(#PB_2DDrawing_Default|#PB_2DDrawing_Transparent)
Box(-*g\rsx+\rect\left, -*g\rsy+ch, TextWidth(\txt), ch+\h, \cBoxBkgnd)
If \sel
RoundBox(-*g\rsx+\rect\left, -*g\rsy+\rect\top, w-\rect\left-4, \h, 3, 3, \cBoxSel)
EndIf
If mx>\rect\left-*g\rsx And mx<\rect\right-*g\rsx And my>\rect\top-*g\rsy And my<\rect\bottom-*g\rsy
RoundBox(-*g\rsx+\rect\left, -*g\rsy+\rect\top, w-\rect\left-4, \h, 3, 3, \cBoxSelHi)
EndIf
EndIf ;/
; Fields
ForEach *g\item()\field()
If \field()\img<>-1
DrawingMode(#PB_2DDrawing_AlphaBlend)
DrawImage(ImageID(\field()\img), -*g\rsx+xw+\rect\left, -*g\rsy+\rect\top, \field()\w, \field()\h)
EndIf
xw+\field()\w
Next
If \rect\bottom-*g\rsy>0 And \rect\top-*g\rsy<h ;/
DrawingMode(#PB_2DDrawing_Default|#PB_2DDrawing_Transparent)
DrawingFont(FontID(\font))
DrawText(-*g\rsx+xw+\rect\left+2, -*g\rsy+ch+2, \txt, \cTitle)
xw+TextWidth(\txt)
DrawingMode(#PB_2DDrawing_Outlined)
Box(1, 1, w-2, h-2, *g\cDkBrd)
Box(0, 0, w, h, *g\cBrd)
DrawingMode(#PB_2DDrawing_Default)
EndIf ;/
ch+\h+*g\spacing
If cw<\rect\left+xw
cw=\rect\left
EndIf
Next
; ScrollBar
*g\ph=ch
*g\pw=cw
If *g\ph>h
*g\sh=h-(h/100*((h/100)*(*g\ph))/100)
If *g\sh<10
*g\sh=10
EndIf
If ((*g\ml=#True And mx>w-22 And my>*g\sy And my<*g\sy+*g\sh) Or *g\scroll=#True) And my>*g\by And my<h-(*g\sh-*g\by)-4
If *g\scroll=#False
*g\scroll=#True
EndIf
*g\sy=(my-*g\by)
*g\rsy=((*g\sy)*((*g\ph)/(h-4)))+(((100/((h-4)-*g\sh))* (*g\sy)) * (*g\sh/100))
ElseIf my<*g\by And *g\scroll=#True
*g\sy=0
*g\rsy=*g\ph/100**g\sy
ElseIf my>h-(*g\sh-*g\by)-4 And *g\scroll=#True
*g\sy=h-(*g\sh)-4
*g\rsy=*g\ph-h
EndIf
Box(w-22, 2, 20, h-4, #Gray)
Box(w-21, 2+*g\sy, 19, *g\sh, RGB($F3,$F3,$F3))
EndIf
If *g\pw<w
*g\sw=w-24-((w-24)/100*(((w-24)/100)**g\pw)/100)-24
If ((*g\ml=#True And mx>2 And my>h-22 And mx<w-24) Or *g\scrollx=#True) And mx>*g\bx And mx<w-(*g\sw-*g\bx)
If *g\scrollx=#False
*g\scrollx=#True
EndIf
*g\sx=(mx-*g\bx)
*g\rsx=*g\pw/*g\sw**g\sx
ElseIf mx<*g\bx And *g\scrollx=#True
*g\sx=0
*g\rsx=*g\pw/100**g\sx
ElseIf mx>w-(*g\sw-*g\bx) And *g\scrollx=#True
*g\sx=w-(*g\sw)
*g\rsx=*g\pw-w
EndIf
Box(2, h-22, w-24, 20, #Gray)
Box(2+*g\sx, h-21, *g\sw-25, 19, RGB($F3,$F3,$F3))
EndIf
EndWith
StopDrawing()
EndProcedure
Procedure HyperTreeGadget(GadgetID.i, x.i, y.i, Width.i, Height.i, font.i, flags.l=#Null)
Protected *g.HYPERTREE_INFO=AllocateMemory(SizeOf(HYPERTREE_INFO))
If GadgetID=#PB_Any
result = CanvasGadget(#PB_Any, x, y, Width, Height)
Else
result = GadgetID
CanvasGadget(GadgetID, x, y, Width, Height)
EndIf
InitializeStructure(*g, HYPERTREE_INFO)
With *g
\id = result
\flags = flags
\cBkgnd = GetSysColor_(#COLOR_WINDOW)
\cBrd = GetSysColor_(#COLOR_3DLIGHT)
\cDkBrd = GetSysColor_(#COLOR_3DSHADOW)
\indent = 32
\spacing = 0
\font = font
EndWith
SetGadgetData(result, *g)
HyperTreeEvent(GadgetID)
ProcedureReturn result
EndProcedure
Procedure AddHyperTreeItem(GadgetID.i, index.i, text.s, level.i=0, redraw=#True, expanded.i=#True, h.i=20)
Protected *g.HYPERTREE_INFO = GetGadgetData(GadgetID)
AddElement(*g\item())
With *g\item()
\txt = text
\pos = index
\lvl = level
\exp = expanded
\h = h
\cBkgnd = GetSysColor_(#COLOR_WINDOW)
\cTitle = GetSysColor_(#COLOR_BTNTEXT)
\cBoxBkgnd = GetSysColor_(#COLOR_WINDOW)
\cBoxBrd = GetSysColor_(#COLOR_3DDKSHADOW)
\cBoxSel = RGB($DF,$E3,$F2)
\cBoxSelHi = RGB($EE,$F3,$F9)
\font = *g\font
EndWith
If redraw=#True
HyperTreeEvent(GadgetID)
EndIf
EndProcedure
Procedure RemoveHyperTreeItem(GadgetID.i, index.i)
Protected *g.HYPERTREE_INFO = GetGadgetData(GadgetID)
Protected expl.i, c.i
If index>-1 And index<=ListSize(*g\item())-1
With *g\item()
SelectElement(*g\item(), index)
expl=\lvl
DeleteElement(*g\item())
If ListSize(*g\item())>0
NextElement(*g\item())
While \lvl>expl And index<ListSize(*g\item())-1
DeleteElement(*g\item())
NextElement(*g\item())
c+1
Wend
EndWith
EndIf
HyperTreeEvent(GadgetID)
EndIf
EndProcedure
Procedure GetHyperTreeState(GadgetID.i)
Protected *g.HYPERTREE_INFO = GetGadgetData(GadgetID)
With *g\item()
ForEach *g\item()
If \sel=#True
ProcedureReturn ListIndex(*g\item())
EndIf
Next
ProcedureReturn -1
EndWith
EndProcedure
Procedure AddHyperTreeItemField(GadgetID.i, index.i, w.i, h.i, img.i=-1, redraw=#True)
Protected *g.HYPERTREE_INFO = GetGadgetData(GadgetID)
With *g\item()\field()
SelectElement(*g\item(), index)
AddElement(*g\item()\field())
\w = w
\h = h
\img = img
EndWith
If redraw=#True
HyperTreeEvent(GadgetID)
EndIf
EndProcedure
Procedure CountHyperTreeItems(GadgetID.i)
Protected *g.HYPERTREE_INFO = GetGadgetData(GadgetID)
ProcedureReturn ListSize(*g\item())
EndProcedure
Procedure GetHyperTreeItemAttribute(GadgetID.i, index.i, attribute.i)
Protected *g.HYPERTREE_INFO = GetGadgetData(GadgetID)
With *g\item()
SelectElement(*g\item(), index)
Select attribute
Case #HyperTree_Selected
ProcedureReturn \sel
Case #HyperTree_Node
ProcedureReturn \node
EndSelect
EndWith
EndProcedure
Procedure SetHyperTreeItemAttribute(GadgetID.i, index.i, attribute.i, value.i)
Protected *g.HYPERTREE_INFO = GetGadgetData(GadgetID)
With *g\item()
SelectElement(*g\item(), index)
Select attribute
Case #HyperTree_Font
\font = value
EndSelect
EndWith
EndProcedure
Procedure.s GetHyperTreeItemText(GadgetID.i, index.i)
Protected *g.HYPERTREE_INFO = GetGadgetData(GadgetID)
With *g\item()
SelectElement(*g\item(), index)
ProcedureReturn \txt
EndWith
EndProcedure
Procedure SetHyperTreeItemText(GadgetID.i, index.i, text.s)
Protected *g.HYPERTREE_INFO = GetGadgetData(GadgetID)
With *g\item()
SelectElement(*g\item(), index)
\txt = text
EndWith
HyperTreeEvent(GadgetID)
EndProcedure
;- Example
UsePNGImageDecoder()
font1 = LoadFont(#PB_Any, "arial", 8, #PB_Font_HighQuality)
font2 = LoadFont(#PB_Any, "comic sans ms", 12, #PB_Font_Bold)
image1 = CreateImage(#PB_Any, 20,20,32|#PB_Image_Transparent)
StartDrawing(ImageOutput(image1))
DrawingMode(#PB_2DDrawing_AlphaBlend|#PB_2DDrawing_AlphaClip)
Circle(8,8,6,RGBA(170,200,240,200))
StopDrawing()
image2 = CreateImage(#PB_Any, 20,20,32|#PB_Image_Transparent)
StartDrawing(ImageOutput(image2))
DrawingMode(#PB_2DDrawing_AlphaBlend|#PB_2DDrawing_AlphaClip)
Circle(8,8,6,RGBA(170,200,140,200))
StopDrawing()
OpenWindow(0, 0, 0, 300, 400, "HyperTreeGadget", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ButtonGadget(2, 5, 5, 70, 20, "Add")
ButtonGadget(1, 80, 5, 70, 20, "Remove")
HyperTreeGadget(0, 5, 30, 290, 360, font1)
AddHyperTreeItem(0, 0, "test0", 0, 1,1)
AddHyperTreeItem(0, 0, "test1", 1)
AddHyperTreeItem(0, 0, "test2", 1, 1,1)
AddHyperTreeItem(0, 0, "test2", 2, 1,1)
AddHyperTreeItem(0, 0, "test2", 3, 1,1)
AddHyperTreeItem(0, 0, "test2", 4, 1,1)
AddHyperTreeItem(0, 0, "test2", 5, 1,1)
AddHyperTreeItem(0, 0, "test2", 6, 1,1)
AddHyperTreeItem(0, 0, "test2", 7, 1,1)
AddHyperTreeItem(0, 0, "test2", 8, 1,1)
AddHyperTreeItem(0, 0, "test3", 2, 1,0, 54) ; A bit higher for the new Font.
AddHyperTreeItem(0, 0, "test4", 2)
AddHyperTreeItem(0, 0, "test5", 1)
AddHyperTreeItem(0, 0, "test6", 0)
Define c.i
For c=7 To 10000
AddHyperTreeItem(0, 0, "test"+Str(c), 1, #False) ; Important! Don't redraw until finished, then it's very fast.
Next
HyperTreeEvent(0)
; Set Font of Item3
SetHyperTreeItemAttribute(0, 3, #HyperTree_Font, font2)
; Add extra Fields to Item5
AddHyperTreeItemField(0, 5, 20, 20, image1)
AddHyperTreeItemField(0, 5, 20, 20, image2)
; Add extra Fields to Item2
AddHyperTreeItemField(0, 2, 20, 20, image1)
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
End
Case #PB_Event_Gadget
HyperTreeEvent(0)
Select EventGadget()
Case 1
RemoveHyperTreeItem(0, GetHyperTreeState(0))
Case 2
AddHyperTreeItem(0, 0, "test", 0)
EndSelect
EndSelect
ForEver