Skip to content

Commit 4a2a70f

Browse files
authored
Various improvements on JS backend (#150)
* Responsive resizing * Targeted canvas placement * prevent event lockup * 'wiser' js * revert to hardware rendering * fix event values after CSS rescaling
1 parent 381484b commit 4a2a70f

File tree

4 files changed

+80
-35
lines changed

4 files changed

+80
-35
lines changed

src/ocamlCanvas.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ module V1 = struct
542542
external createOnscreen :
543543
?autocommit:bool -> ?decorated:bool -> ?resizeable:bool ->
544544
?minimize:bool -> ?maximize:bool -> ?close:bool -> ?title:string ->
545-
?pos:(int * int) -> size:(int * int) -> unit -> t
545+
?target:string -> ?pos:(int * int) -> size:(int * int) -> unit -> t
546546
= "ml_canvas_create_onscreen" "ml_canvas_create_onscreen_n"
547547

548548
external createOffscreen : size:(int * int) -> unit -> t

src/ocamlCanvas.mli

+5-2
Original file line numberDiff line numberDiff line change
@@ -766,9 +766,9 @@ module V1 : sig
766766
val createOnscreen :
767767
?autocommit:bool -> ?decorated:bool -> ?resizeable:bool ->
768768
?minimize:bool -> ?maximize:bool -> ?close:bool -> ?title:string ->
769-
?pos:(int * int) -> size:(int * int) -> unit -> t
769+
?target:string -> ?pos:(int * int) -> size:(int * int) -> unit -> t
770770
(** [createOnscreen ?autocommit ?decorated ?resizeable ?minimize
771-
?maximize ?close ?title ?pos ~size ()] creates a windowed
771+
?maximize ?close ?title ?target ?pos ~size()] creates a windowed
772772
canvas of size [size]. The window title and position can be
773773
specified using the optional arguments [title] and [pos].
774774
The window decorations, which are active by default, can
@@ -777,6 +777,9 @@ module V1 : sig
777777
The [decorated] argument has a higher priority: if set to false,
778778
all other decoration arguments will be ignored (considered to be
779779
false), and all decorations will be removed from the window.
780+
The [target] option is relevant only for the Javascript backend.
781+
It indicates the element id in which the canvas should be placed,
782+
default to the html body.
780783
The [autocommit] option, which is active by default, indicates whether
781784
the canvas should be automatically presented after each frame event.
782785
See {!Canvas.commit} for more info on [autocommit].

src/stubs/ml_canvas.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -673,12 +673,14 @@ ml_canvas_create_onscreen_n(
673673
value mlMaximize, /* bool, optional, default = true */
674674
value mlClose, /* bool, optional, default = true */
675675
value mlTitle, /* string, optional, default = "" */
676+
value mlTarget, /* string, optional */
676677
value mlPos, /* (int * int), optional */
677678
value mlSize, /* (int * int), non-optional */
678679
value mlUnit)
679680
{
680681
CAMLparam5(mlAutocommit, mlDecorated, mlResizeable, mlMinimize, mlMaximize);
681-
CAMLxparam5(mlClose, mlTitle, mlPos, mlSize, mlUnit);
682+
CAMLxparam5(mlClose, mlTitle, mlTarget, mlPos, mlSize);
683+
CAMLxparam1(mlUnit);
682684
CAMLlocal1(mlCanvas);
683685
_ml_canvas_ensure_initialized();
684686
int32_t width = Int31_val_clip(Field(mlSize, 0));
@@ -708,7 +710,7 @@ ml_canvas_create_onscreen_n(
708710
CAMLreturn(mlCanvas);
709711
}
710712

711-
BYTECODE_STUB_10(ml_canvas_create_onscreen)
713+
BYTECODE_STUB_11(ml_canvas_create_onscreen)
712714

713715
CAMLprim value
714716
ml_canvas_create_offscreen(

src/stubs/ml_canvas.js

+70-30
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ var _move = {
5151
prev_y: 0
5252
}
5353

54+
//Provides: _resize
55+
//Requires: _resize_handler
56+
var _resize = new window.ResizeObserver(_resize_handler);
57+
58+
//Provides: _event_canvas_scale
59+
function _event_canvas_scale(e) {
60+
return { scaleX : e.target.canvas.width / e.target.clientWidth,
61+
scaleY : e.target.canvas.height / e.target.clientHeight
62+
}
63+
}
64+
5465
//Provides: _make_key_event
5566
//Requires: _focus, keyname_to_keycode, Val_key_code, Val_key_state, EVENT_TAG
5667
//Requires: caml_int64_of_float
@@ -108,44 +119,46 @@ function _header_down_handler(e) {
108119
_move.target = e.target.canvas.frame;
109120
_move.prev_x = e.pageX;
110121
_move.prev_y = e.pageY;
111-
document.body.insertBefore(_move.target, null);
122+
e.target.canvas.target.insertBefore(_move.target, null);
112123
}
113124
return false;
114125
}
115126

116127
//Provides: _surface_down_handler
117-
//Requires: _focus, _ml_canvas_process_event, EVENT_TAG
128+
//Requires: _focus, _ml_canvas_process_event, _event_canvas_scale, EVENT_TAG
118129
//Requires: caml_int64_of_float
119130
function _surface_down_handler(e) {
120131
if (e.target !== null) {
121132
_focus = e.target.canvas;
122-
document.body.insertBefore(e.target.canvas.frame, null);
133+
e.target.canvas.target.insertBefore(e.target.canvas.frame, null);
134+
var s = _event_canvas_scale(e);
123135
var evt = [EVENT_TAG.BUTTON_ACTION,
124136
[0, e.target.canvas,
125137
caml_int64_of_float(e.timeStamp * 1000.0),
126-
[0, e.offsetX, e.offsetY], e.button + 1, 1]];
138+
[0, e.offsetX*s.scaleX, e.offsetY*s.scaleY], e.button + 1, 1]];
127139
_ml_canvas_process_event(evt);
128140
}
129141
return false;
130142
}
131143

