if (Element.ValueType == DateTimeType.Date)
{
Control.Style = "datepicker/circle";
+ Control.Format = "%d/%b/%Y";
}
else if (Element.ValueType == DateTimeType.Time)
{
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-using System;
-using System.Diagnostics;
using ElmSharp;
+using System.Diagnostics;
+using Tizen.Applications;
namespace Tizen.Wearable.CircularUI.Forms.Renderer
{
+ public class InitOptions
+ {
+ public CoreApplication Context { get; set; }
+
+ public string GoogleMapsAPIKey { get; set; }
+
+ public InitOptions(CoreApplication application)
+ {
+ Context = application;
+ }
+
+ public InitOptions(CoreApplication application, string googleMapsAPIKey)
+ {
+ Context = application;
+ GoogleMapsAPIKey = googleMapsAPIKey;
+ }
+ }
+
public static class FormsCircularUI
{
public static readonly string Tag = "CircularUI";
public static void Init(string apiKey)
{
+
if (!string.IsNullOrEmpty(apiKey))
{
GoogleMaps.Init(apiKey);
Init();
}
+
+ public static void Init(CoreApplication context)
+ {
+ var resPath = context?.DirectoryInfo?.Resource;
+ if (!string.IsNullOrEmpty(resPath))
+ ThemeLoader.Initialize(resPath);
+
+ Init();
+ }
+
+ public static void Init(InitOptions options)
+ {
+ var resPath = options.Context?.DirectoryInfo?.Resource;
+ if (!string.IsNullOrEmpty(resPath))
+ ThemeLoader.Initialize(resPath);
+
+ Init(options.GoogleMapsAPIKey);
+ }
}
}
\ No newline at end of file
using ElmSharp.Wearable;
using System;
using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Tizen;
-using ELayout = ElmSharp.Layout;
using EColor = ElmSharp.Color;
+using ELayout = ElmSharp.Layout;
namespace Tizen.Wearable.CircularUI.Forms.Renderer
{
{
readonly int _dafaultIconSize = 60;
- class Item
+ class Item : INotifyPropertyChanged
{
- public Element Source { get; set; }
+ Element _source;
+ public Element Source
+ {
+ get
+ {
+ return _source;
+ }
+ set
+ {
+ if (_source != null)
+ {
+ _source.PropertyChanged -= OnElementPropertyChanged;
+ }
+ _source = value;
+ _source.PropertyChanged += OnElementPropertyChanged;
+ UpdateContent();
+ }
+ }
+
public string Text { get; set; }
public string Icon { get; set; }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ void UpdateContent()
+ {
+ if (Source is BaseShellItem shellItem)
+ {
+ Text = shellItem.Title;
+ Icon = (shellItem.Icon as FileImageSource)?.ToAbsPath();
+ }
+ else if (Source is MenuItem menuItem)
+ {
+ Text = menuItem.Text;
+ Icon = (menuItem.IconImageSource as FileImageSource)?.ToAbsPath();
+ }
+ else
+ {
+ Text = null;
+ Icon = null;
+ }
+ SendPropertyChanged();
+ }
+
+ void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ UpdateContent();
+ }
+
+ void SendPropertyChanged([CallerMemberName] string propertyName = "")
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
}
Box _outterBox;
}
_itemCache = items;
+ ClearItemPropertyChangedHandler();
_naviMenu.Clear();
_items.Clear();
// header
var genitem = _naviMenu.Append(_defaultClass, data, GenListItemType.Normal);
genitem.SetPartColor("bg", _backgroundColor);
_items.Add(genitem);
+ data.PropertyChanged += OnItemPropertyChanged;
}
}
_footer = _naviMenu.Append(_defaultClass, new Item { Text = "" });
return false;
}
+ void ClearItemPropertyChangedHandler()
+ {
+ foreach (var item in _items)
+ {
+ (item.Data as Item).PropertyChanged -= OnItemPropertyChanged;
+ }
+ }
+
+ void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var item = _items.Where((d) => d.Data == sender).FirstOrDefault();
+ item?.Update();
+ }
+
}
public enum DraggedState
{
--- /dev/null
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using ElmSharp;
+using System.IO;
+
+namespace Tizen.Wearable.CircularUI.Forms.Renderer
+{
+ internal static class ThemeLoader
+ {
+ const string CircularUITheme = "tizen-circular-ui-theme.edj";
+
+ public static string AppResourcePath { get; private set; }
+
+ public static bool IsInitialized { get; private set; }
+
+ public static void Initialize(string resourcePath)
+ {
+ if (!IsInitialized)
+ {
+ AppResourcePath = resourcePath;
+ IsInitialized = true;
+ AddThemeOverlay(CircularUITheme);
+ }
+ }
+
+ public static void AddThemeOverlay(string themeFilePath)
+ {
+ if (!IsInitialized)
+ {
+ Log.Error(FormsCircularUI.Tag, $"ThemeLoader is not initialized properly");
+ return;
+ }
+ Elementary.AddThemeOverlay(Path.Combine(AppResourcePath, themeFilePath));
+ }
+ }
+}
((AdaptiveTrigger)bindable).UpdateState();
}
- internal override void OnAttached()
+ protected override void OnAttached()
{
base.OnAttached();
}
}
- internal override void OnDetached()
+ protected override void OnDetached()
{
base.OnDetached();
--- /dev/null
+using System;
+
+namespace Xamarin.Forms
+{
+ public class AppThemeChangedEventArgs : EventArgs
+ {
+ public AppThemeChangedEventArgs(OSAppTheme appTheme) =>
+ RequestedTheme = appTheme;
+
+ public OSAppTheme RequestedTheme { get; }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+namespace Xamarin.Forms
+{
+ public class AppThemeColor : BindableObject
+ {
+ public AppThemeColor()
+ {
+ ExperimentalFlags.VerifyFlagEnabled(nameof(AppThemeColor), ExperimentalFlags.AppThemeExperimental, nameof(AppThemeColor));
+
+ Application.Current.RequestedThemeChanged += RequestedThemeChanged;
+ }
+
+ public static readonly BindableProperty LightProperty = BindableProperty.Create(nameof(Light), typeof(Color), typeof(AppThemeColor), default(Color), propertyChanged: (bo, __, ___) => UpdateActualValue(bo));
+
+ public Color Light
+ {
+ get => (Color)GetValue(LightProperty);
+ set => SetValue(LightProperty, value);
+ }
+
+ public static readonly BindableProperty DarkProperty = BindableProperty.Create(nameof(Dark), typeof(Color), typeof(AppThemeColor), default(Color), propertyChanged: (bo, __, ___) => UpdateActualValue(bo));
+
+ public Color Dark
+ {
+ get => (Color)GetValue(DarkProperty);
+ set => SetValue(DarkProperty, value);
+ }
+
+ public static readonly BindableProperty DefaultProperty = BindableProperty.Create(nameof(Default), typeof(Color), typeof(AppThemeColor), default(Color), propertyChanged: (bo, __, ___) => UpdateActualValue(bo));
+
+ public Color Default
+ {
+ get => (Color)GetValue(DefaultProperty);
+ set => SetValue(DefaultProperty, value);
+ }
+
+ private Color _value;
+ public Color Value
+ {
+ get => _value;
+ private set
+ {
+ _value = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public static implicit operator Color(AppThemeColor appThemeColor)
+ {
+ switch (Application.Current?.RequestedTheme)
+ {
+ default:
+ case OSAppTheme.Light:
+ return appThemeColor.IsSet(LightProperty) ? appThemeColor.Light : (appThemeColor.IsSet(DefaultProperty) ? appThemeColor.Default : default(Color));
+ case OSAppTheme.Dark:
+ return appThemeColor.IsSet(DarkProperty) ? appThemeColor.Dark : (appThemeColor.IsSet(DefaultProperty) ? appThemeColor.Default : default(Color));
+ }
+ }
+
+ static void UpdateActualValue(BindableObject bo)
+ {
+ var appThemeColor = bo as AppThemeColor;
+ switch (Application.Current?.RequestedTheme)
+ {
+ default:
+ case OSAppTheme.Light:
+ appThemeColor.Value = appThemeColor.IsSet(LightProperty) ? appThemeColor.Light : (appThemeColor.IsSet(DefaultProperty) ? appThemeColor.Default : default(Color));
+ break;
+ case OSAppTheme.Dark:
+ appThemeColor.Value = appThemeColor.IsSet(DarkProperty) ? appThemeColor.Dark : (appThemeColor.IsSet(DefaultProperty) ? appThemeColor.Default : default(Color));
+ break;
+ }
+ }
+
+ void RequestedThemeChanged(object sender, AppThemeChangedEventArgs e)
+ {
+ UpdateActualValue(this);
+ }
+ }
+}
\ No newline at end of file
using Xamarin.Forms.Internals;
using Xamarin.Forms.Platform;
using System.Diagnostics;
+using Xamarin.Forms.Core;
namespace Xamarin.Forms
{
public class Application : Element, IResourcesProvider, IApplicationController, IElementConfiguration<Application>
{
+ readonly WeakEventManager _weakEventManager = new WeakEventManager();
Task<IDictionary<string, object>> _propertiesTask;
readonly Lazy<PlatformConfigurationRegistry<Application>> _platformConfigurationRegistry;
}
}
+ public OSAppTheme RequestedTheme => Device.PlatformServices.RequestedTheme;
+
+ public event EventHandler<AppThemeChangedEventArgs> RequestedThemeChanged
+ {
+ add
+ {
+ ExperimentalFlags.VerifyFlagEnabled(nameof(Application), ExperimentalFlags.AppThemeExperimental, nameof(RequestedThemeChanged));
+
+ _weakEventManager.AddEventHandler(value);
+ }
+ remove => _weakEventManager.RemoveEventHandler(value);
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void OnRequestedThemeChanged(AppThemeChangedEventArgs args)
+ => _weakEventManager.HandleEvent(this, args, nameof(RequestedThemeChanged));
+
public event EventHandler<ModalPoppedEventArgs> ModalPopped;
public event EventHandler<ModalPoppingEventArgs> ModalPopping;
if (value.StartsWith("#", StringComparison.Ordinal))
return Color.FromHex(value);
- if (value.StartsWith("rgba", StringComparison.OrdinalIgnoreCase)) {
+ if (value.StartsWith("rgba", StringComparison.OrdinalIgnoreCase))
+ {
var op = value.IndexOf('(');
var cp = value.LastIndexOf(')');
if (op < 0 || cp < 0 || cp < op)
return new Color(r, g, b, a);
}
- if (value.StartsWith("rgb", StringComparison.OrdinalIgnoreCase)) {
+ if (value.StartsWith("rgb", StringComparison.OrdinalIgnoreCase))
+ {
var op = value.IndexOf('(');
var cp = value.LastIndexOf(')');
if (op < 0 || cp < 0 || cp < op)
return new Color(r, g, b);
}
- if (value.StartsWith("hsla", StringComparison.OrdinalIgnoreCase)) {
+ if (value.StartsWith("hsla", StringComparison.OrdinalIgnoreCase))
+ {
var op = value.IndexOf('(');
var cp = value.LastIndexOf(')');
if (op < 0 || cp < 0 || cp < op)
return Color.FromHsla(h, s, l, a);
}
- if (value.StartsWith("hsl", StringComparison.OrdinalIgnoreCase)) {
+ if (value.StartsWith("hsl", StringComparison.OrdinalIgnoreCase))
+ {
var op = value.IndexOf('(');
var cp = value.LastIndexOf(')');
if (op < 0 || cp < 0 || cp < op)
if (parts.Length == 1 || (parts.Length == 2 && parts[0] == "Color"))
{
string color = parts[parts.Length - 1];
- switch (color.ToLowerInvariant()) {
- case "default": return Color.Default;
- case "accent": return Color.Accent;
- case "aliceblue": return Color.AliceBlue;
- case "antiquewhite": return Color.AntiqueWhite;
- case "aqua": return Color.Aqua;
- case "aquamarine": return Color.Aquamarine;
- case "azure": return Color.Azure;
- case "beige": return Color.Beige;
- case "bisque": return Color.Bisque;
- case "black": return Color.Black;
- case "blanchedalmond": return Color.BlanchedAlmond;
- case "blue": return Color.Blue;
- case "blueViolet": return Color.BlueViolet;
- case "brown": return Color.Brown;
- case "burlywood": return Color.BurlyWood;
- case "cadetblue": return Color.CadetBlue;
- case "chartreuse": return Color.Chartreuse;
- case "chocolate": return Color.Chocolate;
- case "coral": return Color.Coral;
- case "cornflowerblue": return Color.CornflowerBlue;
- case "cornsilk": return Color.Cornsilk;
- case "crimson": return Color.Crimson;
- case "cyan": return Color.Cyan;
- case "darkblue": return Color.DarkBlue;
- case "darkcyan": return Color.DarkCyan;
- case "darkgoldenrod": return Color.DarkGoldenrod;
- case "darkgray": return Color.DarkGray;
- case "darkgreen": return Color.DarkGreen;
- case "darkkhaki": return Color.DarkKhaki;
- case "darkmagenta": return Color.DarkMagenta;
- case "darkolivegreen": return Color.DarkOliveGreen;
- case "darkorange": return Color.DarkOrange;
- case "darkorchid": return Color.DarkOrchid;
- case "darkred": return Color.DarkRed;
- case "darksalmon": return Color.DarkSalmon;
- case "darkseagreen": return Color.DarkSeaGreen;
- case "darkslateblue": return Color.DarkSlateBlue;
- case "darkslategray": return Color.DarkSlateGray;
- case "darkturquoise": return Color.DarkTurquoise;
- case "darkviolet": return Color.DarkViolet;
- case "deeppink": return Color.DeepPink;
- case "deepskyblue": return Color.DeepSkyBlue;
- case "dimgray": return Color.DimGray;
- case "dodgerblue": return Color.DodgerBlue;
- case "firebrick": return Color.Firebrick;
- case "floralwhite": return Color.FloralWhite;
- case "forestgreen": return Color.ForestGreen;
- case "fuchsia": return Color.Fuchsia;
- case "gainsboro": return Color.Gainsboro;
- case "ghostwhite": return Color.GhostWhite;
- case "gold": return Color.Gold;
- case "goldenrod": return Color.Goldenrod;
- case "gray": return Color.Gray;
- case "green": return Color.Green;
- case "greenyellow": return Color.GreenYellow;
- case "honeydew": return Color.Honeydew;
- case "hotpink": return Color.HotPink;
- case "indianred": return Color.IndianRed;
- case "indigo": return Color.Indigo;
- case "ivory": return Color.Ivory;
- case "khaki": return Color.Khaki;
- case "lavender": return Color.Lavender;
- case "lavenderblush": return Color.LavenderBlush;
- case "lawngreen": return Color.LawnGreen;
- case "lemonchiffon": return Color.LemonChiffon;
- case "lightblue": return Color.LightBlue;
- case "lightcoral": return Color.LightCoral;
- case "lightcyan": return Color.LightCyan;
- case "lightgoldenrodyellow": return Color.LightGoldenrodYellow;
- case "lightgrey":
- case "lightgray": return Color.LightGray;
- case "lightgreen": return Color.LightGreen;
- case "lightpink": return Color.LightPink;
- case "lightsalmon": return Color.LightSalmon;
- case "lightseagreen": return Color.LightSeaGreen;
- case "lightskyblue": return Color.LightSkyBlue;
- case "lightslategray": return Color.LightSlateGray;
- case "lightsteelblue": return Color.LightSteelBlue;
- case "lightyellow": return Color.LightYellow;
- case "lime": return Color.Lime;
- case "limegreen": return Color.LimeGreen;
- case "linen": return Color.Linen;
- case "magenta": return Color.Magenta;
- case "maroon": return Color.Maroon;
- case "mediumaquamarine": return Color.MediumAquamarine;
- case "mediumblue": return Color.MediumBlue;
- case "mediumorchid": return Color.MediumOrchid;
- case "mediumpurple": return Color.MediumPurple;
- case "mediumseagreen": return Color.MediumSeaGreen;
- case "mediumslateblue": return Color.MediumSlateBlue;
- case "mediumspringgreen": return Color.MediumSpringGreen;
- case "mediumturquoise": return Color.MediumTurquoise;
- case "mediumvioletred": return Color.MediumVioletRed;
- case "midnightblue": return Color.MidnightBlue;
- case "mintcream": return Color.MintCream;
- case "mistyrose": return Color.MistyRose;
- case "moccasin": return Color.Moccasin;
- case "navajowhite": return Color.NavajoWhite;
- case "navy": return Color.Navy;
- case "oldlace": return Color.OldLace;
- case "olive": return Color.Olive;
- case "olivedrab": return Color.OliveDrab;
- case "orange": return Color.Orange;
- case "orangered": return Color.OrangeRed;
- case "orchid": return Color.Orchid;
- case "palegoldenrod": return Color.PaleGoldenrod;
- case "palegreen": return Color.PaleGreen;
- case "paleturquoise": return Color.PaleTurquoise;
- case "palevioletred": return Color.PaleVioletRed;
- case "papayawhip": return Color.PapayaWhip;
- case "peachpuff": return Color.PeachPuff;
- case "peru": return Color.Peru;
- case "pink": return Color.Pink;
- case "plum": return Color.Plum;
- case "powderblue": return Color.PowderBlue;
- case "purple": return Color.Purple;
- case "red": return Color.Red;
- case "rosybrown": return Color.RosyBrown;
- case "royalblue": return Color.RoyalBlue;
- case "saddlebrown": return Color.SaddleBrown;
- case "salmon": return Color.Salmon;
- case "sandybrown": return Color.SandyBrown;
- case "seagreen": return Color.SeaGreen;
- case "seashell": return Color.SeaShell;
- case "sienna": return Color.Sienna;
- case "silver": return Color.Silver;
- case "skyblue": return Color.SkyBlue;
- case "slateblue": return Color.SlateBlue;
- case "slategray": return Color.SlateGray;
- case "snow": return Color.Snow;
- case "springgreen": return Color.SpringGreen;
- case "steelblue": return Color.SteelBlue;
- case "tan": return Color.Tan;
- case "teal": return Color.Teal;
- case "thistle": return Color.Thistle;
- case "tomato": return Color.Tomato;
- case "transparent": return Color.Transparent;
- case "turquoise": return Color.Turquoise;
- case "violet": return Color.Violet;
- case "wheat": return Color.Wheat;
- case "white": return Color.White;
- case "whitesmoke": return Color.WhiteSmoke;
- case "yellow": return Color.Yellow;
- case "yellowgreen": return Color.YellowGreen;
+ switch (color.ToLowerInvariant())
+ {
+ case "default": return Color.Default;
+ case "accent": return Color.Accent;
+ case "aliceblue": return Color.AliceBlue;
+ case "antiquewhite": return Color.AntiqueWhite;
+ case "aqua": return Color.Aqua;
+ case "aquamarine": return Color.Aquamarine;
+ case "azure": return Color.Azure;
+ case "beige": return Color.Beige;
+ case "bisque": return Color.Bisque;
+ case "black": return Color.Black;
+ case "blanchedalmond": return Color.BlanchedAlmond;
+ case "blue": return Color.Blue;
+ case "blueViolet": return Color.BlueViolet;
+ case "brown": return Color.Brown;
+ case "burlywood": return Color.BurlyWood;
+ case "cadetblue": return Color.CadetBlue;
+ case "chartreuse": return Color.Chartreuse;
+ case "chocolate": return Color.Chocolate;
+ case "coral": return Color.Coral;
+ case "cornflowerblue": return Color.CornflowerBlue;
+ case "cornsilk": return Color.Cornsilk;
+ case "crimson": return Color.Crimson;
+ case "cyan": return Color.Cyan;
+ case "darkblue": return Color.DarkBlue;
+ case "darkcyan": return Color.DarkCyan;
+ case "darkgoldenrod": return Color.DarkGoldenrod;
+ case "darkgray": return Color.DarkGray;
+ case "darkgreen": return Color.DarkGreen;
+ case "darkkhaki": return Color.DarkKhaki;
+ case "darkmagenta": return Color.DarkMagenta;
+ case "darkolivegreen": return Color.DarkOliveGreen;
+ case "darkorange": return Color.DarkOrange;
+ case "darkorchid": return Color.DarkOrchid;
+ case "darkred": return Color.DarkRed;
+ case "darksalmon": return Color.DarkSalmon;
+ case "darkseagreen": return Color.DarkSeaGreen;
+ case "darkslateblue": return Color.DarkSlateBlue;
+ case "darkslategray": return Color.DarkSlateGray;
+ case "darkturquoise": return Color.DarkTurquoise;
+ case "darkviolet": return Color.DarkViolet;
+ case "deeppink": return Color.DeepPink;
+ case "deepskyblue": return Color.DeepSkyBlue;
+ case "dimgray": return Color.DimGray;
+ case "dodgerblue": return Color.DodgerBlue;
+ case "firebrick": return Color.Firebrick;
+ case "floralwhite": return Color.FloralWhite;
+ case "forestgreen": return Color.ForestGreen;
+ case "fuchsia": return Color.Fuchsia;
+ case "gainsboro": return Color.Gainsboro;
+ case "ghostwhite": return Color.GhostWhite;
+ case "gold": return Color.Gold;
+ case "goldenrod": return Color.Goldenrod;
+ case "gray": return Color.Gray;
+ case "green": return Color.Green;
+ case "greenyellow": return Color.GreenYellow;
+ case "honeydew": return Color.Honeydew;
+ case "hotpink": return Color.HotPink;
+ case "indianred": return Color.IndianRed;
+ case "indigo": return Color.Indigo;
+ case "ivory": return Color.Ivory;
+ case "khaki": return Color.Khaki;
+ case "lavender": return Color.Lavender;
+ case "lavenderblush": return Color.LavenderBlush;
+ case "lawngreen": return Color.LawnGreen;
+ case "lemonchiffon": return Color.LemonChiffon;
+ case "lightblue": return Color.LightBlue;
+ case "lightcoral": return Color.LightCoral;
+ case "lightcyan": return Color.LightCyan;
+ case "lightgoldenrodyellow": return Color.LightGoldenrodYellow;
+ case "lightgrey":
+ case "lightgray": return Color.LightGray;
+ case "lightgreen": return Color.LightGreen;
+ case "lightpink": return Color.LightPink;
+ case "lightsalmon": return Color.LightSalmon;
+ case "lightseagreen": return Color.LightSeaGreen;
+ case "lightskyblue": return Color.LightSkyBlue;
+ case "lightslategray": return Color.LightSlateGray;
+ case "lightsteelblue": return Color.LightSteelBlue;
+ case "lightyellow": return Color.LightYellow;
+ case "lime": return Color.Lime;
+ case "limegreen": return Color.LimeGreen;
+ case "linen": return Color.Linen;
+ case "magenta": return Color.Magenta;
+ case "maroon": return Color.Maroon;
+ case "mediumaquamarine": return Color.MediumAquamarine;
+ case "mediumblue": return Color.MediumBlue;
+ case "mediumorchid": return Color.MediumOrchid;
+ case "mediumpurple": return Color.MediumPurple;
+ case "mediumseagreen": return Color.MediumSeaGreen;
+ case "mediumslateblue": return Color.MediumSlateBlue;
+ case "mediumspringgreen": return Color.MediumSpringGreen;
+ case "mediumturquoise": return Color.MediumTurquoise;
+ case "mediumvioletred": return Color.MediumVioletRed;
+ case "midnightblue": return Color.MidnightBlue;
+ case "mintcream": return Color.MintCream;
+ case "mistyrose": return Color.MistyRose;
+ case "moccasin": return Color.Moccasin;
+ case "navajowhite": return Color.NavajoWhite;
+ case "navy": return Color.Navy;
+ case "oldlace": return Color.OldLace;
+ case "olive": return Color.Olive;
+ case "olivedrab": return Color.OliveDrab;
+ case "orange": return Color.Orange;
+ case "orangered": return Color.OrangeRed;
+ case "orchid": return Color.Orchid;
+ case "palegoldenrod": return Color.PaleGoldenrod;
+ case "palegreen": return Color.PaleGreen;
+ case "paleturquoise": return Color.PaleTurquoise;
+ case "palevioletred": return Color.PaleVioletRed;
+ case "papayawhip": return Color.PapayaWhip;
+ case "peachpuff": return Color.PeachPuff;
+ case "peru": return Color.Peru;
+ case "pink": return Color.Pink;
+ case "plum": return Color.Plum;
+ case "powderblue": return Color.PowderBlue;
+ case "purple": return Color.Purple;
+ case "red": return Color.Red;
+ case "rosybrown": return Color.RosyBrown;
+ case "royalblue": return Color.RoyalBlue;
+ case "saddlebrown": return Color.SaddleBrown;
+ case "salmon": return Color.Salmon;
+ case "sandybrown": return Color.SandyBrown;
+ case "seagreen": return Color.SeaGreen;
+ case "seashell": return Color.SeaShell;
+ case "sienna": return Color.Sienna;
+ case "silver": return Color.Silver;
+ case "skyblue": return Color.SkyBlue;
+ case "slateblue": return Color.SlateBlue;
+ case "slategray": return Color.SlateGray;
+ case "snow": return Color.Snow;
+ case "springgreen": return Color.SpringGreen;
+ case "steelblue": return Color.SteelBlue;
+ case "tan": return Color.Tan;
+ case "teal": return Color.Teal;
+ case "thistle": return Color.Thistle;
+ case "tomato": return Color.Tomato;
+ case "transparent": return Color.Transparent;
+ case "turquoise": return Color.Turquoise;
+ case "violet": return Color.Violet;
+ case "wheat": return Color.Wheat;
+ case "white": return Color.White;
+ case "whitesmoke": return Color.WhiteSmoke;
+ case "yellow": return Color.Yellow;
+ case "yellowgreen": return Color.YellowGreen;
}
var field = typeof(Color).GetFields().FirstOrDefault(fi => fi.IsStatic && string.Equals(fi.Name, color, StringComparison.OrdinalIgnoreCase));
if (field != null)
if (property != null)
return (Color)property.GetValue(null, null);
}
+
+ var namedColor = Device.GetNamedColor(value);
+ if (namedColor != default)
+ return namedColor;
}
throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(Color)}");
static double ParseColorValue(string elem, int maxValue, bool acceptPercent)
{
elem = elem.Trim();
- if (elem.EndsWith("%", StringComparison.Ordinal) && acceptPercent) {
+ if (elem.EndsWith("%", StringComparison.Ordinal) && acceptPercent)
+ {
maxValue = 100;
elem = elem.Substring(0, elem.Length - 1);
}
((CompareStateTrigger)bindable).UpdateState();
}
- internal override void OnAttached()
+ protected override void OnAttached()
{
base.OnAttached();
UpdateState();
{
var tcs = new TaskCompletionSource<T>();
BeginInvokeOnMainThread(
- async() =>
+ async () =>
{
try
{
return PlatformServices.GetNamedSize(size, targetElementType, useOldSizes);
}
+ public static Color GetNamedColor(string name)
+ {
+ return PlatformServices.GetNamedColor(name);
+ }
+
internal static Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
{
return PlatformServices.GetStreamAsync(uri, cancellationToken);
{
((IElement)RealParent).RemoveResourcesChangedListener(OnParentResourcesChanged);
- if(value != null && (RealParent is Layout || RealParent is IControlTemplated))
+ if (value != null && (RealParent is Layout || RealParent is IControlTemplated))
Log.Warning("Element", $"{this} is already a child of {RealParent}. Remove {this} from {RealParent} before adding to {value}.");
}
SetInheritedBindingContext(this, null);
}
- VisualDiagnostics.SendVisualTreeChanged(this, value);
+ VisualDiagnostics.SendVisualTreeChanged(value, this);
OnParentSet();
protected virtual void OnParentSet()
{
ParentSet?.Invoke(this, EventArgs.Empty);
- ApplyStyleSheetsOnParentSet();
+ ApplyStyleSheets();
(this as IPropertyPropagationController)?.PropagatePropertyChanged(null);
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
- foreach(var logicalChildren in ChildrenNotDrawnByThisElement)
+ foreach (var logicalChildren in ChildrenNotDrawnByThisElement)
{
- if(logicalChildren is IPropertyPropagationController controller)
+ if (logicalChildren is IPropertyPropagationController controller)
PropertyPropagationExtensions.PropagatePropertyChanged(propertyName, this, new[] { logicalChildren });
}
internal virtual void OnParentResourcesChanged(object sender, ResourcesChangedEventArgs e)
{
if (e == ResourcesChangedEventArgs.StyleSheets)
- ApplyStyleSheetsOnParentSet();
+ ApplyStyleSheets();
else
OnParentResourcesChanged(e.Values);
}
internal virtual void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
{
- OnResourcesChanged(e.Values);
+ if (e == ResourcesChangedEventArgs.StyleSheets)
+ ApplyStyleSheets();
+ else
+ OnResourcesChanged(e.Values);
}
internal void OnResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
internal event EventHandler ParentSet;
- internal static void SetFlowDirectionFromParent(Element child)
- {
- IFlowDirectionController controller = child as IFlowDirectionController;
- if (controller == null)
- return;
-
- if (controller.EffectiveFlowDirection.IsImplicit())
- {
- var parentView = child.Parent as IFlowDirectionController;
- if (parentView == null)
- return;
-
- var flowDirection = parentView.EffectiveFlowDirection.ToFlowDirection();
-
- if (flowDirection != controller.EffectiveFlowDirection.ToFlowDirection())
- {
- controller.EffectiveFlowDirection = flowDirection.ToEffectiveFlowDirection();
- }
- }
- }
-
- internal static void SetVisualfromParent(Element child)
- {
- IVisualController controller = child as IVisualController;
- if (controller == null)
- return;
-
- if (controller.Visual != VisualMarker.MatchParent)
- {
- controller.EffectiveVisual = controller.Visual;
- return;
- }
-
- if (child.Parent is IVisualController parentView)
- controller.EffectiveVisual = parentView.EffectiveVisual;
- }
-
internal virtual void SetChildInheritedBindingContext(Element child, object context)
{
SetInheritedBindingContext(child, context);
using System.Collections.Generic;
-using System.IO;
+using System.Linq;
using System.Reflection;
-
-using Xamarin.Forms.Internals;
using Xamarin.Forms.StyleSheets;
namespace Xamarin.Forms
internal string _cssFallbackTypeName;
string[] _styleSelectableNameAndBaseNames;
- string[] IStyleSelectable.NameAndBases {
- get {
- if (_styleSelectableNameAndBaseNames == null) {
+ string[] IStyleSelectable.NameAndBases
+ {
+ get
+ {
+ if (_styleSelectableNameAndBaseNames == null)
+ {
var list = new List<string>();
if (_cssFallbackTypeName != null)
list.Add(_cssFallbackTypeName);
var t = GetType();
- while (t != typeof(BindableObject)) {
+ while (t != typeof(BindableObject))
+ {
list.Add(t.Name);
t = t.GetTypeInfo().BaseType;
}
IStyleSelectable IStyleSelectable.Parent => Parent;
//on parent set, or on parent stylesheet changed, reapply all
- internal void ApplyStyleSheetsOnParentSet()
+ internal void ApplyStyleSheets()
{
- var parent = Parent;
- if (parent == null)
- return;
var sheets = new List<StyleSheet>();
- while (parent != null) {
+ Element parent = this;
+ while (parent != null)
+ {
var resourceProvider = parent as IResourcesProvider;
var vpSheets = resourceProvider?.GetStyleSheets();
if (vpSheets != null)
sheets.AddRange(vpSheets);
parent = parent.Parent;
}
- for (var i = sheets.Count - 1; i >= 0; i--)
- ((IStyle)sheets[i]).Apply(this);
+
+ ApplyStyleSheets(sheets, this);
+ }
+
+ void ApplyStyleSheets(List<StyleSheet> sheets, Element element)
+ {
+ if (element == null)
+ return;
+
+ for (var i = (sheets?.Count ?? 0) - 1; i >= 0; i--)
+ {
+ ((IStyle)sheets[i]).Apply(element);
+ }
+
+ foreach (Element child in element.AllChildren)
+ {
+ var mergedSheets = sheets;
+ var resourceProvider = child as IResourcesProvider;
+ var childSheets = resourceProvider?.GetStyleSheets();
+ if (childSheets?.Any() ?? false)
+ {
+ mergedSheets = new List<StyleSheet>(childSheets);
+ mergedSheets.AddRange(sheets);
+ }
+ ApplyStyleSheets(mergedSheets, child);
+ }
}
}
}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Runtime.CompilerServices;
+using System.Windows.Input;
+using static System.Math;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty(nameof(Content))]
+ public class Expander : TemplatedView
+ {
+ const string ExpandAnimationName = nameof(ExpandAnimationName);
+ const uint DefaultAnimationLength = 250;
+
+ public event EventHandler Tapped;
+
+ public static readonly BindableProperty SpacingProperty = BindableProperty.Create(nameof(Spacing), typeof(double), typeof(Expander), 0d, propertyChanged: (bindable, oldvalue, newvalue)
+ => ((Expander)bindable).ExpanderLayout.Spacing = (double)newvalue);
+
+ public static readonly BindableProperty HeaderProperty = BindableProperty.Create(nameof(Header), typeof(View), typeof(Expander), default(View), propertyChanged: (bindable, oldValue, newValue)
+ => ((Expander)bindable).SetHeader((View)oldValue));
+
+ public static readonly BindableProperty ContentProperty = BindableProperty.Create(nameof(Content), typeof(View), typeof(Expander), default(View), propertyChanged: (bindable, oldValue, newValue)
+ => ((Expander)bindable).SetContent((View)oldValue, (View)newValue));
+
+ public static readonly BindableProperty ContentTemplateProperty = BindableProperty.Create(nameof(ContentTemplate), typeof(DataTemplate), typeof(Expander), default(DataTemplate), propertyChanged: (bindable, oldValue, newValue)
+ => ((Expander)bindable).SetContent(true));
+
+ public static readonly BindableProperty IsExpandedProperty = BindableProperty.Create(nameof(IsExpanded), typeof(bool), typeof(Expander), default(bool), BindingMode.TwoWay, propertyChanged: (bindable, oldValue, newValue)
+ => ((Expander)bindable).SetContent(false));
+
+ public static readonly BindableProperty ExpandAnimationLengthProperty = BindableProperty.Create(nameof(ExpandAnimationLength), typeof(uint), typeof(Expander), DefaultAnimationLength);
+
+ public static readonly BindableProperty CollapseAnimationLengthProperty = BindableProperty.Create(nameof(CollapseAnimationLength), typeof(uint), typeof(Expander), DefaultAnimationLength);
+
+ public static readonly BindableProperty ExpandAnimationEasingProperty = BindableProperty.Create(nameof(ExpandAnimationEasing), typeof(Easing), typeof(Expander), default(Easing));
+
+ public static readonly BindableProperty CollapseAnimationEasingProperty = BindableProperty.Create(nameof(CollapseAnimationEasing), typeof(Easing), typeof(Expander), default(Easing));
+
+ public static readonly BindableProperty StateProperty = BindableProperty.Create(nameof(State), typeof(ExpanderState), typeof(Expander), default(ExpanderState), BindingMode.OneWayToSource);
+
+ public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(Expander), default(object));
+
+ public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(Expander), default(ICommand));
+
+ public static readonly BindableProperty ForceUpdateSizeCommandProperty = BindableProperty.Create(nameof(ForceUpdateSizeCommand), typeof(ICommand), typeof(Expander), default(ICommand), BindingMode.OneWayToSource);
+
+ DataTemplate _previousTemplate;
+ double _contentHeightRequest = -1;
+ double _lastVisibleHeight = -1;
+ double _previousWidth = -1;
+ double _startHeight;
+ double _endHeight;
+ bool _shouldIgnoreContentSetting;
+ bool _shouldIgnoreAnimation;
+ static bool isExperimentalFlagSet = false;
+
+ public Expander()
+ {
+ ExpanderLayout = new StackLayout { Spacing = Spacing };
+ ForceUpdateSizeCommand = new Command(ForceUpdateSize);
+ InternalChildren.Add(ExpanderLayout);
+ }
+
+ internal static void VerifyExperimental([CallerMemberName] string memberName = "", string constructorHint = null)
+ {
+ if (isExperimentalFlagSet)
+ return;
+
+ ExperimentalFlags.VerifyFlagEnabled(nameof(Markup), ExperimentalFlags.ExpanderExperimental, constructorHint, memberName);
+
+ isExperimentalFlagSet = true;
+ }
+
+ protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
+ {
+ VerifyExperimental();
+ return base.OnMeasure(widthConstraint, heightConstraint);
+ }
+
+ StackLayout ExpanderLayout { get; }
+
+ public double Spacing
+ {
+ get => (double)GetValue(SpacingProperty);
+ set => SetValue(SpacingProperty, value);
+ }
+
+ public View Header
+ {
+ get => (View)GetValue(HeaderProperty);
+ set => SetValue(HeaderProperty, value);
+ }
+
+ public View Content
+ {
+ get => (View)GetValue(ContentProperty);
+ set => SetValue(ContentProperty, value);
+ }
+
+ public DataTemplate ContentTemplate
+ {
+ get => (DataTemplate)GetValue(ContentTemplateProperty);
+ set => SetValue(ContentTemplateProperty, value);
+ }
+
+ public bool IsExpanded
+ {
+ get => (bool)GetValue(IsExpandedProperty);
+ set => SetValue(IsExpandedProperty, value);
+ }
+
+ public uint ExpandAnimationLength
+ {
+ get => (uint)GetValue(ExpandAnimationLengthProperty);
+ set => SetValue(ExpandAnimationLengthProperty, value);
+ }
+
+ public uint CollapseAnimationLength
+ {
+ get => (uint)GetValue(CollapseAnimationLengthProperty);
+ set => SetValue(CollapseAnimationLengthProperty, value);
+ }
+
+ public Easing ExpandAnimationEasing
+ {
+ get => (Easing)GetValue(ExpandAnimationEasingProperty);
+ set => SetValue(ExpandAnimationEasingProperty, value);
+ }
+
+ public Easing CollapseAnimationEasing
+ {
+ get => (Easing)GetValue(CollapseAnimationEasingProperty);
+ set => SetValue(CollapseAnimationEasingProperty, value);
+ }
+
+ public ExpanderState State
+ {
+ get => (ExpanderState)GetValue(StateProperty);
+ set => SetValue(StateProperty, value);
+ }
+
+ public object CommandParameter
+ {
+ get => GetValue(CommandParameterProperty);
+ set => SetValue(CommandParameterProperty, value);
+ }
+
+ public ICommand Command
+ {
+ get => (ICommand)GetValue(CommandProperty);
+ set => SetValue(CommandProperty, value);
+ }
+
+ public ICommand ForceUpdateSizeCommand
+ {
+ get => (ICommand)GetValue(ForceUpdateSizeCommandProperty);
+ set => SetValue(ForceUpdateSizeCommandProperty, value);
+ }
+
+ public void ForceUpdateSize()
+ {
+ _lastVisibleHeight = -1;
+ OnIsExpandedChanged();
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+ _lastVisibleHeight = -1;
+ SetContent(true, true);
+ }
+
+ protected override void OnSizeAllocated(double width, double height)
+ {
+ base.OnSizeAllocated(width, height);
+ if (Abs(width - _previousWidth) >= double.Epsilon)
+ {
+ ForceUpdateSize();
+ }
+ _previousWidth = width;
+ }
+
+ void OnIsExpandedChanged(bool isBindingContextChanged = false)
+ {
+ if (Content == null || (!IsExpanded && !Content.IsVisible))
+ {
+ return;
+ }
+
+ Content.SizeChanged -= OnContentSizeChanged;
+
+ var isAnimationRunning = Content.AnimationIsRunning(ExpandAnimationName);
+ Content.AbortAnimation(ExpandAnimationName);
+
+
+ _startHeight = Content.IsVisible
+ ? Max(Content.Height - (Content is Layout l ? l.Padding.Top + l.Padding.Bottom : 0), 0)
+ : 0;
+
+ if (IsExpanded)
+ {
+ Content.IsVisible = true;
+ }
+
+ _endHeight = _contentHeightRequest >= 0
+ ? _contentHeightRequest
+ : _lastVisibleHeight;
+
+ var shouldInvokeAnimation = true;
+
+ if (IsExpanded)
+ {
+ if (_endHeight <= 0)
+ {
+ shouldInvokeAnimation = false;
+ Content.SizeChanged += OnContentSizeChanged;
+ Content.HeightRequest = -1;
+ }
+ }
+ else
+ {
+ _lastVisibleHeight = _startHeight = _contentHeightRequest >= 0
+ ? _contentHeightRequest
+ : !isAnimationRunning
+ ? Content.Height - (Content is Layout layout
+ ? layout.Padding.Top + layout.Padding.Bottom
+ : 0)
+ : _lastVisibleHeight;
+ _endHeight = 0;
+ }
+
+ _shouldIgnoreAnimation = isBindingContextChanged || Height < 0;
+
+ if (shouldInvokeAnimation)
+ {
+ InvokeAnimation();
+ }
+ }
+
+ void SetHeader(View oldHeader)
+ {
+ if (oldHeader != null)
+ {
+ ExpanderLayout.Children.Remove(oldHeader);
+ }
+ if (Header != null)
+ {
+ ExpanderLayout.Children.Insert(0, Header);
+ Header.GestureRecognizers.Add(new TapGestureRecognizer
+ {
+ CommandParameter = this,
+ Command = new Command(parameter =>
+ {
+ var parent = (parameter as View).Parent;
+ while (parent != null && !(parent is Page))
+ {
+ if (parent is Expander ancestorExpander)
+ {
+ ancestorExpander.Content.HeightRequest = -1;
+ }
+ parent = parent.Parent;
+ }
+ IsExpanded = !IsExpanded;
+ Command?.Execute(CommandParameter);
+ Tapped?.Invoke(this, EventArgs.Empty);
+ })
+ });
+ }
+ }
+
+ void SetContent(bool isForceUpdate, bool isBindingContextChanged = false)
+ {
+ if (IsExpanded && (Content == null || isForceUpdate))
+ {
+ _shouldIgnoreContentSetting = true;
+ Content = CreateContent() ?? Content;
+ _shouldIgnoreContentSetting = false;
+ }
+ OnIsExpandedChanged(isBindingContextChanged);
+ }
+
+ void SetContent(View oldContent, View newContent)
+ {
+ if (oldContent != null)
+ {
+ oldContent.SizeChanged -= OnContentSizeChanged;
+ ExpanderLayout.Children.Remove(oldContent);
+ }
+ if (newContent != null)
+ {
+ if (newContent is Layout layout)
+ {
+ layout.IsClippedToBounds = true;
+ }
+ _contentHeightRequest = newContent.HeightRequest;
+ newContent.HeightRequest = 0;
+ newContent.IsVisible = false;
+ ExpanderLayout.Children.Add(newContent);
+ }
+
+ if (!_shouldIgnoreContentSetting)
+ {
+ SetContent(true);
+ }
+ }
+
+ View CreateContent()
+ {
+ var template = ContentTemplate;
+ while (template is DataTemplateSelector selector)
+ {
+ template = selector.SelectTemplate(BindingContext, this);
+ }
+ if (template == _previousTemplate && Content != null)
+ {
+ return null;
+ }
+ _previousTemplate = template;
+ return (View)template?.CreateContent();
+ }
+
+ void OnContentSizeChanged(object sender, EventArgs e)
+ {
+ if (Content.Height <= 0)
+ {
+ return;
+ }
+ Content.SizeChanged -= OnContentSizeChanged;
+ Content.HeightRequest = 0;
+ _endHeight = Content.Height;
+ InvokeAnimation();
+ }
+
+ void InvokeAnimation()
+ {
+ State = IsExpanded ? ExpanderState.Expanding : ExpanderState.Collapsing;
+
+ if (_shouldIgnoreAnimation)
+ {
+ State = IsExpanded ? ExpanderState.Expanded : ExpanderState.Collapsed;
+ Content.HeightRequest = _endHeight;
+ Content.IsVisible = IsExpanded;
+ return;
+ }
+
+ var length = ExpandAnimationLength;
+ var easing = ExpandAnimationEasing;
+ if (!IsExpanded)
+ {
+ length = CollapseAnimationLength;
+ easing = CollapseAnimationEasing;
+ }
+
+ if (_lastVisibleHeight > 0)
+ {
+ length = Max((uint)(length * (Abs(_endHeight - _startHeight) / _lastVisibleHeight)), 1);
+ }
+
+ new Animation(v => Content.HeightRequest = v, _startHeight, _endHeight)
+ .Commit(Content, ExpandAnimationName, 16, length, easing, (value, isInterrupted) =>
+ {
+ if (isInterrupted)
+ {
+ return;
+ }
+ if (!IsExpanded)
+ {
+ Content.IsVisible = false;
+ State = ExpanderState.Collapsed;
+ return;
+ }
+ State = ExpanderState.Expanded;
+ });
+ }
+ }
+}
--- /dev/null
+namespace Xamarin.Forms
+{
+ public enum ExpanderState
+ {
+ Expanding,
+ Expanded,
+ Collapsing,
+ Collapsed
+ }
+}
internal const string SwipeViewExperimental = "SwipeView_Experimental";
internal const string MediaElementExperimental = "MediaElement_Experimental";
internal const string MarkupExperimental = "Markup_Experimental";
+ internal const string AppThemeExperimental = "AppTheme_Experimental";
+ internal const string ExpanderExperimental = "Expander_Experimental";
[EditorBrowsable(EditorBrowsableState.Never)]
public static void VerifyFlagEnabled(
public static class FontRegistrar
{
internal static readonly Dictionary<string, (ExportFontAttribute attribute, Assembly assembly)> EmbeddedFonts = new Dictionary<string, (ExportFontAttribute attribute, Assembly assembly)>();
-
+ static Dictionary<string, (bool, string)> fontLookupCache = new Dictionary<string, (bool, string)>();
public static void Register(ExportFontAttribute fontAttribute, Assembly assembly)
{
EmbeddedFonts[fontAttribute.FontFileName] = (fontAttribute, assembly);
return (false, null);
}
+ if (fontLookupCache.TryGetValue(font, out var foundResult))
+ return foundResult;
+
+
var fontStream = GetEmbeddedResourceStream(foundFont.assembly, foundFont.attribute.FontFileName);
var type = Registrar.Registered.GetHandlerType(typeof(EmbeddedFont));
var fontHandler = (IEmbeddedFontLoader)Activator.CreateInstance(type);
- return fontHandler.LoadFont(new EmbeddedFont { FontName = foundFont.attribute.FontFileName, ResourceStream = fontStream });
+ var result = fontHandler.LoadFont(new EmbeddedFont { FontName = foundFont.attribute.FontFileName, ResourceStream = fontStream });
+ return fontLookupCache[font] = result;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
- return (false, null);
+ return fontLookupCache[font] = (false, null);
}
static Stream GetEmbeddedResourceStream(Assembly assembly, string resourceFileName)
double GetNamedSize(NamedSize size, Type targetElementType, bool useOldSizes);
+ Color GetNamedColor(string name);
+
+ OSAppTheme RequestedTheme { get; }
+
Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken);
IIsolatedStorageFile GetUserStoreForApplication();
namespace Xamarin.Forms.Internals
{
- internal static class PropertyPropagationExtensions
+ public static class PropertyPropagationExtensions
{
- public static void PropagatePropertyChanged(string propertyName, Element element, IEnumerable children)
+ internal static void PropagatePropertyChanged(string propertyName, Element element, IEnumerable children)
{
if (propertyName == null || propertyName == VisualElement.FlowDirectionProperty.PropertyName)
- Element.SetFlowDirectionFromParent(element);
+ SetFlowDirectionFromParent(element);
- if (OptionalFeatureValues.UseVisual)
- {
- if (propertyName == null || propertyName == VisualElement.VisualProperty.PropertyName)
- Element.SetVisualfromParent(element);
- }
+ if (propertyName == null || propertyName == VisualElement.VisualProperty.PropertyName)
+ SetVisualfromParent(element);
- if (OptionalFeatureValues.UseShell)
- {
- if (propertyName == null || propertyName == Shell.NavBarIsVisibleProperty.PropertyName)
- BaseShellItem.PropagateFromParent(Shell.NavBarIsVisibleProperty, element);
+ if (propertyName == null || propertyName == Shell.NavBarIsVisibleProperty.PropertyName)
+ BaseShellItem.PropagateFromParent(Shell.NavBarIsVisibleProperty, element);
- if (propertyName == null || propertyName == Shell.NavBarHasShadowProperty.PropertyName)
- BaseShellItem.PropagateFromParent(Shell.NavBarHasShadowProperty, element);
+ if (propertyName == null || propertyName == Shell.NavBarHasShadowProperty.PropertyName)
+ BaseShellItem.PropagateFromParent(Shell.NavBarHasShadowProperty, element);
- if (propertyName == null || propertyName == Shell.TabBarIsVisibleProperty.PropertyName)
- BaseShellItem.PropagateFromParent(Shell.TabBarIsVisibleProperty, element);
- }
+ if (propertyName == null || propertyName == Shell.TabBarIsVisibleProperty.PropertyName)
+ BaseShellItem.PropagateFromParent(Shell.TabBarIsVisibleProperty, element);
foreach (var child in children)
{
}
}
- internal static void PropagatePropertyChanged(string propertyName, Element element)
+ public static void PropagatePropertyChanged(string propertyName, Element target, Element source)
{
if (propertyName == null || propertyName == VisualElement.FlowDirectionProperty.PropertyName)
- Element.SetFlowDirectionFromParent(element);
+ PropagateFlowDirection(target, source);
if (propertyName == null || propertyName == VisualElement.VisualProperty.PropertyName)
- Element.SetVisualfromParent(element);
+ PropagateVisual(target, source);
- if (element is IPropertyPropagationController view)
- view.PropagatePropertyChanged(propertyName);
+ if (target is IPropertyPropagationController view)
+ view.PropagatePropertyChanged(propertyName);
+ }
+
+ internal static void PropagateFlowDirection(Element target, Element source)
+ {
+ IFlowDirectionController targetController = target as IFlowDirectionController;
+ if (targetController == null)
+ return;
+
+ if (targetController.EffectiveFlowDirection.IsImplicit())
+ {
+ var sourceController = source as IFlowDirectionController;
+ if (sourceController == null)
+ return;
+
+ var flowDirection = sourceController.EffectiveFlowDirection.ToFlowDirection();
+
+ if (flowDirection != targetController.EffectiveFlowDirection.ToFlowDirection())
+ {
+ targetController.EffectiveFlowDirection = flowDirection.ToEffectiveFlowDirection();
+ }
+ }
+ }
+
+ internal static void SetFlowDirectionFromParent(Element child)
+ {
+ PropagateFlowDirection(child, child.Parent);
+ }
+
+ internal static void PropagateVisual(Element target, Element source)
+ {
+ IVisualController targetController = target as IVisualController;
+ if (targetController == null)
+ return;
+
+ if (targetController.Visual != VisualMarker.MatchParent)
+ {
+ targetController.EffectiveVisual = targetController.Visual;
+ return;
+ }
+
+ if (source is IVisualController sourceController)
+ targetController.EffectiveVisual = sourceController.EffectiveVisual;
+ }
+
+ internal static void SetVisualfromParent(Element child)
+ {
+ PropagateVisual(child, child.Parent);
}
}
}
\ No newline at end of file
_logicalChildren.Add(element);
- PropertyPropagationExtensions.PropagatePropertyChanged(null, element);
-
element.Parent = this;
}
{ "Xamarin.Forms.UriImageSource", UriImageSource.UriProperty },
{ "Xamarin.Forms.UriMediaSource", UriMediaSource.UriProperty },
{ "Xamarin.Forms.UrlWebViewSource", UrlWebViewSource.UrlProperty },
- { "Xamarin.Forms.WebView", WebView.SourceProperty }
+ { "Xamarin.Forms.WebView", WebView.SourceProperty },
+ { "Xamarin.Forms.AppThemeColor", AppThemeColor.DefaultProperty }
};
static Dictionary<string, (BindableProperty, BindableProperty)> bindableObjectTypeDefaultCommandAndParameterProperties = new Dictionary<string, (BindableProperty, BindableProperty)>
using System;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Input;
+using Xamarin.Forms.StyleSheets;
namespace Xamarin.Forms
{
- public class MenuItem : BaseMenuItem, IMenuItemController
+ public class MenuItem : BaseMenuItem, IMenuItemController, IStyleSelectable
{
public static readonly BindableProperty AcceleratorProperty = BindableProperty.CreateAttached(nameof(Accelerator), typeof(Accelerator), typeof(MenuItem), null);
public static void SetAccelerator(BindableObject bindable, Accelerator value) => bindable.SetValue(AcceleratorProperty, value);
+ internal readonly MergedStyle _mergedStyle;
+
+ public MenuItem()
+ {
+ _mergedStyle = new MergedStyle(GetType(), this);
+ }
+
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
[EditorBrowsable(EditorBrowsableState.Never)] set => SetValue(IsEnabledPropertyKey, value);
}
+ [TypeConverter(typeof(ListStringTypeConverter))]
+ public IList<string> StyleClass
+ {
+ get { return @class; }
+ set { @class = value; }
+ }
+
+ [TypeConverter(typeof(ListStringTypeConverter))]
+ public IList<string> @class
+ {
+ get { return _mergedStyle.StyleClass; }
+ set
+ {
+ _mergedStyle.StyleClass = value;
+ }
+ }
+
+ IList<string> IStyleSelectable.Classes => StyleClass;
+
bool IsEnabledCore
{
set => SetValueCore(IsEnabledPropertyKey, value);
{
////If the base type is one of these, stop registering dynamic resources further
////The last one (typeof(Element)) is a safety guard as we might be creating VisualElement directly in internal code
- static readonly IList<Type> s_stopAtTypes = new List<Type> { typeof(View), typeof(Layout<>), typeof(VisualElement), typeof(Element) };
+ static readonly IList<Type> s_stopAtTypes = new List<Type> { typeof(View), typeof(Layout<>), typeof(VisualElement), typeof(NavigableElement), typeof(Element) };
IList<BindableProperty> _classStyleProperties;
_styleClass = value;
- if (_styleClass != null) {
- _classStyleProperties = new List<BindableProperty> ();
- foreach (var styleClass in _styleClass) {
- var classStyleProperty = BindableProperty.Create ("ClassStyle", typeof(IList<Style>), typeof(VisualElement), default(IList<Style>),
- propertyChanged: (bindable, oldvalue, newvalue) => ((VisualElement)bindable)._mergedStyle.OnClassStyleChanged());
- _classStyleProperties.Add (classStyleProperty);
- Target.OnSetDynamicResource (classStyleProperty, Xamarin.Forms.Style.StyleClassPrefix + styleClass);
+ if (_styleClass != null)
+ {
+ _classStyleProperties = new List<BindableProperty>();
+ foreach (var styleClass in _styleClass)
+ {
+ var classStyleProperty = BindableProperty.Create("ClassStyle", typeof(IList<Style>), typeof(Element), default(IList<Style>),
+ propertyChanged: (bindable, oldvalue, newvalue) => OnClassStyleChanged());
+ _classStyleProperties.Add(classStyleProperty);
+ Target.OnSetDynamicResource(classStyleProperty, Xamarin.Forms.Style.StyleClassPrefix + styleClass);
}
//reapply the css stylesheets
if (Target is Element targetelement)
- targetelement.ApplyStyleSheetsOnParentSet();
+ targetelement.ApplyStyleSheets();
}
}
}
void OnClassStyleChanged()
{
- ClassStyles = _classStyleProperties.Select (p => (Target.GetValue (p) as IList<Style>)?.FirstOrDefault (s => s.CanBeAppliedTo (TargetType))).ToList ();
+ ClassStyles = _classStyleProperties.Select(p => (Target.GetValue(p) as IList<Style>)?.FirstOrDefault(s => s.CanBeAppliedTo(TargetType))).ToList();
}
void OnImplicitStyleChanged()
void RegisterImplicitStyles()
{
Type type = TargetType;
- while (true) {
- BindableProperty implicitStyleProperty = BindableProperty.Create("ImplicitStyle", typeof(Style), typeof(VisualElement), default(Style),
+ while (true)
+ {
+ BindableProperty implicitStyleProperty = BindableProperty.Create("ImplicitStyle", typeof(Style), typeof(NavigableElement), default(Style),
propertyChanged: (bindable, oldvalue, newvalue) => OnImplicitStyleChanged());
_implicitStyles.Add(implicitStyleProperty);
Target.SetDynamicResource(implicitStyleProperty, type.FullName);
_implicitStyles.Clear();
//Register the fallback
- BindableProperty implicitStyleProperty = BindableProperty.Create("ImplicitStyle", typeof(Style), typeof(VisualElement), default(Style),
+ BindableProperty implicitStyleProperty = BindableProperty.Create("ImplicitStyle", typeof(Style), typeof(NavigableElement), default(Style),
propertyChanged: (bindable, oldvalue, newvalue) => OnImplicitStyleChanged());
_implicitStyles.Add(implicitStyleProperty);
Target.SetDynamicResource(implicitStyleProperty, fallbackTypeName);
--- /dev/null
+namespace Xamarin.Forms
+{
+ public static class NamedPlatformColor
+ {
+ // iOS
+ public const string SystemBlue = "systemBlue";
+ public const string SystemGreen = "systemGreen";
+ public const string SystemIndigo = "systemIndigo";
+ public const string SystemPink = "systemPink";
+ public const string SystemPurple = "systemPurple";
+ public const string SystemRed = "systemRed";
+ public const string SystemTeal = "systemTeal";
+ public const string SystemYellow = "systemYellow";
+ public const string SystemGray = "systemGray";
+ public const string SystemGray2 = "systemGray2";
+ public const string SystemGray3 = "systemGray3";
+ public const string SystemGray4 = "systemGray4";
+ public const string SystemGray5 = "systemGray5";
+ public const string SystemGray6 = "systemGray6";
+ public const string Label = "label";
+ public const string SecondaryLabel = "secondaryLabel";
+ public const string TertiaryLabel = "tertiaryLabel";
+ public const string QuaternaryLabel = "quaternaryLabellabel";
+ public const string PlaceholderText = "placeholderText";
+ public const string Separator = "separator";
+ public const string OpaqueSeparator = "opaqueSeparator";
+ public const string Link = "link";
+
+ // Android
+ public const string BackgroundDark = "background_dark";
+ public const string BackgroundLight = "background_light";
+ public const string Black = "black";
+ public const string DarkerGray = "darker_gray";
+ public const string HoloBlueBright = "holo_blue_bright";
+ public const string HoloBlueDark = "holo_blue_dark";
+ public const string HoloBlueLight = "holo_blue_light";
+ public const string HoloGreenDark = "holo_green_dark";
+ public const string HoloGreenLight = "holo_green_light";
+ public const string HoloOrangeDark = "holo_orange_dark";
+ public const string HoloOrangeLight = "holo_orange_light";
+ public const string HoloPurple = "holo_purple";
+ public const string HoloRedDark = "holo_red_dark";
+ public const string HoloRedLight = "holo_red_light";
+ public const string TabIndicatorText = "tab_indicator_text";
+ public const string Transparent = "transparent";
+ public const string White = "white";
+ public const string WidgetEditTextDark = "widget_edittext_dark";
+
+ // UWP
+ public const string SystemAltLowColor = "SystemAltLowColor";
+ public const string SystemAltMediumColor = "SystemAltMediumColor";
+ public const string SystemAltMediumHighColor = "SystemAltMediumHighColor";
+ public const string SystemAltMediumLowColor = "SystemAltMediumLowColor";
+ public const string SystemBaseHighColor = "SystemBaseHighColor";
+ public const string SystemBaseLowColor = "SystemBaseLowColor";
+ public const string SystemBaseMediumColor = "SystemBaseMediumColor";
+ public const string SystemBaseMediumHighColor = "SystemBaseMediumHighColor";
+ public const string SystemBaseMediumLowColor = "SystemBaseMediumLowColor";
+ public const string SystemChromeAltLowColor = "SystemChromeAltLowColor";
+ public const string SystemChromeBlackHighColor = "SystemChromeBlackHighColor";
+ public const string SystemChromeBlackLowColor = "SystemChromeBlackLowColor";
+ public const string SystemChromeBlackMediumLowColor = "SystemChromeBlackMediumLowColor";
+ public const string SystemChromeBlackMediumColor = "SystemChromeBlackMediumColor";
+ public const string SystemChromeDisabledHighColor = "SystemChromeDisabledHighColor";
+ public const string SystemChromeDisabledLowColor = "SystemChromeDisabledLowColor";
+ public const string SystemChromeHighColor = "SystemChromeHighColor";
+ public const string SystemChromeLowColor = "SystemChromeLowColor";
+ public const string SystemChromeMediumColor = "SystemChromeMediumColor";
+ public const string SystemChromeMediumLowColor = "SystemChromeMediumLowColor";
+ public const string SystemChromeWhiteColor = "SystemChromeWhiteColor";
+ public const string SystemListLowColor = "SystemListLowColor";
+ public const string SystemListMediumColor = "SystemListMediumColor";
+ public const string SystemAltHighColor = "SystemAltHighColor";
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Xamarin.Forms
+{
+ public enum OSAppTheme
+ {
+ Unspecified,
+ Light,
+ Dark
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+namespace Xamarin.Forms
+{
+ public class OnAppTheme<T> : BindableObject
+ {
+ public OnAppTheme()
+ {
+ Application.Current.RequestedThemeChanged += RequestedThemeChanged;
+ }
+
+ public static readonly BindableProperty LightProperty = BindableProperty.Create(nameof(Light), typeof(T), typeof(OnAppTheme<T>), default(T), propertyChanged: (bo, __, ___) => UpdateActualValue(bo));
+
+ public T Light
+ {
+ get => (T)GetValue(LightProperty);
+ set => SetValue(LightProperty, value);
+ }
+
+ public static readonly BindableProperty DarkProperty = BindableProperty.Create(nameof(Dark), typeof(T), typeof(OnAppTheme<T>), default(T), propertyChanged: (bo, __, ___) => UpdateActualValue(bo));
+
+ public T Dark
+ {
+ get => (T)GetValue(DarkProperty);
+ set => SetValue(DarkProperty, value);
+ }
+
+ public static readonly BindableProperty DefaultProperty = BindableProperty.Create(nameof(Default), typeof(T), typeof(OnAppTheme<T>), default(T), propertyChanged: (bo, __, ___) => UpdateActualValue(bo));
+
+ public T Default
+ {
+ get => (T)GetValue(DefaultProperty);
+ set => SetValue(DefaultProperty, value);
+ }
+
+ public static implicit operator T(OnAppTheme<T> onAppTheme)
+ {
+ switch (Application.Current?.RequestedTheme)
+ {
+ default:
+ case OSAppTheme.Light:
+ return onAppTheme.IsSet(LightProperty) ? onAppTheme.Light : (onAppTheme.IsSet(DefaultProperty) ? onAppTheme.Default : default(T));
+ case OSAppTheme.Dark:
+ return onAppTheme.IsSet(DarkProperty) ? onAppTheme.Dark : (onAppTheme.IsSet(DefaultProperty) ? onAppTheme.Default : default(T));
+ }
+ }
+
+ private T _actualValue;
+ public T ActualValue
+ {
+ get => _actualValue;
+ private set
+ {
+ _actualValue = value;
+ OnPropertyChanged();
+ }
+ }
+
+ static void UpdateActualValue(BindableObject bo)
+ {
+ var appThemeColor = bo as OnAppTheme<T>;
+ switch (Application.Current?.RequestedTheme)
+ {
+ default:
+ case OSAppTheme.Light:
+ appThemeColor.ActualValue = appThemeColor.IsSet(LightProperty) ? appThemeColor.Light : (appThemeColor.IsSet(DefaultProperty) ? appThemeColor.Default : default(T));
+ break;
+ case OSAppTheme.Dark:
+ appThemeColor.ActualValue = appThemeColor.IsSet(DarkProperty) ? appThemeColor.Dark : (appThemeColor.IsSet(DefaultProperty) ? appThemeColor.Default : default(T));
+ break;
+ }
+ }
+
+ void RequestedThemeChanged(object sender, AppThemeChangedEventArgs e)
+ {
+ UpdateActualValue(this);
+ }
+ }
+}
\ No newline at end of file
((OrientationStateTrigger)bindable).UpdateState();
}
- internal override void OnAttached()
+ protected override void OnAttached()
{
base.OnAttached();
}
}
- internal override void OnDetached()
+ protected override void OnDetached()
{
base.OnDetached();
public IList<ToolbarItem> ToolbarItems { get; internal set; }
- [EditorBrowsable(EditorBrowsableState.Never)]
- public Rectangle ContainerArea
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Rectangle ContainerArea
{
get { return _containerArea; }
set
}
}
- [EditorBrowsable(EditorBrowsableState.Never)]
- public bool IgnoresContainerArea
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IgnoresContainerArea
{
get { return (bool)GetValue(IgnoresContainerAreaProperty); }
set { SetValue(IgnoresContainerAreaProperty, value); }
}
- [EditorBrowsable(EditorBrowsableState.Never)]
- public ObservableCollection<Element> InternalChildren { get; } = new ObservableCollection<Element>();
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ObservableCollection<Element> InternalChildren { get; } = new ObservableCollection<Element>();
internal override IEnumerable<Element> ChildrenNotDrawnByThisElement
{
internal override void OnIsPlatformEnabledChanged()
{
base.OnIsPlatformEnabledChanged();
- if(IsPlatformEnabled && _pendingActions.Count > 0)
+ if (IsPlatformEnabled && _pendingActions.Count > 0)
{
var actionsToProcess = _pendingActions.ToList();
_pendingActions.Clear();
- foreach(var pendingAction in actionsToProcess)
+ foreach (var pendingAction in actionsToProcess)
pendingAction();
}
}
return OnBackButtonPressed();
}
+ protected override void OnRequestedThemeChanged(OSAppTheme newValue)
+ {
+ base.OnRequestedThemeChanged(newValue);
+
+ Resources?.Reload();
+ }
+
protected virtual void LayoutChildren(double x, double y, double width, double height)
{
var area = new Rectangle(x, y, width, height);
SetInheritedBindingContext(toolbarItem, BindingContext);
}
- if(_titleView != null)
+ if (_titleView != null)
SetInheritedBindingContext(_titleView, BindingContext);
}
}
}
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void SendAppearing()
+
+ internal void OnAppearing(Action action)
+ {
+ if (_hasAppeared)
+ action();
+ else
+ {
+ EventHandler eventHandler = null;
+ eventHandler = (_, __) =>
+ {
+ this.Appearing -= eventHandler;
+ action();
+ };
+
+ this.Appearing += eventHandler;
+ }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void SendAppearing()
{
if (_hasAppeared)
return;
FindApplication(this)?.OnPageAppearing(this);
}
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void SendDisappearing()
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void SendDisappearing()
{
if (!_hasAppeared)
return;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
-using System.IO;
-using System.Reflection;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Internals
{
public int Depth;
public int Line;
}
- public static List<Datum> Data = new List<Datum>(Capacity);
+ public static List<Datum> Data;
- static Stack<Profile> Stack = new Stack<Profile>(Capacity);
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static bool IsEnabled { get; private set; } = false;
+
+ static Stack<Profile> Stack;
static int Depth = 0;
static bool Running = false;
- static Stopwatch Stopwatch = new Stopwatch();
+ static Stopwatch Stopwatch;
readonly long _start;
readonly string _name;
readonly int _slot;
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Enable()
+ {
+ if (!IsEnabled)
+ {
+ IsEnabled = true;
+ Data = new List<Datum>(Capacity);
+ Stack = new Stack<Profile>(Capacity);
+ Stopwatch = new Stopwatch();
+ }
+ }
+
public static void Start()
{
+ if (!IsEnabled)
+ return;
+
Running = true;
}
public static void Stop()
{
+ if (!IsEnabled)
+ return;
+
// unwind stack
Running = false;
while (Stack.Count > 0)
[CallerMemberName] string name = "",
[CallerLineNumber] int line = 0)
{
- if (!Running)
+ if (!IsEnabled || !Running)
return;
FrameBeginBody(name, null, line);
public static void FrameEnd(
[CallerMemberName] string name = "")
{
- if (!Running)
+ if (!IsEnabled || !Running)
return;
FrameEndBody(name);
string id,
[CallerLineNumber] int line = 0)
{
- if (!Running)
+ if (!IsEnabled || !Running)
return;
FramePartitionBody(id, line);
public void Dispose()
{
+ if (!IsEnabled)
+ return;
if (Running && _start == 0)
return;
public static readonly BindableProperty GroupNameProperty = BindableProperty.Create(
nameof(GroupName), typeof(string), typeof(RadioButton), null, propertyChanged: (b, o, n) => ((RadioButton)b).OnGroupNamePropertyChanged((string)o, (string)n));
- public static readonly BindableProperty ButtonSourceProperty = BindableProperty.Create(
- nameof(ButtonSource), typeof(ImageSource), typeof(RadioButton), null);
+ // TODO Needs implementations beyond Android
+ //public static readonly BindableProperty ButtonSourceProperty = BindableProperty.Create(
+ // nameof(ButtonSource), typeof(ImageSource), typeof(RadioButton), null);
public event EventHandler<CheckedChangedEventArgs> CheckedChanged;
set { SetValue(GroupNameProperty, value); }
}
- public ImageSource ButtonSource
- {
- get { return (ImageSource)GetValue(ButtonSourceProperty); }
- set { SetValue(ButtonSourceProperty, value); }
- }
+ // TODO Needs implementations beyond Android
+ //public ImageSource ButtonSource
+ //{
+ // get { return (ImageSource)GetValue(ButtonSourceProperty); }
+ // set { SetValue(ButtonSourceProperty, value); }
+ //}
public RadioButton()
{
for (int i = 0; i < supportedVisuals.Length; i++)
{
- if(visualRenderers.TryGetValue(supportedVisuals[i], out (Type target, short priority) existingTargetValue))
+ if (visualRenderers.TryGetValue(supportedVisuals[i], out (Type target, short priority) existingTargetValue))
{
- if(existingTargetValue.priority <= priority)
+ if (existingTargetValue.priority <= priority)
visualRenderers[supportedVisuals[i]] = (trender, priority);
}
else
Profile.FramePartition("Reflect");
foreach (Assembly assembly in assemblies)
{
- var assemblyName = assembly.GetName().Name;
- Profile.FrameBegin(assemblyName);
+ string frameName = Profile.IsEnabled ? assembly.GetName().Name : "Assembly";
+ Profile.FrameBegin(frameName);
foreach (Type attrType in attrTypes)
{
{
var a = attributes[i];
var attribute = a as HandlerAttribute;
- if(attribute == null && (a is ExportFontAttribute fa))
+ if (attribute == null && (a is ExportFontAttribute fa))
{
FontRegistrar.Register(fa, assembly);
}
}
}
- object[] effectAttributes = assembly.GetCustomAttributesSafe(typeof (ExportEffectAttribute));
+ object[] effectAttributes = assembly.GetCustomAttributesSafe(typeof(ExportEffectAttribute));
if (effectAttributes == null || effectAttributes.Length == 0)
{
- Profile.FrameEnd(assemblyName);
+ Profile.FrameEnd(frameName);
continue;
}
Array.Copy(effectAttributes, typedEffectAttributes, effectAttributes.Length);
RegisterEffects(resolutionName, typedEffectAttributes);
- Profile.FrameEnd(assemblyName);
+ Profile.FrameEnd(frameName);
}
if ((flags & InitializationFlags.DisableCss) == 0)
ValuesChanged?.Invoke(this, new ResourcesChangedEventArgs(values));
}
+ internal void Reload()
+ {
+ foreach (var mr in MergedResources)
+ OnValuesChanged(mr);
+ }
+
event EventHandler<ResourcesChangedEventArgs> ValuesChanged;
//only used for unit testing
static int s_routeCount = 0;
static Dictionary<string, RouteFactory> s_routes = new Dictionary<string, RouteFactory>();
- internal const string ImplicitPrefix = "IMPL_";
+ const string ImplicitPrefix = "IMPL_";
+ const string DefaultPrefix = "D_FAULT_";
const string _pathSeparator = "/";
internal static string GenerateImplicitRoute(string source)
{
return IsImplicit(GetRoute(source));
}
+ internal static bool IsDefault(string source)
+ {
+ return source.StartsWith(DefaultPrefix, StringComparison.Ordinal);
+ }
internal static void Clear()
{
static object CreateDefaultRoute(BindableObject bindable)
{
- return bindable.GetType().Name + ++s_routeCount;
+ return $"{DefaultPrefix}{bindable.GetType().Name}{++s_routeCount}";
}
internal static string[] GetRouteKeys()
return $"{source}/";
}
- internal static Uri RemoveImplicit(Uri uri)
+ internal static Uri Remove(Uri uri, bool implicitRoutes, bool defaultRoutes)
{
- uri = ShellUriHandler.FormatUri(uri);
+ uri = ShellUriHandler.FormatUri(uri, null);
string[] parts = uri.OriginalString.TrimEnd(_pathSeparator[0]).Split(_pathSeparator[0]);
+ bool userDefinedRouteAdded = false;
List<string> toKeep = new List<string>();
for (int i = 0; i < parts.Length; i++)
- if (!IsImplicit(parts[i]))
+ {
+ // This means there are no routes defined on the shell but the user has navigated to a global route
+ // so we need to attach the final route where the user left the shell
+ if (s_routes.ContainsKey(parts[i]) && !userDefinedRouteAdded && i > 0)
+ {
+ toKeep.Add(parts[i - 1]);
+ }
+
+ if (!(IsDefault(parts[i]) && defaultRoutes) && !(IsImplicit(parts[i]) && implicitRoutes))
+ {
+ if (!String.IsNullOrWhiteSpace(parts[i]))
+ userDefinedRouteAdded = true;
+
toKeep.Add(parts[i]);
+ }
+ }
+
+ if (!userDefinedRouteAdded && parts.Length > 0)
+ {
+ toKeep.Add(parts[parts.Length - 1]);
+ }
return new Uri(string.Join(_pathSeparator, toKeep), UriKind.Relative);
}
}
RouteFactory existingRegistration = null;
- if(s_routes.TryGetValue(route, out existingRegistration) && !existingRegistration.Equals(routeFactory))
+ if (s_routes.TryGetValue(route, out existingRegistration) && !existingRegistration.Equals(routeFactory))
throw new ArgumentException($"Duplicated Route: \"{route}\"");
}
using System.Runtime.CompilerServices;
using Xamarin.Forms.Internals;
using System.ComponentModel;
+using System.Linq;
+using Xamarin.Forms.StyleSheets;
namespace Xamarin.Forms
{
public event EventHandler Disappearing;
bool _hasAppearing;
+ const string DefaultFlyoutItemLabelStyle = "Default_FlyoutItemLabelStyle";
+ const string DefaultFlyoutItemImageStyle = "Default_FlyoutItemImageStyle";
+ const string DefaultFlyoutItemLayoutStyle = "Default_FlyoutItemLayoutStyle";
#region PropertyKeys
action();
else
{
+ if(Navigation.ModalStack.Count > 0)
+ {
+ Navigation.ModalStack[Navigation.ModalStack.Count - 1]
+ .OnAppearing(action);
+
+ return;
+ }
+ else if(Navigation.NavigationStack.Count > 1)
+ {
+ Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]
+ .OnAppearing(action);
+
+ return;
+ }
+
EventHandler eventHandler = null;
eventHandler = (_, __) =>
{
internal virtual void ApplyQueryAttributes(IDictionary<string, string> query)
{
}
+
+ static void UpdateFlyoutItemStyles(Grid flyoutItemCell, IStyleSelectable source)
+ {
+ List<string> bindableObjectStyle = new List<string>() {
+ DefaultFlyoutItemLabelStyle,
+ DefaultFlyoutItemImageStyle,
+ DefaultFlyoutItemLayoutStyle,
+ FlyoutItem.LabelStyle,
+ FlyoutItem.ImageStyle,
+ FlyoutItem.GridStyle };
+
+ if (source?.Classes != null)
+ foreach (var styleClass in source.Classes)
+ bindableObjectStyle.Add(styleClass);
+
+ flyoutItemCell
+ .StyleClass = bindableObjectStyle;
+ flyoutItemCell.Children.OfType<Label>().First()
+ .StyleClass = bindableObjectStyle;
+ flyoutItemCell.Children.OfType<Image>().First()
+ .StyleClass = bindableObjectStyle;
+ }
+
+ internal static DataTemplate CreateDefaultFlyoutItemCell(IStyleSelectable styleSelectable, string textBinding, string iconBinding)
+ {
+ return new DataTemplate(() =>
+ {
+ var grid = new Grid();
+ if (Device.RuntimePlatform == Device.UWP)
+ grid.ColumnSpacing = grid.RowSpacing = 0;
+
+ grid.Resources = new ResourceDictionary();
+
+ var defaultLabelClass = new Style(typeof(Label))
+ {
+ Setters = {
+ new Setter { Property = Label.VerticalTextAlignmentProperty, Value = TextAlignment.Center }
+ },
+ Class = DefaultFlyoutItemLabelStyle,
+ };
+
+ var defaultImageClass = new Style(typeof(Image))
+ {
+ Setters = {
+ new Setter { Property = Image.VerticalOptionsProperty, Value = LayoutOptions.Center }
+ },
+ Class = DefaultFlyoutItemImageStyle,
+ };
+
+ var defaultGridClass = new Style(typeof(Grid))
+ {
+ Class = DefaultFlyoutItemLayoutStyle,
+ };
+
+ var groups = new VisualStateGroupList();
+
+ var commonGroup = new VisualStateGroup();
+ commonGroup.Name = "CommonStates";
+ groups.Add(commonGroup);
+
+ var normalState = new VisualState();
+ normalState.Name = "Normal";
+ commonGroup.States.Add(normalState);
+
+ var selectedState = new VisualState();
+ selectedState.Name = "Selected";
+
+ if (Device.RuntimePlatform != Device.UWP)
+ {
+ selectedState.Setters.Add(new Setter
+ {
+ Property = VisualElement.BackgroundColorProperty,
+ Value = new Color(0.95)
+ });
+ }
+
+ commonGroup.States.Add(selectedState);
+
+ defaultGridClass.Setters.Add(new Setter { Property = VisualStateManager.VisualStateGroupsProperty, Value = groups });
+
+ if (Device.RuntimePlatform == Device.Android)
+ defaultGridClass.Setters.Add(new Setter { Property = Grid.HeightRequestProperty, Value = 50 });
+
+ ColumnDefinitionCollection columnDefinitions = new ColumnDefinitionCollection();
+
+ if (Device.RuntimePlatform == Device.Android)
+ columnDefinitions.Add(new ColumnDefinition { Width = 54 });
+ else if (Device.RuntimePlatform == Device.iOS)
+ columnDefinitions.Add(new ColumnDefinition { Width = 50 });
+ else if (Device.RuntimePlatform == Device.UWP)
+ columnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
+
+ columnDefinitions.Add(new ColumnDefinition { Width = GridLength.Star });
+ defaultGridClass.Setters.Add(new Setter { Property = Grid.ColumnDefinitionsProperty, Value = columnDefinitions });
+
+ var image = new Image();
+
+ double sizeRequest = -1;
+ if (Device.RuntimePlatform == Device.Android)
+ sizeRequest = 24;
+ else if (Device.RuntimePlatform == Device.iOS)
+ sizeRequest = 22;
+ else if (Device.RuntimePlatform == Device.UWP)
+ sizeRequest = 16;
+
+ if (sizeRequest > 0)
+ {
+ defaultImageClass.Setters.Add(new Setter() { Property = Image.HeightRequestProperty, Value = sizeRequest });
+ defaultImageClass.Setters.Add(new Setter() { Property = Image.WidthRequestProperty, Value = sizeRequest });
+ }
+
+ if (Device.RuntimePlatform == Device.UWP)
+ {
+ defaultImageClass.Setters.Add(new Setter { Property = Image.HorizontalOptionsProperty, Value = LayoutOptions.Start });
+ defaultImageClass.Setters.Add(new Setter { Property = Image.MarginProperty, Value = new Thickness(12, 0, 12, 0) });
+ }
+
+ image.SetBinding(Image.SourceProperty, iconBinding);
+ grid.Children.Add(image);
+
+ var label = new Label();
+ label.SetBinding(Label.TextProperty, textBinding);
+ grid.Children.Add(label, 1, 0);
+
+ if (Device.RuntimePlatform == Device.Android)
+ {
+ defaultLabelClass.Setters.Add(new Setter { Property = Label.FontSizeProperty, Value = 14 });
+ defaultLabelClass.Setters.Add(new Setter { Property = Label.TextColorProperty, Value = Color.Black.MultiplyAlpha(0.87) });
+ defaultLabelClass.Setters.Add(new Setter { Property = Label.FontFamilyProperty, Value = "sans-serif-medium" });
+ defaultLabelClass.Setters.Add(new Setter { Property = Label.MarginProperty, Value = new Thickness(20, 0, 0, 0) });
+ }
+ else if (Device.RuntimePlatform == Device.iOS)
+ {
+ defaultLabelClass.Setters.Add(new Setter { Property = Label.FontSizeProperty, Value = Device.GetNamedSize(NamedSize.Small, label) });
+ defaultLabelClass.Setters.Add(new Setter { Property = Label.FontAttributesProperty, Value = FontAttributes.Bold });
+ }
+ else if (Device.RuntimePlatform == Device.UWP)
+ {
+ defaultLabelClass.Setters.Add(new Setter { Property = Label.HorizontalOptionsProperty, Value = LayoutOptions.Start });
+ defaultLabelClass.Setters.Add(new Setter { Property = Label.HorizontalTextAlignmentProperty, Value = TextAlignment.Start });
+ }
+
+ UpdateFlyoutItemStyles(grid, styleSelectable);
+ grid.Resources = new ResourceDictionary() { defaultGridClass, defaultLabelClass, defaultImageClass };
+ return grid;
+ });
+ }
}
public interface IQueryAttributable
ReadOnlyCollection<ShellItem> GetItems();
event NotifyCollectionChangedEventHandler ItemsCollectionChanged;
+
+ DataTemplate GetFlyoutItemDataTemplate(BindableObject bo);
}
}
\ No newline at end of file
-using System.Runtime.CompilerServices;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using Xamarin.Forms.StyleSheets;
namespace Xamarin.Forms
{
- internal class MenuShellItem : ShellItem, IMenuItemController
+ internal class MenuShellItem : ShellItem, IMenuItemController, IStyleSelectable
{
internal MenuShellItem(MenuItem menuItem)
{
MenuItem = menuItem;
-
+ MenuItem.Parent = this;
SetBinding(TitleProperty, new Binding(nameof(MenuItem.Text), BindingMode.OneWay, source: menuItem));
SetBinding(IconProperty, new Binding(nameof(MenuItem.IconImageSource), BindingMode.OneWay, source: menuItem));
SetBinding(FlyoutIconProperty, new Binding(nameof(MenuItem.IconImageSource), BindingMode.OneWay, source: menuItem));
- Shell.SetMenuItemTemplate(this, Shell.GetMenuItemTemplate(MenuItem));
MenuItem.PropertyChanged += OnMenuItemPropertyChanged;
}
+ IList<string> IStyleSelectable.Classes => ((IStyleSelectable)MenuItem).Classes;
+
public string Text => Title;
void OnMenuItemPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
using System.Collections.Generic;
using System.ComponentModel;
using Xamarin.Forms.Internals;
+using Xamarin.Forms.StyleSheets;
namespace Xamarin.Forms
{
- public class NavigableElement : Element, INavigationProxy
+ public class NavigableElement : Element, INavigationProxy, IStyleSelectable
{
static readonly BindablePropertyKey NavigationPropertyKey =
BindableProperty.CreateReadOnly("Navigation", typeof(INavigation), typeof(VisualElement), default(INavigation));
[TypeConverter(typeof(ListStringTypeConverter))]
public IList<string> @class {
get { return _mergedStyle.StyleClass; }
- set { _mergedStyle.StyleClass = value; }
+ set
+ {
+ _mergedStyle.StyleClass = value;
+ }
}
+ IList<string> IStyleSelectable.Classes => StyleClass;
+
[EditorBrowsable(EditorBrowsableState.Never)]
public NavigationProxy NavigationProxy {
get { return Navigation as NavigationProxy; }
BindableProperty.CreateAttached("TitleView", typeof(View), typeof(Shell), null, propertyChanged: OnTitleViewChanged);
public static readonly BindableProperty MenuItemTemplateProperty =
- BindableProperty.CreateAttached(nameof(MenuItemTemplate), typeof(DataTemplate), typeof(Shell), null, BindingMode.OneTime);
+ BindableProperty.CreateAttached(nameof(MenuItemTemplate), typeof(DataTemplate), typeof(Shell), null, BindingMode.OneTime, defaultValueCreator: OnMenuItemTemplateCreate);
public static DataTemplate GetMenuItemTemplate(BindableObject obj) => (DataTemplate)obj.GetValue(MenuItemTemplateProperty);
public static void SetMenuItemTemplate(BindableObject obj, DataTemplate menuItemTemplate) => obj.SetValue(MenuItemTemplateProperty, menuItemTemplate);
public static readonly BindableProperty ItemTemplateProperty =
- BindableProperty.CreateAttached(nameof(ItemTemplate), typeof(DataTemplate), typeof(Shell), null, BindingMode.OneTime);
+ BindableProperty.CreateAttached(nameof(ItemTemplate), typeof(DataTemplate), typeof(Shell), null, BindingMode.OneTime, defaultValueCreator: OnItemTemplateCreator);
+
+ static object OnItemTemplateCreator(BindableObject bindable)
+ {
+ return OnFlyoutItemTemplateCreate(bindable, "Title", "FlyoutIcon");
+ }
+
+ static object OnMenuItemTemplateCreate(BindableObject bindable)
+ {
+ return OnFlyoutItemTemplateCreate(bindable, "Text", "Icon");
+ }
+
+ static object OnFlyoutItemTemplateCreate(BindableObject bindable, string textBinding, string iconBinding)
+ {
+ if (bindable is BaseShellItem baseShellItem)
+ return BaseShellItem.CreateDefaultFlyoutItemCell(baseShellItem, textBinding, iconBinding);
+
+ if (bindable is MenuItem mi)
+ {
+ if (mi.Parent is BaseShellItem bsiMi)
+ return BaseShellItem.CreateDefaultFlyoutItemCell(bsiMi, textBinding, iconBinding);
+ else
+ return null;
+ }
+
+ return BaseShellItem.CreateDefaultFlyoutItemCell(bindable as StyleSheets.IStyleSelectable, textBinding, iconBinding);
+ }
public static DataTemplate GetItemTemplate(BindableObject obj) => (DataTemplate)obj.GetValue(ItemTemplateProperty);
public static void SetItemTemplate(BindableObject obj, DataTemplate itemTemplate) => obj.SetValue(ItemTemplateProperty, itemTemplate);
List<(IAppearanceObserver Observer, Element Pivot)> _appearanceObservers = new List<(IAppearanceObserver Observer, Element Pivot)>();
List<IFlyoutBehaviorObserver> _flyoutBehaviorObservers = new List<IFlyoutBehaviorObserver>();
+ DataTemplate IShellController.GetFlyoutItemDataTemplate(BindableObject bo)
+ {
+ BindableProperty bp = null;
+
+ if (bo is IMenuItemController)
+ {
+ bp = MenuItemTemplateProperty;
+
+ if (bo is MenuItem mi && mi.Parent != null && mi.Parent.IsSet(bp))
+ bo = mi.Parent;
+ else if (bo is MenuShellItem msi && msi.MenuItem != null && msi.MenuItem.IsSet(bp))
+ bo = msi.MenuItem;
+ }
+ else
+ {
+ bp = ItemTemplateProperty;
+ }
+
+ if (bo.IsSet(bp))
+ return (DataTemplate)bo.GetValue(bp);
+
+ return (DataTemplate)GetValue(bp);
+ }
+
event EventHandler IShellController.StructureChanged
{
add { _structureChanged += value; }
var state = GetNavigationState(shellItem, shellSection, shellContent, null, null);
- if (FlyoutIsPresented && FlyoutBehavior == FlyoutBehavior.Flyout)
+ if (FlyoutIsPresented && GetEffectiveFlyoutBehavior() != FlyoutBehavior.Locked)
SetValueFromRenderer(FlyoutIsPresentedProperty, false);
if (shellSection == null)
SetValueFromRenderer(CurrentStatePropertyKey, result);
- OnNavigated(new ShellNavigatedEventArgs(oldState, CurrentState, source));
+ ProcessNavigated(new ShellNavigatedEventArgs(oldState, CurrentState, source));
}
ReadOnlyCollection<ShellItem> IShellController.GetItems() => ((ShellItemCollection)Items).VisibleItems;
internal async Task GoToAsync(ShellNavigationState state, bool? animate, bool enableRelativeShellRoutes)
{
// FIXME: This should not be none, we need to compute the delta and set flags correctly
- var accept = ProposeNavigation(ShellNavigationSource.Unknown, state, true);
+ var accept = ProposeNavigation(ShellNavigationSource.Unknown, state, this.CurrentState != null);
if (!accept)
return;
if (navigationRequest.Request.GlobalRoutes.Count > 0 && navigationRequest.StackRequest != NavigationRequest.WhatToDoWithTheStack.ReplaceIt)
{
// TODO get rid of this hack and fix so if there's a stack the current page doesn't display
- Device.BeginInvokeOnMainThread(async () =>
+ await Device.InvokeOnMainThreadAsync(() =>
{
- await CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate);
+ return CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate);
+ });
+ }
+ else if (navigationRequest.Request.GlobalRoutes.Count == 0 &&
+ navigationRequest.StackRequest == NavigationRequest.WhatToDoWithTheStack.ReplaceIt &&
+ currentShellSection?.Navigation?.NavigationStack?.Count > 1)
+ {
+ // TODO get rid of this hack and fix so if there's a stack the current page doesn't display
+ await Device.InvokeOnMainThreadAsync(() =>
+ {
+ return CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate);
});
}
}
// this can be null in the event that no navigation actually took place!
if (_accumulatedEvent != null)
- OnNavigated(_accumulatedEvent);
+ ProcessNavigated(_accumulatedEvent);
}
internal static void ApplyQueryAttributes(Element element, IDictionary<string, string> query, bool isLastItem)
if (!isLastItem)
{
var route = Routing.GetRoute(element);
- if (string.IsNullOrEmpty(route) || route.StartsWith(Routing.ImplicitPrefix, StringComparison.Ordinal))
+ if (string.IsNullOrEmpty(route) || Routing.IsImplicit(route))
return;
prefix = route + ".";
}
{
var topPage = modalStack[i];
- if(i > 0)
+ if (i > 0)
stateBuilder.Append("/");
stateBuilder.Append(Routing.GetRoute(topPage));
SendStructureChanged();
};
- void SetCurrentItem()
+ async void SetCurrentItem()
{
var shellItems = ShellController.GetItems();
ShellItem shellItem = null;
- foreach (var item in shellItems)
+ // If shell item has been removed try to renavigate to current location
+ // Just in case the item was replaced. This is mainly relevant for hot reload
+ if (CurrentItem != null)
{
- if (item is ShellItem && ValidDefaultShellItem(item))
+ try
{
- shellItem = item;
- break;
+ var location = CurrentState.Location;
+ if (ShellUriHandler.GetNavigationRequest(this, ((ShellNavigationState)location).FullLocation, false) != null)
+ await GoToAsync(location, false);
+
+ return;
+ }
+ catch (Exception exc)
+ {
+ Log.Warning(nameof(Shell), $"If you're using hot reload add a route to everything in your shell file: {exc}");
+ }
+ }
+
+ if (shellItem == null)
+ {
+ foreach (var item in shellItems)
+ {
+ if (item is ShellItem && ValidDefaultShellItem(item))
+ {
+ shellItem = item;
+ break;
+ }
}
}
currentContent.Navigation.PopAsync();
return true;
}
- return false;
+
+ var args = new ShellNavigatingEventArgs(this.CurrentState, "", ShellNavigationSource.Pop, true);
+ OnNavigating(args);
+ return args.Cancelled;
}
bool ValidDefaultShellItem(Element child) => !(child is MenuShellItem);
}
}
-
- protected virtual void OnNavigated(ShellNavigatedEventArgs args)
+ internal void ProcessNavigated(ShellNavigatedEventArgs args)
{
if (_accumulateNavigatedEvents)
_accumulatedEvent = args;
else
{
- var content = CurrentItem?.CurrentItem?.CurrentItem;
- if (content != null)
+ BaseShellItem baseShellItem = CurrentItem?.CurrentItem?.CurrentItem;
+
+ if (baseShellItem != null)
{
- content.OnAppearing(() => Navigated?.Invoke(this, args));
+ baseShellItem.OnAppearing(() =>
+ {
+ OnNavigated(args);
+ Navigated?.Invoke(this, args);
+ });
}
else
{
+ OnNavigated(args);
Navigated?.Invoke(this, args);
}
}
}
+ protected virtual void OnNavigated(ShellNavigatedEventArgs args)
+ {
+ }
+
ShellNavigationState _lastNavigating;
protected virtual void OnNavigating(ShellNavigatingEventArgs args)
{
protected override void OnRemovePage(Page page) => SectionProxy.RemovePage(page);
- protected override Task<Page> OnPopModal(bool animated)
+ protected override async Task<Page> OnPopModal(bool animated)
{
if (ModalStack.Count > 0)
ModalStack[ModalStack.Count - 1].SendDisappearing();
if (!_shell.CurrentItem.CurrentItem.IsPoppingModalStack)
{
- if (ModalStack.Count == 1)
- _shell.CurrentItem.SendAppearing();
- else if (ModalStack.Count > 1)
+ if (ModalStack.Count > 1)
ModalStack[ModalStack.Count - 2].SendAppearing();
}
- return base.OnPopModal(animated);
+ var modalPopped = await base.OnPopModal(animated);
+
+ if (ModalStack.Count == 0 && !_shell.CurrentItem.CurrentItem.IsPoppingModalStack)
+ _shell.CurrentItem.SendAppearing();
+
+ return modalPopped;
}
- protected override Task OnPushModal(Page modal, bool animated)
+
+ protected override async Task OnPushModal(Page modal, bool animated)
{
if (ModalStack.Count == 0)
_shell.CurrentItem.SendDisappearing();
- if(!_shell.CurrentItem.CurrentItem.IsPushingModalStack)
+ if (!_shell.CurrentItem.CurrentItem.IsPushingModalStack)
modal.SendAppearing();
- return base.OnPushModal(modal, animated);
+ await base.OnPushModal(modal, animated);
+
+ modal.NavigationProxy.Inner = new NavigationImplWrapper(modal.NavigationProxy.Inner, this);
+ }
+
+
+ class NavigationImplWrapper : NavigationProxy
+ {
+ readonly INavigation _shellProxy;
+
+ public NavigationImplWrapper(INavigation proxy, INavigation shellProxy)
+ {
+ Inner = proxy;
+ _shellProxy = shellProxy;
+
+ }
+
+ protected override Task<Page> OnPopModal(bool animated) => _shellProxy.PopModalAsync(animated);
+
+ protected override Task OnPushModal(Page modal, bool animated) => _shellProxy.PushModalAsync(modal, animated);
}
}
}
public MenuItemCollection MenuItems => (MenuItemCollection)GetValue(MenuItemsProperty);
- public object Content {
+ public object Content
+ {
get => GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
- public DataTemplate ContentTemplate {
+ public DataTemplate ContentTemplate
+ {
get => (DataTemplate)GetValue(ContentTemplateProperty);
set => SetValue(ContentTemplateProperty, value);
}
shellContent._logicalChildren.Add((Element)newValue);
shellContent.ContentCache = newElement;
}
- else if(newValue != null)
+ else if (newValue != null)
{
throw new InvalidOperationException($"{nameof(ShellContent)} {nameof(Content)} should be of type {nameof(Page)}. Title {shellContent?.Title}, Route {shellContent?.Route} ");
}
base.ApplyQueryAttributes(query);
SetValue(QueryAttributesProperty, query);
- if (Content is BindableObject bindable)
+ if (ContentCache is BindableObject bindable)
bindable.SetValue(QueryAttributesProperty, query);
}
static void OnQueryAttributesPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
- if (newValue is IDictionary<string, string> query)
- ApplyQueryAttributes(bindable, query);
+ ApplyQueryAttributes(bindable, newValue as IDictionary<string, string>, oldValue as IDictionary<string, string>);
}
- static void ApplyQueryAttributes(object content, IDictionary<string, string> query)
+ static void ApplyQueryAttributes(object content, IDictionary<string, string> query, IDictionary<string, string> oldQuery)
{
+ query = query ?? new Dictionary<string, string>();
+ oldQuery = oldQuery ?? new Dictionary<string, string>();
+
if (content is IQueryAttributable attributable)
attributable.ApplyQueryAttributes(query);
if (content is BindableObject bindable && bindable.BindingContext != null && content != bindable.BindingContext)
- ApplyQueryAttributes(bindable.BindingContext, query);
+ ApplyQueryAttributes(bindable.BindingContext, query, oldQuery);
var type = content.GetType();
var typeInfo = type.GetTypeInfo();
if (queryPropertyAttributes.Length == 0)
return;
- foreach (QueryPropertyAttribute attrib in queryPropertyAttributes) {
- if (query.TryGetValue(attrib.QueryId, out var value)) {
+ foreach (QueryPropertyAttribute attrib in queryPropertyAttributes)
+ {
+ if (query.TryGetValue(attrib.QueryId, out var value))
+ {
PropertyInfo prop = type.GetRuntimeProperty(attrib.Name);
if (prop != null && prop.CanWrite && prop.SetMethod.IsPublic)
prop.SetValue(content, value);
}
+ else if (oldQuery.TryGetValue(attrib.QueryId, out var oldValue))
+ {
+ PropertyInfo prop = type.GetRuntimeProperty(attrib.Name);
+
+ if (prop != null && prop.CanWrite && prop.SetMethod.IsPublic)
+ prop.SetValue(content, null);
+ }
}
}
}
[EditorBrowsable(EditorBrowsableState.Always)]
public class FlyoutItem : ShellItem
{
+ public const string LabelStyle = "FlyoutItemLabelStyle";
+ public const string ImageStyle = "FlyoutItemImageStyle";
+ public const string GridStyle = "FlyoutItemGridStyle";
+
public FlyoutItem()
{
Shell.SetFlyoutBehavior(this, FlyoutBehavior.Flyout);
}
#if DEBUG
- [Obsolete ("Please dont use this in core code... its SUPER hard to debug when this happens", true)]
+ [Obsolete("Please dont use this in core code... its SUPER hard to debug when this happens", true)]
#endif
public static implicit operator ShellItem(ShellSection shellSection)
{
internal override void SendAppearing()
{
base.SendAppearing();
- if(CurrentItem != null && Parent is Shell shell && shell.CurrentItem == this)
+ if (CurrentItem != null && Parent is Shell shell && shell.CurrentItem == this)
{
CurrentItem.SendAppearing();
}
set
{
_fullLocation = value;
- Location = Routing.RemoveImplicit(value);
+ Location = Routing.Remove(value, true, true);
}
}
// If we're not on the last loop of the stack then continue
// otherwise pop the rest of the stack
- if(!isLast)
+ if (!isLast)
continue;
}
{
bool isLast = i == nonModalPageStacks.Count - 1;
- if(isLast)
+ if (isLast)
{
bool isAnimated = animate ?? (Shell.GetPresentationMode(nonModalPageStacks[i]) & PresentationMode.NotAnimated) != PresentationMode.NotAnimated;
await OnPushAsync(nonModalPageStacks[i], isAnimated);
if (CurrentItem == null && ((IShellSectionController)this).GetItems().Contains(child))
SetValueFromRenderer(CurrentItemProperty, child);
- if(CurrentItem != null)
+ if (CurrentItem != null)
UpdateDisplayedPage();
}
// indicate that we are done popping down the stack to the modal page requested
// This is mainly used by life cycle events so they don't fire onappearing
- if(page == null && Navigation.ModalStack.Count == 1)
+ if (page == null && Navigation.ModalStack.Count == 1)
{
IsPoppingModalStack = false;
}
- else if(Navigation.ModalStack.Count > 1 && Navigation.ModalStack[Navigation.ModalStack.Count - 2] == page)
+ else if (Navigation.ModalStack.Count > 1 && Navigation.ModalStack[Navigation.ModalStack.Count - 2] == page)
{
IsPoppingModalStack = false;
}
bool currentPage = (((IShellSectionController)this).PresentedPage) == page;
var stack = _navStack.ToList();
stack.Remove(page);
- var allow = ((IShellController)Shell).ProposeNavigation(
+ var allow = (!currentPage) ? true :
+ ((IShellController)Shell).ProposeNavigation(
ShellNavigationSource.Remove,
ShellItem,
this,
if (!allow)
return;
- if(currentPage)
+ if (currentPage)
PresentedPageDisappearing();
+
_navStack.Remove(page);
- if(currentPage)
+ if (currentPage)
PresentedPageAppearing();
RemovePage(page);
{
if (IsVisibleSection && this is IShellSectionController sectionController)
{
- if(_navStack.Count == 1)
+ if (_navStack.Count == 1)
CurrentItem?.SendAppearing();
var presentedPage = sectionController.PresentedPage;
if (presentedPage != null)
{
- if(presentedPage.Parent == null)
+ if (presentedPage.Parent == null)
{
presentedPage.ParentSet += OnPresentedPageParentSet;
protected override Task OnPushAsync(Page page, bool animated) => _owner.OnPushAsync(page, animated);
protected override void OnRemovePage(Page page) => _owner.OnRemovePage(page);
-
- protected override Task<Page> OnPopModal(bool animated)
- {
- if(ModalStack.Count == 1)
- {
- _owner.PresentedPageAppearing();
- }
-
- return base.OnPopModal(animated);
- }
-
- protected override Task OnPushModal(Page modal, bool animated)
- {
- if (ModalStack.Count == 0)
- {
- _owner.PresentedPageDisappearing();
- }
-
- return base.OnPushModal(modal, animated);
- }
}
}
}
\ No newline at end of file
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
+using System.IO;
using System.Linq;
namespace Xamarin.Forms
static readonly char[] _pathSeparators = { '/', '\\' };
const string _pathSeparator = "/";
- internal static Uri FormatUri(Uri path)
+ internal static Uri FormatUri(Uri path, Shell shell)
{
+ if (path.OriginalString.StartsWith("..") && shell?.CurrentState != null)
+ {
+ var result = Path.Combine(shell.CurrentState.FullLocation.OriginalString, path.OriginalString);
+ var returnValue = ConvertToStandardFormat("scheme", "host", null, new Uri(result, UriKind.Relative));
+ return new Uri(FormatUri(returnValue.PathAndQuery), UriKind.Relative);
+ }
+
if (path.IsAbsoluteUri)
+ {
return new Uri(FormatUri(path.OriginalString), UriKind.Absolute);
+ }
return new Uri(FormatUri(path.OriginalString), UriKind.Relative);
}
public static Uri ConvertToStandardFormat(Shell shell, Uri request)
{
- request = FormatUri(request);
+ request = FormatUri(request, shell);
+ return ConvertToStandardFormat(shell?.RouteScheme, shell?.RouteHost, shell?.Route, request);
+ }
+
+ public static Uri ConvertToStandardFormat(string routeScheme, string routeHost, string route, Uri request)
+ {
string pathAndQuery = null;
if (request.IsAbsoluteUri)
pathAndQuery = $"{request.Host}/{request.PathAndQuery}";
var segments = new List<string>(pathAndQuery.Split(_pathSeparators, StringSplitOptions.RemoveEmptyEntries));
+ if (segments[0] != routeHost)
+ segments.Insert(0, routeHost);
- if (segments[0] != shell.RouteHost)
- segments.Insert(0, shell.RouteHost);
-
- if (segments[1] != shell.Route)
- segments.Insert(1, shell.Route);
+ if (segments[1] != route)
+ segments.Insert(1, route);
var path = String.Join(_pathSeparator, segments.ToArray());
- string uri = $"{shell.RouteScheme}://{path}";
+ string uri = $"{routeScheme}://{path}";
return new Uri(uri);
}
internal static NavigationRequest GetNavigationRequest(Shell shell, Uri uri, bool enableRelativeShellRoutes = false)
{
- uri = FormatUri(uri);
+ uri = FormatUri(uri, shell);
// figure out the intent of the Uri
NavigationRequest.WhatToDoWithTheStack whatDoIDo = NavigationRequest.WhatToDoWithTheStack.PushToIt;
if (uri.IsAbsoluteUri)
internal static List<RouteRequestBuilder> GenerateRoutePaths(Shell shell, Uri request)
{
- request = FormatUri(request);
+ request = FormatUri(request, shell);
return GenerateRoutePaths(shell, request, request, false);
}
routeKeys[i] = FormatUri(routeKeys[i]);
}
- request = FormatUri(request);
- originalRequest = FormatUri(originalRequest);
+ request = FormatUri(request, shell);
+ originalRequest = FormatUri(originalRequest, shell);
List<RouteRequestBuilder> possibleRoutePaths = new List<RouteRequestBuilder>();
if (!request.IsAbsoluteUri)
var uri = ConvertToStandardFormat(shell, CreateUri(route));
if (uri.Equals(request))
{
- throw new Exception($"Global routes currently cannot be the only page on the stack, so absolute routing to global routes is not supported. For now, just navigate to: {originalRequest.OriginalString.Replace("//","")}");
+ throw new Exception($"Global routes currently cannot be the only page on the stack, so absolute routing to global routes is not supported. For now, just navigate to: {originalRequest.OriginalString.Replace("//", "")}");
//var builder = new RouteRequestBuilder(route, route, null, segments);
//return new List<RouteRequestBuilder> { builder };
}
depthStart = 0;
}
- if(relativeMatch && shell?.CurrentItem != null)
+ if (relativeMatch && shell?.CurrentItem != null)
{
// retrieve current location
var currentLocation = NodeLocation.Create(shell);
RouteRequestBuilder builder = null;
foreach (var segment in segments)
{
- if(routeKeys.Contains(segment))
+ if (routeKeys.Contains(segment))
{
if (builder == null)
builder = new RouteRequestBuilder(segment, segment, null, segments);
}
}
- if(builder != null && builder.IsFullMatch)
+ if (builder != null && builder.IsFullMatch)
return new List<RouteRequestBuilder> { builder };
}
else
if (segments[0] == route)
{
- yield return new GlobalRouteItem(key, key);
+ yield return new GlobalRouteItem(key, key);
}
}
}
switch (node)
{
case ShellUriHandler.GlobalRouteItem globalRoute:
- if(globalRoute.IsFinished)
+ if (globalRoute.IsFinished)
_globalRouteMatches.Add(globalRoute.SourceRoute);
break;
case Shell shell:
}
}
- internal override void OnAttached()
+ protected override void OnAttached()
{
base.OnAttached();
UpdateState();
internal VisualState VisualState { get; set; }
+ public bool IsAttached { get; private set; }
+
protected void SetActive(bool active)
{
IsActive = active;
VisualState?.VisualStateGroup?.UpdateStateTriggers();
}
- internal virtual void OnAttached()
+ protected virtual void OnAttached()
{
}
- internal virtual void OnDetached()
+ protected virtual void OnDetached()
{
}
+
+ internal void SendAttached()
+ {
+ if (IsAttached)
+ return;
+ OnAttached();
+ IsAttached = true;
+ }
+
+ internal void SendDetached()
+ {
+ if (!IsAttached)
+ return;
+ OnDetached();
+ IsAttached = false;
+ }
}
}
\ No newline at end of file
}
void Apply(Element styleable)
- {
- ApplyCore(styleable);
- foreach (var child in styleable.AllChildren)
- ((IStyle)this).Apply(child);
- }
-
- void ApplyCore(Element styleable)
{
if (!(styleable is VisualElement visualStylable))
return;
static void OnTransformChanged(BindableObject bindable, object oldValue, object newValue)
{
- if ((string)newValue == "none") {
+ if ((string)newValue == "none")
+ {
bindable.ClearValue(TranslationXProperty);
bindable.ClearValue(TranslationYProperty);
bindable.ClearValue(RotationProperty);
return;
}
var transforms = ((string)newValue).Split(' ');
- foreach (var transform in transforms) {
+ foreach (var transform in transforms)
+ {
if (string.IsNullOrEmpty(transform) || transform.IndexOf('(') < 0 || transform.IndexOf(')') < 0)
throw new FormatException("Format for transform is 'none | transform(value) [transform(value) ]*'");
var transformName = transform.Substring(0, transform.IndexOf('('));
bindable.SetValue(TranslationXProperty, translationX);
else if (transformName.StartsWith("translateY", StringComparison.OrdinalIgnoreCase) && double.TryParse(value, out translationY))
bindable.SetValue(TranslationYProperty, translationY);
- else if (transformName.StartsWith("translate", StringComparison.OrdinalIgnoreCase)) {
+ else if (transformName.StartsWith("translate", StringComparison.OrdinalIgnoreCase))
+ {
var translate = value.Split(',');
- if (double.TryParse(translate[0], out translationX) && double.TryParse(translate[1], out translationY)) {
+ if (double.TryParse(translate[0], out translationX) && double.TryParse(translate[1], out translationY))
+ {
bindable.SetValue(TranslationXProperty, translationX);
bindable.SetValue(TranslationYProperty, translationY);
}
bindable.SetValue(ScaleXProperty, scaleX);
else if (transformName.StartsWith("scaleY", StringComparison.OrdinalIgnoreCase) && double.TryParse(value, out scaleY))
bindable.SetValue(ScaleYProperty, scaleY);
- else if (transformName.StartsWith("scale", StringComparison.OrdinalIgnoreCase)) {
+ else if (transformName.StartsWith("scale", StringComparison.OrdinalIgnoreCase))
+ {
var scale = value.Split(',');
- if (double.TryParse(scale[0], out scaleX) && double.TryParse(scale[1], out scaleY)) {
+ if (double.TryParse(scale[0], out scaleX) && double.TryParse(scale[1], out scaleY))
+ {
bindable.SetValue(ScaleXProperty, scaleX);
bindable.SetValue(ScaleYProperty, scaleY);
}
internal VisualElement()
{
+ if (Device.Flags?.IndexOf(ExperimentalFlags.AppThemeExperimental) > 0)
+ Application.Current.RequestedThemeChanged += (s, a) => OnRequestedThemeChanged(a.RequestedTheme);
+ }
+ protected virtual void OnRequestedThemeChanged(OSAppTheme newValue)
+ {
+ ExperimentalFlags.VerifyFlagEnabled(nameof(VisualElement), ExperimentalFlags.AppThemeExperimental, nameof(OnRequestedThemeChanged));
}
public double AnchorX
foreach (var state in group.States)
foreach (var stateTrigger in state.StateTriggers)
{
- if(attach)
- stateTrigger.OnAttached();
+ if (attach)
+ stateTrigger.SendAttached();
else
- stateTrigger.OnDetached();
+ stateTrigger.SendDetached();
}
}
InvalidateMeasureInternal(InvalidationTrigger.Undefined);
}
- internal override void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
- {
- if (e == ResourcesChangedEventArgs.StyleSheets)
- ApplyStyleSheets();
- else
- base.OnResourcesChanged(sender, e);
- }
-
internal override void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
{
if (values == null)
namespace Xamarin.Forms
{
- public partial class VisualElement : IStyleSelectable, IStylable
+ public partial class VisualElement : IStylable
{
- IList<string> IStyleSelectable.Classes
- => StyleClass;
-
BindableProperty IStylable.GetProperty(string key, bool inheriting)
{
if (!Internals.Registrar.StyleProperties.TryGetValue(key, out var attrList))
return (styleAttribute.BindableProperty = bpField.GetValue(null) as BindableProperty);
}
-
- void ApplyStyleSheets()
- {
- foreach (var styleSheet in this.GetStyleSheets())
- ((IStyle)styleSheet).Apply(this);
- }
}
}
\ No newline at end of file
Assemblies = assemblies;
}
- public void UseStaticRegistrar(StaticRegistrarStrategy strategy, Dictionary<Type, Func<IRegisterable>> customHandlers=null, bool disableCss=false)
+ public void UseStaticRegistrar(StaticRegistrarStrategy strategy, Dictionary<Type, Func<IRegisterable>> customHandlers = null, bool disableCss = false)
{
StaticRegistarStrategy = strategy;
CustomHandlers = customHandlers;
}
static IReadOnlyList<string> s_flags;
- public static IReadOnlyList<string> Flags => s_flags ?? (s_flags = new List<string>().AsReadOnly());
+ public static IReadOnlyList<string> Flags => s_flags ?? (s_flags = new string[0]);
public static void SetFlags(params string[] flags)
{
throw new InvalidOperationException($"{nameof(SetFlags)} must be called before {nameof(Init)}");
}
- s_flags = flags.ToList().AsReadOnly();
+ s_flags = (string[])flags.Clone();
+ if (s_flags.Contains("Profile"))
+ Profile.Enable();
}
public static void SetTitleBarVisibility(TizenTitleBarVisibility visibility)
Device.Info = new Forms.TizenDeviceInfo();
Device.SetFlags(s_flags);
}
- else if(Device.info != null)
+ else if (Device.info != null)
{
var info = ((TizenDeviceInfo)Device.info).RefreshByDIP();
((TizenDeviceInfo)Device.info).Dispose();
// static registrar
if (options.StaticRegistarStrategy != StaticRegistrarStrategy.None)
{
- s_staticRegistrarStrategy = options.StaticRegistarStrategy;
- StaticRegistrar.RegisterHandlers(options.CustomHandlers);
+ s_staticRegistrarStrategy = options.StaticRegistarStrategy;
+ StaticRegistrar.RegisterHandlers(options.CustomHandlers);
- if (options.StaticRegistarStrategy == StaticRegistrarStrategy.All)
+ if (options.StaticRegistarStrategy == StaticRegistrarStrategy.All)
+ {
+ Registrar.RegisterAll(new Type[]
{
- Registrar.RegisterAll(new Type[]
- {
typeof(ExportRendererAttribute),
typeof(ExportImageSourceHandlerAttribute),
typeof(ExportCellAttribute),
typeof(ExportHandlerAttribute),
typeof(ExportFontAttribute)
- });
- }
+ });
+ }
}
else
{
{
eWindow = (EWindow)methodInfo.Invoke(null, new object[] { "FormsWindow" });
eLayout = (ELayout)eWindow.GetType().GetProperty("BaseLayout")?.GetValue(eWindow);
- eCircleSurface = (CircleSurface)eWindow.GetType().GetProperty("BaseCircleSurface")?.GetValue(eWindow);
+ eCircleSurface = (CircleSurface)eWindow.GetType().GetProperty("BaseCircleSurface")?.GetValue(eWindow);
}
PreloadedWindow preloadedWindow = null;
var locale = TSystemSetting.LocaleLanguage;
TSystemSetting.LocaleLanguageChanged += DummyHandler;
TSystemSetting.LocaleLanguageChanged -= DummyHandler;
- void DummyHandler(object sender, System.EventArgs e){ }
+ void DummyHandler(object sender, System.EventArgs e) { }
var types = new[]
{
--- /dev/null
+using ElmSharp.Wearable;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public interface IRotaryInteraction
+ {
+ IRotaryActionWidget RotaryWidget { get; }
+ }
+}
\ No newline at end of file
using EScroller = ElmSharp.Scroller;
using ESize = ElmSharp.Size;
using EPoint = ElmSharp.Point;
+using ElmSharp.Wearable;
namespace Xamarin.Forms.Platform.Tizen.Native
{
- public class CollectionView : EBox, ICollectionViewController
+ public class CollectionView : EBox, ICollectionViewController, IRotaryInteraction
{
RecyclerPool _pool = new RecyclerPool();
ICollectionViewLayoutManager _layoutManager;
SnapPointsType _snapPoints;
ESize _itemSize = new ESize(-1, -1);
+ public event EventHandler<ItemsViewScrolledEventArgs> Scrolled;
+
public CollectionView(EvasObject parent) : base(parent)
{
SetLayoutCallback(OnLayout);
Scroller.SetContent(_innerLayout);
}
+ public IRotaryActionWidget RotaryWidget { get => Scroller as IRotaryActionWidget; }
+
+
public CollectionViewSelectionMode SelectionMode
{
get => _selectionMode;
protected virtual EScroller CreateScroller(EvasObject parent)
{
- return new EScroller(parent);
+ if (Device.Idiom == TargetIdiom.Watch)
+ {
+ return new CircleScroller(parent, Forms.CircleSurface);
+ }
+ else
+ {
+ return new EScroller(parent);
+ }
}
void UpdateSelectedItemIndex()
_innerLayout.MinimumHeight = size.Height;
}
+ int _previousHorizontalOffset = 0;
+ int _previousVerticalOffset = 0;
void OnScrolled(object sender, EventArgs e)
{
- _layoutManager.LayoutItems(Scroller.CurrentRegion);
+ _layoutManager.LayoutItems(ViewPort);
+ var args = new ItemsViewScrolledEventArgs();
+ args.FirstVisibleItemIndex = _layoutManager.GetVisibleItemIndex(ViewPort.X, ViewPort.Y);
+ args.CenterItemIndex = _layoutManager.GetVisibleItemIndex(ViewPort.X + (ViewPort.Width / 2), ViewPort.Y + (ViewPort.Height / 2));
+ args.LastVisibleItemIndex = _layoutManager.GetVisibleItemIndex(ViewPort.X + ViewPort.Width, ViewPort.Y + ViewPort.Height);
+ args.HorizontalOffset = ViewPort.X;
+ args.HorizontalDelta = ViewPort.X - _previousHorizontalOffset;
+ args.VerticalOffset = ViewPort.Y;
+ args.VerticalDelta = ViewPort.Y - _previousVerticalOffset;
+
+ Scrolled?.Invoke(this, args);
+
+ _previousHorizontalOffset = ViewPort.X;
+ _previousVerticalOffset = ViewPort.Y;
}
void UpdateSnapPointsType(SnapPointsType snapPoints)
if (IsHorizontal)
{
- _scrollCanvasSize = new ESize(totalItemSize , _allocatedSize.Height);
+ _scrollCanvasSize = new ESize(totalItemSize, _allocatedSize.Height);
}
else
{
}
}
+ public int GetVisibleItemIndex(int x, int y)
+ {
+ int index = 0;
+ if (x < 0 || y < 0)
+ return index;
+ if (_scrollCanvasSize.Width < x || _scrollCanvasSize.Height < y)
+ return CollectionView.Count - 1;
+
+ int first = (IsHorizontal ? x : y) / BaseItemSize;
+ if (_hasUnevenRows)
+ first = _accumulatedItemSizes.FindIndex(current => (IsHorizontal ? x : y) <= current);
+
+ int second = (IsHorizontal ? y : x) / ColumnSize;
+ if (second == Span)
+ second -= 1;
+
+ index = (first * Span) + second;
+
+ if (index < CollectionView.Count)
+ return index;
+ return CollectionView.Count - 1;
+ }
+
void InitializeMeasureCache()
{
_baseItemSize = 0;
void Reset();
void ItemMeasureInvalidated(int index);
+
+ int GetVisibleItemIndex(int x, int y);
}
}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ public class IndicatorView : Index
+ {
+ List<IndexItem> _list = new List<IndexItem>();
+
+ public IndicatorView(EvasObject parent) : base(parent)
+ {
+ AutoHide = false;
+ IsHorizontal = true;
+ Style = "pagecontrol";
+ if (Device.Idiom == TargetIdiom.Watch)
+ Style = "circle";
+ }
+
+ public event EventHandler<SelectedPositionChangedEventArgs> SelectedPosition;
+
+ public void UpdateSelectedIndex(int index)
+ {
+ if (index > -1 && index < _list.Count)
+ {
+ _list[index].Select(true);
+ }
+ }
+
+ public void AppendIndex(int count = 1)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ var item = Append(null);
+ item.Selected += OnSelected;
+ _list.Add(item);
+ }
+ if (Device.Idiom == TargetIdiom.Watch)
+ ApplyStyle();
+ }
+
+ public void ClearIndex()
+ {
+ foreach (var item in _list)
+ {
+ item.Selected -= OnSelected;
+ }
+ _list.Clear();
+ Clear();
+ }
+
+ void ApplyStyle()
+ {
+ foreach (var item in _list)
+ {
+ int center = 10;
+ int start = center - (_list.Count / 2);
+ int index = _list.IndexOf(item);
+ int position = start + index;
+ if (_list.Count % 2 == 0)
+ {
+ string itemStyle = "item/even_" + position;
+ item.Style = itemStyle;
+ }
+ else
+ {
+ string itemStyle = "item/odd_" + position;
+ item.Style = itemStyle;
+ }
+ }
+ }
+
+ void OnSelected(object sender, EventArgs e)
+ {
+ var index = _list.IndexOf((IndexItem)sender);
+ SelectedPosition?.Invoke(this, new SelectedPositionChangedEventArgs(index));
+ UpdateSelectedIndex(index);
+ }
+ }
+}
}
}
+ public int GetVisibleItemIndex(int x, int y)
+ {
+ int coordinate = IsHorizontal ? x : y;
+ int canvasSize = IsHorizontal ? _scrollCanvasSize.Width : _scrollCanvasSize.Height;
+
+ if (coordinate < 0)
+ return 0;
+ if (canvasSize < coordinate)
+ return CollectionView.Count - 1;
+
+ if (!_hasUnevenRows)
+ return coordinate / BaseItemSize;
+ else
+ return _accumulatedItemSizes.FindIndex(current => coordinate <= current);
+ }
+
void InitializeMeasureCache()
{
_baseItemSize = 0;
using System.ComponentModel;
using Xamarin.Forms.Internals;
using ElmSharp;
+using ElmSharp.Wearable;
+using EScroller = ElmSharp.Scroller;
+using EColor = ElmSharp.Color;
namespace Xamarin.Forms.Platform.Tizen.Native
{
internal TemplatedItemsList<ItemsView<Cell>, Cell> ListOfSubItems;
}
- class ScrollerExtension : Scroller
+ class ScrollerExtension : EScroller
{
public ScrollerExtension(GenList scrollableLayout) : base(scrollableLayout)
{
/// </summary>
GenItemClass _headerFooterItemClass = null;
- ScrollerExtension _scrollerExtension;
+ /// <summary>
+ /// The object to handle scroller properties.
+ /// </summary>
+ protected virtual EScroller Scroller { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance has grouping enabled.
/// <summary>
/// Gets the current region in the content object that is visible through the Scroller.
/// </summary>
- public Rect CurrentRegion => _scrollerExtension.CurrentRegion;
+ public virtual Rect CurrentRegion => Scroller?.CurrentRegion ?? new Rect();
+
+ /// <summary>
+ /// Sets or gets the value of VerticalScrollBarVisibility
+ /// </summary>
+ public virtual ScrollBarVisiblePolicy VerticalScrollBarVisibility
+ {
+ get
+ {
+ return Scroller?.VerticalScrollBarVisiblePolicy ?? ScrollBarVisiblePolicy.Auto;
+ }
+ set
+ {
+ if (Scroller != null)
+ Scroller.VerticalScrollBarVisiblePolicy = value;
+ }
+ }
+
+ /// <summary>
+ /// Sets or gets the value of HorizontalScrollBarVisibility
+ /// </summary>
+ public virtual ScrollBarVisiblePolicy HorizontalScrollBarVisibility
+ {
+ get
+ {
+ return Scroller?.HorizontalScrollBarVisiblePolicy ?? ScrollBarVisiblePolicy.Auto;
+ }
+ set
+ {
+ if (Scroller != null)
+ Scroller.HorizontalScrollBarVisiblePolicy = value;
+ }
+ }
/// <summary>
/// Occurs when the ListView has scrolled.
public ListView(EvasObject parent)
: base(parent)
{
- _scrollerExtension = new ScrollerExtension(this);
- new SmartEvent(this, RealHandle, "scroll").On += (s,e) =>
- {
- Scrolled?.Invoke(this, null);
- };
+ Scroller = new ScrollerExtension(this);
+ Scroller.Scrolled += OnScrolled;
}
- protected ListView() : base()
+ protected ListView()
{
}
/// <param name="beforeCell">Cell before which new items will be placed.
/// Null value may also be passed as this parameter, which results in appending new items to the end.
/// </param>
- public void AddSource(IEnumerable source, Cell beforeCell = null)
+ public virtual void AddSource(IEnumerable source, Cell beforeCell = null)
{
foreach (var data in source)
{
/// Sets the header.
/// </summary>
/// <param name="header">Header of the list.</param>
- public void SetHeader(VisualElement header)
+ public virtual void SetHeader(VisualElement header)
{
if (header == null)
{
/// Sets the footer.
/// </summary>
/// <param name="footer">Footer of the list.</param>
- public void SetFooter(VisualElement footer)
+ public virtual void SetFooter(VisualElement footer)
{
if (footer == null)
{
return _footerElement;
}
+ protected virtual void OnScrolled(object sender, EventArgs e)
+ {
+ Scrolled?.Invoke(this, EventArgs.Empty);
+ }
+
/// <summary>
/// Handles the header deleted event.
/// </summary>
{
}
+ protected Scroller()
+ {
+ }
+
protected override void OnRealized()
{
base.OnRealized();
--- /dev/null
+using ElmSharp.Wearable;
+
+namespace Xamarin.Forms.Platform.Tizen.Native.Watch
+{
+ public class FormsMoreOptionItem : MoreOptionItem
+ {
+ public ToolbarItem ToolbarItem { get; set; }
+ }
+}
--- /dev/null
+using System;
+using System.Collections;
+using ElmSharp;
+using ElmSharp.Wearable;
+
+namespace Xamarin.Forms.Platform.Tizen.Native.Watch
+{
+ public class WatchListView : Native.ListView, IRotaryActionWidget, IRotaryInteraction
+ {
+ CircleGenList _circleGenList;
+ CircleSurface _surface;
+
+ GenItem _headerPadding;
+ GenItem _footerPadding;
+
+ public IntPtr CircleHandle => _circleGenList.CircleHandle;
+
+ public CircleSurface CircleSurface => _surface;
+
+ public IRotaryActionWidget RotaryWidget { get => this; }
+
+ public override ScrollBarVisiblePolicy VerticalScrollBarVisibility
+ {
+ get => _circleGenList.VerticalScrollBarVisiblePolicy;
+ set => _circleGenList.VerticalScrollBarVisiblePolicy = value;
+ }
+
+ public WatchListView(EvasObject parent, CircleSurface surface)
+ {
+ _surface = surface;
+ Realize(parent);
+
+ Scroller = new CircleScrollerExtension(this);
+ Scroller.Scrolled += OnScrolled;
+ }
+
+ public override void AddSource(IEnumerable source, Cell beforeCell = null)
+ {
+ base.AddSource(source, beforeCell);
+ AddHeaderPadding();
+ AddFooterPadding();
+ }
+
+ public override void SetHeader(VisualElement header)
+ {
+ if (_headerPadding != null)
+ RemovePaddingItem(_headerPadding);
+
+ base.SetHeader(header);
+
+ if (!HasHeader())
+ AddHeaderPadding();
+ }
+
+ public override void SetFooter(VisualElement footer)
+ {
+ if(_footerPadding != null)
+ RemovePaddingItem(_footerPadding);
+
+ base.SetFooter(footer);
+
+ if (!HasFooter())
+ AddFooterPadding();
+ }
+
+ protected override IntPtr CreateHandle(EvasObject parent)
+ {
+ _circleGenList = new CircleGenList(parent, _surface);
+ RealHandle = _circleGenList.RealHandle;
+ return _circleGenList.Handle;
+ }
+
+ void RemovePaddingItem(GenItem item)
+ {
+ item?.Delete();
+ item = null;
+ }
+
+ void AddHeaderPadding()
+ {
+ var cls = new WatchListView.PaddingItemClass();
+
+ if (FirstItem == null)
+ {
+ _headerPadding = Append(cls, null);
+ }
+ else
+ {
+ _headerPadding = InsertBefore(cls, null, FirstItem);
+ }
+ }
+
+ void AddFooterPadding()
+ {
+ var cls = new WatchListView.PaddingItemClass();
+
+ if (Count > 1)
+ {
+ _footerPadding = Append(cls, null);
+ }
+ }
+
+ class PaddingItemClass : GenItemClass
+ {
+ public PaddingItemClass() : base("padding")
+ {
+ }
+ }
+
+ class CircleScrollerExtension : CircleScroller
+ {
+ WatchListView _list;
+
+ public override IntPtr CircleHandle => _list.CircleHandle;
+
+ public CircleScrollerExtension(WatchListView parent) : base(parent, parent.CircleSurface)
+ {
+ _list = parent;
+ }
+
+ protected override IntPtr CreateHandle(EvasObject parent)
+ {
+ return parent.RealHandle;
+ }
+ }
+ }
+}
--- /dev/null
+using System;
+using ElmSharp;
+using ElmSharp.Wearable;
+
+namespace Xamarin.Forms.Platform.Tizen.Native.Watch
+{
+ public class WatchScroller : Native.Scroller, IRotaryActionWidget, IRotaryInteraction
+ {
+ CircleScroller _circleScroller;
+ CircleSurface _surface;
+
+ public IntPtr CircleHandle => _circleScroller.CircleHandle;
+
+ public CircleSurface CircleSurface => _surface;
+
+ public IRotaryActionWidget RotaryWidget { get => this; }
+
+ public WatchScroller(EvasObject parent, CircleSurface surface)
+ {
+ _surface = surface;
+ Realize(parent);
+
+ VerticalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+ HorizontalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible;
+ }
+
+ public override ScrollBarVisiblePolicy VerticalScrollBarVisiblePolicy
+ {
+ get => _circleScroller.VerticalScrollBarVisiblePolicy;
+ set => _circleScroller.VerticalScrollBarVisiblePolicy = value;
+ }
+
+ public override ScrollBarVisiblePolicy HorizontalScrollBarVisiblePolicy
+ {
+ get => _circleScroller.HorizontalScrollBarVisiblePolicy;
+ set => _circleScroller.HorizontalScrollBarVisiblePolicy = value;
+ }
+
+ protected override IntPtr CreateHandle(EvasObject parent)
+ {
+ _circleScroller = new CircleScroller(parent, _surface);
+ RealHandle = _circleScroller.RealHandle;
+ return _circleScroller.Handle;
+ }
+ }
+}
[assembly: ExportRenderer(typeof(ListView), typeof(ListViewRenderer))]
[assembly: ExportRenderer(typeof(BoxView), typeof(BoxViewRenderer))]
[assembly: ExportRenderer(typeof(ActivityIndicator), typeof(ActivityIndicatorRenderer))]
+[assembly: ExportRenderer(typeof(IndicatorView), typeof(IndicatorViewRenderer))]
[assembly: ExportRenderer(typeof(SearchBar), typeof(SearchBarRenderer))]
[assembly: ExportRenderer(typeof(Entry), typeof(EntryRenderer))]
[assembly: ExportRenderer(typeof(Editor), typeof(EditorRenderer))]
[assembly: ExportRenderer(typeof(SwipeView), typeof(SwipeViewRenderer))]
[assembly: ExportRenderer(typeof(RefreshView), typeof(RefreshViewRenderer))]
[assembly: ExportRenderer(typeof(MediaElement), typeof(MediaElementRenderer))]
+[assembly: ExportRenderer(typeof(RadioButton), typeof(RadioButtonRenderer))]
[assembly: ExportImageSourceHandler(typeof(FileImageSource), typeof(FileImageSourceHandler))]
[assembly: ExportImageSourceHandler(typeof(StreamImageSource), typeof(StreamImageSourceHandler))]
-namespace Xamarin.Forms.Platform.Tizen
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
{
public class CarouselViewRenderer : ItemsViewRenderer<CarouselView, Native.CarouselView>
{
RegisterPropertyHandler(CarouselView.ItemsLayoutProperty, UpdateItemsLayout);
RegisterPropertyHandler(CarouselView.IsBounceEnabledProperty, UpdateIsBounceEnabled);
RegisterPropertyHandler(CarouselView.IsSwipeEnabledProperty, UpdateIsSwipeEnabled);
- RegisterPropertyHandler(CarouselView.PositionProperty, UpdatePosition);
+ RegisterPropertyHandler(CarouselView.PositionProperty, UpdatePositionFromElement);
+ RegisterPropertyHandler(CarouselView.CurrentItemProperty, UpdateCurrentItemFromElement);
}
protected override Native.CarouselView CreateNativeControl(ElmSharp.EvasObject parent)
return Element.ItemsLayout;
}
+ ElmSharp.SmartEvent _animationStart;
+ ElmSharp.SmartEvent _animationStop;
protected override void OnElementChanged(ElementChangedEventArgs<CarouselView> e)
{
base.OnElementChanged(e);
- Control.Scroll.Scrolled += OnScrollStart;
- Control.Scroll.PageScrolled += OnScrollStop;
- }
-
- protected override void OnItemSelectedFromUI(object sender, SelectedItemChangedEventArgs e)
- {
- Element.Position = e.SelectedItemIndex;
- Element.CurrentItem = e.SelectedItem;
+ if (e.NewElement != null)
+ {
+ Control.Scrolled += OnScrolled;
+ Control.Scroll.DragStart += OnDragStart;
+ Control.Scroll.DragStop += OnDragStop;
+ _animationStart = new ElmSharp.SmartEvent(Control.Scroll, Control.Scroll.RealHandle, "scroll,anim,start");
+ _animationStart.On += OnScrollStart;
+ _animationStop = new ElmSharp.SmartEvent(Control.Scroll, Control.Scroll.RealHandle, "scroll,anim,stop");
+ _animationStop.On += OnScrollStop;
+ }
+ UpdatePositionFromElement(false);
+ UpdateCurrentItemFromElement(false);
}
protected override void Dispose(bool disposing)
{
if (Element != null)
{
- Control.Scroll.Scrolled -= OnScrollStart;
- Control.Scroll.PageScrolled -= OnScrollStop;
+ Control.Scrolled -= OnScrolled;
+ Control.Scroll.DragStart -= OnDragStart;
+ Control.Scroll.DragStop -= OnDragStop;
+ _animationStart.On -= OnScrollStart;
+ _animationStop.On -= OnScrollStop;
}
}
base.Dispose(disposing);
}
+ void OnDragStart(object sender, System.EventArgs e)
+ {
+ Element.SetIsDragging(true);
+ Element.IsScrolling = true;
+ }
+
+ void OnDragStop(object sender, System.EventArgs e)
+ {
+ Element.SetIsDragging(false);
+ Element.IsScrolling = false;
+ }
+
void OnScrollStart(object sender, System.EventArgs e)
{
- if (!Element.IsDragging)
- {
- Element.SetIsDragging(true);
- }
+ Element.IsScrolling = true;
}
void OnScrollStop(object sender, System.EventArgs e)
{
+ var scrollerIndex = Control.LayoutManager.IsHorizontal ? Control.Scroll.HorizontalPageIndex : Control.Scroll.VerticalPageIndex;
+ Element.SetValueFromRenderer(CarouselView.PositionProperty, scrollerIndex);
+ Element.SetValueFromRenderer(CarouselView.CurrentItemProperty, Control.Adaptor[scrollerIndex]);
+ Control.Adaptor.RequestItemSelected(Control.Adaptor[scrollerIndex]);
+ Element.IsScrolling = false;
+ }
+
+ void OnScrolled(object sender, ItemsViewScrolledEventArgs e)
+ {
+ if (!Element.IsScrolling)
+ Element.IsScrolling = true;
+
if (Element.IsDragging)
- {
- Element.SetIsDragging(false);
- }
+ if (Element.Position != e.CenterItemIndex)
+ Element.SetValueFromRenderer(CarouselView.PositionProperty, e.CenterItemIndex);
}
- void UpdatePosition(bool initialize)
+ void UpdateCurrentItemFromElement(bool isInitializing)
{
- if (initialize)
- {
+ if (isInitializing)
return;
- }
- if (Element.Position > -1 && Element.Position < Control.Adaptor.Count)
+
+ if (Element.CurrentItem != null)
+ ScrollTo(Control.Adaptor.GetItemIndex(Element.CurrentItem));
+ }
+
+ void UpdatePositionFromElement(bool isInitializing)
+ {
+ if (isInitializing)
+ return;
+
+ ScrollTo(Element.Position);
+ }
+
+ void ScrollTo(int position)
+ {
+ if (Element.IsScrolling)
+ return;
+
+ if (position > -1 && position < Control.Adaptor.Count)
{
- Control.Adaptor.RequestItemSelected(Element.Position);
- Element.CurrentItem = Control.Adaptor[Element.Position];
+ var scrollerIndex = Control.LayoutManager.IsHorizontal ? Control.Scroll.HorizontalPageIndex : Control.Scroll.VerticalPageIndex;
+ if (position != scrollerIndex)
+ Control.ScrollTo(position);
}
}
{
IsSingleLine = false,
};
- entry.Focused += OnFocused;
- entry.Unfocused += OnUnfocused;
+ entry.Focused += OnEntryFocused;
+ entry.Unfocused += OnEntryUnfocused;
entry.TextChanged += OnTextChanged;
entry.Unfocused += OnCompleted;
entry.PrependMarkUpFilter(MaxLengthFilter);
{
Control.TextChanged -= OnTextChanged;
Control.BackButtonPressed -= OnCompleted;
- Control.Unfocused -= OnUnfocused;
- Control.Focused -= OnFocused;
+ Control.Unfocused -= OnEntryUnfocused;
+ Control.Focused -= OnEntryFocused;
}
}
base.Dispose(disposing);
bool _isSendComplate = false;
- void OnFocused(object sender, EventArgs e)
+ void OnEntryFocused(object sender, EventArgs e)
{
// BackButtonPressed is only passed to the object that is at the highest Z-Order, and it does not propagate to lower objects.
// If you want to make Editor input completed by using BackButtonPressed, you should subscribe BackButtonPressed event only when Editor gets focused.
_isSendComplate = false;
}
- void OnUnfocused(object sender, EventArgs e)
+ void OnEntryUnfocused(object sender, EventArgs e)
{
// BackButtonPressed is only passed to the object that is at the highest Z-Order, and it does not propagate to lower objects.
// When the object is unfocesed BackButtonPressed event has to be released to stop using it.
Control.BackButtonPressed -= OnCompleted;
- if(!_isSendComplate)
+ if (!_isSendComplate)
Element.SendCompleted();
}
--- /dev/null
+using System.Collections;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class IndicatorViewRenderer : ViewRenderer<IndicatorView, Native.IndicatorView>
+ {
+ public IndicatorViewRenderer()
+ {
+ RegisterPropertyHandler(IndicatorView.CountProperty, UpdateItemsSource);
+ RegisterPropertyHandler(IndicatorView.PositionProperty, UpdatePosition);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<IndicatorView> e)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new Native.IndicatorView(Forms.NativeParent));
+ }
+ if (e.NewElement != null)
+ {
+ Control.SelectedPosition += OnSelectedPosition;
+ }
+ if (e.OldElement != null)
+ {
+ Control.SelectedPosition -= OnSelectedPosition;
+ }
+ base.OnElementChanged(e);
+ }
+
+ void OnSelectedPosition(object sender, SelectedPositionChangedEventArgs e)
+ {
+ Element.SetValueFromRenderer(IndicatorView.PositionProperty, (int)e.SelectedPosition);
+ }
+
+ void UpdateItemsSource()
+ {
+ Control.ClearIndex();
+ int count = 0;
+ if (Element.ItemsSource is ICollection collection)
+ {
+ count = collection.Count;
+ }
+ else
+ {
+ var enumerator = Element.ItemsSource.GetEnumerator();
+ while (enumerator?.MoveNext() ?? false)
+ {
+ count++;
+ }
+ }
+ Control.AppendIndex(count);
+ }
+
+ void UpdatePosition()
+ {
+ Control.UpdateSelectedIndex(Element.Position);
+ }
+ }
+}
-using System.Collections.Specialized;
+using System;
+using System.Collections.Specialized;
using System.Linq;
using Xamarin.Forms.Platform.Tizen.Native;
if (Control == null)
{
SetNativeControl(CreateNativeControl(Forms.NativeParent));
+ Control.Scrolled += OnScrolled;
}
if (e.NewElement != null)
{
{
Element.ScrollToRequested -= OnScrollToRequest;
ItemsLayout.PropertyChanged -= OnLayoutPropertyChanged;
+ Control.Scrolled -= OnScrolled;
}
if (_observableSource != null)
{
{
}
+ void OnScrolled(object sender, ItemsViewScrolledEventArgs e)
+ {
+ Element.SendScrolled(e);
+ }
+
void OnScrollToRequest(object sender, ScrollToRequestEventArgs e)
{
if (e.Mode == ScrollToMode.Position)
RegisterPropertyHandler("HeaderElement", UpdateHeader);
RegisterPropertyHandler("FooterElement", UpdateFooter);
RegisterPropertyHandler(ListView.SelectionModeProperty, UpdateSelectionMode);
+ RegisterPropertyHandler(ListView.VerticalScrollBarVisibilityProperty, UpdateVerticalScrollBarVisibility);
+ RegisterPropertyHandler(ListView.HorizontalScrollBarVisibilityProperty, UpdateHorizontalScrollBarVisibility);
}
/// <summary>
{
if (Control == null)
{
- SetNativeControl(new Native.ListView(Forms.NativeParent));
+ SetNativeControl(CreateNativeControl());
Control.Scrolled += OnScrolled;
Control.ItemSelected += OnListViewItemSelected;
base.OnElementChanged(e);
}
+ protected virtual Native.ListView CreateNativeControl()
+ {
+ if (Device.Idiom == TargetIdiom.Watch)
+ {
+ return new Native.Watch.WatchListView(Forms.NativeParent, Forms.CircleSurface);
+ }
+ else
+ {
+ return new Native.ListView(Forms.NativeParent);
+ }
+ }
+
/// <summary>
/// Handles the disposing of an existing renderer instance. Results in event handlers
/// being detached and a Dispose() method from base class (VisualElementRenderer) being invoked.
Control.IsHighlight = true;
}
}
+
+ void UpdateVerticalScrollBarVisibility()
+ {
+ Control.VerticalScrollBarVisibility = Element.VerticalScrollBarVisibility.ToNative();
+ }
+
+ void UpdateHorizontalScrollBarVisibility()
+ {
+ Control.HorizontalScrollBarVisibility = Element.HorizontalScrollBarVisibility.ToNative();
+ }
}
}
\ No newline at end of file
using System;
+using System.Collections.Specialized;
+using ElmSharp.Wearable;
+using Xamarin.Forms.Platform.Tizen.Native.Watch;
using EColor = ElmSharp.Color;
namespace Xamarin.Forms.Platform.Tizen
/// Native control which holds the contents.
/// </summary>
Native.Page _page;
+ Lazy<MoreOption> _moreOption;
/// <summary>
/// Default constructor.
base.OnElementChanged(e);
}
+ protected override void OnElementReady()
+ {
+ if (Device.Idiom == TargetIdiom.Watch)
+ {
+ _moreOption = new Lazy<MoreOption>(CreateMoreOption);
+ if (Element.ToolbarItems is INotifyCollectionChanged items)
+ {
+ items.CollectionChanged += OnToolbarCollectionChanged;
+ }
+ if (Element.ToolbarItems.Count > 0)
+ {
+ UpdateToolbarItems(true);
+ }
+ }
+ }
+
protected override void Dispose(bool disposing)
{
if (disposing)
{
_page.LayoutUpdated -= OnLayoutUpdated;
}
+
+ if (Device.Idiom == TargetIdiom.Watch)
+ {
+ if (Element.ToolbarItems is INotifyCollectionChanged items)
+ {
+ items.CollectionChanged -= OnToolbarCollectionChanged;
+ }
+
+ if (_moreOption.IsValueCreated)
+ {
+ _moreOption.Value.Clicked -= OnMoreOptionItemClicked;
+ _moreOption.Value.Items.Clear();
+ _moreOption.Value.Unrealize();
+ }
+ }
}
base.Dispose(disposing);
}
// empty on purpose
}
- void UpdateBackgroundImage(bool initiaize)
+ protected virtual FormsMoreOptionItem CreateMoreOptionItem(ToolbarItem item)
{
- if (initiaize && Element.BackgroundImageSource.IsNullOrEmpty())
+ var moreOptionItem = new FormsMoreOptionItem
+ {
+ MainText = item.Text,
+ ToolbarItem = item
+ };
+ var icon = item.IconImageSource as FileImageSource;
+ if (icon != null)
+ {
+ var img = new ElmSharp.Image(_moreOption.Value);
+ img.Load(ResourcePath.GetPath(icon));
+ moreOptionItem.Icon = img;
+ }
+ return moreOptionItem;
+ }
+
+ void UpdateBackgroundImage(bool initialize)
+ {
+ if (initialize && Element.BackgroundImageSource.IsNullOrEmpty())
return;
// TODO: investigate if we can use the other image source types: stream, font, uri
void OnLayoutUpdated(object sender, Native.LayoutEventArgs e)
{
Element.Layout(e.Geometry.ToDP());
+
+ if (_moreOption != null && _moreOption.IsValueCreated)
+ {
+ _moreOption.Value.Geometry = _page.Geometry;
+ }
+ }
+
+ MoreOption CreateMoreOption()
+ {
+ var moreOption = new MoreOption(_page);
+ moreOption.Clicked += OnMoreOptionItemClicked;
+ _page.Children.Add(moreOption);
+ moreOption.Show();
+ return moreOption;
+ }
+
+ void OnToolbarCollectionChanged(object sender, EventArgs eventArgs)
+ {
+ if (Element.ToolbarItems.Count > 0 || _moreOption.IsValueCreated)
+ {
+ UpdateToolbarItems(false);
+ }
+ }
+
+ void UpdateToolbarItems(bool initialize)
+ {
+ //clear existing more option items and add toolbar item again on purpose.
+ if (!initialize && _moreOption.Value.Items.Count > 0)
+ {
+ _moreOption.Value.Items.Clear();
+ }
+
+ foreach (var item in Element.ToolbarItems)
+ {
+ _moreOption.Value.Items.Add(CreateMoreOptionItem(item));
+ }
+ }
+
+ void OnMoreOptionItemClicked(object sender, MoreOptionItemEventArgs e)
+ {
+ var formsMoreOptionItem = e.Item as FormsMoreOptionItem;
+ if (formsMoreOptionItem != null)
+ {
+ ((IMenuItemController)formsMoreOptionItem.ToolbarItem)?.Activate();
+ }
+ _moreOption.Value.IsOpened = false;
}
}
}
\ No newline at end of file
--- /dev/null
+using System;
+using ElmSharp;
+using ESize = ElmSharp.Size;
+using TSpan = Xamarin.Forms.Platform.Tizen.Native.Span;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class RadioButtonRenderer : ViewRenderer<RadioButton, Radio>
+ {
+ readonly TSpan _span = new TSpan();
+ public RadioButtonRenderer()
+ {
+ RegisterPropertyHandler(RadioButton.IsCheckedProperty, UpdateIsChecked);
+ RegisterPropertyHandler(RadioButton.TextProperty, UpdateText);
+ RegisterPropertyHandler(RadioButton.TextColorProperty, UpdateTextColor);
+ RegisterPropertyHandler(RadioButton.FontProperty, UpdateFont);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<RadioButton> e)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new Radio(Forms.NativeParent) { StateValue = 1 });
+ Control.ValueChanged += OnValueChanged;
+ }
+ base.OnElementChanged(e);
+ ApplyTextAndStyle();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (Control != null)
+ {
+ Control.ValueChanged -= OnValueChanged;
+ }
+ }
+ base.Dispose(disposing);
+ }
+
+ protected override Size MinimumSize()
+ {
+ return Measure(Control.MinimumWidth, Control.MinimumHeight).ToDP();
+ }
+
+ protected override ESize Measure(int availableWidth, int availableHeight)
+ {
+ var size = Control.Geometry;
+ Control.Resize(availableWidth, size.Height);
+ var formattedSize = Control.EdjeObject["elm.text"].TextBlockFormattedSize;
+ Control.Resize(size.Width, size.Height);
+ return new ESize()
+ {
+ Width = Control.MinimumWidth + formattedSize.Width,
+ Height = Math.Max(Control.MinimumHeight, formattedSize.Height),
+ };
+ }
+
+ void OnValueChanged(object sender, EventArgs e)
+ {
+ Element.SetValueFromRenderer(RadioButton.IsCheckedProperty, Control.GroupValue == 1 ? true : false);
+ }
+
+ void UpdateIsChecked()
+ {
+ Control.GroupValue = Element.IsChecked ? 1 : 0;
+ }
+
+ void UpdateText(bool isInitialized)
+ {
+ _span.Text = Element.Text;
+ if (!isInitialized)
+ ApplyTextAndStyle();
+ }
+
+ void UpdateTextColor(bool isInitialized)
+ {
+ _span.ForegroundColor = Element.TextColor.ToNative();
+ if (!isInitialized)
+ ApplyTextAndStyle();
+ }
+
+ void UpdateFont(bool isInitialized)
+ {
+ _span.FontSize = Element.FontSize;
+ _span.FontAttributes = Element.FontAttributes;
+ _span.FontFamily = Element.FontFamily;
+ if (!isInitialized)
+ ApplyTextAndStyle();
+ }
+
+ void ApplyTextAndStyle()
+ {
+ SetInternalTextAndStyle(_span.GetDecoratedText(), _span.GetStyle());
+ }
+
+ void SetInternalTextAndStyle(string formattedText, string textStyle)
+ {
+ string emission = "elm,state,text,visible";
+ if (string.IsNullOrEmpty(formattedText))
+ {
+ formattedText = null;
+ textStyle = null;
+ emission = "elm,state,text,hidden";
+ }
+ Control.Text = formattedText;
+
+ var textblock = Control.EdjeObject["elm.text"];
+ if (textblock != null)
+ {
+ textblock.TextStyle = textStyle;
+ }
+ Control.EdjeObject.EmitSignal(emission, "elm");
+ }
+ }
+}
{
if (Control == null)
{
- SetNativeControl(new NScroller(Forms.NativeParent));
+ SetNativeControl(CreateNativeControl());
Control.Scrolled += OnScrolled;
_scrollCanvas = new NBox(Control);
_scrollCanvas.LayoutUpdated += OnContentLayoutUpdated;
base.OnElementChanged(e);
}
+ protected virtual NScroller CreateNativeControl()
+ {
+
+ if (Device.Idiom == TargetIdiom.Watch)
+ {
+ return new Native.Watch.WatchScroller(Forms.NativeParent, Forms.CircleSurface);
+ }
+ else
+ {
+ return new NScroller(Forms.NativeParent);
+ }
+ }
+
protected override void Dispose(bool disposing)
{
if (disposing)
void UpdateVerticalScrollBarVisibility()
{
- Control.VerticalScrollBarVisiblePolicy = ScrollBarVisibilityToTizen(Element.VerticalScrollBarVisibility);
+ Control.VerticalScrollBarVisiblePolicy = Element.VerticalScrollBarVisibility.ToNative();
}
void UpdateHorizontalScrollBarVisibility()
{
var orientation = Element.Orientation;
if (orientation == ScrollOrientation.Horizontal || orientation == ScrollOrientation.Both)
- Control.HorizontalScrollBarVisiblePolicy = ScrollBarVisibilityToTizen(Element.HorizontalScrollBarVisibility);
+ Control.HorizontalScrollBarVisiblePolicy = Element.HorizontalScrollBarVisibility.ToNative();
}
+ }
- ScrollBarVisiblePolicy ScrollBarVisibilityToTizen(ScrollBarVisibility visibility)
+ static class ScrollBarExtensions
+ {
+ public static ScrollBarVisiblePolicy ToNative(this ScrollBarVisibility visibility)
{
switch (visibility)
{
+using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using ElmSharp;
+using ElmSharp.Wearable;
+using Specific = Xamarin.Forms.PlatformConfiguration.TizenSpecific.Application;
namespace Xamarin.Forms.Platform.Tizen
{
break;
}
}
+
+ protected override void OnFocused(object sender, EventArgs e)
+ {
+ base.OnFocused(sender, e);
+ UpdateRotaryInteraction(true);
+ }
+
+ protected override void OnUnfocused(object sender, EventArgs e)
+ {
+ base.OnUnfocused(sender, e);
+ UpdateRotaryInteraction(false);
+ }
+
+ protected virtual void UpdateRotaryInteraction(bool enable)
+ {
+ if (NativeView is IRotaryInteraction ri)
+ {
+ if (Specific.GetUseBezelInteraction(Application.Current))
+ {
+ if (enable)
+ ri.RotaryWidget?.Activate();
+ else
+ ri.RotaryWidget?.Deactivate();
+ }
+ }
+ }
}
-}
\ No newline at end of file
+}
using System.Linq;
using ElmSharp;
using ElmSharp.Accessible;
+using ElmSharp.Wearable;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Platform.Tizen.Native;
using EFocusDirection = ElmSharp.FocusDirection;
/// <summary>
/// Handles focus events.
/// </summary>
- void OnFocused(object sender, EventArgs e)
+ protected virtual void OnFocused(object sender, EventArgs e)
{
if (null != Element)
{
/// <summary>
/// Handles unfocus events.
/// </summary>
- void OnUnfocused(object sender, EventArgs e)
+ protected virtual void OnUnfocused(object sender, EventArgs e)
{
if (null != Element)
{
Registered.Register(typeof(SwipeView), () => new SwipeViewRenderer());
Registered.Register(typeof(RefreshView), () => new RefreshViewRenderer());
Registered.Register(typeof(MediaElement), () => new MediaElementRenderer());
+ Registered.Register(typeof(IndicatorView), () => new IndicatorViewRenderer());
+ Registered.Register(typeof(RadioButton), () => new RadioButtonRenderer());
//ImageSourceHandlers
Registered.Register(typeof(FileImageSource), () => new FileImageSourceHandler());
return Forms.ConvertToDPFont(pt);
}
+ public Color GetNamedColor(string name)
+ {
+ // Not supported on this platform
+ return Color.Default;
+ }
+
public void OpenUriAction(Uri uri)
{
if (uri == null || uri.AbsoluteUri == null)
{
return Platform.GetNativeSize(view, widthConstraint, heightConstraint);
}
+
+ public OSAppTheme RequestedTheme => OSAppTheme.Unspecified;
+
+ static MD5 CreateChecksum()
+ {
+ return MD5.Create();
+ }
}
}
\ No newline at end of file
--- /dev/null
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace Xamarin.Forms.Xaml
+{
+ [ContentProperty(nameof(Default))]
+ public class OnAppThemeExtension : IMarkupExtension, INotifyPropertyChanged
+ {
+ public OnAppThemeExtension()
+ {
+ ExperimentalFlags.VerifyFlagEnabled(nameof(AppThemeColor), ExperimentalFlags.AppThemeExperimental, nameof(OnAppThemeExtension));
+
+ Application.Current.RequestedThemeChanged += RequestedThemeChanged;
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void OnPropertyChanged([CallerMemberName] string propName = null)
+ => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
+
+ public object Default { get; set; }
+ public object Light { get; set; }
+ public object Dark { get; set; }
+
+ private object _value;
+ public object Value
+ {
+ get => _value;
+ private set
+ {
+ _value = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public IValueConverter Converter { get; set; }
+
+ public object ConverterParameter { get; set; }
+
+ public object ProvideValue(IServiceProvider serviceProvider)
+ {
+ if (Default == null
+ && Light == null
+ && Dark == null)
+ throw new XamlParseException("OnAppThemeExtension requires a non-null value to be specified for at least one theme or Default.", serviceProvider);
+
+ var valueProvider = serviceProvider?.GetService<IProvideValueTarget>() ?? throw new ArgumentException();
+
+ BindableProperty bp;
+ PropertyInfo pi = null;
+ Type propertyType = null;
+
+ if (valueProvider.TargetObject is Setter setter)
+ {
+ bp = setter.Property;
+ }
+ else
+ {
+ bp = valueProvider.TargetProperty as BindableProperty;
+ pi = valueProvider.TargetProperty as PropertyInfo;
+ }
+ propertyType = bp?.ReturnType
+ ?? pi?.PropertyType
+ ?? throw new InvalidOperationException("Cannot determine property to provide the value for.");
+
+ var value = GetValue();
+ var info = propertyType.GetTypeInfo();
+ if (value == null && info.IsValueType)
+ Value = Activator.CreateInstance(propertyType);
+
+ if (Converter != null)
+ Value = Converter.Convert(value, propertyType, ConverterParameter, CultureInfo.CurrentUICulture);
+
+ var converterProvider = serviceProvider?.GetService<IValueConverterProvider>();
+ if (converterProvider != null)
+ {
+ MemberInfo minforetriever()
+ {
+ if (pi != null)
+ return pi;
+
+ MemberInfo minfo = null;
+ try
+ {
+ minfo = bp.DeclaringType.GetRuntimeProperty(bp.PropertyName);
+ }
+ catch (AmbiguousMatchException e)
+ {
+ throw new XamlParseException($"Multiple properties with name '{bp.DeclaringType}.{bp.PropertyName}' found.", serviceProvider, innerException: e);
+ }
+ if (minfo != null)
+ return minfo;
+ try
+ {
+ return bp.DeclaringType.GetRuntimeMethod("Get" + bp.PropertyName, new[] { typeof(BindableObject) });
+ }
+ catch (AmbiguousMatchException e)
+ {
+ throw new XamlParseException($"Multiple methods with name '{bp.DeclaringType}.Get{bp.PropertyName}' found.", serviceProvider, innerException: e);
+ }
+ }
+
+ Value = converterProvider.Convert(value, propertyType, minforetriever, serviceProvider);
+ }
+ if (converterProvider != null)
+ Value = converterProvider.Convert(value, propertyType, () => pi, serviceProvider);
+
+ var ret = value.ConvertTo(propertyType, () => pi, serviceProvider, out Exception exception);
+ if (exception != null)
+ throw exception;
+ Value = ret;
+
+ if (!(value is Binding))
+ return new Binding(nameof(Value), source: this);
+ else
+ return ret;
+ }
+
+ object GetValue()
+ {
+ switch (Application.Current?.RequestedTheme)
+ {
+ default:
+ case OSAppTheme.Light:
+ return Light ?? Default;
+ case OSAppTheme.Dark:
+ return Dark ?? Default;
+ }
+ }
+
+ void RequestedThemeChanged(object sender, AppThemeChangedEventArgs e)
+ {
+ Value = GetValue();
+ }
+ }
+}
\ No newline at end of file