workspace_clip_test.go RawHistoryFolder

1package vulkan
2
3import (
4	"image"
5	"image/color"
6	"testing"
7
8	"github.com/bnema/neferwl/internal/ports"
9)
10
11// A tile and its lines are cut at TileArea, so a bottom layer panel under
12// them stays visible; a float there is drawn over it.
13func TestTileInsetKeepsBottomPanel(t *testing.T) {
14	r, err := New(64, 48)
15	if err != nil {
16		t.Skipf("Vulkan unavailable: %v", err)
17	}
18	defer r.Close()
19	red := color.RGBA{255, 0, 0, 255}
20	green := color.RGBA{0, 255, 0, 255}
21	blue := color.RGBA{0, 0, 255, 255}
22	contents := map[ports.WindowID]ports.SurfaceContent{1: solidContent(t, 64, 48, red), 2: solidContent(t, 64, 8, green), 3: solidContent(t, 8, 8, blue)}
23	scene := ports.Scene{Seq: 1, OutputWidth: 64, OutputHeight: 48, Background: "#000000", TileInset: ports.Insets{Bottom: 8},
24		Border:     ports.Border{Width: 2, Inactive: "#ffffff"},
25		Windows:    []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: 64, H: 48}}},
26		Separators: []ports.Separator{{Rect: ports.Rect{X: 30, W: 2, H: 48}}},
27		Layers:     []ports.SceneLayer{{ID: 2, Rect: ports.Rect{Y: 40, W: 64, H: 8}, Layer: ports.LayerBottom}},
28	}
29	if err := render(r, scene, contents); err != nil {
30		t.Fatal(err)
31	}
32	pixels := readPixels(t, r)
33	white := color.RGBA{255, 255, 255, 255}
34	for point, want := range map[image.Point]color.RGBA{{10, 10}: red, {10, 39}: red, {10, 40}: green, {60, 47}: green, {30, 39}: white, {30, 44}: green} {
35		if got := pixels.RGBAAt(point.X, point.Y); got != want {
36			t.Errorf("pixel %v=%v, want %v", point, got, want)
37		}
38	}
39	scene.Seq++
40	scene.Windows = []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: 64, H: 48}}, {ID: 3, Rect: ports.Rect{Y: 40, W: 8, H: 8}, Floating: true}}
41	if err := render(r, scene, contents); err != nil {
42		t.Fatal(err)
43	}
44	if got := readPixels(t, r).RGBAAt(2, 44); got != blue {
45		t.Fatalf("float clipped at the panel: %v", got)
46	}
47}
48
49// A tile whose content changes damages only what is drawn of it, never the
50// panel under it.
51func TestTileDamageStopsAtPanel(t *testing.T) {
52	r, err := New(64, 48)
53	if err != nil {
54		t.Skipf("Vulkan unavailable: %v", err)
55	}
56	defer r.Close()
57	scene := ports.Scene{Seq: 1, Scale: 1, OutputWidth: 64, OutputHeight: 48, Background: "#000000", TileInset: ports.Insets{Bottom: 8},
58		Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: 64, H: 48}}}}
59	c := solidContent(t, 64, 48, color.RGBA{255, 0, 0, 255})
60	c.ID, c.Seq = 1, 2 // the target holds Seq 1, with no damage history
61	bounds := image.Rect(0, 0, 64, 48)
62	held := &target{valid: true, sceneSeq: scene.Seq, scene: scene, windows: map[ports.WindowID]heldWindow{1: {seq: 1, rect: image.Rect(0, 0, 64, 40)}}}
63	dmg := r.frameDamage(held, scene, bounds)
64	_ = r.draws(scene, map[ports.WindowID]ports.SurfaceContent{1: c}, dmg)
65	dmg.finish()
66	if dmg.all() || dmg.area != image.Rect(0, 0, 64, 40) {
67		t.Fatalf("damage %v (all %v), want the tile above the panel", dmg.area, dmg.all())
68	}
69}
70
71func TestWorkspaceClipWindowsPopupsAndLayers(t *testing.T) {
72	r, err := New(64, 48)
73	if err != nil {
74		t.Skipf("Vulkan unavailable: %v", err)
75	}
76	defer r.Close()
77	red := color.RGBA{255, 0, 0, 255}
78	green := color.RGBA{0, 255, 0, 255}
79	contents := map[ports.WindowID]ports.SurfaceContent{1: solidContent(t, 64, 48, red), 2: solidContent(t, 8, 8, green)}
80	clip := ports.Rect{X: 16, Y: 12, W: 32, H: 24}
81	scene := ports.Scene{Seq: 1, OutputWidth: 64, OutputHeight: 48, Background: "#000000", WorkspaceClip: clip,
82		Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: 64, H: 48}}},
83		Layers:  []ports.SceneLayer{{ID: 2, Rect: ports.Rect{W: 8, H: 8}, Layer: ports.LayerTop}},
84	}
85	if err := render(r, scene, contents); err != nil {
86		t.Fatal(err)
87	}
88	pixels := readPixels(t, r)
89	for point, want := range map[image.Point]color.RGBA{{0, 0}: green, {10, 10}: {0, 0, 0, 255}, {16, 12}: red, {47, 35}: red, {48, 36}: {0, 0, 0, 255}} {
90		if got := pixels.RGBAAt(point.X, point.Y); got != want {
91			t.Errorf("pixel %v=%v, want %v", point, got, want)
92		}
93	}
94	// Content-only damage at the same scene sequence must remain clipped.
95	changed := contents[1]
96	changed.Seq++
97	changed.DamageHistory = []ports.SeqDamage{{Seq: changed.Seq, Full: true}}
98	if _, err := changed.SHM.File.WriteAt([]byte{255, 255, 255, 255}, 0); err != nil {
99		t.Fatal(err)
100	}
101	contents[1] = changed
102	if err := render(r, scene, contents); err != nil {
103		t.Fatal(err)
104	}
105	if got := readPixels(t, r).RGBAAt(10, 10); got != (color.RGBA{0, 0, 0, 255}) {
106		t.Fatalf("damage escaped clip: %v", got)
107	}
108	// A popup from a workspace surface is clipped too; input popups over
109	// layers retain output-wide bounds.
110	scene.Seq++
111	scene.Windows = []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: 64, H: 48}, Popup: true}}
112	if err := render(r, scene, contents); err != nil {
113		t.Fatal(err)
114	}
115	if got := readPixels(t, r).RGBAAt(10, 10); got != (color.RGBA{0, 0, 0, 255}) {
116		t.Fatalf("popup escaped clip: %v", got)
117	}
118	// Popups above layers belong to output-wide controls, not the workspace.
119	// Scenes are immutable snapshots (the target holds the previous one):
120	// a changed window goes in a fresh slice, as core publishes it.
121	scene.Seq++
122	scene.Windows = []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: 64, H: 48}, Popup: true, OverLayers: true}}
123	if err := render(r, scene, contents); err != nil {
124		t.Fatal(err)
125	}
126	if got := readPixels(t, r).RGBAAt(10, 10); got != red {
127		t.Fatalf("output-wide popup clipped: %v", got)
128	}
129	scene.Windows = []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: 64, H: 48}, Popup: true}}
130	// Switching back to inherited geometry repaints the margins.
131	scene.Seq++
132	scene.WorkspaceClip = ports.Rect{}
133	if err := render(r, scene, contents); err != nil {
134		t.Fatal(err)
135	}
136	if got := readPixels(t, r).RGBAAt(10, 10); got != red {
137		t.Fatalf("stale clip after inheritance: %v", got)
138	}
139}

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 23

Tags 10

↑↓ moveenter openesc close