Skip to content

Commit 243e66a

Browse files
authored
js,jsdom: Canvas & context API; Added TypeSymbol.is_js_compatible & temporary hacks for JS ifaces (#12526)
1 parent 258d0d6 commit 243e66a

File tree

9 files changed

+342
-70
lines changed

9 files changed

+342
-70
lines changed

examples/js_dom_draw/draw.js.v

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,90 @@
1-
/*
21
import jsdom
32

4-
fn get_2dcontext(canvas jsdom.IElement) ?jsdom.CanvasRenderingContext2D {
5-
if canvas is jsdom.HTMLCanvasElement {
6-
c := canvas.get_context('2d')
7-
match c {
8-
jsdom.CanvasRenderingContext2D {
9-
return c
10-
}
11-
else {
12-
return error('cannot fetch 2d context')
13-
}
3+
fn get_canvas(elem JS.HTMLElement) &JS.HTMLCanvasElement {
4+
match elem {
5+
JS.HTMLCanvasElement {
6+
return elem
7+
}
8+
else {
9+
panic('Not a canvas')
1410
}
15-
} else {
16-
return error('canvas is not an HTMLCanvasElement')
1711
}
1812
}
1913

20-
fn draw_line(context jsdom.CanvasRenderingContext2D, x1 int, y1 int, x2 int, y2 int) {
21-
context.begin_path()
22-
context.set_stroke_style('black')
23-
context.set_line_width(1)
24-
context.move_to(x1, y1)
25-
context.line_to(x2, y2)
14+
fn draw_line(mut context JS.CanvasRenderingContext2D, x1 int, y1 int, x2 int, y2 int) {
15+
context.beginPath()
16+
context.strokeStyle = 'black'.str
17+
context.lineWidth = JS.Number(1)
18+
context.moveTo(x1, y1)
19+
context.lineTo(x2, y2)
2620
context.stroke()
27-
context.close_path()
21+
context.closePath()
2822
}
2923

3024
struct DrawState {
3125
mut:
26+
context JS.CanvasRenderingContext2D
3227
drawing bool
3328
x int
3429
y int
3530
}
3631

3732
fn main() {
33+
window := jsdom.window()
3834
document := jsdom.document
35+
clear_btn := document.getElementById('clearButton'.str) ?
36+
canvas_elem := document.getElementById('canvas'.str) ?
37+
canvas := get_canvas(canvas_elem)
38+
ctx := canvas.getContext('2d'.str, js_undefined()) ?
39+
context := match ctx {
40+
JS.CanvasRenderingContext2D {
41+
ctx
42+
}
43+
else {
44+
panic('can not get 2d context')
45+
}
46+
}
47+
mut state := DrawState{context, false, 0, 0}
3948

40-
elem := document.get_element_by_id('myButton') ?
41-
elemc := document.get_element_by_id('myCanvas') or { panic('no canvas') }
42-
canv := jsdom.get_html_canvas_element(elemc) or { panic('expected canvas') }
43-
44-
context := canv.get_context_2d()
45-
mut state := DrawState{}
46-
canv.add_event_listener('mousedown', fn [mut state] (_ jsdom.IEventTarget, event jsdom.IEvent) {
49+
canvas.addEventListener('mousedown'.str, fn [mut state] (event JS.Event) {
4750
state.drawing = true
48-
if event is jsdom.MouseEvent {
49-
state.x = event.offset_x()
50-
state.y = event.offset_y()
51+
match event {
52+
JS.MouseEvent {
53+
state.x = int(event.offsetX)
54+
state.y = int(event.offsetY)
55+
}
56+
else {}
5157
}
52-
})
53-
54-
canv.add_event_listener('mousemove', fn [context, mut state] (_ jsdom.IEventTarget, event jsdom.IEvent) {
58+
}, JS.EventListenerOptions{})
59+
canvas.addEventListener('mousemove'.str, fn [mut state] (event JS.Event) {
5560
if state.drawing {
56-
if event is jsdom.MouseEvent {
57-
draw_line(context, state.x, state.y, event.offset_x(), event.offset_y())
58-
state.x = event.offset_x()
59-
state.y = event.offset_y()
61+
match event {
62+
JS.MouseEvent {
63+
draw_line(mut state.context, state.x, state.y, int(event.offsetX),
64+
int(event.offsetY))
65+
state.x = int(event.offsetX)
66+
state.y = int(event.offsetY)
67+
}
68+
else {}
6069
}
6170
}
62-
})
71+
}, JS.EventListenerOptions{})
6372

64-
jsdom.window.add_event_listener('mouseup', fn [context, mut state] (_ jsdom.IEventTarget, event jsdom.IEvent) {
73+
window.addEventListener('mouseup'.str, fn [mut state] (event JS.Event) {
6574
if state.drawing {
66-
if event is jsdom.MouseEvent {
67-
draw_line(context, state.x, state.y, event.offset_x(), event.offset_y())
75+
match event {
76+
JS.MouseEvent {
77+
draw_line(mut state.context, state.x, state.y, int(event.offsetX),
78+
int(event.offsetY))
79+
}
80+
else {}
6881
}
6982
state.x = 0
7083
state.y = 0
7184
state.drawing = false
7285
}
73-
})
74-
elem.add_event_listener('click', fn [context, canv] (_ jsdom.IEventTarget, _ jsdom.IEvent) {
75-
context.clear_rect(0, 0, canv.width(), canv.height())
76-
})
77-
}
78-
*/
79-
80-
fn main() {
81-
panic('jsdom is being refactored; This example will be available soon')
86+
}, JS.EventListenerOptions{})
87+
clear_btn.addEventListener('click'.str, fn [mut state, canvas] (_ JS.Event) {
88+
state.context.clearRect(0, 0, canvas.width, canvas.height)
89+
}, JS.EventListenerOptions{})
8290
}

