1
0

layout.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. package layout
  2. import "slices"
  3. type LayoutManager struct {
  4. Workspaces map[int]Workspace
  5. ActiveIdx int
  6. CurrentSplit SplitType
  7. }
  8. type Workspace struct { // TODO: this is not necessary..
  9. RootContainer Container
  10. }
  11. func (w Workspace) IsEmpty() bool {
  12. return !isPopulated(w.RootContainer)
  13. }
  14. type SplitType int
  15. const (
  16. SplitHorizontal SplitType = iota
  17. SplitVertical
  18. )
  19. func (s *SplitType) SetOpposite() SplitType {
  20. if *s == SplitHorizontal {
  21. *s = SplitVertical
  22. return SplitHorizontal
  23. } else {
  24. *s = SplitHorizontal
  25. return SplitVertical
  26. }
  27. }
  28. type ContainerNode interface {
  29. isContainerNode()
  30. }
  31. type ContainerBuffers struct {
  32. Buffers []int
  33. ActiveIdx int
  34. }
  35. func (ContainerBuffers) isContainerNode() {}
  36. type Container struct {
  37. Children [2]ContainerNode
  38. Split SplitType
  39. ActiveChildIdx int
  40. }
  41. func (Container) isContainerNode() {}
  42. func NewLayoutManager() LayoutManager {
  43. return LayoutManager{
  44. Workspaces: map[int]Workspace{0: NewWorkspace()},
  45. CurrentSplit: SplitHorizontal,
  46. }
  47. }
  48. func NewWorkspace() Workspace {
  49. return Workspace{
  50. RootContainer: NewContainerEmpty(),
  51. }
  52. }
  53. func NewContainerEmpty() Container {
  54. return Container{
  55. Children: [2]ContainerNode{
  56. nil,
  57. nil,
  58. },
  59. Split: SplitHorizontal,
  60. ActiveChildIdx: 0,
  61. }
  62. }
  63. func (c *Container) WalkAndMutateActive(fn func(cb *ContainerBuffers) ContainerNode) {
  64. switch child := c.Children[c.ActiveChildIdx].(type) {
  65. case ContainerBuffers:
  66. c.Children[c.ActiveChildIdx] = fn(&child)
  67. case Container:
  68. child.WalkAndMutateActive(fn)
  69. c.Children[c.ActiveChildIdx] = child
  70. default:
  71. cb := ContainerBuffers{Buffers: []int{}, ActiveIdx: 0}
  72. c.Children[c.ActiveChildIdx] = fn(&cb)
  73. }
  74. }
  75. func (lm *LayoutManager) InsertBuffer(bufferIdx int, newNode bool) {
  76. workspace := lm.Workspaces[lm.ActiveIdx]
  77. workspace.RootContainer.WalkAndMutateActive(func(cb *ContainerBuffers) ContainerNode {
  78. if !newNode || len(cb.Buffers) == 0 {
  79. cb.Buffers = append(cb.Buffers, bufferIdx)
  80. cb.ActiveIdx = len(cb.Buffers) - 1
  81. return *cb
  82. }
  83. cbNew := ContainerBuffers{
  84. Buffers: []int{bufferIdx},
  85. ActiveIdx: 0,
  86. }
  87. return Container{
  88. Children: [2]ContainerNode{*cb, cbNew},
  89. Split: lm.CurrentSplit.SetOpposite(),
  90. ActiveChildIdx: 1,
  91. }
  92. })
  93. lm.Workspaces[lm.ActiveIdx] = workspace
  94. }
  95. func (lm *LayoutManager) CycleActiveBuffer(horisontal float32, vertical float32) {
  96. if horisontal == 0 && vertical == 0 {
  97. return
  98. }
  99. ws := lm.Workspaces[lm.ActiveIdx]
  100. if newRoot, moved := ws.RootContainer.NavigateContainer(horisontal, vertical); moved {
  101. ws.RootContainer = newRoot
  102. lm.Workspaces[lm.ActiveIdx] = ws
  103. }
  104. }
  105. func (c Container) NavigateContainer(horisontal float32, vertical float32) (Container, bool) {
  106. activeChild := c.Children[c.ActiveChildIdx]
  107. if childContainer, ok := activeChild.(Container); ok {
  108. updatedChild, moved := childContainer.NavigateContainer(horisontal, vertical)
  109. if moved {
  110. c.Children[c.ActiveChildIdx] = updatedChild
  111. return c, true
  112. }
  113. }
  114. targetIdx := -1
  115. if horisontal != 0 && c.Split == SplitHorizontal {
  116. if horisontal < 0 && c.ActiveChildIdx == 1 {
  117. targetIdx = 0 // Try moving Left
  118. } else if horisontal > 0 && c.ActiveChildIdx == 0 {
  119. targetIdx = 1 // Try moving Right
  120. }
  121. } else if vertical != 0 && c.Split == SplitVertical {
  122. if vertical < 0 && c.ActiveChildIdx == 1 {
  123. targetIdx = 0 // Try moving Up
  124. } else if vertical > 0 && c.ActiveChildIdx == 0 {
  125. targetIdx = 1 // Try moving Down
  126. }
  127. }
  128. if targetIdx != -1 && isPopulated(c.Children[targetIdx]) {
  129. c.ActiveChildIdx = targetIdx
  130. return c, true
  131. }
  132. return c, false
  133. }
  134. func isPopulated(node ContainerNode) bool {
  135. if node == nil {
  136. return false
  137. }
  138. switch n := node.(type) {
  139. case ContainerBuffers:
  140. return len(n.Buffers) > 0
  141. case Container:
  142. return isPopulated(n.Children[0]) || isPopulated(n.Children[1])
  143. default:
  144. return false
  145. }
  146. }
  147. func (c Container) GetActiveBufferIdx() int {
  148. switch child := c.Children[c.ActiveChildIdx].(type) {
  149. case ContainerBuffers:
  150. if len(child.Buffers) > 0 && child.ActiveIdx < len(child.Buffers) {
  151. return child.Buffers[child.ActiveIdx]
  152. }
  153. case Container:
  154. return child.GetActiveBufferIdx()
  155. }
  156. return -1
  157. }
  158. func (lm LayoutManager) GetActiveBufferIdx() int {
  159. ws, ok := lm.Workspaces[lm.ActiveIdx]
  160. if !ok {
  161. return -1
  162. }
  163. return ws.RootContainer.GetActiveBufferIdx()
  164. }
  165. func (c Container) RemoveActive() (ContainerNode, bool) {
  166. activeChild := c.Children[c.ActiveChildIdx]
  167. switch child := activeChild.(type) {
  168. case ContainerBuffers:
  169. if len(child.Buffers) == 0 {
  170. return nil, true
  171. }
  172. child.Buffers = append(child.Buffers[:child.ActiveIdx], child.Buffers[child.ActiveIdx+1:]...)
  173. if child.ActiveIdx >= len(child.Buffers) && len(child.Buffers) > 0 {
  174. child.ActiveIdx = len(child.Buffers) - 1
  175. }
  176. if len(child.Buffers) == 0 {
  177. siblingIdx := 1 - c.ActiveChildIdx
  178. if siblingIdx < 0 || siblingIdx >= len(c.Children) || c.Children[siblingIdx] == nil {
  179. return nil, true
  180. }
  181. return c.Children[siblingIdx], false
  182. }
  183. c.Children[c.ActiveChildIdx] = child
  184. return c, false
  185. case Container:
  186. updatedChild, childIsEmpty := child.RemoveActive()
  187. if childIsEmpty {
  188. siblingIdx := 1 - c.ActiveChildIdx
  189. if siblingIdx < 0 || siblingIdx >= len(c.Children) || c.Children[siblingIdx] == nil {
  190. return nil, true
  191. }
  192. return c.Children[siblingIdx], false
  193. }
  194. c.Children[c.ActiveChildIdx] = updatedChild
  195. return c, false
  196. default:
  197. return nil, true
  198. }
  199. }
  200. func (c Container) ContainsBufferIdx(target int) bool {
  201. switch child := c.Children[c.ActiveChildIdx].(type) {
  202. case ContainerBuffers:
  203. if slices.Contains(child.Buffers, target) {
  204. return true
  205. }
  206. case Container:
  207. if child.ContainsBufferIdx(target) {
  208. return true
  209. }
  210. }
  211. for _, node := range c.Children {
  212. switch child := node.(type) {
  213. case ContainerBuffers:
  214. if slices.Contains(child.Buffers, target) {
  215. return true
  216. }
  217. case Container:
  218. if child.ContainsBufferIdx(target) {
  219. return true
  220. }
  221. }
  222. }
  223. return false
  224. }
  225. func (c *Container) ActivateBufferIdx(target int) bool {
  226. for i, child := range c.Children {
  227. switch n := child.(type) {
  228. case ContainerBuffers:
  229. for j, idx := range n.Buffers {
  230. if idx == target {
  231. c.ActiveChildIdx = i
  232. n.ActiveIdx = j
  233. c.Children[i] = n
  234. return true
  235. }
  236. }
  237. case Container:
  238. if n.ActivateBufferIdx(target) {
  239. c.Children[i] = n
  240. return true
  241. }
  242. }
  243. }
  244. return false
  245. }
  246. func (lm *LayoutManager) RemoveActiveBufferAndReindex(removedIdx int) int {
  247. ws := lm.Workspaces[lm.ActiveIdx]
  248. if !ws.RootContainer.ContainsBufferIdx(removedIdx) {
  249. return ws.RootContainer.GetActiveBufferIdx()
  250. }
  251. if !ws.RootContainer.ActivateBufferIdx(removedIdx) {
  252. return ws.RootContainer.GetActiveBufferIdx()
  253. }
  254. newRoot, isEmpty := ws.RootContainer.RemoveActive()
  255. if newRoot == nil || isEmpty {
  256. ws.RootContainer = NewContainerEmpty()
  257. lm.Workspaces[lm.ActiveIdx] = ws
  258. return -1
  259. }
  260. switch root := newRoot.(type) {
  261. case Container:
  262. ws.RootContainer = root
  263. case ContainerBuffers:
  264. ws.RootContainer = Container{
  265. Children: [2]ContainerNode{root, nil},
  266. Split: ws.RootContainer.Split,
  267. ActiveChildIdx: 0,
  268. }
  269. }
  270. ws.RootContainer.DecrementIndicesAbove(removedIdx)
  271. lm.Workspaces[lm.ActiveIdx] = ws
  272. return ws.RootContainer.GetActiveBufferIdx()
  273. }
  274. func (c Container) DecrementIndicesAbove(threshold int) {
  275. for i := range c.Children {
  276. if c.Children[i] == nil {
  277. continue
  278. }
  279. switch child := c.Children[i].(type) {
  280. case ContainerBuffers:
  281. for bIdx := range child.Buffers {
  282. if child.Buffers[bIdx] > threshold {
  283. child.Buffers[bIdx]--
  284. }
  285. }
  286. c.Children[i] = child
  287. case Container:
  288. child.DecrementIndicesAbove(threshold)
  289. c.Children[i] = child
  290. }
  291. }
  292. }