Style in place

pull/198/head
M66B 4 years ago
parent f9abefb66b
commit de8ad55e58

@ -66,57 +66,55 @@ public class StyleHelper {
Log.i("Style action=" + action); Log.i("Style action=" + action);
try { try {
int start = etBody.getSelectionStart(); int _start = etBody.getSelectionStart();
int end = etBody.getSelectionEnd(); int _end = etBody.getSelectionEnd();
if (start < 0) if (_start < 0)
start = 0; _start = 0;
if (end < 0) if (_end < 0)
end = 0; _end = 0;
if (start > end) { if (_start > _end) {
int tmp = start; int tmp = _start;
start = end; _start = _end;
end = tmp; _end = tmp;
} }
SpannableString ss = new SpannableString(etBody.getText()); final Editable edit = etBody.getText();
final int start = _start;
final int end = _end;
if (action == R.id.menu_bold || action == R.id.menu_italic) { if (action == R.id.menu_bold || action == R.id.menu_italic) {
int style = (action == R.id.menu_bold ? Typeface.BOLD : Typeface.ITALIC); int style = (action == R.id.menu_bold ? Typeface.BOLD : Typeface.ITALIC);
boolean has = false; boolean has = false;
for (StyleSpan span : ss.getSpans(start, end, StyleSpan.class)) for (StyleSpan span : edit.getSpans(start, end, StyleSpan.class))
if (span.getStyle() == style) { if (span.getStyle() == style) {
has = true; has = true;
ss.removeSpan(span); edit.removeSpan(span);
} }
if (!has) if (!has)
ss.setSpan(new StyleSpan(style), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); edit.setSpan(new StyleSpan(style), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
etBody.setText(ss); etBody.setText(edit);
etBody.setSelection(start, end); etBody.setSelection(start, end);
return true; return true;
} else if (action == R.id.menu_underline) { } else if (action == R.id.menu_underline) {
boolean has = false; boolean has = false;
for (UnderlineSpan span : ss.getSpans(start, end, UnderlineSpan.class)) { for (UnderlineSpan span : edit.getSpans(start, end, UnderlineSpan.class)) {
has = true; has = true;
ss.removeSpan(span); edit.removeSpan(span);
} }
if (!has) if (!has)
ss.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); edit.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
etBody.setText(ss); etBody.setText(edit);
etBody.setSelection(start, end); etBody.setSelection(start, end);
return true; return true;
} else if (action == R.id.menu_style) { } else if (action == R.id.menu_style) {
final int s = start;
final int e = end;
final SpannableStringBuilder t = new SpannableStringBuilder(ss);
PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(anchor.getContext(), owner, anchor); PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(anchor.getContext(), owner, anchor);
popupMenu.inflate(R.menu.popup_style); popupMenu.inflate(R.menu.popup_style);
@ -156,9 +154,9 @@ public class StyleHelper {
} }
private boolean setSize(MenuItem item) { private boolean setSize(MenuItem item) {
RelativeSizeSpan[] spans = t.getSpans(s, e, RelativeSizeSpan.class); RelativeSizeSpan[] spans = edit.getSpans(start, end, RelativeSizeSpan.class);
for (RelativeSizeSpan span : spans) for (RelativeSizeSpan span : spans)
t.removeSpan(span); edit.removeSpan(span);
Float size; Float size;
if (item.getItemId() == R.id.menu_style_size_small) if (item.getItemId() == R.id.menu_style_size_small)
@ -169,10 +167,10 @@ public class StyleHelper {
size = null; size = null;
if (size != null) if (size != null)
t.setSpan(new RelativeSizeSpan(size), s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); edit.setSpan(new RelativeSizeSpan(size), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
etBody.setText(t); etBody.setText(edit);
etBody.setSelection(s, e); etBody.setSelection(start, end);
return true; return true;
} }
@ -210,24 +208,24 @@ public class StyleHelper {
} }
private void _setColor(Integer color) { private void _setColor(Integer color) {
for (ForegroundColorSpan span : t.getSpans(s, e, ForegroundColorSpan.class)) for (ForegroundColorSpan span : edit.getSpans(start, end, ForegroundColorSpan.class))
t.removeSpan(span); edit.removeSpan(span);
if (color != null) if (color != null)
t.setSpan(new ForegroundColorSpan(color), s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); edit.setSpan(new ForegroundColorSpan(color), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
etBody.setText(t); etBody.setText(edit);
etBody.setSelection(s, e); etBody.setSelection(start, end);
} }
private boolean setAlignment(MenuItem item) { private boolean setAlignment(MenuItem item) {
Pair<Integer, Integer> paragraph = ensureParagraph(t, s, e); Pair<Integer, Integer> paragraph = ensureParagraph(edit, start, end);
int start = paragraph.first; int s = paragraph.first;
int end = paragraph.second; int e = paragraph.second;
AlignmentSpan[] spans = t.getSpans(start, end, AlignmentSpan.class); AlignmentSpan[] spans = edit.getSpans(s, e, AlignmentSpan.class);
for (AlignmentSpan span : spans) for (AlignmentSpan span : spans)
t.removeSpan(span); edit.removeSpan(span);
Layout.Alignment alignment = null; Layout.Alignment alignment = null;
boolean ltr = (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_LTR); boolean ltr = (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_LTR);
@ -241,11 +239,11 @@ public class StyleHelper {
} }
if (alignment != null) if (alignment != null)
t.setSpan(new AlignmentSpan.Standard(alignment), edit.setSpan(new AlignmentSpan.Standard(alignment),
start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH);
etBody.setText(t); etBody.setText(edit);
etBody.setSelection(start, end); etBody.setSelection(s, e);
return true; return true;
} }
@ -261,54 +259,54 @@ public class StyleHelper {
int message_zoom = prefs.getInt("message_zoom", 100); int message_zoom = prefs.getInt("message_zoom", 100);
float textSize = Helper.getTextSize(context, 0) * message_zoom / 100f; float textSize = Helper.getTextSize(context, 0) * message_zoom / 100f;
Pair<Integer, Integer> paragraph = ensureParagraph(t, s, e); Pair<Integer, Integer> paragraph = ensureParagraph(edit, start, end);
int start = paragraph.first; int s = paragraph.first;
int end = paragraph.second; int e = paragraph.second;
// Remove existing bullets // Remove existing bullets
BulletSpan[] spans = t.getSpans(start, end, BulletSpan.class); BulletSpan[] spans = edit.getSpans(s, e, BulletSpan.class);
for (BulletSpan span : spans) for (BulletSpan span : spans)
t.removeSpan(span); edit.removeSpan(span);
int i = start; int i = s;
int j = start + 1; int j = s + 1;
int index = 1; int index = 1;
while (j < end) { while (j < e) {
if (i > 0 && t.charAt(i - 1) == '\n' && t.charAt(j) == '\n') { if (i > 0 && edit.charAt(i - 1) == '\n' && edit.charAt(j) == '\n') {
Log.i("Insert " + i + "..." + (j + 1) + " size=" + end); Log.i("Insert " + i + "..." + (j + 1) + " size=" + e);
if (item.getItemId() == R.id.menu_style_list_bullets) if (item.getItemId() == R.id.menu_style_list_bullets)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
t.setSpan(new BulletSpan(dp6, colorAccent), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); edit.setSpan(new BulletSpan(dp6, colorAccent), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH);
else else
t.setSpan(new BulletSpan(dp6, colorAccent, dp3), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); edit.setSpan(new BulletSpan(dp6, colorAccent, dp3), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH);
else else
t.setSpan(new NumberSpan(dp6, colorAccent, textSize, index++), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH); edit.setSpan(new NumberSpan(dp6, colorAccent, textSize, index++), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH);
i = j + 1; i = j + 1;
} }
j++; j++;
} }
etBody.setText(t); etBody.setText(edit);
etBody.setSelection(start, end); etBody.setSelection(s, e);
return true; return true;
} }
private boolean setFont(MenuItem item) { private boolean setFont(MenuItem item) {
TypefaceSpan[] spans = t.getSpans(s, e, TypefaceSpan.class); TypefaceSpan[] spans = edit.getSpans(start, end, TypefaceSpan.class);
for (TypefaceSpan span : spans) for (TypefaceSpan span : spans)
t.removeSpan(span); edit.removeSpan(span);
int id = item.getItemId(); int id = item.getItemId();
String[] names = anchor.getResources().getStringArray(R.array.fontNameValues); String[] names = anchor.getResources().getStringArray(R.array.fontNameValues);
String face = (id < names.length ? names[id] : null); String face = (id < names.length ? names[id] : null);
if (face != null) if (face != null)
t.setSpan(new TypefaceSpan(face), s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); edit.setSpan(new TypefaceSpan(face), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
etBody.setText(t); etBody.setText(edit);
etBody.setSelection(s, e); etBody.setSelection(start, end);
return true; return true;
} }
@ -320,66 +318,65 @@ public class StyleHelper {
int dp3 = Helper.dp2pixels(context, 3); int dp3 = Helper.dp2pixels(context, 3);
int dp6 = Helper.dp2pixels(context, 6); int dp6 = Helper.dp2pixels(context, 6);
Pair<Integer, Integer> paragraph = ensureParagraph(t, s, e); Pair<Integer, Integer> paragraph = ensureParagraph(edit, start, end);
int start = paragraph.first; int s = paragraph.first;
int end = paragraph.second; int e = paragraph.second;
QuoteSpan[] spans = t.getSpans(s, e, QuoteSpan.class); QuoteSpan[] spans = edit.getSpans(s, e, QuoteSpan.class);
for (QuoteSpan span : spans) for (QuoteSpan span : spans)
t.removeSpan(span); edit.removeSpan(span);
QuoteSpan q; QuoteSpan q;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
q = new QuoteSpan(colorPrimary); q = new QuoteSpan(colorPrimary);
else else
q = new QuoteSpan(colorPrimary, dp3, dp6); q = new QuoteSpan(colorPrimary, dp3, dp6);
t.setSpan(q, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); edit.setSpan(q, s, e, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
etBody.setText(t); etBody.setText(edit);
etBody.setSelection(start, end); etBody.setSelection(s, e);
return true; return true;
} }
private boolean setStrikeThrough(MenuItem item) { private boolean setStrikeThrough(MenuItem item) {
boolean has = false; boolean has = false;
for (StrikethroughSpan span : t.getSpans(s, e, StrikethroughSpan.class)) { for (StrikethroughSpan span : edit.getSpans(start, end, StrikethroughSpan.class)) {
has = true; has = true;
t.removeSpan(span); edit.removeSpan(span);
} }
if (!has) if (!has)
t.setSpan(new StrikethroughSpan(), s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); edit.setSpan(new StrikethroughSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
etBody.setText(t); etBody.setText(edit);
etBody.setSelection(s, e); etBody.setSelection(start, end);
return true; return true;
} }
private boolean clear(MenuItem item) { private boolean clear(MenuItem item) {
int start = s; int e = end;
int end = e;
// Expand to paragraph (block quotes) // Expand to paragraph (block quotes)
if (end + 1 < t.length() && t.charAt(end) == '\n') if (e + 1 < edit.length() && edit.charAt(e) == '\n')
end++; e++;
for (Object span : t.getSpans(start, end, Object.class)) for (Object span : edit.getSpans(start, e, Object.class))
if (!(span instanceof ImageSpan)) { if (!(span instanceof ImageSpan)) {
int sstart = t.getSpanStart(span); int sstart = edit.getSpanStart(span);
int send = t.getSpanEnd(span); int send = edit.getSpanEnd(span);
int flags = t.getSpanFlags(span); int flags = edit.getSpanFlags(span);
if (sstart < start && send > start) if (sstart < start && send > start)
setSpan(t, span, sstart, start, flags, etBody.getContext()); setSpan(edit, span, sstart, start, flags, etBody.getContext());
if (sstart < end && send > end) if (sstart < end && send > end)
setSpan(t, span, end, send, flags, etBody.getContext()); setSpan(edit, span, e, send, flags, etBody.getContext());
t.removeSpan(span); edit.removeSpan(span);
} }
etBody.setText(t); etBody.setText(edit);
etBody.setSelection(s, e); etBody.setSelection(start, e);
return true; return true;
} }
@ -392,36 +389,36 @@ public class StyleHelper {
String url = (String) args[0]; String url = (String) args[0];
List<Object> spans = new ArrayList<>(); List<Object> spans = new ArrayList<>();
for (Object span : ss.getSpans(start, end, Object.class)) { for (Object span : edit.getSpans(start, end, Object.class)) {
if (!(span instanceof URLSpan)) if (!(span instanceof URLSpan))
spans.add(span); spans.add(span);
ss.removeSpan(span); edit.removeSpan(span);
} }
if (url != null) { if (url != null) {
int e = end;
if (start == end) { if (start == end) {
etBody.getText().insert(start, url); etBody.getText().insert(start, url);
end += url.length(); e += url.length();
ss = new SpannableString(etBody.getText());
} }
ss.setSpan(new URLSpan(url), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); edit.setSpan(new URLSpan(url), start, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
// Restore other spans // Restore other spans
for (Object span : spans) for (Object span : spans)
ss.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); edit.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
etBody.setText(ss); etBody.setText(edit);
etBody.setSelection(end, end); etBody.setSelection(end, end);
return true; return true;
} else if (action == R.id.menu_clear) { } else if (action == R.id.menu_clear) {
for (Object span : ss.getSpans(0, etBody.length(), Object.class)) for (Object span : edit.getSpans(0, etBody.length(), Object.class))
if (!(span instanceof ImageSpan)) if (!(span instanceof ImageSpan))
ss.removeSpan(span); edit.removeSpan(span);
etBody.setText(ss); etBody.setText(edit);
etBody.setSelection(start, end); etBody.setSelection(start, end);
return true; return true;
@ -433,26 +430,26 @@ public class StyleHelper {
} }
} }
static void setSpan(SpannableStringBuilder ss, Object span, int start, int end, int flags, Context context) { static void setSpan(Editable edit, Object span, int start, int end, int flags, Context context) {
if (span instanceof CharacterStyle) if (span instanceof CharacterStyle)
ss.setSpan(CharacterStyle.wrap((CharacterStyle) span), start, end, flags); edit.setSpan(CharacterStyle.wrap((CharacterStyle) span), start, end, flags);
else if (span instanceof QuoteSpan) { else if (span instanceof QuoteSpan) {
ParagraphStyle ps = (ParagraphStyle) span; ParagraphStyle ps = (ParagraphStyle) span;
Pair<Integer, Integer> p = ensureParagraph(ss, start, end); Pair<Integer, Integer> p = ensureParagraph(edit, start, end);
ss.setSpan(clone(span, ps.getClass(), context), p.first, p.second, flags); edit.setSpan(clone(span, ps.getClass(), context), p.first, p.second, flags);
} }
} }
static private Pair<Integer, Integer> ensureParagraph(SpannableStringBuilder t, int s, int e) { static private Pair<Integer, Integer> ensureParagraph(Editable edit, int s, int e) {
int start = s; int start = s;
int end = e; int end = e;
// Expand selection at start // Expand selection at start
while (start > 0 && t.charAt(start - 1) != '\n') while (start > 0 && edit.charAt(start - 1) != '\n')
start--; start--;
// Expand selection at end // Expand selection at end
while (end > 0 && end < t.length() && t.charAt(end - 1) != '\n') while (end > 0 && end < edit.length() && edit.charAt(end - 1) != '\n')
end++; end++;
// Nothing to do // Nothing to do
@ -460,20 +457,20 @@ public class StyleHelper {
return null; return null;
// Create paragraph at start // Create paragraph at start
if (start == 0 && t.charAt(start) != '\n') { if (start == 0 && edit.charAt(start) != '\n') {
t.insert(0, "\n"); edit.insert(0, "\n");
start++; start++;
end++; end++;
} }
// Create paragraph at end // Create paragraph at end
if (end == t.length() && t.charAt(end - 1) != '\n') { if (end == edit.length() && edit.charAt(end - 1) != '\n') {
t.append("\n"); edit.append("\n");
end++; end++;
} }
if (end == t.length()) if (end == edit.length())
t.append("\n"); // workaround Android bug edit.append("\n"); // workaround Android bug
return new Pair(start, end); return new Pair(start, end);
} }

Loading…
Cancel
Save