From 645c91ea1b9d237790d73349f51d059a79aaf2dd Mon Sep 17 00:00:00 2001 From: Gary Mathews Date: Fri, 19 Feb 2021 07:02:26 -0800 Subject: [PATCH] feat(android): implement ATTRIBUTE_UNDERLINE_COLOR functionality (#12471) Fixes TIMOB-24735 --- .../titanium/ui/AttributedStringProxy.java | 93 +++++++++++++++++- .../attributedString_coloredLink@2x.png | Bin 0 -> 8492 bytes .../attributedString_coloredLink@2x~ipad.png | Bin 0 -> 12896 bytes ...attributedString_coloredLink@3x~iphone.png | Bin 0 -> 19965 bytes .../Resources/ti.ui.attributedstring.test.js | 42 ++++++++ 5 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 tests/Resources/android/snapshots/attributedString_coloredLink@2x.png create mode 100644 tests/Resources/ios/snapshots/attributedString_coloredLink@2x~ipad.png create mode 100644 tests/Resources/ios/snapshots/attributedString_coloredLink@3x~iphone.png diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/AttributedStringProxy.java b/android/modules/ui/src/java/ti/modules/titanium/ui/AttributedStringProxy.java index fa580437b31..7036ca8b1de 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/AttributedStringProxy.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/AttributedStringProxy.java @@ -6,6 +6,7 @@ */ package ti.modules.titanium.ui; +import java.lang.reflect.Method; import java.util.HashMap; import org.appcelerator.kroll.KrollDict; @@ -19,9 +20,11 @@ import android.app.Activity; import android.graphics.Paint; import android.graphics.Typeface; +import android.os.Build; import android.os.Bundle; import android.text.Spannable; import android.text.SpannableString; +import android.text.TextPaint; import android.text.TextUtils; import android.text.style.AbsoluteSizeSpan; import android.text.style.BackgroundColorSpan; @@ -40,6 +43,65 @@ public class AttributedStringProxy extends KrollProxy { private static final String TAG = "AttributedString"; + private static final class UnderlineColorSpan extends UnderlineSpan + { + private final int color; + private final float thickness; + + public UnderlineColorSpan(final int color, final float thickness) + { + this.color = color; + this.thickness = thickness; + } + + public UnderlineColorSpan(final int color) + { + this(color, 2.0f); + } + + @Override + public void updateDrawState(final TextPaint ds) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ds.underlineColor = this.color; + ds.underlineThickness = this.thickness; + } else { + try { + final Method setUnderlineText = + TextPaint.class.getMethod("setUnderlineText", Integer.TYPE, Float.TYPE); + setUnderlineText.invoke(ds, this.color, this.thickness); + } catch (final Exception e) { + + // Fallback to default underline behavior. + ds.setUnderlineText(true); + } + } + } + } + + private static final class URLColorSpan extends URLSpan + { + private boolean underline = true; + + public URLColorSpan(String url) + { + super(url); + } + + public void setUnderline(boolean underline) + { + this.underline = underline; + } + + @Override + public void updateDrawState(final TextPaint ds) + { + super.updateDrawState(ds); + + ds.setUnderlineText(underline); + } + } + public AttributedStringProxy() { } @@ -199,6 +261,21 @@ public static Bundle toSpannableInBundle(AttributedStringProxy attrString, Activ spannableText.setSpan(new StrikethroughSpan(), range[0], range[0] + range[1], Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); break; + case UIModule.ATTRIBUTE_UNDERLINE_COLOR: + final UnderlineColorSpan underlineColorSpan = new UnderlineColorSpan( + TiConvert.toColor(TiConvert.toString(attrValue))); + + spannableText.setSpan(underlineColorSpan, range[0], range[0] + range[1], + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + + final URLColorSpan[] urlSpans = spannableText.getSpans( + range[0], range[0] + range[1], URLColorSpan.class); + for (final URLColorSpan urlSpan : urlSpans) { + + // Disable link underline, override with our underline color. + urlSpan.setUnderline(false); + } + break; case UIModule.ATTRIBUTE_UNDERLINES_STYLE: spannableText.setSpan(new UnderlineSpan(), range[0], range[0] + range[1], Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); @@ -213,9 +290,19 @@ public static Bundle toSpannableInBundle(AttributedStringProxy attrString, Activ break; case UIModule.ATTRIBUTE_LINK: if (attrValue != null) { - spannableText.setSpan(new URLSpan(TiConvert.toString(attrValue)), range[0], - range[0] + range[1], - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + final URLColorSpan urlColorSpan = + new URLColorSpan(TiConvert.toString(attrValue)); + + spannableText.setSpan(urlColorSpan, range[0], range[0] + range[1], + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + + final UnderlineColorSpan[] underlineSpans = spannableText.getSpans( + range[0], range[0] + range[1], UnderlineColorSpan.class); + if (underlineSpans.length > 0) { + + // Disable link underline, allow override with our underline color. + urlColorSpan.setUnderline(false); + } } results.putBoolean(TiC.PROPERTY_HAS_LINK, true); break; diff --git a/tests/Resources/android/snapshots/attributedString_coloredLink@2x.png b/tests/Resources/android/snapshots/attributedString_coloredLink@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d3ca7bff4971f27b56dca43d3ff6f230abca0045 GIT binary patch literal 8492 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 Y4&ON2v>G<4L!^N|Fu7lO*E#;b0QyHbB>(^b literal 0 HcmV?d00001 diff --git a/tests/Resources/ios/snapshots/attributedString_coloredLink@2x~ipad.png b/tests/Resources/ios/snapshots/attributedString_coloredLink@2x~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..dc5791a53e095dcc9f032556ff6efdd1bb1ee2ad GIT binary patch literal 12896 zcmeHs>0gpt+qPOZxtq3?wN0&E&QoemDVSNAnzN?nKxt~uDJm)=&1O#Jm?N3<1UaEN zRy%cz1399oP@<9|q9~vu@X)^R=lKiX5ATP4{eEkmi^X|yEzWfu=di8>8!OYphyFMu zBqVg$?ACQVA)!5=1ml?l`viTL(%*{$Aq=)Ny(WYlI=dwJ5asP`=3{9oq$C(05ZWvJ zhmhzm6+r_EOa9;Zrm%w0o`2>=goNV#g!cYd$4Vf7U2g>K*FFD{d)^EGS6eXmUgUqZ z_k4Q4=YPjSGrw;8c24fKKpcE@%LObXbV~MD6E?HExF(o9>v!GSK_CSW{O1Z5^a_IZ zOA6X4<1!WNn*w>*?7Fc-l<>;z;Q^Gl`~Ee`rysH=7Y47JCd+;^d-HH8u<4O@a+!nN zU$t(SwEK5TE|oo+?;hy0+dKMx?DGB3`^st`zSXx@I~R1W=8Gpb@u%^vtUq5z>xsZn zy?o}%74`-k!^iP~`4mG6IHYS<9UlQwM{ssXl-5ED|Ij1I>QWD+`6EN(_$8pkFj!q!#6;{YLPXtq z<<^*JAOvndg7u6dpob+CBs<`vg|wJk(~o#AucQ!LJ`D;6v4E=~V><6*Y3|mnr=?=6 zk}x3F_krU@E?k_S)1nr-6m3E2xCEGKgfPP>QA#QOEbMbF_goQ<@Cf}c4E^eDFh>Wvk(2a_gB!i!fEKzRKLFH{;)<^rt z#Y{tYCeLnwR$LcfZs4vjr42n_UfJHB8Fsn{dKc?$D3ui&aX(=rdq`$ki;x}lEq1Bj zqyT5yw)HMN)^t@r=-bPc8!u>LUa{7I6z!B=2(6OEs?>$WTq8;J_#(>xJSr^GX8&Hr z)9B4ZiMFGZUEJ^9j7M`1`JF2v7LD7$!i{pd*6Ek7@58UO40G^HtCOSTu%?t3u04zI z_?{$9-3+*#bY8=)ZN$oS!Y+IeM@i`v;e44_CNH)4+&;`aL$fe^*tuEkE)>#FSs({p zz*v@>zsQP&Fdt$E<^0wST;tiWu9u@?x+gYr%&n~5D*5lctO$6RasM1B_?uv1_E z{T=$9Wv9vS<#NGhD)5w>9Qa@-hdwqC!(RN2^W`+j9w+ZriI0jUk4Sc@N2^;Ird5#F z=K$lYe#|o1sZXPiOOBuWShN>QIum!nR-%DE7>el1+gX}d?vh2aGjD{q1yzWePuC5v z3lRHG4b?gZscT*CQIy!p!W8Xlt+)7G){jvEIRY?N84Sti?Avu|jgLQde$OC=Bv}7hUnp_?T^-wX&^dO~(jsK12r$kv z`)6(GkK6>-`|IGYcZ`EX9lwAa9e0h#S|rk>a$LkR<_362rTY`hT0R_O0Qd$^S*0DAF<4X2Ntfsfu73!w#a*CYUNtSoro~! z_sB9=!B8+k6ED(2%;ON#oOy$;K#;*k?Q6Ct`@U0aA#DnG*4HSMOzg z67E}Ty6!I26+UseV3VP}6r#hH87+|+cSG?L-}=rI48(P1JGIQ==Qv#!?K;E0P;yB$tlzRc=kx<|9!1Xa9Qo>0HqzulBjn?N^9n(kqSxOH;F>ULqwj^Zuq z%2D$gZ{@`75q2=tV!G>%p1N(4d{DW35t^(k@9mES&Cc`RF2<;ml`(f_rTrLLo7K6y zup<=JjfxX@sU9eDwRKmv=J6QIf)n2SwuQ3W8N81eR=ta8X_7b?*C885a<$;C^v{I_ zctStKF_9|ubk%f}=J^7L)o%+;%o0^=vvy!8wA`It8iZdYOlV~dz1zgNDk^Q8>-#Pi z8cc_Ux$*XeyLv9S-7D;K!rleDi>tuTdwZmBnR{+e<#&X$*r@Q}@HbGnELWNDNLC+@ z)hE8)x;@8DKLs|c_PSrXJHx-#;la|E;hY&{GWO(XiMJUd^0Oo3&Ieryp^i-W(Q`D* zdXsxSV5@(OZHxifPm5<<vHE^)+XVr6Y4ohc zLYd%E2|o)(-yx08Z(zjQ0Dkw#AK3fX+D`qESQ-?q15WLo0SABROhi58IH#rybQBf_ zrYSzVgWS$r?g25TnUOp+@-96l%h3_EQW#{msufh?wp0W_}`n_ ztruJLYP=1Ax-bpf{@g}&BOAT@*F?RIL3HZIo_Ooa+o{r-E@XdNv7G_$yr0MA(`9TMoutOj-?l)){vchD$fxyTp|dAo`YI`2&l|0LJ9kJ&SSc! zG-net8t+(M8L}#Bs(O*8k6G$xxse=32{4ArWn>s1rOR|1&jPIKMZ>H5_)9w0NcFKS zyNR=2u|Ft8*rFf*8vVV0U4==ryGc^_2)Z#~b^RSWKaG=Rl(_v{CL9ErN<*)K6-0-o zDB2zBBNI1>w*A~y10cjW?Al|YC&GJ)g__HME}HDgz@Hjcv%&t!komn73Ab)H2=$+< zO-Q>lZNu`t*b`*BIOim;9kR_KQ-AlBRWqN-jZ(24Em!>mdu^vkE@WHsXaDcsw$5{D z*IjN+)UV>!H4-8NIL)<|r=_wFPV5(NO46Ll-*&AX?wFme!+hg)Fo*IOpjhTAMDDoh#6eruf>wRK z_tVXsv@C84IBO`HVE*OZv!Uk{^)MW>7)tNs1)0Gk=91jHu22?qOCpRLJKUqjKg6q{ zGV-o8UzDzjC|4!@z3yE}ZITU(Gy9-Y<3GKsF1AQ0Yqj2an}C{##ZZ2HjnFqz4z*1Q z8RXUF7IFpKjp%b)b#FmVXa;enzs?q;0q?j*a>UGlY+v{-n%59YYC#d#rN_0OQ%xXQ zMN2$@TX;?>E*XUFZZL`Uxb5xY78TC&_QK-Mwjw370GeXyaumO-Ps|}p-el(&z_!)Z zvs?Y6Q|GsyqmyFVvw(Q0Jk1`Doy=iVCoycZAd)7skW?!lopM`%46Lq1Ze$iqdZxA< zV4X+IG3+nYQ0B8#LNJjJJd4m7Ph(QnjSHcifl<~Al5r{5M6Z$d8iK-sDxniu=s|$= zXjWzjr$iM#I`NjVFZxL7Kr|=6l@bRk;fi$>n;USFaA~-k0)!NaVy!EvB8%F4B#3G@ z{!;KuWhq>6ZLDhR;uKKM8q-+Y2ewafl1r2gZS2tF6eQf8P}DA($|hmss`|jbDF|W- z{x3V8>F-T7$^l;dCkvBq=%!`H0 zZKC1zGM`E);6e;Gx(lmyJ@RYN9Ai4rDT>-NP8p;BfrF(bhGDdOc|Hh>T=Y$L;!l z-w$m;=6s?C*8WtV%ugQ$$^tQ0o|7P8d63nN+eTSCZZT>cDu>5zVCok8c`4V~0IlHN z+cdBrwMLaW(DD6Y|Lr!TiAK)Jpu753gmdI7!^E<}>$NZsPh2-M$6Pq<9v-o+Vj&w*sI+`*4(yEZvC?!K)rnNZ-_2);-G!IO4ZPjG2 zYgfaAVseB?1!kr>$6(aM79mk#dG7+^<)xLE`q&{sciB+`&nc|nXd0l8zihXvfPMZk zsUR~*Hvw9Ok_3WfF|+6@R@x`R#WSf(ujFl8MpagjDAN3Vi>t`RU+ zssxGj9s)Iq02a?NI}L5)z^-#Qyy{gze{h4S__Xq<44J4}LRbg>xz4tShEn-EIG~pd zr1vk#(_H$%pg{yLoTODP_q}1Kz3PXTT1h8H;%txxhX2em4}>?9oOj9AwC_FGRH)}Z zH8Cnp(OP>d2ik^sM)x;;CX9F`9c9h5kqx=dKcy~b{KrW%-j8%*dc3yNQ-*y5wz-*+ z`CGH{oo*BNa77uI31GQ3j3n`_z1Sxix9ny84*`QMoWi?t?{K!_Iw8ZR`Ip5shg80iKEh=&97GrU{jw$SrRhRzz zIj6@Napk7M>LCoig*<;kE~r@bF}{Cm`HeMjaPQWOoJT6wFEuq z!An+AcC3v%zssL3Y3~Zb61@Wt3)qAyo5-y9jayA)%W7a6ukX||F#SyB+%TU`u{1V1 z*NEf3>torO27l9fCF^JwAAoyqs>xnxTz^UE+I{~$XI3O_BH)@Np-|djafx=k;2S90O%`D_ofYGOan>=>f`uq8lB-;{p3M9a5srkhuPSL3LM~ZjR z?np_^ES=WaVmWQ8^nC$maVX?Be2O_T8GM=paSA1CpJy?#)-v+nU)9%j(9hb+6%wuY z6WKH@T}sX4cVwwrk#i0=PQj?zkV73f{$q#R1)NpJ>-cOouh&&4p(YAIKxZCPd7smY z!bqi~t_QGXSG;Jzw;DUCpJ~wDuZT>}kxPAo14Y;SEDA%Y@8^#0#8FqsG5`; zVN1=XLpM5_={NGOnZwOx@y@8730JdlAGXs7QCI%^jcvO@rd-cA?`QqQw=c(+L|I>NM*?<=o%8%3{&z%gO8cI6w+Gv6{CTvwtD%70qry#cQ}Q(=(H7 zi^%bc*j;@WzWPuKA)9!T5MsxGaPKZqoPQ&Q0rw|PHR1f7s@X4^+Xg)}OgoEndwlb@ zGK__U_qHCGEWt@wlt&)40b9GhpK-MxQZJ}ZYWB`SjKv`y5s?T~W5s!Z^y;?70xYjI z314Tq?ZkO;gfrPS-pWrjm*&_mNb<>~4?~m0S|t*?gKFYB_ay@2E<{%%S9f3FI==0G z){Po2p@`<4@Z-0$w5o$UqaLkGhST^p`6GM&(JYfc(kO+QUXkcyN!s zR71Noty%RC6@P;iuk^q#80%MF(v<3g#O1UdW(%n}Hdf-dWjT;O)m()Z|G+EAzdzh{ z#-HD7E_Q-0LQQ0V>@?#O9(a#@YMsz4a?D|DE38hSai;8Ac572AJttfQ7>O8-o2*+_ zkT@$P@d*!wdT%MBOepm!%8Au=5g+lTToTm16OUnUMdY9kP#Vys_mg~Kt9^QO=8-f@ zo%j-Wm!1y*^`^SBsXP_e6@1meSvc`Vehu!kXC$qGFtOG7Du~^_O5IGUl0-=70Yna* zyL3{7WMczrR2^_Lb~|?j)@EmmuMqucS<{~E&ceTFJ#x5iWtt9jT>%l0gNJsJz|V=$ zqr*fmBh8!b&@Zsw=bT8q=o(V9QzQa*NBpIct&)U#?#FyBCD{d{(@A}+sLMsXyN)@V zGxeYANr&q2U#a?Jzcg?Kz8D?Bn^Ej>OC1fBA&FXweGiPdVbRpa-0Q|JC}${p>#RRZ zD+98fg=FUH9G$4_q*Y8WeH7T~d&T+_`Sk6Ls$#cC;VweeA&@AjH%B8C1)eHw>TT>; zZo@A(P_lV8#uQ%9^at2B>Ge!drR%*4WX&(AGEsN~g>sX?>^U@Vx7*hx)2SxRPzlu8 zZ#v(+wA;KAT~Zegq=G+gxpQjJNlij`03Afz2(nhh+-c#x$NpAaYd*9nR)eDaR^uG{ zWYYJEC&@obGKk-Bst*fr&uBA;lIET=?i)>o%5Ws6Dcjnw4cfc{yJ(lZ4T*c=>bv8- zTj(t|g`#UH!)(#?e6sTMTAcp}Ti)Gw49oO#Myp(wuEPy0IYa!NOfy_1k=qg64Kwt> zow1I{5I0;ffUmv|F)r@Ljja+KHtBxKDcXsj=l7O5S_n*&$%_oL3HVnN6l zwOi>!>SGvj^)qgaGTXW^qy5$80f_OeJ7V`T`x(CgSKO|bDkO4X{+OPzaqJW3^_0iw zZ@Qghxd?TgZyY|=1Ve8rce9kgL0Jb*aeQB*TV^blOwEJ+`7a}nmLjLA5yibRX4_{} z_vECLAh%k4XGGU#&kvP=LRCDak0VJk%S-7&l3F{zIPTb`9sTh|IqZUsnn%yyAyv7% zy$4g}@NBf-tJQ6r3N~pDpiiz$aVe6fLdtRO>(uYTK3?tkJG$Fg7pF!ClBE3im^jh_ z4}K}f2Vp0Bhof37{CXOPjX)2^0*x#ZRmJIbm!6lEl!_M_GH z@dy6t>9~g6^zOi{l0{PS||gJX%D_MobT|6sDY}jy?f24Im3PVLmMr<5|PXNsvJlXDeF3F@44 zPD|xVpn3?@b)}n1-qa^LWjg^1PMvKHX&pIQQ;;==SC?ZxfKHgrwAf63@7h zoRad;`Nu*cn7u3Vqn&@(fqDsENuOknunB)g&>zvKzcc|!-(3DSxTaFwfMZ$Cu5h^y ziC57Tpo(zh$d95}cRuia80&BY9(=PUWTr;-w&4(>sb%}CP27l#&d7G^^8icnCpk)O z+8K!I*jA|`aUvRStsZF@EZH#BRpk$jBTjgmJrS_~ARxA)Z|GU>_1m|5Xw&b1{Qj5k z-^R~9kDT{) zfN{~iwoplumJ;|wHojv?fL>i2KN)h}GPUvhHyDD2_V03Ea@RmEfC1FY$mJ`asaaEf zk?y@LFlmEGfA4n0tj*sDZgRAE@p<9>@^$BvcCbO#S(P{e zXswg!iTU)3vUL~lZhZk}&t*S`d2Nfyg3i`FJQEd?300VZScHLwM6pvvh5IuRn?WXa zC@D3&)}mYVM)CUw?$LDW%?0YILm^{K`9uc#y|ywbcZYrE zziRmg7C|d7+D$8|X(D+|HRNTvrN*PrG3wE%yFy)njA$j|%mtE;Pdc3Nyj65vWf1;$?X$rM?d(YZ)V55MH-<@5j9#)egL=IU;R`wV_%UrTL?7lD(~KRhLgWNpT1< zt%FyC8epqYRpfA#bwZQ9K?~81V|!-rF_uG_2++nto-fSE^|n z%HO^w5chcwLIdZqUl4L>Qsb9_)irwYzg+eS=c;?5bsO4JcvVdbb;s*RAwn5p^)5`! zhUKt}c)qiGhc|A%VVA;iCLZ!OpoF zpyR{f?_)`|K?6FxI@JiV?*jTv=x5;PfB4HHlK9;=Pjba`ZQ{o1<9jJ{&X;r6qIRjC zXK)hfDTk#kLWA}m^j&aT8CESLZHRyY25Zqy6@_00m&G1HZ5{Ij`vdf z=HS65)Ob|QL0b>(_F0vOc%ZAn7>dAtfyCKh87cmMASNXKi3&=hfwoZ%Y3fOVA!6^9 zGly(qP$PckI`{c0Y3i41XReg8l}lZRL|=CU@A~DXH4iU$-0&+JG0jwSyjwbtudOLS z5mxfvOJugM*bBmY`0EiDb6q7s?^&WdEeYsQK?GR}cxk$FcEv!P=k)1gB0VPqB~ zA6DwPr=u?b?!bc&2a!s4X|L}mZnFcrZsYk=$N>Hu)=_6-8eu2pa+Va8klK;Nnhqzi z8$ll~FKJbgqg&CAP&g1r-O`a#x4B}!o!6L3#>chTY!{ws#yQfgaFxignJQJ5N!oOJ zqg^k*@&UOA(ZOCous=_4(#>s|8bJG?eFMflwGYFBuEpo7BIcO!Q`8jc!R%H z!t}mIR-%#-M(}->&~Bw(atl18Rw)umxmU;8bfGUDHcq81tMq3G7V%OS4BYkdN z*Ox{0o_g<&CP$1mm`nSP#ie6oOUj-uwfc~%l3PhbA$P90Epduw1my!Dy~>&YVZ*&y zM`L;iDw%ug(;YR+79*y=LrzvcAnb=YLnc$=u1`2UytGN$gu~vdU3zYcU-VC71S%N# z8Q-Hs%$`ay5xbq3V^G*^&zyQNy)5N#9TuD{*X4+4OR!Pm7?&RI12}3lSE&0&aYIRw zjPYjVm?pYN3AN*+IOU0~GKyP0)5JfQi5czh3m6lbtl8}hLs2emo^iyG^Li|g2c)wu zkhI7w4E8*rd`FP54Zo~cFHRYk;Jn-!eeKPM10+6E0!kVHYpnwh zoO}GqL2S2E>7!^GEYGRwV;z{6yd;fHC6svzj4xFY-efm^Evk43R2=@;E^qJ@9btbL zb%Ns$YldHdc8mm;Rt4qr3f>D%XSSyp)gGFhn{jnmG%u}s@sY{BTZ%EaoW6bXLuuX7fy6D31;=)~!Obn)G1dr5?0AK}1-BMw;YP8AP~pU8bu znWf1udGfi+*MuD4zg^fb_1Z?AE_ zev=n$M2!>4IA74_;WtJ>{#L5FU&+0;(b#X~xn-!%@{G3m{tVrVIiYf(*P|GuQh$k} zNj$5%wQ57Dv^T?(gkIXZQp#PCqtP95TY&z;ZW(!~Fvv+YX%s`tpM1?5x4ut0KE6-8cO0^`*h8k84$7h-ckDr+t~V z>#(!lkRfp4R7M)b|mSA&_O;hA|@n&Y8h5>mQ6JSk1p zr%e#+$j0UynTv|F(njTwx{luJB5+2a?X>r_scudYY#I&87U~0VmZRXZ0ba-;B~P6u zRmzrsRW+O0Dkx2~v@Q?pId}sfB$oU-n((W-1Zl6@c^xU`a2vhoM_K~=ifb_G_nNl# zy7kJI;8G6bS!hb6k2_J0Q+#iEnzOkQ=gOTP#X)8z;D8u-cNI5@5P{j*(D&#CXS8pZ0^9z- zZU_=QftwL@Px3`D@(2uhVMV!4P^jR=zZcX|0pfR`|KAS;itJEzyDA&KUXKhG%OR>l zmL?BLxqNYoYlP1euKfzI+5YJntwF*g}Och;=5_>iae)?J%7r{9FQS=eh z;F;(pFK>pkAM2j*3jd(*uj=$afBqi}{M-F&jeimGFCqSQ3jcb-{~tZ!r(pRtzSn~s Xo6JZ1Mg=d`gv@SOT}NKK_w;`Nvj)`m literal 0 HcmV?d00001 diff --git a/tests/Resources/ios/snapshots/attributedString_coloredLink@3x~iphone.png b/tests/Resources/ios/snapshots/attributedString_coloredLink@3x~iphone.png new file mode 100644 index 0000000000000000000000000000000000000000..d33d094f11dd26f4d7108a8d300c3c934735b038 GIT binary patch literal 19965 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 W-K0~!Z@58cqj|dexvX { it('#createAttributedString()', () => { @@ -65,4 +69,42 @@ describe('Titanium.UI.AttributedString', function () { }); should(attributedString.attributes.length).be.eql(2); }); + + it('colored link', () => { + // FIXME: Does not honour scale correctly on macOS: https://jira.appcelerator.org/browse/TIMOB-28261 + if (isCI && utilities.isMacOS() && OS_VERSION_MAJOR < 11) { + return; + } + + const view = Ti.UI.createView({ + width: '960px', + height: '220px' + }); + const label = Ti.UI.createLabel({ + attributedString: Ti.UI.createAttributedString({ + text: 'Check out the Appcelerator Developer Portal', + attributes: [ + { + type: Ti.UI.ATTRIBUTE_LINK, + value: 'https://developer.appcelerator.com', + range: [ 14, 29 ] + }, + { + type: Ti.UI.ATTRIBUTE_FOREGROUND_COLOR, + value: 'purple', + range: [ 14, 29 ] + }, + { + type: Ti.UI.ATTRIBUTE_UNDERLINE_COLOR, + value: 'orange', + range: [ 14, 29 ] + } + ] + }) + }); + + view.add(label); + + should(view).matchImage('snapshots/attributedString_coloredLink.png', { maxPixelMismatch: OS_IOS ? 2 : 0 }); // 2 pixels differ on actual iPhone + }); });