132144
//Provides: _up_handler
133-
//Requires: _move, _ml_canvas_process_event, EVENT_TAG
145+
//Requires: _move, _ml_canvas_process_event, _event_canvas_scale, EVENT_TAG
134146
//Requires: caml_int64_of_float
135147
function _up_handler(e) {
136148
_move.moving = false;
137149
if (e.target.canvas !== undefined) {
150+
var s = _event_canvas_scale(e);
138151
var evt = [EVENT_TAG.BUTTON_ACTION,
139152
[0, e.target.canvas,
140153
caml_int64_of_float(e.timeStamp * 1000.0),
141-
[0, e.offsetX, e.offsetY], e.button + 1, 0]];
154+
[0, e.offsetX*s.scaleX, e.offsetY*s.scaleY], e.button + 1, 0]];
142155
_ml_canvas_process_event(evt);
143156
}
144157
return false; // = prevent default behavior
145158
}
146159

147160
//Provides: _move_handler
148-
//Requires: _move, _ml_canvas_process_event, EVENT_TAG
161+
//Requires: _move, _ml_canvas_process_event, _event_canvas_scale, EVENT_TAG
149162
//Requires: caml_int64_of_float
150163
function _move_handler(e) {
151164
if (_move.moving) {
@@ -161,15 +174,30 @@ function _move_handler(e) {
161174
_move.target.style.left = canvas.x + "px";
162175
_move.target.style.top = canvas.y + "px";
163176
} else if (e.target.canvas !== undefined) {
177+
var s = _event_canvas_scale(e);
164178
var evt = [EVENT_TAG.MOUSE_MOVE,
165179
[0, e.target.canvas,
166180
caml_int64_of_float(e.timeStamp * 1000.0),
167-
[0, e.offsetX, e.offsetY]]];
181+
[0, e.offsetX*s.scaleX, e.offsetY*s.scaleY]]];
168182
_ml_canvas_process_event(evt);
169183
}
170184
return false;
171185
}
172186

