Support new features of Xamari.Forms (#208)
author유리나/Common Platform Lab(SR)/Staff Engineer/삼성전자 <rina6350.you@samsung.com>
Thu, 23 Apr 2020 02:16:26 +0000 (11:16 +0900)
committer안주원/Common Platform Lab(SR)/Principal Engineer/삼성전자 <juwon.ahn@samsung.com>
Thu, 23 Apr 2020 02:16:26 +0000 (11:16 +0900)
* Fix Flyout item updated (#265)

* Add circular-ui theme (#266)

* Fix Year display issue in latest Tizen 5.5 binary (#269)

* [core] Profile is off by default, save 56KB of allocations (#8963)

Profiling a Blank Forms app template with allocations on Android:

    adb shell setprop debug.mono.profile log:calls,alloc

I saw something worrying:

    Allocation summary
         Bytes      Count  Average Type name
         32064          2    16032 Xamarin.Forms.Internals.Profile.Datum[]
         24032          1    24032 Xamarin.Forms.Internals.Profile[]

This appears to be ~56KB of objects that are allocated at startup.
`Profile` in general looks like it should be useful, but it shouldn't
impact startup in customer's apps!

The `Profile` struct is a public API, so we can't use `#if` or
`[Conditional]`. I'm thinking a new flag is need to enable the
`Profile` class:

    Forms.SetFlags("Profile");

To make everything work, I had to add two public members to `Profile`:

* Profile.Enable()
* Profile.IsEnabled

They can't be `internal`, since `Forms` is in each platform-specific
assembly. I added `[EditorBrowsable(EditorBrowsableState.Never)]`.

Additionally, there was one place in `Registrar.cs`:

    Profile.FrameBegin(assembly.GetName().Name);

`Assembly.GetName()` for every assembly would be expensive, so I check
if `Profile.IsEnabled` is False and use "Assembly" instead.

One last improvement was the handling of:

    public static partial class Forms
    {
        public static IReadOnlyList<string> Flags
        ...

There were some weird `.ToList().AsReadonly()` calls. `string[]`
implements `IReadOnlyList`, so we can just `Clone()` the input or use
`new string[0]` by default.

~~ Results ~~

I saw a small improvement to startup on a Pixel 3 XL:

    Before:
    12-18 16:51:16.427  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +586ms
    12-18 16:51:20.133  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +567ms
    12-18 16:51:23.863  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +573ms
    12-18 16:51:27.581  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +571ms
    12-18 16:51:31.362  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +577ms
    12-18 16:51:35.095  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +575ms
    12-18 16:51:38.846  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +578ms
    12-18 16:51:42.576  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +570ms
    12-18 16:51:46.324  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +569ms
    12-18 16:51:50.056  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +576ms
    Average(ms): 574.2
    Std Err(ms): 1.74355957741627
    Std Dev(ms): 5.51361950083609

    After:
    12-18 16:55:04.122  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +571ms
    12-18 16:55:07.805  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +570ms
    12-18 16:55:11.553  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +569ms
    12-18 16:55:15.303  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +574ms
    12-18 16:55:19.020  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +572ms
    12-18 16:55:22.766  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +576ms
    12-18 16:55:26.500  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +573ms
    12-18 16:55:30.264  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +582ms
    12-18 16:55:33.981  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +571ms
    12-18 16:55:37.697  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +571ms
    Average(ms): 572.9
    Std Err(ms): 1.19675487140108
    Std Dev(ms): 3.78447119452933

* Fix navigating/navigated events and add ".." navigation (#10048)

* Fix hot reload when modifying AppShell file (#10064)

* Ensure that Visual is available to EmptyView when renderers are created (#10022) fixes #8766

* Parent the EmptyView long enough to get the correct Visual

* Temporarily parent empty views on Android to get correct Visual
Fixes #8766

* Make instructions more explicit

* Propagate the visual without a parent

* Clean up last parenting clause

* Fix route removal to keep one default if needed (#10156)

* Inverse Parent/Child fields for VisualDiagnostics.SendVisualTreeChanged (#10072)

* Inverse Parent/Child fields for VisualDiagnostics.SendVisualTreeChanged

The Parent/Child relationship for what's being sent for SendVisualTreeChanged needs to be flipped. With `VisualDiagnostics.SendVisualTreeChanged(this, value);` you end up with, say, a Button being a parent of a StackPanel, even though the button is _inside_ the StackPanel.

* Inverse Parent/Child fields for VisualDiagnostics.SendVisualTreeChanged

The Parent/Child relationship for what's being sent for SendVisualTreeChanged needs to be flipped. In the case of `XamlLoader`, the root XAML page is the "ultimate" parent of all the elements below it, so it should be sent as the "Parent" rather than as a "Child."

* Revert SendVisualTreeChanged for XamlLoader

* Remove VisualTreeChanged event call from XamlLoader

* Revert "Remove VisualTreeChanged event call from XamlLoader"

This reverts commit 2cbeda2d11393ee1f4b57676d2d7929b04c83e49.

* Revert Whitespace

* Fix query string for ".." and renavigating (#10176)

* Fix query string for .. and renavigating

* - pass null if query string is removed

* - fix setting initial value

* [GH-9440] - Fix Flyout remains open on FlyoutItem double click (#9719)

* fix commit

* Update Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue9440.cs

* fixed UITest

* - OnDrawerStateChanged

* Update Shell.cs

Co-authored-by: Pedro Jesus <pedro.jesus@arctouch.com>
Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
* Cross-Platform OS Theme APIs (#9958)

* Implement OnAppTheme

* Implement AppThemeColor

* Added tests

* Finishing up

* Implement UWP

* Xcode 11 and additional test

* Update Forms.cs

* Update ViewRenderer.cs

* Update ViewRenderer.cs

* Remove IAppThemeProvider

* Implemented RequestedThemeChanged

* Update WindowsBasePlatformServices.cs

* Bindable props and Android invalidate

* Update DefaultBindableProperties.cs

* Updates at runtime

* Review feedback part 1

* Implement VisualElement.OnRequestedThemeChanged

* UWP Dispatcher

* Add experimental flag

* ControlGallery restructure

* Update ViewRenderer.cs

* Update WindowsBasePlatformServices.cs

* iOS pre-13 fix

* Propagating StyleClass on ShellElements to Flyout templates (#9886)

* Propagating StyleClass to Flyout templates

* - fix style propagation

* - UWP flyout item templating implementation

* - fix find parent code

* - visual states and styling

* - add styleclass to MenuItem

* - default styles

* - make style classes discoverable

* [Tizen] Enable Page.ToolbarItem on Watch (#10145)

* Add GetNamedColor for platform specific colors (#10008)

* Implementation

* Implement UWP

* Update MockPlatformServices.cs

* Added to ColorTypeConverter

* Update WPFPlatformServices.cs

* Added Android

Co-authored-by: Samantha Houts <samhouts@users.noreply.github.com>
* Remove ButtonSource API (#9967)

fixes #9948

* fix modal onappearing and hardware back button (#10189)

* [Enhancement] Expander control (#9044)

* Expander initial commit

* updated Expander

* tab fixes

* added spacing property

* added sample gallery page

* removed useless class

* updated sample

* cleaned code

* Update Xamarin.Forms.Core/Expander.cs

Co-Authored-By: Gerald Versluis <github@geraldversluis.nl>
* updated sample

* UITest ExpanderView (#3)

* removed empty line

* - expander flag

Co-authored-by: Gerald Versluis <github@geraldversluis.nl>
Co-authored-by: Guido Neele <guido.neele@xs4all.nl>
Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
* Remove controller from UINavigationController stack  when page is removed (#10140)

* Remove controller from UINavigationController stack  when page is removed

* - add uitest

* - remove extra code

* Inverse Parent/Child fields for VisualDiagnostics.SendVisualTreeChanged (#10072)

* Inverse Parent/Child fields for VisualDiagnostics.SendVisualTreeChanged

The Parent/Child relationship for what's being sent for SendVisualTreeChanged needs to be flipped. With `VisualDiagnostics.SendVisualTreeChanged(this, value);` you end up with, say, a Button being a parent of a StackPanel, even though the button is _inside_ the StackPanel.

* Inverse Parent/Child fields for VisualDiagnostics.SendVisualTreeChanged

The Parent/Child relationship for what's being sent for SendVisualTreeChanged needs to be flipped. In the case of `XamlLoader`, the root XAML page is the "ultimate" parent of all the elements below it, so it should be sent as the "Parent" rather than as a "Child."

* Revert SendVisualTreeChanged for XamlLoader

* Remove VisualTreeChanged event call from XamlLoader

* Revert "Remove VisualTreeChanged event call from XamlLoader"

This reverts commit 2cbeda2d11393ee1f4b57676d2d7929b04c83e49.

* Revert Whitespace

* [Core] Stylesheets on page level now always override parent stylesheets (#6772) fixes #5812

* To fix issue #5812 I moved the responsibility for apply a stylesheet to child elements to the Element class.

It would be weird for a stylesheet to get all parent stylesheets of an element and call Apply on them too. I think it's a good thing that StyleSheet.Apply(Element) really only applies it on the given Element? The main issue was that when the ApplyParentStyleSheets was called, the stylesheet of the element itself wasn't applied, so you could never override parent style elements

* Added unit test for subview styling

* Removed .concat linq call

* Revert autosave formatting

* Update MergedStyle.cs

Co-authored-by: chris <chris@careconnections.nl>
Co-authored-by: Samantha Houts <samantha.houts@xamarin.com>
Co-authored-by: Samantha Houts <samhouts@users.noreply.github.com>
* Embedded font's now cache lookup to prevent double setups. fixes #10208 (#10227)

* Embedded font loader can now register twice on iOS

* FontRegistrar caches lookups

* [Tizen] Changed CarouselView scroll logic according to the Core change. (#10235)

* [Tizen] Invoke ItemsView Scrolled event

* [Tizen] Changed CarouselView scroll logic

* [Tizen] Add IndicatorView

* [Tizen] Enhances ListView on Tizen  (#10236)

* [Tizen] Activate IRotaryActionWidget when it has focus

* [Tizen] Add IRotaryInteraction and WatchList

* [Tizen] Change callback name

* [Tizen] Add WatchScroller

* [Tizen] Support scrollbar visibility properties of ListView

* https://github.com/xamarin/Xamarin.Forms/issues/10229 (#10247)

fixes #10229

* Fix default Shell flyout item templates (#10252)

* Fix default shell menu templates

* - fix default styles

* - centralize code for picking flyout template
fixes #10238

* [Tizen] Adds RadioButton (#10237)

* Change StateTriggerBase OnAttached and OnDetached methods to be public (#9888)

* Change OnAttached and OnDetached to be public

* Fixed build error

* Removed unnecessary virtual methods

* Fixed build error

* AppTheme to OSAppTheme (#10302)

* Change file mode from 644 to 755

* Change file mode from 644 to 755 for CollectionView.cs

75 files changed:
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/CircleDateTimeSelectorRenderer.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/CircularUI.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/Shell/NavigationView.cs
src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/ThemeLoader.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Core/AdaptiveTrigger.cs
src/XSF/Xamarin.Forms.Core/AppThemeChangedEventArgs.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Core/AppThemeColor.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Core/Application.cs
src/XSF/Xamarin.Forms.Core/ColorTypeConverter.cs
src/XSF/Xamarin.Forms.Core/CompareStateTrigger.cs
src/XSF/Xamarin.Forms.Core/Device.cs
src/XSF/Xamarin.Forms.Core/Element.cs
src/XSF/Xamarin.Forms.Core/Element_StyleSheets.cs
src/XSF/Xamarin.Forms.Core/Expander.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Core/ExpanderState.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Core/ExperimentalFlags.cs
src/XSF/Xamarin.Forms.Core/FontRegistrar.cs
src/XSF/Xamarin.Forms.Core/IPlatformServices.cs
src/XSF/Xamarin.Forms.Core/Internals/PropertyPropagationExtensions.cs
src/XSF/Xamarin.Forms.Core/Items/ItemsView.cs
src/XSF/Xamarin.Forms.Core/Markup/DefaultBindableProperties.cs
src/XSF/Xamarin.Forms.Core/MenuItem.cs
src/XSF/Xamarin.Forms.Core/MergedStyle.cs
src/XSF/Xamarin.Forms.Core/NamedPlatformColor.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Core/OSAppTheme.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Core/OnAppTheme.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Core/OrientationStateTrigger.cs
src/XSF/Xamarin.Forms.Core/Page.cs
src/XSF/Xamarin.Forms.Core/Profiler.cs
src/XSF/Xamarin.Forms.Core/RadioButton.cs
src/XSF/Xamarin.Forms.Core/Registrar.cs
src/XSF/Xamarin.Forms.Core/ResourceDictionary.cs
src/XSF/Xamarin.Forms.Core/Routing.cs
src/XSF/Xamarin.Forms.Core/Shell/BaseShellItem.cs
src/XSF/Xamarin.Forms.Core/Shell/IShellController.cs
src/XSF/Xamarin.Forms.Core/Shell/MenuShellItem.cs
src/XSF/Xamarin.Forms.Core/Shell/NavigableElement.cs
src/XSF/Xamarin.Forms.Core/Shell/Shell.cs
src/XSF/Xamarin.Forms.Core/Shell/ShellContent.cs
src/XSF/Xamarin.Forms.Core/Shell/ShellItem.cs
src/XSF/Xamarin.Forms.Core/Shell/ShellNavigationState.cs
src/XSF/Xamarin.Forms.Core/Shell/ShellSection.cs
src/XSF/Xamarin.Forms.Core/Shell/ShellUriHandler.cs
src/XSF/Xamarin.Forms.Core/StateTrigger.cs
src/XSF/Xamarin.Forms.Core/StateTriggerBase.cs
src/XSF/Xamarin.Forms.Core/StyleSheets/StyleSheet.cs
src/XSF/Xamarin.Forms.Core/VisualElement.cs
src/XSF/Xamarin.Forms.Core/VisualElement_StyleSheet.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Forms.cs
src/XSF/Xamarin.Forms.Platform.Tizen/IRotaryInteraction.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Native/CollectionView/CollectionView.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Native/CollectionView/GridLayoutManager.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Native/CollectionView/ICollectionViewLayoutManager.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Native/CollectionView/IndicatorView.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Native/CollectionView/LinearLayoutManager.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Native/ListView.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Native/Scroller.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Native/Watch/FormsMoreOptionItem.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Native/Watch/WatchListView.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Native/Watch/WatchScroller.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/CarouselViewRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/EditorRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/IndicatorViewRenderer.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/ItemsViewRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/ListViewRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/PageRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/RadioButtonRenderer.cs [new file with mode: 0644]
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/ScrollViewRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/ViewRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/VisualElementRenderer.cs
src/XSF/Xamarin.Forms.Platform.Tizen/StaticRegistrar.cs
src/XSF/Xamarin.Forms.Platform.Tizen/TizenPlatformServices.cs
src/XSF/Xamarin.Forms.Xaml/MarkupExtensions/OnAppThemeExtension.cs [new file with mode: 0644]
src/XSF/theme/tizen-circular-ui-theme.edj [new file with mode: 0644]

index 8aff8c1e69622d9d44cc90ad9e889651ed3fe90b..461ae1026816d45d0c50927888c4d8b4130be907 100644 (file)
@@ -105,6 +105,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                                if (Element.ValueType == DateTimeType.Date)
                                {
                                        Control.Style = "datepicker/circle";
+                                       Control.Format = "%d/%b/%Y";
                                }
                                else if (Element.ValueType == DateTimeType.Time)
                                {
index 4e5026fcfeeb922398ddaf7eefb760c4cd22dacf..44b0155785ab36f9abc7659b9b07d08183db7bd0 100644 (file)
  * 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";
@@ -33,6 +51,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                public static void Init(string apiKey)
                {
+
                        if (!string.IsNullOrEmpty(apiKey))
                        {
                                GoogleMaps.Init(apiKey);
@@ -44,5 +63,23 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
 
                        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
index aecddf42a4f86e810041e08b587590362c0b4ad0..1206d9dac229e44d20494773213b281fce00c5f3 100644 (file)
@@ -2,10 +2,13 @@
 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
 {
@@ -13,11 +16,61 @@ 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;
@@ -77,6 +130,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
             }
             _itemCache = items;
 
+            ClearItemPropertyChangedHandler();
             _naviMenu.Clear();
             _items.Clear();
             // header
@@ -104,6 +158,7 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
                     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 = "" });
@@ -259,6 +314,20 @@ namespace Tizen.Wearable.CircularUI.Forms.Renderer
             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
     {
diff --git a/src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/ThemeLoader.cs b/src/XSF/Tizen.Wearable.CircularUI.Forms.Renderer/ThemeLoader.cs
new file mode 100644 (file)
index 0000000..31f847e
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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));
+        }
+    }
+}
index 94da4da7ea2cdba67b34217f7ed9ecf27d741f1c..b786ffa683110966e2830e7180d73dbe11bded9a 100644 (file)
@@ -39,7 +39,7 @@ namespace Xamarin.Forms
                        ((AdaptiveTrigger)bindable).UpdateState();
                }
 
-               internal override void OnAttached()
+               protected override void OnAttached()
                {
                        base.OnAttached();
 
@@ -50,7 +50,7 @@ namespace Xamarin.Forms
                        }
                }
 
-               internal override void OnDetached()
+               protected override void OnDetached()
                {
                        base.OnDetached();
 
diff --git a/src/XSF/Xamarin.Forms.Core/AppThemeChangedEventArgs.cs b/src/XSF/Xamarin.Forms.Core/AppThemeChangedEventArgs.cs
new file mode 100644 (file)
index 0000000..3f4194d
--- /dev/null
@@ -0,0 +1,12 @@
+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
diff --git a/src/XSF/Xamarin.Forms.Core/AppThemeColor.cs b/src/XSF/Xamarin.Forms.Core/AppThemeColor.cs
new file mode 100644 (file)
index 0000000..1d908df
--- /dev/null
@@ -0,0 +1,81 @@
+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
index f11147c04a475297aea8b6bb6ec24568d601da5e..110c00d5ac07a514463f0484ff2d1186e27b3862 100644 (file)
@@ -7,11 +7,13 @@ using System.Threading.Tasks;
 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;
 
@@ -154,6 +156,23 @@ namespace Xamarin.Forms
                        }
                }
 
+               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;
index e0391708bb39a6584bcc1e2d361a601324c66126..7d917043985f40366439900bdf962a9767a75c0a 100644 (file)
@@ -26,7 +26,8 @@ namespace Xamarin.Forms
                                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)
@@ -41,7 +42,8 @@ namespace Xamarin.Forms
                                        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)
@@ -55,7 +57,8 @@ namespace Xamarin.Forms
                                        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)
@@ -70,7 +73,8 @@ namespace Xamarin.Forms
                                        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)
@@ -119,151 +123,152 @@ namespace Xamarin.Forms
                                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)
@@ -272,6 +277,10 @@ namespace Xamarin.Forms
                                        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)}");
@@ -280,7 +289,8 @@ namespace Xamarin.Forms
                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);
                        }
index a419e89774efc9a8f51329fa7cc8c7b373d0d81f..caa6ff87bdebe52ed7c1e4ed4c00b7b54f82b99c 100644 (file)
@@ -40,7 +40,7 @@ namespace Xamarin.Forms
                        ((CompareStateTrigger)bindable).UpdateState();
                }
 
-               internal override void OnAttached()
+               protected override void OnAttached()
                {
                        base.OnAttached();
                        UpdateState();
index 10d204d054cb4ca1e971704ac32a4972af1c08ab..6702ca93425b2e4499c55665979119a08965f3cb 100644 (file)
@@ -132,7 +132,7 @@ namespace Xamarin.Forms
                {
                        var tcs = new TaskCompletionSource<T>();
                        BeginInvokeOnMainThread(
-                               async() =>
+                               async () =>
                                {
                                        try
                                        {
@@ -247,6 +247,11 @@ namespace Xamarin.Forms
                        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);
index ae83367519b4a333e6f08ab1275559d5e61a27ce..74977ccc96a8a874612e4423798a2aff7ccc8f8b 100644 (file)
@@ -180,7 +180,7 @@ namespace Xamarin.Forms
                                {
                                        ((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}.");
                                }
 
@@ -201,7 +201,7 @@ namespace Xamarin.Forms
                                        SetInheritedBindingContext(this, null);
                                }
 
-                               VisualDiagnostics.SendVisualTreeChanged(this, value);
+                               VisualDiagnostics.SendVisualTreeChanged(value, this);
 
                                OnParentSet();
 
@@ -344,16 +344,16 @@ namespace Xamarin.Forms
                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 });
                        }
 
@@ -388,7 +388,7 @@ namespace Xamarin.Forms
                internal virtual void OnParentResourcesChanged(object sender, ResourcesChangedEventArgs e)
                {
                        if (e == ResourcesChangedEventArgs.StyleSheets)
-                               ApplyStyleSheetsOnParentSet();
+                               ApplyStyleSheets();
                        else
                                OnParentResourcesChanged(e.Values);
                }
@@ -409,7 +409,10 @@ namespace Xamarin.Forms
 
                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)
@@ -462,43 +465,6 @@ namespace Xamarin.Forms
 
                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);
index 690fb47bc9c2f89dd57cd54aeba82709397d5ad7..a4676abd0e69fa91d9ba8d7bf41c3a1d2cc2a427 100644 (file)
@@ -1,8 +1,6 @@
 using System.Collections.Generic;
-using System.IO;
+using System.Linq;
 using System.Reflection;
-
-using Xamarin.Forms.Internals;
 using Xamarin.Forms.StyleSheets;
 
 namespace Xamarin.Forms
@@ -18,14 +16,18 @@ 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;
                                        }
@@ -38,21 +40,44 @@ namespace Xamarin.Forms
                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
diff --git a/src/XSF/Xamarin.Forms.Core/Expander.cs b/src/XSF/Xamarin.Forms.Core/Expander.cs
new file mode 100644 (file)
index 0000000..b3d4b09
--- /dev/null
@@ -0,0 +1,376 @@
+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;
+                               });
+               }
+       }
+}
diff --git a/src/XSF/Xamarin.Forms.Core/ExpanderState.cs b/src/XSF/Xamarin.Forms.Core/ExpanderState.cs
new file mode 100644 (file)
index 0000000..4f6515b
--- /dev/null
@@ -0,0 +1,10 @@
+namespace Xamarin.Forms
+{
+       public enum ExpanderState
+       {
+               Expanding,
+               Expanded,
+               Collapsing,
+               Collapsed
+       }
+}
index 792289296a63966dd26084ff2bc58ba3faf6871c..e66c8121d016eef8398b887445c54b756d792f8f 100644 (file)
@@ -16,6 +16,8 @@ namespace Xamarin.Forms
                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(
index 12d6634d77fbdf51d303fae03fd3295bd3f897c6..b3efb80da79320d1ad54dc6fac13610326746f26 100644 (file)
@@ -11,7 +11,7 @@ namespace Xamarin.Forms.Internals
        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);
@@ -29,18 +29,23 @@ namespace Xamarin.Forms.Internals
                                        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)
index c7920b31c8d8f65ace1b99cd1fb303f0435f20b7..b0d161bfd1f798096624244ae47eb36411f3ec40 100644 (file)
@@ -26,6 +26,10 @@ namespace Xamarin.Forms.Internals
 
                double GetNamedSize(NamedSize size, Type targetElementType, bool useOldSizes);
 
+               Color GetNamedColor(string name);
+
+               OSAppTheme RequestedTheme { get; }
+
                Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken);
 
                IIsolatedStorageFile GetUserStoreForApplication();
index a23c7239318877e85124902841dff93b0a92a08b..302303e6d9b494016702815486e9b0ccd919304e 100644 (file)
@@ -6,30 +6,24 @@ using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
 
 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)
                        {
@@ -38,16 +32,63 @@ namespace Xamarin.Forms.Internals
                        }
                }
 
-               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
index f238f29f33a72afa7a83e3d1b430420dd6886a00..d1047a6a89f8d4dbda62fd6c3d1cc9b9bb9cde6a 100644 (file)
@@ -97,8 +97,6 @@ namespace Xamarin.Forms
 
                        _logicalChildren.Add(element);
 
-                       PropertyPropagationExtensions.PropagatePropertyChanged(null, element);
-
                        element.Parent = this;
                }
 
index 56c185c931e3d1ff25a0cd40f714d464b5c3b9a0..8b4877b57a5807e12b5c7ee86107597a593895fb 100644 (file)
@@ -64,7 +64,8 @@ namespace Xamarin.Forms.Markup
                        { "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)>
index 73be93f3261d059a021523112649c2cc16d33801..a6e362029ba211e7a851f8dadd3e64e1a77cee9b 100644 (file)
@@ -1,10 +1,12 @@
 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);
 
@@ -32,6 +34,13 @@ namespace Xamarin.Forms
 
                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);
@@ -76,6 +85,25 @@ namespace Xamarin.Forms
                        [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);
index b2439ba98e83a72f672601fe2e95b242e6041014..847f0d06843d55a0419a5c57a16fea81664d5464 100644 (file)
@@ -10,7 +10,7 @@ namespace Xamarin.Forms
        {
                ////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;
 
@@ -59,18 +59,20 @@ namespace Xamarin.Forms
 
                                _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();
                                }
                        }
                }
