Skip to content

Commit 0f33f15

Browse files
committed
Add new TextStyle
1 parent 4451e6f commit 0f33f15

File tree

4 files changed

+138
-16
lines changed

4 files changed

+138
-16
lines changed

app/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
apply plugin: 'com.android.application'
22
apply plugin: 'kotlin-android'
3-
apply plugin: 'kotlin-android-extensions'
43

54
android {
65
compileSdkVersion rootProject.ext.compileSdkVersion

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Top-level build file where you can add configuration options common to all sub-projects/modules.
22

33
buildscript {
4-
ext.kotlin_version = '1.3.41'
4+
ext.kotlin_version = '1.5.31'
55
ext.kotlinCorutines_version = '1.2.1'
66
ext.supportVersion = '28.0.0'
77
ext.compileSdkVersion = 28

omegatypes/src/main/java/com/omega_r/libs/omegatypes/Size.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ abstract class Size : Serializable {
1919

2020
companion object {
2121

22+
val ZERO = 0.px
23+
2224
@JvmStatic
2325
fun from(size: Float, unit: Unit): Size = SimpleSize(size, unit)
2426

omegatypes/src/main/java/com/omega_r/libs/omegatypes/TextStyle.kt

Lines changed: 135 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
package com.omega_r.libs.omegatypes
22

33
import android.content.Context
4-
import android.graphics.*
4+
import android.graphics.Typeface
5+
import android.text.InputFilter
56
import android.text.SpannableString
6-
import android.text.style.*
7-
import java.io.Serializable
7+
import android.text.Spanned
8+
import android.text.TextUtils
9+
import android.text.style.AbsoluteSizeSpan
810
import android.text.style.ForegroundColorSpan
11+
import android.text.style.LeadingMarginSpan
12+
import android.text.style.StrikethroughSpan
913
import android.text.style.StyleSpan
14+
import android.text.style.TypefaceSpan
15+
import android.text.style.UnderlineSpan
1016
import com.omega_r.libs.omegatypes.tools.TypefaceSpanCompat
17+
import java.io.Serializable
18+
import java.util.Locale
1119

1220
/**
1321
* Created by Anton Knyazev on 25.04.2019.
@@ -62,6 +70,29 @@ abstract class TextStyle : Serializable {
6270
@JvmStatic
6371
fun size(size: Size): TextStyle = SizeTextStyle(size)
6472

73+
@JvmStatic
74+
fun leadingMargin(firstMargin: Size, nextMargin: Size): TextStyle = LeadingMarginTextStyle(firstMargin, nextMargin)
75+
76+
@JvmStatic
77+
fun paragraphMargin(margin: Size): TextStyle = leadingMargin(margin, Size.ZERO)
78+
79+
@JvmStatic
80+
fun filter(filter: InputFilter): TextStyle = FilterTextStyle(filter)
81+
82+
@JvmStatic
83+
fun uppercase(): TextStyle = filter(InputFilter.AllCaps())
84+
85+
@JvmStatic
86+
fun lowercase(): TextStyle = filter(LowercaseInputFilter())
87+
88+
@JvmStatic
89+
fun capitalize(): TextStyle = filter(CapitalizeInputFilter())
90+
91+
@JvmStatic
92+
fun decapitalize(): TextStyle = filter(DecapitalizeInputFilter())
93+
94+
@JvmStatic
95+
fun length(max: Int): TextStyle = filter(InputFilter.LengthFilter(max))
6596
}
6697

6798
operator fun plus(textStyle: TextStyle?): TextStyle {
@@ -81,26 +112,30 @@ abstract class TextStyle : Serializable {
81112
}
82113
}
83114

84-
fun applyStyle(context: Context, charSequence: CharSequence): CharSequence {
115+
open fun applyStyle(context: Context, charSequence: CharSequence): CharSequence {
85116
return getSpannableString(charSequence).apply {
86117
applyStyle(context)
87118
}
88119
}
89120

90121
protected abstract fun SpannableString.applyStyle(context: Context)
91122

92-
private fun getSpannableString(charSequence: CharSequence): SpannableString {
93-
return if (charSequence is SpannableString) charSequence else SpannableString(charSequence)
123+
protected open fun getSpannableString(charSequence: CharSequence): SpannableString {
124+
return SpannableString.valueOf(charSequence)
94125
}
95126

96127
private class Array(internal vararg val textStyleArray: TextStyle) : TextStyle() {
97128

98-
override fun SpannableString.applyStyle(context: Context) {
129+
override fun applyStyle(context: Context, charSequence: CharSequence): CharSequence {
130+
var result = charSequence
99131
textStyleArray.forEach {
100-
it.apply {
101-
applyStyle(context)
102-
}
132+
result = it.applyStyle(context, result)
103133
}
134+
return result
135+
}
136+
137+
override fun SpannableString.applyStyle(context: Context) {
138+
// nothing
104139
}
105140
}
106141

@@ -110,7 +145,6 @@ abstract class TextStyle : Serializable {
110145
val color = color.getColorInt(context)
111146
setSpan(ForegroundColorSpan(color), 0, length, 0)
112147
}
113-
114148
}
115149

116150
private class FontStyleTextStyle(private val typeFaceStyle: Int) : TextStyle() {
@@ -134,31 +168,118 @@ abstract class TextStyle : Serializable {
134168
setSpan(TypefaceSpan(it), 0, length, 0)
135169
}
136170
}
137-
138171
}
139172

140173
private class TypefaceFontTextStyle(private val fontTypeface: Typeface) : TextStyle() {
141174

142175
override fun SpannableString.applyStyle(context: Context) {
143176
setSpan(TypefaceSpanCompat(typeface = fontTypeface), 0, length, 0)
144177
}
145-
146178
}
147179

148180
private class SizeTextStyle(private val size: Size) : TextStyle() {
149181

150182
override fun SpannableString.applyStyle(context: Context) {
151183
setSpan(AbsoluteSizeSpan(size.getPixelSize(context), false), 0, length, 0)
152184
}
153-
154185
}
155186

156187
private class StrikethroughTextStyle : TextStyle() {
157188

158189
override fun SpannableString.applyStyle(context: Context) {
159190
setSpan(StrikethroughSpan(), 0, length, 0)
160191
}
192+
}
193+
194+
private class LeadingMarginTextStyle(private val firstLineMargin: Size, private val nextLinesMargin: Size) : TextStyle() {
195+
196+
override fun SpannableString.applyStyle(context: Context) {
197+
val firstMargin = firstLineMargin.getPixelOffset(context)
198+
val nextMargin = nextLinesMargin.getPixelOffset(context)
199+
setSpan(LeadingMarginSpan.Standard(firstMargin, nextMargin), 0, length, 0)
200+
}
201+
}
202+
203+
private class FilterTextStyle(private val filter: InputFilter) : TextStyle() {
204+
205+
override fun SpannableString.applyStyle(context: Context) {
206+
// nothing
207+
}
161208

209+
override fun getSpannableString(charSequence: CharSequence): SpannableString {
210+
val spannableString = super.getSpannableString(charSequence)
211+
val result = filter.filter(charSequence, 0, charSequence.length, spannableString, 0, charSequence.length)
212+
?: charSequence
213+
return SpannableString.valueOf(result)
214+
}
215+
}
216+
217+
private class LowercaseInputFilter : InputFilter {
218+
219+
override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned?, dstart: Int, dend: Int): CharSequence? {
220+
221+
for (i in start until end) {
222+
if (source[i].isUpperCase()) {
223+
val v = CharArray(end - start)
224+
TextUtils.getChars(source, start, end, v, 0)
225+
val s = String(v).lowercase(Locale.getDefault())
226+
227+
return if (source is Spanned) {
228+
val sp = SpannableString(s)
229+
TextUtils.copySpansFrom(source, start, end, null, sp, 0)
230+
sp
231+
} else {
232+
s
233+
}
234+
}
235+
}
236+
237+
return null // keep original
238+
}
239+
}
240+
241+
private class CapitalizeInputFilter : InputFilter {
242+
243+
override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned?, dstart: Int, dend: Int): CharSequence? {
244+
val firstChar = source[start]
245+
if (firstChar.isLowerCase()) {
246+
val v = CharArray(end - start)
247+
TextUtils.getChars(source, start, end, v, 0)
248+
val s = String(v).replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
249+
250+
return if (source is Spanned) {
251+
val sp = SpannableString(s)
252+
TextUtils.copySpansFrom(source, start, end, null, sp, 0)
253+
sp
254+
} else {
255+
s
256+
}
257+
}
258+
259+
return null // keep original
260+
}
261+
}
262+
263+
private class DecapitalizeInputFilter : InputFilter {
264+
265+
override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned?, dstart: Int, dend: Int): CharSequence? {
266+
val firstChar = source[start]
267+
if (firstChar.isUpperCase()) {
268+
val v = CharArray(end - start)
269+
TextUtils.getChars(source, start, end, v, 0)
270+
val s = String(v).replaceFirstChar { it.lowercase(Locale.getDefault()) }
271+
272+
return if (source is Spanned) {
273+
val sp = SpannableString(s)
274+
TextUtils.copySpansFrom(source, start, end, null, sp, 0)
275+
sp
276+
} else {
277+
s
278+
}
279+
}
280+
281+
return null // keep original
282+
}
162283
}
163284

164285
}

0 commit comments

Comments
 (0)