Style in place

This commit is contained in:
M66B 2021-05-06 07:58:04 +02:00
parent f9abefb66b
commit de8ad55e58
1 changed files with 108 additions and 111 deletions

View File

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