Custom TreeGadget with column support, cross-platform

Share your advanced PureBasic knowledge/code with the community.
User avatar
Kukulkan
Addict
Addict
Posts: 1352
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Custom TreeGadget with column support, cross-platform

Post by Kukulkan »

Hello,

[UPDATE, 13 Jul 2018]
You can find the most recent module version here:
http://www.inspirant.de/download/treeViewEx.pbi

The initial code here in the top post is not updated any more and therefore was removed.
[/UPDATE]


I like to share my current version of my custom tree gadget. This are the features:
* unlimited number of root and child elements
* up to 50 columns
* support multi select (#tree_flags_multiSelect)
* support auto-size (#tree_flags_autoSize)
* supporting icon/image for every entry, column entry and column header
* build-in sort function ct_Sort()
* cross-platform
* built in optional search function

I know that this is not finished and by far not a complete replacement of a regular tree gadget. This are the currently open bugs and problems:
* Multi-Select does not support SHIFT key (range selection)
* Scrollbars somehow do not work as normal (don't know the reason for now)
* Missing functions to set/get the top position
* If the first column is resized to be very small, the tree-lines may be overlaying the second column
* Some more customization options
* On MacOS, the cursor pointer does not change during resize yet
* Not aware of different DPI settings

Would be nice if people who extend, enhance or fix this will share their extensions here :D
Last edited by Kukulkan on Fri Jul 13, 2018 10:42 am, edited 4 times in total.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Custom TreeGadget with column support, cross-platform

Post by wilbert »

Nice work :)
Kukulkan wrote:* On MacOS, the cursor pointer does not change during resize yet
To set a cursor on OS X, you can use the NSCursor class.

Code: Select all

CocoaMessage(0, CocoaMessage(0, 0, "NSCursor pointingHandCursor"), "set")
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: Custom TreeGadget with column support, cross-platform

Post by ts-soft »

Image thanks for sharing!
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Custom TreeGadget with column support, cross-platform

Post by Kwai chang caine »

Really nice and usefull :D
Thanks 8)
ImageThe happiness is a road...
Not a destination
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Custom TreeGadget with column support, cross-platform

Post by IdeasVacuum »

Very nice work Kukulkan, the Test is very fast. I'm struggling to understand the code though - how would I create a tree for, say, my drive C?
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Kukulkan
Addict
Addict
Posts: 1352
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Custom TreeGadget with column support, cross-platform

Post by Kukulkan »

Fill a tree with filesystem info like this:

Code: Select all

; #######################################################################
; TEST
;{ #######################################################################

Procedure ScanDir(Path.s, *treeId.ct_main, *parent.ct_entry, IconId.i = 0)
  If Right(Path.s, 1) <> "\" And Right(Path.s, 1) <> "/"
    Path.s = Path.s + "/"
  EndIf
  
  Protected Dir.i = ExamineDirectory(#PB_Any, Path.s, "*.*")
  Protected *new.ct_entry
  Protected Text.s
  
  If Dir.i
    While NextDirectoryEntry(Dir.i)
      If DirectoryEntryType(Dir.i) = #PB_DirectoryEntry_File
        Text.s = DirectoryEntryName(Dir.i) + #TAB$ + Str(DirectoryEntrySize(Dir.i) / 1024)+" KB"
        ct_addItem(*treeId, *parent, Text.s, 0, Path.s + DirectoryEntryName(Dir.i))
      ElseIf DirectoryEntryName(Dir.i) = "." Or DirectoryEntryName(Dir.i) = ".."
        ; ignore
      Else
        *new = ct_addItem(*treeId, *parent, DirectoryEntryName(Dir.i), IconId.i, Path.s + DirectoryEntryName(Dir.i))
        *new\status = #tree_collapsed
        ScanDir(Path.s + DirectoryEntryName(Dir.i), *treeId, *new, IconId.i)
      EndIf
    Wend
    FinishDirectory(Dir.i)
  EndIf

EndProcedure

DataSection
  folderyellow_png:
    Data.q $0A1A0A0D474E5089,$524448490D000000,$3000000030000000,$F902570000000608,$4752730100000087
    Data.q $0000E91CCEAE0042,$FF0044474B620600,$93A7BDA0FF00FF00,$7359487009000000,$C3140000C3140000
    Data.q $000000424D701501,$04DB07454D497407,$E5DE7EDB000B0B16,$54414449B8070000,$575C8F4999EDDE68
    Data.q $3ABAA1BEE77FC715,$445890B1831019C4,$04A26C3624604688,$43E4B0592C483163,$C22241B22C160904
    Data.q $31002C166B023E7B,$98C0885020589158,$50F6EC83B638A090,$78B0F7BA6F0D7774,$AA2C11B9EA77BEF7
    Data.q $7ABD5552A4FC2465,$3C14F73FFCCF7FF7,$F57D52F78F5C9EB9,$FD7D7F0FA79FEEB1,$DBC680AFE738AFCB
    Data.q $FFD0501DAFE24401,$BECC5E4273210016,$4902A71FF7EAF227,$2F67E82F93FF7E42,$F01E0D0EFF5F077F
    Data.q $51D414E0668F3380,$BF7FBB88E0F961CE,$3C27FE46FD2EA7CE,$93EA55C5EA4B3610,$7756887C28B880BF
    Data.q $3F98CCD6501CA151,$2F57EB7D5B4419F3,$D01DF1273E2C93AA,$184C1E8DB40459F8,$B8DD579367C6500D
    Data.q $4AB3E2F1BD0AEB76,$1BFE7CAF8B172D59,$9B0D9B00A5BEBC6F,$885C424146F87BC5,$AAE86D01AC3924D9
    Data.q $C5908B8FE1FB3C6B,$17DC317824A369FB,$183358651033F749,$E421C541442C634A,$45B5411455165108
    Data.q $37873F3BD5F3315C,$AC9CD0DA640A5BE6,$4AAD47B99B89BE08,$25B543200BA2BD18,$66FD742103E08C84
    Data.q $0EA02A0A2126BD1A,$CAF48A032C219068,$0057B45497A22DD8,$CB9CA28444798A8D,$A1C84247BDCB5B83
    Data.q $090C2409F51AAB34,$0777479931E7BFDB,$D54D15EBA8EF04F5,$CDBD08077C1034D2,$BB4B26028D1485E8
    Data.q $0292A342C5338BF8,$C511154045F7A3A2,$7BEDE372DFB7B089,$A8371CD2A880E68F,$626E80B4610A137A
    Data.q $AAA841085704C458,$47158B4735272735,$7D43AA094E842747,$CBEF199C9788BA10,$B6EAD170404A1EA0
    Data.q $FD8CB2C94851A8BE,$42A94EAEEF9B92FD,$8BA50C7F9288FA3F,$AC667CB459B0930B,$E1AAF59EF3A1401F
    Data.q $072AFE5E2E13FDDE,$9D0A53E9F4097E2F,$876114EBF2674E4D,$21684803BC37FBBC,$F06D7ADF7395DF54
    Data.q $B8C49D5E3969BD40,$C3709460D0156E4B,$59E37AC77DE28A86,$1D53AFA5E0E23FDC,$C72D98227AD119FE
    Data.q $7EF1F21549DA4F89,$964B40218B614538,$BC007E776FF373EF,$BE5E0E6DFABF5F96,$12C7FCDC87C3C3C2
    Data.q $BA49BD8231710047,$66C95411C1A6D2ED,$4B5668D7E2FC5C7B,$05128C9332541B54,$AC1A5FBC59F85175
    Data.q $863A73EC9B36D8C2,$381CFB0EB7BB9E73,$DB95ECFE6F0AF978,$BA72B5DAA4BD2F17,$5DA1A5D464754F4B
    Data.q $8F45D446DEAFC03F,$091F3C5A3E39B231,$72A24DFC65102871,$36723863A703C4F5,$2EF7C3E3E24C6EEB
    Data.q $53AD5AD74B7FF774,$64C2F592FB8CE9D5,$572D63F14A4FAC93,$0A9388D2D246520F,$3EC30FC0C3843143
    Data.q $51DB2D59444319FF,$020C239D41B2F581,$4C6A564AF333C110,$2B1EAD62DC91874C,$60E133A8B0DAC007
    Data.q $99431AFFBAC19231,$20BAF7A0547E75D1,$6571A99B33B00C1A,$FCA2DD0DE26E810A,$1A68D0F5A0F0C3B3
    Data.q $DC6B42B58D71F01E,$8AC98B293157932E,$0C85E48C0B3C371A,$690494A8A2A028C1,$A802D005CBA30023
    Data.q $011AB022F607A80C,$A30CEE828C37934B,$9B192E4424A5570F,$6A9903D31A443A39,$F1376629D3A99D22
    Data.q $6AD63061187844D9,$24458C888B118876,$9D9F6D8F7DB41F1F,$72E26168C4CEC989,$BA312EF6EA6A3E4A
    Data.q $D0769D88842B6301,$6C95A2EA37526554,$08CAAF8F2B58C015,$99AA18367B313339,$3CA6D61170860F8D
    Data.q $3BA365826C589116,$84CDA5FA4F51CA87,$56C609B2308EFCEC,$0128433323CAC40E,$309BDF789DA74FD8
    Data.q $62CB607C332E65B0,$2275FBAC615D8EE2,$F04215501AA039DB,$6F34951C634F99DB,$E16393F3D25AAB0D
    Data.q $D68F436754386159,$3A6E488B481C407B,$253380E69655D4C3,$927E721373DE609B,$B067608D48C0BBA0
    Data.q $8EBF7EDA82A94652,$6CBCB498F253738B,$EF5C4CE0BB76CB11,$E2DB10868C1CC30A,$D2C5AD8B33B40C81
    Data.q $53E83ED955899563,$118577D184DD611A,$2751440784C87F0C,$3221958E4CD1D0E6,$6C20D6C342AA6367
    Data.q $6D558CC1186EDD88,$F56D4F222CF11817,$A67B2BDF238564C2,$28DAA2553E686713,$6656AA4FEE971855
    Data.q $C798C0BBDEB6C708,$3C665C1BCC52ACA3,$FBE1E9F8378FE370,$60D3BB4CA51D3F78,$F56D9FB5EE30A848
    Data.q $F77833C8E2863DD8,$C73FCEE78151BDFA,$B329DCEF873B9F0B,$32CD9A9E6194EED0,$AF46059D6E1D9823
    Data.q $3CB6B3BDDC665E50,$AF71A80F23EF04A0,$0A89CCA9564D589A,$57CD4D000118B0A3,$681CC042F04639D8
    Data.q $72756BA3AEF8F782,$6D6243484FB628F0,$D872A6B4386DDB8C,$1D1C257A30AECB03,$734FC9FF3E2619DA
    Data.q $948D37AD7413F970,$C2FEEA6B58D6A84D,$76EF9F6CD793B81C,$BFBC66516287D188,$787CB02BD5BC06B8
    Data.q $849B5EA6867AAE9B,$62ABF23F6C6186C9,$F1ED2669C213F025,$4B8488C2BB4E4266,$F47B707939A7F883
    Data.q $EAD27CE4538D3880,$06126A99E49478C6,$77DF3333E44C33B6,$5EFF86A93B1B118D,$D306825B415894F2
    Data.q $249CB03EDDE3A6BE,$3B57899D91693162,$CD040625DD164B4D,$B40D34038929ACA2,$78C561699D0EED8D
    Data.q $DD22E9328B293666,$D6F8A9FBC7A46219,$7F14BA1152736558,$2701EFB3DB112185,$D70D4F3663611193
    Data.q $10E28619D96FD4DA,$2E8CA6738E7A84F1,$9C17F9CD64EF168E,$55E08BDE2E24E00F,$CB5836D8DC4506AC
    Data.q $5853CEC66B88BDF8,$D8E94890FB532145,$0B53F78A3814BE9C,$8C24328CB58A9B9F,$FAC26C16E90F651A
    Data.q $B801730FC5FEBF0F,$96DA9739966F9174,$63F68845FF8B8219,$FEF78657A4FDA730,$BFA3FA8D3EF81BB3
    Data.q $FDD9869FF87173D3,$40545145DBFC6704,$19FE9393BE915055,$BCBB12A0E77D2080,$7F572F5BA9CCB6B4
    Data.q $46BD42A078FEA6F9,$906F1C29504FE0F6,$1EDEC1915F0BEB3C,$7F6566463DDC1994,$EB7B967ECFFFB872
    Data.q $63FDC35AA42B2C0E,$A1AAA405D211CEE4,$959D0C8A26BFFA6E,$907C2E0CFEE8E650,$47C2E0CB2007ECF0
    Data.q $8E7CC1DC829FE7D1,$5D0665A25FB22DD6,$9B9C8F18C78AAA80,$C45C801FCF0773A3,$FAE4FF51FC9E87C3
    Data.q $78C25AC505FEBFBF,$00000000F7457727,$826042AE444E4549
EndDataSection


If OpenWindow(0, 10, 10, 800, 480, "Custom TreeGadget", #PB_Window_SystemMenu)

  ; PREPARE SOME CUSTOMIZING
  Define Path.s = "c:\git\apps\"
  Define FolderGadget.i  = CatchImage(#PB_Any, ?folderyellow_png)
  
  Define Config.ct_customizing
  With Config
    \fontId = LoadFont(#PB_Any, "Arial", 10)
    \foregroundColor = RGB(0,0,0)
    \backgroundColor = RGB(255,255,255)
    \selForegroundColor = RGB(255,255,255)
    \selBackgroundColor = RGB(160,160,160)
    \lineColor = #PB_Ignore
    \headerBackroundColor = #PB_Ignore
    \headerForegroundColor = #PB_Ignore
  EndWith
  
  ;{ CREATE TREEGADGET #1
  Define *Tree1.ct_main = ct_TreeGadget(10, 10, 780, 460, #tree_flags_autoSize)
  
  ct_TreeGadgetCustomize(*Tree1, Config)
  
  ScanDir(Path.s, *Tree1, 0, FolderGadget.i)
  
  ;}

  *Tree1\header(1)\text = "Size"
  
  ct_redraw(*Tree1)
  
  Define Event.i, Quit.i
  
  Repeat
    Event = WaitWindowEvent()

    If Event = #PB_Event_CloseWindow  ; If the user has pressed on the close button
      Quit = 1
    EndIf

  Until Quit = 1

EndIf
Copy this code to replace the test code in the first post.

Set path in this line:
Define Path.s = "c:\git\apps\"

The respective ItemDataStr field contains the full path
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Custom TreeGadget with column support, cross-platform

Post by davido »

Very good.
Thank you for sharing. :D
DE AA EB
GoodNPlenty
Enthusiast
Enthusiast
Posts: 107
Joined: Wed May 13, 2009 8:38 am
Location: Arizona, USA

Re: Custom TreeGadget with column support, cross-platform

Post by GoodNPlenty »

Awesome code and very fast.
Thank You :D
said
Enthusiast
Enthusiast
Posts: 342
Joined: Thu Apr 14, 2011 6:07 pm

Re: Custom TreeGadget with column support, cross-platform

Post by said »

Nice 8) thanks for sharing :D
User avatar
Tenaja
Addict
Addict
Posts: 1948
Joined: Tue Nov 09, 2010 10:15 pm

Re: Custom TreeGadget with column support, cross-platform

Post by Tenaja »

+1
User avatar
Kukulkan
Addict
Addict
Posts: 1352
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Custom TreeGadget with column support, cross-platform

Post by Kukulkan »

Updated version! This is now re-designed to run as a module. This brings much more comfort in usage...

I keep the initial post and code, as someone might be more interested in the original version.

[UPDATE, 15 Sept 2015] Added some extra margin left from the line and left from the Icon to look better [/UPDATE]
[UPDATE, 18 Dec 2015] Added clear function to cleanup a tree (eg for next usage)[/UPDATE]
[UPDATE, 18 Dec 2015] Added sortColumn() function, added mouseOnHeader() function, added sort by header click to example code[/UPDATE]
[UPDATE, 02 Feb 2018] On MacOS High Sierra, Apple changed the behavior of NSDeviceResolution. Due to the fact that PB is still not DPI aware, on Retina Displays it now does no longer work. I removed DPI awareness for MacOS because of this (check _cust_tree_DetermineSystemDPIFactor() function).
[UPDATE, 13 Jul 2018] Added optional search functionality (see example on bottom of the source). Exchanged the icons.

I had to remove the code from here because the maximum size of 60K was exceed. Please download the PBI include here:
http://www.inspirant.de/download/treeViewEx.pbi

If you enhance this, I'd be happy to see it here :-) Maybe someone is happy to enhance the MacOS usage and the column header size cursor?
Last edited by Kukulkan on Fri Jul 13, 2018 10:37 am, edited 7 times in total.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: Custom TreeGadget with column support, cross-platform

Post by Keya »

Kukulkan that is excellent! And really clean code that gives high performance too, it worked really well on my Win+Mac+Linux VMs
DPI-aware too! you should write a book on creating custom controls, id buy it :D
User avatar
Kukulkan
Addict
Addict
Posts: 1352
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Custom TreeGadget with column support, cross-platform

Post by Kukulkan »

Thank you Keya, highly appreciated :D But I do not plan to write a book :wink:
User avatar
Kukulkan
Addict
Addict
Posts: 1352
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Custom TreeGadget with column support, cross-platform

Post by Kukulkan »

I updated the source three threads above. The module version now also having a clear() function to cleanup the tree if needed.

BTW, the initial code (no module) is no longer continued by me. I will add a hint in the initial entry.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: Custom TreeGadget with column support, cross-platform

Post by Keya »

{edit} disregard, finally figured out how to read a column! i prefer SelectElement over ForEach as theres only a couple columns

Code: Select all

If SelectElement(*Selected\columns(), 0)
  Debug "Column 0 text of the selected item = " + *Selected\columns()\text
EndIf
also I added this to see the full "Parent1\Child1\Child2" path of the selected item

Code: Select all

Protected sFullText.s = *Selected\text
While *Selected\parent <> 0
  *Selected = *Selected\parent
  sFullText = *Selected\text + "\" + sFullText
Wend
Debug "Full text = " + sFullText
ps. you mention in the Features "up to 50 columns", but it seems easy to make that configurable to supporting infinite columns by changing "Array header.ct_header(50)" to "#MaxColumns = 50: Array header.ct_header(#MaxColumns)"? I dont know if it's wasting memory if set to 50 when only 3 are being used, but it was an easy change heehee :)

This really is a fantastic control!! :) it's also really the only control suitable for the information i want to display, I think if i didnt have this option i would have to use a non-combined tree + list sort of like Explorer in Details mode, which would be messy and nowhere near as effective for my type of data, so thankyou very much again for sharing your excellent work, but im sad to read you've discontinued it! although did you mean you're still continuing, but only the module version? :)
Ive never made custom controls before and this seems like it makes a great template to learn from too... and cross-platform! stunning :)
Last edited by Keya on Fri Dec 18, 2015 12:51 pm, edited 1 time in total.
Post Reply