detached 60af2d3

feat(layout): make cascade the default overflow (#148)

Default layout.overflow to cascade, highlight it in the README and
pin core tests written for the scroll layout to scroll explicitly.
commit
60af2d3699d55ffc3810048a6e978ffa0f70d236
author
bnema · · 2026-10-09 17:27 UTC
committer
GitHub · · 2026-10-09 17:27 UTC
parent
be3306f
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
22 files · +81 -76
M README.md +3 -2
@@ -72,7 +72,8 @@ A full build takes seconds, and tests, race detection, profiling and formatting
7272
7373 ### Windows and workspaces
7474
75-- **Column tiling:** scroll horizontally, as in PaperWM and niri, or keep columns on screen in a fixed layout. Choose per output or workspace.
75+- **Cascade tiling (default):** an original layout. Columns fill the screen side by side, then continue in a new band below; scroll bands vertically and switch workspaces sideways. See [Cascade](docs/config.md#cascade).
76+- **Other layouts:** scroll horizontally, as in PaperWM and niri, or keep columns on screen in a fixed spiral. Choose per output or workspace.
7677 - **Overview:** `cmd+o` or a four-finger swipe up shows live workspace previews. Select a window with the keyboard, touchpad or mouse.
7778 - **Stash:** `cmd+s` toggles a floating strip of windows set aside for the current workspace. `cmd+shift+s` moves a window into or out of it.
7879 - **Workspaces:** numbered workspaces appear as needed. Named workspaces can start commands and place their windows in predefined columns.
@@ -202,7 +203,7 @@ border.inactive = #111111
202203 floating.dim = 0.3
203204 layout.gaps = 0
204205 layout.max-columns = 2
205-layout.overflow = scroll
206+layout.overflow = cascade
206207 layout.presets = 1/3, 1/2, 2/3, 1
207208 stash.width = 80
208209 stash.gap = 2
@@ -72,7 +72,8 @@ A full build takes seconds, and tests, race detection, profiling and formatting
72 72
73 ### Windows and workspaces73 ### Windows and workspaces
74 74
75-- **Column tiling:** scroll horizontally, as in PaperWM and niri, or keep columns on screen in a fixed layout. Choose per output or workspace.75+- **Cascade tiling (default):** an original layout. Columns fill the screen side by side, then continue in a new band below; scroll bands vertically and switch workspaces sideways. See [Cascade](docs/config.md#cascade).
76+- **Other layouts:** scroll horizontally, as in PaperWM and niri, or keep columns on screen in a fixed spiral. Choose per output or workspace.
76 - **Overview:** `cmd+o` or a four-finger swipe up shows live workspace previews. Select a window with the keyboard, touchpad or mouse.77 - **Overview:** `cmd+o` or a four-finger swipe up shows live workspace previews. Select a window with the keyboard, touchpad or mouse.
77 - **Stash:** `cmd+s` toggles a floating strip of windows set aside for the current workspace. `cmd+shift+s` moves a window into or out of it.78 - **Stash:** `cmd+s` toggles a floating strip of windows set aside for the current workspace. `cmd+shift+s` moves a window into or out of it.
78 - **Workspaces:** numbered workspaces appear as needed. Named workspaces can start commands and place their windows in predefined columns.79 - **Workspaces:** numbered workspaces appear as needed. Named workspaces can start commands and place their windows in predefined columns.
@@ -202,7 +203,7 @@ border.inactive = #111111
202 floating.dim = 0.3203 floating.dim = 0.3
203 layout.gaps = 0204 layout.gaps = 0
204 layout.max-columns = 2205 layout.max-columns = 2
205-layout.overflow = scroll206+layout.overflow = cascade
206 layout.presets = 1/3, 1/2, 2/3, 1207 layout.presets = 1/3, 1/2, 2/3, 1
207 stash.width = 80208 stash.width = 80
208 stash.gap = 2209 stash.gap = 2
M docs/config.md +2 -2
@@ -30,7 +30,7 @@ NeferWL reads `$XDG_CONFIG_HOME/neferwl/config` (or `~/.config/neferwl/config`).
3030 | `border.active` / `border.inactive` | `#808080` / `#111111` | Colors for focused / other window lines; only the focused output lights up |
3131 | `layout.gaps` | `0` | Space between windows |
3232 | `layout.max-columns` | `2` | Columns that share the screen before scrolling |
33-| `layout.overflow` | `scroll` | Past the max: `scroll` right, `fixed` spiral split, or `cascade` into vertically scrolling bands |
33+| `layout.overflow` | `cascade` | Past the max: `cascade` into vertically scrolling bands, `scroll` right, or `fixed` spiral split |
3434 | `layout.<output>.max-columns`, `.overflow` | layout values | Per-screen layout: connector (`DP-2`) or monitor key |
3535 | `layout.presets` | `1/3, 1/2, 2/3, 1` | Widths for `cycle-column-width` (scroll overflow) |
3636 | `touchpad.natural-scroll` | `off` | Content follows the fingers, for two-finger scroll and three-finger swipes (see [Touchpad](#touchpad)) |
@@ -226,7 +226,7 @@ HDR requires DRM HDR connector properties, suitable KMS planes, and Vulkan fp16
226226
227227 ### Cascade
228228
229-Set `layout.overflow = cascade` globally, per output, or on a named workspace. Columns fill bands of up to `max-columns`, then continue in a fresh band below. The columns of a band share its width equally, as in `fixed` overflow: a lone column takes the full width until another joins its band. New columns append last; closing a column compacts the following bands. Each band fills the usable height after panel reservations.
229+Cascade is the default layout (`layout.overflow = cascade`); it can also be set per output or on a named workspace. Columns fill bands of up to `max-columns`, then continue in a fresh band below. The columns of a band share its width equally, as in `fixed` overflow: a lone column takes the full width until another joins its band. New columns append last; closing a column compacts the following bands. Each band fills the usable height after panel reservations.
230230
231231 Left/right focus follows column order across band boundaries. Up/down visits stacked windows first, then the closest column in the adjacent band, and stops at the first and last band. Column width presets and resizing do nothing; window height shares still apply. Maximizing a column hides its same-band siblings until focus moves or maximization ends.
232232
@@ -30,7 +30,7 @@ NeferWL reads `$XDG_CONFIG_HOME/neferwl/config` (or `~/.config/neferwl/config`).
30 | `border.active` / `border.inactive` | `#808080` / `#111111` | Colors for focused / other window lines; only the focused output lights up |30 | `border.active` / `border.inactive` | `#808080` / `#111111` | Colors for focused / other window lines; only the focused output lights up |
31 | `layout.gaps` | `0` | Space between windows |31 | `layout.gaps` | `0` | Space between windows |
32 | `layout.max-columns` | `2` | Columns that share the screen before scrolling |32 | `layout.max-columns` | `2` | Columns that share the screen before scrolling |
33-| `layout.overflow` | `scroll` | Past the max: `scroll` right, `fixed` spiral split, or `cascade` into vertically scrolling bands |33+| `layout.overflow` | `cascade` | Past the max: `cascade` into vertically scrolling bands, `scroll` right, or `fixed` spiral split |
34 | `layout.<output>.max-columns`, `.overflow` | layout values | Per-screen layout: connector (`DP-2`) or monitor key |34 | `layout.<output>.max-columns`, `.overflow` | layout values | Per-screen layout: connector (`DP-2`) or monitor key |
35 | `layout.presets` | `1/3, 1/2, 2/3, 1` | Widths for `cycle-column-width` (scroll overflow) |35 | `layout.presets` | `1/3, 1/2, 2/3, 1` | Widths for `cycle-column-width` (scroll overflow) |
36 | `touchpad.natural-scroll` | `off` | Content follows the fingers, for two-finger scroll and three-finger swipes (see [Touchpad](#touchpad)) |36 | `touchpad.natural-scroll` | `off` | Content follows the fingers, for two-finger scroll and three-finger swipes (see [Touchpad](#touchpad)) |
@@ -226,7 +226,7 @@ HDR requires DRM HDR connector properties, suitable KMS planes, and Vulkan fp16
226 226
227 ### Cascade227 ### Cascade
228 228
229-Set `layout.overflow = cascade` globally, per output, or on a named workspace. Columns fill bands of up to `max-columns`, then continue in a fresh band below. The columns of a band share its width equally, as in `fixed` overflow: a lone column takes the full width until another joins its band. New columns append last; closing a column compacts the following bands. Each band fills the usable height after panel reservations.229+Cascade is the default layout (`layout.overflow = cascade`); it can also be set per output or on a named workspace. Columns fill bands of up to `max-columns`, then continue in a fresh band below. The columns of a band share its width equally, as in `fixed` overflow: a lone column takes the full width until another joins its band. New columns append last; closing a column compacts the following bands. Each band fills the usable height after panel reservations.
230 230
231 Left/right focus follows column order across band boundaries. Up/down visits stacked windows first, then the closest column in the adjacent band, and stops at the first and last band. Column width presets and resizing do nothing; window height shares still apply. Maximizing a column hides its same-band siblings until focus moves or maximization ends.231 Left/right focus follows column order across band boundaries. Up/down visits stacked windows first, then the closest column in the adjacent band, and stops at the first and last band. Column width presets and resizing do nothing; window height shares still apply. Maximizing a column hides its same-band siblings until focus moves or maximization ends.
232 232
M examples/config +5 -5
@@ -37,13 +37,13 @@ border.inactive = #111111
3737 floating.dim = 0.3
3838
3939 layout.gaps = 0
40-# Columns sharing the screen equally before new ones scroll off to the right.
40+# Columns sharing the screen equally before the overflow applies.
4141 # A lone column fills the screen without separators.
4242 layout.max-columns = 2
43-# Past max-columns: scroll opens columns to the right; fixed keeps everything
44-# on screen, each new window splitting the newest one (spiral); cascade starts
45-# a new band of columns below, scrolling vertically.
46-layout.overflow = scroll
43+# Past max-columns: cascade starts a new band of columns below, scrolling
44+# vertically; scroll opens columns to the right; fixed keeps everything on
45+# screen, each new window splitting the newest one (spiral).
46+layout.overflow = cascade
4747 # cycle-column-width steps through these, then back to the equal share.
4848 layout.presets = 1/3, 1/2, 2/3, 1
4949
@@ -37,13 +37,13 @@ border.inactive = #111111
37 floating.dim = 0.337 floating.dim = 0.3
38 38
39 layout.gaps = 039 layout.gaps = 0
40-# Columns sharing the screen equally before new ones scroll off to the right.40+# Columns sharing the screen equally before the overflow applies.
41 # A lone column fills the screen without separators.41 # A lone column fills the screen without separators.
42 layout.max-columns = 242 layout.max-columns = 2
43-# Past max-columns: scroll opens columns to the right; fixed keeps everything43+# Past max-columns: cascade starts a new band of columns below, scrolling
44-# on screen, each new window splitting the newest one (spiral); cascade starts44+# vertically; scroll opens columns to the right; fixed keeps everything on
45-# a new band of columns below, scrolling vertically.45+# screen, each new window splitting the newest one (spiral).
46-layout.overflow = scroll46+layout.overflow = cascade
47 # cycle-column-width steps through these, then back to the equal share.47 # cycle-column-width steps through these, then back to the equal share.
48 layout.presets = 1/3, 1/2, 2/3, 148 layout.presets = 1/3, 1/2, 2/3, 1
49 49
M internal/adapters/config/config.go +1 -1
@@ -168,7 +168,7 @@ func Defaults() ports.Config {
168168 c.Border.Inactive = "#111111"
169169 c.Layout.MaxColumns = 2
170170 c.Layout.Presets = []string{"1/3", "1/2", "2/3", "1"}
171- c.Layout.Overflow = "scroll"
171+ c.Layout.Overflow = "cascade"
172172 c.Touchpad.Tap = true
173173 c.Touchpad.AccelProfile = ports.AccelAdaptive
174174 c.Touchpad.ScrollFactor = 1
@@ -168,7 +168,7 @@ func Defaults() ports.Config {
168 c.Border.Inactive = "#111111"168 c.Border.Inactive = "#111111"
169 c.Layout.MaxColumns = 2169 c.Layout.MaxColumns = 2
170 c.Layout.Presets = []string{"1/3", "1/2", "2/3", "1"}170 c.Layout.Presets = []string{"1/3", "1/2", "2/3", "1"}
171- c.Layout.Overflow = "scroll"171+ c.Layout.Overflow = "cascade"
172 c.Touchpad.Tap = true172 c.Touchpad.Tap = true
173 c.Touchpad.AccelProfile = ports.AccelAdaptive173 c.Touchpad.AccelProfile = ports.AccelAdaptive
174 c.Touchpad.ScrollFactor = 1174 c.Touchpad.ScrollFactor = 1
M internal/adapters/config/config_test.go +6 -4
@@ -508,18 +508,20 @@ layout.DP-2.color = red
508508 }
509509
510510 func TestFixedOutputWarnsSlotWidths(t *testing.T) {
511- _, w := parseString(t, `layout.DP-2.overflow = fixed
511+ _, w := parseString(t, `layout.overflow = scroll
512+layout.DP-2.overflow = fixed
512513 workspace.dev.monitor = DP-2
513514 workspace.dev.column.1 = 50%, foot
514515 workspace.web.column.1 = 50%, foot
515516 `)
516- if len(w) != 3 || w[1].Line != 3 {
517+ if len(w) != 3 || w[1].Line != 4 {
517518 t.Fatal(w)
518519 }
519520 }
520521
521522 func TestWorkspaceSlots(t *testing.T) {
522- c, w := parseString(t, `workspace.dev.monitor = DP-2
523+ c, w := parseString(t, `layout.overflow = scroll
524+workspace.dev.monitor = DP-2
523525 workspace.dev.column.2 = 33%, foot --title x
524526 workspace.dev.column.1 = 67%, code --new-window
525527 workspace.dev.column.2 = 1/3, kitty
@@ -539,7 +541,7 @@ bind.cmd+d = workspace dev
539541 for _, x := range w {
540542 lines = append(lines, x.Line)
541543 }
542- if !reflect.DeepEqual(lines, []int{4, 5, 6, 7}) {
544+ if !reflect.DeepEqual(lines, []int{5, 6, 7, 8}) {
543545 t.Fatal(w)
544546 }
545547 }
@@ -508,18 +508,20 @@ layout.DP-2.color = red
508 }508 }
509 509
510 func TestFixedOutputWarnsSlotWidths(t *testing.T) {510 func TestFixedOutputWarnsSlotWidths(t *testing.T) {
511- _, w := parseString(t, `layout.DP-2.overflow = fixed511+ _, w := parseString(t, `layout.overflow = scroll
512+layout.DP-2.overflow = fixed
512 workspace.dev.monitor = DP-2513 workspace.dev.monitor = DP-2
513 workspace.dev.column.1 = 50%, foot514 workspace.dev.column.1 = 50%, foot
514 workspace.web.column.1 = 50%, foot515 workspace.web.column.1 = 50%, foot
515 `)516 `)
516- if len(w) != 3 || w[1].Line != 3 {517+ if len(w) != 3 || w[1].Line != 4 {
517 t.Fatal(w)518 t.Fatal(w)
518 }519 }
519 }520 }
520 521
521 func TestWorkspaceSlots(t *testing.T) {522 func TestWorkspaceSlots(t *testing.T) {
522- c, w := parseString(t, `workspace.dev.monitor = DP-2523+ c, w := parseString(t, `layout.overflow = scroll
524+workspace.dev.monitor = DP-2
523 workspace.dev.column.2 = 33%, foot --title x525 workspace.dev.column.2 = 33%, foot --title x
524 workspace.dev.column.1 = 67%, code --new-window526 workspace.dev.column.1 = 67%, code --new-window
525 workspace.dev.column.2 = 1/3, kitty527 workspace.dev.column.2 = 1/3, kitty
@@ -539,7 +541,7 @@ bind.cmd+d = workspace dev
539 for _, x := range w {541 for _, x := range w {
540 lines = append(lines, x.Line)542 lines = append(lines, x.Line)
541 }543 }
542- if !reflect.DeepEqual(lines, []int{4, 5, 6, 7}) {544+ if !reflect.DeepEqual(lines, []int{5, 6, 7, 8}) {
543 t.Fatal(w)545 t.Fatal(w)
544 }546 }
545 }547 }
M internal/core/action_test.go +2 -3
@@ -5,7 +5,6 @@ import (
55 "strings"
66 "testing"
77
8- "github.com/bnema/neferwl/internal/adapters/config"
98 "github.com/bnema/neferwl/internal/core"
109 "github.com/bnema/neferwl/internal/ports"
1110 )
@@ -13,7 +12,7 @@ import (
1312 // altCmdDefaults is the default config with keyboard.cmd = alt. The loader
1413 // drops binds that use both cmd and alt then; so does this helper.
1514 func altCmdDefaults() ports.Config {
16- cfg := config.Defaults()
15+ cfg := scrollDefaults()
1716 cfg.Keyboard.CmdKey = "alt"
1817 for combo := range cfg.Binds {
1918 parts := strings.Split(combo, "+")
@@ -27,7 +26,7 @@ func altCmdDefaults() ports.Config {
2726
2827 func TestDefaultBindsAccepted(t *testing.T) {
2928 for _, cmd := range []string{"super", "alt"} {
30- cfg := config.Defaults()
29+ cfg := scrollDefaults()
3130 if cmd == "alt" {
3231 cfg = altCmdDefaults()
3332 }
@@ -5,7 +5,6 @@ import (
5 "strings"5 "strings"
6 "testing"6 "testing"
7 7
8- "github.com/bnema/neferwl/internal/adapters/config"
9 "github.com/bnema/neferwl/internal/core"8 "github.com/bnema/neferwl/internal/core"
10 "github.com/bnema/neferwl/internal/ports"9 "github.com/bnema/neferwl/internal/ports"
11 )10 )
@@ -13,7 +12,7 @@ import (
13 // altCmdDefaults is the default config with keyboard.cmd = alt. The loader12 // altCmdDefaults is the default config with keyboard.cmd = alt. The loader
14 // drops binds that use both cmd and alt then; so does this helper.13 // drops binds that use both cmd and alt then; so does this helper.
15 func altCmdDefaults() ports.Config {14 func altCmdDefaults() ports.Config {
16- cfg := config.Defaults()15+ cfg := scrollDefaults()
17 cfg.Keyboard.CmdKey = "alt"16 cfg.Keyboard.CmdKey = "alt"
18 for combo := range cfg.Binds {17 for combo := range cfg.Binds {
19 parts := strings.Split(combo, "+")18 parts := strings.Split(combo, "+")
@@ -27,7 +26,7 @@ func altCmdDefaults() ports.Config {
27 26
28 func TestDefaultBindsAccepted(t *testing.T) {27 func TestDefaultBindsAccepted(t *testing.T) {
29 for _, cmd := range []string{"super", "alt"} {28 for _, cmd := range []string{"super", "alt"} {
30- cfg := config.Defaults()29+ cfg := scrollDefaults()
31 if cmd == "alt" {30 if cmd == "alt" {
32 cfg = altCmdDefaults()31 cfg = altCmdDefaults()
33 }32 }
M internal/core/capture_test.go +2 -3
@@ -7,7 +7,6 @@ import (
77 "testing"
88 "time"
99
10- "github.com/bnema/neferwl/internal/adapters/config"
1110 "github.com/bnema/neferwl/internal/core"
1211 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
1312 "github.com/bnema/neferwl/internal/ports"
@@ -24,7 +23,7 @@ type captureRig struct {
2423 // captureCore runs a core with one 100x80 output OUT-1.
2524 func captureCore(t *testing.T) *captureRig {
2625 t.Helper()
27- cfg := config.Defaults()
26+ cfg := scrollDefaults()
2827 cfg.Border.Width = 0
2928 r := &captureRig{
3029 client: make(chan ports.ClientEvent, 16),
@@ -501,7 +500,7 @@ func TestCaptureExclusionUnmapThenDetach(t *testing.T) {
501500 // A captured frame shows the indicator for one second on the clock core
502501 // reads, then the scene goes back without it, whoever asked for the frame.
503502 func TestCaptureFrameFlashRunsOnTheCoreClock(t *testing.T) {
504- cfg := config.Defaults()
503+ cfg := scrollDefaults()
505504 cfg.Border.Width = 0
506505 fire := make(chan time.Time)
507506 timer := portsmocks.NewMockTimer(t)
@@ -7,7 +7,6 @@ import (
7 "testing"7 "testing"
8 "time"8 "time"
9 9
10- "github.com/bnema/neferwl/internal/adapters/config"
11 "github.com/bnema/neferwl/internal/core"10 "github.com/bnema/neferwl/internal/core"
12 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"11 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
13 "github.com/bnema/neferwl/internal/ports"12 "github.com/bnema/neferwl/internal/ports"
@@ -24,7 +23,7 @@ type captureRig struct {
24 // captureCore runs a core with one 100x80 output OUT-1.23 // captureCore runs a core with one 100x80 output OUT-1.
25 func captureCore(t *testing.T) *captureRig {24 func captureCore(t *testing.T) *captureRig {
26 t.Helper()25 t.Helper()
27- cfg := config.Defaults()26+ cfg := scrollDefaults()
28 cfg.Border.Width = 027 cfg.Border.Width = 0
29 r := &captureRig{28 r := &captureRig{
30 client: make(chan ports.ClientEvent, 16),29 client: make(chan ports.ClientEvent, 16),
@@ -501,7 +500,7 @@ func TestCaptureExclusionUnmapThenDetach(t *testing.T) {
501 // A captured frame shows the indicator for one second on the clock core500 // A captured frame shows the indicator for one second on the clock core
502 // reads, then the scene goes back without it, whoever asked for the frame.501 // reads, then the scene goes back without it, whoever asked for the frame.
503 func TestCaptureFrameFlashRunsOnTheCoreClock(t *testing.T) {502 func TestCaptureFrameFlashRunsOnTheCoreClock(t *testing.T) {
504- cfg := config.Defaults()503+ cfg := scrollDefaults()
505 cfg.Border.Width = 0504 cfg.Border.Width = 0
506 fire := make(chan time.Time)505 fire := make(chan time.Time)
507 timer := portsmocks.NewMockTimer(t)506 timer := portsmocks.NewMockTimer(t)
M internal/core/clientgesture_test.go +1 -2
@@ -6,7 +6,6 @@ import (
66 "testing"
77 "time"
88
9- "github.com/bnema/neferwl/internal/adapters/config"
109 "github.com/bnema/neferwl/internal/core"
1110 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
1211 "github.com/bnema/neferwl/internal/ports"
@@ -222,7 +221,7 @@ func TestGesturesRefusedWhileLocked(t *testing.T) {
222221 output: make(chan ports.OutputEvent, 4), reload: make(chan ports.ConfigChanged, 4),
223222 commands: make(chan ports.ClientCommand, 256), scenes: make(chan []ports.Scene, 1),
224223 }
225- cfg := config.Defaults()
224+ cfg := scrollDefaults()
226225 c, err := core.New(cfg, core.Channels{Client: r.client, Input: r.input, Output: r.output, Config: r.reload, Commands: r.commands, Scenes: r.scenes, Constraints: make(chan ports.PointerConstraint, 1)}, core.Options{Security: gate})
227226 if err != nil {
228227 t.Fatal(err)
@@ -6,7 +6,6 @@ import (
6 "testing"6 "testing"
7 "time"7 "time"
8 8
9- "github.com/bnema/neferwl/internal/adapters/config"
10 "github.com/bnema/neferwl/internal/core"9 "github.com/bnema/neferwl/internal/core"
11 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"10 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
12 "github.com/bnema/neferwl/internal/ports"11 "github.com/bnema/neferwl/internal/ports"
@@ -222,7 +221,7 @@ func TestGesturesRefusedWhileLocked(t *testing.T) {
222 output: make(chan ports.OutputEvent, 4), reload: make(chan ports.ConfigChanged, 4),221 output: make(chan ports.OutputEvent, 4), reload: make(chan ports.ConfigChanged, 4),
223 commands: make(chan ports.ClientCommand, 256), scenes: make(chan []ports.Scene, 1),222 commands: make(chan ports.ClientCommand, 256), scenes: make(chan []ports.Scene, 1),
224 }223 }
225- cfg := config.Defaults()224+ cfg := scrollDefaults()
226 c, err := core.New(cfg, core.Channels{Client: r.client, Input: r.input, Output: r.output, Config: r.reload, Commands: r.commands, Scenes: r.scenes, Constraints: make(chan ports.PointerConstraint, 1)}, core.Options{Security: gate})225 c, err := core.New(cfg, core.Channels{Client: r.client, Input: r.input, Output: r.output, Config: r.reload, Commands: r.commands, Scenes: r.scenes, Constraints: make(chan ports.PointerConstraint, 1)}, core.Options{Security: gate})
227 if err != nil {226 if err != nil {
228 t.Fatal(err)227 t.Fatal(err)
M internal/core/core_test.go +18 -19
@@ -9,7 +9,6 @@ import (
99 "testing/synctest"
1010 "time"
1111
12- "github.com/bnema/neferwl/internal/adapters/config"
1312 "github.com/bnema/neferwl/internal/core"
1413 "github.com/bnema/neferwl/internal/ports"
1514 )
@@ -187,7 +186,7 @@ func owner(t *testing.T, animated bool) {
187186 }
188187 }
189188 func TestCancelAndSceneCapacity(t *testing.T) {
190- cfg := config.Defaults()
189+ cfg := scrollDefaults()
191190 if _, err := core.New(cfg, core.Channels{Scenes: make(chan []ports.Scene, 2)}, core.Options{}); err == nil {
192191 t.Fatal("capacity")
193192 }
@@ -205,7 +204,7 @@ func TestCancelAndSceneCapacity(t *testing.T) {
205204 }
206205
207206 func TestKeyPressState(t *testing.T) {
208- cfg := config.Defaults()
207+ cfg := scrollDefaults()
209208 client := make(chan ports.ClientEvent, 8)
210209 input := make(chan ports.InputEvent, 8)
211210 commands := make(chan ports.ClientCommand, 16)
@@ -245,7 +244,7 @@ func TestKeyPressState(t *testing.T) {
245244 }
246245
247246 func TestBoundReleaseSwallowed(t *testing.T) {
248- cfg := config.Defaults()
247+ cfg := scrollDefaults()
249248 input := make(chan ports.InputEvent, 4)
250249 client := make(chan ports.ClientEvent, 2)
251250 commands := make(chan ports.ClientCommand, 8)
@@ -272,7 +271,7 @@ func TestBoundReleaseSwallowed(t *testing.T) {
272271 }
273272
274273 func TestPointerFocusAndGrab(t *testing.T) {
275- cfg := config.Defaults()
274+ cfg := scrollDefaults()
276275 cfg.Border.Width = 0
277276 cfg.Layout.Gaps = 8
278277 client := make(chan ports.ClientEvent, 8)
@@ -337,7 +336,7 @@ func TestPointerFocusAndGrab(t *testing.T) {
337336 func TestBorderInset(t *testing.T) { both(t, borderInset) }
338337
339338 func borderInset(t *testing.T, animated bool) {
340- cfg := config.Defaults()
339+ cfg := scrollDefaults()
341340 cfg.Border.Width = 2
342341 client := make(chan ports.ClientEvent, 8)
343342 input := make(chan ports.InputEvent, 8)
@@ -387,7 +386,7 @@ func borderInset(t *testing.T, animated bool) {
387386 }
388387
389388 func TestLayerKeyboardFocus(t *testing.T) {
390- cfg := config.Defaults()
389+ cfg := scrollDefaults()
391390 client := make(chan ports.ClientEvent, 8)
392391 input := make(chan ports.InputEvent, 8)
393392 output := make(chan ports.OutputEvent, 8)
@@ -464,7 +463,7 @@ func settledScene(t *testing.T, sc *stepClock, frames chan ports.OutputFrame, sc
464463 }
465464
466465 func workspaceSwitch(t *testing.T, animated bool) {
467- cfg := config.Defaults()
466+ cfg := scrollDefaults()
468467 cfg.Animations.On = animated
469468 client := make(chan ports.ClientEvent, 8)
470469 input := make(chan ports.InputEvent, 8)
@@ -529,7 +528,7 @@ func TestClickAfterWorkspaceSwitchAnimated(t *testing.T) {
529528 }
530529
531530 func clickAfterWorkspaceSwitch(t *testing.T, animated bool) {
532- cfg := config.Defaults()
531+ cfg := scrollDefaults()
533532 cfg.Animations.On = animated
534533 client := make(chan ports.ClientEvent, 8)
535534 input := make(chan ports.InputEvent, 8)
@@ -594,7 +593,7 @@ func clickAfterWorkspaceSwitch(t *testing.T, animated bool) {
594593 }
595594
596595 func TestShiftReleasedFirst(t *testing.T) {
597- cfg := config.Defaults()
596+ cfg := scrollDefaults()
598597 input := make(chan ports.InputEvent, 8)
599598 client := make(chan ports.ClientEvent, 2)
600599 commands := make(chan ports.ClientCommand, 16)
@@ -625,7 +624,7 @@ func TestShiftReleasedFirst(t *testing.T) {
625624 func TestOutputScale(t *testing.T) { both(t, outputScale) }
626625
627626 func outputScale(t *testing.T, animated bool) {
628- cfg := config.Defaults()
627+ cfg := scrollDefaults()
629628 cfg.Border.Width = 0
630629 cfg.Outputs = []ports.OutputConfig{{Name: "DP-2", Scale: 2}}
631630 cfg.Binds["Cmd+equal"] = "scale-up"
@@ -707,7 +706,7 @@ func outputScale(t *testing.T, animated bool) {
707706 // A pointer warp moves the cursor on the window under it: input takes the
708707 // position and the window gets the motion. Other windows cannot warp.
709708 func TestPointerWarp(t *testing.T) {
710- cfg := config.Defaults()
709+ cfg := scrollDefaults()
711710 cfg.Border.Width = 0
712711 cfg.Layout.Gaps = 0
713712 client := make(chan ports.ClientEvent, 8)
@@ -759,7 +758,7 @@ func TestPointerWarp(t *testing.T) {
759758 func TestPointerConstraint(t *testing.T) { both(t, pointerConstraint) }
760759
761760 func pointerConstraint(t *testing.T, animated bool) {
762- cfg := config.Defaults()
761+ cfg := scrollDefaults()
763762 cfg.Border.Width = 0
764763 cfg.Layout.Gaps = 0
765764 client := make(chan ports.ClientEvent, 8)
@@ -834,7 +833,7 @@ func pointerConstraint(t *testing.T, animated bool) {
834833
835834 // A click outside a grabbing menu chain closes it, the topmost popup first.
836835 func TestPopupChainDismissOrder(t *testing.T) {
837- cfg := config.Defaults()
836+ cfg := scrollDefaults()
838837 cfg.Border.Width = 0
839838 client := make(chan ports.ClientEvent, 8)
840839 input := make(chan ports.InputEvent, 8)
@@ -883,7 +882,7 @@ func TestPopupChainDismissOrder(t *testing.T) {
883882 // A dialog opened under a fullscreen window stays hidden and out of reach
884883 // of the pointer until the window leaves fullscreen.
885884 func TestFloatOverFullscreenHit(t *testing.T) {
886- cfg := config.Defaults()
885+ cfg := scrollDefaults()
887886 cfg.Border.Width = 0
888887 client := make(chan ports.ClientEvent, 8)
889888 input := make(chan ports.InputEvent, 8)
@@ -926,7 +925,7 @@ func TestFloatOverFullscreenHit(t *testing.T) {
926925 // A dialog of the fullscreen window (a portal dialog parented through
927926 // xdg-foreign) shows over it and takes the pointer at once.
928927 func TestDialogOverFullscreenHit(t *testing.T) {
929- cfg := config.Defaults()
928+ cfg := scrollDefaults()
930929 cfg.Border.Width = 0
931930 client := make(chan ports.ClientEvent, 8)
932931 input := make(chan ports.InputEvent, 8)
@@ -972,7 +971,7 @@ func TestDialogOverFullscreenHit(t *testing.T) {
972971 func TestFullscreenAtMapIgnored(t *testing.T) { both(t, fullscreenAtMapIgnored) }
973972
974973 func fullscreenAtMapIgnored(t *testing.T, animated bool) {
975- cfg := config.Defaults()
974+ cfg := scrollDefaults()
976975 cfg.Layout.Overflow = "fixed"
977976 cfg.Focus.Animation = ports.FocusAnimationOff
978977 client := make(chan ports.ClientEvent, 8)
@@ -1063,7 +1062,7 @@ func TestFixedFullscreenArrivalWaits(t *testing.T) {
10631062 func TestExternalFullscreenAtMapApplies(t *testing.T) { both(t, externalFullscreenAtMapApplies) }
10641063
10651064 func externalFullscreenAtMapApplies(t *testing.T, animated bool) {
1066- cfg := config.Defaults()
1065+ cfg := scrollDefaults()
10671066 cfg.Focus.Animation = ports.FocusAnimationOff
10681067 client := make(chan ports.ClientEvent, 8)
10691068 output := make(chan ports.OutputEvent, 8)
@@ -1204,7 +1203,7 @@ func TestRefusedFullscreenConfigured(t *testing.T) {
12041203
12051204 // An activated window on another workspace comes on screen with focus.
12061205 func TestWindowActivateShowsAndFocuses(t *testing.T) {
1207- cfg := config.Defaults()
1206+ cfg := scrollDefaults()
12081207 client := make(chan ports.ClientEvent, 8)
12091208 input := make(chan ports.InputEvent, 8)
12101209 output := make(chan ports.OutputEvent, 8)
@@ -9,7 +9,6 @@ import (
9 "testing/synctest"9 "testing/synctest"
10 "time"10 "time"
11 11
12- "github.com/bnema/neferwl/internal/adapters/config"
13 "github.com/bnema/neferwl/internal/core"12 "github.com/bnema/neferwl/internal/core"
14 "github.com/bnema/neferwl/internal/ports"13 "github.com/bnema/neferwl/internal/ports"
15 )14 )
@@ -187,7 +186,7 @@ func owner(t *testing.T, animated bool) {
187 }186 }
188 }187 }
189 func TestCancelAndSceneCapacity(t *testing.T) {188 func TestCancelAndSceneCapacity(t *testing.T) {
190- cfg := config.Defaults()189+ cfg := scrollDefaults()
191 if _, err := core.New(cfg, core.Channels{Scenes: make(chan []ports.Scene, 2)}, core.Options{}); err == nil {190 if _, err := core.New(cfg, core.Channels{Scenes: make(chan []ports.Scene, 2)}, core.Options{}); err == nil {
192 t.Fatal("capacity")191 t.Fatal("capacity")
193 }192 }
@@ -205,7 +204,7 @@ func TestCancelAndSceneCapacity(t *testing.T) {
205 }204 }
206 205
207 func TestKeyPressState(t *testing.T) {206 func TestKeyPressState(t *testing.T) {
208- cfg := config.Defaults()207+ cfg := scrollDefaults()
209 client := make(chan ports.ClientEvent, 8)208 client := make(chan ports.ClientEvent, 8)
210 input := make(chan ports.InputEvent, 8)209 input := make(chan ports.InputEvent, 8)
211 commands := make(chan ports.ClientCommand, 16)210 commands := make(chan ports.ClientCommand, 16)
@@ -245,7 +244,7 @@ func TestKeyPressState(t *testing.T) {
245 }244 }
246 245
247 func TestBoundReleaseSwallowed(t *testing.T) {246 func TestBoundReleaseSwallowed(t *testing.T) {
248- cfg := config.Defaults()247+ cfg := scrollDefaults()
249 input := make(chan ports.InputEvent, 4)248 input := make(chan ports.InputEvent, 4)
250 client := make(chan ports.ClientEvent, 2)249 client := make(chan ports.ClientEvent, 2)
251 commands := make(chan ports.ClientCommand, 8)250 commands := make(chan ports.ClientCommand, 8)
@@ -272,7 +271,7 @@ func TestBoundReleaseSwallowed(t *testing.T) {
272 }271 }
273 272
274 func TestPointerFocusAndGrab(t *testing.T) {273 func TestPointerFocusAndGrab(t *testing.T) {
275- cfg := config.Defaults()274+ cfg := scrollDefaults()
276 cfg.Border.Width = 0275 cfg.Border.Width = 0
277 cfg.Layout.Gaps = 8276 cfg.Layout.Gaps = 8
278 client := make(chan ports.ClientEvent, 8)277 client := make(chan ports.ClientEvent, 8)
@@ -337,7 +336,7 @@ func TestPointerFocusAndGrab(t *testing.T) {
337 func TestBorderInset(t *testing.T) { both(t, borderInset) }336 func TestBorderInset(t *testing.T) { both(t, borderInset) }
338 337
339 func borderInset(t *testing.T, animated bool) {338 func borderInset(t *testing.T, animated bool) {
340- cfg := config.Defaults()339+ cfg := scrollDefaults()
341 cfg.Border.Width = 2340 cfg.Border.Width = 2
342 client := make(chan ports.ClientEvent, 8)341 client := make(chan ports.ClientEvent, 8)
343 input := make(chan ports.InputEvent, 8)342 input := make(chan ports.InputEvent, 8)
@@ -387,7 +386,7 @@ func borderInset(t *testing.T, animated bool) {
387 }386 }
388 387
389 func TestLayerKeyboardFocus(t *testing.T) {388 func TestLayerKeyboardFocus(t *testing.T) {
390- cfg := config.Defaults()389+ cfg := scrollDefaults()
391 client := make(chan ports.ClientEvent, 8)390 client := make(chan ports.ClientEvent, 8)
392 input := make(chan ports.InputEvent, 8)391 input := make(chan ports.InputEvent, 8)
393 output := make(chan ports.OutputEvent, 8)392 output := make(chan ports.OutputEvent, 8)
@@ -464,7 +463,7 @@ func settledScene(t *testing.T, sc *stepClock, frames chan ports.OutputFrame, sc
464 }463 }
465 464
466 func workspaceSwitch(t *testing.T, animated bool) {465 func workspaceSwitch(t *testing.T, animated bool) {
467- cfg := config.Defaults()466+ cfg := scrollDefaults()
468 cfg.Animations.On = animated467 cfg.Animations.On = animated
469 client := make(chan ports.ClientEvent, 8)468 client := make(chan ports.ClientEvent, 8)
470 input := make(chan ports.InputEvent, 8)469 input := make(chan ports.InputEvent, 8)
@@ -529,7 +528,7 @@ func TestClickAfterWorkspaceSwitchAnimated(t *testing.T) {
529 }528 }
530 529
531 func clickAfterWorkspaceSwitch(t *testing.T, animated bool) {530 func clickAfterWorkspaceSwitch(t *testing.T, animated bool) {
532- cfg := config.Defaults()531+ cfg := scrollDefaults()
533 cfg.Animations.On = animated532 cfg.Animations.On = animated
534 client := make(chan ports.ClientEvent, 8)533 client := make(chan ports.ClientEvent, 8)
535 input := make(chan ports.InputEvent, 8)534 input := make(chan ports.InputEvent, 8)
@@ -594,7 +593,7 @@ func clickAfterWorkspaceSwitch(t *testing.T, animated bool) {
594 }593 }
595 594
596 func TestShiftReleasedFirst(t *testing.T) {595 func TestShiftReleasedFirst(t *testing.T) {
597- cfg := config.Defaults()596+ cfg := scrollDefaults()
598 input := make(chan ports.InputEvent, 8)597 input := make(chan ports.InputEvent, 8)
599 client := make(chan ports.ClientEvent, 2)598 client := make(chan ports.ClientEvent, 2)
600 commands := make(chan ports.ClientCommand, 16)599 commands := make(chan ports.ClientCommand, 16)
@@ -625,7 +624,7 @@ func TestShiftReleasedFirst(t *testing.T) {
625 func TestOutputScale(t *testing.T) { both(t, outputScale) }624 func TestOutputScale(t *testing.T) { both(t, outputScale) }
626 625
627 func outputScale(t *testing.T, animated bool) {626 func outputScale(t *testing.T, animated bool) {
628- cfg := config.Defaults()627+ cfg := scrollDefaults()
629 cfg.Border.Width = 0628 cfg.Border.Width = 0
630 cfg.Outputs = []ports.OutputConfig{{Name: "DP-2", Scale: 2}}629 cfg.Outputs = []ports.OutputConfig{{Name: "DP-2", Scale: 2}}
631 cfg.Binds["Cmd+equal"] = "scale-up"630 cfg.Binds["Cmd+equal"] = "scale-up"
@@ -707,7 +706,7 @@ func outputScale(t *testing.T, animated bool) {
707 // A pointer warp moves the cursor on the window under it: input takes the706 // A pointer warp moves the cursor on the window under it: input takes the
708 // position and the window gets the motion. Other windows cannot warp.707 // position and the window gets the motion. Other windows cannot warp.
709 func TestPointerWarp(t *testing.T) {708 func TestPointerWarp(t *testing.T) {
710- cfg := config.Defaults()709+ cfg := scrollDefaults()
711 cfg.Border.Width = 0710 cfg.Border.Width = 0
712 cfg.Layout.Gaps = 0711 cfg.Layout.Gaps = 0
713 client := make(chan ports.ClientEvent, 8)712 client := make(chan ports.ClientEvent, 8)
@@ -759,7 +758,7 @@ func TestPointerWarp(t *testing.T) {
759 func TestPointerConstraint(t *testing.T) { both(t, pointerConstraint) }758 func TestPointerConstraint(t *testing.T) { both(t, pointerConstraint) }
760 759
761 func pointerConstraint(t *testing.T, animated bool) {760 func pointerConstraint(t *testing.T, animated bool) {
762- cfg := config.Defaults()761+ cfg := scrollDefaults()
763 cfg.Border.Width = 0762 cfg.Border.Width = 0
764 cfg.Layout.Gaps = 0763 cfg.Layout.Gaps = 0
765 client := make(chan ports.ClientEvent, 8)764 client := make(chan ports.ClientEvent, 8)
@@ -834,7 +833,7 @@ func pointerConstraint(t *testing.T, animated bool) {
834 833
835 // A click outside a grabbing menu chain closes it, the topmost popup first.834 // A click outside a grabbing menu chain closes it, the topmost popup first.
836 func TestPopupChainDismissOrder(t *testing.T) {835 func TestPopupChainDismissOrder(t *testing.T) {
837- cfg := config.Defaults()836+ cfg := scrollDefaults()
838 cfg.Border.Width = 0837 cfg.Border.Width = 0
839 client := make(chan ports.ClientEvent, 8)838 client := make(chan ports.ClientEvent, 8)
840 input := make(chan ports.InputEvent, 8)839 input := make(chan ports.InputEvent, 8)
@@ -883,7 +882,7 @@ func TestPopupChainDismissOrder(t *testing.T) {
883 // A dialog opened under a fullscreen window stays hidden and out of reach882 // A dialog opened under a fullscreen window stays hidden and out of reach
884 // of the pointer until the window leaves fullscreen.883 // of the pointer until the window leaves fullscreen.
885 func TestFloatOverFullscreenHit(t *testing.T) {884 func TestFloatOverFullscreenHit(t *testing.T) {
886- cfg := config.Defaults()885+ cfg := scrollDefaults()
887 cfg.Border.Width = 0886 cfg.Border.Width = 0
888 client := make(chan ports.ClientEvent, 8)887 client := make(chan ports.ClientEvent, 8)
889 input := make(chan ports.InputEvent, 8)888 input := make(chan ports.InputEvent, 8)
@@ -926,7 +925,7 @@ func TestFloatOverFullscreenHit(t *testing.T) {
926 // A dialog of the fullscreen window (a portal dialog parented through925 // A dialog of the fullscreen window (a portal dialog parented through
927 // xdg-foreign) shows over it and takes the pointer at once.926 // xdg-foreign) shows over it and takes the pointer at once.
928 func TestDialogOverFullscreenHit(t *testing.T) {927 func TestDialogOverFullscreenHit(t *testing.T) {
929- cfg := config.Defaults()928+ cfg := scrollDefaults()
930 cfg.Border.Width = 0929 cfg.Border.Width = 0
931 client := make(chan ports.ClientEvent, 8)930 client := make(chan ports.ClientEvent, 8)
932 input := make(chan ports.InputEvent, 8)931 input := make(chan ports.InputEvent, 8)
@@ -972,7 +971,7 @@ func TestDialogOverFullscreenHit(t *testing.T) {
972 func TestFullscreenAtMapIgnored(t *testing.T) { both(t, fullscreenAtMapIgnored) }971 func TestFullscreenAtMapIgnored(t *testing.T) { both(t, fullscreenAtMapIgnored) }
973 972
974 func fullscreenAtMapIgnored(t *testing.T, animated bool) {973 func fullscreenAtMapIgnored(t *testing.T, animated bool) {
975- cfg := config.Defaults()974+ cfg := scrollDefaults()
976 cfg.Layout.Overflow = "fixed"975 cfg.Layout.Overflow = "fixed"
977 cfg.Focus.Animation = ports.FocusAnimationOff976 cfg.Focus.Animation = ports.FocusAnimationOff
978 client := make(chan ports.ClientEvent, 8)977 client := make(chan ports.ClientEvent, 8)
@@ -1063,7 +1062,7 @@ func TestFixedFullscreenArrivalWaits(t *testing.T) {
1063 func TestExternalFullscreenAtMapApplies(t *testing.T) { both(t, externalFullscreenAtMapApplies) }1062 func TestExternalFullscreenAtMapApplies(t *testing.T) { both(t, externalFullscreenAtMapApplies) }
1064 1063
1065 func externalFullscreenAtMapApplies(t *testing.T, animated bool) {1064 func externalFullscreenAtMapApplies(t *testing.T, animated bool) {
1066- cfg := config.Defaults()1065+ cfg := scrollDefaults()
1067 cfg.Focus.Animation = ports.FocusAnimationOff1066 cfg.Focus.Animation = ports.FocusAnimationOff
1068 client := make(chan ports.ClientEvent, 8)1067 client := make(chan ports.ClientEvent, 8)
1069 output := make(chan ports.OutputEvent, 8)1068 output := make(chan ports.OutputEvent, 8)
@@ -1204,7 +1203,7 @@ func TestRefusedFullscreenConfigured(t *testing.T) {
1204 1203
1205 // An activated window on another workspace comes on screen with focus.1204 // An activated window on another workspace comes on screen with focus.
1206 func TestWindowActivateShowsAndFocuses(t *testing.T) {1205 func TestWindowActivateShowsAndFocuses(t *testing.T) {
1207- cfg := config.Defaults()1206+ cfg := scrollDefaults()
1208 client := make(chan ports.ClientEvent, 8)1207 client := make(chan ports.ClientEvent, 8)
1209 input := make(chan ports.InputEvent, 8)1208 input := make(chan ports.InputEvent, 8)
1210 output := make(chan ports.OutputEvent, 8)1209 output := make(chan ports.OutputEvent, 8)
A internal/core/defaults_test.go +14 -0
@@ -0,0 +1,14 @@
1+package core_test
2+
3+import (
4+ "github.com/bnema/neferwl/internal/adapters/config"
5+ "github.com/bnema/neferwl/internal/ports"
6+)
7+
8+// scrollDefaults is the default config with scroll overflow, the layout most
9+// core tests were written against. Cascade has its own tests.
10+func scrollDefaults() ports.Config {
11+ cfg := config.Defaults()
12+ cfg.Layout.Overflow = "scroll"
13+ return cfg
14+}
@@ -0,0 +1,14 @@
1+package core_test
2+
3+import (
4+ "github.com/bnema/neferwl/internal/adapters/config"
5+ "github.com/bnema/neferwl/internal/ports"
6+)
7+
8+// scrollDefaults is the default config with scroll overflow, the layout most
9+// core tests were written against. Cascade has its own tests.
10+func scrollDefaults() ports.Config {
11+ cfg := config.Defaults()
12+ cfg.Layout.Overflow = "scroll"
13+ return cfg
14+}
M internal/core/drag_test.go +1 -2
@@ -7,7 +7,6 @@ import (
77 "testing"
88 "time"
99
10- "github.com/bnema/neferwl/internal/adapters/config"
1110 "github.com/bnema/neferwl/internal/core"
1211 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
1312 "github.com/bnema/neferwl/internal/ports"
@@ -53,7 +52,7 @@ func startAnimatedDragRig(t *testing.T, edit func(*ports.Config), outs ...ports.
5352
5453 func newDragRig(t *testing.T, animated bool, edit func(*ports.Config), outs ...ports.OutputInfo) *dragRig {
5554 t.Helper()
56- cfg := config.Defaults()
55+ cfg := scrollDefaults()
5756 cfg.Border.Width = 0
5857 cfg.Layout.Gaps = 10
5958 cfg.Terminal.AutoOpen = "off"
@@ -7,7 +7,6 @@ import (
7 "testing"7 "testing"
8 "time"8 "time"
9 9
10- "github.com/bnema/neferwl/internal/adapters/config"
11 "github.com/bnema/neferwl/internal/core"10 "github.com/bnema/neferwl/internal/core"
12 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"11 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
13 "github.com/bnema/neferwl/internal/ports"12 "github.com/bnema/neferwl/internal/ports"
@@ -53,7 +52,7 @@ func startAnimatedDragRig(t *testing.T, edit func(*ports.Config), outs ...ports.
53 52
54 func newDragRig(t *testing.T, animated bool, edit func(*ports.Config), outs ...ports.OutputInfo) *dragRig {53 func newDragRig(t *testing.T, animated bool, edit func(*ports.Config), outs ...ports.OutputInfo) *dragRig {
55 t.Helper()54 t.Helper()
56- cfg := config.Defaults()55+ cfg := scrollDefaults()
57 cfg.Border.Width = 056 cfg.Border.Width = 0
58 cfg.Layout.Gaps = 1057 cfg.Layout.Gaps = 10
59 cfg.Terminal.AutoOpen = "off"58 cfg.Terminal.AutoOpen = "off"
M internal/core/inhibit_test.go +2 -3
@@ -6,7 +6,6 @@ import (
66 "testing/synctest"
77 "time"
88
9- "github.com/bnema/neferwl/internal/adapters/config"
109 "github.com/bnema/neferwl/internal/core"
1110 "github.com/bnema/neferwl/internal/ports"
1211 )
@@ -34,7 +33,7 @@ func commandOf[T ports.ClientCommand](t *testing.T, commands <-chan ports.Client
3433 func TestShortcutsInhibit(t *testing.T) { synctest.Test(t, shortcutsInhibit) }
3534
3635 func shortcutsInhibit(t *testing.T) {
37- cfg := config.Defaults()
36+ cfg := scrollDefaults()
3837 input := make(chan ports.InputEvent, 4)
3938 client := make(chan ports.ClientEvent, 4)
4039 commands := make(chan ports.ClientCommand, 64)
@@ -96,7 +95,7 @@ func shortcutsInhibit(t *testing.T) {
9695 // A key whose press ran a bind before inhibiting began is not released to
9796 // the window: it never saw the press.
9897 func TestShortcutsInhibitDropsBoundRelease(t *testing.T) {
99- cfg := config.Defaults()
98+ cfg := scrollDefaults()
10099 input := make(chan ports.InputEvent, 4)
101100 client := make(chan ports.ClientEvent, 4)
102101 commands := make(chan ports.ClientCommand, 64)
@@ -6,7 +6,6 @@ import (
6 "testing/synctest"6 "testing/synctest"
7 "time"7 "time"
8 8
9- "github.com/bnema/neferwl/internal/adapters/config"
10 "github.com/bnema/neferwl/internal/core"9 "github.com/bnema/neferwl/internal/core"
11 "github.com/bnema/neferwl/internal/ports"10 "github.com/bnema/neferwl/internal/ports"
12 )11 )
@@ -34,7 +33,7 @@ func commandOf[T ports.ClientCommand](t *testing.T, commands <-chan ports.Client
34 func TestShortcutsInhibit(t *testing.T) { synctest.Test(t, shortcutsInhibit) }33 func TestShortcutsInhibit(t *testing.T) { synctest.Test(t, shortcutsInhibit) }
35 34
36 func shortcutsInhibit(t *testing.T) {35 func shortcutsInhibit(t *testing.T) {
37- cfg := config.Defaults()36+ cfg := scrollDefaults()
38 input := make(chan ports.InputEvent, 4)37 input := make(chan ports.InputEvent, 4)
39 client := make(chan ports.ClientEvent, 4)38 client := make(chan ports.ClientEvent, 4)
40 commands := make(chan ports.ClientCommand, 64)39 commands := make(chan ports.ClientCommand, 64)
@@ -96,7 +95,7 @@ func shortcutsInhibit(t *testing.T) {
96 // A key whose press ran a bind before inhibiting began is not released to95 // A key whose press ran a bind before inhibiting began is not released to
97 // the window: it never saw the press.96 // the window: it never saw the press.
98 func TestShortcutsInhibitDropsBoundRelease(t *testing.T) {97 func TestShortcutsInhibitDropsBoundRelease(t *testing.T) {
99- cfg := config.Defaults()98+ cfg := scrollDefaults()
100 input := make(chan ports.InputEvent, 4)99 input := make(chan ports.InputEvent, 4)
101 client := make(chan ports.ClientEvent, 4)100 client := make(chan ports.ClientEvent, 4)
102 commands := make(chan ports.ClientCommand, 64)101 commands := make(chan ports.ClientCommand, 64)
M internal/core/layer_input_test.go +8 -9
@@ -4,7 +4,6 @@ import (
44 "context"
55 "testing"
66
7- "github.com/bnema/neferwl/internal/adapters/config"
87 "github.com/bnema/neferwl/internal/core"
98 "github.com/bnema/neferwl/internal/ports"
109 )
@@ -23,7 +22,7 @@ func anyOf[T any](T) bool { return true }
2322
2423 // Bars and launchers get the pointer, popups and, on click, the keyboard.
2524 func TestLayerPointerPopupAndFocus(t *testing.T) {
26- cfg := config.Defaults()
25+ cfg := scrollDefaults()
2726 cfg.Border.Width = 0
2827 client := make(chan ports.ClientEvent, 8)
2928 input := make(chan ports.InputEvent, 8)
@@ -142,7 +141,7 @@ func TestLayerPointerPopupAndFocus(t *testing.T) {
142141
143142 // A window's menu stays with the windows: an overlay layer covers it.
144143 func TestWindowPopupUnderOverlay(t *testing.T) {
145- cfg := config.Defaults()
144+ cfg := scrollDefaults()
146145 cfg.Border.Width = 0
147146 client := make(chan ports.ClientEvent, 8)
148147 input := make(chan ports.InputEvent, 8)
@@ -179,7 +178,7 @@ func TestWindowPopupUnderOverlay(t *testing.T) {
179178 // A fullscreen window hides top and bottom layers from the pointer, not
180179 // overlay; background and bottom layers get it only where no window is.
181180 func TestLayerHitOrder(t *testing.T) {
182- cfg := config.Defaults()
181+ cfg := scrollDefaults()
183182 cfg.Border.Width = 0
184183 client := make(chan ports.ClientEvent, 8)
185184 input := make(chan ports.InputEvent, 8)
@@ -225,7 +224,7 @@ func TestLayerHitOrder(t *testing.T) {
225224 // The pointer moving to another output does not take the keyboard from a
226225 // clicked on-demand layer: keys still reach it.
227226 func TestLayerFocusAcrossOutputs(t *testing.T) {
228- cfg := config.Defaults()
227+ cfg := scrollDefaults()
229228 cfg.Border.Width = 0
230229 client := make(chan ports.ClientEvent, 8)
231230 input := make(chan ports.InputEvent, 8)
@@ -273,7 +272,7 @@ func TestLayerFocusAcrossOutputs(t *testing.T) {
273272
274273 // A layer's transparent margin passes pointer focus through to the window.
275274 func TestLayerInputRegionPassThrough(t *testing.T) {
276- cfg := config.Defaults()
275+ cfg := scrollDefaults()
277276 cfg.Border.Width = 0
278277 client := make(chan ports.ClientEvent, 16)
279278 input := make(chan ports.InputEvent, 16)
@@ -318,7 +317,7 @@ func TestLayerInputRegionPassThrough(t *testing.T) {
318317 }
319318
320319 func TestRemovedLayerInputRegionCleared(t *testing.T) {
321- cfg := config.Defaults()
320+ cfg := scrollDefaults()
322321 cfg.Border.Width = 0
323322 client := make(chan ports.ClientEvent, 16)
324323 input := make(chan ports.InputEvent, 16)
@@ -360,7 +359,7 @@ func TestRemovedLayerInputRegionCleared(t *testing.T) {
360359 }
361360
362361 func TestPopupInputRegionPassesThroughToWindow(t *testing.T) {
363- cfg := config.Defaults()
362+ cfg := scrollDefaults()
364363 cfg.Border.Width = 0
365364 client := make(chan ports.ClientEvent, 16)
366365 input := make(chan ports.InputEvent, 16)
@@ -398,7 +397,7 @@ func TestPopupInputRegionPassesThroughToWindow(t *testing.T) {
398397 // A grabbing popup hidden with its parent under a fullscreen window is
399398 // closed, and the keyboard goes to the fullscreen window.
400399 func TestHiddenGrabPopupLosesKeyboard(t *testing.T) {
401- cfg := config.Defaults()
400+ cfg := scrollDefaults()
402401 cfg.Border.Width = 0
403402 client := make(chan ports.ClientEvent, 8)
404403 output := make(chan ports.OutputEvent, 8)
@@ -4,7 +4,6 @@ import (
4 "context"4 "context"
5 "testing"5 "testing"
6 6
7- "github.com/bnema/neferwl/internal/adapters/config"
8 "github.com/bnema/neferwl/internal/core"7 "github.com/bnema/neferwl/internal/core"
9 "github.com/bnema/neferwl/internal/ports"8 "github.com/bnema/neferwl/internal/ports"
10 )9 )
@@ -23,7 +22,7 @@ func anyOf[T any](T) bool { return true }
23 22
24 // Bars and launchers get the pointer, popups and, on click, the keyboard.23 // Bars and launchers get the pointer, popups and, on click, the keyboard.
25 func TestLayerPointerPopupAndFocus(t *testing.T) {24 func TestLayerPointerPopupAndFocus(t *testing.T) {
26- cfg := config.Defaults()25+ cfg := scrollDefaults()
27 cfg.Border.Width = 026 cfg.Border.Width = 0
28 client := make(chan ports.ClientEvent, 8)27 client := make(chan ports.ClientEvent, 8)
29 input := make(chan ports.InputEvent, 8)28 input := make(chan ports.InputEvent, 8)
@@ -142,7 +141,7 @@ func TestLayerPointerPopupAndFocus(t *testing.T) {
142 141
143 // A window's menu stays with the windows: an overlay layer covers it.142 // A window's menu stays with the windows: an overlay layer covers it.
144 func TestWindowPopupUnderOverlay(t *testing.T) {143 func TestWindowPopupUnderOverlay(t *testing.T) {
145- cfg := config.Defaults()144+ cfg := scrollDefaults()
146 cfg.Border.Width = 0145 cfg.Border.Width = 0
147 client := make(chan ports.ClientEvent, 8)146 client := make(chan ports.ClientEvent, 8)
148 input := make(chan ports.InputEvent, 8)147 input := make(chan ports.InputEvent, 8)
@@ -179,7 +178,7 @@ func TestWindowPopupUnderOverlay(t *testing.T) {
179 // A fullscreen window hides top and bottom layers from the pointer, not178 // A fullscreen window hides top and bottom layers from the pointer, not
180 // overlay; background and bottom layers get it only where no window is.179 // overlay; background and bottom layers get it only where no window is.
181 func TestLayerHitOrder(t *testing.T) {180 func TestLayerHitOrder(t *testing.T) {
182- cfg := config.Defaults()181+ cfg := scrollDefaults()
183 cfg.Border.Width = 0182 cfg.Border.Width = 0
184 client := make(chan ports.ClientEvent, 8)183 client := make(chan ports.ClientEvent, 8)
185 input := make(chan ports.InputEvent, 8)184 input := make(chan ports.InputEvent, 8)
@@ -225,7 +224,7 @@ func TestLayerHitOrder(t *testing.T) {
225 // The pointer moving to another output does not take the keyboard from a224 // The pointer moving to another output does not take the keyboard from a
226 // clicked on-demand layer: keys still reach it.225 // clicked on-demand layer: keys still reach it.
227 func TestLayerFocusAcrossOutputs(t *testing.T) {226 func TestLayerFocusAcrossOutputs(t *testing.T) {
228- cfg := config.Defaults()227+ cfg := scrollDefaults()
229 cfg.Border.Width = 0228 cfg.Border.Width = 0
230 client := make(chan ports.ClientEvent, 8)229 client := make(chan ports.ClientEvent, 8)
231 input := make(chan ports.InputEvent, 8)230 input := make(chan ports.InputEvent, 8)
@@ -273,7 +272,7 @@ func TestLayerFocusAcrossOutputs(t *testing.T) {
273 272
274 // A layer's transparent margin passes pointer focus through to the window.273 // A layer's transparent margin passes pointer focus through to the window.
275 func TestLayerInputRegionPassThrough(t *testing.T) {274 func TestLayerInputRegionPassThrough(t *testing.T) {
276- cfg := config.Defaults()275+ cfg := scrollDefaults()
277 cfg.Border.Width = 0276 cfg.Border.Width = 0
278 client := make(chan ports.ClientEvent, 16)277 client := make(chan ports.ClientEvent, 16)
279 input := make(chan ports.InputEvent, 16)278 input := make(chan ports.InputEvent, 16)
@@ -318,7 +317,7 @@ func TestLayerInputRegionPassThrough(t *testing.T) {
318 }317 }
319 318
320 func TestRemovedLayerInputRegionCleared(t *testing.T) {319 func TestRemovedLayerInputRegionCleared(t *testing.T) {
321- cfg := config.Defaults()320+ cfg := scrollDefaults()
322 cfg.Border.Width = 0321 cfg.Border.Width = 0
323 client := make(chan ports.ClientEvent, 16)322 client := make(chan ports.ClientEvent, 16)
324 input := make(chan ports.InputEvent, 16)323 input := make(chan ports.InputEvent, 16)
@@ -360,7 +359,7 @@ func TestRemovedLayerInputRegionCleared(t *testing.T) {
360 }359 }
361 360
362 func TestPopupInputRegionPassesThroughToWindow(t *testing.T) {361 func TestPopupInputRegionPassesThroughToWindow(t *testing.T) {
363- cfg := config.Defaults()362+ cfg := scrollDefaults()
364 cfg.Border.Width = 0363 cfg.Border.Width = 0
365 client := make(chan ports.ClientEvent, 16)364 client := make(chan ports.ClientEvent, 16)
366 input := make(chan ports.InputEvent, 16)365 input := make(chan ports.InputEvent, 16)
@@ -398,7 +397,7 @@ func TestPopupInputRegionPassesThroughToWindow(t *testing.T) {
398 // A grabbing popup hidden with its parent under a fullscreen window is397 // A grabbing popup hidden with its parent under a fullscreen window is
399 // closed, and the keyboard goes to the fullscreen window.398 // closed, and the keyboard goes to the fullscreen window.
400 func TestHiddenGrabPopupLosesKeyboard(t *testing.T) {399 func TestHiddenGrabPopupLosesKeyboard(t *testing.T) {
401- cfg := config.Defaults()400+ cfg := scrollDefaults()
402 cfg.Border.Width = 0401 cfg.Border.Width = 0
403 client := make(chan ports.ClientEvent, 8)402 client := make(chan ports.ClientEvent, 8)
404 output := make(chan ports.OutputEvent, 8)403 output := make(chan ports.OutputEvent, 8)
M internal/core/overview_input_test.go +4 -5
@@ -4,7 +4,6 @@ import (
44 "context"
55 "testing"
66
7- "github.com/bnema/neferwl/internal/adapters/config"
87 "github.com/bnema/neferwl/internal/core"
98 "github.com/bnema/neferwl/internal/ports"
109 )
@@ -12,7 +11,7 @@ import (
1211 // A launcher holding the keyboard over the overview gets its keys; the
1312 // overview takes them back once it closes.
1413 func TestOverviewLauncherKeepsKeys(t *testing.T) {
15- cfg := config.Defaults()
14+ cfg := scrollDefaults()
1615 client := make(chan ports.ClientEvent, 8)
1716 input := make(chan ports.InputEvent, 8)
1817 output := make(chan ports.OutputEvent, 8)
@@ -65,7 +64,7 @@ func TestOverviewLauncherKeepsKeys(t *testing.T) {
6564 // a covering float: cmd+k/j round-trips through the columns card, then
6665 // cmd+up brings it forward, cmd+left selects a column and cmd+down returns.
6766 func TestOverviewFocusBindsNavigate(t *testing.T) {
68- cfg := config.Defaults()
67+ cfg := scrollDefaults()
6968 client := make(chan ports.ClientEvent, 8)
7069 input := make(chan ports.InputEvent, 8)
7170 output := make(chan ports.OutputEvent, 8)
@@ -116,7 +115,7 @@ func TestOverviewFocusBindsNavigate(t *testing.T) {
116115
117116 // Closing through a real bind follows the preview, not the covering float.
118117 func TestOverviewCloseBindTargetsPreview(t *testing.T) {
119- cfg := config.Defaults()
118+ cfg := scrollDefaults()
120119 client := make(chan ports.ClientEvent, 8)
121120 input := make(chan ports.InputEvent, 8)
122121 output := make(chan ports.OutputEvent, 8)
@@ -188,7 +187,7 @@ func TestFloatOnlyMonitorDirectionalExit(t *testing.T) {
188187
189188 // Three-finger navigation changes selection during movement and resets on lift.
190189 func TestOverviewThreeFingerNavigation(t *testing.T) {
191- cfg := config.Defaults()
190+ cfg := scrollDefaults()
192191 client := make(chan ports.ClientEvent, 8)
193192 input := make(chan ports.InputEvent, 8)
194193 output := make(chan ports.OutputEvent, 8)
@@ -4,7 +4,6 @@ import (
4 "context"4 "context"
5 "testing"5 "testing"
6 6
7- "github.com/bnema/neferwl/internal/adapters/config"
8 "github.com/bnema/neferwl/internal/core"7 "github.com/bnema/neferwl/internal/core"
9 "github.com/bnema/neferwl/internal/ports"8 "github.com/bnema/neferwl/internal/ports"
10 )9 )
@@ -12,7 +11,7 @@ import (
12 // A launcher holding the keyboard over the overview gets its keys; the11 // A launcher holding the keyboard over the overview gets its keys; the
13 // overview takes them back once it closes.12 // overview takes them back once it closes.
14 func TestOverviewLauncherKeepsKeys(t *testing.T) {13 func TestOverviewLauncherKeepsKeys(t *testing.T) {
15- cfg := config.Defaults()14+ cfg := scrollDefaults()
16 client := make(chan ports.ClientEvent, 8)15 client := make(chan ports.ClientEvent, 8)
17 input := make(chan ports.InputEvent, 8)16 input := make(chan ports.InputEvent, 8)
18 output := make(chan ports.OutputEvent, 8)17 output := make(chan ports.OutputEvent, 8)
@@ -65,7 +64,7 @@ func TestOverviewLauncherKeepsKeys(t *testing.T) {
65 // a covering float: cmd+k/j round-trips through the columns card, then64 // a covering float: cmd+k/j round-trips through the columns card, then
66 // cmd+up brings it forward, cmd+left selects a column and cmd+down returns.65 // cmd+up brings it forward, cmd+left selects a column and cmd+down returns.
67 func TestOverviewFocusBindsNavigate(t *testing.T) {66 func TestOverviewFocusBindsNavigate(t *testing.T) {
68- cfg := config.Defaults()67+ cfg := scrollDefaults()
69 client := make(chan ports.ClientEvent, 8)68 client := make(chan ports.ClientEvent, 8)
70 input := make(chan ports.InputEvent, 8)69 input := make(chan ports.InputEvent, 8)
71 output := make(chan ports.OutputEvent, 8)70 output := make(chan ports.OutputEvent, 8)
@@ -116,7 +115,7 @@ func TestOverviewFocusBindsNavigate(t *testing.T) {
116 115
117 // Closing through a real bind follows the preview, not the covering float.116 // Closing through a real bind follows the preview, not the covering float.
118 func TestOverviewCloseBindTargetsPreview(t *testing.T) {117 func TestOverviewCloseBindTargetsPreview(t *testing.T) {
119- cfg := config.Defaults()118+ cfg := scrollDefaults()
120 client := make(chan ports.ClientEvent, 8)119 client := make(chan ports.ClientEvent, 8)
121 input := make(chan ports.InputEvent, 8)120 input := make(chan ports.InputEvent, 8)
122 output := make(chan ports.OutputEvent, 8)121 output := make(chan ports.OutputEvent, 8)
@@ -188,7 +187,7 @@ func TestFloatOnlyMonitorDirectionalExit(t *testing.T) {
188 187
189 // Three-finger navigation changes selection during movement and resets on lift.188 // Three-finger navigation changes selection during movement and resets on lift.
190 func TestOverviewThreeFingerNavigation(t *testing.T) {189 func TestOverviewThreeFingerNavigation(t *testing.T) {
191- cfg := config.Defaults()190+ cfg := scrollDefaults()
192 client := make(chan ports.ClientEvent, 8)191 client := make(chan ports.ClientEvent, 8)
193 input := make(chan ports.InputEvent, 8)192 input := make(chan ports.InputEvent, 8)
194 output := make(chan ports.OutputEvent, 8)193 output := make(chan ports.OutputEvent, 8)
M internal/core/persist_test.go +1 -2
@@ -6,7 +6,6 @@ import (
66 "testing"
77 "time"
88
9- "github.com/bnema/neferwl/internal/adapters/config"
109 "github.com/bnema/neferwl/internal/core"
1110 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
1211 "github.com/bnema/neferwl/internal/ports"
@@ -91,7 +90,7 @@ func TestPersistScalesReportsFailure(t *testing.T) {
9190 // Scale binds report the new scale of the focused output; a bind at the end
9291 // of the range changes nothing and reports nothing.
9392 func TestScaleBindReportsScale(t *testing.T) {
94- cfg := config.Defaults()
93+ cfg := scrollDefaults()
9594 input := make(chan ports.InputEvent, 8)
9695 output := make(chan ports.OutputEvent, 8)
9796 scales := make(chan ports.ScaleChanged, 8)
@@ -6,7 +6,6 @@ import (
6 "testing"6 "testing"
7 "time"7 "time"
8 8
9- "github.com/bnema/neferwl/internal/adapters/config"
10 "github.com/bnema/neferwl/internal/core"9 "github.com/bnema/neferwl/internal/core"
11 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"10 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
12 "github.com/bnema/neferwl/internal/ports"11 "github.com/bnema/neferwl/internal/ports"
@@ -91,7 +90,7 @@ func TestPersistScalesReportsFailure(t *testing.T) {
91 // Scale binds report the new scale of the focused output; a bind at the end90 // Scale binds report the new scale of the focused output; a bind at the end
92 // of the range changes nothing and reports nothing.91 // of the range changes nothing and reports nothing.
93 func TestScaleBindReportsScale(t *testing.T) {92 func TestScaleBindReportsScale(t *testing.T) {
94- cfg := config.Defaults()93+ cfg := scrollDefaults()
95 input := make(chan ports.InputEvent, 8)94 input := make(chan ports.InputEvent, 8)
96 output := make(chan ports.OutputEvent, 8)95 output := make(chan ports.OutputEvent, 8)
97 scales := make(chan ports.ScaleChanged, 8)96 scales := make(chan ports.ScaleChanged, 8)
M internal/core/power_test.go +1 -2
@@ -7,7 +7,6 @@ import (
77 "testing/synctest"
88 "time"
99
10- "github.com/bnema/neferwl/internal/adapters/config"
1110 "github.com/bnema/neferwl/internal/core"
1211 "github.com/bnema/neferwl/internal/ports"
1312 )
@@ -24,7 +23,7 @@ func powerCoreWith(t *testing.T, opts core.Options) (chan ports.InputEvent, chan
2423 output := make(chan ports.OutputEvent, 2)
2524 commands := make(chan ports.ClientCommand, 64)
2625 scenes := make(chan []ports.Scene, 1)
27- c, err := core.New(config.Defaults(), core.Channels{Input: input, Client: client, Output: output, Commands: commands, Scenes: scenes, Spawn: make(chan ports.SpawnRequest, 4)}, opts)
26+ c, err := core.New(scrollDefaults(), core.Channels{Input: input, Client: client, Output: output, Commands: commands, Scenes: scenes, Spawn: make(chan ports.SpawnRequest, 4)}, opts)
2827 if err != nil {
2928 t.Fatal(err)
3029 }
@@ -7,7 +7,6 @@ import (
7 "testing/synctest"7 "testing/synctest"
8 "time"8 "time"
9 9
10- "github.com/bnema/neferwl/internal/adapters/config"
11 "github.com/bnema/neferwl/internal/core"10 "github.com/bnema/neferwl/internal/core"
12 "github.com/bnema/neferwl/internal/ports"11 "github.com/bnema/neferwl/internal/ports"
13 )12 )
@@ -24,7 +23,7 @@ func powerCoreWith(t *testing.T, opts core.Options) (chan ports.InputEvent, chan
24 output := make(chan ports.OutputEvent, 2)23 output := make(chan ports.OutputEvent, 2)
25 commands := make(chan ports.ClientCommand, 64)24 commands := make(chan ports.ClientCommand, 64)
26 scenes := make(chan []ports.Scene, 1)25 scenes := make(chan []ports.Scene, 1)
27- c, err := core.New(config.Defaults(), core.Channels{Input: input, Client: client, Output: output, Commands: commands, Scenes: scenes, Spawn: make(chan ports.SpawnRequest, 4)}, opts)26+ c, err := core.New(scrollDefaults(), core.Channels{Input: input, Client: client, Output: output, Commands: commands, Scenes: scenes, Spawn: make(chan ports.SpawnRequest, 4)}, opts)
28 if err != nil {27 if err != nil {
29 t.Fatal(err)28 t.Fatal(err)
30 }29 }
M internal/core/screens_test.go +1 -2
@@ -8,7 +8,6 @@ import (
88 "testing"
99 "time"
1010
11- "github.com/bnema/neferwl/internal/adapters/config"
1211 "github.com/bnema/neferwl/internal/core"
1312 "github.com/bnema/neferwl/internal/ports"
1413 )
@@ -620,7 +619,7 @@ func TestPointerFocusesOutput(t *testing.T) {
620619
621620 // The first terminal cannot launch before wayland receives its output layout.
622621 func TestFirstTerminalFollowsSetOutputs(t *testing.T) {
623- cfg := config.Defaults()
622+ cfg := scrollDefaults()
624623 commands := make(chan ports.ClientCommand)
625624 // The launcher handoff is deliberately nonblocking. A ready buffered
626625 // queue tests ordering without requiring core to wait for this reader.
@@ -8,7 +8,6 @@ import (
8 "testing"8 "testing"
9 "time"9 "time"
10 10
11- "github.com/bnema/neferwl/internal/adapters/config"
12 "github.com/bnema/neferwl/internal/core"11 "github.com/bnema/neferwl/internal/core"
13 "github.com/bnema/neferwl/internal/ports"12 "github.com/bnema/neferwl/internal/ports"
14 )13 )
@@ -620,7 +619,7 @@ func TestPointerFocusesOutput(t *testing.T) {
620 619
621 // The first terminal cannot launch before wayland receives its output layout.620 // The first terminal cannot launch before wayland receives its output layout.
622 func TestFirstTerminalFollowsSetOutputs(t *testing.T) {621 func TestFirstTerminalFollowsSetOutputs(t *testing.T) {
623- cfg := config.Defaults()622+ cfg := scrollDefaults()
624 commands := make(chan ports.ClientCommand)623 commands := make(chan ports.ClientCommand)
625 // The launcher handoff is deliberately nonblocking. A ready buffered624 // The launcher handoff is deliberately nonblocking. A ready buffered
626 // queue tests ordering without requiring core to wait for this reader.625 // queue tests ordering without requiring core to wait for this reader.
M internal/core/security_test.go +1 -2
@@ -5,7 +5,6 @@ import (
55 "sync/atomic"
66 "testing"
77
8- "github.com/bnema/neferwl/internal/adapters/config"
98 "github.com/bnema/neferwl/internal/core"
109 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
1110 "github.com/bnema/neferwl/internal/ports"
@@ -24,7 +23,7 @@ func TestSecurityOwnerAdversarialQueues(t *testing.T) {
2423 scenes := make(chan []ports.Scene, 1)
2524 spawn := make(chan ports.SpawnRequest, 8)
2625 constraints := make(chan ports.PointerConstraint, 1)
27- cfg := config.Defaults()
26+ cfg := scrollDefaults()
2827 cfg.Terminal.Command = []string{"terminal"}
2928 cfg.Terminal.AutoOpen = "off"
3029 cfg.Binds = map[string]string{"Super+q": "quit"}
@@ -5,7 +5,6 @@ import (
5 "sync/atomic"5 "sync/atomic"
6 "testing"6 "testing"
7 7
8- "github.com/bnema/neferwl/internal/adapters/config"
9 "github.com/bnema/neferwl/internal/core"8 "github.com/bnema/neferwl/internal/core"
10 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"9 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
11 "github.com/bnema/neferwl/internal/ports"10 "github.com/bnema/neferwl/internal/ports"
@@ -24,7 +23,7 @@ func TestSecurityOwnerAdversarialQueues(t *testing.T) {
24 scenes := make(chan []ports.Scene, 1)23 scenes := make(chan []ports.Scene, 1)
25 spawn := make(chan ports.SpawnRequest, 8)24 spawn := make(chan ports.SpawnRequest, 8)
26 constraints := make(chan ports.PointerConstraint, 1)25 constraints := make(chan ports.PointerConstraint, 1)
27- cfg := config.Defaults()26+ cfg := scrollDefaults()
28 cfg.Terminal.Command = []string{"terminal"}27 cfg.Terminal.Command = []string{"terminal"}
29 cfg.Terminal.AutoOpen = "off"28 cfg.Terminal.AutoOpen = "off"
30 cfg.Binds = map[string]string{"Super+q": "quit"}29 cfg.Binds = map[string]string{"Super+q": "quit"}
M internal/core/slots_test.go +1 -2
@@ -7,7 +7,6 @@ import (
77 "testing"
88 "time"
99
10- "github.com/bnema/neferwl/internal/adapters/config"
1110 "github.com/bnema/neferwl/internal/core"
1211 "github.com/bnema/neferwl/internal/ports"
1312 )
@@ -421,7 +420,7 @@ func pendingSlotRespawnedOnSecondShow(t *testing.T, r *slotRig) {
421420 // workspace again refills it.
422421 // Startup commands are spawned once, before the slots.
423422 func TestStartupCommands(t *testing.T) {
424- cfg := config.Defaults()
423+ cfg := scrollDefaults()
425424 cfg.Startup = [][]string{{"wl-paste", "--watch", "cliphist", "store"}, {"waybar"}}
426425 spawn := make(chan ports.SpawnRequest, 4)
427426 c, err := core.New(cfg, core.Channels{Client: make(chan ports.ClientEvent), Input: make(chan ports.InputEvent), Output: make(chan ports.OutputEvent), Config: make(chan ports.ConfigChanged), Commands: make(chan ports.ClientCommand, 16), Spawn: spawn, Scenes: make(chan []ports.Scene, 1), ConfigErrors: make(chan error, 1)}, core.Options{})
@@ -7,7 +7,6 @@ import (
7 "testing"7 "testing"
8 "time"8 "time"
9 9
10- "github.com/bnema/neferwl/internal/adapters/config"
11 "github.com/bnema/neferwl/internal/core"10 "github.com/bnema/neferwl/internal/core"
12 "github.com/bnema/neferwl/internal/ports"11 "github.com/bnema/neferwl/internal/ports"
13 )12 )
@@ -421,7 +420,7 @@ func pendingSlotRespawnedOnSecondShow(t *testing.T, r *slotRig) {
421 // workspace again refills it.420 // workspace again refills it.
422 // Startup commands are spawned once, before the slots.421 // Startup commands are spawned once, before the slots.
423 func TestStartupCommands(t *testing.T) {422 func TestStartupCommands(t *testing.T) {
424- cfg := config.Defaults()423+ cfg := scrollDefaults()
425 cfg.Startup = [][]string{{"wl-paste", "--watch", "cliphist", "store"}, {"waybar"}}424 cfg.Startup = [][]string{{"wl-paste", "--watch", "cliphist", "store"}, {"waybar"}}
426 spawn := make(chan ports.SpawnRequest, 4)425 spawn := make(chan ports.SpawnRequest, 4)
427 c, err := core.New(cfg, core.Channels{Client: make(chan ports.ClientEvent), Input: make(chan ports.InputEvent), Output: make(chan ports.OutputEvent), Config: make(chan ports.ConfigChanged), Commands: make(chan ports.ClientCommand, 16), Spawn: spawn, Scenes: make(chan []ports.Scene, 1), ConfigErrors: make(chan error, 1)}, core.Options{})426 c, err := core.New(cfg, core.Channels{Client: make(chan ports.ClientEvent), Input: make(chan ports.InputEvent), Output: make(chan ports.OutputEvent), Config: make(chan ports.ConfigChanged), Commands: make(chan ports.ClientCommand, 16), Spawn: spawn, Scenes: make(chan []ports.Scene, 1), ConfigErrors: make(chan error, 1)}, core.Options{})
M internal/core/startup_alloc_test.go +2 -1
@@ -1,8 +1,9 @@
11 package core
22
33 import (
4- "github.com/bnema/neferwl/internal/ports"
54 "testing"
5+
6+ "github.com/bnema/neferwl/internal/ports"
67 )
78
89 // A full launcher must not copy the pending command on every unrelated event.
@@ -1,8 +1,9 @@
1 package core1 package core
2 2
3 import (3 import (
4- "github.com/bnema/neferwl/internal/ports"
5 "testing"4 "testing"
5+
6+ "github.com/bnema/neferwl/internal/ports"
6 )7 )
7 8
8 // A full launcher must not copy the pending command on every unrelated event.9 // A full launcher must not copy the pending command on every unrelated event.
M internal/core/workspace_size_internal_test.go +2 -1
@@ -1,9 +1,10 @@
11 package core
22
33 import (
4- "github.com/bnema/neferwl/internal/ports"
54 "math"
65 "testing"
6+
7+ "github.com/bnema/neferwl/internal/ports"
78 )
89
910 func sizedMonitor(overflow Overflow) *Monitor {
@@ -1,9 +1,10 @@
1 package core1 package core
2 2
3 import (3 import (
4- "github.com/bnema/neferwl/internal/ports"
5 "math"4 "math"
6 "testing"5 "testing"
6+
7+ "github.com/bnema/neferwl/internal/ports"
7 )8 )
8 9
9 func sizedMonitor(overflow Overflow) *Monitor {10 func sizedMonitor(overflow Overflow) *Monitor {
M internal/core/workspaces_test.go +3 -4
@@ -4,20 +4,19 @@ import (
44 "context"
55 "testing"
66
7- "github.com/bnema/neferwl/internal/adapters/config"
87 "github.com/bnema/neferwl/internal/core"
98 "github.com/bnema/neferwl/internal/ports"
109 )
1110
1211 func TestWorkspaceChannelCapacity(t *testing.T) {
13- _, err := core.New(config.Defaults(), core.Channels{Scenes: make(chan []ports.Scene, 1), Workspaces: make(chan ports.Workspaces, 2)}, core.Options{})
12+ _, err := core.New(scrollDefaults(), core.Channels{Scenes: make(chan []ports.Scene, 1), Workspaces: make(chan ports.Workspaces, 2)}, core.Options{})
1413 if err == nil {
1514 t.Fatal("expected capacity-1 validation")
1615 }
1716 }
1817
1918 func TestWorkspaceSnapshotsAndActivation(t *testing.T) {
20- cfg := config.Defaults()
19+ cfg := scrollDefaults()
2120 cfg.Workspaces = append(cfg.Workspaces, ports.WorkspaceConfig{Name: "dev"})
2221 client := make(chan ports.ClientEvent, 16)
2322 input := make(chan ports.InputEvent, 8)
@@ -106,7 +105,7 @@ func TestWorkspaceSnapshotsAndActivation(t *testing.T) {
106105 }
107106
108107 func TestMoveWorkspaceReordersSnapshot(t *testing.T) {
109- cfg := config.Defaults()
108+ cfg := scrollDefaults()
110109 client := make(chan ports.ClientEvent, 16)
111110 input := make(chan ports.InputEvent, 8)
112111 output := make(chan ports.OutputEvent, 8)
@@ -4,20 +4,19 @@ import (
4 "context"4 "context"
5 "testing"5 "testing"
6 6
7- "github.com/bnema/neferwl/internal/adapters/config"
8 "github.com/bnema/neferwl/internal/core"7 "github.com/bnema/neferwl/internal/core"
9 "github.com/bnema/neferwl/internal/ports"8 "github.com/bnema/neferwl/internal/ports"
10 )9 )
11 10
12 func TestWorkspaceChannelCapacity(t *testing.T) {11 func TestWorkspaceChannelCapacity(t *testing.T) {
13- _, err := core.New(config.Defaults(), core.Channels{Scenes: make(chan []ports.Scene, 1), Workspaces: make(chan ports.Workspaces, 2)}, core.Options{})12+ _, err := core.New(scrollDefaults(), core.Channels{Scenes: make(chan []ports.Scene, 1), Workspaces: make(chan ports.Workspaces, 2)}, core.Options{})
14 if err == nil {13 if err == nil {
15 t.Fatal("expected capacity-1 validation")14 t.Fatal("expected capacity-1 validation")
16 }15 }
17 }16 }
18 17
19 func TestWorkspaceSnapshotsAndActivation(t *testing.T) {18 func TestWorkspaceSnapshotsAndActivation(t *testing.T) {
20- cfg := config.Defaults()19+ cfg := scrollDefaults()
21 cfg.Workspaces = append(cfg.Workspaces, ports.WorkspaceConfig{Name: "dev"})20 cfg.Workspaces = append(cfg.Workspaces, ports.WorkspaceConfig{Name: "dev"})
22 client := make(chan ports.ClientEvent, 16)21 client := make(chan ports.ClientEvent, 16)
23 input := make(chan ports.InputEvent, 8)22 input := make(chan ports.InputEvent, 8)
@@ -106,7 +105,7 @@ func TestWorkspaceSnapshotsAndActivation(t *testing.T) {
106 }105 }
107 106
108 func TestMoveWorkspaceReordersSnapshot(t *testing.T) {107 func TestMoveWorkspaceReordersSnapshot(t *testing.T) {
109- cfg := config.Defaults()108+ cfg := scrollDefaults()
110 client := make(chan ports.ClientEvent, 16)109 client := make(chan ports.ClientEvent, 16)
111 input := make(chan ports.InputEvent, 8)110 input := make(chan ports.InputEvent, 8)
112 output := make(chan ports.OutputEvent, 8)111 output := make(chan ports.OutputEvent, 8)

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