package core import "github.com/bnema/neferwl/internal/ports" // Separator lines follow tmux (screen-redraw.c): tiles share one line per // side where they touch, owned by the left or top tile (Placement.Inset). // The focused tile lights the lines along its sides, corners included. // With exactly two tiles one line splits them, and each lights only its // half of it: the left or top tile the first half. With gaps each tile // has lines of its own and lights them whole. Floats get a full border // of their own, drawn with them (Separator.Window). // // Only what is on the output counts: lines at the output edge (a column // scrolled off beyond it) are not drawn, and only tiles on it count. var sides = [...]ports.Sides{ports.SideLeft, ports.SideRight, ports.SideTop, ports.SideBottom} // separators are the lines of a layout on output o, inactive ones first // so active ones draw over them. lit says whether the focused tile shows // focus (only on the focused output). func separators(ps []Placement, width, gap int, o Rect, lit bool) []ports.Separator { if width <= 0 { return nil } var out []ports.Separator add := func(p *Placement, r Rect, active bool) { if !p.Floating && (r.X < o.X+width || r.X+r.W > o.X+o.W-width) && r.H > r.W { return // a vertical line at the output edge } var id WindowID if p.Floating { id = p.ID } out = append(out, ports.Separator{Rect: r, Active: active, Window: id}) } var focused *Placement tiles := 0 for i := range ps { p := &ps[i] if p.Hidden || p.Fullscreen || p.Rect.W <= 0 || p.Rect.H <= 0 { continue } if p.Neighbors != 0 && p.Rect.X < o.X+o.W && p.Rect.X+p.Rect.W > o.X { tiles++ } for _, s := range sides { if p.Inset&s != 0 { add(p, strip(p.Rect, s, 0, width), false) } } if p.Focused && lit { focused = p } } if focused == nil { return out } f := focused for _, s := range sides { var line ports.Rect switch { case f.Neighbors&s != 0 && f.Inset&s != 0: line = strip(f.Rect, s, 0, width) case f.Neighbors&s != 0: line = strip(f.Rect, s, width, width) case f.Inset&s != 0: // A float's own border. add(f, strip(f.Rect, s, 0, width), true) continue default: continue } line = closeCorners(line, *f, s, width) // Two tiles share one line only without gaps. if tiles == 2 && gap == 0 { line = half(line, s) } add(f, line, true) } return out } // strip is the b-wide strip along side s of r: inside r when out is 0, // else just outside it (the neighbor's line). func strip(r Rect, s ports.Sides, out, b int) Rect { b = min(b, max(r.W/2, out), max(r.H/2, out)) switch s { case ports.SideLeft: return Rect{X: r.X - out, Y: r.Y, W: b, H: r.H} case ports.SideRight: return Rect{X: r.X + r.W - b + out, Y: r.Y, W: b, H: r.H} case ports.SideTop: return Rect{X: r.X, Y: r.Y - out, W: r.W, H: b} } return Rect{X: r.X, Y: r.Y + r.H - b + out, W: r.W, H: b} } // closeCorners runs a line along side s of p b further at each end where // the crossing line lies outside p (a neighbor's), so lit lines meet. func closeCorners(line Rect, p Placement, s ports.Sides, b int) Rect { outside := func(s ports.Sides) bool { return p.Neighbors&s != 0 && p.Inset&s == 0 } if s == ports.SideLeft || s == ports.SideRight { if outside(ports.SideTop) { line.Y, line.H = line.Y-b, line.H+b } if outside(ports.SideBottom) { line.H += b } return line } if outside(ports.SideLeft) { line.X, line.W = line.X-b, line.W+b } if outside(ports.SideRight) { line.W += b } return line } // half keeps the half of a line owned by the tile on side s of it: the // first half for the left or top tile, else the second. func half(r Rect, s ports.Sides) Rect { switch s { case ports.SideRight: r.H /= 2 case ports.SideLeft: r.Y, r.H = r.Y+r.H/2, r.H-r.H/2 case ports.SideBottom: r.W /= 2 case ports.SideTop: r.X, r.W = r.X+r.W/2, r.W-r.W/2 } return r }