GitNex-Android-App/app/src/main/java/org/mian/gitnex/views/SyntaxHighlightedArea.java

241 lines
6.4 KiB
Java

package org.mian.gitnex.views;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.mian.gitnex.core.MainGrammarLocator;
import org.mian.gitnex.helpers.AppUtil;
import org.mian.gitnex.helpers.codeeditor.markwon.SyntaxHighlighter;
import org.mian.gitnex.helpers.codeeditor.theme.Theme;
/**
* @author opyale
*/
public class SyntaxHighlightedArea extends LinearLayout {
private Theme theme;
private TextView sourceView;
private LinesView linesView;
public SyntaxHighlightedArea(@NonNull Context context) {
super(context);
setup();
}
public SyntaxHighlightedArea(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setup();
}
public SyntaxHighlightedArea(
@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setup();
}
public void setup() {
theme = Theme.getDefaultTheme(getContext());
sourceView = new TextView(getContext());
sourceView.setLayoutParams(
new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
sourceView.setTypeface(
Typeface.createFromAsset(
getContext().getAssets(), "fonts/sourcecodeproregular.ttf"));
sourceView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
sourceView.setTextColor(
getContext().getResources().getColor(theme.getDefaultColor(), null));
sourceView.setTextIsSelectable(true);
int padding = AppUtil.getPixelsFromDensity(getContext(), 5);
sourceView.setPadding(padding, 0, padding, 0);
HorizontalScrollView horizontalScrollView = new HorizontalScrollView(getContext());
horizontalScrollView.setLayoutParams(
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
horizontalScrollView.addView(sourceView);
linesView = new LinesView(getContext());
linesView.setLayoutParams(
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
linesView.setPadding(
AppUtil.getPixelsFromDensity(getContext(), 3),
0,
AppUtil.getPixelsFromDensity(getContext(), 6),
0);
linesView.getPaint().setTypeface(sourceView.getTypeface());
linesView.getPaint().setTextSize(sourceView.getTextSize());
linesView.setBackgroundColor(
getContext().getResources().getColor(theme.getBackgroundColor(), null));
linesView.setTextColor(getContext().getResources().getColor(theme.getDefaultColor(), null));
linesView.setLineColor(getContext().getResources().getColor(theme.getDefaultColor(), null));
setOrientation(HORIZONTAL);
setBackgroundColor(getContext().getResources().getColor(theme.getBackgroundColor(), null));
addView(linesView);
addView(horizontalScrollView);
}
public void setContent(@NonNull String source, @NonNull String extension) {
if (!source.isEmpty()) {
Thread highlightingThread =
new Thread(
() -> {
try {
CharSequence highlightedSource =
SyntaxHighlighter.create(
getContext(),
theme,
MainGrammarLocator
.DEFAULT_FALLBACK_LANGUAGE)
.highlight(
MainGrammarLocator.fromExtension(
extension)
.toUpperCase(),
source);
getActivity()
.runOnUiThread(
() -> sourceView.setText(highlightedSource));
} catch (Throwable ignored) {
// Fall back to plaintext if something fails
getActivity().runOnUiThread(() -> sourceView.setText(source));
}
});
Thread lineCountingThread =
new Thread(
() -> {
long lineCount = AppUtil.getLineCount(source);
try {
highlightingThread.join();
} catch (InterruptedException ignored) {
}
getActivity()
.runOnUiThread(() -> linesView.setLineCount(lineCount));
});
highlightingThread.start();
lineCountingThread.start();
}
}
private Activity getActivity() {
return (Activity) getContext();
}
public String getContent() {
return sourceView.getText().toString();
}
private static class LinesView extends View {
private final Paint paint = new Paint();
private final Rect textBounds = new Rect();
@ColorInt private int backgroundColor;
@ColorInt private int textColor;
@ColorInt private int lineColor;
private long lineCount;
public LinesView(Context context) {
super(context);
}
public void setLineCount(long lineCount) {
this.lineCount = lineCount;
requestLayout();
}
@Override
public void setBackgroundColor(@ColorInt int backgroundColor) {
this.backgroundColor = backgroundColor;
invalidate();
}
public void setTextColor(@ColorInt int textColor) {
this.textColor = textColor;
invalidate();
}
public void setLineColor(@ColorInt int lineColor) {
this.lineColor = lineColor;
invalidate();
}
public Paint getPaint() {
return paint;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
String highestLineNumber = String.valueOf(lineCount);
paint.getTextBounds(highestLineNumber, 0, highestLineNumber.length(), textBounds);
setMeasuredDimension(
getPaddingLeft() + textBounds.width() + getPaddingRight(),
MeasureSpec.getSize(heightMeasureSpec));
}
@Override
protected void onDraw(Canvas canvas) {
paint.setColor(backgroundColor);
canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
float marginTopBottom = (float) (getHeight() - (textBounds.height() / 2)) / lineCount;
paint.setColor(textColor);
canvas.save();
canvas.translate(getPaddingLeft(), marginTopBottom);
for (int currentLine = 1; currentLine <= lineCount; currentLine++) {
canvas.drawText(String.valueOf(currentLine), 0, 0, paint);
canvas.translate(0, marginTopBottom);
}
paint.setColor(lineColor);
int dividerX = getWidth() - 1;
int dividerY = getHeight();
canvas.restore();
canvas.drawLine(dividerX, 0, dividerX, dividerY, paint);
}
}
}