From 783b5f6031d8234f7c7d2ea5b19e5a4996f84021 Mon Sep 17 00:00:00 2001 From: proneon267 Date: Wed, 3 Apr 2024 04:36:23 -0700 Subject: [PATCH 1/9] Implemented Divider widget on iOS --- docs/reference/data/widgets_by_platform.csv | 2 +- iOS/src/toga_iOS/factory.py | 2 + iOS/src/toga_iOS/widgets/divider.py | 41 +++++++++++++++++++++ iOS/tests_backend/widgets/divider.py | 7 ++++ testbed/tests/widgets/test_divider.py | 2 - 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 iOS/src/toga_iOS/widgets/divider.py create mode 100644 iOS/tests_backend/widgets/divider.py diff --git a/docs/reference/data/widgets_by_platform.csv b/docs/reference/data/widgets_by_platform.csv index 343fc0afd6..12aa357601 100644 --- a/docs/reference/data/widgets_by_platform.csv +++ b/docs/reference/data/widgets_by_platform.csv @@ -8,7 +8,7 @@ Button,General Widget,:class:`~toga.Button`,Basic clickable Button,|y|,|y|,|y|,| Canvas,General Widget,:class:`~toga.Canvas`,A drawing area for 2D vector graphics.,|y|,|y|,|y|,|y|,|y|,, DateInput,General Widget,:class:`~toga.DateInput`,A widget to select a calendar date,,,|y|,,|y|,, DetailedList,General Widget,:class:`~toga.DetailedList`,"An ordered list of content where each item has an icon, a main heading, and a line of supplementary text.",|y|,|y|,|b|,|y|,|y|,, -Divider,General Widget,:class:`~toga.Divider`,A horizontal or vertical line,|y|,|y|,|y|,,|y|,|b|, +Divider,General Widget,:class:`~toga.Divider`,A horizontal or vertical line,|y|,|y|,|y|,|y|,|y|,|b|, ImageView,General Widget,:class:`~toga.ImageView`,A widget that displays an image,|y|,|y|,|y|,|y|,|y|,, Label,General Widget,:class:`~toga.Label`,Text label,|y|,|y|,|y|,|y|,|y|,|b|,|b| MapView,General Widget,:class:`~toga.MapView`,A zoomable map that can be annotated with location pins,|y|,|y|,|y|,|y|,|y|,, diff --git a/iOS/src/toga_iOS/factory.py b/iOS/src/toga_iOS/factory.py index e15b63935c..496e6e8e24 100644 --- a/iOS/src/toga_iOS/factory.py +++ b/iOS/src/toga_iOS/factory.py @@ -15,6 +15,7 @@ from .widgets.button import Button from .widgets.canvas import Canvas from .widgets.detailedlist import DetailedList +from .widgets.divider import Divider from .widgets.imageview import ImageView from .widgets.label import Label from .widgets.mapview import MapView @@ -61,6 +62,7 @@ def not_implemented(feature): "Button", "Canvas", "DetailedList", + "Divider", "ImageView", "Label", "MapView", diff --git a/iOS/src/toga_iOS/widgets/divider.py b/iOS/src/toga_iOS/widgets/divider.py new file mode 100644 index 0000000000..42e8bf3e02 --- /dev/null +++ b/iOS/src/toga_iOS/widgets/divider.py @@ -0,0 +1,41 @@ +from travertino.size import at_least + +from toga_iOS.libs import UIColor, UIView +from toga_iOS.widgets.base import Widget + + +class Divider(Widget): + def create(self): + self.native = UIView.alloc().init() + self.native.interface = self.interface + self.native.impl = self + + # Background color needs to be set or else divider will not be visible. + system_gray_color = UIColor.systemGrayColor() + self.native.backgroundColor = system_gray_color + + # Add the layout constraints + self.add_constraints() + + # Set the initial direction + self._direction = self.interface.HORIZONTAL + + def set_background_color(self, value): + if value is not None: + self.set_background_color_simple(value) + + def rehint(self): + content_size = self.native.intrinsicContentSize() + + if self._direction == self.interface.VERTICAL: + self.interface.intrinsic.width = 1 + self.interface.intrinsic.height = at_least(content_size.height) + else: + self.interface.intrinsic.width = at_least(content_size.width) + self.interface.intrinsic.height = 1 + + def get_direction(self): + return self._direction + + def set_direction(self, value): + self._direction = value diff --git a/iOS/tests_backend/widgets/divider.py b/iOS/tests_backend/widgets/divider.py new file mode 100644 index 0000000000..53b26e7720 --- /dev/null +++ b/iOS/tests_backend/widgets/divider.py @@ -0,0 +1,7 @@ +from toga_iOS.libs import UIView + +from .base import SimpleProbe + + +class DividerProbe(SimpleProbe): + native_class = UIView diff --git a/testbed/tests/widgets/test_divider.py b/testbed/tests/widgets/test_divider.py index ef9afe4848..d5287b2cfe 100644 --- a/testbed/tests/widgets/test_divider.py +++ b/testbed/tests/widgets/test_divider.py @@ -4,7 +4,6 @@ from toga.constants import Direction from toga.style.pack import COLUMN, ROW -from ..conftest import skip_on_platforms from .properties import ( # noqa: F401 test_enable_noop, test_focus_noop, @@ -13,7 +12,6 @@ @pytest.fixture async def widget(): - skip_on_platforms("iOS") return toga.Divider() From 3716411bc6dd0fb785ba429100ae7f0f3385bbc1 Mon Sep 17 00:00:00 2001 From: proneon267 Date: Wed, 3 Apr 2024 04:50:03 -0700 Subject: [PATCH 2/9] Added changelog --- changes/2478.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/2478.feature.rst diff --git a/changes/2478.feature.rst b/changes/2478.feature.rst new file mode 100644 index 0000000000..b5a363c391 --- /dev/null +++ b/changes/2478.feature.rst @@ -0,0 +1 @@ +The Divider widget was implemented on iOS. From 7cad67398bcb9b17c72a97c06d99353c8056482b Mon Sep 17 00:00:00 2001 From: proneon267 Date: Wed, 3 Apr 2024 19:40:52 -0700 Subject: [PATCH 3/9] Fixed implementation --- iOS/src/toga_iOS/widgets/divider.py | 6 ++++-- testbed/tests/widgets/test_divider.py | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/iOS/src/toga_iOS/widgets/divider.py b/iOS/src/toga_iOS/widgets/divider.py index 42e8bf3e02..21a8cc9658 100644 --- a/iOS/src/toga_iOS/widgets/divider.py +++ b/iOS/src/toga_iOS/widgets/divider.py @@ -11,8 +11,8 @@ def create(self): self.native.impl = self # Background color needs to be set or else divider will not be visible. - system_gray_color = UIColor.systemGrayColor() - self.native.backgroundColor = system_gray_color + self.system_gray_color = UIColor.systemGrayColor() + self.native.backgroundColor = self.system_gray_color # Add the layout constraints self.add_constraints() @@ -23,6 +23,8 @@ def create(self): def set_background_color(self, value): if value is not None: self.set_background_color_simple(value) + else: + self.native.backgroundColor = self.system_gray_color def rehint(self): content_size = self.native.intrinsicContentSize() diff --git a/testbed/tests/widgets/test_divider.py b/testbed/tests/widgets/test_divider.py index d5287b2cfe..51a115c313 100644 --- a/testbed/tests/widgets/test_divider.py +++ b/testbed/tests/widgets/test_divider.py @@ -5,6 +5,9 @@ from toga.style.pack import COLUMN, ROW from .properties import ( # noqa: F401 + test_background_color, + test_background_color_reset, + test_background_color_transparent, test_enable_noop, test_focus_noop, ) From d1837442adfcf9fdbe898681674b08e54c30bcb7 Mon Sep 17 00:00:00 2001 From: proneon267 Date: Thu, 4 Apr 2024 09:03:25 -0700 Subject: [PATCH 4/9] Fixed bug in Android --- android/src/toga_android/widgets/base.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/android/src/toga_android/widgets/base.py b/android/src/toga_android/widgets/base.py index 90a05849d4..987f6f0067 100644 --- a/android/src/toga_android/widgets/base.py +++ b/android/src/toga_android/widgets/base.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from decimal import ROUND_HALF_EVEN, Decimal -from android.graphics import PorterDuff, PorterDuffColorFilter, Rect +from android.graphics import Color, PorterDuff, PorterDuffColorFilter, Rect from android.graphics.drawable import ColorDrawable, InsetDrawable from android.view import Gravity, View from android.widget import RelativeLayout @@ -140,6 +140,8 @@ def set_background_simple(self, value): if value in (None, TRANSPARENT): self.native.setBackground(self._default_background) + if value is TRANSPARENT: + self.native.setBackgroundColor(Color.TRANSPARENT) else: background = ColorDrawable(native_color(value)) if isinstance(self._default_background, InsetDrawable): From cc9cbe6d9aab5c9046290c175f4370ef6a8ce8ed Mon Sep 17 00:00:00 2001 From: proneon267 Date: Thu, 4 Apr 2024 09:23:24 -0700 Subject: [PATCH 5/9] Fixed bug in Android --- android/src/toga_android/widgets/canvas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/toga_android/widgets/canvas.py b/android/src/toga_android/widgets/canvas.py index a2be9aaace..8e42ecbfe3 100644 --- a/android/src/toga_android/widgets/canvas.py +++ b/android/src/toga_android/widgets/canvas.py @@ -242,7 +242,7 @@ def get_image_data(self): ) canvas = A_Canvas(bitmap) background = self.native.getBackground() - if background: + if background: # pragma: no branch background.draw(canvas) self.native.draw(canvas) From dccdde4143ab1f6457745aae7c0b52d9a197d055 Mon Sep 17 00:00:00 2001 From: proneon267 <45512885+proneon267@users.noreply.github.com> Date: Thu, 4 Apr 2024 17:31:18 -0700 Subject: [PATCH 6/9] Update android/src/toga_android/widgets/base.py Co-authored-by: Russell Keith-Magee --- android/src/toga_android/widgets/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/toga_android/widgets/base.py b/android/src/toga_android/widgets/base.py index 987f6f0067..46a83712cc 100644 --- a/android/src/toga_android/widgets/base.py +++ b/android/src/toga_android/widgets/base.py @@ -138,10 +138,10 @@ def set_background_simple(self, value): if not hasattr(self, "_default_background"): self._default_background = self.native.getBackground() - if value in (None, TRANSPARENT): + if value is None: self.native.setBackground(self._default_background) - if value is TRANSPARENT: - self.native.setBackgroundColor(Color.TRANSPARENT) + elif value is TRANSPARENT: + self.native.setBackgroundColor(Color.TRANSPARENT) else: background = ColorDrawable(native_color(value)) if isinstance(self._default_background, InsetDrawable): From d802fd3eced0121304b1e8a454ad91159906fdde Mon Sep 17 00:00:00 2001 From: proneon267 <45512885+proneon267@users.noreply.github.com> Date: Thu, 22 Aug 2024 00:17:50 +0000 Subject: [PATCH 7/9] Removed unnecessary changes to android --- android/src/toga_android/widgets/base.py | 6 ++---- android/src/toga_android/widgets/canvas.py | 2 +- iOS/src/toga_iOS/widgets/divider.py | 6 ++---- testbed/tests/widgets/test_divider.py | 3 --- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/android/src/toga_android/widgets/base.py b/android/src/toga_android/widgets/base.py index 46a83712cc..90a05849d4 100644 --- a/android/src/toga_android/widgets/base.py +++ b/android/src/toga_android/widgets/base.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from decimal import ROUND_HALF_EVEN, Decimal -from android.graphics import Color, PorterDuff, PorterDuffColorFilter, Rect +from android.graphics import PorterDuff, PorterDuffColorFilter, Rect from android.graphics.drawable import ColorDrawable, InsetDrawable from android.view import Gravity, View from android.widget import RelativeLayout @@ -138,10 +138,8 @@ def set_background_simple(self, value): if not hasattr(self, "_default_background"): self._default_background = self.native.getBackground() - if value is None: + if value in (None, TRANSPARENT): self.native.setBackground(self._default_background) - elif value is TRANSPARENT: - self.native.setBackgroundColor(Color.TRANSPARENT) else: background = ColorDrawable(native_color(value)) if isinstance(self._default_background, InsetDrawable): diff --git a/android/src/toga_android/widgets/canvas.py b/android/src/toga_android/widgets/canvas.py index 8e42ecbfe3..a2be9aaace 100644 --- a/android/src/toga_android/widgets/canvas.py +++ b/android/src/toga_android/widgets/canvas.py @@ -242,7 +242,7 @@ def get_image_data(self): ) canvas = A_Canvas(bitmap) background = self.native.getBackground() - if background: # pragma: no branch + if background: background.draw(canvas) self.native.draw(canvas) diff --git a/iOS/src/toga_iOS/widgets/divider.py b/iOS/src/toga_iOS/widgets/divider.py index 21a8cc9658..7a281aa7cc 100644 --- a/iOS/src/toga_iOS/widgets/divider.py +++ b/iOS/src/toga_iOS/widgets/divider.py @@ -21,10 +21,8 @@ def create(self): self._direction = self.interface.HORIZONTAL def set_background_color(self, value): - if value is not None: - self.set_background_color_simple(value) - else: - self.native.backgroundColor = self.system_gray_color + # Do nothing, since background color of Divider shouldn't be changed. + pass def rehint(self): content_size = self.native.intrinsicContentSize() diff --git a/testbed/tests/widgets/test_divider.py b/testbed/tests/widgets/test_divider.py index 51a115c313..d5287b2cfe 100644 --- a/testbed/tests/widgets/test_divider.py +++ b/testbed/tests/widgets/test_divider.py @@ -5,9 +5,6 @@ from toga.style.pack import COLUMN, ROW from .properties import ( # noqa: F401 - test_background_color, - test_background_color_reset, - test_background_color_transparent, test_enable_noop, test_focus_noop, ) From 00d827b8811bc3ccae66580d98d990c6c995cf9a Mon Sep 17 00:00:00 2001 From: proneon267 <45512885+proneon267@users.noreply.github.com> Date: Wed, 21 Aug 2024 17:34:48 -0700 Subject: [PATCH 8/9] Restart CI From ba18622db6bef208f534e03e5576e2a5fde9c248 Mon Sep 17 00:00:00 2001 From: proneon267 Date: Thu, 22 Aug 2024 05:27:52 -0400 Subject: [PATCH 9/9] Added screenshot to the docs --- docs/reference/api/widgets/divider.rst | 6 ++++-- docs/reference/images/divider-iOS.png | Bin 0 -> 9887 bytes 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 docs/reference/images/divider-iOS.png diff --git a/docs/reference/api/widgets/divider.rst b/docs/reference/api/widgets/divider.rst index d2a8fa9a8b..663f6cf809 100644 --- a/docs/reference/api/widgets/divider.rst +++ b/docs/reference/api/widgets/divider.rst @@ -29,9 +29,11 @@ A separator used to visually distinguish two sections of content in a layout. :align: center :width: 300px - .. group-tab:: iOS |no| + .. group-tab:: iOS - Not supported + .. figure:: /reference/images/divider-iOS.png + :align: center + :width: 300px .. group-tab:: Web |beta| diff --git a/docs/reference/images/divider-iOS.png b/docs/reference/images/divider-iOS.png new file mode 100644 index 0000000000000000000000000000000000000000..e34ee113afc3fa104f00010eedf9b84a10ac91b0 GIT binary patch literal 9887 zcmeHtXINAHx8(s8Y=DYNQSennl%^sG(iKsWriP+al}-pquL;=TRRNVMAWftM1e8vw zih|NY4-x?t>4eZrLYaO1|L@!~&pdZN+z&JJ-17lK2+8m4vi4eQ^Z2UnCC+UE+Ykid z)VzG*I)bp(z+=W1cKGTvAk~Z@M{P7OoYVJyMeFl%*B|=1F+;ZE{!4SyJ%mU7{w2U+^Ny|L~FveRjepeQc|+i&(!!_!!yHY7xokxzSqw*xN6nKzMFTa-A>M} znol-87kT({ZGHX6$${@>5lso2=XXyrJ#<^f=9&tcA7NI0`%G0z)t=nJ0*65EKG|+h z-;6#&?n>_r{PSFx8iF1Wk!NM$L%;vnvgsE3Jy7`n*%ugVdU_WB`t?g&^-|aY`(pAN z%?NE8=K6Krb_clSSE6q#c@ZSz$@sW~po)KwzAfLy0{r;Igbv=p!J+q-HQfC3Ej37M zF3EkY-I0H~*uLZF+H6dMuyBpPB13}z{AW(Lf+S=NhqgkAXhK3p%8&7u$ zDtf+6Fl0m2Mm{RTdxZ6{Ky>AO(D(aykKqXf5#@%M_wKrs49C(H61u+`p=IWH24b;T z17qW?*RKz2T)gNo+M1$3E9_tuNUf+arIt7wX=pt9H8?orS-zKtr$cN4`#3I6ASyaK ztF-inhlj_N%a@;1X-;!}ZnY;112zcgZ{EJu-nw!o`^1kz# zvvYGE^P|^9L`1&QJjEqK0*61z5#*ml9muOTYOuBZP^3wOz-@ktc);Ukb2`RW= z-p(u4sdb|A&HekVnCjNnt0JPJ*q(Q{eaDFzkuP8JEyv7RuCJ~bzq@rWV{+m#nPL%8 z`TF&1iG__7a)3KyEJIF_;Ad{4DJCh|HTkWe$aldZDl+orF!%QD9iMqE-db!hC~-3O z;pflGm$D;vhuDx-v3(LZZe5mMJD}j6`AsFSdYwdE6Mm-j%PTQ6^B5-S^HbjaLAUv^ zlw8?6mX?y(>2*zA-80=eCbj|v`T6ZHmHmpXC({Y}`S~FaIJnvUh>nV$lP_IH61)U{ z{`^_oE%NnlnQ7gp$2_5U=Hj=+3?i1XK(W|wo9L6C<5TW2VbhePI$#G^7j}BOmYcdh z)vs!8ZLJeV?WZi(9}JKY!p~nz)ryfS9#&ya7KD;jm~TH*c#t2Ct73bR-fP%CwpTGR z86hD%(86}6Lg3OeGO*NQTzJ?)T7tColSJ=dTRU{k+@d8-(lsNF5>rwRWiIqygK9fv zS$9lP(WI!TsF=wglrq#9BQ7gzU{d15>AMa^Z8tyK)?4XUlC!vX?_S2xi2&kpO!_9} zk+1Fj6vJ@BVnNba+ogjB*4Ak`i;7-T`o6v-mDTCmea+jEyJy1G*whpqLj3~w3kbLm z;FpLbl5zN+g9r2ALo=NRs(P#N<*sgSI`t`OgG;{so>W|Kp?ySRz`9T6Qomm!`MXoe z#ZZB?Dm*hQIa#f&tgN@(E6=*W2um4<3T=JBt-?1Qw|4ya@oNqaS!5sP{8(yak&Q8|Ad zs!aUo(TkfmZ*Cxyjn19BHxz>eY%m8rAdTZs#B;YyYRMfseq1xgf>4&>KmFqzuD{}2 zrwtV5czy`UwSu1Ut1u#r#6GlQk^RoTM`kHb*W~3znt8Zi! z-zz60!libehYug}g@^q8_ZG{UU!O0UnI%}aK=LiWJU zg$c;UKa%K|Yu(~TR;Fa;n$Gi{yy*FSlbZc zOJO${T@=cGdKEMGl=t+l#tNqrYG!8UyR`%sME)MK1Ian^5-~M5cXI4`_hGK%yK=m! zj7QSctFvx#v=ZyaeW_mbeidfWqeuKu?YQENL}kB_-@mQ22157CG0fcl$}cF8bbIsW zO%LfzeVE`J|MdEFu&N9-kBIdh43!gDSXgLY%*o1>~?FmId06suuW6hH+@_bdSpVhgQt+MIg%iQom zFCg9D3^$er0tVWb06iSPQce-Gvrp5iu1NdNkKR5Yf463ludA!8F8SO8ZOezj!E%a0 zdDytv*fF@*A&GEy+!XAy1B|YN-Z5q5c$Nl@X1l}%8P_P44_;eB= zgQ>Fb{5$1~7cM*;h9b$z&dHJO8=2Izv&%I1qscxB4yHP&5>~xA52}3EU!s)^^d{1+ zMvtMLyFQnM{q*;)Vrr6Sue~pzrsQ1J#yXP)8W(|BD3oFIDh*$}cv0MIe)q%X=wOkw z^mK-Q@Y@qJGcyWFEHm=_kh=B|)Pz*>IxZ*G)I>8*7He2D*YDaW=~~{GPEvayhafOjM}N1R9XUY7 zsh>Z;2TD9^|7ja-wK+}!nJ@aed3oHK0Vsaih-GACh)YP6^6s;;v59NQHp=UUI&@s9 zT>kZxEs@@Ink*CLMeE%=&4(kVno>VJ;1GvO|8dq#DrV>_`C!1Ri&~xwD-sCRm|#lp zuQcf#P~6G1T)J^L8QP=JmMnxZ(=r!{OiXkVqjYy?6&7|H4lFM(2aaf6zI-x@R)txy z8of+8BP^WSHJtPItz(z)G7^Y6_$O-5pJN6$8t|L>=~wx;&`c0f8M))NlZ>Q_=Hr9_^6NK z>o9j5egXHj0s^G=7x0HbuAjjN3GX^L`zn>c@9DXr)pw7L50pfVv#upP?~ZU zq;(JLNhDc)dT$o+lLtcd(YDm$;k)aSD61x+mS zY-eSIdEDP%so@lxm2ql&a1EUA5kzWqr~l?-bWvW8k46@((;N1ASnQI0Th%H(V56XO z7f{N3e<5s$!b3p1h0(TJ{Sx1W@#K1w0-I3cq-$Acb-+e){X~;Ody!d@g8|%s-dPEW zruWsIkoREp?N1)netAnQ&$R4%(CtmK-B0$4YY(-hUQ%Qb+QM9M)vL3O$w}W=;l|?{ zEbGGz+}+);TEfvD|M}b8>UCdC8L%CC#hh-<;P2sKhmLfe<}ke2S^wpC17`$>dIf?~ z%G^fSmFNvo;tS1k124y6TC)mqzzjv>8tgI@Wt8Q&G?h-`KW#G#omHp|6Q2Yfp;I0& z6{AHl^D@iMDNtVV-TOR&=>Tw&py-vmZQC}-lDzEf4j4st#3Xr_J8E;R2+FM8`Ji{= z&bp-&bAdWVi0T#Z?Gz9YkntGTZ{!*>^Xf|@tTBqljj4?__t+#oHdaSe*A~$Prl(&t zvcMPR{4h5%$}n*%)7V&}2k0!w4fq%A=HX$B0}A^mu`A8agw)%$L9yd1%RkuC($a2z zG_qV-Ss@L_VGHm6JddfQ(z=t@=QGaS#QOam8d{{(Iu6Vy2AIx|rNsWQySP55k|SgB z&Q)z`G3Hs;jnT4YQ#fkz*m!ncH`Tcsmj$iCJicJN4QTAgUO%9!GW!GsvNWFVvj^M& zZaLpM^85E_xRz8KK&kAVPg~HztE6Q5=+UFzeCwx%ImXsoucxG>_^u4aNL~t1ET0*y zpXl=%knPfmKchodUV6pOE%a7M**Ej`>+^Y)i*dlMlyM3li}15wMHhfoj6t3{r~1l0 znq?_=(?33tpbCJ={mV4X&Z}w{P4fSB`hY5sBBeEcWaponuRRN8Fh7=&BqlDd1*0sT z?CR(R(2bE^y;A>dSM_|m*176#Vr3rA-Mz3sU<0p{sPxS<$*GStRzxxHfYy7E_|KHq zj(21{>&`XHhFLdWOm)IoSy@Hk)Ya8xU4C82fzAL@)&)~W_tzD@Dqvxm@7`UlhU$=o zLF+;5?V2xZ913q7a>z#~^YXH{4l>qU^WWIrvO9x3Ao5qT9q7 zH&cNiwr>3Ei7#0YHI8dHZxTO#JWswhKq4uK9656A(4licA`mVUeROwd~K%wU{q349*tU(4U$DuQ!_DXdU`r}m>sFv z4?u$y@|VN6z=(UmcF^k*3K?)6K#2idcQ-+0!&S5Ek4pMCFF5o^x*@t|pt@YkOcPy! zD=X%*BR||LIR2?#58V6zP0m6j{wqZJZOJhuuc-}ejt3fB|P3;t2v|0v!-Rp*MCpep8+qTX2%5e=CAw9?nb zKO^Z|5jDR<=PZrYqE*jH6*JES$$N7)%GdI2_}y5c-M-W9?~L z1*p;SXXpxQ!x;THKkoQb5x57?je`H~G1|PzIPis%AN<@RgCJ_uAF`K`)(?eUTz7G} zoGV&d2B81He7VdisGy&b;AyO885t9UWiHYHTCe*1SAdWM+GA{u&q*sTHbixzy*&}0 zWZ%KpYEwQ6AR;Cv)(%h))<){5PZt)Z`b?x^vUP^HU(nGBMc?1l^zA=j=?l%%?KO0U z9m*GP-T%f?HT`k>9~45TKtn@A_>sN5yx9Ht72rqAtmV^VNag4_q?~1#WUQkZEO-dPn!M* z*TYNqKe!$?^`N4HbIXPWFnw7D*}#+nOeTClh0XU^KJ#gK|Nec$*RSvKIyTL5XwXAbgXact^ms-Km>`}O2;%=!2u-c=gwT(Q3MFeRt5GOw z0(IY>J;#)l&A`-%I08lm48YOHqKPMI5^}cR@~u%r2l&-}W9O5JaF#XTQxp)7jzy~O zqtB-*m&}_Jl@*hHxC9m6%+I@{NdwJR%%t_aSGTvfXVj~%?`rkGcML3}m!~Wv6B2N} zD~t6fcSu@PnG%Ua)JPzoIdtgI6+Jz*R*S2Ecpa0K?HWnaNK(N^hKIj~HU*|f z%*?zDyu{$zwHHJ)_=5U1ve<4rutlO>q)(q-0HWd3;K??ft`qOSK5ISH5XI=D6Glol z7z-0RL!j+B56HK6e+PC+tSm`p`JunBY1b&c!^_?irlqZPrT+Rz5SsL}!wOzgrl}-S zZ)a6X%7^9O5qcp|l8p?h@=0moBS$_xx4LlQ0w3yejE|2?xIwk>q1MK{p0@UmQGsti z@iNPk1?0uIeM7GNz~7!-yK!T*z}d+u*QLwA=w>(n?%j9%A^e1qD!5@uJXw*hWVtds zbP=jH4S)M~%FI)X)sG`H6P12T?DNWNg7;d!e0kRt5gBPE0qiFeSYx5Ho!!ak6BbV3 zPuyT|@=Jf3UHWu##~_nlEe8-pq=AV*AIajc15*+&RLRB7EeFzUZ87N#E{-?=j@Lv9 zH-fCAFw41_C$O=xF_d%v8E*n$+GS1?%%i1M#79#_-);9 zaq<+TUJ@_o^nKK;`Q9{FkSO82x_YRQxzA_kji4a*L8z*t`GYF{rR1G{A3;9ajYYh8 zaa+8ma<)PIBG?5)I;Y(CovllbQi!dstx0!uc=(+OdLsyH|DX}o)t}rdR6mYklx`A6 z_*DG8BbE+U0}>LL+=2?7owg7u0uxY~dkJAv<8bS>&lqdhIBoY|*sBKK3mYGVSeaa)M zE7LVHG-MB5uCrKm|pSEq0 z=>jRL4ub!EPW1^hl6%|iP@}9}>tC2PaI`WG5|%%-s|X9%>MwfjU_eM(c`f{Z9#@5P zrl+oo8|BVZH$JV6hJ$sU0?I{>%7Gw?Kuqs$d-C*Y2S!iXI-6GNGAQ8zLXR{QZQ{64 zJ^K2ogh$nsCJs09$}KEiFG=WX5@?KLhYx?$Um^#rci2+NsvD*Q1ZEC#3e2P(qbXL; zRM$%x^JA7-*ieo;=tu%&C}`?Ry~Itmgo?JC`d*e!>g_e|s2mXWR`qYNK-Nc7pJ^|h z(@Rk3+H-47c?%XS&2%IAP$6aGK9UEv7>dXfis;84cUloe3_#kDx?QSN4OqWi7nnFO zX&VeTKp!Ny)bXd2)|g9F6T9uJizU?t-rnA7coaiwIptoCuY>T+uBz&9?^et_Dk3u0 zAfC6wt}558+yP}Dq%^GfWj)3_1M9ywHFbi;HZa)}jvB5Vxn2YU zp&+0x)XSS=2Oo$RcKeSHwuY+ZX&LeJi5_2wYr|BL*UNs=qst__mCa4*P@ zSNDCFruts5_uuPDQuDfFGJb!%)E0&!qxZ9J%_EhyVuAr)&hJo9 zm9AX%>xuY%Au?0B8mx2`itQ5LJS-+~3akN#mmzwwgj3MMHL#dERY?+4QRyF%-Guzb zhsqb-bB7LPEYjt8cz7O6QDH)QjJ0b-MMWhl&;`E%!{|`X448*pd^^JsX)Y zX2e<61hOaq#VT?e(bcT?@`c8?IxE@gZR_>D)FnMQNay9r4|6|q8XB(Pz}wCDYo4)1 z)JznLO11z&eFLs|rN&yNI%>!13>6dpHuCZDX$5diKh`Qd5&F5V&Ul>bfc%dF+~7$E zzMwOXJ+~MY;0vH<&X~j}Eds03(Q{k$*g_M6tMv6|2R%0G1N2`H&!YO#t|x%0nI#-LXQm=!OU; zq;Eq%i~Qlk=YbC-rv3&Z-gw~LNiV6hJM-CP!wcFKYF8R2GthpgP(Xe;I1@7|Y|-WIg9qmGZI zHiU~Oumnd!IR20Z)>$@(M~BqtPPa-eR8h~707SW{S23c#RG&(A}8 z+KqUN_E})(2x9Jk`7hwa5HBbgaK#oP<(&H+d+|=IhyKG4m#@3EtpmM7NxZ?k2jc=dlr2+DhKB$c^HcWeLdcm8cb}Vc;#c8<7CtG8;7pL z79!qpz}u)8z`%`62Ns$Ef90cs9anC4Dlt(H=30o1-&T{6MDPvnap6E-q3sMO&&}0E z-PWsk)zxWrOc|`t1eARX(-M|+hd_rsh|81o4sMJRH!?P-S{V(RnP{TF9!eaotMIbc zd)Qq!2zyu!a8orG$mmuU)$qT@3EqnJCqbRX1E8Np|{C@jckX^y^EBi+3o}D|n{m(Zy1s zJ*L3YZ@;wF-}b@F7Z9h42u1MClPL2K>sXdyT>z6B1DG?$VtgO~pkg!HKA7H8m(+g$ zx#mDdQ=Dw$?5E$b4^6_IK7G2Y2y2AtgMmb3Fc^Wu{$OZzRr-06^;})^0WMetYN=61 z=0p%Y@#8j4FH;~)ZHs(Cw@25Rbh)+NABJ4VNE5kp)RLJ~qbI7C&jaGg2&OrWuMhA5 z!LshPHh+I-^U?+@qPy)+A2U9mAlxnsE;~>Zc{G{uaHWt818NUa5cm`fIR(9e|l+Wo6}u%)m!< z(*0~|Yio<_+781+?v<${c}&PS_MC(jjH&R&kgLGCL2njX)^iPwj9?)U>4MkBmUC(0 zB3kI~x}-sN3nypSYmKKe;HYAj$zQ*=iNu(`1|jPJw(AvbZGCU=(rw$f7s0kH$GE6r z_Guuj$SYy6MO$|1JPr-*LSGIW;-=Zy>Orz7>_{4hTvsZd#iRAoyA5^xkL$!rYsAYr zccTCYSZ>BEX$%W5sFzGwbyFSuVA*63TmqP9J5WuXBFlle1Vj)^?NioHO=z}t_cRHr06B7kWhA{I0ndp zN-S1m*fV+l@lWlQ=bBFYF8>VPD=3J|#$Uet_f+);0akYb7`RoM&vgtYDn34Ym+-|+ z6?KhL<=OxwU0p^Nf_E(xuV23&86Qub#0JMnTYcGy`(An~Q@Fm0;SQAnh*+c?l=n`F zu)4f&K$sIz!~DS|@gqkrJX2nL={ebxvfsJl{nxAef#Q`jdovZ%!LDTS7ZA5p*hV`5NRep8)VRvLvobRJ zz*tdbR^%~Z2)nCenP;Je?<`Dy>(KWCE1YAGIbJYkc-;b~qbvWtstdbdqXQE{7o~;k zpypuVqz|wP{;7cc-DD`A?kqR(S5Uhbx>8I^$`JT^i^7BiD5tAH-(tY??OI_4|JL5v z1jfhi-Mb^7KTigI)*=2kSUD+9HzdjDznmDUiO QKgO-5hVF&j^SAE*57I99od5s; literal 0 HcmV?d00001