1cb93a386Sopenharmony_ci<!DOCTYPE html>
2cb93a386Sopenharmony_ci<title>TextEdit demo in CanvasKit</title>
3cb93a386Sopenharmony_ci<meta charset="utf-8" />
4cb93a386Sopenharmony_ci<meta http-equiv="X-UA-Compatible" content="IE=edge">
5cb93a386Sopenharmony_ci<meta name="viewport" content="width=device-width, initial-scale=1.0">
6cb93a386Sopenharmony_ci<script type="text/javascript" src="https://particles.skia.org/dist/canvaskit.js"></script>
7cb93a386Sopenharmony_ci<script type="text/javascript" src="textapi_utils.js"></script>
8cb93a386Sopenharmony_ci<script type="text/javascript" src="spiralshader.js"></script>
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci<style>
11cb93a386Sopenharmony_cicanvas {
12cb93a386Sopenharmony_ci  border: 1px dashed grey;
13cb93a386Sopenharmony_ci}
14cb93a386Sopenharmony_ci</style>
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ci<body>
17cb93a386Sopenharmony_ci  <h1>TextEdit in CanvasKit</h1>
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_ci  <canvas id=para2 width=600 height=600 tabindex='-1'></canvas>
20cb93a386Sopenharmony_ci</body>
21cb93a386Sopenharmony_ci
22cb93a386Sopenharmony_ci<script type="text/javascript" charset="utf-8">
23cb93a386Sopenharmony_ci    let CanvasKit;
24cb93a386Sopenharmony_ci    onload = async () => {
25cb93a386Sopenharmony_ci      CanvasKit = await CanvasKitInit({ locateFile: (file) => 'https://particles.skia.org/dist/'+file });
26cb93a386Sopenharmony_ci      ParagraphAPI2();
27cb93a386Sopenharmony_ci    };
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ci    function ParagraphAPI2() {
30cb93a386Sopenharmony_ci      const surface = CanvasKit.MakeCanvasSurface('para2');
31cb93a386Sopenharmony_ci      if (!surface) {
32cb93a386Sopenharmony_ci        console.error('Could not make surface');
33cb93a386Sopenharmony_ci        return;
34cb93a386Sopenharmony_ci      }
35cb93a386Sopenharmony_ci
36cb93a386Sopenharmony_ci      const mouse = MakeMouse();
37cb93a386Sopenharmony_ci      const cursor = MakeCursor(CanvasKit);
38cb93a386Sopenharmony_ci      const canvas = surface.getCanvas();
39cb93a386Sopenharmony_ci      const spiralEffect = MakeSpiralShaderEffect(CanvasKit);
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_ci      const text0 = "In a hole in the ground there lived a hobbit. Not a nasty, dirty, " +
42cb93a386Sopenharmony_ci                    "wet hole full of worms and oozy smells. This was a hobbit-hole and " +
43cb93a386Sopenharmony_ci                    "that means good food, a warm hearth, and all the comforts of home.";
44cb93a386Sopenharmony_ci      const LOC_X = 20,
45cb93a386Sopenharmony_ci            LOC_Y = 20;
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ci      const bgPaint = new CanvasKit.Paint();
48cb93a386Sopenharmony_ci      bgPaint.setColor([0.965, 0.965, 0.965, 1]);
49cb93a386Sopenharmony_ci
50cb93a386Sopenharmony_ci      const editor = MakeEditor(text0, {typeface:null, size:30}, cursor, 540);
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci      editor.applyStyleToRange({size:130}, 0, 1);
53cb93a386Sopenharmony_ci      editor.applyStyleToRange({italic:true}, 38, 38+6);
54cb93a386Sopenharmony_ci      editor.applyStyleToRange({color:[1,0,0,1]}, 5, 5+4);
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci      editor.setXY(LOC_X, LOC_Y);
57cb93a386Sopenharmony_ci
58cb93a386Sopenharmony_ci      function drawFrame(canvas) {
59cb93a386Sopenharmony_ci        const lines = editor.getLines();
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
62cb93a386Sopenharmony_ci
63cb93a386Sopenharmony_ci        if (mouse.isActive()) {
64cb93a386Sopenharmony_ci            const pos = mouse.getPos(-LOC_X, -LOC_Y);
65cb93a386Sopenharmony_ci            const a = lines_pos_to_index(lines, pos[0], pos[1]);
66cb93a386Sopenharmony_ci            const b = lines_pos_to_index(lines, pos[2], pos[3]);
67cb93a386Sopenharmony_ci            if (a === b) {
68cb93a386Sopenharmony_ci                editor.setIndex(a);
69cb93a386Sopenharmony_ci            } else {
70cb93a386Sopenharmony_ci                editor.setIndices(a, b);
71cb93a386Sopenharmony_ci            }
72cb93a386Sopenharmony_ci        }
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_ci        canvas.drawRect(editor.bounds(), bgPaint);
75cb93a386Sopenharmony_ci
76cb93a386Sopenharmony_ci        {
77cb93a386Sopenharmony_ci            // update our animated shaders
78cb93a386Sopenharmony_ci            const rad_scale = Math.sin(Date.now() / 5000) / 2;
79cb93a386Sopenharmony_ci            const shader0 = spiralEffect.makeShader([
80cb93a386Sopenharmony_ci                rad_scale,
81cb93a386Sopenharmony_ci                editor.width()/2, editor.width()/2,
82cb93a386Sopenharmony_ci                1,0,0,1,                            // color0
83cb93a386Sopenharmony_ci                0,0,1,1                             // color1
84cb93a386Sopenharmony_ci             ]);
85cb93a386Sopenharmony_ci            editor.draw(canvas, [shader0]);
86cb93a386Sopenharmony_ci            shader0.delete();
87cb93a386Sopenharmony_ci        }
88cb93a386Sopenharmony_ci
89cb93a386Sopenharmony_ci        surface.requestAnimationFrame(drawFrame);
90cb93a386Sopenharmony_ci      }
91cb93a386Sopenharmony_ci      surface.requestAnimationFrame(drawFrame);
92cb93a386Sopenharmony_ci
93cb93a386Sopenharmony_ci      function interact(e) {
94cb93a386Sopenharmony_ci        const type = e.type;
95cb93a386Sopenharmony_ci        if (type === 'pointerup') {
96cb93a386Sopenharmony_ci            mouse.setUp(e.offsetX, e.offsetY);
97cb93a386Sopenharmony_ci        } else if (type === 'pointermove') {
98cb93a386Sopenharmony_ci            mouse.setMove(e.offsetX, e.offsetY);
99cb93a386Sopenharmony_ci        } else if (type === 'pointerdown') {
100cb93a386Sopenharmony_ci            mouse.setDown(e.offsetX, e.offsetY);
101cb93a386Sopenharmony_ci        }
102cb93a386Sopenharmony_ci      };
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_ci      function keyhandler(e) {
105cb93a386Sopenharmony_ci          switch (e.key) {
106cb93a386Sopenharmony_ci              case 'ArrowLeft':  editor.moveDX(-1); return;
107cb93a386Sopenharmony_ci              case 'ArrowRight': editor.moveDX(1); return;
108cb93a386Sopenharmony_ci              case 'ArrowUp':
109cb93a386Sopenharmony_ci                e.preventDefault();
110cb93a386Sopenharmony_ci                editor.moveDY(-1);
111cb93a386Sopenharmony_ci                return;
112cb93a386Sopenharmony_ci              case 'ArrowDown':
113cb93a386Sopenharmony_ci                e.preventDefault();
114cb93a386Sopenharmony_ci                editor.moveDY(1);
115cb93a386Sopenharmony_ci                return;
116cb93a386Sopenharmony_ci            case 'Backspace':
117cb93a386Sopenharmony_ci                editor.deleteSelection(-1);
118cb93a386Sopenharmony_ci                return;
119cb93a386Sopenharmony_ci            case 'Delete':
120cb93a386Sopenharmony_ci                editor.deleteSelection(1);
121cb93a386Sopenharmony_ci                return;
122cb93a386Sopenharmony_ci              case 'Shift':
123cb93a386Sopenharmony_ci                return;
124cb93a386Sopenharmony_ci              case 'Tab':   // todo: figure out how to handle...
125cb93a386Sopenharmony_ci                e.preventDefault();
126cb93a386Sopenharmony_ci                return;
127cb93a386Sopenharmony_ci            }
128cb93a386Sopenharmony_ci            if (e.ctrlKey) {
129cb93a386Sopenharmony_ci                e.preventDefault();
130cb93a386Sopenharmony_ci                e.stopImmediatePropagation();
131cb93a386Sopenharmony_ci                switch (e.key) {
132cb93a386Sopenharmony_ci                    case 'r': editor.applyStyleToSelection({color:[1,0,0,1]}); return;
133cb93a386Sopenharmony_ci                    case 'g': editor.applyStyleToSelection({color:[0,0.6,0,1]}); return;
134cb93a386Sopenharmony_ci                    case 'u': editor.applyStyleToSelection({color:[0,0,1,1]}); return;
135cb93a386Sopenharmony_ci                    case 'k': editor.applyStyleToSelection({color:[0,0,0,1]}); return;
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ci                    case 's': editor.applyStyleToSelection({shaderIndex:0}); return;
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_ci                    case 'i': editor.applyStyleToSelection({italic:'toggle'}); return;
140cb93a386Sopenharmony_ci                    case 'b': editor.applyStyleToSelection({bold:'toggle'}); return;
141cb93a386Sopenharmony_ci                    case 'w': editor.applyStyleToSelection({wavy:'toggle'}); return;
142cb93a386Sopenharmony_ci
143cb93a386Sopenharmony_ci                    case ']': editor.applyStyleToSelection({size_add:1});  return;
144cb93a386Sopenharmony_ci                    case '[': editor.applyStyleToSelection({size_add:-1}); return;
145cb93a386Sopenharmony_ci                    case '}': editor.applyStyleToSelection({size_add:10});  return;
146cb93a386Sopenharmony_ci                    case '{': editor.applyStyleToSelection({size_add:-10}); return;
147cb93a386Sopenharmony_ci                }
148cb93a386Sopenharmony_ci            }
149cb93a386Sopenharmony_ci            if (!e.ctrlKey && !e.metaKey) {
150cb93a386Sopenharmony_ci                if (e.key.length == 1) {  // avoid keys like "Escape" for now
151cb93a386Sopenharmony_ci                    e.preventDefault();
152cb93a386Sopenharmony_ci                    e.stopImmediatePropagation();
153cb93a386Sopenharmony_ci                    editor.insert(e.key);
154cb93a386Sopenharmony_ci                }
155cb93a386Sopenharmony_ci            }
156cb93a386Sopenharmony_ci      }
157cb93a386Sopenharmony_ci
158cb93a386Sopenharmony_ci      document.getElementById('para2').addEventListener('pointermove', interact);
159cb93a386Sopenharmony_ci      document.getElementById('para2').addEventListener('pointerdown', interact);
160cb93a386Sopenharmony_ci      document.getElementById('para2').addEventListener('pointerup', interact);
161cb93a386Sopenharmony_ci      document.getElementById('para2').addEventListener('keydown', keyhandler);
162cb93a386Sopenharmony_ci      return surface;
163cb93a386Sopenharmony_ci    }
164cb93a386Sopenharmony_ci
165cb93a386Sopenharmony_ci</script>
166