detached d458689

refactor(core): store row shares in basis points

commit
d458689758fcb0affa099046e5d659b2279efa56
author
brice · · 2026-10-21 05:17 UTC
committer
brice · · 2026-10-21 07:11 UTC
parent
dbc10e2
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +31 -31
M internal/core/layout.go +12 -12
@@ -26,9 +26,9 @@ type Column struct {
2626 // Slot is the declared column number (workspace.<name>.column.N) of a
2727 // slot window; 0 for normal columns.
2828 Slot int
29- // Shares are the row heights set by set-window-height, in percent, in
29+ // Shares are the row heights set by set-window-height, in basis points, in
3030 // Windows order. They apply only while there is one per window and
31- // they sum to 100; otherwise the rows share the height equally.
31+ // they sum to 10000; otherwise the rows share the height equally.
3232 Shares []int
3333 // BandStart marks the first column of a cascade band. It belongs to the
3434 // position, not the column: swaps leave it in place. See cascade.go.
@@ -916,33 +916,33 @@ func (w *Workspace) MoveWindow(dir int) {
916916 w.scroll()
917917 }
918918
919-// minShare is the smallest row height set-window-height leaves, percent.
920-const minShare = 10
919+// minShare is the smallest row height set-window-height leaves, in basis points.
920+const minShare = 1000
921921
922922 // ResizeRow changes the focused window's height share by pct percent; the
923923 // other rows of its column give or take it in proportion to their shares.
924-// Every row keeps at least minShare percent.
924+// Every row keeps at least minShare basis points.
925925 func (w *Workspace) ResizeRow(pct int) {
926926 if w.onFloat() || len(w.Columns) == 0 || w.fullscreenHides() {
927927 return
928928 }
929929 c := &w.Columns[w.Focus]
930930 n := len(c.Windows)
931- if n < 2 || n*minShare > 100 {
931+ if n < 2 || n*minShare > 10000 {
932932 return
933933 }
934934 shares := c.Shares
935935 if !validShares(*c) {
936936 shares = make([]int, n)
937937 for i := range shares {
938- shares[i] = 100 / n
938+ shares[i] = 10000 / n
939939 }
940- shares[n-1] += 100 % n
940+ shares[n-1] += 10000 % n
941941 } else {
942942 shares = slices.Clone(shares)
943943 }
944944 f := c.Focus
945- target := min(max(shares[f]+pct, minShare), 100-minShare*(n-1))
945+ target := min(max(shares[f]+pct*100, minShare), 10000-minShare*(n-1))
946946 delta := target - shares[f]
947947 if delta == 0 {
948948 return
@@ -1804,7 +1804,7 @@ func rowRectsInto(dst []Rect, r Rect, c Column, gap int) []Rect {
18041804 avail := max(r.H-(n-1)*gap, 0)
18051805 yy, bottom, used := r.Y, r.Y+r.H, 0
18061806 for i, share := range c.Shares {
1807- hh := avail * share / 100
1807+ hh := avail * share / 10000
18081808 if i == n-1 {
18091809 hh = avail - used
18101810 }
@@ -1818,7 +1818,7 @@ func rowRectsInto(dst []Rect, r Rect, c Column, gap int) []Rect {
18181818 }
18191819
18201820 // validShares reports whether c.Shares has one share per window, each at
1821-// least 1, summing to 100.
1821+// least 1, summing to 10000.
18221822 func validShares(c Column) bool {
18231823 if len(c.Shares) != len(c.Windows) || len(c.Shares) < 2 {
18241824 return false
@@ -1830,7 +1830,7 @@ func validShares(c Column) bool {
18301830 }
18311831 sum += v
18321832 }
1833- return sum == 100
1833+ return sum == 10000
18341834 }
18351835
18361836 // split cuts r in two halves with a gap: top/bottom when vertical, else left/right.
@@ -26,9 +26,9 @@ type Column struct {
26 // Slot is the declared column number (workspace.<name>.column.N) of a26 // Slot is the declared column number (workspace.<name>.column.N) of a
27 // slot window; 0 for normal columns.27 // slot window; 0 for normal columns.
28 Slot int28 Slot int
29- // Shares are the row heights set by set-window-height, in percent, in29+ // Shares are the row heights set by set-window-height, in basis points, in
30 // Windows order. They apply only while there is one per window and30 // Windows order. They apply only while there is one per window and
31- // they sum to 100; otherwise the rows share the height equally.31+ // they sum to 10000; otherwise the rows share the height equally.
32 Shares []int32 Shares []int
33 // BandStart marks the first column of a cascade band. It belongs to the33 // BandStart marks the first column of a cascade band. It belongs to the
34 // position, not the column: swaps leave it in place. See cascade.go.34 // position, not the column: swaps leave it in place. See cascade.go.
@@ -916,33 +916,33 @@ func (w *Workspace) MoveWindow(dir int) {
916 w.scroll()916 w.scroll()
917 }917 }
918 918
919-// minShare is the smallest row height set-window-height leaves, percent.919+// minShare is the smallest row height set-window-height leaves, in basis points.
920-const minShare = 10920+const minShare = 1000
921 921
922 // ResizeRow changes the focused window's height share by pct percent; the922 // ResizeRow changes the focused window's height share by pct percent; the
923 // other rows of its column give or take it in proportion to their shares.923 // other rows of its column give or take it in proportion to their shares.
924-// Every row keeps at least minShare percent.924+// Every row keeps at least minShare basis points.
925 func (w *Workspace) ResizeRow(pct int) {925 func (w *Workspace) ResizeRow(pct int) {
926 if w.onFloat() || len(w.Columns) == 0 || w.fullscreenHides() {926 if w.onFloat() || len(w.Columns) == 0 || w.fullscreenHides() {
927 return927 return
928 }928 }
929 c := &w.Columns[w.Focus]929 c := &w.Columns[w.Focus]
930 n := len(c.Windows)930 n := len(c.Windows)
931- if n < 2 || n*minShare > 100 {931+ if n < 2 || n*minShare > 10000 {
932 return932 return
933 }933 }
934 shares := c.Shares934 shares := c.Shares
935 if !validShares(*c) {935 if !validShares(*c) {
936 shares = make([]int, n)936 shares = make([]int, n)
937 for i := range shares {937 for i := range shares {
938- shares[i] = 100 / n938+ shares[i] = 10000 / n
939 }939 }
940- shares[n-1] += 100 % n940+ shares[n-1] += 10000 % n
941 } else {941 } else {
942 shares = slices.Clone(shares)942 shares = slices.Clone(shares)
943 }943 }
944 f := c.Focus944 f := c.Focus
945- target := min(max(shares[f]+pct, minShare), 100-minShare*(n-1))945+ target := min(max(shares[f]+pct*100, minShare), 10000-minShare*(n-1))
946 delta := target - shares[f]946 delta := target - shares[f]
947 if delta == 0 {947 if delta == 0 {
948 return948 return
@@ -1804,7 +1804,7 @@ func rowRectsInto(dst []Rect, r Rect, c Column, gap int) []Rect {
1804 avail := max(r.H-(n-1)*gap, 0)1804 avail := max(r.H-(n-1)*gap, 0)
1805 yy, bottom, used := r.Y, r.Y+r.H, 01805 yy, bottom, used := r.Y, r.Y+r.H, 0
1806 for i, share := range c.Shares {1806 for i, share := range c.Shares {
1807- hh := avail * share / 1001807+ hh := avail * share / 10000
1808 if i == n-1 {1808 if i == n-1 {
1809 hh = avail - used1809 hh = avail - used
1810 }1810 }
@@ -1818,7 +1818,7 @@ func rowRectsInto(dst []Rect, r Rect, c Column, gap int) []Rect {
1818 }1818 }
1819 1819
1820 // validShares reports whether c.Shares has one share per window, each at1820 // validShares reports whether c.Shares has one share per window, each at
1821-// least 1, summing to 100.1821+// least 1, summing to 10000.
1822 func validShares(c Column) bool {1822 func validShares(c Column) bool {
1823 if len(c.Shares) != len(c.Windows) || len(c.Shares) < 2 {1823 if len(c.Shares) != len(c.Windows) || len(c.Shares) < 2 {
1824 return false1824 return false
@@ -1830,7 +1830,7 @@ func validShares(c Column) bool {
1830 }1830 }
1831 sum += v1831 sum += v
1832 }1832 }
1833- return sum == 1001833+ return sum == 10000
1834 }1834 }
1835 1835
1836 // split cuts r in two halves with a gap: top/bottom when vertical, else left/right.1836 // split cuts r in two halves with a gap: top/bottom when vertical, else left/right.
M internal/core/layout_buffers_test.go +2 -2
@@ -39,7 +39,7 @@ func layoutVariants(t *testing.T) map[string]*Monitor {
3939 m = base(OverflowScroll, 4)
4040 w := m.Current()
4141 w.Columns[0].Windows = []WindowID{1, 5, 6}
42- w.Columns[0].Shares = []int{50, 30, 20}
42+ w.Columns[0].Shares = []int{5000, 3000, 2000}
4343 m.AddWindow(5)
4444 v["shares"] = m
4545
@@ -204,7 +204,7 @@ func TestRectHelpersKeepAppendSemantics(t *testing.T) {
204204 if !slices.Equal(got[:2], pre) || !slices.Equal(got[2:], stackRects(r, 3, 5)) {
205205 t.Fatalf("stackRectsInto: %+v", got)
206206 }
207- c := Column{Windows: []WindowID{1, 2, 3}, Shares: []int{50, 30, 20}}
207+ c := Column{Windows: []WindowID{1, 2, 3}, Shares: []int{5000, 3000, 2000}}
208208 got = rowRectsInto(slices.Clone(pre), r, c, 5)
209209 if !slices.Equal(got[:2], pre) || !slices.Equal(got[2:], rowRects(r, c, 5)) {
210210 t.Fatalf("rowRectsInto: %+v", got)
@@ -39,7 +39,7 @@ func layoutVariants(t *testing.T) map[string]*Monitor {
39 m = base(OverflowScroll, 4)39 m = base(OverflowScroll, 4)
40 w := m.Current()40 w := m.Current()
41 w.Columns[0].Windows = []WindowID{1, 5, 6}41 w.Columns[0].Windows = []WindowID{1, 5, 6}
42- w.Columns[0].Shares = []int{50, 30, 20}42+ w.Columns[0].Shares = []int{5000, 3000, 2000}
43 m.AddWindow(5)43 m.AddWindow(5)
44 v["shares"] = m44 v["shares"] = m
45 45
@@ -204,7 +204,7 @@ func TestRectHelpersKeepAppendSemantics(t *testing.T) {
204 if !slices.Equal(got[:2], pre) || !slices.Equal(got[2:], stackRects(r, 3, 5)) {204 if !slices.Equal(got[:2], pre) || !slices.Equal(got[2:], stackRects(r, 3, 5)) {
205 t.Fatalf("stackRectsInto: %+v", got)205 t.Fatalf("stackRectsInto: %+v", got)
206 }206 }
207- c := Column{Windows: []WindowID{1, 2, 3}, Shares: []int{50, 30, 20}}207+ c := Column{Windows: []WindowID{1, 2, 3}, Shares: []int{5000, 3000, 2000}}
208 got = rowRectsInto(slices.Clone(pre), r, c, 5)208 got = rowRectsInto(slices.Clone(pre), r, c, 5)
209 if !slices.Equal(got[:2], pre) || !slices.Equal(got[2:], rowRects(r, c, 5)) {209 if !slices.Equal(got[:2], pre) || !slices.Equal(got[2:], rowRects(r, c, 5)) {
210 t.Fatalf("rowRectsInto: %+v", got)210 t.Fatalf("rowRectsInto: %+v", got)
M internal/core/row_shares_test.go +17 -17
@@ -29,15 +29,15 @@ func TestRowRectsEqualWithoutShares(t *testing.T) {
2929 if !slices.Equal(rowRects(r, c, 5), stackRects(r, 3, 5)) {
3030 t.Fatal(rowRects(r, c, 5))
3131 }
32- c.Shares = []int{50, 50} // wrong length: ignored
32+ c.Shares = []int{5000, 5000} // wrong length: ignored
3333 if !slices.Equal(rowRects(r, c, 5), stackRects(r, 3, 5)) {
3434 t.Fatal(rowRects(r, c, 5))
3535 }
36- c.Shares = []int{50, 30, 30} // wrong sum: ignored
36+ c.Shares = []int{5000, 3000, 3000} // wrong sum: ignored
3737 if !slices.Equal(rowRects(r, c, 5), stackRects(r, 3, 5)) {
3838 t.Fatal(rowRects(r, c, 5))
3939 }
40- c.Shares = []int{50, 30, 20}
40+ c.Shares = []int{5000, 3000, 2000}
4141 rows := rowRects(r, c, 5)
4242 if rows[0].H != 46 || rows[1].H != 27 || rows[2].H != 93-46-27 || rows[2].Y+rows[2].H != 103 {
4343 t.Fatal(rows)
@@ -51,7 +51,7 @@ func TestResizeRow(t *testing.T) {
5151 w.SetOutput(1000, 800)
5252 w.Columns = []Column{{Windows: []WindowID{1, 2}}}
5353 w.Apply("set-window-height +10%")
54- if !slices.Equal(w.Columns[0].Shares, []int{60, 40}) {
54+ if !slices.Equal(w.Columns[0].Shares, []int{6000, 4000}) {
5555 t.Fatal(o, w.Columns[0].Shares)
5656 }
5757 h := heights(w)
@@ -61,13 +61,13 @@ func TestResizeRow(t *testing.T) {
6161 for range 10 {
6262 w.Apply("set-window-height +10%")
6363 }
64- if !slices.Equal(w.Columns[0].Shares, []int{90, 10}) {
64+ if !slices.Equal(w.Columns[0].Shares, []int{9000, 1000}) {
6565 t.Fatal(o, w.Columns[0].Shares)
6666 }
6767 for range 10 {
6868 w.Apply("set-window-height -10%")
6969 }
70- if !slices.Equal(w.Columns[0].Shares, []int{10, 90}) {
70+ if !slices.Equal(w.Columns[0].Shares, []int{1000, 9000}) {
7171 t.Fatal(o, w.Columns[0].Shares)
7272 }
7373 }
@@ -79,24 +79,24 @@ func TestResizeRowThree(t *testing.T) {
7979 w.Columns = []Column{{Windows: []WindowID{1, 2, 3}, Focus: 1}}
8080 w.Apply("set-window-height +10%")
8181 s := w.Columns[0].Shares
82- if sum(s) != 100 || s[1] != 43 || s[0] < 10 || s[2] < 10 {
82+ if sum(s) != 10000 || s[1] != 4333 || s[0] < 1000 || s[2] < 1000 {
8383 t.Fatal(s)
8484 }
8585 for _, step := range []string{"+25%", "+25%", "-7%", "-100%", "+100%", "-3%"} {
8686 w.Apply(Action("set-window-height " + step))
8787 s := w.Columns[0].Shares
88- if sum(s) != 100 || slices.Min(s) < 10 {
88+ if sum(s) != 10000 || slices.Min(s) < 1000 {
8989 t.Fatal(step, s)
9090 }
9191 }
92- if s := w.Columns[0].Shares; s[1] != 77 {
92+ if s := w.Columns[0].Shares; s[1] != 7700 {
9393 t.Fatal(s)
9494 }
9595 // Proportional: the others shrink by their room above the minimum.
96- w.Columns[0].Shares = []int{50, 20, 30}
96+ w.Columns[0].Shares = []int{5000, 2000, 3000}
9797 w.Columns[0].Focus = 1
9898 w.Apply("set-window-height +20%")
99- if s := w.Columns[0].Shares; !slices.Equal(s, []int{37, 40, 23}) {
99+ if s := w.Columns[0].Shares; !slices.Equal(s, []int{3667, 4000, 2333}) {
100100 t.Fatal(s)
101101 }
102102 }
@@ -140,25 +140,25 @@ func TestSharesResetAndTravel(t *testing.T) {
140140 t.Fatal(w.Columns)
141141 }
142142 w.Apply("set-window-height +20%")
143- if !slices.Equal(w.Columns[0].Shares, []int{30, 70}) {
143+ if !slices.Equal(w.Columns[0].Shares, []int{3000, 7000}) {
144144 t.Fatal(w.Columns[0].Shares)
145145 }
146146 // Moving the window inside the column swaps the shares with it.
147147 w.Apply(ActionMoveWindowUp)
148- if !slices.Equal(w.Columns[0].Shares, []int{70, 30}) || w.Columns[0].Windows[0] != 2 {
148+ if !slices.Equal(w.Columns[0].Shares, []int{7000, 3000}) || w.Columns[0].Windows[0] != 2 {
149149 t.Fatal(w.Columns[0])
150150 }
151151 // The column takes its shares to another workspace.
152152 m.MoveToWorkspace(1, true)
153153 m.FocusNumber(2)
154- if c := m.Current().Columns[0]; !slices.Equal(c.Shares, []int{70, 30}) {
154+ if c := m.Current().Columns[0]; !slices.Equal(c.Shares, []int{7000, 3000}) {
155155 t.Fatal(c)
156156 }
157157 // MoveColumn keeps them.
158158 m.AddWindow(3)
159159 m.Current().FocusColumn(-1)
160160 m.Current().MoveColumn(1)
161- if c := m.Current().Columns[1]; !slices.Equal(c.Shares, []int{70, 30}) {
161+ if c := m.Current().Columns[1]; !slices.Equal(c.Shares, []int{7000, 3000}) {
162162 t.Fatal(m.Current().Columns)
163163 }
164164 // A window leaving resets the column to equal rows.
@@ -171,7 +171,7 @@ func TestSharesResetAndTravel(t *testing.T) {
171171 }
172172 }
173173 // A window joining invalidates them: equal rows.
174- w.Columns = []Column{{Windows: []WindowID{1, 3}, Shares: []int{80, 20}}}
174+ w.Columns = []Column{{Windows: []WindowID{1, 3}, Shares: []int{8000, 2000}}}
175175 w.Columns[0].Windows = append(w.Columns[0].Windows, 4)
176176 h := heights(w)
177177 if h[1] != h[3] {
@@ -182,7 +182,7 @@ func TestSharesResetAndTravel(t *testing.T) {
182182 func TestOverviewPreviewUsesShares(t *testing.T) {
183183 w := workspace()
184184 w.SetOutput(1000, 800)
185- w.Columns = []Column{{Windows: []WindowID{1, 2}, Shares: []int{70, 30}}}
185+ w.Columns = []Column{{Windows: []WindowID{1, 2}, Shares: []int{7000, 3000}}}
186186 tiles, _, _ := w.previewTiles()
187187 h := map[WindowID]int{}
188188 for _, p := range tiles {
@@ -29,15 +29,15 @@ func TestRowRectsEqualWithoutShares(t *testing.T) {
29 if !slices.Equal(rowRects(r, c, 5), stackRects(r, 3, 5)) {29 if !slices.Equal(rowRects(r, c, 5), stackRects(r, 3, 5)) {
30 t.Fatal(rowRects(r, c, 5))30 t.Fatal(rowRects(r, c, 5))
31 }31 }
32- c.Shares = []int{50, 50} // wrong length: ignored32+ c.Shares = []int{5000, 5000} // wrong length: ignored
33 if !slices.Equal(rowRects(r, c, 5), stackRects(r, 3, 5)) {33 if !slices.Equal(rowRects(r, c, 5), stackRects(r, 3, 5)) {
34 t.Fatal(rowRects(r, c, 5))34 t.Fatal(rowRects(r, c, 5))
35 }35 }
36- c.Shares = []int{50, 30, 30} // wrong sum: ignored36+ c.Shares = []int{5000, 3000, 3000} // wrong sum: ignored
37 if !slices.Equal(rowRects(r, c, 5), stackRects(r, 3, 5)) {37 if !slices.Equal(rowRects(r, c, 5), stackRects(r, 3, 5)) {
38 t.Fatal(rowRects(r, c, 5))38 t.Fatal(rowRects(r, c, 5))
39 }39 }
40- c.Shares = []int{50, 30, 20}40+ c.Shares = []int{5000, 3000, 2000}
41 rows := rowRects(r, c, 5)41 rows := rowRects(r, c, 5)
42 if rows[0].H != 46 || rows[1].H != 27 || rows[2].H != 93-46-27 || rows[2].Y+rows[2].H != 103 {42 if rows[0].H != 46 || rows[1].H != 27 || rows[2].H != 93-46-27 || rows[2].Y+rows[2].H != 103 {
43 t.Fatal(rows)43 t.Fatal(rows)
@@ -51,7 +51,7 @@ func TestResizeRow(t *testing.T) {
51 w.SetOutput(1000, 800)51 w.SetOutput(1000, 800)
52 w.Columns = []Column{{Windows: []WindowID{1, 2}}}52 w.Columns = []Column{{Windows: []WindowID{1, 2}}}
53 w.Apply("set-window-height +10%")53 w.Apply("set-window-height +10%")
54- if !slices.Equal(w.Columns[0].Shares, []int{60, 40}) {54+ if !slices.Equal(w.Columns[0].Shares, []int{6000, 4000}) {
55 t.Fatal(o, w.Columns[0].Shares)55 t.Fatal(o, w.Columns[0].Shares)
56 }56 }
57 h := heights(w)57 h := heights(w)
@@ -61,13 +61,13 @@ func TestResizeRow(t *testing.T) {
61 for range 10 {61 for range 10 {
62 w.Apply("set-window-height +10%")62 w.Apply("set-window-height +10%")
63 }63 }
64- if !slices.Equal(w.Columns[0].Shares, []int{90, 10}) {64+ if !slices.Equal(w.Columns[0].Shares, []int{9000, 1000}) {
65 t.Fatal(o, w.Columns[0].Shares)65 t.Fatal(o, w.Columns[0].Shares)
66 }66 }
67 for range 10 {67 for range 10 {
68 w.Apply("set-window-height -10%")68 w.Apply("set-window-height -10%")
69 }69 }
70- if !slices.Equal(w.Columns[0].Shares, []int{10, 90}) {70+ if !slices.Equal(w.Columns[0].Shares, []int{1000, 9000}) {
71 t.Fatal(o, w.Columns[0].Shares)71 t.Fatal(o, w.Columns[0].Shares)
72 }72 }
73 }73 }
@@ -79,24 +79,24 @@ func TestResizeRowThree(t *testing.T) {
79 w.Columns = []Column{{Windows: []WindowID{1, 2, 3}, Focus: 1}}79 w.Columns = []Column{{Windows: []WindowID{1, 2, 3}, Focus: 1}}
80 w.Apply("set-window-height +10%")80 w.Apply("set-window-height +10%")
81 s := w.Columns[0].Shares81 s := w.Columns[0].Shares
82- if sum(s) != 100 || s[1] != 43 || s[0] < 10 || s[2] < 10 {82+ if sum(s) != 10000 || s[1] != 4333 || s[0] < 1000 || s[2] < 1000 {
83 t.Fatal(s)83 t.Fatal(s)
84 }84 }
85 for _, step := range []string{"+25%", "+25%", "-7%", "-100%", "+100%", "-3%"} {85 for _, step := range []string{"+25%", "+25%", "-7%", "-100%", "+100%", "-3%"} {
86 w.Apply(Action("set-window-height " + step))86 w.Apply(Action("set-window-height " + step))
87 s := w.Columns[0].Shares87 s := w.Columns[0].Shares
88- if sum(s) != 100 || slices.Min(s) < 10 {88+ if sum(s) != 10000 || slices.Min(s) < 1000 {
89 t.Fatal(step, s)89 t.Fatal(step, s)
90 }90 }
91 }91 }
92- if s := w.Columns[0].Shares; s[1] != 77 {92+ if s := w.Columns[0].Shares; s[1] != 7700 {
93 t.Fatal(s)93 t.Fatal(s)
94 }94 }
95 // Proportional: the others shrink by their room above the minimum.95 // Proportional: the others shrink by their room above the minimum.
96- w.Columns[0].Shares = []int{50, 20, 30}96+ w.Columns[0].Shares = []int{5000, 2000, 3000}
97 w.Columns[0].Focus = 197 w.Columns[0].Focus = 1
98 w.Apply("set-window-height +20%")98 w.Apply("set-window-height +20%")
99- if s := w.Columns[0].Shares; !slices.Equal(s, []int{37, 40, 23}) {99+ if s := w.Columns[0].Shares; !slices.Equal(s, []int{3667, 4000, 2333}) {
100 t.Fatal(s)100 t.Fatal(s)
101 }101 }
102 }102 }
@@ -140,25 +140,25 @@ func TestSharesResetAndTravel(t *testing.T) {
140 t.Fatal(w.Columns)140 t.Fatal(w.Columns)
141 }141 }
142 w.Apply("set-window-height +20%")142 w.Apply("set-window-height +20%")
143- if !slices.Equal(w.Columns[0].Shares, []int{30, 70}) {143+ if !slices.Equal(w.Columns[0].Shares, []int{3000, 7000}) {
144 t.Fatal(w.Columns[0].Shares)144 t.Fatal(w.Columns[0].Shares)
145 }145 }
146 // Moving the window inside the column swaps the shares with it.146 // Moving the window inside the column swaps the shares with it.
147 w.Apply(ActionMoveWindowUp)147 w.Apply(ActionMoveWindowUp)
148- if !slices.Equal(w.Columns[0].Shares, []int{70, 30}) || w.Columns[0].Windows[0] != 2 {148+ if !slices.Equal(w.Columns[0].Shares, []int{7000, 3000}) || w.Columns[0].Windows[0] != 2 {
149 t.Fatal(w.Columns[0])149 t.Fatal(w.Columns[0])
150 }150 }
151 // The column takes its shares to another workspace.151 // The column takes its shares to another workspace.
152 m.MoveToWorkspace(1, true)152 m.MoveToWorkspace(1, true)
153 m.FocusNumber(2)153 m.FocusNumber(2)
154- if c := m.Current().Columns[0]; !slices.Equal(c.Shares, []int{70, 30}) {154+ if c := m.Current().Columns[0]; !slices.Equal(c.Shares, []int{7000, 3000}) {
155 t.Fatal(c)155 t.Fatal(c)
156 }156 }
157 // MoveColumn keeps them.157 // MoveColumn keeps them.
158 m.AddWindow(3)158 m.AddWindow(3)
159 m.Current().FocusColumn(-1)159 m.Current().FocusColumn(-1)
160 m.Current().MoveColumn(1)160 m.Current().MoveColumn(1)
161- if c := m.Current().Columns[1]; !slices.Equal(c.Shares, []int{70, 30}) {161+ if c := m.Current().Columns[1]; !slices.Equal(c.Shares, []int{7000, 3000}) {
162 t.Fatal(m.Current().Columns)162 t.Fatal(m.Current().Columns)
163 }163 }
164 // A window leaving resets the column to equal rows.164 // A window leaving resets the column to equal rows.
@@ -171,7 +171,7 @@ func TestSharesResetAndTravel(t *testing.T) {
171 }171 }
172 }172 }
173 // A window joining invalidates them: equal rows.173 // A window joining invalidates them: equal rows.
174- w.Columns = []Column{{Windows: []WindowID{1, 3}, Shares: []int{80, 20}}}174+ w.Columns = []Column{{Windows: []WindowID{1, 3}, Shares: []int{8000, 2000}}}
175 w.Columns[0].Windows = append(w.Columns[0].Windows, 4)175 w.Columns[0].Windows = append(w.Columns[0].Windows, 4)
176 h := heights(w)176 h := heights(w)
177 if h[1] != h[3] {177 if h[1] != h[3] {
@@ -182,7 +182,7 @@ func TestSharesResetAndTravel(t *testing.T) {
182 func TestOverviewPreviewUsesShares(t *testing.T) {182 func TestOverviewPreviewUsesShares(t *testing.T) {
183 w := workspace()183 w := workspace()
184 w.SetOutput(1000, 800)184 w.SetOutput(1000, 800)
185- w.Columns = []Column{{Windows: []WindowID{1, 2}, Shares: []int{70, 30}}}185+ w.Columns = []Column{{Windows: []WindowID{1, 2}, Shares: []int{7000, 3000}}}
186 tiles, _, _ := w.previewTiles()186 tiles, _, _ := w.previewTiles()
187 h := map[WindowID]int{}187 h := map[WindowID]int{}
188 for _, p := range tiles {188 for _, p := range tiles {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close