gg: add text rendering, keyboard event handling for JS and other fixes (#12932)
parent
6eb44f472a
commit
2b9f993574
|
@ -0,0 +1,17 @@
|
||||||
|
# snek
|
||||||
|
|
||||||
|
Snake game implemented using `gg` module.
|
||||||
|
|
||||||
|
# Compiling & running
|
||||||
|
|
||||||
|
## Compiling to binary
|
||||||
|
```sh
|
||||||
|
v -prod examples/snek/snek.v
|
||||||
|
./examples/snek/snek # run snek game!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compiling to JS
|
||||||
|
```sh
|
||||||
|
v -b js_browser examples/snek/snek.js.v
|
||||||
|
```
|
||||||
|
And then open `examples/snek/index.html` in your favourite browser.
|
|
@ -0,0 +1,6 @@
|
||||||
|
<body class="main">
|
||||||
|
<title>gg</title>
|
||||||
|
<canvas style="border: 1px solid black;" width="700" height="800" id="canvas"></canvas>
|
||||||
|
<script type="text/javascript" src="snek.js"></script>
|
||||||
|
|
||||||
|
</body>
|
|
@ -0,0 +1,197 @@
|
||||||
|
import gg
|
||||||
|
import gx
|
||||||
|
// import sokol.sapp
|
||||||
|
import time
|
||||||
|
import rand
|
||||||
|
|
||||||
|
// constants
|
||||||
|
const (
|
||||||
|
top_height = 100
|
||||||
|
canvas_size = 700
|
||||||
|
game_size = 17
|
||||||
|
tile_size = canvas_size / game_size
|
||||||
|
tick_rate_ms = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
// types
|
||||||
|
struct Pos {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (a Pos) + (b Pos) Pos {
|
||||||
|
return Pos{a.x + b.x, a.y + b.y}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (a Pos) - (b Pos) Pos {
|
||||||
|
return Pos{a.x - b.x, a.y - b.y}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Direction {
|
||||||
|
up
|
||||||
|
down
|
||||||
|
left
|
||||||
|
right
|
||||||
|
}
|
||||||
|
|
||||||
|
struct App {
|
||||||
|
mut:
|
||||||
|
gg &gg.Context
|
||||||
|
score int
|
||||||
|
snake []Pos
|
||||||
|
dir Direction
|
||||||
|
last_dir Direction
|
||||||
|
food Pos
|
||||||
|
start_time i64
|
||||||
|
last_tick i64
|
||||||
|
}
|
||||||
|
|
||||||
|
// utility
|
||||||
|
fn (mut app App) reset_game() {
|
||||||
|
app.score = 0
|
||||||
|
app.snake = [
|
||||||
|
Pos{3, 8},
|
||||||
|
Pos{2, 8},
|
||||||
|
Pos{1, 8},
|
||||||
|
Pos{0, 8},
|
||||||
|
]
|
||||||
|
app.dir = .right
|
||||||
|
app.last_dir = app.dir
|
||||||
|
app.food = Pos{10, 8}
|
||||||
|
app.start_time = time.ticks()
|
||||||
|
app.last_tick = time.ticks()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) move_food() {
|
||||||
|
for {
|
||||||
|
x := rand.int_in_range(0, game_size)
|
||||||
|
y := rand.int_in_range(0, game_size)
|
||||||
|
app.food = Pos{x, y}
|
||||||
|
|
||||||
|
if app.food !in app.snake {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// events
|
||||||
|
fn on_keydown(key gg.KeyCode, mod gg.Modifier, mut app App) {
|
||||||
|
match key {
|
||||||
|
.w, .up {
|
||||||
|
if app.last_dir != .down {
|
||||||
|
app.dir = .up
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.s, .down {
|
||||||
|
if app.last_dir != .up {
|
||||||
|
app.dir = .down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.a, .left {
|
||||||
|
if app.last_dir != .right {
|
||||||
|
app.dir = .left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.d, .right {
|
||||||
|
if app.last_dir != .left {
|
||||||
|
app.dir = .right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_frame(mut app App) {
|
||||||
|
app.gg.begin()
|
||||||
|
|
||||||
|
now := time.ticks()
|
||||||
|
|
||||||
|
if now - app.last_tick >= tick_rate_ms {
|
||||||
|
app.last_tick = now
|
||||||
|
|
||||||
|
// finding delta direction
|
||||||
|
delta_dir := match app.dir {
|
||||||
|
.up { Pos{0, -1} }
|
||||||
|
.down { Pos{0, 1} }
|
||||||
|
.left { Pos{-1, 0} }
|
||||||
|
.right { Pos{1, 0} }
|
||||||
|
}
|
||||||
|
|
||||||
|
// "snaking" along
|
||||||
|
mut prev := app.snake[0]
|
||||||
|
app.snake[0] = app.snake[0] + delta_dir
|
||||||
|
|
||||||
|
for i in 1 .. app.snake.len {
|
||||||
|
tmp := app.snake[i]
|
||||||
|
app.snake[i] = prev
|
||||||
|
prev = tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
// adding last segment
|
||||||
|
if app.snake[0] == app.food {
|
||||||
|
app.move_food()
|
||||||
|
app.score++
|
||||||
|
/*
|
||||||
|
if app.score > app.best {
|
||||||
|
app.best = app.score
|
||||||
|
app.best.save()
|
||||||
|
}*/
|
||||||
|
app.snake << app.snake.last() + app.snake.last() - app.snake[app.snake.len - 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
app.last_dir = app.dir
|
||||||
|
}
|
||||||
|
// drawing snake
|
||||||
|
for pos in app.snake {
|
||||||
|
app.gg.draw_rect(tile_size * pos.x, tile_size * pos.y + top_height, tile_size,
|
||||||
|
tile_size, gx.blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// drawing food
|
||||||
|
app.gg.draw_rect(tile_size * app.food.x, tile_size * app.food.y + top_height, tile_size,
|
||||||
|
tile_size, gx.red)
|
||||||
|
|
||||||
|
// drawing top
|
||||||
|
app.gg.draw_rect(0, 0, canvas_size, top_height, gx.black)
|
||||||
|
app.gg.draw_text(350, top_height / 2, 'Score: $app.score', gx.TextCfg{
|
||||||
|
color: gx.white
|
||||||
|
align: .center
|
||||||
|
vertical_align: .middle
|
||||||
|
size: 80
|
||||||
|
})
|
||||||
|
|
||||||
|
// checking if snake bit itself
|
||||||
|
if app.snake[0] in app.snake[1..] {
|
||||||
|
app.reset_game()
|
||||||
|
}
|
||||||
|
// checking if snake hit a wall
|
||||||
|
if app.snake[0].x < 0 || app.snake[0].x >= game_size || app.snake[0].y < 0
|
||||||
|
|| app.snake[0].y >= game_size {
|
||||||
|
app.reset_game()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.gg.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup
|
||||||
|
fn main() {
|
||||||
|
mut app := App{
|
||||||
|
gg: &gg.Context{}
|
||||||
|
}
|
||||||
|
app.reset_game()
|
||||||
|
|
||||||
|
app.gg = gg.new_context(
|
||||||
|
bg_color: gx.white
|
||||||
|
frame_fn: on_frame
|
||||||
|
keydown_fn: on_keydown
|
||||||
|
user_data: &app
|
||||||
|
width: canvas_size
|
||||||
|
height: top_height + canvas_size
|
||||||
|
create_window: true
|
||||||
|
resizable: false
|
||||||
|
window_title: 'snek'
|
||||||
|
canvas: 'canvas'
|
||||||
|
)
|
||||||
|
|
||||||
|
app.gg.run()
|
||||||
|
}
|
348
vlib/gg/gg.js.v
348
vlib/gg/gg.js.v
|
@ -36,7 +36,7 @@ pub struct Event {
|
||||||
pub mut:
|
pub mut:
|
||||||
frame_count u64
|
frame_count u64
|
||||||
typ DOMEventType
|
typ DOMEventType
|
||||||
key_code DOMKeyCode
|
key_code KeyCode
|
||||||
char_code u32
|
char_code u32
|
||||||
key_repeat bool
|
key_repeat bool
|
||||||
modifiers u32
|
modifiers u32
|
||||||
|
@ -252,7 +252,13 @@ pub:
|
||||||
enable_dragndrop bool // enable file dropping (drag'n'drop), default is false
|
enable_dragndrop bool // enable file dropping (drag'n'drop), default is false
|
||||||
max_dropped_files int = 1 // max number of dropped files to process (default: 1)
|
max_dropped_files int = 1 // max number of dropped files to process (default: 1)
|
||||||
max_dropped_file_path_length int = 2048 // max length in bytes of a dropped UTF-8 file path (default: 2048)
|
max_dropped_file_path_length int = 2048 // max length in bytes of a dropped UTF-8 file path (default: 2048)
|
||||||
canvas JS.HTMLCanvasElement
|
canvas string
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = Size{0, 0}
|
||||||
|
|
||||||
|
pub fn window_size() Size {
|
||||||
|
return gg.size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
@ -284,9 +290,33 @@ pub mut:
|
||||||
pressed_keys [key_code_max]bool // an array representing all currently pressed keys
|
pressed_keys [key_code_max]bool // an array representing all currently pressed keys
|
||||||
pressed_keys_edge [key_code_max]bool // true when the previous state of pressed_keys,
|
pressed_keys_edge [key_code_max]bool // true when the previous state of pressed_keys,
|
||||||
context JS.CanvasRenderingContext2D [noinit]
|
context JS.CanvasRenderingContext2D [noinit]
|
||||||
|
canvas JS.HTMLCanvasElement [noinit]
|
||||||
// *before* the current event was different
|
// *before* the current event was different
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_canvas(elem JS.HTMLElement) &JS.HTMLCanvasElement {
|
||||||
|
match elem {
|
||||||
|
JS.HTMLCanvasElement {
|
||||||
|
return elem
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic('gg: element is not an HTMLCanvasElement')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_context(canvas JS.HTMLCanvasElement) JS.CanvasRenderingContext2D {
|
||||||
|
ctx := canvas.getContext('2d'.str, js_undefined()) or { panic('cannot get context') }
|
||||||
|
match ctx {
|
||||||
|
JS.CanvasRenderingContext2D {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic('failed to get 2D context')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_context(cfg Config) &Context {
|
pub fn new_context(cfg Config) &Context {
|
||||||
mut g := &Context{}
|
mut g := &Context{}
|
||||||
|
|
||||||
|
@ -294,24 +324,30 @@ pub fn new_context(cfg Config) &Context {
|
||||||
g.width = cfg.width
|
g.width = cfg.width
|
||||||
g.height = cfg.height
|
g.height = cfg.height
|
||||||
g.ui_mode = cfg.ui_mode
|
g.ui_mode = cfg.ui_mode
|
||||||
|
mut sz := gg.size
|
||||||
|
sz.height = g.height
|
||||||
|
sz.width = g.width
|
||||||
g.config = cfg
|
g.config = cfg
|
||||||
if isnil(cfg.user_data) {
|
if isnil(cfg.user_data) {
|
||||||
g.user_data = g
|
g.user_data = g
|
||||||
}
|
}
|
||||||
g.window = dom.window()
|
g.window = dom.window()
|
||||||
ctx := cfg.canvas.getContext('2d'.str, js_undefined()) or { panic('') }
|
document := dom.document
|
||||||
match ctx {
|
canvas_elem := document.getElementById(cfg.canvas.str) or {
|
||||||
JS.CanvasRenderingContext2D {
|
panic('gg: cannot get canvas element')
|
||||||
g.context = ctx
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
panic('gg: cannot get 2D context')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
canvas := get_canvas(canvas_elem)
|
||||||
|
g.canvas = canvas
|
||||||
|
g.context = get_context(g.canvas)
|
||||||
|
|
||||||
mouse_down_event_handler := fn [mut g] (event JS.Event) {
|
mouse_down_event_handler := fn [mut g] (event JS.Event) {
|
||||||
match event {
|
match event {
|
||||||
JS.MouseEvent {
|
JS.MouseEvent {
|
||||||
e := g.handle_mouse_event(event)
|
e := g.handle_mouse_event(event, .mouse_down)
|
||||||
|
if !isnil(g.config.event_fn) {
|
||||||
|
f := g.config.event_fn
|
||||||
|
f(e, g.config.user_data)
|
||||||
|
}
|
||||||
if !isnil(g.config.click_fn) {
|
if !isnil(g.config.click_fn) {
|
||||||
f := g.config.click_fn
|
f := g.config.click_fn
|
||||||
f(e.mouse_x, e.mouse_y, e.mouse_button, g.config.user_data)
|
f(e.mouse_x, e.mouse_y, e.mouse_button, g.config.user_data)
|
||||||
|
@ -324,7 +360,11 @@ pub fn new_context(cfg Config) &Context {
|
||||||
mouse_up_event_handler := fn [mut g] (event JS.Event) {
|
mouse_up_event_handler := fn [mut g] (event JS.Event) {
|
||||||
match event {
|
match event {
|
||||||
JS.MouseEvent {
|
JS.MouseEvent {
|
||||||
e := g.handle_mouse_event(event)
|
e := g.handle_mouse_event(event, .mouse_up)
|
||||||
|
if !isnil(g.config.event_fn) {
|
||||||
|
f := g.config.event_fn
|
||||||
|
f(e, g.config.user_data)
|
||||||
|
}
|
||||||
if !isnil(g.config.unclick_fn) {
|
if !isnil(g.config.unclick_fn) {
|
||||||
f := g.config.unclick_fn
|
f := g.config.unclick_fn
|
||||||
f(e.mouse_x, e.mouse_y, e.mouse_button, g.config.user_data)
|
f(e.mouse_x, e.mouse_y, e.mouse_button, g.config.user_data)
|
||||||
|
@ -336,7 +376,11 @@ pub fn new_context(cfg Config) &Context {
|
||||||
mouse_move_event_handler := fn [mut g] (event JS.Event) {
|
mouse_move_event_handler := fn [mut g] (event JS.Event) {
|
||||||
match event {
|
match event {
|
||||||
JS.MouseEvent {
|
JS.MouseEvent {
|
||||||
e := g.handle_mouse_event(event)
|
e := g.handle_mouse_event(event, .mouse_move)
|
||||||
|
if !isnil(g.config.event_fn) {
|
||||||
|
f := g.config.event_fn
|
||||||
|
f(e, g.config.user_data)
|
||||||
|
}
|
||||||
if !isnil(g.config.move_fn) {
|
if !isnil(g.config.move_fn) {
|
||||||
f := g.config.move_fn
|
f := g.config.move_fn
|
||||||
f(e.mouse_x, e.mouse_y, g.config.user_data)
|
f(e.mouse_x, e.mouse_y, g.config.user_data)
|
||||||
|
@ -349,7 +393,11 @@ pub fn new_context(cfg Config) &Context {
|
||||||
mouse_leave_event_handler := fn [mut g] (event JS.Event) {
|
mouse_leave_event_handler := fn [mut g] (event JS.Event) {
|
||||||
match event {
|
match event {
|
||||||
JS.MouseEvent {
|
JS.MouseEvent {
|
||||||
e := g.handle_mouse_event(event)
|
e := g.handle_mouse_event(event, .mouse_leave)
|
||||||
|
if !isnil(g.config.event_fn) {
|
||||||
|
f := g.config.event_fn
|
||||||
|
f(e, g.config.user_data)
|
||||||
|
}
|
||||||
if !isnil(g.config.leave_fn) {
|
if !isnil(g.config.leave_fn) {
|
||||||
f := g.config.leave_fn
|
f := g.config.leave_fn
|
||||||
f(e, g.config.user_data)
|
f(e, g.config.user_data)
|
||||||
|
@ -362,7 +410,11 @@ pub fn new_context(cfg Config) &Context {
|
||||||
mouse_enter_event_handler := fn [mut g] (event JS.Event) {
|
mouse_enter_event_handler := fn [mut g] (event JS.Event) {
|
||||||
match event {
|
match event {
|
||||||
JS.MouseEvent {
|
JS.MouseEvent {
|
||||||
e := g.handle_mouse_event(event)
|
e := g.handle_mouse_event(event, .mouse_enter)
|
||||||
|
if !isnil(g.config.event_fn) {
|
||||||
|
f := g.config.event_fn
|
||||||
|
f(e, g.config.user_data)
|
||||||
|
}
|
||||||
if !isnil(g.config.enter_fn) {
|
if !isnil(g.config.enter_fn) {
|
||||||
f := g.config.enter_fn
|
f := g.config.enter_fn
|
||||||
f(e, g.config.user_data)
|
f(e, g.config.user_data)
|
||||||
|
@ -371,11 +423,32 @@ pub fn new_context(cfg Config) &Context {
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cfg.canvas.addEventListener('mousedown'.str, mouse_down_event_handler, JS.EventListenerOptions{})
|
|
||||||
|
keydown_event_handler := fn [mut g] (event JS.Event) {
|
||||||
|
println('keyboard')
|
||||||
|
match event {
|
||||||
|
JS.KeyboardEvent {
|
||||||
|
e := g.handle_keyboard_event(event, .key_down)
|
||||||
|
|
||||||
|
if !isnil(g.config.event_fn) {
|
||||||
|
f := g.config.event_fn
|
||||||
|
f(e, g.config.user_data)
|
||||||
|
}
|
||||||
|
if !isnil(g.config.keydown_fn) {
|
||||||
|
f := g.config.keydown_fn
|
||||||
|
// todo: modifiers
|
||||||
|
f(e.key_code, .super, g.config.user_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.canvas.addEventListener('mousedown'.str, mouse_down_event_handler, JS.EventListenerOptions{})
|
||||||
dom.window().addEventListener('mouseup'.str, mouse_up_event_handler, JS.EventListenerOptions{})
|
dom.window().addEventListener('mouseup'.str, mouse_up_event_handler, JS.EventListenerOptions{})
|
||||||
cfg.canvas.addEventListener('mousemove'.str, mouse_move_event_handler, JS.EventListenerOptions{})
|
g.canvas.addEventListener('mousemove'.str, mouse_move_event_handler, JS.EventListenerOptions{})
|
||||||
cfg.canvas.addEventListener('mouseleave'.str, mouse_leave_event_handler, JS.EventListenerOptions{})
|
g.canvas.addEventListener('mouseleave'.str, mouse_leave_event_handler, JS.EventListenerOptions{})
|
||||||
cfg.canvas.addEventListener('mouseenter'.str, mouse_enter_event_handler, JS.EventListenerOptions{})
|
g.canvas.addEventListener('mouseenter'.str, mouse_enter_event_handler, JS.EventListenerOptions{})
|
||||||
|
dom.document.addEventListener('keydown'.str, keydown_event_handler, JS.EventListenerOptions{})
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,6 +473,9 @@ pub fn (mut ctx Context) draw_line(x1 f32, y1 f32, x2 f32, y2 f32, c gx.Color) {
|
||||||
ctx.context.closePath()
|
ctx.context.closePath()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut ctx Context) quit() {
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (mut ctx Context) draw_rect(x f32, y f32, w f32, h f32, c gx.Color) {
|
pub fn (mut ctx Context) draw_rect(x f32, y f32, w f32, h f32, c gx.Color) {
|
||||||
ctx.context.beginPath()
|
ctx.context.beginPath()
|
||||||
ctx.context.fillStyle = c.to_css_string().str
|
ctx.context.fillStyle = c.to_css_string().str
|
||||||
|
@ -423,10 +499,10 @@ fn gg_animation_frame_fn(mut g Context) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Context) handle_mouse_event(event JS.MouseEvent) Event {
|
fn (mut g Context) handle_mouse_event(event JS.MouseEvent, typ DOMEventType) Event {
|
||||||
mut e := Event{}
|
mut e := Event{}
|
||||||
|
|
||||||
e.typ = .mouse_down
|
e.typ = typ
|
||||||
e.frame_count = g.frame
|
e.frame_count = g.frame
|
||||||
|
|
||||||
match int(event.button) {
|
match int(event.button) {
|
||||||
|
@ -457,3 +533,233 @@ fn (mut g Context) handle_mouse_event(event JS.MouseEvent) Event {
|
||||||
g.mouse_dy = int(event.movementY)
|
g.mouse_dy = int(event.movementY)
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut g Context) handle_keyboard_event(event JS.KeyboardEvent, typ DOMEventType) Event {
|
||||||
|
mut e := Event{}
|
||||||
|
e.typ = typ
|
||||||
|
e.frame_count = g.frame
|
||||||
|
|
||||||
|
match string(event.code) {
|
||||||
|
'Space' {
|
||||||
|
e.key_code = .space
|
||||||
|
}
|
||||||
|
'Minus' {
|
||||||
|
e.key_code = .minus
|
||||||
|
}
|
||||||
|
'Quote' {
|
||||||
|
e.key_code = .apostrophe
|
||||||
|
}
|
||||||
|
'Comma' {
|
||||||
|
e.key_code = .comma
|
||||||
|
}
|
||||||
|
'Period' {
|
||||||
|
e.key_code = .period
|
||||||
|
}
|
||||||
|
'Digit0' {
|
||||||
|
e.key_code = ._0
|
||||||
|
}
|
||||||
|
'Digit1' {
|
||||||
|
e.key_code = ._1
|
||||||
|
}
|
||||||
|
'Digit2' {
|
||||||
|
e.key_code = ._2
|
||||||
|
}
|
||||||
|
'Digit3' {
|
||||||
|
e.key_code = ._3
|
||||||
|
}
|
||||||
|
'Digit4' {
|
||||||
|
e.key_code = ._4
|
||||||
|
}
|
||||||
|
'Digit5' {
|
||||||
|
e.key_code = ._5
|
||||||
|
}
|
||||||
|
'Digit6' {
|
||||||
|
e.key_code = ._6
|
||||||
|
}
|
||||||
|
'Digit7' {
|
||||||
|
e.key_code = ._7
|
||||||
|
}
|
||||||
|
'Digit8' {
|
||||||
|
e.key_code = ._8
|
||||||
|
}
|
||||||
|
'Digit9' {
|
||||||
|
e.key_code = ._9
|
||||||
|
}
|
||||||
|
'Semicolon' {
|
||||||
|
e.key_code = .semicolon
|
||||||
|
}
|
||||||
|
'Equal' {
|
||||||
|
e.key_code = .equal
|
||||||
|
}
|
||||||
|
'KeyA' {
|
||||||
|
e.key_code = .a
|
||||||
|
}
|
||||||
|
'KeyB' {
|
||||||
|
e.key_code = .b
|
||||||
|
}
|
||||||
|
'KeyC' {
|
||||||
|
e.key_code = .c
|
||||||
|
}
|
||||||
|
'KeyD' {
|
||||||
|
e.key_code = .d
|
||||||
|
}
|
||||||
|
'KeyE' {
|
||||||
|
e.key_code = .e
|
||||||
|
}
|
||||||
|
'KeyF' {
|
||||||
|
e.key_code = .f
|
||||||
|
}
|
||||||
|
'KeyG' {
|
||||||
|
e.key_code = .g
|
||||||
|
}
|
||||||
|
'KeyH' {
|
||||||
|
e.key_code = .h
|
||||||
|
}
|
||||||
|
'KeyI' {
|
||||||
|
e.key_code = .i
|
||||||
|
}
|
||||||
|
'KeyJ' {
|
||||||
|
e.key_code = .j
|
||||||
|
}
|
||||||
|
'KeyK' {
|
||||||
|
e.key_code = .k
|
||||||
|
}
|
||||||
|
'KeyL' {
|
||||||
|
e.key_code = .l
|
||||||
|
}
|
||||||
|
'KeyM' {
|
||||||
|
e.key_code = .m
|
||||||
|
}
|
||||||
|
'KeyN' {
|
||||||
|
e.key_code = .n
|
||||||
|
}
|
||||||
|
'KeyO' {
|
||||||
|
e.key_code = .o
|
||||||
|
}
|
||||||
|
'KeyP' {
|
||||||
|
e.key_code = .p
|
||||||
|
}
|
||||||
|
'KeyQ' {
|
||||||
|
e.key_code = .q
|
||||||
|
}
|
||||||
|
'KeyR' {
|
||||||
|
e.key_code = .r
|
||||||
|
}
|
||||||
|
'KeyS' {
|
||||||
|
e.key_code = .s
|
||||||
|
}
|
||||||
|
'KeyT' {
|
||||||
|
e.key_code = .t
|
||||||
|
}
|
||||||
|
'KeyU' {
|
||||||
|
e.key_code = .u
|
||||||
|
}
|
||||||
|
'KeyV' {
|
||||||
|
e.key_code = .v
|
||||||
|
}
|
||||||
|
'KeyW' {
|
||||||
|
e.key_code = .w
|
||||||
|
}
|
||||||
|
'KeyX' {
|
||||||
|
e.key_code = .x
|
||||||
|
}
|
||||||
|
'KeyY' {
|
||||||
|
e.key_code = .y
|
||||||
|
}
|
||||||
|
'KeyZ' {
|
||||||
|
e.key_code = .z
|
||||||
|
}
|
||||||
|
'BracketLeft' {
|
||||||
|
e.key_code = .left_bracket
|
||||||
|
}
|
||||||
|
'BracketRight' {
|
||||||
|
e.key_code = .right_bracket
|
||||||
|
}
|
||||||
|
'Backslash' {
|
||||||
|
e.key_code = .backslash
|
||||||
|
}
|
||||||
|
'Backquote' {
|
||||||
|
e.key_code = .grave_accent
|
||||||
|
}
|
||||||
|
'Escape' {
|
||||||
|
e.key_code = .escape
|
||||||
|
}
|
||||||
|
'Enter' {
|
||||||
|
e.key_code = .enter
|
||||||
|
}
|
||||||
|
'Tab' {
|
||||||
|
e.key_code = .tab
|
||||||
|
}
|
||||||
|
'Backspace' {
|
||||||
|
e.key_code = .backspace
|
||||||
|
}
|
||||||
|
'Insert' {
|
||||||
|
e.key_code = .insert
|
||||||
|
}
|
||||||
|
'Delete' {
|
||||||
|
e.key_code = .delete
|
||||||
|
}
|
||||||
|
'ArrowRight' {
|
||||||
|
e.key_code = .right
|
||||||
|
}
|
||||||
|
'ArrowLeft' {
|
||||||
|
e.key_code = .left
|
||||||
|
}
|
||||||
|
'ArrowUp' {
|
||||||
|
e.key_code = .up
|
||||||
|
}
|
||||||
|
'ArrowDown' {
|
||||||
|
e.key_code = .down
|
||||||
|
}
|
||||||
|
'PageUp' {
|
||||||
|
e.key_code = .page_up
|
||||||
|
}
|
||||||
|
'PageDown' {
|
||||||
|
e.key_code = .page_down
|
||||||
|
}
|
||||||
|
'Home' {
|
||||||
|
e.key_code = .home
|
||||||
|
}
|
||||||
|
'End' {
|
||||||
|
e.key_code = .end
|
||||||
|
}
|
||||||
|
'CapsLock' {
|
||||||
|
e.key_code = .caps_lock
|
||||||
|
}
|
||||||
|
'ScrollLock' {
|
||||||
|
e.key_code = .scroll_lock
|
||||||
|
}
|
||||||
|
'NumLock' {
|
||||||
|
e.key_code = .num_lock
|
||||||
|
}
|
||||||
|
'PrintScreen' {
|
||||||
|
e.key_code = .print_screen
|
||||||
|
}
|
||||||
|
'Pause' {
|
||||||
|
e.key_code = .pause
|
||||||
|
}
|
||||||
|
'ShiftLeft' {
|
||||||
|
e.key_code = .left_shift
|
||||||
|
}
|
||||||
|
'ShiftRight' {
|
||||||
|
e.key_code = .right_shift
|
||||||
|
}
|
||||||
|
'AltLeft' {
|
||||||
|
e.key_code = .left_alt
|
||||||
|
}
|
||||||
|
'AltRight' {
|
||||||
|
e.key_code = .right_alt
|
||||||
|
}
|
||||||
|
'ControlLeft' {
|
||||||
|
e.key_code = .left_control
|
||||||
|
}
|
||||||
|
'ControlRight' {
|
||||||
|
e.key_code = .right_control
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic('todo: more keycodes (${string(event.code)})')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub struct PenConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
pub:
|
pub mut:
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
module gg
|
||||||
|
|
||||||
|
import gx
|
||||||
|
|
||||||
|
pub fn (mut ctx Context) draw_text(x int, y int, text_ string, cfg gx.TextCfg) {
|
||||||
|
ctx.context.fillStyle = cfg.color.to_css_string().str
|
||||||
|
ctx.context.font = cfg.to_css_string().str
|
||||||
|
ctx.context.fillText(text_.str, x, y)
|
||||||
|
}
|
|
@ -234,5 +234,5 @@ pub fn color_from_string(s string) Color {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (c Color) to_css_string() string {
|
pub fn (c Color) to_css_string() string {
|
||||||
return 'rgb($c.r,$c.g,$c.b)'
|
return 'rgba($c.r,$c.g,$c.b,$c.a)'
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,3 +19,17 @@ pub:
|
||||||
mono bool
|
mono bool
|
||||||
italic bool
|
italic bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (cfg TextCfg) to_css_string() string {
|
||||||
|
mut font_style := ''
|
||||||
|
if cfg.bold {
|
||||||
|
font_style += 'bold '
|
||||||
|
}
|
||||||
|
if cfg.mono {
|
||||||
|
font_style += 'mono '
|
||||||
|
}
|
||||||
|
if cfg.italic {
|
||||||
|
font_style += 'italic '
|
||||||
|
}
|
||||||
|
return '$font_style ${cfg.size}px $cfg.family'
|
||||||
|
}
|
||||||
|
|
|
@ -462,6 +462,7 @@ pub interface JS.CanvasRenderingContext2D {
|
||||||
translate(x JS.Number, y JS.Number)
|
translate(x JS.Number, y JS.Number)
|
||||||
drawFocusIfNeeded(path JS.Path2D, element JS.Element)
|
drawFocusIfNeeded(path JS.Path2D, element JS.Element)
|
||||||
stroke()
|
stroke()
|
||||||
|
fillText(text JS.String, x JS.Number, y JS.Number)
|
||||||
mut:
|
mut:
|
||||||
lineCap JS.String
|
lineCap JS.String
|
||||||
lineDashOffset JS.Number
|
lineDashOffset JS.Number
|
||||||
|
@ -472,6 +473,7 @@ mut:
|
||||||
strokeStyle FillStyle
|
strokeStyle FillStyle
|
||||||
globalAlpha JS.Number
|
globalAlpha JS.Number
|
||||||
globalCompositeOperation JS.String
|
globalCompositeOperation JS.String
|
||||||
|
font JS.String
|
||||||
}
|
}
|
||||||
|
|
||||||
pub interface JS.CanvasGradient {
|
pub interface JS.CanvasGradient {
|
||||||
|
@ -990,3 +992,16 @@ pub interface JS.ProgressEvent {
|
||||||
target JS.Any
|
target JS.Any
|
||||||
total JS.Number
|
total JS.Number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub interface JS.KeyboardEvent {
|
||||||
|
JS.UIEvent
|
||||||
|
altKey JS.Boolean
|
||||||
|
code JS.String
|
||||||
|
ctrlKey JS.Boolean
|
||||||
|
isComposing JS.Boolean
|
||||||
|
key JS.String
|
||||||
|
location JS.Number
|
||||||
|
metaKey JS.Boolean
|
||||||
|
repeat JS.Boolean
|
||||||
|
shiftKey JS.Boolean
|
||||||
|
}
|
||||||
|
|
|
@ -42,3 +42,10 @@ pub fn sleep(dur Duration) {
|
||||||
#let toWait = BigInt(dur.val) / BigInt(time__millisecond)
|
#let toWait = BigInt(dur.val) / BigInt(time__millisecond)
|
||||||
#while (new Date().getTime() < now + Number(toWait)) {}
|
#while (new Date().getTime() < now + Number(toWait)) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ticks() i64 {
|
||||||
|
t := i64(0)
|
||||||
|
#t.val = BigInt(new Date().getTime())
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ module time
|
||||||
pub fn sys_mono_now() u64 {
|
pub fn sys_mono_now() u64 {
|
||||||
$if js_browser {
|
$if js_browser {
|
||||||
mut res := u64(0)
|
mut res := u64(0)
|
||||||
#res = new u64(window.performance.now() * 1000000)
|
#res = new u64(Math.floor(window.performance.now() * 1000000))
|
||||||
|
|
||||||
return res
|
return res
|
||||||
} $else $if js_node {
|
} $else $if js_node {
|
||||||
|
|
|
@ -3437,7 +3437,7 @@ fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (from_type_sym.name == 'Any' && from_type_sym.language == .js)
|
if (from_type_sym.name == 'Any' && from_type_sym.language == .js)
|
||||||
|| from_type_sym.name == 'JS.Any' {
|
|| from_type_sym.name == 'JS.Any' || from_type_sym.name == 'voidptr' {
|
||||||
if it.typ.is_ptr() {
|
if it.typ.is_ptr() {
|
||||||
g.write('new \$ref(')
|
g.write('new \$ref(')
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue