22// Distributed under the terms of the Modified BSD License.
33
44import {
5- DOMWidgetModel , DOMWidgetView , ISerializers , Dict , ViewList , unpack_models
5+ DOMWidgetModel , DOMWidgetView , ISerializers , Dict , unpack_models
66} from '@jupyter-widgets/base' ;
77
88import {
@@ -219,7 +219,7 @@ class CanvasModel extends DOMWidgetModel {
219219
220220 const image = await unpack_models ( serializedImage , this . widget_manager ) ;
221221
222- if ( image instanceof CanvasModel ) {
222+ if ( image instanceof CanvasModel || image instanceof MultiCanvasModel ) {
223223 this . _drawImage ( image . canvas , x , y , width , height ) ;
224224 return ;
225225 }
@@ -431,6 +431,8 @@ class MultiCanvasModel extends DOMWidgetModel {
431431 _canvases : [ ] ,
432432 sync_image_data : false ,
433433 image_data : null ,
434+ width : 700 ,
435+ height : 500 ,
434436 } ;
435437 }
436438
@@ -445,45 +447,74 @@ class MultiCanvasModel extends DOMWidgetModel {
445447 initialize ( attributes : any , options : any ) {
446448 super . initialize ( attributes , options ) ;
447449
448- this . on ( 'change:_canvases' , this . updateListeners . bind ( this ) ) ;
450+ this . canvas = document . createElement ( 'canvas' ) ;
451+ this . ctx = getContext ( this . canvas ) ;
452+
453+ this . resizeCanvas ( ) ;
454+
455+ this . on_some_change ( [ 'width' , 'height' ] , this . resizeCanvas , this ) ;
456+ this . on ( 'change:_canvases' , this . updateCanvasModels . bind ( this ) ) ;
449457 this . on ( 'change:sync_image_data' , this . syncImageData . bind ( this ) ) ;
450- this . updateListeners ( ) ;
458+
459+ this . updateCanvasModels ( ) ;
451460 }
452461
453- private updateListeners ( ) {
462+ get canvasModels ( ) : CanvasModel [ ] {
463+ return this . get ( '_canvases' ) ;
464+ }
465+
466+ private updateCanvasModels ( ) {
454467 // TODO: Remove old listeners
455- for ( const canvasModel of this . get ( '_canvases' ) ) {
456- canvasModel . on ( 'new-frame' , this . syncImageData , this ) ;
468+ for ( const canvasModel of this . canvasModels ) {
469+ canvasModel . on ( 'new-frame' , this . updateCanvas , this ) ;
457470 }
471+
472+ this . updateCanvas ( ) ;
458473 }
459474
460- private async syncImageData ( ) {
461- if ( ! this . get ( 'sync_image_data' ) ) {
462- return ;
475+ private updateCanvas ( ) {
476+ this . ctx . clearRect ( 0 , 0 , this . get ( 'width' ) , this . get ( 'height' ) ) ;
477+
478+ for ( const canvasModel of this . canvasModels ) {
479+ this . ctx . drawImage ( canvasModel . canvas , 0 , 0 ) ;
463480 }
464481
465- // Draw on a temporary off-screen canvas.
466- const offscreenCanvas = document . createElement ( 'canvas' ) ;
467- offscreenCanvas . width = this . get ( 'width' ) ;
468- offscreenCanvas . height = this . get ( 'height' ) ;
469- const ctx = getContext ( offscreenCanvas ) ;
482+ this . forEachView ( ( view : MultiCanvasView ) => {
483+ view . updateCanvas ( ) ;
484+ } ) ;
470485
471- for ( const canvasModel of this . get ( '_canvases' ) ) {
472- ctx . drawImage ( canvasModel . canvas , 0 , 0 ) ;
486+ this . syncImageData ( ) ;
487+ }
473488
474- // Also update the sub-canvas image-data
475- const bytes = await toBytes ( canvasModel . canvas ) ;
489+ private resizeCanvas ( ) {
490+ this . canvas . setAttribute ( 'width' , this . get ( 'width' ) ) ;
491+ this . canvas . setAttribute ( 'height' , this . get ( 'height' ) ) ;
492+ }
476493
477- canvasModel . set ( 'image_data' , bytes ) ;
478- canvasModel . save_changes ( ) ;
494+ private async syncImageData ( ) {
495+ if ( ! this . get ( 'sync_image_data' ) ) {
496+ return ;
479497 }
480498
481- const bytes = await toBytes ( offscreenCanvas ) ;
499+ const bytes = await toBytes ( this . canvas ) ;
482500
483501 this . set ( 'image_data' , bytes ) ;
484502 this . save_changes ( ) ;
485503 }
486504
505+ private forEachView ( callback : ( view : MultiCanvasView ) => void ) {
506+ for ( const view_id in this . views ) {
507+ this . views [ view_id ] . then ( ( view : MultiCanvasView ) => {
508+ callback ( view ) ;
509+ } ) ;
510+ }
511+ }
512+
513+ canvas : HTMLCanvasElement ;
514+ ctx : CanvasRenderingContext2D ;
515+
516+ views : Dict < Promise < MultiCanvasView > > ;
517+
487518 static model_name = 'MultiCanvasModel' ;
488519 static model_module = MODULE_NAME ;
489520 static model_module_version = MODULE_VERSION ;
@@ -496,48 +527,34 @@ class MultiCanvasModel extends DOMWidgetModel {
496527export
497528class MultiCanvasView extends DOMWidgetView {
498529 render ( ) {
499- this . container = document . createElement ( 'div' ) ;
500- this . container . style . position = 'relative' ;
501-
502- this . el . appendChild ( this . container ) ;
530+ this . ctx = getContext ( this . el ) ;
503531
504- this . canvas_views = new ViewList < CanvasView > ( this . createCanvasView , this . removeCanvasView , this ) ;
505- this . updateCanvasViews ( ) ;
532+ this . resizeCanvas ( ) ;
533+ this . model . on_some_change ( [ 'width' , 'height' ] , this . resizeCanvas , this ) ;
506534
507- this . model . on ( 'change:_canvases' , this . updateCanvasViews . bind ( this ) ) ;
535+ this . updateCanvas ( ) ;
508536 }
509537
510- private updateCanvasViews ( ) {
511- this . canvas_views . update ( this . model . get ( '_canvases' ) ) ;
538+ clear ( ) {
539+ this . ctx . clearRect ( 0 , 0 , this . el . width , this . el . height ) ;
512540 }
513541
514- private createCanvasView ( canvasModel : CanvasModel , index : number ) {
515- // The following ts-ignore is needed due to ipywidgets's implementation
516- // @ts -ignore
517- return this . create_child_view ( canvasModel ) . then ( ( canvasView : CanvasView ) => {
518- const canvasContainer = document . createElement ( 'div' ) ;
519-
520- canvasContainer . style . zIndex = index . toString ( ) ;
521-
522- if ( index == 0 ) {
523- // This will enforce the container to respect the children size.
524- canvasContainer . style . position = 'relative' ;
525- canvasContainer . style . float = 'left' ;
526- } else {
527- canvasContainer . style . position = 'absolute' ;
528- }
529-
530- canvasContainer . appendChild ( canvasView . el ) ;
531- this . container . appendChild ( canvasContainer ) ;
542+ updateCanvas ( ) {
543+ this . clear ( ) ;
544+ this . ctx . drawImage ( this . model . canvas , 0 , 0 ) ;
545+ }
532546
533- return canvasView ;
534- } ) ;
547+ private resizeCanvas ( ) {
548+ this . el . setAttribute ( 'width' , this . model . get ( 'width' ) ) ;
549+ this . el . setAttribute ( 'height' , this . model . get ( 'height' ) ) ;
535550 }
536551
537- private removeCanvasView ( canvasView : CanvasView ) {
538- this . container . removeChild ( canvasView . el ) ;
552+ get tagName ( ) : string {
553+ return 'canvas' ;
539554 }
540555
541- private container : HTMLDivElement ;
542- private canvas_views : ViewList < CanvasView > ;
556+ el : HTMLCanvasElement ;
557+ ctx : CanvasRenderingContext2D ;
558+
559+ model : MultiCanvasModel ;
543560}
0 commit comments