mirror of https://github.com/M66B/FairEmail.git
Style in place
This commit is contained in:
parent
f9abefb66b
commit
de8ad55e58
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue