1package core
2
3import (
4 "image"
5
6 "github.com/bnema/neferwl/internal/ports"
7)
8
9// placeItem is one output to place: its connector name, logical size, the
10// explicit origin when one is set (pos: explicit origin set at runtime by
11// wlr-output-management, not a config key) and the output.<name> relation to
12// another output.
13type placeItem struct {
14 name string
15 w, h int
16 pos *image.Point
17 anchor ports.OutputAnchor
18}
19
20// placeOutputs returns the logical origin of every item, in item order.
21// Precedence: an explicit position, then the relation to another placed
22// output, then automatic placement: from the rightmost edge placed so far
23// (never left of 0), at y=0. Automatic placement runs only when no relation
24// can be resolved, so an anchored output never lands on an automatic one.
25// A relation to an absent output, or in a cycle, falls back to automatic.
26func placeOutputs(items []placeItem) []image.Point {
27 out := make([]image.Point, len(items))
28 done := make([]bool, len(items))
29 left := len(items)
30 for i, it := range items {
31 if it.pos != nil {
32 out[i], done[i] = *it.pos, true
33 left--
34 }
35 }
36 anchored := func(i int) bool { return !done[i] && items[i].anchor.Relation != ports.RelationNone }
37 for left > 0 {
38 for progress := true; progress; {
39 progress = false
40 for i, it := range items {
41 if !anchored(i) {
42 continue
43 }
44 for j, ref := range items {
45 if !done[j] || ref.name == "" || ref.name != it.anchor.To {
46 continue
47 }
48 out[i], done[i] = relativeTo(it, ref, out[j]), true
49 left--
50 progress = true
51 break
52 }
53 }
54 }
55 if left == 0 {
56 break
57 }
58 next := -1
59 for i := range items {
60 if !done[i] && (next < 0 || (anchored(next) && !anchored(i))) {
61 next = i
62 }
63 }
64 x := 0
65 for i, it := range items {
66 if done[i] {
67 x = max(x, out[i].X+it.w)
68 }
69 }
70 out[next], done[next] = image.Point{X: x}, true
71 left--
72 }
73 return out
74}
75
76// relativeTo is the origin of it placed against ref, whose origin is at.
77func relativeTo(it, ref placeItem, at image.Point) image.Point {
78 off := it.anchor.Offset
79 switch it.anchor.Relation {
80 case ports.RelationRightOf:
81 return image.Point{X: at.X + ref.w, Y: at.Y + off}
82 case ports.RelationLeftOf:
83 return image.Point{X: at.X - it.w, Y: at.Y + off}
84 case ports.RelationAbove:
85 return image.Point{X: at.X + off, Y: at.Y - it.h}
86 default: // RelationBelow
87 return image.Point{X: at.X + off, Y: at.Y + ref.h}
88 }
89}