@@ -111,7 +113,7 @@ namespace Xamarin.Forms
 
                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()
@@ -138,8 +140,9 @@ namespace Xamarin.Forms
                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);
@@ -157,7 +160,7 @@ namespace Xamarin.Forms
                        _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);
diff --git a/src/XSF/Xamarin.Forms.Core/NamedPlatformColor.cs b/src/XSF/Xamarin.Forms.Core/NamedPlatformColor.cs
new file mode 100644 (file)
index 0000000..0393d7c
--- /dev/null
@@ -0,0 +1,75 @@
+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
diff --git a/src/XSF/Xamarin.Forms.Core/OSAppTheme.cs b/src/XSF/Xamarin.Forms.Core/OSAppTheme.cs
new file mode 100644 (file)
index 0000000..1d75194
--- /dev/null
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms
+{
+       public enum OSAppTheme
+       {
+               Unspecified,
+               Light,
+               Dark
+       }
+}
\ No newline at end of file
diff --git a/src/XSF/Xamarin.Forms.Core/OnAppTheme.cs b/src/XSF/Xamarin.Forms.Core/OnAppTheme.cs
new file mode 100644 (file)
index 0000000..4287390
--- /dev/null
@@ -0,0 +1,79 @@
+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
index f594d288fa462948e5775792732c37f9d1d17655..bbe7a4a82889902b6fec1b47cb61a809c038d087 100644 (file)
@@ -26,7 +26,7 @@ namespace Xamarin.Forms
                        ((OrientationStateTrigger)bindable).UpdateState();
                }
 
