mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-30 14:47:16 +09:00
Start working on splits
This commit is contained in:
354
cmd/micro/views/splits.go
Normal file
354
cmd/micro/views/splits.go
Normal file
@@ -0,0 +1,354 @@
|
||||
package views
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SplitType uint8
|
||||
|
||||
const (
|
||||
STVert = 0
|
||||
STHoriz = 1
|
||||
STUndef = 2
|
||||
)
|
||||
|
||||
var idcounter uint64
|
||||
|
||||
func NewID() uint64 {
|
||||
idcounter++
|
||||
return idcounter
|
||||
}
|
||||
|
||||
type View struct {
|
||||
X, Y int
|
||||
W, H int
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
View
|
||||
|
||||
kind SplitType
|
||||
|
||||
parent *Node
|
||||
children []*Node
|
||||
|
||||
// Nodes can be marked as non resizable if they shouldn't be rescaled
|
||||
// when the terminal window is resized or when a new split is added
|
||||
// Only the splits on the edges of the screen can be marked as non resizable
|
||||
canResize bool
|
||||
// A node may also be marked with proportional scaling. This means that when
|
||||
// the window is resized the split maintains its proportions
|
||||
propScale bool
|
||||
|
||||
id uint64
|
||||
}
|
||||
|
||||
func (n *Node) ID() uint64 {
|
||||
if n.IsLeaf() {
|
||||
return n.id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func (n *Node) CanResize() bool {
|
||||
return n.canResize
|
||||
}
|
||||
func (n *Node) PropScale() bool {
|
||||
return n.propScale
|
||||
}
|
||||
func (n *Node) SetResize(b bool) {
|
||||
n.canResize = b
|
||||
}
|
||||
func (n *Node) SetPropScale(b bool) {
|
||||
n.propScale = b
|
||||
}
|
||||
func (n *Node) GetView() View {
|
||||
return n.View
|
||||
}
|
||||
func (n *Node) SetView(v View) {
|
||||
n.X, n.Y, n.W, n.H = v.X, v.Y, v.W, v.H
|
||||
}
|
||||
|
||||
func (n *Node) GetNode(id uint64) *Node {
|
||||
if n.id == id && n.IsLeaf() {
|
||||
return n
|
||||
}
|
||||
for _, c := range n.children {
|
||||
if c.id == id && c.IsLeaf() {
|
||||
return c
|
||||
}
|
||||
gc := c.GetNode(id)
|
||||
if gc != nil {
|
||||
return gc
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewNode(kind SplitType, x, y, w, h int, parent *Node, id uint64) *Node {
|
||||
n := new(Node)
|
||||
n.kind = kind
|
||||
n.canResize = true
|
||||
n.propScale = true
|
||||
n.X, n.Y, n.W, n.H = x, y, w, h
|
||||
n.children = make([]*Node, 0)
|
||||
n.parent = parent
|
||||
n.id = id
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func NewRoot(x, y, w, h int) *Node {
|
||||
n1 := NewNode(STUndef, x, y, w, h, nil, NewID())
|
||||
|
||||
return n1
|
||||
}
|
||||
|
||||
func (n *Node) IsLeaf() bool {
|
||||
return len(n.children) == 0
|
||||
}
|
||||
|
||||
func (n *Node) vResizeSplit(i int, size int) bool {
|
||||
if i < 0 || i >= len(n.children)-1 {
|
||||
return false
|
||||
}
|
||||
v1, v2 := n.children[i].GetView(), n.children[i+1].GetView()
|
||||
toth := v1.H + v2.H
|
||||
if size >= toth {
|
||||
return false
|
||||
}
|
||||
v1.H, v2.H = size, toth-size
|
||||
v2.Y = size
|
||||
n.children[i].SetView(v1)
|
||||
n.children[i+1].SetView(v2)
|
||||
return true
|
||||
}
|
||||
func (n *Node) hResizeSplit(i int, size int) bool {
|
||||
if i < 0 || i >= len(n.children)-1 {
|
||||
return false
|
||||
}
|
||||
v1, v2 := n.children[i].GetView(), n.children[i+1].GetView()
|
||||
totw := v1.W + v2.W
|
||||
if size >= totw {
|
||||
return false
|
||||
}
|
||||
v1.W, v2.W = size, totw-size
|
||||
v2.X = size
|
||||
n.children[i].SetView(v1)
|
||||
n.children[i+1].SetView(v2)
|
||||
return true
|
||||
}
|
||||
|
||||
func (n *Node) ResizeSplit(size int) bool {
|
||||
ind := 0
|
||||
for i, c := range n.parent.children {
|
||||
if c.id == n.id {
|
||||
ind = i
|
||||
}
|
||||
}
|
||||
if n.parent.kind == STVert {
|
||||
return n.parent.vResizeSplit(ind, size)
|
||||
}
|
||||
return n.parent.hResizeSplit(ind, size)
|
||||
}
|
||||
|
||||
func (n *Node) vVSplit(right bool) uint64 {
|
||||
ind := 0
|
||||
for i, c := range n.parent.children {
|
||||
if c.id == n.id {
|
||||
ind = i
|
||||
}
|
||||
}
|
||||
return n.parent.hVSplit(ind, right)
|
||||
}
|
||||
func (n *Node) hHSplit(bottom bool) uint64 {
|
||||
ind := 0
|
||||
for i, c := range n.parent.children {
|
||||
if c.id == n.id {
|
||||
ind = i
|
||||
}
|
||||
}
|
||||
return n.parent.vHSplit(ind, bottom)
|
||||
}
|
||||
func (n *Node) vHSplit(i int, right bool) uint64 {
|
||||
if n.IsLeaf() {
|
||||
newid := NewID()
|
||||
hn1 := NewNode(STHoriz, n.X, n.Y, n.W, n.H/2, n, n.id)
|
||||
hn2 := NewNode(STHoriz, n.X, n.Y+hn1.H, n.W, n.H/2, n, newid)
|
||||
if !right {
|
||||
hn1.id, hn2.id = hn2.id, hn1.id
|
||||
}
|
||||
|
||||
n.children = append(n.children, hn1, hn2)
|
||||
return newid
|
||||
} else {
|
||||
numr := 0
|
||||
numnr := 0
|
||||
nonrh := 0
|
||||
for _, c := range n.children {
|
||||
view := c.GetView()
|
||||
if !c.CanResize() {
|
||||
nonrh += view.H
|
||||
numnr++
|
||||
} else {
|
||||
numr++
|
||||
}
|
||||
}
|
||||
|
||||
// if there are no resizable splits make them all resizable
|
||||
if numr == 0 {
|
||||
numr = numnr
|
||||
}
|
||||
|
||||
height := (n.H - nonrh) / (numr + 1)
|
||||
|
||||
newid := NewID()
|
||||
hn := NewNode(STHoriz, n.X, 0, n.W, height, n, newid)
|
||||
n.children = append(n.children, nil)
|
||||
inspos := i
|
||||
if right {
|
||||
inspos++
|
||||
}
|
||||
copy(n.children[inspos+1:], n.children[inspos:])
|
||||
n.children[inspos] = hn
|
||||
|
||||
y := 0
|
||||
for _, c := range n.children {
|
||||
view := c.GetView()
|
||||
if c.CanResize() {
|
||||
view.H = height
|
||||
view.Y = y
|
||||
} else {
|
||||
view.Y = y
|
||||
}
|
||||
y += view.H
|
||||
c.SetView(view)
|
||||
}
|
||||
return newid
|
||||
}
|
||||
}
|
||||
func (n *Node) hVSplit(i int, right bool) uint64 {
|
||||
if n.IsLeaf() {
|
||||
newid := NewID()
|
||||
vn1 := NewNode(STVert, n.X, n.Y, n.W/2, n.H, n, n.id)
|
||||
vn2 := NewNode(STVert, n.X+vn1.W, n.Y, n.W/2, n.H, n, newid)
|
||||
if !right {
|
||||
vn1.id, vn2.id = vn2.id, vn1.id
|
||||
}
|
||||
|
||||
n.children = append(n.children, vn1, vn2)
|
||||
return newid
|
||||
} else {
|
||||
numr := 0
|
||||
numnr := 0
|
||||
nonrw := 0
|
||||
for _, c := range n.children {
|
||||
view := c.GetView()
|
||||
if !c.CanResize() {
|
||||
nonrw += view.W
|
||||
numnr++
|
||||
} else {
|
||||
numr++
|
||||
}
|
||||
}
|
||||
|
||||
// if there are no resizable splits make them all resizable
|
||||
if numr == 0 {
|
||||
numr = numnr
|
||||
}
|
||||
|
||||
width := (n.W - nonrw) / (numr + 1)
|
||||
|
||||
newid := NewID()
|
||||
vn := NewNode(STVert, 0, n.Y, width, n.H, n, newid)
|
||||
n.children = append(n.children, nil)
|
||||
inspos := i
|
||||
if right {
|
||||
inspos++
|
||||
}
|
||||
copy(n.children[inspos+1:], n.children[inspos:])
|
||||
n.children[inspos] = vn
|
||||
|
||||
x := 0
|
||||
for _, c := range n.children {
|
||||
view := c.GetView()
|
||||
if c.CanResize() {
|
||||
view.W = width
|
||||
view.X = x
|
||||
} else {
|
||||
view.X = x
|
||||
}
|
||||
x += view.W
|
||||
c.SetView(view)
|
||||
}
|
||||
return newid
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) HSplit(bottom bool) uint64 {
|
||||
if !n.IsLeaf() {
|
||||
return 0
|
||||
}
|
||||
if n.kind == STUndef {
|
||||
n.kind = STVert
|
||||
}
|
||||
if n.kind == STVert {
|
||||
return n.vHSplit(0, bottom)
|
||||
}
|
||||
return n.hHSplit(bottom)
|
||||
}
|
||||
|
||||
func (n *Node) VSplit(right bool) uint64 {
|
||||
if !n.IsLeaf() {
|
||||
return 0
|
||||
}
|
||||
if n.kind == STUndef {
|
||||
n.kind = STHoriz
|
||||
}
|
||||
if n.kind == STVert {
|
||||
return n.vVSplit(right)
|
||||
}
|
||||
return n.hVSplit(0, right)
|
||||
}
|
||||
|
||||
func (n *Node) Resize(w, h int) {
|
||||
propW, propH := float64(w)/float64(n.W), float64(h)/float64(n.H)
|
||||
x, y := n.X, n.Y
|
||||
for _, c := range n.children {
|
||||
cW := int(float64(c.W) * propW)
|
||||
cH := int(float64(c.H) * propH)
|
||||
c.Resize(cW, cH)
|
||||
c.X = x
|
||||
c.Y = y
|
||||
if n.kind == STHoriz {
|
||||
x += cW
|
||||
} else {
|
||||
y += cH
|
||||
}
|
||||
}
|
||||
n.W, n.H = w, h
|
||||
}
|
||||
|
||||
func (n *Node) Unsplit() {
|
||||
|
||||
}
|
||||
|
||||
func (n *Node) String() string {
|
||||
var strf func(n *Node, ident int) string
|
||||
strf = func(n *Node, ident int) string {
|
||||
marker := "|"
|
||||
if n.kind == STHoriz {
|
||||
marker = "-"
|
||||
}
|
||||
str := fmt.Sprint(strings.Repeat("\t", ident), marker, n.View, n.id)
|
||||
if n.IsLeaf() {
|
||||
str += "🍁"
|
||||
}
|
||||
str += "\n"
|
||||
for _, c := range n.children {
|
||||
str += strf(c, ident+1)
|
||||
}
|
||||
return str
|
||||
}
|
||||
return strf(n, 0)
|
||||
}
|
||||
16
cmd/micro/views/splits_test.go
Normal file
16
cmd/micro/views/splits_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package views
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHSplit(t *testing.T) {
|
||||
root := NewRoot(0, 0, 80, 80)
|
||||
n1 := root.VSplit(true)
|
||||
root.GetNode(n1).VSplit(true)
|
||||
root.GetNode(root.id).ResizeSplit(7)
|
||||
root.Resize(120, 120)
|
||||
|
||||
fmt.Println(root.String())
|
||||
}
|
||||
Reference in New Issue
Block a user