1package core
2
3import "github.com/bnema/neferwl/internal/ports"
4
5// Separator lines follow tmux (screen-redraw.c): tiles share one line per
6// side where they touch, owned by the left or top tile (Placement.Inset).
7// The focused tile lights the lines along its sides, corners included.
8// With exactly two tiles one line splits them, and each lights only its
9// half of it: the left or top tile the first half. With gaps each tile
10// has lines of its own and lights them whole. Floats get a full border
11// of their own, drawn with them (Separator.Window).
12//
13// Only what is on the output counts: lines at the output edge (a column
14// scrolled off beyond it) are not drawn, and only tiles on it count.
15
16var sides = [...]ports.Sides{ports.SideLeft, ports.SideRight, ports.SideTop, ports.SideBottom}
17
18// separators are the lines of a layout on output o, inactive ones first
19// so active ones draw over them. lit says whether the focused tile shows
20// focus (only on the focused output).
21func separators(ps []Placement, width, gap int, o Rect, lit bool) []ports.Separator {
22 if width <= 0 {
23 return nil
24 }
25 var out []ports.Separator
26 add := func(p *Placement, r Rect, active bool) {
27 if !p.Floating && (r.X < o.X+width || r.X+r.W > o.X+o.W-width) && r.H > r.W {
28 return // a vertical line at the output edge
29 }
30 var id WindowID
31 if p.Floating {
32 id = p.ID
33 }
34 out = append(out, ports.Separator{Rect: r, Active: active, Window: id})
35 }
36 var focused *Placement
37 tiles := 0
38 for i := range ps {
39 p := &ps[i]
40 if p.Hidden || p.Fullscreen || p.Rect.W <= 0 || p.Rect.H <= 0 {
41 continue
42 }
43 if p.Neighbors != 0 && p.Rect.X < o.X+o.W && p.Rect.X+p.Rect.W > o.X {
44 tiles++
45 }
46 for _, s := range sides {
47 if p.Inset&s != 0 {
48 add(p, strip(p.Rect, s, 0, width), false)
49 }
50 }
51 if p.Focused && lit {
52 focused = p
53 }
54 }
55 if focused == nil {
56 return out
57 }
58 f := focused
59 for _, s := range sides {
60 var line ports.Rect
61 switch {
62 case f.Neighbors&s != 0 && f.Inset&s != 0:
63 line = strip(f.Rect, s, 0, width)
64 case f.Neighbors&s != 0:
65 line = strip(f.Rect, s, width, width)
66 case f.Inset&s != 0:
67 // A float's own border.
68 add(f, strip(f.Rect, s, 0, width), true)
69 continue
70 default:
71 continue
72 }
73 line = closeCorners(line, *f, s, width)
74 // Two tiles share one line only without gaps.
75 if tiles == 2 && gap == 0 {
76 line = half(line, s)
77 }
78 add(f, line, true)
79 }
80 return out
81}
82
83// strip is the b-wide strip along side s of r: inside r when out is 0,
84// else just outside it (the neighbor's line).
85func strip(r Rect, s ports.Sides, out, b int) Rect {
86 b = min(b, max(r.W/2, out), max(r.H/2, out))
87 switch s {
88 case ports.SideLeft:
89 return Rect{X: r.X - out, Y: r.Y, W: b, H: r.H}
90 case ports.SideRight:
91 return Rect{X: r.X + r.W - b + out, Y: r.Y, W: b, H: r.H}
92 case ports.SideTop:
93 return Rect{X: r.X, Y: r.Y - out, W: r.W, H: b}
94 }
95 return Rect{X: r.X, Y: r.Y + r.H - b + out, W: r.W, H: b}
96}
97
98// closeCorners runs a line along side s of p b further at each end where
99// the crossing line lies outside p (a neighbor's), so lit lines meet.
100func closeCorners(line Rect, p Placement, s ports.Sides, b int) Rect {
101 outside := func(s ports.Sides) bool { return p.Neighbors&s != 0 && p.Inset&s == 0 }
102 if s == ports.SideLeft || s == ports.SideRight {
103 if outside(ports.SideTop) {
104 line.Y, line.H = line.Y-b, line.H+b
105 }
106 if outside(ports.SideBottom) {
107 line.H += b
108 }
109 return line
110 }
111 if outside(ports.SideLeft) {
112 line.X, line.W = line.X-b, line.W+b
113 }
114 if outside(ports.SideRight) {
115 line.W += b
116 }
117 return line
118}
119
120// half keeps the half of a line owned by the tile on side s of it: the
121// first half for the left or top tile, else the second.
122func half(r Rect, s ports.Sides) Rect {
123 switch s {
124 case ports.SideRight:
125 r.H /= 2
126 case ports.SideLeft:
127 r.Y, r.H = r.Y+r.H/2, r.H-r.H/2
128 case ports.SideBottom:
129 r.W /= 2
130 case ports.SideTop:
131 r.X, r.W = r.X+r.W/2, r.W-r.W/2
132 }
133 return r
134}