diff --git a/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java b/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java
index 39f02c6fbd..c08faa08e9 100644
--- a/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java
+++ b/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java
@@ -33,6 +33,7 @@
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
+import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.SearchView;
import android.widget.Toast;
@@ -51,6 +52,7 @@
import net.gsantner.markor.frontend.MarkorDialogFactory;
import net.gsantner.markor.frontend.filebrowser.MarkorFileBrowserFactory;
import net.gsantner.markor.frontend.textview.HighlightingEditor;
+import net.gsantner.markor.frontend.textview.LineNumbersTextView;
import net.gsantner.markor.frontend.textview.TextViewUtils;
import net.gsantner.markor.model.AppSettings;
import net.gsantner.markor.model.Document;
@@ -91,10 +93,12 @@ public static DocumentEditAndViewFragment newInstance(final @NonNull Document do
private HighlightingEditor _hlEditor;
private WebView _webView;
private MarkorWebViewClient _webViewClient;
+ private FrameLayout _editorHolder;
private ViewGroup _textActionsBar;
private DraggableScrollbarScrollView _primaryScrollView;
private HorizontalScrollView _hsView;
+ private LineNumbersTextView _lineNumbersView;
private SearchView _menuSearchViewForViewMode;
private Document _document;
private FormatRegistry _format;
@@ -103,6 +107,7 @@ public static DocumentEditAndViewFragment newInstance(final @NonNull Document do
private MenuItem _saveMenuItem, _undoMenuItem, _redoMenuItem;
private boolean _isPreviewVisible;
private boolean _nextConvertToPrintMode = false;
+ private long _lineNumbersRefreshTime; // Line numbers refresh time on scroll changed
public DocumentEditAndViewFragment() {
@@ -132,9 +137,23 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
final Activity activity = getActivity();
_hlEditor = view.findViewById(R.id.document__fragment__edit__highlighting_editor);
+ _editorHolder = view.findViewById(R.id.document__fragment__edit__editor_holder);
_textActionsBar = view.findViewById(R.id.document__fragment__edit__text_actions_bar);
_webView = view.findViewById(R.id.document__fragment_view_webview);
_primaryScrollView = view.findViewById(R.id.document__fragment__edit__content_editor__scrolling_parent);
+ _primaryScrollView.getViewTreeObserver().addOnScrollChangedListener(() ->
+ {
+ if (_lineNumbersView.isLineNumbersEnabled()) {
+ final long time = System.currentTimeMillis();
+ if (time - _lineNumbersRefreshTime > 125) {
+ _lineNumbersRefreshTime = time;
+ _lineNumbersView.forceRefresh();
+ }
+ }
+ });
+ _lineNumbersView = view.findViewById(R.id.document__fragment__edit__line_numbers_view);
+ _lineNumbersView.setEditText(_hlEditor);
+ _lineNumbersView.setLineNumbersEnabled(_appSettings.getDocumentLineNumbersEnabled(_document.path));
_cu = new MarkorContextUtils(activity);
// Using `if (_document != null)` everywhere is dangerous
@@ -202,7 +221,6 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
_hlEditor.setTextColor(_appSettings.getEditorForegroundColor());
_hlEditor.setGravity(_appSettings.isEditorStartEditingInCenter() ? Gravity.CENTER : Gravity.NO_GRAVITY);
_hlEditor.setHighlightingEnabled(_appSettings.getDocumentHighlightState(_document.path, _hlEditor.getText()));
- _hlEditor.setLineNumbersEnabled(_appSettings.getDocumentLineNumbersEnabled(_document.path));
_hlEditor.setAutoFormatEnabled(_appSettings.getDocumentAutoFormatEnabled(_document.path));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Do not need to send contents to accessibility
@@ -286,7 +304,7 @@ public void onPause() {
_appSettings.setDocumentPreviewState(_document.path, _isPreviewVisible);
_appSettings.setLastEditPosition(_document.path, TextViewUtils.getSelection(_hlEditor)[0]);
- if(_document.path.equals(_appSettings.getTodoFile().getAbsolutePath())){
+ if (_document.path.equals(_appSettings.getTodoFile().getAbsolutePath())) {
TodoWidgetProvider.updateTodoWidgets();
}
super.onPause();
@@ -522,7 +540,7 @@ public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
if (saveDocument(false)) {
TextConverterBase converter = FormatRegistry.getFormat(_document.getFormat(), activity, _document).getConverter();
_cu.shareText(getActivity(),
- converter.convertMarkup(getTextString(), getActivity(), false, _hlEditor.isLineNumbersEnabled(), _document.file),
+ converter.convertMarkup(getTextString(), getActivity(), false, _lineNumbersView.isLineNumbersEnabled(), _document.file),
"text/" + (item.getItemId() == R.id.action_share_html ? "html" : "plain")
);
}
@@ -611,9 +629,9 @@ public void onFsViewerConfig(GsFileBrowserOptions.Options dopt) {
return true;
}
case R.id.action_line_numbers: {
- final boolean newState = !_hlEditor.isLineNumbersEnabled();
+ final boolean newState = !_lineNumbersView.isLineNumbersEnabled();
_appSettings.setDocumentLineNumbersEnabled(_document.path, newState);
- _hlEditor.setLineNumbersEnabled(newState);
+ _lineNumbersView.setLineNumbersEnabled(newState);
updateMenuToggleStates(0);
return true;
}
@@ -742,7 +760,7 @@ private void updateMenuToggleStates(final int selectedFormatActionId) {
mi.setChecked(_hlEditor.getHighlightingEnabled());
}
if ((mi = _fragmentMenu.findItem(R.id.action_line_numbers)) != null) {
- mi.setChecked(_hlEditor.isLineNumbersEnabled());
+ mi.setChecked(_lineNumbersView.isLineNumbersEnabled());
}
if ((mi = _fragmentMenu.findItem(R.id.action_enable_auto_format)) != null) {
mi.setChecked(_hlEditor.getAutoFormatEnabled());
@@ -760,19 +778,17 @@ private void updateMenuToggleStates(final int selectedFormatActionId) {
}
private boolean isWrapped() {
- return _hsView == null || (_hlEditor.getParent() == _primaryScrollView);
+ return _hsView == null || (_hlEditor.getParent() == _editorHolder);
}
private void setHorizontalScrollMode(final boolean wrap) {
final Context context = getContext();
if (context != null && _hlEditor != null && isWrapped() != wrap) {
-
final int[] sel = TextViewUtils.getSelection(_hlEditor);
-
final boolean hlEnabled = _hlEditor.getHighlightingEnabled();
_hlEditor.setHighlightingEnabled(false);
- _primaryScrollView.removeAllViews();
+ _editorHolder.removeAllViews();
if (_hsView != null) {
_hsView.removeAllViews();
}
@@ -782,15 +798,14 @@ private void setHorizontalScrollMode(final boolean wrap) {
_hsView.setFillViewport(true);
}
_hsView.addView(_hlEditor);
- _primaryScrollView.addView(_hsView);
+ _editorHolder.addView(_hsView);
} else {
- _primaryScrollView.addView(_hlEditor);
+ _editorHolder.addView(_hlEditor);
}
_hlEditor.setHighlightingEnabled(hlEnabled);
-
// Run after layout() of immediate parent completes
- (wrap ? _primaryScrollView : _hsView).post(() -> TextViewUtils.setSelectionAndShow(_hlEditor, sel));
+ (wrap ? _editorHolder : _hsView).post(() -> TextViewUtils.setSelectionAndShow(_hlEditor, sel));
}
}
@@ -862,7 +877,7 @@ private boolean isDisplayedAtMainActivity() {
}
public void updateViewModeText() {
- _format.getConverter().convertMarkupShowInWebView(_document, getTextString(), getActivity(), _webView, _nextConvertToPrintMode, _hlEditor.isLineNumbersEnabled());
+ _format.getConverter().convertMarkupShowInWebView(_document, getTextString(), getActivity(), _webView, _nextConvertToPrintMode, _lineNumbersView.isLineNumbersEnabled());
}
public void setViewModeVisibility(final boolean show) {
diff --git a/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java b/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java
index d29063abc4..0f86c492c1 100644
--- a/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java
+++ b/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java
@@ -30,10 +30,10 @@
import net.gsantner.markor.ApplicationObject;
import net.gsantner.markor.activity.MainActivity;
import net.gsantner.markor.model.AppSettings;
+import net.gsantner.markor.util.TextCasingUtils;
import net.gsantner.opoc.format.GsTextUtils;
import net.gsantner.opoc.wrapper.GsCallback;
import net.gsantner.opoc.wrapper.GsTextWatcherAdapter;
-import net.gsantner.markor.util.TextCasingUtils;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
@@ -65,7 +65,6 @@ public class HighlightingEditor extends AppCompatEditText {
private TextWatcher _autoFormatModifier;
private boolean _autoFormatEnabled;
private boolean _saveInstanceState = true;
- private final LineNumbersDrawer _lineNumbersDrawer = new LineNumbersDrawer(this);
private final ExecutorService executor = new ThreadPoolExecutor(0, 3, 60, TimeUnit.SECONDS, new SynchronousQueue<>());
private final AtomicBoolean _textUnchangedWhileHighlighting = new AtomicBoolean(true);
@@ -115,17 +114,12 @@ public void afterTextChanged(final Editable s) {
@Override
public boolean onPreDraw() {
- _lineNumbersDrawer.setTextSize(getTextSize());
return super.onPreDraw();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
-
- if (_numEnabled) {
- _lineNumbersDrawer.draw(canvas);
- }
}
// Highlighting
@@ -253,23 +247,6 @@ public boolean setHighlightingEnabled(final boolean enable) {
return prev;
}
- public boolean isLineNumbersEnabled() {
- return _numEnabled;
- }
-
- public void setLineNumbersEnabled(final boolean enable) {
- if (enable ^ _numEnabled) {
- post(this::invalidate);
- }
- _numEnabled = enable;
- if (_numEnabled) {
- _lineNumbersDrawer.startLineTracking();
- } else {
- _lineNumbersDrawer.reset();
- _lineNumbersDrawer.stopLineTracking();
- }
- }
-
// Region to highlight
private int[] hlRegion() {
if (_isDynamicHighlightingEnabled) {
@@ -553,174 +530,4 @@ public int moveCursorToBeginOfLine(int offset) {
public boolean indexesValid(int... indexes) {
return GsTextUtils.inRange(0, length(), indexes);
}
-
- static class LineNumbersDrawer {
-
- private final AppCompatEditText _editor;
- private final Paint _paint = new Paint();
-
- private final int _defaultPaddingLeft;
- private static final int LINE_NUMBER_PADDING_LEFT = 18;
- private static final int LINE_NUMBER_PADDING_RIGHT = 12;
-
- private final Rect _visibleArea = new Rect();
- private final Rect _lineNumbersArea = new Rect();
-
- private int _numberX;
- private int _gutterX;
- private int _maxNumber = 1; // to gauge gutter width
- private int _maxNumberDigits;
- private float _oldTextSize;
- private final int[] _startLine = {0, 1}; // {line index, actual line number}
-
- private final GsTextWatcherAdapter _lineTrackingWatcher = new GsTextWatcherAdapter() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- _maxNumber -= GsTextUtils.countChar(s, start, start + count, '\n');
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- _maxNumber += GsTextUtils.countChar(s, start, start + count, '\n');
- }
- };
-
- public LineNumbersDrawer(final AppCompatEditText editor) {
- _editor = editor;
- _paint.setColor(0xFF999999);
- _paint.setTextAlign(Paint.Align.RIGHT);
- _defaultPaddingLeft = editor.getPaddingLeft();
- }
-
- public void setTextSize(final float textSize) {
- _paint.setTextSize(textSize);
- }
-
- public boolean isTextSizeChanged() {
- if (_paint.getTextSize() == _oldTextSize) {
- return false;
- } else {
- _oldTextSize = _paint.getTextSize();
- return true;
- }
- }
-
- public boolean isMaxNumberDigitsChanged() {
- final int oldDigits = _maxNumberDigits;
-
- if (_maxNumber < 10) {
- _maxNumberDigits = 1;
- } else if (_maxNumber < 100) {
- _maxNumberDigits = 2;
- } else if (_maxNumber < 1000) {
- _maxNumberDigits = 3;
- } else if (_maxNumber < 10000) {
- _maxNumberDigits = 4;
- } else {
- _maxNumberDigits = 5;
- }
- return _maxNumberDigits != oldDigits;
- }
-
- public boolean isOutOfLineNumbersArea() {
- final int margin = (int) (_visibleArea.height() * 0.5f);
- final int top = _visibleArea.top - margin;
- final int bottom = _visibleArea.bottom + margin;
-
- if (top < _lineNumbersArea.top || bottom > _lineNumbersArea.bottom) {
- // Reset line numbers area
- // height of line numbers area = (1.5 + 1 + 1.5) * height of visible area
- _lineNumbersArea.top = top - _visibleArea.height();
- _lineNumbersArea.bottom = bottom + _visibleArea.height();
- return true;
- } else {
- return false;
- }
- }
-
- public void startLineTracking() {
- _editor.removeTextChangedListener(_lineTrackingWatcher);
- _maxNumber = 1;
- final CharSequence text = _editor.getText();
- if (text != null) {
- _maxNumber += GsTextUtils.countChar(text, 0, text.length(), '\n');
- }
- _editor.addTextChangedListener(_lineTrackingWatcher);
- }
-
- public void stopLineTracking() {
- _editor.removeTextChangedListener(_lineTrackingWatcher);
- }
-
- /**
- * Draw line numbers.
- *
- * @param canvas The canvas on which the line numbers will be drawn.
- */
- public void draw(final Canvas canvas) {
- if (!_editor.getLocalVisibleRect(_visibleArea)) {
- return;
- }
-
- final CharSequence text = _editor.getText();
- final Layout layout = _editor.getLayout();
- if (text == null || layout == null) {
- return;
- }
-
- // If text size or the max line number of digits changed,
- // update the variables and reset padding
- if (isTextSizeChanged() || isMaxNumberDigitsChanged()) {
- _numberX = LINE_NUMBER_PADDING_LEFT + (int) _paint.measureText(String.valueOf(_maxNumber));
- _gutterX = _numberX + LINE_NUMBER_PADDING_RIGHT;
- _editor.setPadding(_gutterX + 12, _editor.getPaddingTop(), _editor.getPaddingRight(), _editor.getPaddingBottom());
- }
-
- int i = _startLine[0], number = _startLine[1];
- // If current visible area is out of current line numbers area,
- // iterate from the first line to recalculate the start line
- if (isOutOfLineNumbersArea()) {
- i = 0;
- number = 1;
- _startLine[0] = -1;
- }
-
- // Draw border of the gutter
- canvas.drawLine(_gutterX, _lineNumbersArea.top, _gutterX, _lineNumbersArea.bottom, _paint);
-
- // Draw line numbers
- final int count = layout.getLineCount();
- final int offsetY = _editor.getPaddingTop();
- for (; i < count; i++) {
- int start;
- try {
- start = layout.getLineStart(i);
- } catch (IndexOutOfBoundsException ex) {
- break; // Even though the drawing is against count, might throw IndexOutOfBounds during drawing
- }
- if (start == 0 || text.charAt(start - 1) == '\n') {
- final int y = layout.getLineBaseline(i);
- if (y > _lineNumbersArea.bottom) {
- break;
- }
- if (y > _lineNumbersArea.top) {
- if (_startLine[0] < 0) {
- _startLine[0] = i;
- _startLine[1] = number;
- }
- canvas.drawText(String.valueOf(number), _numberX, y + offsetY, _paint);
- }
- number++;
- }
- }
- }
-
- /**
- * Reset to the state without line numbers.
- */
- public void reset() {
- _editor.setPadding(_defaultPaddingLeft, _editor.getPaddingTop(), _editor.getPaddingRight(), _editor.getPaddingBottom());
- _maxNumberDigits = 0;
- }
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/net/gsantner/markor/frontend/textview/LineNumbersTextView.java b/app/src/main/java/net/gsantner/markor/frontend/textview/LineNumbersTextView.java
new file mode 100644
index 0000000000..947c8744a7
--- /dev/null
+++ b/app/src/main/java/net/gsantner/markor/frontend/textview/LineNumbersTextView.java
@@ -0,0 +1,302 @@
+package net.gsantner.markor.frontend.textview;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.text.Editable;
+import android.text.Layout;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.widget.EditText;
+
+import androidx.appcompat.widget.AppCompatTextView;
+
+/**
+ * @author Li Guanglin
+ */
+public class LineNumbersTextView extends AppCompatTextView {
+ private boolean lineNumbersEnabled;
+ private LineNumbersDrawer lineNumbersDrawer;
+
+ public LineNumbersTextView(Context context) {
+ super(context);
+ }
+
+ public LineNumbersTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public LineNumbersTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (lineNumbersEnabled) {
+ lineNumbersDrawer.draw(canvas);
+ }
+ }
+
+ public void forceRefresh() {
+ setText(""); // Use setText("") to activate LineNumbersTextView refresh
+ }
+
+ public void setEditText(final EditText editText) {
+ lineNumbersDrawer = new LineNumbersDrawer(editText, this);
+ }
+
+ public void setLineNumbersEnabled(final boolean enabled) {
+ lineNumbersEnabled = enabled;
+ if (lineNumbersEnabled) {
+ lineNumbersDrawer.prepare();
+ } else {
+ lineNumbersDrawer.reset();
+ }
+ forceRefresh();
+ }
+
+ public boolean isLineNumbersEnabled() {
+ return lineNumbersEnabled;
+ }
+
+ static class LineNumbersDrawer {
+ public final EditText editText;
+ public final LineNumbersTextView textView;
+
+ private final Paint paint = new Paint();
+
+ private static final int NUMBER_PADDING_LEFT = 18;
+ private static final int NUMBER_PADDING_RIGHT = 14;
+ private static final int PADDING_LEFT_NUMBERS_ENABLED = 10;
+ private final int paddingLeft;
+
+ private final Rect visibleArea = new Rect();
+ private final Rect lineNumbersArea = new Rect();
+
+ private int fenceX;
+ private int numberX;
+ private int maxNumber = 1; // To gauge the width of line numbers fence
+ private int maxNumberDigits;
+ private int lastMaxNumber;
+ private int lastLayoutLineCount;
+ private float lastTextSize;
+
+ private final int[] startLine = {0, 1}; // {line index, actual line number}
+
+ private final TextWatcher lineTrackingWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ maxNumber -= countLines(s, start, start + count);
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ maxNumber += countLines(s, start, start + count);
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ if (isLayoutLineCountChanged() || isMaxNumberChanged()) {
+ textView.forceRefresh();
+ }
+ }
+ };
+
+ public LineNumbersDrawer(final EditText editText, final LineNumbersTextView textView) {
+ this.editText = editText;
+ this.textView = textView;
+ this.paddingLeft = editText.getPaddingLeft();
+ paint.setColor(0xFF999999);
+ paint.setTextAlign(Paint.Align.RIGHT);
+ }
+
+ private boolean isOutOfLineNumbersArea() {
+ final int margin = (int) (visibleArea.height() * 0.5f);
+ final int top = visibleArea.top - margin;
+ final int bottom = visibleArea.bottom + margin;
+
+ if (top < lineNumbersArea.top || bottom > lineNumbersArea.bottom) {
+ // Set line numbers area
+ // height of line numbers area = (1.5 + 1 + 1.5) * height of visible area
+ lineNumbersArea.top = top - visibleArea.height();
+ lineNumbersArea.bottom = bottom + visibleArea.height();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private boolean isTextSizeChanged() {
+ if (editText.getTextSize() == lastTextSize) {
+ return false;
+ } else {
+ lastTextSize = editText.getTextSize();
+ paint.setTextSize(lastTextSize);
+ return true;
+ }
+ }
+
+ private boolean isMaxNumberChanged() {
+ if (maxNumber == lastMaxNumber) {
+ return false;
+ } else {
+ lastMaxNumber = maxNumber;
+ return true;
+ }
+ }
+
+ private boolean isMaxNumberDigitsChanged() {
+ int digits;
+ if (maxNumber < 10) {
+ digits = 1;
+ } else if (maxNumber < 100) {
+ digits = 2;
+ } else if (maxNumber < 1000) {
+ digits = 3;
+ } else if (maxNumber < 10000) {
+ digits = 4;
+ } else {
+ digits = 5;
+ }
+
+ if (digits == maxNumberDigits) {
+ return false;
+ }
+
+ maxNumberDigits = digits;
+ return true;
+ }
+
+ private boolean isLayoutLineCountChanged() {
+ final Layout layout = editText.getLayout();
+ if (layout == null) {
+ return true;
+ }
+
+ final int lineCount = layout.getLineCount();
+ if (lineCount == lastLayoutLineCount) {
+ return false;
+ } else {
+ lastLayoutLineCount = lineCount;
+ return true;
+ }
+ }
+
+ private int countLines(final CharSequence s, int start, int end) {
+ int count = 0;
+ for (; start < end; start++) {
+ if (s.charAt(start) == '\n') {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ private void lineTracking(boolean enabled) {
+ editText.removeTextChangedListener(lineTrackingWatcher);
+
+ if (enabled) {
+ maxNumber = 1;
+ final CharSequence text = editText.getText();
+ if (text != null) {
+ maxNumber += countLines(text, 0, text.length());
+ }
+ editText.addTextChangedListener(lineTrackingWatcher);
+ }
+ }
+
+ /**
+ * Prepare for drawing line numbers.
+ */
+ public void prepare() {
+ lineTracking(true);
+ textView.setVisibility(VISIBLE);
+ editText.setPadding(PADDING_LEFT_NUMBERS_ENABLED, editText.getPaddingTop(), editText.getPaddingRight(), editText.getPaddingBottom());
+ }
+
+ /**
+ * Draw line numbers.
+ *
+ * @param canvas The canvas on which the line numbers will be drawn.
+ */
+ public void draw(final Canvas canvas) {
+ if (!editText.getLocalVisibleRect(visibleArea)) {
+ return;
+ }
+
+ final CharSequence text = editText.getText();
+ final Layout layout = editText.getLayout();
+ if (text == null || layout == null) {
+ return;
+ }
+
+ // If text size or the max line number of digits changed, update related variables
+ if (isTextSizeChanged() || isMaxNumberDigitsChanged()) {
+ numberX = NUMBER_PADDING_LEFT + (int) paint.measureText(String.valueOf(maxNumber));
+ fenceX = numberX + NUMBER_PADDING_RIGHT;
+ textView.setWidth(fenceX + 1);
+ }
+
+ // If current visible area is out of current line numbers area,
+ // will recalculate the start line
+ boolean invalid = false;
+ if (isOutOfLineNumbersArea()) {
+ invalid = true;
+ startLine[0] = 0;
+ startLine[1] = 1;
+ }
+
+ // Draw right border of the fence
+ canvas.drawLine(fenceX, lineNumbersArea.top, fenceX, lineNumbersArea.bottom, paint);
+
+ // Draw line numbers
+ int i = startLine[0];
+ int number = startLine[1];
+ int y = layout.getLineBaseline(i);
+ final int count = layout.getLineCount();
+ final int offsetY = editText.getPaddingTop();
+
+ if (y > lineNumbersArea.top) {
+ if (invalid) {
+ invalid = false;
+ startLine[0] = i;
+ startLine[1] = number;
+ }
+ canvas.drawText(String.valueOf(number), numberX, layout.getLineBaseline(i) + offsetY, paint);
+ }
+ i++;
+ number++;
+
+ for (; i < count; i++) {
+ if (text.charAt(layout.getLineStart(i) - 1) == '\n') {
+ y = layout.getLineBaseline(i);
+ if (y > lineNumbersArea.top) {
+ if (invalid) {
+ invalid = false;
+ startLine[0] = i;
+ startLine[1] = number;
+ }
+ canvas.drawText(String.valueOf(number), numberX, y + offsetY, paint);
+ if (y > lineNumbersArea.bottom) {
+ break;
+ }
+ }
+ number++;
+ }
+ }
+ }
+
+ /**
+ * Reset states related line numbers.
+ */
+ public void reset() {
+ lineTracking(false);
+ maxNumberDigits = 0;
+ textView.setWidth(0);
+ textView.setVisibility(GONE);
+ editText.setPadding(paddingLeft, editText.getPaddingTop(), editText.getPaddingRight(), editText.getPaddingBottom());
+ }
+ }
+}
diff --git a/app/src/main/res/layout/document__fragment__edit.xml b/app/src/main/res/layout/document__fragment__edit.xml
index 7bd95f3cea..a947f1e6ed 100644
--- a/app/src/main/res/layout/document__fragment__edit.xml
+++ b/app/src/main/res/layout/document__fragment__edit.xml
@@ -26,21 +26,40 @@
android:background="@android:color/transparent"
android:fillViewport="true">
-
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+
+
+
+
+
+
+
-