Tree.lua 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. --[[
  2. MIT License
  3. Copyright (c) 2019 Mitchell Davis <coding.jackalope@gmail.com>
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in all
  11. copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  18. SOFTWARE.
  19. --]]
  20. local Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')
  21. local DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')
  22. local Image = require(SLAB_PATH .. '.Internal.UI.Image')
  23. local LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')
  24. local Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')
  25. local Region = require(SLAB_PATH .. '.Internal.UI.Region')
  26. local Stats = require(SLAB_PATH .. '.Internal.Core.Stats')
  27. local Style = require(SLAB_PATH .. '.Style')
  28. local Text = require(SLAB_PATH .. '.Internal.UI.Text')
  29. local Tooltip = require(SLAB_PATH .. '.Internal.UI.Tooltip')
  30. local Window = require(SLAB_PATH .. '.Internal.UI.Window')
  31. local Tree = {}
  32. local Instances = {}
  33. local Hierarchy = {}
  34. local Radius = 4.0
  35. local function GetInstance(Id)
  36. if #Hierarchy > 0 then
  37. local Top = Hierarchy[1]
  38. Id = Top.Id .. "." .. Id
  39. end
  40. if Instances[Id] == nil then
  41. local Instance = {}
  42. Instance.X = 0.0
  43. Instance.Y = 0.0
  44. Instance.W = 0.0
  45. Instance.H = 0.0
  46. Instance.IsOpen = false
  47. Instance.WasOpen = false
  48. Instance.Id = Id
  49. Instance.StatHandle = nil
  50. Instance.TreeR = 0
  51. Instance.TreeB = 0
  52. Instances[Id] = Instance
  53. end
  54. return Instances[Id]
  55. end
  56. function Tree.Begin(Id, Options)
  57. local StatHandle = Stats.Begin('Tree', 'Slab')
  58. Options = Options == nil and {} or Options
  59. Options.Label = Options.Label == nil and Id or Options.Label
  60. Options.Tooltip = Options.Tooltip == nil and "" or Options.Tooltip
  61. Options.OpenWithHighlight = Options.OpenWithHighlight == nil and true or OpenWithHighlight
  62. Options.Icon = Options.Icon == nil and nil or Options.Icon
  63. Options.IconPath = Options.IconPath == nil and nil or Options.IconPath
  64. Options.IsSelected = Options.IsSelected == nil and false or Options.IsSelected
  65. Options.IsOpen = Options.IsOpen == nil and false or Options.IsOpen
  66. local Instance = GetInstance(Id)
  67. Instance.WasOpen = Instance.IsOpen
  68. Instance.StatHandle = StatHandle
  69. local WinItemId = Window.GetItemId(Instance.Id)
  70. local MouseX, MouseY = Mouse.Position()
  71. local TMouseX, TMouseY = Region.InverseTransform(nil, MouseX, MouseY)
  72. local WinX, WinY = Window.GetPosition()
  73. local WinW, WinH = Window.GetBorderlessSize()
  74. local IsObstructed = Window.IsObstructedAtMouse() or Region.IsHoverScrollBar()
  75. local W = Text.GetWidth(Options.Label)
  76. local H = math.max(Style.Font:getHeight(), Instance.H)
  77. local Diameter = Radius * 2.0
  78. if not Options.IsLeaf then
  79. W = W + Diameter + Radius
  80. end
  81. local Icon = Options.Icon
  82. if Icon == nil then
  83. Icon = Options.IconPath
  84. end
  85. local ImageW, ImageH = Image.GetSize(Icon)
  86. W = W + ImageW
  87. H = math.max(H, ImageH)
  88. WinX = WinX + Window.GetBorder()
  89. WinY = WinY + Window.GetBorder()
  90. if #Hierarchy == 0 then
  91. local ControlW, ControlH = W, H
  92. if Instance.TreeR > 0 and Instance.TreeB > 0 then
  93. ControlW = Instance.TreeR - Instance.X
  94. ControlH = Instance.TreeB - Instance.Y
  95. end
  96. LayoutManager.AddControl(ControlW, ControlH)
  97. Instance.TreeR = 0
  98. Instance.TreeB = 0
  99. end
  100. local X, Y = Cursor.GetPosition()
  101. local TriX, TriY = X + Radius, Y + H * 0.5
  102. local IsHot = not IsObstructed and WinX <= TMouseX and TMouseX <= WinX + WinW and Y <= TMouseY and TMouseY <= Y + H and Region.Contains(MouseX, MouseY)
  103. if IsHot or Options.IsSelected then
  104. DrawCommands.Rectangle('fill', WinX, Y, WinW, H, Style.TextHoverBgColor)
  105. end
  106. if IsHot then
  107. if Mouse.IsClicked(1) and not Options.IsLeaf and Options.OpenWithHighlight then
  108. Instance.IsOpen = not Instance.IsOpen
  109. end
  110. end
  111. local IsExpanderClicked = false
  112. if not Options.IsLeaf then
  113. if not IsObstructed and X <= TMouseX and TMouseX <= X + Diameter and Y <= TMouseY and TMouseY <= Y + H then
  114. if Mouse.IsClicked(1) and not Options.OpenWithHighlight then
  115. Instance.IsOpen = not Instance.IsOpen
  116. Window.SetHotItem(nil)
  117. IsExpanderClicked = true
  118. end
  119. end
  120. local Dir = Instance.IsOpen and 180 or 90
  121. DrawCommands.Triangle('fill', TriX, TriY, Radius, Dir, Style.TextColor)
  122. end
  123. if not Instance.IsOpen and Instance.WasOpen then
  124. Window.ResetContentSize()
  125. Region.ResetContentSize()
  126. end
  127. Cursor.AdvanceX(Diameter)
  128. Instance.X = X
  129. Instance.Y = Y
  130. Instance.W = W
  131. Instance.H = H
  132. local CursorX, CursorY = Cursor.GetPosition()
  133. LayoutManager.Begin('Ignore', {Ignore = true})
  134. if Options.Icon ~= nil or Options.IconPath ~= nil then
  135. Image.Begin(Instance.Id .. '_Icon', {
  136. Image = Options.Icon,
  137. Path = Options.IconPath
  138. })
  139. local ItemX, ItemY, ItemW, ItemH = Cursor.GetItemBounds()
  140. Instance.H = math.max(Instance.H, ItemH)
  141. Cursor.SameLine({CenterY = true})
  142. end
  143. Text.Begin(Options.Label)
  144. LayoutManager.End()
  145. local Root = Instance
  146. if #Hierarchy > 0 then
  147. Root = Hierarchy[#Hierarchy]
  148. end
  149. local ItemX, ItemY, ItemW, ItemH = Cursor.GetItemBounds()
  150. Root.TreeR = math.max(Root.TreeR, ItemX + ItemW)
  151. Root.TreeB = math.max(Root.TreeB, Y + H)
  152. Cursor.SetY(Instance.Y)
  153. Cursor.AdvanceY(H)
  154. if Options.IsOpen then
  155. Instance.IsOpen = true
  156. end
  157. if Instance.IsOpen then
  158. table.insert(Hierarchy, 1, Instance)
  159. Cursor.SetX(CursorX)
  160. else
  161. Cursor.SetX(X)
  162. end
  163. if IsHot then
  164. Tooltip.Begin(Options.Tooltip)
  165. if not IsExpanderClicked then
  166. Window.SetHotItem(WinItemId)
  167. end
  168. end
  169. Window.AddItem(X, Y, (WinX + WinW) - Instance.X, H, WinItemId)
  170. if not Instance.IsOpen then
  171. Stats.End(Instance.StatHandle)
  172. end
  173. return Instance.IsOpen
  174. end
  175. function Tree.End()
  176. local StatHandle = Hierarchy[1].StatHandle
  177. table.remove(Hierarchy, 1)
  178. local Instance = Hierarchy[1]
  179. if Instance ~= nil then
  180. Cursor.SetX(Instance.X)
  181. else
  182. Cursor.SetX(Cursor.GetAnchorX())
  183. end
  184. Stats.End(StatHandle)
  185. end
  186. return Tree