main 3d41bc5

grid.go RawHistoryFolder

1package core
2
3import (
4	"errors"
5	"image"
6	"math"
7
8	"github.com/bnema/nefercap/internal/ports"
9)
10
11// GridLineCount is the number of guide lines of a monitor grid.
12const GridLineCount = 6
13
14// MaxGridScale is the largest output scale a grid accepts. It bounds the
15// physical size to MaxDimension * MaxGridScale, far inside int32.
16const MaxGridScale = 8
17
18// scaleEpsilon absorbs binary floating-point noise at exact integer
19// boundaries (1600 * 1.2 is 1920.0000000000002), like the surface buffer
20// size the window system derives from the same logical size and scale.
21const scaleEpsilon = 1e-9
22
23// ErrInvalidGrid reports a grid requested for an unusable size or scale.
24var ErrInvalidGrid = errors.New("invalid grid size or scale")
25
26// GridLines are the guide lines of a monitor in physical pixels, origin at the
27// monitor's top-left: three vertical lines (1/3, 1/2, 2/3 of the width) then
28// three horizontal lines (1/3, 1/2, 2/3 of the height). Each line is one
29// physical pixel thick and spans the whole monitor. It is a plain value.
30type GridLines [GridLineCount]image.Rectangle
31
32// PhysicalExtent returns the physical pixel count of a logical extent at a
33// possibly fractional scale: ceil(logical * scale), computed like the surface
34// buffer size with a small epsilon so float noise never adds a pixel. It
35// fails with ErrInvalidGrid for a logical extent outside 1..MaxDimension, a
36// scale that is not finite or above MaxGridScale, or a result below one pixel.
37func PhysicalExtent(logical int, scale float64) (int, error) {
38	if logical < 1 || logical > ports.MaxDimension ||
39		math.IsNaN(scale) || scale <= 0 || scale > MaxGridScale {
40		return 0, ErrInvalidGrid
41	}
42	n := int(math.Ceil(float64(logical)*scale - scaleEpsilon))
43	if n < 1 {
44		return 0, ErrInvalidGrid
45	}
46	return n, nil
47}
48
49// MonitorGrid returns the fixed guide lines of a whole monitor of the given
50// logical size at the given scale, on the surface buffer PhysicalExtent gives
51// for each axis. Every line coordinate is snapped to a whole physical pixel
52// first (floor of the exact 1/3, 1/2 and 2/3 position) and is exactly one
53// pixel thick, so callers convert to logical units only afterwards. It does
54// not allocate and returns the zero value with ErrInvalidGrid on bad input.
55//
56// The buffer can be a pixel larger than the output mode when the logical size
57// was rounded; use MonitorGridPhysical with the mode when it is known.
58func MonitorGrid(width, height int, scale float64) (GridLines, error) {
59	pw, err := PhysicalExtent(width, scale)
60	if err != nil {
61		return GridLines{}, err
62	}
63	ph, err := PhysicalExtent(height, scale)
64	if err != nil {
65		return GridLines{}, err
66	}
67	return physicalGrid(pw, ph), nil
68}
69
70// MonitorGridPhysical returns the guide lines of a monitor whose physical
71// size is exactly width x height pixels, for example the output mode. The
72// lines are snapped and one pixel thick as in MonitorGrid and span the given
73// size. Each size must be 1..MaxDimension*MaxGridScale, since a physical mode
74// can exceed MaxDimension when the scale is above 1. It does not allocate and
75// returns the zero value with ErrInvalidGrid on bad input.
76func MonitorGridPhysical(width, height int) (GridLines, error) {
77	const limit = ports.MaxDimension * MaxGridScale
78	if width < 1 || width > limit || height < 1 || height > limit {
79		return GridLines{}, ErrInvalidGrid
80	}
81	return physicalGrid(width, height), nil
82}
83
84// physicalGrid lays the lines out on an exact physical size of at least 1x1.
85func physicalGrid(pw, ph int) GridLines {
86	var g GridLines
87	// Integer floor division keeps the snap exact for every size.
88	xs := [3]int{pw / 3, pw / 2, pw * 2 / 3}
89	ys := [3]int{ph / 3, ph / 2, ph * 2 / 3}
90	for i := range xs {
91		g[i] = image.Rect(xs[i], 0, xs[i]+1, ph)
92		g[3+i] = image.Rect(0, ys[i], pw, ys[i]+1)
93	}
94	return g
95}

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 2

Tags 9

↑↓ moveenter openesc close