MacOS (#650)
authorRui Marinho <me@ruimarinho.net>
Thu, 26 Jan 2017 15:33:15 +0000 (15:33 +0000)
committerKangho Hur <kangho.hur@samsung.com>
Fri, 24 Mar 2017 04:15:53 +0000 (13:15 +0900)
* [MacOS] Add SwitchRenderer

* [MacOS] Add TimePickerRenderer

* [MacOS] Cleanup TimePcikerRender

* [MacOS] Add WebViewRenderer

* [MacOS] Add Javascript evaluate to webview

* [MacOS] Fix build error on WebViewRenderer

* [MacOS] Add Base and TextCell renderers

* [MacOS] Start on ListViewRenderer

* [MacOS] Cleanup

* [MacOS] Vertical center text on default NSTextField

* [MacOS] Center NSTextField vertically

* [MacOS] Add ImageCellRenderer

* [MacOS] Add SwitchCellRenderer

* [MacOS] Add SwitchCellRenderer

* [iOS] Allow to set background color on other CellRenderers

* [MacOS] Fix selection mode on ListView

* [MacOS] Set background on Entry of entry cell

* [MacOS] Fix casting bug on CellRenderer

* [MacOS] Other fix on CellRenderer background

* [MacOS] Add ViewCellRenderer

* [MacOS] Fixes and cleanup on cells

* [MacOS] Add NSScrollView so NSTableView can scroll

* [MacOS] Add HeaderView to ListView

* [MacOS] Cleanup

* [Controls] Add Header support to ListView

* [MacOS] NSView reuse on NSTableView

* [MacOS] Some fix on layour order

* [MacOS] Add CarouselPageRenderer

* [MacOS] Implement EventTracker on PageRenderer

* [MacOS] Cleanup CarouselPageRenderer

* [MacOS] Add MasterDetailPage renderer

* [MacOS] MDP renderer don't allow drag of splitter

* [MacOS] Add TabbedPage renderer

* [MacOS] Initial sketch of NavigationPageRenderer

* [MacOS] Send disappearing of CurrentPage on Dispose on NavigationPageRenderer

* [MacOS] Add Gallery page for Mac

* [MacOS] Add MacOSExpressionSearch

* [MacOS] Fix ColorExtension

* [MacOS] Fix MDP renderer layout

* [MacOS] Implement native selection on ListViewRenderer

* [MacOS] Deselect a item on NSTableView

* [MacOS] Remove previous SplitViewItems

* [MacOS] Fix navigationpage height

* [MacOS] Add toolbar for NavigationPageRenderer

* [MacOS] Don't remove selection for now (crashing)

* [MacOS] Refactor page and back button title on NavigationPageRenderer

* [MacOS] Fix bug when native navigate back

* [MacOS] Hide layer when transition

* [MacOS] ListviewRenderer fix BbackgroundColor

* [MacOS] Fix background on ScrollViewRenderer

* [MacOS] Fix header measure on ListViewRenderer

* [MacOS] Add Mac twitter demo

* [Controls] Spaces for easy reading

* [MacOS] More xaml cleanup

* [Core] Add Mac as aTargetPlatform

* [MacOS] Add alerts and actionsheets

* [MacOS] Add GestureRecognizers

* [MacOS] Fix Layout issues when adding children, enable transformations

* [MacOS] Fix title on tab item, move to tabbed navigation based on segmented control

* [MacOS] Hide toolbar when not needed, this allows to work with tabbed page, cleanup

* [MacOS] Add NativeBindings and NativeViewWrapper

* [MacOS] Fix AssemblyInfo

* [MacOS] FIX NRE on SetBackgroundColor BoxView

* [MacOS] Fix NavigationPageRenderer

* [MacOS] Fix build

* [MacOS] Also update page when it resizes

* [MacOS] Add LayoutRenderer for handle items position when the bounds change.

* [MacOS] Refactor/Cleanup

* [MacOS] Add toolbar items support to NavigationPage

* [MacOS] Resize images for TabViewITems

* [MacOS] Fix TabbedPage resize issues , allow users to override some features when creating TVI

* [MacOS] Fix hide/show Navigation toolbar

* [MacOS] Redo CarouselPageRenderer with NSPageController

* [MacOS] Add support for Modal pages

* [MacOS] Refactor navigation from platform

* [Nuget] Add nuget for MacOS

* [Nuget]Fix nuspec

* [Nuget] Add variables for CI

* [Controls] Remove MainMenu from MacOS

* [MacOS] Add TableView renderer (no headers yet)

* [MacOS] Refactoring, marking extensions as internal

* [MacOS] Add group headers for TableViewRenderer

* [MacOS] Workaround for updates on listview collection

* [MacOS] Handle updates of rows in the ListViewRenderer properly

* [MacOS] Fix navigation animation

* Fix navigation header issues with modal pages

* [MacOS] Fix MDP issues with resizing

* [MacOS] Fix general dispose

* [MacOS] Add a ViewControllerWrapper for NSSplitView

* [MacOS] MDP renderer fix animation

* [MacOS] Fix ListView selection bug

* [MacOS] Fix rendering MDP Layout inside wrappers

* [MacOS] Re write the MainToolbar handler

* [MacOS] Don't use Sierra new extensions so we can run in stable channel

* [MacOS] Another way to hide the toolbar (smarter i think)

* [MacOS] Fix MDP bug and remove debug color

* [Controls] Add HanselForms sample

* [MacOS] Fix NRE WebviewRenderer

* [MacOS] Fix uneven rows on ListView renderer

* [MacOS] Fix NRE on load (can+t find the reason this happens)

* [MacOS] Fix uneven rows

* [MacOS] Fix header sizing on ListViewRenderer

* [Controls] More stuff on HanselForms

* [MacOS] Remove warning from ListViewRenderer

* [MacOS] Fix PageRenderer bug double init

* [MacOS] Don't calculate height if RowHeight is provided

* [Controls] More Hanselforms stuff

* [MacOS] Once again a new implementation for the NavigationBar, this time using a custom view to support BackgroundColor

* [MacOS] Fix build

* [MacOS] Refactoring AwesomeBar related controls

* Fix build

* [MacOS] NavigationBar update background and t test colors

* [MacOS] Fix when we remove navigation so it works when the NavigationRenderer wasn't removed from the parent controller like in a TabbedPage

* [MacOS] Add support for ListView  grouping

* [MacOS] Fix image extension method.

* [MacOS] Add base Maps project

* [MacOS] Export MapRenderer

* [MacOS] Add pin click and geocoderbacked for Maps

* [MacOS] Add extra binding project for API not in stable.

* [MacOS] Add MacOS Maps lib

* [MacOS]Fix build on alpha

* [MacOS] Remove MacOS Maps extra binding

* [UITest] Basic macOS setup

* [UITest] Add MacOSApp wrapper implementation

* [MacOS] Set AutomationID

* [UITests] Add ActionSheetUITests to MacOS UITest

* [MacOS] Fix bug on Picker

* [UITests] Link basic uitest basefixture and related files

* [MacOS] Fix pickers reuse

* [UItests] Fix MacOS app path

* [UITest] Ignore UItest for appearing on macOS for now

* [UITest] Update macOS for 2.0.3

* [UITest] Refactor EnterText MacOS app

* [UITest]Fix ViewQuery on MacOS

* [UITest]Fix IsEnabled UItest on macOS

* [UITest] Implement Enter, mark some tests inconclusive fix others

* [MacOS] Implement Entry Completed event

* [UITests] Fix UITest for IsVisible, ignore ToolbarItem test for now

* [UITests] Fix ISVisible again add extra category

* [Controls] Cleanup macOS gallery

* [MacOS] Fix Assembly info

* [Docs] Fix docs

* Fix build

* [Nuget] Fix nuspec

* [Controls] Link files on MacOS

* [Core] Update Forms stack before firing a event saying page was removed, possible breaking change

* [MacOS] Implement RemovePage on NavigationPAgeRenderer

* [UItest] Ignore some , implement back on MacOS UITest app

* [MacOS] Add default back button name (needs to be translated)

* [MacOS] Fix dispose

* [UITest] Make 29257 work on MacOS

* [MacOS] Rename stuff

* [MacOS] More renaming and cleanup

* [MacOS] Share implementations for iOS

* [MacOS] Reuse more IOS extensions

* [MacOS] Reuse FontExtensions

* [MacOS] Share NativeViewWrapper related stuff

* [MAcOS] Share event args and ExportRenderer

* [MacOS] Share platform effect

* [MacOS] Fix build

* [Docs]Fixing docs

* [MacOS] Fix ViewCell reuse

* [Core] Support ListView CachingStrategy on MacOS

* [MacOS] Fix issues with TextCell and ImageCell (we can’t set null to a NSControl value)

* [MacOS] Fix MDP child sizing bug
[UITest] Query marked by id and text

* [MacOS] Comment test related with context actions

* [MacOS] Implement missing stuff on ticker

* [MacOS] Make sure VisualElemenTracker calls the ticker update

* [UITests]Ignore context actions and not possible to test

* [MacOS] Fix Grouping bug on Listview

* [MacOS] Fix selection on Listview when using grouping

* [MacOS] Update navbar when page is popped

* [MacOS] Cleanup NavigationBar

* [Controls] More info on exceptions

* [MacOS] Fix bug animation pop modal

* [MacOS] Bring back BackgroundColor of NavigationBar

* [MacOS] Fix UITest animation delay

* [MacOS] Treat warnings as errors

* [MacOS] Center title on toolbar

* [Core] Add Platform configuration specific for MacOS

* [MacOS] Implement TabbedPage platform specific to handle TabItems on NavigationPage bar

* [MacOS] Fix warning

* [MacOS] Fix bug on SearchBar color

* [MacOS]Fix build

* [MacOS] remove extra dll from maps

* [Docs] Update docs

* [MacOS]Cleanup and refactoring

* Revert "[MacOS] remove extra dll from maps"

This reverts commit 73b948937001fea3f28449a963d0b94943e07aa0.

* [MacOS] Fix wrong refactoring

* [MacOS] Remove gallery and uitest project

* [MacOS] dix formatting

* [MacOS] Remove extra stuff

* Merge branch 'master' into macOS-gallery

* [MacOS] Fix rebase

* [MacOS] Fix TargetPlatform

* fix docs

* [MacOS] Fix bug on TabbedPageRenderer no title

* [MacOS] Remove FormsNSView

* [MacOS] Cleanup on dispose on MDP renderer

* [MacOS] Update current page when source changes

* [MacOS] More cleanup

* [MacOS] Make sure we show the previous page when popping a Modal

* [MacOS] Fix issue with sizing the Header and visibility, remove for now header renderer reuse

* [MacOS] Clean CustomNSTableView

* [MacOS] Share LabelRenderer with iOS

* [MacOS] Share ResourcesProvider with iOS

* [MAcoS] Share VisualElementPackager with iOS

* [MacOS] Share ViewRenderer with iOS

* [MacOS] Merge with VisualElementTracker from iOS

* [MacOS] Merge with EventTracker from iOS

* [MacOS] Merge with VisualElementRenderer of iOS

* [MacOS] Make sure we always have a layer

* [MacOS] Fix Tracker merge with iOS version

* [MacOS] Fix bug with tabbed page on modal without navigation

* [Core] Rever change on core

* [MacOS] Clear renderers before setting them MDP

* [MacOS] Update tabbedPage ContainerArea

* [MacOS] Fix ListViewRenderer

* [MacOS] Make sure we don’t pass null to TextField string value

* [MacOS] Support for multiple clicks in same selected item on NSTableView

* [MacOS] Support Focus on EntryRenderer

* [MacOS] Fix index bug on TablevIewDataSource

* [MacOS] Fix SelectedItem TableViewDataSource

* [Nuget] Add Mac to Maps nuspec

* [Nuget]Fix path

* [macOS] Fixed Tab NSImage crash in TabbedPageRenderer (#705)

* [macOS] Fixed Tab NSImage crash in TabbedPageRenderer

* Coding Style

* Coding Style

* [MacOS] Fix previous merge with master

* [MacOS] Possible simple fix for click on views overlapping

* [MacOS] Rename to IsOnViewCell

* [MacOS] Cleanup, Address feedback from Samantha’s review

124 files changed:
.nuspec/Xamarin.Forms.Maps.nuspec
.nuspec/Xamarin.Forms.nuspec
Xamarin.Forms.Controls/GalleryPages/MacOSTestGallery.cs [new file with mode: 0644]
Xamarin.Forms.Core/Device.cs
Xamarin.Forms.Core/ListView.cs
Xamarin.Forms.Core/PlatformConfiguration/ExtensionPoints.cs
Xamarin.Forms.Core/PlatformConfiguration/macOSSpecific/TabbedPage.cs [new file with mode: 0644]
Xamarin.Forms.Core/PlatformConfiguration/macOSSpecific/TabsStyle.cs [new file with mode: 0644]
Xamarin.Forms.Core/Properties/AssemblyInfo.cs
Xamarin.Forms.Core/TargetPlatform.cs
Xamarin.Forms.Core/Xamarin.Forms.Core.csproj
Xamarin.Forms.Maps.MacOS.Extra/ApiDefinition.cs [new file with mode: 0644]
Xamarin.Forms.Maps.MacOS.Extra/Properties/AssemblyInfo.cs [new file with mode: 0644]
Xamarin.Forms.Maps.MacOS.Extra/Xamarin.Forms.Maps.MacOS.Extra.csproj [new file with mode: 0644]
Xamarin.Forms.Maps.MacOS/Libs/Xamarin.Forms.Maps.MacOS.Extra.dll [new file with mode: 0755]
Xamarin.Forms.Maps.MacOS/Properties/AssemblyInfo.cs [new file with mode: 0644]
Xamarin.Forms.Maps.MacOS/Xamarin.Forms.Maps.macOS.csproj [new file with mode: 0644]
Xamarin.Forms.Maps.iOS/FormsMaps.cs
Xamarin.Forms.Maps.iOS/GeocoderBackend.cs
Xamarin.Forms.Maps.iOS/MapPool.cs
Xamarin.Forms.Maps.iOS/MapRenderer.cs
Xamarin.Forms.Maps/Properties/AssemblyInfo.cs
Xamarin.Forms.Platform.MacOS/CADisplayLinkTicker.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Cells/CellNSView.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Cells/CellRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Cells/EntryCellRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Cells/ImageCellRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Cells/NSTableViewCellStyle.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Cells/SwitchCellRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Cells/TextCellRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Cells/ViewCellNSView.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Cells/ViewCellRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Controls/FormsImageView.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Controls/FormsPageControllerDelegate.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Controls/MacOSOpenGLView.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Controls/NSToolbarItemGroup.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Controls/NavigationChildPageWrapper.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Controls/ScrollViewScrollChangedEventArgs.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Controls/VerticallyCenteredTextFieldCell.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Extensions/AlignmentExtensions.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Extensions/ButtonExtensions.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Extensions/NSButtonExtensions.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Extensions/NSImageExtensions.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Extensions/NSScrollViewExtensions.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Extensions/NSTableViewExtensions.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Extensions/NSTextFieldExtensions.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Extensions/NSViewControllerExtensions.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Extensions/PageExtensions.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/FormsApplicationDelegate.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/ImageSourceHandlers.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/ModalPageTracker.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/NativeToolbarTracker.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Platform.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/PlatformNavigation.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/PlatformRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Properties/AssemblyInfo.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/ActivityIndicatorRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/BoxViewRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/ButtonRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/CarouselPageRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/CustomNSTableHeaderView.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/DatePickerRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/DefaultRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/EditorRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/EntryRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/FrameRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/ImageRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/LayoutRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/ListViewDataSource.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/ListViewRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/MasterDetailPageRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/NSPageContainer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/NavigationPageRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/OpenGLViewRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/PageControllerDelegate.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/PageRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/PickerRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/ProgressBarRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/ScrollViewRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/SearchBarRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/SliderRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/StepperRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/SwitchRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/TabbedPageRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/TableViewDataSource.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/TableViewRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/TimePickerRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Renderers/WebViewRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.MacOS/Xamarin.Forms.Platform.macOS.csproj [new file with mode: 0644]
Xamarin.Forms.Platform.iOS/Deserializer.cs
Xamarin.Forms.Platform.iOS/EffectUtilities.cs
Xamarin.Forms.Platform.iOS/ElementChangedEventArgs.cs
Xamarin.Forms.Platform.iOS/EventTracker.cs
Xamarin.Forms.Platform.iOS/ExportRendererAttribute.cs
Xamarin.Forms.Platform.iOS/Extensions/ColorExtensions.cs
Xamarin.Forms.Platform.iOS/Extensions/DateExtensions.cs
Xamarin.Forms.Platform.iOS/Extensions/LayoutExtensions.cs
Xamarin.Forms.Platform.iOS/Extensions/PlatformConfigurationExtensions.cs
Xamarin.Forms.Platform.iOS/Extensions/UIViewExtensions.cs
Xamarin.Forms.Platform.iOS/Forms.cs
Xamarin.Forms.Platform.iOS/IVisualElementRenderer.cs
Xamarin.Forms.Platform.iOS/NativeValueConverterService.cs
Xamarin.Forms.Platform.iOS/NativeViewPropertyListener.cs
Xamarin.Forms.Platform.iOS/NativeViewWrapper.cs
Xamarin.Forms.Platform.iOS/NativeViewWrapperRenderer.cs
Xamarin.Forms.Platform.iOS/PlatformEffect.cs
Xamarin.Forms.Platform.iOS/RendererPool.cs
Xamarin.Forms.Platform.iOS/Renderers/FontExtensions.cs
Xamarin.Forms.Platform.iOS/Renderers/FormattedStringExtensions.cs
Xamarin.Forms.Platform.iOS/Renderers/LabelRenderer.cs
Xamarin.Forms.Platform.iOS/ResourcesProvider.cs
Xamarin.Forms.Platform.iOS/ViewInitializedEventArgs.cs
Xamarin.Forms.Platform.iOS/ViewRenderer.cs
Xamarin.Forms.Platform.iOS/VisualElementPackager.cs
Xamarin.Forms.Platform.iOS/VisualElementRenderer.cs
Xamarin.Forms.Platform.iOS/VisualElementTracker.cs
Xamarin.Forms.sln
docs/Xamarin.Forms.Core/Xamarin.Forms.PlatformConfiguration.macOSSpecific/TabbedPage.xml [new file with mode: 0644]
docs/Xamarin.Forms.Core/Xamarin.Forms.PlatformConfiguration/macOS.xml [new file with mode: 0644]
docs/Xamarin.Forms.Core/Xamarin.Forms/Device.xml
docs/Xamarin.Forms.Core/Xamarin.Forms/TabsStyle.xml [new file with mode: 0644]
docs/Xamarin.Forms.Core/index.xml
docs/Xamarin.Forms.Core/ns-Xamarin.Forms.PlatformConfiguration.macOSSpecific.xml [new file with mode: 0644]
docs/Xamarin.Forms.Maps/index.xml

index 9e7aef4..7221c1c 100644 (file)
@@ -30,7 +30,7 @@
       </group>
     </dependencies>
     <references>
-      <group targetFramework="portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10">
+      <group targetFramework="portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20">
         <reference file="Xamarin.Forms.Maps.dll" />
       </group>
       <group targetFramework="Xamarin.iOS10">
         <reference file="Xamarin.Forms.Maps.dll" />
         <reference file="Xamarin.Forms.Maps.WP8.dll" />
       </group>
+      <group targetFramework="Xamarin.Mac">
+               <reference file="Xamarin.Forms.Maps.dll" />
+        <reference file="Xamarin.Forms.Maps.macOS.dll" />
+        <reference file="Xamarin.Forms.Maps.MacOS.Extra.dll" />
+      </group>
     </references>
   </metadata>
   <files>
-    <file src="..\Xamarin.Forms.Maps\bin\$Configuration$\Xamarin.Forms.Maps.dll" target="lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10" />
-    <file src="..\docs\Xamarin.Forms.Maps.xml" target="lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10" />
+    <file src="..\Xamarin.Forms.Maps\bin\$Configuration$\Xamarin.Forms.Maps.dll" target="lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20" />
+    <file src="..\docs\Xamarin.Forms.Maps.xml" target="lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20" />
 
     <file src="..\Xamarin.Forms.Maps.Android\bin\$Configuration$\Xamarin.Forms.Maps.Android.dll" target="lib\MonoAndroid10" />
     <file src="..\Xamarin.Forms.Maps\bin\$Configuration$\Xamarin.Forms.Maps.dll" target="lib\MonoAndroid10" />
     <file src="..\Xamarin.Forms.Maps.Design\bin\$Configuration$\Xamarin.Forms.Maps.Design.dll" target="lib\win81\Design" />
     <file src="..\Xamarin.Forms.Maps.Design\bin\$Configuration$\Xamarin.Forms.Maps.Design.dll" target="lib\uap10.0\Design" />
 
+       <file src="..\Xamarin.Forms.Maps.MacOS\bin\$Configuration$\Xamarin.Forms.Maps.macOS.dll" target="lib\Xamarin.Mac" />
+       <file src="..\Xamarin.Forms.Maps.MacOS\bin\$Configuration$\Xamarin.Forms.Maps.MacOS.Extra.dll" target="lib\Xamarin.Mac" />
+       <file src="..\Xamarin.Forms.Maps\bin\$Configuration$\Xamarin.Forms.Maps.dll" target="lib\Xamarin.Mac" />
+    <file src="..\docs\Xamarin.Forms.Maps.xml" target="lib\Xamarin.Mac" />
 
   </files>
 </package>
index ce12380..879280a 100644 (file)
         <reference file="Xamarin.Forms.Xaml.dll" />
         <reference file="Xamarin.Forms.Platform.WP8.dll" />
       </group>
+         <group targetFramework="Xamarin.Mac">
+               <reference file="Xamarin.Forms.Core.dll" />
+        <reference file="Xamarin.Forms.Platform.MacOS.dll" />
+        <reference file="Xamarin.Forms.Platform.dll" />
+        <reference file="Xamarin.Forms.Xaml.dll" />
+      </group>
     </references>
   </metadata>
   <files>
        <file src="..\Xamarin.Forms.Platform.UAP\bin\$Configuration$\PageControl.xbf" target="lib\uap10.0\Xamarin.Forms.Platform.UAP" />
        <file src="..\Xamarin.Forms.Platform.UAP\bin\$Configuration$\Resources.xbf" target="lib\uap10.0\Xamarin.Forms.Platform.UAP" />
        <file src="..\Xamarin.Forms.Platform.UAP\bin\$Configuration$\FormsTextBoxStyle.xbf" target="lib\uap10.0\Xamarin.Forms.Platform.UAP" />
-         <file src="..\Xamarin.Forms.Platform.UAP\bin\$Configuration$\AutoSuggestStyle.xbf" target="lib\uap10.0\Xamarin.Forms.Platform.UAP" />
+       <file src="..\Xamarin.Forms.Platform.UAP\bin\$Configuration$\AutoSuggestStyle.xbf" target="lib\uap10.0\Xamarin.Forms.Platform.UAP" />
        <file src="..\Xamarin.Forms.Platform.UAP\bin\$Configuration$\MasterDetailControlStyle.xbf" target="lib\uap10.0\Xamarin.Forms.Platform.UAP" />
        <file src="..\Xamarin.Forms.Platform.UAP\bin\$Configuration$\PageControlStyle.xbf" target="lib\uap10.0\Xamarin.Forms.Platform.UAP" />
        <file src="..\Xamarin.Forms.Platform.UAP\bin\$Configuration$\TabbedPageStyle.xbf" target="lib\uap10.0\Xamarin.Forms.Platform.UAP" />
+       
+        <!--Mac-->
+       <file src="..\Xamarin.Forms.Core\bin\$Configuration$\Xamarin.Forms.Core.dll" target="lib\Xamarin.Mac" />
+       <file src="..\Xamarin.Forms.Xaml\bin\$Configuration$\Xamarin.Forms.Xaml.dll" target="lib\Xamarin.Mac" />
+       <file src="..\Xamarin.Forms.Platform.MacOS\bin\$Configuration$\Xamarin.Forms.Platform.MacOS.dll" target="lib\Xamarin.Mac" />
+       <file src="..\Xamarin.Forms.Platform.MacOS\bin\$Configuration$\Xamarin.Forms.Platform.dll" target="lib\Xamarin.Mac" />
 
     <file src="..\Xamarin.Forms.Core\bin\$Configuration$\Xamarin.Forms.Core.dll" target="lib\uap10.0" />
     <file src="..\docs\Xamarin.Forms.Core.xml" target="lib\uap10.0" />
diff --git a/Xamarin.Forms.Controls/GalleryPages/MacOSTestGallery.cs b/Xamarin.Forms.Controls/GalleryPages/MacOSTestGallery.cs
new file mode 100644 (file)
index 0000000..3b44683
--- /dev/null
@@ -0,0 +1,401 @@
+using System;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms.Controls
+{
+       public class MacOSTestGallery : ContentPage
+       {
+               public MacOSTestGallery()
+               {
+                       mainDemoStack.Children.Add(MakeNewStackLayout());
+                       var items = new List<MyItem1>();
+                       for (int i = 0; i < 5000; i++)
+                       {
+                               items.Add(new MyItem1 { Reference = "Hello this is a big text " + i.ToString(), ShowButton = i % 2 == 0, Image = "bank.png" });
+                       }
+
+                       var header = new Label { Text = "HELLO HEADER ", FontSize = 40, BackgroundColor = Color.Pink };
+                       var lst4 = new ListView { Header = header, ItemTemplate = new DataTemplate(typeof(DemoViewCell)), BackgroundColor = Color.Yellow, HeightRequest = 300, RowHeight = 50, ItemsSource = items, };
+
+                       var lst = new ListView { ItemTemplate = new DataTemplate(typeof(DemoEntryCell)), BackgroundColor = Color.Yellow, HeightRequest = 300, RowHeight = 50, ItemsSource = items, };
+                       var lst1 = new ListView { ItemTemplate = new DataTemplate(typeof(DemoTextCell)), BackgroundColor = Color.Yellow, HeightRequest = 300, RowHeight = 50, ItemsSource = items, };
+                       var lst2 = new ListView { ItemTemplate = new DataTemplate(typeof(DemoSwitchCell)), BackgroundColor = Color.Yellow, HeightRequest = 300, RowHeight = 50, ItemsSource = items, };
+                       var lst3 = new ListView { ItemTemplate = new DataTemplate(typeof(DemoImageCell)), BackgroundColor = Color.Yellow, HeightRequest = 300, RowHeight = 50, ItemsSource = items, };
+
+                       var bigbUtton = new Button { WidthRequest = 200, HeightRequest = 300, Image = "bank.png" };
+
+                       var picker = new DatePicker();
+
+                       var timePicker = new TimePicker { Format = "T", Time = TimeSpan.FromHours(2) };
+
+                       var editor = new Editor { Text = "Edit this text on editor", HeightRequest = 100, TextColor = Color.Yellow, BackgroundColor = Color.Gray };
+
+                       var entry = new Entry { Placeholder = "Edit this text on entry", PlaceholderColor = Color.Pink, TextColor = Color.Yellow, BackgroundColor = Color.Green };
+
+                       var frame = new Frame { HasShadow = true, BackgroundColor = Color.Maroon, OutlineColor = Color.Lime, MinimumHeightRequest = 100 };
+
+
+                       var image = new Image { HeightRequest = 100, Source = "crimson.jpg" };
+
+                       var picker1 = new Picker { Title = "Select a team player", TextColor = Color.Pink, BackgroundColor = Color.Silver };
+                       picker1.Items.Add("Rui");
+                       picker1.Items.Add("Jason");
+                       picker1.Items.Add("Ez");
+                       picker1.Items.Add("Stephane");
+                       picker1.Items.Add("Samantha");
+                       picker1.Items.Add("Paul");
+
+                       picker1.SelectedIndex = 1;
+
+                       var progress = new ProgressBar { BackgroundColor = Color.Purple, Progress = 0.5, HeightRequest = 50 };
+
+                       picker1.SelectedIndexChanged += (sender, e) =>
+                       {
+                               entry.Text = $"Selected {picker1.Items[picker1.SelectedIndex]}";
+
+                               progress.Progress += 0.1;
+                       };
+
+                       var searchBar = new SearchBar { BackgroundColor = Color.Olive, TextColor = Color.Maroon, CancelButtonColor = Color.Pink };
+                       searchBar.Placeholder = "Please search";
+                       searchBar.PlaceholderColor = Color.Orange;
+                       searchBar.SearchButtonPressed += (sender, e) =>
+                       {
+                               searchBar.Text = "Search was pressed";
+                       };
+
+                       var slider = new Slider { BackgroundColor = Color.Lime, Value = 0.5 };
+
+                       slider.ValueChanged += (sender, e) =>
+                       {
+                               editor.Text = $"Slider value changed {slider.Value}";
+                       };
+
+                       var stepper = new Stepper { BackgroundColor = Color.Yellow, Maximum = 100, Minimum = 0, Value = 10, Increment = 0.5 };
+
+                       stepper.ValueChanged += (sender, e) =>
+                       {
+                               editor.Text = $"Stepper value changed {stepper.Value}";
+                       };
+
+                       var labal = new Label { Text = "This is a Switch" };
+                       var switchR = new Switch { BackgroundColor = Color.Fuchsia, IsToggled = true };
+                       switchR.Toggled += (sender, e) =>
+                       {
+                               entry.Text = $"switchR is toogle {switchR.IsToggled}";
+                       };
+                       var layoutSwitch = new StackLayout { Orientation = StackOrientation.Horizontal, BackgroundColor = Color.Green };
+                       layoutSwitch.Children.Add(labal);
+                       layoutSwitch.Children.Add(switchR);
+
+                       var webView = new WebView { HeightRequest = 200, Source = "http://google.pt" };
+
+                       var mainStck = new StackLayout
+                       {
+                               Spacing = 10,
+                               BackgroundColor = Color.Blue,
+                               VerticalOptions = LayoutOptions.Center,
+                               HorizontalOptions = LayoutOptions.Center,
+                               Children =
+                                                       {
+                                                               lst4,
+                                                               lst,
+                                                               lst1,
+                                                               lst2,
+                                                               lst3,
+                                                               webView,
+                                                               layoutSwitch,
+                                                               stepper,
+                                                               slider,
+                                                               searchBar,
+                                                               progress,
+                                                               picker1,
+                                                               image,
+                                                               frame,
+                                                               entry,
+                                                               editor,
+                                                               picker,
+                                                               timePicker,
+                                                               bigbUtton,
+                                                               new Button { Text = "Click Me", BackgroundColor = Color.Gray },
+                                                               new Button { Image = "bank.png", BackgroundColor = Color.Gray },
+                                                               CreateButton(new Button.ButtonContentLayout(Button.ButtonContentLayout.ImagePosition.Left, 10)),
+                                                               CreateButton(new Button.ButtonContentLayout(Button.ButtonContentLayout.ImagePosition.Top, 10)),
+                                                               CreateButton(new Button.ButtonContentLayout(Button.ButtonContentLayout.ImagePosition.Bottom, 10)),
+                                                               CreateButton(new Button.ButtonContentLayout(Button.ButtonContentLayout.ImagePosition.Right, 10)),
+                                                               mainDemoStack
+                                                       }
+                       };
+                       var lbl = new Label { Text = "Second label", TextColor = Color.White, VerticalTextAlignment = TextAlignment.Start, HorizontalTextAlignment = TextAlignment.Center };
+                       mainStck.Children.Add(new Label { Text = "HELLO XAMARIN FORMS MAC", TextColor = Color.White, HorizontalTextAlignment = TextAlignment.Center });
+                       mainStck.Children.Add(lbl);
+                       mainStck.Children.Add(new BoxView { Color = Color.Pink, HeightRequest = 200 });
+
+                       var scroller = new ScrollView { BackgroundColor = Color.Yellow, HorizontalOptions = LayoutOptions.Center };
+
+                       scroller.Scrolled += (sender, e) =>
+                       {
+                               lbl.Text = $"Current postion {scroller.ScrollY}";
+                       };
+
+                       scroller.Content = mainStck;
+
+                       var actv = new ActivityIndicator { BackgroundColor = Color.White, Color = Color.Fuchsia, IsRunning = true };
+                       mainStck.Children.Add(actv);
+
+                       bigbUtton.Clicked += async (sender, e) =>
+                       {
+                               await scroller.ScrollToAsync(actv, ScrollToPosition.Center, true);
+                               actv.Color = Color.Default;
+                       };
+
+                       Content = scroller;
+               }
+
+               public static ContentPage MacDemoContentPage()
+               {
+                       return new MacOSTestGallery();
+               }
+
+               public static NavigationPage MacDemoNavigationPage()
+               {
+                       var np = new NavigationPage(GetNewPage()) { BarTextColor = Color.Red, BarBackgroundColor = Color.Yellow };
+
+                       np.Pushed += (sender, e) => System.Diagnostics.Debug.WriteLine("Pushed + " + np.CurrentPage.Title);
+
+                       np.Popped += (sender, e) => System.Diagnostics.Debug.WriteLine("Popped");
+
+                       np.PoppedToRoot += (sender, e) => System.Diagnostics.Debug.WriteLine("Popped to root");
+
+                       return np;
+               }
+
+               public static TabbedPage MacDemoTabbedPage()
+               {
+
+                       var btnGo = new Button { Text = "Change Title" };
+                       var btnGo1 = new Button { Text = "Change Icon" };
+
+                       var lyout = new StackLayout();
+                       lyout.Children.Add(btnGo);
+                       //lyout.Children.Add(btnGo1);
+
+                       var tp = new TabbedPage { BarTextColor = Color.Red, BarBackgroundColor = Color.Yellow };
+
+                       var master = new ContentPage { Icon = "bank.png", BackgroundColor = Color.Red, Title = "Master", Content = lyout };
+
+                       var detail = new ContentPage { Icon = "bank.png", BackgroundColor = Color.Blue, Title = "Detail", Content = new Label { Text = "This is Detail Page" } };
+
+                       tp.Children.Add(master);
+                       tp.Children.Add(detail);
+
+                       tp.CurrentPage = detail;
+
+                       tp.CurrentPageChanged += (sender, e) =>
+                       {
+                               System.Diagnostics.Debug.WriteLine(tp.CurrentPage.Title);
+                       };
+
+                       btnGo.Clicked += (sender, e) =>
+                       {
+                               tp.CurrentPage.Title = "Tile changed";
+                               tp.CurrentPage.Icon = null;
+                       };
+
+                       btnGo1.Clicked += (sender, e) =>
+                       {
+
+                       };
+                       return tp;
+               }
+
+               public static MasterDetailPage MacDemoMasterDetailPage()
+               {
+                       var mdp = new MasterDetailPage();
+
+                       var master = new ContentPage { BackgroundColor = Color.Red, Title = "Master", Content = new Label { Text = "This is Master Page" } };
+
+                       var detail = new ContentPage { BackgroundColor = Color.Blue, Title = "Detail", Content = new Label { Text = "This is Detail Page" } };
+
+                       mdp.Master = master;
+                       mdp.Detail = detail;
+
+                       return mdp;
+               }
+
+               public static CarouselPage MacDemoCarouselPage()
+               {
+
+                       var carouselPage = new CarouselPage { BackgroundColor = Color.Yellow };
+
+                       var btnGo = new Button { Text = "Goto To Page 1 " };
+                       var btnGo1 = new Button { Text = "Goto To Page 3 " };
+                       var stck = new StackLayout();
+                       stck.Children.Add(btnGo);
+                       stck.Children.Add(btnGo1);
+                       var page = new ContentPage { Title = "Page1", BackgroundColor = Color.Red, Content = new Label { Text = "Page 1 label", TextColor = Color.White, VerticalTextAlignment = TextAlignment.Start, HorizontalTextAlignment = TextAlignment.Center } };
+                       var page2 = new ContentPage { Title = "Page2", BackgroundColor = Color.Blue, Content = stck };
+                       var page3 = new ContentPage { Title = "Page3", BackgroundColor = Color.Green, Content = new Label { Text = "Page 3 label" } };
+
+                       carouselPage.Children.Add(page);
+                       carouselPage.Children.Add(page2);
+                       carouselPage.Children.Add(page3);
+
+                       carouselPage.CurrentPage = page2;
+
+                       btnGo.Clicked += (sender, e) =>
+                       {
+                               carouselPage.CurrentPage = page;
+                       };
+
+                       btnGo1.Clicked += (sender, e) =>
+                       {
+                               carouselPage.CurrentPage = page3;
+                       };
+
+                       carouselPage.CurrentPageChanged += (sender, e) =>
+                       {
+                               System.Diagnostics.Debug.WriteLine(carouselPage.CurrentPage.Title);
+                       };
+                       return carouselPage;
+               }
+
+               static int _pageID;
+
+               static StackLayout mainDemoStack = new StackLayout { BackgroundColor = Color.Blue };
+
+               static ContentPage GetNewPage()
+               {
+                       var label = new Label { Text = $"Page {_pageID}" };
+                       var btnGo = new Button { Text = "Push Page" };
+                       var btnGo1 = new Button { Text = "Pop Page" };
+                       var lyout = new StackLayout();
+                       lyout.Children.Add(label);
+                       lyout.Children.Add(btnGo);
+                       lyout.Children.Add(btnGo1);
+
+                       btnGo.Clicked += async (sender, e) =>
+                       {
+                               _pageID++;
+                               await (lyout.Parent as Page).Navigation?.PushAsync(GetNewPage());
+
+                       };
+
+                       btnGo1.Clicked += async (sender, e) =>
+                       {
+                               _pageID--;
+                               await (lyout.Parent as Page).Navigation?.PopAsync();
+
+                       };
+
+                       return new ContentPage { Icon = "bank.png", BackgroundColor = _pageID % 2 == 0 ? Color.Blue : Color.Green, Title = label.Text, Content = lyout };
+               }
+
+               static StackLayout MakeNewStackLayout()
+               {
+                       var count = 0;
+                       var stacklayout = new StackLayout { BackgroundColor = Color.Red };
+
+                       stacklayout.Children.Add(new Label { Text = $"HEllO {count}" });
+                       stacklayout.Children.Add(new Button
+                       {
+                               Text = "Change layout",
+                               Command = new Command(() =>
+                               {
+                                       count += 2;
+                                       stacklayout.Children.RemoveAt(2);
+
+                                       var ll = new StackLayout();
+                                       ll.Children.Add(new Label { Text = $"HEllO {count}" });
+                                       ll.Children.Add(new Label { Text = $"HEllO {count + 1}" });
+                                       stacklayout.Children.Add(ll);
+                               })
+                       });
+                       stacklayout.Children.Add(new Label { Text = $"HEllO {count + 1}" });
+                       count += 2;
+                       return stacklayout;
+               }
+
+
+
+               static Button CreateButton(Button.ButtonContentLayout layout)
+               {
+                       return new Button
+                       {
+                               Text = "Click Me On Mac",
+                               Image = "bank.png",
+                               Font = Font.OfSize("Helvetica", 14),
+                               ContentLayout = layout,
+                               BackgroundColor = Color.Black,
+                               TextColor = Color.White
+                       };
+               }
+
+               class DemoSwitchCell : SwitchCell
+               {
+                       public DemoSwitchCell()
+                       {
+                               SetBinding(TextProperty, new Binding("Reference"));
+                               SetBinding(OnProperty, new Binding("ShowButton"));
+                       }
+               }
+
+               class DemoImageCell : ImageCell
+               {
+                       public DemoImageCell()
+                       {
+                               SetBinding(TextProperty, new Binding("Reference"));
+                               SetBinding(DetailProperty, new Binding("ShowButton"));
+                               SetBinding(ImageSourceProperty, new Binding("Image"));
+                       }
+               }
+
+               class DemoTextCell : TextCell
+               {
+                       public DemoTextCell()
+                       {
+                               SetBinding(TextProperty, new Binding("Reference"));
+                               SetBinding(DetailProperty, new Binding("ShowButton"));
+                       }
+               }
+
+               class DemoEntryCell : EntryCell
+               {
+                       public DemoEntryCell()
+                       {
+                               SetBinding(LabelProperty, new Binding("Reference"));
+                               SetBinding(TextProperty, new Binding("ShowButton"));
+                               LabelColor = Color.Red;
+                               Placeholder = "This is a entry cell";
+                       }
+               }
+
+               class DemoViewCell : ViewCell
+               {
+                       public DemoViewCell()
+                       {
+                               var box = new Image { BackgroundColor = Color.Pink, WidthRequest = 100, HeightRequest = 40, Source = "bank.png" };
+                               var label = new Label { TextColor = Color.White };
+                               var labelDetail = new Label { TextColor = Color.White };
+
+                               label.SetBinding(Label.TextProperty, new Binding("Reference"));
+                               labelDetail.SetBinding(Label.TextProperty, new Binding("ShowButton"));
+
+                               var grid = new Grid { BackgroundColor = Color.Black };
+
+                               grid.Children.Add(box, 0, 1, 0, 1);
+                               grid.Children.Add(label, 1, 0);
+                               grid.Children.Add(labelDetail, 1, 1);
+
+                               View = grid;
+                       }
+               }
+
+               public class MyItem1
+               {
+                       public string Reference { get; set; }
+                       public string Image { get; set; }
+                       public bool ShowButton { get; set; }
+               }
+       }
+}
index f18251d..ba83ca0 100644 (file)
@@ -12,6 +12,7 @@ namespace Xamarin.Forms
                public const string Android = "Android";
                public const string WinPhone = "WinPhone";
                public const string Windows = "Windows";
+               public const string macOS = "macOS";
 
                internal static DeviceInfo info;
 
@@ -21,8 +22,10 @@ namespace Xamarin.Forms
 
                [Obsolete("Use RuntimePlatform instead.")]
 #pragma warning disable 0618
-               public static TargetPlatform OS {
-                       get {
+               public static TargetPlatform OS
+               {
+                       get
+                       {
                                TargetPlatform platform;
                                if (Enum.TryParse(RuntimePlatform, out platform))
                                        return platform;
index 0751e1c..8f234cf 100644 (file)
@@ -71,7 +71,7 @@ namespace Xamarin.Forms
 
                public ListView([Parameter("CachingStrategy")] ListViewCachingStrategy cachingStrategy) : this()
                {
-                       if (Device.RuntimePlatform == Device.Android || Device.RuntimePlatform == Device.iOS)
+                       if (Device.RuntimePlatform == Device.Android || Device.RuntimePlatform == Device.iOS || Device.RuntimePlatform == Device.macOS)
                                CachingStrategy = cachingStrategy;
                }
 
index b74e9b9..fbddfeb 100644 (file)
@@ -5,4 +5,5 @@ namespace Xamarin.Forms.PlatformConfiguration
        public sealed class iOS : IConfigPlatform { }
        public sealed class Windows : IConfigPlatform { }
        public sealed class Tizen : IConfigPlatform { }
+       public sealed class macOS : IConfigPlatform { }
 }
diff --git a/Xamarin.Forms.Core/PlatformConfiguration/macOSSpecific/TabbedPage.cs b/Xamarin.Forms.Core/PlatformConfiguration/macOSSpecific/TabbedPage.cs
new file mode 100644 (file)
index 0000000..3c51805
--- /dev/null
@@ -0,0 +1,50 @@
+namespace Xamarin.Forms.PlatformConfiguration.macOSSpecific
+{
+       using FormsElement = Forms.TabbedPage;
+
+       public static class TabbedPage
+       {
+               #region TabsStyle
+               public static readonly BindableProperty TabsStyleProperty = BindableProperty.Create("TabsStyle", typeof(TabsStyle), typeof(TabbedPage), TabsStyle.Default);
+
+               public static TabsStyle GetTabsStyle(BindableObject element)
+               {
+                       return (TabsStyle)element.GetValue(TabsStyleProperty);
+               }
+
+               public static void SetTabsStyle(BindableObject element, TabsStyle value)
+               {
+                       element.SetValue(TabsStyleProperty, value);
+               }
+
+               public static TabsStyle GetTabsStyle(this IPlatformElementConfiguration<macOS, FormsElement> config)
+               {
+                       return GetTabsStyle(config.Element);
+               }
+
+               public static IPlatformElementConfiguration<macOS, FormsElement> SetShowTabs(this IPlatformElementConfiguration<macOS, FormsElement> config, TabsStyle value)
+               {
+                       SetTabsStyle(config.Element, value);
+                       return config;
+               }
+
+               public static IPlatformElementConfiguration<macOS, FormsElement> ShowTabsOnNavigation(this IPlatformElementConfiguration<macOS, FormsElement> config)
+               {
+                       SetTabsStyle(config.Element, TabsStyle.OnNavigation);
+                       return config;
+               }
+
+               public static IPlatformElementConfiguration<macOS, FormsElement> ShowTabs(this IPlatformElementConfiguration<macOS, FormsElement> config)
+               {
+                       SetTabsStyle(config.Element, TabsStyle.Default);
+                       return config;
+               }
+
+               public static IPlatformElementConfiguration<macOS, FormsElement> HideTabs(this IPlatformElementConfiguration<macOS, FormsElement> config)
+               {
+                       SetTabsStyle(config.Element, TabsStyle.Hidden);
+                       return config;
+               }
+               #endregion
+       }
+}
diff --git a/Xamarin.Forms.Core/PlatformConfiguration/macOSSpecific/TabsStyle.cs b/Xamarin.Forms.Core/PlatformConfiguration/macOSSpecific/TabsStyle.cs
new file mode 100644 (file)
index 0000000..0b498ae
--- /dev/null
@@ -0,0 +1,12 @@
+using System;
+namespace Xamarin.Forms
+{
+       public enum TabsStyle
+       {
+               Default = 0,
+               Hidden = 1,
+               Icons = 2,
+               OnNavigation = 3,
+               OnBottom = 4
+       }
+}
index 6bbdb60..8f50765 100644 (file)
@@ -28,7 +28,7 @@ using Xamarin.Forms.Internals;
 [assembly: InternalsVisibleTo("Xamarin.Forms.Platform.WinRT.Tablet")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Platform.WinRT.Phone")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Platform.WP8")]
-[assembly: InternalsVisibleTo("Xamarin.Forms.Platform.MacOS")]
+[assembly: InternalsVisibleTo("Xamarin.Forms.Platform.macOS")]
 [assembly: InternalsVisibleTo("iOSUnitTests")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Controls")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Core.Design")]
@@ -47,6 +47,7 @@ using Xamarin.Forms.Internals;
 [assembly: InternalsVisibleTo("Xamarin.Forms.Core.iOS.UITests")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Core.Android.UITests")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Core.Windows.UITests")]
+[assembly: InternalsVisibleTo("Xamarin.Forms.Core.macOS.UITests")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.iOS.UITests")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Android.UITests")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Loader")]
index 6125695..ff34d83 100644 (file)
@@ -9,6 +9,6 @@ namespace Xamarin.Forms
                iOS,
                Android,
                WinPhone,
-               Windows,
+               Windows
        }
 }
index ebc9f8b..6b978ac 100644 (file)
     <Compile Include="ProvideCompiledAttribute.cs" />
     <Compile Include="TypedBinding.cs" />
     <Compile Include="XmlnsDefinitionAttribute.cs" />
+    <Compile Include="PlatformConfiguration\macOSSpecific\TabbedPage.cs" />
+    <Compile Include="PlatformConfiguration\macOSSpecific\TabsStyle.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
   <ItemGroup>
     </PostBuildEvent>
   </PropertyGroup>
   <ItemGroup />
+  <ItemGroup>
+    <Folder Include="PlatformConfiguration\macOSSpecific\" />
+  </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/Xamarin.Forms.Maps.MacOS.Extra/ApiDefinition.cs b/Xamarin.Forms.Maps.MacOS.Extra/ApiDefinition.cs
new file mode 100644 (file)
index 0000000..c6a92d9
--- /dev/null
@@ -0,0 +1,33 @@
+using CoreLocation;
+using Foundation;
+
+namespace Xamarin.Forms.Maps.MacOS.Extra
+{
+       delegate void CLGeocodeCompletionHandler(CLPlacemark[] placemarks, NSError error);
+
+       [BaseType(typeof(NSObject))]
+       interface CLGeocoder
+       {
+               [Export("isGeocoding")]
+               bool Geocoding { get; }
+
+               [Export("reverseGeocodeLocation:completionHandler:")]
+               [Async]
+               void ReverseGeocodeLocation(CLLocation location, CLGeocodeCompletionHandler completionHandler);
+
+               [Export("geocodeAddressDictionary:completionHandler:")]
+               [Async]
+               void GeocodeAddress(NSDictionary addressDictionary, CLGeocodeCompletionHandler completionHandler);
+
+               [Export("geocodeAddressString:completionHandler:")]
+               [Async]
+               void GeocodeAddress(string addressString, CLGeocodeCompletionHandler completionHandler);
+
+               [Export("geocodeAddressString:inRegion:completionHandler:")]
+               [Async]
+               void GeocodeAddress(string addressString, CLRegion region, CLGeocodeCompletionHandler completionHandler);
+
+               [Export("cancelGeocode")]
+               void CancelGeocode();
+       }
+}
diff --git a/Xamarin.Forms.Maps.MacOS.Extra/Properties/AssemblyInfo.cs b/Xamarin.Forms.Maps.MacOS.Extra/Properties/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..9e5eaa0
--- /dev/null
@@ -0,0 +1,34 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+using Foundation;
+
+// This attribute allows you to mark your assemblies as “safe to link”. 
+// When the attribute is present, the linker—if enabled—will process the assembly 
+// even if you’re using the “Link SDK assemblies only” option, which is the default for device builds.
+
+[assembly: LinkerSafe]
+
+// Information about this assembly is defined by the following attributes. 
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle("Xamarin.Forms.Maps.MacOS.Extra")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly, 
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
diff --git a/Xamarin.Forms.Maps.MacOS.Extra/Xamarin.Forms.Maps.MacOS.Extra.csproj b/Xamarin.Forms.Maps.MacOS.Extra/Xamarin.Forms.Maps.MacOS.Extra.csproj
new file mode 100644 (file)
index 0000000..af4f4aa
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{6346D187-BA87-4F18-A165-F27C5B7F25D0}</ProjectGuid>
+    <ProjectTypeGuids>{810C163F-4746-4721-8B8E-88A3673A62EA};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <RootNamespace>Xamarin.Forms.Maps.MacOS.Extra</RootNamespace>
+    <AssemblyName>Xamarin.Forms.Maps.MacOS.Extra</AssemblyName>
+    <MacResourcePrefix>Resources</MacResourcePrefix>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <DefineConstants></DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="Xamarin.Mac" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ObjcBindingApiDefinition Include="ApiDefinition.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Xamarin\Mac\Xamarin.Mac.ObjcBinding.CSharp.targets" />
+</Project>
\ No newline at end of file
diff --git a/Xamarin.Forms.Maps.MacOS/Libs/Xamarin.Forms.Maps.MacOS.Extra.dll b/Xamarin.Forms.Maps.MacOS/Libs/Xamarin.Forms.Maps.MacOS.Extra.dll
new file mode 100755 (executable)
index 0000000..0c17fff
Binary files /dev/null and b/Xamarin.Forms.Maps.MacOS/Libs/Xamarin.Forms.Maps.MacOS.Extra.dll differ
diff --git a/Xamarin.Forms.Maps.MacOS/Properties/AssemblyInfo.cs b/Xamarin.Forms.Maps.MacOS/Properties/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..26a0c01
--- /dev/null
@@ -0,0 +1,16 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using Xamarin.Forms;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.Maps;
+using Xamarin.Forms.Maps.MacOS;
+
+// Information about this assembly is defined by the following attributes. 
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle("Xamarin.Forms.Maps.macOS")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+
+[assembly: ExportRenderer(typeof(Map), typeof(MapRenderer))]
+[assembly: Preserve]
diff --git a/Xamarin.Forms.Maps.MacOS/Xamarin.Forms.Maps.macOS.csproj b/Xamarin.Forms.Maps.MacOS/Xamarin.Forms.Maps.macOS.csproj
new file mode 100644 (file)
index 0000000..af3da20
--- /dev/null
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}</ProjectGuid>
+    <ProjectTypeGuids>{A3F8F2AB-B479-4A4A-A458-A89E7DC349F1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <RootNamespace>Xamarin.Forms.Maps.MacOS</RootNamespace>
+    <AssemblyName>Xamarin.Forms.Maps.macOS</AssemblyName>
+    <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
+    <TargetFrameworkIdentifier>Xamarin.Mac</TargetFrameworkIdentifier>
+    <MonoMacResourcePrefix>Resources</MonoMacResourcePrefix>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <EnableCodeSigning>false</EnableCodeSigning>
+    <CreatePackage>false</CreatePackage>
+    <EnablePackageSigning>false</EnablePackageSigning>
+    <IncludeMonoRuntime>false</IncludeMonoRuntime>
+    <UseSGen>false</UseSGen>
+    <HttpClientHandler></HttpClientHandler>
+    <TlsProvider></TlsProvider>
+    <LinkMode></LinkMode>
+    <XamMacArch></XamMacArch>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <DefineConstants></DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <EnableCodeSigning>false</EnableCodeSigning>
+    <CreatePackage>false</CreatePackage>
+    <EnablePackageSigning>false</EnablePackageSigning>
+    <IncludeMonoRuntime>false</IncludeMonoRuntime>
+    <UseSGen>false</UseSGen>
+    <HttpClientHandler></HttpClientHandler>
+    <TlsProvider></TlsProvider>
+    <LinkMode></LinkMode>
+    <XamMacArch></XamMacArch>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="Xamarin.Mac" />
+    <Reference Include="Xamarin.Forms.Maps.MacOS.Extra">
+      <HintPath>Libs\Xamarin.Forms.Maps.MacOS.Extra.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="..\Xamarin.Forms.Maps.iOS\FormsMaps.cs">
+      <Link>FormsMaps.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Maps.iOS\GeocoderBackend.cs">
+      <Link>GeocoderBackend.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Maps.iOS\MapRenderer.cs">
+      <Link>MapRenderer.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Core\Properties\GlobalAssemblyInfo.cs">
+      <Link>Properties\GlobalAssemblyInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Maps.iOS\MapPool.cs">
+      <Link>MapPool.cs</Link>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj">
+      <Project>{57B8B73D-C3B5-4C42-869E-7B2F17D354AC}</Project>
+      <Name>Xamarin.Forms.Core</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Xamarin.Forms.Platform.MacOS\Xamarin.Forms.Platform.MacOS.csproj">
+      <Project>{C0059C45-EA1E-42F3-8A0E-794BB547EC3C}</Project>
+      <Name>Xamarin.Forms.Platform.MacOS</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Xamarin.Forms.Maps\Xamarin.Forms.Maps.csproj">
+      <Project>{7D13BAC2-C6A4-416A-B07E-C169B199E52B}</Project>
+      <Name>Xamarin.Forms.Maps</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Libs\Xamarin.Forms.Maps.MacOS.Extra.dll" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Xamarin\Mac\Xamarin.Mac.CSharp.targets" />
+</Project>
\ No newline at end of file
index a87cb8d..0f5df42 100644 (file)
@@ -1,11 +1,16 @@
+#if __MOBILE__
 using UIKit;
 using Xamarin.Forms.Maps.iOS;
+#else
+using Xamarin.Forms.Maps.MacOS;
+#endif
 
 namespace Xamarin
 {
        public static class FormsMaps
        {
                static bool s_isInitialized;
+#if __MOBILE__
                static bool? s_isiOs8OrNewer;
                static bool? s_isiOs9OrNewer;
                static bool? s_isiOs10OrNewer;
@@ -39,7 +44,7 @@ namespace Xamarin
                                return s_isiOs10OrNewer.Value;
                        }
                }
-
+#endif
                public static void Init()
                {
                        if (s_isInitialized)
index 08ffbbe..e5d1b25 100644 (file)
@@ -1,10 +1,22 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
-using AddressBookUI;
+using Contacts;
 using CoreLocation;
 
+#if __MOBILE__
+using AddressBookUI;
+using CCLGeocoder = CoreLocation.CLGeocoder;
+#else
+using Xamarin.Forms.Maps.MacOS.Extra;
+using CCLGeocoder = Xamarin.Forms.Maps.MacOS.Extra.CLGeocoder;
+#endif
+
+#if __MOBILE__
 namespace Xamarin.Forms.Maps.iOS
+#else
+namespace Xamarin.Forms.Maps.MacOS
+#endif
 {
        internal class GeocoderBackend
        {
@@ -17,21 +29,36 @@ namespace Xamarin.Forms.Maps.iOS
                static Task<IEnumerable<string>> GetAddressesForPositionAsync(Position position)
                {
                        var location = new CLLocation(position.Latitude, position.Longitude);
-                       var geocoder = new CLGeocoder();
+                       var geocoder = new CCLGeocoder();
                        var source = new TaskCompletionSource<IEnumerable<string>>();
                        geocoder.ReverseGeocodeLocation(location, (placemarks, error) =>
                        {
                                if (placemarks == null)
                                        placemarks = new CLPlacemark[0];
-                               IEnumerable<string> addresses = placemarks.Select(p => ABAddressFormatting.ToString(p.AddressDictionary, false));
+                               List<string> addresses = new List<string>();
+#if __MOBILE__
+                               addresses = placemarks.Select(p => ABAddressFormatting.ToString(p.AddressDictionary, false)).ToList();
+#else
+                               foreach (var item in placemarks)
+                               {
+                                       var address = new CNMutablePostalAddress();
+                                       address.Street = item.AddressDictionary["Street"] == null ? "" : item.AddressDictionary["Street"].ToString();
+                                       address.State = item.AddressDictionary["State"] == null ? "" : item.AddressDictionary["State"].ToString();
+                                       address.City = item.AddressDictionary["City"] == null ? "" : item.AddressDictionary["City"].ToString();
+                                       address.Country = item.AddressDictionary["Country"] == null ? "" : item.AddressDictionary["Country"].ToString();
+                                       address.PostalCode = item.AddressDictionary["ZIP"] == null ? "" : item.AddressDictionary["ZIP"].ToString();
+                                       addresses.Add(CNPostalAddressFormatter.GetStringFrom(address, CNPostalAddressFormatterStyle.MailingAddress));
+                               }
+#endif
                                source.SetResult(addresses);
+
                        });
                        return source.Task;
                }
 
                static Task<IEnumerable<Position>> GetPositionsForAddressAsync(string address)
                {
-                       var geocoder = new CLGeocoder();
+                       var geocoder = new CCLGeocoder();
                        var source = new TaskCompletionSource<IEnumerable<Position>>();
                        geocoder.GeocodeAddress(address, (placemarks, error) =>
                        {
index dc8a9c4..1b3823c 100644 (file)
@@ -1,7 +1,11 @@
 using System.Collections.Concurrent;
 using MapKit;
 
+#if __MOBILE__
 namespace Xamarin.Forms.Maps.iOS
+#else
+namespace Xamarin.Forms.Maps.MacOS
+#endif
 {
        // A static pool of MKMapView instances we can reuse 
        internal class MapPool
index 794a4a0..0616fa9 100644 (file)
@@ -5,21 +5,27 @@ using System.Collections.ObjectModel;
 using System.Collections.Specialized;
 using System.ComponentModel;
 using CoreLocation;
-using Foundation;
 using MapKit;
 using ObjCRuntime;
-using UIKit;
-using Xamarin.Forms.Platform.iOS;
 using RectangleF = CoreGraphics.CGRect;
+using Foundation;
 
+#if __MOBILE__
+using UIKit;
+using Xamarin.Forms.Platform.iOS;
 namespace Xamarin.Forms.Maps.iOS
+#else
+using AppKit;
+using Xamarin.Forms.Platform.MacOS;
+namespace Xamarin.Forms.Maps.MacOS
+#endif
 {
        internal class MapDelegate : MKMapViewDelegate
        {
                // keep references alive, removing this will cause a segfault
                readonly List<object> List = new List<object>();
                Map _map;
-               UIView _lastTouchedView;
+               object _lastTouchedView;
                bool _disposed;
 
                internal MapDelegate(Map map)
@@ -49,9 +55,10 @@ namespace Xamarin.Forms.Maps.iOS
 
                        return mapPin;
                }
-
+#if __MOBILE__
                void AttachGestureToPin(MKPinAnnotationView mapPin, IMKAnnotation annotation)
                {
+
                        UIGestureRecognizer[] recognizers = mapPin.GestureRecognizers;
 
                        if (recognizers != null)
@@ -71,9 +78,33 @@ namespace Xamarin.Forms.Maps.iOS
                        List.Add(action);
                        List.Add(recognizer);
                        mapPin.AddGestureRecognizer(recognizer);
-               }
+                       }
+#else
+               void AttachGestureToPin(MKPinAnnotationView mapPin, IMKAnnotation annotation)
+               {
+                       NSGestureRecognizer[] recognizers = mapPin.GestureRecognizers;
+
+                       if (recognizers != null)
+                       {
+                               foreach (NSGestureRecognizer r in recognizers)
+                               {
+                                       mapPin.RemoveGestureRecognizer(r);
+                               }
+                       }
+
+                       Action<NSClickGestureRecognizer> action = g => OnClick(annotation, g);
+                       var recognizer = new NSClickGestureRecognizer(action);
+                       List.Add(action);
+                       List.Add(recognizer);
+                       mapPin.AddGestureRecognizer(recognizer);
 
+               }
+#endif
+#if __MOBILE__
                void OnClick(object annotationObject, UITapGestureRecognizer recognizer)
+#else
+               void OnClick(object annotationObject, NSClickGestureRecognizer recognizer)
+#endif
                {
                        // https://bugzilla.xamarin.com/show_bug.cgi?id=26416
                        NSObject annotation = Runtime.GetNSObject(((IMKAnnotation)annotationObject).Handle);
@@ -126,9 +157,9 @@ namespace Xamarin.Forms.Maps.iOS
 
        public class MapRenderer : ViewRenderer
        {
-           CLLocationManager _locationManager;
+               CLLocationManager _locationManager;
                bool _shouldUpdateRegion;
-               bool _disposed; 
+               bool _disposed;
 
                const string MoveMessageName = "MapMoveToRegion";
 
@@ -142,8 +173,10 @@ namespace Xamarin.Forms.Maps.iOS
                // as much as possible to prevent creating new ones and losing more memory
 
                // For the time being, we don't want ViewRenderer handling disposal of the MKMapView
-               // if we're on iOS 9 or 10; during Dispose we'll be putting the MKMapView in a pool instead
+               // if we're on iOS 10; during Dispose we'll be putting the MKMapView in a pool instead
+#if __MOBILE__
                protected override bool ManageNativeControlLifetime => !FormsMaps.IsiOs9OrNewer;
+#endif
 
                protected override void Dispose(bool disposing)
                {
@@ -169,16 +202,15 @@ namespace Xamarin.Forms.Maps.iOS
                                mkMapView.Delegate.Dispose();
                                mkMapView.Delegate = null;
                                mkMapView.RemoveFromSuperview();
-
+#if __MOBILE__
                                if (FormsMaps.IsiOs9OrNewer)
                                {
                                        // This renderer is done with the MKMapView; we can put it in the pool
                                        // for other rendererers to use in the future
                                        MapPool.Add(mkMapView);
                                }
-
+#endif
                                // For iOS versions < 9, the MKMapView will be disposed in ViewRenderer's Dispose method
-
                                if (_locationManager != null)
                                {
                                        _locationManager.Dispose();
@@ -207,13 +239,13 @@ namespace Xamarin.Forms.Maps.iOS
                                if (Control == null)
                                {
                                        MKMapView mapView = null;
-
+#if __MOBILE__
                                        if (FormsMaps.IsiOs9OrNewer)
                                        {
                                                // See if we've got an MKMapView available in the pool; if so, use it
                                                mapView = MapPool.Get();
                                        }
-
+#endif
                                        if (mapView == null)
                                        {
                                                // If this is iOS 8 or lower, or if there weren't any MKMapViews in the pool,
@@ -260,9 +292,22 @@ namespace Xamarin.Forms.Maps.iOS
                                _shouldUpdateRegion = true;
                }
 
+#if __MOBILE__
                public override void LayoutSubviews()
                {
                        base.LayoutSubviews();
+                       UpdateRegion();
+               }
+#else
+               public override void Layout()
+               {
+                       base.Layout();
+                       UpdateRegion();
+               }
+#endif
+
+               void UpdateRegion()
+               {
                        if (_shouldUpdateRegion)
                        {
                                MoveToRegion(((Map)Element).LastMoveToRegion, false);
@@ -343,12 +388,13 @@ namespace Xamarin.Forms.Maps.iOS
 
                void UpdateIsShowingUser()
                {
+#if __MOBILE__
                        if (FormsMaps.IsiOs8OrNewer && ((Map)Element).IsShowingUser)
                        {
                                _locationManager = new CLLocationManager();
                                _locationManager.RequestWhenInUseAuthorization();
                        }
-
+#endif
                        ((MKMapView)Control).ShowsUserLocation = ((Map)Element).IsShowingUser;
                }
 
index 2220fff..1e3c2f3 100644 (file)
@@ -13,6 +13,7 @@ using Xamarin.Forms.Internals;
 [assembly: AssemblyConfiguration("")]
 [assembly: AssemblyCulture("")]
 [assembly: NeutralResourcesLanguage("en")]
+[assembly: InternalsVisibleTo("Xamarin.Forms.Maps.macOS")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Maps.iOS")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Maps.iOS.Classic")]
 [assembly: InternalsVisibleTo("Xamarin.Forms.Maps.Android")]
diff --git a/Xamarin.Forms.Platform.MacOS/CADisplayLinkTicker.cs b/Xamarin.Forms.Platform.MacOS/CADisplayLinkTicker.cs
new file mode 100644 (file)
index 0000000..1e965ff
--- /dev/null
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Concurrent;
+using System.Threading;
+using Foundation;
+using Xamarin.Forms.Internals;
+using CoreVideo;
+using AppKit;
+using CoreAnimation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       // ReSharper disable once InconsistentNaming
+       internal class CADisplayLinkTicker : Ticker
+       {
+               readonly BlockingCollection<Action> _queue = new BlockingCollection<Action>();
+               CVDisplayLink _link;
+
+               public CADisplayLinkTicker()
+               {
+                       var thread = new Thread(StartThread);
+                       thread.Start();
+               }
+
+               internal new static CADisplayLinkTicker Default => Ticker.Default as CADisplayLinkTicker;
+
+               public void Invoke(Action action)
+               {
+                       _queue.Add(action);
+               }
+
+               protected override void DisableTimer()
+               {
+                       _link?.Stop();
+                       _link?.Dispose();
+                       _link = null;
+               }
+
+               protected override void EnableTimer()
+               {
+                       _link = new CVDisplayLink();
+                       _link.SetOutputCallback(DisplayLinkOutputCallback);
+                       _link.Start();
+               }
+
+               public CVReturn DisplayLinkOutputCallback(CVDisplayLink displayLink, ref CVTimeStamp inNow,
+                       ref CVTimeStamp inOutputTime, CVOptionFlags flagsIn, ref CVOptionFlags flagsOut)
+               {
+                       // There is no autorelease pool when this method is called because it will be called from a background thread
+                       // It's important to create one or you will leak objects
+                       // ReSharper disable once UnusedVariable
+                       using (var pool = new NSAutoreleasePool())
+                       {
+                               Device.BeginInvokeOnMainThread(() => SendSignals());
+                       }
+                       return CVReturn.Success;
+               }
+
+               void StartThread()
+               {
+                       while (true)
+                       {
+                               Action action = _queue.Take();
+                               bool previous = NSApplication.CheckForIllegalCrossThreadCalls;
+                               NSApplication.CheckForIllegalCrossThreadCalls = false;
+
+                               CATransaction.Begin();
+                               action.Invoke();
+
+                               while (_queue.TryTake(out action))
+                                       action.Invoke();
+                               CATransaction.Commit();
+
+                               NSApplication.CheckForIllegalCrossThreadCalls = previous;
+                       }
+                       // ReSharper disable once FunctionNeverReturns
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/CellNSView.cs b/Xamarin.Forms.Platform.MacOS/Cells/CellNSView.cs
new file mode 100644 (file)
index 0000000..1ba964e
--- /dev/null
@@ -0,0 +1,167 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using CoreGraphics;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class CellNSView : NSView, INativeElementView
+       {
+               static readonly NSColor s_defaultChildViewsBackground = NSColor.Clear;
+               static readonly CGColor s_defaultHeaderViewsBackground = NSColor.LightGray.CGColor;
+               Cell _cell;
+               readonly NSTableViewCellStyle _style;
+
+               public Action<object, PropertyChangedEventArgs> PropertyChanged;
+
+               public CellNSView(NSTableViewCellStyle style)
+               {
+                       WantsLayer = true;
+                       _style = style;
+                       CreateUI();
+               }
+
+               public NSTextField TextLabel { get; private set; }
+
+               public NSTextField DetailTextLabel { get; private set; }
+
+               public NSImageView ImageView { get; private set; }
+
+               public NSView AccessoryView { get; private set; }
+
+               public Element Element => Cell;
+
+               public Cell Cell
+               {
+                       get { return _cell; }
+                       set
+                       {
+                               if (_cell == value)
+                                       return;
+
+                               ICellController cellController = _cell;
+
+                               if (cellController != null)
+                                       Device.BeginInvokeOnMainThread(cellController.SendDisappearing);
+
+                               _cell = value;
+                               cellController = value;
+
+                               if (cellController != null)
+                                       Device.BeginInvokeOnMainThread(cellController.SendAppearing);
+                       }
+               }
+
+               public void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       PropertyChanged?.Invoke(this, e);
+               }
+
+               public override void Layout()
+               {
+                       const int padding = 10;
+                       nfloat availableHeight = Frame.Height;
+                       nfloat availableWidth = Frame.Width - padding * 2;
+                       nfloat imageWidth = 0;
+                       nfloat accessoryViewWidth = 0;
+
+                       if (ImageView != null)
+                       {
+                               nfloat imageHeight = imageWidth = availableHeight;
+                               ImageView.Frame = new CGRect(padding, 0, imageWidth, imageHeight);
+                       }
+
+                       if (AccessoryView != null)
+                       {
+                               accessoryViewWidth = _style == NSTableViewCellStyle.Value1 ? 50 : availableWidth - 100;
+                               AccessoryView.Frame = new CGRect(availableWidth - accessoryViewWidth + padding, 0, accessoryViewWidth,
+                                       availableHeight);
+                               foreach (var subView in AccessoryView.Subviews)
+                               {
+                                       //try to find the size the control wants, if no width use default width
+                                       var size = subView.FittingSize;
+                                       if (size.Width == 0)
+                                               size.Width = accessoryViewWidth;
+
+                                       var x = AccessoryView.Bounds.Width - size.Width;
+                                       var y = (AccessoryView.Bounds.Height - size.Height) / 2;
+                                       subView.Frame = new CGRect(new CGPoint(x, y), size);
+                               }
+                       }
+
+                       nfloat labelHeights = availableHeight;
+                       nfloat labelWidth = availableWidth - imageWidth - accessoryViewWidth;
+
+                       if (!string.IsNullOrEmpty(DetailTextLabel?.StringValue))
+                       {
+                               labelHeights = availableHeight / 2;
+                               DetailTextLabel.CenterTextVertically(new CGRect(imageWidth + padding, 0, labelWidth, labelHeights));
+                       }
+
+                       TextLabel.CenterTextVertically(new CGRect(imageWidth + padding, availableHeight - labelHeights, labelWidth,
+                               labelHeights));
+                       base.Layout();
+               }
+
+               internal static NSView GetNativeCell(NSTableView tableView, Cell cell, string templateId = "", bool isHeader = false,
+                       bool isRecycle = false)
+               {
+                       var reusable = tableView.MakeView(templateId, tableView);
+                       NSView nativeCell;
+                       if (reusable == null || !isRecycle)
+                       {
+                               var renderer = (CellRenderer)Registrar.Registered.GetHandler(cell.GetType());
+                               nativeCell = renderer.GetCell(cell, null, tableView);
+                       }
+                       else
+                       {
+                               nativeCell = reusable;
+                       }
+
+                       if (string.IsNullOrEmpty(nativeCell.Identifier))
+                               nativeCell.Identifier = templateId;
+
+                       if (!isHeader) return nativeCell;
+                       if (nativeCell.Layer != null) nativeCell.Layer.BackgroundColor = s_defaultHeaderViewsBackground;
+                       return nativeCell;
+               }
+
+               void CreateUI()
+               {
+                       var style = _style;
+
+                       AddSubview(TextLabel = new NSTextField
+                       {
+                               Bordered = false,
+                               Selectable = false,
+                               Editable = false,
+                               Font = NSFont.LabelFontOfSize(NSFont.SystemFontSize)
+                       });
+
+                       TextLabel.Cell.BackgroundColor = s_defaultChildViewsBackground;
+
+                       if (style == NSTableViewCellStyle.Image || style == NSTableViewCellStyle.Subtitle ||
+                               style == NSTableViewCellStyle.ImageSubtitle)
+                       {
+                               AddSubview(DetailTextLabel = new NSTextField
+                               {
+                                       Bordered = false,
+                                       Selectable = false,
+                                       Editable = false,
+                                       Font = NSFont.LabelFontOfSize(NSFont.SmallSystemFontSize)
+                               });
+                               DetailTextLabel.Cell.BackgroundColor = s_defaultChildViewsBackground;
+                       }
+
+                       if (style == NSTableViewCellStyle.Image || style == NSTableViewCellStyle.ImageSubtitle)
+                               AddSubview(ImageView = new NSImageView());
+
+                       if (style == NSTableViewCellStyle.Value1 || style == NSTableViewCellStyle.Value2)
+                       {
+                               var accessoryView = new NSView { WantsLayer = true };
+                               accessoryView.Layer.BackgroundColor = s_defaultChildViewsBackground.CGColor;
+                               AddSubview(AccessoryView = accessoryView);
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/CellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/CellRenderer.cs
new file mode 100644 (file)
index 0000000..54e540a
--- /dev/null
@@ -0,0 +1,68 @@
+using System;
+using AppKit;
+using CoreGraphics;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class CellRenderer : IRegisterable
+       {
+               static readonly BindableProperty s_realCellProperty = BindableProperty.CreateAttached("RealCell", typeof(NSView),
+                       typeof(Cell), null);
+
+               EventHandler _onForceUpdateSizeRequested;
+
+               public virtual NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+               {
+                       var tvc = reusableView as CellNSView ?? new CellNSView(NSTableViewCellStyle.Default);
+
+                       tvc.Cell = item;
+
+                       WireUpForceUpdateSizeRequested(item, tvc, tv);
+
+                       tvc.TextLabel.StringValue = item.ToString();
+
+                       UpdateBackground(tvc, item);
+
+                       return tvc;
+               }
+
+               protected void UpdateBackground(NSView tableViewCell, Cell cell)
+               {
+                       tableViewCell.WantsLayer = true;
+                       var bgColor = NSColor.White;
+                       var element = cell.RealParent as VisualElement;
+                       if (element != null)
+                               bgColor = element.BackgroundColor == Color.Default ? bgColor : element.BackgroundColor.ToNSColor();
+
+                       UpdateBackgroundChild(cell, bgColor);
+
+                       tableViewCell.Layer.BackgroundColor = bgColor.CGColor;
+               }
+
+               protected void WireUpForceUpdateSizeRequested(ICellController cell, NSView nativeCell, NSTableView tableView)
+               {
+                       cell.ForceUpdateSizeRequested -= _onForceUpdateSizeRequested;
+
+                       _onForceUpdateSizeRequested = (sender, e) =>
+                       {
+                               //TODO: Implement ForceUpdateSize
+                       };
+
+                       cell.ForceUpdateSizeRequested += _onForceUpdateSizeRequested;
+               }
+
+               internal virtual void UpdateBackgroundChild(Cell cell, NSColor backgroundColor)
+               {
+               }
+
+               internal static NSView GetRealCell(BindableObject cell)
+               {
+                       return (NSView)cell.GetValue(s_realCellProperty);
+               }
+
+               internal static void SetRealCell(BindableObject cell, NSView renderer)
+               {
+                       cell.SetValue(s_realCellProperty, renderer);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/EntryCellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/EntryCellRenderer.cs
new file mode 100644 (file)
index 0000000..43789dc
--- /dev/null
@@ -0,0 +1,144 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using CoreGraphics;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class EntryCellRenderer : CellRenderer
+       {
+               static readonly Color s_defaultTextColor = Color.Black;
+
+               public override NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+               {
+                       NSTextField nsEntry = null;
+                       var tvc = reusableView as CellNSView;
+                       if (tvc == null)
+                               tvc = new CellNSView(NSTableViewCellStyle.Value2);
+                       else
+                       {
+                               tvc.Cell.PropertyChanged -= OnCellPropertyChanged;
+
+                               nsEntry = tvc.AccessoryView.Subviews[0] as NSTextField;
+                               if (nsEntry != null)
+                               {
+                                       nsEntry.RemoveFromSuperview();
+                                       nsEntry.Changed -= OnTextFieldTextChanged;
+                               }
+                       }
+
+                       SetRealCell(item, tvc);
+
+                       if (nsEntry == null)
+                               tvc.AccessoryView.AddSubview(nsEntry = new NSTextField());
+
+                       var entryCell = (EntryCell)item;
+
+                       tvc.Cell = item;
+                       tvc.Cell.PropertyChanged += OnCellPropertyChanged;
+                       nsEntry.Changed += OnTextFieldTextChanged;
+
+                       WireUpForceUpdateSizeRequested(item, tvc, tv);
+
+                       UpdateBackground(tvc, entryCell);
+                       UpdateLabel(tvc, entryCell);
+                       UpdateText(tvc, entryCell);
+                       UpdatePlaceholder(tvc, entryCell);
+                       UpdateLabelColor(tvc, entryCell);
+                       UpdateHorizontalTextAlignment(tvc, entryCell);
+                       UpdateIsEnabled(tvc, entryCell);
+
+                       return tvc;
+               }
+
+               internal override void UpdateBackgroundChild(Cell cell, NSColor backgroundColor)
+               {
+                       var realCell = (CellNSView)GetRealCell(cell);
+
+                       var nsTextField = realCell.AccessoryView.Subviews[0] as NSTextField;
+                       if (nsTextField != null)
+                               nsTextField.BackgroundColor = backgroundColor;
+
+                       base.UpdateBackgroundChild(cell, backgroundColor);
+               }
+
+               static void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       var entryCell = (EntryCell)sender;
+                       var realCell = (CellNSView)GetRealCell(entryCell);
+
+                       if (e.PropertyName == EntryCell.LabelProperty.PropertyName)
+                               UpdateLabel(realCell, entryCell);
+                       else if (e.PropertyName == EntryCell.TextProperty.PropertyName)
+                               UpdateText(realCell, entryCell);
+                       else if (e.PropertyName == EntryCell.PlaceholderProperty.PropertyName)
+                               UpdatePlaceholder(realCell, entryCell);
+                       else if (e.PropertyName == EntryCell.LabelColorProperty.PropertyName)
+                               UpdateLabelColor(realCell, entryCell);
+                       else if (e.PropertyName == EntryCell.HorizontalTextAlignmentProperty.PropertyName)
+                               UpdateHorizontalTextAlignment(realCell, entryCell);
+                       else if (e.PropertyName == Cell.IsEnabledProperty.PropertyName)
+                               UpdateIsEnabled(realCell, entryCell);
+               }
+
+               static void OnTextFieldTextChanged(object sender, EventArgs eventArgs)
+               {
+                       var notification = (NSNotification)sender;
+                       var view = (NSView)notification.Object;
+                       var field = (NSTextField)view;
+
+                       CellNSView realCell = null;
+                       while (view.Superview != null && realCell == null)
+                       {
+                               view = view.Superview;
+                               realCell = view as CellNSView;
+                       }
+
+                       if (realCell != null)
+                               ((EntryCell)realCell.Cell).Text = field.StringValue;
+               }
+
+               static void UpdateHorizontalTextAlignment(CellNSView cell, EntryCell entryCell)
+               {
+                       var nsTextField = cell.AccessoryView.Subviews[0] as NSTextField;
+                       if (nsTextField != null)
+                               nsTextField.Alignment = entryCell.HorizontalTextAlignment.ToNativeTextAlignment();
+               }
+
+               static void UpdateIsEnabled(CellNSView cell, EntryCell entryCell)
+               {
+                       cell.TextLabel.Enabled = entryCell.IsEnabled;
+                       var nsTextField = cell.AccessoryView.Subviews[0] as NSTextField;
+                       if (nsTextField != null)
+                               nsTextField.Enabled = entryCell.IsEnabled;
+               }
+
+               static void UpdateLabel(CellNSView cell, EntryCell entryCell)
+               {
+                       cell.TextLabel.StringValue = entryCell.Label ?? "";
+               }
+
+               static void UpdateLabelColor(CellNSView cell, EntryCell entryCell)
+               {
+                       cell.TextLabel.TextColor = entryCell.LabelColor.ToNSColor(s_defaultTextColor);
+               }
+
+               static void UpdatePlaceholder(CellNSView cell, EntryCell entryCell)
+               {
+                       var nsTextField = cell.AccessoryView.Subviews[0] as NSTextField;
+                       if (nsTextField != null)
+                               nsTextField.PlaceholderString = entryCell.Placeholder ?? "";
+               }
+
+               static void UpdateText(CellNSView cell, EntryCell entryCell)
+               {
+                       var nsTextField = cell.AccessoryView.Subviews[0] as NSTextField;
+                       if (nsTextField != null && nsTextField.StringValue == entryCell.Text)
+                               return;
+
+                       if (nsTextField != null)
+                               nsTextField.StringValue = entryCell.Text ?? "";
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/ImageCellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/ImageCellRenderer.cs
new file mode 100644 (file)
index 0000000..8bd7677
--- /dev/null
@@ -0,0 +1,68 @@
+using System.ComponentModel;
+using System.Threading.Tasks;
+using AppKit;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class ImageCellRenderer : TextCellRenderer
+       {
+               public override NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+               {
+                       var tvc = reusableView as CellNSView ?? new CellNSView(NSTableViewCellStyle.ImageSubtitle);
+
+                       var result = (CellNSView)base.GetCell(item, tvc, tv);
+
+                       var imageCell = (ImageCell)item;
+
+                       WireUpForceUpdateSizeRequested(item, result, tv);
+
+#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
+                       SetImage(imageCell, result);
+#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
+
+                       return result;
+               }
+
+               protected override async void HandlePropertyChanged(object sender, PropertyChangedEventArgs args)
+               {
+                       var tvc = (CellNSView)sender;
+                       var imageCell = (ImageCell)tvc.Cell;
+
+                       base.HandlePropertyChanged(sender, args);
+
+                       if (args.PropertyName == ImageCell.ImageSourceProperty.PropertyName)
+                               await SetImage(imageCell, tvc);
+               }
+
+               static async Task SetImage(ImageCell cell, CellNSView target)
+               {
+                       var source = cell.ImageSource;
+
+                       target.ImageView.Image = null;
+
+                       IImageSourceHandler handler;
+
+                       if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null)
+                       {
+                               NSImage uiimage;
+                               try
+                               {
+                                       uiimage = await handler.LoadImageAsync(source).ConfigureAwait(false);
+                               }
+                               catch (TaskCanceledException)
+                               {
+                                       uiimage = null;
+                               }
+
+                               NSRunLoop.Main.BeginInvokeOnMainThread(() =>
+                               {
+                                       target.ImageView.Image = uiimage;
+                                       target.NeedsLayout = true;
+                               });
+                       }
+                       else
+                               target.ImageView.Image = null;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/NSTableViewCellStyle.cs b/Xamarin.Forms.Platform.MacOS/Cells/NSTableViewCellStyle.cs
new file mode 100644 (file)
index 0000000..3e0235d
--- /dev/null
@@ -0,0 +1,12 @@
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal enum NSTableViewCellStyle
+       {
+               Default,
+               Value1,
+               Value2,
+               Subtitle,
+               Image,
+               ImageSubtitle
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/SwitchCellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/SwitchCellRenderer.cs
new file mode 100644 (file)
index 0000000..5f086b4
--- /dev/null
@@ -0,0 +1,88 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class SwitchCellRenderer : CellRenderer
+       {
+               public override NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+               {
+                       var tvc = reusableView as CellNSView;
+                       NSButton nsSwitch = null;
+                       if (tvc == null)
+                               tvc = new CellNSView(NSTableViewCellStyle.Value1);
+                       else
+                       {
+                               nsSwitch = tvc.AccessoryView.Subviews[0] as NSButton;
+                               if (nsSwitch != null)
+                               {
+                                       nsSwitch.RemoveFromSuperview();
+                                       nsSwitch.Activated -= OnSwitchValueChanged;
+                               }
+                               tvc.Cell.PropertyChanged -= OnCellPropertyChanged;
+                       }
+
+                       SetRealCell(item, tvc);
+
+                       if (nsSwitch == null)
+                       {
+                               nsSwitch = new NSButton { AllowsMixedState = false, Title = string.Empty };
+                               nsSwitch.SetButtonType(NSButtonType.Switch);
+                       }
+
+                       var boolCell = (SwitchCell)item;
+
+                       tvc.Cell = item;
+                       tvc.Cell.PropertyChanged += OnCellPropertyChanged;
+                       tvc.AccessoryView.AddSubview(nsSwitch);
+                       tvc.TextLabel.StringValue = boolCell.Text ?? "";
+
+                       nsSwitch.State = boolCell.On ? NSCellStateValue.On : NSCellStateValue.Off;
+                       nsSwitch.Activated += OnSwitchValueChanged;
+                       WireUpForceUpdateSizeRequested(item, tvc, tv);
+
+                       UpdateBackground(tvc, item);
+                       UpdateIsEnabled(tvc, boolCell);
+
+                       return tvc;
+               }
+
+               static void UpdateIsEnabled(CellNSView cell, SwitchCell switchCell)
+               {
+                       cell.TextLabel.Enabled = switchCell.IsEnabled;
+                       var uiSwitch = cell.AccessoryView.Subviews[0] as NSButton;
+                       if (uiSwitch != null)
+                               uiSwitch.Enabled = switchCell.IsEnabled;
+               }
+
+               void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       var boolCell = (SwitchCell)sender;
+                       var realCell = (CellNSView)GetRealCell(boolCell);
+
+                       if (e.PropertyName == SwitchCell.OnProperty.PropertyName)
+                               ((NSButton)realCell.AccessoryView.Subviews[0]).State = boolCell.On ? NSCellStateValue.On : NSCellStateValue.Off;
+                       else if (e.PropertyName == SwitchCell.TextProperty.PropertyName)
+                               realCell.TextLabel.StringValue = boolCell.Text ?? "";
+                       else if (e.PropertyName == Cell.IsEnabledProperty.PropertyName)
+                               UpdateIsEnabled(realCell, boolCell);
+               }
+
+               void OnSwitchValueChanged(object sender, EventArgs eventArgs)
+               {
+                       var view = (NSView)sender;
+                       var sw = (NSButton)view;
+
+                       CellNSView realCell = null;
+                       while (view.Superview != null && realCell == null)
+                       {
+                               view = view.Superview;
+                               realCell = view as CellNSView;
+                       }
+
+                       if (realCell != null)
+                               ((SwitchCell)realCell.Cell).On = sw.State == NSCellStateValue.On;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/TextCellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/TextCellRenderer.cs
new file mode 100644 (file)
index 0000000..6e36ce7
--- /dev/null
@@ -0,0 +1,66 @@
+using System.ComponentModel;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class TextCellRenderer : CellRenderer
+       {
+               static readonly Color s_defaultDetailColor = new Color(.32, .4, .57);
+               static readonly Color s_defaultTextColor = Color.Black;
+
+               public override NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+               {
+                       var textCell = (TextCell)item;
+
+                       var tvc = reusableView as CellNSView ?? new CellNSView(NSTableViewCellStyle.Subtitle);
+
+                       if (tvc.Cell != null)
+                               tvc.Cell.PropertyChanged -= tvc.HandlePropertyChanged;
+
+                       tvc.Cell = textCell;
+                       textCell.PropertyChanged += tvc.HandlePropertyChanged;
+                       tvc.PropertyChanged = HandlePropertyChanged;
+
+                       tvc.TextLabel.StringValue = textCell.Text ?? "";
+                       tvc.DetailTextLabel.StringValue = textCell.Detail ?? "";
+                       tvc.TextLabel.TextColor = textCell.TextColor.ToNSColor(s_defaultTextColor);
+                       tvc.DetailTextLabel.TextColor = textCell.DetailColor.ToNSColor(s_defaultDetailColor);
+
+                       WireUpForceUpdateSizeRequested(item, tvc, tv);
+
+                       UpdateIsEnabled(tvc, textCell);
+
+                       UpdateBackground(tvc, item);
+
+                       return tvc;
+               }
+
+               protected virtual void HandlePropertyChanged(object sender, PropertyChangedEventArgs args)
+               {
+                       var tvc = (CellNSView)sender;
+                       var textCell = (TextCell)tvc.Cell;
+                       if (args.PropertyName == TextCell.TextProperty.PropertyName)
+                       {
+                               tvc.TextLabel.StringValue = textCell.Text ?? "";
+                               tvc.TextLabel.SizeToFit();
+                       }
+                       else if (args.PropertyName == TextCell.DetailProperty.PropertyName)
+                       {
+                               tvc.DetailTextLabel.StringValue = textCell.Detail ?? "";
+                               tvc.DetailTextLabel.SizeToFit();
+                       }
+                       else if (args.PropertyName == TextCell.TextColorProperty.PropertyName)
+                               tvc.TextLabel.TextColor = textCell.TextColor.ToNSColor(s_defaultTextColor);
+                       else if (args.PropertyName == TextCell.DetailColorProperty.PropertyName)
+                               tvc.DetailTextLabel.TextColor = textCell.DetailColor.ToNSColor(s_defaultTextColor);
+                       else if (args.PropertyName == Cell.IsEnabledProperty.PropertyName)
+                               UpdateIsEnabled(tvc, textCell);
+               }
+
+               static void UpdateIsEnabled(CellNSView cell, TextCell entryCell)
+               {
+                       cell.TextLabel.Enabled = entryCell.IsEnabled;
+                       cell.DetailTextLabel.Enabled = entryCell.IsEnabled;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/ViewCellNSView.cs b/Xamarin.Forms.Platform.MacOS/Cells/ViewCellNSView.cs
new file mode 100644 (file)
index 0000000..0dd766a
--- /dev/null
@@ -0,0 +1,105 @@
+using System;
+using AppKit;
+using RectangleF = CoreGraphics.CGRect;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class ViewCellNSView : NSView, INativeElementView
+       {
+               WeakReference<IVisualElementRenderer> _rendererRef;
+
+               ViewCell _viewCell;
+
+               public Element Element => ViewCell;
+
+               public ViewCell ViewCell
+               {
+                       get { return _viewCell; }
+                       set
+                       {
+                               if (_viewCell == value)
+                                       return;
+                               UpdateCell(value);
+                       }
+               }
+
+               public override void Layout()
+               {
+                       LayoutSubviews();
+                       base.Layout();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               IVisualElementRenderer renderer;
+                               if (_rendererRef != null && _rendererRef.TryGetTarget(out renderer) && renderer.Element != null)
+                               {
+                                       Platform.DisposeModelAndChildrenRenderers(renderer.Element);
+
+                                       _rendererRef = null;
+                               }
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               void LayoutSubviews()
+               {
+                       var contentFrame = Frame;
+                       var view = ViewCell.View;
+
+                       Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(view, contentFrame.ToRectangle());
+
+                       if (_rendererRef == null)
+                               return;
+
+                       IVisualElementRenderer renderer;
+                       if (_rendererRef.TryGetTarget(out renderer))
+                               renderer.NativeView.Frame = view.Bounds.ToRectangleF();
+               }
+
+               IVisualElementRenderer GetNewRenderer()
+               {
+                       var newRenderer = Platform.CreateRenderer(_viewCell.View);
+                       _rendererRef = new WeakReference<IVisualElementRenderer>(newRenderer);
+                       AddSubview(newRenderer.NativeView);
+                       return newRenderer;
+               }
+
+               void UpdateCell(ViewCell cell)
+               {
+                       ICellController cellController = _viewCell;
+                       if (cellController != null)
+                               Device.BeginInvokeOnMainThread(cellController.SendDisappearing);
+
+                       _viewCell = cell;
+                       cellController = cell;
+
+                       Device.BeginInvokeOnMainThread(cellController.SendAppearing);
+
+                       IVisualElementRenderer renderer;
+                       if (_rendererRef == null || !_rendererRef.TryGetTarget(out renderer))
+                               renderer = GetNewRenderer();
+                       else
+                       {
+                               if (renderer.Element != null && renderer == Platform.GetRenderer(renderer.Element))
+                                       renderer.Element.ClearValue(Platform.RendererProperty);
+
+                               var type = Registrar.Registered.GetHandlerType(_viewCell.View.GetType());
+                               if (renderer.GetType() == type || (renderer is DefaultRenderer && type == null))
+                                       renderer.SetElement(_viewCell.View);
+                               else
+                               {
+                                       //when cells are getting reused the element could be already set to another cell
+                                       //so we should dispose based on the renderer and not the renderer.Element
+                                       Platform.DisposeRendererAndChildren(renderer);
+                                       renderer = GetNewRenderer();
+                               }
+                       }
+
+                       Platform.SetRenderer(_viewCell.View, renderer);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Cells/ViewCellRenderer.cs b/Xamarin.Forms.Platform.MacOS/Cells/ViewCellRenderer.cs
new file mode 100644 (file)
index 0000000..8b34518
--- /dev/null
@@ -0,0 +1,46 @@
+using System.ComponentModel;
+using AppKit;
+
+// ReSharper disable UnusedParameter.Local
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class ViewCellRenderer : CellRenderer
+       {
+               public override NSView GetCell(Cell item, NSView reusableView, NSTableView tv)
+               {
+                       var viewCell = (ViewCell)item;
+
+                       var cell = reusableView as ViewCellNSView;
+                       if (cell == null)
+                               cell = new ViewCellNSView();
+                       else
+                               cell.ViewCell.PropertyChanged -= ViewCellPropertyChanged;
+
+                       viewCell.PropertyChanged += ViewCellPropertyChanged;
+                       cell.ViewCell = viewCell;
+
+                       SetRealCell(item, cell);
+
+                       WireUpForceUpdateSizeRequested(item, cell, tv);
+
+                       UpdateBackground(cell, item);
+                       UpdateIsEnabled(cell, viewCell);
+                       return cell;
+               }
+
+               static void UpdateIsEnabled(ViewCellNSView cell, ViewCell viewCell)
+               {
+                       //TODO: Implement IsEnabled on ViewCell
+               }
+
+               static void ViewCellPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       var viewCell = (ViewCell)sender;
+                       var realCell = (ViewCellNSView)GetRealCell(viewCell);
+
+                       if (e.PropertyName == Cell.IsEnabledProperty.PropertyName)
+                               UpdateIsEnabled(realCell, viewCell);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Controls/FormsImageView.cs b/Xamarin.Forms.Platform.MacOS/Controls/FormsImageView.cs
new file mode 100644 (file)
index 0000000..33ed73b
--- /dev/null
@@ -0,0 +1,16 @@
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class FormsNSImageView : NSImageView
+       {
+               bool _isOpaque;
+
+               public void SetIsOpaque(bool isOpaque)
+               {
+                       _isOpaque = isOpaque;
+               }
+
+               public override bool IsOpaque => _isOpaque;
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Controls/FormsPageControllerDelegate.cs b/Xamarin.Forms.Platform.MacOS/Controls/FormsPageControllerDelegate.cs
new file mode 100644 (file)
index 0000000..5fb7455
--- /dev/null
@@ -0,0 +1,29 @@
+using System;
+using AppKit;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class FormsPageControllerDelegate : NSPageControllerDelegate
+       {
+               readonly Func<NSObject, string> _getIdentifier;
+               readonly Func<string, NSViewController> _getViewController;
+
+               public FormsPageControllerDelegate(Func<NSObject, string> getIdentifier,
+                       Func<string, NSViewController> getViewController)
+               {
+                       _getIdentifier = getIdentifier;
+                       _getViewController = getViewController;
+               }
+
+               public override NSViewController GetViewController(NSPageController pageController, string identifier)
+               {
+                       return _getViewController(identifier);
+               }
+
+               public override string GetIdentifier(NSPageController pv, NSObject obj)
+               {
+                       return _getIdentifier(obj);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Controls/MacOSOpenGLView.cs b/Xamarin.Forms.Platform.MacOS/Controls/MacOSOpenGLView.cs
new file mode 100644 (file)
index 0000000..1fc2387
--- /dev/null
@@ -0,0 +1,12 @@
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       //TODO: Still not implemented on MacOS
+       public class MacOSOpenGLView : NSView
+       {
+               public MacOSOpenGLView()
+               {
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Controls/NSToolbarItemGroup.cs b/Xamarin.Forms.Platform.MacOS/Controls/NSToolbarItemGroup.cs
new file mode 100644 (file)
index 0000000..ee02293
--- /dev/null
@@ -0,0 +1,102 @@
+using System;
+using System.Runtime.InteropServices;
+using AppKit;
+using Foundation;
+using ObjCRuntime;
+
+[Register("NSToolbarItemGroup", true)]
+// ReSharper disable once CheckNamespace
+// ReSharper disable once InconsistentNaming
+public class NSToolbarItemGroup : NSToolbarItem
+{
+       const string SelSetSubitems = "setSubitems:";
+       const string SelSubitems = "subitems";
+       const string SelInitWithItemIdentifier = "initWithItemIdentifier:";
+       static readonly IntPtr s_selSetSubitemsHandle = Selector.GetHandle(SelSetSubitems);
+       static readonly IntPtr s_selSubitemsHandle = Selector.GetHandle(SelSubitems);
+       static readonly IntPtr s_selInitWithItemIdentifierHandle = Selector.GetHandle(SelInitWithItemIdentifier);
+       static readonly IntPtr s_classPtr = Class.GetHandle("NSToolbarItemGroup");
+
+       [Export("init")]
+       public NSToolbarItemGroup() : base(NSObjectFlag.Empty)
+       {
+               InitializeHandle(
+                       IsDirectBinding
+                               ? IntPtr_objc_msgSend(Handle, Selector.GetHandle("init"))
+                               : IntPtr_objc_msgSendSuper(SuperHandle, Selector.GetHandle("init")), "init");
+       }
+
+       [Export("initWithItemIdentifier:")]
+       public NSToolbarItemGroup(string itemIdentifier)
+               : base(NSObjectFlag.Empty)
+       {
+               NSApplication.EnsureUIThread();
+               if (itemIdentifier == null)
+                       throw new ArgumentNullException(nameof(itemIdentifier));
+               IntPtr nsitemIdentifier = NSString.CreateNative(itemIdentifier);
+
+               InitializeHandle(
+                       IsDirectBinding
+                               ? IntPtr_objc_msgSend_IntPtr(Handle, s_selInitWithItemIdentifierHandle, nsitemIdentifier)
+                               : IntPtr_objc_msgSendSuper_IntPtr(SuperHandle, s_selInitWithItemIdentifierHandle, nsitemIdentifier),
+                       "initWithItemIdentifier:");
+               NSString.ReleaseNative(nsitemIdentifier);
+       }
+
+       protected internal NSToolbarItemGroup(IntPtr handle) : base(handle)
+       {
+       }
+
+       protected NSToolbarItemGroup(NSObjectFlag t) : base(t)
+       {
+       }
+
+       public override IntPtr ClassHandle => s_classPtr;
+
+       public virtual NSToolbarItem[] Subitems
+       {
+               [Export(SelSubitems, ArgumentSemantic.Copy)]
+               get
+               {
+                       NSApplication.EnsureUIThread();
+                       NSToolbarItem[] ret =
+                               NSArray.ArrayFromHandle<NSToolbarItem>(IsDirectBinding
+                                       ? IntPtr_objc_msgSend(Handle, s_selSubitemsHandle)
+                                       : IntPtr_objc_msgSendSuper(SuperHandle, s_selSubitemsHandle));
+                       return ret;
+               }
+
+               [Export(SelSetSubitems, ArgumentSemantic.Copy)]
+               set
+               {
+                       NSApplication.EnsureUIThread();
+                       if (value == null)
+                               throw new ArgumentNullException(nameof(value));
+                       // ReSharper disable once CoVariantArrayConversion
+                       NSArray nsaValue = NSArray.FromNSObjects(value);
+
+                       if (IsDirectBinding)
+                               void_objc_msgSend_IntPtr(Handle, s_selSetSubitemsHandle, nsaValue.Handle);
+                       else void_objc_msgSendSuper_IntPtr(SuperHandle, s_selSetSubitemsHandle, nsaValue.Handle);
+                       nsaValue.Dispose();
+               }
+       }
+
+       [DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
+       public static extern IntPtr IntPtr_objc_msgSend(IntPtr receiver, IntPtr selector);
+
+       [DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
+       public static extern IntPtr IntPtr_objc_msgSend_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);
+
+       [DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
+       public static extern IntPtr IntPtr_objc_msgSendSuper(IntPtr receiver, IntPtr selector);
+
+       [DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
+       public static extern IntPtr IntPtr_objc_msgSendSuper_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);
+
+       [DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
+       public static extern void void_objc_msgSend_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);
+
+       [DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
+       public static extern void void_objc_msgSendSuper_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Controls/NavigationChildPageWrapper.cs b/Xamarin.Forms.Platform.MacOS/Controls/NavigationChildPageWrapper.cs
new file mode 100644 (file)
index 0000000..a0e6dd4
--- /dev/null
@@ -0,0 +1,41 @@
+using System;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class NavigationChildPageWrapper : NSObject
+       {
+               bool _disposed;
+
+               public NavigationChildPageWrapper(Page page)
+               {
+                       Page = page;
+                       Page.PropertyChanged += PagePropertyChanged;
+                       Identifier = Guid.NewGuid().ToString();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               _disposed = true;
+                               if (Page != null)
+                                       Page.PropertyChanged -= PagePropertyChanged;
+                               Page = null;
+                       }
+                       base.Dispose(disposing);
+               }
+
+               void PagePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName
+                               || e.PropertyName == Page.TitleProperty.PropertyName
+                               || e.PropertyName == NavigationPage.HasBackButtonProperty.PropertyName)
+                               Platform.NativeToolbarTracker.UpdateToolBar();
+               }
+
+               public string Identifier { get; set; }
+
+               public Page Page { get; private set; }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Controls/ScrollViewScrollChangedEventArgs.cs b/Xamarin.Forms.Platform.MacOS/Controls/ScrollViewScrollChangedEventArgs.cs
new file mode 100644 (file)
index 0000000..a8a825c
--- /dev/null
@@ -0,0 +1,10 @@
+using System;
+using PointF = CoreGraphics.CGPoint;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class ScrollViewScrollChangedEventArgs : EventArgs
+       {
+               public PointF CurrentScrollPoint { get; set; }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Controls/VerticallyCenteredTextFieldCell.cs b/Xamarin.Forms.Platform.MacOS/Controls/VerticallyCenteredTextFieldCell.cs
new file mode 100644 (file)
index 0000000..397f632
--- /dev/null
@@ -0,0 +1,36 @@
+using System;
+using AppKit;
+using CoreGraphics;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       sealed class VerticallyCenteredTextFieldCell : NSTextFieldCell
+       {
+               readonly nfloat _yOffset;
+
+               public VerticallyCenteredTextFieldCell(nfloat yOffset, NSFont font = null)
+               {
+                       if (font != null)
+                               Font = font;
+                       _yOffset = yOffset;
+               }
+
+               public override CGRect DrawingRectForBounds(CGRect theRect)
+               {
+                       // Get the parent's idea of where we should draw.
+                       CGRect newRect = base.DrawingRectForBounds(theRect);
+
+                       // Ideal size for the text.
+                       CGSize textSize = CellSizeForBounds(theRect);
+
+                       // Center in the rect.
+                       nfloat heightDelta = newRect.Size.Height - textSize.Height;
+                       if (heightDelta > 0)
+                       {
+                               newRect.Size = new CGSize(newRect.Width, newRect.Height - heightDelta);
+                               newRect.Location = new CGPoint(newRect.X, newRect.Y + heightDelta / 2 + _yOffset);
+                       }
+                       return newRect;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Extensions/AlignmentExtensions.cs b/Xamarin.Forms.Platform.MacOS/Extensions/AlignmentExtensions.cs
new file mode 100644 (file)
index 0000000..35d0f26
--- /dev/null
@@ -0,0 +1,21 @@
+using System;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal static class AlignmentExtensions
+       {
+               internal static NSTextAlignment ToNativeTextAlignment(this TextAlignment alignment)
+               {
+                       switch (alignment)
+                       {
+                               case TextAlignment.Center:
+                                       return NSTextAlignment.Center;
+                               case TextAlignment.End:
+                                       return NSTextAlignment.Right;
+                               default:
+                                       return NSTextAlignment.Left;
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Extensions/ButtonExtensions.cs b/Xamarin.Forms.Platform.MacOS/Extensions/ButtonExtensions.cs
new file mode 100644 (file)
index 0000000..1bdc62a
--- /dev/null
@@ -0,0 +1,25 @@
+using System;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal static class ButtonExtensions
+       {
+               public static NSCellImagePosition ToNSCellImagePosition(this Button control)
+               {
+                       switch (control.ContentLayout.Position)
+                       {
+                               case Button.ButtonContentLayout.ImagePosition.Left:
+                                       return NSCellImagePosition.ImageLeft;
+                               case Button.ButtonContentLayout.ImagePosition.Top:
+                                       return NSCellImagePosition.ImageAbove;
+                               case Button.ButtonContentLayout.ImagePosition.Right:
+                                       return NSCellImagePosition.ImageRight;
+                               case Button.ButtonContentLayout.ImagePosition.Bottom:
+                                       return NSCellImagePosition.ImageBelow;
+                               default:
+                                       return NSCellImagePosition.ImageOnly;
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Extensions/NSButtonExtensions.cs b/Xamarin.Forms.Platform.MacOS/Extensions/NSButtonExtensions.cs
new file mode 100644 (file)
index 0000000..d37f1a3
--- /dev/null
@@ -0,0 +1,25 @@
+using System;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public static class NSButtonExtensions
+       {
+               public static NSButton CreateButton(string text, Action activate = null)
+               {
+                       return CreateButton(text, null, activate);
+               }
+
+               public static NSButton CreateButton(string text, NSImage image = null, Action activate = null)
+               {
+                       var btn = new NSButton { Title = text };
+                       btn.BezelStyle = NSBezelStyle.TexturedRounded;
+
+                       if (image != null)
+                               btn.Image = image;
+                       if (activate != null)
+                               btn.Activated += (sender, e) => activate();
+                       return btn;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Extensions/NSImageExtensions.cs b/Xamarin.Forms.Platform.MacOS/Extensions/NSImageExtensions.cs
new file mode 100644 (file)
index 0000000..0952173
--- /dev/null
@@ -0,0 +1,22 @@
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public static class NSImageExtensions
+       {
+               public static NSImage ResizeTo(this NSImage self, CoreGraphics.CGSize newSize)
+               {
+                       if (self == null)
+                               return null;
+                       self.ResizingMode = NSImageResizingMode.Stretch;
+                       var resizedImage = new NSImage(newSize);
+                       resizedImage.LockFocus();
+                       self.Size = newSize;
+                       NSGraphicsContext.CurrentContext.ImageInterpolation = NSImageInterpolation.High;
+                       self.Draw(CoreGraphics.CGPoint.Empty, new CoreGraphics.CGRect(0, 0, newSize.Width, newSize.Height),
+                               NSCompositingOperation.Copy, 1.0f);
+                       resizedImage.UnlockFocus();
+                       return resizedImage;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Extensions/NSScrollViewExtensions.cs b/Xamarin.Forms.Platform.MacOS/Extensions/NSScrollViewExtensions.cs
new file mode 100644 (file)
index 0000000..f9104d4
--- /dev/null
@@ -0,0 +1,36 @@
+using System.Threading.Tasks;
+using AppKit;
+using PointF = CoreGraphics.CGPoint;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal static class NSScrollViewExtensions
+       {
+               public static Task ScrollToPositionAsync(this NSScrollView scrollView, PointF point, bool animate,
+                       double duration = 0.5)
+               {
+                       if (!animate)
+                       {
+                               var nsView = scrollView.DocumentView as NSView;
+                               nsView?.ScrollPoint(point);
+                               return Task.FromResult(true);
+                       }
+
+                       TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
+
+                       NSAnimationContext.BeginGrouping();
+
+                       NSAnimationContext.CurrentContext.CompletionHandler += () => { source.TrySetResult(true); };
+
+                       NSAnimationContext.CurrentContext.Duration = duration;
+
+                       var animator = scrollView.ContentView.Animator as NSView;
+
+                       animator?.SetBoundsOrigin(point);
+
+                       NSAnimationContext.EndGrouping();
+
+                       return source.Task;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Extensions/NSTableViewExtensions.cs b/Xamarin.Forms.Platform.MacOS/Extensions/NSTableViewExtensions.cs
new file mode 100644 (file)
index 0000000..c81e31e
--- /dev/null
@@ -0,0 +1,22 @@
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal static class NSTableViewExtensions
+       {
+               public static NSTableView AsListViewLook(this NSTableView self)
+               {
+                       self.SelectionHighlightStyle = NSTableViewSelectionHighlightStyle.SourceList;
+
+                       self.AllowsColumnReordering = false;
+                       self.AllowsColumnResizing = false;
+                       self.AllowsColumnSelection = false;
+
+                       //this is needed .. can we go around it ?
+                       self.AddColumn(new NSTableColumn("1"));
+                       //this line hides the header by default
+                       self.HeaderView = new CustomNSTableHeaderView();
+                       return self;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Extensions/NSTextFieldExtensions.cs b/Xamarin.Forms.Platform.MacOS/Extensions/NSTextFieldExtensions.cs
new file mode 100644 (file)
index 0000000..9905fcd
--- /dev/null
@@ -0,0 +1,46 @@
+using AppKit;
+using CoreGraphics;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal static class NSTextFieldExtensions
+       {
+               public static NSTextField CreateLabel(string text)
+               {
+                       var textField = new NSTextField();
+                       textField.StringValue = text;
+                       textField.DrawsBackground = false;
+                       textField.Editable = false;
+                       textField.Bezeled = false;
+                       textField.Selectable = false;
+                       textField.SizeToFit();
+                       textField.CenterTextVertically();
+                       return textField;
+               }
+
+               public static NSTextFieldCell CreateLabelCentered(string text)
+               {
+                       var textField = new VerticallyCenteredTextFieldCell(0);
+                       textField.StringValue = text;
+                       textField.DrawsBackground = false;
+                       textField.Editable = false;
+                       textField.Bezeled = false;
+                       textField.Selectable = false;
+                       return textField;
+               }
+
+               public static void CenterTextVertically(this NSTextField self)
+               {
+                       self.CenterTextVertically(self.Frame);
+               }
+
+               public static void CenterTextVertically(this NSTextField self, CGRect frame)
+               {
+                       var stringHeight = self.Cell.AttributedStringValue.Size.Height;
+                       var titleRect = self.Cell.TitleRectForBounds(frame);
+                       var newTitleRect = new CGRect(titleRect.X, frame.Y + (frame.Height - stringHeight) / 2.0, titleRect.Width,
+                               stringHeight);
+                       self.Frame = newTitleRect;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Extensions/NSViewControllerExtensions.cs b/Xamarin.Forms.Platform.MacOS/Extensions/NSViewControllerExtensions.cs
new file mode 100644 (file)
index 0000000..f556232
--- /dev/null
@@ -0,0 +1,24 @@
+using System;
+using System.Threading.Tasks;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal static class NSViewControllerExtensions
+       {
+               public static Task<T> HandleAsyncAnimation<T>(this NSViewController container, NSViewController fromViewController,
+                       NSViewController toViewController, NSViewControllerTransitionOptions transitonOption,
+                       Action animationFinishedCallback, T result)
+               {
+                       var tcs = new TaskCompletionSource<T>();
+
+                       container.TransitionFromViewController(fromViewController, toViewController, transitonOption, () =>
+                       {
+                               tcs.SetResult(result);
+                               animationFinishedCallback?.Invoke();
+                       });
+
+                       return tcs.Task;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Extensions/PageExtensions.cs b/Xamarin.Forms.Platform.MacOS/Extensions/PageExtensions.cs
new file mode 100644 (file)
index 0000000..24c9a52
--- /dev/null
@@ -0,0 +1,28 @@
+using System;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public static class PageExtensions
+       {
+               public static NSViewController CreateViewController(this Page view)
+               {
+                       if (!Forms.IsInitialized)
+                               throw new InvalidOperationException("call Forms.Init() before this");
+
+                       if (!(view.RealParent is Application))
+                       {
+                               Application app = new DefaultApplication();
+                               app.MainPage = view;
+                       }
+
+                       var result = new Platform();
+                       result.SetPage(view);
+                       return result.ViewController;
+               }
+
+               class DefaultApplication : Application
+               {
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/FormsApplicationDelegate.cs b/Xamarin.Forms.Platform.MacOS/FormsApplicationDelegate.cs
new file mode 100644 (file)
index 0000000..4899698
--- /dev/null
@@ -0,0 +1,85 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public abstract class FormsApplicationDelegate : NSApplicationDelegate
+       {
+               Application _application;
+               bool _isSuspended;
+
+               public abstract NSWindow MainWindow { get; }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && _application != null)
+                               _application.PropertyChanged -= ApplicationOnPropertyChanged;
+
+                       base.Dispose(disposing);
+               }
+
+               protected void LoadApplication(Application application)
+               {
+                       if (application == null)
+                               throw new ArgumentNullException(nameof(application));
+
+                       Application.Current = application;
+                       _application = application;
+
+                       application.PropertyChanged += ApplicationOnPropertyChanged;
+               }
+
+               public override void DidFinishLaunching(Foundation.NSNotification notification)
+               {
+                       if (MainWindow == null)
+                               throw new InvalidOperationException("Please provide a main window in your app");
+
+                       MainWindow.Display();
+                       MainWindow.MakeKeyAndOrderFront(NSApplication.SharedApplication);
+                       if (_application == null)
+                               throw new InvalidOperationException("You MUST invoke LoadApplication () before calling base.FinishedLaunching ()");
+
+                       SetMainPage();
+                       _application.SendStart();
+               }
+
+               public override void DidBecomeActive(Foundation.NSNotification notification)
+               {
+                       // applicationDidBecomeActive
+                       // execute any OpenGL ES drawing calls
+                       if (_application == null || !_isSuspended) return;
+                       _isSuspended = false;
+                       _application.SendResume();
+               }
+
+               public override async void DidResignActive(Foundation.NSNotification notification)
+               {
+                       // applicationWillResignActive
+                       if (_application == null) return;
+                       _isSuspended = true;
+                       await _application.SendSleepAsync();
+               }
+
+               void ApplicationOnPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == nameof(Application.MainPage))
+                               UpdateMainPage();
+               }
+
+               void SetMainPage()
+               {
+                       UpdateMainPage();
+               }
+
+               void UpdateMainPage()
+               {
+                       if (_application.MainPage == null)
+                               return;
+
+                       var platformRenderer = (PlatformRenderer)MainWindow.ContentViewController;
+                       MainWindow.ContentViewController = _application.MainPage.CreateViewController();
+                       (platformRenderer?.Platform as IDisposable)?.Dispose();
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/ImageSourceHandlers.cs b/Xamarin.Forms.Platform.MacOS/ImageSourceHandlers.cs
new file mode 100644 (file)
index 0000000..7a73ace
--- /dev/null
@@ -0,0 +1,64 @@
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public interface IImageSourceHandler : IRegisterable
+       {
+               Task<NSImage> LoadImageAsync(ImageSource imagesource, CancellationToken cancelationToken = default(CancellationToken),
+                       float scale = 1);
+       }
+
+       public sealed class FileImageSourceHandler : IImageSourceHandler
+       {
+               public Task<NSImage> LoadImageAsync(ImageSource imagesource,
+                       CancellationToken cancelationToken = default(CancellationToken), float scale = 1f)
+               {
+                       NSImage image = null;
+                       var filesource = imagesource as FileImageSource;
+                       var file = filesource?.File;
+                       if (!string.IsNullOrEmpty(file))
+                               image = File.Exists(file) ? new NSImage(file) : null;
+                       return Task.FromResult(image);
+               }
+       }
+
+       public sealed class StreamImagesourceHandler : IImageSourceHandler
+       {
+               public async Task<NSImage> LoadImageAsync(ImageSource imagesource,
+                       CancellationToken cancelationToken = default(CancellationToken), float scale = 1f)
+               {
+                       NSImage image = null;
+                       var streamsource = imagesource as StreamImageSource;
+                       if (streamsource?.Stream == null) return null;
+                       using (
+                               var streamImage = await ((IStreamImageSource)streamsource).GetStreamAsync(cancelationToken).ConfigureAwait(false))
+                       {
+                               if (streamImage != null)
+                                       image = NSImage.FromStream(streamImage);
+                       }
+                       return image;
+               }
+       }
+
+       public sealed class ImageLoaderSourceHandler : IImageSourceHandler
+       {
+               public async Task<NSImage> LoadImageAsync(ImageSource imagesource,
+                       CancellationToken cancelationToken = default(CancellationToken), float scale = 1f)
+               {
+                       NSImage image = null;
+                       var imageLoader = imagesource as UriImageSource;
+                       if (imageLoader != null && imageLoader.Uri != null)
+                       {
+                               using (var streamImage = await imageLoader.GetStreamAsync(cancelationToken).ConfigureAwait(false))
+                               {
+                                       if (streamImage != null)
+                                               image = NSImage.FromStream(streamImage);
+                               }
+                       }
+                       return image;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/ModalPageTracker.cs b/Xamarin.Forms.Platform.MacOS/ModalPageTracker.cs
new file mode 100644 (file)
index 0000000..c492f4c
--- /dev/null
@@ -0,0 +1,118 @@
+using System;
+using System.Threading.Tasks;
+using System.Linq;
+using AppKit;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class ModalPageTracker : IDisposable
+       {
+               NSViewController _renderer;
+               List<Page> _modals;
+               bool _disposed;
+
+               public ModalPageTracker(NSViewController mainRenderer)
+               {
+                       if (mainRenderer == null)
+                               throw new ArgumentNullException(nameof(mainRenderer));
+                       _renderer = mainRenderer;
+                       _renderer.View.WantsLayer = true;
+                       _modals = new List<Page>();
+               }
+
+               public List<Page> ModalStack => _modals;
+
+               public Task PushAsync(Page modal, bool animated)
+               {
+                       _modals.Add(modal);
+                       modal.DescendantRemoved += HandleChildRemoved;
+                       Platform.NativeToolbarTracker.TryHide(modal as NavigationPage);
+                       return PresentModalAsync(modal, animated);
+               }
+
+               public Task<Page> PopAsync(bool animated)
+               {
+                       var modal = _modals.LastOrDefault();
+                       if (modal == null)
+                               throw new InvalidOperationException("No Modal pages found in the stack, make sure you pushed a modal page");
+                       _modals.Remove(modal);
+                       modal.DescendantRemoved -= HandleChildRemoved;
+                       return HideModalAsync(modal, animated);
+               }
+
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (!_disposed)
+                       {
+                               if (disposing)
+                               {
+                                       foreach (var modal in _modals)
+                                               Platform.DisposeModelAndChildrenRenderers(modal);
+                                       _renderer = null;
+                               }
+                               _disposed = true;
+                       }
+               }
+
+               void HandleChildRemoved(object sender, ElementEventArgs e)
+               {
+                       var view = e.Element;
+                       Platform.DisposeModelAndChildrenRenderers(view);
+               }
+
+               Task PresentModalAsync(Page modal, bool animated)
+               {
+                       var modalRenderer = Platform.GetRenderer(modal);
+                       if (modalRenderer == null)
+                       {
+                               modalRenderer = Platform.CreateRenderer(modal);
+                               Platform.SetRenderer(modal, modalRenderer);
+                               modalRenderer.SetElementSize(new Size(_renderer.View.Bounds.Width, _renderer.View.Bounds.Height));
+                       }
+
+                       var toViewController = modalRenderer as NSViewController;
+
+                       var i = Math.Max(0, _renderer.ChildViewControllers.Length - 1);
+                       var fromViewController = _renderer.ChildViewControllers[i];
+
+                       _renderer.AddChildViewController(toViewController);
+
+                       NSViewControllerTransitionOptions option = animated
+                               ? NSViewControllerTransitionOptions.SlideUp
+                               : NSViewControllerTransitionOptions.None;
+
+                       var task = _renderer.HandleAsyncAnimation(fromViewController, toViewController, option,
+                               () =>
+                               {
+                                       //Hack: adjust if needed
+                                       toViewController.View.Frame = _renderer.View.Bounds;
+                                       fromViewController.View.Layer.Hidden = true;
+                               }, true);
+                       return task;
+               }
+
+               Task<Page> HideModalAsync(Page modal, bool animated)
+               {
+                       var controller = Platform.GetRenderer(modal) as NSViewController;
+
+                       var i = Math.Max(0, _renderer.ChildViewControllers.Length - 2);
+                       var toViewController = _renderer.ChildViewControllers[i];
+
+                       toViewController.View.Layer.Hidden = false;
+
+                       NSViewControllerTransitionOptions option = animated
+                                                       ? NSViewControllerTransitionOptions.SlideDown
+                                                       : NSViewControllerTransitionOptions.None;
+
+                       var task = _renderer.HandleAsyncAnimation(controller, toViewController, option,
+                               () => Platform.DisposeModelAndChildrenRenderers(modal), modal);
+                       return task;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/NativeToolbarTracker.cs b/Xamarin.Forms.Platform.MacOS/NativeToolbarTracker.cs
new file mode 100644 (file)
index 0000000..826b5b7
--- /dev/null
@@ -0,0 +1,429 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using AppKit;
+using CoreGraphics;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.PlatformConfiguration.macOSSpecific;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       class NativeToolbarGroup
+       {
+               public class Item
+               {
+                       public NSToolbarItem ToolbarItem;
+                       public NSButton Button;
+               }
+
+               public NativeToolbarGroup(NSToolbarItemGroup itemGroup)
+               {
+                       Group = itemGroup;
+                       Items = new List<Item>();
+               }
+
+               public NSToolbarItemGroup Group { get; }
+
+               public List<Item> Items { get; }
+       }
+
+       internal class NativeToolbarTracker : NSToolbarDelegate
+       {
+               const string ToolBarId = "AwesomeBarToolbar";
+
+               INavigationPageController NavigationController => _navigation;
+
+               readonly string _defaultBackButtonTitle = "Back";
+               readonly ToolbarTracker _toolbarTracker;
+
+               NSToolbar _toolbar;
+               NavigationPage _navigation;
+
+               bool _hasTabs;
+
+               const double BackButtonItemWidth = 36;
+               const double ToolbarItemWidth = 44;
+               const double ToolbarItemHeight = 25;
+               const double ToolbarItemSpacing = 6;
+               const double ToolbarHeight = 30;
+               const double NavigationTitleMinSize = 300;
+
+               const string NavigationGroupIdentifier = "NavigationGroup";
+               const string TabbedGroupIdentifier = "TabbedGroup";
+               const string ToolbarItemsGroupIdentifier = "ToolbarGroup";
+               const string TitleGroupIdentifier = "TitleGroup";
+
+               NativeToolbarGroup _navigationGroup;
+               NativeToolbarGroup _tabbedGroup;
+               NativeToolbarGroup _toolbarGroup;
+               NativeToolbarGroup _titleGroup;
+
+               NSView _nsToolbarItemViewer;
+
+               public NativeToolbarTracker()
+               {
+                       _toolbarTracker = new ToolbarTracker();
+                       _toolbarTracker.CollectionChanged += ToolbarTrackerOnCollectionChanged;
+               }
+
+               public NavigationPage Navigation
+               {
+                       get { return _navigation; }
+                       set
+                       {
+                               if (_navigation == value)
+                                       return;
+
+                               if (_navigation != null)
+                                       _navigation.PropertyChanged -= NavigationPagePropertyChanged;
+
+                               _navigation = value;
+
+                               if (_navigation != null)
+                               {
+                                       var parentTabbedPage = _navigation.Parent as TabbedPage;
+                                       if (parentTabbedPage != null)
+                                       {
+                                               _hasTabs = parentTabbedPage.OnThisPlatform().GetTabsStyle() == TabsStyle.OnNavigation;
+                                       }
+                                       _toolbarTracker.Target = _navigation.CurrentPage;
+                                       _navigation.PropertyChanged += NavigationPagePropertyChanged;
+                               }
+
+                               UpdateToolBar();
+                       }
+               }
+
+               public void TryHide(NavigationPage navPage = null)
+               {
+                       if (navPage == null || navPage == _navigation)
+                       {
+                               Navigation = null;
+                       }
+               }
+
+               public override string[] AllowedItemIdentifiers(NSToolbar toolbar)
+               {
+                       return new string[] { };
+               }
+
+               public override string[] DefaultItemIdentifiers(NSToolbar toolbar)
+               {
+                       return new string[] { };
+               }
+
+               public override NSToolbarItem WillInsertItem(NSToolbar toolbar, string itemIdentifier, bool willBeInserted)
+               {
+                       var group = new NSToolbarItemGroup(itemIdentifier);
+                       var view = new NSView();
+                       group.View = view;
+
+                       if (itemIdentifier == NavigationGroupIdentifier)
+                               _navigationGroup = new NativeToolbarGroup(group);
+                       else if (itemIdentifier == TitleGroupIdentifier)
+                               _titleGroup = new NativeToolbarGroup(group);
+                       else if (itemIdentifier == TabbedGroupIdentifier)
+                               _tabbedGroup = new NativeToolbarGroup(group);
+                       else if (itemIdentifier == ToolbarItemsGroupIdentifier)
+                               _toolbarGroup = new NativeToolbarGroup(group);
+
+                       return group;
+               }
+
+               protected virtual bool HasTabs => _hasTabs;
+
+               protected virtual NSToolbar ConfigureToolbar()
+               {
+                       var toolbar = new NSToolbar(ToolBarId)
+                       {
+                               DisplayMode = NSToolbarDisplayMode.Icon,
+                               AllowsUserCustomization = false,
+                               ShowsBaselineSeparator = true,
+                               SizeMode = NSToolbarSizeMode.Regular,
+                               Delegate = this
+                       };
+
+                       return toolbar;
+               }
+
+               internal void UpdateToolBar()
+               {
+                       if (NSApplication.SharedApplication.MainWindow == null)
+                               return;
+
+                       if (NavigationController == null)
+                       {
+                               if (_toolbar != null)
+                                       _toolbar.Visible = false;
+                               _toolbar = null;
+                               return;
+                       }
+
+                       var currentPage = NavigationController.Peek();
+
+                       if (NavigationPage.GetHasNavigationBar(currentPage))
+                       {
+                               if (_toolbar == null)
+                               {
+                                       _toolbar = ConfigureToolbar();
+                                       NSApplication.SharedApplication.MainWindow.Toolbar = _toolbar;
+
+                                       _toolbar.InsertItem(NavigationGroupIdentifier, 0);
+                                       _toolbar.InsertItem(
+                                               HasTabs ? NSToolbar.NSToolbarSpaceItemIdentifier : NSToolbar.NSToolbarFlexibleSpaceItemIdentifier, 1);
+                                       _toolbar.InsertItem(HasTabs ? TabbedGroupIdentifier : TitleGroupIdentifier, 2);
+                                       _toolbar.InsertItem(NSToolbar.NSToolbarFlexibleSpaceItemIdentifier, 3);
+                                       _toolbar.InsertItem(ToolbarItemsGroupIdentifier, 4);
+                               }
+
+                               _toolbar.Visible = true;
+                               UpdateToolbarItems();
+                               UpdateTitle();
+                               UpdateNavigationItems();
+                               if (HasTabs)
+                                       UpdateTabbedItems();
+                               UpdateBarBackgroundColor();
+                       }
+                       else
+                       {
+                               if (_toolbar != null)
+                               {
+                                       _toolbar.Visible = false;
+                               }
+                       }
+               }
+
+               void UpdateBarBackgroundColor()
+               {
+                       var bgColor = GetBackgroundColor().CGColor;
+
+                       if (_nsToolbarItemViewer?.Superview?.Superview == null ||
+                               _nsToolbarItemViewer.Superview.Superview.Superview == null) return;
+                       // NSTitlebarView
+                       _nsToolbarItemViewer.Superview.Superview.Superview.WantsLayer = true;
+                       _nsToolbarItemViewer.Superview.Superview.Superview.Layer.BackgroundColor = bgColor;
+               }
+
+               void NavigationPagePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName.Equals(NavigationPage.BarTextColorProperty.PropertyName) ||
+                               e.PropertyName.Equals(NavigationPage.BarBackgroundColorProperty.PropertyName))
+                               UpdateToolBar();
+               }
+
+               void ToolbarTrackerOnCollectionChanged(object sender, EventArgs eventArgs)
+               {
+                       UpdateToolbarItems();
+               }
+
+               async Task NavigateBackFrombackButton()
+               {
+                       var popAsyncInner = NavigationController?.PopAsyncInner(true, true);
+                       if (popAsyncInner != null)
+                               await popAsyncInner;
+               }
+
+               bool ShowBackButton()
+               {
+                       if (_navigation == null)
+                               return false;
+
+                       return NavigationPage.GetHasBackButton(_navigation.CurrentPage) && !IsRootPage();
+               }
+
+               bool IsRootPage()
+               {
+                       if (NavigationController == null)
+                               return true;
+                       return NavigationController.StackDepth <= 1;
+               }
+
+               NSColor GetBackgroundColor()
+               {
+                       var backgroundNSColor = NSColor.Clear;
+                       if (Navigation != null && Navigation.BarBackgroundColor != Color.Default)
+                               backgroundNSColor = Navigation.BarBackgroundColor.ToNSColor();
+                       return backgroundNSColor;
+               }
+
+               NSColor GetTitleColor()
+               {
+                       var titleNSColor = NSColor.Black;
+                       if (Navigation != null && Navigation?.BarTextColor != Color.Default)
+                               titleNSColor = Navigation.BarTextColor.ToNSColor();
+
+                       return titleNSColor;
+               }
+
+               string GetCurrentPageTitle()
+               {
+                       if (NavigationController == null)
+                               return string.Empty;
+                       return NavigationController.Peek().Title ?? "";
+               }
+
+               string GetPreviousPageTitle()
+               {
+                       if (NavigationController == null || NavigationController.StackDepth <= 1)
+                               return string.Empty;
+
+                       return NavigationController.Peek(1).Title ?? _defaultBackButtonTitle;
+               }
+
+               List<ToolbarItem> GetToolbarItems()
+               {
+                       return _toolbarTracker.ToolbarItems.ToList();
+               }
+
+               void UpdateTitle()
+               {
+                       if (_toolbar == null || _navigation == null || _titleGroup == null)
+                               return;
+
+                       var title = GetCurrentPageTitle();
+                       var item = new NSToolbarItem(title);
+                       var view = new NSView();
+                       var titleField = new NSTextField
+                       {
+                               AllowsEditingTextAttributes = true,
+                               Bordered = false,
+                               DrawsBackground = false,
+                               Bezeled = false,
+                               Editable = false,
+                               Selectable = false,
+                               Cell = new VerticallyCenteredTextFieldCell(0f, NSFont.TitleBarFontOfSize(18)),
+                               StringValue = title
+                       };
+                       titleField.Cell.TextColor = GetTitleColor();
+                       titleField.SizeToFit();
+                       _titleGroup.Group.MinSize = new CGSize(NavigationTitleMinSize, ToolbarHeight);
+                       _titleGroup.Group.Subitems = new NSToolbarItem[] { item };
+                       view.AddSubview(titleField);
+                       _titleGroup.Group.View = view;
+                       //save a reference so we can paint this for the background
+                       _nsToolbarItemViewer = _titleGroup.Group.View.Superview;
+                       //position is hard .. we manually set the title to be centered 
+                       var totalWidth = _titleGroup.Group.View.Superview.Superview.Frame.Width;
+                       var fieldWidth = titleField.Frame.Width;
+                       var x = ((totalWidth - fieldWidth) / 2) - _nsToolbarItemViewer.Frame.X;
+                       titleField.Frame = new CGRect(x, 0, fieldWidth, ToolbarHeight);
+               }
+
+               void UpdateToolbarItems()
+               {
+                       if (_toolbar == null || _navigation == null || _toolbarGroup == null)
+                               return;
+
+                       var currentPage = NavigationController.Peek();
+                       UpdateGroup(_toolbarGroup, currentPage.ToolbarItems, ToolbarItemWidth, ToolbarItemSpacing);
+               }
+
+               void UpdateNavigationItems()
+               {
+                       if (_toolbar == null || _navigation == null || _navigationGroup == null)
+                               return;
+                       var items = new List<ToolbarItem>();
+                       if (ShowBackButton())
+                       {
+                               var backButtonItem = new ToolbarItem
+                               {
+                                       Text = GetPreviousPageTitle(),
+                                       Command = new Command(async () => await NavigateBackFrombackButton())
+                               };
+                               items.Add(backButtonItem);
+                       }
+
+                       UpdateGroup(_navigationGroup, items, BackButtonItemWidth, -1);
+
+                       var navItemBack = _navigationGroup.Items.FirstOrDefault();
+                       if (navItemBack != null)
+                       {
+                               navItemBack.Button.Image = NSImage.ImageNamed(NSImageName.GoLeftTemplate);
+                               navItemBack.Button.SizeToFit();
+                               navItemBack.Button.AccessibilityTitle = "NSBackButton";
+                       }
+               }
+
+               void UpdateTabbedItems()
+               {
+                       if (_toolbar == null || _navigation == null || _tabbedGroup == null)
+                               return;
+
+                       var items = new List<ToolbarItem>();
+
+                       var tabbedPage = _navigation.Parent as TabbedPage;
+                       if (tabbedPage != null)
+                       {
+                               foreach (var item in tabbedPage.Children)
+                               {
+                                       var tbI = new ToolbarItem
+                                       {
+                                               Text = item.Title,
+                                               Icon = item.Icon,
+                                               Command = new Command(() => tabbedPage.SelectedItem = item)
+                                       };
+                                       items.Add(tbI);
+                               }
+                       }
+
+                       UpdateGroup(_tabbedGroup, items, ToolbarItemWidth, ToolbarItemSpacing);
+               }
+
+               static void UpdateGroup(NativeToolbarGroup group, IList<ToolbarItem> toolbarItems, double itemWidth,
+                       double itemSpacing)
+               {
+                       int count = toolbarItems.Count;
+                       group.Items.Clear();
+                       if (count > 0)
+                       {
+                               var subItems = new NSToolbarItem[count];
+                               var view = new NSView();
+                               nfloat totalWidth = 0;
+                               var currentX = 0.0;
+                               for (int i = 0; i < toolbarItems.Count; i++)
+                               {
+                                       var element = toolbarItems[i];
+
+                                       var item = new NSToolbarItem(element.Text);
+                                       item.Activated += (sender, e) => (element as IMenuItemController).Activate();
+
+                                       var button = new NSButton();
+                                       button.Title = element.Text;
+                                       button.SizeToFit();
+                                       var buttonWidth = itemWidth;
+                                       if (button.FittingSize.Width > itemWidth)
+                                       {
+                                               buttonWidth = button.FittingSize.Width + 10;
+                                       }
+                                       button.Frame = new CGRect(currentX + i * itemSpacing, 0, buttonWidth, ToolbarItemHeight);
+                                       currentX += buttonWidth;
+                                       totalWidth += button.Frame.Width;
+                                       button.Activated += (sender, e) => (element as IMenuItemController).Activate();
+
+                                       button.BezelStyle = NSBezelStyle.TexturedRounded;
+                                       if (!string.IsNullOrEmpty(element.Icon))
+                                               button.Image = new NSImage(element.Icon);
+
+                                       button.SizeToFit();
+                                       view.AddSubview(button);
+
+                                       item.Label = item.PaletteLabel = item.ToolTip = button.ToolTip = element.Text;
+
+                                       subItems[i] = item;
+
+                                       group.Items.Add(new NativeToolbarGroup.Item { ToolbarItem = item, Button = button });
+                               }
+                               view.Frame = new CGRect(0, 0, totalWidth + (itemSpacing * (count - 1)), ToolbarItemHeight);
+
+                               group.Group.Subitems = subItems;
+                               group.Group.View = view;
+                       }
+                       else
+                       {
+                               group.Group.Subitems = new NSToolbarItem[] { };
+                               group.Group.View = new NSView();
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Platform.cs b/Xamarin.Forms.Platform.MacOS/Platform.cs
new file mode 100644 (file)
index 0000000..8629684
--- /dev/null
@@ -0,0 +1,262 @@
+using System;
+using AppKit;
+using RectangleF = CoreGraphics.CGRect;
+using System.Linq;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class Platform : BindableObject, IPlatform, IDisposable
+       {
+               internal static readonly BindableProperty RendererProperty = BindableProperty.CreateAttached("Renderer",
+                       typeof(IVisualElementRenderer), typeof(Platform), default(IVisualElementRenderer),
+                       propertyChanged: (bindable, oldvalue, newvalue) =>
+                       {
+                               var view = bindable as VisualElement;
+                               if (view != null)
+                                       view.IsPlatformEnabled = newvalue != null;
+                       });
+
+               readonly PlatformRenderer PlatformRenderer;
+
+               bool _appeared;
+               bool _disposed;
+
+               internal static NativeToolbarTracker NativeToolbarTracker = new NativeToolbarTracker();
+
+               internal Platform()
+               {
+                       PlatformRenderer = new PlatformRenderer(this);
+
+                       MessagingCenter.Subscribe(this, Page.AlertSignalName, (Page sender, AlertArguments arguments) =>
+                       {
+                               var alert = NSAlert.WithMessage(arguments.Title, arguments.Cancel, arguments.Accept, null, arguments.Message);
+                               var result = alert.RunModal();
+                               arguments.SetResult(result == 1);
+                       });
+
+                       MessagingCenter.Subscribe(this, Page.ActionSheetSignalName, (Page sender, ActionSheetArguments arguments) =>
+                       {
+                               var alert = NSAlert.WithMessage(arguments.Title, arguments.Cancel, arguments.Destruction, null, "");
+                               if (arguments.Buttons != null)
+                               {
+                                       alert.AccessoryView = GetExtraButton(arguments);
+                                       alert.Layout();
+                               }
+
+                               var result = (int)alert.RunSheetModal(NSApplication.SharedApplication.MainWindow);
+                               var titleResult = string.Empty;
+                               if (result == 1)
+                                       titleResult = arguments.Cancel;
+                               else if (result == 0)
+                                       titleResult = arguments.Destruction;
+                               else if (result > 1 && arguments.Buttons != null && result - 2 <= arguments.Buttons.Count())
+                                       titleResult = arguments.Buttons.ElementAt(result - 2);
+
+                               arguments.SetResult(titleResult);
+                       });
+               }
+
+               SizeRequest IPlatform.GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint)
+               {
+                       var renderView = GetRenderer(view);
+                       if (renderView == null || renderView.NativeView == null)
+                               return new SizeRequest(Size.Zero);
+
+                       return renderView.GetDesiredSize(widthConstraint, heightConstraint);
+               }
+
+               Page Page { get; set; }
+
+               Application TargetApplication
+               {
+                       get
+                       {
+                               if (Page == null)
+                                       return null;
+                               return Page.RealParent as Application;
+                       }
+               }
+
+               void IDisposable.Dispose()
+               {
+                       if (_disposed)
+                               return;
+                       _disposed = true;
+
+                       Page.DescendantRemoved -= HandleChildRemoved;
+                       MessagingCenter.Unsubscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName);
+                       MessagingCenter.Unsubscribe<Page, AlertArguments>(this, Page.AlertSignalName);
+                       MessagingCenter.Unsubscribe<Page, bool>(this, Page.BusySetSignalName);
+
+                       DisposeModelAndChildrenRenderers(Page);
+                       PlatformRenderer.Dispose();
+               }
+
+               public static IVisualElementRenderer CreateRenderer(VisualElement element)
+               {
+                       var t = element.GetType();
+                       var renderer = Registrar.Registered.GetHandler<IVisualElementRenderer>(t) ?? new DefaultRenderer();
+                       renderer.SetElement(element);
+                       return renderer;
+               }
+
+               public static IVisualElementRenderer GetRenderer(VisualElement bindable)
+               {
+                       return (IVisualElementRenderer)bindable.GetValue(RendererProperty);
+               }
+
+               public static void SetRenderer(VisualElement bindable, IVisualElementRenderer value)
+               {
+                       bindable.SetValue(RendererProperty, value);
+               }
+
+               protected override void OnBindingContextChanged()
+               {
+                       SetInheritedBindingContext(Page, BindingContext);
+
+                       base.OnBindingContextChanged();
+               }
+
+               internal NSViewController ViewController => PlatformRenderer;
+
+               internal static void DisposeModelAndChildrenRenderers(Element view)
+               {
+                       IVisualElementRenderer renderer;
+                       foreach (VisualElement child in view.Descendants())
+                               DisposeModelAndChildrenRenderers(child);
+
+                       renderer = GetRenderer((VisualElement)view);
+                       if (renderer?.ViewController?.ParentViewController != null)
+                               renderer?.ViewController?.RemoveFromParentViewController();
+
+                       renderer?.NativeView?.RemoveFromSuperview();
+                       renderer?.Dispose();
+
+                       view.ClearValue(RendererProperty);
+               }
+
+               internal static void DisposeRendererAndChildren(IVisualElementRenderer rendererToRemove)
+               {
+                       if (rendererToRemove == null || rendererToRemove.Element == null)
+                               return;
+
+                       if (GetRenderer(rendererToRemove.Element) == rendererToRemove)
+                               rendererToRemove.Element.ClearValue(RendererProperty);
+
+                       if (rendererToRemove.NativeView != null)
+                       {
+                               var subviews = rendererToRemove.NativeView.Subviews;
+                               for (var i = 0; i < subviews.Length; i++)
+                               {
+                                       var childRenderer = subviews[i] as IVisualElementRenderer;
+                                       if (childRenderer != null)
+                                               DisposeRendererAndChildren(childRenderer);
+                               }
+
+                               rendererToRemove.NativeView.RemoveFromSuperview();
+                       }
+                       rendererToRemove.Dispose();
+               }
+
+               internal void LayoutSubviews()
+               {
+                       if (Page == null)
+                               return;
+
+                       var rootRenderer = GetRenderer(Page);
+
+                       if (rootRenderer == null)
+                               return;
+
+                       rootRenderer.SetElementSize(new Size(PlatformRenderer.View.Bounds.Width, PlatformRenderer.View.Bounds.Height));
+               }
+
+               internal void SetPage(Page newRoot)
+               {
+                       if (newRoot == null)
+                               return;
+                       if (Page != null)
+                               throw new NotImplementedException();
+                       Page = newRoot;
+
+                       if (_appeared == false)
+                               return;
+
+                       Page.Platform = this;
+                       AddChild(Page);
+
+                       Page.DescendantRemoved += HandleChildRemoved;
+
+                       TargetApplication.NavigationProxy.Inner = PlatformRenderer.Navigation;
+               }
+
+               internal void DidAppear()
+               {
+                       PlatformRenderer.Navigation.AnimateModalPages = false;
+                       TargetApplication.NavigationProxy.Inner = PlatformRenderer.Navigation;
+                       PlatformRenderer.Navigation.AnimateModalPages = true;
+               }
+
+               internal void WillAppear()
+               {
+                       if (_appeared)
+                               return;
+
+                       Page.Platform = this;
+                       AddChild(Page);
+
+                       Page.DescendantRemoved += HandleChildRemoved;
+
+                       _appeared = true;
+               }
+
+               static NSView GetExtraButton(ActionSheetArguments arguments)
+               {
+                       var newView = new NSView();
+                       int height = 50;
+                       int width = 300;
+                       int i = 0;
+                       foreach (var button in arguments.Buttons)
+                       {
+                               var btn = new NSButton { Title = button, Tag = i };
+                               btn.SetButtonType(NSButtonType.MomentaryPushIn);
+                               btn.Activated +=
+                                       (s, e) =>
+                                       {
+                                               NSApplication.SharedApplication.EndSheet(NSApplication.SharedApplication.MainWindow.AttachedSheet,
+                                                       ((NSButton)s).Tag + 2);
+                                       };
+                               btn.Frame = new RectangleF(0, height * i, width, height);
+                               newView.AddSubview(btn);
+                               i++;
+                       }
+                       newView.Frame = new RectangleF(0, 0, width, height * i);
+                       return newView;
+               }
+
+               void AddChild(VisualElement view)
+               {
+                       if (!Application.IsApplicationOrNull(view.RealParent))
+                               Console.Error.WriteLine("Tried to add parented view to canvas directly");
+
+                       if (GetRenderer(view) == null)
+                       {
+                               var viewRenderer = CreateRenderer(view);
+                               SetRenderer(view, viewRenderer);
+
+                               PlatformRenderer.View.AddSubview(viewRenderer.NativeView);
+                               if (viewRenderer.ViewController != null)
+                                       PlatformRenderer.AddChildViewController(viewRenderer.ViewController);
+                               viewRenderer.SetElementSize(new Size(PlatformRenderer.View.Bounds.Width, PlatformRenderer.View.Bounds.Height));
+                       }
+                       else
+                               Console.Error.WriteLine("A Renderer was already found, potential view double add");
+               }
+
+               void HandleChildRemoved(object sender, ElementEventArgs e)
+               {
+                       var view = e.Element;
+                       DisposeModelAndChildrenRenderers(view);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/PlatformNavigation.cs b/Xamarin.Forms.Platform.MacOS/PlatformNavigation.cs
new file mode 100644 (file)
index 0000000..7af8f07
--- /dev/null
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class PlatformNavigation : INavigation, IDisposable
+       {
+               ModalPageTracker _modalTracker;
+               PlatformRenderer _platformRenderer;
+               bool _animateModals;
+               bool _disposed;
+
+               public PlatformNavigation(PlatformRenderer mainRenderer)
+               {
+                       _platformRenderer = mainRenderer;
+                       _modalTracker = new ModalPageTracker(_platformRenderer);
+                       _animateModals = true;
+               }
+
+               public IReadOnlyList<Page> ModalStack => _modalTracker.ModalStack;
+
+               public IReadOnlyList<Page> NavigationStack => new List<Page>();
+
+               public bool AnimateModalPages
+               {
+                       get { return _animateModals; }
+                       set { _animateModals = value; }
+               }
+
+               Task<Page> INavigation.PopAsync()
+               {
+                       return ((INavigation)this).PopAsync(true);
+               }
+
+               Task<Page> INavigation.PopAsync(bool animated)
+               {
+                       throw new InvalidOperationException("PopAsync is not supported globally on MacOS, please use a NavigationPage.");
+               }
+
+               Task INavigation.PopToRootAsync()
+               {
+                       return ((INavigation)this).PopToRootAsync(true);
+               }
+
+               Task INavigation.PopToRootAsync(bool animated)
+               {
+                       throw new InvalidOperationException("PopToRootAsync is not supported globally on MacOS, please use a NavigationPage.");
+               }
+
+               Task INavigation.PushAsync(Page root)
+               {
+                       return ((INavigation)this).PushAsync(root, true);
+               }
+
+               Task INavigation.PushAsync(Page root, bool animated)
+               {
+                       throw new InvalidOperationException("PushAsync is not supported globally on MacOS, please use a NavigationPage.");
+               }
+
+               Task INavigation.PushModalAsync(Page modal)
+               {
+                       return ((INavigation)this).PushModalAsync(modal, true);
+               }
+
+               Task<Page> INavigation.PopModalAsync()
+               {
+                       return ((INavigation)this).PopModalAsync(true);
+               }
+
+               Task INavigation.PushModalAsync(Page modal, bool animated)
+               {
+                       modal.Platform = _platformRenderer.Platform;
+                       return _modalTracker.PushAsync(modal, _animateModals && animated);
+               }
+
+               Task<Page> INavigation.PopModalAsync(bool animated)
+               {
+                       return _modalTracker.PopAsync(animated);
+               }
+
+               void INavigation.RemovePage(Page page)
+               {
+                       throw new InvalidOperationException("RemovePage is not supported globally on macOS, please use a NavigationPage.");
+               }
+
+               void INavigation.InsertPageBefore(Page page, Page before)
+               {
+                       throw new InvalidOperationException(
+                               "InsertPageBefore is not supported globally on macOS, please use a NavigationPage.");
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (!_disposed)
+                       {
+                               if (disposing)
+                               {
+                                       _modalTracker.Dispose();
+                                       _modalTracker = null;
+                                       _platformRenderer = null;
+                               }
+
+                               _disposed = true;
+                       }
+               }
+
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/PlatformRenderer.cs b/Xamarin.Forms.Platform.MacOS/PlatformRenderer.cs
new file mode 100644 (file)
index 0000000..c0fcae3
--- /dev/null
@@ -0,0 +1,50 @@
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class PlatformRenderer : NSViewController
+       {
+               PlatformNavigation _platformNavigation;
+               bool _disposed;
+
+               internal PlatformRenderer(Platform platform)
+               {
+                       Platform = platform;
+                       View = new NSView(NSApplication.SharedApplication.Windows[0].Frame);
+                       _platformNavigation = new PlatformNavigation(this);
+               }
+
+               public Platform Platform { get; set; }
+
+               public PlatformNavigation Navigation => _platformNavigation;
+
+               public override void ViewDidAppear()
+               {
+                       Platform.DidAppear();
+                       base.ViewDidAppear();
+               }
+
+               public override void ViewDidLayout()
+               {
+                       base.ViewDidLayout();
+                       Platform.LayoutSubviews();
+               }
+
+               public override void ViewWillAppear()
+               {
+                       Platform.WillAppear();
+                       base.ViewWillAppear();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (!_disposed)
+                       {
+                               _platformNavigation.Dispose();
+                               _platformNavigation = null;
+                       }
+                       _disposed = true;
+                       base.Dispose(disposing);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.MacOS/Properties/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..a9383ba
--- /dev/null
@@ -0,0 +1,53 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using Xamarin.Forms.Platform.MacOS;
+using Xamarin.Forms;
+using Xamarin.Forms.Internals;
+
+[assembly: AssemblyTitle("Xamarin.Forms.Platform.macOS")]
+[assembly: AssemblyDescription("macOS Backend for Xamarin.Forms")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCulture("")]
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
+[assembly: Xamarin.Forms.Dependency(typeof(Deserializer))]
+[assembly: Xamarin.Forms.Dependency(typeof(ResourcesProvider))]
+[assembly: ExportImageSourceHandler(typeof(FileImageSource), typeof(FileImageSourceHandler))]
+[assembly: ExportImageSourceHandler(typeof(StreamImageSource), typeof(StreamImagesourceHandler))]
+[assembly: ExportImageSourceHandler(typeof(UriImageSource), typeof(ImageLoaderSourceHandler))]
+[assembly: ExportRenderer(typeof(Page), typeof(PageRenderer))]
+[assembly: ExportRenderer(typeof(CarouselPage), typeof(CarouselPageRenderer))]
+[assembly: ExportRenderer(typeof(MasterDetailPage), typeof(MasterDetailPageRenderer))]
+[assembly: ExportRenderer(typeof(TabbedPage), typeof(TabbedPageRenderer))]
+[assembly: ExportRenderer(typeof(NavigationPage), typeof(NavigationPageRenderer))]
+[assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))]
+[assembly: ExportRenderer(typeof(Button), typeof(ButtonRenderer))]
+[assembly: ExportRenderer(typeof(BoxView), typeof(BoxViewRenderer))]
+[assembly: ExportRenderer(typeof(ScrollView), typeof(ScrollViewRenderer))]
+[assembly: ExportRenderer(typeof(ActivityIndicator), typeof(ActivityIndicatorRenderer))]
+[assembly: ExportRenderer(typeof(DatePicker), typeof(DatePickerRenderer))]
+[assembly: ExportRenderer(typeof(Editor), typeof(EditorRenderer))]
+[assembly: ExportRenderer(typeof(Entry), typeof(EntryRenderer))]
+[assembly: ExportRenderer(typeof(Frame), typeof(FrameRenderer))]
+[assembly: ExportRenderer(typeof(Image), typeof(ImageRenderer))]
+[assembly: ExportRenderer(typeof(OpenGLView), typeof(OpenGLViewRenderer))]
+[assembly: ExportRenderer(typeof(Picker), typeof(PickerRenderer))]
+[assembly: ExportRenderer(typeof(ProgressBar), typeof(ProgressBarRenderer))]
+[assembly: ExportRenderer(typeof(SearchBar), typeof(SearchBarRenderer))]
+[assembly: ExportRenderer(typeof(Slider), typeof(SliderRenderer))]
+[assembly: ExportRenderer(typeof(Stepper), typeof(StepperRenderer))]
+[assembly: ExportRenderer(typeof(Switch), typeof(SwitchRenderer))]
+[assembly: ExportRenderer(typeof(TimePicker), typeof(TimePickerRenderer))]
+[assembly: ExportRenderer(typeof(WebView), typeof(WebViewRenderer))]
+[assembly: ExportRenderer(typeof(ListView), typeof(ListViewRenderer))]
+[assembly: ExportRenderer(typeof(TableView), typeof(TableViewRenderer))]
+[assembly: ExportRenderer(typeof(NativeViewWrapper), typeof(NativeViewWrapperRenderer))]
+[assembly: ExportRenderer(typeof(Layout), typeof(LayoutRenderer))]
+[assembly: ExportCell(typeof(Cell), typeof(CellRenderer))]
+[assembly: ExportCell(typeof(TextCell), typeof(TextCellRenderer))]
+[assembly: ExportCell(typeof(ImageCell), typeof(ImageCellRenderer))]
+[assembly: ExportCell(typeof(EntryCell), typeof(EntryCellRenderer))]
+[assembly: ExportCell(typeof(ViewCell), typeof(ViewCellRenderer))]
+[assembly: ExportCell(typeof(SwitchCell), typeof(SwitchCellRenderer))]
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/ActivityIndicatorRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/ActivityIndicatorRenderer.cs
new file mode 100644 (file)
index 0000000..2e17916
--- /dev/null
@@ -0,0 +1,70 @@
+using System.ComponentModel;
+using System.Drawing;
+using AppKit;
+using CoreImage;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class ActivityIndicatorRenderer : ViewRenderer<ActivityIndicator, NSProgressIndicator>
+       {
+               static CIColorPolynomial s_currentColorFilter;
+               static NSColor s_currentColor;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<ActivityIndicator> e)
+               {
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                                       SetNativeControl(new NSProgressIndicator(RectangleF.Empty) { Style = NSProgressIndicatorStyle.Spinning });
+
+                               UpdateColor();
+                               UpdateIsRunning();
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == ActivityIndicator.ColorProperty.PropertyName)
+                               UpdateColor();
+                       else if (e.PropertyName == ActivityIndicator.IsRunningProperty.PropertyName)
+                               UpdateIsRunning();
+               }
+
+               void UpdateColor()
+               {
+                       var color = Element.Color;
+                       if (s_currentColorFilter == null && color.IsDefault)
+                               return;
+
+                       if (color.IsDefault)
+                               Control.ContentFilters = new CIFilter[0];
+
+                       var newColor = Element.Color.ToNSColor();
+                       if (Equals(s_currentColor, newColor))
+                               return;
+
+                       s_currentColor = newColor;
+
+                       s_currentColorFilter = new CIColorPolynomial
+                       {
+                               RedCoefficients = new CIVector(s_currentColor.RedComponent),
+                               BlueCoefficients = new CIVector(s_currentColor.BlueComponent),
+                               GreenCoefficients = new CIVector(s_currentColor.GreenComponent)
+                       };
+
+                       Control.ContentFilters = new CIFilter[] { s_currentColorFilter };
+               }
+
+               void UpdateIsRunning()
+               {
+                       if (Element.IsRunning)
+                               Control.StartAnimation(this);
+                       else
+                               Control.StopAnimation(this);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/BoxViewRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/BoxViewRenderer.cs
new file mode 100644 (file)
index 0000000..d4e9ad0
--- /dev/null
@@ -0,0 +1,39 @@
+using System.ComponentModel;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class BoxViewRenderer : ViewRenderer<BoxView, NSView>
+       {
+               protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
+               {
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                               {
+                                       SetNativeControl(new NSView());
+                               }
+                               SetBackgroundColor(Element.Color);
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+                       if (e.PropertyName == BoxView.ColorProperty.PropertyName)
+                               SetBackgroundColor(Element.BackgroundColor);
+                       else if (e.PropertyName == VisualElement.IsVisibleProperty.PropertyName && Element.IsVisible)
+                               SetNeedsDisplayInRect(Bounds);
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       if (Element == null || Control == null)
+                               return;
+                       Control.WantsLayer = true;
+                       Control.Layer.BackgroundColor = color.ToCGColor();
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/ButtonRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/ButtonRenderer.cs
new file mode 100644 (file)
index 0000000..05b87fa
--- /dev/null
@@ -0,0 +1,131 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using Foundation;
+using SizeF = CoreGraphics.CGSize;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class ButtonRenderer : ViewRenderer<Button, NSButton>
+       {
+               protected override void Dispose(bool disposing)
+               {
+                       if (Control != null)
+                               Control.Activated -= OnButtonActivated;
+
+                       base.Dispose(disposing);
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
+               {
+                       base.OnElementChanged(e);
+
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                               {
+                                       var btn = new NSButton();
+                                       btn.SetButtonType(NSButtonType.MomentaryPushIn);
+                                       SetNativeControl(btn);
+
+                                       Control.Activated += OnButtonActivated;
+                               }
+
+                               UpdateText();
+                               UpdateFont();
+                               UpdateBorder();
+                               UpdateImage();
+                       }
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == Button.TextProperty.PropertyName || e.PropertyName == Button.TextColorProperty.PropertyName)
+                               UpdateText();
+                       else if (e.PropertyName == Button.FontProperty.PropertyName)
+                               UpdateFont();
+                       else if (e.PropertyName == Button.BorderWidthProperty.PropertyName ||
+                                       e.PropertyName == Button.BorderRadiusProperty.PropertyName ||
+                                       e.PropertyName == Button.BorderColorProperty.PropertyName)
+                               UpdateBorder();
+                       else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+                               UpdateBackgroundVisibility();
+                       else if (e.PropertyName == Button.ImageProperty.PropertyName)
+                               UpdateImage();
+               }
+
+               void OnButtonActivated(object sender, EventArgs eventArgs)
+               {
+                       ((IButtonController)Element)?.SendClicked();
+               }
+
+               void UpdateBackgroundVisibility()
+               {
+                       var model = Element;
+                       var shouldDrawImage = model.BackgroundColor == Color.Default;
+                       if (!shouldDrawImage)
+                               Control.Cell.BackgroundColor = model.BackgroundColor.ToNSColor();
+               }
+
+               void UpdateBorder()
+               {
+                       var uiButton = Control;
+                       var button = Element;
+
+                       if (button.BorderColor != Color.Default)
+                               uiButton.Layer.BorderColor = button.BorderColor.ToCGColor();
+
+                       uiButton.Layer.BorderWidth = (float)button.BorderWidth;
+                       uiButton.Layer.CornerRadius = button.BorderRadius;
+
+                       UpdateBackgroundVisibility();
+               }
+
+               void UpdateFont()
+               {
+                       Control.Font = Element.Font.ToNSFont();
+               }
+
+               async void UpdateImage()
+               {
+                       IImageSourceHandler handler;
+                       FileImageSource source = Element.Image;
+                       if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null)
+                       {
+                               NSImage uiimage;
+                               try
+                               {
+                                       uiimage = await handler.LoadImageAsync(source);
+                               }
+                               catch (OperationCanceledException)
+                               {
+                                       uiimage = null;
+                               }
+                               NSButton button = Control;
+                               if (button != null && uiimage != null)
+                               {
+                                       button.Image = uiimage;
+                                       if (!string.IsNullOrEmpty(button.Title))
+                                               button.ImagePosition = Element.ToNSCellImagePosition();
+                               }
+                       }
+                       ((IVisualElementController)Element).NativeSizeChanged();
+               }
+
+               void UpdateText()
+               {
+                       var color = Element.TextColor;
+                       if (color == Color.Default)
+                       {
+                               Control.Title = Element.Text ?? "";
+                       }
+                       else
+                       {
+                               var textWithColor = new NSAttributedString(Element.Text ?? "", foregroundColor: color.ToNSColor());
+                               Control.AttributedTitle = textWithColor;
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/CarouselPageRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/CarouselPageRenderer.cs
new file mode 100644 (file)
index 0000000..014d503
--- /dev/null
@@ -0,0 +1,229 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using AppKit;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       [Register("CarouselPageRenderer")]
+       public class CarouselPageRenderer : NSPageController, IVisualElementRenderer
+       {
+               bool _appeared;
+               bool _disposed;
+               EventTracker _events;
+               VisualElementTracker _tracker;
+
+               public CarouselPageRenderer()
+               {
+                       View = new NSView
+                       {
+                               WantsLayer = true,
+                               Layer = { BackgroundColor = NSColor.White.CGColor }
+                       };
+               }
+
+               public CarouselPageRenderer(IntPtr handle) : base(handle)
+               {
+               }
+
+               IElementController ElementController => Element;
+
+               IPageController PageController => (IPageController)Element;
+
+               public override nint SelectedIndex
+               {
+                       get { return base.SelectedIndex; }
+                       set
+                       {
+                               if (base.SelectedIndex == value)
+                                       return;
+                               base.SelectedIndex = value;
+                               if (Carousel != null)
+                                       Carousel.CurrentPage = (ContentPage)ElementController.LogicalChildren[(int)SelectedIndex];
+                       }
+               }
+
+               public VisualElement Element { get; private set; }
+
+               public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+               public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+               {
+                       return NativeView.GetSizeRequest(widthConstraint, heightConstraint);
+               }
+
+               public NSView NativeView => View;
+
+               public void SetElement(VisualElement element)
+               {
+                       VisualElement oldElement = Element;
+                       Element = element;
+
+                       Init();
+
+                       OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+               }
+
+               public void SetElementSize(Size size)
+               {
+                       Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height));
+               }
+
+               public NSViewController ViewController => this;
+
+               public override void ViewDidAppear()
+               {
+                       base.ViewDidAppear();
+                       if (_appeared || _disposed)
+                               return;
+
+                       _appeared = true;
+                       PageController.SendAppearing();
+               }
+
+               public override void ViewDidDisappear()
+               {
+                       base.ViewDidDisappear();
+
+                       if (!_appeared || _disposed)
+                               return;
+
+                       _appeared = false;
+                       PageController.SendDisappearing();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               if (Carousel != null)
+                               {
+                                       Carousel.PropertyChanged -= OnPropertyChanged;
+                                       Carousel.PagesChanged -= OnPagesChanged;
+                               }
+
+                               Platform.SetRenderer(Element, null);
+
+                               if (_appeared)
+                               {
+                                       _appeared = false;
+                                       PageController?.SendDisappearing();
+                               }
+
+                               if (_events != null)
+                               {
+                                       _events.Dispose();
+                                       _events = null;
+                               }
+
+                               if (_tracker != null)
+                               {
+                                       _tracker.Dispose();
+                                       _tracker = null;
+                               }
+
+                               Element = null;
+                               _disposed = true;
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               void OnElementChanged(VisualElementChangedEventArgs e)
+               {
+                       ElementChanged?.Invoke(this, e);
+               }
+
+               void ConfigureNSPageController()
+               {
+                       TransitionStyle = NSPageControllerTransitionStyle.HorizontalStrip;
+               }
+
+               CarouselPage Carousel => Element as CarouselPage;
+
+               void Init()
+               {
+                       Delegate = new PageControllerDelegate();
+
+                       _tracker = new VisualElementTracker(this);
+                       _events = new EventTracker(this);
+                       _events.LoadEvents(View);
+
+                       ConfigureNSPageController();
+
+                       UpdateBackground();
+                       UpdateSource();
+
+                       Carousel.PropertyChanged += OnPropertyChanged;
+                       Carousel.PagesChanged += OnPagesChanged;
+               }
+
+               void UpdateSource()
+               {
+                       var pages = new List<NSPageContainer>();
+                       for (var i = 0; i < ElementController.LogicalChildren.Count; i++)
+                       {
+                               Element element = ElementController.LogicalChildren[i];
+                               var child = element as ContentPage;
+                               if (child != null)
+                                       pages.Add(new NSPageContainer(child, i));
+                       }
+
+                       ArrangedObjects = pages.ToArray();
+                       UpdateCurrentPage(false);
+               }
+
+               void OnPagesChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       UpdateSource();
+               }
+
+               void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == nameof(TabbedPage.CurrentPage))
+                               UpdateCurrentPage();
+                       else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+                               UpdateBackground();
+                       else if (e.PropertyName == Page.BackgroundImageProperty.PropertyName)
+                               UpdateBackground();
+               }
+
+               void UpdateBackground()
+               {
+                       if (View.Layer == null)
+                               return;
+
+                       string bgImage = ((Page)Element).BackgroundImage;
+
+                       if (!string.IsNullOrEmpty(bgImage))
+                       {
+                               View.Layer.BackgroundColor = NSColor.FromPatternImage(NSImage.ImageNamed(bgImage)).CGColor;
+                               return;
+                       }
+
+                       Color bgColor = Element.BackgroundColor;
+                       View.Layer.BackgroundColor = bgColor.IsDefault ? NSColor.White.CGColor : bgColor.ToCGColor();
+               }
+
+               void UpdateCurrentPage(bool animated = true)
+               {
+                       ContentPage current = Carousel.CurrentPage;
+                       if (current != null)
+                       {
+                               int index = Carousel.CurrentPage != null ? CarouselPage.GetIndex(Carousel.CurrentPage) : 0;
+                               if (index < 0)
+                                       index = 0;
+
+                               if (SelectedIndex == index)
+                                       return;
+
+                               if (animated)
+                                       NSAnimationContext.RunAnimation(context => { ((NSPageController)Animator).SelectedIndex = index; },
+                                               CompleteTransition);
+                               else SelectedIndex = index;
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/CustomNSTableHeaderView.cs b/Xamarin.Forms.Platform.MacOS/Renderers/CustomNSTableHeaderView.cs
new file mode 100644 (file)
index 0000000..79628b6
--- /dev/null
@@ -0,0 +1,46 @@
+using AppKit;
+using CoreGraphics;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       sealed class CustomNSTableHeaderView : NSTableHeaderView
+       {
+               public CustomNSTableHeaderView() : this(0, null) { }
+               public CustomNSTableHeaderView(double width, IVisualElementRenderer headerRenderer)
+               {
+                       var view = new NSView { WantsLayer = true, Layer = { BackgroundColor = NSColor.Clear.CGColor } };
+                       AddSubview(view);
+                       Update(width, headerRenderer);
+               }
+
+               public void Update(double width, IVisualElementRenderer headerRenderer)
+               {
+                       double height = 1;
+                       if (headerRenderer != null)
+                       {
+                               var headerView = headerRenderer.Element;
+                               var request = headerView.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins);
+                               height = request.Request.Height;
+                               var bounds = new Rectangle(0, 0, width, height);
+                               Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(headerView, bounds);
+                               headerRenderer.NativeView.Frame = bounds.ToRectangleF();
+                               AddSubview(headerRenderer.NativeView);
+
+                       }
+                       Frame = new CGRect(0, 0, width, height);
+               }
+
+               //hides default text field
+               public override NSAttributedString PageHeader => new NSAttributedString("");
+
+               public override void DrawRect(CGRect dirtyRect) { }
+
+               public override void Layout()
+               {
+                       foreach (var view in Subviews)
+                               view.Frame = Frame;
+                       base.Layout();
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/DatePickerRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/DatePickerRenderer.cs
new file mode 100644 (file)
index 0000000..f31fa36
--- /dev/null
@@ -0,0 +1,138 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class DatePickerRenderer : ViewRenderer<DatePicker, NSDatePicker>
+       {
+               NSDatePicker _picker;
+               NSColor _defaultTextColor;
+               NSColor _defaultBackgroundColor;
+               bool _disposed;
+
+               IElementController ElementController => Element;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
+               {
+                       base.OnElementChanged(e);
+
+                       if (e.OldElement == null)
+                       {
+                               if (Control == null)
+                               {
+                                       _picker = new NSDatePicker
+                                       {
+                                               DatePickerMode = NSDatePickerMode.Single,
+                                               TimeZone = new NSTimeZone("UTC"),
+                                               DatePickerStyle = NSDatePickerStyle.TextFieldAndStepper,
+                                               DatePickerElements = NSDatePickerElementFlags.YearMonthDateDay
+                                       };
+                                       _picker.ValidateProposedDateValue += HandleValueChanged;
+                                       _defaultTextColor = _picker.TextColor;
+                                       _defaultBackgroundColor = _picker.BackgroundColor;
+
+                                       SetNativeControl(_picker);
+                               }
+                       }
+
+                       UpdateDateFromModel();
+                       UpdateMaximumDate();
+                       UpdateMinimumDate();
+                       UpdateTextColor();
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == DatePicker.DateProperty.PropertyName ||
+                               e.PropertyName == DatePicker.FormatProperty.PropertyName)
+                               UpdateDateFromModel();
+                       else if (e.PropertyName == DatePicker.MinimumDateProperty.PropertyName)
+                               UpdateMinimumDate();
+                       else if (e.PropertyName == DatePicker.MaximumDateProperty.PropertyName)
+                               UpdateMaximumDate();
+                       else if (e.PropertyName == DatePicker.TextColorProperty.PropertyName ||
+                                       e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+                               UpdateTextColor();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               if (_picker != null)
+                                       _picker.ValidateProposedDateValue -= HandleValueChanged;
+
+                               _disposed = true;
+                       }
+                       base.Dispose(disposing);
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       base.SetBackgroundColor(color);
+
+                       if (Control == null)
+                               return;
+
+                       if (color == Color.Default)
+                               Control.BackgroundColor = _defaultBackgroundColor;
+                       else
+                               Control.BackgroundColor = color.ToNSColor();
+               }
+
+               void HandleValueChanged(object sender, NSDatePickerValidatorEventArgs e)
+               {
+                       if (Control == null || Element == null)
+                               return;
+                       ElementController?.SetValueFromRenderer(DatePicker.DateProperty, _picker.DateValue.ToDateTime().Date);
+               }
+
+               void OnEnded(object sender, EventArgs eventArgs)
+               {
+                       ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
+               }
+
+               void OnStarted(object sender, EventArgs eventArgs)
+               {
+                       ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
+               }
+
+               void UpdateDateFromModel()
+               {
+                       if (Control == null || Element == null)
+                               return;
+                       if (_picker.DateValue.ToDateTime().Date != Element.Date.Date)
+                               _picker.DateValue = Element.Date.ToNSDate();
+               }
+
+               void UpdateMaximumDate()
+               {
+                       if (Control == null || Element == null)
+                               return;
+                       _picker.MaxDate = Element.MaximumDate.ToNSDate();
+               }
+
+               void UpdateMinimumDate()
+               {
+                       if (Control == null || Element == null)
+                               return;
+                       _picker.MinDate = Element.MinimumDate.ToNSDate();
+               }
+
+               void UpdateTextColor()
+               {
+                       if (Control == null || Element == null)
+                               return;
+                       var textColor = Element.TextColor;
+
+                       if (textColor.IsDefault || !Element.IsEnabled)
+                               Control.TextColor = _defaultTextColor;
+                       else
+                               Control.TextColor = textColor.ToNSColor();
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/DefaultRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/DefaultRenderer.cs
new file mode 100644 (file)
index 0000000..3d1d7cd
--- /dev/null
@@ -0,0 +1,6 @@
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class DefaultRenderer : VisualElementRenderer<VisualElement>
+       {
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/EditorRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/EditorRenderer.cs
new file mode 100644 (file)
index 0000000..75a6020
--- /dev/null
@@ -0,0 +1,128 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class EditorRenderer : ViewRenderer<Editor, NSTextField>
+       {
+               const string NewLineSelector = "insertNewline";
+               bool _disposed;
+
+               IElementController ElementController => Element;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
+               {
+                       base.OnElementChanged(e);
+
+                       if (Control == null)
+                       {
+                               SetNativeControl(new NSTextField { UsesSingleLineMode = false });
+                               Control.Cell.Scrollable = true;
+                               Control.Cell.Wraps = true;
+                               Control.Changed += HandleChanged;
+                               Control.EditingBegan += OnEditingBegan;
+                               Control.EditingEnded += OnEditingEnded;
+                               Control.DoCommandBySelector = (control, textView, commandSelector) =>
+                               {
+                                       var result = false;
+                                       if (commandSelector.Name.StartsWith(NewLineSelector, StringComparison.InvariantCultureIgnoreCase))
+                                       {
+                                               textView.InsertText(new NSString(Environment.NewLine));
+                                               result = true;
+                                       }
+                                       return result;
+                               };
+                       }
+
+                       if (e.NewElement == null) return;
+                       UpdateText();
+                       UpdateFont();
+                       UpdateTextColor();
+                       UpdateEditable();
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == Editor.TextProperty.PropertyName)
+                               UpdateText();
+                       else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+                               UpdateEditable();
+                       else if (e.PropertyName == Editor.TextColorProperty.PropertyName)
+                               UpdateTextColor();
+                       else if (e.PropertyName == Editor.FontAttributesProperty.PropertyName)
+                               UpdateFont();
+                       else if (e.PropertyName == Editor.FontFamilyProperty.PropertyName)
+                               UpdateFont();
+                       else if (e.PropertyName == Editor.FontSizeProperty.PropertyName)
+                               UpdateFont();
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       if (Control == null)
+                               return;
+
+                       Control.BackgroundColor = color == Color.Default ? NSColor.Clear : color.ToNSColor();
+
+                       base.SetBackgroundColor(color);
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               _disposed = true;
+                               if (Control != null)
+                               {
+                                       Control.Changed -= HandleChanged;
+                                       Control.EditingBegan -= OnEditingBegan;
+                                       Control.EditingEnded -= OnEditingEnded;
+                               }
+                       }
+                       base.Dispose(disposing);
+               }
+
+               void HandleChanged(object sender, EventArgs e)
+               {
+                       ElementController.SetValueFromRenderer(Editor.TextProperty, Control.StringValue);
+               }
+
+               void OnEditingEnded(object sender, EventArgs eventArgs)
+               {
+                       Element.SetValue(VisualElement.IsFocusedPropertyKey, false);
+                       Element.SendCompleted();
+               }
+
+               void OnEditingBegan(object sender, EventArgs eventArgs)
+               {
+                       ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
+               }
+
+               void UpdateEditable()
+               {
+                       Control.Editable = Element.IsEnabled;
+               }
+
+               void UpdateFont()
+               {
+                       Control.Font = Element.ToNSFont();
+               }
+
+               void UpdateText()
+               {
+                       if (Control.StringValue != Element.Text)
+                               Control.StringValue = Element.Text ?? string.Empty;
+               }
+
+               void UpdateTextColor()
+               {
+                       var textColor = Element.TextColor;
+
+                       Control.TextColor = textColor.IsDefault ? NSColor.Black : textColor.ToNSColor();
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/EntryRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/EntryRenderer.cs
new file mode 100644 (file)
index 0000000..5fb6554
--- /dev/null
@@ -0,0 +1,205 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class EntryRenderer : ViewRenderer<Entry, NSTextField>
+       {
+               class BoolEventArgs : EventArgs
+               {
+                       public BoolEventArgs(bool value)
+                       {
+                               Value = value;
+                       }
+                       public bool Value
+                       {
+                               get;
+                               private set;
+                       }
+               }
+               class FormsNSTextField : NSTextField
+               {
+                       public EventHandler<BoolEventArgs> FocusChanged;
+                       public override bool ResignFirstResponder()
+                       {
+                               FocusChanged?.Invoke(this, new BoolEventArgs(false));
+                               return base.ResignFirstResponder();
+                       }
+                       public override bool BecomeFirstResponder()
+                       {
+                               FocusChanged?.Invoke(this, new BoolEventArgs(true));
+                               return base.BecomeFirstResponder();
+                       }
+               }
+
+               bool _disposed;
+               NSColor _defaultTextColor;
+
+               IElementController ElementController => Element;
+
+               IEntryController EntryController => Element;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
+               {
+                       base.OnElementChanged(e);
+
+                       if (Control == null)
+                       {
+                               NSTextField textField;
+                               if (e.NewElement.IsPassword)
+                                       textField = new NSSecureTextField();
+                               else
+                               {
+                                       textField = new FormsNSTextField();
+                                       (textField as FormsNSTextField).FocusChanged += TextFieldFocusChanged;
+                               }
+
+                               SetNativeControl(textField);
+
+                               _defaultTextColor = textField.TextColor;
+
+                               textField.Changed += OnChanged;
+                               textField.EditingBegan += OnEditingBegan;
+                               textField.EditingEnded += OnEditingEnded;
+                       }
+
+                       if (e.NewElement != null)
+                       {
+                               UpdatePlaceholder();
+                               UpdateText();
+                               UpdateColor();
+                               UpdateFont();
+                               UpdateAlignment();
+                       }
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == Entry.PlaceholderProperty.PropertyName ||
+                               e.PropertyName == Entry.PlaceholderColorProperty.PropertyName)
+                               UpdatePlaceholder();
+                       else if (e.PropertyName == Entry.IsPasswordProperty.PropertyName)
+                               UpdatePassword();
+                       else if (e.PropertyName == Entry.TextProperty.PropertyName)
+                               UpdateText();
+                       else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
+                               UpdateColor();
+                       else if (e.PropertyName == Entry.HorizontalTextAlignmentProperty.PropertyName)
+                               UpdateAlignment();
+                       else if (e.PropertyName == Entry.FontAttributesProperty.PropertyName)
+                               UpdateFont();
+                       else if (e.PropertyName == Entry.FontFamilyProperty.PropertyName)
+                               UpdateFont();
+                       else if (e.PropertyName == Entry.FontSizeProperty.PropertyName)
+                               UpdateFont();
+                       else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+                       {
+                               UpdateColor();
+                               UpdatePlaceholder();
+                       }
+
+                       base.OnElementPropertyChanged(sender, e);
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       if (Control == null)
+                               return;
+                       Control.BackgroundColor = color == Color.Default ? NSColor.Clear : color.ToNSColor();
+
+                       base.SetBackgroundColor(color);
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               _disposed = true;
+                               if (Control != null)
+                               {
+                                       Control.EditingBegan -= OnEditingBegan;
+                                       Control.Changed -= OnChanged;
+                                       Control.EditingEnded -= OnEditingEnded;
+                                       var formsNSTextField = (Control as FormsNSTextField);
+                                       if (formsNSTextField != null)
+                                               formsNSTextField.FocusChanged -= TextFieldFocusChanged;
+                               }
+                       }
+
+                       base.Dispose(disposing);
+               }
+               void TextFieldFocusChanged(object sender, BoolEventArgs e)
+               {
+                       ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, e.Value);
+               }
+
+               void OnEditingBegan(object sender, EventArgs e)
+               {
+                       ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
+               }
+
+               void OnChanged(object sender, EventArgs eventArgs)
+               {
+                       ElementController.SetValueFromRenderer(Entry.TextProperty, Control.StringValue);
+               }
+
+               void OnEditingEnded(object sender, EventArgs e)
+               {
+                       ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
+                       EntryController?.SendCompleted();
+               }
+
+               void UpdateAlignment()
+               {
+                       Control.Alignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
+               }
+
+               void UpdateColor()
+               {
+                       var textColor = Element.TextColor;
+
+                       if (textColor.IsDefault || !Element.IsEnabled)
+                               Control.TextColor = _defaultTextColor;
+                       else
+                               Control.TextColor = textColor.ToNSColor();
+               }
+
+               void UpdatePassword()
+               {
+                       if (Element.IsPassword && (Control is NSSecureTextField))
+                               return;
+                       if (!Element.IsPassword && !(Control is NSSecureTextField))
+                               return;
+               }
+
+               void UpdateFont()
+               {
+                       Control.Font = Element.ToNSFont();
+               }
+
+               void UpdatePlaceholder()
+               {
+                       var formatted = (FormattedString)Element.Placeholder;
+
+                       if (formatted == null)
+                               return;
+
+                       var targetColor = Element.PlaceholderColor;
+
+                       // Placeholder default color is 70% gray
+                       // https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UITextField_Class/index.html#//apple_ref/occ/instp/UITextField/placeholder
+
+                       var color = Element.IsEnabled && !targetColor.IsDefault ? targetColor : ColorExtensions.SeventyPercentGrey.ToColor();
+
+                       Control.PlaceholderAttributedString = formatted.ToAttributed(Element, color);
+               }
+
+               void UpdateText()
+               {
+                       // ReSharper disable once RedundantCheckBeforeAssignment
+                       if (Control.StringValue != Element.Text)
+                               Control.StringValue = Element.Text ?? string.Empty;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/FrameRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/FrameRenderer.cs
new file mode 100644 (file)
index 0000000..7cbabc1
--- /dev/null
@@ -0,0 +1,57 @@
+using System.ComponentModel;
+using System.Drawing;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class FrameRenderer : VisualElementRenderer<Frame>
+       {
+               protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
+               {
+                       base.OnElementChanged(e);
+
+                       if (e.NewElement != null)
+                               SetupLayer();
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName ||
+                               e.PropertyName == Xamarin.Forms.Frame.OutlineColorProperty.PropertyName ||
+                               e.PropertyName == Xamarin.Forms.Frame.HasShadowProperty.PropertyName)
+                               SetupLayer();
+               }
+
+               void SetupLayer()
+               {
+                       Layer.CornerRadius = 5;
+                       if (Element.BackgroundColor == Color.Default)
+                               Layer.BackgroundColor = NSColor.White.CGColor;
+                       else
+                               Layer.BackgroundColor = Element.BackgroundColor.ToCGColor();
+
+                       if (Element.HasShadow)
+                       {
+                               Layer.ShadowRadius = 5;
+                               Layer.ShadowColor = NSColor.Black.CGColor;
+                               Layer.ShadowOpacity = 0.8f;
+                               Layer.ShadowOffset = new SizeF();
+                       }
+                       else
+                               Layer.ShadowOpacity = 0;
+
+                       if (Element.OutlineColor == Color.Default)
+                               Layer.BorderColor = NSColor.Clear.CGColor;
+                       else
+                       {
+                               Layer.BorderColor = Element.OutlineColor.ToCGColor();
+                               Layer.BorderWidth = 1;
+                       }
+
+                       Layer.RasterizationScale = NSScreen.MainScreen.BackingScaleFactor;
+                       Layer.ShouldRasterize = true;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/ImageRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/ImageRenderer.cs
new file mode 100644 (file)
index 0000000..090df9a
--- /dev/null
@@ -0,0 +1,117 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class ImageRenderer : ViewRenderer<Image, NSImageView>
+       {
+               bool _isDisposed;
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (_isDisposed)
+                               return;
+
+                       if (disposing)
+                       {
+                               NSImage oldUIImage;
+                               if (Control != null && (oldUIImage = Control.Image) != null)
+                               {
+                                       oldUIImage.Dispose();
+                               }
+                       }
+
+                       _isDisposed = true;
+
+                       base.Dispose(disposing);
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
+               {
+                       if (Control == null)
+                       {
+                               var imageView = new FormsNSImageView();
+                               SetNativeControl(imageView);
+                       }
+
+                       if (e.NewElement != null)
+                       {
+                               SetAspect();
+                               SetImage(e.OldElement);
+                               SetOpacity();
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+                       if (e.PropertyName == Image.SourceProperty.PropertyName)
+                               SetImage();
+                       else if (e.PropertyName == Image.IsOpaqueProperty.PropertyName)
+                               SetOpacity();
+                       else if (e.PropertyName == Image.AspectProperty.PropertyName)
+                               SetAspect();
+               }
+
+               void SetAspect()
+               {
+                       //TODO: Implement set Image Aspect
+                       //Control.ContentMode = Element.Aspect.ToUIViewContentMode();
+               }
+
+               async void SetImage(Image oldElement = null)
+               {
+                       var source = Element.Source;
+
+                       if (oldElement != null)
+                       {
+                               var oldSource = oldElement.Source;
+                               if (Equals(oldSource, source))
+                                       return;
+
+                               var imageSource = oldSource as FileImageSource;
+                               if (imageSource != null && source is FileImageSource && imageSource.File == ((FileImageSource)source).File)
+                                       return;
+
+                               Control.Image = null;
+                       }
+
+                       IImageSourceHandler handler;
+
+                       ((IImageController)Element).SetIsLoading(true);
+
+                       if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null)
+                       {
+                               NSImage uiimage;
+                               try
+                               {
+                                       uiimage = await handler.LoadImageAsync(source, scale: (float)NSScreen.MainScreen.BackingScaleFactor);
+                               }
+                               catch (OperationCanceledException)
+                               {
+                                       uiimage = null;
+                               }
+
+                               var imageView = Control;
+                               if (imageView != null)
+                                       imageView.Image = uiimage;
+
+                               if (!_isDisposed)
+                                       ((IVisualElementController)Element).NativeSizeChanged();
+                       }
+                       else
+                               Control.Image = null;
+
+                       if (!_isDisposed)
+                               ((IImageController)Element).SetIsLoading(false);
+               }
+
+               void SetOpacity()
+               {
+                       (Control as FormsNSImageView)?.SetIsOpaque(Element.IsOpaque);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/LayoutRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/LayoutRenderer.cs
new file mode 100644 (file)
index 0000000..1be5a1a
--- /dev/null
@@ -0,0 +1,39 @@
+using System;
+using CoreGraphics;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class LayoutRenderer : DefaultRenderer
+       {
+               CGRect _bounds;
+
+               public override void Layout()
+               {
+                       base.Layout();
+
+                       if (_bounds == Bounds)
+                               return;
+
+                       _bounds = Bounds;
+
+                       //when the layout changes we might need to update  the children position based in our new size,
+                       //this is only needed in MacOS because of the inversion of the Y coordinate. 
+                       //Forms layout system doesn't know we need to relayout the other items ,(first ones for example)
+                       //so we do it here 
+                       for (int i = 0; i < Subviews.Length; i++)
+                       {
+                               var item = Subviews[i] as IVisualElementRenderer;
+                               if (item == null)
+                                       continue;
+                               var oldFrame = item.NativeView.Frame;
+
+                               var newY = Math.Max(0, (float)(Element.Height - item.Element.Height - item.Element.Y));
+                               if (oldFrame.Y == newY)
+                                       continue;
+                               var newPosition = new CGPoint(oldFrame.X, newY);
+                               item.NativeView.Frame = new CGRect(newPosition, oldFrame.Size);
+                               Console.WriteLine($"New Frame - {item.NativeView.Frame}");
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/ListViewDataSource.cs b/Xamarin.Forms.Platform.MacOS/Renderers/ListViewDataSource.cs
new file mode 100644 (file)
index 0000000..6742053
--- /dev/null
@@ -0,0 +1,299 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using AppKit;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class ListViewDataSource : NSTableViewSource
+       {
+               IVisualElementRenderer _prototype;
+               const int DefaultItemTemplateId = 1;
+               static int s_dataTemplateIncrementer = 2; // lets start at not 0 because
+               static int s_sectionCount;
+               readonly nfloat _defaultSectionHeight;
+               readonly Dictionary<DataTemplate, int> _templateToId = new Dictionary<DataTemplate, int>();
+               readonly NSTableView _nsTableView;
+               protected readonly ListView List;
+
+               IListViewController Controller => List;
+
+               ITemplatedItemsView<Cell> TemplatedItemsView => List;
+
+               bool _selectionFromNative;
+
+               public virtual bool IsGroupingEnabled => List.IsGroupingEnabled;
+
+               public Dictionary<int, int> Counts { get; set; }
+
+               public ListViewDataSource(ListViewDataSource source)
+               {
+                       List = source.List;
+                       _nsTableView = source._nsTableView;
+                       _defaultSectionHeight = source._defaultSectionHeight;
+                       _selectionFromNative = source._selectionFromNative;
+                       Counts = new Dictionary<int, int>();
+               }
+
+               public ListViewDataSource(ListView list, NSTableView tableView)
+               {
+                       List = list;
+                       List.ItemSelected += OnItemSelected;
+                       _nsTableView = tableView;
+                       Counts = new Dictionary<int, int>();
+               }
+
+               public void Update()
+               {
+                       _nsTableView.ReloadData();
+               }
+
+               public void OnRowClicked()
+               {
+                       var selectedRow = _nsTableView.SelectedRow;
+                       if (selectedRow == -1)
+                               return;
+
+                       Cell cell = null;
+                       NSIndexPath indexPath = GetPathFromRow(selectedRow, ref cell);
+
+                       if (cell == null)
+                               return;
+
+                       _selectionFromNative = true;
+                       Controller.NotifyRowTapped((int)indexPath.Section, (int)indexPath.Item, cell);
+               }
+
+
+               public void OnItemSelected(object sender, SelectedItemChangedEventArgs eventArg)
+               {
+                       if (_selectionFromNative)
+                       {
+                               _selectionFromNative = false;
+                               return;
+                       }
+
+                       var location = TemplatedItemsView.TemplatedItems.GetGroupAndIndexOfItem(eventArg.SelectedItem);
+                       if (location.Item1 == -1 || location.Item2 == -1)
+                       {
+                               var row = _nsTableView.SelectedRow;
+                               int groupIndex = 1;
+                               var selectedIndexPath = NSIndexPath.FromItemSection(row, groupIndex);
+                               if (selectedIndexPath != null)
+                                       _nsTableView.DeselectRow(selectedIndexPath.Item);
+                               return;
+                       }
+
+                       var rowId = location.Item2;
+
+                       _nsTableView.SelectRow(rowId, false);
+               }
+
+               public override bool IsGroupRow(NSTableView tableView, nint row)
+               {
+                       if (!IsGroupingEnabled)
+                               return false;
+
+                       int sectionIndex;
+                       bool isGroupHeader;
+                       int itemIndexInSection;
+
+                       GetComputedIndexes(row, out sectionIndex, out itemIndexInSection, out isGroupHeader);
+                       return isGroupHeader;
+               }
+
+               public override bool ShouldSelectRow(NSTableView tableView, nint row)
+               {
+                       return !IsGroupRow(tableView, row);
+               }
+
+               public override nfloat GetRowHeight(NSTableView tableView, nint row)
+               {
+                       if (!List.HasUnevenRows)
+                               return List.RowHeight == -1 ? ListViewRenderer.DefaultRowHeight : List.RowHeight;
+
+                       Cell cell = null;
+                       GetPathFromRow(row, ref cell);
+
+                       return CalculateHeightForCell(tableView, cell);
+               }
+
+               public override nint GetRowCount(NSTableView tableView)
+               {
+                       var templatedItems = TemplatedItemsView.TemplatedItems;
+                       nint count = 0;
+
+                       if (!IsGroupingEnabled)
+                       {
+                               count = templatedItems.Count;
+                       }
+                       else
+                       {
+                               var sections = templatedItems.Count;
+                               for (int i = 0; i < sections; i++)
+                               {
+                                       var group = (IList)((IList)templatedItems)[i];
+                                       count += group.Count + 1;
+                               }
+                               s_sectionCount = sections;
+                       }
+                       return count;
+               }
+
+               public override NSView GetViewForItem(NSTableView tableView, NSTableColumn tableColumn, nint row)
+               {
+                       var sectionIndex = 0;
+                       var itemIndexInSection = (int)row;
+                       Cell cell;
+
+                       var isHeader = false;
+
+                       if (IsGroupingEnabled)
+                               GetComputedIndexes(row, out sectionIndex, out itemIndexInSection, out isHeader);
+
+                       var indexPath = NSIndexPath.FromItemSection(itemIndexInSection, sectionIndex);
+                       var templateId = isHeader ? "headerCell" : TemplateIdForPath(indexPath).ToString();
+
+                       NSView nativeCell;
+
+                       var cachingStrategy = Controller.CachingStrategy;
+                       if (cachingStrategy == ListViewCachingStrategy.RetainElement)
+                       {
+                               cell = GetCellForPath(indexPath, isHeader);
+                               nativeCell = CellNSView.GetNativeCell(tableView, cell, templateId, isHeader);
+                       }
+                       else if (cachingStrategy == ListViewCachingStrategy.RecycleElement)
+                       {
+                               nativeCell = tableView.MakeView(templateId, tableView);
+                               if (nativeCell == null)
+                               {
+                                       cell = GetCellForPath(indexPath, isHeader);
+                                       nativeCell = CellNSView.GetNativeCell(tableView, cell, templateId, isHeader, true);
+                               }
+                               else
+                               {
+                                       var templatedList = TemplatedItemsView.TemplatedItems.GetGroup(sectionIndex);
+                                       cell = (Cell)((INativeElementView)nativeCell).Element;
+                                       ICellController controller = cell;
+                                       controller.SendDisappearing();
+                                       templatedList.UpdateContent(cell, itemIndexInSection);
+                                       controller.SendAppearing();
+                               }
+                       }
+                       else
+                               throw new NotSupportedException();
+                       return nativeCell;
+               }
+
+               protected virtual Cell GetCellForPath(NSIndexPath indexPath, bool isGroupHeader)
+               {
+                       var templatedItems = TemplatedItemsView.TemplatedItems;
+                       if (IsGroupingEnabled)
+                               templatedItems = (TemplatedItemsList<ItemsView<Cell>, Cell>)((IList)templatedItems)[(int)indexPath.Section];
+
+                       var cell = isGroupHeader ? templatedItems.HeaderContent : templatedItems[(int)indexPath.Item];
+                       return cell;
+               }
+
+               int TemplateIdForPath(NSIndexPath indexPath)
+               {
+                       var itemTemplate = List.ItemTemplate;
+                       var selector = itemTemplate as DataTemplateSelector;
+                       if (selector == null)
+                               return DefaultItemTemplateId;
+
+                       var templatedList = TemplatedItemsView.TemplatedItems;
+                       if (List.IsGroupingEnabled)
+                               templatedList = (TemplatedItemsList<ItemsView<Cell>, Cell>)((IList)templatedList)[(int)indexPath.Section];
+
+                       var item = templatedList.ListProxy[(int)indexPath.Item];
+
+                       itemTemplate = selector.SelectTemplate(item, List);
+                       int key;
+                       if (!_templateToId.TryGetValue(itemTemplate, out key))
+                       {
+                               s_dataTemplateIncrementer++;
+                               key = s_dataTemplateIncrementer;
+                               _templateToId[itemTemplate] = key;
+                       }
+                       return key;
+               }
+
+               NSIndexPath GetPathFromRow(nint row, ref Cell cell)
+               {
+                       var sectionIndex = 0;
+                       bool isGroupHeader = false;
+                       int itemIndexInSection;
+                       if (IsGroupingEnabled)
+                               GetComputedIndexes(row, out sectionIndex, out itemIndexInSection, out isGroupHeader);
+                       else
+                               itemIndexInSection = (int)row;
+                       NSIndexPath indexPath = NSIndexPath.FromItemSection(itemIndexInSection, sectionIndex);
+                       cell = GetCellForPath(indexPath, isGroupHeader);
+                       return indexPath;
+               }
+
+               nfloat CalculateHeightForCell(NSTableView tableView, Cell cell)
+               {
+                       var viewCell = cell as ViewCell;
+                       double renderHeight;
+                       if (List.RowHeight == -1 && viewCell?.View != null)
+                       {
+                               var target = viewCell.View;
+                               if (_prototype == null)
+                               {
+                                       _prototype = Platform.CreateRenderer(target);
+                                       Platform.SetRenderer(target, _prototype);
+                               }
+                               else
+                               {
+                                       _prototype.SetElement(target);
+                                       Platform.SetRenderer(target, _prototype);
+                               }
+
+                               var req = target.Measure(tableView.Frame.Width, double.PositiveInfinity, MeasureFlags.IncludeMargins);
+
+                               target.ClearValue(Platform.RendererProperty);
+                               foreach (var descendant in target.Descendants())
+                                       descendant.ClearValue(Platform.RendererProperty);
+
+                               renderHeight = req.Request.Height;
+                       }
+                       else
+                       {
+                               renderHeight = cell.RenderHeight;
+                       }
+
+                       return renderHeight > 0 ? (nfloat)renderHeight : ListViewRenderer.DefaultRowHeight;
+               }
+
+               void GetComputedIndexes(nint row, out int sectionIndex, out int itemIndexInSection, out bool isHeader)
+               {
+                       var templatedItems = TemplatedItemsView.TemplatedItems;
+                       var totalItems = 0;
+                       isHeader = false;
+                       sectionIndex = 0;
+                       itemIndexInSection = 0;
+
+                       for (int i = 0; i < s_sectionCount; i++)
+                       {
+                               var group = (IList)((IList)templatedItems)[i];
+                               var itemsInSection = group.Count + 1;
+
+                               if (row < totalItems + itemsInSection)
+                               {
+                                       sectionIndex = i;
+                                       itemIndexInSection = (int)row - totalItems;
+                                       isHeader = itemIndexInSection == 0;
+                                       if (isHeader)
+                                               itemIndexInSection = -1;
+                                       else
+                                               itemIndexInSection = itemIndexInSection - 1;
+                                       break;
+                               }
+                               totalItems += itemsInSection;
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/ListViewRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/ListViewRenderer.cs
new file mode 100644 (file)
index 0000000..08265b3
--- /dev/null
@@ -0,0 +1,342 @@
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Linq;
+using AppKit;
+using Foundation;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class ListViewRenderer : ViewRenderer<ListView, NSView>
+       {
+               bool _disposed;
+               NSTableView _table;
+               ListViewDataSource _dataSource;
+               IVisualElementRenderer _headerRenderer;
+               IVisualElementRenderer _footerRenderer;
+
+               IListViewController Controller => Element;
+
+               ITemplatedItemsView<Cell> TemplatedItemsView => Element;
+
+               public const int DefaultRowHeight = 16;
+
+               public NSTableView NativeTableView => _table;
+
+               public override void ViewWillDraw()
+               {
+                       UpdateHeader();
+                       base.ViewWillDraw();
+               }
+
+               protected virtual NSTableView CreateNSTableView(ListView list)
+               {
+                       NSTableView table = new FormsNSTableView().AsListViewLook();
+                       table.Source = _dataSource = new ListViewDataSource(list, table);
+                       return table;
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               _disposed = true;
+
+                               var viewsToLookAt = new Stack<NSView>(Subviews);
+                               while (viewsToLookAt.Count > 0)
+                               {
+                                       var view = viewsToLookAt.Pop();
+                                       var viewCellRenderer = view as ViewCellNSView;
+                                       if (viewCellRenderer != null)
+                                               viewCellRenderer.Dispose();
+                                       else
+                                       {
+                                               foreach (var child in view.Subviews)
+                                                       viewsToLookAt.Push(child);
+                                       }
+                               }
+
+                               if (Element != null)
+                               {
+                                       var templatedItems = TemplatedItemsView.TemplatedItems;
+                                       templatedItems.CollectionChanged -= OnCollectionChanged;
+                                       templatedItems.GroupedCollectionChanged -= OnGroupedCollectionChanged;
+                               }
+                       }
+
+                       if (disposing)
+                       {
+                               ClearHeader();
+                               if (_footerRenderer != null)
+                               {
+                                       Platform.DisposeModelAndChildrenRenderers(_footerRenderer.Element);
+                                       _footerRenderer = null;
+                               }
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       base.SetBackgroundColor(color);
+                       if (_table == null)
+                               return;
+
+                       _table.BackgroundColor = color.ToNSColor(NSColor.White);
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
+               {
+                       if (e.OldElement != null)
+                       {
+                               var controller = (IListViewController)e.OldElement;
+                               controller.ScrollToRequested -= OnScrollToRequested;
+
+                               var templatedItems = ((ITemplatedItemsView<Cell>)e.OldElement).TemplatedItems;
+                               templatedItems.CollectionChanged -= OnCollectionChanged;
+                               templatedItems.GroupedCollectionChanged -= OnGroupedCollectionChanged;
+                       }
+
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                               {
+                                       var scroller = new NSScrollView
+                                       {
+                                               AutoresizingMask = NSViewResizingMask.HeightSizable | NSViewResizingMask.WidthSizable,
+                                               DocumentView = _table = CreateNSTableView(e.NewElement)
+                                       };
+                                       SetNativeControl(scroller);
+                               }
+
+                               var controller = (IListViewController)e.NewElement;
+                               controller.ScrollToRequested += OnScrollToRequested;
+
+                               var templatedItems = ((ITemplatedItemsView<Cell>)e.NewElement).TemplatedItems;
+                               templatedItems.CollectionChanged += OnCollectionChanged;
+                               templatedItems.GroupedCollectionChanged += OnGroupedCollectionChanged;
+
+                               UpdateRowHeight();
+                               UpdateHeader();
+                               UpdateFooter();
+                               UpdatePullToRefreshEnabled();
+                               UpdateIsRefreshing();
+                               UpdateSeparatorColor();
+                               UpdateSeparatorVisibility();
+
+                               var selected = e.NewElement.SelectedItem;
+                               if (selected != null)
+                                       _dataSource.OnItemSelected(null, new SelectedItemChangedEventArgs(selected));
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+                       if (e.PropertyName == ListView.RowHeightProperty.PropertyName)
+                               UpdateRowHeight();
+                       else if (e.PropertyName == ListView.IsGroupingEnabledProperty.PropertyName ||
+                                       (e.PropertyName == ListView.HasUnevenRowsProperty.PropertyName))
+                               _dataSource.Update();
+                       else if (e.PropertyName == ListView.IsPullToRefreshEnabledProperty.PropertyName)
+                               UpdatePullToRefreshEnabled();
+                       else if (e.PropertyName == ListView.IsRefreshingProperty.PropertyName)
+                               UpdateIsRefreshing();
+                       else if (e.PropertyName == ListView.SeparatorColorProperty.PropertyName)
+                               UpdateSeparatorColor();
+                       else if (e.PropertyName == ListView.SeparatorVisibilityProperty.PropertyName)
+                               UpdateSeparatorVisibility();
+                       else if (e.PropertyName == "HeaderElement")
+                               UpdateHeader();
+                       else if (e.PropertyName == "FooterElement")
+                               UpdateFooter();
+                       else if (e.PropertyName == "RefreshAllowed")
+                               UpdatePullToRefreshEnabled();
+               }
+
+               void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       UpdateItems(e, 0, true);
+               }
+
+               void OnGroupedCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       var til = (TemplatedItemsList<ItemsView<Cell>, Cell>)sender;
+
+                       var templatedItems = TemplatedItemsView.TemplatedItems;
+                       var groupIndex = templatedItems.IndexOf(til.HeaderContent);
+                       UpdateItems(e, groupIndex, false);
+               }
+
+               void UpdateHeader()
+               {
+                       var header = Controller.HeaderElement;
+                       var headerView = (View)header;
+
+                       if (headerView != null)
+                       {
+                               //Header reuse is not working for now , problem with size of something that is not inside a layout
+                               //if (_headerRenderer != null)
+                               //{
+                               //      if (header != null && _headerRenderer.GetType() == Registrar.Registered.GetHandlerType(header.GetType()))
+                               //      {
+                               //              _headerRenderer.SetElement(headerView);
+                               //              _table.HeaderView = new CustomNSTableHeaderView(Bounds.Width, _headerRenderer);
+                               //              //      Layout();
+                               //              //var customNSTableHeaderView = _table.HeaderView as CustomNSTableHeaderView;
+                               //              //customNSTableHeaderView?.Update(Bounds.Width, _headerRenderer);
+                               //              //NativeView.Layout();
+                               //              //NativeView.SetNeedsDisplayInRect(NativeView.Bounds);
+                               //              //NativeView.LayoutSubtreeIfNeeded();
+                               //              //_table.LayoutSubtreeIfNeeded();
+                               //              //_table.NeedsDisplay = true;
+                               //              //NativeView.NeedsDisplay = true;
+                               //              return;
+                               //      }
+                               ClearHeader();
+                               //}
+
+                               _headerRenderer = Platform.CreateRenderer(headerView);
+                               Platform.SetRenderer(headerView, _headerRenderer);
+                               _table.HeaderView = new CustomNSTableHeaderView(Bounds.Width, _headerRenderer);
+
+                               //We need this since the NSSCrollView doesn't know of the new size of our header
+                               //TODO: Find a better solution 
+                               (Control as NSScrollView)?.ContentView.ScrollToPoint(new CoreGraphics.CGPoint(0, -_table.HeaderView.Frame.Height));
+                       }
+                       else if (_headerRenderer != null)
+                       {
+                               ClearHeader();
+                       }
+               }
+
+               void ClearHeader()
+               {
+                       _table.HeaderView = null;
+                       if (_headerRenderer == null)
+                               return;
+                       Platform.DisposeModelAndChildrenRenderers(_headerRenderer.Element);
+                       _headerRenderer = null;
+               }
+
+               void UpdateItems(NotifyCollectionChangedEventArgs e, int section, bool resetWhenGrouped)
+               {
+                       var exArgs = e as NotifyCollectionChangedEventArgsEx;
+                       if (exArgs != null)
+                               _dataSource.Counts[section] = exArgs.Count;
+
+                       var groupReset = resetWhenGrouped && Element.IsGroupingEnabled;
+
+                       switch (e.Action)
+                       {
+                               case NotifyCollectionChangedAction.Add:
+                                       if (e.NewStartingIndex == -1 || groupReset)
+                                               goto case NotifyCollectionChangedAction.Reset;
+
+                                       _table.BeginUpdates();
+                                       _table.InsertRows(NSIndexSet.FromArray(Enumerable.Range(e.NewStartingIndex, e.NewItems.Count).ToArray()),
+                                               NSTableViewAnimation.SlideUp);
+                                       _table.EndUpdates();
+
+                                       break;
+
+                               case NotifyCollectionChangedAction.Remove:
+                                       if (e.OldStartingIndex == -1 || groupReset)
+                                               goto case NotifyCollectionChangedAction.Reset;
+
+                                       _table.BeginUpdates();
+                                       _table.RemoveRows(NSIndexSet.FromArray(Enumerable.Range(e.OldStartingIndex, e.OldItems.Count).ToArray()),
+                                               NSTableViewAnimation.SlideDown);
+                                       _table.EndUpdates();
+
+                                       break;
+
+                               case NotifyCollectionChangedAction.Move:
+                                       if (e.OldStartingIndex == -1 || e.NewStartingIndex == -1 || groupReset)
+                                               goto case NotifyCollectionChangedAction.Reset;
+                                       _table.BeginUpdates();
+                                       for (var i = 0; i < e.OldItems.Count; i++)
+                                       {
+                                               var oldi = e.OldStartingIndex;
+                                               var newi = e.NewStartingIndex;
+
+                                               if (e.NewStartingIndex < e.OldStartingIndex)
+                                               {
+                                                       oldi += i;
+                                                       newi += i;
+                                               }
+
+                                               _table.MoveRow(oldi, newi);
+                                       }
+                                       _table.EndUpdates();
+
+                                       break;
+
+                               case NotifyCollectionChangedAction.Replace:
+                               case NotifyCollectionChangedAction.Reset:
+                                       _table.ReloadData();
+                                       return;
+                       }
+               }
+
+               void UpdateRowHeight()
+               {
+                       var rowHeight = Element.RowHeight;
+                       if (Element.HasUnevenRows && rowHeight == -1)
+                       {
+                               //      _table.RowHeight = NoIntrinsicMetric;
+                       }
+                       else
+                               _table.RowHeight = rowHeight <= 0 ? DefaultRowHeight : rowHeight;
+               }
+
+               //TODO: Implement UpdateIsRefreshing
+               void UpdateIsRefreshing()
+               {
+               }
+
+               //TODO: Implement PullToRefresh
+               void UpdatePullToRefreshEnabled()
+               {
+               }
+
+               //TODO: Implement SeparatorColor
+               void UpdateSeparatorColor()
+               {
+               }
+
+               //TODO: Implement UpdateSeparatorVisibility
+               void UpdateSeparatorVisibility()
+               {
+               }
+
+               //TODO: Implement ScrollTo
+               void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e)
+               {
+               }
+
+               //TODO: Implement Footer
+               void UpdateFooter()
+               {
+               }
+
+               class FormsNSTableView : NSTableView
+               {
+                       //NSTableView doesn't support selection notfications after the items is already selected
+                       //so we do it ourselves by hooking mouse down event
+                       public override void MouseDown(NSEvent theEvent)
+                       {
+                               var clickLocation = ConvertPointFromView(theEvent.LocationInWindow, null);
+                               var clickedRow = GetRow(clickLocation);
+
+                               base.MouseDown(theEvent);
+                               if (clickedRow != -1)
+                                       (Source as ListViewDataSource)?.OnRowClicked();
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/MasterDetailPageRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/MasterDetailPageRenderer.cs
new file mode 100644 (file)
index 0000000..8d2fd55
--- /dev/null
@@ -0,0 +1,204 @@
+using System;
+using System.ComponentModel;
+using System.Linq;
+using AppKit;
+using CoreGraphics;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class MasterDetailPageRenderer : NSSplitViewController, IVisualElementRenderer, IEffectControlProvider
+       {
+               bool _disposed;
+               EventTracker _events;
+               VisualElementTracker _tracker;
+               MasterDetailPage _masterDetailPage;
+
+               IPageController PageController => Element as IPageController;
+
+               void IEffectControlProvider.RegisterEffect(Effect effect)
+               {
+                       var platformEffect = effect as PlatformEffect;
+                       if (platformEffect != null)
+                               platformEffect.Container = View;
+               }
+
+               protected MasterDetailPage MasterDetailPage => _masterDetailPage ?? (_masterDetailPage = (MasterDetailPage)Element);
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (!_disposed && disposing)
+                       {
+                               PageController?.SendDisappearing();
+
+                               if (Element != null)
+                               {
+                                       Element.PropertyChanged -= HandlePropertyChanged;
+                                       Element = null;
+                               }
+
+                               ClearControllers();
+
+                               _tracker?.Dispose();
+                               _tracker = null;
+                               _events?.Dispose();
+                               _events = null;
+
+                               _disposed = true;
+                       }
+                       base.Dispose(disposing);
+               }
+
+               public NSViewController ViewController => this;
+
+               public NSView NativeView => View;
+
+               public VisualElement Element { get; private set; }
+
+               public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+               public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+               {
+                       return NativeView.GetSizeRequest(widthConstraint, heightConstraint);
+               }
+
+               public void SetElement(VisualElement element)
+               {
+                       var oldElement = Element;
+                       Element = element;
+
+                       UpdateControllers();
+
+                       OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+
+                       EffectUtilities.RegisterEffectControlProvider(this, oldElement, element);
+               }
+
+               public void SetElementSize(Size size)
+               {
+                       Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height));
+                       UpdateChildrenLayout();
+               }
+
+               protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
+               {
+                       if (e.OldElement != null)
+                               e.OldElement.PropertyChanged -= HandlePropertyChanged;
+
+                       if (e.NewElement != null)
+                               e.NewElement.PropertyChanged += HandlePropertyChanged;
+
+                       ElementChanged?.Invoke(this, e);
+               }
+
+               protected virtual double MasterWidthPercentage => 0.3;
+
+               public override void ViewWillAppear()
+               {
+                       UpdateBackground();
+                       _tracker = new VisualElementTracker(this);
+                       _events = new EventTracker(this);
+                       _events.LoadEvents(NativeView);
+                       UpdateChildrenLayout();
+
+                       base.ViewWillAppear();
+               }
+
+               public override CGRect GetEffectiveRect(NSSplitView splitView, CGRect proposedEffectiveRect, CGRect drawnRect,
+                       nint dividerIndex)
+               {
+                       return CGRect.Empty;
+               }
+
+               void UpdateChildrenLayout()
+               {
+                       if (View.Frame.Width == -1)
+                               return;
+                       var width = View.Frame.Width;
+                       var masterWidth = MasterWidthPercentage * width;
+                       if (SplitViewItems.Length > 0)
+                               SplitViewItems[0].MaximumThickness = SplitViewItems[0].MinimumThickness = (nfloat)masterWidth;
+               }
+
+               void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (_tracker == null)
+                               return;
+
+                       if (e.PropertyName == "Master" || e.PropertyName == "Detail")
+                               UpdateControllers();
+               }
+
+               void UpdateControllers()
+               {
+                       ClearControllers();
+
+                       if (Platform.GetRenderer(MasterDetailPage.Master) == null)
+                               Platform.SetRenderer(MasterDetailPage.Master, Platform.CreateRenderer(MasterDetailPage.Master));
+                       if (Platform.GetRenderer(MasterDetailPage.Detail) == null)
+                               Platform.SetRenderer(MasterDetailPage.Detail, Platform.CreateRenderer(MasterDetailPage.Detail));
+
+                       AddSplitViewItem(new NSSplitViewItem
+                       {
+                               ViewController = new ViewControllerWrapper(Platform.GetRenderer(MasterDetailPage.Master))
+                       });
+                       AddSplitViewItem(new NSSplitViewItem
+                       {
+                               ViewController = new ViewControllerWrapper(Platform.GetRenderer(MasterDetailPage.Detail))
+                       });
+
+                       UpdateChildrenLayout();
+               }
+
+               void ClearControllers()
+               {
+                       while (SplitViewItems.Length > 0)
+                       {
+                               var splitItem = SplitViewItems.Last();
+                               var childVisualRenderer = splitItem.ViewController as ViewControllerWrapper;
+                               RemoveSplitViewItem(splitItem);
+                               IVisualElementRenderer render = null;
+                               if (childVisualRenderer.RendererWeakRef.TryGetTarget(out render))
+                               {
+                                       render.Dispose();
+                               }
+                               childVisualRenderer.Dispose();
+                               childVisualRenderer = null;
+                       }
+               }
+
+               //TODO: Implement Background color on MDP
+               void UpdateBackground()
+               {
+               }
+
+               sealed class ViewControllerWrapper : NSViewController
+               {
+                       internal WeakReference<IVisualElementRenderer> RendererWeakRef;
+
+                       public ViewControllerWrapper(IVisualElementRenderer renderer)
+                       {
+                               RendererWeakRef = new WeakReference<IVisualElementRenderer>(renderer);
+                               View = new NSView { WantsLayer = true };
+                               AddChildViewController(renderer.ViewController);
+                               View.AddSubview(renderer.NativeView);
+                       }
+
+                       public override void ViewWillLayout()
+                       {
+                               IVisualElementRenderer renderer;
+                               if (RendererWeakRef.TryGetTarget(out renderer))
+                                       renderer?.Element?.Layout(new Rectangle(0, 0, View.Bounds.Width, View.Bounds.Height));
+                               base.ViewWillLayout();
+                       }
+
+                       protected override void Dispose(bool disposing)
+                       {
+                               if (disposing && RendererWeakRef != null)
+                               {
+                                       RendererWeakRef = null;
+                               }
+                               base.Dispose(disposing);
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/NSPageContainer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/NSPageContainer.cs
new file mode 100644 (file)
index 0000000..80f409c
--- /dev/null
@@ -0,0 +1,17 @@
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class NSPageContainer : NSObject
+       {
+               public NSPageContainer(Page element, int index)
+               {
+                       Page = element;
+                       Index = index;
+               }
+
+               public Page Page { get; }
+
+               public int Index { get; set; }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/NavigationPageRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/NavigationPageRenderer.cs
new file mode 100644 (file)
index 0000000..23d7f85
--- /dev/null
@@ -0,0 +1,355 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Threading.Tasks;
+using AppKit;
+using CoreAnimation;
+using Foundation;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class NavigationPageRenderer : NSViewController, IVisualElementRenderer, IEffectControlProvider
+       {
+               bool _disposed;
+               bool _appeared;
+               EventTracker _events;
+               VisualElementTracker _tracker;
+               Stack<NavigationChildPageWrapper> _currentStack = new Stack<NavigationChildPageWrapper>();
+
+               IPageController PageController => Element as IPageController;
+
+               IElementController ElementController => Element as IElementController;
+
+               INavigationPageController NavigationController => Element as INavigationPageController;
+
+               void IEffectControlProvider.RegisterEffect(Effect effect)
+               {
+                       var platformEffect = effect as PlatformEffect;
+                       if (platformEffect != null)
+                               platformEffect.Container = View;
+               }
+
+               public NavigationPageRenderer() : this(IntPtr.Zero)
+               {
+               }
+
+               public NavigationPageRenderer(IntPtr handle)
+               {
+                       View = new NSView { WantsLayer = true };
+               }
+
+               public VisualElement Element { get; private set; }
+
+               public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+               public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+               {
+                       return NativeView.GetSizeRequest(widthConstraint, heightConstraint);
+               }
+
+               public NSViewController ViewController => this;
+
+               public NSView NativeView => View;
+
+               public void SetElement(VisualElement element)
+               {
+                       var oldElement = Element;
+                       Element = element;
+
+                       Init();
+
+                       OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+
+                       EffectUtilities.RegisterEffectControlProvider(this, oldElement, element);
+               }
+
+               public void SetElementSize(Size size)
+               {
+                       Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height));
+               }
+
+               public Task<bool> PopToRootAsync(Page page, bool animated = true)
+               {
+                       return OnPopToRoot(page, animated);
+               }
+
+               public Task<bool> PopViewAsync(Page page, bool animated = true)
+               {
+                       return OnPop(page, animated);
+               }
+
+               public Task<bool> PushPageAsync(Page page, bool animated = true)
+               {
+                       return OnPush(page, animated);
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (!_disposed && disposing)
+                       {
+                               if (Element != null)
+                               {
+                                       PageController?.SendDisappearing();
+                                       ((Element as IPageContainer<Page>)?.CurrentPage as IPageController)?.SendDisappearing();
+                                       Element.PropertyChanged -= HandlePropertyChanged;
+                                       Element = null;
+                               }
+
+                               _tracker?.Dispose();
+                               _tracker = null;
+
+                               _events?.Dispose();
+                               _events = null;
+
+                               _disposed = true;
+                       }
+                       base.Dispose(disposing);
+               }
+
+               public override void ViewDidDisappear()
+               {
+                       base.ViewDidDisappear();
+                       if (!_appeared)
+                               return;
+                       Platform.NativeToolbarTracker.TryHide(Element as NavigationPage);
+                       _appeared = false;
+                       PageController?.SendDisappearing();
+               }
+
+               public override void ViewDidAppear()
+               {
+                       base.ViewDidAppear();
+                       Platform.NativeToolbarTracker.Navigation = (NavigationPage)Element;
+                       if (_appeared)
+                               return;
+
+                       _appeared = true;
+                       PageController?.SendAppearing();
+               }
+
+               protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
+               {
+                       if (e.OldElement != null)
+                               e.OldElement.PropertyChanged -= HandlePropertyChanged;
+
+                       if (e.NewElement != null)
+                               e.NewElement.PropertyChanged += HandlePropertyChanged;
+
+                       ElementChanged?.Invoke(this, e);
+               }
+
+               protected virtual void ConfigurePageRenderer()
+               {
+                       View.WantsLayer = true;
+               }
+
+               protected virtual Task<bool> OnPopToRoot(Page page, bool animated)
+               {
+                       var renderer = Platform.GetRenderer(page);
+                       if (renderer == null || renderer.ViewController == null)
+                               return Task.FromResult(false);
+
+                       Platform.NativeToolbarTracker.UpdateToolBar();
+                       return Task.FromResult(true);
+               }
+
+               protected virtual async Task<bool> OnPop(Page page, bool animated)
+               {
+                       var removed = await PopPageAsync(page, animated);
+                       Platform.NativeToolbarTracker.UpdateToolBar();
+                       return removed;
+               }
+
+               protected virtual async Task<bool> OnPush(Page page, bool animated)
+               {
+                       var shown = await AddPage(page, animated);
+                       Platform.NativeToolbarTracker.UpdateToolBar();
+                       return shown;
+               }
+
+               void Init()
+               {
+                       ConfigurePageRenderer();
+
+                       var navPage = (NavigationPage)Element;
+
+                       if (navPage.CurrentPage == null)
+                               throw new InvalidOperationException(
+                                       "NavigationPage must have a root Page before being used. Either call PushAsync with a valid Page, or pass a Page to the constructor before usage.");
+
+                       Platform.NativeToolbarTracker.Navigation = navPage;
+
+                       NavigationController.PushRequested += OnPushRequested;
+                       NavigationController.PopRequested += OnPopRequested;
+                       NavigationController.PopToRootRequested += OnPopToRootRequested;
+                       NavigationController.RemovePageRequested += OnRemovedPageRequested;
+                       NavigationController.InsertPageBeforeRequested += OnInsertPageBeforeRequested;
+
+                       navPage.Popped += (sender, e) => Platform.NativeToolbarTracker.UpdateToolBar();
+                       navPage.PoppedToRoot += (sender, e) => Platform.NativeToolbarTracker.UpdateToolBar();
+
+                       UpdateBarBackgroundColor();
+                       UpdateBarTextColor();
+
+                       _events = new EventTracker(this);
+                       _events.LoadEvents(NativeView);
+                       _tracker = new VisualElementTracker(this);
+
+                       ((INavigationPageController)navPage).Pages.ForEach(async p => await PushPageAsync(p, false));
+
+                       UpdateBackgroundColor();
+               }
+
+               IVisualElementRenderer CreateViewControllerForPage(Page page)
+               {
+                       if (Platform.GetRenderer(page) == null)
+                               Platform.SetRenderer(page, Platform.CreateRenderer(page));
+
+                       var pageRenderer = Platform.GetRenderer(page);
+                       return pageRenderer;
+               }
+
+               //TODO: Implement InserPageBefore
+               void InsertPageBefore(Page page, Page before)
+               {
+                       if (before == null)
+                               throw new ArgumentNullException(nameof(before));
+                       if (page == null)
+                               throw new ArgumentNullException(nameof(page));
+               }
+
+               void OnInsertPageBeforeRequested(object sender, NavigationRequestedEventArgs e)
+               {
+                       InsertPageBefore(e.Page, e.BeforePage);
+               }
+
+               void OnPopRequested(object sender, NavigationRequestedEventArgs e)
+               {
+                       e.Task = PopViewAsync(e.Page, e.Animated);
+               }
+
+               void OnPopToRootRequested(object sender, NavigationRequestedEventArgs e)
+               {
+                       e.Task = PopToRootAsync(e.Page, e.Animated);
+               }
+
+               void OnPushRequested(object sender, NavigationRequestedEventArgs e)
+               {
+                       e.Task = PushPageAsync(e.Page, e.Animated);
+               }
+
+               void OnRemovedPageRequested(object sender, NavigationRequestedEventArgs e)
+               {
+                       RemovePage(e.Page, true);
+                       Platform.NativeToolbarTracker.UpdateToolBar();
+               }
+
+               void RemovePage(Page page, bool removeFromStack)
+               {
+                       (page as IPageController)?.SendDisappearing();
+                       var target = Platform.GetRenderer(page);
+                       target?.NativeView?.RemoveFromSuperview();
+                       target?.ViewController?.RemoveFromParentViewController();
+                       target?.Dispose();
+                       if (removeFromStack)
+                       {
+                               var newStack = new Stack<NavigationChildPageWrapper>();
+                               foreach (var stack in _currentStack)
+                               {
+                                       if (stack.Page != page)
+                                       {
+                                               newStack.Push(stack);
+                                       }
+                               }
+                               _currentStack = newStack;
+                       }
+               }
+
+               async Task<bool> PopPageAsync(Page page, bool animated)
+               {
+                       if (page == null)
+                               throw new ArgumentNullException(nameof(page));
+
+                       var wrapper = _currentStack.Peek();
+                       if (page != wrapper.Page)
+                               throw new NotSupportedException("Popped page does not appear on top of current navigation stack, please file a bug.");
+
+                       _currentStack.Pop();
+                       (page as IPageController)?.SendDisappearing();
+
+                       var target = Platform.GetRenderer(page);
+                       var previousPage = _currentStack.Peek().Page;
+
+                       if (animated)
+                       {
+                               var previousPageRenderer = Platform.GetRenderer(previousPage);
+                               return await this.HandleAsyncAnimation(target.ViewController, previousPageRenderer.ViewController,
+                                       NSViewControllerTransitionOptions.SlideBackward, () => Platform.DisposeRendererAndChildren(target), true);
+                       }
+
+                       RemovePage(page, false);
+                       return true;
+               }
+
+               async Task<bool> AddPage(Page page, bool animated)
+               {
+                       if (page == null)
+                               throw new ArgumentNullException(nameof(page));
+
+                       Page oldPage = null;
+                       if (_currentStack.Count >= 1)
+                               oldPage = _currentStack.Peek().Page;
+
+                       _currentStack.Push(new NavigationChildPageWrapper(page));
+
+                       var vc = CreateViewControllerForPage(page);
+                       vc.SetElementSize(new Size(View.Bounds.Width, View.Bounds.Height));
+                       page.Layout(new Rectangle(0, 0, View.Bounds.Width, View.Frame.Height));
+
+                       if (_currentStack.Count == 1 || !animated)
+                       {
+                               vc.NativeView.WantsLayer = true;
+                               AddChildViewController(vc.ViewController);
+                               View.AddSubview(vc.NativeView);
+                               return true;
+                       }
+                       var vco = Platform.GetRenderer(oldPage);
+                       AddChildViewController(vc.ViewController);
+                       return await this.HandleAsyncAnimation(vco.ViewController, vc.ViewController,
+                               NSViewControllerTransitionOptions.SlideForward, () => (page as IPageController)?.SendAppearing(), true);
+               }
+
+               void UpdateBackgroundColor()
+               {
+                       if (View.Layer == null)
+                               return;
+                       var color = Element.BackgroundColor == Color.Default ? Color.White : Element.BackgroundColor;
+                       View.Layer.BackgroundColor = color.ToCGColor();
+               }
+
+               void UpdateBarBackgroundColor()
+               {
+                       Platform.NativeToolbarTracker.UpdateToolBar();
+               }
+
+               void UpdateBarTextColor()
+               {
+                       Platform.NativeToolbarTracker.UpdateToolBar();
+               }
+
+               void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (_tracker == null)
+                               return;
+
+                       if (e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName)
+                               UpdateBarBackgroundColor();
+                       else if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName)
+                               UpdateBarTextColor();
+                       else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+                               UpdateBackgroundColor();
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/OpenGLViewRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/OpenGLViewRenderer.cs
new file mode 100644 (file)
index 0000000..dda6ac5
--- /dev/null
@@ -0,0 +1,104 @@
+using System;
+using System.ComponentModel;
+using CoreVideo;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       // ReSharper disable once InconsistentNaming
+       internal class OpenGLViewRenderer : ViewRenderer<OpenGLView, MacOSOpenGLView>
+       {
+               CVDisplayLink _displayLink;
+
+               public void Display(object sender, EventArgs eventArgs)
+               {
+                       if (Element.HasRenderLoop)
+                               return;
+                       SetupRenderLoop(true);
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (_displayLink != null)
+                       {
+                               _displayLink.Dispose();
+                               _displayLink = null;
+
+                               if (Element != null)
+                                       ((IOpenGlViewController)Element).DisplayRequested -= Display;
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<OpenGLView> e)
+               {
+                       if (e.OldElement != null)
+                               ((IOpenGlViewController)e.OldElement).DisplayRequested -= Display;
+
+                       if (e.NewElement != null)
+                       {
+                               //var context = new EAGLContext(EAGLRenderingAPI.OpenGLES2);
+                               //var glkView = new GLKView(RectangleF.Empty) { Context = context, DrawableDepthFormat = GLKViewDrawableDepthFormat.Format24, Delegate = new Delegate(e.NewElement) };
+                               var glkView = new MacOSOpenGLView();
+                               SetNativeControl(glkView);
+
+                               ((IOpenGlViewController)e.NewElement).DisplayRequested += Display;
+
+                               SetupRenderLoop(false);
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == OpenGLView.HasRenderLoopProperty.PropertyName)
+                               SetupRenderLoop(false);
+               }
+
+               void SetupRenderLoop(bool oneShot)
+               {
+                       if (_displayLink != null)
+                               return;
+                       if (!oneShot && !Element.HasRenderLoop)
+                               return;
+
+                       _displayLink = new CVDisplayLink();
+
+                       //.Create(() =>
+                       //{
+                       //      var control = Control;
+                       //      var model = Element;
+                       //      if (control != null)
+                       //              control.Display();
+                       //      if (control == null || model == null || !model.HasRenderLoop)
+                       //      {
+                       //              _displayLink.Invalidate();
+                       //              _displayLink.Dispose();
+                       //              _displayLink = null;
+                       //      }
+                       //});
+                       //_displayLink.(NSRunLoop.Current, NSRunLoop.NSDefaultRunLoopMode);
+               }
+
+               //class Delegate : GLKViewDelegate
+               //{
+               //      readonly OpenGLView _model;
+
+               //      public Delegate(OpenGLView model)
+               //      {
+               //              _model = model;
+               //      }
+
+               //      public override void DrawInRect(GLKView view, RectangleF rect)
+               //      {
+               //              var onDisplay = _model.OnDisplay;
+               //              if (onDisplay == null)
+               //                      return;
+               //              onDisplay(rect.ToRectangle());
+               //      }
+               //}
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/PageControllerDelegate.cs b/Xamarin.Forms.Platform.MacOS/Renderers/PageControllerDelegate.cs
new file mode 100644 (file)
index 0000000..d1f40d3
--- /dev/null
@@ -0,0 +1,30 @@
+using AppKit;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class PageControllerDelegate : NSPageControllerDelegate
+       {
+               public override string GetIdentifier(NSPageController pageController, NSObject targetObject)
+               {
+                       return nameof(PageRenderer);
+               }
+
+               public override NSViewController GetViewController(NSPageController pageController, string identifier)
+               {
+                       return new PageRenderer();
+               }
+
+               public override void PrepareViewController(NSPageController pageController, NSViewController viewController,
+                       NSObject targetObject)
+               {
+                       var pageContainer = targetObject as NSPageContainer;
+                       var pageRenderer = (viewController as PageRenderer);
+                       if (pageContainer == null || pageRenderer == null)
+                               return;
+                       Page page = pageContainer.Page;
+                       pageRenderer.SetElement(page);
+                       Platform.SetRenderer(page, pageRenderer);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/PageRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/PageRenderer.cs
new file mode 100644 (file)
index 0000000..e0845d5
--- /dev/null
@@ -0,0 +1,182 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class PageRenderer : NSViewController, IVisualElementRenderer, IEffectControlProvider
+       {
+               bool _init;
+               bool _appeared;
+               bool _disposed;
+               EventTracker _events;
+               VisualElementPackager _packager;
+               VisualElementTracker _tracker;
+
+               IPageController PageController => Element as IPageController;
+
+               public PageRenderer()
+               {
+                       View = new NSView { WantsLayer = true };
+               }
+
+               void IEffectControlProvider.RegisterEffect(Effect effect)
+               {
+                       var platformEffect = effect as PlatformEffect;
+                       if (platformEffect != null)
+                               platformEffect.Container = View;
+               }
+
+               public VisualElement Element { get; private set; }
+
+               public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+               public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+               {
+                       return NativeView.GetSizeRequest(widthConstraint, heightConstraint);
+               }
+
+               public NSView NativeView => _disposed ? null : View;
+
+               public void SetElement(VisualElement element)
+               {
+                       VisualElement oldElement = Element;
+                       Element = element;
+                       UpdateTitle();
+
+                       OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+
+                       if (Element != null && !string.IsNullOrEmpty(Element.AutomationId))
+                               SetAutomationId(Element.AutomationId);
+
+                       EffectUtilities.RegisterEffectControlProvider(this, oldElement, element);
+               }
+
+               public void SetElementSize(Size size)
+               {
+                       Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height));
+               }
+
+               public NSViewController ViewController => _disposed ? null : this;
+
+               public override void ViewDidAppear()
+               {
+                       base.ViewDidAppear();
+
+                       if (_appeared || _disposed)
+                               return;
+
+                       _appeared = true;
+                       PageController.SendAppearing();
+               }
+
+               public override void ViewDidDisappear()
+               {
+                       base.ViewDidDisappear();
+
+                       if (!_appeared || _disposed)
+                               return;
+
+                       _appeared = false;
+                       PageController.SendDisappearing();
+               }
+
+               public override void ViewWillAppear()
+               {
+                       Init();
+                       base.ViewWillAppear();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               Element.PropertyChanged -= OnHandlePropertyChanged;
+                               Platform.SetRenderer(Element, null);
+                               if (_appeared)
+                                       PageController.SendDisappearing();
+
+                               _appeared = false;
+
+                               if (_events != null)
+                               {
+                                       _events.Dispose();
+                                       _events = null;
+                               }
+
+                               if (_packager != null)
+                               {
+                                       _packager.Dispose();
+                                       _packager = null;
+                               }
+
+                               if (_tracker != null)
+                               {
+                                       _tracker.Dispose();
+                                       _tracker = null;
+                               }
+
+                               Element = null;
+                               _disposed = true;
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               void OnElementChanged(VisualElementChangedEventArgs e)
+               {
+                       ElementChanged?.Invoke(this, e);
+               }
+
+               void SetAutomationId(string id)
+               {
+                       if (NativeView != null)
+                               NativeView.AccessibilityIdentifier = id;
+               }
+
+               void Init()
+               {
+                       if (_init)
+                               return;
+                       UpdateBackground();
+
+                       _packager = new VisualElementPackager(this);
+                       _packager.Load();
+
+                       Element.PropertyChanged += OnHandlePropertyChanged;
+                       _tracker = new VisualElementTracker(this);
+
+                       _events = new EventTracker(this);
+                       _events.LoadEvents(View);
+                       _init = true;
+               }
+
+               void OnHandlePropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+                               UpdateBackground();
+                       else if (e.PropertyName == Page.BackgroundImageProperty.PropertyName)
+                               UpdateBackground();
+                       else if (e.PropertyName == Page.TitleProperty.PropertyName)
+                               UpdateTitle();
+               }
+
+               void UpdateBackground()
+               {
+                       string bgImage = ((Page)Element).BackgroundImage;
+                       if (!string.IsNullOrEmpty(bgImage))
+                       {
+                               View.Layer.BackgroundColor = NSColor.FromPatternImage(NSImage.ImageNamed(bgImage)).CGColor;
+                               return;
+                       }
+                       Color bgColor = Element.BackgroundColor;
+                       View.Layer.BackgroundColor = bgColor.IsDefault ? NSColor.White.CGColor : bgColor.ToCGColor();
+               }
+
+               void UpdateTitle()
+               {
+                       if (!string.IsNullOrWhiteSpace(((Page)Element).Title))
+                               Title = ((Page)Element).Title;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/PickerRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/PickerRenderer.cs
new file mode 100644 (file)
index 0000000..ac07bfd
--- /dev/null
@@ -0,0 +1,154 @@
+using System;
+using AppKit;
+using System.ComponentModel;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class PickerRenderer : ViewRenderer<Picker, NSComboBox>
+       {
+               bool _disposed;
+               NSColor _defaultTextColor;
+               NSColor _defaultBackgroundColor;
+
+               IElementController ElementController => Element;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
+               {
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                                       SetNativeControl(new NSComboBox { Editable = false });
+
+                               _defaultTextColor = Control.TextColor;
+                               _defaultBackgroundColor = Control.BackgroundColor;
+
+                               Control.UsesDataSource = true;
+                               Control.DataSource = new ComboDataSource(this);
+
+                               Control.SelectionChanged += ComboBoxSelectionChanged;
+
+                               UpdatePicker();
+                               UpdateTextColor();
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+                       if (e.PropertyName == Picker.TitleProperty.PropertyName)
+                               UpdatePicker();
+                       if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName)
+                               UpdatePicker();
+                       if (e.PropertyName == Picker.TextColorProperty.PropertyName ||
+                               e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+                               UpdateTextColor();
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       base.SetBackgroundColor(color);
+
+                       if (Control == null)
+                               return;
+
+                       Control.BackgroundColor = color == Color.Default ? _defaultBackgroundColor : color.ToNSColor();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               if (!_disposed)
+                               {
+                                       _disposed = true;
+                                       if (Element != null)
+                                       {
+                                               //TODO: Implement ObservableList picker source change 
+                                               //((ObservableList<string>)Element.Items).CollectionChanged -= RowsCollectionChanged;
+                                       }
+
+                                       if (Control != null)
+                                               Control.SelectionChanged -= ComboBoxSelectionChanged;
+                               }
+                       }
+                       base.Dispose(disposing);
+               }
+
+               void ComboBoxSelectionChanged(object sender, EventArgs e)
+               {
+                       ElementController?.SetValueFromRenderer(Picker.SelectedIndexProperty, (int)Control.SelectedIndex);
+               }
+
+               void OnEnded(object sender, EventArgs eventArgs)
+               {
+                       ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
+               }
+
+               void OnStarted(object sender, EventArgs eventArgs)
+               {
+                       ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
+               }
+
+               void RowsCollectionChanged(object sender, EventArgs e)
+               {
+                       UpdatePicker();
+               }
+
+               void UpdatePicker()
+               {
+                       if (Control == null || Element == null)
+                               return;
+
+                       var selectedIndex = Element.SelectedIndex;
+                       var items = Element.Items;
+                       Control.PlaceholderString = Element.Title ?? string.Empty;
+                       Control.ReloadData();
+                       if (items == null || items.Count == 0 || selectedIndex < 0)
+                               return;
+
+                       Control.SelectItem(selectedIndex);
+               }
+
+               void UpdateTextColor()
+               {
+                       if (Control == null || Element == null)
+                               return;
+
+                       var textColor = Element.TextColor;
+
+                       if (textColor.IsDefault || !Element.IsEnabled)
+                               Control.TextColor = _defaultTextColor;
+                       else
+                               Control.TextColor = textColor.ToNSColor();
+               }
+
+               class ComboDataSource : NSComboBoxDataSource
+               {
+                       readonly PickerRenderer _renderer;
+
+                       public ComboDataSource(PickerRenderer model)
+                       {
+                               _renderer = model;
+                       }
+
+                       public override nint ItemCount(NSComboBox comboBox)
+                       {
+                               return _renderer.Element.Items?.Count ?? 0;
+                       }
+
+                       public override NSObject ObjectValueForItem(NSComboBox comboBox, nint index)
+                       {
+                               return new NSString(_renderer.Element.Items[(int)index]);
+                       }
+
+                       public override nint IndexOfItem(NSComboBox comboBox, string value)
+                       {
+                               var index = _renderer.Element.Items?.IndexOf(value) ?? -1;
+                               return index;
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/ProgressBarRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/ProgressBarRenderer.cs
new file mode 100644 (file)
index 0000000..7b3c84a
--- /dev/null
@@ -0,0 +1,66 @@
+using System.ComponentModel;
+using AppKit;
+using CoreImage;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class ProgressBarRenderer : ViewRenderer<ProgressBar, NSProgressIndicator>
+       {
+               static CIColorPolynomial s_currentColorFilter;
+               static NSColor s_currentColor;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<ProgressBar> e)
+               {
+                       if (e.NewElement == null) return;
+                       if (Control == null)
+                               SetNativeControl(new NSProgressIndicator
+                               {
+                                       IsDisplayedWhenStopped = true,
+                                       Style = NSProgressIndicatorStyle.Bar,
+                                       MinValue = 0,
+                                       MaxValue = 1
+                               });
+                       UpdateProgress();
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == ProgressBar.ProgressProperty.PropertyName)
+                               UpdateProgress();
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       if (Control == null)
+                               return;
+
+                       if (s_currentColorFilter == null && color.IsDefault)
+                               return;
+
+                       if (color.IsDefault)
+                               Control.ContentFilters = new CIFilter[0];
+
+                       var newColor = Element.BackgroundColor.ToNSColor();
+                       if (Equals(s_currentColor, newColor))
+                               return;
+
+                       s_currentColor = newColor;
+
+                       s_currentColorFilter = new CIColorPolynomial
+                       {
+                               RedCoefficients = new CIVector(s_currentColor.RedComponent),
+                               BlueCoefficients = new CIVector(s_currentColor.BlueComponent),
+                               GreenCoefficients = new CIVector(s_currentColor.GreenComponent)
+                       };
+
+                       Control.ContentFilters = new CIFilter[] { s_currentColorFilter };
+               }
+
+               void UpdateProgress()
+               {
+                       Control.DoubleValue = Element.Progress;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/ScrollViewRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/ScrollViewRenderer.cs
new file mode 100644 (file)
index 0000000..c41d1d2
--- /dev/null
@@ -0,0 +1,214 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using RectangleF = CoreGraphics.CGRect;
+using ObjCRuntime;
+using Foundation;
+
+// ReSharper disable UnusedMember.Local
+// ReSharper disable UnusedParameter.Local
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class ScrollViewRenderer : NSScrollView, IVisualElementRenderer
+       {
+               EventTracker _events;
+               VisualElementTracker _tracker;
+               ScrollToRequestedEventArgs _requestedScroll;
+               IVisualElementRenderer _contentRenderer;
+
+               public ScrollViewRenderer() : base(RectangleF.Empty)
+               {
+                       DrawsBackground = false;
+                       ContentView.PostsBoundsChangedNotifications = true;
+                       NSNotificationCenter.DefaultCenter.AddObserver(this, new Selector(nameof(UpdateScrollPosition)),
+                               BoundsChangedNotification, ContentView);
+               }
+
+               IScrollViewController Controller => Element as IScrollViewController;
+
+               public VisualElement Element { get; private set; }
+
+               public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+               public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+               {
+                       return NativeView.GetSizeRequest(widthConstraint, heightConstraint, 44, 44);
+               }
+
+               public NSView NativeView => this;
+
+               public void SetElement(VisualElement element)
+               {
+                       _requestedScroll = null;
+                       var oldElement = Element;
+                       Element = element;
+
+                       if (oldElement != null)
+                       {
+                               oldElement.PropertyChanged -= HandlePropertyChanged;
+                               ((IScrollViewController)oldElement).ScrollToRequested -= OnScrollToRequested;
+                       }
+
+                       if (element != null)
+                       {
+                               element.PropertyChanged += HandlePropertyChanged;
+                               ((IScrollViewController)element).ScrollToRequested += OnScrollToRequested;
+                               if (_tracker == null)
+                               {
+                                       PackContent();
+
+                                       _events = new EventTracker(this);
+                                       _events.LoadEvents(this);
+
+                                       _tracker = new VisualElementTracker(this);
+                                       _tracker.NativeControlUpdated += OnNativeControlUpdated;
+                               }
+
+                               UpdateContentSize();
+                               UpdateBackgroundColor();
+
+                               OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+                       }
+               }
+
+               public void SetElementSize(Size size)
+               {
+                       Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(Element,
+                               new Rectangle(Element.X, Element.Y, size.Width, size.Height));
+               }
+
+               public NSViewController ViewController => null;
+
+               public override void Layout()
+               {
+                       base.Layout();
+                       LayoutSubviews();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               if (_tracker == null)
+                                       return;
+
+                               SetElement(null);
+
+                               _tracker.NativeControlUpdated -= OnNativeControlUpdated;
+                               _tracker.Dispose();
+                               _tracker = null;
+
+                               _events.Dispose();
+                               _events = null;
+
+                               ClearContentRenderer();
+
+                               //NSNotificationCenter.DefaultCenter.RemoveObserver(this, BoundsChangedNotification);
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               void OnElementChanged(VisualElementChangedEventArgs e)
+               {
+                       ElementChanged?.Invoke(this, e);
+               }
+
+               void PackContent()
+               {
+                       ClearContentRenderer();
+
+                       if (Controller.Children.Count == 0 || !(Controller.Children[0] is VisualElement))
+                               return;
+
+                       var content = (VisualElement)Controller.Children[0];
+                       if (Platform.GetRenderer(content) == null)
+                               Platform.SetRenderer(content, Platform.CreateRenderer(content));
+
+                       _contentRenderer = Platform.GetRenderer(content);
+
+                       DocumentView = _contentRenderer.NativeView;
+               }
+
+               void LayoutSubviews()
+               {
+                       if (_requestedScroll != null && Superview != null)
+                       {
+                               var request = _requestedScroll;
+                               _requestedScroll = null;
+                               OnScrollToRequested(this, request);
+                       }
+               }
+
+               void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == ScrollView.ContentSizeProperty.PropertyName)
+                               UpdateContentSize();
+                       else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+                               UpdateBackgroundColor();
+               }
+
+               void HandleScrollAnimationEnded(object sender, EventArgs e)
+               {
+                       Controller.SendScrollFinished();
+               }
+
+               void HandleScrolled(object sender, EventArgs e)
+               {
+                       UpdateScrollPosition();
+               }
+
+               void OnNativeControlUpdated(object sender, EventArgs eventArgs)
+               {
+                       UpdateContentSize();
+               }
+
+               void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e)
+               {
+                       if (Superview == null)
+                       {
+                               _requestedScroll = e;
+                               return;
+                       }
+
+                       Point scrollPoint = (e.Mode == ScrollToMode.Position)
+                               ? new Point(e.ScrollX, Element.Height - e.ScrollY)
+                               : Controller.GetScrollPositionForElement(e.Element as VisualElement, e.Position);
+
+                       (DocumentView as NSView)?.ScrollPoint(scrollPoint.ToPointF());
+
+                       Controller.SendScrollFinished();
+               }
+
+               void UpdateBackgroundColor()
+               {
+                       BackgroundColor = Element.BackgroundColor.ToNSColor(Color.Transparent);
+               }
+
+               void UpdateContentSize()
+               {
+                       if (_contentRenderer == null)
+                               return;
+                       var contentSize = ((ScrollView)Element).ContentSize.ToSizeF();
+                       if (!contentSize.IsEmpty)
+                               _contentRenderer.NativeView.Frame = new RectangleF(0, Element.Height - contentSize.Height, contentSize.Width,
+                                       contentSize.Height);
+               }
+
+               [Export(nameof(UpdateScrollPosition))]
+               void UpdateScrollPosition()
+               {
+                       var convertedPoint = (DocumentView as NSView)?.ConvertPointFromView(ContentView.Bounds.Location, ContentView);
+                       if (convertedPoint.HasValue)
+                               Controller.SetScrolledPosition(Math.Max(0, convertedPoint.Value.X), Math.Max(0, convertedPoint.Value.Y));
+               }
+
+               void ClearContentRenderer()
+               {
+                       _contentRenderer?.NativeView?.RemoveFromSuperview();
+                       _contentRenderer?.Dispose();
+                       _contentRenderer = null;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/SearchBarRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/SearchBarRenderer.cs
new file mode 100644 (file)
index 0000000..50c7d5f
--- /dev/null
@@ -0,0 +1,179 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class SearchBarRenderer : ViewRenderer<SearchBar, NSSearchField>
+       {
+               NSColor _defaultTextColor;
+
+               IElementController ElementController => Element;
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               if (Control != null)
+                               {
+                                       Control.Changed -= OnTextChanged;
+                                       Control.Cell.CancelButtonCell.Activated -= OnCancelClicked;
+                                       Control.Cell.SearchButtonCell.Activated -= OnSearchButtonClicked;
+                                       Control.EditingEnded -= OnEditingEnded;
+                                       Control.EditingBegan -= OnEditingStarted;
+                               }
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
+               {
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                               {
+                                       SetNativeControl(new NSSearchField { BackgroundColor = NSColor.Clear, DrawsBackground = false });
+
+                                       Control.Cell.CancelButtonCell.Activated += OnCancelClicked;
+                                       Control.Cell.SearchButtonCell.Activated += OnSearchButtonClicked;
+
+                                       Control.Changed += OnTextChanged;
+                                       Control.EditingBegan += OnEditingStarted;
+                                       Control.EditingEnded += OnEditingEnded;
+                               }
+
+                               UpdatePlaceholder();
+                               UpdateText();
+                               UpdateFont();
+                               UpdateIsEnabled();
+                               UpdateCancelButton();
+                               UpdateAlignment();
+                               UpdateTextColor();
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == SearchBar.PlaceholderProperty.PropertyName ||
+                               e.PropertyName == SearchBar.PlaceholderColorProperty.PropertyName)
+                               UpdatePlaceholder();
+                       else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+                       {
+                               UpdateIsEnabled();
+                               UpdateTextColor();
+                               UpdatePlaceholder();
+                       }
+                       else if (e.PropertyName == SearchBar.TextColorProperty.PropertyName)
+                               UpdateTextColor();
+                       else if (e.PropertyName == SearchBar.TextProperty.PropertyName)
+                               UpdateText();
+                       else if (e.PropertyName == SearchBar.CancelButtonColorProperty.PropertyName)
+                               UpdateCancelButton();
+                       else if (e.PropertyName == SearchBar.FontAttributesProperty.PropertyName)
+                               UpdateFont();
+                       else if (e.PropertyName == SearchBar.FontFamilyProperty.PropertyName)
+                               UpdateFont();
+                       else if (e.PropertyName == SearchBar.FontSizeProperty.PropertyName)
+                               UpdateFont();
+                       else if (e.PropertyName == SearchBar.HorizontalTextAlignmentProperty.PropertyName)
+                               UpdateAlignment();
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       base.SetBackgroundColor(color);
+
+                       if (Control == null)
+                               return;
+                       Control.BackgroundColor = color == Color.Default ? NSColor.Clear : color.ToNSColor();
+
+                       UpdateCancelButton();
+               }
+
+               void OnCancelClicked(object sender, EventArgs args)
+               {
+                       ElementController.SetValueFromRenderer(SearchBar.TextProperty, null);
+                       Control.ResignFirstResponder();
+               }
+
+               void OnEditingEnded(object sender, EventArgs e)
+               {
+                       ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
+               }
+
+               void OnEditingStarted(object sender, EventArgs e)
+               {
+                       ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
+               }
+
+               void OnSearchButtonClicked(object sender, EventArgs e)
+               {
+                       ((ISearchBarController)Element).OnSearchButtonPressed();
+                       Control.ResignFirstResponder();
+               }
+
+               void OnTextChanged(object sender, EventArgs a)
+               {
+                       ElementController.SetValueFromRenderer(SearchBar.TextProperty, Control.StringValue);
+               }
+
+               void UpdateAlignment()
+               {
+                       Control.Alignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
+               }
+
+               void UpdateCancelButton()
+               {
+                       var cancelButtonColor = Element.CancelButtonColor;
+
+                       if (cancelButtonColor.IsDefault)
+                       {
+                               Control.Cell.CancelButtonCell.Title = "";
+                       }
+                       else
+                       {
+                               var textWithColor = new NSAttributedString(Control.Cell.CancelButtonCell.Title ?? "",
+                                       foregroundColor: cancelButtonColor.ToNSColor());
+                               Control.Cell.CancelButtonCell.AttributedTitle = textWithColor;
+                       }
+               }
+
+               void UpdateFont()
+               {
+                       Control.Font = Element.ToNSFont();
+               }
+
+               void UpdateIsEnabled()
+               {
+                       Control.Enabled = Element.IsEnabled;
+               }
+
+               void UpdatePlaceholder()
+               {
+                       var formatted = (FormattedString)Element.Placeholder ?? string.Empty;
+                       var targetColor = Element.PlaceholderColor;
+                       var color = Element.IsEnabled && !targetColor.IsDefault ? targetColor : ColorExtensions.SeventyPercentGrey.ToColor();
+                       Control.PlaceholderAttributedString = formatted.ToAttributed(Element, color);
+               }
+
+               void UpdateText()
+               {
+                       Control.StringValue = Element.Text ?? "";
+                       UpdateCancelButton();
+               }
+
+               void UpdateTextColor()
+               {
+                       _defaultTextColor = _defaultTextColor ?? Control.TextColor;
+                       var targetColor = Element.TextColor;
+
+                       Control.TextColor = targetColor.ToNSColor(_defaultTextColor);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/SliderRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/SliderRenderer.cs
new file mode 100644 (file)
index 0000000..9b495de
--- /dev/null
@@ -0,0 +1,77 @@
+using System;
+using SizeF = CoreGraphics.CGSize;
+using AppKit;
+using System.ComponentModel;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class SliderRenderer : ViewRenderer<Slider, NSSlider>
+       {
+               bool _disposed;
+
+               IElementController ElementController => Element;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
+               {
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                               {
+                                       SetNativeControl(new NSSlider { Continuous = true });
+                                       Control.Activated += OnControlActivated;
+                               }
+
+                               UpdateMaximum();
+                               UpdateMinimum();
+                               UpdateValue();
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == Slider.MaximumProperty.PropertyName)
+                               UpdateMaximum();
+                       else if (e.PropertyName == Slider.MinimumProperty.PropertyName)
+                               UpdateMinimum();
+                       else if (e.PropertyName == Slider.ValueProperty.PropertyName)
+                               UpdateValue();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               _disposed = true;
+                               if (Control != null)
+                                       Control.Activated -= OnControlActivated;
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               void OnControlActivated(object sender, EventArgs eventArgs)
+               {
+                       ElementController?.SetValueFromRenderer(Slider.ValueProperty, Control.DoubleValue);
+               }
+
+               void UpdateMaximum()
+               {
+                       Control.MaxValue = (float)Element.Maximum;
+               }
+
+               void UpdateMinimum()
+               {
+                       Control.MinValue = (float)Element.Minimum;
+               }
+
+               void UpdateValue()
+               {
+                       if (Math.Abs(Element.Value - Control.DoubleValue) > 0)
+                               Control.DoubleValue = (float)Element.Value;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/StepperRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/StepperRenderer.cs
new file mode 100644 (file)
index 0000000..f28bf1f
--- /dev/null
@@ -0,0 +1,84 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class StepperRenderer : ViewRenderer<Stepper, NSStepper>
+       {
+               bool _disposed;
+
+               IElementController ElementController => Element;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
+               {
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                               {
+                                       SetNativeControl(new NSStepper());
+                                       Control.Activated += OnControlActivated;
+                               }
+
+                               UpdateMinimum();
+                               UpdateMaximum();
+                               UpdateValue();
+                               UpdateIncrement();
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == Stepper.MinimumProperty.PropertyName)
+                               UpdateMinimum();
+                       else if (e.PropertyName == Stepper.MaximumProperty.PropertyName)
+                               UpdateMaximum();
+                       else if (e.PropertyName == Stepper.ValueProperty.PropertyName)
+                               UpdateValue();
+                       else if (e.PropertyName == Stepper.IncrementProperty.PropertyName)
+                               UpdateIncrement();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               _disposed = true;
+                               if (Control != null)
+                                       Control.Activated -= OnControlActivated;
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               void OnControlActivated(object sender, EventArgs e)
+               {
+                       ElementController?.SetValueFromRenderer(Stepper.ValueProperty, Control.DoubleValue);
+               }
+
+               void UpdateIncrement()
+               {
+                       Control.Increment = Element.Increment;
+               }
+
+               void UpdateMaximum()
+               {
+                       Control.MaxValue = Element.Maximum;
+               }
+
+               void UpdateMinimum()
+               {
+                       Control.MinValue = Element.Minimum;
+               }
+
+               void UpdateValue()
+               {
+                       if (Math.Abs(Control.DoubleValue - Element.Value) > 0)
+                               Control.DoubleValue = Element.Value;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/SwitchRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/SwitchRenderer.cs
new file mode 100644 (file)
index 0000000..c93007e
--- /dev/null
@@ -0,0 +1,61 @@
+using System;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class SwitchRenderer : ViewRenderer<Switch, NSButton>
+       {
+               bool _disposed;
+
+               IElementController ElementController => Element;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
+               {
+                       if (e.OldElement != null)
+                               e.OldElement.Toggled -= OnElementToggled;
+
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                               {
+                                       SetNativeControl(new NSButton { AllowsMixedState = false, Title = string.Empty });
+
+                                       Control.SetButtonType(NSButtonType.Switch);
+                                       Control.Activated += OnControlActivated;
+                               }
+
+                               UpdateState();
+                               e.NewElement.Toggled += OnElementToggled;
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               _disposed = true;
+                               if (Control != null)
+                                       Control.Activated -= OnControlActivated;
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               void OnControlActivated(object sender, EventArgs e)
+               {
+                       ElementController?.SetValueFromRenderer(Switch.IsToggledProperty, Control.State == NSCellStateValue.On);
+               }
+
+               void OnElementToggled(object sender, EventArgs e)
+               {
+                       UpdateState();
+               }
+
+               void UpdateState()
+               {
+                       Control.State = Element.IsToggled ? NSCellStateValue.On : NSCellStateValue.Off;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/TabbedPageRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/TabbedPageRenderer.cs
new file mode 100644 (file)
index 0000000..3aee095
--- /dev/null
@@ -0,0 +1,403 @@
+using System;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using AppKit;
+using CoreGraphics;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.PlatformConfiguration.macOSSpecific;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class TabbedPageRenderer : NSTabViewController, IVisualElementRenderer, IEffectControlProvider
+       {
+               const float DefaultImageSizeSegmentedButton = 19;
+               const int TabHolderHeight = 30;
+
+               bool _disposed;
+               bool _updatingControllers;
+               bool _barBackgroundColorWasSet;
+               bool _barTextColorWasSet;
+               bool _defaultBarTextColorSet;
+               bool _defaultBarColorSet;
+               VisualElementTracker _tracker;
+               bool _loaded;
+               Size _queuedSize;
+
+
+               public VisualElement Element { get; private set; }
+
+               public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+               public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+               {
+                       return NativeView.GetSizeRequest(widthConstraint, heightConstraint);
+               }
+
+               public NSView NativeView => View;
+
+               public void SetElement(VisualElement element)
+               {
+                       var oldElement = Element;
+                       Element = element;
+
+                       if (oldElement != null)
+                       {
+                               oldElement.PropertyChanged -= OnPropertyChanged;
+                               var tabbedPage = oldElement as TabbedPage;
+                               if (tabbedPage != null) tabbedPage.PagesChanged -= OnPagesChanged;
+                       }
+
+                       if (element != null)
+                       {
+                               if (_tracker == null)
+                               {
+                                       _tracker = new VisualElementTracker(this);
+                                       _tracker.NativeControlUpdated += (sender, e) => UpdateNativeWidget();
+                               }
+                       }
+
+                       OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+
+                       ConfigureTabView();
+
+                       OnPagesChanged(null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+
+                       Tabbed.PropertyChanged += OnPropertyChanged;
+                       Tabbed.PagesChanged += OnPagesChanged;
+
+                       UpdateBarBackgroundColor();
+
+                       UpdateBarTextColor();
+
+                       EffectUtilities.RegisterEffectControlProvider(this, oldElement, element);
+               }
+
+               IPageController PageController => Element as IPageController;
+
+               IElementController ElementController => Element;
+
+               void IEffectControlProvider.RegisterEffect(Effect effect)
+               {
+                       var platformEffect = effect as PlatformEffect;
+                       if (platformEffect != null)
+                               platformEffect.Container = View;
+               }
+
+               public void SetElementSize(Size size)
+               {
+                       if (_loaded)
+                               Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height));
+                       else
+                               _queuedSize = size;
+               }
+
+               public NSViewController ViewController => this;
+
+               public override void ViewWillLayout()
+               {
+                       base.ViewWillLayout();
+
+                       if (Element == null)
+                               return;
+
+                       if (!Element.Bounds.IsEmpty)
+                               View.Frame = new System.Drawing.RectangleF((float)Element.X, (float)Element.Y, (float)Element.Width, (float)Element.Height);
+
+                       var frame = View.Frame;
+                       PageController.ContainerArea = new Rectangle(0, 0, frame.Width, frame.Height - TabHolderHeight);
+
+                       if (!_queuedSize.IsZero)
+                       {
+                               Element.Layout(new Rectangle(Element.X, Element.Y, _queuedSize.Width, _queuedSize.Height));
+                               _queuedSize = Size.Zero;
+                       }
+
+                       _loaded = true;
+               }
+
+
+               public override nint SelectedTabViewItemIndex
+               {
+                       get { return base.SelectedTabViewItemIndex; }
+                       set
+                       {
+                               base.SelectedTabViewItemIndex = value;
+                               if (!_updatingControllers)
+                                       UpdateCurrentPage();
+                       }
+               }
+
+               public override void ViewDidAppear()
+               {
+                       PageController.SendAppearing();
+                       base.ViewDidAppear();
+               }
+
+               public override void ViewDidDisappear()
+               {
+                       base.ViewDidDisappear();
+                       PageController.SendDisappearing();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               _disposed = true;
+                               PageController.SendDisappearing();
+                               Tabbed.PropertyChanged -= OnPropertyChanged;
+                               Tabbed.PagesChanged -= OnPagesChanged;
+
+                               if (_tracker != null)
+                               {
+                                       _tracker.Dispose();
+                                       _tracker = null;
+                               }
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               protected virtual void ConfigureTabView()
+               {
+                       View.WantsLayer = true;
+                       TabView.WantsLayer = true;
+                       TabView.DrawsBackground = false;
+                       var tabStyle = Tabbed.OnThisPlatform().GetTabsStyle();
+                       switch (tabStyle)
+                       {
+                               case TabsStyle.OnNavigation:
+                               case TabsStyle.Hidden:
+                                       TabStyle = NSTabViewControllerTabStyle.Unspecified;
+                                       break;
+                               case TabsStyle.Icons:
+                                       TabStyle = NSTabViewControllerTabStyle.Toolbar;
+                                       break;
+                               case TabsStyle.OnBottom:
+                                       TabStyle = NSTabViewControllerTabStyle.SegmentedControlOnBottom;
+                                       break;
+                               default:
+                                       TabStyle = NSTabViewControllerTabStyle.SegmentedControlOnTop;
+                                       break;
+                       }
+
+                       TabView.TabViewType = NSTabViewType.NSNoTabsNoBorder;
+               }
+
+               protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
+               {
+                       ElementChanged?.Invoke(this, e);
+               }
+
+               protected virtual NSTabViewItem GetTabViewItem(Page page, IVisualElementRenderer pageRenderer)
+               {
+                       var tvi = new NSTabViewItem { ViewController = pageRenderer.ViewController, Label = page.Title ?? "" };
+                       if (!string.IsNullOrEmpty (page.Icon)) {
+                               var image = GetTabViewItemIcon (page.Icon);
+                               if (image != null)
+                                       tvi.Image = image;
+                       }
+                       return tvi;
+               }
+
+               protected virtual NSImage GetTabViewItemIcon(string imageName)
+               {
+                       var image = NSImage.ImageNamed (imageName);
+                       if(image == null)
+                               image = new NSImage (imageName);
+
+                       if (image == null)
+                               return null;
+
+                       bool shouldResize = TabStyle == NSTabViewControllerTabStyle.SegmentedControlOnTop ||
+                                                               TabStyle == NSTabViewControllerTabStyle.SegmentedControlOnBottom;
+                       if (shouldResize)
+                               image = image.ResizeTo(new CGSize(DefaultImageSizeSegmentedButton, DefaultImageSizeSegmentedButton));
+                       return image;
+               }
+
+               protected virtual void UpdateNativeWidget()
+               {
+                       TabView.Layout();
+               }
+
+               protected TabbedPage Tabbed => (TabbedPage)Element;
+
+               void OnPagePropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == Page.TitleProperty.PropertyName)
+                       {
+                               var page = (Page)sender;
+                               var index = TabbedPage.GetIndex(page);
+                               TabViewItems[index].Label = page.Title;
+                       }
+                       else if (e.PropertyName == Page.IconProperty.PropertyName)
+                       {
+                               var page = (Page)sender;
+
+                               var index = TabbedPage.GetIndex(page);
+                               TabViewItems[index].Label = page.Title;
+
+                               if (!string.IsNullOrEmpty(page.Icon))
+                               {
+                                       TabViewItems[index].Image = new NSImage(page.Icon);
+                               }
+                               else if (TabViewItems[index].Image != null)
+                               {
+                                       TabViewItems[index].Image = new NSImage();
+                               }
+                       }
+               }
+
+               void OnPagesChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       e.Apply((o, i, c) => SetupPage((Page)o, i), (o, i) => TeardownPage((Page)o), Reset);
+
+                       SetControllers();
+
+                       UpdateChildrenOrderIndex();
+
+                       SetSelectedTabViewItem();
+               }
+
+               void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == nameof(TabbedPage.CurrentPage))
+                       {
+                               var current = Tabbed.CurrentPage;
+                               if (current == null)
+                                       return;
+
+                               SetSelectedTabViewItem();
+                       }
+                       else if (e.PropertyName == TabbedPage.BarBackgroundColorProperty.PropertyName)
+                               UpdateBarBackgroundColor();
+                       else if (e.PropertyName == TabbedPage.BarTextColorProperty.PropertyName)
+                               UpdateBarTextColor();
+               }
+
+               void Reset()
+               {
+                       var i = 0;
+                       foreach (var page in Tabbed.Children)
+                               SetupPage(page, i++);
+               }
+
+               void SetControllers()
+               {
+                       _updatingControllers = true;
+                       for (var i = 0; i < ElementController.LogicalChildren.Count; i++)
+                       {
+                               var child = ElementController.LogicalChildren[i];
+                               var page = child as Page;
+                               if (page == null)
+                                       continue;
+
+                               var pageRenderer = Platform.GetRenderer(page);
+                               if (pageRenderer != null)
+                               {
+                                       pageRenderer.ViewController.Identifier = i.ToString();
+
+                                       NSTabViewItem newTvi = GetTabViewItem(page, pageRenderer);
+
+                                       AddTabViewItem(newTvi);
+                               }
+                       }
+                       _updatingControllers = false;
+               }
+
+               void SetupPage(Page page, int index)
+               {
+                       var renderer = Platform.GetRenderer(page);
+                       if (renderer == null)
+                       {
+                               renderer = Platform.CreateRenderer(page);
+                               Platform.SetRenderer(page, renderer);
+                       }
+
+                       renderer.ViewController.Identifier = index.ToString();
+
+                       page.PropertyChanged += OnPagePropertyChanged;
+               }
+
+               void TeardownPage(Page page)
+               {
+                       page.PropertyChanged -= OnPagePropertyChanged;
+
+                       Platform.SetRenderer(page, null);
+               }
+
+               void SetSelectedTabViewItem()
+               {
+                       if (Tabbed.CurrentPage == null)
+                               return;
+                       var selectedIndex = TabbedPage.GetIndex(Tabbed.CurrentPage);
+                       SelectedTabViewItemIndex = selectedIndex;
+               }
+
+               void UpdateChildrenOrderIndex()
+               {
+                       for (var i = 0; i < TabViewItems.Length; i++)
+                       {
+                               int originalIndex;
+                               if (int.TryParse(TabViewItems[i].ViewController.Identifier, out originalIndex))
+                               {
+                                       var page = PageController.InternalChildren[originalIndex];
+                                       TabbedPage.SetIndex(page as Page, i);
+                               }
+                       }
+               }
+
+               void UpdateCurrentPage()
+               {
+                       var count = PageController.InternalChildren.Count;
+                       Tabbed.CurrentPage = SelectedTabViewItemIndex >= 0 && SelectedTabViewItemIndex < count
+                               ? Tabbed.GetPageByIndex((int)SelectedTabViewItemIndex)
+                               : null;
+               }
+
+               //TODO: Implement UpdateBarBackgroundColor
+               void UpdateBarBackgroundColor()
+               {
+                       if (Tabbed == null || TabView == null)
+                               return;
+
+                       var barBackgroundColor = Tabbed.BarBackgroundColor;
+                       var isDefaultColor = barBackgroundColor.IsDefault;
+
+                       if (isDefaultColor && !_barBackgroundColorWasSet)
+                               return;
+
+                       if (!_defaultBarColorSet)
+                       {
+                               //_defaultBarColor = TabView.color;
+                               _defaultBarColorSet = true;
+                       }
+
+                       if (!isDefaultColor)
+                               _barBackgroundColorWasSet = true;
+               }
+
+               //TODO: Implement UpdateBarTextColor
+               void UpdateBarTextColor()
+               {
+                       if (Tabbed == null || TabView == null)
+                               return;
+
+                       var barTextColor = Tabbed.BarTextColor;
+                       var isDefaultColor = barTextColor.IsDefault;
+
+                       if (isDefaultColor && !_barTextColorWasSet)
+                               return;
+
+                       if (!_defaultBarTextColorSet)
+                       {
+                               //      _defaultBarTextColor = TabBar.TintColor;
+                               _defaultBarTextColorSet = true;
+                       }
+
+                       if (!isDefaultColor)
+                               _barTextColorWasSet = true;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/TableViewDataSource.cs b/Xamarin.Forms.Platform.MacOS/Renderers/TableViewDataSource.cs
new file mode 100644 (file)
index 0000000..90335ee
--- /dev/null
@@ -0,0 +1,131 @@
+using System;
+using AppKit;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       internal class TableViewDataSource : NSTableViewSource
+       {
+               static int s_sectionCount;
+
+               const string HeaderIdentifier = nameof(TextCell);
+               const string ItemIdentifier = nameof(ViewCell);
+
+               protected ITableViewController Controller => _tableView;
+
+               readonly NSTableView _nsTableView;
+               readonly TableView _tableView;
+
+               public TableViewDataSource(TableViewRenderer tableViewRenderer)
+               {
+                       _tableView = tableViewRenderer.Element;
+                       _nsTableView = tableViewRenderer.TableView;
+                       Controller.ModelChanged += (s, e) => { _nsTableView?.ReloadData(); };
+                       AutomaticallyDeselect = true;
+               }
+
+               public bool AutomaticallyDeselect { get; set; }
+
+               public override void SelectionDidChange(NSNotification notification)
+               {
+                       var row = _nsTableView.SelectedRow;
+                       if (row == -1)
+                               return;
+
+                       int sectionIndex;
+                       bool isHeader;
+                       int itemIndexInSection;
+
+                       GetComputedIndexes(row, out sectionIndex, out itemIndexInSection, out isHeader);
+
+                       var cell = Controller.Model.GetCell(sectionIndex, itemIndexInSection);
+                       Controller.Model.RowSelected(cell);
+                       if (AutomaticallyDeselect)
+                               _nsTableView.DeselectRow(row);
+               }
+
+               public override nint GetRowCount(NSTableView tableView)
+               {
+                       nint count = 0;
+                       s_sectionCount = Controller.Model.GetSectionCount();
+                       for (int i = 0; i < s_sectionCount; i++)
+                       {
+                               count += Controller.Model.GetRowCount(i) + 1;
+                       }
+
+                       return count;
+               }
+
+               public override bool ShouldSelectRow(NSTableView tableView, nint row)
+               {
+                       int sectionIndex;
+                       bool isHeader;
+                       int itemIndexInSection;
+
+                       GetComputedIndexes(row, out sectionIndex, out itemIndexInSection, out isHeader);
+
+                       return !isHeader;
+               }
+
+               public override NSView GetViewForItem(NSTableView tableView, NSTableColumn tableColumn, nint row)
+               {
+                       int sectionIndex;
+                       bool isHeader;
+                       int itemIndexInSection;
+
+                       GetComputedIndexes(row, out sectionIndex, out itemIndexInSection, out isHeader);
+
+                       string id;
+                       Cell cell;
+                       if (isHeader)
+                       {
+                               id = HeaderIdentifier;
+                               cell = Controller.Model.GetHeaderCell(sectionIndex) ??
+                                               new TextCell { Text = Controller.Model.GetSectionTitle(sectionIndex) };
+                       }
+                       else
+                       {
+                               id = ItemIdentifier;
+                               cell = Controller.Model.GetCell(sectionIndex, itemIndexInSection);
+                       }
+
+                       var nativeCell = CellNSView.GetNativeCell(tableView, cell, id, isHeader);
+                       return nativeCell;
+               }
+
+               void GetComputedIndexes(nint row, out int sectionIndex, out int itemIndexInSection, out bool isHeader)
+               {
+                       var totalItems = 0;
+                       isHeader = false;
+                       sectionIndex = 0;
+                       itemIndexInSection = 0;
+
+                       for (int i = 0; i < s_sectionCount; i++)
+                       {
+                               var groupCount = Controller.Model.GetRowCount(i);
+                               var itemsInSection = groupCount + 1;
+
+                               if (row < totalItems + itemsInSection)
+                               {
+                                       sectionIndex = i;
+                                       itemIndexInSection = (int)row - totalItems;
+                                       isHeader = itemIndexInSection == 0;
+                                       if (isHeader)
+                                               itemIndexInSection = -1;
+                                       else
+                                               itemIndexInSection = itemIndexInSection - 1;
+                                       break;
+                               }
+                               totalItems += itemsInSection;
+                       }
+               }
+       }
+
+       //TODO: Implement Uneven rows
+       internal class UnEvenTableViewModelRenderer : TableViewDataSource
+       {
+               public UnEvenTableViewModelRenderer(TableViewRenderer model) : base(model)
+               {
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/TableViewRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/TableViewRenderer.cs
new file mode 100644 (file)
index 0000000..de447dc
--- /dev/null
@@ -0,0 +1,98 @@
+using System.Collections.Generic;
+using System.ComponentModel;
+using AppKit;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class TableViewRenderer : ViewRenderer<TableView, NSView>
+       {
+               const int DefaultRowHeight = 44;
+
+               internal NSTableView TableView { get; set; }
+
+               public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+               {
+                       return Control.GetSizeRequest(widthConstraint, heightConstraint, DefaultRowHeight, DefaultRowHeight);
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               var viewsToLookAt = new Stack<NSView>(Subviews);
+                               while (viewsToLookAt.Count > 0)
+                               {
+                                       var view = viewsToLookAt.Pop();
+                                       var viewCellRenderer = view as IVisualElementRenderer;
+                                       if (viewCellRenderer != null)
+                                       {
+                                               viewCellRenderer.Dispose();
+                                       }
+                                       else
+                                       {
+                                               foreach (var child in view.Subviews)
+                                                       viewsToLookAt.Push(child);
+                                       }
+                               }
+                       }
+
+                       base.Dispose(disposing);
+               }
+
+               protected virtual NSTableView CreateNSTableView(TableView list)
+               {
+                       return new NSTableView().AsListViewLook();
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
+               {
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                               {
+                                       var scroller = new NSScrollView
+                                       {
+                                               AutoresizingMask = NSViewResizingMask.HeightSizable | NSViewResizingMask.WidthSizable,
+                                               DocumentView = TableView = CreateNSTableView(e.NewElement)
+                                       };
+
+                                       SetNativeControl(scroller);
+                               }
+
+                               SetSource();
+                               UpdateRowHeight();
+                               UpdateBackgroundView();
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == Xamarin.Forms.TableView.RowHeightProperty.PropertyName)
+                               UpdateRowHeight();
+                       else if (e.PropertyName == Xamarin.Forms.TableView.HasUnevenRowsProperty.PropertyName)
+                               SetSource();
+                       else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+                               UpdateBackgroundView();
+               }
+
+               void SetSource()
+               {
+                       var modeledView = Element;
+                       TableView.Source = modeledView.HasUnevenRows ? new UnEvenTableViewModelRenderer(this) : new TableViewDataSource(this);
+               }
+
+               void UpdateBackgroundView()
+               {
+               }
+
+               void UpdateRowHeight()
+               {
+                       var rowHeight = Element.RowHeight;
+                       TableView.RowHeight = rowHeight <= 0 ? DefaultRowHeight : rowHeight;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/TimePickerRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/TimePickerRenderer.cs
new file mode 100644 (file)
index 0000000..fc645c4
--- /dev/null
@@ -0,0 +1,104 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using Foundation;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class TimePickerRenderer : ViewRenderer<TimePicker, NSDatePicker>
+       {
+               NSColor _defaultTextColor;
+               NSColor _defaultBackgroundColor;
+               bool _disposed;
+
+               IElementController ElementController => Element;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
+               {
+                       base.OnElementChanged(e);
+
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                               {
+                                       SetNativeControl(new NSDatePicker
+                                       {
+                                               DatePickerMode = NSDatePickerMode.Single,
+                                               TimeZone = new NSTimeZone("UTC"),
+                                               DatePickerStyle = NSDatePickerStyle.TextFieldAndStepper,
+                                               DatePickerElements = NSDatePickerElementFlags.HourMinuteSecond
+                                       });
+
+                                       Control.ValidateProposedDateValue += HandleValueChanged;
+                                       _defaultTextColor = Control.TextColor;
+                                       _defaultBackgroundColor = Control.BackgroundColor;
+                               }
+
+                               UpdateTime();
+                               UpdateTextColor();
+                       }
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == TimePicker.TimeProperty.PropertyName ||
+                               e.PropertyName == TimePicker.FormatProperty.PropertyName)
+                               UpdateTime();
+
+                       if (e.PropertyName == TimePicker.TextColorProperty.PropertyName ||
+                               e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+                               UpdateTextColor();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               if (Control != null)
+                                       Control.ValidateProposedDateValue -= HandleValueChanged;
+
+                               _disposed = true;
+                       }
+                       base.Dispose(disposing);
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       base.SetBackgroundColor(color);
+
+                       if (Control == null)
+                               return;
+                       Control.BackgroundColor = color == Color.Default ? _defaultBackgroundColor : color.ToNSColor();
+               }
+
+               void HandleValueChanged(object sender, NSDatePickerValidatorEventArgs e)
+               {
+                       ElementController?.SetValueFromRenderer(TimePicker.TimeProperty,
+                               Control.DateValue.ToDateTime() - new DateTime(2001, 1, 1));
+               }
+
+               void UpdateTime()
+               {
+                       if (Control == null || Element == null)
+                               return;
+                       var time = new DateTime(2001, 1, 1).Add(Element.Time);
+                       var newDate = time.ToNSDate();
+                       if (!Equals(Control.DateValue, newDate))
+                               Control.DateValue = newDate;
+               }
+
+               void UpdateTextColor()
+               {
+                       if (Control == null || Element == null)
+                               return;
+                       var textColor = Element.TextColor;
+
+                       if (textColor.IsDefault || !Element.IsEnabled)
+                               Control.TextColor = _defaultTextColor;
+                       else
+                               Control.TextColor = textColor.ToNSColor();
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/WebViewRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/WebViewRenderer.cs
new file mode 100644 (file)
index 0000000..8d93c2c
--- /dev/null
@@ -0,0 +1,149 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using Foundation;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+       public class WebViewRenderer : ViewRenderer<WebView, WebKit.WebView>, IWebViewDelegate
+       {
+               bool _disposed;
+               bool _ignoreSourceChanges;
+               WebNavigationEvent _lastBackForwardEvent;
+               WebNavigationEvent _lastEvent;
+
+               IElementController ElementController => Element;
+
+               void IWebViewDelegate.LoadHtml(string html, string baseUrl)
+               {
+                       if (html != null)
+                               Control.MainFrame.LoadHtmlString(html,
+                                       baseUrl == null ? new NSUrl(NSBundle.MainBundle.BundlePath, true) : new NSUrl(baseUrl, true));
+               }
+
+               void IWebViewDelegate.LoadUrl(string url)
+               {
+                       Control.MainFrame.LoadRequest(new NSUrlRequest(new NSUrl(url)));
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
+               {
+                       base.OnElementChanged(e);
+
+                       if (e.NewElement != null)
+                       {
+                               if (Control == null)
+                               {
+                                       SetNativeControl(new WebKit.WebView
+                                       {
+                                               AutoresizingMask = NSViewResizingMask.WidthSizable,
+                                               AutoresizesSubviews = true
+                                       });
+                                       Control.OnFinishedLoading += OnNSWebViewFinishedLoad;
+                                       Control.OnFailedLoading += OnNSWebViewFailedLoadWithError;
+
+                                       Element.EvalRequested += OnEvalRequested;
+                                       Element.GoBackRequested += OnGoBackRequested;
+                                       Element.GoForwardRequested += OnGoForwardRequested;
+
+                                       Layer.BackgroundColor = NSColor.Clear.CGColor;
+                               }
+                       }
+
+                       Load();
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == WebView.SourceProperty.PropertyName)
+                               Load();
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_disposed)
+                       {
+                               _disposed = true;
+                               Control.OnFinishedLoading -= OnNSWebViewFinishedLoad;
+                               Control.OnFailedLoading -= OnNSWebViewFailedLoadWithError;
+                               Element.EvalRequested -= OnEvalRequested;
+                               Element.GoBackRequested -= OnGoBackRequested;
+                               Element.GoForwardRequested -= OnGoForwardRequested;
+                       }
+                       base.Dispose(disposing);
+               }
+
+               void Load()
+               {
+                       if (_ignoreSourceChanges)
+                               return;
+
+                       Element?.Source?.Load(this);
+
+                       UpdateCanGoBackForward();
+               }
+
+               void UpdateCanGoBackForward()
+               {
+                       if (Element == null)
+                               return;
+                       Element.CanGoBack = Control.CanGoBack();
+                       Element.CanGoForward = Control.CanGoForward();
+               }
+
+               void OnEvalRequested(object sender, EvalRequested eventArg)
+               {
+                       Control?.StringByEvaluatingJavaScriptFromString(eventArg?.Script);
+               }
+
+               void OnGoBackRequested(object sender, EventArgs eventArgs)
+               {
+                       if (Control.CanGoBack())
+                       {
+                               _lastBackForwardEvent = WebNavigationEvent.Back;
+                               Control.GoBack();
+                       }
+
+                       UpdateCanGoBackForward();
+               }
+
+               void OnGoForwardRequested(object sender, EventArgs eventArgs)
+               {
+                       if (Control.CanGoForward())
+                       {
+                               _lastBackForwardEvent = WebNavigationEvent.Forward;
+                               Control.GoForward();
+                       }
+
+                       UpdateCanGoBackForward();
+               }
+
+               void OnNSWebViewFailedLoadWithError(object sender, WebKit.WebResourceErrorEventArgs e)
+               {
+                       _lastEvent = _lastBackForwardEvent;
+                       Element?.SendNavigated(new WebNavigatedEventArgs(_lastEvent, new UrlWebViewSource { Url = Control.MainFrameUrl },
+                               Control.MainFrameUrl, WebNavigationResult.Failure));
+
+                       UpdateCanGoBackForward();
+               }
+
+               void OnNSWebViewFinishedLoad(object sender, WebKit.WebResourceCompletedEventArgs e)
+               {
+                       if (Control.IsLoading)
+                               return;
+
+                       _ignoreSourceChanges = true;
+                       ElementController?.SetValueFromRenderer(WebView.SourceProperty, new UrlWebViewSource { Url = Control.MainFrameUrl });
+                       _ignoreSourceChanges = false;
+
+                       _lastEvent = _lastBackForwardEvent;
+                       Element?.SendNavigated(new WebNavigatedEventArgs(_lastEvent, Element?.Source, Control.MainFrameUrl,
+                               WebNavigationResult.Success));
+
+                       UpdateCanGoBackForward();
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.MacOS/Xamarin.Forms.Platform.macOS.csproj b/Xamarin.Forms.Platform.MacOS/Xamarin.Forms.Platform.macOS.csproj
new file mode 100644 (file)
index 0000000..fe0eff2
--- /dev/null
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{C0059C45-EA1E-42F3-8A0E-794BB547EC3C}</ProjectGuid>
+    <ProjectTypeGuids>{A3F8F2AB-B479-4A4A-A458-A89E7DC349F1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <RootNamespace>Xamarin.Forms.Platform.macOS</RootNamespace>
+    <AssemblyName>Xamarin.Forms.Platform.macOS</AssemblyName>
+    <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
+    <TargetFrameworkIdentifier>Xamarin.Mac</TargetFrameworkIdentifier>
+    <MonoMacResourcePrefix>Resources</MonoMacResourcePrefix>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <ConsolePause>false</ConsolePause>
+    <EnableCodeSigning>false</EnableCodeSigning>
+    <CreatePackage>false</CreatePackage>
+    <EnablePackageSigning>false</EnablePackageSigning>
+    <IncludeMonoRuntime>false</IncludeMonoRuntime>
+    <UseSGen>false</UseSGen>
+    <HttpClientHandler>
+    </HttpClientHandler>
+    <TlsProvider>
+    </TlsProvider>
+    <LinkMode>
+    </LinkMode>
+    <XamMacArch>
+    </XamMacArch>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <DefineConstants>
+    </DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <EnableCodeSigning>false</EnableCodeSigning>
+    <CreatePackage>false</CreatePackage>
+    <EnablePackageSigning>false</EnablePackageSigning>
+    <IncludeMonoRuntime>false</IncludeMonoRuntime>
+    <UseSGen>false</UseSGen>
+    <HttpClientHandler>
+    </HttpClientHandler>
+    <TlsProvider>
+    </TlsProvider>
+    <LinkMode>
+    </LinkMode>
+    <XamMacArch>
+    </XamMacArch>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="Xamarin.Mac" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.Net.Http" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="FormsApplicationDelegate.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="PlatformRenderer.cs" />
+    <Compile Include="Platform.cs" />
+    <Compile Include="CADisplayLinkTicker.cs" />
+    <Compile Include="Extensions\PageExtensions.cs" />
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\ExportCellAttribute.cs">
+      <Link>ExportCellAttribute.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\ExportImageSourceHandlerAttribute.cs">
+      <Link>ExportImageSourceHandlerAttribute.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\Deserializer.cs">
+      <Link>Deserializer.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\RendererPool.cs">
+      <Link>RendererPool.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\EffectUtilities.cs">
+      <Link>EffectUtilities.cs</Link>
+    </Compile>
+    <Compile Include="Renderers\PageRenderer.cs" />
+    <Compile Include="Renderers\DefaultRenderer.cs" />
+    <Compile Include="Extensions\AlignmentExtensions.cs" />
+    <Compile Include="Renderers\ButtonRenderer.cs" />
+    <Compile Include="ImageSourceHandlers.cs" />
+    <Compile Include="Extensions\ButtonExtensions.cs" />
+    <Compile Include="Renderers\BoxViewRenderer.cs" />
+    <Compile Include="Renderers\ScrollViewRenderer.cs" />
+    <Compile Include="Renderers\ActivityIndicatorRenderer.cs" />
+    <Compile Include="Renderers\DatePickerRenderer.cs" />
+    <Compile Include="Renderers\EntryRenderer.cs" />
+    <Compile Include="Renderers\EditorRenderer.cs" />
+    <Compile Include="Renderers\FrameRenderer.cs" />
+    <Compile Include="Controls\FormsImageView.cs" />
+    <Compile Include="Renderers\ImageRenderer.cs" />
+    <Compile Include="Renderers\OpenGLViewRenderer.cs" />
+    <Compile Include="Controls\MacOSOpenGLView.cs" />
+    <Compile Include="Renderers\PickerRenderer.cs" />
+    <Compile Include="Renderers\ProgressBarRenderer.cs" />
+    <Compile Include="Renderers\SearchBarRenderer.cs" />
+    <Compile Include="Renderers\SliderRenderer.cs" />
+    <Compile Include="Renderers\StepperRenderer.cs" />
+    <Compile Include="Renderers\SwitchRenderer.cs" />
+    <Compile Include="Renderers\TimePickerRenderer.cs" />
+    <Compile Include="Renderers\WebViewRenderer.cs" />
+    <Compile Include="Renderers\ListViewRenderer.cs" />
+    <Compile Include="Cells\CellNSView.cs" />
+    <Compile Include="Cells\CellRenderer.cs" />
+    <Compile Include="Cells\TextCellRenderer.cs" />
+    <Compile Include="Cells\NSTableViewCellStyle.cs" />
+    <Compile Include="Extensions\NSTextFieldExtensions.cs" />
+    <Compile Include="Cells\ImageCellRenderer.cs" />
+    <Compile Include="Cells\SwitchCellRenderer.cs" />
+    <Compile Include="Cells\EntryCellRenderer.cs" />
+    <Compile Include="Cells\ViewCellRenderer.cs" />
+    <Compile Include="Cells\ViewCellNSView.cs" />
+    <Compile Include="Renderers\ListViewDataSource.cs" />
+    <Compile Include="Renderers\CustomNSTableHeaderView.cs" />
+    <Compile Include="Renderers\CarouselPageRenderer.cs" />
+    <Compile Include="Extensions\NSScrollViewExtensions.cs" />
+    <Compile Include="Controls\ScrollViewScrollChangedEventArgs.cs" />
+    <Compile Include="Renderers\MasterDetailPageRenderer.cs" />
+    <Compile Include="Renderers\TabbedPageRenderer.cs" />
+    <Compile Include="Renderers\NavigationPageRenderer.cs" />
+    <Compile Include="Controls\FormsPageControllerDelegate.cs" />
+    <Compile Include="Controls\NavigationChildPageWrapper.cs" />
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\NativeViewPropertyListener.cs">
+      <Link>NativeViewPropertyListener.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Core\Properties\GlobalAssemblyInfo.cs">
+      <Link>Properties\GlobalAssemblyInfo.cs</Link>
+    </Compile>
+    <Compile Include="Renderers\LayoutRenderer.cs" />
+    <Compile Include="Extensions\NSImageExtensions.cs" />
+    <Compile Include="Renderers\NSPageContainer.cs" />
+    <Compile Include="Renderers\PageControllerDelegate.cs" />
+    <Compile Include="Extensions\NSViewControllerExtensions.cs" />
+    <Compile Include="ModalPageTracker.cs" />
+    <Compile Include="PlatformNavigation.cs" />
+    <Compile Include="Renderers\TableViewRenderer.cs" />
+    <Compile Include="Extensions\NSTableViewExtensions.cs" />
+    <Compile Include="Renderers\TableViewDataSource.cs" />
+    <Compile Include="NativeToolbarTracker.cs" />
+    <Compile Include="Extensions\NSButtonExtensions.cs" />
+    <Compile Include="Controls\VerticallyCenteredTextFieldCell.cs" />
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\Forms.cs">
+      <Link>Forms.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\Extensions\DateExtensions.cs">
+      <Link>Extensions\DateExtensions.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\Extensions\LayoutExtensions.cs">
+      <Link>Extensions\LayoutExtensions.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\Extensions\ColorExtensions.cs">
+      <Link>Extensions\ColorExtensions.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\Extensions\UIViewExtensions.cs">
+      <Link>Extensions\UIViewExtensions.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\Renderers\FontExtensions.cs">
+      <Link>Extensions\FontExtensions.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\Renderers\FormattedStringExtensions.cs">
+      <Link>Extensions\FormattedStringExtensions.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\IVisualElementRenderer.cs">
+      <Link>IVisualElementRenderer.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\NativeValueConverterService.cs">
+      <Link>NativeValueConverterService.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\NativeViewWrapper.cs">
+      <Link>NativeViewWrapper.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\NativeViewWrapperRenderer.cs">
+      <Link>NativeViewWrapperRenderer.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\ElementChangedEventArgs.cs">
+      <Link>ElementChangedEventArgs.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\ExportRendererAttribute.cs">
+      <Link>ExportRendererAttribute.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\PlatformEffect.cs">
+      <Link>PlatformEffect.cs</Link>
+    </Compile>
+    <Compile Include="Controls\NSToolbarItemGroup.cs" />
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\Extensions\PlatformConfigurationExtensions.cs">
+      <Link>Extensions\PlatformConfigurationExtensions.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\ViewInitializedEventArgs.cs">
+      <Link>ViewInitializedEventArgs.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\Renderers\LabelRenderer.cs">
+      <Link>Renderers\LabelRenderer.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\ResourcesProvider.cs">
+      <Link>ResourcesProvider.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\VisualElementPackager.cs">
+      <Link>VisualElementPackager.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\ViewRenderer.cs">
+      <Link>ViewRenderer.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\VisualElementTracker.cs">
+      <Link>VisualElementTracker.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\EventTracker.cs">
+      <Link>EventTracker.cs</Link>
+    </Compile>
+    <Compile Include="..\Xamarin.Forms.Platform.iOS\VisualElementRenderer.cs">
+      <Link>VisualElementRenderer.cs</Link>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Xamarin.Forms.Platform\Xamarin.Forms.Platform.csproj">
+      <Project>{67F9D3A8-F71E-4428-913F-C37AE82CDB24}</Project>
+      <Name>Xamarin.Forms.Platform</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj">
+      <Project>{57B8B73D-C3B5-4C42-869E-7B2F17D354AC}</Project>
+      <Name>Xamarin.Forms.Core</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup />
+  <Import Project="$(MSBuildExtensionsPath)\Xamarin\Mac\Xamarin.Mac.CSharp.targets" />
+</Project>
\ No newline at end of file
index 8665757..dcef049 100644 (file)
@@ -6,7 +6,12 @@ using System.Runtime.Serialization;
 using System.Threading.Tasks;
 using System.Xml;
 
+#if __MOBILE__
 namespace Xamarin.Forms.Platform.iOS
+#else
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        internal class Deserializer : IDeserializer
        {
index c952347..6b05378 100644 (file)
@@ -1,4 +1,10 @@
+
+#if __MOBILE__
 namespace Xamarin.Forms.Platform.iOS
+#else
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        internal static class EffectUtilities
        {
index 560cfd1..49cf85d 100644 (file)
@@ -1,10 +1,16 @@
 using System;
 
+#if __MOBILE__
 namespace Xamarin.Forms.Platform.iOS
+#else
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public class VisualElementChangedEventArgs : ElementChangedEventArgs<VisualElement>
        {
-               public VisualElementChangedEventArgs(VisualElement oldElement, VisualElement newElement) : base(oldElement, newElement)
+               public VisualElementChangedEventArgs(VisualElement oldElement, VisualElement newElement)
+                       : base(oldElement, newElement)
                {
                }
        }
index 9c6c90e..596506a 100644 (file)
@@ -3,27 +3,42 @@ using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.Specialized;
 using System.Linq;
+
+#if __MOBILE__
 using UIKit;
+using NativeView = UIKit.UIView;
+using NativeGestureRecognizer = UIKit.UIGestureRecognizer;
+using NativeGestureRecognizerState = UIKit.UIGestureRecognizerState;
 
 namespace Xamarin.Forms.Platform.iOS
+#else
+using AppKit;
+using NativeView = AppKit.NSView;
+using NativeGestureRecognizer = AppKit.NSGestureRecognizer;
+using NativeGestureRecognizerState = AppKit.NSGestureRecognizerState;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public class EventTracker : IDisposable
        {
                readonly NotifyCollectionChangedEventHandler _collectionChangedHandler;
 
-               readonly Dictionary<IGestureRecognizer, UIGestureRecognizer> _gestureRecognizers = new Dictionary<IGestureRecognizer, UIGestureRecognizer>();
+               readonly Dictionary<IGestureRecognizer, NativeGestureRecognizer> _gestureRecognizers = new Dictionary<IGestureRecognizer, NativeGestureRecognizer>();
 
                readonly IVisualElementRenderer _renderer;
                bool _disposed;
-               UIView _handler;
+               NativeView _handler;
 
                double _previousScale = 1.0;
+#if __MOBILE__
                UITouchEventArgs _shouldReceive;
+#endif
 
                public EventTracker(IVisualElementRenderer renderer)
                {
                        if (renderer == null)
-                               throw new ArgumentNullException("renderer");
+                               throw new ArgumentNullException(nameof(renderer));
 
                        _collectionChangedHandler = ModelGestureRecognizersOnCollectionChanged;
 
@@ -62,18 +77,18 @@ namespace Xamarin.Forms.Platform.iOS
                        _handler = null;
                }
 
-               public void LoadEvents(UIView handler)
+               public void LoadEvents(NativeView handler)
                {
                        if (_disposed)
                                throw new ObjectDisposedException(null);
-
+#if __MOBILE__
                        _shouldReceive = (r, t) => t.View is IVisualElementRenderer;
-
+#endif
                        _handler = handler;
                        OnElementChanged(this, new VisualElementChangedEventArgs(null, _renderer.Element));
                }
 
-               protected virtual UIGestureRecognizer GetNativeRecognizer(IGestureRecognizer recognizer)
+               protected virtual NativeGestureRecognizer GetNativeRecognizer(IGestureRecognizer recognizer)
                {
                        if (recognizer == null)
                                return null;
@@ -84,7 +99,7 @@ namespace Xamarin.Forms.Platform.iOS
                        var tapRecognizer = recognizer as TapGestureRecognizer;
                        if (tapRecognizer != null)
                        {
-                               var uiRecognizer = CreateTapRecognizer(1, tapRecognizer.NumberOfTapsRequired, r =>
+                               var returnAction = new Action(() =>
                                {
                                        var tapGestureRecognizer = weakRecognizer.Target as TapGestureRecognizer;
                                        var eventTracker = weakEventTracker.Target as EventTracker;
@@ -93,6 +108,7 @@ namespace Xamarin.Forms.Platform.iOS
                                        if (tapGestureRecognizer != null && view != null)
                                                tapGestureRecognizer.SendTapped(view);
                                });
+                               var uiRecognizer = CreateTapRecognizer(tapRecognizer.NumberOfTapsRequired, returnAction);
                                return uiRecognizer;
                        }
 
@@ -110,41 +126,51 @@ namespace Xamarin.Forms.Platform.iOS
                                        {
                                                var oldScale = eventTracker._previousScale;
                                                var originPoint = r.LocationInView(null);
+#if __MOBILE__
                                                originPoint = UIApplication.SharedApplication.KeyWindow.ConvertPointToView(originPoint, eventTracker._renderer.NativeView);
+#else
+                                               originPoint = NSApplication.SharedApplication.KeyWindow.ContentView.ConvertPointToView(originPoint, eventTracker._renderer.NativeView);
+#endif
                                                var scaledPoint = new Point(originPoint.X / view.Width, originPoint.Y / view.Height);
 
                                                switch (r.State)
                                                {
-                                                       case UIGestureRecognizerState.Began:
+                                                       case NativeGestureRecognizerState.Began:
+#if __MOBILE__
                                                                if (r.NumberOfTouches < 2)
                                                                        return;
+#endif
                                                                pinchGestureRecognizer.SendPinchStarted(view, scaledPoint);
                                                                startingScale = view.Scale;
                                                                break;
-                                                       case UIGestureRecognizerState.Changed:
+                                                       case NativeGestureRecognizerState.Changed:
+#if __MOBILE__
                                                                if (r.NumberOfTouches < 2 && pinchGestureRecognizer.IsPinching)
                                                                {
-                                                                       r.State = UIGestureRecognizerState.Ended;
+                                                                       r.State = NativeGestureRecognizerState.Ended;
                                                                        pinchGestureRecognizer.SendPinchEnded(view);
                                                                        return;
                                                                }
-
+                                                               var scale = r.Scale;
+#else
+                                                               var scale = r.Magnification;
+#endif
                                                                var delta = 1.0;
-                                                               var dif = Math.Abs(r.Scale - oldScale) * startingScale;
-                                                               if (oldScale < r.Scale)
+                                                               var dif = Math.Abs(scale - oldScale) * startingScale;
+                                                               if (oldScale < scale)
                                                                        delta = 1 + dif;
-                                                               if (oldScale > r.Scale)
+                                                               if (oldScale > scale)
                                                                        delta = 1 - dif;
 
                                                                pinchGestureRecognizer.SendPinch(view, delta, scaledPoint);
-                                                               eventTracker._previousScale = r.Scale;
+                                                               eventTracker._previousScale = scale;
                                                                break;
-                                                       case UIGestureRecognizerState.Cancelled:
-                                                       case UIGestureRecognizerState.Failed:
+                                                       case NativeGestureRecognizerState.Cancelled:
+                                                       case NativeGestureRecognizerState.Failed:
                                                                if (pinchGestureRecognizer.IsPinching)
                                                                        pinchGestureRecognizer.SendPinchCanceled(view);
                                                                break;
-                                                       case UIGestureRecognizerState.Ended:
+                                                       case NativeGestureRecognizerState.Ended:
                                                                if (pinchGestureRecognizer.IsPinching)
                                                                        pinchGestureRecognizer.SendPinchEnded(view);
                                                                eventTracker._previousScale = 1;
@@ -168,33 +194,42 @@ namespace Xamarin.Forms.Platform.iOS
                                        {
                                                switch (r.State)
                                                {
-                                                       case UIGestureRecognizerState.Began:
+                                                       case NativeGestureRecognizerState.Began:
+#if __MOBILE__
                                                                if (r.NumberOfTouches != panRecognizer.TouchPoints)
                                                                        return;
+#endif
                                                                panGestureRecognizer.SendPanStarted(view, Application.Current.PanGestureId);
                                                                break;
-                                                       case UIGestureRecognizerState.Changed:
+                                                       case NativeGestureRecognizerState.Changed:
+#if __MOBILE__
                                                                if (r.NumberOfTouches != panRecognizer.TouchPoints)
                                                                {
-                                                                       r.State = UIGestureRecognizerState.Ended;
+                                                                       r.State = NativeGestureRecognizerState.Ended;
                                                                        panGestureRecognizer.SendPanCompleted(view, Application.Current.PanGestureId);
                                                                        Application.Current.PanGestureId++;
                                                                        return;
                                                                }
+#endif
                                                                var translationInView = r.TranslationInView(_handler);
                                                                panGestureRecognizer.SendPan(view, translationInView.X, translationInView.Y, Application.Current.PanGestureId);
                                                                break;
-                                                       case UIGestureRecognizerState.Cancelled:
-                                                       case UIGestureRecognizerState.Failed:
+                                                       case NativeGestureRecognizerState.Cancelled:
+                                                       case NativeGestureRecognizerState.Failed:
                                                                panGestureRecognizer.SendPanCanceled(view, Application.Current.PanGestureId);
                                                                Application.Current.PanGestureId++;
                                                                break;
-                                                       case UIGestureRecognizerState.Ended:
+                                                       case NativeGestureRecognizerState.Ended:
+#if __MOBILE__
                                                                if (r.NumberOfTouches != panRecognizer.TouchPoints)
                                                                {
                                                                        panGestureRecognizer.SendPanCompleted(view, Application.Current.PanGestureId);
                                                                        Application.Current.PanGestureId++;
                                                                }
+#else
+                                                               panGestureRecognizer.SendPanCompleted(view, Application.Current.PanGestureId);
+                                                               Application.Current.PanGestureId++;
+#endif
                                                                break;
                                                }
                                        }
@@ -204,7 +239,7 @@ namespace Xamarin.Forms.Platform.iOS
 
                        return null;
                }
-
+#if __MOBILE__
                UIPanGestureRecognizer CreatePanRecognizer(int numTouches, Action<UIPanGestureRecognizer> action)
                {
                        var result = new UIPanGestureRecognizer(action);
@@ -218,14 +253,33 @@ namespace Xamarin.Forms.Platform.iOS
                        return result;
                }
 
-               UITapGestureRecognizer CreateTapRecognizer(int numFingers, int numTaps, Action<UITapGestureRecognizer> action)
+               UITapGestureRecognizer CreateTapRecognizer(int numTaps, Action action, int numFingers = 1)
                {
                        var result = new UITapGestureRecognizer(action);
                        result.NumberOfTouchesRequired = (uint)numFingers;
                        result.NumberOfTapsRequired = (uint)numTaps;
                        return result;
                }
+#else
+               NSPanGestureRecognizer CreatePanRecognizer(int numTouches, Action<NSPanGestureRecognizer> action)
+               {
+                       var result = new NSPanGestureRecognizer(action);
+                       return result;
+               }
+
+               NSMagnificationGestureRecognizer CreatePinchRecognizer(Action<NSMagnificationGestureRecognizer> action)
+               {
+                       var result = new NSMagnificationGestureRecognizer(action);
+                       return result;
+               }
 
+               NSClickGestureRecognizer CreateTapRecognizer(int numTaps, Action action)
+               {
+                       var result = new NSClickGestureRecognizer(action);
+                       result.NumberOfClicksRequired = numTaps;
+                       return result;
+               }
+#endif
                void LoadRecognizers()
                {
                        if (ElementGestureRecognizers == null)
@@ -239,7 +293,9 @@ namespace Xamarin.Forms.Platform.iOS
                                var nativeRecognizer = GetNativeRecognizer(recognizer);
                                if (nativeRecognizer != null)
                                {
+#if __MOBILE__
                                        nativeRecognizer.ShouldReceiveTouch = _shouldReceive;
+#endif
                                        _handler.AddGestureRecognizer(nativeRecognizer);
 
                                        _gestureRecognizers[recognizer] = nativeRecognizer;
index 50bba17..3b3ef91 100644 (file)
@@ -1,29 +1,37 @@
 using System;
+
+#if __MOBILE__
 using UIKit;
+#endif
 
 namespace Xamarin.Forms
 {
        [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
        public sealed class ExportRendererAttribute : HandlerAttribute
        {
+#if __MOBILE__
                public ExportRendererAttribute(Type handler, Type target, UIUserInterfaceIdiom idiom) : base(handler, target)
                {
                        Idiomatic = true;
                        Idiom = idiom;
                }
+               internal UIUserInterfaceIdiom Idiom { get; }
+#endif
 
                public ExportRendererAttribute(Type handler, Type target) : base(handler, target)
                {
                        Idiomatic = false;
                }
 
-               internal UIUserInterfaceIdiom Idiom { get; }
-
                internal bool Idiomatic { get; }
 
                public override bool ShouldRegister()
                {
+#if __MOBILE__
                        return !Idiomatic || Idiom == UIDevice.CurrentDevice.UserInterfaceIdiom;
+#else
+                       return !Idiomatic;
+#endif
                }
        }
 }
\ No newline at end of file
index bb4342a..2b8b910 100644 (file)
@@ -1,16 +1,27 @@
 using System;
 using CoreGraphics;
-using UIKit;
 using PointF = CoreGraphics.CGPoint;
 using RectangleF = CoreGraphics.CGRect;
 using SizeF = CoreGraphics.CGSize;
-
+#if __MOBILE__
+using UIKit;
 namespace Xamarin.Forms.Platform.iOS
+#else
+using AppKit;
+using UIColor = AppKit.NSColor;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public static class ColorExtensions
        {
+#if __MOBILE__
                internal static readonly UIColor Black = UIColor.Black;
                internal static readonly UIColor SeventyPercentGrey = new UIColor(0.7f, 0.7f, 0.7f, 1);
+#else
+               internal static readonly NSColor Black = NSColor.Black;
+               internal static readonly NSColor SeventyPercentGrey = NSColor.FromRgba(0.7f, 0.7f, 0.7f, 1);
+#endif
 
                public static CGColor ToCGColor(this Color color)
                {
@@ -23,10 +34,15 @@ namespace Xamarin.Forms.Platform.iOS
                        nfloat green;
                        nfloat blue;
                        nfloat alpha;
+#if __MOBILE__
                        color.GetRGBA(out red, out green, out blue, out alpha);
+#else
+                       color.GetRgba(out red, out green, out blue, out alpha);
+#endif
                        return new Color(red, green, blue, alpha);
                }
 
+#if __MOBILE__
                public static UIColor ToUIColor(this Color color)
                {
                        return new UIColor((float)color.R, (float)color.G, (float)color.B, (float)color.A);
@@ -47,6 +63,28 @@ namespace Xamarin.Forms.Platform.iOS
 
                        return color.ToUIColor();
                }
+#else
+               public static NSColor ToNSColor(this Color color)
+               {
+                       return NSColor.FromRgba((float)color.R, (float)color.G, (float)color.B, (float)color.A);
+               }
+
+               public static NSColor ToNSColor(this Color color, Color defaultColor)
+               {
+                       if (color.IsDefault)
+                               return defaultColor.ToNSColor();
+
+                       return color.ToNSColor();
+               }
+
+               public static NSColor ToNSColor(this Color color, NSColor defaultColor)
+               {
+                       if (color.IsDefault)
+                               return defaultColor;
+
+                       return color.ToNSColor();
+               }
+#endif
        }
 
        public static class PointExtensions
@@ -55,6 +93,11 @@ namespace Xamarin.Forms.Platform.iOS
                {
                        return new Point(point.X, point.Y);
                }
+
+               public static PointF ToPointF(this Point point)
+               {
+                       return new PointF(point.X, point.Y);
+               }
        }
 
        public static class SizeExtensions
index bb0dfff..73ca347 100644 (file)
@@ -1,7 +1,12 @@
 using System;
 using Foundation;
 
+#if __MOBILE__
 namespace Xamarin.Forms.Platform.iOS
+#else
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public static class DateExtensions
        {
index 823c3f3..d65caf1 100644 (file)
@@ -1,10 +1,19 @@
 using System.Collections.Generic;
 using CoreGraphics;
+#if __MOBILE__
 using UIKit;
-
+using Xamarin.Forms.Platform.iOS;
 namespace Xamarin.Forms.Platform.iOS
+#else
+using AppKit;
+using UIView = AppKit.NSView;
+using Xamarin.Forms.Platform.MacOS;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
-       public delegate SizeRequest? GetDesiredSizeDelegate(NativeViewWrapperRenderer renderer, double widthConstraint, double heightConstraint);
+       public delegate SizeRequest? GetDesiredSizeDelegate(
+               NativeViewWrapperRenderer renderer, double widthConstraint, double heightConstraint);
 
        public delegate CGSize? SizeThatFitsDelegate(CGSize size);
 
@@ -12,13 +21,15 @@ namespace Xamarin.Forms.Platform.iOS
 
        public static class LayoutExtensions
        {
-               public static void Add(this IList<View> children, UIView view, GetDesiredSizeDelegate getDesiredSizeDelegate = null, SizeThatFitsDelegate sizeThatFitsDelegate = null,
-                                                          LayoutSubviewsDelegate layoutSubViews = null)
+               public static void Add(this IList<View> children, UIView view, GetDesiredSizeDelegate getDesiredSizeDelegate = null,
+                       SizeThatFitsDelegate sizeThatFitsDelegate = null,
+                       LayoutSubviewsDelegate layoutSubViews = null)
                {
                        children.Add(view.ToView(getDesiredSizeDelegate, sizeThatFitsDelegate, layoutSubViews));
                }
 
-               public static View ToView(this UIView view, GetDesiredSizeDelegate getDesiredSizeDelegate = null, SizeThatFitsDelegate sizeThatFitsDelegate = null, LayoutSubviewsDelegate layoutSubViews = null)
+               public static View ToView(this UIView view, GetDesiredSizeDelegate getDesiredSizeDelegate = null,
+                       SizeThatFitsDelegate sizeThatFitsDelegate = null, LayoutSubviewsDelegate layoutSubViews = null)
                {
                        return new NativeViewWrapper(view, getDesiredSizeDelegate, sizeThatFitsDelegate, layoutSubViews);
                }
index f61837f..f2932e3 100644 (file)
@@ -1,11 +1,18 @@
+#if __MOBILE__
+using CurrentPlatform = Xamarin.Forms.PlatformConfiguration.iOS;
 namespace Xamarin.Forms.Platform.iOS
+#else
+using CurrentPlatform = Xamarin.Forms.PlatformConfiguration.macOS;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public static class PlatformConfigurationExtensions
        {
-               public static IPlatformElementConfiguration<PlatformConfiguration.iOS, T> OnThisPlatform<T>(this T element) 
+               public static IPlatformElementConfiguration<CurrentPlatform, T> OnThisPlatform<T>(this T element)
                        where T : Element, IElementConfiguration<T>
                {
-                       return (element).On<PlatformConfiguration.iOS>();
+                       return (element).On<CurrentPlatform>();
                }
        }
 }
\ No newline at end of file
index 4c3eed9..8248af1 100644 (file)
@@ -2,10 +2,15 @@ using System;
 using System.Collections.Generic;
 using System.Drawing;
 using System.Linq;
-using UIKit;
 using static System.String;
-
+#if __MOBILE__
+using UIKit;
 namespace Xamarin.Forms.Platform.iOS
+#else
+using UIView = AppKit.NSView;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public static class UIViewExtensions
        {
@@ -16,15 +21,28 @@ namespace Xamarin.Forms.Platform.iOS
                        return self.Subviews.Concat(self.Subviews.SelectMany(s => s.Descendants()));
                }
 
-               public static SizeRequest GetSizeRequest(this UIView self, double widthConstraint, double heightConstraint, double minimumWidth = -1, double minimumHeight = -1)
+               public static SizeRequest GetSizeRequest(this UIView self, double widthConstraint, double heightConstraint,
+                       double minimumWidth = -1, double minimumHeight = -1)
                {
-                       var s = self.SizeThatFits(new SizeF((float)widthConstraint, (float)heightConstraint));
-                       var request = new Size(s.Width == float.PositiveInfinity ? double.PositiveInfinity : s.Width, s.Height == float.PositiveInfinity ? double.PositiveInfinity : s.Height);
-                       var minimum = new Size(minimumWidth < 0 ? request.Width : minimumWidth, minimumHeight < 0 ? request.Height : minimumHeight);
+                       CoreGraphics.CGSize s;
+#if __MOBILE__
+                       s = self.SizeThatFits(new SizeF((float)widthConstraint, (float)heightConstraint));
+#else
+                       var control = self as AppKit.NSControl;
+                       if (control != null)
+                               s = control.SizeThatFits(new CoreGraphics.CGSize(widthConstraint, heightConstraint));
+                       else
+                               s = self.FittingSize;
+#endif
+                       var request = new Size(s.Width == float.PositiveInfinity ? double.PositiveInfinity : s.Width,
+                               s.Height == float.PositiveInfinity ? double.PositiveInfinity : s.Height);
+                       var minimum = new Size(minimumWidth < 0 ? request.Width : minimumWidth,
+                               minimumHeight < 0 ? request.Height : minimumHeight);
                        return new SizeRequest(request, minimum);
                }
 
-               public static void SetBinding(this UIView view, string propertyName, BindingBase bindingBase, string updateSourceEventName = null)
+               public static void SetBinding(this UIView view, string propertyName, BindingBase bindingBase,
+                       string updateSourceEventName = null)
                {
                        var binding = bindingBase as Binding;
                        //This will allow setting bindings from Xaml by reusing the MarkupExtension
@@ -51,6 +69,7 @@ namespace Xamarin.Forms.Platform.iOS
                                                view.AddObserver(nativePropertyListener, key, Foundation.NSKeyValueObservingOptions.New, IntPtr.Zero);
                                        }
                                }
+#if __MOBILE__
                                catch (Foundation.MonoTouchException ex)
                                {
                                        nativePropertyListener = null;
@@ -61,7 +80,12 @@ namespace Xamarin.Forms.Platform.iOS
                                        }
                                        throw ex;
                                }
-
+#else
+                               catch (Exception ex)
+                               {
+                                       throw ex;
+                               }
+#endif
                        }
 
                        NativeBindingHelpers.SetBinding(view, propertyName, bindingBase, nativePropertyListener);
@@ -77,7 +101,8 @@ namespace Xamarin.Forms.Platform.iOS
                        NativeBindingHelpers.SetValue(target, targetProperty, value);
                }
 
-               public static void SetBindingContext(this UIView target, object bindingContext, Func<UIView, IEnumerable<UIView>> getChildren = null)
+               public static void SetBindingContext(this UIView target, object bindingContext,
+                       Func<UIView, IEnumerable<UIView>> getChildren = null)
                {
                        NativeBindingHelpers.SetBindingContext(target, bindingContext, getChildren);
                }
@@ -107,6 +132,7 @@ namespace Xamarin.Forms.Platform.iOS
                        return null;
                }
 
+#if __MOBILE__
                internal static UIView FindFirstResponder(this UIView view)
                {
                        if (view.IsFirstResponder)
@@ -121,5 +147,6 @@ namespace Xamarin.Forms.Platform.iOS
 
                        return null;
                }
+#endif
        }
 }
\ No newline at end of file
index 2c8fc6f..d6e7e07 100644 (file)
@@ -11,11 +11,18 @@ using System.Security.Cryptography;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
-using CoreFoundation;
+using Xamarin.Forms.Internals;
 using Foundation;
+#if __MOBILE__
 using UIKit;
-using Xamarin.Forms.Internals;
 using Xamarin.Forms.Platform.iOS;
+using TNativeView = UIKit.UIView;
+#else
+using AppKit;
+using Xamarin.Forms.Platform.MacOS;
+using TNativeView = AppKit.NSView;
+
+#endif
 
 namespace Xamarin.Forms
 {
@@ -24,7 +31,11 @@ namespace Xamarin.Forms
                //Preserve GetCallingAssembly
                static readonly bool nevertrue = false;
 
+               public static bool IsInitialized { get; private set; }
+
+#if __MOBILE__
                static bool? s_isiOS9OrNewer;
+#endif
 
                static Forms()
                {
@@ -32,8 +43,7 @@ namespace Xamarin.Forms
                                Assembly.GetCallingAssembly();
                }
 
-               public static bool IsInitialized { get; private set; }
-
+#if __MOBILE__
                internal static bool IsiOS9OrNewer
                {
                        get
@@ -43,6 +53,7 @@ namespace Xamarin.Forms
                                return s_isiOS9OrNewer.Value;
                        }
                }
+#endif
 
                public static void Init()
                {
@@ -53,19 +64,22 @@ namespace Xamarin.Forms
 
                        Log.Listeners.Add(new DelegateLogListener((c, m) => Trace.WriteLine(m, c)));
 
+#if __MOBILE__
+                       Device.Idiom = UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad ? TargetIdiom.Tablet : TargetIdiom.Phone;
+#else
+                       Device.Idiom = TargetIdiom.Desktop;
+#endif
                        Device.PlatformServices = new IOSPlatformServices();
                        Device.Info = new IOSDeviceInfo();
 
-                       Registrar.RegisterAll(new[] { typeof(ExportRendererAttribute), typeof(ExportCellAttribute), typeof(ExportImageSourceHandlerAttribute) });
-
-                       Device.Idiom = UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad ? TargetIdiom.Tablet : TargetIdiom.Phone;
-
+                       Registrar.RegisterAll(new[]
+                               { typeof(ExportRendererAttribute), typeof(ExportCellAttribute), typeof(ExportImageSourceHandlerAttribute) });
                        ExpressionSearch.Default = new iOSExpressionSearch();
                }
 
                public static event EventHandler<ViewInitializedEventArgs> ViewInitialized;
 
-               internal static void SendViewInitialized(this VisualElement self, UIView nativeView)
+               internal static void SendViewInitialized(this VisualElement self, TNativeView nativeView)
                {
                        ViewInitialized?.Invoke(self, new ViewInitializedEventArgs { View = self, NativeView = nativeView });
                }
@@ -99,16 +113,22 @@ namespace Xamarin.Forms
 
                internal class IOSDeviceInfo : DeviceInfo
                {
+#if __MOBILE__
                        readonly NSObject _notification;
+#endif
                        readonly Size _scaledScreenSize;
                        readonly double _scalingFactor;
 
                        public IOSDeviceInfo()
                        {
+#if __MOBILE__
                                _notification = UIDevice.Notifications.ObserveOrientationDidChange((sender, args) => CurrentOrientation = UIDevice.CurrentDevice.Orientation.ToDeviceOrientation());
-
                                _scalingFactor = UIScreen.MainScreen.Scale;
                                _scaledScreenSize = new Size(UIScreen.MainScreen.Bounds.Width, UIScreen.MainScreen.Bounds.Height);
+#else
+                               _scalingFactor = NSScreen.MainScreen.BackingScaleFactor;
+                               _scaledScreenSize = new Size(NSScreen.MainScreen.Frame.Width, NSScreen.MainScreen.Frame.Height);
+#endif
                                PixelScreenSize = new Size(_scaledScreenSize.Width * _scalingFactor, _scaledScreenSize.Height * _scalingFactor);
                        }
 
@@ -120,7 +140,9 @@ namespace Xamarin.Forms
 
                        protected override void Dispose(bool disposing)
                        {
+#if __MOBILE__
                                _notification.Dispose();
+#endif
                                base.Dispose(disposing);
                        }
                }
@@ -198,11 +220,19 @@ namespace Xamarin.Forms
 
                        public bool IsInvokeRequired => !NSThread.IsMain;
 
+#if __MOBILE__
                        public string RuntimePlatform => Device.iOS;
+#else
+                       public string RuntimePlatform => Device.macOS;
+#endif
 
                        public void OpenUriAction(Uri uri)
                        {
+#if __MOBILE__
                                UIApplication.SharedApplication.OpenUrl(new NSUrl(uri.AbsoluteUri));
+#else
+                               NSWorkspace.SharedWorkspace.OpenUrl(new NSUrl(uri.AbsoluteUri));
+#endif
                        }
 
                        public void StartTimer(TimeSpan interval, Func<bool> callback)
@@ -217,11 +247,11 @@ namespace Xamarin.Forms
 
                        HttpClient GetHttpClient()
                        {
-                               var proxy = CFNetwork.GetSystemProxySettings();
+                               var proxy = CoreFoundation.CFNetwork.GetSystemProxySettings();
                                var handler = new HttpClientHandler();
                                if (!string.IsNullOrEmpty(proxy.HTTPProxy))
                                {
-                                       handler.Proxy = CFNetwork.GetDefaultProxy();
+                                       handler.Proxy = CoreFoundation.CFNetwork.GetDefaultProxy();
                                        handler.UseProxy = true;
                                }
                                return new HttpClient(handler);
@@ -272,7 +302,8 @@ namespace Xamarin.Forms
 
                                public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
                                {
-                                       Stream stream = _isolatedStorageFile.OpenFile(path, (System.IO.FileMode)mode, (System.IO.FileAccess)access, (System.IO.FileShare)share);
+                                       Stream stream = _isolatedStorageFile.OpenFile(path, (System.IO.FileMode)mode, (System.IO.FileAccess)access,
+                                               (System.IO.FileShare)share);
                                        return Task.FromResult(stream);
                                }
                        }
index 9a2bf58..ffb2de2 100644 (file)
@@ -1,15 +1,24 @@
 using System;
+#if __MOBILE__
 using UIKit;
+using NativeView = UIKit.UIView;
+using NativeViewController = UIKit.UIViewController;
 
 namespace Xamarin.Forms.Platform.iOS
+#else
+using NativeView = AppKit.NSView;
+using NativeViewController = AppKit.NSViewController;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public interface IVisualElementRenderer : IDisposable, IRegisterable
        {
                VisualElement Element { get; }
 
-               UIView NativeView { get; }
+               NativeView NativeView { get; }
 
-               UIViewController ViewController { get; }
+               NativeViewController ViewController { get; }
 
                event EventHandler<VisualElementChangedEventArgs> ElementChanged;
 
index 9e20e04..acdd652 100644 (file)
@@ -1,16 +1,23 @@
 using System;
+#if __MOBILE__
 using UIKit;
-
 [assembly: Xamarin.Forms.Dependency(typeof(Xamarin.Forms.Platform.iOS.NativeValueConverterService))]
-
 namespace Xamarin.Forms.Platform.iOS
+#else
+using UIView = AppKit.NSView;
+
+[assembly: Xamarin.Forms.Dependency(typeof(Xamarin.Forms.Platform.MacOS.NativeValueConverterService))]
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        class NativeValueConverterService : Xaml.INativeValueConverterService
        {
                public bool ConvertTo(object value, Type toType, out object nativeValue)
                {
                        nativeValue = null;
-                       if (typeof(UIView).IsInstanceOfType(value) && toType.IsAssignableFrom(typeof(View))) {
+                       if (typeof(UIView).IsInstanceOfType(value) && toType.IsAssignableFrom(typeof(View)))
+                       {
                                nativeValue = ((UIView)value).ToView();
                                return true;
                        }
index 4e6601e..e819519 100644 (file)
@@ -2,7 +2,12 @@ using System;
 using System.ComponentModel;
 using Foundation;
 
+#if __MOBILE__
 namespace Xamarin.Forms.Platform.iOS
+#else
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        class NativeViewPropertyListener : NSObject, INotifyPropertyChanged
        {
@@ -23,4 +28,4 @@ namespace Xamarin.Forms.Platform.iOS
                                base.ObserveValue(keyPath, ofObject, change, context);
                }
        }
-}
+}
\ No newline at end of file
index 31cb186..165bc0f 100644 (file)
@@ -1,10 +1,16 @@
-using UIKit;
-
+#if __MOBILE__
+using UIKit;
 namespace Xamarin.Forms.Platform.iOS
+#else
+using UIView = AppKit.NSView;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public class NativeViewWrapper : View
        {
-               public NativeViewWrapper(UIView nativeView, GetDesiredSizeDelegate getDesiredSizeDelegate = null, SizeThatFitsDelegate sizeThatFitsDelegate = null, LayoutSubviewsDelegate layoutSubViews = null)
+               public NativeViewWrapper(UIView nativeView, GetDesiredSizeDelegate getDesiredSizeDelegate = null,
+                       SizeThatFitsDelegate sizeThatFitsDelegate = null, LayoutSubviewsDelegate layoutSubViews = null)
                {
                        GetDesiredSizeDelegate = getDesiredSizeDelegate;
                        SizeThatFitsDelegate = sizeThatFitsDelegate;
index 4d1252e..cde9fe8 100644 (file)
@@ -1,8 +1,13 @@
 using CoreGraphics;
-using UIKit;
 using Xamarin.Forms.Internals;
-
+#if __MOBILE__
+using UIKit;
 namespace Xamarin.Forms.Platform.iOS
+#else
+using UIView = AppKit.NSView;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public class NativeViewWrapperRenderer : ViewRenderer<NativeViewWrapper, UIView>
        {
@@ -19,6 +24,7 @@ namespace Xamarin.Forms.Platform.iOS
                        return result ?? base.GetDesiredSize(widthConstraint, heightConstraint);
                }
 
+#if __MOBILE__
                public override void LayoutSubviews()
                {
                        if (Element?.LayoutSubViews == null)
@@ -50,6 +56,26 @@ namespace Xamarin.Forms.Platform.iOS
                        // if it returns null, fall back to the default implementation
                        return result ?? base.SizeThatFits(size);
                }
+#else
+               public override void Layout()
+               {
+                       if (Element?.LayoutSubViews == null)
+                       {
+                               ((IVisualElementController)Element)?.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+                               base.Layout();
+                               return;
+                       }
+
+                       // The user has specified a different implementation of LayoutSubviews
+                       var handled = Element.LayoutSubViews();
+
+                       if (!handled)
+                       {
+                               // If the delegate wasn't able to handle the request, fall back to the default implementation
+                               base.Layout();
+                       }
+               }
+#endif
 
                protected override void OnElementChanged(ElementChangedEventArgs<NativeViewWrapper> e)
                {
index 8fb4f97..6a1ca49 100644 (file)
@@ -1,6 +1,11 @@
-using UIKit;
-
+#if __MOBILE__
+using UIKit;
 namespace Xamarin.Forms.Platform.iOS
+#else
+using UIView = AppKit.NSView;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public abstract class PlatformEffect : PlatformEffect<UIView, UIView>
        {
index babfa7b..38fe3c0 100644 (file)
@@ -1,11 +1,17 @@
 using System;
 using System.Collections.Generic;
 
+#if __MOBILE__
 namespace Xamarin.Forms.Platform.iOS
+#else
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public sealed class RendererPool
        {
-               readonly Dictionary<Type, Stack<IVisualElementRenderer>> _freeRenderers = new Dictionary<Type, Stack<IVisualElementRenderer>>();
+               readonly Dictionary<Type, Stack<IVisualElementRenderer>> _freeRenderers =
+                       new Dictionary<Type, Stack<IVisualElementRenderer>>();
 
                readonly VisualElement _oldElement;
 
index cbc2e62..ba8acbe 100644 (file)
@@ -1,15 +1,26 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Linq;
+using Xamarin.Forms;
+#if __MOBILE__
 using UIKit;
-
 namespace Xamarin.Forms.Platform.iOS
+#else
+using AppKit;
+using UIFont = AppKit.NSFont;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public static class FontExtensions
        {
                static readonly Dictionary<ToUIFontKey, UIFont> ToUiFont = new Dictionary<ToUIFontKey, UIFont>();
-
+#if __MOBILE__
                public static UIFont ToUIFont(this Font self)
+
+#else
+               public static UIFont ToNSFont(this Font self)
+#endif
                {
                        var size = (float)self.FontSize;
                        if (self.UseNamedSize)
@@ -41,6 +52,7 @@ namespace Xamarin.Forms.Platform.iOS
                        {
                                try
                                {
+#if __MOBILE__
                                        if (UIFont.FamilyNames.Contains(self.FontFamily))
                                        {
                                                var descriptor = new UIFontDescriptor().CreateWithFamily(self.FontFamily);
@@ -59,6 +71,25 @@ namespace Xamarin.Forms.Platform.iOS
                                        }
 
                                        return UIFont.FromName(self.FontFamily, size);
+
+#else
+
+                                       var descriptor = new NSFontDescriptor().FontDescriptorWithFamily(self.FontFamily);
+
+                                       if (bold || italic)
+                                       {
+                                               var traits = (NSFontSymbolicTraits)0;
+                                               if (bold)
+                                                       traits = traits | NSFontSymbolicTraits.BoldTrait;
+                                               if (italic)
+                                                       traits = traits | NSFontSymbolicTraits.ItalicTrait;
+
+                                               descriptor = descriptor.FontDescriptorWithSymbolicTraits(traits);
+                                               return NSFont.FromDescription(descriptor, size);
+                                       }
+
+                                       return NSFont.FromFontName(self.FontFamily, size);
+#endif
                                }
                                catch
                                {
@@ -68,28 +99,54 @@ namespace Xamarin.Forms.Platform.iOS
                        if (bold && italic)
                        {
                                var defaultFont = UIFont.SystemFontOfSize(size);
+#if __MOBILE__
                                var descriptor = defaultFont.FontDescriptor.CreateWithTraits(UIFontDescriptorSymbolicTraits.Bold | UIFontDescriptorSymbolicTraits.Italic);
                                return UIFont.FromDescriptor(descriptor, 0);
                        }
-                       if (bold)
-                               return UIFont.BoldSystemFontOfSize(size);
                        if (italic)
                                return UIFont.ItalicSystemFontOfSize(size);
+#else
+                               var descriptor = defaultFont.FontDescriptor.FontDescriptorWithSymbolicTraits(
+                                       NSFontSymbolicTraits.BoldTrait |
+                                       NSFontSymbolicTraits.ItalicTrait);
+
+                               return NSFont.FromDescription(descriptor, 0);
+                       }
+                       if (italic)
+                       {
+                               Debug.WriteLine("Italic font requested, passing regular one");
+                               return NSFont.UserFontOfSize(size);
+                       }
+#endif
+
+                       if (bold)
+                               return UIFont.BoldSystemFontOfSize(size);
+
                        return UIFont.SystemFontOfSize(size);
                }
 
                internal static bool IsDefault(this Span self)
                {
-                       return self.FontFamily == null && self.FontSize == Device.GetNamedSize(NamedSize.Default, typeof(Label), true) && self.FontAttributes == FontAttributes.None;
+                       return self.FontFamily == null && self.FontSize == Device.GetNamedSize(NamedSize.Default, typeof(Label), true) &&
+                                       self.FontAttributes == FontAttributes.None;
                }
 
+#if __MOBILE__
                internal static UIFont ToUIFont(this Label label)
+#else
+               internal static UIFont ToNSFont(this Label label)
+#endif
                {
                        var values = label.GetValues(Label.FontFamilyProperty, Label.FontSizeProperty, Label.FontAttributesProperty);
-                       return ToUIFont((string)values[0], (float)(double)values[1], (FontAttributes)values[2]) ?? UIFont.SystemFontOfSize(UIFont.LabelFontSize);
+                       return ToUIFont((string)values[0], (float)(double)values[1], (FontAttributes)values[2]) ??
+                                       UIFont.SystemFontOfSize(UIFont.LabelFontSize);
                }
 
+#if __MOBILE__
                internal static UIFont ToUIFont(this IFontElement element)
+#else
+               internal static NSFont ToNSFont(this IFontElement element)
+#endif
                {
                        return ToUIFont(element.FontFamily, (float)element.FontSize, element.FontAttributes);
                }
@@ -104,6 +161,7 @@ namespace Xamarin.Forms.Platform.iOS
                                try
                                {
                                        UIFont result;
+#if __MOBILE__
                                        if (UIFont.FamilyNames.Contains(family))
                                        {
                                                var descriptor = new UIFontDescriptor().CreateWithFamily(family);
@@ -124,6 +182,26 @@ namespace Xamarin.Forms.Platform.iOS
                                        }
 
                                        result = UIFont.FromName(family, size);
+#else
+
+                                       var descriptor = new NSFontDescriptor().FontDescriptorWithFamily(family);
+
+                                       if (bold || italic)
+                                       {
+                                               var traits = (NSFontSymbolicTraits)0;
+                                               if (bold)
+                                                       traits = traits | NSFontSymbolicTraits.BoldTrait;
+                                               if (italic)
+                                                       traits = traits | NSFontSymbolicTraits.ItalicTrait;
+
+                                               descriptor = descriptor.FontDescriptorWithSymbolicTraits(traits);
+                                               result = NSFont.FromDescription(descriptor, size);
+                                               if (result != null)
+                                                       return result;
+                                       }
+
+                                       result = NSFont.FromFontName(family, size);
+#endif
                                        if (result != null)
                                                return result;
                                }
@@ -137,13 +215,27 @@ namespace Xamarin.Forms.Platform.iOS
                        {
                                var defaultFont = UIFont.SystemFontOfSize(size);
 
+#if __MOBILE__
                                var descriptor = defaultFont.FontDescriptor.CreateWithTraits(UIFontDescriptorSymbolicTraits.Bold | UIFontDescriptorSymbolicTraits.Italic);
                                return UIFont.FromDescriptor(descriptor, 0);
                        }
-                       if (bold)
-                               return UIFont.BoldSystemFontOfSize(size);
                        if (italic)
                                return UIFont.ItalicSystemFontOfSize(size);
+#else
+                               var descriptor = defaultFont.FontDescriptor.FontDescriptorWithSymbolicTraits(
+                                       NSFontSymbolicTraits.BoldTrait |
+                                       NSFontSymbolicTraits.ItalicTrait);
+
+                               return NSFont.FromDescription(descriptor, 0);
+                       }
+                       if (italic)
+                       {
+                               Debug.WriteLine("Italic font requested, passing regular one");
+                               return NSFont.UserFontOfSize(size);
+                       }
+#endif
+                       if (bold)
+                               return UIFont.BoldSystemFontOfSize(size);
 
                        return UIFont.SystemFontOfSize(size);
                }
index dce86b9..825a20b 100644 (file)
@@ -1,7 +1,13 @@
 using Foundation;
+#if __MOBILE__
 using UIKit;
-
 namespace Xamarin.Forms.Platform.iOS
+#else
+using AppKit;
+using UIColor = AppKit.NSColor;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public static class FormattedStringExtensions
        {
@@ -19,10 +25,16 @@ namespace Xamarin.Forms.Platform.iOS
                        if (fgcolor.IsDefault)
                                fgcolor = Color.Black; // as defined by apple docs              
 
+#if __MOBILE__
                        return new NSAttributedString(span.Text, font == Font.Default ? null : font.ToUIFont(), fgcolor.ToUIColor(), span.BackgroundColor.ToUIColor());
+#else
+                       return new NSAttributedString(span.Text, font == Font.Default ? null : font.ToNSFont(), fgcolor.ToNSColor(),
+                               span.BackgroundColor.ToNSColor());
+#endif
                }
 
-               public static NSAttributedString ToAttributed(this FormattedString formattedString, Font defaultFont, Color defaultForegroundColor)
+               public static NSAttributedString ToAttributed(this FormattedString formattedString, Font defaultFont,
+                       Color defaultForegroundColor)
                {
                        if (formattedString == null)
                                return null;
@@ -43,6 +55,7 @@ namespace Xamarin.Forms.Platform.iOS
                        if (span == null)
                                return null;
 
+#if __MOBILE__
                        UIFont targetFont;
                        if (span.IsDefault())
                                targetFont = ((IFontElement)owner).ToUIFont();
@@ -56,9 +69,25 @@ namespace Xamarin.Forms.Platform.iOS
                                fgcolor = Color.Black; // as defined by apple docs
 
                        return new NSAttributedString(span.Text, targetFont, fgcolor.ToUIColor(), span.BackgroundColor.ToUIColor());
+#else
+                       NSFont targetFont;
+                       if (span.IsDefault())
+                               targetFont = ((IFontElement)owner).ToNSFont();
+                       else
+                               targetFont = span.ToNSFont();
+
+                       var fgcolor = span.ForegroundColor;
+                       if (fgcolor.IsDefault)
+                               fgcolor = defaultForegroundColor;
+                       if (fgcolor.IsDefault)
+                               fgcolor = Color.Black; // as defined by apple docs
+
+                       return new NSAttributedString(span.Text, targetFont, fgcolor.ToNSColor(), span.BackgroundColor.ToNSColor());
+#endif
                }
 
-               internal static NSAttributedString ToAttributed(this FormattedString formattedString, Element owner, Color defaultForegroundColor)
+               internal static NSAttributedString ToAttributed(this FormattedString formattedString, Element owner,
+                       Color defaultForegroundColor)
                {
                        if (formattedString == null)
                                return null;
index e4967c1..445abf3 100644 (file)
@@ -1,12 +1,21 @@
 using System;
 using System.ComponentModel;
-using UIKit;
 using RectangleF = CoreGraphics.CGRect;
 using SizeF = CoreGraphics.CGSize;
 
+#if __MOBILE__
+using UIKit;
+using NativeLabel = UIKit.UILabel;
+
 namespace Xamarin.Forms.Platform.iOS
+#else
+using AppKit;
+using NativeLabel = AppKit.NSTextField;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
-       public class LabelRenderer : ViewRenderer<Label, UILabel>
+       public class LabelRenderer : ViewRenderer<Label, NativeLabel>
        {
                SizeRequest _perfectSize;
 
@@ -51,9 +60,16 @@ namespace Xamarin.Forms.Platform.iOS
                        return result;
                }
 
+#if __MOBILE__
                public override void LayoutSubviews()
                {
                        base.LayoutSubviews();
+#else
+               public override void Layout()
+               {
+                       base.Layout();
+#endif
+
                        if (Control == null)
                                return;
 
@@ -85,7 +101,12 @@ namespace Xamarin.Forms.Platform.iOS
                        {
                                if (Control == null)
                                {
-                                       SetNativeControl(new UILabel(RectangleF.Empty) { BackgroundColor = UIColor.Clear });
+                                       SetNativeControl(new NativeLabel(RectangleF.Empty));
+#if !__MOBILE__
+                                       Control.Editable = false;
+                                       Control.Bezeled = false;
+                                       Control.DrawsBackground = false;
+#endif
                                }
 
                                UpdateText();
@@ -103,7 +124,7 @@ namespace Xamarin.Forms.Platform.iOS
                        if (e.PropertyName == Label.HorizontalTextAlignmentProperty.PropertyName)
                                UpdateAlignment();
                        else if (e.PropertyName == Label.VerticalTextAlignmentProperty.PropertyName)
-                               LayoutSubviews();
+                               UpdateLayout();
                        else if (e.PropertyName == Label.TextColorProperty.PropertyName)
                                UpdateText();
                        else if (e.PropertyName == Label.FontProperty.PropertyName)
@@ -118,21 +139,33 @@ namespace Xamarin.Forms.Platform.iOS
 
                protected override void SetBackgroundColor(Color color)
                {
+#if __MOBILE__
                        if (color == Color.Default)
                                BackgroundColor = UIColor.Clear;
                        else
                                BackgroundColor = color.ToUIColor();
+#else
+                       if (color == Color.Default)
+                               Layer.BackgroundColor = NSColor.Clear.CGColor;
+                       else
+                               Layer.BackgroundColor = color.ToCGColor();
+#endif
+
                }
 
                void UpdateAlignment()
                {
+#if __MOBILE__
                        Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
+#else
+                       Control.Alignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
+#endif
                }
 
                void UpdateLineBreakMode()
                {
                        _perfectSizeValid = false;
-
+#if __MOBILE__
                        switch (Element.LineBreakMode)
                        {
                                case LineBreakMode.NoWrap:
@@ -160,6 +193,35 @@ namespace Xamarin.Forms.Platform.iOS
                                        Control.Lines = 1;
                                        break;
                        }
+#else
+                       switch (Element.LineBreakMode)
+                       {
+                               case LineBreakMode.NoWrap:
+                                       Control.LineBreakMode = NSLineBreakMode.Clipping;
+                                       Control.MaximumNumberOfLines = 1;
+                                       break;
+                               case LineBreakMode.WordWrap:
+                                       Control.LineBreakMode = NSLineBreakMode.ByWordWrapping;
+                                       Control.MaximumNumberOfLines = 0;
+                                       break;
+                               case LineBreakMode.CharacterWrap:
+                                       Control.LineBreakMode = NSLineBreakMode.CharWrapping;
+                                       Control.MaximumNumberOfLines = 0;
+                                       break;
+                               case LineBreakMode.HeadTruncation:
+                                       Control.LineBreakMode = NSLineBreakMode.TruncatingHead;
+                                       Control.MaximumNumberOfLines = 1;
+                                       break;
+                               case LineBreakMode.MiddleTruncation:
+                                       Control.LineBreakMode = NSLineBreakMode.TruncatingMiddle;
+                                       Control.MaximumNumberOfLines = 1;
+                                       break;
+                               case LineBreakMode.TailTruncation:
+                                       Control.LineBreakMode = NSLineBreakMode.TruncatingTail;
+                                       Control.MaximumNumberOfLines = 1;
+                                       break;
+                       }
+#endif
                }
 
                void UpdateText()
@@ -167,9 +229,12 @@ namespace Xamarin.Forms.Platform.iOS
                        _perfectSizeValid = false;
 
                        var values = Element.GetValues(Label.FormattedTextProperty, Label.TextProperty, Label.TextColorProperty);
-                       var formatted = (FormattedString)values[0];
+                       var formatted = values[0] as FormattedString;
                        if (formatted != null)
+                       {
+#if __MOBILE__
                                Control.AttributedText = formatted.ToAttributed(Element, (Color)values[2]);
+                       }
                        else
                        {
                                Control.Text = (string)values[1];
@@ -177,8 +242,28 @@ namespace Xamarin.Forms.Platform.iOS
                                Control.Font = Element.ToUIFont();
                                Control.TextColor = ((Color)values[2]).ToUIColor(ColorExtensions.Black);
                        }
+#else
+                               Control.AttributedStringValue = formatted.ToAttributed(Element, (Color)values[2]);
+                       }
+                       else
+                       {
+                               Control.StringValue = (string)values[1] ?? "";
+                               // default value of color documented to be black in iOS docs
+                               Control.Font = Element.ToNSFont();
+                               Control.TextColor = ((Color)values[2]).ToNSColor(ColorExtensions.Black);
+                       }
+#endif
+
+                       UpdateLayout();
+               }
 
+               void UpdateLayout()
+               {
+#if __MOBILE__
                        LayoutSubviews();
+#else
+                       Layout();
+#endif
                }
        }
 }
\ No newline at end of file
index 3e5655c..f02655a 100644 (file)
@@ -1,6 +1,11 @@
+#if __MOBILE__
 using UIKit;
 
 namespace Xamarin.Forms.Platform.iOS
+#else
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        internal class ResourcesProvider : ISystemResourcesProvider
        {
@@ -8,7 +13,9 @@ namespace Xamarin.Forms.Platform.iOS
 
                public ResourcesProvider()
                {
+#if __MOBILE__
                        UIApplication.Notifications.ObserveContentSizeCategoryChanged((sender, args) => UpdateStyles());
+#endif
                }
 
                public IResourceDictionary GetSystemResources()
@@ -19,6 +26,7 @@ namespace Xamarin.Forms.Platform.iOS
                        return _dictionary;
                }
 
+#if __MOBILE__
                Style GenerateListItemDetailTextStyle()
                {
                        var font = new UITableViewCell(UITableViewCellStyle.Subtitle, "Foobar").DetailTextLabel.Font;
@@ -44,7 +52,7 @@ namespace Xamarin.Forms.Platform.iOS
 
                void UpdateStyles()
                {
-                       
+
                        _dictionary[Device.Styles.TitleStyleKey] = GenerateStyle(UIFont.PreferredHeadline);
                        _dictionary[Device.Styles.SubtitleStyleKey] = GenerateStyle(UIFont.PreferredSubheadline);
                        _dictionary[Device.Styles.BodyStyleKey] = GenerateStyle(UIFont.PreferredBody);
@@ -53,5 +61,11 @@ namespace Xamarin.Forms.Platform.iOS
                        _dictionary[Device.Styles.ListItemTextStyleKey] = GenerateListItemTextStyle();
                        _dictionary[Device.Styles.ListItemDetailTextStyleKey] = GenerateListItemDetailTextStyle();
                }
+#else
+               void UpdateStyles()
+               {
+               }
+#endif
+
        }
 }
\ No newline at end of file
index 7304116..9ee0da8 100644 (file)
@@ -1,11 +1,16 @@
 using System;
-using UIKit;
+#if __MOBILE__
+using TNativeView = UIKit.UIView;
+#else
+using TNativeView = AppKit.NSView;
+
+#endif
 
 namespace Xamarin.Forms
 {
        public class ViewInitializedEventArgs : EventArgs
        {
-               public UIView NativeView { get; internal set; }
+               public TNativeView NativeView { get; internal set; }
 
                public VisualElement View { get; internal set; }
        }
index 6ecc307..01f8e06 100644 (file)
@@ -1,21 +1,34 @@
 using System;
 using System.ComponentModel;
-using UIKit;
+
 using RectangleF = CoreGraphics.CGRect;
 using SizeF = CoreGraphics.CGSize;
 
+#if __MOBILE__
+using UIKit;
+using NativeView = UIKit.UIView;
+using NativeColor = UIKit.UIColor;
+using NativeControl = UIKit.UIControl;
+
 namespace Xamarin.Forms.Platform.iOS
+#else
+using NativeView = AppKit.NSView;
+using NativeColor = CoreGraphics.CGColor;
+using NativeControl = AppKit.NSControl;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
-       public abstract class ViewRenderer : ViewRenderer<View, UIView>
+       public abstract class ViewRenderer : ViewRenderer<View, NativeView>
        {
        }
 
-       public abstract class ViewRenderer<TView, TNativeView> : VisualElementRenderer<TView> where TView : View where TNativeView : UIView
+       public abstract class ViewRenderer<TView, TNativeView> : VisualElementRenderer<TView> where TView : View where TNativeView : NativeView
        {
-               UIColor _defaultColor;
+               NativeColor _defaultColor;
 
                public TNativeView Control { get; private set; }
-
+#if __MOBILE__
                public override void LayoutSubviews()
                {
                        base.LayoutSubviews();
@@ -30,6 +43,19 @@ namespace Xamarin.Forms.Platform.iOS
 
                        return Control.SizeThatFits(size);
                }
+#else
+               public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+               {
+                       return (Control ?? NativeView).GetSizeRequest(widthConstraint, heightConstraint);
+               }
+
+               public override void Layout()
+               {
+                       if (Control != null)
+                               Control.Frame = new RectangleF(0, 0, (nfloat)Element.Width, (nfloat)Element.Height);
+                       base.Layout();
+               }
+#endif
 
                /// <summary>
                /// Determines whether the native control is disposed of when this renderer is disposed
@@ -100,16 +126,24 @@ namespace Xamarin.Forms.Platform.iOS
                {
                        if (Control == null)
                                return;
-
+#if __MOBILE__
                        if (color == Color.Default)
                                Control.BackgroundColor = _defaultColor;
                        else
                                Control.BackgroundColor = color.ToUIColor();
+#else
+                       Control.Layer.BackgroundColor = color == Color.Default ? _defaultColor : color.ToCGColor();
+#endif
                }
 
                protected void SetNativeControl(TNativeView uiview)
                {
+#if __MOBILE__
                        _defaultColor = uiview.BackgroundColor;
+#else
+                       uiview.WantsLayer = true;
+                       _defaultColor = uiview.Layer.BackgroundColor;
+#endif
                        Control = uiview;
 
                        if (Element.BackgroundColor != Color.Default)
@@ -120,17 +154,19 @@ namespace Xamarin.Forms.Platform.iOS
                        AddSubview(uiview);
                }
 
-               internal override void SendVisualElementInitialized(VisualElement element, UIView nativeView)
+#if __MOBILE__
+               internal override void SendVisualElementInitialized(VisualElement element, NativeView nativeView)
                {
                        base.SendVisualElementInitialized(element, Control);
                }
+#endif
 
                void UpdateIsEnabled()
                {
                        if (Element == null || Control == null)
                                return;
 
-                       var uiControl = Control as UIControl;
+                       var uiControl = Control as NativeControl;
                        if (uiControl == null)
                                return;
                        uiControl.Enabled = Element.IsEnabled;
index 1670176..fa91cf6 100644 (file)
@@ -1,6 +1,11 @@
 using System;
 
+#if __MOBILE__
 namespace Xamarin.Forms.Platform.iOS
+#else
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public class VisualElementPackager : IDisposable
        {
@@ -13,7 +18,7 @@ namespace Xamarin.Forms.Platform.iOS
                public VisualElementPackager(IVisualElementRenderer renderer)
                {
                        if (renderer == null)
-                               throw new ArgumentNullException("renderer");
+                               throw new ArgumentNullException(nameof(renderer));
 
                        Renderer = renderer;
                        renderer.ElementChanged += OnRendererElementChanged;
@@ -100,8 +105,9 @@ namespace Xamarin.Forms.Platform.iOS
                                        continue;
 
                                var nativeControl = childRenderer.NativeView;
-
+#if __MOBILE__
                                Renderer.NativeView.BringSubviewToFront(nativeControl);
+#endif
                                nativeControl.Layer.ZPosition = z * 1000;
                        }
                }
index 155f8b6..0e7af2a 100644 (file)
@@ -1,13 +1,25 @@
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
-using UIKit;
-using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
 using RectangleF = CoreGraphics.CGRect;
 using SizeF = CoreGraphics.CGSize;
 
+#if __MOBILE__
+using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
+using UIKit;
+using NativeView = UIKit.UIView;
+using NativeViewController = UIKit.UIViewController;
+using NativeColor = UIKit.UIColor;
 
 namespace Xamarin.Forms.Platform.iOS
+#else
+using AppKit;
+using NativeView = AppKit.NSView;
+using NativeViewController = AppKit.NSViewController;
+using NativeColor = AppKit.NSColor;
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        [Flags]
        public enum VisualElementRendererFlags
@@ -17,9 +29,9 @@ namespace Xamarin.Forms.Platform.iOS
                AutoPackage = 1 << 2
        }
 
-       public class VisualElementRenderer<TElement> : UIView, IVisualElementRenderer, IEffectControlProvider where TElement : VisualElement
+       public class VisualElementRenderer<TElement> : NativeView, IVisualElementRenderer, IEffectControlProvider where TElement : VisualElement
        {
-               readonly UIColor _defaultColor = UIColor.Clear;
+               readonly NativeColor _defaultColor = NativeColor.Clear;
 
                readonly List<EventHandler<VisualElementChangedEventArgs>> _elementChangedHandlers = new List<EventHandler<VisualElementChangedEventArgs>>();
 
@@ -31,21 +43,30 @@ namespace Xamarin.Forms.Platform.iOS
                VisualElementPackager _packager;
                VisualElementTracker _tracker;
 
+#if __MOBILE__
                UIVisualEffectView _blur;
                BlurEffectStyle _previousBlur;
+#endif
 
                protected VisualElementRenderer() : base(RectangleF.Empty)
                {
                        _propertyChangedHandler = OnElementPropertyChanged;
-                       BackgroundColor = UIColor.Clear;
+#if __MOBILE__
+                       BackgroundColor = _defaultColor;
+#else
+                       WantsLayer = true;
+                       Layer.BackgroundColor = _defaultColor.CGColor;
+#endif
                }
 
+#if __MOBILE__
                // prevent possible crashes in overrides
-               public sealed override UIColor BackgroundColor
+               public sealed override NativeColor BackgroundColor
                {
                        get { return base.BackgroundColor; }
                        set { base.BackgroundColor = value; }
                }
+#endif
 
                public TElement Element { get; private set; }
 
@@ -73,7 +94,7 @@ namespace Xamarin.Forms.Platform.iOS
                        }
                }
 
-               public static void RegisterEffect(Effect effect, UIView container, UIView control = null)
+               public static void RegisterEffect(Effect effect, NativeView container, NativeView control = null)
                {
                        var platformEffect = effect as PlatformEffect;
                        if (platformEffect == null)
@@ -106,10 +127,7 @@ namespace Xamarin.Forms.Platform.iOS
                        return NativeView.GetSizeRequest(widthConstraint, heightConstraint);
                }
 
-               public UIView NativeView
-               {
-                       get { return this; }
-               }
+               public NativeView NativeView => this;
 
                void IVisualElementRenderer.SetElement(VisualElement element)
                {
@@ -118,13 +136,10 @@ namespace Xamarin.Forms.Platform.iOS
 
                public void SetElementSize(Size size)
                {
-                       Layout.LayoutChildIntoBoundingRegion(Element, new Rectangle(Element.X, Element.Y, size.Width, size.Height));
+                       Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(Element, new Rectangle(Element.X, Element.Y, size.Width, size.Height));
                }
 
-               public virtual UIViewController ViewController
-               {
-                       get { return null; }
-               }
+               public virtual NativeViewController ViewController => null;
 
                public event EventHandler<ElementChangedEventArgs<TElement>> ElementChanged;
 
@@ -175,6 +190,7 @@ namespace Xamarin.Forms.Platform.iOS
                                SetAutomationId(Element.AutomationId);
                }
 
+#if __MOBILE__
                public override SizeF SizeThatFits(SizeF size)
                {
                        return new SizeF(0, 0);
@@ -190,6 +206,16 @@ namespace Xamarin.Forms.Platform.iOS
                                        Superview.Add(_blur);
                        }
                }
+#else
+               public override void MouseDown(NSEvent theEvent)
+               {
+                       bool inViewCell = IsOnViewCell(Element);
+
+                       if (Element.InputTransparent || inViewCell)
+                               base.MouseDown(theEvent);
+               }
+#endif
+
                protected override void Dispose(bool disposing)
                {
                        if ((_flags & VisualElementRendererFlags.Disposed) != 0)
@@ -227,22 +253,23 @@ namespace Xamarin.Forms.Platform.iOS
                        for (var i = 0; i < _elementChangedHandlers.Count; i++)
                                _elementChangedHandlers[i](this, args);
 
-                       var changed = ElementChanged;
-                       if (changed != null)
-                               changed(this, e);
-
+                       ElementChanged?.Invoke(this, e);
+#if __MOBILE__
                        if (e.NewElement != null)
                                SetBlur((BlurEffectStyle)e.NewElement.GetValue(PlatformConfiguration.iOSSpecific.VisualElement.BlurEffectProperty));
+#endif
                }
 
                protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
                {
                        if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
                                SetBackgroundColor(Element.BackgroundColor);
-                       else if (e.PropertyName == Layout.IsClippedToBoundsProperty.PropertyName)
+                       else if (e.PropertyName == Xamarin.Forms.Layout.IsClippedToBoundsProperty.PropertyName)
                                UpdateClipToBounds();
+#if __MOBILE__
                        else if (e.PropertyName == PlatformConfiguration.iOSSpecific.VisualElement.BlurEffectProperty.PropertyName)
                                SetBlur((BlurEffectStyle)Element.GetValue(PlatformConfiguration.iOSSpecific.VisualElement.BlurEffectProperty));
+#endif
                }
 
                protected virtual void OnRegisterEffect(PlatformEffect effect)
@@ -258,11 +285,20 @@ namespace Xamarin.Forms.Platform.iOS
                protected virtual void SetBackgroundColor(Color color)
                {
                        if (color == Color.Default)
+#if __MOBILE__
+                       
                                BackgroundColor = _defaultColor;
                        else
                                BackgroundColor = color.ToUIColor();
+                               
+#else
+                               Layer.BackgroundColor = _defaultColor.CGColor;
+                       else
+                               Layer.BackgroundColor = color.ToCGColor();
+#endif
                }
 
+#if __MOBILE__
                protected virtual void SetBlur(BlurEffectStyle blur)
                {
                        if (_previousBlur == blur)
@@ -300,21 +336,35 @@ namespace Xamarin.Forms.Platform.iOS
                        _blur = new UIVisualEffectView(blurEffect);
                        LayoutSubviews();
                }
+#endif
 
                protected virtual void UpdateNativeWidget()
                {
                }
 
-               internal virtual void SendVisualElementInitialized(VisualElement element, UIView nativeView)
+               internal virtual void SendVisualElementInitialized(VisualElement element, NativeView nativeView)
                {
                        element.SendViewInitialized(nativeView);
                }
 
                void UpdateClipToBounds()
                {
+#if __MOBILE__
                        var clippableLayout = Element as Layout;
                        if (clippableLayout != null)
                                ClipsToBounds = clippableLayout.IsClippedToBounds;
+#endif
+               }
+
+               static bool IsOnViewCell(Element element)
+               {
+
+                       if (element.Parent == null)
+                               return false;
+                       else if (element.Parent is ViewCell)
+                               return true;
+                       else
+                               return IsOnViewCell(element.Parent);
                }
        }
 }
\ No newline at end of file
index 5b41ca8..29840e2 100644 (file)
@@ -3,8 +3,13 @@ using System.ComponentModel;
 using System.Drawing;
 using System.Threading;
 using CoreAnimation;
+#if __MOBILE__
 
 namespace Xamarin.Forms.Platform.iOS
+#else
+
+namespace Xamarin.Forms.Platform.MacOS
+#endif
 {
        public class VisualElementTracker : IDisposable
        {
@@ -18,7 +23,9 @@ namespace Xamarin.Forms.Platform.iOS
                // Track these by hand because the calls down into iOS are too expensive
                bool _isInteractive;
                Rectangle _lastBounds;
-
+#if !__MOBILE__
+               Rectangle _lastParentBounds;
+#endif
                CALayer _layer;
                int _updateCount;
 
@@ -107,12 +114,17 @@ namespace Xamarin.Forms.Platform.iOS
                        var shouldInteract = !view.InputTransparent && view.IsEnabled;
                        if (_isInteractive != shouldInteract)
                        {
+#if __MOBILE__
                                uiview.UserInteractionEnabled = shouldInteract;
+#endif
                                _isInteractive = shouldInteract;
                        }
 
                        var boundsChanged = _lastBounds != view.Bounds;
-
+#if !__MOBILE__
+                       var viewParent = view.RealParent as VisualElement;
+                       var parentBoundsChanged = _lastParentBounds != (viewParent == null ? Rectangle.Zero : viewParent.Bounds);
+#endif
                        var thread = !boundsChanged && !caLayer.Frame.IsEmpty;
 
                        var anchorX = (float)view.AnchorX;
@@ -136,13 +148,17 @@ namespace Xamarin.Forms.Platform.iOS
                        {
                                if (updateTarget != _updateCount)
                                        return;
-
+#if __MOBILE__
                                var visualElement = view;
+#endif
                                var parent = view.RealParent;
 
                                var shouldRelayoutSublayers = false;
                                if (isVisible && caLayer.Hidden)
                                {
+#if !__MOBILE__
+                                       uiview.Hidden = false;
+#endif
                                        caLayer.Hidden = false;
                                        if (!caLayer.Frame.IsEmpty)
                                                shouldRelayoutSublayers = true;
@@ -150,6 +166,9 @@ namespace Xamarin.Forms.Platform.iOS
 
                                if (!isVisible && !caLayer.Hidden)
                                {
+#if !__MOBILE__
+                                       uiview.Hidden = true;
+#endif
                                        caLayer.Hidden = true;
                                        shouldRelayoutSublayers = true;
                                }
@@ -157,11 +176,26 @@ namespace Xamarin.Forms.Platform.iOS
                                // ripe for optimization
                                var transform = CATransform3D.Identity;
 
+#if __MOBILE__
+                               bool shouldUpdate = (!(visualElement is Page) || visualElement is ContentPage) && width > 0 && height > 0 && parent != null && boundsChanged;
+#else
+                               // We don't care if it's a page or not since bounds of the window can change
+                               // TODO: Find why it doesn't work to check if the parentsBounds changed  and remove true;
+                               parentBoundsChanged = true;
+                               bool shouldUpdate = width > 0 && height > 0 && parent != null && (boundsChanged || parentBoundsChanged);
+#endif
                                // Dont ever attempt to actually change the layout of a Page unless it is a ContentPage
                                // iOS is a really big fan of you not actually modifying the View's of the UIViewControllers
-                               if ((!(visualElement is Page) || visualElement is ContentPage) && width > 0 && height > 0 && parent != null && boundsChanged)
+                               if (shouldUpdate)
                                {
+#if __MOBILE__
                                        var target = new RectangleF(x, y, width, height);
+#else
+                                       var visualParent = parent as VisualElement;
+                                       float newY = visualParent == null ? y : Math.Max(0, (float)(visualParent.Height - y - view.Height));
+                                       var target = new RectangleF(x, newY, width, height);
+#endif
+
                                        // must reset transform prior to setting frame...
                                        caLayer.Transform = transform;
                                        uiview.Frame = target;
@@ -170,11 +204,17 @@ namespace Xamarin.Forms.Platform.iOS
                                }
                                else if (width <= 0 || height <= 0)
                                {
+                                       //TODO: FInd why it doesn't work
+#if __MOBILE__
                                        caLayer.Hidden = true;
+#endif
                                        return;
                                }
-
+#if __MOBILE__
                                caLayer.AnchorPoint = new PointF(anchorX, anchorY);
+#else
+                               caLayer.AnchorPoint = new PointF(anchorX - 0.5f, anchorY - 0.5f);
+#endif
                                caLayer.Opacity = opacity;
                                const double epsilon = 0.001;
 
@@ -209,6 +249,9 @@ namespace Xamarin.Forms.Platform.iOS
                                update();
 
                        _lastBounds = view.Bounds;
+#if !__MOBILE__
+                       _lastParentBounds = viewParent?.Bounds ?? Rectangle.Zero;
+#endif
                }
 
                void SetElement(VisualElement oldElement, VisualElement newElement)
@@ -239,14 +282,18 @@ namespace Xamarin.Forms.Platform.iOS
 
                        if (_layer == null)
                        {
+#if !__MOBILE__
+                               Renderer.NativeView.WantsLayer = true;
+#endif
                                _layer = Renderer.NativeView.Layer;
+#if __MOBILE__
                                _isInteractive = Renderer.NativeView.UserInteractionEnabled;
+#endif
                        }
 
                        OnUpdateNativeControl(_layer);
 
-                       if (NativeControlUpdated != null)
-                               NativeControlUpdated(this, EventArgs.Empty);
+                       NativeControlUpdated?.Invoke(this, EventArgs.Empty);
                }
        }
 }
\ No newline at end of file
index 12ba3e8..8eb2262 100644 (file)
@@ -143,10 +143,15 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Pages.Azure",
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PagesGallery.iOS", "PagesGallery\PagesGallery.iOS\PagesGallery.iOS.csproj", "{392156B2-760A-4EE3-A822-CABD3238A21D}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Platform.MacOS", "Xamarin.Forms.Platform.MacOS\Xamarin.Forms.Platform.MacOS.csproj", "{C0059C45-EA1E-42F3-8A0E-794BB547EC3C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Forms.Maps.MacOS", "Xamarin.Forms.Maps.MacOS\Xamarin.Forms.Maps.MacOS.csproj", "{C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}"
+EndProject
 Global
        GlobalSection(SharedMSBuildProjectFiles) = preSolution
                Xamarin.Forms.Controls.Issues\Xamarin.Forms.Controls.Issues.Shared\Xamarin.Forms.Controls.Issues.Shared.projitems*{0a39a74b-6f7a-4d41-84f2-b0ccdce899df}*SharedItemsImports = 4
                Xamarin.Forms.Controls.Issues\Xamarin.Forms.Controls.Issues.Shared\Xamarin.Forms.Controls.Issues.Shared.projitems*{0f0db9cc-ea65-429c-9363-38624bf8f49c}*SharedItemsImports = 13
+               Xamarin.Forms.Controls.Issues\Xamarin.Forms.Controls.Issues.Shared\Xamarin.Forms.Controls.Issues.Shared.projitems*{9db0cabb-24cc-4538-88ec-6e0a0fe40248}*SharedItemsImports = 4
                Xamarin.Forms.Controls.Issues\Xamarin.Forms.Controls.Issues.Shared\Xamarin.Forms.Controls.Issues.Shared.projitems*{a34ebe01-25bf-4e69-a2dc-2288dc625541}*SharedItemsImports = 4
                Xamarin.Forms.Controls.Issues\Xamarin.Forms.Controls.Issues.Shared\Xamarin.Forms.Controls.Issues.Shared.projitems*{cb9c96ce-125c-4a68-b6a1-c3ff1fbf93e1}*SharedItemsImports = 4
                docs\APIDocs.projitems*{dc1f3933-ac99-4887-8b09-e13c2b346d4f}*SharedItemsImports = 13
@@ -1494,6 +1499,62 @@ Global
                {392156B2-760A-4EE3-A822-CABD3238A21D}.Release|Templates.ActiveCfg = Release|iPhone
                {392156B2-760A-4EE3-A822-CABD3238A21D}.Release|x64.ActiveCfg = Release|iPhone
                {392156B2-760A-4EE3-A822-CABD3238A21D}.Release|x86.ActiveCfg = Release|iPhone
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|ARM.Build.0 = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|iPhone.Build.0 = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|Templates.ActiveCfg = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|Templates.Build.0 = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|x64.ActiveCfg = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|x64.Build.0 = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|x86.ActiveCfg = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Debug|x86.Build.0 = Debug|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|Any CPU.Build.0 = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|ARM.ActiveCfg = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|ARM.Build.0 = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|iPhone.ActiveCfg = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|iPhone.Build.0 = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|Templates.ActiveCfg = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|Templates.Build.0 = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|x64.ActiveCfg = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|x64.Build.0 = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|x86.ActiveCfg = Release|Any CPU
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C}.Release|x86.Build.0 = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|ARM.ActiveCfg = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|ARM.Build.0 = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|iPhone.Build.0 = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|Templates.ActiveCfg = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|Templates.Build.0 = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|x64.ActiveCfg = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|x64.Build.0 = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|x86.ActiveCfg = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Debug|x86.Build.0 = Debug|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|Any CPU.Build.0 = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|ARM.ActiveCfg = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|ARM.Build.0 = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|iPhone.ActiveCfg = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|iPhone.Build.0 = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|Templates.ActiveCfg = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|Templates.Build.0 = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|x64.ActiveCfg = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|x64.Build.0 = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|x86.ActiveCfg = Release|Any CPU
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884}.Release|x86.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -1551,5 +1612,7 @@ Global
                {42DB052E-0909-45D2-8240-187F99F393FB} = {29AC50BF-B4FB-450B-9386-0C5AD4B84226}
                {C9696465-7657-4843-872E-3C01891C4A9B} = {9AD757F5-E57A-459D-A0A7-E0675E045B84}
                {392156B2-760A-4EE3-A822-CABD3238A21D} = {80BAC3FB-357A-4D05-A050-9F234DF49C97}
+               {C0059C45-EA1E-42F3-8A0E-794BB547EC3C} = {29AC50BF-B4FB-450B-9386-0C5AD4B84226}
+               {C3C24A6D-2D0C-4053-9FCC-E54FF9CA1884} = {132FB9A4-613F-44CE-95D5-758D32D231DD}
        EndGlobalSection
 EndGlobal
diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms.PlatformConfiguration.macOSSpecific/TabbedPage.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms.PlatformConfiguration.macOSSpecific/TabbedPage.xml
new file mode 100644 (file)
index 0000000..550ca29
--- /dev/null
@@ -0,0 +1,176 @@
+<Type Name="TabbedPage" FullName="Xamarin.Forms.PlatformConfiguration.macOSSpecific.TabbedPage">
+  <TypeSignature Language="C#" Value="public static class TabbedPage" />
+  <TypeSignature Language="ILAsm" Value=".class public auto ansi abstract sealed beforefieldinit TabbedPage extends System.Object" />
+  <AssemblyInfo>
+    <AssemblyName>Xamarin.Forms.Core</AssemblyName>
+    <AssemblyVersion>2.0.0.0</AssemblyVersion>
+  </AssemblyInfo>
+  <Base>
+    <BaseTypeName>System.Object</BaseTypeName>
+  </Base>
+  <Interfaces />
+  <Docs>
+    <summary>To be added.</summary>
+    <remarks>To be added.</remarks>
+  </Docs>
+  <Members>
+    <Member MemberName="GetTabsStyle">
+      <MemberSignature Language="C#" Value="public static Xamarin.Forms.TabsStyle GetTabsStyle (Xamarin.Forms.BindableObject element);" />
+      <MemberSignature Language="ILAsm" Value=".method public static hidebysig valuetype Xamarin.Forms.TabsStyle GetTabsStyle(class Xamarin.Forms.BindableObject element) cil managed" />
+      <MemberType>Method</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.TabsStyle</ReturnType>
+      </ReturnValue>
+      <Parameters>
+        <Parameter Name="element" Type="Xamarin.Forms.BindableObject" />
+      </Parameters>
+      <Docs>
+        <param name="element">To be added.</param>
+        <summary>To be added.</summary>
+        <returns>To be added.</returns>
+        <remarks>To be added.</remarks>
+      </Docs>
+    </Member>
+    <Member MemberName="GetTabsStyle">
+      <MemberSignature Language="C#" Value="public static Xamarin.Forms.TabsStyle GetTabsStyle (this Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; config);" />
+      <MemberSignature Language="ILAsm" Value=".method public static hidebysig valuetype Xamarin.Forms.TabsStyle GetTabsStyle(class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; config) cil managed" />
+      <MemberType>Method</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.TabsStyle</ReturnType>
+      </ReturnValue>
+      <Parameters>
+        <Parameter Name="config" Type="Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;" RefType="this" />
+      </Parameters>
+      <Docs>
+        <param name="config">To be added.</param>
+        <summary>To be added.</summary>
+        <returns>To be added.</returns>
+        <remarks>To be added.</remarks>
+      </Docs>
+    </Member>
+    <Member MemberName="HideTabs">
+      <MemberSignature Language="C#" Value="public static Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; HideTabs (this Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; config);" />
+      <MemberSignature Language="ILAsm" Value=".method public static hidebysig class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; HideTabs(class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; config) cil managed" />
+      <MemberType>Method</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;</ReturnType>
+      </ReturnValue>
+      <Parameters>
+        <Parameter Name="config" Type="Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;" RefType="this" />
+      </Parameters>
+      <Docs>
+        <param name="config">To be added.</param>
+        <summary>To be added.</summary>
+        <returns>To be added.</returns>
+        <remarks>To be added.</remarks>
+      </Docs>
+    </Member>
+    <Member MemberName="SetShowTabs">
+      <MemberSignature Language="C#" Value="public static Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; SetShowTabs (this Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; config, Xamarin.Forms.TabsStyle value);" />
+      <MemberSignature Language="ILAsm" Value=".method public static hidebysig class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; SetShowTabs(class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; config, valuetype Xamarin.Forms.TabsStyle value) cil managed" />
+      <MemberType>Method</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;</ReturnType>
+      </ReturnValue>
+      <Parameters>
+        <Parameter Name="config" Type="Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;" RefType="this" />
+        <Parameter Name="value" Type="Xamarin.Forms.TabsStyle" />
+      </Parameters>
+      <Docs>
+        <param name="config">To be added.</param>
+        <param name="value">To be added.</param>
+        <summary>To be added.</summary>
+        <returns>To be added.</returns>
+        <remarks>To be added.</remarks>
+      </Docs>
+    </Member>
+    <Member MemberName="SetTabsStyle">
+      <MemberSignature Language="C#" Value="public static void SetTabsStyle (Xamarin.Forms.BindableObject element, Xamarin.Forms.TabsStyle value);" />
+      <MemberSignature Language="ILAsm" Value=".method public static hidebysig void SetTabsStyle(class Xamarin.Forms.BindableObject element, valuetype Xamarin.Forms.TabsStyle value) cil managed" />
+      <MemberType>Method</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>System.Void</ReturnType>
+      </ReturnValue>
+      <Parameters>
+        <Parameter Name="element" Type="Xamarin.Forms.BindableObject" />
+        <Parameter Name="value" Type="Xamarin.Forms.TabsStyle" />
+      </Parameters>
+      <Docs>
+        <param name="element">To be added.</param>
+        <param name="value">To be added.</param>
+        <summary>To be added.</summary>
+        <remarks>To be added.</remarks>
+      </Docs>
+    </Member>
+    <Member MemberName="ShowTabs">
+      <MemberSignature Language="C#" Value="public static Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; ShowTabs (this Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; config);" />
+      <MemberSignature Language="ILAsm" Value=".method public static hidebysig class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; ShowTabs(class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; config) cil managed" />
+      <MemberType>Method</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;</ReturnType>
+      </ReturnValue>
+      <Parameters>
+        <Parameter Name="config" Type="Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;" RefType="this" />
+      </Parameters>
+      <Docs>
+        <param name="config">To be added.</param>
+        <summary>To be added.</summary>
+        <returns>To be added.</returns>
+        <remarks>To be added.</remarks>
+      </Docs>
+    </Member>
+    <Member MemberName="ShowTabsOnNavigation">
+      <MemberSignature Language="C#" Value="public static Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; ShowTabsOnNavigation (this Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; config);" />
+      <MemberSignature Language="ILAsm" Value=".method public static hidebysig class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; ShowTabsOnNavigation(class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; config) cil managed" />
+      <MemberType>Method</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;</ReturnType>
+      </ReturnValue>
+      <Parameters>
+        <Parameter Name="config" Type="Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;" RefType="this" />
+      </Parameters>
+      <Docs>
+        <param name="config">To be added.</param>
+        <summary>To be added.</summary>
+        <returns>To be added.</returns>
+        <remarks>To be added.</remarks>
+      </Docs>
+    </Member>
+    <Member MemberName="TabsStyleProperty">
+      <MemberSignature Language="C#" Value="public static readonly Xamarin.Forms.BindableProperty TabsStyleProperty;" />
+      <MemberSignature Language="ILAsm" Value=".field public static initonly class Xamarin.Forms.BindableProperty TabsStyleProperty" />
+      <MemberType>Field</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.BindableProperty</ReturnType>
+      </ReturnValue>
+      <Docs>
+        <summary>To be added.</summary>
+        <remarks>To be added.</remarks>
+      </Docs>
+    </Member>
+  </Members>
+</Type>
diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms.PlatformConfiguration/macOS.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms.PlatformConfiguration/macOS.xml
new file mode 100644 (file)
index 0000000..5012c5e
--- /dev/null
@@ -0,0 +1,35 @@
+<Type Name="macOS" FullName="Xamarin.Forms.PlatformConfiguration.macOS">
+  <TypeSignature Language="C#" Value="public sealed class macOS : Xamarin.Forms.IConfigPlatform" />
+  <TypeSignature Language="ILAsm" Value=".class public auto ansi sealed beforefieldinit macOS extends System.Object implements class Xamarin.Forms.IConfigPlatform" />
+  <AssemblyInfo>
+    <AssemblyName>Xamarin.Forms.Core</AssemblyName>
+    <AssemblyVersion>2.0.0.0</AssemblyVersion>
+  </AssemblyInfo>
+  <Base>
+    <BaseTypeName>System.Object</BaseTypeName>
+  </Base>
+  <Interfaces>
+    <Interface>
+      <InterfaceName>Xamarin.Forms.IConfigPlatform</InterfaceName>
+    </Interface>
+  </Interfaces>
+  <Docs>
+    <summary>To be added.</summary>
+    <remarks>To be added.</remarks>
+  </Docs>
+  <Members>
+    <Member MemberName=".ctor">
+      <MemberSignature Language="C#" Value="public macOS ();" />
+      <MemberSignature Language="ILAsm" Value=".method public hidebysig specialname rtspecialname instance void .ctor() cil managed" />
+      <MemberType>Constructor</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <Parameters />
+      <Docs>
+        <summary>To be added.</summary>
+        <remarks>To be added.</remarks>
+      </Docs>
+    </Member>
+  </Members>
+</Type>
index 16d1348..37fbad3 100644 (file)
@@ -159,6 +159,21 @@ Device.BeginInvokeOnMainThread (() => {
         <remarks>To be added.</remarks>
       </Docs>
     </Member>
+    <Member MemberName="macOS">
+      <MemberSignature Language="C#" Value="public const string macOS;" />
+      <MemberSignature Language="ILAsm" Value=".field public static literal string macOS" />
+      <MemberType>Field</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>System.String</ReturnType>
+      </ReturnValue>
+      <Docs>
+        <summary>To be added.</summary>
+        <remarks>To be added.</remarks>
+      </Docs>
+    </Member>
     <Member MemberName="OnPlatform">
       <MemberSignature Language="C#" Value="public static void OnPlatform (Action iOS = null, Action Android = null, Action WinPhone = null, Action Default = null);" />
       <MemberSignature Language="ILAsm" Value=".method public static hidebysig void OnPlatform(class System.Action iOS, class System.Action Android, class System.Action WinPhone, class System.Action Default) cil managed" />
diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms/TabsStyle.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms/TabsStyle.xml
new file mode 100644 (file)
index 0000000..7565a1f
--- /dev/null
@@ -0,0 +1,87 @@
+<Type Name="TabsStyle" FullName="Xamarin.Forms.TabsStyle">
+  <TypeSignature Language="C#" Value="public enum TabsStyle" />
+  <TypeSignature Language="ILAsm" Value=".class public auto ansi sealed TabsStyle extends System.Enum" />
+  <AssemblyInfo>
+    <AssemblyName>Xamarin.Forms.Core</AssemblyName>
+    <AssemblyVersion>2.0.0.0</AssemblyVersion>
+  </AssemblyInfo>
+  <Base>
+    <BaseTypeName>System.Enum</BaseTypeName>
+  </Base>
+  <Docs>
+    <summary>To be added.</summary>
+    <remarks>To be added.</remarks>
+  </Docs>
+  <Members>
+    <Member MemberName="Default">
+      <MemberSignature Language="C#" Value="Default" />
+      <MemberSignature Language="ILAsm" Value=".field public static literal valuetype Xamarin.Forms.TabsStyle Default = int32(0)" />
+      <MemberType>Field</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.TabsStyle</ReturnType>
+      </ReturnValue>
+      <Docs>
+        <summary>To be added.</summary>
+      </Docs>
+    </Member>
+    <Member MemberName="Hidden">
+      <MemberSignature Language="C#" Value="Hidden" />
+      <MemberSignature Language="ILAsm" Value=".field public static literal valuetype Xamarin.Forms.TabsStyle Hidden = int32(1)" />
+      <MemberType>Field</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.TabsStyle</ReturnType>
+      </ReturnValue>
+      <Docs>
+        <summary>To be added.</summary>
+      </Docs>
+    </Member>
+    <Member MemberName="Icons">
+      <MemberSignature Language="C#" Value="Icons" />
+      <MemberSignature Language="ILAsm" Value=".field public static literal valuetype Xamarin.Forms.TabsStyle Icons = int32(2)" />
+      <MemberType>Field</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.TabsStyle</ReturnType>
+      </ReturnValue>
+      <Docs>
+        <summary>To be added.</summary>
+      </Docs>
+    </Member>
+    <Member MemberName="OnBottom">
+      <MemberSignature Language="C#" Value="OnBottom" />
+      <MemberSignature Language="ILAsm" Value=".field public static literal valuetype Xamarin.Forms.TabsStyle OnBottom = int32(4)" />
+      <MemberType>Field</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.TabsStyle</ReturnType>
+      </ReturnValue>
+      <Docs>
+        <summary>To be added.</summary>
+      </Docs>
+    </Member>
+    <Member MemberName="OnNavigation">
+      <MemberSignature Language="C#" Value="OnNavigation" />
+      <MemberSignature Language="ILAsm" Value=".field public static literal valuetype Xamarin.Forms.TabsStyle OnNavigation = int32(3)" />
+      <MemberType>Field</MemberType>
+      <AssemblyInfo>
+        <AssemblyVersion>2.0.0.0</AssemblyVersion>
+      </AssemblyInfo>
+      <ReturnValue>
+        <ReturnType>Xamarin.Forms.TabsStyle</ReturnType>
+      </ReturnValue>
+      <Docs>
+        <summary>To be added.</summary>
+      </Docs>
+    </Member>
+  </Members>
+</Type>
index 1b6cb74..89af75c 100644 (file)
           <AttributeName>System.Runtime.CompilerServices.InternalsVisibleTo("Xamarin.Forms.Core.Windows.UITests")</AttributeName>
         </Attribute>
         <Attribute>
+          <AttributeName>System.Runtime.CompilerServices.InternalsVisibleTo("Xamarin.Forms.Core.macOS.UITests")</AttributeName>
+        </Attribute>
+        <Attribute>
           <AttributeName>System.Runtime.CompilerServices.InternalsVisibleTo("Xamarin.Forms.iOS.UITests")</AttributeName>
         </Attribute>
         <Attribute>
       <Type Name="TableSectionBase" Kind="Class" />
       <Type Name="TableSectionBase`1" DisplayName="TableSectionBase&lt;T&gt;" Kind="Class" />
       <Type Name="TableView" Kind="Class" />
+      <Type Name="TabsStyle" Kind="Enumeration" />
       <Type Name="TapGestureRecognizer" Kind="Class" />
       <Type Name="TappedEventArgs" Kind="Class" />
       <Type Name="TargetIdiom" Kind="Enumeration" />
     <Namespace Name="Xamarin.Forms.PlatformConfiguration">
       <Type Name="Android" Kind="Class" />
       <Type Name="iOS" Kind="Class" />
+      <Type Name="macOS" Kind="Class" />
       <Type Name="Tizen" Kind="Class" />
       <Type Name="Windows" Kind="Class" />
     </Namespace>
       <Type Name="StatusBarTextColorMode" Kind="Enumeration" />
       <Type Name="VisualElement" Kind="Class" />
     </Namespace>
+    <Namespace Name="Xamarin.Forms.PlatformConfiguration.macOSSpecific">
+      <Type Name="TabbedPage" Kind="Class" />
+    </Namespace>
     <Namespace Name="Xamarin.Forms.PlatformConfiguration.WindowsSpecific">
       <Type Name="CollapseStyle" Kind="Enumeration" />
       <Type Name="MasterDetailPage" Kind="Class" />
       <Targets>
         <Target Type="T:Xamarin.Forms.IPlatformElementConfiguration`2" />
       </Targets>
+      <Member MemberName="GetTabsStyle">
+        <MemberSignature Language="C#" Value="public static Xamarin.Forms.TabsStyle GetTabsStyle (this Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; config);" />
+        <MemberSignature Language="ILAsm" Value=".method public static hidebysig valuetype Xamarin.Forms.TabsStyle GetTabsStyle(class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; config) cil managed" />
+        <MemberType>ExtensionMethod</MemberType>
+        <ReturnValue>
+          <ReturnType>Xamarin.Forms.TabsStyle</ReturnType>
+        </ReturnValue>
+        <Parameters>
+          <Parameter Name="config" Type="Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;" RefType="this" />
+        </Parameters>
+        <Docs>
+          <param name="config">To be added.</param>
+          <summary>To be added.</summary>
+        </Docs>
+        <Link Type="Xamarin.Forms.PlatformConfiguration.macOSSpecific.TabbedPage" Member="M:Xamarin.Forms.PlatformConfiguration.macOSSpecific.TabbedPage.GetTabsStyle(Xamarin.Forms.IPlatformElementConfiguration{Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage})" />
+      </Member>
+    </ExtensionMethod>
+    <ExtensionMethod>
+      <Targets>
+        <Target Type="T:Xamarin.Forms.IPlatformElementConfiguration`2" />
+      </Targets>
+      <Member MemberName="HideTabs">
+        <MemberSignature Language="C#" Value="public static Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; HideTabs (this Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; config);" />
+        <MemberSignature Language="ILAsm" Value=".method public static hidebysig class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; HideTabs(class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; config) cil managed" />
+        <MemberType>ExtensionMethod</MemberType>
+        <ReturnValue>
+          <ReturnType>Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;</ReturnType>
+        </ReturnValue>
+        <Parameters>
+          <Parameter Name="config" Type="Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;" RefType="this" />
+        </Parameters>
+        <Docs>
+          <param name="config">To be added.</param>
+          <summary>To be added.</summary>
+        </Docs>
+        <Link Type="Xamarin.Forms.PlatformConfiguration.macOSSpecific.TabbedPage" Member="M:Xamarin.Forms.PlatformConfiguration.macOSSpecific.TabbedPage.HideTabs(Xamarin.Forms.IPlatformElementConfiguration{Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage})" />
+      </Member>
+    </ExtensionMethod>
+    <ExtensionMethod>
+      <Targets>
+        <Target Type="T:Xamarin.Forms.IPlatformElementConfiguration`2" />
+      </Targets>
+      <Member MemberName="SetShowTabs">
+        <MemberSignature Language="C#" Value="public static Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; SetShowTabs (this Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; config, Xamarin.Forms.TabsStyle value);" />
+        <MemberSignature Language="ILAsm" Value=".method public static hidebysig class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; SetShowTabs(class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; config, valuetype Xamarin.Forms.TabsStyle value) cil managed" />
+        <MemberType>ExtensionMethod</MemberType>
+        <ReturnValue>
+          <ReturnType>Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;</ReturnType>
+        </ReturnValue>
+        <Parameters>
+          <Parameter Name="config" Type="Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;" RefType="this" />
+          <Parameter Name="value" Type="Xamarin.Forms.TabsStyle" />
+        </Parameters>
+        <Docs>
+          <param name="config">To be added.</param>
+          <param name="value">To be added.</param>
+          <summary>To be added.</summary>
+        </Docs>
+        <Link Type="Xamarin.Forms.PlatformConfiguration.macOSSpecific.TabbedPage" Member="M:Xamarin.Forms.PlatformConfiguration.macOSSpecific.TabbedPage.SetShowTabs(Xamarin.Forms.IPlatformElementConfiguration{Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage},Xamarin.Forms.TabsStyle)" />
+      </Member>
+    </ExtensionMethod>
+    <ExtensionMethod>
+      <Targets>
+        <Target Type="T:Xamarin.Forms.IPlatformElementConfiguration`2" />
+      </Targets>
+      <Member MemberName="ShowTabs">
+        <MemberSignature Language="C#" Value="public static Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; ShowTabs (this Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; config);" />
+        <MemberSignature Language="ILAsm" Value=".method public static hidebysig class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; ShowTabs(class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; config) cil managed" />
+        <MemberType>ExtensionMethod</MemberType>
+        <ReturnValue>
+          <ReturnType>Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;</ReturnType>
+        </ReturnValue>
+        <Parameters>
+          <Parameter Name="config" Type="Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;" RefType="this" />
+        </Parameters>
+        <Docs>
+          <param name="config">To be added.</param>
+          <summary>To be added.</summary>
+        </Docs>
+        <Link Type="Xamarin.Forms.PlatformConfiguration.macOSSpecific.TabbedPage" Member="M:Xamarin.Forms.PlatformConfiguration.macOSSpecific.TabbedPage.ShowTabs(Xamarin.Forms.IPlatformElementConfiguration{Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage})" />
+      </Member>
+    </ExtensionMethod>
+    <ExtensionMethod>
+      <Targets>
+        <Target Type="T:Xamarin.Forms.IPlatformElementConfiguration`2" />
+      </Targets>
+      <Member MemberName="ShowTabsOnNavigation">
+        <MemberSignature Language="C#" Value="public static Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; ShowTabsOnNavigation (this Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt; config);" />
+        <MemberSignature Language="ILAsm" Value=".method public static hidebysig class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; ShowTabsOnNavigation(class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.macOS, class Xamarin.Forms.TabbedPage&gt; config) cil managed" />
+        <MemberType>ExtensionMethod</MemberType>
+        <ReturnValue>
+          <ReturnType>Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;</ReturnType>
+        </ReturnValue>
+        <Parameters>
+          <Parameter Name="config" Type="Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage&gt;" RefType="this" />
+        </Parameters>
+        <Docs>
+          <param name="config">To be added.</param>
+          <summary>To be added.</summary>
+        </Docs>
+        <Link Type="Xamarin.Forms.PlatformConfiguration.macOSSpecific.TabbedPage" Member="M:Xamarin.Forms.PlatformConfiguration.macOSSpecific.TabbedPage.ShowTabsOnNavigation(Xamarin.Forms.IPlatformElementConfiguration{Xamarin.Forms.PlatformConfiguration.macOS,Xamarin.Forms.TabbedPage})" />
+      </Member>
+    </ExtensionMethod>
+    <ExtensionMethod>
+      <Targets>
+        <Target Type="T:Xamarin.Forms.IPlatformElementConfiguration`2" />
+      </Targets>
       <Member MemberName="CollapsedPaneWidth">
         <MemberSignature Language="C#" Value="public static double CollapsedPaneWidth (this Xamarin.Forms.IPlatformElementConfiguration&lt;Xamarin.Forms.PlatformConfiguration.Windows,Xamarin.Forms.MasterDetailPage&gt; config);" />
         <MemberSignature Language="ILAsm" Value=".method public static hidebysig float64 CollapsedPaneWidth(class Xamarin.Forms.IPlatformElementConfiguration`2&lt;class Xamarin.Forms.PlatformConfiguration.Windows, class Xamarin.Forms.MasterDetailPage&gt; config) cil managed" />
diff --git a/docs/Xamarin.Forms.Core/ns-Xamarin.Forms.PlatformConfiguration.macOSSpecific.xml b/docs/Xamarin.Forms.Core/ns-Xamarin.Forms.PlatformConfiguration.macOSSpecific.xml
new file mode 100644 (file)
index 0000000..644ef85
--- /dev/null
@@ -0,0 +1,6 @@
+<Namespace Name="Xamarin.Forms.PlatformConfiguration.macOSSpecific">
+  <Docs>
+    <summary>To be added.</summary>
+    <remarks>To be added.</remarks>
+  </Docs>
+</Namespace>
index c8db288..775e9db 100644 (file)
@@ -33,7 +33,7 @@
           <AttributeName>System.Resources.NeutralResourcesLanguage("en")</AttributeName>
         </Attribute>
         <Attribute>
-          <AttributeName>System.Runtime.CompilerServices.CompilationRelaxations(8)</AttributeName>
+          <AttributeName>System.Runtime.CompilerServices.InternalsVisibleTo("Xamarin.Forms.Maps.macOS")</AttributeName>
         </Attribute>
         <Attribute>
           <AttributeName>System.Runtime.CompilerServices.InternalsVisibleTo("Xamarin.Forms.Maps.iOS")</AttributeName>