[Header formatting][New]: Implement more complete configuration for header section (H1, H2, ..., H6)
In original version, we could only configure the line break color and thickness, for H1 and H2. With this new implementation, we can set individual settings for all headers, with the options being: - Text color - Text font - Text size
This commit is contained in:
parent
4d0336c13b
commit
11f072edc2
@ -10,12 +10,14 @@ import android.text.TextPaint;
|
|||||||
import android.text.style.LeadingMarginSpan;
|
import android.text.style.LeadingMarginSpan;
|
||||||
import android.text.style.MetricAffectingSpan;
|
import android.text.style.MetricAffectingSpan;
|
||||||
|
|
||||||
|
import ru.noties.markwon.spans.heading.HeadingType;
|
||||||
|
|
||||||
public class HeadingSpan extends MetricAffectingSpan implements LeadingMarginSpan {
|
public class HeadingSpan extends MetricAffectingSpan implements LeadingMarginSpan {
|
||||||
|
|
||||||
private final SpannableTheme theme;
|
private final SpannableTheme theme;
|
||||||
private final Rect rect = ObjectsPool.rect();
|
private final Rect rect = ObjectsPool.rect();
|
||||||
private final Paint paint = ObjectsPool.paint();
|
private final Paint paint = ObjectsPool.paint();
|
||||||
private final int level;
|
@HeadingType private final int level;
|
||||||
|
|
||||||
public HeadingSpan(@NonNull SpannableTheme theme, @IntRange(from = 1, to = 6) int level) {
|
public HeadingSpan(@NonNull SpannableTheme theme, @IntRange(from = 1, to = 6) int level) {
|
||||||
this.theme = theme;
|
this.theme = theme;
|
||||||
|
@ -9,12 +9,15 @@ import android.support.annotation.AttrRes;
|
|||||||
import android.support.annotation.ColorInt;
|
import android.support.annotation.ColorInt;
|
||||||
import android.support.annotation.Dimension;
|
import android.support.annotation.Dimension;
|
||||||
import android.support.annotation.FloatRange;
|
import android.support.annotation.FloatRange;
|
||||||
import android.support.annotation.IntRange;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextPaint;
|
import android.text.TextPaint;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
|
|
||||||
|
import ru.noties.markwon.spans.heading.HeadingConfig;
|
||||||
|
import ru.noties.markwon.spans.heading.HeadingType;
|
||||||
|
import ru.noties.markwon.spans.heading.HeadingTypeConfig;
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public class SpannableTheme {
|
public class SpannableTheme {
|
||||||
|
|
||||||
@ -78,16 +81,16 @@ public class SpannableTheme {
|
|||||||
|
|
||||||
final Dip dip = new Dip(context);
|
final Dip dip = new Dip(context);
|
||||||
return new Builder()
|
return new Builder()
|
||||||
.linkColor(linkColor)
|
.linkColor(linkColor)
|
||||||
.codeMultilineMargin(dip.toPx(8))
|
.codeMultilineMargin(dip.toPx(8))
|
||||||
.blockMargin(dip.toPx(24))
|
.blockMargin(dip.toPx(24))
|
||||||
.blockQuoteWidth(dip.toPx(4))
|
.blockQuoteWidth(dip.toPx(4))
|
||||||
.bulletListItemStrokeWidth(dip.toPx(1))
|
.bulletListItemStrokeWidth(dip.toPx(1))
|
||||||
.headingBreakHeight(dip.toPx(1))
|
.headingConfig(new HeadingConfig(), dip.density)
|
||||||
.thematicBreakHeight(dip.toPx(4))
|
.thematicBreakHeight(dip.toPx(4))
|
||||||
.tableCellPadding(dip.toPx(4))
|
.tableCellPadding(dip.toPx(4))
|
||||||
.tableBorderWidth(dip.toPx(1))
|
.tableBorderWidth(dip.toPx(1))
|
||||||
.taskListDrawable(new TaskListDrawable(linkColor, linkColor, backgroundColor));
|
.taskListDrawable(new TaskListDrawable(linkColor, linkColor, backgroundColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int resolve(Context context, @AttrRes int attr) {
|
private static int resolve(Context context, @AttrRes int attr) {
|
||||||
@ -104,6 +107,7 @@ public class SpannableTheme {
|
|||||||
protected static final int BLOCK_QUOTE_DEF_COLOR_ALPHA = 25;
|
protected static final int BLOCK_QUOTE_DEF_COLOR_ALPHA = 25;
|
||||||
|
|
||||||
protected static final int CODE_DEF_BACKGROUND_COLOR_ALPHA = 25;
|
protected static final int CODE_DEF_BACKGROUND_COLOR_ALPHA = 25;
|
||||||
|
|
||||||
protected static final float CODE_DEF_TEXT_SIZE_RATIO = .87F;
|
protected static final float CODE_DEF_TEXT_SIZE_RATIO = .87F;
|
||||||
|
|
||||||
protected static final int HEADING_DEF_BREAK_COLOR_ALPHA = 75;
|
protected static final int HEADING_DEF_BREAK_COLOR_ALPHA = 75;
|
||||||
@ -111,7 +115,7 @@ public class SpannableTheme {
|
|||||||
// taken from html spec (most browsers render headings like that)
|
// taken from html spec (most browsers render headings like that)
|
||||||
// is not exposed via protected modifier in order to disallow modification
|
// is not exposed via protected modifier in order to disallow modification
|
||||||
private static final float[] HEADING_SIZES = {
|
private static final float[] HEADING_SIZES = {
|
||||||
2.F, 1.5F, 1.17F, 1.F, .83F, .67F,
|
2.F, 1.5F, 1.17F, 1.F, .83F, .67F,
|
||||||
};
|
};
|
||||||
|
|
||||||
protected static final float SCRIPT_DEF_TEXT_SIZE_RATIO = .75F;
|
protected static final float SCRIPT_DEF_TEXT_SIZE_RATIO = .75F;
|
||||||
@ -159,11 +163,7 @@ public class SpannableTheme {
|
|||||||
// applied ONLY if default typeface was used, otherwise, not applied
|
// applied ONLY if default typeface was used, otherwise, not applied
|
||||||
protected final int codeTextSize;
|
protected final int codeTextSize;
|
||||||
|
|
||||||
// by default paint.getStrokeWidth
|
protected final HeadingConfig headingConfig;
|
||||||
protected final int headingBreakHeight;
|
|
||||||
|
|
||||||
// by default, text color with `HEADING_DEF_BREAK_COLOR_ALPHA` applied alpha
|
|
||||||
protected final int headingBreakColor;
|
|
||||||
|
|
||||||
// by default `SCRIPT_DEF_TEXT_SIZE_RATIO`
|
// by default `SCRIPT_DEF_TEXT_SIZE_RATIO`
|
||||||
protected final float scriptTextSizeRatio;
|
protected final float scriptTextSizeRatio;
|
||||||
@ -202,8 +202,7 @@ public class SpannableTheme {
|
|||||||
this.codeMultilineMargin = builder.codeMultilineMargin;
|
this.codeMultilineMargin = builder.codeMultilineMargin;
|
||||||
this.codeTypeface = builder.codeTypeface;
|
this.codeTypeface = builder.codeTypeface;
|
||||||
this.codeTextSize = builder.codeTextSize;
|
this.codeTextSize = builder.codeTextSize;
|
||||||
this.headingBreakHeight = builder.headingBreakHeight;
|
this.headingConfig = builder.headingConfig;
|
||||||
this.headingBreakColor = builder.headingBreakColor;
|
|
||||||
this.scriptTextSizeRatio = builder.scriptTextSizeRatio;
|
this.scriptTextSizeRatio = builder.scriptTextSizeRatio;
|
||||||
this.thematicBreakColor = builder.thematicBreakColor;
|
this.thematicBreakColor = builder.thematicBreakColor;
|
||||||
this.thematicBreakHeight = builder.thematicBreakHeight;
|
this.thematicBreakHeight = builder.thematicBreakHeight;
|
||||||
@ -214,7 +213,6 @@ public class SpannableTheme {
|
|||||||
this.taskListDrawable = builder.taskListDrawable;
|
this.taskListDrawable = builder.taskListDrawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void applyLinkStyle(@NonNull Paint paint) {
|
public void applyLinkStyle(@NonNull Paint paint) {
|
||||||
paint.setUnderlineText(true);
|
paint.setUnderlineText(true);
|
||||||
if (linkColor != 0) {
|
if (linkColor != 0) {
|
||||||
@ -269,7 +267,7 @@ public class SpannableTheme {
|
|||||||
|
|
||||||
final int width;
|
final int width;
|
||||||
if (bulletWidth == 0
|
if (bulletWidth == 0
|
||||||
|| bulletWidth > min) {
|
|| bulletWidth > min) {
|
||||||
width = min;
|
width = min;
|
||||||
} else {
|
} else {
|
||||||
width = bulletWidth;
|
width = bulletWidth;
|
||||||
@ -322,13 +320,51 @@ public class SpannableTheme {
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyHeadingTextStyle(@NonNull Paint paint, @IntRange(from = 1, to = 6) int level) {
|
public void applyHeadingTextStyle(@NonNull Paint paint, @HeadingType int level) {
|
||||||
|
HeadingTypeConfig headingTypeConfig;
|
||||||
|
switch (level) {
|
||||||
|
case HeadingType.H1:
|
||||||
|
headingTypeConfig = headingConfig.getH1Config();
|
||||||
|
break;
|
||||||
|
case HeadingType.H2:
|
||||||
|
headingTypeConfig = headingConfig.getH2Config();
|
||||||
|
break;
|
||||||
|
case HeadingType.H3:
|
||||||
|
headingTypeConfig = headingConfig.getH3Config();
|
||||||
|
break;
|
||||||
|
case HeadingType.H4:
|
||||||
|
headingTypeConfig = headingConfig.getH4Config();
|
||||||
|
break;
|
||||||
|
case HeadingType.H5:
|
||||||
|
headingTypeConfig = headingConfig.getH5Config();
|
||||||
|
break;
|
||||||
|
case HeadingType.H6:
|
||||||
|
headingTypeConfig = headingConfig.getH6Config();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
headingTypeConfig = new HeadingTypeConfig();
|
||||||
|
}
|
||||||
|
|
||||||
paint.setFakeBoldText(true);
|
paint.setFakeBoldText(true);
|
||||||
paint.setTextSize(paint.getTextSize() * HEADING_SIZES[level - 1]);
|
|
||||||
|
final float textSize = headingTypeConfig.getTextSize() > 0 ?
|
||||||
|
headingTypeConfig.getTextSize() : HEADING_SIZES[level - 1];
|
||||||
|
paint.setTextSize(paint.getTextSize() * textSize);
|
||||||
|
|
||||||
|
final int textColor = headingTypeConfig.getTextColor();
|
||||||
|
if (textColor != -1) {
|
||||||
|
paint.setColor(textColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Typeface typeface = headingTypeConfig.getTypeface();
|
||||||
|
if(typeface != null){
|
||||||
|
paint.setTypeface(typeface);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyHeadingBreakStyle(@NonNull Paint paint) {
|
public void applyHeadingBreakStyle(@NonNull Paint paint) {
|
||||||
final int color;
|
final int color;
|
||||||
|
final int headingBreakColor = headingConfig.getHeadingBreakConfig().getHeadingBreakColor();
|
||||||
if (headingBreakColor != 0) {
|
if (headingBreakColor != 0) {
|
||||||
color = headingBreakColor;
|
color = headingBreakColor;
|
||||||
} else {
|
} else {
|
||||||
@ -336,6 +372,7 @@ public class SpannableTheme {
|
|||||||
}
|
}
|
||||||
paint.setColor(color);
|
paint.setColor(color);
|
||||||
paint.setStyle(Paint.Style.FILL);
|
paint.setStyle(Paint.Style.FILL);
|
||||||
|
final float headingBreakHeight = headingConfig.getHeadingBreakConfig().getHeadingBreakStrokeWidth();
|
||||||
if (headingBreakHeight >= 0) {
|
if (headingBreakHeight >= 0) {
|
||||||
//noinspection SuspiciousNameCombination
|
//noinspection SuspiciousNameCombination
|
||||||
paint.setStrokeWidth(headingBreakHeight);
|
paint.setStrokeWidth(headingBreakHeight);
|
||||||
@ -441,8 +478,7 @@ public class SpannableTheme {
|
|||||||
private int codeMultilineMargin;
|
private int codeMultilineMargin;
|
||||||
private Typeface codeTypeface;
|
private Typeface codeTypeface;
|
||||||
private int codeTextSize;
|
private int codeTextSize;
|
||||||
private int headingBreakHeight = -1;
|
private HeadingConfig headingConfig;
|
||||||
private int headingBreakColor;
|
|
||||||
private float scriptTextSizeRatio;
|
private float scriptTextSizeRatio;
|
||||||
private int thematicBreakColor;
|
private int thematicBreakColor;
|
||||||
private int thematicBreakHeight = -1;
|
private int thematicBreakHeight = -1;
|
||||||
@ -468,8 +504,7 @@ public class SpannableTheme {
|
|||||||
this.codeMultilineMargin = theme.codeMultilineMargin;
|
this.codeMultilineMargin = theme.codeMultilineMargin;
|
||||||
this.codeTypeface = theme.codeTypeface;
|
this.codeTypeface = theme.codeTypeface;
|
||||||
this.codeTextSize = theme.codeTextSize;
|
this.codeTextSize = theme.codeTextSize;
|
||||||
this.headingBreakHeight = theme.headingBreakHeight;
|
this.headingConfig = theme.headingConfig;
|
||||||
this.headingBreakColor = theme.headingBreakColor;
|
|
||||||
this.scriptTextSizeRatio = theme.scriptTextSizeRatio;
|
this.scriptTextSizeRatio = theme.scriptTextSizeRatio;
|
||||||
this.thematicBreakColor = theme.thematicBreakColor;
|
this.thematicBreakColor = theme.thematicBreakColor;
|
||||||
this.thematicBreakHeight = theme.thematicBreakHeight;
|
this.thematicBreakHeight = theme.thematicBreakHeight;
|
||||||
@ -553,14 +588,9 @@ public class SpannableTheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public Builder headingBreakHeight(@Dimension int headingBreakHeight) {
|
public Builder headingConfig(HeadingConfig headingConfig, float density) {
|
||||||
this.headingBreakHeight = headingBreakHeight;
|
headingConfig.setDensityFactor(density);
|
||||||
return this;
|
this.headingConfig = headingConfig;
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public Builder headingBreakColor(@ColorInt int headingBreakColor) {
|
|
||||||
this.headingBreakColor = headingBreakColor;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,7 +660,7 @@ public class SpannableTheme {
|
|||||||
|
|
||||||
private static class Dip {
|
private static class Dip {
|
||||||
|
|
||||||
private final float density;
|
protected final float density;
|
||||||
|
|
||||||
Dip(@NonNull Context context) {
|
Dip(@NonNull Context context) {
|
||||||
this.density = context.getResources().getDisplayMetrics().density;
|
this.density = context.getResources().getDisplayMetrics().density;
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
package ru.noties.markwon.spans.heading
|
||||||
|
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.support.annotation.ColorInt
|
||||||
|
import android.support.annotation.Dimension
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for heading type (H1, H2, ..., H6)
|
||||||
|
*
|
||||||
|
* Can define different configurations for all six types of headings,
|
||||||
|
* plus an extra configuration for the line breaks (only applies to H1 and H2)
|
||||||
|
*
|
||||||
|
* @property h1Config Config for H1 heading
|
||||||
|
* @property h2Config Config for H2 heading
|
||||||
|
* @property h3Config Config for H3 heading
|
||||||
|
* @property h4Config Config for H4 heading
|
||||||
|
* @property h5Config Config for H5 heading
|
||||||
|
* @property h6Config Config for H6 heading
|
||||||
|
* @property headingBreakConfig Config for line breaks (for H1 and H2)
|
||||||
|
*/
|
||||||
|
data class HeadingConfig @JvmOverloads constructor(
|
||||||
|
val h1Config: HeadingTypeConfig = HeadingTypeConfig(),
|
||||||
|
val h2Config: HeadingTypeConfig = HeadingTypeConfig(),
|
||||||
|
val h3Config: HeadingTypeConfig = HeadingTypeConfig(),
|
||||||
|
val h4Config: HeadingTypeConfig = HeadingTypeConfig(),
|
||||||
|
val h5Config: HeadingTypeConfig = HeadingTypeConfig(),
|
||||||
|
val h6Config: HeadingTypeConfig = HeadingTypeConfig(),
|
||||||
|
val headingBreakConfig: HeadingBreakConfig = HeadingBreakConfig()
|
||||||
|
) {
|
||||||
|
fun setDensityFactor(factor: Float) {
|
||||||
|
h1Config.densityFactor = factor
|
||||||
|
h2Config.densityFactor = factor
|
||||||
|
h3Config.densityFactor = factor
|
||||||
|
h4Config.densityFactor = factor
|
||||||
|
h5Config.densityFactor = factor
|
||||||
|
h6Config.densityFactor = factor
|
||||||
|
headingBreakConfig.densityFactor = factor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for given heading type (H1, H2, ..., H6)
|
||||||
|
*
|
||||||
|
* Can set text size, text color and font (typeface)
|
||||||
|
*
|
||||||
|
* @property textSize Text size for heading
|
||||||
|
* @property textColor Text color for heading
|
||||||
|
* @property typeface Text size for heading
|
||||||
|
*/
|
||||||
|
class HeadingTypeConfig @JvmOverloads constructor(
|
||||||
|
//Standard sizes available at #SpannableTheme.java:HEADING_SIZES
|
||||||
|
textSize: Float = -1F,
|
||||||
|
|
||||||
|
@ColorInt val textColor: Int = -1,
|
||||||
|
|
||||||
|
val typeface: Typeface? = null
|
||||||
|
) {
|
||||||
|
internal var densityFactor: Float = -1F
|
||||||
|
|
||||||
|
val textSize: Float = textSize
|
||||||
|
get() = field * densityFactor
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for given heading type (H1, H2, ..., H6)
|
||||||
|
*
|
||||||
|
* Can set text size, text color and font (typeface)
|
||||||
|
*
|
||||||
|
* @property headingBreakStrokeWidth Stroke width for heading's line break
|
||||||
|
* @property headingBreakColor Color for heading's line break
|
||||||
|
*/
|
||||||
|
class HeadingBreakConfig @JvmOverloads constructor(
|
||||||
|
// by default paint.getStrokeWidth
|
||||||
|
@Dimension headingBreakStrokeWidth: Int = -1,
|
||||||
|
|
||||||
|
// by default, text color with `HEADING_DEF_BREAK_COLOR_ALPHA` applied alpha
|
||||||
|
@ColorInt val headingBreakColor: Int = 0
|
||||||
|
) {
|
||||||
|
internal var densityFactor: Float = -1F
|
||||||
|
|
||||||
|
val headingBreakStrokeWidth: Float = headingBreakStrokeWidth.toFloat()
|
||||||
|
get() = field * densityFactor
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package ru.noties.markwon.spans.heading;
|
||||||
|
|
||||||
|
import android.support.annotation.IntDef;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import static ru.noties.markwon.spans.heading.HeadingType.H1;
|
||||||
|
import static ru.noties.markwon.spans.heading.HeadingType.H2;
|
||||||
|
import static ru.noties.markwon.spans.heading.HeadingType.H3;
|
||||||
|
import static ru.noties.markwon.spans.heading.HeadingType.H4;
|
||||||
|
import static ru.noties.markwon.spans.heading.HeadingType.H5;
|
||||||
|
import static ru.noties.markwon.spans.heading.HeadingType.H6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by daniel.leal on 13.12.17.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef({H1, H2, H3, H4, H5, H6})
|
||||||
|
public @interface HeadingType {
|
||||||
|
int H1 = 1;
|
||||||
|
int H2 = 2;
|
||||||
|
int H3 = 3;
|
||||||
|
int H4 = 4;
|
||||||
|
int H5 = 5;
|
||||||
|
int H6 = 6;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user