From e01f39e8e1083af25ec187e9ec7fd9b7bc11868a Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 27 Feb 2019 14:14:33 -0500 Subject: [PATCH 1/4] Apply image editor design. --- .../image_editor_brush.imageset/Contents.json | 23 +++++++++++ .../marker-32@1x.png | Bin 0 -> 355 bytes .../marker-32@2x.png | Bin 0 -> 640 bytes .../marker-32@3x.png | Bin 0 -> 960 bytes .../Contents.json | 23 +++++++++++ .../add-caption-32@1x.png | Bin 0 -> 326 bytes .../add-caption-32@2x.png | Bin 0 -> 559 bytes .../add-caption-32@3x.png | Bin 0 -> 866 bytes .../Contents.json | 23 +++++++++++ .../checkmark-circle-outline-32@1x.png | Bin 0 -> 503 bytes .../checkmark-circle-outline-32@2x.png | Bin 0 -> 966 bytes .../checkmark-circle-outline-32@3x.png | Bin 0 -> 1681 bytes .../Contents.json | 23 +++++++++++ .../checkmark-circle-filled-32@1x.png | Bin 0 -> 383 bytes .../checkmark-circle-filled-32@2x.png | Bin 0 -> 724 bytes .../checkmark-circle-filled-32@3x.png | Bin 0 -> 1267 bytes .../image_editor_crop.imageset/Contents.json | 23 +++++++++++ .../image_editor_crop.imageset/crop-32@1x.png | Bin 0 -> 286 bytes .../image_editor_crop.imageset/crop-32@2x.png | Bin 0 -> 531 bytes .../image_editor_crop.imageset/crop-32@3x.png | Bin 0 -> 731 bytes .../Contents.json | 23 +++++++++++ .../crop-lock-32@1x.png | Bin 0 -> 400 bytes .../crop-lock-32@2x.png | Bin 0 -> 681 bytes .../crop-lock-32@3x.png | Bin 0 -> 981 bytes .../Contents.json | 23 +++++++++++ .../crop-unlock-32@1x.png | Bin 0 -> 409 bytes .../crop-unlock-32@2x.png | Bin 0 -> 689 bytes .../crop-unlock-32@3x.png | Bin 0 -> 989 bytes .../image_editor_flip.imageset/Contents.json | 23 +++++++++++ .../image_editor_flip.imageset/flip-32@1x.png | Bin 0 -> 458 bytes .../image_editor_flip.imageset/flip-32@2x.png | Bin 0 -> 893 bytes .../image_editor_flip.imageset/flip-32@3x.png | Bin 0 -> 1148 bytes .../Contents.json | 23 +++++++++++ .../rotate-32@1x.png | Bin 0 -> 431 bytes .../rotate-32@2x.png | Bin 0 -> 868 bytes .../rotate-32@3x.png | Bin 0 -> 1266 bytes .../image_editor_text.imageset/Contents.json | 23 +++++++++++ .../image_editor_text.imageset/text-32@1x.png | Bin 0 -> 491 bytes .../image_editor_text.imageset/text-32@2x.png | Bin 0 -> 989 bytes .../image_editor_text.imageset/text-32@3x.png | Bin 0 -> 1550 bytes .../image_editor_undo.imageset/Contents.json | 23 +++++++++++ .../image_editor_undo.imageset/undo-32@1x.png | Bin 0 -> 446 bytes .../image_editor_undo.imageset/undo-32@2x.png | Bin 0 -> 808 bytes .../image_editor_undo.imageset/undo-32@3x.png | Bin 0 -> 1237 bytes .../ImageEditorCropViewController.swift | 16 +++++--- .../Views/ImageEditor/ImageEditorView.swift | 38 ++++++++---------- SignalMessaging/Views/OWSButton.swift | 17 ++++++++ 47 files changed, 297 insertions(+), 27 deletions(-) create mode 100644 Signal/Images.xcassets/image_editor_brush.imageset/Contents.json create mode 100644 Signal/Images.xcassets/image_editor_brush.imageset/marker-32@1x.png create mode 100644 Signal/Images.xcassets/image_editor_brush.imageset/marker-32@2x.png create mode 100644 Signal/Images.xcassets/image_editor_brush.imageset/marker-32@3x.png create mode 100644 Signal/Images.xcassets/image_editor_caption.imageset/Contents.json create mode 100644 Signal/Images.xcassets/image_editor_caption.imageset/add-caption-32@1x.png create mode 100644 Signal/Images.xcassets/image_editor_caption.imageset/add-caption-32@2x.png create mode 100644 Signal/Images.xcassets/image_editor_caption.imageset/add-caption-32@3x.png create mode 100644 Signal/Images.xcassets/image_editor_checkmark_empty.imageset/Contents.json create mode 100644 Signal/Images.xcassets/image_editor_checkmark_empty.imageset/checkmark-circle-outline-32@1x.png create mode 100644 Signal/Images.xcassets/image_editor_checkmark_empty.imageset/checkmark-circle-outline-32@2x.png create mode 100644 Signal/Images.xcassets/image_editor_checkmark_empty.imageset/checkmark-circle-outline-32@3x.png create mode 100644 Signal/Images.xcassets/image_editor_checkmark_full.imageset/Contents.json create mode 100644 Signal/Images.xcassets/image_editor_checkmark_full.imageset/checkmark-circle-filled-32@1x.png create mode 100644 Signal/Images.xcassets/image_editor_checkmark_full.imageset/checkmark-circle-filled-32@2x.png create mode 100644 Signal/Images.xcassets/image_editor_checkmark_full.imageset/checkmark-circle-filled-32@3x.png create mode 100644 Signal/Images.xcassets/image_editor_crop.imageset/Contents.json create mode 100644 Signal/Images.xcassets/image_editor_crop.imageset/crop-32@1x.png create mode 100644 Signal/Images.xcassets/image_editor_crop.imageset/crop-32@2x.png create mode 100644 Signal/Images.xcassets/image_editor_crop.imageset/crop-32@3x.png create mode 100644 Signal/Images.xcassets/image_editor_crop_lock.imageset/Contents.json create mode 100644 Signal/Images.xcassets/image_editor_crop_lock.imageset/crop-lock-32@1x.png create mode 100644 Signal/Images.xcassets/image_editor_crop_lock.imageset/crop-lock-32@2x.png create mode 100644 Signal/Images.xcassets/image_editor_crop_lock.imageset/crop-lock-32@3x.png create mode 100644 Signal/Images.xcassets/image_editor_crop_unlock.imageset/Contents.json create mode 100644 Signal/Images.xcassets/image_editor_crop_unlock.imageset/crop-unlock-32@1x.png create mode 100644 Signal/Images.xcassets/image_editor_crop_unlock.imageset/crop-unlock-32@2x.png create mode 100644 Signal/Images.xcassets/image_editor_crop_unlock.imageset/crop-unlock-32@3x.png create mode 100644 Signal/Images.xcassets/image_editor_flip.imageset/Contents.json create mode 100644 Signal/Images.xcassets/image_editor_flip.imageset/flip-32@1x.png create mode 100644 Signal/Images.xcassets/image_editor_flip.imageset/flip-32@2x.png create mode 100644 Signal/Images.xcassets/image_editor_flip.imageset/flip-32@3x.png create mode 100644 Signal/Images.xcassets/image_editor_rotate.imageset/Contents.json create mode 100644 Signal/Images.xcassets/image_editor_rotate.imageset/rotate-32@1x.png create mode 100644 Signal/Images.xcassets/image_editor_rotate.imageset/rotate-32@2x.png create mode 100644 Signal/Images.xcassets/image_editor_rotate.imageset/rotate-32@3x.png create mode 100644 Signal/Images.xcassets/image_editor_text.imageset/Contents.json create mode 100644 Signal/Images.xcassets/image_editor_text.imageset/text-32@1x.png create mode 100644 Signal/Images.xcassets/image_editor_text.imageset/text-32@2x.png create mode 100644 Signal/Images.xcassets/image_editor_text.imageset/text-32@3x.png create mode 100644 Signal/Images.xcassets/image_editor_undo.imageset/Contents.json create mode 100644 Signal/Images.xcassets/image_editor_undo.imageset/undo-32@1x.png create mode 100644 Signal/Images.xcassets/image_editor_undo.imageset/undo-32@2x.png create mode 100644 Signal/Images.xcassets/image_editor_undo.imageset/undo-32@3x.png diff --git a/Signal/Images.xcassets/image_editor_brush.imageset/Contents.json b/Signal/Images.xcassets/image_editor_brush.imageset/Contents.json new file mode 100644 index 000000000..1a496d37d --- /dev/null +++ b/Signal/Images.xcassets/image_editor_brush.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "marker-32@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "marker-32@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "marker-32@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/image_editor_brush.imageset/marker-32@1x.png b/Signal/Images.xcassets/image_editor_brush.imageset/marker-32@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..33e6dbc8edc50fc097f5f8299141ae95ac0cba3c GIT binary patch literal 355 zcmV-p0i6DcP)1EDTw-P{snw$NpEn>{N0~wqQ3F5%%scqmVk%^?f`56+%&z59t>n+Ab<}#o~F|B2W9UyI1zpF{Zujo-ju*C z474S1t4%oQ^%UI!24?P}tgn=-j0$Y$&fIDVir_^CEa0;ZSixr*u!LtDu!d(Dr~#jB zpcZ_Rftv7-25Q5n7^n&#H6W+lhyhdhI|EM3-_?XmL`|tSkp~TVh@unBP^shi?2Q}k za_cE5{j1RCt{2o9$7;KoExCb^I?Sqy#(A66nCtfp$O%c3|j0JD>zg zfDW*Nb)m+|}mWOK&%(>bCcpSpz4OlAyahSs2^|Bg?)`%%!Rc zox|)N5CQxzd>cB4ch1mYE%0UpL;y#JHz6Pbm<3*4Km;%wys7~Cj1EHNg4Y(X3_;|C z*B0=q4X-Vrk-y^9;8g_(^h^_8U4S;cg%zL*uPVR-yqW+j@G1f4L+*?i}2Y5 zScT6bz%u;k0<6Q2Dxd`T(FBwNKZ<~o;IA&AH2A9uC=vc@0!oFyihz>gFBVWb{6zw8 z0p8Um{~m9>k$4u&+(l;16mD>e5zCVR` zc_E@=-sxD9D?pa`_GsycQpRQ%N#6>1O~8Kxcq-r=&XJj?;ex@0000y95KI&fq}Wm z)5S5QV$R#S4=3I-5OCXn_DQ|Q!yv~O3R4`|6=hXKTS^=_FE|D{>d8*v)03USWwzKk zhDXTlQ{wvYTlsFq%_-++eBYq^WU@k9o2y=a&)yuLh64O(m6(Hzx6&=(8KwstOWn9%=s-7Y|5X~@uKABPXUvA>g;LrQsu*WncN!w z%cLz^@X4*~J?q_udWH|iD`X8_xjh>8Cp=Sah`lgtzVrOKU7`~>>fgubH|c)*y!d+D zM(g5-d*^Pht9A?DygI@>L|~ux=^GnE)EzkfX_e_*<-EYLUxG2}p}3-ham`nzEr-K( z7qI-e#hRnk5OVOko&sB_Xw6n8n~1L;VlP^WU5VLaCw*IS(N1~xD)Bcnr=LHwNA}Cy zBWpA)x#yYGe~Rxj4_oPSAjrDr+CPs=yq3nr%(>I&hILh_tQ58Ls!&)gX}5*@0-yXW z)+r3~QLIy#c$3c3CmY8$*PkDxZFr3g($1NVpA^%Orv=__-3j!t=$ zUP5)?{$8KD?x`2mj`$XBN?c>a{O=c2#=4eO5BGZ=n0oWoN+GG{dY!%dI|MKOJJr>q zXPp+ebzAJu%Du++2dmDWd6)n7rRo9QuY3=pFI-SODCEDb;mks|$py-_yB5ywS=i?P zR&SN6&oxO07jB0AO>M7#KE8fs%j>^WpUh)?z2I5ZKI6}K9+W@NP0!Z;clp0z2FK^y zy&c=GrEV)xdCs$=)VQ=;tv2;p>p_42PqqjCW(6ESTmIbs>v~DfwMLfd4m|sq|KBS$ z<*nPVa-aE+>M_qBujhZiwf&~`l3DT}XJ)-w&eNLA)PfDEFh7vL!Fu;_-QW49z%0q& M>FVdQ&MBb@0PFmrPXGV_ literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_caption.imageset/Contents.json b/Signal/Images.xcassets/image_editor_caption.imageset/Contents.json new file mode 100644 index 000000000..22defce50 --- /dev/null +++ b/Signal/Images.xcassets/image_editor_caption.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "add-caption-32@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "add-caption-32@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "add-caption-32@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/image_editor_caption.imageset/add-caption-32@1x.png b/Signal/Images.xcassets/image_editor_caption.imageset/add-caption-32@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..2d1325e014eecf410f1d981026df1a85d07607f9 GIT binary patch literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?3oArNM~bhqvgQ1FMR zi(^Pc>)UA$d7B(~TK7x+Kg^M{K-pqe-T{uj2OND3(guet9eI~YJZRCdv=FNB{J+Kh z_qy*pR-H4!?nAY%;sK`+^w|cR!ECpp6guM%u8S1 z@14pf$XVsOYE{KKMWzEsKl?PqF|DflHb*#NH>bV3f>NZh!ETPwts^$?fdBV}+)3w=0nR-&rsm)XTV>ZcE-YXesK)4@61>=R4 WEb`8ktgnG#z~JfX=d#Wzp$P!D(}NrU literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_caption.imageset/add-caption-32@2x.png b/Signal/Images.xcassets/image_editor_caption.imageset/add-caption-32@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ba403c8b3fc8b64806371d50524e5d886ef121b3 GIT binary patch literal 559 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9E$svykh8Km-ofr0U% zr;B4q#hka%7jq982-L3o6>pZ|c7mhl0Ec8_phTmf5tFh3Q;vgrL8{;t<@DJuSuZD+ z`)^9Io%`T!x3K(2!K9vqW74JH@*1QcuzTuvO>4AKm`4QvO} zEq|@FV76eiV0w|<(|$63p4UV7WDm32jGBoPx0p>XIz6$R@%rl8%7stzl{52-El&pi zH575mXiNR7_(GqddgBwF2W8%6l50=41P3Oc6D|CD{Is3wzEek|Bs^~~2+Xp(vPIKL zWxt&I{BGl{BR5(~_4ImgneC9&UAnewL-*SSmeWtmX0dHanz!zstE@f qy95KI&fq_}X z)5S5QV$R#S7YlDW2(*1Ss^phFBHI!oF#Um00lTK+%LB44LP1Tjjvnk~>;VVU^e%bl zeZS!4@yGFdy|KBRpHS1^lBk~!-a(;?7WWLlMQAYaur@4YFc4vAV>%GPAi>4p%$R^8 zxMzO$cg>}1J8U{U{_Qr@E~%aG^Zx(Id_haA^*Y~WU7jpoC3ohM=9BZxdYMX_gePAA z6Vki&&%AQ+yjz&eslNM2-L0Ev-tdCp)KY* zS#Nzdoo&2-Wm|Og>-8_b`N{lKd+u6vVCLt4N5h%6-+I>3`Ete9Qy2HIUweN7OTJ2r zyQ2ns$hp5i=hQD>b*=SsSC(5mLGjQ zIkI_*p=RPNo&8d!`GW`x_OQNu zQF!OK5|hXdM@G%17xtLVJhp%#wAOU4ip&4G3pW>7RXHl%pZ%!BL_#eQdwi{)+cz(J zS4E};G%AJ9GA@5)7xA9&z_sOzPtVvh(^O`Wd6xZjQI|Z`ld;^-m7chUzHcu(A1nZg z_eD%+(r4E)@(1;|cFuGUX($hWa_-BPZgFn?G`4B0cTQM6W%bMXEB35AYh7ccz5V!P z;Xm68429QsO}-wn>5Q4+&sRs1XHH6u(vLp2sP_N1=Iy>~H|9L$n5GfEv+dB@;~9lZ z1Y_0~WS3^l$xi>o=q=vgyY#x`lmn0M#_q2B_|A#t^!4})X}@g~|1}&o`O$nX!9tn~ hdq5$l)Xt7SjO@lPMcrQ|#DSTH!PC{xWt~$(69E1JZeIWZ literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_checkmark_empty.imageset/Contents.json b/Signal/Images.xcassets/image_editor_checkmark_empty.imageset/Contents.json new file mode 100644 index 000000000..7ad664647 --- /dev/null +++ b/Signal/Images.xcassets/image_editor_checkmark_empty.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "checkmark-circle-outline-32@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "checkmark-circle-outline-32@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "checkmark-circle-outline-32@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/image_editor_checkmark_empty.imageset/checkmark-circle-outline-32@1x.png b/Signal/Images.xcassets/image_editor_checkmark_empty.imageset/checkmark-circle-outline-32@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..102b6d7e8c6abec42255544ed0facca47961d946 GIT binary patch literal 503 zcmV+!GflK%$2#cM>}7$7J`ht{c&}Ji})3nYK~gv|^8lO-X!?hU+Nv{ z^9_+-#n;tjp_BCql^`g*A*X*qt}wU2g#?7385lU^D1$4K3vON$pYDNVpjx7#a0ep# zkt1BSkF0>4(IF?|9rF9AECEby>0NHx64*`Xks%^NYseP(^0`e|JcfX94|(wz_OIHo z5wVGUd2Yu&5>VBO1vkme;xcwgE4H9^A0wdh^Pl6T^@T(kwY!o55ud-5nsT(cmYZnW t{4oJSdO81=EQ*>0Q(dlq{_mKGegRCG$rET`I<^1+002ovPDHLkV1kGj*{J{k literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_checkmark_empty.imageset/checkmark-circle-outline-32@2x.png b/Signal/Images.xcassets/image_editor_checkmark_empty.imageset/checkmark-circle-outline-32@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..19a221ccd15b1e280b97cc184082ae741c72a77b GIT binary patch literal 966 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 literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_crop.imageset/crop-32@2x.png b/Signal/Images.xcassets/image_editor_crop.imageset/crop-32@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..804fafee5d7671c41a5b56ef100d0efed1532062 GIT binary patch literal 531 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9E$svykh8Km-ofr0U; zr;B4q#hkaZ|K=Vt5IA~2^zZ(=3+^uHVUR9iacl5A#UZu3>Uq zn-065x$AGGSru`l?#7X8u{|m_#m}q0BsxemR0sAYr2V$%opkMf)t?>n850{c&T%li z6uL<$bQwR9Quq-q3 zJ$7`7q(Q@Uzx8P<*LT{AGG`q;BgSxV(z`rQyXR&5Z!KpyDcX>#WIOX_jFg;WHQS7v zb+ab!`Bh}N*>{tSo##HU#d2Oe5?2d%Y!)d~kzX3fzkP$?EtTv1pI!>ydCzi~A#jc$ zTgRgw0|$}x7RE&u$~*!`C7XE+Hm_+uKJy08&sz(P6O>N1D*m*7(zlTX>ik2?9c(_F5`n4B9E0lIU)~MszCK|Q*+w}F*CQmuBWvZla;-M`n zuC<=KOnJ>ep1Uw%QSS}TE`)mz?`8b>ZUZQ2-7d7hW_-Y}yut6`t>uUOfU(Tr>FVdQ I&MBb@0DDp4+W-In literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_crop.imageset/crop-32@3x.png b/Signal/Images.xcassets/image_editor_crop.imageset/crop-32@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c780199d2e0afdf6350f77ec1a5b7ad80e63d007 GIT binary patch literal 731 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGooCO|{#S9FJ<{->y95KI&fq|*l z)5S5QV$Rz;7k#B2Mcf`%FW%EGr4-88rT^~p1Fm$#X0{@qbA^U*z_1$l{S*Y`RXaIM;H zu=)AD@X|=e*^w?s-pk4V$lmt+uJg9%1#@c)&gCJ7LOQ!}@85y1a_GZOX%@;XaYR~6=v^>il|Fv>kIzN-ll8znvch*nNIp4>zL7K@Y z=a0pQ#aEZBt)psHeR28fa}JY?DhOUpx{9c1yl{-u!>@pGiI-uJ#Ew4#7kG!U;Ho07C+_Qv!6Hjh}>Io-DPti>pQzW zhQBSX6Ut`ZE9`4ZTfV@=I&q?`!R+0?&*$j>%d}dPd{#?hSzpXXp(5XUevj1;)T}?$ zzdM&J_3R+?=Ptd+S$F>G@$UWY(apXs73l4&YPqGe#kaV&EEW)q+cp2Lp6e3E@}-vr zmvJP#KmBgYr#CNN+?=9(`jYXI$X|=^PTpcV+iKJMtmO)6$8r^X&;Q+Ab^4ZhNpwt~ n7_P|QA*ON+DQT!0S2LTfUuJKx(Ow6bS{XcD{an^LB{Ts5>uomm literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_crop_lock.imageset/Contents.json b/Signal/Images.xcassets/image_editor_crop_lock.imageset/Contents.json new file mode 100644 index 000000000..eb76b325a --- /dev/null +++ b/Signal/Images.xcassets/image_editor_crop_lock.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "crop-lock-32@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "crop-lock-32@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "crop-lock-32@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/image_editor_crop_lock.imageset/crop-lock-32@1x.png b/Signal/Images.xcassets/image_editor_crop_lock.imageset/crop-lock-32@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..d7bf30c25c14a3a5a2a57ce17d0bb9e24e639a73 GIT binary patch literal 400 zcmV;B0dM|^P)QFc5vO%73y!86hKdgl^D|!U!E9-Jm06gpR-n zWP(?jUJ%OJ29y{ol_x#Pvd{M8e0MgKgf;QB>h3uKBt^1z14{{53Y!3gEU-*a0iFO< z*F9qF(~#uZsk;vqfM);~07pqpbhVhON6|dcc_Ou@>$o|RIyN?k0Gi+LjL%yl2;;6d zY-zZ2JS#AJpQ7a-~6ItM6r1SAikw%Da6Yag6pEHwscn9ScXn%hI8HHP-t?UPz4 zHDQK;>*hye8>;?yLrnic#8}dV*~|sSbo^l%#h_oI>--M#cVokKd0*o0Pq7$o=AvQlzedj0000F%1ClvuTh-z4!EC zegk9q*G zQjQI1Jp%yzOf!;KUoHR&Z#nEpJ}ajhbP-@u$KhZp97)cUQw{z@AXIqIcdE1j<|Dx@ zfMSdx3!oTdFfTyzaX?>~p8;6_#Uug%a{wCvWwk7wm5#gkRG8!*0AS(LV&_8NqstT( zswAwdj{qWaPqI`_Bf^btQPX)}gaygHD)G>F2`m&dKGs3!cU3mX+0NtdoNqm1d9peS za&f&9lAV&6BBl5H+erD=D=%Yp_?FZr2w)bB{)ck<%{3m6lYR}JNrE&7#hCpwM>~ z`5fNKV)b{K0bB3+(S4dq7co^X7?pi60xvPY0cXKzGoaJ`0C$PvGoTOdAkr!pKNHV~ zzcy95KI&fq}W* z)5S5QV$Rz+Z$obzh_t=it+}=#O9J_1INvb28<=lHxzsrlqYcJ^}ooS z{OT}^^3T%A-!0wkCx}{q6ZU-e%K5AhN1uMpw+Iae9@d713QMd>@mNgZp9zHiWoy`6sI=+p3>n_^eqI;=BR>+_bs{JnEOdK`){fJ0%%ET(yj z_Is~Bd(td+;iZ0+{<(jI=gC(7sC>4t#bQZ}c5CtZqU-nkOtT$?#DBhNDAf)redXN0 zlW|6r%+G+-bdP<@8e+Z_%zM()e6Brc=DF;vA`_zht#7uAe%4*7cHq0;4qv_wozSn* zlf3V(_$xK*wx`OL*6NMrF5IiQK12n~GAwvIyRv6fvRhznt;3B#uTcM*%e!ZEe&?>= zRBS!5W=hQS`8CT`id9S^7YKd}``kWf@&@1D`fFvD$1_+>xLtp+mG6Ak!K3J47W#k7 zhq96nCgKl-;`W%a2;O&nvV8mB!Z$AzBy%Rt+pnn0Z5<%1!{gozPUz4Ckm3LQ-^zBa z1t*V5vwFQgchACuF-?pmrc7tYYDbIjOXF8_n7?N`Y=e*&<)yA)D1d9x-|5d=A_@J_ zD>fPKOwauh6EUf<>JE@Rna&BS9f61(;@zT;8nt~ehMqJgGEC}sjvDsZ=#;U2G5e$z zN=>-IfnoEbvh(hP+A4S_`5778X0);l-*VYMpqdIu7FARKryfWoQR|K6I8C?&JAIyK zlkg+H=OU85NE|!^*xT+Ad$sA2%YmSVFCy}kdDX^5D?Rrgm1A|H>`k)lCTJmZV&))xVa0PYhkG^{sxkZvm3;BJxvF z-vDflV+VRi0D#XhBYF4X0-#dE;Xv|-acaR30T#6!j)uaS0%V^DjD`CMC;}KJ699Muumeyx!!lSMxSLmnSs%gZ{TjTo3EDaFX=Ek9 zEfa3vN!fiO)cc6cy)kv};XVS20ETgfB7kAKaaJDxBozkms#biR7X9|(pH7QY>0z5i zsbAr}GO3^UO^%0EX9;|xUr!#G0`U;%ZwA4KF2 XU`(vfd4!aX00000NkvXXu0mjf<#;NO literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_crop_unlock.imageset/crop-unlock-32@3x.png b/Signal/Images.xcassets/image_editor_crop_unlock.imageset/crop-unlock-32@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..0bdca3444c4c143d836ba73461f00b968c199d0f GIT binary patch literal 989 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGooCO|{#S9FJ<{->y95KI&fq}W- z)5S5QV$Rz+ZwsXi1=`Auf988_&@QlQ5W3U&md}IrplAzU1J|1p2H6K(KAZsu)1Kro zE$;vPGvcmL#eB)X4(jh-J6>9NwbQkwK|a3VeQhH{y21fTjsiBOIflXq&Im2_4B}8( zz-GbBc+R20Sl|H<3nueuM})+6PowkG!sRbF;aaH&3 zXNKg2&d?T_QT|d`A>q99!ke6z1=*F1E{WuQ3J!X8zTE!4NNZ=LnC2TTFYnd0-zRot z%-z}M`SOO9v!34mb5nD@(u^M)UA`IqP<*n8O`y^9{g>mb1=H>w+v?`BBgBq<)`HsK z@|*pNf^JR8WMMkBMe=V81Gh`c?Lza|{onb)(d+1t=Km)rUX@9VnX&8Z?)Y3s;U`G3 z+q1Oc`itV1xy1zyOB)vdpZ498L%HqZ8J`1tOO+PMf7Fz(DZi~25*d16Bjf8RgU=q$ z{W}?YM8l<4<;YChE;8Zwa|^#Bzvg4JlYAa;)M0&cZ@KKe!_uEk4TTqEtLseSbqP-U zH|Vf`b#1!0DrY5VIb=I#9y{P8{@LPW-%ak%7cUsBI&tRut{1Uq0)Dsd zzqxa&hn@Gj&;I8$Pud8s;&E@^di9Iia}}ASkLhdA+WCb!Y+c9^`#|ryOrsskg?+E? zi_GiOoA-(-9d((v<@;r0Rra>3eC5oHifDY5bg*CxO0o`^f9z|peyD@~ zWxlDGs?*=cEMXAjWRjDP+p$^U&F{eeP?qj;>t;J>9zn`3jST)BFU+m}eSMa$kQNNg OuMD29elF{r5}E*}e4?NL literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_flip.imageset/Contents.json b/Signal/Images.xcassets/image_editor_flip.imageset/Contents.json new file mode 100644 index 000000000..92ead99d3 --- /dev/null +++ b/Signal/Images.xcassets/image_editor_flip.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "flip-32@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "flip-32@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "flip-32@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/image_editor_flip.imageset/flip-32@1x.png b/Signal/Images.xcassets/image_editor_flip.imageset/flip-32@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..7cc8a1a6556454252772cb6fb08e525345af269c GIT binary patch literal 458 zcmV;*0X6=KP)R9J=Wm(gtkF${+7s_$h37@-?9BQQcoxDn_EjL>d!-GC8b z12%A#;^U(DPp}ac57d$+F6TJEzl0cAMChLObl24%Ucl7?nE92NSH<&|ncq#I$MPYJ zI1%NA=*y!g`pVugt$&Ch05Yav$AnEfSDlhz{a|aXPA)wVCQRSBiR1}Uq z3Oxc@`HLBiQTWgtZ3;+LedV^iM*vWSS9FU&yr}>ak-Ye}n-i#ZRqr7HzDb4xA`ogO zP6|`?Hg}~qp-NlsApmaE)FXiR={ANNRiO|zoSsJrv>Fvc^p^rmqk5+K$42IK zXA2M7sM2raD%9;zl|8QrM0_{R{r_+H-~x!~2Sk)IO%rFWCIA2c07*qoM6N<$f&kgX AhyVZp literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_flip.imageset/flip-32@2x.png b/Signal/Images.xcassets/image_editor_flip.imageset/flip-32@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c2b4c0dc15e073d3aca9d1c9a5dac35f7c6bc0e6 GIT binary patch literal 893 zcmV-@1A_dCP)V%a7KD5ObB0dJ+%v6BBi=Ob6 zLf?EE+8z+)u1UZBE9?OyFEDA<$6t;RE3Hvl{Za*l{KZlQBGE}eD%os+zBvL?Qm<(4 z#R>2hHn&(5pg$xaw0$LGoX|TgO+az=GmF&P{IPnsP{EK*u*2{aA)Np5(yi$#e01-A1Wb0X>GN z74c=L-zXtgDyo0is;mVVuSfyl#JTsVphp2C3{NY@870I@K?RDhytx2l7ApX#p!tg2 zs(@Mk#&!Z@j1po+ixJgFRo2>aF~TT`M1%B}B%s?UiG&ulhDQtWA`Jw%0Y*tAv=~)= zv`DQ@3ve7)sX!!hK}2V?kfjL-eLs`&&*(W6#b&6*@Mx$tLmGOFQqeIum*Htemr+8p zNaWRQc(efXG=Boo7}Z9pL;*_}o>tTwCB#Zn{bs|X1(>T_0pO&XG)hwnSkmycqRuEG zRvf-%y@UdQ3Y>OD7Kvz(^q&1n1RRq2F6g-|0oK(=i`3d0NhmT(B9W2MD-uv-lw@L{ z;n4!jJCp!dXp~SV^Xj8TYHb|^Od6%M!%)Mcg?JGI0y1klYXr%e5I@(LRN-KC^?ecr__ZD4dAI*JC^9RyI=vodT1Ji$9G2O|0hE?iy` Tjm67e00000NkvXXu0mjfUhat$ literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_flip.imageset/flip-32@3x.png b/Signal/Images.xcassets/image_editor_flip.imageset/flip-32@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9d82e6a1e214959705ca412abb38ac54fe3256e5 GIT binary patch literal 1148 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGooCO|{#S9FJ<{->y95KI&fq_NO z)5S5QV$R#S{{wG32(;#3W+-8J*}$hDyM$TepsWMu66Q;eml#VN*c=CDmgi2suYcL@OwumKLQ77?Ill7gX{Xrs_fNlf zM%#EgQ*-h*Mwb$0-+fJ+x9Crvk;5Q()b!s@kpqX=T;iNL#LxVhmdlvDm2vl_C)$?Y zzfIbAvMt&C-*v}=f6f;hPS~{+6t9L{8&Mwk5Yu+uv;C%R8Lq zE&+k;oCY>?fgN`dqnVa(Y|E%39 z-NAOip26_5^Dph2Qyv~SGifMki%pj{@R(d{yNh+Pbb?#cQZbuUX*T)C8yKv(wo5G$ z;}I=YmR8sssd45{M8?4l$+AKR6do>f-K8w0_R7uF;n#;`rnEY@u3hgKdBpOfmZ+w8 ztavSTAmiaOR?Syc<_wB=wm7)NZf6KMUo3cQIaaTt6vr(Yi>6W398r zUiPh6xgz@}!%OEgt0Wa)&0^Ug1XLMSG^=yhD~4-IceZ>HE7g_Sp!RUt(%CCkb0y>+ z^7Zx)48PrA%4Mz_>oV7>VJnca*Cp1Mx$oPd=tb=-f>{ex4*7PQ+{_AGo_pX`>+EUQ z7UdN(xPx4!8_;Rx%e+MVzKE{t-9-$2Av-cBzUvU{-nEo>f?ZF}q_9PKWeo0mK$YI- zlmgAQ8?c}}DRB$;+^_3lOf;P|vYgf#^+@Q;8uA3EDZqTq6$S_^>E`g!$ z;oSbPE27Ldb`)Gz+WKI@3T?IxsSlU6&U|cM{8wyV+#GL12JyVmI~?Lq7OfE8zb)v; za?WKDAGS^noMD^B{9AeMfk*a}HCQLuXK*|%cRxI_FFK}aPT>@$2{GG)+ijK?a?bj< zMBk=2TRr;E#6C;e?I$1m7FTM?Zm!@h_~1R^F=Gxh&g|>S@lfB^_sETZdkSX&ixUP< LS3j3^P6IEX=g1`CUhfiM#+zoj2>z+yWp-tfTmE60w&t8gU{!2Z40Z(Y6f0LFKyvU+>I#=55RpzmvXF002ovPDHLkV1m9cx9k7_ literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_rotate.imageset/rotate-32@2x.png b/Signal/Images.xcassets/image_editor_rotate.imageset/rotate-32@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6db61ae4a8322c314fbf70583ecb50488ab31f2e GIT binary patch literal 868 zcmV-q1DpJbP)GRc~XV|?>xgdM0^vwCYd;aO9n=nmh`;_`aed#`#10oB2uMI3fPk)9 zPxt`#U6v`Wf;9f{z*Y2sqT39R|QH07v3#Jmqe|^TC@w?yHrUcjP~(%(j4`%Y zdZe#cMBE7_4cgGq8*=J_0wi|xnmbV5KBrZhny}aTHbduUmAk!nhep$xTn8x0#o|nL zc}wR)qNecLGgk%*je1Hr7-MYg|74=Cqt|Jy3{+c3O)pZl*e`ddzBpCA>kgV8Z>~|` zz<+r}Otz9kw$203G69rx0(cfZiL>#+ZBEPRH}KFLtqNTP@Yx!79K`{* zS$SCC3oWD9xd`CNFLc720&df%=HQ+)dYy}aO`s4UBA=wxT0YY+ntMaM5Rt=SaOr@_Av3?YlfFv6eJP~U%l}f;tPzVr`jiC@ABpX8^ zKu9)*LV%F`!-XA>PA^9Qw|R+W#6y95KI&fq`X- zr;B4q#hkZu-{#&n5NPXn{eNBhiu9Ga3tqClV>!uIYW9KS9osuL$%8i!%vAWq_Kig+ zVvb;=jn@*5mwn%C85$cFseFq2_}atxSQIyx{QN85_JpW67^b~|f?X!CMFR^+U{4?AU zB$>pzgZ*)H2;;k>KNJER_#W@SJL{w4EC$(Q|BJhiv3nj4NIt{IP{%HQ$&%6GG8a38 zMO60r;_0gFo=g_ae>wD~d~Qp6qk3&YwZMYj)vEdo8zvjo?=a7Q5uWqx-rNVrX0$6D zJ>#*8Q9DO#U&VC2A6K{SztLLkSn)FK$X?AoOb4z-RZHv-;j~~}!eX-MwaHrXm^*5! z^YgZ_Fr4$=<$m!``q3uu{R`$vCFzkCXs zxLvL3`yJblXCBBFUu{!(bj@{x{MJSO9gk&{SOmW-IeB+5e|%FmdCQ`Ad$}2(2{7LO zc;#cCrz?k&mhi!&x+|;M_9WlD80fF&BoH`}W3e>rbMcM)9@@Ch7jOwtI<)1iQ%uW6 z>u0aZjy>#=TvoQGbjPv(eLH2{wyTw0P&}lc{nlH+`iZQTgG}-nruLQh_qd3^`O|$} zsw6*yJ>!9?)SlkrUvD3kFI&v?rQtQ3w9?hb67Ok=iy-IBh%XfaOWO`hY`$?qr_z0?Y%GIG-I>HtXN@w|gw3y}Z(Vq4 zS<4zb1tXyskDll*^FJt5ZoMSPuE_A2htIiv{6+mK+kRdNf2XE>d!@J@Tg}qj9o)C4 zed832=E-Vn>WJtl*|AyYwy1>Gv&ZsxQf^eF*DSfzr^MiQe5HQ;nvcz=9Y3koIA&e{hvbS)a;|Zu-dTI)@s>3&9 zcI(Gkw^rK+#;v?~*Qn#AYB8fJdH&IuxCZ=1vUus(?OL;C*Z3avGNY^L`$ zxg}=sF0fK~`J-~)E#G-9W{zi8FrS1hYn-ocmj!szU|3xN}oo87BA z**<&7?r@o!SvIL(%ix&Fk$`u<=J&e%i`n}4%=XD!!(4=CU1ks1zqOP#yT>rv^X;0` z-KT}$#eM7LpSWp*!p9ALZ8Nr(8U9X`O`fFfu*U0p(+)M`Y>6((`UC%W2T89N`n~#1 zWrH?D*u(Tk)7ym#D{IOsr&&boxa$1-oP_zSg|)#8j)xg7-iI0-u%Pi{cp!f^^;W$7 TsvXyWg&2dUtDnm{r-UW|^#d#P literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_text.imageset/Contents.json b/Signal/Images.xcassets/image_editor_text.imageset/Contents.json new file mode 100644 index 000000000..c0ff55700 --- /dev/null +++ b/Signal/Images.xcassets/image_editor_text.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "text-32@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "text-32@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "text-32@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/image_editor_text.imageset/text-32@1x.png b/Signal/Images.xcassets/image_editor_text.imageset/text-32@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..a28f50dd79943a4421cc2a080163f2f7d21e6338 GIT binary patch literal 491 zcmVJNR9J=WSJ7>RFbuV;`!5@m5!j%NzzB@M2p!=@V1$go24w`g z0i7UKs+T+CIB?PlQK?w60?u>%;yAv8z4vsayc`<+-xD|q{0Rb=!~$XFl89Pq^WJ|X zz-J}&0ZWM}1Pc!n#-OEzeMZ-gpuy;f=u6r5%|O_x*mXrjS3D0Eevb^)7{9&uV+f4* zcV;dU^bQ$t*l{mxF(a+zBXFU1Mu8u2bQPW#S+o`?tYCta%LFb|V6E{O{T6{nI2OX* z!Zv3JIN{etU#ur@(k6l4DmYb`jhd^htf}nGj}nN$&X|iSzAAgn&S+&v^_2yI#Vugw zu#;E34wOo?>3&dR921YbSnDONShn6zW=hyU7<553FrUsl<0}{|#0Arz65G{==?>Bsw*IR&wwMO$mqmun&LeS002ovPDHLkV1jC{)b9WQ literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_text.imageset/text-32@2x.png b/Signal/Images.xcassets/image_editor_text.imageset/text-32@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..26ea5757ac6ed831f132f248c674158018640d75 GIT binary patch literal 989 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9E$svykh8Km-ofq}W- z)5S5QV$R#>w-coddA1t;C^y;D|DQm};CYXOTDhw22r9bB7UHk2GF-4OENjM}Z) zu2r9oKRc0guFm*hJGk`DvNcW-Jiuqm@iG7S-tXei@A!Yb`{uC1cNUNKxSJmtjd<*A z7(d-OeZJpiOVH{w9P@*H6W!&U`KsSXEA6s+F4nZAIm&=prYd@0{SsdLImO5CiS6rQ zkKV34xBL4)j`BD45}uNK7RdMVolOopR;9jV`i|#ahfk^Z*oj=X$=Cf7y`|2+$jL#j zUh+M2iiC~u#h^^r13!=5wEQk|t;V_DvfliYh;)bO2bF0LICG3k%ssA4J>Wcb@smaK zd#i$|v%(8lGCo|MF1>bw-fQo}eIES0Y8#k;mv)p~yU%>LT=jv5n2qw@iTjK=UM^(( zCN*z|=Z_ax6dv7cW!U*a?q%;6=>(VNl7ma9=h)21pDS0~`$F}^`UR~5?5}Iz*UB@k zciI!$CH0AEx7s=BZ*S+`U#ZXJQOWJWyzWf`Pw*zEsTrL<#$0vxy+8Rb5m4x!|KKwJ z3tN?x)=d?&ZbcsQv&t-`S?+NAqSJGSmAKDl)gQeh?*5wPV6P_kb7GC__O@rlK=;`yTLdBFG#I>mj zd%FbZddy32V=(w~`^e(k`)}>*v(v9S{_9@jr9Z*9`<-))Kl)CXW3%##K|pV6XvxHo zUkNYXIs|TZ*>%kz$2>Su^?@s2aJQt1!{jGPFBV2AY@f8J!E_Vngl?aAuPW~N8g(l# zmTFOVJCogTH@_uX-IKdOb@{6gK1>0VQ_^3ob2!1H`cdkIzT=ApZYO6RPL@=5(0i7= zwL*#WlE&uc2_^~0zB^eiSuX%g>Q6$dlVJ*7I#iNex%1)>_$R{1s@jM?yW zqN77u#z&dcf+a_!A98cGN;BpC(`WegvM>MPnpt<41m7MFUptZiPJ{3w-XF0KPO;2g yv(Nr~?hJva{(ld*OcH7HYQRTq;w(7#muXkik{fkBa}EOYD}$%2pUXO@geCyBrnmzD literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_text.imageset/text-32@3x.png b/Signal/Images.xcassets/image_editor_text.imageset/text-32@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..ebc8902f1e888845a413021068f31162a497e748 GIT binary patch literal 1550 zcmbW1`#;kQ7{|XGleypGh^hC83f0f45(Pk~ImOJe{4(uq25?-^Chd&tXGJ1mzv!`&gny#xG(6JFHy zR=(IM=^gv1$TIw{mZgfZCKEC{B^LW*&2C~%+(aubZcUc3UfgCBpq0QJwXd#)SXBNTC=%v&yDRtdL5gL2 z`M1=iBjEin1^6Hc)c}CX)t1&qh=v|}dC0AAo@GE4wL{=W-&~{Dy8ik2)R07fk(EDX{TLP7>_s znD(^D+rAHh4pJrx6QRfIeXwpmue1cQLw5Q%J+CXxPOJ}XWt&IsxO;H4D-Kz53I;NU zW>7~mR1^DEUf=E?E`QcouSvldr0TIA5de~rIPnlEMyD#$d&RS&A!2Y_H}hOIomb#M ze)8xnFh{o4fvGUo)^K?yjI#j$RwRB`8#)~Qk49bZ&gh&XEGBx1t0GTgBkrXpKlB(1 z;;>3l$UZd+A>!i|gx3h0VEx{5Bd=2*qnH8kou~ana&B&6g%Xy!|TXIfuZ6n*o_DQfK9r+{^ zwiEuDQK8-zfap*jGg%LRWY(5#PS}%@sehq77BYVlaRW=KoHo91+YTwK?B%B!S6_{p z(boSmb-lB5-*+_cM5c&- zAH(d%i$N{c`y`0_ZFOOxW*Ju9V0S(i>9BA&xdEei)vFG@+>7)|Xqr@Fq7&x6RL`cZ zSy$PEV@og{QXZVQ9y$Oij&i?2QN|AwFOJm}pLLkcESEPwHP^aVMMFVmPqPwOyIsIc)-)lbZk^YL#!iN zd2#Y%j`QJxXQG;QeG#Airxshg?)+WyHYW^5e>}75IdZyfbbJUB;MIZSlMd?ZZQpg^ zE*)5^@iEWmf69MpP<%wInmKx}qU-S!0!rYTAr{}2K6uG5BJCQq@6+tyxbM@nT@=0h z6!Wjk8)^ggucEh8gx5>k)y%@^?*fvTlb5v2RyBvu&=y-Y^qmwUzG9m?SB9;W;KNMf z2Yj%42(%WbA<^$oUUt3$5VtM6Yz0m+Ii4MShRmPS{hlHpm=!D2+AT`1_xCSMGbP;l zBr2Gq6o(F-@X4pjaSvnc6y#S$*p0((HAWY8!Oc-bZeBKnAlu}sP`Ec?JDwgqF3;Qj zOpZ1|qFv2+#arX8iyJKTO!=>PLD4~7M(IXBS0 jXRiq;ANGrw39Bp{ottHyz6pCFeDDCu(e-!}(*O4VcLwEb literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_undo.imageset/Contents.json b/Signal/Images.xcassets/image_editor_undo.imageset/Contents.json new file mode 100644 index 000000000..d32e57835 --- /dev/null +++ b/Signal/Images.xcassets/image_editor_undo.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "undo-32@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "undo-32@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "undo-32@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/image_editor_undo.imageset/undo-32@1x.png b/Signal/Images.xcassets/image_editor_undo.imageset/undo-32@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c3ec13abd676e9fd7520dc560fd6b12da86e4cb6 GIT binary patch literal 446 zcmV;v0YUzWP)8eUWu8}(r%i5{VxEx%_3q%0@Swq~9Kmis0EgS`8X4FD#IulXdoJ8Rt37fDD&zTpXgpOLS^oiv^OOeLK6pa9Iq zIy)9pP7b8`dXbVpA&sf&I}zR1-j8zth3%IEcJ1kY}z9 zD%=CT(iWMX$T}p#;$(Fyc^zi~)%mE9_g2oD|5%X7AmeXKvWvTbhzh($07*qoM6N<$g8L}Im;e9( literal 0 HcmV?d00001 diff --git a/Signal/Images.xcassets/image_editor_undo.imageset/undo-32@2x.png b/Signal/Images.xcassets/image_editor_undo.imageset/undo-32@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..13a8413098012e6c1257813dc6677ea0f3d5ce7e GIT binary patch literal 808 zcmV+@1K0eCP)eJOLk-rUQt$ zy;%@Z6Ssc<82lMT#J>fI2$g}hUju+6Z6oB9GW>@N0K5W#Bex-N001}I=LDS&iF@|iYS&mv76OQPCZ%T zR@MoCh&v7o`O^I{xIT-Om>A5*WLB@>7qB0SgF_uh5f%^h{7pl)) z)8*VYMEp_ry+R=?0yyS>2~}AHFyut&3vb8-ks|^AXLqYC1hON5UwRjzDvJO{E3S(R z0q_Wj?JQ+WK<}O8;txhD0ZNl0Vsn8a2#Bo;YX~S!9tgU8DFkf9^+nwkD1m@r{5xbv z0B?xO*a7eB=jAU8o?VxK^VtO(!<&o`m!JSEC~Pp}*Uc=Y9t@Kc`zih3}j zGGGXvFnQHlYbZ~16?wv3P^j>navXT>rp z>N+(~XJi|8M&}2&Ln(SzzBiI0?lMJ2DJGK8WS=C}gM_@(*q|y|I mN`TT7hDw0a6o#@10D#{s@HSv0TIy;50000jD9kfdI-t0A(P6 zG7vx+2%roEPzC}h^SRXNiD=u&T3c(^-iB$%o)}|Xni^wny$$~7XpGq#V-D8ZgZJym zC?hv$fV9y$v;SV?ia7J5(pFLXXRL^}-nQ zC>*DBdgOE6N@Z!CR0UwOvSzbUyW*wf&OMdN)+(tuVQ15c$X)y&W6ZrV=EmEo4es2} z&VLcnOrroLeZU}W-&e+%pAoygoZ0Y`gYDzws82p5L|o@*eiPA#TW&k&UC{(Rbbg;l zn3|KFo=E44EZ5}Bzl5CMZL^e)RU=gwfbYf-=en=$9M7EnRycn*ZCf~MFQcFUOEv*} z;kWbTE_5axv+9MvmTfJRr`sB;I%zMXpa83ZChvr4UhzV?rvWWuA?-VM0WPJcHa4RC z%YhrFiU?jtWdZgj9BV}pCIcq3xCmZGMFG}h>3s%VOP_BjDniSSn7g|UL}YuCN@u8x zwbnf;{qTNxvD zpr;jliCuMp7LWk9f}ODE+{+mN0;D4?I*S$nCMXde3pxn>f+#bD7LW*6vb6dy>TwhT zk00hX# zGeH4w@w@>6lz{-sKmcVRfHDw383>>Z1W*P7ySeByCT&VmfEKO{O z^iqbjOr;l?paifuS08C9`X(hF9a=ynJ$FA|&}hViaGG7}+no>~0)8)83Zp5#ycYya zP$0ElQ-uJ5)Ot}HCMW<7vDvMBQzF7*6#-l~-Q6)nlx!Qv0ue1ZzGxTccqNA%hZIC8 zGuY8>C+bwScxw#;H!XyKO{cJHMS@Rx8HFqVEr6f2{X4CETQZWOmyucMnNZlbkY&<7 zBdz>qA%XtSx{IixsUBF;^((bp&pe5?|awxFB^Qt}b-(r4$jo zdyC-Rb%hwRvFGW-gs*Qp7Q_?T4;Oltml*nh!?CkGFy#XdC*JP|91`xz79Z>hO==GW zUWS4YANnIcqZp26(=_Mw-P@onPu%(*$M8+D(ai^x{E!JPcSiwT)){*)nR#FW8| zm@*JR83>>Z1W*P7C<6hMfdI-t0A(P6CdQb5gw$v=9+Arq00000NkvXXu0mjf<$E1_ literal 0 HcmV?d00001 diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift b/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift index 51ee588c1..1980d8c09 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift @@ -82,26 +82,32 @@ class ImageEditorCropViewController: OWSViewController { self.view = UIView() self.view.backgroundColor = .black + self.view.layoutMargins = .zero // MARK: - Buttons // TODO: Apply icons. - let doneButton = OWSButton(title: "Done") { [weak self] in + let doneButton = OWSButton(imageName: "image_editor_checkmark_full", + tintColor: UIColor.white) { [weak self] in self?.didTapBackButton() } - let rotate90Button = OWSButton(title: "Rotate 90°") { [weak self] in + let rotate90Button = OWSButton(imageName: "image_editor_rotate", + tintColor: UIColor.white) { [weak self] in self?.rotate90ButtonPressed() } let rotate45Button = OWSButton(title: "Rotate 45°") { [weak self] in self?.rotate45ButtonPressed() } - let resetButton = OWSButton(title: "Reset") { [weak self] in + // TODO: Myles may change this asset. + let resetButton = OWSButton(imageName: "image_editor_undo", + tintColor: UIColor.white) { [weak self] in self?.resetButtonPressed() } let zoom2xButton = OWSButton(title: "Zoom 2x") { [weak self] in self?.zoom2xButtonPressed() } - let flipButton = OWSButton(title: "Flip") { [weak self] in + let flipButton = OWSButton(imageName: "image_editor_flip", + tintColor: UIColor.white) { [weak self] in self?.flipButtonPressed() } @@ -171,7 +177,7 @@ class ImageEditorCropViewController: OWSViewController { stackView.axis = .vertical stackView.alignment = .fill stackView.spacing = 24 - stackView.layoutMargins = UIEdgeInsets(top: 16, left: 20, bottom: 16, right: 20) + stackView.layoutMargins = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20) stackView.isLayoutMarginsRelativeArrangement = true self.view.addSubview(stackView) stackView.autoPinEdgesToSuperviewEdges() diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift index d75b5ddb2..b026f5540 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift @@ -129,8 +129,8 @@ public class ImageEditorView: UIView { } } + // The model supports redo if we ever want to add it. private let undoButton = UIButton(type: .custom) - private let redoButton = UIButton(type: .custom) private let brushButton = UIButton(type: .custom) private let cropButton = UIButton(type: .custom) private let newTextButton = UIButton(type: .custom) @@ -140,26 +140,22 @@ public class ImageEditorView: UIView { @objc public func addControls(to containerView: UIView) { configure(button: undoButton, - label: NSLocalizedString("BUTTON_UNDO", comment: "Label for undo button."), + imageName: "image_editor_undo", selector: #selector(didTapUndo(sender:))) - configure(button: redoButton, - label: NSLocalizedString("BUTTON_REDO", comment: "Label for redo button."), - selector: #selector(didTapRedo(sender:))) - configure(button: brushButton, - label: NSLocalizedString("IMAGE_EDITOR_BRUSH_BUTTON", comment: "Label for brush button in image editor."), + imageName: "image_editor_brush", selector: #selector(didTapBrush(sender:))) configure(button: cropButton, - label: NSLocalizedString("IMAGE_EDITOR_CROP_BUTTON", comment: "Label for crop button in image editor."), + imageName: "image_editor_crop", selector: #selector(didTapCrop(sender:))) configure(button: newTextButton, - label: "Text", + imageName: "image_editor_text", selector: #selector(didTapNewText(sender:))) - allButtons = [brushButton, cropButton, undoButton, redoButton, newTextButton] + allButtons = [brushButton, cropButton, undoButton, newTextButton] let stackView = UIStackView(arrangedSubviews: allButtons) stackView.axis = .vertical @@ -178,19 +174,26 @@ public class ImageEditorView: UIView { } private func configure(button: UIButton, - label: String, + imageName: String, selector: Selector) { - button.setTitle(label, for: .normal) + if let image = UIImage(named: imageName) { + button.setImage(image.withRenderingMode(.alwaysTemplate), for: .normal) + } else { + owsFailDebug("Missing asset: \(imageName)") + } + button.tintColor = .white button.setTitleColor(.white, for: .normal) button.setTitleColor(.gray, for: .disabled) button.setTitleColor(UIColor.ows_materialBlue, for: .selected) button.titleLabel?.font = UIFont.ows_dynamicTypeBody.ows_mediumWeight() button.addTarget(self, action: selector, for: .touchUpInside) + button.layer.shadowColor = UIColor.black.cgColor + button.layer.shadowRadius = 4 + button.layer.shadowOpacity = 0.66 } private func updateButtons() { undoButton.isEnabled = model.canUndo() - redoButton.isEnabled = model.canRedo() brushButton.isSelected = editorMode == .brush cropButton.isSelected = false newTextButton.isSelected = false @@ -211,15 +214,6 @@ public class ImageEditorView: UIView { model.undo() } - @objc func didTapRedo(sender: UIButton) { - Logger.verbose("") - guard model.canRedo() else { - owsFailDebug("Can't redo.") - return - } - model.redo() - } - @objc func didTapBrush(sender: UIButton) { Logger.verbose("") diff --git a/SignalMessaging/Views/OWSButton.swift b/SignalMessaging/Views/OWSButton.swift index 03ffdf0a0..f9726b583 100644 --- a/SignalMessaging/Views/OWSButton.swift +++ b/SignalMessaging/Views/OWSButton.swift @@ -29,6 +29,23 @@ public class OWSButton: UIButton { setTitle(title, for: .normal) } + @objc + init(imageName: String, + tintColor: UIColor, + block: @escaping () -> Void = { }) { + super.init(frame: .zero) + + self.block = block + addTarget(self, action: #selector(didTap), for: .touchUpInside) + + if let image = UIImage(named: imageName) { + setImage(image.withRenderingMode(.alwaysTemplate), for: .normal) + } else { + owsFailDebug("Missing asset: \(imageName)") + } + self.tintColor = tintColor + } + public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } From fac123eeb2b9f74cc7c1e16a28b3aa8197ab7779 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 27 Feb 2019 14:40:12 -0500 Subject: [PATCH 2/4] Add "crop lock" button and feature. --- .../ImageEditorCropViewController.swift | 103 +++++++++++++----- SignalMessaging/Views/OWSButton.swift | 7 +- 2 files changed, 81 insertions(+), 29 deletions(-) diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift b/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift index 1980d8c09..b40b231c8 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift @@ -78,6 +78,9 @@ class ImageEditorCropViewController: OWSViewController { // MARK: - View Lifecycle + private var isCropLocked = false + private var cropLockButton: OWSButton? + override func loadView() { self.view = UIView() @@ -95,21 +98,20 @@ class ImageEditorCropViewController: OWSViewController { tintColor: UIColor.white) { [weak self] in self?.rotate90ButtonPressed() } - let rotate45Button = OWSButton(title: "Rotate 45°") { [weak self] in - self?.rotate45ButtonPressed() - } // TODO: Myles may change this asset. let resetButton = OWSButton(imageName: "image_editor_undo", tintColor: UIColor.white) { [weak self] in self?.resetButtonPressed() } - let zoom2xButton = OWSButton(title: "Zoom 2x") { [weak self] in - self?.zoom2xButtonPressed() - } let flipButton = OWSButton(imageName: "image_editor_flip", tintColor: UIColor.white) { [weak self] in - self?.flipButtonPressed() + self?.flipButtonPressed() + } + let cropLockButton = OWSButton(imageName: "image_editor_crop_unlock", + tintColor: UIColor.white) { [weak self] in + self?.cropLockButtonPressed() } + self.cropLockButton = cropLockButton // MARK: - Header @@ -158,17 +160,17 @@ class ImageEditorCropViewController: OWSViewController { // MARK: - Footer let footer = UIStackView(arrangedSubviews: [ - flipButton, rotate90Button, - rotate45Button, + flipButton, UIView.hStretchingSpacer(), - zoom2xButton + cropLockButton ]) footer.axis = .horizontal footer.spacing = 16 footer.backgroundColor = .clear footer.isOpaque = false + let imageMargin: CGFloat = 20 let stackView = UIStackView(arrangedSubviews: [ header, wrapperView, @@ -176,8 +178,8 @@ class ImageEditorCropViewController: OWSViewController { ]) stackView.axis = .vertical stackView.alignment = .fill - stackView.spacing = 24 - stackView.layoutMargins = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20) + stackView.spacing = imageMargin + stackView.layoutMargins = UIEdgeInsets(top: 8, left: imageMargin, bottom: 8, right: imageMargin) stackView.isLayoutMarginsRelativeArrangement = true self.view.addSubview(stackView) stackView.autoPinEdgesToSuperviewEdges() @@ -217,6 +219,16 @@ class ImageEditorCropViewController: OWSViewController { configureGestures() } + private func updateCropLockButton() { + guard let cropLockButton = cropLockButton else { + owsFailDebug("Missing cropLockButton") + return + } + cropLockButton.setImage(imageName: (isCropLocked + ? "image_editor_crop_lock" + : "image_editor_crop_unlock")) + } + private static let desiredCornerSize: CGFloat = 24 private static let minCropSize: CGFloat = desiredCornerSize * 2 private var cornerSize = CGSize.zero @@ -506,6 +518,14 @@ class ImageEditorCropViewController: OWSViewController { let cropRectangleStart = clipView.bounds var cropRectangleNow = cropRectangleStart + // Derive the new crop rectangle. + + // We limit the crop rectangle's minimum size for two reasons. + // + // * To ensure that the crop rectangles "corner handles" + // can always be safely drawn. + // * To avoid awkward interactions when the crop rectangle + // is very small. Users can always crop multiple times. let maxDeltaX = cropRectangleNow.size.width - cornerSize.width * 2 let maxDeltaY = cropRectangleNow.size.height - cornerSize.height * 2 @@ -533,6 +553,44 @@ class ImageEditorCropViewController: OWSViewController { break } + // If crop is locked, update the crop rectangle + // to retain the original aspect ratio. + if (isCropLocked) { + let scaleX = cropRectangleNow.width / cropRectangleStart.width + let scaleY = cropRectangleNow.height / cropRectangleStart.height + var cropRectangleLocked = cropRectangleStart + // Find a new crop rectangle size with the correct aspect + // ratio which is always larger than the "naive" crop rectangle. + // We always expand and never shrink the crop rectangle to + // fix its aspect ratio, to ensure the "max deltas" enforced + // above still are honored. + if scaleX > scaleY { + cropRectangleLocked.size.width = cropRectangleNow.width + cropRectangleLocked.size.height = cropRectangleNow.width * cropRectangleStart.height / cropRectangleStart.width + } else { + cropRectangleLocked.size.height = cropRectangleNow.height + cropRectangleLocked.size.width = cropRectangleNow.height * cropRectangleStart.width / cropRectangleStart.height + } + + // Pin the crop rectangle to the sides that aren't being manipulated. + switch panCropRegion { + case .left, .topLeft, .bottomLeft: + cropRectangleLocked.origin.x = cropRectangleStart.maxX - cropRectangleLocked.width + default: + // Bias towards aligning left. + cropRectangleLocked.origin.x = cropRectangleStart.minX + } + switch panCropRegion { + case .top, .topLeft, .topRight: + cropRectangleLocked.origin.y = cropRectangleStart.maxY - cropRectangleLocked.height + default: + // Bias towards aligning top. + cropRectangleLocked.origin.y = cropRectangleStart.minY + } + + cropRectangleNow = cropRectangleLocked + } + cropView.frame = view.convert(cropRectangleNow, from: clipView) switch gestureRecognizer.state { @@ -689,10 +747,6 @@ class ImageEditorCropViewController: OWSViewController { rotateButtonPressed(angleRadians: CGFloat.pi * 0.5, rotateCanvas: true) } - @objc public func rotate45ButtonPressed() { - rotateButtonPressed(angleRadians: CGFloat.pi * 0.25, rotateCanvas: false) - } - private func rotateButtonPressed(angleRadians: CGFloat, rotateCanvas: Bool) { let outputSizePixels = (rotateCanvas // Invert width and height. @@ -709,18 +763,6 @@ class ImageEditorCropViewController: OWSViewController { isFlipped: transform.isFlipped).normalize(srcImageSizePixels: model.srcImageSizePixels)) } - @objc public func zoom2xButtonPressed() { - let outputSizePixels = transform.outputSizePixels - let unitTranslation = transform.unitTranslation - let rotationRadians = transform.rotationRadians - let scaling = transform.scaling * 2.0 - updateTransform(ImageEditorTransform(outputSizePixels: outputSizePixels, - unitTranslation: unitTranslation, - rotationRadians: rotationRadians, - scaling: scaling, - isFlipped: transform.isFlipped).normalize(srcImageSizePixels: model.srcImageSizePixels)) - } - @objc public func flipButtonPressed() { updateTransform(ImageEditorTransform(outputSizePixels: transform.outputSizePixels, unitTranslation: transform.unitTranslation, @@ -732,6 +774,11 @@ class ImageEditorCropViewController: OWSViewController { @objc public func resetButtonPressed() { updateTransform(ImageEditorTransform.defaultTransform(srcImageSizePixels: model.srcImageSizePixels)) } + + @objc public func cropLockButtonPressed() { + isCropLocked = !isCropLocked + updateCropLockButton() + } } // MARK: - diff --git a/SignalMessaging/Views/OWSButton.swift b/SignalMessaging/Views/OWSButton.swift index f9726b583..7985be1dd 100644 --- a/SignalMessaging/Views/OWSButton.swift +++ b/SignalMessaging/Views/OWSButton.swift @@ -38,12 +38,17 @@ public class OWSButton: UIButton { self.block = block addTarget(self, action: #selector(didTap), for: .touchUpInside) + setImage(imageName: imageName) + self.tintColor = tintColor + } + + @objc + public func setImage(imageName: String) { if let image = UIImage(named: imageName) { setImage(image.withRenderingMode(.alwaysTemplate), for: .normal) } else { owsFailDebug("Missing asset: \(imageName)") } - self.tintColor = tintColor } public required init?(coder aDecoder: NSCoder) { From d08445969d8a2d0d251c77659722a22a2827eb16 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 27 Feb 2019 14:58:26 -0500 Subject: [PATCH 3/4] Generate gradient for color picker. --- .../Contents.json | 21 --------- .../Screen Shot 2019-02-26 at 1.57.23 PM.png | Bin 15805 -> 0 bytes .../ImageEditor/ImageEditorPaletteView.swift | 43 ++++++++++++++---- 3 files changed, 35 insertions(+), 29 deletions(-) delete mode 100644 Signal/Images.xcassets/image_editor_palette.imageset/Contents.json delete mode 100644 Signal/Images.xcassets/image_editor_palette.imageset/Screen Shot 2019-02-26 at 1.57.23 PM.png diff --git a/Signal/Images.xcassets/image_editor_palette.imageset/Contents.json b/Signal/Images.xcassets/image_editor_palette.imageset/Contents.json deleted file mode 100644 index bf932ac9b..000000000 --- a/Signal/Images.xcassets/image_editor_palette.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Screen Shot 2019-02-26 at 1.57.23 PM.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Signal/Images.xcassets/image_editor_palette.imageset/Screen Shot 2019-02-26 at 1.57.23 PM.png b/Signal/Images.xcassets/image_editor_palette.imageset/Screen Shot 2019-02-26 at 1.57.23 PM.png deleted file mode 100644 index 4b8e2e2bccdb87b8a80d807098152ff6afec8d40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15805 zcmZv@bwE_z_dQH^N_R+i4&5SBg7hHLDIH3el=MjV&`5VmDK$upbazR2*E>G(`TF>K z`G-6A-gD2n`<%1)+H0>HuKrR18-pAJ4h{}m@x?O@*ned>I7DeQ6xflO38)SZ4&KK` zPEK7>PL5XH#nIBn&H@gO<&&AQF`FU>3&h04*cdXz&WhpUuJQ3>l!kFfSLaY?7j3t3 z4{cV8fx$8<-f~v}fzOxTPPAO^-4BCGr+1!Xh4nrU#DL0F7ac~?rt?5}ccr(^e29oo zQ9+p*8W|aw=38(xqW#G7jM5iaw67am^WnNB;mson;i=Hfm*A_%==N}F-#PFBZ zc(RUVt_(MWporIi@-Y*~mx;Hz*v|3*ZEG3sZRl46j7Q*3V6h% zb9&;_NXW5VQ1J$MJb0RTSa?$SNqO}6e)0(?vctn$T0 z4C-bLPWdj5g5qb35^BGN@g(xe3XauXuk1&o3`;vClT%?{o~p&$$H!W7M8sPLl+X@> z$H&LEpO23nk%1S8YkSf-a18D?%TcyZaAEV1VWXwzrl+bRYUXIqWn%7VYQg1U?*yAC zI5=?+QP`oqg_{YjhrOMHtEh(r{a<&8!j6BP=BB6p>lQa#33@$Mby_(`7YkZJE&(nc zdPxjgT3T@zb4yW;XV3p>4*MlRZ|&yhB+AY0?(WXz&d=rOV#UoXA|k@g!^h3X#|gWG z)78_#&BTM#!S(6i5Bc{w&n#TcTx^`&Y#beEe?8a4)bX901U>z)j{f)e_c$#)Z2qq& z2iJdI3-*HCzpilea`ABg@7b`X;=j&{s@r&2*y%m9vA1w=h4mpRBq;vZ?f=h(|LgF# zhQR-8D9q3Id&}Rh{L@mL``7q?d!N6J_19ULwIngbx&LQ*Neq6Yudm_YM70#3$!K}N z@B7-+Stz_S%=F%Q>9{j>x~o(AdZ9c=Fz;&}9ytduIR(P|RY-8XT}_;EA4808l1-C7P|8o#*(O9IDu^D8T;#&U~KEqY5a~8w~Q(lao>j(=!TSQWV^=HY3OOdEklN zNFJ~bqQIZ6dP{U)_c;O}3O4#F&MirFhy0(B-wevWiH?dh8hJ*cZj|xCXN-9ymfR@J zw=|k!GQjU$K2np8JcV|-sc*GxdcIuUC(jziy)f!`-Pw|AT)b>rABmBMY!i7#JKXWX? z@We=HFRzzI55kS2|Ju|d?sdoM5ES0;iv5wmso;aJ6je42N#Atnr2UtQcpA-|7yq%5 zX!3L(Pgs^pOmDo~M?Z}hE)A~;NQ~gmtD;AXh6C^<{Q-jilK8V4o*yKXGFSzA>0=s9NAsiZlyCCLrlY`(O7vz#vSb0;jJv+ zjw{N=DueZdP&<(IkUv*KpqiX}n5LFe`jt+GPW$DJvW0@l2gu^iU32g>A|HI$rsn6V z@{*H~1)2jv8=I*4vcx}DP(;Clse-o6Z3o(G~qctwMF>!u5OcYvNCRmpBp~sq$0+lIwSFEKa#Ww8Q z%&9zpC|8WerF(LZjN?~P%*s*b3S2Pbx%_TCt9@#08cvQ?@wCErlNW#zdERWDL}oR8 zMNp?7(XAp!*dRLOv;XO?Q`G%xB;ZxJu%nGv!)VP%CKAFLgFTQ6gkIJ_JvJu#YA^aa}+#`=7qZEtrNj)l}e>;}H z7Q2_ZPo{nf7(P+7r<(iIlAw67Z&6CSCrcT2VYQEPmSA8s%N&oIr)@-&C507bTwj5d zTj9tAC~1QZz)4&)YUgnf!htJeQ~mJufvo85sh5a&w0uU<8>(2V_g=dR-^vAeh*`>RlROFD>#18WVqM zCPINlvnTT39^$VB<+avQQ!nl*od+Cjw-yM&yd7U#Eik9%1i zl$x_?_0OToW+@idn9Bo)K4Qzn`)fygBvSAvLJLREoos4(^LG)XW)1~T-t0-1z2b%; z#O!1pyJPO>5|*2&o{Uy_HGHja=-3f~UJE~i*&Le)YxY{#w&1yScyvr25&hv$4D2zg zkmr>v_SMl{zCf@A=(2PKgP62<%A`_Fq^0!@_kFmg7<8YU;o?>*K(i&MeiP!tZ!{Vw$r8ur?tX*&=P`OEu^ygtlv`4#mJ8xkx6}u z`?$19dX`CB+xzt6Xbnh#`AJR2S++VvRx;xSx2j|@y}p#N=aWnQN#0(gH;q=Pge}g9 z(JMg$@T368SfgsmISv8VhhvV(q7$>Ui8L@tIcxAdK9lh3B!3UH&%q8q38oSa5^W`SbZG%3LaUS=1gu!&)z zQ-*x+lt$5RbLeWCl?S}_!MbFP_EeJ=4Pzc&0h;ODOIes5aYm5Y{}89A0)KXC6I?tY z(|2s}{{Wf*Qz+A$0~5~Feh4l4G}o)Kf?tu3iHK6RYb{pQ`eWNG>ZEV%Qnc8fDs7Y6 z%J-?1GWGO?#V_q9uyPJ4?*ozPb~#8E#pni&@a!!rTSkg)C({(t9({kM1YNs<+=VlXmW=D?v3Kuc?1aN`8 zx^5;=lTF&(!xbe3BK#N4w0z2_&Y(1h-uZE)*4>!r|*%WZQ zAf)C)A+U;eUr{xksgU?b+-d=3RBO%#+vZOlwj#Sw-E8^>*z$W!-VMK&Fgs%8qqnQg zK0^YDM(Rce5va-Gd%@rLW(Rn4qqZqx*Z`%|DbRope$s5_@Kg@jeSib4xx*`>AEH-x z`~kQUVQbpS2Hs*2?v5u=R3sCMWfys6JI1P^lNsSsICojMpEBwnpJtF)bns85rz9jH z*Zm$(z1*hu-)1$ei(Rx;%2eo=H@;{}Zm|)Isdh~7Jm!Y&&z}u?t7X*~@8M@Ol47KA z$Yf`W=+TZIcNVDwU?vCrZt~;?!;%aCeQudjNq?-PmU3OV_EmDq!|$(e(>{1z)Qj!u zUf*$nGpH5nS*8P{-qwbfdb_mAPmI>2yg((cp z>Cj5BPVia?v6p}|q2~E|?F~@<2BXs|Pw%CC!gt{!ycFaRi8f-?o68Hoq$K%gE!m(A z(=c;cV*FKDr>TygdBPB1emw#}Tzk%i5QkHvalDGdYyuZ&5!w(rsK4JR22dhD)P|3W zM5);V7$n(Gy>ppBB9F+4GLY{_E$={ft)t^lK^D6Qw(y6up?(vD=L?0&kZO~T2;Sn? zYe4l|jDXY1>{S)|r-^y|?A9LaGncuysQDHmK=~X{h>!3m&_+ILh*vdvvEb`Q>79D1 zT9Ts^6JXnT-a7$(W#PLZ&pEWNj=O3sz1DK5OT~!UnBL&+*FJcMEKSm#tVAZx$nf{Z zbOSOtk-;36-GTL7F`reFbB*fl*@qELg9|iVh06=AZArd}HUJxXcQmHaJni&ZV*xgS z7mpZ`Ye(X``@XLK5B6)@s41OL^)|xPr;FC5%BfwptuyfgWf-j8IWK_{XL_r!zn4Ha zUOil63{zArMF4K&(hou@opa{glVIzo={G#)HFhDflgs&2_qg9kv(DK{1@OH)99$j| z^-@P_z|6Hw*mJUI2P&z1>A?D}E&Fo*1fUM2r?|DQvGTiR{sZ@%3FC>x#oqzTHz{atlMaL*cO${J@fcHy#Ve z#kku$*701`PA4rk@Id0wIGc})l#!0Tt8}s`e)+TK%_gQ>l8&_53I2Pk`1?k2!CZ6* z^_OK<$x`7oDKwxGqd>_7BW0e@?FtN{&;Ab4B^F{T8Pc_w8qwV))FWbIbn%Ct>OT*+ zBMxz6JMgkhI`z-rvU0XT6D#$p0Og{hnMVs~{_Hy;yZOH;)gZx!^3ys*fV=6%78$_0 z<4OtpHp#B`ZK)5V-G}-58HI+peywpL9ZFOh4dZqbt3>~Co#U+7Jy{btBYml=a)xDV$ZKn>Dc86 zU_bD$8I@dJ;0HC zC9|+;HCP0Q2Iu~ddzw456qRrj z;plZDoO`0hZ_HAhj`h3M^$GLL8hN1Sw3)x=vA@7(2fq#C+ZQ zaL`_8P_A9YkYOPW5giD*d&Laq4!%(XX{AfT3o|Ly3eUgM|53~UXjg2W*LKaESkZrY`d(X0wwRA_+>22gsVT|(beVJb1I^jdl0G&u9)t&T) zbEquY<;%quBKwO`LX-?Di97QmH1iN;A}kobfMuJ1Me`CUrBv<*gbEAR=U1xSgLFlz z`muV3{^*3?vl|l!Tx$CAId)SiN)I9ER~|Y>qX}jeyFzbF9mI51tqO*?P4{}S8~65q zz7kXqg)^1@E9HZ@RLk+TH90*O#LXcJPivY{!OK;pFzg_U81X`@nhmnbssZ=-3ISyF zQud!$XPmBenAjHp)UuZ#06ULU9mv_R(BY0T3~xmKjyI$(i~RGs+72eN>+QET0_0A* za5~2K*~tnF0sx$TV@rVC;dkij2tOEH^x8s1#Gw3qmI~|#J=N(hS)S_yn+>jy~Ey0P!0A9oA9Mj$dcrL|Xuvigle$V{|LB)&cdk zcR*|%4&1}U(w%(g)ZRC}7&+yi$(b2L?q@J+%l{a_PIbpuT?Cx(0@OFgwTZ^#3f0 zB;4@+3;b~`6@yr?3{^+@1M>3a=S#$gQRg}n=Ggr`NHrmZK7b6bCI$a*G{-U{#XeJ% zaPg#lcfT<(3;O&X_53#baSUbJ@+Rw2g21{h@HP~0-J;;Gn>Ngi`Nf1mz%i{yFN?F;!1i~-{3g2fY-hz`;UgOn{b^bHn#5hbKE1yrJY*K7af`J;9 zL#+S$qLrC$l7-@gf?@6NjO`OmRfxumIm->1fFRw>V@4|%sM z6WAY@=CqP=ud(=BP5yT?)LSZ>`pVV-@qW1N@B_UE=b+h+38-Tf?r~}8t?d@~ci6~O z{~Z|$*sqWrEZRa-(|T{^ELFCBhYm8b<}Fw|SIvCn2Ga>3{*LguhlI49?GWmyMVye1 zxQ*Up8XHz_y~adL^~ZpJq%oAcKa^Zrn0v0T^P6dcc#2M1uz;4t&i=)yh0a4q-jl0d z;*gAh0StNc=44DLO*^uz zMM}u)c1R0cf*LH+uh!O@Dy^K0kxnM77cINn#@#oa9?KhJ+HlSs*;dHN9kHBgUa7zA zG$-hrYuDeeFadah!Ck&&583B3Pj?KMX&!Kv7uJg4jU)mA$8ni8m56u(Xq`-gFJdtD zU=+#G$@$dbeVy0bmnC%IWMUs&GOJ!Y=99w9in0j{oJt7@AexrdFFq6 z6*h4uenaMeRlO8qu*P^+OD{n7mg!k~J%DUV?a?&4nt+!YKL zYtB+x9I%w(w|0~IkGSKxLH^(6XK)w2(A$yh5G4084Y+>6?;DzjUEESDuor%f_#>s? zSn_J$rqXFfkGHwX4e=ZW3yCW;>ZpL)Lh7oyQcjq+Rl= zNXrHY81vN(E^8m3MxYzm%y*1#x z#6E*feNQ3lKh;bBdB5==K+))anP=<1pn(_Y7sJ%k<1AtXghR1<(B^yBe( z>an_H@MWNm^N{fJ{_90dkNX35cuidlb9+4Y9g5K_!q=QN0pWmI!i)4K*>THa4Q?ti zlyHl*plO~6KfUVdbAmtIdZ1xOmeR2K{)KOn_LiYuyZw^SPEZH()ui{)eC9&qvUoN< z#e|lyB^1!tps4-^;YUQhI3Q|ZZ4^g!wbiou;|6rlbjiv0a1dw%9Lz_=1md{pF_`l{ z#i6`A4&tv)6T@KZqrMVeMubYhc{rnY99L862Hkb|top-KYx@%%U89_0fVV1i?ZXsE z$)2Z+YRR_R_)Zp$?K|nPbykM&6Ox@U6WG4NK=t!arOKFHw?MgFAjW|+4kJrWa z?f9Nz6g#)$SJJ@~yxEK%`3&92G=Wbk6*;blG4HDE@kjyy;RWygz>8^|M7DAzLJ{h++JT z`xPD67bO459O6lthvTX0V%NUvph{Q%sbP2PxUB3DvX44*-mIA>5Qc|j{d7c@`OQ|A zQPO$wg4!`gG6s;$unLqI$nmCha{SK6-EeRnbDLs#Yh<%(sla-07KTBqSC2XB@VDj; zG0KkFyr{{S_{S1PYX&K({+rT-$Jep}5CLsi=O{O?M7xjKFG?ZUuXFSdC@QiM6!&zG z#Vkb3FFw#mFO4kePNr>trl{;uTPgo3tMiM)_Wku9En;p#86oTMp9y63c-JHK;%{p@ zm(2qrPx}PgH{&mh8yxvA@AU@=1`&invi|yr#{zj>T>vm zPl4e;`!lJk^v};C??p(~Dn(@tAF8(SCZT?miV(`<+ zrcYzWWN8SDG^W_vrI@Y8vD5ScK)eu?mhTKCi{ppZ9tkvzeSV}{B_#ET|6V+4SvVXR zNpX#dQa4BVswz`=4J+gAMbFN#-vTAmT>x9(-R<)92Hr?we|6nNGQ0lO`6Pzcx$={m z8{S&k7EnC`>wTPCf?L9>==-F)UOiOm_=eSHh}zosJ=g@Gm-$ziy=eBqVAXyV-1gcq z*19DEhHeypM>nz2Sfz}!ML8|Or@?k{(}j$PrZH>@Z%gpwY9E>#Ob$+D`Sis%hpX{q zpty-i@|7dVAsi%P3k zmeV{R3*J{m^FUhU=SlLETehO51+Pd8yi0^3IrDc>1mOF`qEjqo#(9e7n&4ghQf2G0 zvKyON>9oSy@gYC$0R#b7MLuQ^JIc^*{WW(wFu{0{unlm)vHQO?`};_-xJ>y;R2eG@2_Quo@pdCp$B&#&=p{TEfM&1{@MSV9Y3TY?1aY;sOl|o6?Zq5%N_A0 zmGZhf@Z5FjhekFMH3Kt6*RiIK5h%h8vZfo&G;3F3c=65{1+{bjBMQW2r3ALT7uy_e zwUDE?23gWbqFASM6ou_Pg{{&pqSam@+^SB}m_i{MPRu|2jznga!={*0{jDkvzRAy; zNJGB=!22~p+ZQ*>2t6Pa0VXJKfu&F1xY;Bgh25L-6|M%6V05taki&_v*09jtB8SP<>)ZCw~X~%=Kh^YOP-UTKvX>bLTbioI4JGwLNQ!=@>Nt zYI}}dEb5nq(gO~ulhW(7P4v3eKhkt+{-6m7IfM4Wnb1B>wjYb1iEMBpx z`2i_&u@N@_%k4P@%e;I#;z`=lg~5@4wVS>?W~cq(Z%>BVW6FU`tVa4eC}|-aFn@ta zrX@zh{6$3k`H(k4uRyFG+v>xS&b_krY1xpmxI?@kF2pZQJLfhCsM)#2>bL;Mn31KB zu0tY)c}0fSWb{>9-auFhwp`rH=WeT^uP!Mk^=t)l03;i9p(gS0)@l8 zg(9N1k(YzDI~yq)%7FxYvjW2hWRJP~-^THxr);8aKW%P)d9JIUB=7c!L{};$l=KWz z!?F$jK?YrQZ0Z3-O_DTRMPa5{)s>XSVXwjq40rkw?s~5^{=`+kzI*g=+|Yo(uezH( z@W`y=pdbCs7wp-|b@^$0uBz<01wCI66NH2UiCmU`)F>tdsbmlhbTWmX+fXMc>2gM- zMnjlf%EEwP7S702x8GRncl@aYW8_j^i(v`8hijbxXbEmuYkB+UiA1yXZsgUqn}NK$ zf+x|{V^`Yq-$X(TrtcA9IhtU%`=xRX3Ez3<^z&4~u0lIo zZi=ToQ)PgG^0xooj8WX|qts!ROB#PvUO;=-$(pMArQf<#eGXo}Sd(gn z25B?SaRB1&f|}qatwgpie+qfsnxvZME{cqxYzg*$O&5WcZ$fk6D`Y3(7|4j|26%Wo zwQ+H=i`mXY;uh)6EuoMH&9jl!5Rzi5`~WXsl~X2Sqz z&+h>A+}z%Tb>@=$-j;E9_RZw+JR8sN0IB<5U`q;OODE)udIB~><9sb zjq8${nni1`Fr6dMeAEY&H@er>m0{KWn>W5kao8l;q*V?yWh2T|)5|9v&FvNJQP-oW z8mb{=`MRb1$xBDCbfuN{u#3F$2!3`qeO3yVrt)z9n=uEOXDX3?5pz^_1L$RcSCsr-J1)QIr&_ zB@*V>*J9!pMLwRKX4LH~G059DT(^3DeBsM zYf$OQd#XtGXklp0?Qen9Uuf<8YR>g3PKaJBqvR60o$ECg zCLge0Dez!X>{mk1`oA1~58_vEPt_~Rp#U|S=s5lYLUyMjY1M*x7o@+u3qgF}M0Z*_h;h(lsj4No8p|g%WLr#z&Zg71 zs|*l%UcH-CQS1%9$sfrbrUkPTsvGG|9^eHANnc~3niLm3{l)C74RL%%?0YGfWXrvq zHB*(Lr|`7Xx0E!wg~N(ph%rsbJU*y^xtS&K_ANuh*^C+=)TBa0G$_xo$d}TZo7$`^ z-+~MUwFv`=2r}0S=IH8%)0JrO%bWE-BmCU%BuuuZbYC$Kh?7;CU=0;WHe*NHDUtRg zh7MG#ewzt7>t6rjCIrlJ%1rc8+yt?ldPX z`_Nc!u~EN=sz!K|;}hx^lOz~iX2EtR23gF=o?v{#K!_-W9h$Y=6{L9+OAhyTMinAK%_z=p~%{930dVUUR}+3P#HfIdC6~y zR8QaM*L-i5#f|ywXl?pjwE=Vy7VG7WpftdTh3!OCgF@T?84Va34qtJDs>c0!pz@nu zD7&KqMT6ECs)&+CCqsLGd^1#O5qSBNTD=4QSCavWLWolVO?uzjo$4c8(Ow}=!LNIJ zpS~E7rOtc-aTyJgrNg{-qL=Vp&iBGkKKT4bHyhiwJ6VQqY3Y)7c{L5a6RXwi$ULa# z;=DhcjCtm=IDcwe26PXB<8}g7!3M+CixfotlYuZ zTiQ|9h@V)+!na>?KO#BhMfi5DLQp{b5OU4$*HA&jlrE8A`u#8?w%j2;-p?mwmL3GM zNwC~15Ua%NptE-8Huf-x!{}!9KEPHOsWP;$F+hB(vn^F71+Ee-A47Z#WN6r=Do@ei z3ZVYjP^&AbA4w>G^)=PCh&K^~=pzh3LBa^ofiJz(KZAX)=5~6ztZ3_XzMhBnth@p> z?(hec;>VZ-@xV`d5VPYo^wz#uoEwhNMD5-9Wb&h&)8q~qbRiIxTVe1=%JY-N$V*X` zJC|9LWi{J}(&X;RVR8k+9=l7TUF|eeH8@zUtH85sz6}F^e-4j5${RXjC{SLwcWu>y ztQO%DO8sHfl#YEUbjwiJHrkxq1dHDO)gYsLuk4Nia-djB4&?dA>Mc57Q!cJ$B(~*c z^fOF%`>!G&Lx53IkzN$vln3bP->Cmp#`sH#DpCDjk4h@YH}5UMJzEU2bzC$?zvBvq z^Rx7V(mrvY4PM~R-0g*2!Ny5JfKWLQ(c`^k$^VZUG)}i1?LNv(^AuD%BKxvwzIaL@A0P&e7u(e zU*eC}a4_96qXF-ms~KdIX)sy5JM$-c_db&GuEWa9udt4TruLsbz;1p`s?`7P_H;vSI%CV~EL6$*9RP%# zE*|8(!31v3owC&MKV$#(jP|&hZ*S93_pP)GEUr^j`$NhI$=YAj4fLriRpg;mlD@Pf zy{6jdtvqw|Aze##tC6bR?ep!Ln5C>>xAmNtHJx2ke`Pe20KL8vK`J7}tI;sO`e!pb z)Ti~g(p(_BPO8v)@lg`!b7YHa`)wull-b*T=x@|-W-VGughuO_*XrLrdgC&1meS<0 z=VJodL8IJ?ke$9kjO~`TN~iJ2Q-F(DA)$x`Mcy@q5b}A&bS=XQ9B9`p9UzVWlIVb4 z-@jscD82^C5bfAsRwa5XCqUo>e|WO zv>CGJv08;P-pG38rYuY}_AjfEu}3wL8{V~e((=#)deuLc<(V;JRjUW))wY@up1d@@ z=@3^VM>5a%7E~HI;q%s!`Ms!1qJ)g2rXkRp*e#oF^aMon1aQ zU`jR$IHx5L6lGw9A!z$nX@ESy;2&1{??~+?8O7NV0%frOv^H_JcWaCxG=(rD9=~|0 z1n3$a(5I&Q@YV0NHzVBL&A6IAZ1wZfn&Z>`BX{I_n|Y=Elli#Sq}{z8++Vsq81{K% zCi3};mwY92PfTL5$Mts|E8veUl(pITVV<9inng!z?KSwY&;SE$zlR3JGC3}?Acf)S z*wywYU?Q5-VNEc`DQ|_YxnP5|^((zwg|F7N2~6^x?SzO4r_KhSWKZ|%fy%(psg_1VF z`^6)SZP`hT{Ih>37OyAZ9wVO`mynwX`PnZaw=eO_->rCI@E*i>3f3T-i6n20!4Q3i z2jYRF|682P(296$%vFcY;yFGPx6bJLhF^YJbNwxKX-+kPMS0ki%%N zK#n2T(EV2PT*AYm9@{rMY)|j8W`<^o)L7>EWL4C==K!7YA^&RA$aC0+(_f@6nCiqzM8pUsHQlEMjot|C_`Rz|Q%37$TVGqFO9iXBoeg6nCeu?a& z_cq^x_tigP3H0lW5H5z(2XT})MeQpZME4Atd&r+2u6vgg1yR7m3+>RiLDQkmo(=|i zo>2N8$zZp_u+*u9q>W9YzjgJ0DdCxH^UbtXF7A~dLqnZ72TVu(RUS(ODwCjj*vh-_ z4@Wj<0Boh$FkPX$=Q&%iVI505hr*T;3C0`0e2W>!2FD1{L^AnnTJxEVGu7>Bj>X0t zGBXv6Tf3~YSZGA$gzxDG*aihKzR|tRve%lt)1!|ys;B3Wa>!QYucrD#t;+)ZGNx5d z?pvD?qJnfNR3I0Hy}b8*HnMk87|eQ(5>t6>DBfRadL(?VKNV`i_8H9>M&-ib3 zA{;w=*9vp**)G1sVXx59aDKAn{rNr+io3>P&d|2@X6B5Q!)W5Qbr(k6=3La0;=tim`oZc2+#G4yQj+U~-3_JY_gj5| z8OzHOm|VYA>^Hf-KI7KnDfzZ}q{mgY{0QM(r>!@wD?-VZ50+$l^tXlm_{_H~Niv_n z{f|X6V!ALN)B*gRE^ag{M~DS?10t)TxS}~c!;6iMNK)Lpwe#IRX>OBC4oGe6a}4~w z4&$?1UitnlBxY=!>_ZV-hL97y1Y%5}4M6;|e>O8?wVuOMKh|mniBI3hl6?yHiQi&) z&E@_T=p=p7iVr1Mk}&}w^r(b z+i6K~Ys!i7ez~1-im#xiSJVl8i|xiARFb>_0CES%^sD_jTh$FY6arBAQXLO>iY8g@}v(tWEp?ZCl%*iYblSAkoM-tf4fMfOQ%@|V4 zB{f5KlSl2Pz{Bq8{Wy2+4U84wR)NXob*zlSKo=YNJWi#;usU2r(B%vVweQKb;v}Ml z%8xy(S7i$GV!MlkUz)~!Wnt9Rr~gt@jbSb>>w5Lj`WKHL2OaXq%5GR{qPAOpp{7(i zwt2!*x<6|@J!@Xg>qhlk(`04sYtg}&zTfq!@#mrYu6fQX`l#l$);`SP8DaFNZcIpM z1UxV@nD_CTdgS{jqf`##4+=35%ghYJJqT-G2^ zkJ679#FOOEC+vy5F|8Bd?w4`fuLQ*V%*&{RLal{~u*c-(O{f)jVG5$m;oGE-^V5y?55rfRD)IehLebtcpKcpm#8Vr)oY zm{TAhsa?0SiKPo$gn7RSwm`z9fvZ<+s#aFVl7(r#>of44E1qYy)OROsM})DyhrFGS zJ4SwRMSgNu1W?x?(j+xzj(O|0C{GDM)qcWFhCTuYDEeUp}jl IHU9Ab0UT6sFaQ7m diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift b/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift index 73d3d442e..1d4889198 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift @@ -39,8 +39,10 @@ public class ImageEditorPaletteView: UIView { self.backgroundColor = .clear self.isOpaque = false - if let image = UIImage(named: "image_editor_palette") { + if let image = ImageEditorPaletteView.buildPaletteGradientImage() { imageView.image = image + imageView.layer.cornerRadius = image.size.width * 0.5 + imageView.clipsToBounds = true } else { owsFailDebug("Missing image.") } @@ -48,8 +50,6 @@ public class ImageEditorPaletteView: UIView { // We use an invisible margin to expand the hot area of // this control. let margin: CGFloat = 8 - // TODO: Review sizing when there's an asset. - imageView.autoSetDimensions(to: CGSize(width: 8, height: 200)) imageView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)) selectionWrapper.layoutCallback = { [weak self] (view) in @@ -58,8 +58,8 @@ public class ImageEditorPaletteView: UIView { } strongSelf.updateState() } - imageView.addSubview(selectionWrapper) - selectionWrapper.autoPinEdgesToSuperviewEdges() + self.addSubview(selectionWrapper) + selectionWrapper.autoPin(toEdgesOf: imageView) selectionView.addBorder(with: .white) selectionView.layer.cornerRadius = selectionSize / 2 @@ -95,9 +95,8 @@ public class ImageEditorPaletteView: UIView { private func updateState() { var selectedColor = UIColor.white - if let image = imageView.image, - let cgImage = image.cgImage { - if let imageColor = image.color(atLocation: CGPoint(x: CGFloat(cgImage.width) * 0.5, y: CGFloat(cgImage.height) * selectionAlpha)) { + if let image = imageView.image { + if let imageColor = image.color(atLocation: CGPoint(x: CGFloat(image.size.width) * 0.5, y: CGFloat(image.size.height) * selectionAlpha)) { selectedColor = imageColor } else { owsFailDebug("Couldn't determine image color.") @@ -132,6 +131,31 @@ public class ImageEditorPaletteView: UIView { let location = gesture.location(in: imageView) selectColor(atLocationY: location.y) } + + private static func buildPaletteGradientImage() -> UIImage? { + let gradientSize = CGSize(width: 8, height: 200) + let gradientBounds = CGRect(origin: .zero, size: gradientSize) + let gradientView = UIView() + gradientView.frame = gradientBounds + let gradientLayer = CAGradientLayer() + gradientView.layer.addSublayer(gradientLayer) + gradientLayer.frame = gradientBounds + gradientLayer.colors = [ + UIColor(rgbHex: 0xffffff).cgColor, + UIColor(rgbHex: 0xff0000).cgColor, + UIColor(rgbHex: 0xff00ff).cgColor, + UIColor(rgbHex: 0x0000ff).cgColor, + UIColor(rgbHex: 0x00ffff).cgColor, + UIColor(rgbHex: 0x00ff00).cgColor, + UIColor(rgbHex: 0xffff00).cgColor, + UIColor(rgbHex: 0xff5500).cgColor, + UIColor(rgbHex: 0x000000).cgColor + ] + gradientLayer.startPoint = CGPoint.zero + gradientLayer.endPoint = CGPoint(x: 0, y: gradientSize.height) + gradientLayer.endPoint = CGPoint(x: 0, y: 1.0) + return gradientView.renderAsImage(opaque: true, scale: UIScreen.main.scale) + } } // MARK: - @@ -162,6 +186,9 @@ extension UIImage { owsFailDebug("Invalid image size.") return nil } + + Logger.verbose("scale: \(self.scale)") + // Convert the location from points to pixels and clamp to the image bounds. let xPixels: Int = Int(round(locationPoints.x * self.scale)).clamp(0, imageWidth - 1) let yPixels: Int = Int(round(locationPoints.y * self.scale)).clamp(0, imageHeight - 1) From be26c135e1c33111d4a43dd4dd9540e5d5d58d5c Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 27 Feb 2019 15:32:09 -0500 Subject: [PATCH 4/4] Rework image editor buttons, modes, etc. --- .../AttachmentApprovalViewController.swift | 3 +- .../ImageEditor/ImageEditorPaletteView.swift | 1 + .../Views/ImageEditor/ImageEditorView.swift | 121 +++++++++++------- 3 files changed, 78 insertions(+), 47 deletions(-) diff --git a/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift b/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift index 398c3e2c3..53b149cc9 100644 --- a/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift +++ b/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift @@ -953,7 +953,8 @@ public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarD autoPinView(toBottomOfViewControllerOrKeyboard: imageEditorView, avoidNotch: true) imageEditorView.autoPinWidthToSuperview() - imageEditorView.addControls(to: imageEditorView) + imageEditorView.addControls(to: imageEditorView, + viewController: self) } } #endif diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift b/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift index 1d4889198..1f35fe06d 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift @@ -140,6 +140,7 @@ public class ImageEditorPaletteView: UIView { let gradientLayer = CAGradientLayer() gradientView.layer.addSublayer(gradientLayer) gradientLayer.frame = gradientBounds + // See: https://github.com/signalapp/Signal-Android/blob/master/res/values/arrays.xml#L267 gradientLayer.colors = [ UIColor(rgbHex: 0xffffff).cgColor, UIColor(rgbHex: 0xff0000).cgColor, diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift index b026f5540..4f6234503 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift @@ -29,12 +29,14 @@ public class ImageEditorView: UIView { // This is the default mode. It is used for interacting with text items. case none case brush + case text } private var editorMode = EditorMode.none { didSet { AssertIsOnMainThread() + updateButtons() updateGestureState() } } @@ -130,15 +132,18 @@ public class ImageEditorView: UIView { } // The model supports redo if we ever want to add it. - private let undoButton = UIButton(type: .custom) - private let brushButton = UIButton(type: .custom) - private let cropButton = UIButton(type: .custom) - private let newTextButton = UIButton(type: .custom) - private var allButtons = [UIButton]() + private let undoButton = OWSButton() + private let brushButton = OWSButton() + private let cropButton = OWSButton() + private let newTextButton = OWSButton() + private let captionButton = OWSButton() + private let doneButton = OWSButton() + private let buttonStackView = UIStackView() // TODO: Should this method be private? @objc - public func addControls(to containerView: UIView) { + public func addControls(to containerView: UIView, + viewController: UIViewController) { configure(button: undoButton, imageName: "image_editor_undo", selector: #selector(didTapUndo(sender:))) @@ -155,16 +160,21 @@ public class ImageEditorView: UIView { imageName: "image_editor_text", selector: #selector(didTapNewText(sender:))) - allButtons = [brushButton, cropButton, undoButton, newTextButton] + configure(button: captionButton, + imageName: "image_editor_caption", + selector: #selector(didTapCaption(sender:))) + + configure(button: doneButton, + imageName: "image_editor_checkmark_full", + selector: #selector(didTapDone(sender:))) - let stackView = UIStackView(arrangedSubviews: allButtons) - stackView.axis = .vertical - stackView.alignment = .center - stackView.spacing = 10 + buttonStackView.axis = .horizontal + buttonStackView.alignment = .center + buttonStackView.spacing = 20 - containerView.addSubview(stackView) - stackView.autoAlignAxis(toSuperviewAxis: .horizontal) - stackView.autoPinTrailingToSuperviewMargin(withInset: 10) + containerView.addSubview(buttonStackView) + buttonStackView.autoPin(toTopLayoutGuideOf: viewController, withInset: 0) + buttonStackView.autoPinTrailingToSuperviewMargin(withInset: 18) containerView.addSubview(paletteView) paletteView.autoVCenterInSuperview() @@ -182,10 +192,6 @@ public class ImageEditorView: UIView { owsFailDebug("Missing asset: \(imageName)") } button.tintColor = .white - button.setTitleColor(.white, for: .normal) - button.setTitleColor(.gray, for: .disabled) - button.setTitleColor(UIColor.ows_materialBlue, for: .selected) - button.titleLabel?.font = UIFont.ows_dynamicTypeBody.ows_mediumWeight() button.addTarget(self, action: selector, for: .touchUpInside) button.layer.shadowColor = UIColor.black.cgColor button.layer.shadowRadius = 4 @@ -193,14 +199,39 @@ public class ImageEditorView: UIView { } private func updateButtons() { - undoButton.isEnabled = model.canUndo() - brushButton.isSelected = editorMode == .brush - cropButton.isSelected = false - newTextButton.isSelected = false + var buttons = [OWSButton]() + + var hasPalette = false + switch editorMode { + case .text: + // TODO: + hasPalette = true + break + case .brush: + hasPalette = true + + if model.canUndo() { + buttons = [undoButton, doneButton] + } else { + buttons = [doneButton] + } + case .none: + if model.canUndo() { + buttons = [undoButton, newTextButton, brushButton, cropButton, captionButton] + } else { + buttons = [newTextButton, brushButton, cropButton, captionButton] + } + } - for button in allButtons { - button.isHidden = isEditingTextItem + for subview in buttonStackView.subviews { + subview.removeFromSuperview() } + buttonStackView.addArrangedSubview(UIView.hStretchingSpacer()) + for button in buttons { + buttonStackView.addArrangedSubview(button) + } + + paletteView.isHidden = !hasPalette } // MARK: - Actions @@ -217,7 +248,7 @@ public class ImageEditorView: UIView { @objc func didTapBrush(sender: UIButton) { Logger.verbose("") - toggle(editorMode: .brush) + self.editorMode = .brush } @objc func didTapCrop(sender: UIButton) { @@ -244,13 +275,16 @@ public class ImageEditorView: UIView { edit(textItem: textItem) } - func toggle(editorMode: EditorMode) { - if self.editorMode == editorMode { - self.editorMode = .none - } else { - self.editorMode = editorMode - } - updateButtons() + @objc func didTapCaption(sender: UIButton) { + Logger.verbose("") + + // TODO: + } + + @objc func didTapDone(sender: UIButton) { + Logger.verbose("") + + self.editorMode = .none } // MARK: - Gestures @@ -270,6 +304,11 @@ public class ImageEditorView: UIView { brushGestureRecognizer?.isEnabled = true tapGestureRecognizer?.isEnabled = false pinchGestureRecognizer?.isEnabled = false + case .text: + moveTextGestureRecognizer?.isEnabled = false + brushGestureRecognizer?.isEnabled = false + tapGestureRecognizer?.isEnabled = false + pinchGestureRecognizer?.isEnabled = false } } @@ -537,20 +576,10 @@ public class ImageEditorView: UIView { // MARK: - Edit Text Tool - private var isEditingTextItem = false { - didSet { - AssertIsOnMainThread() - - updateButtons() - } - } - private func edit(textItem: ImageEditorTextItem) { Logger.verbose("") - toggle(editorMode: .none) - - isEditingTextItem = true + self.editorMode = .text // TODO: let maxTextWidthPoints = model.srcImageSizePixels.width * ImageEditorTextItem.kDefaultUnitWidth @@ -566,7 +595,7 @@ public class ImageEditorView: UIView { private func presentCropTool() { Logger.verbose("") - toggle(editorMode: .none) + self.editorMode = .none guard let srcImage = canvasView.loadSrcImage() else { owsFailDebug("Couldn't load src image.") @@ -628,7 +657,7 @@ extension ImageEditorView: ImageEditorTextViewControllerDelegate { public func textEditDidComplete(textItem: ImageEditorTextItem, text: String?) { AssertIsOnMainThread() - isEditingTextItem = false + self.editorMode = .none guard let text = text?.ows_stripped(), text.count > 0 else { @@ -648,7 +677,7 @@ extension ImageEditorView: ImageEditorTextViewControllerDelegate { } public func textEditDidCancel() { - isEditingTextItem = false + self.editorMode = .none } }