-               internal override void OnAttached()
+               protected override void OnAttached()
                {
                        base.OnAttached();
 
@@ -37,7 +37,7 @@ namespace Xamarin.Forms
                        }
                }
 
-               internal override void OnDetached()
+               protected override void OnDetached()
                {
                        base.OnDetached();
 
index cc1dafce3d2ebc023f41fccacd61a951b339726b..9243e72c76a469f7e927778fcab21408efc6b0cc 100644 (file)
@@ -129,8 +129,8 @@ namespace Xamarin.Forms
 
                public IList<ToolbarItem> ToolbarItems { get; internal set; }
 
-       [EditorBrowsable(EditorBrowsableState.Never)]
-       public Rectangle ContainerArea
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               public Rectangle ContainerArea
                {
                        get { return _containerArea; }
                        set
@@ -143,15 +143,15 @@ namespace Xamarin.Forms
                        }
                }
 
-       [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
                {
@@ -231,11 +231,11 @@ namespace Xamarin.Forms
                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();
                        }
                }
@@ -250,6 +250,13 @@ namespace Xamarin.Forms
                        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);
@@ -306,7 +313,7 @@ namespace Xamarin.Forms
                                SetInheritedBindingContext(toolbarItem, BindingContext);
                        }
 
-                       if(_titleView != null)
+                       if (_titleView != null)
                                SetInheritedBindingContext(_titleView, BindingContext);
                }
 
