mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
term.ui: allow setting the terminal title (#6809)
This commit is contained in:
parent
7feb1742d3
commit
c315218ed1
@ -6,15 +6,13 @@ mut:
|
||||
}
|
||||
|
||||
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')
|
||||
}
|
||||
mut app := &App(x)
|
||||
app.tui.clear()
|
||||
app.tui.set_cursor_position(0, 0)
|
||||
app.tui.write('V term.input event viewer (press `esc` to exit)\n\n')
|
||||
app.tui.write('$e')
|
||||
app.tui.write('\n\nRaw event bytes: "${e.utf8.bytes().hex()}" = ${e.utf8.bytes()}')
|
||||
app.tui.flush()
|
||||
|
||||
if e.typ == .key_down && e.code == .escape { exit(0) }
|
||||
}
|
||||
@ -24,6 +22,7 @@ app.tui = tui.init(
|
||||
user_data: app,
|
||||
event_fn: event
|
||||
|
||||
window_title: 'V term.ui event viewer'
|
||||
hide_cursor: true
|
||||
capture_events: true
|
||||
frame_rate: 60
|
||||
|
@ -116,6 +116,7 @@ fn main() {
|
||||
event_fn: event
|
||||
frame_rate: frame_rate
|
||||
hide_cursor: true
|
||||
window_title: 'V terminal pixelart drawing app'
|
||||
})
|
||||
app.mouse_pos.x = 40
|
||||
app.mouse_pos.y = 15
|
||||
@ -398,11 +399,13 @@ fn (mut app App) draw_footer() {
|
||||
app.tui.bold()
|
||||
app.tui.draw_text(3, wh - 1, '$select_size $app.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)
|
||||
}
|
||||
|
||||
// TODO: help button
|
||||
// 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]
|
||||
@ -436,6 +439,7 @@ fn (mut app App) footer_click(event &tui.Event) {
|
||||
return
|
||||
}
|
||||
idx := footer_y * 19 - 6 + event.x / 3
|
||||
if idx < 0 || idx > 56 { return }
|
||||
color := colors[idx / 19][idx % 19]
|
||||
if event.button == .primary {
|
||||
app.primary_color = color
|
||||
|
@ -5,7 +5,39 @@ A V module for designing terminal UI apps
|
||||
#### Quickstart
|
||||
|
||||
```v
|
||||
// todo
|
||||
import term.ui as tui
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
tui &tui.Context = 0
|
||||
}
|
||||
|
||||
fn event(e &tui.Event, x voidptr) {
|
||||
mut app := &App(x)
|
||||
println(e)
|
||||
}
|
||||
|
||||
fn frame(x voidptr) {
|
||||
mut app := &App(x)
|
||||
|
||||
app.tui.clear()
|
||||
app.tui.set_bg_color(r: 63, g: 81, b: 181)
|
||||
app.tui.draw_rect(20, 6, 41, 10)
|
||||
app.tui.draw_text(24, 8, 'Hello from V!')
|
||||
app.tui.set_cursor_position(0, 0)
|
||||
|
||||
app.tui.reset()
|
||||
app.tui.flush()
|
||||
}
|
||||
|
||||
mut app := &App{}
|
||||
app.tui = tui.init(
|
||||
user_data: app,
|
||||
event_fn: event,
|
||||
frame_fn: frame
|
||||
hide_cursor: true
|
||||
)
|
||||
app.tui.run()
|
||||
```
|
||||
|
||||
See the `/examples/term.ui/` folder for more usage examples.
|
||||
@ -22,6 +54,7 @@ See the `/examples/term.ui/` folder for more usage examples.
|
||||
- `frame_rate int = 30` - the number of times per second that the `frame` callback will be fired. 30fps is a nice balance between smoothness and performance, but you can increase or lower it as you wish.
|
||||
- `hide_cursor bool` - whether to hide the mouse cursor. Useful if you want to use your own.
|
||||
- `capture_events bool` - sets the terminal into raw mode, which makes it intercept some escape codes such as `ctrl + c` and `ctrl + z`. Useful if you want to use those key combinations in your app.
|
||||
- `window_title string` - sets the title of the terminal window. This may be changed later, by calling the `set_window_title()` method.
|
||||
- `reset []int = [1, 2, 3, 4, 6, 7, 8, 9, 11, 13, 14, 15, 19]` - a list of reset signals, to setup handlers to cleanup the terminal state when they're received. You should not need to change this, unless you know what you're doing.
|
||||
|
||||
All of these fields may be omitted, in which case, the default value will be used. In the case of the various callbacks, they will not be fired if a handler has not been specified.
|
||||
|
@ -187,6 +187,7 @@ pub struct Config {
|
||||
frame_rate int = 30
|
||||
use_x11 bool
|
||||
|
||||
window_title string
|
||||
hide_cursor bool
|
||||
capture_events bool
|
||||
// All kill signals
|
||||
|
@ -9,12 +9,17 @@ pub fn init(cfg Config) &Context {
|
||||
cfg: cfg,
|
||||
read_buf: []byte{ cap: cfg.buffer_size }
|
||||
}
|
||||
ctx.save_title()
|
||||
|
||||
if cfg.hide_cursor {
|
||||
s := '\x1b[?25l'
|
||||
C.write(C.STDOUT_FILENO, s.str, s.len)
|
||||
print('\x1b[?25l')
|
||||
}
|
||||
|
||||
if cfg.window_title != '' {
|
||||
print('\x1b]0;$cfg.window_title\x07')
|
||||
}
|
||||
|
||||
// lmao
|
||||
unsafe {
|
||||
x := &ctx_ptr
|
||||
*x = ctx
|
||||
@ -23,6 +28,15 @@ pub fn init(cfg Config) &Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
pub fn (mut ctx Context) save_title() {
|
||||
// restore the previously saved terminal title
|
||||
print('\x1b[22;0t')
|
||||
}
|
||||
pub fn (mut ctx Context) load_title() {
|
||||
// restore the previously saved terminal title
|
||||
print('\x1b[23;0t')
|
||||
}
|
||||
|
||||
pub fn (mut ctx Context) run() {
|
||||
if ctx.cfg.use_x11 {
|
||||
ctx.fail('error: x11 backend not implemented yet')
|
||||
|
@ -1,10 +1,22 @@
|
||||
module ui
|
||||
|
||||
const (
|
||||
not_implemented_yet = "term.input: error: Windows support isn't implemented yet"
|
||||
)
|
||||
|
||||
pub fn init(cfg Config) &Context {
|
||||
panic("term.input: error: Windows support isn't implemented yet")
|
||||
panic(not_implemented_yet)
|
||||
return &Context{}
|
||||
}
|
||||
|
||||
pub fn (mut ctx Context) run() {
|
||||
panic("term.input: error: Windows support isn't implemented yet")
|
||||
panic(not_implemented_yet)
|
||||
}
|
||||
|
||||
pub fn (mut ctx Context) save_title() {
|
||||
panic(not_implemented_yet)
|
||||
}
|
||||
|
||||
pub fn (mut ctx Context) load_title() {
|
||||
panic(not_implemented_yet)
|
||||
}
|
||||
|
@ -41,6 +41,15 @@ fn get_terminal_size() (u16, u16) {
|
||||
return winsz.ws_row, winsz.ws_col
|
||||
}
|
||||
|
||||
fn restore_terminal_state() {
|
||||
termios_reset()
|
||||
mut c := ctx_ptr
|
||||
if c != 0 {
|
||||
c.load_title()
|
||||
}
|
||||
println('')
|
||||
}
|
||||
|
||||
fn (mut ctx Context) termios_setup() {
|
||||
mut termios := get_termios()
|
||||
|
||||
@ -64,11 +73,12 @@ fn (mut ctx Context) termios_setup() {
|
||||
ctx.window_height, ctx.window_width = get_terminal_size()
|
||||
|
||||
// Reset console on exit
|
||||
C.atexit(termios_reset)
|
||||
os.signal(C.SIGTSTP, termios_reset)
|
||||
C.atexit(restore_terminal_state)
|
||||
os.signal(C.SIGTSTP, restore_terminal_state)
|
||||
os.signal(C.SIGCONT, fn () {
|
||||
mut c := ctx_ptr
|
||||
if c != 0 {
|
||||
c.save_title()
|
||||
c.termios_setup()
|
||||
c.window_height, c.window_width = get_terminal_size()
|
||||
mut event := &Event{
|
||||
@ -111,56 +121,6 @@ fn termios_reset() {
|
||||
|
||||
///////////////////////////////////////////
|
||||
|
||||
/*
|
||||
fn (mut ctx Context) termios_loop() {
|
||||
frame_time := 1_000_000 / ctx.cfg.frame_rate
|
||||
mut init_called := false
|
||||
|
||||
mut sw := time.new_stopwatch(auto_start: false)
|
||||
|
||||
mut last_frame_time := 0
|
||||
mut sleep_len := 0
|
||||
|
||||
for {
|
||||
sw.restart()
|
||||
if !init_called {
|
||||
ctx.init()
|
||||
init_called = true
|
||||
}
|
||||
for _ in 0 .. 7 {
|
||||
// println('SLEEPING: $sleep_len')
|
||||
if sleep_len > 0 {
|
||||
time.usleep(sleep_len)
|
||||
}
|
||||
if ctx.cfg.event_fn != voidptr(0) {
|
||||
len := C.read(C.STDIN_FILENO, ctx.read_buf.data, ctx.read_buf.cap - ctx.read_buf.len)
|
||||
if len > 0 {
|
||||
ctx.resize_arr(len)
|
||||
ctx.parse_events()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.frame()
|
||||
|
||||
sw.pause()
|
||||
last_frame_time = int(sw.elapsed().microseconds())
|
||||
|
||||
if
|
||||
println('Sleeping for $frame_time - $last_frame_time = ${frame_time - last_frame_time}')
|
||||
// time.usleep(frame_time - last_frame_time - sleep_len * 7)
|
||||
last_frame_time = 0
|
||||
|
||||
sw.start()
|
||||
sw.pause()
|
||||
last_frame_time += int(sw.elapsed().microseconds())
|
||||
sleep_len = (frame_time - last_frame_time) / 8
|
||||
|
||||
ctx.frame_count++
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO: do multiple sleep/read cycles, rather than one big one
|
||||
fn (mut ctx Context) termios_loop() {
|
||||
frame_time := 1_000_000 / ctx.cfg.frame_rate
|
||||
@ -252,7 +212,7 @@ fn escape_end(buf string) int {
|
||||
for {
|
||||
if i + 1 == buf.len { return buf.len }
|
||||
|
||||
if buf[i].is_letter() {
|
||||
if buf[i].is_letter() || buf[i] == `~` {
|
||||
if buf[i] == `O` && i + 2 <= buf.len {
|
||||
n := buf[i+1]
|
||||
if (n >= `A` && n <= `D`) || (n >= `P` && n <= `S`) || n == `F` || n == `H` {
|
||||
@ -260,7 +220,8 @@ fn escape_end(buf string) int {
|
||||
}
|
||||
}
|
||||
return i + 1
|
||||
}
|
||||
// escape hatch to avoid potential issues/crashes, although ideally this should never eval to true
|
||||
} else if buf[i + 1] == 0x1b { return i + 1 }
|
||||
i++
|
||||
}
|
||||
// this point should be unreachable
|
||||
@ -299,7 +260,8 @@ fn escape_sequence(buf_ string) (&Event, int) {
|
||||
// Mouse events
|
||||
// ----------------
|
||||
|
||||
if buf.len > 2 && buf[1] == `<` { // Mouse control
|
||||
// TODO: rxvt uses different escape sequences for mouse events :/
|
||||
if buf.len > 2 && buf[1] == `<` {
|
||||
split := buf[2..].split(';')
|
||||
if split.len < 3 { return &Event(0), 0 }
|
||||
|
||||
|
@ -79,15 +79,10 @@ pub fn (mut ctx Context) clear() {
|
||||
ctx.write('\x1b[2J\x1b[3J')
|
||||
}
|
||||
|
||||
// pub const (
|
||||
// default_color = gx.rgb(183, 101, 94) // hopefully nobody actually tries to use this color...
|
||||
// )
|
||||
|
||||
// pub struct DrawConfig {
|
||||
// pub mut:
|
||||
// fg_color gx.Color = default_color
|
||||
// bg_color gx.Color = default_color
|
||||
// }
|
||||
[inline]
|
||||
pub fn (mut ctx Context) set_window_title(s string) {
|
||||
print('\x1b]0;$s\x07')
|
||||
}
|
||||
|
||||
[inline]
|
||||
pub fn (mut ctx Context) draw_point(x int, y int) {
|
||||
|
Loading…
Reference in New Issue
Block a user