feat/hints a471fa4

scene.go RawHistoryFolder

1package core
2
3import (
4	"context"
5	"slices"
6
7	"github.com/bnema/neferwl/internal/ports"
8)
9
10// sceneFor builds the scene of screen i and sends the configures it sizes.
11func (c *Core) sceneFor(ctx context.Context, i int, sc *screen, only *screen, capture *capView) (scene ports.Scene, err error) {
12	o := sc.mon.Output()
13	// view.frame is the viewport of the workspace on screen: the whole
14	// output unless it has a size override (never in the overview).
15	view := sc.mon.viewport()
16	var clip Rect
17	if view.frame != (Rect{W: o.W, H: o.H}) {
18		clip = view.frame
19	}
20	// layout is what is drawn; settled (same indexes) is where the
21	// windows are going, and alone sizes the configures.
22	layout, settled := sc.shown, sc.settledLayout
23	scene = ports.Scene{Security: c.security, Output: sc.name(), OutputWidth: o.W, OutputHeight: o.H, WorkspaceClip: clip, Scale: sc.scale, Transform: sc.transform, Off: sc.off, Background: c.cfg.Background.Color, Border: ports.Border{Width: c.cfg.Border.Width, Active: c.cfg.Border.Active, Inactive: c.cfg.Border.Inactive}, Windows: make([]ports.SceneWindow, 0, len(layout)+len(c.popupOrder)+1), Layers: shownLayers(sc)}
24	if !sc.mon.previewing() {
25		// Tiles stop at the panels; the overview lays out previews over
26		// the whole output.
27		u := view.usable
28		scene.TileInset = ports.Insets{Top: u.Y, Left: u.X, Right: o.W - u.X - u.W, Bottom: o.H - u.Y - u.H}
29	}
30	var real map[WindowID]Placement
31	if sc.mon.previewing() {
32		// Local to this screen's build: reused, cleared each time.
33		if c.overviewReal == nil {
34			c.overviewReal = make(map[WindowID]Placement)
35		}
36		clear(c.overviewReal)
37		real = c.overviewReal
38		for w := range sc.mon.all() {
39			c.realBuf = w.layoutInto(c.realBuf)
40			for _, p := range c.realBuf {
41				real[p.ID] = p
42			}
43		}
44	}
45	scene.Dim = floatDim(layout, view, c.cfg.Floating.Dim)
46	if sc.mon.previewing() {
47		// Darken the wallpaper around the previews.
48		scene.Dim = c.cfg.Floating.Dim
49		scene.DimBehind = scene.Dim > 0
50	}
51	if d := c.drag; d != nil && d.target.screen == sc && d.target.kind != dropNone {
52		scene.DropHints = slices.Clone(d.target.hints)
53	}
54	if sc.mon.ov.open {
55		// Frame the selection and separate numbered and named row groups.
56		// The scene's own slice (immutable once sent), sized for both.
57		_, dividers := sc.mon.overviewRows()
58		var seps []ports.Separator
59		if len(dividers) > 0 {
60			seps = append(make([]ports.Separator, 0, len(dividers)+outlineLines), dividers...)
61		}
62		scene.Separators = overviewOutline(seps, layout, max(c.cfg.Border.Width, 2))
63	} else if sc.mon.switcherShown() {
64		// The switcher's cards: only the selection is framed.
65		scene.Separators = overviewOutline(nil, layout, max(c.cfg.Border.Width, 2))
66	} else {
67		// Only the focused output lights the focused window's lines.
68		scene.Separators = separators(layout, c.cfg.Border.Width, sc.mon.Current().gap(), view, i == c.focusScreen)
69	}
70	for k, p := range layout {
71		ps := settled[k]
72		// Only the focused output has an activated window.
73		focused := p.Focused && i == c.focusScreen
74		sw := ports.SceneWindow{ID: p.ID, Rect: p.Rect, Focused: focused, Fullscreen: p.Fullscreen, Hidden: p.Hidden, Floating: p.Floating, Below: p.Below, Inset: p.Inset, Preview: p.Preview, Fade: p.Fade}
75		if p.Zoom > 0 && (p.Zoom < 1 || p.Preview > 0) {
76			// A scale motion: the content follows the drawn size. A
77			// card in flight drawn at its size still needs Zoom 1:
78			// without it the renderer would shrink it by Preview.
79			sw.Zoom = p.Zoom
80		}
81		// A peek's veil is the configured one plus its animated offset.
82		sw.Dim = max(0, min(p.Dim+c.peekDim(p), 1))
83		if p.Leaving {
84			// A window fading out after it closed or hid: drawn, but
85			// hidden to everything else, its configures included.
86			sw.Hidden = false
87			scene.Windows = append(scene.Windows, sw)
88			// A window the workspace still holds (a hidden stash
89			// window) keeps its last configure: prune would forget it,
90			// and its next one would start from nothing. A closed one
91			// was forgotten at its unmap and stays so.
92			if w, _ := sc.mon.find(p.ID); w != nil {
93				c.configures.keep(p.ID)
94			}
95			continue
96		}
97		scene.Windows = append(scene.Windows, sw)
98		t := configureTarget{output: sc.name(), view: view, focused: focused, captured: capture != nil && capture.window == p.ID}
99		if !p.Hidden && p.Preview == 0 {
100			// Only a sized configure needs the client size.
101			t.client, t.imposed = c.clientRect(ps), sc.mon.Current().imposedFloat(p.ID)
102		} else if t.captured && p.Hidden {
103			// A captured hidden window is sized like its capture.
104			t.client = capture.windowSz
105		} else if p.Preview > 0 && !p.Hidden {
106			if rp, ok := real[p.ID]; ok && !rp.Hidden {
107				t.realTiled = !rp.Floating
108				t.client = c.clientRect(rp)
109			}
110		}
111		cp, t := c.captureConfigure(sc, ps, t)
112		if v, send := c.configures.nextWithCapture(ps, t, cp); send {
113			if err := c.command(ctx, v); err != nil {
114				return ports.Scene{}, err
115			}
116			c.configures.mark(v)
117		}
118	}
119	scene.Windows = append(scene.Windows, c.scenePopups(sc)...)
120	if icon, ok := c.dataDragIconOn(i, sc); ok {
121		// The drag icon is over everything, popups and layers included.
122		scene.Windows = append(scene.Windows, icon)
123	}
124	scene.Flash = c.flashFor(sc)
125	scene.CaptureIndicators = c.captureIndicators(sc)
126	scene.HintLabels = c.hintLabels(sc)
127	scene.HintStyle = ports.HintStyle{Size: c.cfg.Hint.Size, Background: c.cfg.Hint.Background, Foreground: c.cfg.Hint.Foreground, Width: sc.info.Width, Height: sc.info.Height, PhysicalW: sc.info.PhysicalW, PhysicalH: sc.info.PhysicalH}
128	scene.Capture = c.captureSceneFor(sc, capture)
129	// The springs of this frame are advanced: a scene shown while one still
130	// runs wakes core when it flips; the one a spring settles on does not.
131	scene.Animating = c.sliding(sc.name())
132	// A scene carrying a capture image always gets a fresh Seq, and so
133	// does one of an animating screen (the one being stepped, or any
134	// when none is): the outputs drop a scene they already show, and a
135	// spring that starts at the previous scene, or rounds to it in its
136	// tail, would get no flip and wait for the fallback timer. So does the
137	// scene that flips Animating off: SameAs ignores the flag, and keeping
138	// the Seq of the animating scene would leave outputs showing it, and
139	// waking core on every later flip. Any other keeps its output's Seq
140	// while it draws the same, so an idle output is not recomposed.
141	withCapture := scene.Capture != nil && (sc == capture.hiddenScr || sc == capture.windowScr)
142	animating := scene.Animating && (only == nil || only == sc)
143	if !withCapture && !animating && sc.last.Animating == scene.Animating && sc.last.Seq != 0 && scene.SameAs(sc.last) {
144		scene.Seq = sc.last.Seq
145	} else {
146		c.seq++
147		scene.Seq = c.seq
148	}
149	if withCapture {
150		switch sc {
151		case capture.hiddenScr:
152			scene.CaptureScene = c.captureScene(scene.Seq)
153		case capture.windowScr:
154			scene.CaptureScene = c.captureWindowScene(capture, scene.Seq)
155		}
156	}
157	return scene, nil
158}

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