@@ -394,8 +401,26 @@ namespace Xamarin.Forms
                        }
                }
 
-       [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;
@@ -419,8 +444,8 @@ namespace Xamarin.Forms
                        FindApplication(this)?.OnPageAppearing(this);
                }
 
-       [EditorBrowsable(EditorBrowsableState.Never)]
-       public void SendDisappearing()
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               public void SendDisappearing()
                {
                        if (!_hasAppeared)
                                return;
index 19e9a0544673712007a89e94bee40a9f6d5c5c7d..0b005b74b6659eac11a5ca44281598ddd63b87bf 100644 (file)
@@ -2,13 +2,7 @@
 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
 {
@@ -26,24 +20,45 @@ 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)
@@ -54,7 +69,7 @@ namespace Xamarin.Forms.Internals
                        [CallerMemberName] string name = "",
                        [CallerLineNumber] int line = 0)
                {
-                       if (!Running)
+                       if (!IsEnabled || !Running)
                                return;
 
                        FrameBeginBody(name, null, line);
@@ -63,7 +78,7 @@ namespace Xamarin.Forms.Internals
                public static void FrameEnd(
                        [CallerMemberName] string name = "")
                {
-                       if (!Running)
+                       if (!IsEnabled || !Running)
                                return;
 
                        FrameEndBody(name);
@@ -73,7 +88,7 @@ namespace Xamarin.Forms.Internals
                        string id,
                        [CallerLineNumber] int line = 0)
                {
-                       if (!Running)
+                       if (!IsEnabled || !Running)
                                return;
 
                        FramePartitionBody(id, line);
@@ -135,6 +150,8 @@ namespace Xamarin.Forms.Internals
 
                public void Dispose()
                {
+                       if (!IsEnabled)
+                               return;
                        if (Running && _start == 0)
                                return;
 
index 9499316af19aacb9de13728c6bce812344346b5a..76e050a82662071b396d6e96889a4045d5b34594 100644 (file)
@@ -20,8 +20,9 @@ namespace Xamarin.Forms
                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;
 
@@ -37,11 +38,12 @@ namespace Xamarin.Forms
                        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()
                {
index aafcb004ebe4d98af8c01a405c851c4dffd851bf..f192ea479e63d6eb4be2a6d4d35c0183f1cb5b9a 100644 (file)
@@ -48,9 +48,9 @@ namespace Xamarin.Forms.Internals
 
                        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
@@ -332,8 +332,8 @@ namespace Xamarin.Forms.Internals
                        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)
                                {
@@ -346,7 +346,7 @@ namespace Xamarin.Forms.Internals
                                        {
                                                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);
                                                }
@@ -358,10 +358,10 @@ namespace Xamarin.Forms.Internals
                                        }
                                }
 
-                               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;
                                }
 
