TUI IDE multiplexer?!
golang tui ide
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 227 lines 5.4 kB view raw
1package main 2 3import ( 4 "fmt" 5 6 "github.com/gdamore/tcell/v2" 7 "github.com/rivo/tview" 8) 9 10type Gripper struct { 11 *tview.Box 12 isVertical bool 13 isHeld bool 14} 15 16func NewGripper(isVertical bool) *Gripper { 17 return &Gripper{ 18 Box: tview.NewBox(), 19 isVertical: isVertical, 20 } 21} 22 23func (g *Gripper) Draw(screen tcell.Screen) { 24 g.DrawForSubclass(screen, g) 25 x, y, w, h := g.GetInnerRect() 26 27 ch := '\u2502' 28 if g.isVertical { 29 ch = '\u2500' 30 } 31 if g.isHeld { 32 ch = '\u2503' 33 if g.isVertical { 34 ch = '\u2501' 35 } 36 } 37 38 color := tcell.StyleDefault.Dim(true) 39 40 for x1 := range w { 41 for y1 := range h { 42 screen.SetContent(x+x1, y+y1, ch, nil, color) 43 } 44 } 45} 46 47// Resizable is a rather hacky wrapper around Flex to allow resizable items controlled by grippers within. 48type Resizable struct { 49 *tview.Flex 50 items []ResizableItem 51 grippers []*Gripper 52 heldGripper *Gripper 53 heldGripperIndex int 54 lastX, lastY int 55 isVertical bool 56 subscription int 57 id string 58} 59 60type ResizableItem struct { 61 item tview.Primitive 62 size int // static size 63 proportion int 64 preferResize bool 65} 66 67func NewResizable(id string, isVertical bool) *Resizable { 68 r := &Resizable{ 69 id: id, 70 Flex: tview.NewFlex(), 71 isVertical: isVertical, 72 } 73 if r.isVertical { 74 r.Flex.SetDirection(tview.FlexRow) 75 } else { 76 r.Flex.SetDirection(tview.FlexColumn) 77 } 78 79 // Hook into global bus for convenient program-wide handling. 80 // eh... register as well. 81 bus.Register(UI_CANCEL) 82 r.subscription = bus.Subscribe(UI_CANCEL, func(v ...any) { 83 if r.heldGripper != nil { 84 r.heldGripper.isHeld = false 85 r.heldGripper = nil 86 } 87 }) 88 89 return r 90} 91 92func (r *Resizable) Cleanup() { 93 bus.Unsubscribe(UI_CANCEL, r.subscription) 94} 95 96func (r *Resizable) Restore() { 97 if ps, ok := econfig.PanelSizes[r.id]; ok { 98 for index, size := range ps { 99 if index < len(r.items) { 100 r.Flex.ResizeItem(r.items[index].item, size, r.items[index].proportion) 101 } 102 } 103 } 104} 105 106func (r *Resizable) AddItem(item tview.Primitive, size int, preferResize bool) { 107 ritem := ResizableItem{item: item, proportion: 1000, size: size, preferResize: preferResize} 108 r.items = append(r.items, ritem) 109 110 if len(r.items) > 1 { 111 gripper := NewGripper(r.isVertical) 112 r.Flex.AddItem(gripper, 1, 0, false) 113 r.grippers = append(r.grippers, gripper) 114 } 115 r.Flex.AddItem(ritem.item, ritem.size, ritem.proportion, true) 116 117 // FIXME: Inefficient... 118 r.Restore() 119} 120 121func (r *Resizable) Draw(screen tcell.Screen) { 122 r.Flex.Draw(screen) 123 if r.heldGripper != nil { 124 startIndex := r.heldGripperIndex 125 x, y, w, h := r.items[startIndex].item.GetRect() 126 127 _, _, sw, sh := r.GetRect() 128 var size int 129 if r.isVertical { 130 size = sh 131 } else { 132 size = sw 133 } 134 step := int(1000.0 / float64(size)) 135 136 screen.PutStr(x, y, fmt.Sprintf("%dx%d %d %d %d %d", w, h, r.items[startIndex].proportion, step, size, size*step)) 137 } 138} 139 140func (r *Resizable) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) { 141 return r.WrapMouseHandler(func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) { 142 x, y := event.Position() 143 if !r.InRect(x, y) { 144 return false, nil 145 } 146 147 if r.heldGripper != nil { 148 if action == tview.MouseMove { 149 startIndex := r.heldGripperIndex 150 var delta int 151 var rsize int 152 153 which := startIndex 154 if r.items[startIndex].preferResize { 155 which = startIndex + 1 156 } 157 158 _, _, rw, rh := r.items[which].item.GetRect() 159 if r.isVertical { 160 delta = y - r.lastY 161 rsize = rh 162 } else { 163 delta = x - r.lastX 164 rsize = rw 165 } 166 if which == startIndex { 167 delta *= -1 168 } 169 r.items[which].size = rsize - delta 170 r.Flex.ResizeItem(r.items[which].item, r.items[which].size, r.items[which].proportion) 171 172 if _, ok := econfig.PanelSizes[r.id]; !ok { 173 econfig.PanelSizes[r.id] = make(map[int]int) 174 } 175 econfig.PanelSizes[r.id][which] = r.items[which].size 176 econfig.Save() 177 178 r.lastX, r.lastY = x, y 179 } else if action == tview.MouseLeftUp || action == tview.MouseLeftClick || action == tview.MouseLeftDoubleClick { 180 r.heldGripper.isHeld = false 181 r.heldGripper = nil 182 } else { 183 return r.MouseHandleItems(action, event, setFocus) 184 } 185 return true, r 186 } 187 188 // This is hacky, but whatever for now. 189 if action == tview.MouseLeftDown { 190 var gripper *Gripper 191 var gripperIndex int 192 for i, g := range r.grippers { 193 if g.InRect(x, y) { 194 gripper = g 195 gripperIndex = i 196 break 197 } 198 } 199 200 if gripper != nil { 201 gripper.isHeld = true 202 r.heldGripper = gripper 203 r.heldGripperIndex = gripperIndex 204 r.lastX, r.lastY = x, y 205 return true, gripper 206 } 207 } 208 209 // If we hit here, pass down to actual items. 210 return r.MouseHandleItems(action, event, setFocus) 211 }) 212} 213 214func (r *Resizable) MouseHandleItems(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) { 215 for _, item := range r.items { 216 consumed, capture = item.item.MouseHandler()(action, event, setFocus) 217 if consumed { 218 return 219 } 220 } 221 return false, nil 222} 223 224func (r *Resizable) SetRect(x, y, width, height int) { 225 r.Flex.SetRect(x, y, width, height) 226 // TODO: Redistribute items. 227}