271 lines
8.9 KiB
Java
271 lines
8.9 KiB
Java
package ru.noties.markwon.spans2;
|
|
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Paint;
|
|
import android.graphics.Rect;
|
|
import android.support.annotation.IntRange;
|
|
import android.support.annotation.NonNull;
|
|
import android.support.annotation.Nullable;
|
|
import android.text.style.ReplacementSpan;
|
|
|
|
import ru.noties.debug.Debug;
|
|
|
|
// we will use Replacement span because code blocks cannot contain other markdown
|
|
// so we will render the string (not a charSequence with possible metric affecting spans)
|
|
public class CodeSpan extends ReplacementSpan/* implements LeadingMarginSpan*/ {
|
|
|
|
private final boolean multiline;
|
|
private final int start;
|
|
private final int end;
|
|
|
|
private final Rect rect = new Rect();
|
|
private final Rect borderRect = new Rect();
|
|
private final Paint paint = new Paint();
|
|
|
|
public CodeSpan(boolean multiline, int start, int end) {
|
|
this.multiline = multiline;
|
|
this.start = start;
|
|
this.end = end;
|
|
|
|
paint.setStyle(Paint.Style.FILL);
|
|
}
|
|
|
|
@Override
|
|
public int getSize(
|
|
@NonNull Paint paint,
|
|
CharSequence text,
|
|
@IntRange(from = 0) int start,
|
|
@IntRange(from = 0) int end,
|
|
@Nullable Paint.FontMetricsInt fm
|
|
) {
|
|
|
|
final CharSequence cs = text.subSequence(start, end);
|
|
final int width = 32 + (int) (paint.measureText(cs, 0, cs.length()) + .5F);
|
|
|
|
// final StaticLayout layout = new StaticLayout(cs, new TextPaint(paint), 10000, Layout.Alignment.ALIGN_NORMAL, 1.F, .0F, false);
|
|
// final float width = layout.getLineWidth(0);
|
|
// final int out = 32 + (int) (width + .5F);
|
|
|
|
// Debug.i("text: %s, width: %s", cs, width);
|
|
|
|
if (fm != null) {
|
|
// we add a padding top & bottom
|
|
Debug.i("a: %s, d: %s, t: %s, b: %s", fm.ascent, fm.descent, fm.top, fm.bottom);
|
|
final float ratio = .62F; // golden ratio
|
|
fm.ascent = fm.ascent - 8;
|
|
fm.descent = (int) (-fm.ascent * ratio);
|
|
fm.top = fm.ascent;
|
|
fm.bottom = fm.descent;
|
|
}
|
|
|
|
return width;
|
|
}
|
|
|
|
@Override
|
|
public void draw(
|
|
@NonNull Canvas canvas,
|
|
CharSequence text,
|
|
@IntRange(from = 0) int start,
|
|
@IntRange(from = 0) int end,
|
|
float x,
|
|
int top,
|
|
int y,
|
|
int bottom,
|
|
@NonNull Paint paint
|
|
) {
|
|
|
|
final int left = (int) (x + .5F);
|
|
|
|
final int right;
|
|
if (multiline) {
|
|
right = canvas.getWidth();
|
|
} else {
|
|
final int width = (16 * 2) + (int) (paint.measureText(text, start, end) + .5F);
|
|
right = left + width;
|
|
}
|
|
|
|
rect.set(left, top, right, bottom);
|
|
|
|
|
|
// okay, draw background first
|
|
drawBackground(canvas);
|
|
|
|
// then, if any, draw borders
|
|
drawBorders(canvas, this.start == start, this.end == end);
|
|
|
|
// draw text
|
|
// y center position
|
|
final int b = bottom - ((bottom - top) / 2) - (int) ((paint.descent() + paint.ascent()) / 2);
|
|
// if (config.textColor != 0) {
|
|
// // we will use Paint object that is used to draw all the text (textSize, textColor, typeface, etc)
|
|
// paint.setColor(config.textColor);
|
|
// }
|
|
canvas.drawText(text, start, end, x + 16, b, paint);
|
|
|
|
|
|
// Debug.i("text: %s, x: %s, top: %s, y: %s, bottom: %s", text.subSequence(start, end), x, top, y, bottom);
|
|
//
|
|
// final CharSequence cs = text.subSequence(start, end);
|
|
//
|
|
// final int width = 32 + (int) (paint.measureText(cs, 0, cs.length()) + .5F);
|
|
//
|
|
// final int left = (int) (x + .5F);
|
|
// final int right = multiline
|
|
// ? canvas.getWidth()
|
|
// : left + width;
|
|
//
|
|
// final Rect rect = new Rect(
|
|
// left,
|
|
// top,
|
|
// right,
|
|
// bottom
|
|
// );
|
|
//
|
|
// final Paint p = new Paint();
|
|
// p.setStyle(Paint.Style.FILL);
|
|
// p.setColor(0x80ff0000);
|
|
// canvas.drawRect(rect, p);
|
|
//
|
|
// // y center position
|
|
// final int b = bottom - ((bottom - top) / 2) - (int) ((paint.descent() + paint.ascent()) / 2);
|
|
// p.setColor(0xFF000000);
|
|
// canvas.drawText(cs, 0, cs.length(), x + 16, b, paint);
|
|
}
|
|
|
|
private void drawBackground(Canvas canvas) {
|
|
// final int color = config.backgroundColor;
|
|
// if (color != 0) {
|
|
paint.setColor(0x40ff0000);
|
|
canvas.drawRect(rect, paint);
|
|
// }
|
|
}
|
|
|
|
private void drawBorders(Canvas canvas, boolean top, boolean bottom) {
|
|
|
|
final int color = 0xFFff0000;
|
|
final int width = 4;
|
|
// if (color == 0
|
|
// || width == 0) {
|
|
// return;
|
|
// }
|
|
|
|
paint.setColor(color);
|
|
|
|
// left and right are always drawn
|
|
|
|
// LEFT
|
|
borderRect.set(rect.left, rect.top, rect.left + width, rect.bottom);
|
|
canvas.drawRect(borderRect, paint);
|
|
|
|
// RIGHT
|
|
borderRect.set(rect.right - width, rect.top, rect.right, rect.bottom);
|
|
canvas.drawRect(borderRect, paint);
|
|
|
|
// TOP
|
|
if (top) {
|
|
borderRect.set(rect.left, rect.top, rect.right, rect.top + width);
|
|
canvas.drawRect(borderRect, paint);
|
|
}
|
|
|
|
// BOTTOM
|
|
if (bottom) {
|
|
borderRect.set(rect.left, rect.bottom - width, rect.right, rect.bottom);
|
|
canvas.drawRect(borderRect, paint);
|
|
}
|
|
}
|
|
|
|
|
|
// @Override
|
|
// public int getLeadingMargin(boolean first) {
|
|
// return 1;
|
|
// }
|
|
//
|
|
// @Override
|
|
// public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
|
|
//// Debug.i("x: %d, top: %d, bottom: %d", x, top, bottom);
|
|
//
|
|
//// Debug.i("this: [%d, %d], came: [%d, %d]", this.start, this.end, start, end);
|
|
// Debug.i("x: %d, canvas: [%d-%d], text: %s", x, c.getWidth(), c.getHeight(), (text.subSequence(start, end)));
|
|
//
|
|
// // the thing is... if we do not draw, then text won't be drawn also
|
|
// final Rect rect = new Rect();
|
|
//
|
|
// final Paint paint = new Paint();
|
|
// paint.setStyle(Paint.Style.FILL);
|
|
// paint.setColor(0xffcccccc);
|
|
//
|
|
// rect.set(x, top, c.getWidth(), bottom);
|
|
// c.drawRect(rect, paint);
|
|
//
|
|
// if (this.start == start) {
|
|
// this.top = top;
|
|
//
|
|
//// final int save = c.save();
|
|
//// try {
|
|
//// c.drawColor(0x00ffffff);
|
|
//// } finally {
|
|
//// c.restoreToCount(save);
|
|
//// }
|
|
//
|
|
//// c.drawColor(0x00ffffff);
|
|
// }
|
|
//
|
|
// if (this.end == end) {
|
|
// // draw borders
|
|
// final Rect r = new Rect(x + 1, this.top, c.getWidth() - x, bottom);
|
|
// final Paint pa = new Paint();
|
|
// pa.setStyle(Paint.Style.STROKE);
|
|
// pa.setColor(0xff999999);
|
|
// c.drawRect(r, pa);
|
|
// }
|
|
//// rect.inset((int) paint.getStrokeWidth(), (int) paint.getStrokeWidth());
|
|
//// paint.setStyle(Paint.Style.STROKE);
|
|
//// paint.setColor(0xff333333);
|
|
//// c.drawRect(rect, paint);
|
|
// }
|
|
|
|
// @Override
|
|
// public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fm) {
|
|
//// int ht = mDrawable.getIntrinsicHeight();
|
|
////
|
|
//// int need = ht - (v + fm.descent - fm.ascent - istartv);
|
|
//// if (need > 0)
|
|
//// fm.descent += need;
|
|
////
|
|
//// need = ht - (v + fm.bottom - fm.top - istartv);
|
|
//// if (need > 0)
|
|
//// fm.bottom += need;
|
|
////
|
|
//
|
|
//// final int lineOffset = v - spanstartv;
|
|
//// final int desired = 128;
|
|
//// final int currentLineHeight = -fm.ascent + fm.descent;
|
|
//// final float ratio = (float) desired / currentLineHeight;
|
|
////
|
|
//// Debug.i("fm, came: %s", fm);
|
|
//// Debug.i("lineOffset: %d, current: %d, ratio: %s", lineOffset, currentLineHeight, ratio);
|
|
////
|
|
//// fm.ascent = (int) (ratio * fm.ascent + .5F);
|
|
//// fm.descent = (int) (ratio * fm.descent + .5F);
|
|
////
|
|
//// Debug.i("fm, out: %s", fm);
|
|
//
|
|
//// Debug.i("top: %d, bottom: %d, ascent: %d, descent: %d", fm.top, fm.bottom, fm.ascent, fm.descent);
|
|
//// Debug.i("lineHeight: %d, v: %d, spanstartv: %d", lineOffset, v, spanstartv);
|
|
////
|
|
//// final int h = 128;
|
|
//// final int descentNeed = h - (v + fm.descent - fm.ascent - spanstartv);
|
|
//// if (descentNeed > 0) {
|
|
//// fm.ascent -= descentNeed / 2;
|
|
//// fm.descent += descentNeed / 2;
|
|
//// }
|
|
//// final int bottomNeed = h - (v + fm.bottom - fm.top - spanstartv);
|
|
//// if (bottomNeed > 0) {
|
|
//// fm.top -= bottomNeed;
|
|
//// fm.bottom += bottomNeed;
|
|
//// }
|
|
////
|
|
//// Debug.i("out, ascent: %d, descent: %d, bottom: %d", fm.ascent, fm.descent, fm.bottom);
|
|
// }
|
|
}
|