Lists in text formatting (AND-221)

This commit is contained in:
Grishka 2024-10-09 03:00:37 +03:00
parent 4177faa553
commit 82c6c8076a
2 changed files with 88 additions and 9 deletions

View File

@ -1,5 +1,6 @@
package org.joinmastodon.android.ui.text;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Typeface;
import android.text.SpannableStringBuilder;
@ -104,10 +105,18 @@ public class HtmlParser{
return false;
}
@SuppressLint("DefaultLocale")
@Override
public void head(@NonNull Node node, int depth){
if(node instanceof TextNode textNode){
ssb.append(isInsidePre() ? textNode.getWholeText().stripTrailing() : textNode.text());
if(isInsidePre()){
ssb.append(textNode.getWholeText().stripTrailing());
}else{
String text=textNode.text();
if(ssb.length()==0 || ssb.charAt(ssb.length()-1)=='\n')
text=text.stripLeading();
ssb.append(text);
}
}else if(node instanceof Element el){
switch(el.nodeName()){
case "a" -> {
@ -152,8 +161,29 @@ public class HtmlParser{
ssb.append(" ", new SpacerSpan(V.dp(4), 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
case "pre" -> {
openSpans.add(new SpanInfo(new CodeBlockSpan(context), ssb.length(), el));
case "pre" -> openSpans.add(new SpanInfo(new CodeBlockSpan(context), ssb.length(), el));
case "li" -> {
Element parent=el.parent();
if(parent==null)
return;
if(ssb.length()>0 && ssb.charAt(ssb.length()-1)!='\n')
ssb.append('\n');
String markerText;
if("ol".equals(parent.nodeName())){
markerText=String.format("%d.", (parent.hasAttr("start") ? safeParseInt(parent.attr("start")) : 1)+el.elementSiblingIndex());
}else{
markerText="";
}
openSpans.add(new SpanInfo(new ListItemMarkerSpan(markerText), ssb.length(), el));
StringBuilder copyableText=new StringBuilder();
for(SpanInfo si:openSpans){
if(si.span instanceof ListItemMarkerSpan ims){
copyableText.append(ims.text);
}
}
copyableText.append(' ');
ssb.append(copyableText.toString(), new InvisibleSpan(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
@ -162,18 +192,25 @@ public class HtmlParser{
@Override
public void tail(@NonNull Node node, int depth){
if(node instanceof Element el){
if("span".equals(el.nodeName()) && el.hasClass("ellipsis")){
String name=el.nodeName();
if("span".equals(name) && el.hasClass("ellipsis")){
ssb.append("", new DeleteWhenCopiedSpan(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}else if("p".equals(el.nodeName())){
}else if("p".equals(name) || ("ol".equals(name) || "ul".equals(name))){
if(node.nextSibling()!=null)
ssb.append("\n\n");
}else if(!openSpans.isEmpty()){
}else if("pre".equals(name)){
if(node.nextSibling()!=null)
ssb.append("\n");
}
if(!openSpans.isEmpty()){
SpanInfo si=openSpans.get(openSpans.size()-1);
if(si.element==el){
if(si.span instanceof MonospaceSpan){
ssb.append(" ", new SpacerSpan(V.dp(4), 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if(si.span!=null){
if(si.span instanceof MonospaceSpan){
ssb.append(" ", new SpacerSpan(V.dp(4), 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
ssb.setSpan(si.span, si.start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
ssb.setSpan(si.span, si.start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
openSpans.remove(openSpans.size()-1);
}
}
@ -185,6 +222,14 @@ public class HtmlParser{
return ssb;
}
private static int safeParseInt(String s){
try{
return Integer.parseInt(s);
}catch(NumberFormatException x){
return 0;
}
}
public static void parseCustomEmoji(SpannableStringBuilder ssb, List<Emoji> emojis){
Map<String, Emoji> emojiByCode =
emojis.stream()

View File

@ -0,0 +1,34 @@
package org.joinmastodon.android.ui.text;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Layout;
import android.text.Spanned;
import android.text.style.LeadingMarginSpan;
import me.grishka.appkit.utils.V;
public class ListItemMarkerSpan implements LeadingMarginSpan{
public String text;
public ListItemMarkerSpan(String text){
this.text=text;
}
@Override
public int getLeadingMargin(boolean first){
return V.dp(32);
}
@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 s && s.getSpanStart(this)==start){
int level=s.getSpans(start, end, LeadingMarginSpan.class).length-1;
if(dir<0){ // RTL
c.drawText(this.text, layout.getWidth()-V.dp(32*level)-p.measureText(this.text), baseline, p);
}else{
c.drawText(this.text, x+V.dp(32*level), baseline, p);
}
}
}
}