11package com.omega_r.libs.omegatypes
22
33import android.content.Context
4- import android.graphics.*
4+ import android.graphics.Typeface
5+ import android.text.InputFilter
56import 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
810import android.text.style.ForegroundColorSpan
11+ import android.text.style.LeadingMarginSpan
12+ import android.text.style.StrikethroughSpan
913import android.text.style.StyleSpan
14+ import android.text.style.TypefaceSpan
15+ import android.text.style.UnderlineSpan
1016import 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