Markdown対応の続き

This commit is contained in:
tateisu 2018-08-22 10:05:54 +09:00
parent 605afa9323
commit 7bf9e6b40a
10 changed files with 267 additions and 121 deletions

View File

@ -1113,7 +1113,6 @@ class ActMain : AppCompatActivity()
} catch(ex : Throwable) {
log.trace(ex)
}
}
sv = Pref.spTimelineFontBold(pref)
@ -1124,13 +1123,12 @@ class ActMain : AppCompatActivity()
log.trace(ex)
}
} else if(timeline_font != null) {
} else {
try {
timeline_font_bold = Typeface.create(timeline_font, Typeface.BOLD)
} catch(ex : Throwable) {
log.trace(ex)
}
}
fun parseIconSize(stringPref : Pref.StringPref) : Int {

View File

@ -163,17 +163,15 @@ class ColumnViewHolder(
init {
if(ActMain.timeline_font != null) {
viewRoot.scan { v ->
try {
if(v is Button) {
// ボタンは触らない
} else if(v is TextView) {
v.typeface = ActMain.timeline_font
}
} catch(ex : Throwable) {
log.trace(ex)
viewRoot.scan { v ->
try {
if(v is Button) {
// ボタンは触らない
} else if(v is TextView) {
v.typeface = ActMain.timeline_font
}
} catch(ex : Throwable) {
log.trace(ex)
}
}

View File

@ -242,36 +242,31 @@ internal class ItemViewHolder(
this.access_info = column.access_info
if(ActMain.timeline_font != null || ActMain.timeline_font_bold != null) {
val font_bold = ActMain.timeline_font_bold ?: ActMain.timeline_font
val font_normal = ActMain.timeline_font ?: ActMain.timeline_font_bold
viewRoot.scan { v ->
try {
if(v is CountImageButton) {
// ボタンは太字なので触らない
} else if(v is Button) {
// ボタンは太字なので触らない
} else if(v is TextView) {
val typeface = when {
v === tvName ||
v === tvFollowerName ||
v === tvBoosted ||
v === tvTrendTagCount ||
v === tvTrendTagName ||
v === tvFilterPhrase -> font_bold
else -> font_normal
}
if(typeface != null) v.typeface = typeface
val font_bold = ActMain.timeline_font_bold
val font_normal = ActMain.timeline_font
viewRoot.scan { v ->
try {
when(v) {
// ボタンは太字なので触らない
is CountImageButton -> {
}
// ボタンは太字なので触らない
is Button -> {
}
is TextView -> v.typeface = when {
v === tvName ||
v === tvFollowerName ||
v === tvBoosted ||
v === tvTrendTagCount ||
v === tvTrendTagName ||
v === tvFilterPhrase -> font_bold
else -> font_normal
}
} catch(ex : Throwable) {
log.trace(ex)
}
} catch(ex : Throwable) {
log.trace(ex)
}
} else {
tvName.typeface = Typeface.DEFAULT_BOLD
tvFollowerName.typeface = Typeface.DEFAULT_BOLD
tvBoosted.typeface = Typeface.DEFAULT_BOLD
tvTrendTagCount.typeface = Typeface.DEFAULT_BOLD
}
if(bSimpleList) {

View File

@ -24,9 +24,7 @@ internal abstract class ViewHolderHeaderBase(val activity : ActMain, val viewRoo
if(v is Button) {
// ボタンは太字なので触らない
} else if(v is TextView) {
if(ActMain.timeline_font != null) {
v.typeface = ActMain.timeline_font
}
v.typeface = ActMain.timeline_font
if(! activity.timeline_font_size_sp.isNaN()) {
v.textSize = activity.timeline_font_size_sp
}

View File

@ -262,8 +262,8 @@ internal class ViewHolderHeaderProfile(
val content_color = column.content_color
val c = if(content_color != 0) content_color else default_color
val nameTypeface = ActMain.timeline_font_bold ?: Typeface.DEFAULT_BOLD
val valueTypeface = ActMain.timeline_font ?: Typeface.DEFAULT
val nameTypeface = ActMain.timeline_font_bold
val valueTypeface = ActMain.timeline_font
for(item in who.fields) {

View File

@ -0,0 +1,16 @@
package jp.juggler.subwaytooter.span
interface AnimatableSpanInvalidator {
val timeFromStart : Long
fun delayInvalidate(delay : Long)
}
interface AnimatableSpan{
fun setInvalidateCallback(
draw_target_tag : Any,
invalidate_callback : AnimatableSpanInvalidator
)
}

View File

@ -0,0 +1,129 @@
package jp.juggler.subwaytooter.span
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Typeface
import android.text.TextPaint
import android.text.style.MetricAffectingSpan
import java.lang.ref.WeakReference
import java.util.*
class MisskeyBigSpan (
private val typeface : Typeface
): MetricAffectingSpan() ,AnimatableSpan{
private var invalidate_callback : AnimatableSpanInvalidator? = null
private var refDrawTarget : WeakReference<Any>? = null
override fun setInvalidateCallback(
draw_target_tag : Any,
invalidate_callback : AnimatableSpanInvalidator
) {
this.refDrawTarget = WeakReference(draw_target_tag)
this.invalidate_callback = invalidate_callback
}
private val textScalingMax = 1.5f
override fun updateMeasureState(paint : TextPaint) {
apply(paint,bDrawing=false)
}
override fun updateDrawState(drawState : TextPaint) {
apply(drawState,bDrawing=true)
}
private fun apply(paint : Paint,bDrawing:Boolean ) {
val textScaling = when{
!bDrawing -> textScalingMax
else -> {
invalidate_callback?.delayInvalidate(100L)
val t = (invalidate_callback?.timeFromStart ?: 0L) /500f
textScalingMax * ( Math.sin(t.toDouble()).toFloat() * 0.1f + 0.9f)
}
}
paint.textSize = paint.textSize * textScaling
val oldTypeface = paint.typeface
val oldStyle = oldTypeface?.style ?: 0
val fakeStyle = oldStyle and typeface.style.inv()
if(fakeStyle and Typeface.BOLD != 0) {
paint.isFakeBoldText = true
}
if(fakeStyle and Typeface.ITALIC != 0) {
paint.textSkewX = - 0.25f
}
paint.typeface = typeface
}
}
class MisskeyMotionSpan (
private val typeface : Typeface
): MetricAffectingSpan() ,AnimatableSpan{
private var invalidate_callback : AnimatableSpanInvalidator? = null
private var refDrawTarget : WeakReference<Any>? = null
override fun setInvalidateCallback(
draw_target_tag : Any,
invalidate_callback : AnimatableSpanInvalidator
) {
this.refDrawTarget = WeakReference(draw_target_tag)
this.invalidate_callback = invalidate_callback
}
private val textScalingMax = 1.0f
override fun updateMeasureState(paint : TextPaint) {
apply(paint,bDrawing=false)
}
override fun updateDrawState(drawState : TextPaint) {
apply(drawState,bDrawing=true)
}
private fun apply(paint : Paint,bDrawing:Boolean ) {
val textScaling = when{
!bDrawing -> textScalingMax
else -> {
invalidate_callback?.delayInvalidate(100L)
val t = (invalidate_callback?.timeFromStart ?: 0L) /100f
textScalingMax * ( Math.sin(t.toDouble()).toFloat() * 0.1f + 0.9f)
}
}
paint.textSize = paint.textSize * textScaling
val oldTypeface = paint.typeface
val oldStyle = oldTypeface?.style ?: 0
val fakeStyle = oldStyle and typeface.style.inv()
if(fakeStyle and Typeface.BOLD != 0) {
paint.isFakeBoldText = true
}
if(fakeStyle and Typeface.ITALIC != 0) {
paint.textSkewX = - 0.25f
}
paint.typeface = typeface
}
}

View File

@ -13,7 +13,7 @@ import jp.juggler.subwaytooter.Pref
import jp.juggler.subwaytooter.util.LogCategory
import java.lang.ref.WeakReference
class NetworkEmojiSpan internal constructor(private val url : String) : ReplacementSpan() {
class NetworkEmojiSpan internal constructor(private val url : String) : ReplacementSpan(),AnimatableSpan {
companion object {
@ -27,9 +27,7 @@ class NetworkEmojiSpan internal constructor(private val url : String) : Replacem
private val rect_src = Rect()
private val rect_dst = RectF()
private var invalidate_callback : InvalidateCallback? = null
private var refDrawTarget : WeakReference<Any>? = null
// フレーム探索結果を格納する構造体を確保しておく
private val mFrameFindResult = ApngFrames.FindFrameResult()
@ -37,18 +35,17 @@ class NetworkEmojiSpan internal constructor(private val url : String) : Replacem
mPaint.isFilterBitmap = true
}
interface InvalidateCallback {
val timeFromStart : Long
fun delayInvalidate(delay : Long)
}
private var invalidate_callback : AnimatableSpanInvalidator? = null
private var refDrawTarget : WeakReference<Any>? = null
fun setInvalidateCallback(
override fun setInvalidateCallback(
draw_target_tag : Any,
invalidate_callback : InvalidateCallback
invalidate_callback : AnimatableSpanInvalidator
) {
this.refDrawTarget = WeakReference(draw_target_tag)
this.invalidate_callback = invalidate_callback
}
override fun getSize(
paint : Paint,

View File

@ -15,9 +15,7 @@ import jp.juggler.subwaytooter.ActMain
import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.Pref
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.span.EmojiImageSpan
import jp.juggler.subwaytooter.span.HighlightSpan
import jp.juggler.subwaytooter.span.MyClickableSpan
import jp.juggler.subwaytooter.span.*
import jp.juggler.subwaytooter.table.HighlightWord
import uk.co.chrisjenx.calligraphy.CalligraphyTypefaceSpan
import java.util.regex.Pattern
@ -656,6 +654,16 @@ object MisskeyMarkdownDecoder {
generateMotionNodeParser(NodeType.Motion)
)
private val reStartEmptyLines = """\A(?:[  ]*?[\x0d\x0a]+)+""".toRegex()
private val reEndEmptyLines = """[\s\x0d\x0a]+\z""".toRegex()
private fun trimBlock(s:String?):String?{
s?:return null
return s
.replace(reStartEmptyLines,"")
.replace(reEndEmptyLines,"")
}
private fun parse(source : String?) : ArrayList<Node> {
val result = ArrayList<Node>()
@ -762,7 +770,7 @@ object MisskeyMarkdownDecoder {
try {
if(src != null) {
val font_bold = ActMain.timeline_font_bold ?: ActMain.timeline_font
val font_bold = ActMain.timeline_font_bold
for( node in parse(src) ){
val nodeSource = src.substring(node.sourceStart,node.sourceStart+node.sourceLength)
@ -884,58 +892,13 @@ object MisskeyMarkdownDecoder {
appendLink(acct,"https://${linkHelper}/$acct")
}
}
NodeType.Hashtag ->{
val tag = data?.get(0)
if(tag?.isNotEmpty()==true){
appendLink("#$tag","https://misskey.m544.net/tags/"+tag.encodePercent())
}
}
NodeType.Text->{
appendText(nodeSource)
}
NodeType.Big ->{
appendText(data?.get(0))
setSpan(RelativeSizeSpan(1.5f))
setSpan(CalligraphyTypefaceSpan(font_bold))
// TODO アニメーション
}
NodeType.Bold->{
appendText(data?.get(0))
setSpan(CalligraphyTypefaceSpan(font_bold))
}
NodeType.Title->{
appendText(data?.get(0)?.trim{it<=' '})
setSpan(AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER))
// TODO 見出し背景に色をつける
// ブロック要素なので改行必須
appendText("\n")
}
NodeType.CodeBlock->{
appendTextCode(data?.get(0)?.trimEnd())
setSpan(BackgroundColorSpan(0x40808080))
setSpan(CalligraphyTypefaceSpan(Typeface.MONOSPACE))
// ブロック要素なので改行必須
appendText("\n")
// TODO Syntax highlight
}
NodeType.CodeInline->{
appendTextCode(data?.get(0))
setSpan(BackgroundColorSpan(0x40808080))
setSpan(CalligraphyTypefaceSpan(Typeface.MONOSPACE))
// TODO Syntax highlight
}
NodeType.Quote->{
appendText(data?.get(0)?.trim())
setSpan(CalligraphyTypefaceSpan(Typeface.defaultFromStyle(Typeface.ITALIC)))
// ブロック要素なので改行必須
appendText("\n")
}
NodeType.Emoji->{
val code = data?.get(0)
@ -944,23 +907,74 @@ object MisskeyMarkdownDecoder {
}
}
NodeType.Search->{
val text = data?.get(0)
if( text?.isNotEmpty()==true){
appendText(text)
start = sb.length
appendLink(
context.getString(R.string.search),
"https://www.google.co.jp/search?q="+text.encodePercent()
)
// ブロック要素なので改行必須
appendText("\n")
}
////////////////////////////////////////////
// 装飾インライン要素
NodeType.Text->{
appendText(nodeSource)
}
NodeType.Big ->{
appendText(data?.get(0))
setSpan(MisskeyBigSpan(font_bold))
}
NodeType.Motion->{
val code = data?.get(0)
appendText(code)
// TODO なんかする
setSpan(MisskeyMotionSpan(ActMain.timeline_font))
}
NodeType.Bold->{
appendText(data?.get(0))
setSpan(CalligraphyTypefaceSpan(font_bold))
}
NodeType.CodeInline->{
appendTextCode(data?.get(0))
setSpan(BackgroundColorSpan(0x40808080))
setSpan(CalligraphyTypefaceSpan(Typeface.MONOSPACE))
}
////////////////////////////////////////////
// ブロック要素
NodeType.Title->{
appendText(trimBlock(data?.get(0)))
setSpan(AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER))
setSpan(BackgroundColorSpan(0x20808080))
setSpan(RelativeSizeSpan(1.5f))
appendText("\n")
}
NodeType.CodeBlock->{
appendTextCode(trimBlock(data?.get(0)))
setSpan(BackgroundColorSpan(0x40808080))
setSpan(RelativeSizeSpan(0.7f))
setSpan(CalligraphyTypefaceSpan(Typeface.MONOSPACE))
appendText("\n")
}
NodeType.Quote->{
appendText(trimBlock(data?.get(0)))
setSpan(BackgroundColorSpan(0x20808080))
setSpan(CalligraphyTypefaceSpan(Typeface.defaultFromStyle(Typeface.ITALIC)))
appendText("\n")
}
NodeType.Search->{
val text = data?.get(0)
val kw_start = sb.length // キーワードの開始位置
appendText(text)
appendText(" ")
start = sb.length // 検索リンクの開始位置
appendLink(
context.getString(R.string.search),
"https://www.google.co.jp/search?q="+(text?:"Subway Tooter").encodePercent()
)
sb.setSpan(RelativeSizeSpan(1.5f), kw_start, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
appendText("\n")
}
}
}

View File

@ -8,10 +8,11 @@ import android.view.View
import java.util.ArrayList
import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.span.NetworkEmojiSpan
import jp.juggler.subwaytooter.span.AnimatableSpan
import jp.juggler.subwaytooter.span.AnimatableSpanInvalidator
import java.lang.ref.WeakReference
class NetworkEmojiInvalidator(internal val handler : Handler, internal val view : View) : Runnable, NetworkEmojiSpan.InvalidateCallback {
class NetworkEmojiInvalidator(internal val handler : Handler, internal val view : View) : Runnable, AnimatableSpanInvalidator {
private val draw_target_list = ArrayList<WeakReference<Any>>()
@ -41,7 +42,7 @@ class NetworkEmojiInvalidator(internal val handler : Handler, internal val view
draw_target_list.clear()
if(dst != null) {
for(span in dst.getSpans(0, dst.length, NetworkEmojiSpan::class.java)) {
for(span in dst.getSpans(0, dst.length, AnimatableSpan::class.java)) {
val tag = WeakReference(Any())
draw_target_list.add(tag)
span.setInvalidateCallback(tag, this)