@@ -374,7 +374,7 @@ namespace Xamarin.Forms.Internals
                                Array.Copy(effectAttributes, typedEffectAttributes, effectAttributes.Length);
                                RegisterEffects(resolutionName, typedEffectAttributes);
 
-                               Profile.FrameEnd(assemblyName);
+                               Profile.FrameEnd(frameName);
                        }
 
                        if ((flags & InitializationFlags.DisableCss) == 0)
index e4db274c344341e57c552a36f42abc462b289fb5..230ba86caa199ee1c3ec86ddd713aad75746d3d9 100644 (file)
@@ -327,6 +327,12 @@ namespace Xamarin.Forms
                        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
index c586fba740402f30fb027f7da8b595f54c37f5f1..fd2633b7f4b7f45a455905eeb27eda229c3b8569 100644 (file)
@@ -9,7 +9,8 @@ namespace Xamarin.Forms
                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)
@@ -26,6 +27,10 @@ namespace Xamarin.Forms
                {
                        return IsImplicit(GetRoute(source));
                }
+               internal static bool IsDefault(string source)
+               {
+                       return source.StartsWith(DefaultPrefix, StringComparison.Ordinal);
+               }
 
                internal static void Clear()
                {
@@ -38,7 +43,7 @@ namespace Xamarin.Forms
 
                static object CreateDefaultRoute(BindableObject bindable)
                {
-                       return bindable.GetType().Name + ++s_routeCount;
+                       return $"{DefaultPrefix}{bindable.GetType().Name}{++s_routeCount}";
                }
 
                internal static string[] GetRouteKeys()
@@ -83,16 +88,36 @@ namespace Xamarin.Forms
                        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);
                }
@@ -150,7 +175,7 @@ namespace Xamarin.Forms
                        }
 
                        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}\"");
                }
 
index 9f08fbd02257d0f54e30ac1e0b10b3e658bee129..9a6390fd7956a14bd4d8ff332734cdb57afe33a1 100644 (file)
@@ -4,6 +4,8 @@ using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using Xamarin.Forms.Internals;
 using System.ComponentModel;
+using System.Linq;
+using Xamarin.Forms.StyleSheets;
 
 namespace Xamarin.Forms
 {
@@ -14,6 +16,9 @@ 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
 
@@ -146,6 +151,21 @@ namespace Xamarin.Forms
                                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 = (_, __) =>
                                {
@@ -246,6 +266,153 @@ namespace Xamarin.Forms
                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
index 9ac3f42afd7b3278504e5ed51bb4d39d64566b9c..40cbe999823e23cc0bf087578dd727e74b607f31 100644 (file)
@@ -49,5 +49,7 @@ namespace Xamarin.Forms
                ReadOnlyCollection<ShellItem> GetItems();
 
                event NotifyCollectionChangedEventHandler ItemsCollectionChanged;
+
+               DataTemplate GetFlyoutItemDataTemplate(BindableObject bo);
        }
 }
\ No newline at end of file
index c1a8131cda8e2df96b44bae07adec6093b6d0262..74c9fb641970f57b3031fa6a6557c64002bce212 100644 (file)
@@ -1,21 +1,24 @@
-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)
index 0676fa0363bd7d8003a566f06142763e9911e314..a397d051e3a7ce7deed50ed5c7730578031f6eb6 100644 (file)
@@ -1,10 +1,11 @@
 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));
@@ -42,9 +43,14 @@ namespace Xamarin.Forms
                [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; }
index b7d91800eff6ba281b2d32f689ed78ace7eb6379..41fbfd5507a7ac195af97fd5f220b7ba5b7b3c96 100644 (file)
@@ -58,13 +58,39 @@ namespace Xamarin.Forms
                        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);
@@ -200,6 +226,30 @@ namespace Xamarin.Forms
                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; }
@@ -319,7 +369,7 @@ namespace Xamarin.Forms
 
                        var state = GetNavigationState(shellItem, shellSection, shellContent, null, null);
 
-                       if (FlyoutIsPresented && FlyoutBehavior == FlyoutBehavior.Flyout)
+                       if (FlyoutIsPresented && GetEffectiveFlyoutBehavior() != FlyoutBehavior.Locked)
                                SetValueFromRenderer(FlyoutIsPresentedProperty, false);
 
                        if (shellSection == null)
@@ -376,7 +426,7 @@ namespace Xamarin.Forms
 
                        SetValueFromRenderer(CurrentStatePropertyKey, result);
 
-                       OnNavigated(new ShellNavigatedEventArgs(oldState, CurrentState, source));
+                       ProcessNavigated(new ShellNavigatedEventArgs(oldState, CurrentState, source));
                }
                ReadOnlyCollection<ShellItem> IShellController.GetItems() => ((ShellItemCollection)Items).VisibleItems;
 
@@ -431,7 +481,7 @@ namespace Xamarin.Forms
                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;
 
@@ -503,9 +553,19 @@ namespace Xamarin.Forms
                                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);
                                        });
                                }
                        }
@@ -518,7 +578,7 @@ namespace Xamarin.Forms
 
                        // 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)
@@ -527,7 +587,7 @@ namespace Xamarin.Forms
                        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 + ".";
                        }
@@ -610,7 +670,7 @@ namespace Xamarin.Forms
                                                {
                                                        var topPage = modalStack[i];
 
-                                                       if(i > 0)
+                                                       if (i > 0)
                                                                stateBuilder.Append("/");
 
                                                        stateBuilder.Append(Routing.GetRoute(topPage));
@@ -688,7 +748,7 @@ namespace Xamarin.Forms
                                SendStructureChanged();
                        };
 
-                       void SetCurrentItem()
+                       async void SetCurrentItem()
                        {
                                var shellItems = ShellController.GetItems();
 
@@ -697,12 +757,33 @@ namespace Xamarin.Forms
 
                                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;
+                                               }
                                        }
                                }
 
@@ -917,7 +998,10 @@ namespace Xamarin.Forms
                                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);
@@ -931,25 +1015,34 @@ namespace Xamarin.Forms
                        }
                }
 
-
-               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)
                {
@@ -1253,30 +1346,53 @@ namespace Xamarin.Forms
 
                        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);
                        }
                }
        }
index 7afb2fd59f3967a8f952e4dc97001cfe34435c9f..32143a48d5eb71013bfed8cfb6a94bc5af0e100f 100644 (file)
@@ -34,12 +34,14 @@ namespace Xamarin.Forms
 
                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);
                }
@@ -210,7 +212,7 @@ namespace Xamarin.Forms
                                        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} ");
                                }
@@ -236,23 +238,25 @@ namespace Xamarin.Forms
                        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();
@@ -265,13 +269,22 @@ namespace Xamarin.Forms
                        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);
+                               }
                        }
                }
        }
index 3eec9359623113013d1def436d84f7191af0b2bf..6c8c55ec5aabae6467c920378803d3e3e62e334b 100644 (file)
@@ -12,6 +12,10 @@ namespace Xamarin.Forms
        [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);
@@ -154,7 +158,7 @@ namespace Xamarin.Forms
                }
 
 #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)
                {
@@ -254,7 +258,7 @@ namespace Xamarin.Forms
                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();
                        }
