okami.mom/ultraviolet

git clone https://git.nkp.pet/okami.mom/ultraviolet

Ayman Bagabasfix: account for wide cells when cloning and filling areas7813617

upstream
3.2 KiB117 linesraw
1// Package screen provides functions and helpers to manipulate a [uv.Screen].
2package screen
3
4import (
5	uv "github.com/charmbracelet/ultraviolet"
6)
7
8// Clear clears the screen with empty cells. This is equivalent to filling the
9// screen with empty cells.
10//
11// If the screen implements a [Clear] method, it will be called instead of
12// filling the screen with empty cells.
13func Clear(scr uv.Screen) {
14	if c, ok := scr.(interface {
15		Clear()
16	}); ok {
17		c.Clear()
18		return
19	}
20	Fill(scr, nil)
21}
22
23// ClearArea clears the given area of the screen with empty cells. This is
24// equivalent to filling the area with empty cells.
25//
26// If the screen implements a [ClearArea] method, it will be called instead of
27// filling the area with empty cells.
28func ClearArea(scr uv.Screen, area uv.Rectangle) {
29	if c, ok := scr.(interface {
30		ClearArea(area uv.Rectangle)
31	}); ok {
32		c.ClearArea(area)
33		return
34	}
35	FillArea(scr, nil, area)
36}
37
38// Fill fills the screen with the given cell. If the cell is nil, it fills the
39// screen with empty cells.
40//
41// If the screen implements a [Fill] method, it will be called instead of
42// filling the screen with empty cells.
43func Fill(scr uv.Screen, cell *uv.Cell) {
44	if f, ok := scr.(interface {
45		Fill(cell *uv.Cell)
46	}); ok {
47		f.Fill(cell)
48		return
49	}
50	FillArea(scr, cell, scr.Bounds())
51}
52
53// FillArea fills the given area of the screen with the given cell. If the cell
54// is nil, it fills the area with empty cells.
55//
56// If the screen implements a [FillArea] method, it will be called instead of
57// filling the area with empty cells.
58func FillArea(scr uv.Screen, cell *uv.Cell, area uv.Rectangle) {
59	if f, ok := scr.(interface {
60		FillArea(cell *uv.Cell, area uv.Rectangle)
61	}); ok {
62		f.FillArea(cell, area)
63		return
64	}
65	cellWidth := 1
66	if cell != nil && cell.Width > 1 {
67		cellWidth = cell.Width
68	}
69	for y := area.Min.Y; y < area.Max.Y; y++ {
70		for x := area.Min.X; x < area.Max.X; x += cellWidth {
71			scr.SetCell(x, y, cell)
72		}
73	}
74}
75
76// CloneArea clones the given area of the screen and returns a new buffer
77// with the same size as the area. The new buffer will contain the same cells
78// as the area in the screen.
79// Use [uv.Buffer.Draw] to draw the cloned buffer to a screen again.
80//
81// If the screen implements a [CloneArea] method, it will be called instead of
82// cloning the area manually.
83func CloneArea(scr uv.Screen, area uv.Rectangle) *uv.Buffer {
84	if c, ok := scr.(interface {
85		CloneArea(area uv.Rectangle) *uv.Buffer
86	}); ok {
87		return c.CloneArea(area)
88	}
89	buf := uv.NewBuffer(area.Dx(), area.Dy())
90	for y := area.Min.Y; y < area.Max.Y; y++ {
91		for x := area.Min.X; x < area.Max.X; {
92			cell := scr.CellAt(x, y)
93			if cell == nil || cell.IsZero() {
94				x++
95				continue
96			}
97			buf.SetCell(x-area.Min.X, y-area.Min.Y, cell)
98			x += max(cell.Width, 1)
99		}
100	}
101	return buf
102}
103
104// Clone creates a new [uv.Buffer] clone of the given screen. The new buffer will
105// have the same size as the screen and will contain the same cells.
106// Use [uv.Buffer.Draw] to draw the cloned buffer to a screen again.
107//
108// If the screen implements a [Clone] method, it will be called instead of
109// cloning the entire screen manually.
110func Clone(scr uv.Screen) *uv.Buffer {
111	if c, ok := scr.(interface {
112		Clone() *uv.Buffer
113	}); ok {
114		return c.Clone()
115	}
116	return CloneArea(scr, scr.Bounds())
117}