|
|
@@ -1,5 +1,7 @@
|
|
|
package layout
|
|
|
|
|
|
+import "slices"
|
|
|
+
|
|
|
type LayoutManager struct {
|
|
|
Workspaces map[int]Workspace
|
|
|
ActiveIdx int
|
|
|
@@ -11,22 +13,23 @@ type Workspace struct { // TODO: this is not necessary..
|
|
|
}
|
|
|
|
|
|
func (w Workspace) IsEmpty() bool {
|
|
|
- return !isPopulated(w.RootContainer)
|
|
|
+ return !isPopulated(w.RootContainer)
|
|
|
}
|
|
|
|
|
|
type SplitType int
|
|
|
+
|
|
|
const (
|
|
|
SplitHorizontal SplitType = iota
|
|
|
SplitVertical
|
|
|
)
|
|
|
|
|
|
-func (s *SplitType) SetOpposite() SplitType{
|
|
|
+func (s *SplitType) SetOpposite() SplitType {
|
|
|
if *s == SplitHorizontal {
|
|
|
*s = SplitVertical
|
|
|
- return SplitHorizontal
|
|
|
+ return SplitHorizontal
|
|
|
} else {
|
|
|
*s = SplitHorizontal
|
|
|
- return SplitVertical
|
|
|
+ return SplitVertical
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -51,7 +54,7 @@ func (Container) isContainerNode() {}
|
|
|
|
|
|
func NewLayoutManager() LayoutManager {
|
|
|
return LayoutManager{
|
|
|
- Workspaces: map[int]Workspace{0: NewWorkspace()},
|
|
|
+ Workspaces: map[int]Workspace{0: NewWorkspace()},
|
|
|
CurrentSplit: SplitHorizontal,
|
|
|
}
|
|
|
}
|
|
|
@@ -63,14 +66,14 @@ func NewWorkspace() Workspace {
|
|
|
}
|
|
|
|
|
|
func NewContainerEmpty() Container {
|
|
|
- return Container{
|
|
|
- Children: [2]ContainerNode{
|
|
|
- nil,
|
|
|
- nil,
|
|
|
- },
|
|
|
- Split: SplitHorizontal,
|
|
|
- ActiveChildIdx: 0,
|
|
|
- }
|
|
|
+ return Container{
|
|
|
+ Children: [2]ContainerNode{
|
|
|
+ nil,
|
|
|
+ nil,
|
|
|
+ },
|
|
|
+ Split: SplitHorizontal,
|
|
|
+ ActiveChildIdx: 0,
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
func (c *Container) WalkAndMutateActive(fn func(cb *ContainerBuffers) ContainerNode) {
|
|
|
@@ -112,237 +115,233 @@ func (lm *LayoutManager) InsertBuffer(bufferIdx int, newNode bool) {
|
|
|
}
|
|
|
|
|
|
func (lm *LayoutManager) CycleActiveBuffer(horisontal float32, vertical float32) {
|
|
|
- if horisontal == 0 && vertical == 0 {
|
|
|
- return
|
|
|
- }
|
|
|
+ if horisontal == 0 && vertical == 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
- ws := lm.Workspaces[lm.ActiveIdx]
|
|
|
+ ws := lm.Workspaces[lm.ActiveIdx]
|
|
|
|
|
|
- if newRoot, moved := ws.RootContainer.NavigateContainer(horisontal, vertical); moved {
|
|
|
- ws.RootContainer = newRoot
|
|
|
- lm.Workspaces[lm.ActiveIdx] = ws
|
|
|
- }
|
|
|
+ if newRoot, moved := ws.RootContainer.NavigateContainer(horisontal, vertical); moved {
|
|
|
+ ws.RootContainer = newRoot
|
|
|
+ lm.Workspaces[lm.ActiveIdx] = ws
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
func (c Container) NavigateContainer(horisontal float32, vertical float32) (Container, bool) {
|
|
|
- activeChild := c.Children[c.ActiveChildIdx]
|
|
|
-
|
|
|
- if childContainer, ok := activeChild.(Container); ok {
|
|
|
- updatedChild, moved := childContainer.NavigateContainer(horisontal, vertical)
|
|
|
- if moved {
|
|
|
- c.Children[c.ActiveChildIdx] = updatedChild
|
|
|
- return c, true
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- targetIdx := -1
|
|
|
-
|
|
|
- if horisontal != 0 && c.Split == SplitHorizontal {
|
|
|
- if horisontal < 0 && c.ActiveChildIdx == 1 {
|
|
|
- targetIdx = 0 // Try moving Left
|
|
|
- } else if horisontal > 0 && c.ActiveChildIdx == 0 {
|
|
|
- targetIdx = 1 // Try moving Right
|
|
|
- }
|
|
|
- } else if vertical != 0 && c.Split == SplitVertical {
|
|
|
- if vertical < 0 && c.ActiveChildIdx == 1 {
|
|
|
- targetIdx = 0 // Try moving Up
|
|
|
- } else if vertical > 0 && c.ActiveChildIdx == 0 {
|
|
|
- targetIdx = 1 // Try moving Down
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if targetIdx != -1 && isPopulated(c.Children[targetIdx]) {
|
|
|
- c.ActiveChildIdx = targetIdx
|
|
|
- return c, true
|
|
|
- }
|
|
|
-
|
|
|
- return c, false
|
|
|
+ activeChild := c.Children[c.ActiveChildIdx]
|
|
|
+
|
|
|
+ if childContainer, ok := activeChild.(Container); ok {
|
|
|
+ updatedChild, moved := childContainer.NavigateContainer(horisontal, vertical)
|
|
|
+ if moved {
|
|
|
+ c.Children[c.ActiveChildIdx] = updatedChild
|
|
|
+ return c, true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ targetIdx := -1
|
|
|
+
|
|
|
+ if horisontal != 0 && c.Split == SplitHorizontal {
|
|
|
+ if horisontal < 0 && c.ActiveChildIdx == 1 {
|
|
|
+ targetIdx = 0 // Try moving Left
|
|
|
+ } else if horisontal > 0 && c.ActiveChildIdx == 0 {
|
|
|
+ targetIdx = 1 // Try moving Right
|
|
|
+ }
|
|
|
+ } else if vertical != 0 && c.Split == SplitVertical {
|
|
|
+ if vertical < 0 && c.ActiveChildIdx == 1 {
|
|
|
+ targetIdx = 0 // Try moving Up
|
|
|
+ } else if vertical > 0 && c.ActiveChildIdx == 0 {
|
|
|
+ targetIdx = 1 // Try moving Down
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if targetIdx != -1 && isPopulated(c.Children[targetIdx]) {
|
|
|
+ c.ActiveChildIdx = targetIdx
|
|
|
+ return c, true
|
|
|
+ }
|
|
|
+
|
|
|
+ return c, false
|
|
|
}
|
|
|
|
|
|
func isPopulated(node ContainerNode) bool {
|
|
|
- if node == nil {
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- switch n := node.(type) {
|
|
|
- case ContainerBuffers:
|
|
|
- return len(n.Buffers) > 0
|
|
|
- case Container:
|
|
|
- return isPopulated(n.Children[0]) || isPopulated(n.Children[1])
|
|
|
- default:
|
|
|
- return false
|
|
|
- }
|
|
|
+ if node == nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ switch n := node.(type) {
|
|
|
+ case ContainerBuffers:
|
|
|
+ return len(n.Buffers) > 0
|
|
|
+ case Container:
|
|
|
+ return isPopulated(n.Children[0]) || isPopulated(n.Children[1])
|
|
|
+ default:
|
|
|
+ return false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
func (c Container) GetActiveBufferIdx() int {
|
|
|
- switch child := c.Children[c.ActiveChildIdx].(type) {
|
|
|
- case ContainerBuffers:
|
|
|
- if len(child.Buffers) > 0 && child.ActiveIdx < len(child.Buffers) {
|
|
|
- return child.Buffers[child.ActiveIdx]
|
|
|
- }
|
|
|
- case Container:
|
|
|
- return child.GetActiveBufferIdx()
|
|
|
- }
|
|
|
- return -1
|
|
|
+ switch child := c.Children[c.ActiveChildIdx].(type) {
|
|
|
+ case ContainerBuffers:
|
|
|
+ if len(child.Buffers) > 0 && child.ActiveIdx < len(child.Buffers) {
|
|
|
+ return child.Buffers[child.ActiveIdx]
|
|
|
+ }
|
|
|
+ case Container:
|
|
|
+ return child.GetActiveBufferIdx()
|
|
|
+ }
|
|
|
+ return -1
|
|
|
}
|
|
|
|
|
|
func (lm LayoutManager) GetActiveBufferIdx() int {
|
|
|
- ws, ok := lm.Workspaces[lm.ActiveIdx]
|
|
|
- if !ok {
|
|
|
- return -1
|
|
|
- }
|
|
|
- return ws.RootContainer.GetActiveBufferIdx()
|
|
|
+ ws, ok := lm.Workspaces[lm.ActiveIdx]
|
|
|
+ if !ok {
|
|
|
+ return -1
|
|
|
+ }
|
|
|
+ return ws.RootContainer.GetActiveBufferIdx()
|
|
|
}
|
|
|
|
|
|
func (c Container) RemoveActive() (ContainerNode, bool) {
|
|
|
- activeChild := c.Children[c.ActiveChildIdx]
|
|
|
-
|
|
|
- switch child := activeChild.(type) {
|
|
|
- case ContainerBuffers:
|
|
|
- if len(child.Buffers) == 0 {
|
|
|
- return nil, true
|
|
|
- }
|
|
|
-
|
|
|
- child.Buffers = append(child.Buffers[:child.ActiveIdx], child.Buffers[child.ActiveIdx+1:]...)
|
|
|
-
|
|
|
- if child.ActiveIdx >= len(child.Buffers) && len(child.Buffers) > 0 {
|
|
|
- child.ActiveIdx = len(child.Buffers) - 1
|
|
|
- }
|
|
|
-
|
|
|
- if len(child.Buffers) == 0 {
|
|
|
- siblingIdx := 1 - c.ActiveChildIdx
|
|
|
- if siblingIdx < 0 || siblingIdx >= len(c.Children) || c.Children[siblingIdx] == nil {
|
|
|
- return nil, true
|
|
|
- }
|
|
|
- return c.Children[siblingIdx], false
|
|
|
- }
|
|
|
-
|
|
|
- c.Children[c.ActiveChildIdx] = child
|
|
|
- return c, false
|
|
|
-
|
|
|
- case Container:
|
|
|
- updatedChild, childIsEmpty := child.RemoveActive()
|
|
|
- if childIsEmpty {
|
|
|
- siblingIdx := 1 - c.ActiveChildIdx
|
|
|
- if siblingIdx < 0 || siblingIdx >= len(c.Children) || c.Children[siblingIdx] == nil {
|
|
|
- return nil, true
|
|
|
- }
|
|
|
- return c.Children[siblingIdx], false
|
|
|
- }
|
|
|
-
|
|
|
- c.Children[c.ActiveChildIdx] = updatedChild
|
|
|
- return c, false
|
|
|
-
|
|
|
- default:
|
|
|
- return nil, true
|
|
|
- }
|
|
|
+ activeChild := c.Children[c.ActiveChildIdx]
|
|
|
+
|
|
|
+ switch child := activeChild.(type) {
|
|
|
+ case ContainerBuffers:
|
|
|
+ if len(child.Buffers) == 0 {
|
|
|
+ return nil, true
|
|
|
+ }
|
|
|
+
|
|
|
+ child.Buffers = append(child.Buffers[:child.ActiveIdx], child.Buffers[child.ActiveIdx+1:]...)
|
|
|
+
|
|
|
+ if child.ActiveIdx >= len(child.Buffers) && len(child.Buffers) > 0 {
|
|
|
+ child.ActiveIdx = len(child.Buffers) - 1
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(child.Buffers) == 0 {
|
|
|
+ siblingIdx := 1 - c.ActiveChildIdx
|
|
|
+ if siblingIdx < 0 || siblingIdx >= len(c.Children) || c.Children[siblingIdx] == nil {
|
|
|
+ return nil, true
|
|
|
+ }
|
|
|
+ return c.Children[siblingIdx], false
|
|
|
+ }
|
|
|
+
|
|
|
+ c.Children[c.ActiveChildIdx] = child
|
|
|
+ return c, false
|
|
|
+
|
|
|
+ case Container:
|
|
|
+ updatedChild, childIsEmpty := child.RemoveActive()
|
|
|
+ if childIsEmpty {
|
|
|
+ siblingIdx := 1 - c.ActiveChildIdx
|
|
|
+ if siblingIdx < 0 || siblingIdx >= len(c.Children) || c.Children[siblingIdx] == nil {
|
|
|
+ return nil, true
|
|
|
+ }
|
|
|
+ return c.Children[siblingIdx], false
|
|
|
+ }
|
|
|
+
|
|
|
+ c.Children[c.ActiveChildIdx] = updatedChild
|
|
|
+ return c, false
|
|
|
+
|
|
|
+ default:
|
|
|
+ return nil, true
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
func (c Container) ContainsBufferIdx(target int) bool {
|
|
|
- switch child := c.Children[c.ActiveChildIdx].(type) {
|
|
|
- case ContainerBuffers:
|
|
|
- for _, idx := range child.Buffers {
|
|
|
- if idx == target {
|
|
|
- return true
|
|
|
- }
|
|
|
- }
|
|
|
- case Container:
|
|
|
- if child.ContainsBufferIdx(target) {
|
|
|
- return true
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for _, node := range c.Children {
|
|
|
- switch child := node.(type) {
|
|
|
- case ContainerBuffers:
|
|
|
- for _, idx := range child.Buffers {
|
|
|
- if idx == target {
|
|
|
- return true
|
|
|
- }
|
|
|
- }
|
|
|
- case Container:
|
|
|
- if child.ContainsBufferIdx(target) {
|
|
|
- return true
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return false
|
|
|
+ switch child := c.Children[c.ActiveChildIdx].(type) {
|
|
|
+ case ContainerBuffers:
|
|
|
+ if slices.Contains(child.Buffers, target) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ case Container:
|
|
|
+ if child.ContainsBufferIdx(target) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, node := range c.Children {
|
|
|
+ switch child := node.(type) {
|
|
|
+ case ContainerBuffers:
|
|
|
+ if slices.Contains(child.Buffers, target) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ case Container:
|
|
|
+ if child.ContainsBufferIdx(target) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false
|
|
|
}
|
|
|
|
|
|
func (c *Container) ActivateBufferIdx(target int) bool {
|
|
|
- for i, child := range c.Children {
|
|
|
- switch n := child.(type) {
|
|
|
- case ContainerBuffers:
|
|
|
- for j, idx := range n.Buffers {
|
|
|
- if idx == target {
|
|
|
- c.ActiveChildIdx = i
|
|
|
- n.ActiveIdx = j
|
|
|
- c.Children[i] = n
|
|
|
- return true
|
|
|
- }
|
|
|
- }
|
|
|
- case Container:
|
|
|
- if n.ActivateBufferIdx(target) {
|
|
|
- c.Children[i] = n
|
|
|
- return true
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return false
|
|
|
+ for i, child := range c.Children {
|
|
|
+ switch n := child.(type) {
|
|
|
+ case ContainerBuffers:
|
|
|
+ for j, idx := range n.Buffers {
|
|
|
+ if idx == target {
|
|
|
+ c.ActiveChildIdx = i
|
|
|
+ n.ActiveIdx = j
|
|
|
+ c.Children[i] = n
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case Container:
|
|
|
+ if n.ActivateBufferIdx(target) {
|
|
|
+ c.Children[i] = n
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
}
|
|
|
|
|
|
func (lm *LayoutManager) RemoveActiveBufferAndReindex(removedIdx int) int {
|
|
|
- ws := lm.Workspaces[lm.ActiveIdx]
|
|
|
-
|
|
|
- if !ws.RootContainer.ContainsBufferIdx(removedIdx) {
|
|
|
- return ws.RootContainer.GetActiveBufferIdx()
|
|
|
- }
|
|
|
-
|
|
|
- if !ws.RootContainer.ActivateBufferIdx(removedIdx) {
|
|
|
- return ws.RootContainer.GetActiveBufferIdx()
|
|
|
- }
|
|
|
-
|
|
|
- newRoot, isEmpty := ws.RootContainer.RemoveActive()
|
|
|
- if newRoot == nil || isEmpty {
|
|
|
- ws.RootContainer = NewContainerEmpty()
|
|
|
- lm.Workspaces[lm.ActiveIdx] = ws
|
|
|
- return -1
|
|
|
- }
|
|
|
-
|
|
|
- switch root := newRoot.(type) {
|
|
|
- case Container:
|
|
|
- ws.RootContainer = root
|
|
|
- case ContainerBuffers:
|
|
|
- ws.RootContainer = Container{
|
|
|
- Children: [2]ContainerNode{root, nil},
|
|
|
- Split: ws.RootContainer.Split,
|
|
|
- ActiveChildIdx: 0,
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ws.RootContainer.DecrementIndicesAbove(removedIdx)
|
|
|
- lm.Workspaces[lm.ActiveIdx] = ws
|
|
|
-
|
|
|
- return ws.RootContainer.GetActiveBufferIdx()
|
|
|
+ ws := lm.Workspaces[lm.ActiveIdx]
|
|
|
+
|
|
|
+ if !ws.RootContainer.ContainsBufferIdx(removedIdx) {
|
|
|
+ return ws.RootContainer.GetActiveBufferIdx()
|
|
|
+ }
|
|
|
+
|
|
|
+ if !ws.RootContainer.ActivateBufferIdx(removedIdx) {
|
|
|
+ return ws.RootContainer.GetActiveBufferIdx()
|
|
|
+ }
|
|
|
+
|
|
|
+ newRoot, isEmpty := ws.RootContainer.RemoveActive()
|
|
|
+ if newRoot == nil || isEmpty {
|
|
|
+ ws.RootContainer = NewContainerEmpty()
|
|
|
+ lm.Workspaces[lm.ActiveIdx] = ws
|
|
|
+ return -1
|
|
|
+ }
|
|
|
+
|
|
|
+ switch root := newRoot.(type) {
|
|
|
+ case Container:
|
|
|
+ ws.RootContainer = root
|
|
|
+ case ContainerBuffers:
|
|
|
+ ws.RootContainer = Container{
|
|
|
+ Children: [2]ContainerNode{root, nil},
|
|
|
+ Split: ws.RootContainer.Split,
|
|
|
+ ActiveChildIdx: 0,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ws.RootContainer.DecrementIndicesAbove(removedIdx)
|
|
|
+ lm.Workspaces[lm.ActiveIdx] = ws
|
|
|
+
|
|
|
+ return ws.RootContainer.GetActiveBufferIdx()
|
|
|
}
|
|
|
|
|
|
func (c Container) DecrementIndicesAbove(threshold int) {
|
|
|
- for i := range c.Children {
|
|
|
- if c.Children[i] == nil {
|
|
|
- continue
|
|
|
- }
|
|
|
- switch child := c.Children[i].(type) {
|
|
|
- case ContainerBuffers:
|
|
|
- for bIdx := range child.Buffers {
|
|
|
- if child.Buffers[bIdx] > threshold {
|
|
|
- child.Buffers[bIdx]--
|
|
|
- }
|
|
|
- }
|
|
|
- c.Children[i] = child
|
|
|
- case Container:
|
|
|
- child.DecrementIndicesAbove(threshold)
|
|
|
- c.Children[i] = child
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+ for i := range c.Children {
|
|
|
+ if c.Children[i] == nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ switch child := c.Children[i].(type) {
|
|
|
+ case ContainerBuffers:
|
|
|
+ for bIdx := range child.Buffers {
|
|
|
+ if child.Buffers[bIdx] > threshold {
|
|
|
+ child.Buffers[bIdx]--
|
|
|
+ }
|
|
|
+ }
|
|
|
+ c.Children[i] = child
|
|
|
+ case Container:
|
|
|
+ child.DecrementIndicesAbove(threshold)
|
|
|
+ c.Children[i] = child
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|