index 4f01285bc620df47fb6096c2231103da6fa32f25..c507da9b9b019ad86910e54f0be75fdec8ec6b3b 100644 (file)
@@ -15,7 +15,7 @@ namespace Xamarin.Forms
                        set
                        {
                                _fullLocation = value;
-                               Location = Routing.RemoveImplicit(value);
+                               Location = Routing.Remove(value, true, true);
                        }
                }
 
index 96b9157cb30c71b1bc031d744c69c5960df767da..f117c2991d992f90d67b7def3f9860f274806bc2 100644 (file)
@@ -354,7 +354,7 @@ namespace Xamarin.Forms
 
                                                        // 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;
                                                }
 
@@ -436,7 +436,7 @@ namespace Xamarin.Forms
                        {
                                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);
@@ -506,7 +506,7 @@ namespace Xamarin.Forms
                        if (CurrentItem == null && ((IShellSectionController)this).GetItems().Contains(child))
                                SetValueFromRenderer(CurrentItemProperty, child);
 
-                       if(CurrentItem != null)
+                       if (CurrentItem != null)
                                UpdateDisplayedPage();
                }
 
@@ -695,11 +695,11 @@ namespace Xamarin.Forms
 
                                        // 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;
                                        }
@@ -724,7 +724,8 @@ namespace Xamarin.Forms
                        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,
@@ -736,11 +737,12 @@ namespace Xamarin.Forms
                        if (!allow)
                                return;
 
-                       if(currentPage)
+                       if (currentPage)
                                PresentedPageDisappearing();
+
                        _navStack.Remove(page);
 
-                       if(currentPage)
+                       if (currentPage)
                                PresentedPageAppearing();
 
                        RemovePage(page);
@@ -767,13 +769,13 @@ namespace Xamarin.Forms
                {
                        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;
 
@@ -889,26 +891,6 @@ namespace Xamarin.Forms
                        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
index 0ce2b0b5bb49c2db4055bb17bb35156993f2f100..0d788357c586f3479c31be8973b022962c1f25db 100644 (file)
@@ -2,6 +2,7 @@
 using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.IO;
 using System.Linq;
 
 namespace Xamarin.Forms
@@ -12,10 +13,19 @@ 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);
                }
@@ -43,7 +53,12 @@ namespace Xamarin.Forms
 
                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}";
@@ -52,22 +67,21 @@ namespace Xamarin.Forms
 
                        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)
@@ -115,7 +129,7 @@ namespace Xamarin.Forms
 
                internal static List<RouteRequestBuilder> GenerateRoutePaths(Shell shell, Uri request)
                {
-                       request = FormatUri(request);
+                       request = FormatUri(request, shell);
                        return GenerateRoutePaths(shell, request, request, false);
                }
 
@@ -132,8 +146,8 @@ namespace Xamarin.Forms
                                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)
@@ -156,7 +170,7 @@ namespace Xamarin.Forms
                                        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 };
                                        }
@@ -175,7 +189,7 @@ namespace Xamarin.Forms
                                depthStart = 0;
                        }
 
-                       if(relativeMatch && shell?.CurrentItem != null)
+                       if (relativeMatch && shell?.CurrentItem != null)
                        {
                                // retrieve current location
                                var currentLocation = NodeLocation.Create(shell);
@@ -227,7 +241,7 @@ namespace Xamarin.Forms
                                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);
@@ -236,7 +250,7 @@ namespace Xamarin.Forms
                                        }
                                }
 
-                               if(builder != null && builder.IsFullMatch)
+                               if (builder != null && builder.IsFullMatch)
                                        return new List<RouteRequestBuilder> { builder };
                        }
                        else
@@ -537,7 +551,7 @@ namespace Xamarin.Forms
 
                                if (segments[0] == route)
                                {
-                                       yield return  new GlobalRouteItem(key, key);
+                                       yield return new GlobalRouteItem(key, key);
                                }
                        }
                }
@@ -650,7 +664,7 @@ namespace Xamarin.Forms
                        switch (node)
                        {
                                case ShellUriHandler.GlobalRouteItem globalRoute:
-                                       if(globalRoute.IsFinished)
+                                       if (globalRoute.IsFinished)
                                                _globalRouteMatches.Add(globalRoute.SourceRoute);
                                        break;
                                case Shell shell:
index c66088afbb235f4f413c0f5ab2c88c37fa9e11a2..a4ebc9f9f509fd47b759ad908aef41ff6a89fa35 100644 (file)
@@ -20,7 +20,7 @@
                        }
                }
 
-               internal override void OnAttached()
+               protected override void OnAttached()
                {
                        base.OnAttached();
                        UpdateState();
index bee3409f7d1328750bd29ac30b2a42a54c7d3db3..c3bfc1bc25940842afb729d3ffa6aceac236109f 100644 (file)
@@ -27,6 +27,8 @@ namespace Xamarin.Forms
 
                internal VisualState VisualState { get; set; }
 
+               public bool IsAttached { get; private set; }
+
                protected void SetActive(bool active)
                {
                        IsActive = active;
@@ -34,14 +36,30 @@ namespace Xamarin.Forms
                        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
index abac34619ab3758a504083c346a3ed3dffa811b3..0cc5561e265ca5821b344845be97a14325543cbd 100644 (file)
@@ -107,13 +107,6 @@ namespace Xamarin.Forms.StyleSheets
                }
 
                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;
index dd7488dcdbb41e7b68ac518a9bada594adf46b0e..ddd993560b51620d21e4db9c09b83eed2fdcdae0 100644 (file)
@@ -90,7 +90,8 @@ namespace Xamarin.Forms
 
                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);
@@ -102,7 +103,8 @@ namespace Xamarin.Forms
                                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('('));
@@ -112,9 +114,11 @@ namespace Xamarin.Forms
                                        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);
                                        }
@@ -123,9 +127,11 @@ namespace Xamarin.Forms
                                        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);
                                        }
@@ -268,7 +274,13 @@ namespace Xamarin.Forms
 
                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
@@ -840,10 +852,10 @@ namespace Xamarin.Forms
                                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();
                                        }
                }
 
@@ -873,14 +885,6 @@ namespace Xamarin.Forms
                        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)
index ac3bfcba7382f0f20ec5c3dcad5702f62f3fccdb..f22f994b1cb3b17d5228475fe92018d296405bd0 100644 (file)
@@ -6,11 +6,8 @@ using Xamarin.Forms.StyleSheets;
 
 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))
@@ -49,11 +46,5 @@ namespace Xamarin.Forms
 
                        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
index 73951ae7a79195267a27f94c53eb3f1789308a7d..0b471295f0b89763346015751ae77f3bcf9b383f 100644 (file)
@@ -74,7 +74,7 @@ namespace Xamarin.Forms
                        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;
@@ -282,7 +282,7 @@ namespace Xamarin.Forms
                }
 
                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)
                {
@@ -291,7 +291,9 @@ namespace Xamarin.Forms
                                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)
@@ -403,7 +405,7 @@ namespace Xamarin.Forms
                                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();
