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