turbo-editors/turbo-corepublic Fork 0
main
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

🛟 Updated. 28d5985 · on main · k33g · 4h ago
rect.go · 86 lines · 2.9 KBGo Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Package ui is the widget framework the editor is built from: a desktop
// holding movable windows, a menu bar with drop-down menus, modal dialogs,
// buttons, input lines and list boxes — the Turbo Vision furniture, drawn on
// tcell.
//
// Nothing here knows about Go source, buffers or language servers. Widgets
// draw through a clipping Painter and answer key and mouse events; what they
// mean is decided by the package above.
package ui

// Rect is a rectangle of terminal cells: its top-left corner and its size.
//
// A rectangle with a zero or negative width or height is empty and covers no
// cell at all, which is what an off-screen or fully clipped widget reduces to.
type Rect struct {
	X, Y, W, H int
}

// Right returns the column just past the rectangle's right edge.
func (r Rect) Right() int { return r.X + r.W }

// Bottom returns the row just past the rectangle's bottom edge.
func (r Rect) Bottom() int { return r.Y + r.H }

// IsEmpty reports whether the rectangle covers no cell.
func (r Rect) IsEmpty() bool { return r.W <= 0 || r.H <= 0 }

// Contains reports whether the cell at (x, y) is inside the rectangle.
func (r Rect) Contains(x, y int) bool {
	return x >= r.X && x < r.Right() && y >= r.Y && y < r.Bottom()
}

// Intersect returns the rectangle covered by both r and other, which is empty
// when they do not overlap.
func (r Rect) Intersect(other Rect) Rect {
	x := max(r.X, other.X)
	y := max(r.Y, other.Y)
	right := min(r.Right(), other.Right())
	bottom := min(r.Bottom(), other.Bottom())

	if right <= x || bottom <= y {
		return Rect{X: x, Y: y}
	}
	return Rect{X: x, Y: y, W: right - x, H: bottom - y}
}

// Inset returns the rectangle shrunk by dx on the left and right and by dy on
// the top and bottom. It is how a widget moves inside its own frame.
//
//	body := window.Bounds().Inset(1, 1)
func (r Rect) Inset(dx, dy int) Rect {
	return Rect{X: r.X + dx, Y: r.Y + dy, W: r.W - 2*dx, H: r.H - 2*dy}
}

// Move returns the rectangle shifted by (dx, dy), keeping its size.
func (r Rect) Move(dx, dy int) Rect {
	return Rect{X: r.X + dx, Y: r.Y + dy, W: r.W, H: r.H}
}

// WithSize returns the rectangle with a different size and the same corner.
func (r Rect) WithSize(w, h int) Rect {
	return Rect{X: r.X, Y: r.Y, W: w, H: h}
}

// CenteredIn returns a rectangle of the same size placed in the middle of
// outer, which is where a dialog opens.
func (r Rect) CenteredIn(outer Rect) Rect {
	return Rect{
		X: outer.X + (outer.W-r.W)/2,
		Y: outer.Y + (outer.H-r.H)/2,
		W: r.W,
		H: r.H,
	}
}

// ClampInto returns the rectangle moved — never resized — so that it lies
// inside outer, as far as its size allows. A window dragged past the edge of
// the screen comes back this way.
func (r Rect) ClampInto(outer Rect) Rect {
	out := r
	out.X = min(out.X, outer.Right()-out.W)
	out.Y = min(out.Y, outer.Bottom()-out.H)
	out.X = max(out.X, outer.X)
	out.Y = max(out.Y, outer.Y)
	return out
}