mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
term: add term.ui
module (part 2) (#6798)
This commit is contained in:
94
examples/term.ui/cursor_chaser.v
Normal file
94
examples/term.ui/cursor_chaser.v
Normal file
@ -0,0 +1,94 @@
|
||||
import term.ui as tui
|
||||
|
||||
struct Point {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
const (
|
||||
colors = [
|
||||
tui.Color{33, 150, 243}
|
||||
tui.Color{0, 150, 136}
|
||||
tui.Color{205, 220, 57}
|
||||
tui.Color{255, 152, 0}
|
||||
tui.Color{244, 67, 54}
|
||||
tui.Color{156, 39, 176}
|
||||
]
|
||||
)
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
tui &tui.Context = 0
|
||||
points []Point
|
||||
color tui.Color = colors[0]
|
||||
color_idx int
|
||||
cut_rate f64 = 5
|
||||
}
|
||||
|
||||
fn frame(x voidptr) {
|
||||
mut app := &App(x)
|
||||
|
||||
app.tui.clear()
|
||||
|
||||
if app.points.len > 0 {
|
||||
app.tui.set_bg_color(app.color)
|
||||
mut last := app.points[0]
|
||||
for segment in app.points {
|
||||
// if the cursor moveds quickly enough, different events are not
|
||||
// necessarily touching, so we need to draw a line between them
|
||||
app.tui.draw_line(last.x, last.y, segment.x, segment.y)
|
||||
last = segment
|
||||
}
|
||||
app.tui.reset()
|
||||
|
||||
l := int(app.points.len / app.cut_rate) + 1
|
||||
app.points = app.points[l..].clone()
|
||||
}
|
||||
|
||||
ww := app.tui.window_width
|
||||
|
||||
app.tui.bold()
|
||||
app.tui.draw_text(ww / 6, 2, 'V term.input: cursor chaser demo')
|
||||
app.tui.draw_text((ww - ww / 6) - 14, 2, 'cut rate: ${(100 / app.cut_rate):3.0f}%')
|
||||
app.tui.horizontal_separator(3)
|
||||
app.tui.reset()
|
||||
app.tui.flush()
|
||||
}
|
||||
|
||||
fn event(e &tui.Event, x voidptr) {
|
||||
mut app := &App(x)
|
||||
|
||||
match e.typ {
|
||||
.key_down {
|
||||
match e.code {
|
||||
.escape {
|
||||
app.tui.set_cursor_position(0, 0)
|
||||
app.tui.flush()
|
||||
exit(0)
|
||||
}
|
||||
.space, .enter {
|
||||
app.color_idx++
|
||||
if app.color_idx == colors.len { app.color_idx = 0 }
|
||||
app.color = colors[app.color_idx]
|
||||
} else {}
|
||||
}
|
||||
} .mouse_move, .mouse_drag, .mouse_down {
|
||||
app.points << Point{ e.x, e.y }
|
||||
} .mouse_scroll {
|
||||
d := if e.direction == .up { 0.1 } else { -0.1 }
|
||||
app.cut_rate += d
|
||||
if app.cut_rate < 1 { app.cut_rate = 1 }
|
||||
} else {}
|
||||
}
|
||||
}
|
||||
|
||||
mut app := &App{}
|
||||
app.tui = tui.init(
|
||||
user_data: app,
|
||||
frame_fn: frame,
|
||||
event_fn: event,
|
||||
|
||||
hide_cursor: true
|
||||
)
|
||||
|
||||
app.tui.run()
|
33
examples/term.ui/event_viewer.v
Normal file
33
examples/term.ui/event_viewer.v
Normal file
@ -0,0 +1,33 @@
|
||||
import term.ui as tui
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
tui &tui.Context = 0
|
||||
}
|
||||
|
||||
fn event(e &tui.Event, x voidptr) {
|
||||
print('\x1b[0;0H\x1b[2J\x1b[3J') // Clear everything
|
||||
println('V term.input event viewer (press `esc` to exit)\n\n')
|
||||
|
||||
println(e)
|
||||
println('Raw event bytes: "${e.utf8.bytes().hex()}" = ${e.utf8.bytes()}')
|
||||
|
||||
if e.modifiers == tui.ctrl | tui.alt {
|
||||
println('CTRL + ALT')
|
||||
}
|
||||
|
||||
if e.typ == .key_down && e.code == .escape { exit(0) }
|
||||
}
|
||||
|
||||
mut app := &App{}
|
||||
app.tui = tui.init(
|
||||
user_data: app,
|
||||
event_fn: event
|
||||
|
||||
hide_cursor: true
|
||||
capture_events: true
|
||||
frame_rate: 60
|
||||
)
|
||||
|
||||
println('V term.input event viewer (press `esc` to exit)\n\n')
|
||||
app.tui.run()
|
90
examples/term.ui/rectangles.v
Normal file
90
examples/term.ui/rectangles.v
Normal file
@ -0,0 +1,90 @@
|
||||
import term.ui as tui
|
||||
import rand
|
||||
|
||||
struct Rect {
|
||||
mut:
|
||||
c tui.Color
|
||||
x int
|
||||
y int
|
||||
x2 int
|
||||
y2 int
|
||||
}
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
tui &tui.Context = 0
|
||||
rects []Rect
|
||||
cur_rect Rect
|
||||
is_drag bool
|
||||
redraw bool
|
||||
}
|
||||
|
||||
fn random_color() tui.Color {
|
||||
return {
|
||||
r: byte(rand.intn(256))
|
||||
g: byte(rand.intn(256))
|
||||
b: byte(rand.intn(256))
|
||||
}
|
||||
}
|
||||
|
||||
fn event(e &tui.Event, x voidptr) {
|
||||
mut app := &App(x)
|
||||
match e.typ {
|
||||
.mouse_down {
|
||||
app.is_drag = true
|
||||
app.cur_rect = {
|
||||
c: random_color()
|
||||
x: e.x
|
||||
y: e.y
|
||||
x2: e.x
|
||||
y2: e.y
|
||||
}
|
||||
}
|
||||
.mouse_drag {
|
||||
app.cur_rect.x2 = e.x
|
||||
app.cur_rect.y2 = e.y
|
||||
} .mouse_up {
|
||||
app.rects << app.cur_rect
|
||||
app.is_drag = false
|
||||
} .key_down {
|
||||
if e.code == .c { app.rects.clear() }
|
||||
else if e.code == .escape { exit(0) }
|
||||
} else {}
|
||||
}
|
||||
app.redraw = true
|
||||
}
|
||||
|
||||
fn frame(x voidptr) {
|
||||
mut app := &App(x)
|
||||
if !app.redraw { return }
|
||||
|
||||
app.tui.clear()
|
||||
|
||||
for rect in app.rects {
|
||||
app.tui.set_bg_color(rect.c)
|
||||
app.tui.draw_rect(rect.x, rect.y, rect.x2, rect.y2)
|
||||
}
|
||||
|
||||
if app.is_drag {
|
||||
r := app.cur_rect
|
||||
app.tui.set_bg_color(r.c)
|
||||
app.tui.draw_empty_rect(r.x, r.y, r.x2, r.y2)
|
||||
}
|
||||
|
||||
app.tui.reset_bg_color()
|
||||
app.tui.flush()
|
||||
app.redraw = false
|
||||
}
|
||||
|
||||
|
||||
mut app := &App{}
|
||||
app.tui = tui.init(
|
||||
user_data: app,
|
||||
event_fn: event,
|
||||
frame_fn: frame
|
||||
|
||||
hide_cursor: true
|
||||
frame_rate: 60
|
||||
)
|
||||
|
||||
app.tui.run()
|
335
examples/term.ui/term_drawing.v
Normal file
335
examples/term.ui/term_drawing.v
Normal file
@ -0,0 +1,335 @@
|
||||
module main
|
||||
|
||||
import term.ui as tui
|
||||
|
||||
// The color palette, taken from Google's Material design
|
||||
const (
|
||||
colors = [
|
||||
[
|
||||
tui.Color{239, 154, 154}
|
||||
tui.Color{244, 143, 177}
|
||||
tui.Color{206, 147, 216}
|
||||
tui.Color{179, 157, 219}
|
||||
tui.Color{159, 168, 218}
|
||||
tui.Color{144, 202, 249}
|
||||
tui.Color{129, 212, 250}
|
||||
tui.Color{128, 222, 234}
|
||||
tui.Color{128, 203, 196}
|
||||
tui.Color{165, 214, 167}
|
||||
tui.Color{197, 225, 165}
|
||||
tui.Color{230, 238, 156}
|
||||
tui.Color{255, 245, 157}
|
||||
tui.Color{255, 224, 130}
|
||||
tui.Color{255, 204, 128}
|
||||
tui.Color{255, 171, 145}
|
||||
tui.Color{188, 170, 164}
|
||||
tui.Color{238, 238, 238}
|
||||
tui.Color{176, 190, 197}
|
||||
], [
|
||||
tui.Color{244, 67, 54}
|
||||
tui.Color{233, 30, 99}
|
||||
tui.Color{156, 39, 176}
|
||||
tui.Color{103, 58, 183}
|
||||
tui.Color{63, 81, 181}
|
||||
tui.Color{33, 150, 243}
|
||||
tui.Color{3, 169, 244}
|
||||
tui.Color{0, 188, 212}
|
||||
tui.Color{0, 150, 136}
|
||||
tui.Color{76, 175, 80}
|
||||
tui.Color{139, 195, 74}
|
||||
tui.Color{205, 220, 57}
|
||||
tui.Color{255, 235, 59}
|
||||
tui.Color{255, 193, 7}
|
||||
tui.Color{255, 152, 0}
|
||||
tui.Color{255, 87, 34}
|
||||
tui.Color{121, 85, 72}
|
||||
tui.Color{120, 120, 120}
|
||||
tui.Color{96, 125, 139}
|
||||
], [
|
||||
tui.Color{198, 40, 40}
|
||||
tui.Color{173, 20, 87}
|
||||
tui.Color{106, 27, 154}
|
||||
tui.Color{69, 39, 160}
|
||||
tui.Color{40, 53, 147}
|
||||
tui.Color{21, 101, 192}
|
||||
tui.Color{2, 119, 189}
|
||||
tui.Color{0, 131, 143}
|
||||
tui.Color{0, 105, 92}
|
||||
tui.Color{46, 125, 50}
|
||||
tui.Color{85, 139, 47}
|
||||
tui.Color{158, 157, 36}
|
||||
tui.Color{249, 168, 37}
|
||||
tui.Color{255, 143, 0}
|
||||
tui.Color{239, 108, 0}
|
||||
tui.Color{216, 67, 21}
|
||||
tui.Color{78, 52, 46}
|
||||
tui.Color{33, 33, 33}
|
||||
tui.Color{55, 71, 79}
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
const (
|
||||
frame_rate = 30 // fps
|
||||
msg_display_time = 5 * frame_rate
|
||||
|
||||
w = 200
|
||||
h = 100
|
||||
|
||||
space = ' '
|
||||
spaces = ' '
|
||||
|
||||
select_color = 'Select color:'
|
||||
select_size = 'Size: + -'
|
||||
|
||||
help_1 = '╭────────╮'
|
||||
help_2 = '│ HELP │'
|
||||
help_3 = '╰────────╯'
|
||||
)
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
tui &tui.Context = 0
|
||||
header_text []string
|
||||
mouse_pos Point
|
||||
msg string
|
||||
msg_hide_tick int
|
||||
primary_color tui.Color = colors[1][6]
|
||||
secondary_color tui.Color = colors[1][9]
|
||||
drawing [][]tui.Color = [][]tui.Color{ len: h, init: []tui.Color{ len: w } }
|
||||
size int = 1
|
||||
should_redraw bool = true
|
||||
is_dragging bool
|
||||
}
|
||||
|
||||
struct Point {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut app := &App{}
|
||||
app.tui = tui.init(
|
||||
user_data: app
|
||||
frame_fn: frame
|
||||
event_fn: event
|
||||
|
||||
frame_rate: frame_rate
|
||||
hide_cursor: true
|
||||
)
|
||||
|
||||
app.tui.run()
|
||||
}
|
||||
|
||||
fn frame(x voidptr) {
|
||||
mut app := &App(x)
|
||||
|
||||
mut redraw := app.should_redraw
|
||||
|
||||
if app.msg != '' && app.tui.frame_count >= app.msg_hide_tick {
|
||||
app.msg = ''
|
||||
redraw = true
|
||||
}
|
||||
|
||||
if redraw {
|
||||
app.render(false)
|
||||
app.should_redraw = false
|
||||
}
|
||||
}
|
||||
|
||||
fn event(event &tui.Event, x voidptr) {
|
||||
mut app := &App(x)
|
||||
|
||||
match event.typ {
|
||||
.mouse_down {
|
||||
app.is_dragging = true
|
||||
if app.tui.window_height - event.y < 5 {
|
||||
app.footer_click(event)
|
||||
} else {
|
||||
app.paint(event)
|
||||
}
|
||||
} .mouse_up {
|
||||
app.is_dragging = false
|
||||
} .mouse_drag {
|
||||
app.mouse_pos = { x: event.x, y: event.y }
|
||||
app.paint(event)
|
||||
} .mouse_move {
|
||||
app.mouse_pos = { x: event.x, y: event.y }
|
||||
} .mouse_scroll {
|
||||
if event.direction == .down { app.inc_size() } else { app.dec_size() }
|
||||
} .key_down {
|
||||
match event.code {
|
||||
.c {
|
||||
app.drawing = [][]tui.Color{ len: h, init: []tui.Color{ len: w } }
|
||||
} .escape {
|
||||
app.render(true)
|
||||
exit(0)
|
||||
} else {}
|
||||
}
|
||||
} else {}
|
||||
}
|
||||
|
||||
app.should_redraw = true
|
||||
}
|
||||
|
||||
fn (mut app App) render(paint_only bool) {
|
||||
app.tui.clear()
|
||||
|
||||
app.draw_header()
|
||||
app.draw_content()
|
||||
|
||||
if !paint_only {
|
||||
app.draw_footer()
|
||||
app.draw_cursor()
|
||||
}
|
||||
|
||||
app.tui.flush()
|
||||
}
|
||||
|
||||
fn (mut app App) set_pixel(x_ int, y_ int, c tui.Color) {
|
||||
// Term coords start at 1, and adjust for the header
|
||||
x, y := x_ - 1, y_ - 4
|
||||
if y < 0 || app.tui.window_height - y < 3 { return }
|
||||
if y >= app.drawing.len || x < 0 || x >= app.drawing[0].len { return }
|
||||
app.drawing[y][x] = c
|
||||
}
|
||||
|
||||
fn (mut app App) paint(event &tui.Event) {
|
||||
x_start, y_start := int(f32((event.x - 1) / 2) - app.size / 2 + 1), event.y - app.size / 2
|
||||
color := if event.button == .primary { app.primary_color } else { app.secondary_color }
|
||||
|
||||
for x in x_start .. x_start + app.size {
|
||||
for y in y_start .. y_start + app.size {
|
||||
app.set_pixel(x, y, color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut app App) draw_content() {
|
||||
w, mut h := app.tui.window_width / 2, app.tui.window_height - 8
|
||||
if h > app.drawing.len {
|
||||
h = app.drawing.len
|
||||
}
|
||||
|
||||
for row_idx, row in app.drawing[..h] {
|
||||
app.tui.set_cursor_position(0, row_idx + 4)
|
||||
mut last := tui.Color{ 0, 0, 0 }
|
||||
for cell in row[..w] {
|
||||
if cell.r == 0 && cell.g == 0 && cell.b == 0 {
|
||||
if !(cell.r == last.r && cell.g == last.g && cell.b == last.b) {
|
||||
app.tui.reset()
|
||||
}
|
||||
} else {
|
||||
if !(cell.r == last.r && cell.g == last.g && cell.b == last.b) {
|
||||
app.tui.set_bg_color(cell)
|
||||
}
|
||||
}
|
||||
app.tui.write(spaces)
|
||||
last = cell
|
||||
}
|
||||
app.tui.reset()
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut app App) draw_cursor() {
|
||||
if app.mouse_pos.y in [3, app.tui.window_height - 5] {
|
||||
// inside the horizontal separators
|
||||
return
|
||||
}
|
||||
|
||||
cursor_color := if app.is_dragging { tui.Color{ 220, 220, 220 } } else { tui.Color{ 160, 160, 160 } }
|
||||
app.tui.set_bg_color(cursor_color)
|
||||
|
||||
if app.mouse_pos.y >= 3 && app.mouse_pos.y <= app.tui.window_height - 4 {
|
||||
// inside the main content
|
||||
mut x_start := int(f32((app.mouse_pos.x - 1) / 2) - app.size / 2 + 1) * 2 - 1
|
||||
mut y_start := app.mouse_pos.y - app.size / 2
|
||||
mut x_end := x_start + app.size * 2 - 1
|
||||
mut y_end := y_start + app.size - 1
|
||||
|
||||
if x_start < 1 { x_start = 1 }
|
||||
if y_start < 4 { y_start = 4 }
|
||||
if x_end > app.tui.window_width { x_end = app.tui.window_width }
|
||||
if y_end > app.tui.window_height - 5 { y_end = app.tui.window_height - 5 }
|
||||
|
||||
app.tui.draw_rect(x_start, y_start, x_end, y_end)
|
||||
} else {
|
||||
app.tui.draw_text(app.mouse_pos.x, app.mouse_pos.y, space)
|
||||
}
|
||||
app.tui.reset()
|
||||
}
|
||||
|
||||
fn (mut app App) draw_header() {
|
||||
if app.msg != '' {
|
||||
app.tui.set_color(r: 0, g: 0, b: 0)
|
||||
app.tui.set_bg_color(r: 220, g: 220, b: 220)
|
||||
app.tui.draw_text(0, 0, ' $app.msg ')
|
||||
app.tui.reset()
|
||||
}
|
||||
|
||||
app.tui.draw_text(3, 2, /* 'tick: $app.tui.frame_count | ' + */ 'terminal size: ($app.tui.window_width, $app.tui.window_height) | primary color: $app.primary_color.hex() | secondary color: $app.secondary_color.hex()')
|
||||
app.tui.horizontal_separator(3)
|
||||
}
|
||||
|
||||
fn (mut app App) draw_footer() {
|
||||
ww, wh := app.tui.window_width, app.tui.window_height
|
||||
|
||||
app.tui.horizontal_separator(wh - 4)
|
||||
|
||||
for i, color_row in colors {
|
||||
for j, color in color_row {
|
||||
x := j * 3 + 19
|
||||
y := wh - 3 + i
|
||||
|
||||
app.tui.set_bg_color(color)
|
||||
app.tui.draw_rect(x, y, x+1, y)
|
||||
}
|
||||
}
|
||||
app.tui.reset_bg_color()
|
||||
|
||||
app.tui.draw_text(3, wh - 3, select_color)
|
||||
app.tui.bold()
|
||||
app.tui.draw_text(3, wh - 1, select_size)
|
||||
app.tui.reset()
|
||||
|
||||
if ww >= 90 {
|
||||
app.tui.draw_text(80, wh - 3, help_1)
|
||||
app.tui.draw_text(80, wh - 2, help_2)
|
||||
app.tui.draw_text(80, wh - 1, help_3)
|
||||
}
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn (mut app App) inc_size() {
|
||||
if app.size < 20 { app.size++ }
|
||||
app.show_msg('inc. size: $app.size', 1)
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn (mut app App) dec_size() {
|
||||
if app.size > 1 { app.size-- }
|
||||
app.show_msg('dec. size: $app.size', 1)
|
||||
}
|
||||
|
||||
fn (mut app App) footer_click(event &tui.Event) {
|
||||
footer_y := 3 - (app.tui.window_height - event.y)
|
||||
match event.x {
|
||||
8...11 {
|
||||
app.inc_size()
|
||||
} 12...15 {
|
||||
app.dec_size()
|
||||
} 18...75 {
|
||||
if (event.x % 3) == 0 { return } // Inside the gap between tiles
|
||||
idx := footer_y * 19 - 6 + event.x / 3
|
||||
color := colors[idx / 19][idx % 19]
|
||||
if event.button == .primary { app.primary_color = color } else { app.secondary_color = color }
|
||||
app.show_msg('set ${event.button.str().to_lower()} color idx: $idx', 1)
|
||||
} else {}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut app App) show_msg(text string, time int) {
|
||||
frames := time * frame_rate
|
||||
app.msg_hide_tick = if time > 0 { int(app.tui.frame_count) + frames } else { -1 }
|
||||
app.msg = text
|
||||
}
|
Reference in New Issue
Block a user