187+
//Provides: _resize_handler
188+
//Requires: _ml_canvas_process_event, EVENT_TAG
189+
//Requires: caml_int64_of_float
190+
function _resize_handler(entries) {
191+
entries.forEach(function (e) {
192+
var evt = [EVENT_TAG.CANVAS_RESIZED,
193+
[0, e.target.canvas,
194+
caml_int64_of_float(e.timeStamp * 1000.0),
195+
[0, e.target.clientWidth, e.target.clientHeight]]];
196+
_ml_canvas_process_event(evt);
197+
});
198+
return false;
199+
}
200+
173201
//Provides: _frame_handler
174202
//Requires: _ml_canvas_process_event, EVENT_TAG
175203
//Requires: caml_int64_of_float
@@ -588,7 +616,7 @@ var _next_id = 0;
588616

589617
//Provides: _ml_canvas_decorate
590618
//Requires: caml_jsstring_of_string
591-
function _ml_canvas_decorate(header, resizeable, minimize,
619+
function _ml_canvas_decorate(header, minimize,
592620
maximize, close, title) {
593621
var width = header.width;
594622
var ctxt = header.getContext("2d");
@@ -613,10 +641,11 @@ function _ml_canvas_decorate(header, resizeable, minimize,
613641
}
614642

615643
//Provides: ml_canvas_create_onscreen
616-
//Requires: _ml_canvas_ensure_initialized, _ml_canvas_valid_canvas_size, _next_id, _header_down_handler, _surface_down_handler, _up_handler, _move_handler, _ml_canvas_decorate, Optional_bool_val, Optional_val
644+
//Requires: _ml_canvas_ensure_initialized, _ml_canvas_valid_canvas_size, _resize, _next_id, _header_down_handler
645+
//Requires: _surface_down_handler, _up_handler, _move_handler, _ml_canvas_decorate, Optional_bool_val, Optional_val
617646
//Requires: caml_invalid_argument
618-
function ml_canvas_create_onscreen(autocmmit, decorated, resizeable, minimize,
619-
maximize, close, title, pos, size) {
647+
function ml_canvas_create_onscreen(autocommit, decorated, resizeable, minimize,
648+
maximize, close, title, target, pos, size) {
620649

621650
_ml_canvas_ensure_initialized();
622651

@@ -631,17 +660,23 @@ function ml_canvas_create_onscreen(autocmmit, decorated, resizeable, minimize,
631660
var y = pos[2];
632661

633662
var autocommit = Optional_bool_val(autocommit, true);
634-
var decorated = Optional_bool_val(decorated, true);
663+
var decorated = false; // Optional_bool_val(decorated, true);
635664
var resizeable = Optional_bool_val(resizeable, true);
636665
var minimize = Optional_bool_val(minimize, true);
637666
var maximize = Optional_bool_val(maximize, true);
638667
var close = Optional_bool_val(close, true);
639668
var title = Optional_val(title, null);
669+
var target = Optional_val(target, null);
670+
target = document.getElementById(target);
671+
if(target == null) {
672+
target = document.body;
673+
}
640674

641675
var id = ++_next_id;
642676

643677
var canvas = {
644678
name: title,
679+
target: target,
645680
frame: null,
646681
header: null,
647682
surface: null,
@@ -661,16 +696,18 @@ function ml_canvas_create_onscreen(autocmmit, decorated, resizeable, minimize,
661696

662697
var frame = document.createElement("div");
663698
frame.id = "f" + id;
664-
frame.style.width = width + "px";
665-
frame.style.height = height + header_height + "px";
699+
if (resizeable == true) {
700+
frame.style.width = "100%";
701+
frame.style.height = "100%";
702+
} else {
703+
frame.style.width = width + "px";
704+
frame.style.height = height + header_height + "px";
705+
}
666706
frame.style.visibility = "hidden";
667-
frame.style.position = "absolute";
668-
frame.style.left = x + "px";
669-
frame.style.top = y + "px";
670707
frame.oncontextmenu = function() { return false; }
671708
frame.canvas = canvas;
672709
canvas.frame = frame;
673-
document.body.appendChild(frame);
710+
target.appendChild(frame);
674711

675712
var header = null;
676713
if (decorated === true) {
@@ -679,8 +716,7 @@ function ml_canvas_create_onscreen(autocmmit, decorated, resizeable, minimize,
679716
header.id = "h" + id;
680717
header.width = width;
681718
header.height = 30;
682-
header.style.position = "absolute";
683-
_ml_canvas_decorate(header, resizeable, minimize, maximize, close, title);
719+
_ml_canvas_decorate(header, minimize, maximize, close, title);
684720
header.onmousedown = _header_down_handler;
685721
header.canvas = canvas;
686722
canvas.header = header;
@@ -691,13 +727,17 @@ function ml_canvas_create_onscreen(autocmmit, decorated, resizeable, minimize,
691727
surface.id = "s" + id;
692728
surface.width = width;
693729
surface.height = height;
694-
surface.style.position = "absolute"
695-
surface.style.top = header_height + "px";
696730
surface.onmousedown = _surface_down_handler;
697731
surface.canvas = canvas;
698732
canvas.surface = surface;
699733
frame.appendChild(surface);
700734

735+
if (resizeable === true) {
736+
surface.style.width = "100%";
737+
surface.style.height = "100%";
738+
_resize.observe(surface);
739+
}
740+
701741
var ctxt = surface.getContext("2d");
702742
ctxt.globalAlpha = 1.0;
703743
ctxt.lineWidth = 1.0;
@@ -907,7 +947,7 @@ function ml_canvas_set_size(canvas, size) {
907947
var img = canvas.ctxt.getImageData(0, 0, canvas.width, canvas.height);
908948
if (canvas.header !== null) {
909949
canvas.header.width = width;
910-
_ml_canvas_decorate(canvas.header, canvas.resizeable, canvas.minimize,
950+
_ml_canvas_decorate(canvas.header, canvas.minimize,
911951
canvas.maximize, canvas.close, canvas.name);
912952
}
913953
canvas.surface.width = canvas.width = width;
@@ -1551,16 +1591,16 @@ function ml_canvas_key_of_int(keycode) {
15511591
/* Backend */
15521592

15531593
//Provides: ml_canvas_init
1554-
//Requires: _key_down_handler, _key_up_handler, _up_handler, _move_handler, _frame_handler, _ml_canvas_initialized
1555-
//Requires: caml_list_to_js_array
1594+
//Requires: _key_down_handler, _key_up_handler, _up_handler, _move_handler, _resize_handler, _frame_handler
1595+
//Requires: _ml_canvas_initialized, caml_list_to_js_array
15561596
function ml_canvas_init() {
15571597
if (_ml_canvas_initialized === true) {
15581598
return 0;
15591599
}
1560-
document.onkeydown = _key_down_handler;
1561-
document.onkeyup = _key_up_handler;
1562-
document.onmouseup = _up_handler;
1563-
document.onmousemove = _move_handler;
1600+
document.addEventListener("keydown", _key_down_handler, {passive: true});
1601+
document.addEventListener("keyup", _key_up_handler, {passive: true});
1602+
document.addEventListener("mouseup", _up_handler, {passive: true});
1603+
document.addEventListener("mousemove", _move_handler, {passive: true});
15641604
window.requestAnimationFrame(_frame_handler);
15651605
_ml_canvas_initialized = true;
15661606
return 0;

0 commit comments

Comments
 (0)