v2 / vlib / gg / gg.js.v
768 lines · 726 sloc · 15.52 KB · 5d91447d09493d073ae7b8cb1c45a4a00de9222d
Raw
1module gg
2
3import js.dom
4
5pub enum DOMEventType {
6 invalid
7 key_down
8 key_up
9 char
10 mouse_down
11 mouse_up
12 mouse_scroll
13 mouse_move
14 mouse_enter
15 mouse_leave
16 touches_began
17 touches_moved
18 touches_ended
19 touches_cancelled
20 resized
21 iconified
22 restored
23 focused
24 unfocused
25 suspended
26 resumed
27 update_cursor
28 quit_requested
29 clipboard_pasted
30 files_dropped
31 num
32}
33
34pub struct Event {
35pub mut:
36 frame_count u64
37 typ DOMEventType
38 key_code KeyCode
39 char_code u32
40 key_repeat bool
41 modifiers u32
42 mouse_button MouseButton
43 mouse_x f32
44 mouse_y f32
45 mouse_dx f32
46 mouse_dy f32
47 scroll_x f32
48 scroll_y f32
49 // todo(playX): add touches API support in js.dom
50 // num_touches int
51 // touches [8]TouchPoint
52 window_width int
53 window_height int
54 framebuffer_width int
55 framebuffer_height int
56}
57
58pub enum DOMMouseButton {
59 invalid = -1
60 left = 0
61 right = 1
62 middle = 2
63}
64
65pub enum DOMModifier {
66 shift = 1 //(1<<0)
67 ctrl = 2 //(1<<1)
68 alt = 4 //(1<<2)
69 super = 8 //(1<<3)
70 lmb = 0x100
71 rmb = 0x200
72 mmb = 0x400
73}
74
75pub enum DOMKeyCode {
76 invalid = 0
77 space = 32
78 apostrophe = 39 //'
79 comma = 44 //,
80 minus = 45 //-
81 period = 46 //.
82 slash = 47 ///
83 _0 = 48
84 _1 = 49
85 _2 = 50
86 _3 = 51
87 _4 = 52
88 _5 = 53
89 _6 = 54
90 _7 = 55
91 _8 = 56
92 _9 = 57
93 semicolon = 59 //;
94 equal = 61 //=
95 a = 65
96 b = 66
97 c = 67
98 d = 68
99 e = 69
100 f = 70
101 g = 71
102 h = 72
103 i = 73
104 j = 74
105 k = 75
106 l = 76
107 m = 77
108 n = 78
109 o = 79
110 p = 80
111 q = 81
112 r = 82
113 s = 83
114 t = 84
115 u = 85
116 v = 86
117 w = 87
118 x = 88
119 y = 89
120 z = 90
121 left_bracket = 91 //[
122 backslash = 92 //\
123 right_bracket = 93 //]
124 grave_accent = 96 //`
125 world_1 = 161 // non-us #1
126 world_2 = 162 // non-us #2
127 escape = 256
128 enter = 257
129 tab = 258
130 backspace = 259
131 insert = 260
132 delete = 261
133 right = 262
134 left = 263
135 down = 264
136 up = 265
137 page_up = 266
138 page_down = 267
139 home = 268
140 end = 269
141 caps_lock = 280
142 scroll_lock = 281
143 num_lock = 282
144 print_screen = 283
145 pause = 284
146 f1 = 290
147 f2 = 291
148 f3 = 292
149 f4 = 293
150 f5 = 294
151 f6 = 295
152 f7 = 296
153 f8 = 297
154 f9 = 298
155 f10 = 299
156 f11 = 300
157 f12 = 301
158 f13 = 302
159 f14 = 303
160 f15 = 304
161 f16 = 305
162 f17 = 306
163 f18 = 307
164 f19 = 308
165 f20 = 309
166 f21 = 310
167 f22 = 311
168 f23 = 312
169 f24 = 313
170 f25 = 314
171 kp_0 = 320
172 kp_1 = 321
173 kp_2 = 322
174 kp_3 = 323
175 kp_4 = 324
176 kp_5 = 325
177 kp_6 = 326
178 kp_7 = 327
179 kp_8 = 328
180 kp_9 = 329
181 kp_decimal = 330
182 kp_divide = 331
183 kp_multiply = 332
184 kp_subtract = 333
185 kp_add = 334
186 kp_enter = 335
187 kp_equal = 336
188 left_shift = 340
189 left_control = 341
190 left_alt = 342
191 left_super = 343
192 right_shift = 344
193 right_control = 345
194 right_alt = 346
195 right_super = 347
196 menu = 348
197}
198
199pub struct Config {
200pub:
201 width int
202 height int
203 retina bool
204 resizable bool
205 user_data voidptr
206 font_size int
207 create_window bool
208 // window_user_ptr voidptr
209 window_title string
210 borderless_window bool
211 always_on_top bool
212 bg_color Color
213 init_fn FNCb = unsafe { nil }
214 frame_fn FNCb = unsafe { nil }
215 native_frame_fn FNCb = unsafe { nil }
216 cleanup_fn FNCb = unsafe { nil }
217 fail_fn FNFail = unsafe { nil }
218
219 event_fn FNEvent = unsafe { nil }
220 quit_fn FNEvent = unsafe { nil }
221
222 keydown_fn FNKeyDown = unsafe { nil }
223 keyup_fn FNKeyUp = unsafe { nil }
224 char_fn FNChar = unsafe { nil }
225
226 move_fn FNMove = unsafe { nil }
227 click_fn FNClick = unsafe { nil }
228 unclick_fn FNUnClick = unsafe { nil }
229 leave_fn FNEvent = unsafe { nil }
230 enter_fn FNEvent = unsafe { nil }
231 resized_fn FNEvent = unsafe { nil }
232 scroll_fn FNEvent = unsafe { nil }
233 // wait_events bool // set this to true for UIs, to save power
234 fullscreen bool
235 scale f32 = 1.0
236 sample_count int
237 texture_filter TextureFilter = .linear
238 swap_interval int = 1 // 1 = 60fps, 2 = 30fps etc. Ignored by the JS backend; frame pacing follows requestAnimationFrame.
239 // ved needs this
240 // init_text bool
241 font_path string
242 custom_bold_font_path string
243 ui_mode bool // refreshes only on events to save CPU usage
244 // font bytes for embedding
245 font_bytes_normal []u8
246 font_bytes_bold []u8
247 font_bytes_mono []u8
248 font_bytes_italic []u8
249 native_rendering bool // Cocoa on macOS/iOS, GDI+ on Windows
250 // drag&drop
251 enable_dragndrop bool // enable file dropping (drag'n'drop), default is false
252 max_dropped_files int = 1 // max number of dropped files to process (default: 1)
253 max_dropped_file_path_length int = 2048 // max length in bytes of a dropped UTF-8 file path (default: 2048)
254 html5_canvas_name string = 'canvas' // the id/name of the canvas element, that will be used to render GG apps
255}
256
257const size = Size{0, 0}
258
259pub fn window_size() Size {
260 return size
261}
262
263pub struct Context {
264mut:
265 render_text bool = true
266 image_cache []Image
267 needs_refresh bool = true
268 ticks int
269pub mut:
270 scale f32 = 1.0
271 width int
272 height int
273 window JS.Window @[noinit]
274 config Config
275 user_data voidptr
276 ui_mode bool
277 frame u64
278 mbtn_mask u8
279 mouse_buttons MouseButtons
280 mouse_pos_x int
281 mouse_pos_y int
282 mouse_dx int
283 mouse_dy int
284 scroll_x int
285 scroll_y int
286
287 key_modifiers Modifier // the current key modifiers
288 key_repeat bool // whether the pressed key was an autorepeated one
289 pressed_keys [key_code_max]bool // an array representing all currently pressed keys
290 pressed_keys_edge [key_code_max]bool // true when the previous state of pressed_keys,
291 context JS.CanvasRenderingContext2D @[noinit]
292 canvas JS.HTMLCanvasElement @[noinit]
293 // *before* the current event was different
294}
295
296fn get_canvas(elem JS.HTMLElement) JS.HTMLCanvasElement {
297 match elem {
298 JS.HTMLCanvasElement {
299 return elem
300 }
301 else {
302 panic('gg: element is not an HTMLCanvasElement')
303 }
304 }
305}
306
307fn get_context(canvas JS.HTMLCanvasElement) JS.CanvasRenderingContext2D {
308 ctx := canvas.getContext('2d'.str, js_undefined()) or { panic('cannot get context') }
309 match ctx {
310 JS.CanvasRenderingContext2D {
311 return ctx
312 }
313 else {
314 panic('failed to get 2D context')
315 }
316 }
317}
318
319pub fn new_context(cfg Config) &Context {
320 mut g := &Context{}
321
322 g.user_data = cfg.user_data
323 g.width = cfg.width
324 g.height = cfg.height
325 g.ui_mode = cfg.ui_mode
326 mut sz := size
327 sz.height = g.height
328 sz.width = g.width
329 g.config = cfg
330 g.window = dom.window()
331 document := dom.document
332 canvas_elem := document.getElementById(cfg.html5_canvas_name.str) or {
333 panic('gg: cannot get canvas element from cfg.html5_canvas_name.str')
334 }
335 canvas := get_canvas(canvas_elem)
336 g.canvas = canvas
337 g.context = get_context(g.canvas)
338
339 mouse_down_event_handler := fn [mut g] (event JS.Event) {
340 match event {
341 JS.MouseEvent {
342 e := g.handle_mouse_event(event, .mouse_down)
343 if !isnil(g.config.event_fn) {
344 f := g.config.event_fn
345 f(e, g.config.user_data)
346 }
347 if !isnil(g.config.click_fn) {
348 f := g.config.click_fn
349 f(e.mouse_x, e.mouse_y, e.mouse_button, g.config.user_data)
350 }
351 }
352 else {}
353 }
354 }
355
356 mouse_up_event_handler := fn [mut g] (event JS.Event) {
357 match event {
358 JS.MouseEvent {
359 e := g.handle_mouse_event(event, .mouse_up)
360 if !isnil(g.config.event_fn) {
361 f := g.config.event_fn
362 f(e, g.config.user_data)
363 }
364 if !isnil(g.config.unclick_fn) {
365 f := g.config.unclick_fn
366 f(e.mouse_x, e.mouse_y, e.mouse_button, g.config.user_data)
367 }
368 }
369 else {}
370 }
371 }
372 mouse_move_event_handler := fn [mut g] (event JS.Event) {
373 match event {
374 JS.MouseEvent {
375 e := g.handle_mouse_event(event, .mouse_move)
376 if !isnil(g.config.event_fn) {
377 f := g.config.event_fn
378 f(e, g.config.user_data)
379 }
380 if !isnil(g.config.move_fn) {
381 f := g.config.move_fn
382 f(e.mouse_x, e.mouse_y, g.config.user_data)
383 }
384 }
385 else {}
386 }
387 }
388
389 mouse_leave_event_handler := fn [mut g] (event JS.Event) {
390 match event {
391 JS.MouseEvent {
392 e := g.handle_mouse_event(event, .mouse_leave)
393 if !isnil(g.config.event_fn) {
394 f := g.config.event_fn
395 f(e, g.config.user_data)
396 }
397 if !isnil(g.config.leave_fn) {
398 f := g.config.leave_fn
399 f(e, g.config.user_data)
400 }
401 }
402 else {}
403 }
404 }
405
406 mouse_enter_event_handler := fn [mut g] (event JS.Event) {
407 match event {
408 JS.MouseEvent {
409 e := g.handle_mouse_event(event, .mouse_enter)
410 if !isnil(g.config.event_fn) {
411 f := g.config.event_fn
412 f(e, g.config.user_data)
413 }
414 if !isnil(g.config.enter_fn) {
415 f := g.config.enter_fn
416 f(e, g.config.user_data)
417 }
418 }
419 else {}
420 }
421 }
422
423 keydown_event_handler := fn [mut g] (event JS.Event) {
424 println('keyboard')
425 match event {
426 JS.KeyboardEvent {
427 e := g.handle_keyboard_event(event, .key_down)
428
429 if !isnil(g.config.event_fn) {
430 f := g.config.event_fn
431 f(e, g.config.user_data)
432 }
433 if !isnil(g.config.keydown_fn) {
434 f := g.config.keydown_fn
435 // todo: modifiers
436 f(e.key_code, .super, g.config.user_data)
437 }
438 }
439 else {}
440 }
441 }
442 g.canvas.addEventListener('mousedown'.str, mouse_down_event_handler, JS.EventListenerOptions{})
443 dom.window().addEventListener('mouseup'.str, mouse_up_event_handler, JS.EventListenerOptions{})
444 g.canvas.addEventListener('mousemove'.str, mouse_move_event_handler, JS.EventListenerOptions{})
445 g.canvas.addEventListener('mouseleave'.str, mouse_leave_event_handler, JS.EventListenerOptions{})
446 g.canvas.addEventListener('mouseenter'.str, mouse_enter_event_handler, JS.EventListenerOptions{})
447 dom.document.addEventListener('keydown'.str, keydown_event_handler, JS.EventListenerOptions{})
448 return g
449}
450
451pub fn (mut ctx Context) run() {
452 // set context late, in case it changed (e.g., due to embedding)
453 if isnil(ctx.user_data) {
454 ctx.user_data = ctx
455 }
456
457 gg_animation_frame_fn(mut ctx)
458}
459
460pub fn (mut ctx Context) begin() {
461 // ctx.context.beginPath()
462}
463
464pub fn (mut ctx Context) end() {
465 // ctx.context.closePath()
466}
467
468pub fn (mut ctx Context) draw_line(x1 f32, y1 f32, x2 f32, y2 f32, c Color) {
469 ctx.context.beginPath()
470 ctx.context.strokeStyle = c.to_css_string().str
471 ctx.context.moveTo(JS.Number(x1), JS.Number(y1))
472 ctx.context.lineTo(JS.Number(x2), JS.Number(y2))
473 ctx.context.stroke()
474 ctx.context.closePath()
475}
476
477pub fn (mut ctx Context) quit() {
478}
479
480pub fn (mut ctx Context) draw_rect(x f32, y f32, w f32, h f32, c Color) {
481 ctx.context.beginPath()
482 ctx.context.fillStyle = c.to_css_string().str
483 ctx.context.fillRect(JS.Number(x), JS.Number(y), JS.Number(w), JS.Number(h))
484 ctx.context.closePath()
485}
486
487fn gg_animation_frame_fn(mut g Context) {
488 g.frame++
489 g.context.clearRect(JS.Number(0), JS.Number(0), JS.Number(g.config.width),
490 JS.Number(g.config.height))
491 // todo(playXE): handle events
492
493 if !isnil(g.config.frame_fn) {
494 f := g.config.frame_fn
495 f(g.user_data)
496 g.needs_refresh = false
497 }
498
499 g.window.requestAnimationFrame(fn [mut g] (time JS.Number) {
500 gg_animation_frame_fn(mut g)
501 })
502}
503
504fn (mut g Context) handle_mouse_event(event JS.MouseEvent, typ DOMEventType) Event {
505 mut e := Event{}
506
507 e.typ = typ
508 e.frame_count = g.frame
509
510 match int(event.button) {
511 0 {
512 e.mouse_button = .left
513 }
514 1 {
515 e.mouse_button = .middle
516 }
517 2 {
518 e.mouse_button = .right
519 }
520 else {
521 e.mouse_button = .invalid
522 }
523 }
524
525 e.mouse_x = f32(event.offsetX)
526 e.mouse_y = f32(event.offsetY)
527 e.mouse_dx = f32(event.movementX)
528 e.mouse_dy = f32(event.movementY)
529 bitplace := int(event.button)
530 g.mbtn_mask |= u8(1 << bitplace)
531 // g.mouse_buttons = MouseButtons(g.mbtn_mask)
532
533 g.mouse_pos_x = int(event.offsetX)
534 g.mouse_pos_y = int(event.offsetY)
535 g.mouse_dx = int(event.movementX)
536 g.mouse_dy = int(event.movementY)
537 return e
538}
539
540fn (mut g Context) handle_keyboard_event(event JS.KeyboardEvent, typ DOMEventType) Event {
541 mut e := Event{}
542 e.typ = typ
543 e.frame_count = g.frame
544
545 match string(event.code) {
546 'Space' {
547 e.key_code = .space
548 }
549 'Minus' {
550 e.key_code = .minus
551 }
552 'Quote' {
553 e.key_code = .apostrophe
554 }
555 'Comma' {
556 e.key_code = .comma
557 }
558 'Period' {
559 e.key_code = .period
560 }
561 'Digit0' {
562 e.key_code = ._0
563 }
564 'Digit1' {
565 e.key_code = ._1
566 }
567 'Digit2' {
568 e.key_code = ._2
569 }
570 'Digit3' {
571 e.key_code = ._3
572 }
573 'Digit4' {
574 e.key_code = ._4
575 }
576 'Digit5' {
577 e.key_code = ._5
578 }
579 'Digit6' {
580 e.key_code = ._6
581 }
582 'Digit7' {
583 e.key_code = ._7
584 }
585 'Digit8' {
586 e.key_code = ._8
587 }
588 'Digit9' {
589 e.key_code = ._9
590 }
591 'Semicolon' {
592 e.key_code = .semicolon
593 }
594 'Equal' {
595 e.key_code = .equal
596 }
597 'KeyA' {
598 e.key_code = .a
599 }
600 'KeyB' {
601 e.key_code = .b
602 }
603 'KeyC' {
604 e.key_code = .c
605 }
606 'KeyD' {
607 e.key_code = .d
608 }
609 'KeyE' {
610 e.key_code = .e
611 }
612 'KeyF' {
613 e.key_code = .f
614 }
615 'KeyG' {
616 e.key_code = .g
617 }
618 'KeyH' {
619 e.key_code = .h
620 }
621 'KeyI' {
622 e.key_code = .i
623 }
624 'KeyJ' {
625 e.key_code = .j
626 }
627 'KeyK' {
628 e.key_code = .k
629 }
630 'KeyL' {
631 e.key_code = .l
632 }
633 'KeyM' {
634 e.key_code = .m
635 }
636 'KeyN' {
637 e.key_code = .n
638 }
639 'KeyO' {
640 e.key_code = .o
641 }
642 'KeyP' {
643 e.key_code = .p
644 }
645 'KeyQ' {
646 e.key_code = .q
647 }
648 'KeyR' {
649 e.key_code = .r
650 }
651 'KeyS' {
652 e.key_code = .s
653 }
654 'KeyT' {
655 e.key_code = .t
656 }
657 'KeyU' {
658 e.key_code = .u
659 }
660 'KeyV' {
661 e.key_code = .v
662 }
663 'KeyW' {
664 e.key_code = .w
665 }
666 'KeyX' {
667 e.key_code = .x
668 }
669 'KeyY' {
670 e.key_code = .y
671 }
672 'KeyZ' {
673 e.key_code = .z
674 }
675 'BracketLeft' {
676 e.key_code = .left_bracket
677 }
678 'BracketRight' {
679 e.key_code = .right_bracket
680 }
681 'Backslash' {
682 e.key_code = .backslash
683 }
684 'Backquote' {
685 e.key_code = .grave_accent
686 }
687 'Escape' {
688 e.key_code = .escape
689 }
690 'Enter' {
691 e.key_code = .enter
692 }
693 'Tab' {
694 e.key_code = .tab
695 }
696 'Backspace' {
697 e.key_code = .backspace
698 }
699 'Insert' {
700 e.key_code = .insert
701 }
702 'Delete' {
703 e.key_code = .delete
704 }
705 'ArrowRight' {
706 e.key_code = .right
707 }
708 'ArrowLeft' {
709 e.key_code = .left
710 }
711 'ArrowUp' {
712 e.key_code = .up
713 }
714 'ArrowDown' {
715 e.key_code = .down
716 }
717 'PageUp' {
718 e.key_code = .page_up
719 }
720 'PageDown' {
721 e.key_code = .page_down
722 }
723 'Home' {
724 e.key_code = .home
725 }
726 'End' {
727 e.key_code = .end
728 }
729 'CapsLock' {
730 e.key_code = .caps_lock
731 }
732 'ScrollLock' {
733 e.key_code = .scroll_lock
734 }
735 'NumLock' {
736 e.key_code = .num_lock
737 }
738 'PrintScreen' {
739 e.key_code = .print_screen
740 }
741 'Pause' {
742 e.key_code = .pause
743 }
744 'ShiftLeft' {
745 e.key_code = .left_shift
746 }
747 'ShiftRight' {
748 e.key_code = .right_shift
749 }
750 'AltLeft' {
751 e.key_code = .left_alt
752 }
753 'AltRight' {
754 e.key_code = .right_alt
755 }
756 'ControlLeft' {
757 e.key_code = .left_control
758 }
759 'ControlRight' {
760 e.key_code = .right_control
761 }
762 else {
763 panic('todo: more keycodes (${string(event.code)})')
764 }
765 }
766
767 return e
768}
769