Track mouse for cursor and hilites
Change-Id: Icfc8a28fa0c9e3118ef4ddc5ae12162aab3fb484 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/398556 Commit-Queue: Mike Reed <reed@google.com> Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
parent
31fddc3769
commit
097263bb50
@ -32,6 +32,7 @@
|
||||
|
||||
<h2> Paragraph </h2>
|
||||
<canvas id=para1 width=600 height=600></canvas>
|
||||
<canvas id=para2 width=600 height=600></canvas>
|
||||
|
||||
<h2> CanvasKit can serialize/deserialize .skp files</h2>
|
||||
<canvas id=skp width=500 height=500></canvas>
|
||||
@ -141,6 +142,7 @@
|
||||
|
||||
Promise.all([ckLoaded, loadFont]).then((results) => {
|
||||
ParagraphAPI1(...results);
|
||||
ParagraphAPI2(...results);
|
||||
GlyphGame(...results)
|
||||
});
|
||||
Promise.all([ckLoaded, loadSkp]).then((results) => {SkpExample(...results)});
|
||||
@ -303,6 +305,253 @@
|
||||
return surface;
|
||||
}
|
||||
|
||||
function MakeCursor(CanvasKit) {
|
||||
const linePaint = new CanvasKit.Paint();
|
||||
linePaint.setColor([0,0,1,1]);
|
||||
linePaint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
linePaint.setStrokeWidth(2);
|
||||
linePaint.setAntiAlias(true);
|
||||
|
||||
const pathPaint = new CanvasKit.Paint();
|
||||
pathPaint.setColor([0,0,1,0.25]);
|
||||
linePaint.setAntiAlias(true);
|
||||
|
||||
return {
|
||||
_line_paint: linePaint, // wrap in weak-ref so we can delete it?
|
||||
_path_paint: pathPaint,
|
||||
_x: 0,
|
||||
_top: 0,
|
||||
_bottom: 0,
|
||||
_path: null, // only use x,top,bottom if path is null
|
||||
_draws_per_sec: 2,
|
||||
|
||||
// pass 0 for no-draw, pass inf. for always on
|
||||
setBlinkRate: function(blinks_per_sec) {
|
||||
this._draws_per_sec = blinks_per_sec;
|
||||
},
|
||||
place: function(x, y, fontMetrics) {
|
||||
this._x = x;
|
||||
this._top = y + fontMetrics.ascent;
|
||||
this._bottom = y + fontMetrics.descent;
|
||||
|
||||
this._path = null;
|
||||
},
|
||||
setPath: function(path) {
|
||||
this._path = path;
|
||||
},
|
||||
draw_before: function(canvas) {
|
||||
if (this._path) {
|
||||
canvas.drawPath(this._path, this._path_paint);
|
||||
}
|
||||
},
|
||||
draw_after: function(canvas) {
|
||||
if (this._path) {
|
||||
return;
|
||||
}
|
||||
if (Math.floor(Date.now() * this._draws_per_sec / 1000) & 1) {
|
||||
canvas.drawLine(this._x, this._top, this._x, this._bottom, this._line_paint);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let mouse = {
|
||||
_start_x: 0, _start_y: 0,
|
||||
_curr_x: 0, _curr_y: 0,
|
||||
_active: false,
|
||||
|
||||
isActive: function() {
|
||||
return this._active;
|
||||
},
|
||||
setDown: function(x, y) {
|
||||
this._start_x = this._curr_x = x;
|
||||
this._start_y = this._curr_y = y;
|
||||
this._active = true;
|
||||
},
|
||||
setMove: function(x, y) {
|
||||
this._curr_x = x;
|
||||
this._curr_y = y;
|
||||
},
|
||||
setUp: function(x, y) {
|
||||
this._curr_x = x;
|
||||
this._curr_y = y;
|
||||
this._active = false;
|
||||
},
|
||||
getPos: function() {
|
||||
return [ this._start_x, this._start_y, this._curr_x, this._curr_y ];
|
||||
},
|
||||
}
|
||||
|
||||
function ParagraphAPI2(CanvasKit, fontData) {
|
||||
if (!CanvasKit || !fontData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const surface = CanvasKit.MakeCanvasSurface('para2');
|
||||
if (!surface) {
|
||||
console.error('Could not make surface');
|
||||
return;
|
||||
}
|
||||
|
||||
const cursor = MakeCursor(CanvasKit);
|
||||
const canvas = surface.getCanvas();
|
||||
const fontMgr = CanvasKit.FontMgr.FromData([fontData]);
|
||||
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
color: CanvasKit.GRAY,
|
||||
fontFamilies: ['Roboto'],
|
||||
fontSize: 40,
|
||||
},
|
||||
textAlign: CanvasKit.TextAlign.Left,
|
||||
});
|
||||
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
const text = "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.";
|
||||
builder.addText(text);
|
||||
const paragraph = builder.build();
|
||||
const WIDTH = 600;
|
||||
paragraph.layout(WIDTH);
|
||||
|
||||
const tf = fontMgr.MakeTypefaceFromData(fontData);
|
||||
const font = new CanvasKit.Font(tf, 40);
|
||||
const fm = font.getMetrics();
|
||||
const fontPaint = new CanvasKit.Paint();
|
||||
fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
|
||||
fontPaint.setAntiAlias(true);
|
||||
|
||||
function mid(a, b) { return (a + b) * 0.5; }
|
||||
|
||||
function pos_to_index(runs, x, y) {
|
||||
if (y < runs[0].positions[1] + fm.ascent) {
|
||||
return 0;
|
||||
}
|
||||
for (const r of runs) {
|
||||
if (y >= r.positions[1] + fm.ascent &&
|
||||
y <= r.positions[1] + fm.descent) {
|
||||
for (let i = 1; i < r.offsets.length; i += 1) {
|
||||
if (x < r.positions[i*2]) {
|
||||
if (x <= mid(r.positions[i*2-2], r.positions[i*2])) {
|
||||
return r.offsets[i-1];
|
||||
} else {
|
||||
return r.offsets[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return r.offsets[r.offsets.length-1];
|
||||
}
|
||||
}
|
||||
const r = runs[runs.length-1];
|
||||
return r.offsets[r.offsets.length-1]; // last
|
||||
}
|
||||
|
||||
function find_run(runs, index) {
|
||||
for (const r of runs) {
|
||||
if (index <= r.offsets[r.offsets.length-1]) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function get_run_pos(r, index) {
|
||||
for (const i in r.offsets) {
|
||||
if (index == r.offsets[i]) {
|
||||
return r.positions.slice(i*2, i*2+2);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function index_to_pos(runs, index) {
|
||||
const r = find_run(runs, index);
|
||||
return get_run_pos(r, index);
|
||||
}
|
||||
|
||||
function indices_to_path(runs, a, b, fm, width) {
|
||||
if (a == b) {
|
||||
return null;
|
||||
}
|
||||
if (a > b) {
|
||||
const tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
const path = new CanvasKit.Path();
|
||||
const first = find_run(runs, a);
|
||||
const last = find_run(runs, b);
|
||||
const p = get_run_pos(first, a);
|
||||
const q = get_run_pos(last, b);
|
||||
if (first == last) {
|
||||
path.addRect([p[0], p[1] + fm.ascent, q[0], q[1] + fm.descent]);
|
||||
} else {
|
||||
const p_top = p[1] + fm.ascent;
|
||||
const p_bot = p[1] + fm.descent;
|
||||
path.addRect([p[0], p_top, width, p_bot]);
|
||||
const q_top = q[1] + fm.ascent;
|
||||
const q_bot = q[1] + fm.descent;
|
||||
path.addRect([ 0, q_top, q[0], q_bot]);
|
||||
if (p_bot < q_top) {
|
||||
path.addRect([0, p_bot, width, q_top]); // extra lines inbetween
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
let INDEX = 0;
|
||||
let runs;
|
||||
|
||||
function drawFrame(canvas) {
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawParagraph(paragraph, 0, 0);
|
||||
if (!runs) {
|
||||
runs = paragraph.getShapedRuns();
|
||||
}
|
||||
|
||||
if (mouse.isActive()) {
|
||||
const pos = mouse.getPos();
|
||||
const a = pos_to_index(runs, pos[0], pos[1]);
|
||||
const b = pos_to_index(runs, pos[2], pos[3]);
|
||||
if (a == b) {
|
||||
INDEX = a;
|
||||
const p = index_to_pos(runs, INDEX);
|
||||
cursor.place(p[0], p[1], fm);
|
||||
} else {
|
||||
cursor.setPath(indices_to_path(runs, a, b, fm, WIDTH));
|
||||
}
|
||||
}
|
||||
|
||||
cursor.draw_before(canvas);
|
||||
for (let r of runs) {
|
||||
canvas.drawGlyphs(r.glyphs, r.positions, 0, 0, font, fontPaint);
|
||||
}
|
||||
cursor.draw_after(canvas);
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('para2').addEventListener('pointermove', interact);
|
||||
document.getElementById('para2').addEventListener('pointerdown', interact);
|
||||
// document.getElementById('para2').addEventListener('lostpointercapture', interact);
|
||||
// document.getElementById('para2').addEventListener('pointerleave', interact);
|
||||
document.getElementById('para2').addEventListener('pointerup', interact);
|
||||
return surface;
|
||||
}
|
||||
|
||||
function RTShaderAPI1(CanvasKit) {
|
||||
if (!CanvasKit) {
|
||||
return;
|
||||
|
@ -94,9 +94,13 @@ protected:
|
||||
}
|
||||
|
||||
void drawFromVisitor(SkCanvas* canvas, skia::textlayout::Paragraph* para) const {
|
||||
SkPaint p;
|
||||
SkPaint p, p2;
|
||||
p.setColor(0xFF0000FF);
|
||||
para->visit([canvas, p](const skia::textlayout::Paragraph::VisitorInfo& info) {
|
||||
p2.setColor(0xFFFF0000);
|
||||
p2.setStrokeWidth(4);
|
||||
p2.setStrokeCap(SkPaint::kSquare_Cap);
|
||||
|
||||
para->visit([&](const skia::textlayout::Paragraph::VisitorInfo& info) {
|
||||
canvas->drawGlyphs(info.count, info.glyphs, info.positions, info.origin, info.font, p);
|
||||
|
||||
if (info.utf8Starts && false) {
|
||||
@ -106,6 +110,13 @@ protected:
|
||||
}
|
||||
SkDebugf("'%s'\n", str.c_str());
|
||||
}
|
||||
|
||||
if (false) { // show position points
|
||||
for (int i = 0; i < info.count; ++i) {
|
||||
auto pos = info.positions[i];
|
||||
canvas->drawPoint(pos.fX + info.origin.fX, pos.fY + info.origin.fY, p2);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -151,7 +162,7 @@ protected:
|
||||
bool runAsBench() const override { return true; }
|
||||
|
||||
bool onAnimate(double /*nanos*/) override {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
|
Loading…
Reference in New Issue
Block a user