@@ -449,20 +451,20 @@ namespace Xamarin.Forms
                                                // 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
                                                {
@@ -667,7 +669,7 @@ namespace Xamarin.Forms
                        {
                                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;
@@ -683,7 +685,7 @@ namespace Xamarin.Forms
                        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[]
                        {
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/IRotaryInteraction.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/IRotaryInteraction.cs
new file mode 100644 (file)
index 0000000..d72eba0
--- /dev/null
@@ -0,0 +1,9 @@
+using ElmSharp.Wearable;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public interface IRotaryInteraction
+       {
+               IRotaryActionWidget RotaryWidget { get; }
+       }
+}
\ No newline at end of file
index 7621f2d1f35a4cf3f8d26aa669cd2d00f883cc5e..6eaa096125eafb30253f20f666ca4d1418bce2b7 100644 (file)
@@ -7,10 +7,11 @@ using EBox = ElmSharp.Box;
 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;
@@ -27,6 +28,8 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                SnapPointsType _snapPoints;
                ESize _itemSize = new ESize(-1, -1);
 
+               public event EventHandler<ItemsViewScrolledEventArgs> Scrolled;
+
                public CollectionView(EvasObject parent) : base(parent)
                {
                        SetLayoutCallback(OnLayout);
@@ -41,6 +44,9 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        Scroller.SetContent(_innerLayout);
                }
 
+               public IRotaryActionWidget RotaryWidget { get => Scroller as IRotaryActionWidget; }
+
+
                public CollectionViewSelectionMode SelectionMode
                {
                        get => _selectionMode;
@@ -314,7 +320,14 @@ namespace Xamarin.Forms.Platform.Tizen.Native
 
                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()
@@ -522,9 +535,24 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        _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)
index 0cc386a87dd3e6307e4042b4e97e30f88e6ebb12..817abae25fed3bf35fe0c46946e4045ed3f44931 100644 (file)
@@ -62,7 +62,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
 
                        if (IsHorizontal)
                        {
-                               _scrollCanvasSize = new ESize(totalItemSize , _allocatedSize.Height);
+                               _scrollCanvasSize = new ESize(totalItemSize, _allocatedSize.Height);
                        }
                        else
                        {
@@ -338,6 +338,29 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        }
                }
 
+               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;
index 21ab0feca7b8b0c217ca858785a166fc0e54d3d3..ae72d8b7a31a087324181a9f503a558dbd13b5ca 100644 (file)
@@ -28,5 +28,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                void Reset();
 
                void ItemMeasureInvalidated(int index);
+
+               int GetVisibleItemIndex(int x, int y);
        }
 }
\ No newline at end of file
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Native/CollectionView/IndicatorView.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Native/CollectionView/IndicatorView.cs
new file mode 100644 (file)
index 0000000..1020350
--- /dev/null
@@ -0,0 +1,80 @@
+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);
+               }
+       }
+}
index 0ee4c48a439dc17dfcdb18352ffa182e8760b5ee..a3be7da28c6d61fa49a51343fc73c0cca56f6730 100644 (file)
@@ -296,6 +296,22 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        }
                }
 
+               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;
index 9bc3e08e4e926d3678d555ae0cfad5832508cf91..c7b39e3e281c98ebd23735fe8af1367ec7069179 100644 (file)
@@ -4,6 +4,9 @@ using System.Collections.Generic;
 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
 {
@@ -45,7 +48,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        internal TemplatedItemsList<ItemsView<Cell>, Cell> ListOfSubItems;
                }
 
-               class ScrollerExtension : Scroller
+               class ScrollerExtension : EScroller
                {
                        public ScrollerExtension(GenList scrollableLayout) : base(scrollableLayout)
                        {
@@ -97,7 +100,10 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                /// </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.
@@ -108,7 +114,39 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                /// <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.
@@ -122,14 +160,11 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                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()
                {
                }
 
@@ -167,7 +202,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                /// <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)
                        {
@@ -274,7 +309,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                /// 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)
                        {
@@ -307,7 +342,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                /// 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)
                        {
@@ -393,6 +428,11 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        return _footerElement;
                }
 
+               protected virtual void OnScrolled(object sender, EventArgs e)
+               {
+                       Scrolled?.Invoke(this, EventArgs.Empty);
+               }
+
                /// <summary>
                /// Handles the header deleted event.
                /// </summary>
index aef32a49d4898fbb8b3ffdbc3f072226d76990a0..9838c8d92c875532009566def6c7f0a61dc8b683 100644 (file)
@@ -13,6 +13,10 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                {
                }
 
+               protected Scroller()
+               {
+               }
+
                protected override void OnRealized()
                {
                        base.OnRealized();
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Native/Watch/FormsMoreOptionItem.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Native/Watch/FormsMoreOptionItem.cs
new file mode 100644 (file)
index 0000000..4eb64aa
--- /dev/null
@@ -0,0 +1,9 @@
+using ElmSharp.Wearable;
+
+namespace Xamarin.Forms.Platform.Tizen.Native.Watch
+{
+       public class FormsMoreOptionItem : MoreOptionItem
+       {
+               public ToolbarItem ToolbarItem { get; set; }
+       }
+}
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Native/Watch/WatchListView.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Native/Watch/WatchListView.cs
new file mode 100644 (file)
index 0000000..ee79e7b
--- /dev/null
@@ -0,0 +1,127 @@
+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;
+                       }
+               }
+       }
+}
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Native/Watch/WatchScroller.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Native/Watch/WatchScroller.cs
new file mode 100644 (file)
index 0000000..506766e
--- /dev/null
@@ -0,0 +1,46 @@
+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;
+               }
+       }
+}
index 6e80fe1fef5eee83d620406cf535781313c23016..60d42b266c65b41b714e31b858e75a84ea2dae88 100644 (file)
@@ -30,6 +30,7 @@ using Xamarin.Forms.Platform.Tizen;
 [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))]
@@ -42,6 +43,7 @@ using Xamarin.Forms.Platform.Tizen;
 [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))]
index 63138d7b8e28008fb05625bfb79718329f8cc938..57eb8dcc7be73948cfafe8ae192bac1482d736cd 100644 (file)
@@ -1,4 +1,6 @@
-namespace Xamarin.Forms.Platform.Tizen
+using System;
+
+namespace Xamarin.Forms.Platform.Tizen
 {
        public class CarouselViewRenderer : ItemsViewRenderer<CarouselView, Native.CarouselView>
        {
@@ -7,7 +9,8 @@
                        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);
                        }
                }
 
index 853417484edbbd62aa340e22fafea472955a9d7f..6ac21a5c5878648a06b0fe61b6dba8cdf3e4cf2a 100644 (file)
@@ -28,8 +28,8 @@ namespace Xamarin.Forms.Platform.Tizen
                                {
                                        IsSingleLine = false,
                                };
-                               entry.Focused += OnFocused;
-                               entry.Unfocused += OnUnfocused;
+                               entry.Focused += OnEntryFocused;
+                               entry.Unfocused += OnEntryUnfocused;
                                entry.TextChanged += OnTextChanged;
                                entry.Unfocused += OnCompleted;
                                entry.PrependMarkUpFilter(MaxLengthFilter);
@@ -47,8 +47,8 @@ namespace Xamarin.Forms.Platform.Tizen
                                {
                                        Control.TextChanged -= OnTextChanged;
                                        Control.BackButtonPressed -= OnCompleted;
-                                       Control.Unfocused -= OnUnfocused;
-                                       Control.Focused -= OnFocused;
+                                       Control.Unfocused -= OnEntryUnfocused;
+                                       Control.Focused -= OnEntryFocused;
                                }
                        }
                        base.Dispose(disposing);
@@ -66,7 +66,7 @@ namespace Xamarin.Forms.Platform.Tizen
 
                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.
@@ -74,12 +74,12 @@ namespace Xamarin.Forms.Platform.Tizen
                        _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();
                }
 
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/IndicatorViewRenderer.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/IndicatorViewRenderer.cs
new file mode 100644 (file)
index 0000000..d339695
--- /dev/null
@@ -0,0 +1,59 @@
+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);
+               }
+       }
+}
index ee96212ecc37040ee732ebad64c507616d5ab640..bf4295d163abe1302236f4242e278ca724bad66f 100644 (file)
@@ -1,4 +1,5 @@
-using System.Collections.Specialized;
+using System;
+using System.Collections.Specialized;
 using System.Linq;
 
 using Xamarin.Forms.Platform.Tizen.Native;
