|  | <!DOCTYPE html> | 
|  | <title>TextEdit demo in CanvasKit</title> | 
|  | <meta charset="utf-8" /> | 
|  | <meta http-equiv="X-UA-Compatible" content="IE=edge"> | 
|  | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 
|  | <script type="text/javascript" src="https://unpkg.com/canvaskit-wasm@0.34.1/bin/full/canvaskit.js"></script> | 
|  | <script type="text/javascript" src="textapi_utils.js"></script> | 
|  | <script type="text/javascript" src="spiralshader.js"></script> | 
|  |  | 
|  | <style> | 
|  | canvas { | 
|  | border: 1px dashed grey; | 
|  | } | 
|  | </style> | 
|  |  | 
|  | <body> | 
|  | <h1>TextEdit in CanvasKit</h1> | 
|  |  | 
|  | <canvas id=para2 width=600 height=600 tabindex='-1'></canvas> | 
|  | </body> | 
|  |  | 
|  | <script type="text/javascript" charset="utf-8"> | 
|  | let CanvasKit; | 
|  | onload = async () => { | 
|  | CanvasKit = await CanvasKitInit({ locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.34.1/bin/full/'+file }); | 
|  | ParagraphAPI2(); | 
|  | }; | 
|  |  | 
|  | function ParagraphAPI2() { | 
|  | const surface = CanvasKit.MakeCanvasSurface('para2'); | 
|  | if (!surface) { | 
|  | console.error('Could not make surface'); | 
|  | return; | 
|  | } | 
|  |  | 
|  | const mouse = MakeMouse(); | 
|  | const cursor = MakeCursor(CanvasKit); | 
|  | const canvas = surface.getCanvas(); | 
|  | const spiralEffect = MakeSpiralShaderEffect(CanvasKit); | 
|  |  | 
|  | const text0 = "In a hole in the ground there lived a hobbit. Not a nasty, dirty, " + | 
|  | "wet hole full of worms and oozy smells. This was a hobbit-hole and " + | 
|  | "that means good food, a warm hearth, and all the comforts of home."; | 
|  | const LOC_X = 20, | 
|  | LOC_Y = 20; | 
|  |  | 
|  | const bgPaint = new CanvasKit.Paint(); | 
|  | bgPaint.setColor([0.965, 0.965, 0.965, 1]); | 
|  |  | 
|  | const editor = MakeEditor(text0, {typeface:null, size:30}, cursor, 540); | 
|  |  | 
|  | editor.applyStyleToRange({size:130}, 0, 1); | 
|  | editor.applyStyleToRange({italic:true}, 38, 38+6); | 
|  | editor.applyStyleToRange({color:[1,0,0,1]}, 5, 5+4); | 
|  |  | 
|  | editor.setXY(LOC_X, LOC_Y); | 
|  |  | 
|  | function drawFrame(canvas) { | 
|  | const lines = editor.getLines(); | 
|  |  | 
|  | canvas.clear(CanvasKit.WHITE); | 
|  |  | 
|  | if (mouse.isActive()) { | 
|  | const pos = mouse.getPos(-LOC_X, -LOC_Y); | 
|  | const a = lines_pos_to_index(lines, pos[0], pos[1]); | 
|  | const b = lines_pos_to_index(lines, pos[2], pos[3]); | 
|  | if (a === b) { | 
|  | editor.setIndex(a); | 
|  | } else { | 
|  | editor.setIndices(a, b); | 
|  | } | 
|  | } | 
|  |  | 
|  | canvas.drawRect(editor.bounds(), bgPaint); | 
|  |  | 
|  | { | 
|  | // update our animated shaders | 
|  | const rad_scale = Math.sin(Date.now() / 5000) / 2; | 
|  | const shader0 = spiralEffect.makeShader([ | 
|  | rad_scale, | 
|  | editor.width()/2, editor.width()/2, | 
|  | 1,0,0,1,                            // color0 | 
|  | 0,0,1,1                             // color1 | 
|  | ]); | 
|  | editor.draw(canvas, [shader0]); | 
|  | shader0.delete(); | 
|  | } | 
|  |  | 
|  | surface.requestAnimationFrame(drawFrame); | 
|  | } | 
|  | surface.requestAnimationFrame(drawFrame); | 
|  |  | 
|  | function interact(e) { | 
|  | const type = e.type; | 
|  | if (type === 'pointerup') { | 
|  | mouse.setUp(e.offsetX, e.offsetY); | 
|  | } else if (type === 'pointermove') { | 
|  | mouse.setMove(e.offsetX, e.offsetY); | 
|  | } else if (type === 'pointerdown') { | 
|  | mouse.setDown(e.offsetX, e.offsetY); | 
|  | } | 
|  | }; | 
|  |  | 
|  | function keyhandler(e) { | 
|  | switch (e.key) { | 
|  | case 'ArrowLeft':  editor.moveDX(-1); return; | 
|  | case 'ArrowRight': editor.moveDX(1); return; | 
|  | case 'ArrowUp': | 
|  | e.preventDefault(); | 
|  | editor.moveDY(-1); | 
|  | return; | 
|  | case 'ArrowDown': | 
|  | e.preventDefault(); | 
|  | editor.moveDY(1); | 
|  | return; | 
|  | case 'Backspace': | 
|  | editor.deleteSelection(-1); | 
|  | return; | 
|  | case 'Delete': | 
|  | editor.deleteSelection(1); | 
|  | return; | 
|  | case 'Shift': | 
|  | return; | 
|  | case 'Tab':   // todo: figure out how to handle... | 
|  | e.preventDefault(); | 
|  | return; | 
|  | } | 
|  | if (e.ctrlKey) { | 
|  | e.preventDefault(); | 
|  | e.stopImmediatePropagation(); | 
|  | switch (e.key) { | 
|  | case 'r': editor.applyStyleToSelection({color:[1,0,0,1]}); return; | 
|  | case 'g': editor.applyStyleToSelection({color:[0,0.6,0,1]}); return; | 
|  | case 'u': editor.applyStyleToSelection({color:[0,0,1,1]}); return; | 
|  | case 'k': editor.applyStyleToSelection({color:[0,0,0,1]}); return; | 
|  |  | 
|  | case 's': editor.applyStyleToSelection({shaderIndex:0}); return; | 
|  |  | 
|  | case 'i': editor.applyStyleToSelection({italic:'toggle'}); return; | 
|  | case 'b': editor.applyStyleToSelection({bold:'toggle'}); return; | 
|  | case 'w': editor.applyStyleToSelection({wavy:'toggle'}); return; | 
|  |  | 
|  | case ']': editor.applyStyleToSelection({size_add:1});  return; | 
|  | case '[': editor.applyStyleToSelection({size_add:-1}); return; | 
|  | case '}': editor.applyStyleToSelection({size_add:10});  return; | 
|  | case '{': editor.applyStyleToSelection({size_add:-10}); return; | 
|  | } | 
|  | } | 
|  | if (!e.ctrlKey && !e.metaKey) { | 
|  | if (e.key.length == 1) {  // avoid keys like "Escape" for now | 
|  | e.preventDefault(); | 
|  | e.stopImmediatePropagation(); | 
|  | editor.insert(e.key); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | document.getElementById('para2').addEventListener('pointermove', interact); | 
|  | document.getElementById('para2').addEventListener('pointerdown', interact); | 
|  | document.getElementById('para2').addEventListener('pointerup', interact); | 
|  | document.getElementById('para2').addEventListener('keydown', keyhandler); | 
|  | return surface; | 
|  | } | 
|  |  | 
|  | </script> |