3 using System.Reflection;
4 using System.Threading.Tasks;
5 using TWebView = Tizen.WebView.WebView;
7 namespace Xamarin.Forms.Platform.Tizen
9 class RefreshIcon : ContentView
11 public static readonly int IconSize = 48;
12 static readonly Color DefaultColor = Color.FromHex("#6200EE");
13 static readonly string IconPath = "XSF.Resources.refresh_48dp.png";
20 HeightRequest = IconSize;
21 WidthRequest = IconSize;
22 var layout = new AbsoluteLayout()
24 HeightRequest = IconSize,
25 WidthRequest = IconSize,
28 layout.Children.Add(new BoxView
31 CornerRadius = new CornerRadius(IconSize),
32 }, new Rectangle(0.5, 0.5, IconSize, IconSize), AbsoluteLayoutFlags.PositionProportional);
36 Source = ImageSource.FromResource(IconPath, typeof(ShellItemRenderer).Assembly),
39 layout.Children.Add(_icon, new Rectangle(0.5, 0.5, IconSize - 8, IconSize - 8), AbsoluteLayoutFlags.PositionProportional);
42 IconColor = DefaultColor;
45 public Color IconColor
49 return PlatformConfiguration.TizenSpecific.Image.GetBlendColor(_icon);
53 PlatformConfiguration.TizenSpecific.Image.SetBlendColor(_icon, value == Color.Default ? DefaultColor : value);
57 public double IconRotation
61 return _icon.Rotation;
65 _icon.Rotation = value;
79 _icon.AbortAnimation("RotateTo");
82 async void TurnInternal()
84 await _icon.RelRotateTo(360, 1000);
90 class RefreshLayout : StackLayout
92 static readonly int MaximumDistance = 100;
94 public RefreshLayout()
97 HorizontalOptions = LayoutOptions.FillAndExpand;
99 RefreshIcon = new RefreshIcon
101 HorizontalOptions = LayoutOptions.Center,
102 VerticalOptions = LayoutOptions.Center,
103 TranslationY = -RefreshIcon.IconSize,
106 Children.Add(RefreshIcon);
109 RefreshIcon RefreshIcon { get; set; }
111 public Color RefreshIconColor
113 get => RefreshIcon.IconColor;
114 set => RefreshIcon.IconColor = value;
117 public void SetDistance(double distance)
119 var calculated = -RefreshIcon.IconSize + distance;
120 if (calculated > MaximumDistance)
121 calculated = MaximumDistance;
122 RefreshIcon.TranslationY = calculated;
123 RefreshIcon.IconRotation = 180 * (calculated / (float)MaximumDistance);
124 RefreshIcon.Opacity = 0.5 + (calculated / (float)MaximumDistance);
129 _ = RefreshIcon.TranslateTo(0, MaximumDistance / 2.0, length:200);
133 public bool ShouldRefresh()
135 return RefreshIcon.TranslationY > (MaximumDistance - 30);
138 public async Task StopAsync()
140 _ = RefreshIcon.FadeTo(0);
141 await RefreshIcon.ScaleTo(0.2);
145 public async Task ResetRefreshIconAsync()
149 RefreshIcon.IconRotation = 180 * (RefreshIcon.TranslationY / (float)MaximumDistance);
150 }).Commit(RefreshIcon, "reset", length: 250);
151 _ = RefreshIcon.FadeTo(0.5, length: 250);
152 await RefreshIcon.TranslateTo(0, -RefreshIcon.IconSize, length: 250);
163 public class RefreshViewRenderer : LayoutRenderer
165 GestureLayer _gestureLayer;
167 RefreshLayout _refreshLayout;
168 IVisualElementRenderer _refreshLayoutRenderer;
170 public RefreshViewRenderer()
172 RegisterPropertyHandler(RefreshView.RefreshColorProperty, UpdateRefreshColor);
173 RegisterPropertyHandler(RefreshView.IsRefreshingProperty, UpdateIsRefreshing);
176 RefreshView RefreshView => Element as RefreshView;
177 RefreshState RefreshState { get; set; }
180 protected override void OnElementChanged(ElementChangedEventArgs<Layout> e)
182 base.OnElementChanged(e);
188 _gestureLayer?.Unrealize();
189 _gestureLayer = new GestureLayer(NativeView);
190 _gestureLayer.Attach(NativeView);
192 _gestureLayer.SetMomentumCallback(GestureLayer.GestureState.Move, OnMoved);
193 _gestureLayer.SetMomentumCallback(GestureLayer.GestureState.End, OnEnd);
194 _gestureLayer.SetMomentumCallback(GestureLayer.GestureState.Abort, OnEnd);
197 void UpdateRefreshLayout()
199 _refreshLayout = new RefreshLayout();
200 _refreshLayout.RefreshIconColor = RefreshView.RefreshColor;
201 _refreshLayoutRenderer = Platform.GetOrCreateRenderer(_refreshLayout);
202 (_refreshLayoutRenderer as LayoutRenderer).RegisterOnLayoutUpdated();
204 Control.Children.Add(_refreshLayoutRenderer.NativeView);
205 var measured = _refreshLayout.Measure(Element.Width, Element.Height);
206 var parentBound = NativeView.Geometry;
211 Width = parentBound.Width,
212 Height = Forms.ConvertToScaledPixel(measured.Request.Height)
215 _refreshLayoutRenderer.NativeView.Geometry = bound;
216 RefreshState = RefreshState.Drag;
219 bool IsEdgeScrolling()
221 if (RefreshView.Content is ScrollView scrollview)
223 if (scrollview.ScrollY == 0)
228 else if (Platform.GetRenderer(RefreshView.Content) is CarouselViewRenderer carouselViewRenderer)
230 var collectionView = carouselViewRenderer.NativeView;
232 var scroller = collectionView.GetType().GetProperty("Scroller", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(collectionView);
234 if (scroller != null)
236 if ((scroller as Scroller)?.CurrentRegion.Y == 0)
242 else if (Platform.GetRenderer(RefreshView.Content) is StructuredItemsViewRenderer itemsViewRenderer)
244 var collectionView = itemsViewRenderer.NativeView;
246 var scroller = collectionView.GetType().GetProperty("Scroller", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(collectionView);
248 if (scroller != null)
250 if ((scroller as Scroller)?.CurrentRegion.Y == 0)
256 else if (Platform.GetRenderer(RefreshView.Content) is ListViewRenderer listViewRenderer)
258 if (GetScrollYOnGenList(listViewRenderer.Control.RealHandle) == 0)
263 else if (Platform.GetRenderer(RefreshView.Content) is WebViewRenderer webviewRenderer)
265 if (GetScrollYOnWebView(webviewRenderer.Control.WebView) == 0)
274 int GetScrollYOnGenList(IntPtr handle)
276 var interop = typeof(EvasObject).Assembly.GetType("Interop");
277 var elementary = interop?.GetNestedType("Elementary", BindingFlags.NonPublic | BindingFlags.Static) ?? null;
279 if (elementary != null)
281 object[] parameters = new object[] { handle, -1, -1, -1, -1 };
282 elementary.GetMethod("elm_scroller_region_get", BindingFlags.NonPublic | BindingFlags.Static)?.Invoke(null, parameters);
283 return (int)parameters[2];
288 int GetScrollYOnWebView(TWebView webview)
290 var property = webview.GetType().GetProperty("ScrollPosition");
291 if (property != null)
293 var point = (ElmSharp.Point)property.GetValue(webview);
299 void OnMoved(GestureLayer.MomentumData moment)
301 if (RefreshState == RefreshState.Idle)
303 if (IsEdgeScrolling())
305 UpdateRefreshLayout();
309 if (RefreshState == RefreshState.Drag)
311 var dy = moment.Y2 - moment.Y1;
312 _refreshLayout?.SetDistance(Forms.ConvertToScaledDP(dy));
316 void OnEnd(GestureLayer.MomentumData moment)
318 if (RefreshState == RefreshState.Drag && _refreshLayout != null && _refreshLayoutRenderer != null)
320 if (_refreshLayout.ShouldRefresh())
322 _refreshLayout.Start();
323 RefreshState = RefreshState.Loading;
324 RefreshView.SetValueFromRenderer(RefreshView.IsRefreshingProperty, true);
328 _ = ResetRefreshAsync();
333 async Task ResetRefreshAsync()
335 var refreshLayout = _refreshLayout;
336 var refreshIconRenderer = _refreshLayoutRenderer;
337 _refreshLayout = null;
338 _refreshLayoutRenderer = null;
339 await refreshLayout.ResetRefreshIconAsync();
340 refreshIconRenderer?.Dispose();
341 RefreshState = RefreshState.Idle;
344 void UpdateRefreshColor()
346 if (_refreshLayout != null)
348 _refreshLayout.RefreshIconColor = RefreshView.RefreshColor;
352 async void UpdateIsRefreshing(bool init)
357 if (!RefreshView.IsRefreshing && RefreshState == RefreshState.Loading)
359 var refreshLayout = _refreshLayout;
360 var refreshIconRenderer = _refreshLayoutRenderer;
361 _refreshLayout = null;
362 _refreshLayoutRenderer = null;
363 await refreshLayout?.StopAsync();
364 refreshIconRenderer?.Dispose();
366 RefreshState = RefreshState.Idle;