From be3f9bd2770d2a6b2fab48f8552d440e78784b17 Mon Sep 17 00:00:00 2001 From: redth Date: Mon, 27 Apr 2026 09:53:27 -0400 Subject: [PATCH 01/16] Add macOS AppKit MAUI backend (platforms/MacOS) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrates the macOS AppKit backend from shinyorg/mauiplatforms (commit 62d4022) into platforms/MacOS/, refactored to match the conventions of the canonical Linux.Gtk4 backend. New projects (all net10.0-macos, SupportedOSPlatformVersion=14.0): - Microsoft.Maui.Platforms.MacOS — core handlers, hosting, platform services - Microsoft.Maui.Platforms.MacOS.Essentials — Essentials implementations - Microsoft.Maui.Platforms.MacOS.BlazorWebView — Blazor Hybrid via WKWebView - MacOS.Sample — comprehensive ~30-page programmatic sample, with optional EnableMauiDevFlow ProjectReference toggle (matches Linux.Gtk4.Sample pattern) Naming refactor (upstream → maui-labs): - Package id Platform.Maui.MacOS[.X] → Microsoft.Maui.Platforms.MacOS[.X] - Namespace Microsoft.Maui.Platform.MacOS[.Sub] → Microsoft.Maui.Platforms.MacOS[.Sub] - MacOS* class prefix preserved - UseMauiAppMacOS() entry point preserved Folder reorganization (to match Linux.Gtk4 layout): - Dispatching/MacOSDispatcher*.cs → Platform/ - Hosting/MacOS{MauiApplication,MauiContext,MauiContextExtensions}.cs → Platform/ - Handlers/MacOSFontNamedSizeService.cs → Platform/ - build/*.targets → buildTransitive/ - ILLink.Descriptors.xml updated to reference the new assembly names DevFlow integration: - Microsoft.Maui.DevFlow.Agent and .Blazor switched from PackageReference on the shinyorg NuGets to ProjectReference on the local platforms/MacOS projects, eliminating the duplicate AddMacOS lifecycle extension. - BlazorWebViewDebugService.cs and adjacent samples updated to the new namespace. - Removed shinyorg Platform.Maui.MacOS* PackageVersion entries from Directory.Packages.props and the corresponding properties from eng/Versions.props. Build validation: dotnet build platforms/MacOS/MacOS.slnx → 0 errors. DevFlow.Sample.MacOS, Microsoft.Maui.DevFlow.Agent, and Linux.Gtk4.slnx also continue to build cleanly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Directory.Packages.props | 3 - eng/Versions.props | 3 - platforms/MacOS/Directory.Build.props | 26 + platforms/MacOS/Directory.Build.targets | 5 + platforms/MacOS/Directory.Packages.props | 8 + platforms/MacOS/LICENSE | 21 + platforms/MacOS/MacOS.slnx | 10 + platforms/MacOS/README.md | 141 ++ platforms/MacOS/docs/README.md | 40 + platforms/MacOS/docs/guides/blazor-hybrid.md | 157 ++ platforms/MacOS/docs/guides/controls.md | 162 ++ .../MacOS/docs/guides/getting-started.md | 281 ++++ platforms/MacOS/docs/guides/index.md | 59 + platforms/MacOS/docs/guides/lifecycle.md | 97 ++ platforms/MacOS/docs/guides/menu-bar.md | 88 + platforms/MacOS/docs/guides/sidebar.md | 163 ++ platforms/MacOS/docs/guides/theming.md | 56 + platforms/MacOS/docs/guides/toolbar-items.md | 450 ++++++ platforms/MacOS/docs/guides/toolbar.md | 194 +++ platforms/MacOS/docs/guides/window.md | 180 +++ platforms/MacOS/docs/handler-audit-status.md | 658 ++++++++ platforms/MacOS/samples/MacOS.Sample/App.cs | 150 ++ .../MacOS/samples/MacOS.Sample/AppColors.cs | 111 ++ .../MacOS.Sample/Components/Counter.razor | 29 + .../samples/MacOS.Sample/MacOS.Sample.csproj | 56 + platforms/MacOS/samples/MacOS.Sample/Main.cs | 13 + .../samples/MacOS.Sample/MauiMacOSApp.cs | 11 + .../MacOS/samples/MacOS.Sample/MauiProgram.cs | 63 + .../samples/MacOS.Sample/Pages/AlertsPage.cs | 120 ++ .../MacOS.Sample/Pages/BatteryNetworkPage.cs | 94 ++ .../samples/MacOS.Sample/Pages/BlazorPage.cs | 57 + .../MacOS.Sample/Pages/CarouselViewPage.cs | 164 ++ .../MacOS.Sample/Pages/ClipboardPrefsPage.cs | 158 ++ .../MacOS.Sample/Pages/CollectionViewPage.cs | 909 +++++++++++ .../MacOS.Sample/Pages/ControlsPage.cs | 182 +++ .../MacOS.Sample/Pages/DeviceInfoPage.cs | 91 ++ .../MacOS.Sample/Pages/FlyoutPageDemo.cs | 175 ++ .../samples/MacOS.Sample/Pages/FontsPage.cs | 241 +++ .../MacOS.Sample/Pages/FormattedTextPage.cs | 115 ++ .../MacOS.Sample/Pages/GesturesPage.cs | 206 +++ .../MacOS.Sample/Pages/GraphicsPage.cs | 181 +++ .../samples/MacOS.Sample/Pages/HomePage.cs | 75 + .../MacOS.Sample/Pages/LaunchSharePage.cs | 116 ++ .../samples/MacOS.Sample/Pages/LayoutsPage.cs | 234 +++ .../MacOS.Sample/Pages/ListViewPage.cs | 119 ++ .../samples/MacOS.Sample/Pages/MapPage.cs | 203 +++ .../samples/MacOS.Sample/Pages/MenuBarPage.cs | 271 ++++ .../MacOS.Sample/Pages/MultiWindowPage.cs | 356 ++++ .../MacOS.Sample/Pages/NavigationDemoPage.cs | 322 ++++ .../samples/MacOS.Sample/Pages/PickersPage.cs | 81 + .../MacOS.Sample/Pages/RadioButtonPage.cs | 75 + .../samples/MacOS.Sample/Pages/ShapesPage.cs | 162 ++ .../MacOS.Sample/Pages/TabbedPageDemo.cs | 115 ++ .../MacOS.Sample/Pages/TableViewPage.cs | 72 + .../samples/MacOS.Sample/Pages/ThemePage.cs | 103 ++ .../samples/MacOS.Sample/Pages/ToolbarPage.cs | 1247 ++++++++++++++ .../MacOS.Sample/Pages/TransformsPage.cs | 201 +++ .../samples/MacOS.Sample/Pages/WebViewPage.cs | 87 + .../Resources/AppIcon/appicon.png | Bin 0 -> 30106 bytes .../Resources/Fonts/OpenSans-Regular.ttf | Bin 0 -> 532636 bytes .../MacOS/samples/MacOS.Sample/_Imports.razor | 1 + .../samples/MacOS.Sample/wwwroot/index.html | 41 + .../BlazorWebViewExtensions.cs | 21 + .../BlazorWebViewHandler.cs | 534 ++++++ .../ILLink.Descriptors.xml | 3 + .../MacOS.BlazorWebView.csproj | 37 + .../MacOSBlazorDispatcher.cs | 88 + .../MacOS.BlazorWebView/MacOSBlazorWebView.cs | 82 + .../MacOSMauiAssetFileProvider.cs | 69 + .../MacOSWebViewManager.cs | 54 + ...Maui.Platforms.MacOS.BlazorWebView.targets | 30 + .../MacOS.Essentials/AppInfoImplementation.cs | 61 + .../MacOS.Essentials/BatteryImplementation.cs | 265 +++ .../MacOS.Essentials/BrowserImplementation.cs | 15 + .../ClipboardImplementation.cs | 35 + .../ConnectivityImplementation.cs | 147 ++ .../DeviceDisplayImplementation.cs | 145 ++ .../DeviceInfoImplementation.cs | 86 + .../MacOS.Essentials/EmailImplementation.cs | 38 + .../MacOS.Essentials/EssentialsExtensions.cs | 109 ++ .../FilePickerImplementation.cs | 89 + .../FileSystemImplementation.cs | 40 + .../FlashlightImplementation.cs | 16 + .../GeolocationImplementation.cs | 146 ++ .../HapticFeedbackImplementation.cs | 19 + .../MacOS.Essentials/ILLink.Descriptors.xml | 3 + .../LauncherImplementation.cs | 46 + .../MacOS.Essentials/MacOS.Essentials.csproj | 31 + .../src/MacOS.Essentials/MainThreadHelper.cs | 21 + .../src/MacOS.Essentials/MapImplementation.cs | 62 + .../MediaPickerImplementation.cs | 106 ++ .../PhoneDialerImplementation.cs | 11 + .../PreferencesImplementation.cs | 110 ++ .../ScreenshotImplementation.cs | 143 ++ .../SecureStorageImplementation.cs | 81 + .../SemanticScreenReaderImplementation.cs | 24 + .../MacOS.Essentials/SensorImplementations.cs | 53 + .../MacOS.Essentials/ShareImplementation.cs | 64 + .../src/MacOS.Essentials/SmsImplementation.cs | 11 + .../TextToSpeechImplementation.cs | 90 ++ .../VibrationImplementation.cs | 17 + platforms/MacOS/src/MacOS/Controls/MapView.cs | 130 ++ .../Handlers/ActivityIndicatorHandler.cs | 54 + .../src/MacOS/Handlers/ApplicationHandler.cs | 80 + .../MacOS/src/MacOS/Handlers/BorderHandler.cs | 270 ++++ .../MacOS/src/MacOS/Handlers/ButtonHandler.cs | 465 ++++++ .../src/MacOS/Handlers/CarouselViewHandler.cs | 316 ++++ .../src/MacOS/Handlers/CheckBoxHandler.cs | 74 + .../MacOS/Handlers/CollectionViewHandler.cs | 1108 +++++++++++++ .../src/MacOS/Handlers/ContentPageHandler.cs | 99 ++ .../src/MacOS/Handlers/ContentViewHandler.cs | 81 + .../src/MacOS/Handlers/DatePickerHandler.cs | 114 ++ .../MacOS/src/MacOS/Handlers/EditorHandler.cs | 207 +++ .../MacOS/src/MacOS/Handlers/EntryHandler.cs | 266 +++ .../src/MacOS/Handlers/FlyoutPageHandler.cs | 384 +++++ .../src/MacOS/Handlers/GestureManager.cs | 400 +++++ .../src/MacOS/Handlers/GraphicsViewHandler.cs | 116 ++ .../src/MacOS/Handlers/ImageButtonHandler.cs | 132 ++ .../MacOS/src/MacOS/Handlers/ImageHandler.cs | 252 +++ .../MacOS/Handlers/IndicatorViewHandler.cs | 106 ++ .../MacOS/src/MacOS/Handlers/LabelHandler.cs | 409 +++++ .../MacOS/src/MacOS/Handlers/LayoutHandler.cs | 151 ++ .../src/MacOS/Handlers/ListViewHandler.cs | 500 ++++++ .../src/MacOS/Handlers/MacOSViewHandler.cs | 617 +++++++ .../src/MacOS/Handlers/MapViewHandler.cs | 219 +++ .../src/MacOS/Handlers/MenuBarManager.cs | 275 ++++ .../NativeSidebarFlyoutPageHandler.cs | 708 ++++++++ .../MacOS/Handlers/NavigationPageHandler.cs | 128 ++ .../MacOS/src/MacOS/Handlers/PickerHandler.cs | 164 ++ .../src/MacOS/Handlers/ProgressBarHandler.cs | 57 + .../src/MacOS/Handlers/RadioButtonHandler.cs | 142 ++ .../src/MacOS/Handlers/RefreshViewHandler.cs | 109 ++ .../src/MacOS/Handlers/ScrollViewHandler.cs | 214 +++ .../src/MacOS/Handlers/SearchBarHandler.cs | 116 ++ .../src/MacOS/Handlers/ShapeViewHandler.cs | 282 ++++ .../src/MacOS/Handlers/ShellContentHandler.cs | 46 + .../MacOS/src/MacOS/Handlers/ShellHandler.cs | 836 ++++++++++ .../src/MacOS/Handlers/ShellItemHandler.cs | 34 + .../src/MacOS/Handlers/ShellSectionHandler.cs | 64 + .../MacOS/src/MacOS/Handlers/SliderHandler.cs | 103 ++ .../src/MacOS/Handlers/StepperHandler.cs | 86 + .../src/MacOS/Handlers/SwipeViewHandler.cs | 239 +++ .../MacOS/src/MacOS/Handlers/SwitchHandler.cs | 74 + .../src/MacOS/Handlers/TabbedPageHandler.cs | 240 +++ .../src/MacOS/Handlers/TableViewHandler.cs | 317 ++++ .../src/MacOS/Handlers/TimePickerHandler.cs | 97 ++ .../src/MacOS/Handlers/ToolbarHandler.cs | 1439 +++++++++++++++++ .../src/MacOS/Handlers/WebViewHandler.cs | 200 +++ .../MacOS/src/MacOS/Handlers/WindowHandler.cs | 581 +++++++ .../MacOS/Hosting/AppHostBuilderExtensions.cs | 126 ++ .../MacOS/Hosting/MenuBarBuilderExtensions.cs | 24 + .../MacOS/src/MacOS/ILLink.Descriptors.xml | 7 + .../LifecycleEvents/IMacOSLifecycleBuilder.cs | 7 + .../MacOS/LifecycleEvents/MacOSLifecycle.cs | 15 + .../MacOSLifecycleBuilderExtensions.cs | 13 + .../MacOSLifecycleExtensions.cs | 25 + platforms/MacOS/src/MacOS/MacOS.csproj | 37 + .../Platform/AlertManagerSubscription.cs | 147 ++ .../MacOS/Platform/FontImageSourceHelper.cs | 56 + .../src/MacOS/Platform/MacOSContainerView.cs | 232 +++ .../src/MacOS/Platform/MacOSDispatcher.cs | 43 + .../MacOS/Platform/MacOSDispatcherTimer.cs | 50 + .../MacOS/Platform/MacOSEmbeddedFontLoader.cs | 83 + .../src/MacOS/Platform/MacOSFlyoutPage.cs | 122 ++ .../src/MacOS/Platform/MacOSFontManager.cs | 57 + .../Platform/MacOSFontNamedSizeService.cs | 34 + .../src/MacOS/Platform/MacOSFontRegistrar.cs | 114 ++ .../MacOS/Platform/MacOSMauiApplication.cs | 148 ++ .../src/MacOS/Platform/MacOSMauiContext.cs | 60 + .../Platform/MacOSMauiContextExtensions.cs | 22 + .../src/MacOS/Platform/MacOSMenuBarOptions.cs | 27 + .../src/MacOS/Platform/MacOSModalManager.cs | 406 +++++ .../MacOS/src/MacOS/Platform/MacOSPage.cs | 160 ++ .../MacOS/src/MacOS/Platform/MacOSShell.cs | 61 + .../src/MacOS/Platform/MacOSSidebarItem.cs | 45 + .../MacOS/src/MacOS/Platform/MacOSTicker.cs | 38 + .../src/MacOS/Platform/MacOSToolbarItem.cs | 786 +++++++++ .../MacOS/src/MacOS/Platform/MacOSWindow.cs | 156 ++ .../src/MacOS/Platform/MauiNSTextField.cs | 101 ++ .../src/MacOS/Platform/ViewExtensions.cs | 34 + .../Microsoft.Maui.Platforms.MacOS.targets | 65 + .../DevFlow.Sample.MacOS.csproj | 6 +- samples/DevFlow.Sample.MacOS/MauiProgram.cs | 4 +- samples/DevFlow.Sample.MacOS/Program.cs | 2 +- samples/DevFlow.Sample/AppShell.xaml.cs | 2 +- samples/DevFlow.Sample/BlazorTodoPage.xaml.cs | 2 +- samples/DevFlow.Sample/ModalTestPage.xaml.cs | 2 +- .../DevFlow.Sample/MultiBlazorPage.xaml.cs | 2 +- .../Microsoft.Maui.DevFlow.Agent.csproj | 2 +- .../BlazorWebViewDebugService.cs | 2 +- .../Microsoft.Maui.DevFlow.Blazor.csproj | 4 +- 191 files changed, 29221 insertions(+), 20 deletions(-) create mode 100644 platforms/MacOS/Directory.Build.props create mode 100644 platforms/MacOS/Directory.Build.targets create mode 100644 platforms/MacOS/Directory.Packages.props create mode 100644 platforms/MacOS/LICENSE create mode 100644 platforms/MacOS/MacOS.slnx create mode 100644 platforms/MacOS/README.md create mode 100644 platforms/MacOS/docs/README.md create mode 100644 platforms/MacOS/docs/guides/blazor-hybrid.md create mode 100644 platforms/MacOS/docs/guides/controls.md create mode 100644 platforms/MacOS/docs/guides/getting-started.md create mode 100644 platforms/MacOS/docs/guides/index.md create mode 100644 platforms/MacOS/docs/guides/lifecycle.md create mode 100644 platforms/MacOS/docs/guides/menu-bar.md create mode 100644 platforms/MacOS/docs/guides/sidebar.md create mode 100644 platforms/MacOS/docs/guides/theming.md create mode 100644 platforms/MacOS/docs/guides/toolbar-items.md create mode 100644 platforms/MacOS/docs/guides/toolbar.md create mode 100644 platforms/MacOS/docs/guides/window.md create mode 100644 platforms/MacOS/docs/handler-audit-status.md create mode 100644 platforms/MacOS/samples/MacOS.Sample/App.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/AppColors.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Components/Counter.razor create mode 100644 platforms/MacOS/samples/MacOS.Sample/MacOS.Sample.csproj create mode 100644 platforms/MacOS/samples/MacOS.Sample/Main.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/MauiMacOSApp.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/MauiProgram.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/AlertsPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/BatteryNetworkPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/BlazorPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/CarouselViewPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/ClipboardPrefsPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/CollectionViewPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/ControlsPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/DeviceInfoPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/FlyoutPageDemo.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/FontsPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/FormattedTextPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/GesturesPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/GraphicsPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/HomePage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/LaunchSharePage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/LayoutsPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/ListViewPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/MapPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/MenuBarPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/MultiWindowPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/NavigationDemoPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/PickersPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/RadioButtonPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/ShapesPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/TabbedPageDemo.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/TableViewPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/ThemePage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/ToolbarPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/TransformsPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Pages/WebViewPage.cs create mode 100644 platforms/MacOS/samples/MacOS.Sample/Resources/AppIcon/appicon.png create mode 100644 platforms/MacOS/samples/MacOS.Sample/Resources/Fonts/OpenSans-Regular.ttf create mode 100644 platforms/MacOS/samples/MacOS.Sample/_Imports.razor create mode 100644 platforms/MacOS/samples/MacOS.Sample/wwwroot/index.html create mode 100644 platforms/MacOS/src/MacOS.BlazorWebView/BlazorWebViewExtensions.cs create mode 100644 platforms/MacOS/src/MacOS.BlazorWebView/BlazorWebViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS.BlazorWebView/ILLink.Descriptors.xml create mode 100644 platforms/MacOS/src/MacOS.BlazorWebView/MacOS.BlazorWebView.csproj create mode 100644 platforms/MacOS/src/MacOS.BlazorWebView/MacOSBlazorDispatcher.cs create mode 100644 platforms/MacOS/src/MacOS.BlazorWebView/MacOSBlazorWebView.cs create mode 100644 platforms/MacOS/src/MacOS.BlazorWebView/MacOSMauiAssetFileProvider.cs create mode 100644 platforms/MacOS/src/MacOS.BlazorWebView/MacOSWebViewManager.cs create mode 100644 platforms/MacOS/src/MacOS.BlazorWebView/buildTransitive/Microsoft.Maui.Platforms.MacOS.BlazorWebView.targets create mode 100644 platforms/MacOS/src/MacOS.Essentials/AppInfoImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/BatteryImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/BrowserImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/ClipboardImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/ConnectivityImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/DeviceDisplayImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/DeviceInfoImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/EmailImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/EssentialsExtensions.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/FilePickerImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/FileSystemImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/FlashlightImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/GeolocationImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/HapticFeedbackImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/ILLink.Descriptors.xml create mode 100644 platforms/MacOS/src/MacOS.Essentials/LauncherImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/MacOS.Essentials.csproj create mode 100644 platforms/MacOS/src/MacOS.Essentials/MainThreadHelper.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/MapImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/MediaPickerImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/PhoneDialerImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/PreferencesImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/ScreenshotImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/SecureStorageImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/SemanticScreenReaderImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/SensorImplementations.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/ShareImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/SmsImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/TextToSpeechImplementation.cs create mode 100644 platforms/MacOS/src/MacOS.Essentials/VibrationImplementation.cs create mode 100644 platforms/MacOS/src/MacOS/Controls/MapView.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ActivityIndicatorHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ApplicationHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/BorderHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ButtonHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/CarouselViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/CheckBoxHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/CollectionViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ContentPageHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ContentViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/DatePickerHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/EditorHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/EntryHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/FlyoutPageHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/GestureManager.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/GraphicsViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ImageButtonHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ImageHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/IndicatorViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/LabelHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/LayoutHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ListViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/MacOSViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/MapViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/MenuBarManager.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/NativeSidebarFlyoutPageHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/NavigationPageHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/PickerHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ProgressBarHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/RadioButtonHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/RefreshViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ScrollViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/SearchBarHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ShapeViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ShellContentHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ShellHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ShellItemHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ShellSectionHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/SliderHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/StepperHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/SwipeViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/SwitchHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/TabbedPageHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/TableViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/TimePickerHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/ToolbarHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/WebViewHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Handlers/WindowHandler.cs create mode 100644 platforms/MacOS/src/MacOS/Hosting/AppHostBuilderExtensions.cs create mode 100644 platforms/MacOS/src/MacOS/Hosting/MenuBarBuilderExtensions.cs create mode 100644 platforms/MacOS/src/MacOS/ILLink.Descriptors.xml create mode 100644 platforms/MacOS/src/MacOS/LifecycleEvents/IMacOSLifecycleBuilder.cs create mode 100644 platforms/MacOS/src/MacOS/LifecycleEvents/MacOSLifecycle.cs create mode 100644 platforms/MacOS/src/MacOS/LifecycleEvents/MacOSLifecycleBuilderExtensions.cs create mode 100644 platforms/MacOS/src/MacOS/LifecycleEvents/MacOSLifecycleExtensions.cs create mode 100644 platforms/MacOS/src/MacOS/MacOS.csproj create mode 100644 platforms/MacOS/src/MacOS/Platform/AlertManagerSubscription.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/FontImageSourceHelper.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSContainerView.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSDispatcher.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSDispatcherTimer.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSEmbeddedFontLoader.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSFlyoutPage.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSFontManager.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSFontNamedSizeService.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSFontRegistrar.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSMauiApplication.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSMauiContext.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSMauiContextExtensions.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSMenuBarOptions.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSModalManager.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSPage.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSShell.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSSidebarItem.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSTicker.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSToolbarItem.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MacOSWindow.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/MauiNSTextField.cs create mode 100644 platforms/MacOS/src/MacOS/Platform/ViewExtensions.cs create mode 100644 platforms/MacOS/src/MacOS/buildTransitive/Microsoft.Maui.Platforms.MacOS.targets diff --git a/Directory.Packages.props b/Directory.Packages.props index 51b1c2381..9cd5f2594 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,9 +44,6 @@ - - - diff --git a/eng/Versions.props b/eng/Versions.props index 0bcae406d..6c21b9e24 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -49,9 +49,6 @@ 1.0.0-preview.1.26201.1 - 0.3.0 - $(PlatformMauiMacOSVersion) - $(PlatformMauiMacOSVersion) 0.6.0 $(PlatformMauiLinuxGtk4Version) $(PlatformMauiLinuxGtk4Version) diff --git a/platforms/MacOS/Directory.Build.props b/platforms/MacOS/Directory.Build.props new file mode 100644 index 000000000..fecdc00c1 --- /dev/null +++ b/platforms/MacOS/Directory.Build.props @@ -0,0 +1,26 @@ + + + + + preview + enable + enable + 10.0.41 + + false + + + + $(DefineConstants);MAUIDEVFLOW + + + + + Microsoft + MIT + https://github.com/dotnet/maui-labs/tree/main/platforms/MacOS + https://github.com/dotnet/maui-labs.git + git + README.md + + diff --git a/platforms/MacOS/Directory.Build.targets b/platforms/MacOS/Directory.Build.targets new file mode 100644 index 000000000..0b1760d19 --- /dev/null +++ b/platforms/MacOS/Directory.Build.targets @@ -0,0 +1,5 @@ + + + + + diff --git a/platforms/MacOS/Directory.Packages.props b/platforms/MacOS/Directory.Packages.props new file mode 100644 index 000000000..6fb5f6cc7 --- /dev/null +++ b/platforms/MacOS/Directory.Packages.props @@ -0,0 +1,8 @@ + + + + false + false + + + diff --git a/platforms/MacOS/LICENSE b/platforms/MacOS/LICENSE new file mode 100644 index 000000000..94286c941 --- /dev/null +++ b/platforms/MacOS/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) .NET Foundation and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/platforms/MacOS/MacOS.slnx b/platforms/MacOS/MacOS.slnx new file mode 100644 index 000000000..efeab01fe --- /dev/null +++ b/platforms/MacOS/MacOS.slnx @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/platforms/MacOS/README.md b/platforms/MacOS/README.md new file mode 100644 index 000000000..5833a72e4 --- /dev/null +++ b/platforms/MacOS/README.md @@ -0,0 +1,141 @@ +# .NET MAUI for macOS (AppKit) + +A native [.NET MAUI](https://dot.net/maui) backend for macOS using AppKit — not Mac Catalyst. + +This backend lets MAUI applications run as true native macOS apps that use AppKit controls +(`NSWindow`, `NSButton`, `NSScrollView`, etc.) and follow standard macOS UI conventions +(menu bar, toolbar, sidebar flyout, native dialogs, etc.). + +> **Inspiration:** Originally based on the +> [shinyorg/mauiplatforms](https://github.com/shinyorg/mauiplatforms) project. The Xamarin.Forms +> [`Xamarin.Forms.Platform.MacOS`](https://github.com/xamarin/Xamarin.Forms/tree/5.0.0/Xamarin.Forms.ControlGallery.MacOS) +> backend is also a useful historical reference for AppKit control mappings, although this +> project uses MAUI's modern handler architecture rather than the legacy renderer model. + +## Packages + +| Package | Description | +| --- | --- | +| `Microsoft.Maui.Platforms.MacOS` | Core handlers, hosting, platform services | +| `Microsoft.Maui.Platforms.MacOS.Essentials` | MAUI Essentials implementations (clipboard, preferences, sensors, …) | +| `Microsoft.Maui.Platforms.MacOS.BlazorWebView` | Blazor Hybrid (`BlazorWebView`) support | + +## Prerequisites + +- .NET 10 SDK +- macOS 14 (Sonoma) or later +- Xcode command line tools (for `sips` / `iconutil` — used by the icon build target) + +## Quick start + +### 1. Project file + +```xml + + + net10.0-macos + Exe + true + true + 14.0 + + My macOS App + com.example.myapp + + + + + + + + + + + + +``` + +### 2. `Main.cs` + +```csharp +using AppKit; + +public class MainClass +{ + static void Main(string[] args) + { + NSApplication.Init(); + NSApplication.SharedApplication.Delegate = new MauiMacOSApp(); + NSApplication.Main(args); + } +} +``` + +### 3. `MauiMacOSApp.cs` + +```csharp +using Foundation; +using Microsoft.Maui.Platforms.MacOS; + +[Register("MauiMacOSApp")] +public class MauiMacOSApp : MacOSMauiApplication +{ + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); +} +``` + +### 4. `MauiProgram.cs` + +```csharp +using Microsoft.Maui.Platforms.MacOS.Hosting; + +public static class MauiProgram +{ + public static MauiApp CreateMauiApp() + { + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiAppMacOS() + .AddMacOSEssentials() + .ConfigureFonts(fonts => + { + fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); + }); + + return builder.Build(); + } +} +``` + +### 5. `App.cs` + +```csharp +public class App : Application +{ + protected override Window CreateWindow(IActivationState? activationState) + => new Window(new MainPage()); +} +``` + +## Building / running the sample + +```bash +dotnet build platforms/MacOS/MacOS.slnx +dotnet run --project platforms/MacOS/samples/MacOS.Sample/ +``` + +## MAUI DevFlow integration + +The sample app supports the optional in-process MAUI DevFlow agent: + +```bash +dotnet run --project platforms/MacOS/samples/MacOS.Sample/ -p:EnableMauiDevFlow=true +``` + +This exposes a local HTTP API and MCP server for inspecting the running app's visual tree, +capturing screenshots, automating interactions, and more. See `src/DevFlow/` and the +`maui-platform-backend` skill's `devflow-integration.md` reference for details. + +## License + +MIT — see [LICENSE](LICENSE). diff --git a/platforms/MacOS/docs/README.md b/platforms/MacOS/docs/README.md new file mode 100644 index 000000000..788ab1077 --- /dev/null +++ b/platforms/MacOS/docs/README.md @@ -0,0 +1,40 @@ +# macOS AppKit MAUI Backend — docs + +This directory contains design and reference documentation for the +`Microsoft.Maui.Platforms.MacOS` backend (and its Essentials / BlazorWebView companions). + +## Layout + +- [`handler-audit-status.md`](handler-audit-status.md) — implementation status / audit + carried over from the upstream `shinyorg/mauiplatforms` checklist. Tracks which MAUI + controls have native AppKit handlers and what's still TODO. +- [`guides/`](guides/) — feature guides (MenuBar, Toolbar, Sidebar, Theming, Lifecycle, + Window, Blazor Hybrid, etc.). + +## Quick start + +See the platform [README](../README.md) for a minimal `MauiProgram.CreateMauiApp()` +example using `UseMauiAppMacOS()`. + +## Origin + +The implementation was migrated from +[shinyorg/mauiplatforms](https://github.com/shinyorg/mauiplatforms) (commit `62d4022`) +and refactored to match the conventions of the canonical `Linux.Gtk4` backend in this +repository: + +| Upstream (shinyorg) | maui-labs | +| --- | --- | +| `Platform.Maui.MacOS` | `Microsoft.Maui.Platforms.MacOS` | +| `Platform.Maui.MacOS.Essentials` | `Microsoft.Maui.Platforms.MacOS.Essentials` | +| `Platform.Maui.MacOS.BlazorWebView` | `Microsoft.Maui.Platforms.MacOS.BlazorWebView` | +| `Microsoft.Maui.Platform.MacOS[.Sub]` namespace | `Microsoft.Maui.Platforms.MacOS[.Sub]` namespace | +| `MacOS*` class prefix | `MacOS*` (preserved) | +| `UseMauiAppMacOS()` | `UseMauiAppMacOS()` (unchanged) | + +Folder-level changes (to match the canonical Gtk4 layout): + +- `Dispatching/MacOSDispatcher*.cs` collapsed into `Platform/`. +- `Hosting/MacOSMauiApplication.cs`, `Hosting/MacOSMauiContext*.cs`, and + `Handlers/MacOSFontNamedSizeService.cs` moved into `Platform/`. +- `build/*.targets` ship from `buildTransitive/`. diff --git a/platforms/MacOS/docs/guides/blazor-hybrid.md b/platforms/MacOS/docs/guides/blazor-hybrid.md new file mode 100644 index 000000000..ef6e13a86 --- /dev/null +++ b/platforms/MacOS/docs/guides/blazor-hybrid.md @@ -0,0 +1,157 @@ +# Blazor Hybrid + +Host Blazor components in a native macOS WKWebView using the BlazorWebView control. + +## Setup + +### 1. Add the NuGet Package + +```xml + + +``` + +### 2. Register the Handler + +```csharp +// MauiProgram.cs +using Microsoft.Maui.Platforms.MacOS.Hosting; + +public static MauiApp CreateMauiApp() +{ + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiAppMacOS() + .AddMacOSBlazorWebView(); // Register BlazorWebView handler + + return builder.Build(); +} +``` + +### 3. Link wwwroot Resources + +In your macOS app head `.csproj`, link the Blazor static assets: + +```xml + + + +``` + +The `wwwroot/` folder should contain your `index.html` and any static assets (CSS, JS, images). + +### 4. Use BlazorWebView in a Page + +```csharp +using Microsoft.Maui.Platforms.MacOS.Controls; + +var blazorView = new MacOSBlazorWebView +{ + HostPage = "wwwroot/index.html", +}; + +blazorView.RootComponents.Add(new RootComponent +{ + Selector = "#app", + ComponentType = typeof(MyBlazorApp.Main), +}); + +Content = blazorView; +``` + +## How It Works + +- **WKWebView**: Blazor components are rendered inside a native `WKWebView` +- **Asset loading**: The `MacOSMauiAssetFileProvider` loads files from the app bundle +- **JavaScript interop**: Full Blazor JS interop support via the WebView bridge +- **Threading**: `MacOSBlazorDispatcher` ensures UI updates run on the main AppKit thread + +## Conditional Compilation + +If your shared project has Blazor pages that should only be available on macOS: + +```xml + + + $(DefineConstants);MACAPP + +``` + +```csharp +#if MACAPP +// Register Blazor-specific Shell routes +shell.Items.Add(new ShellContent +{ + Title = "Blazor", + Route = "blazor", + ContentTemplate = new DataTemplate(typeof(BlazorPage)), +}); +#endif +``` + +## Debugging with MauiDevFlow + +If you have [MauiDevFlow](https://github.com/Redth/MauiDevFlow) set up, you can inspect Blazor WebView content via CDP: + +```bash +maui-devflow cdp snapshot # View DOM as accessible text +maui-devflow cdp Runtime evaluate "document.title" # Run JS +``` + +## Content Insets + +The `ContentInsets` property controls how the WebView's scrollable content is positioned within its bounds. Content scrolls through the full `WKWebView` area, but the scroll indicators and initial content position are inset by the specified amounts. + +This is useful when your BlazorWebView extends behind the toolbar or titlebar (e.g., with `FullSizeContentView` enabled) — you can inset the top so content isn't obscured. + +### Usage + +```csharp +var blazorView = new MacOSBlazorWebView +{ + HostPage = "wwwroot/index.html", + ContentInsets = new Thickness(0, 52, 0, 0), // 52pt top inset (toolbar height) +}; +``` + +### Dynamic Updates + +Content insets can be changed at runtime and the WebView will update immediately: + +```csharp +blazorView.ContentInsets = new Thickness(0, 38, 0, 0); // Adjust for compact toolbar +``` + +### How It Works + +- Uses `_topContentInset` on WKWebView (macOS 14+) for top insets +- Uses `ObscuredContentInsets` via `NSEdgeInsets` when available (future macOS versions) for all edges +- The `Thickness` maps to `NSEdgeInsets(top, left, bottom, right)` +- Scroll indicators honor the insets — they won't appear behind obscuring UI + +> **Note:** On current macOS versions, only the **top** inset is reliably supported. Left, bottom, and right insets will take effect when `ObscuredContentInsets` becomes available in a future macOS SDK. + +## Titlebar Drag (FullSizeContentView) + +When `FullSizeContentView` is enabled (the default), the `WKWebView` extends behind the toolbar and can intercept mouse events in the titlebar area, making the window undraggable from the content region. + +The `BlazorWebViewHandler` automatically installs a transparent drag overlay that: + +- Captures mouse events in the titlebar zone and initiates `Window.PerformWindowDrag()` +- Supports double-click to zoom the window +- Passes all other mouse events through to the WKWebView below + +This happens automatically — no configuration needed. If you disable `FullSizeContentView`, the overlay is not installed. + +## Transparent Background + +To let the native window background show through the WebView, set the HTML body background to `transparent`: + +```css +body { + background-color: transparent; +} +``` + +The handler automatically sets `drawsBackground = false` on the underlying `WKWebView`, so transparent CSS backgrounds work out of the box. diff --git a/platforms/MacOS/docs/guides/controls.md b/platforms/MacOS/docs/guides/controls.md new file mode 100644 index 000000000..1248b20b0 --- /dev/null +++ b/platforms/MacOS/docs/guides/controls.md @@ -0,0 +1,162 @@ +# Controls & Platform Notes + +Platform-specific control behaviors, custom controls, and macOS-specific features. + +## App Icons + +The `MauiIcon` build item is automatically converted to a macOS `.icns` file at build time. No manual icon generation is needed. + +```xml + +``` + +The build targets use `sips` and `iconutil` to generate all required sizes: + +| Size | File | +|------|------| +| 16×16 | `icon_16x16.png` | +| 32×32 | `icon_16x16@2x.png`, `icon_32x32.png` | +| 64×64 | `icon_32x32@2x.png` | +| 128×128 | `icon_128x128.png` | +| 256×256 | `icon_128x128@2x.png`, `icon_256x256.png` | +| 512×512 | `icon_256x256@2x.png`, `icon_512x512.png` | +| 1024×1024 | `icon_512x512@2x.png` | + +The `CFBundleIconFile` entry is automatically injected into `Info.plist`. + +## Modal Pages + +Modal pages are presented as sheet-style overlays with: + +- Semi-transparent backdrop (40% black) +- `NSVisualEffectView` with `WindowBackground` material for vibrancy +- 20px inset from safe area edges +- 10pt rounded corners +- Automatic light/dark mode adaptation + +```csharp +await Navigation.PushModalAsync(new MyModalPage()); +await Navigation.PopModalAsync(); +``` + +Multiple modals can be stacked — each new modal overlays the previous one. + +## MapView + +A custom `MapView` control wrapping `MKMapView`: + +```csharp +using Microsoft.Maui.Platforms.MacOS.Controls; + +var map = new MapView +{ + Latitude = 47.6062, + Longitude = -122.3321, + LatitudeDelta = 0.05, + LongitudeDelta = 0.05, + MapType = MapType.Standard, + IsScrollEnabled = true, + IsZoomEnabled = true, + IsShowingUser = true, +}; + +// Add overlays +map.Pins.Add(new MapPin { Latitude = 47.6062, Longitude = -122.3321, Title = "Seattle" }); +map.Circles.Add(new MapCircle { Center = new Location(47.6, -122.3), Radius = 500 }); +map.Polylines.Add(new MapPolyline { Points = { ... } }); +map.Polygons.Add(new MapPolygon { Points = { ... } }); +``` + +### MapType Values + +| Value | Description | +|-------|-------------| +| `Standard` | Street map | +| `Satellite` | Satellite imagery | +| `Hybrid` | Streets overlaid on satellite | + +## Gesture Recognizers + +All MAUI gesture recognizers are supported via `NSGestureRecognizer` wrappers: + +| MAUI Gesture | macOS Implementation | +|-------------|---------------------| +| `TapGestureRecognizer` | `NSClickGestureRecognizer` with configurable tap count | +| `PanGestureRecognizer` | `NSPanGestureRecognizer` with velocity tracking | +| `SwipeGestureRecognizer` | Custom four-direction swipe detection | +| `PinchGestureRecognizer` | `NSMagnificationGestureRecognizer` | +| `PointerGestureRecognizer` | `NSTrackingArea` for enter/exit/move events | + +## Coordinate System + +macOS AppKit uses a bottom-left origin coordinate system, but the platform automatically handles the conversion: + +- All `NSView` subclasses used by the platform override `IsFlipped = true` to use MAUI's top-left origin +- Layout and hit-testing work with MAUI coordinates — no manual conversion needed + +## Multi-Window Support + +The platform supports `Application.OpenWindow()` for creating multiple windows: + +```csharp +Application.Current.OpenWindow(new Window(new SecondPage())); +``` + +Each window gets its own toolbar state, title, and lifecycle events. + +## Fonts + +- Default system font: SF Pro Display (13pt) +- Custom fonts registered via `MauiFont` are loaded with `CTFontManager` +- Font weight mapping supports Bold and Light traits + +```csharp +builder.ConfigureFonts(fonts => +{ + fonts.AddFont("MyFont-Regular.ttf", "MyFont"); + fonts.AddFont("MyFont-Bold.ttf", "MyFontBold"); +}); +``` + +## Text Input + +- `Entry` maps to `NSTextField` +- `Editor` maps to `NSTextView` in an `NSScrollView` +- `SearchBar` maps to `NSSearchField` + +All support standard macOS text editing behaviors (spell check, auto-correct, Services menu). + +## Dispatcher & Threading + +The platform provides a main-thread dispatcher using `DispatchQueue.MainQueue`. + +> **⚠️ Important:** `MainThread.BeginInvokeOnMainThread()` and `MainThread.InvokeOnMainThreadAsync()` from `Microsoft.Maui.ApplicationModel` will throw `NotImplementedInReferenceAssemblyException` on macOS AppKit. Use `Dispatcher` or `IDispatcher` instead: + +```csharp +// ✅ Correct — use Dispatcher (available on any BindableObject / View) +Dispatcher.Dispatch(() => +{ + myLabel.Text = "Updated"; +}); + +// ✅ Correct — use IDispatcher from DI +var dispatcher = serviceProvider.GetRequiredService(); +dispatcher.Dispatch(() => { /* UI work */ }); + +// ✅ Correct — use MainThreadHelper from Essentials package +MainThreadHelper.BeginInvokeOnMainThread(() => { /* UI work */ }); + +// ❌ Throws NotImplementedInReferenceAssemblyException +// MainThread.BeginInvokeOnMainThread(() => { }); +``` + +The `MainThreadHelper` class in `Microsoft.Maui.Platforms.MacOS.Essentials` provides a familiar static API as an alternative: + +```csharp +using Microsoft.Maui.Platforms.MacOS.Essentials; + +if (!MainThreadHelper.IsMainThread) + MainThreadHelper.BeginInvokeOnMainThread(UpdateUI); +``` + +Animation tickers use `NSTimer` for smooth AppKit-thread animation timing. diff --git a/platforms/MacOS/docs/guides/getting-started.md b/platforms/MacOS/docs/guides/getting-started.md new file mode 100644 index 000000000..43b3ac0fd --- /dev/null +++ b/platforms/MacOS/docs/guides/getting-started.md @@ -0,0 +1,281 @@ +# Getting Started + +Set up a .NET MAUI macOS (AppKit) app from scratch, or add macOS support to an existing MAUI project. + +## Overview + +The macOS backend runs as a separate **app head project** that references your shared MAUI code. This is similar to how MAUI uses platform-specific head projects, but since the macOS/AppKit backend is a community package (not built into the MAUI workload), you create a standalone `net10.0-macos` project that links to your shared pages, resources, and platform code. + +``` +MyApp/ +├── MyApp/ # Shared MAUI project (pages, view models, etc.) +│ ├── Pages/ +│ ├── Resources/ +│ │ ├── Fonts/ +│ │ └── AppIcon/ +│ ├── wwwroot/ # (if using Blazor Hybrid) +│ └── Platforms/ +│ └── macOS/ # macOS platform-specific code +│ ├── App.cs +│ ├── Main.cs +│ ├── MauiMacOSApp.cs +│ └── MauiProgram.cs +│ +├── MyApp.MacOS/ # macOS app head project +│ ├── MyApp.MacOS.csproj +│ ├── Info.plist # (optional — auto-generated if absent) +│ └── Entitlements.plist # (optional — for sandboxing, etc.) +│ +└── MyApp.sln +``` + +## Step 1: Create the macOS App Head Project + +Create a new class library targeting `net10.0-macos`: + +```xml + + + + + Exe + net10.0-macos + 14.0 + MyApp + + + false + false + + + + + + + + + +``` + +> **Note:** If referencing the platform project source instead of NuGet, use `` instead of ``. + +## Step 2: Link Shared Code + +Link your shared pages, view models, and helpers from the main MAUI project: + +```xml + + + + + + + + + + + + + + +``` + +### Link Resources + +```xml + + + + + + + + + + +``` + +### App Icon + +The `MauiIcon` item is automatically processed into a macOS `.icns` file at build time. The build targets use `sips` and `iconutil` to generate all required icon sizes (16×16 through 512×512 with @2x variants) from your source PNG or SVG. + +## Step 3: Create Platform Bootstrap Files + +Create these 4 files in your shared project's `Platforms/macOS/` folder. + +### Main.cs — Entry Point + +```csharp +// Platforms/macOS/Main.cs +using AppKit; + +namespace MyApp; + +static class MainClass +{ + static void Main(string[] args) + { + NSApplication.Init(); + NSApplication.SharedApplication.Delegate = new MauiMacOSApp(); + NSApplication.Main(args); + } +} +``` + +### MauiMacOSApp.cs — Application Delegate + +```csharp +// Platforms/macOS/MauiMacOSApp.cs +using Foundation; +using Microsoft.Maui.Platforms.MacOS.Hosting; + +namespace MyApp; + +[Register("MauiMacOSApp")] +public class MauiMacOSApp : MacOSMauiApplication +{ + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); +} +``` + +### MauiProgram.cs — App Builder + +```csharp +// Platforms/macOS/MauiProgram.cs +using Microsoft.Maui.Hosting; +using Microsoft.Maui.Platforms.MacOS.Hosting; + +namespace MyApp; + +public static class MauiProgram +{ + public static MauiApp CreateMauiApp() + { + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiAppMacOS() + .ConfigureFonts(fonts => + { + fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); + fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); + }); + + return builder.Build(); + } +} +``` + +### App.cs — MAUI Application + +```csharp +// Platforms/macOS/App.cs +using Microsoft.Maui.Controls; + +namespace MyApp; + +public class MacOSApp : Application +{ + protected override Window CreateWindow(IActivationState? activationState) + { + return new Window(new MainPage()); + } +} +``` + +Or with Shell navigation: + +```csharp +public class MacOSApp : Application +{ + protected override Window CreateWindow(IActivationState? activationState) + { + var shell = new Shell(); + + // Enable native macOS sidebar + MacOSShell.SetUseNativeSidebar(shell, true); + + // Add shell items + var main = new ShellContent + { + Title = "Home", + ContentTemplate = new DataTemplate(typeof(MainPage)), + Route = "home" + }; + shell.Items.Add(main); + + return new Window(shell); + } +} +``` + +## Step 4: Build and Run + +```bash +# Build +dotnet build MyApp.MacOS/MyApp.MacOS.csproj + +# The app bundle is created at: +# MyApp.MacOS/bin/Debug/net10.0-macos/osx-arm64/MyApp.app + +# Launch +open MyApp.MacOS/bin/Debug/net10.0-macos/osx-arm64/MyApp.app +``` + +## Conditional Compilation + +Use `#if` directives for macOS-specific code in shared files: + +```xml + + + $(DefineConstants);MACAPP + +``` + +```csharp +#if MACAPP +using Microsoft.Maui.Platforms.MacOS.Platform; + +// macOS-specific code +MacOSWindow.SetTitlebarStyle(window, MacOSTitlebarStyle.UnifiedCompact); +#endif +``` + +## Adding Platform-Specific Handlers + +Register custom handlers or override existing ones in `MauiProgram.cs`: + +```csharp +builder.ConfigureMauiHandlers(handlers => +{ + // Use native sidebar for FlyoutPage + handlers.AddHandler(); +}); +``` + +## Optional: Blazor Hybrid Support + +See [Blazor Hybrid](blazor-hybrid.md) for adding BlazorWebView support. + +## Optional: Essentials Support + +Add the Essentials package for macOS implementations of device APIs: + +```xml + +``` + +```csharp +// In MauiProgram.cs +builder.AddMacOSEssentials(); +``` + +## Tips + +- **SF Symbols**: Use SF Symbol names directly as `IconImageSource` values (e.g., `"gear"`, `"plus"`, `"square.and.arrow.up"`) +- **Window size**: Set initial window size via `Window.Width` and `Window.Height` in your `App.CreateWindow()` +- **Debug**: Use `open MyApp.app` to launch — the app runs independently of the terminal +- **Hot reload**: Not currently supported — rebuild and relaunch for changes +- **MainThread**: `MainThread.BeginInvokeOnMainThread()` is **not supported** — use `Dispatcher.Dispatch()` or `MainThreadHelper.BeginInvokeOnMainThread()` instead (see [Controls & Platform Notes](controls.md#dispatcher--threading)) diff --git a/platforms/MacOS/docs/guides/index.md b/platforms/MacOS/docs/guides/index.md new file mode 100644 index 000000000..2fbd1021c --- /dev/null +++ b/platforms/MacOS/docs/guides/index.md @@ -0,0 +1,59 @@ +# macOS (AppKit) Platform APIs + +This .NET MAUI backend provides native macOS experiences using AppKit. The following platform-specific APIs let you build apps that look and feel like first-class Mac citizens. + +## Guides + +| Topic | Description | +|-------|-------------| +| [Getting Started](getting-started.md) | Create a macOS app head project, link shared code & resources | +| [Sidebar Navigation](sidebar.md) | Native `NSSplitViewController` sidebar for Shell and FlyoutPage | +| [Toolbar](toolbar.md) | NSToolbar with native item types, placement, and layout | +| [Toolbar Item Types](toolbar-items.md) | Search, menu, segmented control, share, and popup toolbar items | +| [Window Configuration](window.md) | Titlebar style, transparency, and toolbar style | +| [Menu Bar](menu-bar.md) | Application menu bar and default menus | +| [Lifecycle Events](lifecycle.md) | App and window lifecycle events | +| [Theming](theming.md) | Light/dark mode and appearance | +| [Blazor Hybrid](blazor-hybrid.md) | BlazorWebView with WKWebView | +| [Controls & Platform Notes](controls.md) | MapView, gestures, modals, icons, fonts, threading | + +## Quick Start + +Register the macOS backend in your app: + +```csharp +// MauiProgram.cs +builder.UseMauiApp() + .UseMacOS(); +``` + +### Native Sidebar with Shell + +```csharp +var shell = new Shell(); +MacOSShell.SetUseNativeSidebar(shell, true); +``` + +### Toolbar Items + +```csharp +// Add a toolbar item to the sidebar area +var item = new ToolbarItem { Text = "Refresh", IconImageSource = "arrow.clockwise" }; +MacOSToolbarItem.SetPlacement(item, MacOSToolbarItemPlacement.SidebarLeading); +page.ToolbarItems.Add(item); +``` + +### Window Titlebar + +```csharp +MacOSWindow.SetTitlebarStyle(window, MacOSTitlebarStyle.UnifiedCompact); +MacOSWindow.SetTitlebarTransparent(window, true); +``` + +## Platform-Specific Namespace + +All macOS APIs are in the `Microsoft.Maui.Platforms.MacOS.Platform` namespace: + +```csharp +using Microsoft.Maui.Platforms.MacOS.Platform; +``` diff --git a/platforms/MacOS/docs/guides/lifecycle.md b/platforms/MacOS/docs/guides/lifecycle.md new file mode 100644 index 000000000..e72a59ec9 --- /dev/null +++ b/platforms/MacOS/docs/guides/lifecycle.md @@ -0,0 +1,97 @@ +# Lifecycle Events + +Subscribe to macOS application lifecycle events for state management, analytics, and cleanup. + +## Available Events + +| Event | When it Fires | +|-------|---------------| +| `DidFinishLaunching` | App has finished launching and is ready | +| `DidBecomeActive` | App (or a window) gained focus | +| `DidResignActive` | App (or a window) lost focus | +| `DidHide` | App was hidden (⌘H or Hide menu) | +| `DidUnhide` | App was unhidden / shown again | +| `WillTerminate` | App is about to quit | + +## Subscribing to Events + +Register lifecycle handlers in `MauiProgram.cs`: + +```csharp +// MauiProgram.cs +builder.ConfigureLifecycleEvents(events => +{ + events.AddMacOS(mac => + { + mac.DidFinishLaunching((notification) => + { + Console.WriteLine("App launched"); + }); + + mac.DidBecomeActive((notification) => + { + Console.WriteLine("App became active"); + }); + + mac.DidResignActive((notification) => + { + Console.WriteLine("App resigned active"); + }); + + mac.DidHide((notification) => + { + Console.WriteLine("App hidden"); + }); + + mac.DidUnhide((notification) => + { + Console.WriteLine("App shown"); + }); + + mac.WillTerminate((notification) => + { + Console.WriteLine("App terminating — save state"); + }); + }); +}); +``` + +## MAUI Application Lifecycle + +The standard MAUI `Application` lifecycle methods also work: + +```csharp +public class MacOSApp : Application +{ + protected override Window CreateWindow(IActivationState? activationState) + { + var window = new Window(new MainPage()); + + window.Created += (s, e) => Console.WriteLine("Window created"); + window.Activated += (s, e) => Console.WriteLine("Window activated"); + window.Deactivated += (s, e) => Console.WriteLine("Window deactivated"); + window.Stopped += (s, e) => Console.WriteLine("Window stopped"); + window.Resumed += (s, e) => Console.WriteLine("Window resumed"); + window.Destroying += (s, e) => Console.WriteLine("Window destroying"); + + return window; + } +} +``` + +## Window Close Button + +When the user clicks the red close button (traffic light), the platform fires `IWindow.Destroying()` via a `MacOSWindowDelegate`. This allows you to save state or prompt the user before the window closes. + +## Event Flow + +Typical lifecycle sequence: + +``` +App start: DidFinishLaunching → DidBecomeActive +Switch away: DidResignActive +Switch back: DidBecomeActive +Hide (⌘H): DidResignActive → DidHide +Unhide: DidUnhide → DidBecomeActive +Quit (⌘Q): DidResignActive → WillTerminate +``` diff --git a/platforms/MacOS/docs/guides/menu-bar.md b/platforms/MacOS/docs/guides/menu-bar.md new file mode 100644 index 000000000..cf209e985 --- /dev/null +++ b/platforms/MacOS/docs/guides/menu-bar.md @@ -0,0 +1,88 @@ +# Menu Bar + +Configure the native macOS application menu bar. + +## Default Menus + +The platform automatically creates standard macOS menus: + +- **App menu** — About, Preferences, Quit (with ⌘Q) +- **Edit menu** — Undo, Redo, Cut, Copy, Paste, Delete, Select All +- **Window menu** — Minimize, Zoom, Full Screen + +### Customizing Defaults + +Control which default menus are included via `ConfigureMacOSMenuBar()`: + +```csharp +// MauiProgram.cs +builder.ConfigureMacOSMenuBar(options => +{ + options.IncludeDefaultMenus = true; // Master toggle (default: true) + options.IncludeDefaultEditMenu = true; // Edit menu (default: true) + options.IncludeDefaultWindowMenu = true; // Window menu (default: true) +}); +``` + +Setting `IncludeDefaultMenus = false` disables all default menus. You can then build the entire menu bar from scratch using `Page.MenuBarItems`. + +## Page-Level Menu Items + +Add custom menus via `Page.MenuBarItems` on any `ContentPage`: + +```csharp +public class MyPage : ContentPage +{ + public MyPage() + { + // Add a "File" menu + var fileMenu = new MenuBarItem { Text = "File" }; + + fileMenu.Add(new MenuFlyoutItem + { + Text = "New", + Command = new Command(() => CreateNew()), + KeyboardAccelerators = + { + new KeyboardAccelerator { Modifiers = KeyboardAcceleratorModifiers.Cmd, Key = "n" } + } + }); + + fileMenu.Add(new MenuFlyoutItem + { + Text = "Open...", + Command = new Command(() => OpenFile()), + KeyboardAccelerators = + { + new KeyboardAccelerator { Modifiers = KeyboardAcceleratorModifiers.Cmd, Key = "o" } + } + }); + + fileMenu.Add(new MenuFlyoutSeparator()); + + fileMenu.Add(new MenuFlyoutItem + { + Text = "Save", + Command = new Command(() => Save()), + KeyboardAccelerators = + { + new KeyboardAccelerator { Modifiers = KeyboardAcceleratorModifiers.Cmd, Key = "s" } + } + }); + + MenuBarItems.Add(fileMenu); + } +} +``` + +Menu bar items are merged with the default menus. Page-level menus update automatically when the active page changes. + +## API Reference + +### MacOSMenuBarOptions + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `IncludeDefaultMenus` | `bool` | `true` | Include any default menus | +| `IncludeDefaultEditMenu` | `bool` | `true` | Include Edit menu (Undo, Copy, Paste, etc.) | +| `IncludeDefaultWindowMenu` | `bool` | `true` | Include Window menu (Minimize, Zoom, etc.) | diff --git a/platforms/MacOS/docs/guides/sidebar.md b/platforms/MacOS/docs/guides/sidebar.md new file mode 100644 index 000000000..7d4e757c3 --- /dev/null +++ b/platforms/MacOS/docs/guides/sidebar.md @@ -0,0 +1,163 @@ +# Sidebar Navigation + +Build native macOS sidebar navigation using `NSSplitViewController` with behind-window vibrancy, SF Symbol icons, and structured source-list groups. + +## Setup + +There are two sidebar implementations: **Shell sidebar** and **FlyoutPage sidebar**. Both use `NSSplitViewController` with `NSOutlineView` source-list styling. + +### Shell Sidebar + +Enable the native sidebar on a `Shell`: + +```csharp +var shell = new Shell(); +MacOSShell.SetUseNativeSidebar(shell, true); +``` + +`UseNativeSidebar` is an opt-in flag. When `true`, the `ShellHandler` creates an `NSOutlineView` with source-list styling and behind-window vibrancy. When `false` (default), the Shell uses a standard MAUI flyout layout. + +### FlyoutPage Sidebar + +For `FlyoutPage`, you must **register the handler** and **opt in** to the native sidebar: + +```csharp +// MauiProgram.cs — register the handler (required) +builder.ConfigureMauiHandlers(handlers => +{ + handlers.AddHandler(); +}); +``` + +```csharp +// Page code — opt in to NSSplitViewController sidebar (required) +var flyoutPage = new FlyoutPage(); +MacOSFlyoutPage.SetUseNativeSidebar(flyoutPage, true); +``` + +> **Important:** Both steps are required for `FlyoutPage`: +> - **Handler registration** replaces the default `FlyoutPageHandler` with `NativeSidebarFlyoutPageHandler` +> - **`UseNativeSidebar = true`** opts the specific `FlyoutPage` instance into `NSSplitViewController` with sidebar vibrancy and traffic lights inside the sidebar +> - Without `UseNativeSidebar`, the handler uses a plain `NSSplitView` without source-list styling + +The sidebar renders `ShellItem` and `ShellSection` entries in an `NSOutlineView` source list. Sections with multiple `ShellContent` children appear as expandable groups. + +### SF Symbol Icons + +Assign SF Symbol names to shell items for native icons: + +```csharp +var section = new ShellSection { Title = "Settings" }; +MacOSShell.SetSystemImage(section, "gear"); +``` + +Works on `ShellItem`, `ShellSection`, and `ShellContent`. + +### Sidebar Resizing + +By default the sidebar can be resized by dragging. To lock it: + +```csharp +MacOSShell.SetIsSidebarResizable(shell, false); +``` + +### Sidebar Width + +Set the sidebar width via `Shell.FlyoutWidth`: + +```csharp +shell.FlyoutWidth = 280; +``` + +The default width is 300px. The minimum is 150px and maximum is 400px. + +## FlyoutPage Sidebar + +`FlyoutPage` can also use the native sidebar appearance: + +```csharp +var flyoutPage = new FlyoutPage(); +MacOSFlyoutPage.SetUseNativeSidebar(flyoutPage, true); +``` + +### Structured Sidebar Items + +Provide structured items with groups, icons, and hierarchy: + +```csharp +var items = new List +{ + new MacOSSidebarItem + { + Title = "Library", + Children = new List + { + new MacOSSidebarItem { Title = "Music", SystemImage = "music.note" }, + new MacOSSidebarItem { Title = "Movies", SystemImage = "film" }, + new MacOSSidebarItem { Title = "Podcasts", SystemImage = "mic" }, + } + }, + new MacOSSidebarItem + { + Title = "Playlists", + Children = new List + { + new MacOSSidebarItem { Title = "Favorites", SystemImage = "heart" }, + new MacOSSidebarItem { Title = "Recently Added", SystemImage = "clock" }, + } + } +}; + +MacOSFlyoutPage.SetSidebarItems(flyoutPage, items); +``` + +Items with `Children` become group headers (non-selectable, bold uppercase text). Leaf items are selectable rows. + +### Selection Handling + +```csharp +MacOSFlyoutPage.SetSidebarSelectionChanged(flyoutPage, item => +{ + Console.WriteLine($"Selected: {item.Title}"); + // Navigate to the appropriate detail page + flyoutPage.Detail = new NavigationPage(GetPageForItem(item)); +}); +``` + +### Programmatic Selection + +```csharp +MacOSFlyoutPage.SetSelectedItem(flyoutPage, items[0].Children[1]); +``` + +> **Note:** Programmatic selection via `SetSelectedItem` or `SelectSidebarItem` does **not** trigger the `SidebarSelectionChanged` callback. This is intentional — it prevents infinite loops when you update the detail page in the selection callback, which would otherwise re-trigger the callback. If you need to respond to programmatic selections, update your UI directly after calling `SetSelectedItem`. + +## MacOSSidebarItem + +| Property | Type | Description | +|----------|------|-------------| +| `Title` | `string` | Display text | +| `SystemImage` | `string?` | SF Symbol name for the icon | +| `Icon` | `ImageSource?` | Custom icon image | +| `Children` | `List?` | Child items (makes this a group header) | +| `Tag` | `string?` | Identifier for programmatic lookup | +| `IsGroup` | `bool` | Read-only; `true` when `Children` is non-empty | + +## API Reference + +### MacOSShell (Attached Properties) + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `UseNativeSidebar` | `bool` | `false` | Use `NSSplitViewController` for sidebar | +| `SystemImage` | `string?` | `null` | SF Symbol name for sidebar icon | +| `IsSidebarResizable` | `bool` | `true` | Allow user to resize sidebar | + +### MacOSFlyoutPage (Attached Properties) + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `UseNativeSidebar` | `bool` | `false` | Use `NSSplitViewController` for sidebar | +| `SidebarItems` | `IList?` | `null` | Structured sidebar items | +| `SidebarSelectionChanged` | `Action?` | `null` | Selection callback | +| `SelectedItem` | `MacOSSidebarItem?` | `null` | Currently selected item | diff --git a/platforms/MacOS/docs/guides/theming.md b/platforms/MacOS/docs/guides/theming.md new file mode 100644 index 000000000..0f9333e57 --- /dev/null +++ b/platforms/MacOS/docs/guides/theming.md @@ -0,0 +1,56 @@ +# Theming + +Light and dark mode support with automatic theme change detection. + +## Setting the App Theme + +Set the application theme programmatically: + +```csharp +Application.Current.UserAppTheme = AppTheme.Dark; // Force dark +Application.Current.UserAppTheme = AppTheme.Light; // Force light +Application.Current.UserAppTheme = AppTheme.Unspecified; // Follow system +``` + +The platform maps MAUI themes to macOS appearances: + +| `AppTheme` | `NSAppearance` | +|------------|----------------| +| `Light` | `NSAppearance.NameAqua` | +| `Dark` | `NSAppearance.NameDarkAqua` | +| `Unspecified` | System setting (follows macOS Appearance preference) | + +## Responding to Theme Changes + +When macOS switches between light and dark mode (via System Settings or auto-schedule), the platform automatically detects the change and fires `Application.RequestedThemeChanged`: + +```csharp +Application.Current.RequestedThemeChanged += (s, e) => +{ + Console.WriteLine($"Theme changed to: {e.RequestedTheme}"); +}; +``` + +### AppThemeBinding in XAML + +Use `AppThemeBinding` for theme-aware resources (works the same as iOS/Android): + +```xml +