mirror of
https://github.com/M66B/FairEmail.git
synced 2024-12-21 23:32:51 +00:00
Compose numbered lists
This commit is contained in:
parent
7292d5d30f
commit
4727598373
7 changed files with 238 additions and 103 deletions
|
@ -211,7 +211,7 @@ public class HtmlEx {
|
|||
|
||||
private /* static */ void withinBlockquoteIndividual(StringBuilder out, Spanned text, int start,
|
||||
int end) {
|
||||
boolean isInList = false;
|
||||
Boolean isInBulletList = null;
|
||||
int next;
|
||||
for (int i = start; i <= end; i = next) {
|
||||
next = TextUtils.indexOf(text, '\n', i, end);
|
||||
|
@ -220,42 +220,47 @@ public class HtmlEx {
|
|||
}
|
||||
|
||||
if (next == i) {
|
||||
if (isInList) {
|
||||
if (isInBulletList != null) {
|
||||
// Current paragraph is no longer a list item; close the previously opened list
|
||||
isInList = false;
|
||||
out.append("</ul>\n");
|
||||
out.append(isInBulletList ? "</ul>\n" : "</ol>\n");
|
||||
isInBulletList = null;
|
||||
}
|
||||
out.append("<br>\n");
|
||||
} else {
|
||||
boolean isListItem = false;
|
||||
Boolean isBulletListItem = null;
|
||||
ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class);
|
||||
for (ParagraphStyle paragraphStyle : paragraphStyles) {
|
||||
final int spanFlags = text.getSpanFlags(paragraphStyle);
|
||||
if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH
|
||||
&& paragraphStyle instanceof BulletSpan) {
|
||||
isListItem = true;
|
||||
isBulletListItem = !(paragraphStyle instanceof eu.faircode.email.NumberSpan);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isListItem && !isInList) {
|
||||
if (isBulletListItem != null && isInBulletList != null && isBulletListItem != isInBulletList) {
|
||||
out.append(isInBulletList ? "</ul>\n" : "</ol>\n");
|
||||
isInBulletList = null;
|
||||
}
|
||||
|
||||
if (isBulletListItem != null && isInBulletList == null) {
|
||||
// Current paragraph is the first item in a list
|
||||
isInList = true;
|
||||
out.append("<ul")
|
||||
isInBulletList = isBulletListItem;
|
||||
out.append(isInBulletList ? "<ul" : "<ol")
|
||||
.append(getTextStyles(text, i, next, true, false))
|
||||
.append(">\n");
|
||||
}
|
||||
|
||||
if (isInList && !isListItem) {
|
||||
if (isInBulletList != null && isBulletListItem == null) {
|
||||
// Current paragraph is no longer a list item; close the previously opened list
|
||||
isInList = false;
|
||||
out.append("</ul>\n");
|
||||
out.append(isInBulletList ? "</ul>\n" : "</ol>\n");
|
||||
isInBulletList = null;
|
||||
}
|
||||
|
||||
String tagType = isListItem ? "li" : "p";
|
||||
String tagType = isBulletListItem != null ? "li" : "p";
|
||||
out.append("<").append(tagType)
|
||||
.append(getTextDirection(text, i, next))
|
||||
.append(getTextStyles(text, i, next, !isListItem, true))
|
||||
.append(getTextStyles(text, i, next, isBulletListItem == null, true))
|
||||
.append(">");
|
||||
|
||||
withinParagraph(out, text, i, next);
|
||||
|
@ -264,9 +269,9 @@ public class HtmlEx {
|
|||
out.append(tagType);
|
||||
out.append(">\n");
|
||||
|
||||
if (next == end && isInList) {
|
||||
isInList = false;
|
||||
out.append("</ul>\n");
|
||||
if (next == end && isInBulletList != null) {
|
||||
out.append(isInBulletList ? "</ul>\n" : "</ol>\n");
|
||||
isInBulletList = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -767,22 +767,6 @@ public class HtmlHelper {
|
|||
for (Element subp : document.select("sub,sup"))
|
||||
subp.tagName("small");
|
||||
|
||||
// Lists
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li
|
||||
if (!view) {
|
||||
for (Element li : document.select("li")) {
|
||||
Element parent = li.parent();
|
||||
if (parent == null || "ul".equals(parent.tagName()))
|
||||
continue; // li.prependText("• ");
|
||||
else
|
||||
li.prependText((li.elementSiblingIndex() + 1) + ". ");
|
||||
li.tagName("span");
|
||||
li.appendElement("br"); // line break after list item
|
||||
}
|
||||
document.select("ol").tagName("div");
|
||||
//document.select("ul").tagName("div");
|
||||
}
|
||||
|
||||
// Tables
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table
|
||||
for (Element col : document.select("th,td")) {
|
||||
|
@ -2291,7 +2275,7 @@ public class HtmlHelper {
|
|||
int s = start.get(spans[i]);
|
||||
int e = end.get(spans[i]);
|
||||
int f = flags.get(spans[i]);
|
||||
if (spans[i] instanceof BulletSpan)
|
||||
if (spans[i] instanceof BulletSpan || spans[i] instanceof NumberSpan)
|
||||
if (s > 1 && ssb.charAt(s - 1) == '\n' &&
|
||||
e > 1 && ssb.charAt(e - 1) == '\n')
|
||||
f |= Spanned.SPAN_PARAGRAPH;
|
||||
|
@ -2349,42 +2333,6 @@ public class HtmlHelper {
|
|||
return reverse;
|
||||
}
|
||||
|
||||
private static class NumberSpan implements LeadingMarginSpan {
|
||||
private TextPaint tp;
|
||||
private String number;
|
||||
private int margin;
|
||||
|
||||
public NumberSpan(int gapWidth, int color, float textSize, int index) {
|
||||
tp = new TextPaint();
|
||||
tp.setStyle(Paint.Style.FILL);
|
||||
tp.setColor(color);
|
||||
tp.setTypeface(Typeface.MONOSPACE);
|
||||
tp.setTextSize(textSize);
|
||||
|
||||
number = index + ".";
|
||||
margin = Math.round(tp.measureText(number) + gapWidth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLeadingMargin(boolean first) {
|
||||
// https://issuetracker.google.com/issues/36956124
|
||||
// This is called before drawLeadingMargin to justify the text
|
||||
return margin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
|
||||
if (text instanceof Spanned &&
|
||||
((Spanned) text).getSpanStart(this) == start) {
|
||||
float textSize = tp.getTextSize();
|
||||
if (textSize > p.getTextSize())
|
||||
tp.setTextSize(p.getTextSize());
|
||||
c.drawText(number, x + dir, baseline, tp);
|
||||
tp.setTextSize(textSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LineSpan extends ReplacementSpan {
|
||||
private int lineColor;
|
||||
private float strokeWidth;
|
||||
|
|
64
app/src/main/java/eu/faircode/email/NumberSpan.java
Normal file
64
app/src/main/java/eu/faircode/email/NumberSpan.java
Normal file
|
@ -0,0 +1,64 @@
|
|||
package eu.faircode.email;
|
||||
|
||||
/*
|
||||
This file is part of FairEmail.
|
||||
|
||||
FairEmail is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FairEmail is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2018-2020 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Layout;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.BulletSpan;
|
||||
|
||||
public class NumberSpan extends BulletSpan {
|
||||
private TextPaint tp;
|
||||
private String number;
|
||||
private int margin;
|
||||
|
||||
public NumberSpan(int gapWidth, int color, float textSize, int index) {
|
||||
tp = new TextPaint();
|
||||
tp.setStyle(Paint.Style.FILL);
|
||||
tp.setColor(color);
|
||||
tp.setTypeface(Typeface.MONOSPACE);
|
||||
tp.setTextSize(textSize);
|
||||
|
||||
number = index + ".";
|
||||
margin = Math.round(tp.measureText(number) + gapWidth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLeadingMargin(boolean first) {
|
||||
// https://issuetracker.google.com/issues/36956124
|
||||
// This is called before drawLeadingMargin to justify the text
|
||||
return margin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
|
||||
if (text instanceof Spanned &&
|
||||
((Spanned) text).getSpanStart(this) == start) {
|
||||
float textSize = tp.getTextSize();
|
||||
if (textSize > p.getTextSize())
|
||||
tp.setTextSize(p.getTextSize());
|
||||
c.drawText(number, x + dir, baseline, tp);
|
||||
tp.setTextSize(textSize);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package eu.faircode.email;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
|
@ -16,6 +17,7 @@ import android.text.style.TypefaceSpan;
|
|||
import android.text.style.URLSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
|
@ -96,9 +98,10 @@ public class StyleHelper {
|
|||
popupMenu.inflate(R.menu.popup_style);
|
||||
|
||||
String[] fontNames = anchor.getResources().getStringArray(R.array.fontNameNames);
|
||||
SubMenu smenu = popupMenu.getMenu().findItem(R.id.menu_style_font).getSubMenu();
|
||||
for (int i = 0; i < fontNames.length; i++)
|
||||
popupMenu.getMenu().add(R.id.group_style_font, i, 4, fontNames[i]);
|
||||
popupMenu.getMenu().add(R.id.group_style_font, fontNames.length, 4, R.string.title_style_font_default);
|
||||
smenu.add(R.id.group_style_font, i, 0, fontNames[i]);
|
||||
smenu.add(R.id.group_style_font, fontNames.length, 0, R.string.title_style_font_default);
|
||||
|
||||
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
|
@ -198,18 +201,25 @@ public class StyleHelper {
|
|||
for (BulletSpan span : spans)
|
||||
t.removeSpan(span);
|
||||
|
||||
int colorAccent = Helper.resolveColor(etBody.getContext(), R.attr.colorAccent);
|
||||
int dp3 = Helper.dp2pixels(etBody.getContext(), 3);
|
||||
int dp6 = Helper.dp2pixels(etBody.getContext(), 6);
|
||||
Context context = etBody.getContext();
|
||||
int colorAccent = Helper.resolveColor(context, R.attr.colorAccent);
|
||||
int dp3 = Helper.dp2pixels(context, 3);
|
||||
int dp6 = Helper.dp2pixels(context, 6);
|
||||
float textSize = Helper.getTextSize(context, 0);
|
||||
|
||||
|
||||
int i = s;
|
||||
int j = s + 1;
|
||||
int index = 1;
|
||||
while (j < e) {
|
||||
if (i > 0 && t.charAt(i - 1) == '\n' && t.charAt(j) == '\n') {
|
||||
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);
|
||||
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);
|
||||
else
|
||||
t.setSpan(new BulletSpan(dp6, colorAccent, dp3), 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);
|
||||
t.setSpan(new NumberSpan(dp6, colorAccent, textSize, index++), i, j + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PARAGRAPH);
|
||||
|
||||
i = j + 1;
|
||||
}
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<group
|
||||
android:id="@+id/group_style_size"
|
||||
android:orderInCategory="1">
|
||||
<item
|
||||
android:id="@+id/menu_style_size_small"
|
||||
android:title="@string/title_style_size_small" />
|
||||
<item
|
||||
android:id="@+id/menu_style_size"
|
||||
android:orderInCategory="1"
|
||||
android:title="@string/title_style_size">
|
||||
<menu>
|
||||
<group android:id="@+id/group_style_size">
|
||||
<item
|
||||
android:id="@+id/menu_style_size_small"
|
||||
android:title="@string/title_style_size_small" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_style_size_medium"
|
||||
android:title="@string/title_style_size_medium" />
|
||||
<item
|
||||
android:id="@+id/menu_style_size_medium"
|
||||
android:title="@string/title_style_size_medium" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_style_size_large"
|
||||
android:title="@string/title_style_size_large" />
|
||||
</group>
|
||||
<item
|
||||
android:id="@+id/menu_style_size_large"
|
||||
android:title="@string/title_style_size_large" />
|
||||
</group>
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
<group
|
||||
android:id="@+id/group_style_color"
|
||||
|
@ -24,17 +29,31 @@
|
|||
android:title="@string/title_style_color" />
|
||||
</group>
|
||||
|
||||
<group
|
||||
android:id="@+id/group_style_list"
|
||||
android:orderInCategory="3">
|
||||
<item
|
||||
android:id="@+id/menu_style_list"
|
||||
android:title="@string/title_style_list" />
|
||||
</group>
|
||||
<item
|
||||
android:id="@+id/menu_style_list"
|
||||
android:orderInCategory="3"
|
||||
android:title="@string/title_style_list">
|
||||
<menu>
|
||||
<group android:id="@+id/group_style_list">
|
||||
<item
|
||||
android:id="@+id/menu_style_list_bullets"
|
||||
android:title="@string/title_style_list_bullets" />
|
||||
<item
|
||||
android:id="@+id/menu_style_list_numbered"
|
||||
android:title="@string/title_style_list_numbered" />
|
||||
</group>
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
<group
|
||||
android:id="@+id/group_style_font"
|
||||
android:orderInCategory="4" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_style_font"
|
||||
android:orderInCategory="4"
|
||||
android:title="@string/title_style_font">
|
||||
<menu>
|
||||
<group android:id="@+id/group_style_font" />
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
<group
|
||||
android:id="@+id/group_style_clear"
|
||||
|
|
|
@ -913,10 +913,12 @@
|
|||
<string name="title_style_size_small">Small</string>
|
||||
<string name="title_style_size_medium">Medium</string>
|
||||
<string name="title_style_size_large">Large</string>
|
||||
<string name="title_style_color">Color …</string>
|
||||
<string name="title_style_list">List</string>
|
||||
<string name="title_style_list_bullets">Bullets</string>
|
||||
<string name="title_style_list_numbered">Numbered</string>
|
||||
<string name="title_style_font">Font</string>
|
||||
<string name="title_style_font_default">Default</string>
|
||||
<string name="title_style_color">Color …</string>
|
||||
<string name="title_style_list">Create list</string>
|
||||
<string name="title_style_clear">Clear formatting</string>
|
||||
<string name="title_style_link">Insert link</string>
|
||||
|
||||
|
|
87
patches/HtmlExNumber.patch
Normal file
87
patches/HtmlExNumber.patch
Normal file
|
@ -0,0 +1,87 @@
|
|||
diff --git a/app/src/main/java/eu/faircode/email/HtmlEx.java b/app/src/main/java/eu/faircode/email/HtmlEx.java
|
||||
index 5cfceaccd..f6db9f051 100644
|
||||
--- a/app/src/main/java/eu/faircode/email/HtmlEx.java
|
||||
+++ b/app/src/main/java/eu/faircode/email/HtmlEx.java
|
||||
@@ -211,7 +211,7 @@ public class HtmlEx {
|
||||
|
||||
private /* static */ void withinBlockquoteIndividual(StringBuilder out, Spanned text, int start,
|
||||
int end) {
|
||||
- boolean isInList = false;
|
||||
+ Boolean isInBulletList = null;
|
||||
int next;
|
||||
for (int i = start; i <= end; i = next) {
|
||||
next = TextUtils.indexOf(text, '\n', i, end);
|
||||
@@ -220,42 +220,47 @@ public class HtmlEx {
|
||||
}
|
||||
|
||||
if (next == i) {
|
||||
- if (isInList) {
|
||||
+ if (isInBulletList != null) {
|
||||
// Current paragraph is no longer a list item; close the previously opened list
|
||||
- isInList = false;
|
||||
- out.append("</ul>\n");
|
||||
+ out.append(isInBulletList ? "</ul>\n" : "</ol>\n");
|
||||
+ isInBulletList = null;
|
||||
}
|
||||
out.append("<br>\n");
|
||||
} else {
|
||||
- boolean isListItem = false;
|
||||
+ Boolean isBulletListItem = null;
|
||||
ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class);
|
||||
for (ParagraphStyle paragraphStyle : paragraphStyles) {
|
||||
final int spanFlags = text.getSpanFlags(paragraphStyle);
|
||||
if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH
|
||||
&& paragraphStyle instanceof BulletSpan) {
|
||||
- isListItem = true;
|
||||
+ isBulletListItem = !(paragraphStyle instanceof eu.faircode.email.NumberSpan);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- if (isListItem && !isInList) {
|
||||
+ if (isBulletListItem != null && isInBulletList != null && isBulletListItem != isInBulletList) {
|
||||
+ out.append(isInBulletList ? "</ul>\n" : "</ol>\n");
|
||||
+ isInBulletList = null;
|
||||
+ }
|
||||
+
|
||||
+ if (isBulletListItem != null && isInBulletList == null) {
|
||||
// Current paragraph is the first item in a list
|
||||
- isInList = true;
|
||||
- out.append("<ul")
|
||||
+ isInBulletList = isBulletListItem;
|
||||
+ out.append(isInBulletList ? "<ul" : "<ol")
|
||||
.append(getTextStyles(text, i, next, true, false))
|
||||
.append(">\n");
|
||||
}
|
||||
|
||||
- if (isInList && !isListItem) {
|
||||
+ if (isInBulletList != null && isBulletListItem == null) {
|
||||
// Current paragraph is no longer a list item; close the previously opened list
|
||||
- isInList = false;
|
||||
- out.append("</ul>\n");
|
||||
+ out.append(isInBulletList ? "</ul>\n" : "</ol>\n");
|
||||
+ isInBulletList = null;
|
||||
}
|
||||
|
||||
- String tagType = isListItem ? "li" : "p";
|
||||
+ String tagType = isBulletListItem != null ? "li" : "p";
|
||||
out.append("<").append(tagType)
|
||||
.append(getTextDirection(text, i, next))
|
||||
- .append(getTextStyles(text, i, next, !isListItem, true))
|
||||
+ .append(getTextStyles(text, i, next, isBulletListItem == null, true))
|
||||
.append(">");
|
||||
|
||||
withinParagraph(out, text, i, next);
|
||||
@@ -264,9 +269,9 @@ public class HtmlEx {
|
||||
out.append(tagType);
|
||||
out.append(">\n");
|
||||
|
||||
- if (next == end && isInList) {
|
||||
- isInList = false;
|
||||
- out.append("</ul>\n");
|
||||
+ if (next == end && isInBulletList != null) {
|
||||
+ out.append(isInBulletList ? "</ul>\n" : "</ol>\n");
|
||||
+ isInBulletList = null;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue