convert to kotlin

This commit is contained in:
cgspine
2021-01-20 22:17:34 +08:00
parent fa4f9a3f05
commit 1744053908
19 changed files with 1214 additions and 1647 deletions
+2 -1
View File
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.4.10'
ext.kotlin_version = '1.4.21'
repositories {
mavenLocal()
google()
@@ -30,6 +30,7 @@ allprojects {
minSdkVersion = 19
targetSdkVersion = 29
compileSdkVersion = 29
coreVersion='1.5.0-beta01'
appcompatVersion= '1.3.0-alpha01'
materialVersion='1.2.1'
annotationVersion='1.1.0-beta01'
+3 -1
View File
@@ -1,4 +1,5 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
version = QMUI_TYPE_VERSION
@@ -25,7 +26,8 @@ android {
dependencies {
api "androidx.annotation:annotation:$annotationVersion"
api "androidx.core:core:$appcompatVersion"
api "androidx.core:core-ktx:$appcompatVersion"
api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
// deploy
@@ -13,9 +13,8 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type
package com.qmuiteam.qmui.type;
public interface EnvironmentUpdater {
void update(TypeEnvironment env);
}
interface EnvironmentUpdater {
fun update(env: TypeEnvironment)
}
+192 -232
View File
@@ -13,293 +13,253 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type
package com.qmuiteam.qmui.type;
import android.graphics.Canvas
import androidx.core.util.Pools
import com.qmuiteam.qmui.type.TypeEnvironment
import com.qmuiteam.qmui.type.element.BreakWordLineElement
import com.qmuiteam.qmui.type.element.Element
import com.qmuiteam.qmui.type.element.NextParagraphElement
import com.qmuiteam.qmui.type.element.TextElement
import java.util.*
import android.graphics.Canvas;
class Line private constructor() {
companion object {
private val sLinePool: Pools.Pool<Line> = Pools.SimplePool(16)
import androidx.core.util.Pools;
import com.qmuiteam.qmui.type.element.BreakWordLineElement;
import com.qmuiteam.qmui.type.element.CharOrPhraseElement;
import com.qmuiteam.qmui.type.element.Element;
import com.qmuiteam.qmui.type.element.NextParagraphElement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
public class Line {
private static Pools.Pool<Line> sLinePool = new Pools.SimplePool<>(16);
public static Line acquire() {
Line line = sLinePool.acquire();
if (line == null) {
line = new Line();
}
return line;
}
private Line() {
}
private int mX;
private int mY;
private int mWidthLimit;
private int mContentWidth;
private int mContentHeight;
private int mLayoutWidth;
private List<Element> mElements = new LinkedList<>();
private HashMap<Element, Integer> mVisibleChanged;
public void init(int x, int y, int widthLimit) {
mX = x;
mY = y;
mWidthLimit = widthLimit;
}
public void add(Element element) {
mElements.add(element);
mContentWidth += element.getMeasureWidth();
mContentHeight = (int) Math.max(mContentHeight, element.getMeasureHeight());
}
public void addFirst(Element element){
mElements.add(0, element);
mContentWidth += element.getMeasureWidth();
mContentHeight = (int) Math.max(mContentHeight, element.getMeasureHeight());
}
public Element first(){
return mElements.isEmpty() ? null : mElements.get(0);
}
public int getSize() {
return mElements.size();
}
public int getContentWidth() {
return mContentWidth;
}
public int getLayoutWidth() {
return mLayoutWidth;
}
public int getWidthLimit() {
return mWidthLimit;
}
public int getContentHeight() {
return mContentHeight;
}
public int getX() {
return mX;
}
public int getY() {
return mY;
}
public void setX(int x) {
mX = x;
}
public void setY(int y) {
mY = y;
}
void move(TypeEnvironment environment){
for(Element el: mElements){
el.move(environment);
}
}
public List<Element> handleWordBreak(TypeEnvironment environment) {
if (mElements.size() == 0) {
return null;
}
int lastIndex = mElements.size() - 1;
Element last = mElements.get(lastIndex);
Element next = last.getNext();
List<Element> back = new LinkedList<>();
if(last.getWordPart() == Element.WORD_PART_WHOLE){
if(last.getLineBreakType() == Element.LINE_BREAK_TYPE_NOT_END ||
(next != null && next.getLineBreakType() == Element.LINE_BREAK_TYPE_NOT_START)){
mElements.remove(lastIndex);
back.add(last);
fun acquire(): Line {
var line = sLinePool.acquire()
if (line == null) {
line = Line()
}
}else if(last.getWordPart() == Element.WORD_PART_END && (next != null && next.getLineBreakType() != Element.LINE_BREAK_TYPE_NOT_START)){
return line
}
}
var x = 0
var y = 0
var widthLimit = 0
private set
var contentWidth = 0
private set
var contentHeight = 0
private set
var layoutWidth = 0
private set
private val mElements = LinkedList<Element>()
private var mVisibleChanged: HashMap<Element, Int>? = null
val size: Int
get() = mElements.size
fun init(x: Int, y: Int, widthLimit: Int) {
this.x = x
this.y = y
this.widthLimit = widthLimit
}
fun add(element: Element) {
mElements.add(element)
contentWidth = (contentWidth + element.measureWidth).toInt()
contentHeight = contentHeight.toFloat().coerceAtLeast(element.measureHeight).toInt()
}
fun addFirst(element: Element) {
mElements.add(0, element)
contentWidth = (contentWidth + element.measureWidth).toInt()
contentHeight = contentHeight.toFloat().coerceAtLeast(element.measureHeight).toInt()
}
fun first(): Element? {
return if (mElements.isEmpty()) null else mElements[0]
}
fun move(environment: TypeEnvironment?) {
for (el in mElements) {
el.move(environment!!)
}
}
fun handleWordBreak(environment: TypeEnvironment?): List<Element>? {
if (mElements.size == 0) {
return null
}
var lastIndex = mElements.size - 1
val last = mElements[lastIndex]
val next = last.next
val back: MutableList<Element> = LinkedList()
if (last.wordPart == Element.WORD_PART_WHOLE) {
if (last.lineBreakType == Element.LINE_BREAK_TYPE_NOT_END ||
next != null && next.lineBreakType == Element.LINE_BREAK_TYPE_NOT_START) {
mElements.removeAt(lastIndex)
back.add(last)
}
} else if (last.wordPart == Element.WORD_PART_END && next != null && next.lineBreakType != Element.LINE_BREAK_TYPE_NOT_START) {
// do nothing
}else if(last.getWordPart() == Element.WORD_PART_START){
mElements.remove(lastIndex);
back.add(last);
}else{
back.add(last);
mElements.remove(lastIndex);
lastIndex--;
int min = Math.max(0, lastIndex - 30); // try 30 letter.
boolean find = false;
} else if (last.wordPart == Element.WORD_PART_START) {
mElements.removeAt(lastIndex)
back.add(last)
} else {
back.add(last)
mElements.removeAt(lastIndex)
lastIndex--
val min = Math.max(0, lastIndex - 30) // try 30 letter.
var find = false
while (lastIndex > min) {
Element el = mElements.get(lastIndex);
if (el.getWordPart() == Element.WORD_PART_WHOLE || el.getWordPart() == Element.WORD_PART_END) {
find = true;
break;
} else if (el.getLineBreakType() == Element.LINE_BREAK_WORD_BREAK_ALLOWED) {
val el = mElements[lastIndex]
if (el.wordPart == Element.WORD_PART_WHOLE || el.wordPart == Element.WORD_PART_END) {
find = true
break
} else if (el.lineBreakType == Element.LINE_BREAK_WORD_BREAK_ALLOWED) {
// TODO what if environment had changed after break? the measure may be wrong
BreakWordLineElement b = new BreakWordLineElement();
b.measure(environment);
add(b);
find = true;
break;
val b = BreakWordLineElement()
b.measure(environment!!)
add(b)
find = true
break
} else {
back.add(0, el);
mElements.remove(lastIndex);
lastIndex--;
back.add(0, el)
mElements.removeAt(lastIndex)
lastIndex--
}
}
if (!find) {
// give up
mElements.addAll(back);
return null;
mElements.addAll(back)
return null
}
}
if (back.isEmpty()) {
return null;
return null
}
for (Element el : back) {
mContentWidth -= el.getMeasureWidth();
for (el in back) {
contentWidth = (contentWidth -el.measureWidth).toInt()
}
return back;
return back
}
private boolean hideLastIfSpaceIfNeeded(boolean dropLastIfSpace) {
Element last = mElements.get(mElements.size() - 1);
if (dropLastIfSpace && last instanceof CharOrPhraseElement && last.getChar() == ' ' && last.getVisible() != Element.GONE) {
changeVisibleInner(last, Element.GONE);
mContentWidth -= last.getMeasureWidth();
return true;
private fun hideLastIfSpaceIfNeeded(dropLastIfSpace: Boolean): Boolean {
val last = mElements[mElements.size - 1]
if (dropLastIfSpace && last is TextElement && last.length == 1 && last.text[0] == ' ' && last.visible != Element.GONE) {
changeVisibleInner(last, Element.GONE)
contentWidth = (contentWidth - last.measureWidth).toInt()
return true
}
return false;
return false
}
private void changeVisibleInner(Element element, int visible) {
int oldVal = element.getVisible();
private fun changeVisibleInner(element: Element, visible: Int) {
val oldVal = element.visible
if (visible == oldVal) {
return;
return
}
if (mVisibleChanged == null) {
mVisibleChanged = new HashMap<>();
mVisibleChanged = HashMap()
}
mVisibleChanged.put(element, oldVal);
element.setVisible(visible);
mVisibleChanged!![element] = oldVal
element.visible = visible
}
private int calculateGapCount() {
int ret = 0;
for (int i = 1; i < mElements.size(); i++) {
Element el = mElements.get(i);
if (el.getVisible() != Element.GONE &&
(el.getWordPart() == Element.WORD_PART_WHOLE ||
el.getWordPart() == Element.WORD_PART_START)) {
ret++;
private fun calculateGapCount(): Int {
var ret = 0
for (i in 1 until mElements.size) {
val el = mElements[i]
if (el.visible != Element.GONE &&
(el.wordPart == Element.WORD_PART_WHOLE ||
el.wordPart == Element.WORD_PART_START)) {
ret++
}
}
return ret;
return ret
}
public boolean isMiddleParagraphEndLine(){
return !mElements.isEmpty() && mElements.get(mElements.size() - 1) instanceof NextParagraphElement;
}
val isMiddleParagraphEndLine: Boolean
get() = !mElements.isEmpty() && mElements[mElements.size - 1] is NextParagraphElement
public void layout(TypeEnvironment env, boolean dropLastIfSpace, boolean isEnd) {
fun layout(env: TypeEnvironment, dropLastIfSpace: Boolean, isEnd: Boolean) {
if (mElements.isEmpty()) {
return;
return
}
hideLastIfSpaceIfNeeded(dropLastIfSpace);
mLayoutWidth = mContentWidth;
TypeEnvironment.Alignment alignment = env.getAlignment();
float start = mX;
float addSpace = 0;
if (alignment == TypeEnvironment.Alignment.RIGHT) {
start = mX + mWidthLimit - mContentWidth;
} else if (alignment == TypeEnvironment.Alignment.CENTER) {
start = mX + (mWidthLimit - mContentWidth) / 2f;
} else if (alignment == TypeEnvironment.Alignment.JUSTIFY) {
float remain = mWidthLimit - mContentWidth;
if (!(isEnd || isMiddleParagraphEndLine()) || remain < env.getLastLineJustifyMaxWidth()) {
int gapCount = calculateGapCount();
hideLastIfSpaceIfNeeded(dropLastIfSpace)
layoutWidth = contentWidth
val alignment = env.alignment
var start = x.toFloat()
var addSpace = 0f
if (alignment === TypeEnvironment.Alignment.RIGHT) {
start = (x + widthLimit - contentWidth).toFloat()
} else if (alignment === TypeEnvironment.Alignment.CENTER) {
start = x + (widthLimit - contentWidth) / 2f
} else if (alignment === TypeEnvironment.Alignment.JUSTIFY) {
val remain = (widthLimit - contentWidth).toFloat()
if (!(isEnd || isMiddleParagraphEndLine) || remain < env.lastLineJustifyMaxWidth) {
val gapCount = calculateGapCount()
if (gapCount > 0) {
addSpace = remain / gapCount;
mLayoutWidth = mWidthLimit;
addSpace = remain / gapCount
layoutWidth = widthLimit
}
}
}
float x = start;
for (int i = 0; i < mElements.size(); i++) {
Element el = mElements.get(i);
if (i > 0 && (el.getWordPart() == Element.WORD_PART_WHOLE
|| el.getWordPart() == Element.WORD_PART_START)) {
x += addSpace;
mElements.get(i - 1).setNextGapWidth(addSpace);
var x = start
for (i in mElements.indices) {
val el = mElements[i]
if (i > 0 && (el.wordPart == Element.WORD_PART_WHOLE
|| el.wordPart == Element.WORD_PART_START)) {
x += addSpace
mElements[i - 1].nextGapWidth = addSpace
}
el.setX((int) x);
x += el.getMeasureWidth();
el.setY(mY + (mContentHeight - el.getMeasureHeight()) / 2f);
el.x = x
x += el.measureWidth
el.y = y + (contentHeight - el.measureHeight) / 2f
}
}
public void draw(TypeEnvironment env, Canvas canvas) {
for (Element element : mElements) {
element.draw(env, canvas);
fun draw(env: TypeEnvironment, canvas: Canvas) {
for (element in mElements) {
element.draw(env, canvas)
}
}
void restoreVisibleChange(){
fun restoreVisibleChange() {
if (mVisibleChanged != null) {
for (Element entry : mVisibleChanged.keySet()) {
Integer visible = mVisibleChanged.get(entry);
for (entry in mVisibleChanged!!.keys) {
val visible = mVisibleChanged!![entry]
if (visible != null) {
entry.setVisible(visible);
entry.visible = visible
}
}
mVisibleChanged.clear();
mVisibleChanged!!.clear()
}
}
public List<Element> popAll(){
List<Element> elements = new ArrayList<>(mElements);
mElements.clear();
restoreVisibleChange();
mContentWidth = 0;
mContentHeight = 0;
mLayoutWidth = 0;
return elements;
fun popAll(): List<Element> {
val elements: List<Element> = ArrayList(mElements)
mElements.clear()
restoreVisibleChange()
contentWidth = 0
contentHeight = 0
layoutWidth = 0
return elements
}
public void clear(){
mElements.clear();
restoreVisibleChange();
mContentWidth = 0;
mContentHeight = 0;
mLayoutWidth = 0;
fun clear() {
mElements.clear()
restoreVisibleChange()
contentWidth = 0
contentHeight = 0
layoutWidth = 0
}
public void release() {
mX = 0;
mY = 0;
mWidthLimit = 0;
mContentWidth = 0;
mContentHeight = 0;
mLayoutWidth = 0;
mElements.clear();
restoreVisibleChange();
sLinePool.release(this);
fun release() {
x = 0
y = 0
widthLimit = 0
contentWidth = 0
contentHeight = 0
layoutWidth = 0
mElements.clear()
restoreVisibleChange()
sLinePool.release(this)
}
}
}
@@ -13,459 +13,425 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type
package com.qmuiteam.qmui.type;
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Typeface
import android.text.TextUtils.TruncateAt
import com.qmuiteam.qmui.type.element.*
import java.util.*
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Typeface;
import android.text.TextUtils;
class LineLayout(private val mTypeEnvironment: TypeEnvironment) {
private var mMaxLines = Int.MAX_VALUE
private var mEllipsize: TruncateAt? = null
private var mCalculateWholeLines = true
private val mLines: MutableList<Line> = ArrayList()
private var mDropLastIfSpace = true
private var mMoreText: String? = null
private var mMoreTextColor = 0
private var mMoreTextTypeface: Typeface? = null
private var mMoreUnderlineColor = Color.TRANSPARENT
private var mMoreBgColor = 0
private var mMoreUnderlineHeight = 0
private var mTotalLineCount = 0
import com.qmuiteam.qmui.type.element.BreakWordLineElement;
import com.qmuiteam.qmui.type.element.CharOrPhraseElement;
import com.qmuiteam.qmui.type.element.IgnoreEffectElement;
import com.qmuiteam.qmui.type.element.Element;
import com.qmuiteam.qmui.type.element.NextParagraphElement;
var typeModel: TypeModel? = null
private set
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class LineLayout {
private int mMaxLines = Integer.MAX_VALUE;
private TextUtils.TruncateAt mEllipsize;
private boolean mCalculateWholeLines = true;
private TypeEnvironment mTypeEnvironment;
private TypeModel mTypeModel;
private List<Line> mLines = new ArrayList<>();
private boolean mDropLastIfSpace = true;
private String mMoreText = null;
private int mMoreTextColor = 0;
private Typeface mMoreTextTypeface = null;
private int mMoreUnderlineColor = Color.TRANSPARENT;
private int mMoreBgColor = 0;
private int mMoreUnderlineHeight = 0;
private int mTotalLineCount = 0;
public LineLayout(TypeEnvironment environment) {
mTypeEnvironment = environment;
fun setMaxLines(maxLines: Int): LineLayout {
mMaxLines = maxLines
return this
}
public LineLayout setMaxLines(int maxLines) {
mMaxLines = maxLines;
return this;
fun setEllipsize(ellipsize: TruncateAt?): LineLayout {
mEllipsize = ellipsize
return this
}
public LineLayout setEllipsize(TextUtils.TruncateAt ellipsize) {
mEllipsize = ellipsize;
return this;
fun setCalculateWholeLines(calculateWholeLines: Boolean): LineLayout {
mCalculateWholeLines = calculateWholeLines
return this
}
public LineLayout setCalculateWholeLines(boolean calculateWholeLines) {
mCalculateWholeLines = calculateWholeLines;
return this;
fun setDropLastIfSpace(dropLastIfSpace: Boolean): LineLayout {
mDropLastIfSpace = dropLastIfSpace
return this
}
public LineLayout setDropLastIfSpace(boolean dropLastIfSpace) {
mDropLastIfSpace = dropLastIfSpace;
return this;
fun setMoreText(text: String?, color: Int, typeface: Typeface?): LineLayout {
mMoreText = text
mMoreTextColor = color
mMoreTextTypeface = typeface
return this
}
public LineLayout setMoreText(String text, int color, Typeface typeface) {
mMoreText = text;
mMoreTextColor = color;
mMoreTextTypeface = typeface;
return this;
fun setMoreBackgroundColor(color: Int): LineLayout {
mMoreBgColor = color
return this
}
public LineLayout setMoreBackgroundColor(int color) {
mMoreBgColor = color;
return this;
fun setUnderline(height: Int, color: Int): LineLayout {
mMoreUnderlineHeight = height
mMoreUnderlineColor = color
return this
}
public LineLayout setUnderline(int height, int color) {
mMoreUnderlineHeight = height;
mMoreUnderlineColor = color;
return this;
fun setTypeModel(typeModel: TypeModel?): LineLayout {
this.typeModel = typeModel
return this
}
public LineLayout setTypeModel(TypeModel typeModel) {
mTypeModel = typeModel;
return this;
}
public TypeModel getTypeModel() {
return mTypeModel;
}
public void measureAndLayout() {
mTypeEnvironment.clear();
release();
if (mTypeModel == null) {
return;
fun measureAndLayout() {
mTypeEnvironment.clear()
release()
if (typeModel == null) {
return
}
Element element = mTypeModel.firstElement();
if (element == null) {
return;
}
Line line = Line.acquire();
int y = 0;
line.init(0, y, mTypeEnvironment.getWidthLimit());
var element: Element? = typeModel!!.firstElement() ?: return
var line = Line.acquire()
var y = 0
line.init(0, y, mTypeEnvironment.widthLimit)
while (element != null) {
element.measure(mTypeEnvironment);
if (element instanceof NextParagraphElement) {
line.add(element);
line.layout(mTypeEnvironment, mDropLastIfSpace, false);
mLines.add(line);
element.measure(mTypeEnvironment)
if (element is NextParagraphElement) {
line.add(element)
line.layout(mTypeEnvironment, mDropLastIfSpace, false)
mLines.add(line)
if (canInterrupt()) {
return;
return
}
y += line.getContentHeight() + mTypeEnvironment.getParagraphSpace();
line = createNewLine(y);
} else if (line.getContentWidth() + element.getMeasureWidth() > mTypeEnvironment.getWidthLimit()) {
if (mLines.size() == 0 && line.getSize() == 0) {
y += line.contentHeight + mTypeEnvironment.paragraphSpace
line = createNewLine(y)
} else if (line.contentWidth + element.measureWidth > mTypeEnvironment.widthLimit) {
if (mLines.size == 0 && line.size == 0) {
// the width is too small.
line.release();
return;
line.release()
return
}
List<Element> back = line.handleWordBreak(mTypeEnvironment);
line.layout(mTypeEnvironment, mDropLastIfSpace, false);
mLines.add(line);
val back = line.handleWordBreak(mTypeEnvironment)
line.layout(mTypeEnvironment, mDropLastIfSpace, false)
mLines.add(line)
if (canInterrupt()) {
handleEllipse(true);
return;
handleEllipse(true)
return
}
y += line.getContentHeight() + mTypeEnvironment.getLineSpace();
line = createNewLine(y);
y += line.contentHeight + mTypeEnvironment.lineSpace
line = createNewLine(y)
if (back != null && !back.isEmpty()) {
for (Element el : back) {
line.add(el);
for (el in back) {
line.add(el)
}
}
line.add(element);
line.add(element)
} else {
line.add(element);
line.add(element)
}
element = element.getNext();
element = element.next
}
if (line.getSize() > 0) {
line.layout(mTypeEnvironment, mDropLastIfSpace, true);
mLines.add(line);
if (line.size > 0) {
line.layout(mTypeEnvironment, mDropLastIfSpace, true)
mLines.add(line)
} else {
line.release();
line.release()
}
mTotalLineCount = mLines.size();
handleEllipse(false);
mTotalLineCount = mLines.size
handleEllipse(false)
}
private Line createNewLine(int y) {
Line line = Line.acquire();
line.init(0, y, mTypeEnvironment.getWidthLimit());
return line;
private fun createNewLine(y: Int): Line {
val line = Line.acquire()
line.init(0, y, mTypeEnvironment.widthLimit)
return line
}
private void handleEllipse(boolean fromInterrupt) {
if (mLines.isEmpty() || mLines.size() < mMaxLines || (mLines.size() == mMaxLines && !fromInterrupt)) {
return;
private fun handleEllipse(fromInterrupt: Boolean) {
if (mLines.isEmpty() || mLines.size < mMaxLines || mLines.size == mMaxLines && !fromInterrupt) {
return
}
if (mEllipsize == TextUtils.TruncateAt.END) {
handleEllipseEnd();
} else if (mEllipsize == TextUtils.TruncateAt.START) {
handleEllipseStart();
} else if (mEllipsize == TextUtils.TruncateAt.MIDDLE) {
handleEllipseMiddle();
if (mEllipsize == TruncateAt.END) {
handleEllipseEnd()
} else if (mEllipsize == TruncateAt.START) {
handleEllipseStart()
} else if (mEllipsize == TruncateAt.MIDDLE) {
handleEllipseMiddle()
}
}
private void handleEllipseEnd() {
for (int i = mLines.size() - 1; i >= mMaxLines; i--) {
mLines.remove(mLines.get(i));
private fun handleEllipseEnd() {
for (i in mLines.size - 1 downTo mMaxLines) {
mLines.remove(mLines[i])
}
Line lastLine = mLines.get(mLines.size() - 1);
int limitWidth = lastLine.getWidthLimit();
Element ellipseElement = new CharOrPhraseElement("...", -1, -1);
ellipseElement.addSingleEnvironmentUpdater(null, new EnvironmentUpdater() {
@Override
public void update(TypeEnvironment env) {
env.clear();
val lastLine = mLines[mLines.size - 1]
var limitWidth = lastLine.widthLimit
val ellipseElement: Element = TextElement("...", -1, -1)
ellipseElement.addSingleEnvironmentUpdater(null, object : EnvironmentUpdater {
override fun update(env: TypeEnvironment) {
env.clear()
}
});
ellipseElement.measure(mTypeEnvironment);
limitWidth -= ellipseElement.getMeasureWidth();
Element moreElement = null;
if (mMoreText != null && !mMoreText.isEmpty()) {
moreElement = new CharOrPhraseElement(mMoreText, -1, -1);
List<Integer> changeTypes = new ArrayList<>();
changeTypes.add(TypeEnvironment.TYPE_TEXT_COLOR);
changeTypes.add(TypeEnvironment.TYPE_BG_COLOR);
changeTypes.add(TypeEnvironment.TYPE_TYPEFACE);
changeTypes.add(TypeEnvironment.TYPE_BORDER_BOTTOM_COLOR);
changeTypes.add(TypeEnvironment.TYPE_BORDER_BOTTOM_WIDTH);
moreElement.addSingleEnvironmentUpdater(changeTypes, new EnvironmentUpdater() {
@Override
public void update(TypeEnvironment env) {
})
ellipseElement.measure(mTypeEnvironment)
limitWidth = (limitWidth - ellipseElement.measureWidth).toInt()
var moreElement: Element? = null
if (mMoreText != null && !mMoreText!!.isEmpty()) {
moreElement = TextElement(mMoreText!!, -1, -1)
val changeTypes: MutableList<Int> = ArrayList()
changeTypes.add(TypeEnvironment.TYPE_TEXT_COLOR)
changeTypes.add(TypeEnvironment.TYPE_BG_COLOR)
changeTypes.add(TypeEnvironment.TYPE_TYPEFACE)
changeTypes.add(TypeEnvironment.TYPE_BORDER_BOTTOM_COLOR)
changeTypes.add(TypeEnvironment.TYPE_BORDER_BOTTOM_WIDTH)
moreElement.addSingleEnvironmentUpdater(changeTypes, object : EnvironmentUpdater {
override fun update(env: TypeEnvironment) {
if (mMoreTextColor != 0) {
env.setTextColor(mMoreTextColor);
env.textColor = mMoreTextColor
}
if (mMoreBgColor != 0) {
env.setBackgroundColor(mMoreBgColor);
env.backgroundColor = mMoreBgColor
}
if (mMoreTextTypeface != null) {
env.setTypeface(mMoreTextTypeface);
env.typeface = mMoreTextTypeface
}
if (mMoreUnderlineHeight > 0) {
env.setBorderBottom(mMoreUnderlineHeight, mMoreUnderlineColor);
env.setBorderBottom(mMoreUnderlineHeight, mMoreUnderlineColor)
}
}
});
moreElement.measure(mTypeEnvironment);
limitWidth -= moreElement.getMeasureWidth();
})
moreElement.measure(mTypeEnvironment)
limitWidth = (limitWidth - moreElement.measureWidth).toInt()
}
int contentWidth = lastLine.getContentWidth();
val contentWidth = lastLine.contentWidth
if (contentWidth < limitWidth) {
lastLine.restoreVisibleChange();
lastLine.restoreVisibleChange()
} else {
List<Element> elements = lastLine.popAll();
for (Element el : elements) {
if (el.getMeasureWidth() <= limitWidth) {
lastLine.add(el);
limitWidth -= el.getMeasureWidth();
val elements = lastLine.popAll()
for (el in elements) {
if (el.measureWidth <= limitWidth) {
lastLine.add(el)
limitWidth -= (limitWidth - el.measureWidth).toInt()
} else {
break;
break
}
}
}
lastLine.add(ellipseElement);
lastLine.add(ellipseElement)
if (moreElement != null) {
lastLine.add(moreElement);
lastLine.add(moreElement)
}
lastLine.layout(mTypeEnvironment, mDropLastIfSpace, true);
lastLine.layout(mTypeEnvironment, mDropLastIfSpace, true)
}
private void handleEllipseStart() {
mTypeEnvironment.clear();
for (int i = mLines.size() - 1; i >= mMaxLines; i--) {
mLines.remove(mLines.get(i));
private fun handleEllipseStart() {
mTypeEnvironment.clear()
for (i in mLines.size - 1 downTo mMaxLines) {
mLines.remove(mLines[i])
}
Element ellipseElement = new CharOrPhraseElement("...", -1, -1);
ellipseElement.addSingleEnvironmentUpdater(null, new EnvironmentUpdater() {
@Override
public void update(TypeEnvironment env) {
env.clear();
val ellipseElement: Element = TextElement("...", -1, -1)
ellipseElement.addSingleEnvironmentUpdater(null, object : EnvironmentUpdater {
override fun update(env: TypeEnvironment) {
env.clear()
}
});
ellipseElement.measure(mTypeEnvironment);
Queue<Element> elements = new LinkedList<>();
elements.add(ellipseElement);
for (int i = 0; i < mLines.size(); i++) {
Line line = mLines.get(i);
int limitWidth = line.getWidthLimit();
elements.addAll(line.popAll());
})
ellipseElement.measure(mTypeEnvironment)
val elements: Queue<Element> = LinkedList()
elements.add(ellipseElement)
for (i in mLines.indices) {
val line = mLines[i]
val limitWidth = line.widthLimit
elements.addAll(line.popAll())
while (!elements.isEmpty()) {
Element el = elements.peek();
val el = elements.peek()
if (el != null) {
if (el instanceof NextParagraphElement) {
elements.poll();
line.add(el);
el.move(mTypeEnvironment);
break;
if (el is NextParagraphElement) {
elements.poll()
line.add(el)
el.move(mTypeEnvironment)
break
}
if (el instanceof BreakWordLineElement) {
elements.poll();
continue;
if (el is BreakWordLineElement) {
elements.poll()
continue
}
if (line.getContentWidth() + el.getMeasureWidth() <= limitWidth) {
elements.poll();
line.add(el);
el.move(mTypeEnvironment);
if (line.contentWidth + el.measureWidth <= limitWidth) {
elements.poll()
line.add(el)
el.move(mTypeEnvironment)
} else {
break;
break
}
} else {
elements.poll();
elements.poll()
}
}
line.handleWordBreak(mTypeEnvironment);
line.layout(mTypeEnvironment, mDropLastIfSpace, false);
line.handleWordBreak(mTypeEnvironment)
line.layout(mTypeEnvironment, mDropLastIfSpace, false)
if (elements.isEmpty()) {
return;
return
}
}
}
private void handleEllipseMiddle() {
mTypeEnvironment.clear();
List<Line> lines = new ArrayList<>(mLines);
mLines.clear();
Element ellipseElement = new CharOrPhraseElement("...", -1, -1);
ellipseElement.measure(mTypeEnvironment);
int ellipseLine = mMaxLines % 2 == 0 ? mMaxLines / 2 : (mMaxLines + 1) / 2;
for (int i = 0; i < ellipseLine; i++) {
mLines.add(lines.get(i));
private fun handleEllipseMiddle() {
mTypeEnvironment.clear()
val lines: List<Line> = ArrayList(mLines)
mLines.clear()
val ellipseElement: Element = TextElement("...", -1, -1)
ellipseElement.measure(mTypeEnvironment)
val ellipseLine = if (mMaxLines % 2 == 0) mMaxLines / 2 else (mMaxLines + 1) / 2
for (i in 0 until ellipseLine) {
mLines.add(lines[i])
}
Line handleLine = lines.get(ellipseLine - 1);
int limitWidth = handleLine.getWidthLimit();
Deque<Element> unHandled = new LinkedList<>(handleLine.popAll());
val handleLine = lines[ellipseLine - 1]
val limitWidth = handleLine.widthLimit
val unHandled: Deque<Element> = LinkedList(handleLine.popAll())
while (!unHandled.isEmpty()) {
Element el = unHandled.peek();
val el = unHandled.peek()
if (el != null) {
if (handleLine.getContentWidth() + el.getMeasureWidth() <= limitWidth / 2f - ellipseElement.getMeasureWidth() / 2) {
unHandled.poll();
handleLine.add(el);
el.move(mTypeEnvironment);
if (handleLine.contentWidth + el.measureWidth <= limitWidth / 2f - ellipseElement.measureWidth / 2) {
unHandled.poll()
handleLine.add(el)
el.move(mTypeEnvironment)
} else {
break;
break
}
} else {
unHandled.poll();
unHandled.poll()
}
}
ellipseElement.measure(mTypeEnvironment);
handleLine.add(ellipseElement);
int nextFullShowLine = lines.size() - mMaxLines + ellipseLine;
int startLine = lines.size() - 1;
ellipseElement.measure(mTypeEnvironment)
handleLine.add(ellipseElement)
val nextFullShowLine = lines.size - mMaxLines + ellipseLine
var startLine = lines.size - 1
// find the latest paragraph end line.
for (int i = lines.size() - 2; i > nextFullShowLine; i--) {
if (lines.get(i).isMiddleParagraphEndLine()) {
startLine = i;
for (i in lines.size - 2 downTo nextFullShowLine + 1) {
if (lines[i].isMiddleParagraphEndLine) {
startLine = i
}
}
for (int i = ellipseLine; i <= startLine; i++) {
unHandled.addAll(lines.get(i).popAll());
for (i in ellipseLine..startLine) {
unHandled.addAll(lines[i].popAll())
}
for (int i = startLine; i >= nextFullShowLine; i--) {
Line line = lines.get(i);
for (i in startLine downTo nextFullShowLine) {
val line = lines[i]
while (!unHandled.isEmpty()) {
Element element = unHandled.peekLast();
val element = unHandled.peekLast()
if (element != null) {
if (element instanceof NextParagraphElement) {
unHandled.pollLast();
continue;
if (element is NextParagraphElement) {
unHandled.pollLast()
continue
}
if (element instanceof BreakWordLineElement) {
unHandled.pollLast();
continue;
if (element is BreakWordLineElement) {
unHandled.pollLast()
continue
}
if (line.getContentWidth() + element.getMeasureWidth() <= line.getWidthLimit()) {
unHandled.pollLast();
line.addFirst(element);
if (line.contentWidth + element.measureWidth <= line.widthLimit) {
unHandled.pollLast()
line.addFirst(element)
} else {
break;
break
}
} else {
unHandled.pollLast();
unHandled.pollLast()
}
}
}
List<Element> toAdd = new LinkedList<>();
int toAddWidth = 0;
val toAdd = LinkedList<Element>()
var toAddWidth = 0
while (!unHandled.isEmpty()) {
Element element = unHandled.peekLast();
val element = unHandled.peekLast()
if (element != null) {
if (element instanceof NextParagraphElement) {
unHandled.pollLast();
continue;
if (element is NextParagraphElement) {
unHandled.pollLast()
continue
}
if (element instanceof BreakWordLineElement) {
unHandled.pollLast();
continue;
if (element is BreakWordLineElement) {
unHandled.pollLast()
continue
}
if (handleLine.getContentWidth() + toAddWidth + element.getMeasureWidth() <= handleLine.getWidthLimit()) {
unHandled.pollLast();
toAdd.add(0, element);
toAddWidth += element.getMeasureWidth();
if (handleLine.contentWidth + toAddWidth + element.measureWidth <= handleLine.widthLimit) {
unHandled.pollLast()
toAdd.add(0, element)
toAddWidth = (toAddWidth + element.measureWidth).toInt()
} else {
break;
break
}
} else {
unHandled.pollLast();
unHandled.pollLast()
}
}
Element firstUnHandle = unHandled.peekFirst();
Element lastUnHandle = unHandled.peekLast();
Element effect = mTypeModel.getFirstEffect();
val firstUnHandle = unHandled.peekFirst()
val lastUnHandle = unHandled.peekLast()
var effect = typeModel!!.firstEffect
if (firstUnHandle != null && lastUnHandle != null) {
List<Element> ellipseEffect = new ArrayList<>();
while (effect != null && effect.getIndex() <= lastUnHandle.getIndex()) {
if (effect.getIndex() >= firstUnHandle.getIndex()) {
ellipseEffect.add(effect);
val ellipseEffect: MutableList<Element> = ArrayList()
while (effect != null && effect.index <= lastUnHandle.index) {
if (effect.index >= firstUnHandle.index) {
ellipseEffect.add(effect)
}
effect = effect.getNext();
effect = effect.next
}
if (ellipseEffect.size() > 0) {
IgnoreEffectElement ignoreEffectElement = new IgnoreEffectElement(ellipseEffect);
ignoreEffectElement.move(mTypeEnvironment);
handleLine.add(ignoreEffectElement);
if (ellipseEffect.size > 0) {
val ignoreEffectElement = IgnoreEffectElement(ellipseEffect)
ignoreEffectElement.move(mTypeEnvironment)
handleLine.add(ignoreEffectElement)
}
}
for (Element el : toAdd) {
el.move(mTypeEnvironment);
handleLine.add(el);
for (el in toAdd) {
el.move(mTypeEnvironment)
handleLine.add(el)
}
handleLine.handleWordBreak(mTypeEnvironment);
handleLine.layout(mTypeEnvironment, mDropLastIfSpace, ellipseLine == lines.size());
int lastEnd = handleLine.getY() + handleLine.getContentHeight();
for (int i = nextFullShowLine; i < lines.size(); i++) {
Line line = lines.get(i);
Line prev = lines.get(i - 1);
if (prev.isMiddleParagraphEndLine()) {
line.setY(lastEnd + mTypeEnvironment.getParagraphSpace());
handleLine.handleWordBreak(mTypeEnvironment)
handleLine.layout(mTypeEnvironment, mDropLastIfSpace, ellipseLine == lines.size)
var lastEnd = handleLine.y + handleLine.contentHeight
for (i in nextFullShowLine until lines.size) {
val line = lines[i]
val prev = lines[i - 1]
if (prev.isMiddleParagraphEndLine) {
line.y = lastEnd + mTypeEnvironment.paragraphSpace
} else {
line.setY(lastEnd + mTypeEnvironment.getLineSpace());
line.y = lastEnd + mTypeEnvironment.lineSpace
}
lastEnd = line.getY() + line.getContentHeight();
line.move(mTypeEnvironment);
line.handleWordBreak(mTypeEnvironment);
line.layout(mTypeEnvironment, mDropLastIfSpace, i == lines.size() - 1);
mLines.add(line);
lastEnd = line.y + line.contentHeight
line.move(mTypeEnvironment)
line.handleWordBreak(mTypeEnvironment)
line.layout(mTypeEnvironment, mDropLastIfSpace, i == lines.size - 1)
mLines.add(line)
}
}
public int getMaxLayoutWidth() {
int maxWidth = 0;
for (Line line : mLines) {
maxWidth = Math.max(maxWidth, line.getLayoutWidth());
val maxLayoutWidth: Int
get() {
var maxWidth = 0
for (line in mLines) {
maxWidth = Math.max(maxWidth, line.layoutWidth)
}
return maxWidth
}
return maxWidth;
}
public int getContentHeight() {
if (mLines.isEmpty()) {
return 0;
val contentHeight: Int
get() {
if (mLines.isEmpty()) {
return 0
}
val last = mLines[mLines.size - 1]
return last.y + last.contentHeight
}
Line last = mLines.get(mLines.size() - 1);
return last.getY() + last.getContentHeight();
}
public void draw(Canvas canvas) {
mTypeEnvironment.clear();
for (Line line : mLines) {
line.draw(mTypeEnvironment, canvas);
fun draw(canvas: Canvas) {
mTypeEnvironment.clear()
for (line in mLines) {
line.draw(mTypeEnvironment, canvas)
}
}
private boolean canInterrupt() {
return mLines.size() == mMaxLines && !mCalculateWholeLines &&
(mEllipsize == null || mEllipsize == TextUtils.TruncateAt.END);
private fun canInterrupt(): Boolean {
return mLines.size == mMaxLines && !mCalculateWholeLines &&
(mEllipsize == null || mEllipsize == TruncateAt.END)
}
public void release() {
for (Line line : mLines) {
line.release();
fun release() {
for (line in mLines) {
line.release()
}
mLines.clear();
mLines.clear()
}
}
}
@@ -13,335 +13,248 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type
package com.qmuiteam.qmui.type;
import android.content.res.Resources
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Typeface
import android.util.Log
import android.util.SparseArray
import java.util.*
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.Log;
import android.util.SparseArray;
class TypeEnvironment {
companion object {
private const val TAG = "TypeEnvironment"
const val TYPE_TEXT_COLOR = -1
const val TYPE_BG_COLOR = -2
const val TYPE_TYPEFACE = -3
const val TYPE_TEXT_SIZE = -4
const val TYPE_ALIGNMENT = -5
const val TYPE_LINE_SPACE = -6
const val TYPE_PARAGRAPH_SPACE = -7
const val TYPE_BORDER_TOP_WIDTH = -8
const val TYPE_BORDER_TOP_COLOR = -9
const val TYPE_BORDER_RIGHT_WIDTH = -10
const val TYPE_BORDER_RIGHT_COLOR = -11
const val TYPE_BORDER_BOTTOM_WIDTH = -12
const val TYPE_BORDER_BOTTOM_COLOR = -13
const val TYPE_BORDER_LEFT_WIDTH = -14
const val TYPE_BORDER_LEFT_COLOR = -15
const val TYPE_BORDER_PAINT = -16
import com.qmuiteam.qmui.type.element.Element;
import java.util.Stack;
public class TypeEnvironment {
private static final String TAG = "TypeEnvironment";
public static final int TYPE_TEXT_COLOR = -1;
public static final int TYPE_BG_COLOR = -2;
public static final int TYPE_TYPEFACE = -3;
public static final int TYPE_TEXT_SIZE = -4;
public static final int TYPE_ALIGNMENT = -5;
public static final int TYPE_LINE_SPACE = -6;
public static final int TYPE_PARAGRAPH_SPACE = -7;
public static final int TYPE_BORDER_TOP_WIDTH = -8;
public static final int TYPE_BORDER_TOP_COLOR = -9;
public static final int TYPE_BORDER_RIGHT_WIDTH = -10;
public static final int TYPE_BORDER_RIGHT_COLOR = -11;
public static final int TYPE_BORDER_BOTTOM_WIDTH = -12;
public static final int TYPE_BORDER_BOTTOM_COLOR = -13;
public static final int TYPE_BORDER_LEFT_WIDTH = -14;
public static final int TYPE_BORDER_LEFT_COLOR = -15;
public static final int TYPE_BORDER_PAINT = -16;
public enum Alignment {
LEFT,
RIGHT,
CENTER,
JUSTIFY
val DEFAULT_LAST_LINE_JUSTIFY_MAX_WIDTH = (Resources.getSystem().displayMetrics.density * 36).toInt()
}
private int mWidthLimit;
private int mHeightLimit;
private Typeface mTypeface;
private float mTextSize;
private int mLineSpace;
private int mParagraphSpace;
private Alignment mAlignment = Alignment.JUSTIFY;
private int mLastLineJustifyMaxWidth = (int) (Resources.getSystem().getDisplayMetrics().density * 36);
private int mTextColor = Color.BLACK;
private int mBackgroundColor = Color.TRANSPARENT;
private Paint mPaint = new Paint();
private Paint mBgPaint = new Paint();
private SparseArray<Object> mCustomProp = new SparseArray<>();
private SparseArray<Stack<Object>> mStack = new SparseArray<>();
private Element mLastRunElement = null;
public TypeEnvironment() {
mPaint.setAntiAlias(true);
mBgPaint.setAntiAlias(true);
enum class Alignment {
LEFT, RIGHT, CENTER, JUSTIFY
}
var widthLimit = 0
private set
var heightLimit = 0
private set
public void setMeasureLimit(int widthLimit, int heightLimit) {
mWidthLimit = widthLimit;
mHeightLimit = heightLimit;
var alignment: Alignment = Alignment.JUSTIFY
var lastLineJustifyMaxWidth = DEFAULT_LAST_LINE_JUSTIFY_MAX_WIDTH
val paint = Paint().apply {
isAntiAlias = true
}
public int getWidthLimit() {
return mWidthLimit;
val bgPaint = Paint().apply {
isAntiAlias = true
}
private val mCustomProp: SparseArray<Any?> = SparseArray()
private val mStack = SparseArray<Stack<Any?>>()
public int getHeightLimit() {
return mHeightLimit;
}
public void setLastLineJustifyMaxWidth(int lastLineJustifyMaxWidth) {
mLastLineJustifyMaxWidth = lastLineJustifyMaxWidth;
}
var lineSpace = 0
public int getLastLineJustifyMaxWidth() {
return mLastLineJustifyMaxWidth;
}
var paragraphSpace: Int = 0
get() = field.coerceAtLeast(lineSpace)
public void setTypeface(Typeface typeface) {
mTypeface = typeface;
mPaint.setTypeface(typeface);
}
public void setTextColor(int textColor) {
mTextColor = textColor;
mPaint.setColor(textColor);
}
public void setTextSize(float textSize) {
mTextSize = textSize;
mPaint.setTextSize(textSize);
}
public void setBackgroundColor(int backgroundColor) {
mBackgroundColor = backgroundColor;
mBgPaint.setColor(backgroundColor);
}
public void setAlignment(Alignment alignment) {
mAlignment = alignment;
}
public void setLineSpace(int lineSpace) {
mLineSpace = lineSpace;
}
public void setParagraphSpace(int paragraphSpace) {
mParagraphSpace = paragraphSpace;
}
public int getParagraphSpace() {
return Math.max(mParagraphSpace, mLineSpace);
}
public Alignment getAlignment() {
return mAlignment;
}
public int getLineSpace() {
return mLineSpace;
}
public float getTextSize() {
return mTextSize;
}
public int getTextColor() {
return mTextColor;
}
public int getBackgroundColor() {
return mBackgroundColor;
}
public Paint getBgPaint() {
return mBgPaint;
}
public Paint getPaint() {
return mPaint;
}
public void setCustomProp(int type, Object value) {
mCustomProp.put(type, value);
}
public void setBorderTop(int width, int color) {
setCustomProp(TYPE_BORDER_TOP_WIDTH, width);
setCustomProp(TYPE_BORDER_TOP_COLOR, color);
}
public int getBorderTopWidth() {
return getIntCustomProp(TYPE_BORDER_TOP_WIDTH);
}
public int getBorderTopColor() {
return getIntCustomProp(TYPE_BORDER_TOP_COLOR);
}
public void setBorderRight(int width, int color) {
setCustomProp(TYPE_BORDER_RIGHT_WIDTH, width);
setCustomProp(TYPE_BORDER_RIGHT_COLOR, color);
}
public int getBorderRightWidth() {
return getIntCustomProp(TYPE_BORDER_RIGHT_WIDTH);
}
public int getBorderRightColor() {
return getIntCustomProp(TYPE_BORDER_RIGHT_COLOR);
}
public void setBorderBottom(int width, int color) {
setCustomProp(TYPE_BORDER_BOTTOM_WIDTH, width);
setCustomProp(TYPE_BORDER_BOTTOM_COLOR, color);
}
public int getBorderBottomWidth() {
return getIntCustomProp(TYPE_BORDER_BOTTOM_WIDTH);
}
public int getBorderBottomColor() {
return getIntCustomProp(TYPE_BORDER_BOTTOM_COLOR);
}
public void setBorderLeft(int width, int color) {
setCustomProp(TYPE_BORDER_LEFT_WIDTH, width);
setCustomProp(TYPE_BORDER_LEFT_COLOR, color);
}
public int getBorderLeftWidth() {
return getIntCustomProp(TYPE_BORDER_LEFT_WIDTH);
}
public int getBorderLeftColor() {
return getIntCustomProp(TYPE_BORDER_LEFT_COLOR);
}
public Paint getBorderPaint() {
Object obj = getCustomProp(TYPE_BORDER_PAINT);
Paint paint;
if (obj == null) {
paint = new Paint();
paint.setAntiAlias(true);
setCustomProp(TYPE_BORDER_PAINT, paint);
} else {
paint = (Paint) obj;
}
return paint;
}
public Object getCustomProp(int type) {
return mCustomProp.get(type);
}
public int getIntCustomProp(int type) {
Object obj = mCustomProp.get(type);
if (!(obj instanceof Integer)) {
return 0;
}
return (int) obj;
}
public TypeEnvironment snapshot() {
TypeEnvironment env = new TypeEnvironment();
env.setMeasureLimit(mWidthLimit, mHeightLimit);
env.setAlignment(mAlignment);
env.setLineSpace(mLineSpace);
env.setParagraphSpace(mParagraphSpace);
env.setTextSize(mTextSize);
env.setTypeface(mTypeface);
env.setTextColor(mTextColor);
env.setBackgroundColor(mBackgroundColor);
for(int i =0; i< mStack.size(); i++){
env.mStack.put(mStack.keyAt(i), (Stack<Object>) mStack.valueAt(i).clone());
var typeface: Typeface? = null
set(value) {
field = value
paint.typeface = value
}
var textSize: Float = 0f
set(value) {
field = value
paint.textSize = value
}
var textColor: Int = Color.BLACK
set(value) {
field = value
paint.color = value
}
var backgroundColor: Int = Color.TRANSPARENT
set(value) {
field = value
bgPaint.color = value
}
fun setCustomProp(type: Int, value: Any?) {
mCustomProp!!.put(type, value)
}
fun setBorderTop(width: Int, color: Int) {
setCustomProp(TYPE_BORDER_TOP_WIDTH, width)
setCustomProp(TYPE_BORDER_TOP_COLOR, color)
}
val borderTopWidth: Int
get() = getIntCustomProp(TYPE_BORDER_TOP_WIDTH)
val borderTopColor: Int
get() = getIntCustomProp(TYPE_BORDER_TOP_COLOR)
fun setBorderRight(width: Int, color: Int) {
setCustomProp(TYPE_BORDER_RIGHT_WIDTH, width)
setCustomProp(TYPE_BORDER_RIGHT_COLOR, color)
}
val borderRightWidth: Int
get() = getIntCustomProp(TYPE_BORDER_RIGHT_WIDTH)
val borderRightColor: Int
get() = getIntCustomProp(TYPE_BORDER_RIGHT_COLOR)
fun setBorderBottom(width: Int, color: Int) {
setCustomProp(TYPE_BORDER_BOTTOM_WIDTH, width)
setCustomProp(TYPE_BORDER_BOTTOM_COLOR, color)
}
val borderBottomWidth: Int
get() = getIntCustomProp(TYPE_BORDER_BOTTOM_WIDTH)
val borderBottomColor: Int
get() = getIntCustomProp(TYPE_BORDER_BOTTOM_COLOR)
fun setBorderLeft(width: Int, color: Int) {
setCustomProp(TYPE_BORDER_LEFT_WIDTH, width)
setCustomProp(TYPE_BORDER_LEFT_COLOR, color)
}
val borderLeftWidth: Int
get() = getIntCustomProp(TYPE_BORDER_LEFT_WIDTH)
val borderLeftColor: Int
get() = getIntCustomProp(TYPE_BORDER_LEFT_COLOR)
val borderPaint: Paint
get() {
val obj = getCustomProp(TYPE_BORDER_PAINT)
val paint: Paint
if (obj == null) {
paint = Paint()
paint.isAntiAlias = true
setCustomProp(TYPE_BORDER_PAINT, paint)
} else {
paint = obj as Paint
}
return paint
}
fun getCustomProp(type: Int): Any? {
return mCustomProp!![type]
}
fun getIntCustomProp(type: Int): Int {
val obj = mCustomProp!![type]
return if (obj !is Int) {
0
} else obj
}
fun setMeasureLimit(widthLimit: Int, heightLimit: Int) {
this.widthLimit = widthLimit
this.heightLimit = heightLimit
}
fun snapshot(): TypeEnvironment {
val env = TypeEnvironment()
env.setMeasureLimit(widthLimit, heightLimit)
env.alignment = alignment
env.lineSpace = lineSpace
env.paragraphSpace = paragraphSpace
env.textSize = textSize
env.typeface = typeface
env.textColor = textColor
env.backgroundColor = backgroundColor
for (i in 0 until mStack.size()) {
env.mStack.put(mStack.keyAt(i), mStack.valueAt(i).clone() as Stack<Any?>)
}
if (mCustomProp != null) {
for (int i = 0; i < mCustomProp.size(); i++) {
env.setCustomProp(mCustomProp.keyAt(i), mCustomProp.valueAt(i));
for (i in 0 until mCustomProp.size()) {
env.setCustomProp(mCustomProp.keyAt(i), mCustomProp.valueAt(i))
}
}
return env;
return env
}
void setLastRunElement(Element lastRunElement) {
mLastRunElement = lastRunElement;
}
Element getLastRunElement() {
return mLastRunElement;
}
public void save(int type) {
Stack<Object> stack = mStack.get(type);
fun save(type: Int) {
var stack = mStack[type]
if (stack == null) {
stack = new Stack<>();
mStack.put(type, stack);
stack = Stack()
mStack.put(type, stack)
}
if (type == TYPE_TEXT_COLOR) {
stack.push(mTextColor);
stack.push(textColor)
} else if (type == TYPE_BG_COLOR) {
stack.push(mBackgroundColor);
stack.push(backgroundColor)
} else if (type == TYPE_TYPEFACE) {
stack.push(mTypeface);
stack.push(typeface)
} else if (type == TYPE_TEXT_SIZE) {
stack.push(mTextSize);
stack.push(textSize)
} else if (type == TYPE_ALIGNMENT) {
stack.push(mAlignment);
stack.push(alignment)
} else if (type == TYPE_LINE_SPACE) {
stack.push(mLineSpace);
stack.push(lineSpace)
} else if (type == TYPE_PARAGRAPH_SPACE) {
stack.push(mParagraphSpace);
stack.push(paragraphSpace)
} else {
stack.push(mCustomProp.get(type));
stack.push(mCustomProp[type])
}
}
public void restore(int type) {
Stack<Object> stack = mStack.get(type);
fun restore(type: Int) {
val stack = mStack[type]
if (stack == null || stack.isEmpty()) {
Log.d(TAG, "restore (type = " + type + ")with a empty stack.");
return;
Log.d(TAG, "restore (type = $type)with a empty stack.")
return
}
Object v = stack.pop();
restore(type, v);
val v = stack.pop()
restore(type, v)
}
private void restore(int type, Object v){
private fun restore(type: Int, v: Any?) {
if (type == TYPE_TEXT_COLOR) {
setTextColor((Integer) v);
textColor = v as Int
} else if (type == TYPE_BG_COLOR) {
setBackgroundColor((Integer) v);
backgroundColor = v as Int
} else if (type == TYPE_TYPEFACE) {
setTypeface((Typeface) v);
typeface = v as? Typeface
} else if (type == TYPE_TEXT_SIZE) {
setTextSize((Float) v);
textSize = v as Float
} else if (type == TYPE_ALIGNMENT) {
setAlignment((Alignment) v);
alignment = v as Alignment
} else if (type == TYPE_LINE_SPACE) {
setLineSpace((Integer) v);
lineSpace = v as Int
} else if (type == TYPE_PARAGRAPH_SPACE) {
setParagraphSpace((Integer) v);
paragraphSpace = v as Int
} else {
setCustomProp(type, v);
setCustomProp(type, v)
}
}
public void clear() {
for (int i = 0; i < mStack.size(); i++) {
Stack<Object> stack = mStack.valueAt(i);
if (stack != null && stack.size() > 0) {
while (stack.size() > 1){
stack.pop();
fun clear() {
for (i in 0 until mStack.size()) {
val stack = mStack.valueAt(i)
if (stack != null && stack.size > 0) {
while (stack.size > 1) {
stack.pop()
}
restore(mStack.keyAt(i), stack.pop());
restore(mStack.keyAt(i), stack.pop())
}
}
}
}
}
@@ -13,162 +13,110 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type
package com.qmuiteam.qmui.type;
import com.qmuiteam.qmui.type.element.Element
import java.util.*
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
class TypeModel(
val origin: CharSequence,
private val mElementMap: Map<Int, Element>,
private val mFirstElement: Element,
private val mLastElement: Element) {
import com.qmuiteam.qmui.type.element.Element;
var firstEffect: Element? = null
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class TypeModel {
private CharSequence mOrigin;
private final Map<Integer, Element> mElementMap;
private Element mFirstElement;
private Element mLastElement;
@Nullable
private Element mFirstEffect;
public TypeModel(
CharSequence origin,
@NonNull Map<Integer, Element> elementMap,
Element firstElement,
Element lastElement,
@Nullable Element firstEffect) {
mOrigin = origin;
mElementMap = elementMap;
mFirstElement = firstElement;
mLastElement = lastElement;
mFirstEffect = firstEffect;
}
@Nullable
public Element getFirstEffect() {
return mFirstEffect;
}
public CharSequence getOrigin() {
return mOrigin;
}
public EffectRemover addBgEffect(int start, int end, final int bgColor){
List<Integer> types = new ArrayList<>();
types.add(TypeEnvironment.TYPE_BG_COLOR);
return unsafeAddEffect(start, end, types, new EnvironmentUpdater() {
@Override
public void update(TypeEnvironment env) {
env.setBackgroundColor(bgColor);
fun addBgEffect(start: Int, end: Int, bgColor: Int): EffectRemover? {
val types: MutableList<Int> = ArrayList()
types.add(TypeEnvironment.TYPE_BG_COLOR)
return unsafeAddEffect(start, end, types, object : EnvironmentUpdater {
override fun update(env: TypeEnvironment) {
env.backgroundColor = bgColor
}
});
})
}
public EffectRemover addTextColorEffect(int start, int end, final int textColor){
List<Integer> types = new ArrayList<>();
types.add(TypeEnvironment.TYPE_TEXT_COLOR);
return unsafeAddEffect(start, end, types, new EnvironmentUpdater() {
@Override
public void update(TypeEnvironment env) {
env.setTextColor(textColor);
fun addTextColorEffect(start: Int, end: Int, textColor: Int): EffectRemover? {
val types: MutableList<Int> = ArrayList()
types.add(TypeEnvironment.TYPE_TEXT_COLOR)
return unsafeAddEffect(start, end, types, object : EnvironmentUpdater {
override fun update(env: TypeEnvironment) {
env.textColor = textColor
}
});
})
}
public EffectRemover addUnderLineEffect(int start, int end, final int underLineColor, final int underLineHeight){
List<Integer> types = new ArrayList<>();
types.add(TypeEnvironment.TYPE_BORDER_BOTTOM_WIDTH);
types.add(TypeEnvironment.TYPE_BORDER_BOTTOM_COLOR);
return unsafeAddEffect(start, end, types, new EnvironmentUpdater() {
@Override
public void update(TypeEnvironment env) {
env.setBorderBottom(underLineHeight, underLineColor);
fun addUnderLineEffect(start: Int, end: Int, underLineColor: Int, underLineHeight: Int): EffectRemover? {
val types: MutableList<Int> = ArrayList()
types.add(TypeEnvironment.TYPE_BORDER_BOTTOM_WIDTH)
types.add(TypeEnvironment.TYPE_BORDER_BOTTOM_COLOR)
return unsafeAddEffect(start, end, types, object : EnvironmentUpdater {
override fun update(env: TypeEnvironment) {
env.setBorderBottom(underLineHeight, underLineColor)
}
});
})
}
public EffectRemover unsafeAddEffect(int start, int end, List<Integer> types, EnvironmentUpdater environmentUpdater){
Element elementStart = mElementMap.get(start);
Element elementEnd = mElementMap.get(end);
if(elementStart == null || elementEnd == null){
return null;
fun unsafeAddEffect(start: Int, end: Int, types: List<Int>, environmentUpdater: EnvironmentUpdater): EffectRemover? {
val elementStart = mElementMap[start]
val elementEnd = mElementMap[end]
if (elementStart == null || elementEnd == null) {
return null
}
for(Integer type: types){
elementStart.addSaveType(type);
elementEnd.addRestoreType(type);
for (type in types) {
elementStart.addSaveType(type)
elementEnd.addRestoreType(type)
}
elementStart.addEnvironmentUpdater(environmentUpdater);
if(mFirstEffect == null){
mFirstEffect = elementStart;
elementStart.addEnvironmentUpdater(environmentUpdater)
firstEffect = if (firstEffect == null) {
elementStart
} else {
mFirstEffect = elementStart.insertTo(mFirstElement);
elementStart.insertEffectTo(firstEffect!!)
}
mFirstEffect = elementEnd.insertTo(mFirstElement);
return new DefaultEffectRemove(this, start, end, types, environmentUpdater);
firstEffect = elementEnd.insertEffectTo(firstEffect!!)
return DefaultEffectRemove(this, start, end, types, environmentUpdater)
}
public boolean unsafeRemoveEffect(int start, int end, List<Integer> types, EnvironmentUpdater environmentUpdater){
Element elementStart = mElementMap.get(start);
Element elementEnd = mElementMap.get(end);
if(elementStart == null || elementEnd == null){
return false;
fun unsafeRemoveEffect(start: Int, end: Int, types: List<Int>, environmentUpdater: EnvironmentUpdater): Boolean {
val elementStart = mElementMap[start]
val elementEnd = mElementMap[end]
if (elementStart == null || elementEnd == null) {
return false
}
for(Integer type: types){
elementStart.removeSaveType(type);
elementEnd.removeStoreType(type);
for (type in types) {
elementStart.removeSaveType(type)
elementEnd.removeStoreType(type)
}
elementStart.removeEnvironmentUpdater(environmentUpdater);
mFirstEffect = elementStart.removeFromEffectListIfNeeded(mFirstEffect);
mFirstEffect = elementEnd.removeFromEffectListIfNeeded(mFirstEffect);
return true;
elementStart.removeEnvironmentUpdater(environmentUpdater)
firstEffect = elementStart.removeFromEffectListIfNeeded(firstEffect)
firstEffect = elementEnd.removeFromEffectListIfNeeded(firstEffect)
return true
}
public Element firstElement() {
return mFirstElement;
fun firstElement(): Element {
return mFirstElement
}
public Element lastElement() {
return mLastElement;
fun lastElement(): Element {
return mLastElement
}
@Nullable
public Element get(int pos){
return mElementMap.get(pos);
operator fun get(pos: Int): Element? {
return mElementMap[pos]
}
public interface EffectRemover {
void remove();
interface EffectRemover {
fun remove()
}
}
static class DefaultEffectRemove implements EffectRemover{
private final int mStart;
private final int mEnd;
private final List<Integer> mTypes;
private final EnvironmentUpdater mEnvironmentUpdater;
private final TypeModel mTypeModel;
public DefaultEffectRemove(
TypeModel typeModel,
int start,
int end,
List<Integer> types,
EnvironmentUpdater environmentUpdater) {
mTypeModel = typeModel;
mStart = start;
mEnd = end;
mTypes = types;
mEnvironmentUpdater = environmentUpdater;
}
@Override
public void remove() {
mTypeModel.unsafeRemoveEffect(mStart, mEnd, mTypes, mEnvironmentUpdater);
}
class DefaultEffectRemove(
private val typeModel: TypeModel,
private val start: Int,
private val end: Int,
private val types: List<Int>,
private val environmentUpdater: EnvironmentUpdater) : TypeModel.EffectRemover {
override fun remove() {
typeModel.unsafeRemoveEffect(start, end, types, environmentUpdater)
}
}
@@ -13,13 +13,10 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.element
package com.qmuiteam.qmui.type.element;
public class BreakWordLineElement extends CharOrPhraseElement {
public BreakWordLineElement() {
super('-', -1, -1);
setWordPart(WORD_PART_MIDDLE);
class BreakWordLineElement : TextElement("-", -1, -1) {
init {
wordPart = WORD_PART_MIDDLE
}
}
}
@@ -1,44 +1,43 @@
package com.qmuiteam.qmui.type.element;
/*
* Tencent is pleased to support the open source community by making QMUI_Android available.
*
* Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.element
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import com.qmuiteam.qmui.type.TypeEnvironment
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
class DrawableElement(
drawable: Drawable,
text: CharSequence,
index: Int, start: Int) : Element(text, index, start) {
import com.qmuiteam.qmui.type.TypeEnvironment;
public class DrawableElement extends Element {
private final Drawable mDrawable;
public DrawableElement(
@NonNull Drawable drawable,
Character singleChar,
@Nullable CharSequence text,
int index, int originIndex) {
super(singleChar, text, index, originIndex,
text != null && text.length() > 2 && text.charAt(0) == '['
? text.subSequence(1, text.length() - 1).toString() : null);
mDrawable = drawable.mutate();
mDrawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
val drawable = drawable.mutate().apply {
setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
}
@Override
protected void onMeasure(TypeEnvironment env) {
setMeasureDimen(
mDrawable.getIntrinsicWidth(),
mDrawable.getIntrinsicHeight(),
0);
override fun onMeasure(env: TypeEnvironment) {
setMeasureDimen(drawable.intrinsicWidth.toFloat(), drawable.intrinsicHeight.toFloat(), 0f)
}
@Override
protected void onDraw(TypeEnvironment env, Canvas canvas) {
drawBg(env, canvas);
canvas.save();
canvas.translate(getX(), getY());
mDrawable.draw(canvas);
canvas.restore();
drawBorder(env, canvas);
override fun onDraw(env: TypeEnvironment, canvas: Canvas) {
drawBg(env, canvas)
canvas.save()
canvas.translate(x, y)
drawable.draw(canvas)
canvas.restore()
drawBorder(env, canvas)
}
}
}
@@ -13,424 +13,291 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.element
package com.qmuiteam.qmui.type.element;
import android.graphics.Canvas
import android.graphics.Color
import com.qmuiteam.qmui.type.EnvironmentUpdater
import com.qmuiteam.qmui.type.TypeEnvironment
import java.util.*
import kotlin.collections.ArrayList
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
abstract class Element(val text: CharSequence, val index: Int, val start: Int) {
companion object {
const val VISIBLE = 0
const val GONE = 1
const val WORD_PART_WHOLE = 0
const val WORD_PART_START = 1
const val WORD_PART_MIDDLE = 2
const val WORD_PART_END = 3
const val LINE_BREAK_TYPE_NORMAL = 0
const val LINE_BREAK_TYPE_NOT_START = 1
const val LINE_BREAK_TYPE_NOT_END = 2
const val LINE_BREAK_WORD_BREAK_ALLOWED = 3
private val NOT_START_CHARS = charArrayOf(
',', '.', ';', ']', '>', ')', '?', '"', '\'', '!', ':', '}', '」',
',', '。', ';', '、', '】', '》', ')', '?', '”', '!', ':', '』')
private val NOT_END_CHARS = charArrayOf(
'(', '<', '[', '{', '“', '「', '『', '(', '《'
)
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.qmuiteam.qmui.type.EnvironmentUpdater;
import com.qmuiteam.qmui.type.TypeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public abstract class Element {
public static final int VISIBLE = 0;
public static final int INVISIBLE = 1;
public static final int GONE = 2;
public static final int WORD_PART_WHOLE = 0;
public static final int WORD_PART_START = 1;
public static final int WORD_PART_MIDDLE = 2;
public static final int WORD_PART_END = 3;
public static final int LINE_BREAK_TYPE_NORMAL = 0;
public static final int LINE_BREAK_TYPE_NOT_START = 1;
public static final int LINE_BREAK_TYPE_NOT_END = 2;
public static final int LINE_BREAK_WORD_BREAK_ALLOWED = 3;
private static final char[] NOT_START_CHARS =new char[]{
',', '.', ';', ']', '>', ')', '?', '"', '\'', '!', ':', '}', '」',
',', '。', ';', '、', '】', '》', ')', '?', '”', '!', ':', '』',
};
private static final char[] NOT_END_CHARS = new char[]{
'(', '<', '[', '{', '“', '「', '『', '(', '《'
};
static {
Arrays.sort(NOT_START_CHARS);
Arrays.sort(NOT_END_CHARS);
}
private final char mChar;
private final CharSequence mText;
private final int mIndex;
private final int mOriginIndex;
private final String mDescription;
private Element mPrevEffect;
private Element mNextEffect;
private Element mPrev;
private Element mNext;
private int mWordPart = WORD_PART_WHOLE;
private int mLineBreakType = LINE_BREAK_TYPE_NORMAL;
private int mVisible = VISIBLE;
private List<Integer> mSaveType;
private List<Integer> mRestoreType;
@Nullable
private List<EnvironmentUpdater> mEnvironmentUpdater;
private float mMeasureWidth;
private float mMeasureHeight;
private float mX;
private float mY;
private float mBaseLine;
private float mNextGapWidth;
private String mToStringCache;
public Element(Character singleChar, @Nullable CharSequence text, int index, int originIndex) {
this(singleChar, text, index, originIndex, null);
}
public Element(char singleChar, @Nullable CharSequence text, int index, int originIndex, @Nullable String description) {
mChar = singleChar;
mText = text;
mIndex = index;
mOriginIndex = originIndex;
mDescription = description;
if(Arrays.binarySearch(NOT_START_CHARS, singleChar) >= 0){
mLineBreakType = LINE_BREAK_TYPE_NOT_START;
} else if(Arrays.binarySearch(NOT_END_CHARS, singleChar) >= 0){
mLineBreakType = LINE_BREAK_TYPE_NOT_END;
init {
Arrays.sort(NOT_START_CHARS)
Arrays.sort(NOT_END_CHARS)
}
}
public Element insertTo(Element head){
if(head == null){
return this;
var prevEffect: Element? = null
private set
var nextEffect: Element? = null
private set
var wordPart = WORD_PART_WHOLE
var lineBreakType = LINE_BREAK_TYPE_NORMAL
var visible = VISIBLE
var measureWidth = 0f
private set
var measureHeight = 0f
private set
var x = 0f
var y = 0f
var baseLine = 0f
var nextGapWidth = 0f
private var saveTypeList: MutableList<Int>? = null
private var restoreTypeList: MutableList<Int>? = null
private var environmentUpdaterList: MutableList<EnvironmentUpdater>? = null
val length: Int = text.length
private var _prev: Element? = null
private var _next: Element? = null
var next: Element?
get() = _next
set(element) {
_next = element
if (element != null) {
element._prev = this
}
}
if(head == this){
return head;
var prev: Element?
get() = _prev
set(element) {
_prev = element
if (element != null) {
element._next = this
}
}
if(mIndex < head.mIndex){
head.setPrev(this);
this.setNext(head);
return this;
private val rightWithGap: Float
get() = x + measureWidth + nextGapWidth + 0.5f
init {
if(text.length == 1){
if (Arrays.binarySearch(NOT_START_CHARS, text[0]) >= 0) {
lineBreakType = LINE_BREAK_TYPE_NOT_START
} else if (Arrays.binarySearch(NOT_END_CHARS, text[0]) >= 0) {
lineBreakType = LINE_BREAK_TYPE_NOT_END
}
}
Element current = head;
Element next = head.mNext;
while (next != null){
if(next == this){
}
fun insertEffectTo(head: Element): Element {
if (head === this) {
return head
}
if (index < head.index) {
head.prevEffect = this
nextEffect = head
return this
}
var current: Element = head
var next = head.nextEffect
while (next != null) {
if (next === this) {
// already in list
return head;
return head
}
if(next.mIndex > mIndex){
current.setNext(this);
next.setPrev(this);
this.setPrev(current);
this.setNext(next);
return head;
if (next.index > index) {
current.nextEffect = this
next.prevEffect = this
prevEffect = current
nextEffect = next
return head
}
current = next;
next = next.mNext;
current = next
next = next.nextEffect
}
current.setNext(this);
this.setPrev(current);
this.setNext(null);
return head;
current.nextEffect = this
prevEffect = current
nextEffect = null
return head
}
public Element removeFromEffectListIfNeeded(Element head){
boolean noSaveType = mSaveType == null || mSaveType.isEmpty();
boolean noRestoreType = mRestoreType == null || mRestoreType.isEmpty();
if(noSaveType && noRestoreType){
Element prev = mPrevEffect;
Element next = mNextEffect;
if(prev != null){
prev.mNextEffect = next;
mPrevEffect = null;
fun removeFromEffectListIfNeeded(head: Element?): Element? {
if(head == null){
return null
}
val noSaveType = saveTypeList.isNullOrEmpty()
val noRestoreType = restoreTypeList.isNullOrEmpty()
if (noSaveType && noRestoreType) {
val prev = prevEffect
val next = nextEffect
if (prev != null) {
prev.nextEffect = next
prevEffect = null
}
if(next != null){
next.mPrevEffect = prev;
mNextEffect = null;
if (next != null) {
next.prevEffect = prev
nextEffect = null
}
if(head == this){
return next;
if (head === this) {
return next
}
}
return head;
return head
}
public Element getNextEffect() {
return mNextEffect;
fun addSaveType(type: Int) {
val list = saveTypeList ?: ArrayList<Int>().also { saveTypeList = it}
list.add(type)
}
public Element getPrevEffect() {
return mPrevEffect;
}
public int getLineBreakType() {
return mLineBreakType;
}
public void setLineBreakType(int lineBreakType) {
mLineBreakType = lineBreakType;
}
public void setPrev(Element element) {
this.mPrev = element;
if (element != null) {
element.mNext = this;
}
}
public void setNext(Element element) {
this.mNext = element;
if (element != null) {
element.mPrev = this;
}
}
public void setWordPart(int wordPart) {
this.mWordPart = wordPart;
}
public int getWordPart() {
return mWordPart;
}
public void addSaveType(int type) {
if (mSaveType == null) {
mSaveType = new ArrayList<>();
}
mSaveType.add(type);
}
public void removeSaveType(int type) {
if (mSaveType != null) {
for (int i = 0; i < mSaveType.size(); i++) {
if (mSaveType.get(i) == type) {
mSaveType.remove(i);
break;
}
fun removeSaveType(type: Int) {
val list = saveTypeList ?: return
for (i in list.indices) {
if (list[i] == type) {
list.removeAt(i)
break
}
}
}
public void addRestoreType(int type) {
if (mRestoreType == null) {
mRestoreType = new ArrayList<>();
}
mRestoreType.add(type);
fun addRestoreType(type: Int) {
val list = restoreTypeList ?: ArrayList<Int>().also { restoreTypeList = it}
list.add(type)
}
public void removeStoreType(int type) {
if (mRestoreType != null) {
for (int i = 0; i < mRestoreType.size(); i++) {
if (mRestoreType.get(i) == type) {
mRestoreType.remove(i);
break;
}
fun removeStoreType(type: Int) {
val list = restoreTypeList ?: return
for (i in list.indices) {
if (list[i] == type) {
list.removeAt(i)
break
}
}
}
public boolean hasEnvironmentUpdater() {
return mEnvironmentUpdater != null && mEnvironmentUpdater.size() > 0;
fun hasEnvironmentUpdater(): Boolean {
return (environmentUpdaterList?.size ?: 0) > 0
}
public void addEnvironmentUpdater(@NonNull EnvironmentUpdater environmentUpdater) {
if (mEnvironmentUpdater == null) {
mEnvironmentUpdater = new ArrayList<>();
}
mEnvironmentUpdater.add(environmentUpdater);
fun hasSaveType(): Boolean {
return !saveTypeList.isNullOrEmpty()
}
public void removeEnvironmentUpdater(@NonNull EnvironmentUpdater environmentUpdater) {
if (mEnvironmentUpdater != null) {
mEnvironmentUpdater.remove(environmentUpdater);
}
fun hasRestoreType(): Boolean {
return !restoreTypeList.isNullOrEmpty()
}
public void addSingleEnvironmentUpdater(@Nullable List<Integer> changedTypes, @NonNull EnvironmentUpdater environmentUpdater) {
fun addEnvironmentUpdater(environmentUpdater: EnvironmentUpdater) {
val list = environmentUpdaterList ?: ArrayList<EnvironmentUpdater>().also { environmentUpdaterList = it }
list.add(environmentUpdater)
}
fun removeEnvironmentUpdater(environmentUpdater: EnvironmentUpdater) {
val list = environmentUpdaterList ?: return
list.remove(environmentUpdater)
}
fun addSingleEnvironmentUpdater(changedTypes: List<Int>?, environmentUpdater: EnvironmentUpdater) {
if (changedTypes != null) {
for (Integer type : changedTypes) {
addRestoreType(type);
addSaveType(type);
for (type in changedTypes) {
addSaveType(type)
addRestoreType(type)
}
}
addEnvironmentUpdater(environmentUpdater);
addEnvironmentUpdater(environmentUpdater)
}
public void move(TypeEnvironment environment) {
fun move(environment: TypeEnvironment) {
if (hasEnvironmentUpdater()) {
updateEnv(environment);
restoreEnv(environment);
updateEnv(environment)
restoreEnv(environment)
}
}
public int getIndex() {
return mIndex;
override fun toString(): String {
return text.toString()
}
public int getOriginIndex() {
return mOriginIndex;
protected fun setMeasureDimen(measureWidth: Float, measureHeight: Float, baseline: Float) {
this.measureWidth = measureWidth
this.measureHeight = measureHeight
baseLine = baseline
}
public CharSequence getText() {
return mText;
fun measure(env: TypeEnvironment) {
updateEnv(env)
onMeasure(env)
restoreEnv(env)
}
public char getChar() {
return mChar;
}
public boolean isSingleChar() {
return mText == null;
}
@NonNull
@Override
public String toString() {
if(mToStringCache != null){
return mToStringCache;
fun draw(env: TypeEnvironment, canvas: Canvas) {
updateEnv(env)
if (visible == VISIBLE) {
onDraw(env, canvas)
}
mToStringCache = mText != null ? mText.toString() : String.valueOf(mChar);
return mToStringCache;
restoreEnv(env)
}
public int getLength() {
return mText != null ? mText.length() : 1;
}
public String getDescription() {
return mDescription != null ? mDescription : toString();
}
protected void setMeasureDimen(float measureWidth, float measureHeight, float baseline) {
mMeasureWidth = measureWidth;
mMeasureHeight = measureHeight;
mBaseLine = baseline;
}
public void setVisible(int visible) {
mVisible = visible;
}
public int getVisible() {
return mVisible;
}
public void setX(float x) {
mX = x;
}
public void setY(float y) {
mY = y;
}
public void setNextGapWidth(float nextGapWidth) {
mNextGapWidth = nextGapWidth;
}
public float getBaseLine() {
return mBaseLine;
}
public float getMeasureWidth() {
return mMeasureWidth;
}
public float getMeasureHeight() {
return mMeasureHeight;
}
public float getX() {
return mX;
}
public float getY() {
return mY;
}
public Element getNext() {
return mNext;
}
public Element getPrev() {
return mPrev;
}
public final void measure(TypeEnvironment env) {
updateEnv(env);
onMeasure(env);
restoreEnv(env);
}
public final void draw(TypeEnvironment env, Canvas canvas) {
updateEnv(env);
if (mVisible == VISIBLE) {
onDraw(env, canvas);
fun updateEnv(env: TypeEnvironment) {
saveTypeList?.forEach {
env.save(it)
}
restoreEnv(env);
// environmentUpdaterList?.forEach {
// it.update(env)
// }
}
void updateEnv(TypeEnvironment env) {
if (mSaveType != null) {
for (Integer type : mSaveType) {
env.save(type);
}
}
if (mEnvironmentUpdater != null) {
for (EnvironmentUpdater updater : mEnvironmentUpdater) {
updater.update(env);
}
fun restoreEnv(env: TypeEnvironment) {
restoreTypeList?.forEach {
env.restore(it)
}
}
void restoreEnv(TypeEnvironment env) {
if (mRestoreType != null) {
for (Integer type : mRestoreType) {
env.restore(type);
}
protected abstract fun onMeasure(env: TypeEnvironment)
protected abstract fun onDraw(env: TypeEnvironment, canvas: Canvas)
protected fun drawBg(env: TypeEnvironment, canvas: Canvas) {
if (env.backgroundColor != Color.TRANSPARENT) {
canvas.drawRect(x, y, rightWithGap, y + measureHeight, env.bgPaint)
}
}
protected abstract void onMeasure(TypeEnvironment env);
protected abstract void onDraw(TypeEnvironment env, Canvas canvas);
protected void drawBg(TypeEnvironment env, Canvas canvas){
if(env.getBackgroundColor() != Color.TRANSPARENT){
canvas.drawRect(mX, mY, getRightWithGap(), mY + mMeasureHeight, env.getBgPaint());
protected fun drawBorder(env: TypeEnvironment, canvas: Canvas) {
val paint = env.borderPaint
if (env.borderLeftWidth > 0) {
paint.color = env.borderLeftColor
canvas.drawRect(x, y, x + env.borderLeftWidth, y + measureHeight, paint)
}
if (env.borderTopWidth > 0) {
paint.color = env.borderTopColor
canvas.drawRect(x, y, rightWithGap, y + env.borderTopWidth, paint)
}
if (env.borderRightWidth > 0) {
paint.color = env.borderRightColor
canvas.drawRect(x + measureWidth - env.borderRightWidth, y,
x + measureWidth, y + measureHeight, paint)
}
if (env.borderBottomWidth > 0) {
paint.color = env.borderBottomColor
canvas.drawRect(x, y + measureHeight - env.borderBottomWidth,
rightWithGap, y + measureHeight, paint)
}
}
protected float getRightWithGap(){
return mX + mMeasureWidth + mNextGapWidth + 0.5f;
}
protected void drawBorder(TypeEnvironment env, Canvas canvas){
Paint paint = env.getBorderPaint();
if(env.getBorderLeftWidth() > 0){
paint.setColor(env.getBorderLeftColor());
canvas.drawRect(mX, mY, mX + env.getBorderLeftWidth(), mY + mMeasureHeight, paint);
}
if(env.getBorderTopWidth() > 0) {
paint.setColor(env.getBorderTopColor());
canvas.drawRect(mX, mY, getRightWithGap(), mY + env.getBorderTopWidth(), paint);
}
if(env.getBorderRightWidth() > 0){
paint.setColor(env.getBorderRightColor());
canvas.drawRect(mX + mMeasureWidth - env.getBorderRightWidth(), mY,
mX + mMeasureWidth, mY + mMeasureHeight, paint);
}
if(env.getBorderBottomWidth() > 0){
paint.setColor(env.getBorderBottomColor());
canvas.drawRect(mX, mY + mMeasureHeight - env.getBorderBottomWidth(),
getRightWithGap(), mY + mMeasureHeight, paint);
}
}
}
}
@@ -1,44 +1,39 @@
package com.qmuiteam.qmui.type.element;
/*
* Tencent is pleased to support the open source community by making QMUI_Android available.
*
* Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the MIT License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.element
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import com.qmuiteam.qmui.type.TypeEnvironment
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
class EmojiElement(val drawable: Drawable, text: CharSequence, index: Int, start: Int) : Element(text, index, start) {
import com.qmuiteam.qmui.type.TypeEnvironment;
public class EmojiElement extends Element {
private final Drawable mDrawable;
public EmojiElement(
@NonNull Drawable drawable,
Character singleChar,
@Nullable CharSequence text,
int index, int originIndex) {
super(singleChar, text, index, originIndex,
text != null && text.length() > 2 && text.charAt(0) == '['
? text.subSequence(1, text.length() - 1).toString() : null);
mDrawable = drawable;
override fun onMeasure(env: TypeEnvironment) {
val paint = env.paint
val size = (paint.fontMetrics.descent - paint.fontMetrics.ascent).toInt()
drawable.setBounds(0, 0, size, size)
setMeasureDimen(size.toFloat(), size.toFloat(), 0f)
}
@Override
protected void onMeasure(TypeEnvironment env) {
Paint paint = env.getPaint();
int size = (int) (paint.getFontMetrics().descent - paint.getFontMetrics().ascent);
mDrawable.setBounds(0, 0, size, size);
setMeasureDimen(size, size, 0);
override fun onDraw(env: TypeEnvironment, canvas: Canvas) {
drawBg(env, canvas)
canvas.save()
canvas.translate(x, y)
drawable.draw(canvas)
canvas.restore()
drawBorder(env, canvas)
}
@Override
protected void onDraw(TypeEnvironment env, Canvas canvas) {
drawBg(env, canvas);
canvas.save();
canvas.translate(getX(), getY());
mDrawable.draw(canvas);
canvas.restore();
drawBorder(env, canvas);
}
}
}
@@ -13,37 +13,29 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.element
package com.qmuiteam.qmui.type.element;
import android.graphics.Canvas
import com.qmuiteam.qmui.type.EnvironmentUpdater
import com.qmuiteam.qmui.type.TypeEnvironment
import android.graphics.Canvas;
class IgnoreEffectElement(list: List<Element>) : Element("", -1, -1) {
import com.qmuiteam.qmui.type.EnvironmentUpdater;
import com.qmuiteam.qmui.type.TypeEnvironment;
import java.util.List;
public class IgnoreEffectElement extends Element {
public IgnoreEffectElement(final List<Element> list) {
super(' ', null, -1, -1, "");
addEnvironmentUpdater(new EnvironmentUpdater() {
@Override
public void update(TypeEnvironment env) {
for (Element element : list) {
element.move(env);
init {
addEnvironmentUpdater(object: EnvironmentUpdater {
override fun update(env: TypeEnvironment) {
for (element in list) {
element.move(env)
}
}
});
})
}
@Override
protected void onMeasure(TypeEnvironment env) {
setMeasureDimen(0, 0, 0);
override fun onMeasure(env: TypeEnvironment) {
setMeasureDimen(0f, 0f, 0f)
}
@Override
protected void onDraw(TypeEnvironment env, Canvas canvas) {
override fun onDraw(env: TypeEnvironment, canvas: Canvas) {}
}
}
}
@@ -13,38 +13,16 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.element
package com.qmuiteam.qmui.type.element;
import android.graphics.Canvas
import com.qmuiteam.qmui.type.TypeEnvironment
import android.graphics.Canvas;
class NextParagraphElement(text: CharSequence, index: Int, start: Int) : Element(text, index, start) {
import androidx.annotation.Nullable;
import com.qmuiteam.qmui.type.TypeEnvironment;
public class NextParagraphElement extends Element {
public NextParagraphElement(int index, int originIndex) {
this('\n', null, index, originIndex, "\n");
override fun onMeasure(env: TypeEnvironment) {
setMeasureDimen(0f, 0f, 0f)
}
public NextParagraphElement(char singleChar, @Nullable String text, int index, int originIndex) {
super(singleChar, null, index, originIndex);
}
public NextParagraphElement(char singleChar, @Nullable CharSequence text, int index, int originIndex, @Nullable String description) {
super(singleChar, text, index, originIndex, description);
}
@Override
protected void onMeasure(TypeEnvironment env) {
setMeasureDimen(0, 0, 0);
}
@Override
protected void onDraw(TypeEnvironment env, Canvas canvas) {
}
}
override fun onDraw(env: TypeEnvironment, canvas: Canvas) {}
}
@@ -13,42 +13,23 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.element
package com.qmuiteam.qmui.type.element;
import android.graphics.Canvas
import com.qmuiteam.qmui.type.TypeEnvironment
import android.graphics.Canvas;
import android.graphics.Paint;
open class TextElement(text: CharSequence, index: Int, start: Int) : Element(text, index, start) {
import androidx.annotation.Nullable;
import com.qmuiteam.qmui.type.TypeEnvironment;
public class CharOrPhraseElement extends Element {
public CharOrPhraseElement(char singleChar, int index, int originIndex) {
super(singleChar, null, index, originIndex);
override fun onMeasure(env: TypeEnvironment) {
val paint = env.paint
setMeasureDimen(paint.measureText(text, 0, text.length),
paint.fontMetrics.descent - paint.fontMetrics.ascent,
-paint.fontMetrics.ascent)
}
public CharOrPhraseElement(char singleChar, int index, int originIndex, @Nullable String description) {
super(singleChar, null, index, originIndex, description);
override fun onDraw(env: TypeEnvironment, canvas: Canvas) {
drawBg(env, canvas)
canvas.drawText(text, 0, text.length, x, y + baseLine, env.paint)
drawBorder(env, canvas)
}
public CharOrPhraseElement(String text, int index, int originIndex) {
super('\u0000', text, index, originIndex);
}
@Override
protected void onMeasure(TypeEnvironment env) {
Paint paint = env.getPaint();
setMeasureDimen(paint.measureText(toString()),
paint.getFontMetrics().descent - paint.getFontMetrics().ascent,
-paint.getFontMetrics().ascent);
}
@Override
protected void onDraw(TypeEnvironment env, Canvas canvas) {
drawBg(env, canvas);
canvas.drawText(toString(), getX(), getY() + getBaseLine(), env.getPaint());
drawBorder(env, canvas);
}
}
}
@@ -13,26 +13,13 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.parser
package com.qmuiteam.qmui.type.parser;
import android.graphics.drawable.Drawable
import android.graphics.drawable.Drawable;
import androidx.annotation.Nullable;
public interface EmojiResourceProvider {
@Nullable
Drawable queryForDrawable(CharSequence text);
@Nullable
Drawable queryForDrawable(char c);
@Nullable
Drawable queryForDrawable(int codePoint);
@Nullable
Drawable queryForDrawable(int firstCodePoint, int secondCodePint);
}
interface EmojiResourceProvider {
fun queryForDrawable(text: CharSequence): Drawable?
fun queryForDrawable(c: Char): Drawable?
fun queryForDrawable(codePoint: Int): Drawable?
fun queryForDrawable(firstCodePoint: Int, secondCodePint: Int): Drawable?
}
@@ -13,116 +13,101 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.parser
package com.qmuiteam.qmui.type.parser;
import com.qmuiteam.qmui.type.TypeModel
import com.qmuiteam.qmui.type.element.*
import java.util.*
import android.graphics.drawable.Drawable;
class EmojiTextParser(private val emojiProvider: EmojiResourceProvider) : TextParser {
import com.qmuiteam.qmui.type.TypeModel;
import com.qmuiteam.qmui.type.element.CharOrPhraseElement;
import com.qmuiteam.qmui.type.element.DrawableElement;
import com.qmuiteam.qmui.type.element.Element;
import com.qmuiteam.qmui.type.element.EmojiElement;
import com.qmuiteam.qmui.type.element.NextParagraphElement;
import java.util.HashMap;
public class EmojiTextParser implements TextParser {
private final EmojiResourceProvider mEmojiProvider;
public EmojiTextParser(EmojiResourceProvider provider) {
mEmojiProvider = provider;
}
@Override
public TypeModel parse(CharSequence text) {
int size = text.length();
if (size == 0) {
return null;
override fun parse(text: CharSequence?): TypeModel? {
if(text == null || text.isEmpty()){
return null
}
HashMap<Integer, Element> map = new HashMap<>(text.length());
Element first = null, last = null, tmp = null;
int index = 0;
for (int i = 0; i < size; i++) {
char c = text.charAt(i);
val size = text.length
val map = HashMap<Int, Element>(size)
var first: Element? = null
var last: Element? = null
var tmp: Element? = null
var index = 0
var i = 0
while (i < size) {
val c = text[i]
if (c == '\n') {
tmp = new NextParagraphElement(c, null, index, i);
tmp = NextParagraphElement(text.subSequence(i, i + 1), index, i)
} else if (c == '\r') {
if (i + 1 < text.length() && text.charAt(i + 1) == '\n') {
tmp = new NextParagraphElement('\u0000', "\r\n", index, i);
i++;
if (i + 1 < text.length && text[i + 1] == '\n') {
tmp = NextParagraphElement(text.subSequence(i, i + 2), index, i)
i++
} else {
tmp = new NextParagraphElement(c, null, index, i);
tmp = NextParagraphElement(text.subSequence(i, i + 1), index, i)
}
} else if (c == '[') {
int j = i + 1;
boolean find = false;
int end = Math.min(i + 30, size);
var j = i + 1
var find = false
val end = Math.min(i + 30, size)
while (j < end) {
if (text.charAt(j) == ']') {
CharSequence sub = text.subSequence(i, j + 1);
Drawable emoji = mEmojiProvider.queryForDrawable(sub);
if (text[j] == ']') {
val sub = text.subSequence(i, j + 1)
val emoji = emojiProvider.queryForDrawable(sub)
if (emoji != null) {
tmp = new EmojiElement(emoji, '\u0000', sub, index, i);
i = j;
find = true;
break;
tmp = EmojiElement(emoji, text.subSequence(i, j + 1), index, i)
i = j
find = true
break
}
}
j++;
j++
}
if (!find) {
tmp = new CharOrPhraseElement(c, index, i);
tmp = TextElement(text.subSequence(i, i + 1), index, i)
}
} else {
boolean handled = false;
Drawable emoji = mEmojiProvider.queryForDrawable(c);
var handled = false
var emoji = emojiProvider.queryForDrawable(c)
if (emoji != null) {
handled = true;
tmp = new DrawableElement(emoji, c, null, index, i);
handled = true
tmp = DrawableElement(emoji, text.subSequence(i, i + 1), index, i)
}
if (!handled) {
int unicode = Character.codePointAt(text, i);
int codeCount = Character.charCount(unicode);
emoji = mEmojiProvider.queryForDrawable(unicode);
val unicode = Character.codePointAt(text, i)
val codeCount = Character.charCount(unicode)
emoji = emojiProvider.queryForDrawable(unicode)
if (emoji != null) {
handled = true;
tmp = new DrawableElement(emoji, c, text.subSequence(i, i + codeCount), index, i);
i += codeCount - 1;
handled = true
tmp = DrawableElement(emoji, text.subSequence(i, i + codeCount), index, i)
i += codeCount - 1
}
int nextStart = i + codeCount;
val nextStart = i + codeCount
if (!handled && nextStart < size) {
int nextUnicode = Character.codePointAt(text, nextStart);
emoji = mEmojiProvider.queryForDrawable(unicode, nextUnicode);
val nextUnicode = Character.codePointAt(text, nextStart)
emoji = emojiProvider.queryForDrawable(unicode, nextUnicode)
if (emoji != null) {
handled = true;
int nextCodeCount = Character.charCount(nextUnicode);
tmp = new DrawableElement(emoji, c, text.subSequence(i, nextStart + nextCodeCount), index, i);
i = nextStart + nextCodeCount - 1;
handled = true
val nextCodeCount = Character.charCount(nextUnicode)
tmp = DrawableElement(emoji, text.subSequence(i, nextStart + nextCodeCount), index, i)
i = nextStart + nextCodeCount - 1
}
}
}
if (!handled) {
tmp = new CharOrPhraseElement(c, index, i);
tmp = TextElement(text.subSequence(i, i + 1), index, i)
}
}
ParserHelper.handleWordPart(c, last, tmp);
index++;
ParserHelper.handleWordPart(c, last, tmp!!)
index++
if (first == null) {
first = tmp;
last = tmp;
first = tmp
last = tmp
} else {
last.setNext(tmp);
last = tmp;
last!!.next = tmp
last = tmp
}
map.put(tmp.getIndex(), tmp);
map[tmp.index] = tmp
i++
}
return new TypeModel(text, map, first, last, null);
return TypeModel(text, map, first!!, last!!)
}
}
}
@@ -13,31 +13,28 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.parser
package com.qmuiteam.qmui.type.parser;
import com.qmuiteam.qmui.type.element.Element
import com.qmuiteam.qmui.type.element.Element;
object ParserHelper {
public class ParserHelper {
public static boolean isEnglishLetterOrNumber(char c){
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
fun isEnglishLetterOrNumber(c: Char): Boolean {
return c in 'a'..'z' || c in 'A'..'Z' || c in '0'..'9'
}
public static void handleWordPart(char c, Element prev, Element curr){
if(ParserHelper.isEnglishLetterOrNumber(c)){
if(prev == null ||
prev.getWordPart() == Element.WORD_PART_WHOLE ||
prev.getWordPart() == Element.WORD_PART_END){
curr.setWordPart(Element.WORD_PART_START);
}else{
curr.setWordPart(Element.WORD_PART_MIDDLE);
fun handleWordPart(c: Char, prev: Element?, curr: Element) {
if (isEnglishLetterOrNumber(c)) {
if (prev == null || prev.wordPart == Element.WORD_PART_WHOLE || prev.wordPart == Element.WORD_PART_END) {
curr.wordPart = Element.WORD_PART_START
} else {
curr.wordPart = Element.WORD_PART_MIDDLE
}
}else{
if(prev != null && prev.getWordPart() == Element.WORD_PART_MIDDLE){
prev.setWordPart(Element.WORD_PART_END);
} else {
if (prev != null && prev.wordPart == Element.WORD_PART_MIDDLE) {
prev.wordPart = Element.WORD_PART_END
}
curr.setWordPart(Element.WORD_PART_WHOLE);
curr.wordPart = Element.WORD_PART_WHOLE
}
}
}
}
@@ -13,52 +13,53 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.parser
package com.qmuiteam.qmui.type.parser;
import com.qmuiteam.qmui.type.TypeModel
import com.qmuiteam.qmui.type.element.Element
import com.qmuiteam.qmui.type.element.NextParagraphElement
import com.qmuiteam.qmui.type.element.TextElement
import com.qmuiteam.qmui.type.parser.ParserHelper.handleWordPart
import java.util.*
import com.qmuiteam.qmui.type.TypeModel;
import com.qmuiteam.qmui.type.element.CharOrPhraseElement;
import com.qmuiteam.qmui.type.element.Element;
import com.qmuiteam.qmui.type.element.NextParagraphElement;
import java.util.HashMap;
public class PlainTextParser implements TextParser {
@Override
public TypeModel parse(CharSequence text) {
if (text.length() == 0) {
return null;
class PlainTextParser : TextParser {
override fun parse(text: CharSequence?): TypeModel? {
if (text == null || text.isEmpty()) {
return null
}
HashMap<Integer, Element> map = new HashMap<>(text.length());
Element first = null, last = null, tmp;
int index = 0;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
val size = text.length
val map = HashMap<Int, Element>(size)
var first: Element? = null
var last: Element? = null
var tmp: Element
var index = 0
var i = 0
while (i < size) {
val c = text[i]
if (c == '\n') {
tmp = new NextParagraphElement(c, null, index, i);
tmp = NextParagraphElement(text.subSequence(i, i+1), index, i)
} else if (c == '\r') {
if (i + 1 < text.length() && text.charAt(i + 1) == '\n') {
tmp = new NextParagraphElement('\u0000', "\r\n", index, i);
i++;
if (i + 1 < text.length && text[i + 1] == '\n') {
tmp = NextParagraphElement(text.subSequence(i, i+2), index, i)
i++
} else {
tmp = new NextParagraphElement(c, null, index, i);
tmp = NextParagraphElement(text.subSequence(i, i+1), index, i)
}
} else {
tmp = new CharOrPhraseElement(c, index, i);
tmp = TextElement(text.subSequence(i, i+1), index, i)
}
ParserHelper.handleWordPart(c, last, tmp);
index++;
handleWordPart(c, last, tmp)
index++
if (first == null) {
first = tmp;
last = tmp;
first = tmp
last = tmp
} else {
last.setNext(tmp);
last = tmp;
last!!.next = tmp
last = tmp
}
map.put(tmp.getIndex(), tmp);
map[tmp.index] = tmp
i++
}
return new TypeModel(text, map, first, last, null);
return TypeModel(text, map, first!!, last!!)
}
}
}
@@ -13,11 +13,10 @@
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.qmuiteam.qmui.type.parser
package com.qmuiteam.qmui.type.parser;
import com.qmuiteam.qmui.type.TypeModel
import com.qmuiteam.qmui.type.TypeModel;
public interface TextParser {
TypeModel parse(CharSequence text);
}
interface TextParser {
fun parse(text: CharSequence?): TypeModel?
}