v0.4.1 03674e0

rect.go RawHistoryFolder

1package graphics
2
3import "fmt"
4
5const (
6	maxInt64 = int64(^uint64(0) >> 1)
7	minInt64 = -maxInt64 - 1
8)
9
10// PixelRect is a half-open rectangle in pixel coordinates. Width and Height
11// must be non-negative, and the exclusive right and bottom edges must fit in
12// int64. Coordinates may be negative so that scenes can be clipped without
13// first translating them into a viewport.
14type PixelRect struct {
15	X, Y          int64
16	Width, Height int64
17}
18
19// CellRect is a half-open rectangle in cell coordinates. It has the same
20// overflow rules as PixelRect but is kept as a distinct type so that pixel
21// and cell coordinates cannot be accidentally mixed.
22type CellRect struct {
23	X, Y          int64
24	Width, Height int64
25}
26
27type rect struct {
28	x, y          int64
29	width, height int64
30}
31
32func pixelRect(r PixelRect) rect { return rect{x: r.X, y: r.Y, width: r.Width, height: r.Height} }
33func cellRect(r CellRect) rect   { return rect{x: r.X, y: r.Y, width: r.Width, height: r.Height} }
34
35// NewPixelRect constructs a checked pixel rectangle.
36func NewPixelRect(x, y, width, height int64) (PixelRect, error) {
37	r := PixelRect{X: x, Y: y, Width: width, Height: height}
38	if !r.Valid() {
39		return PixelRect{}, fmt.Errorf("pixel rectangle: %w", ErrInvalidRect)
40	}
41	return r, nil
42}
43
44// NewCellRect constructs a checked cell rectangle.
45func NewCellRect(x, y, width, height int64) (CellRect, error) {
46	r := CellRect{X: x, Y: y, Width: width, Height: height}
47	if !r.Valid() {
48		return CellRect{}, fmt.Errorf("cell rectangle: %w", ErrInvalidRect)
49	}
50	return r, nil
51}
52
53func (r rect) valid() bool {
54	return r.width >= 0 && r.height >= 0 && edge(r.x, r.width) && edge(r.y, r.height)
55}
56
57func (r rect) empty() bool { return r.width == 0 || r.height == 0 }
58
59func (r rect) right() (int64, bool) { return checkedEdge(r.x, r.width) }
60
61func (r rect) bottom() (int64, bool) { return checkedEdge(r.y, r.height) }
62
63func (r rect) contains(x, y int64) bool {
64	right, okRight := r.right()
65	bottom, okBottom := r.bottom()
66	return okRight && okBottom && x >= r.x && x < right && y >= r.y && y < bottom
67}
68
69func (r rect) intersect(other rect) (rect, bool) {
70	if !r.valid() || !other.valid() || r.empty() || other.empty() {
71		return rect{}, false
72	}
73	right, _ := r.right()
74	otherRight, _ := other.right()
75	bottom, _ := r.bottom()
76	otherBottom, _ := other.bottom()
77	left := max(r.x, other.x)
78	top := max(r.y, other.y)
79	right = min(right, otherRight)
80	bottom = min(bottom, otherBottom)
81	if right <= left || bottom <= top {
82		return rect{}, false
83	}
84	return rect{x: left, y: top, width: right - left, height: bottom - top}, true
85}
86
87// Valid reports whether the rectangle's dimensions and exclusive edges are
88// representable without signed overflow.
89func (r PixelRect) Valid() bool { return pixelRect(r).valid() }
90
91// Valid reports whether the rectangle's dimensions and exclusive edges are
92// representable without signed overflow.
93func (r CellRect) Valid() bool { return cellRect(r).valid() }
94
95// Empty reports whether the rectangle has no area.
96func (r PixelRect) Empty() bool { return pixelRect(r).empty() }
97
98// Empty reports whether the rectangle has no area.
99func (r CellRect) Empty() bool { return cellRect(r).empty() }
100
101// Right returns the exclusive right edge. The boolean is false for an
102// invalid rectangle; it is never produced by arithmetic that wraps.
103func (r PixelRect) Right() (int64, bool) { return pixelRect(r).right() }
104
105// Bottom returns the exclusive bottom edge. The boolean is false for an
106// invalid rectangle; it is never produced by arithmetic that wraps.
107func (r PixelRect) Bottom() (int64, bool) { return pixelRect(r).bottom() }
108
109// Right returns the exclusive right edge. The boolean is false for an
110// invalid rectangle; it is never produced by arithmetic that wraps.
111func (r CellRect) Right() (int64, bool) { return cellRect(r).right() }
112
113// Bottom returns the exclusive bottom edge. The boolean is false for an
114// invalid rectangle; it is never produced by arithmetic that wraps.
115func (r CellRect) Bottom() (int64, bool) { return cellRect(r).bottom() }
116
117// Contains reports whether a point is inside the half-open rectangle.
118func (r PixelRect) Contains(x, y int64) bool { return pixelRect(r).contains(x, y) }
119
120// Contains reports whether a point is inside the half-open rectangle.
121func (r CellRect) Contains(x, y int64) bool { return cellRect(r).contains(x, y) }
122
123// Intersect returns the non-empty intersection of two rectangles. Invalid or
124// empty rectangles do not intersect. The result is safe even when the input
125// edges are close to the int64 limits.
126func (r PixelRect) Intersect(other PixelRect) (PixelRect, bool) {
127	intersect, ok := pixelRect(r).intersect(pixelRect(other))
128	return PixelRect{X: intersect.x, Y: intersect.y, Width: intersect.width, Height: intersect.height}, ok
129}
130
131// Intersect returns the non-empty intersection of two rectangles.
132func (r CellRect) Intersect(other CellRect) (CellRect, bool) {
133	intersect, ok := cellRect(r).intersect(cellRect(other))
134	return CellRect{X: intersect.x, Y: intersect.y, Width: intersect.width, Height: intersect.height}, ok
135}
136
137func edge(start, length int64) bool {
138	_, ok := checkedEdge(start, length)
139	return ok
140}
141
142func checkedEdge(start, length int64) (int64, bool) {
143	if length < 0 {
144		return 0, false
145	}
146	// For a non-negative length, a negative start cannot overflow the
147	// positive edge: even MinInt64 + MaxInt64 is -1. Avoid computing
148	// maxInt64-start in that case because the subtraction itself could wrap.
149	if start >= 0 && length > maxInt64-start {
150		return 0, false
151	}
152	return start + length, true
153}

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 13

↑↓ moveenter openesc close