@@ -26,6 +27,7 @@ namespace Xamarin.Forms.Platform.Tizen
                        if (Control == null)
                        {
                                SetNativeControl(CreateNativeControl(Forms.NativeParent));
+                               Control.Scrolled += OnScrolled;
                        }
                        if (e.NewElement != null)
                        {
@@ -44,6 +46,7 @@ namespace Xamarin.Forms.Platform.Tizen
                                {
                                        Element.ScrollToRequested -= OnScrollToRequest;
                                        ItemsLayout.PropertyChanged -= OnLayoutPropertyChanged;
+                                       Control.Scrolled -= OnScrolled;
                                }
                                if (_observableSource != null)
                                {
@@ -93,6 +96,11 @@ namespace Xamarin.Forms.Platform.Tizen
                {
                }
 
+               void OnScrolled(object sender, ItemsViewScrolledEventArgs e)
+               {
+                       Element.SendScrolled(e);
+               }
+
                void OnScrollToRequest(object sender, ScrollToRequestEventArgs e)
                {
                        if (e.Mode == ScrollToMode.Position)
index 5cc1f4fb51c6aacab79e8044fd1a2eaf4b85c43e..94dfc14543c052223102bd2c62aa04ab4cdff2d6 100644 (file)
@@ -43,6 +43,8 @@ namespace Xamarin.Forms.Platform.Tizen
                        RegisterPropertyHandler("HeaderElement", UpdateHeader);
                        RegisterPropertyHandler("FooterElement", UpdateFooter);
                        RegisterPropertyHandler(ListView.SelectionModeProperty, UpdateSelectionMode);
+                       RegisterPropertyHandler(ListView.VerticalScrollBarVisibilityProperty, UpdateVerticalScrollBarVisibility);
+                       RegisterPropertyHandler(ListView.HorizontalScrollBarVisibilityProperty, UpdateHorizontalScrollBarVisibility);
                }
 
                /// <summary>
@@ -54,7 +56,7 @@ namespace Xamarin.Forms.Platform.Tizen
                {
                        if (Control == null)
                        {
-                               SetNativeControl(new Native.ListView(Forms.NativeParent));
+                               SetNativeControl(CreateNativeControl());
 
                                Control.Scrolled += OnScrolled;
                                Control.ItemSelected += OnListViewItemSelected;
@@ -78,6 +80,18 @@ namespace Xamarin.Forms.Platform.Tizen
                        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.
@@ -410,5 +424,15 @@ namespace Xamarin.Forms.Platform.Tizen
                                Control.IsHighlight = true;
                        }
                }
+
+               void UpdateVerticalScrollBarVisibility()
+               {
+                       Control.VerticalScrollBarVisibility = Element.VerticalScrollBarVisibility.ToNative();
+               }
+
+               void UpdateHorizontalScrollBarVisibility()
+               {
+                       Control.HorizontalScrollBarVisibility = Element.HorizontalScrollBarVisibility.ToNative();
+               }
        }
 }
\ No newline at end of file
index 3b575fb1b11ea6865d88a13f5f34bd1acb0e4bf3..4cda34ea873e0733aa1524cc4b3483e5efb71074 100644 (file)
@@ -1,4 +1,7 @@
 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
@@ -12,6 +15,7 @@ namespace Xamarin.Forms.Platform.Tizen
                /// Native control which holds the contents.
                /// </summary>
                Native.Page _page;
+               Lazy<MoreOption> _moreOption;
 
                /// <summary>
                /// Default constructor.
@@ -32,6 +36,22 @@ namespace Xamarin.Forms.Platform.Tizen
                        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)
@@ -40,6 +60,21 @@ namespace Xamarin.Forms.Platform.Tizen
                                {
                                        _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);
                }
@@ -61,9 +96,26 @@ namespace Xamarin.Forms.Platform.Tizen
                        // 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
@@ -78,6 +130,52 @@ namespace Xamarin.Forms.Platform.Tizen
                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
diff --git a/src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/RadioButtonRenderer.cs b/src/XSF/Xamarin.Forms.Platform.Tizen/Renderers/RadioButtonRenderer.cs
new file mode 100644 (file)
index 0000000..81c6524
--- /dev/null
@@ -0,0 +1,117 @@
+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");
+               }
+       }
+}
index 5d3092ce4266f8ce6539a6699a4f3eb4c85c83de..815dc82b0e6da40c27bb07fb18e85ac0b5dd8e56 100644 (file)
@@ -39,7 +39,7 @@ namespace Xamarin.Forms.Platform.Tizen
                {
                        if (Control == null)
                        {
-                               SetNativeControl(new NScroller(Forms.NativeParent));
+                               SetNativeControl(CreateNativeControl());
                                Control.Scrolled += OnScrolled;
                                _scrollCanvas = new NBox(Control);
                                _scrollCanvas.LayoutUpdated += OnContentLayoutUpdated;
@@ -61,6 +61,19 @@ namespace Xamarin.Forms.Platform.Tizen
                        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)
@@ -190,17 +203,20 @@ namespace Xamarin.Forms.Platform.Tizen
 
                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)
                        {
index 664666f611ab112d74b81930d3005df626d2aa00..285f76b5729934c18335f117ea8cb8d98594f439 100644 (file)
@@ -1,8 +1,11 @@
+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
 {
@@ -92,5 +95,31 @@ 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
+}
index 0e13ce83e25be1dd550cf3e85471592184b13adf..06a92812ce8bc2b3dc99365fa4312f6ef5d12c41 100644 (file)
@@ -4,6 +4,7 @@ using System.ComponentModel;
 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;
@@ -693,7 +694,7 @@ namespace Xamarin.Forms.Platform.Tizen
                /// <summary>
                /// Handles focus events.
                /// </summary>
-               void OnFocused(object sender, EventArgs e)
+               protected virtual void OnFocused(object sender, EventArgs e)
                {
                        if (null != Element)
                        {
@@ -704,7 +705,7 @@ namespace Xamarin.Forms.Platform.Tizen
                /// <summary>
                /// Handles unfocus events.
                /// </summary>
-               void OnUnfocused(object sender, EventArgs e)
+               protected virtual void OnUnfocused(object sender, EventArgs e)
                {
                        if (null != Element)
                        {
index ad57faecf07fa48cd9abbe3832bd2dec12b19b64..0912efba526748dc545a2a8514f04eecc51b0166 100644 (file)
@@ -106,6 +106,8 @@ namespace Xamarin.Forms.Platform.Tizen
                                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());
index 0b336c678c56f33ffd8fc33c5dbcb368e549c8b0..dd9e43fd5b50ac8b4711de9a0c544209f1f2b0a3 100644 (file)
@@ -88,6 +88,12 @@ namespace Xamarin.Forms.Platform.Tizen
                        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)
@@ -247,5 +253,12 @@ namespace Xamarin.Forms.Platform.Tizen
                {
                        return Platform.GetNativeSize(view, widthConstraint, heightConstraint);
                }
+
+               public OSAppTheme RequestedTheme => OSAppTheme.Unspecified;
+
+               static MD5 CreateChecksum()
+               {
+                       return MD5.Create();
+               }
        }
 }
\ No newline at end of file
diff --git a/src/XSF/Xamarin.Forms.Xaml/MarkupExtensions/OnAppThemeExtension.cs b/src/XSF/Xamarin.Forms.Xaml/MarkupExtensions/OnAppThemeExtension.cs
new file mode 100644 (file)
index 0000000..8bb51b1
--- /dev/null
@@ -0,0 +1,139 @@
+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
diff --git a/src/XSF/theme/tizen-circular-ui-theme.edj b/src/XSF/theme/tizen-circular-ui-theme.edj
new file mode 100644 (file)
index 0000000..32e66ab
Binary files /dev/null and b/src/XSF/theme/tizen-circular-ui-theme.edj differ