examples/js_dom_draw/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<body class="main">
22
<title>Drawing with mouse events</title>
3-
<input type="button" id="myButton" value="Clear canvas">
4-
<canvas style="border: 1px solid black;" width="720" height="480" id="myCanvas"></canvas>
3+
<input type="button" id="clearButton" value="Clear canvas">
4+
<canvas style="border: 1px solid black;" width="720" height="480" id="canvas"></canvas>
55
<script type="text/javascript" src="draw.js"></script>
66

77
</body>

vlib/builtin/js/jsfns.js.v

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,34 @@ pub interface JS.Map {
5757

5858
pub interface JS.Any {}
5959

60+
pub fn js_is_null(x JS.Any) bool {
61+
res := false
62+
#res.val = x === null
63+
64+
return res
65+
}
66+
67+
pub fn js_is_undefined(x JS.Any) bool {
68+
res := false
69+
#res.val = x === undefined
70+
71+
return res
72+
}
73+
74+
pub fn js_null() JS.Any {
75+
mut obj := JS.Any{}
76+
#obj = null;
77+
78+
return obj
79+
}
80+
81+
pub fn js_undefined() JS.Any {
82+
mut obj := JS.Any{}
83+
#obj = undefined;
84+
85+
return obj
86+
}
87+
6088
pub interface JS.Array {
6189
JS.Any // map(fn (JS.Any) JS.Any) JS.Array
6290
map(JS.Any) JS.Array

vlib/jsdom/jsdom.js.v

Lines changed: 165 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ pub mut:
88
will_read_frequently bool
99
}
1010

11+
pub fn (settings CanvasRenderingContext2DSettings) to_js() JS.Any {
12+
mut object := JS.Any{}
13+
#object = { alpha: settings.alpha, colorSpace: settings.color_space.str, desynchronized: settings.desynchronized.val, willReadFrequently: settings.will_read_frequently.val };
14+
15+
return object
16+
}
17+
1118
pub interface JS.DOMMatrix2DInit {
1219
mut:
1320
a JS.Number
@@ -168,9 +175,9 @@ pub struct JS.EventListenerOptions {
168175
}
169176

170177
pub interface JS.EventTarget {
171-
addEventListener(cb EventCallback, options JS.EventListenerOptions)
178+
addEventListener(event JS.String, cb EventCallback, options JS.EventListenerOptions)
172179
dispatchEvent(event JS.Event) JS.Boolean
173-
removeEventListener(cb EventCallback, options JS.EventListenerOptions)
180+
removeEventListener(event JS.String, cb EventCallback, options JS.EventListenerOptions)
174181
}
175182

176183
// Event is an event which takes place in the DOM.
@@ -320,6 +327,7 @@ pub interface JS.Document {
320327
lastModified JS.String
321328
inputEncoding JS.String
322329
implementation JS.DOMImplementation
330+
getElementById(id JS.String) ?JS.HTMLElement
323331
mut:
324332
bgColor JS.String
325333
body JS.HTMLElement
@@ -368,6 +376,14 @@ pub interface JS.Element {
368376
scroll(x JS.Number, y JS.Number)
369377
scrollBy(x JS.Number, y JS.Number)
370378
toggleAttribute(qualifiedName JS.String, force JS.Boolean) JS.Boolean
379+
getElementsByClassName(className JS.String) JS.HTMLCollection
380+
getElementsByTagName(qualifiedName JS.String) JS.HTMLCollection
381+
getEelementsByTagNameNS(namespaecURI JS.String, localName JS.String) JS.HTMLCollection
382+
hasAttribute(qualifiedName JS.String) JS.Boolean
383+
hasAttributeNS(namespace JS.String, localName JS.String) JS.Boolean
384+
hasAttributes() JS.Boolean
385+
hasPointerCapture(pointerId JS.Number) JS.Boolean
386+
matches(selectors JS.String) JS.Boolean
371387
mut:
372388
className JS.String
373389
id JS.String
@@ -383,6 +399,13 @@ pub const (
383399
document = JS.Document{}
384400
)
385401

402+
pub fn window() JS.Window {
403+
mut x := JS.Any(voidptr(0))
404+
#x = window;
405+
406+
return x
407+
}
408+
386409
fn init() {
387410
#jsdom__document = document;
388411
}
@@ -398,3 +421,143 @@ pub fn event_listener(callback fn (JS.EventTarget, JS.Event)) EventCallback {
398421
callback(target, event)
399422
}
400423
}
424+
425+
pub interface JS.HTMLCollection {
426+
length JS.Number
427+
item(idx JS.Number) ?JS.Any
428+
namedItem(name JS.String) ?JS.Any
429+
}
430+
431+
pub interface JS.HTMLElement {
432+
JS.Element
433+
accessKeyLabel JS.String
434+
offsetHeight JS.Number
435+
offsetLeft JS.Number
436+
offsetParent JS.Any
437+
offsetTop JS.Number
438+
offsetWidth JS.Number
439+
click()
440+
mut:
441+
accessKey JS.String
442+
autocapitalize JS.String
443+
dir JS.String
444+
draggable JS.Boolean
445+
hidden JS.Boolean
446+
innerText JS.String
447+
lang JS.String
448+
outerText JS.String
449+
spellcheck JS.Boolean
450+
title JS.String
451+
translate JS.Boolean
452+
}
453+
454+
pub fn JS.HTMLElement.prototype.constructor() JS.HTMLElement
455+
456+
pub interface JS.HTMLEmbedElement {
457+
getSVGDocument() ?JS.Document
458+
mut:
459+
align JS.String
460+
height JS.String
461+
src JS.String
462+
width JS.String
463+
}
464+
465+
pub fn html_embed_type(embed JS.HTMLEmbedElement) JS.String {
466+
mut str := JS.String{}
467+
#str = embed.type
468+
469+
return str
470+
}
471+
472+
pub fn JS.HTMLEmbedElement.prototype.constructor() JS.HTMLEmbedElement
473+
474+
pub type CanvasContext = JS.CanvasRenderingContext2D
475+
| JS.WebGL2RenderingContext
476+
| JS.WebGLRenderingContext
477+
478+
pub interface JS.HTMLCanvasElement {
479+
JS.HTMLElement
480+
getContext(contextId JS.String, options JS.Any) ?CanvasContext
481+
mut:
482+
height JS.Number
483+
width JS.Number
484+
}
485+
486+
pub type FillStyle = JS.CanvasGradient | JS.CanvasPattern | JS.String
487+
488+
pub interface JS.CanvasRenderingContext2D {
489+
canvas JS.HTMLCanvasElement
490+
beginPath()
491+
clip(path JS.Path2D, fillRule JS.String)
492+
fill(path JS.Path2D, fillRule JS.String)
493+
isPointInPath(path JS.Path2D, x JS.Number, y JS.Number, fillRule JS.String) JS.Boolean
494+
isPointInStroke(path JS.Path2D, x JS.Number, y JS.Number) JS.Boolean
495+
stoke(path JS.Path2D)
496+
createLinearGradient(x0 JS.Number, y0 JS.Number, x1 JS.Number, y1 JS.Number) JS.CanvasGradient
497+
createRadialGradient(x0 JS.Number, y0 JS.Number, r0 JS.Number, x1 JS.Number, y1 JS.Number, r1 JS.Number) JS.CanvasGradient
498+
createPattern(image JS.CanvasImageSource, repetition JS.String) ?JS.CanvasPattern
499+
arc(x JS.Number, y JS.Number, radius JS.Number, startAngle JS.Number, endAngle JS.Number, counterclockwise JS.Boolean)
500+
arcTo(x1 JS.Number, y1 JS.Number, x2 JS.Number, y2 JS.Number, radius JS.Number)
501+
bezierCurveTo(cp1x JS.Number, cp1y JS.Number, cp2x JS.Number, cp2y JS.Number, x JS.Number, y JS.Number)
502+
closePath()
503+
ellipse(x JS.Number, y JS.Number, radiusX JS.Number, radiusY JS.Number, rotation JS.Number, startAngle JS.Number, endAngle JS.Number, counterclockwise JS.Boolean)
504+
lineTo(x JS.Number, y JS.Number)
505+
moveTo(x JS.Number, y JS.Number)
506+
quadraticCurveTo(cpx JS.Number, cpy JS.Number, x JS.Number, y JS.Number)
507+
rect(x JS.Number, y JS.Number, w JS.Number, h JS.Number)
508+
getLineDash() JS.Array
509+
setLineDash(segments JS.Array)
510+
clearRect(x JS.Number, y JS.Number, w JS.Number, h JS.Number)
511+
fillRect(x JS.Number, y JS.Number, w JS.null, h JS.Number)
512+
strokeRect(x JS.Number, y JS.Number, w JS.Number, h JS.Number)
513+
getTransformt() JS.DOMMatrix
514+
resetTransform()
515+
rotate(angle JS.Number)
516+
scale(x JS.Number, y JS.Number)
517+
setTransform(matrix JS.DOMMatrix)
518+
transform(a JS.Number, b JS.Number, c JS.Number, d JS.Number, e JS.Number, f JS.Number)
519+
translate(x JS.Number, y JS.Number)
520+
drawFocusIfNeeded(path JS.Path2D, element JS.Element)
521+
stroke()
522+
mut:
523+
lineCap JS.String
524+
lineDashOffset JS.Number
525+
lineJoin JS.String
526+
lineWidth JS.Number
527+
miterLimit JS.Number
528+
fillStyle FillStyle
529+
strokeStyle FillStyle
530+
globalAlpha JS.Number
531+
globalCompositeOperation JS.String
532+
}
533+
534+
pub interface JS.CanvasGradient {
535+
addColorStop(offset JS.Number, color JS.String)
536+
}
537+
538+
pub interface JS.CanvasPattern {
539+
setTransform(transform JS.DOMMatrix)
540+
}
541+
542+
pub interface JS.WebGLRenderingContext {
543+
canvas JS.HTMLCanvasElement
544+
drawingBufferHeight JS.Number
545+
drawingBufferWidth JS.Number
546+
}
547+
548+
pub interface JS.WebGL2RenderingContext {
549+
JS.WebGLRenderingContext
550+
}
551+
552+
pub interface JS.Window {
553+
JS.EventTarget
554+
closed JS.Boolean
555+
devicePixelRatio JS.Number
556+
document JS.Document
557+
frameElement JS.Element
558+
innerHeight JS.Number
559+
innerWidth JS.Number
560+
length JS.Number
561+
}
562+
563+
pub interface JS.Path2D {}

0 commit comments

Comments
 (0)