From bccb425f7489c1803f2b7a4c7d6b72e79c45946c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 31 Jul 2026 18:04:12 +1000 Subject: [PATCH 1/8] Add Avalonia Catalog sample and backend --- .gitignore | 2 - ImageSharp.Drawing.All.sln | 740 ++++++- samples/AvaloniaControlCatalog/App.xaml | 97 + samples/AvaloniaControlCatalog/App.xaml.cs | 175 ++ .../Assets/CurvedHeader/avatar.jpg | 3 + .../Assets/CurvedHeader/featured.jpg | 3 + .../Assets/CurvedHeader/product1.jpg | 3 + .../Assets/CurvedHeader/product2.jpg | 3 + .../Assets/CurvedHeader/product3.jpg | 3 + .../Assets/CurvedHeader/update1.jpg | 3 + .../Assets/CurvedHeader/update2.jpg | 3 + .../Assets/CurvedHeader/update3.jpg | 3 + .../Assets/Fonts/SourceSansPro-Bold.ttf | 3 + .../Assets/Fonts/SourceSansPro-BoldItalic.ttf | 3 + .../Assets/Fonts/SourceSansPro-Italic.ttf | 3 + .../Assets/Fonts/SourceSansPro-Regular.ttf | 3 + .../Assets/Fonts/WenQuanYiMicroHei-01.ttf | 3 + .../AvaloniaControlCatalog/Assets/Icon.png | 3 + .../Assets/ModernApp/avatar.jpg | 3 + .../Assets/ModernApp/dest_alps.jpg | 3 + .../Assets/ModernApp/dest_forest.jpg | 3 + .../Assets/ModernApp/dest_norway.jpg | 3 + .../Assets/ModernApp/exp_angkor.jpg | 3 + .../Assets/ModernApp/exp_tokyo.jpg | 3 + .../Assets/ModernApp/gallery_alpine.jpg | 3 + .../Assets/ModernApp/gallery_bay.jpg | 3 + .../Assets/ModernApp/gallery_city.jpg | 3 + .../Assets/ModernApp/gallery_paris.jpg | 3 + .../Assets/ModernApp/gallery_tropical.jpg | 3 + .../Assets/ModernApp/gallery_venice.jpg | 3 + .../Assets/ModernApp/story1.jpg | 3 + .../Assets/ModernApp/story2.jpg | 3 + .../Assets/ModernApp/story3.jpg | 3 + .../Assets/Movies/cast1.jpg | 3 + .../Assets/Movies/cast2.jpg | 3 + .../Assets/Movies/continue1.jpg | 3 + .../Assets/Movies/hero.jpg | 3 + .../Assets/Movies/morelike1.jpg | 3 + .../Assets/Movies/search1.jpg | 3 + .../Assets/Movies/toprated1.jpg | 3 + .../Assets/Movies/toprated2.jpg | 3 + .../Assets/Movies/toprated3.jpg | 3 + .../Assets/Movies/toprated4.jpg | 3 + .../Assets/Movies/trending1.jpg | 3 + .../Assets/Movies/trending2.jpg | 3 + .../Assets/Pulse/cat_hiit.jpg | 3 + .../Assets/Pulse/cat_strength.jpg | 3 + .../Assets/Pulse/cat_yoga.jpg | 3 + .../Assets/Pulse/ex_bench.jpg | 3 + .../Assets/Pulse/ex_deadlifts.jpg | 3 + .../Assets/Pulse/ex_overhead.jpg | 3 + .../Assets/Pulse/ex_pullups.jpg | 3 + .../Assets/Pulse/ex_squats.jpg | 3 + .../Assets/Pulse/profile_avatar.jpg | 3 + .../Assets/Pulse/rec_fullbody.jpg | 3 + .../Assets/Pulse/rec_mobility.jpg | 3 + .../Assets/Pulse/rec_powercore.jpg | 3 + .../Assets/Pulse/workout_hero.jpg | 3 + .../Assets/Restaurant/dish1.jpg | 3 + .../Assets/Restaurant/dish2.jpg | 3 + .../Assets/Restaurant/dish3.jpg | 3 + .../Assets/Restaurant/dish4.jpg | 3 + .../Assets/Restaurant/featured_dish.jpg | 3 + .../Assets/Restaurant/user_avatar.jpg | 3 + .../Assets/RetroGaming/cyber_city.jpg | 3 + .../Assets/RetroGaming/dungeon_bit.jpg | 3 + .../Assets/RetroGaming/forest_spirit.jpg | 3 + .../Assets/RetroGaming/hero.jpg | 3 + .../Assets/RetroGaming/neon_ninja.jpg | 3 + .../Assets/RetroGaming/neon_racer.jpg | 3 + .../Assets/RetroGaming/pixel_quest.jpg | 3 + .../Assets/RetroGaming/space_voids.jpg | 3 + .../Assets/Sanctuary/city_bg.jpg | 3 + .../Assets/Sanctuary/forest_bg.jpg | 3 + .../Assets/Sanctuary/main_arctic_silence.jpg | 3 + .../Assets/Sanctuary/main_deep_forest.jpg | 3 + .../Assets/Sanctuary/main_desert_sands.jpg | 3 + .../Assets/Sanctuary/main_hero.jpg | 3 + .../Assets/Sanctuary/mountain_bg.jpg | 3 + .../Assets/avalonia-32.png | 3 + .../Assets/delicate-arch-896885_640.jpg | 3 + .../Assets/github_icon.png | 3 + .../Assets/hirsch-899118_640.jpg | 3 + .../AvaloniaControlCatalog/Assets/image1.jpg | 3 + .../AvaloniaControlCatalog/Assets/image2.jpg | 3 + .../AvaloniaControlCatalog/Assets/image3.jpg | 3 + .../AvaloniaControlCatalog/Assets/image4.jpg | 3 + .../AvaloniaControlCatalog/Assets/image5.jpg | 3 + .../AvaloniaControlCatalog/Assets/image6.jpg | 3 + .../AvaloniaControlCatalog/Assets/image7.jpg | 3 + .../Assets/maple-leaf-888807_640.jpg | 3 + .../Assets/test_icon.ico | 3 + .../AvaloniaControlCatalog.csproj | 99 + .../AnonymousObserverNonGenericHelper.cs | 13 + .../AvaloniaPropertyBindingObservable.cs | 126 ++ .../AvaloniaPropertyChangedObservable.cs | 46 + .../AvaloniaPropertyObservable.cs | 117 ++ .../AvaloniaReactive/CombinedSubject.cs | 23 + .../AvaloniaReactive/CompositeDisposable.cs | 427 ++++ .../AvaloniaReactive/Disposable.cs | 98 + .../AvaloniaReactive/DisposableMixin.cs | 37 + .../AvaloniaReactive/IAvaloniaSubject.cs | 8 + .../LightweightObservableBase.cs | 237 +++ .../AvaloniaReactive/LightweightSubject.cs | 30 + .../AvaloniaReactive/Observable.cs | 259 +++ .../Operators/CombineLatest.cs | 374 ++++ .../AvaloniaReactive/Operators/Sink.cs | 111 + .../AvaloniaReactive/Operators/Switch.cs | 145 ++ .../SingleSubscriberObservableBase.cs | 78 + .../CombinedGeometryImpl.cs | 49 + .../Converter/DegToRadConverter.cs | 29 + .../Converter/GdpToBrushConverter.cs | 31 + .../Converter/HexConverter.cs | 34 + .../Converter/MathSubtractConverter.cs | 22 + .../TableViewColumnWidthConverter.cs | 27 + .../DecoratedWindow.xaml | 66 + .../DecoratedWindow.xaml.cs | 45 + .../DrawingBackendMode.cs | 25 + .../DrawingContextImpl.cs | 1810 +++++++++++++++++ .../EllipseGeometryImpl.cs | 32 + .../AvaloniaControlCatalog/FontManagerImpl.cs | 297 +++ .../GeometryGroupImpl.cs | 51 + .../AvaloniaControlCatalog/GeometryImpl.cs | 280 +++ .../AvaloniaControlCatalog/GlobalUsings.cs | 35 + .../AvaloniaControlCatalog/GlyphRunImpl.cs | 243 +++ .../IDrawableBitmapImpl.cs | 32 + samples/AvaloniaControlCatalog/Icons.cs | 194 ++ .../ImageSharpDrawingAppBuilderExtensions.cs | 26 + .../ImageSharpExtensions.cs | 328 +++ .../ImmutableBitmapImpl.cs | 274 +++ .../LineGeometryImpl.cs | 23 + samples/AvaloniaControlCatalog/MainView.xaml | 161 ++ .../AvaloniaControlCatalog/MainView.xaml.cs | 375 ++++ .../AvaloniaControlCatalog/MainWindow.xaml | 79 + .../AvaloniaControlCatalog/MainWindow.xaml.cs | 74 + .../MiniMvvm/MiniCommand.cs | 67 + .../MiniMvvm/PropertyChangedExtensions.cs | 110 + .../MiniMvvm/ViewModelBase.cs | 25 + .../Models/CatalogTheme.cs | 8 + .../AvaloniaControlCatalog/Models/Person.cs | 125 ++ .../Models/StateData.cs | 20 + .../AvaloniaControlCatalog/NavHeaderItem.xaml | 101 + samples/AvaloniaControlCatalog/NuGet.Config | 27 + .../Pages/AcceleratorPage.xaml | 116 ++ .../Pages/AcceleratorPage.xaml.cs | 12 + .../Pages/AcrylicPage.xaml | 165 ++ .../Pages/AcrylicPage.xaml.cs | 12 + .../Pages/AdornerLayerPage.xaml | 76 + .../Pages/AdornerLayerPage.xaml.cs | 34 + .../Pages/AutoCompleteBoxPage.xaml | 83 + .../Pages/AutoCompleteBoxPage.xaml.cs | 178 ++ .../Pages/BitmapCachePage.axaml | 49 + .../Pages/BitmapCachePage.axaml.cs | 13 + .../Pages/BorderPage.xaml | 65 + .../Pages/BorderPage.xaml.cs | 12 + .../Pages/ButtonSpinnerPage.xaml | 34 + .../Pages/ButtonSpinnerPage.xaml.cs | 49 + .../Pages/ButtonsPage.xaml | 292 +++ .../Pages/ButtonsPage.xaml.cs | 21 + .../Pages/CalendarDatePickerPage.xaml | 56 + .../Pages/CalendarDatePickerPage.xaml.cs | 23 + .../Pages/CalendarPage.xaml | 53 + .../Pages/CalendarPage.xaml.cs | 20 + .../Pages/CanvasPage.xaml | 38 + .../Pages/CanvasPage.xaml.cs | 12 + .../Pages/CarouselDemoPage.xaml | 14 + .../Pages/CarouselDemoPage.xaml.cs | 91 + .../Pages/CarouselPage.xaml | 119 ++ .../Pages/CarouselPage.xaml.cs | 149 ++ .../CarouselPage/CareCompanionAppPage.xaml | 98 + .../CarouselPage/CareCompanionAppPage.xaml.cs | 1069 ++++++++++ .../CarouselCustomizationPage.xaml | 119 ++ .../CarouselCustomizationPage.xaml.cs | 48 + .../CarouselPage/CarouselDataBindingPage.xaml | 60 + .../CarouselDataBindingPage.xaml.cs | 95 + .../CarouselPage/CarouselGalleryAppPage.xaml | 557 +++++ .../CarouselGalleryAppPage.xaml.cs | 101 + .../CarouselPage/CarouselGesturesPage.xaml | 93 + .../CarouselPage/CarouselGesturesPage.xaml.cs | 59 + .../CarouselGettingStartedPage.xaml | 74 + .../CarouselGettingStartedPage.xaml.cs | 40 + .../CarouselPage/CarouselMultiItemPage.xaml | 140 ++ .../CarouselMultiItemPage.xaml.cs | 47 + .../CarouselPageCustomizationPage.xaml | 115 ++ .../CarouselPageCustomizationPage.xaml.cs | 79 + .../CarouselPageDataTemplatePage.xaml | 44 + .../CarouselPageDataTemplatePage.xaml.cs | 209 ++ .../CarouselPage/CarouselPageEventsPage.xaml | 37 + .../CarouselPageEventsPage.xaml.cs | 92 + .../CarouselPageFirstLookPage.xaml | 61 + .../CarouselPageFirstLookPage.xaml.cs | 35 + .../CarouselPage/CarouselPageGesturePage.xaml | 64 + .../CarouselPageGesturePage.xaml.cs | 44 + .../CarouselPagePerformancePage.xaml | 45 + .../CarouselPagePerformancePage.xaml.cs | 103 + .../CarouselPageSelectionPage.xaml | 90 + .../CarouselPageSelectionPage.xaml.cs | 56 + .../CarouselPageTransitionsPage.xaml | 65 + .../CarouselPageTransitionsPage.xaml.cs | 69 + .../CarouselPage/CarouselTransitionsPage.xaml | 97 + .../CarouselTransitionsPage.xaml.cs | 66 + .../CarouselPage/CarouselVerticalPage.xaml | 132 ++ .../CarouselPage/CarouselVerticalPage.xaml.cs | 39 + .../Pages/CarouselPage/SanctuaryMainPage.xaml | 298 +++ .../CarouselPage/SanctuaryMainPage.xaml.cs | 11 + .../CarouselPage/SanctuaryShowcasePage.xaml | 335 +++ .../SanctuaryShowcasePage.xaml.cs | 70 + .../Pages/CheckBoxPage.xaml | 29 + .../Pages/CheckBoxPage.xaml.cs | 12 + .../Pages/ClipboardPage.xaml | 31 + .../Pages/ClipboardPage.xaml.cs | 213 ++ .../Pages/ColorPickerPage.xaml | 129 ++ .../Pages/ColorPickerPage.xaml.cs | 28 + .../Pages/ComboBoxPage.xaml | 152 ++ .../Pages/ComboBoxPage.xaml.cs | 17 + .../CommandBarCustomizationPage.xaml | 136 ++ .../CommandBarCustomizationPage.xaml.cs | 85 + .../CommandBarDynamicOverflowPage.xaml | 93 + .../CommandBarDynamicOverflowPage.xaml.cs | 101 + .../CommandBar/CommandBarEventsPage.xaml | 95 + .../CommandBar/CommandBarEventsPage.xaml.cs | 220 ++ .../CommandBar/CommandBarFirstLookPage.xaml | 94 + .../CommandBarFirstLookPage.xaml.cs | 27 + .../CommandBar/CommandBarKeyboardPage.xaml | 121 ++ .../CommandBar/CommandBarKeyboardPage.xaml.cs | 93 + .../CommandBarLabelPositionPage.xaml | 61 + .../CommandBarLabelPositionPage.xaml.cs | 12 + .../CommandBar/CommandBarOverflowPage.xaml | 74 + .../CommandBar/CommandBarOverflowPage.xaml.cs | 54 + .../CommandBar/CommandBarTogglePage.xaml | 77 + .../CommandBar/CommandBarTogglePage.xaml.cs | 50 + .../Pages/CommandBarPage.xaml | 11 + .../Pages/CommandBarPage.xaml.cs | 160 ++ .../Pages/CompositionPage.axaml | 61 + .../Pages/CompositionPage.axaml.cs | 357 ++++ .../Pages/ConnectedAnimationDemoPage.xaml | 14 + .../Pages/ConnectedAnimationDemoPage.xaml.cs | 144 ++ .../Pages/ContainerQueryPage.xaml | 111 + .../Pages/ContainerQueryPage.xaml.cs | 12 + .../Pages/ContentDemoPage.xaml | 14 + .../Pages/ContentDemoPage.xaml.cs | 160 ++ .../ContentPageCommandBarPage.xaml | 83 + .../ContentPageCommandBarPage.xaml.cs | 212 ++ .../ContentPageCustomizationPage.xaml | 106 + .../ContentPageCustomizationPage.xaml.cs | 64 + .../ContentPage/ContentPageEventsPage.xaml | 66 + .../ContentPage/ContentPageEventsPage.xaml.cs | 103 + .../ContentPage/ContentPageFirstLookPage.xaml | 48 + .../ContentPageFirstLookPage.xaml.cs | 93 + .../ContentPagePerformancePage.xaml | 104 + .../ContentPagePerformancePage.xaml.cs | 125 ++ .../ContentPage/ContentPageSafeAreaPage.xaml | 171 ++ .../ContentPageSafeAreaPage.xaml.cs | 70 + .../Pages/ContextFlyoutPage.xaml | 170 ++ .../Pages/ContextFlyoutPage.xaml.cs | 59 + .../Pages/ContextMenuPage.xaml | 100 + .../Pages/ContextMenuPage.xaml.cs | 51 + .../Pages/CursorPage.xaml | 31 + .../Pages/CursorPage.xaml.cs | 14 + .../Pages/CustomDrawing.xaml | 108 + .../Pages/CustomDrawing.xaml.cs | 40 + .../Pages/CustomDrawingExampleControl.cs | 216 ++ .../Pages/DataValidationPage.axaml | 43 + .../Pages/DataValidationPage.axaml.cs | 14 + .../Pages/DateTimePickerPage.xaml | 211 ++ .../Pages/DateTimePickerPage.xaml.cs | 23 + .../Pages/DialogsPage.xaml | 88 + .../Pages/DialogsPage.xaml.cs | 519 +++++ .../Pages/DragAndDropPage.xaml | 61 + .../Pages/DragAndDropPage.xaml.cs | 174 ++ .../Pages/DrawerDemoPage.xaml | 14 + .../Pages/DrawerDemoPage.xaml.cs | 76 + .../DrawerPage/ControlsGalleryAppPage.xaml | 400 ++++ .../DrawerPage/ControlsGalleryAppPage.xaml.cs | 464 +++++ .../DrawerPage/DrawerPageBreakpointPage.xaml | 113 + .../DrawerPageBreakpointPage.xaml.cs | 84 + .../DrawerPage/DrawerPageCompactPage.xaml | 103 + .../DrawerPage/DrawerPageCompactPage.xaml.cs | 78 + .../DrawerPageCustomFlyoutPage.xaml | 268 +++ .../DrawerPageCustomFlyoutPage.xaml.cs | 146 ++ .../DrawerPageCustomizationPage.xaml | 196 ++ .../DrawerPageCustomizationPage.xaml.cs | 326 +++ .../DrawerPage/DrawerPageEventsPage.xaml | 96 + .../DrawerPage/DrawerPageEventsPage.xaml.cs | 129 ++ .../DrawerPage/DrawerPageFirstLookPage.xaml | 66 + .../DrawerPageFirstLookPage.xaml.cs | 78 + .../DrawerPage/DrawerPageNavigationPage.xaml | 112 + .../DrawerPageNavigationPage.xaml.cs | 52 + .../DrawerPage/DrawerPagePerformancePage.xaml | 148 ++ .../DrawerPagePerformancePage.xaml.cs | 150 ++ .../Pages/DrawerPage/DrawerPageRtlPage.xaml | 108 + .../DrawerPage/DrawerPageRtlPage.xaml.cs | 57 + .../DrawerPage/DrawerPageTransitionsPage.xaml | 118 ++ .../DrawerPageTransitionsPage.xaml.cs | 94 + .../Pages/DrawerPage/EcoTrackerAppPage.xaml | 302 +++ .../DrawerPage/EcoTrackerAppPage.xaml.cs | 319 +++ .../DrawerPage/EcoTrackerCommunityView.xaml | 239 +++ .../EcoTrackerCommunityView.xaml.cs | 8 + .../DrawerPage/EcoTrackerHabitsView.xaml | 189 ++ .../DrawerPage/EcoTrackerHabitsView.xaml.cs | 8 + .../Pages/DrawerPage/EcoTrackerHomeView.xaml | 255 +++ .../DrawerPage/EcoTrackerHomeView.xaml.cs | 14 + .../Pages/DrawerPage/EcoTrackerStatsView.xaml | 224 ++ .../DrawerPage/EcoTrackerStatsView.xaml.cs | 8 + .../Pages/DrawerPage/ModernAppPage.xaml | 176 ++ .../Pages/DrawerPage/ModernAppPage.xaml.cs | 118 ++ .../Pages/DrawerPage/ModernDiscoverView.xaml | 360 ++++ .../DrawerPage/ModernDiscoverView.xaml.cs | 8 + .../Pages/DrawerPage/ModernMyTripsView.xaml | 124 ++ .../DrawerPage/ModernMyTripsView.xaml.cs | 8 + .../Pages/DrawerPage/ModernProfileView.xaml | 160 ++ .../DrawerPage/ModernProfileView.xaml.cs | 8 + .../Pages/DrawerPage/ModernSettingsView.xaml | 177 ++ .../DrawerPage/ModernSettingsView.xaml.cs | 8 + .../Pages/ExpanderPage.xaml | 76 + .../Pages/ExpanderPage.xaml.cs | 17 + .../Pages/FlyoutsPage.axaml | 286 +++ .../Pages/FlyoutsPage.axaml.cs | 94 + .../Pages/FocusPage.xaml | 51 + .../Pages/FocusPage.xaml.cs | 14 + .../Pages/GesturePage.xaml | 14 + .../Pages/GesturePage.xaml.cs | 39 + .../Gestures/GesturePinchRotationPage.xaml | 59 + .../Gestures/GesturePinchRotationPage.xaml.cs | 33 + .../Pages/Gestures/GesturePinchZoomPage.xaml | 51 + .../Gestures/GesturePinchZoomPage.xaml.cs | 134 ++ .../Pages/Gestures/GesturePullPage.xaml | 113 + .../Pages/Gestures/GesturePullPage.xaml.cs | 100 + .../Pages/Gestures/GestureSwipePage.xaml | 123 ++ .../Pages/Gestures/GestureSwipePage.xaml.cs | 125 ++ .../Pages/HeaderedContentPage.axaml | 31 + .../Pages/HeaderedContentPage.axaml.cs | 14 + .../Pages/ImagePage.xaml | 69 + .../Pages/ImagePage.xaml.cs | 63 + .../Pages/LabelsPage.axaml | 45 + .../Pages/LabelsPage.axaml.cs | 35 + .../Pages/LayoutTransformControlPage.xaml | 28 + .../Pages/LayoutTransformControlPage.xaml.cs | 12 + .../Pages/ListBoxPage.xaml | 51 + .../Pages/ListBoxPage.xaml.cs | 14 + .../Pages/MenuPage.xaml | 99 + .../Pages/MenuPage.xaml.cs | 28 + .../Pages/NativeEmbedPage.xaml | 69 + .../Pages/NativeEmbedPage.xaml.cs | 77 + .../Pages/NavigationDemoHelper.cs | 242 +++ .../Pages/NavigationDemoPage.xaml | 14 + .../Pages/NavigationDemoPage.xaml.cs | 94 + .../NavigationPage/AvaloniaFlixAppPage.xaml | 258 +++ .../AvaloniaFlixAppPage.xaml.cs | 191 ++ .../AvaloniaFlixDetailView.xaml | 332 +++ .../AvaloniaFlixDetailView.xaml.cs | 60 + .../NavigationPage/AvaloniaFlixHomeView.xaml | 630 ++++++ .../AvaloniaFlixHomeView.xaml.cs | 21 + .../AvaloniaFlixSearchView.xaml | 302 +++ .../AvaloniaFlixSearchView.xaml.cs | 36 + .../CurvedHeaderHomeScrollView.xaml | 205 ++ .../CurvedHeaderHomeScrollView.xaml.cs | 16 + .../CurvedHeaderProfileScrollView.xaml | 104 + .../CurvedHeaderProfileScrollView.xaml.cs | 8 + .../Pages/NavigationPage/LAvenirAppPage.xaml | 191 ++ .../NavigationPage/LAvenirAppPage.xaml.cs | 243 +++ .../NavigationPage/LAvenirDishDetailView.xaml | 119 ++ .../LAvenirDishDetailView.xaml.cs | 34 + .../Pages/NavigationPage/LAvenirMenuView.xaml | 186 ++ .../NavigationPage/LAvenirMenuView.xaml.cs | 28 + .../NavigationPage/LAvenirProfileView.xaml | 32 + .../NavigationPage/LAvenirProfileView.xaml.cs | 8 + .../LAvenirReservationsView.xaml | 189 ++ .../LAvenirReservationsView.xaml.cs | 8 + .../NavigationPageAppearancePage.xaml | 149 ++ .../NavigationPageAppearancePage.xaml.cs | 172 ++ .../NavigationPageAttachedMethodsPage.xaml | 90 + .../NavigationPageAttachedMethodsPage.xaml.cs | 175 ++ .../NavigationPageBackButtonPage.xaml | 104 + .../NavigationPageBackButtonPage.xaml.cs | 176 ++ .../NavigationPageCurvedHeaderPage.xaml | 48 + .../NavigationPageCurvedHeaderPage.xaml.cs | 298 +++ .../NavigationPageEventsPage.xaml | 77 + .../NavigationPageEventsPage.xaml.cs | 105 + .../NavigationPageFirstLookPage.xaml | 57 + .../NavigationPageFirstLookPage.xaml.cs | 66 + .../NavigationPageGesturePage.xaml | 57 + .../NavigationPageGesturePage.xaml.cs | 66 + .../NavigationPageInteractiveHeaderPage.xaml | 26 + ...avigationPageInteractiveHeaderPage.xaml.cs | 154 ++ .../NavigationPageModalPage.xaml | 63 + .../NavigationPageModalPage.xaml.cs | 66 + .../NavigationPageModalTransitionsPage.xaml | 61 + ...NavigationPageModalTransitionsPage.xaml.cs | 135 ++ .../NavigationPageMvvmNavigation.cs | 95 + .../NavigationPageMvvmPage.xaml | 86 + .../NavigationPageMvvmPage.xaml.cs | 32 + .../NavigationPageMvvmPageFactory.cs | 252 +++ .../NavigationPageMvvmViewModels.cs | 238 +++ .../NavigationPagePassDataPage.xaml | 56 + .../NavigationPagePassDataPage.xaml.cs | 255 +++ .../NavigationPagePerformancePage.xaml | 81 + .../NavigationPagePerformancePage.xaml.cs | 119 ++ .../NavigationPageScrollAwarePage.xaml | 56 + .../NavigationPageScrollAwarePage.xaml.cs | 167 ++ .../NavigationPageStackPage.xaml | 46 + .../NavigationPageStackPage.xaml.cs | 176 ++ .../NavigationPageTitlePage.xaml | 62 + .../NavigationPageTitlePage.xaml.cs | 177 ++ .../NavigationPageToolbarPage.xaml | 80 + .../NavigationPageToolbarPage.xaml.cs | 138 ++ .../NavigationPageTransitionsPage.xaml | 67 + .../NavigationPageTransitionsPage.xaml.cs | 83 + .../Pages/NavigationPage/PulseAppPage.xaml | 46 + .../Pages/NavigationPage/PulseAppPage.xaml.cs | 140 ++ .../Pages/NavigationPage/PulseHomeView.xaml | 270 +++ .../NavigationPage/PulseHomeView.xaml.cs | 25 + .../Pages/NavigationPage/PulseLoginView.xaml | 145 ++ .../NavigationPage/PulseLoginView.xaml.cs | 15 + .../NavigationPage/PulseProfileView.xaml | 202 ++ .../NavigationPage/PulseProfileView.xaml.cs | 8 + .../PulseWorkoutDetailView.xaml | 248 +++ .../PulseWorkoutDetailView.xaml.cs | 15 + .../NavigationPage/PulseWorkoutsView.xaml | 290 +++ .../NavigationPage/PulseWorkoutsView.xaml.cs | 8 + .../NavigationPage/RetroGamingAppPage.xaml | 179 ++ .../NavigationPage/RetroGamingAppPage.xaml.cs | 306 +++ .../NavigationPage/RetroGamingDetailView.xaml | 229 +++ .../RetroGamingDetailView.xaml.cs | 69 + .../RetroGamingFavoritesView.xaml | 26 + .../RetroGamingFavoritesView.xaml.cs | 8 + .../NavigationPage/RetroGamingGamesView.xaml | 214 ++ .../RetroGamingGamesView.xaml.cs | 39 + .../NavigationPage/RetroGamingHomeView.xaml | 346 ++++ .../RetroGamingHomeView.xaml.cs | 22 + .../RetroGamingProfileView.xaml | 70 + .../RetroGamingProfileView.xaml.cs | 8 + .../NavigationPage/RetroGamingSearchView.xaml | 239 +++ .../RetroGamingSearchView.xaml.cs | 24 + .../Transitions/CompositeTransition.cs | 120 ++ .../Transitions/FadeThroughTransition.cs | 137 ++ .../Transitions/PageSlideTransition.cs | 97 + .../Transitions/ParallaxSlideTransition.cs | 121 ++ .../NavigationPerformanceMonitorHelper.cs | 275 +++ .../Pages/NotificationsPage.xaml | 25 + .../Pages/NotificationsPage.xaml.cs | 39 + .../Pages/NumericUpDownPage.xaml | 143 ++ .../Pages/NumericUpDownPage.xaml.cs | 100 + .../Pages/OpenGl/GlPageKnobs.xaml | 30 + .../Pages/OpenGl/GlPageKnobs.xaml.cs | 70 + .../Pages/OpenGl/OpenGlContent.cs | 311 +++ .../Pages/OpenGl/OpenGlFbo.cs | 118 ++ .../Pages/OpenGl/OpenGlLeasePage.xaml | 9 + .../Pages/OpenGl/OpenGlLeasePage.xaml.cs | 207 ++ .../Pages/OpenGlPage.xaml | 14 + .../Pages/OpenGlPage.xaml.cs | 82 + .../PipsPager/PipsPagerCarouselPage.xaml | 50 + .../PipsPager/PipsPagerCarouselPage.xaml.cs | 11 + .../PipsPagerCustomButtonThemesPage.xaml | 78 + .../PipsPagerCustomButtonThemesPage.xaml.cs | 11 + .../PipsPager/PipsPagerCustomColorsPage.xaml | 59 + .../PipsPagerCustomColorsPage.xaml.cs | 11 + .../PipsPagerCustomTemplatesPage.xaml | 197 ++ .../PipsPagerCustomTemplatesPage.xaml.cs | 11 + .../Pages/PipsPager/PipsPagerEventsPage.xaml | 35 + .../PipsPager/PipsPagerEventsPage.xaml.cs | 29 + .../PipsPagerGettingStartedPage.xaml | 46 + .../PipsPagerGettingStartedPage.xaml.cs | 11 + .../PipsPagerLargeCollectionPage.xaml | 52 + .../PipsPagerLargeCollectionPage.xaml.cs | 11 + .../Pages/PipsPagerPage.xaml | 14 + .../Pages/PipsPagerPage.xaml.cs | 54 + .../Pages/PlatformInfoPage.xaml | 57 + .../Pages/PlatformInfoPage.xaml.cs | 14 + .../Pages/PointerCanvas.cs | 236 +++ .../Pages/PointerContactsTab.cs | 109 + .../Pages/PointersPage.xaml | 69 + .../Pages/PointersPage.xaml.cs | 59 + .../Pages/ProgressBarPage.xaml | 43 + .../Pages/ProgressBarPage.xaml.cs | 12 + .../Pages/RadioButtonPage.xaml | 41 + .../Pages/RadioButtonPage.xaml.cs | 12 + .../Pages/RefreshContainerPage.axaml | 44 + .../Pages/RefreshContainerPage.axaml.cs | 28 + .../Pages/RelativePanelPage.axaml | 64 + .../Pages/RelativePanelPage.axaml.cs | 12 + .../Pages/ScreenPage.cs | 174 ++ .../Pages/ScrollViewerPage.xaml | 271 +++ .../Pages/ScrollViewerPage.xaml.cs | 104 + .../Pages/SliderPage.xaml | 84 + .../Pages/SliderPage.xaml.cs | 12 + .../Pages/SplitViewPage.xaml | 113 + .../Pages/SplitViewPage.xaml.cs | 14 + .../Pages/TabControlPage.xaml | 112 + .../Pages/TabControlPage.xaml.cs | 47 + .../Pages/TabStripPage.xaml | 35 + .../Pages/TabStripPage.xaml.cs | 33 + .../Pages/TabbedDemoPage.xaml | 14 + .../Pages/TabbedDemoPage.xaml.cs | 96 + .../Pages/TabbedPage/CenteredTabPanel.cs | 63 + .../TabbedPage/FluidNavBar/FluidNavBar.cs | 620 ++++++ .../TabbedPage/FluidNavBar/FluidNavItem.cs | 14 + .../TabbedPage/TabbedPageCollectionPage.xaml | 55 + .../TabbedPageCollectionPage.xaml.cs | 82 + .../TabbedPageCustomTabBarPage.xaml | 187 ++ .../TabbedPageCustomTabBarPage.xaml.cs | 34 + .../TabbedPageCustomizationPage.xaml | 117 ++ .../TabbedPageCustomizationPage.xaml.cs | 126 ++ .../TabbedPageDataTemplatePage.xaml | 43 + .../TabbedPageDataTemplatePage.xaml.cs | 207 ++ .../TabbedPageDisabledTabsPage.xaml | 70 + .../TabbedPageDisabledTabsPage.xaml.cs | 40 + .../TabbedPage/TabbedPageEventsPage.xaml | 51 + .../TabbedPage/TabbedPageEventsPage.xaml.cs | 82 + .../Pages/TabbedPage/TabbedPageFabPage.xaml | 367 ++++ .../TabbedPage/TabbedPageFabPage.xaml.cs | 45 + .../TabbedPage/TabbedPageFirstLookPage.xaml | 74 + .../TabbedPageFirstLookPage.xaml.cs | 83 + .../TabbedPage/TabbedPageFluidNavPage.xaml | 305 +++ .../TabbedPage/TabbedPageFluidNavPage.xaml.cs | 78 + .../TabbedPage/TabbedPageGesturePage.xaml | 72 + .../TabbedPage/TabbedPageGesturePage.xaml.cs | 43 + .../TabbedPage/TabbedPageKeyboardPage.xaml | 108 + .../TabbedPage/TabbedPageKeyboardPage.xaml.cs | 46 + .../TabbedPage/TabbedPagePerformancePage.xaml | 47 + .../TabbedPagePerformancePage.xaml.cs | 93 + .../TabbedPage/TabbedPagePlacementPage.xaml | 58 + .../TabbedPagePlacementPage.xaml.cs | 26 + .../TabbedPageProgrammaticPage.xaml | 71 + .../TabbedPageProgrammaticPage.xaml.cs | 52 + .../TabbedPage/TabbedPageTransitionsPage.xaml | 77 + .../TabbedPageTransitionsPage.xaml.cs | 48 + .../TabbedPage/TabbedPageWithDrawerPage.xaml | 56 + .../TabbedPageWithDrawerPage.xaml.cs | 114 ++ .../TabbedPageWithNavigationPage.xaml | 50 + .../TabbedPageWithNavigationPage.xaml.cs | 100 + .../Pages/TableViewPage.axaml | 71 + .../Pages/TableViewPage.axaml.cs | 13 + .../Pages/TextBlockPage.xaml | 141 ++ .../Pages/TextBlockPage.xaml.cs | 12 + .../Pages/TextBoxPage.xaml | 105 + .../Pages/TextBoxPage.xaml.cs | 12 + .../Pages/ThemePage.axaml | 80 + .../Pages/ThemePage.axaml.cs | 33 + .../Pages/ToggleSwitchPage.xaml | 81 + .../Pages/ToggleSwitchPage.xaml.cs | 12 + .../Pages/ToolTipPage.xaml | 95 + .../Pages/ToolTipPage.xaml.cs | 42 + .../TransitioningContentControlPage.axaml | 85 + .../TransitioningContentControlPage.axaml.cs | 12 + .../Transitions/CardStackPageTransition.cs | 447 ++++ .../Transitions/WaveRevealPageTransition.cs | 380 ++++ .../Pages/TreeViewPage.xaml | 36 + .../Pages/TreeViewPage.xaml.cs | 14 + .../Pages/ViewboxPage.xaml | 54 + .../Pages/ViewboxPage.xaml.cs | 12 + .../Pages/WindowCustomizationsPage.xaml | 68 + .../Pages/WindowCustomizationsPage.xaml.cs | 12 + .../AvaloniaControlCatalog/Pages/teapot.bin | Bin 0 -> 15464 bytes .../PlatformRenderInterface.cs | 315 +++ samples/AvaloniaControlCatalog/Program.cs | 56 + .../RectangleGeometryImpl.cs | 27 + samples/AvaloniaControlCatalog/RegionImpl.cs | 106 + .../RenderTargetBitmapImpl.cs | 106 + .../RenderTargetImpl.cs | 58 + .../AvaloniaControlCatalog/ScrollPage.xaml | 34 + .../StreamGeometryImpl.cs | 216 ++ .../AvaloniaControlCatalog/TextShaperImpl.cs | 185 ++ .../TransformedGeometryImpl.cs | 47 + .../TransparentStyles.axaml | 25 + .../TransparentStyles.axaml.cs | 5 + .../ViewModels/ApplicationViewModel.cs | 26 + .../ViewModels/ComboBoxPageViewModel.cs | 50 + .../ViewModels/ContextPageViewModel.cs | 74 + .../ViewModels/CursorPageViewModel.cs | 43 + .../ViewModels/DataValidationViewModel.cs | 45 + .../ViewModels/ExpanderPageViewModel.cs | 27 + .../ViewModels/ListBoxPageViewModel.cs | 132 ++ .../ViewModels/MainWindowViewModel.cs | 150 ++ .../MainWindowViewModel_PageList.cs | 248 +++ .../ViewModels/MenuItemViewModel.cs | 13 + .../ViewModels/MenuPageViewModel.cs | 96 + .../ViewModels/NotificationViewModel.cs | 44 + .../PlatformInformationViewModel.cs | 58 + .../ViewModels/RefreshContainerViewModel.cs | 25 + .../ViewModels/SplitViewPageViewModel.cs | 54 + .../ViewModels/TabControlPageViewModel.cs | 26 + .../ViewModels/TableViewPageViewModel.cs | 246 +++ ...ransitioningContentControlPageViewModel.cs | 317 +++ .../ViewModels/TreeViewPageViewModel.cs | 122 ++ .../Views/CustomNotificationView.xaml | 21 + .../Views/CustomNotificationView.xaml.cs | 12 + .../AvaloniaControlCatalog/WebGPULayerImpl.cs | 153 ++ .../WebGPURenderTargetImpl.cs | 142 ++ .../WriteableBitmapImpl.cs | 202 ++ samples/AvaloniaControlCatalog/app.manifest | 11 + 591 files changed, 54327 insertions(+), 92 deletions(-) create mode 100644 samples/AvaloniaControlCatalog/App.xaml create mode 100644 samples/AvaloniaControlCatalog/App.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Assets/CurvedHeader/avatar.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/CurvedHeader/featured.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/CurvedHeader/product1.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/CurvedHeader/product2.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/CurvedHeader/product3.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/CurvedHeader/update1.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/CurvedHeader/update2.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/CurvedHeader/update3.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Bold.ttf create mode 100644 samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-BoldItalic.ttf create mode 100644 samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Italic.ttf create mode 100644 samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Regular.ttf create mode 100644 samples/AvaloniaControlCatalog/Assets/Fonts/WenQuanYiMicroHei-01.ttf create mode 100644 samples/AvaloniaControlCatalog/Assets/Icon.png create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/avatar.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/dest_alps.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/dest_forest.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/dest_norway.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/exp_angkor.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/exp_tokyo.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_alpine.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_bay.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_city.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_paris.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_tropical.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_venice.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/story1.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/story2.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/ModernApp/story3.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/cast1.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/cast2.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/continue1.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/hero.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/morelike1.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/search1.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/toprated1.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/toprated2.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/toprated3.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/toprated4.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/trending1.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Movies/trending2.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/cat_hiit.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/cat_strength.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/cat_yoga.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/ex_bench.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/ex_deadlifts.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/ex_overhead.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/ex_pullups.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/ex_squats.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/profile_avatar.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/rec_fullbody.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/rec_mobility.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/rec_powercore.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Pulse/workout_hero.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Restaurant/dish1.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Restaurant/dish2.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Restaurant/dish3.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Restaurant/dish4.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Restaurant/featured_dish.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Restaurant/user_avatar.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/RetroGaming/cyber_city.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/RetroGaming/dungeon_bit.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/RetroGaming/forest_spirit.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/RetroGaming/hero.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/RetroGaming/neon_ninja.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/RetroGaming/neon_racer.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/RetroGaming/pixel_quest.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/RetroGaming/space_voids.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Sanctuary/city_bg.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Sanctuary/forest_bg.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Sanctuary/main_arctic_silence.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Sanctuary/main_deep_forest.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Sanctuary/main_desert_sands.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Sanctuary/main_hero.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/Sanctuary/mountain_bg.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/avalonia-32.png create mode 100644 samples/AvaloniaControlCatalog/Assets/delicate-arch-896885_640.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/github_icon.png create mode 100644 samples/AvaloniaControlCatalog/Assets/hirsch-899118_640.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/image1.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/image2.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/image3.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/image4.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/image5.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/image6.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/image7.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/maple-leaf-888807_640.jpg create mode 100644 samples/AvaloniaControlCatalog/Assets/test_icon.ico create mode 100644 samples/AvaloniaControlCatalog/AvaloniaControlCatalog.csproj create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/AnonymousObserverNonGenericHelper.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyBindingObservable.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyChangedObservable.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyObservable.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/CombinedSubject.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/CompositeDisposable.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/Disposable.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/DisposableMixin.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/IAvaloniaSubject.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/LightweightObservableBase.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/LightweightSubject.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/Observable.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/CombineLatest.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/Sink.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/Switch.cs create mode 100644 samples/AvaloniaControlCatalog/AvaloniaReactive/SingleSubscriberObservableBase.cs create mode 100644 samples/AvaloniaControlCatalog/CombinedGeometryImpl.cs create mode 100644 samples/AvaloniaControlCatalog/Converter/DegToRadConverter.cs create mode 100644 samples/AvaloniaControlCatalog/Converter/GdpToBrushConverter.cs create mode 100644 samples/AvaloniaControlCatalog/Converter/HexConverter.cs create mode 100644 samples/AvaloniaControlCatalog/Converter/MathSubtractConverter.cs create mode 100644 samples/AvaloniaControlCatalog/Converter/TableViewColumnWidthConverter.cs create mode 100644 samples/AvaloniaControlCatalog/DecoratedWindow.xaml create mode 100644 samples/AvaloniaControlCatalog/DecoratedWindow.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/DrawingBackendMode.cs create mode 100644 samples/AvaloniaControlCatalog/DrawingContextImpl.cs create mode 100644 samples/AvaloniaControlCatalog/EllipseGeometryImpl.cs create mode 100644 samples/AvaloniaControlCatalog/FontManagerImpl.cs create mode 100644 samples/AvaloniaControlCatalog/GeometryGroupImpl.cs create mode 100644 samples/AvaloniaControlCatalog/GeometryImpl.cs create mode 100644 samples/AvaloniaControlCatalog/GlobalUsings.cs create mode 100644 samples/AvaloniaControlCatalog/GlyphRunImpl.cs create mode 100644 samples/AvaloniaControlCatalog/IDrawableBitmapImpl.cs create mode 100644 samples/AvaloniaControlCatalog/Icons.cs create mode 100644 samples/AvaloniaControlCatalog/ImageSharpDrawingAppBuilderExtensions.cs create mode 100644 samples/AvaloniaControlCatalog/ImageSharpExtensions.cs create mode 100644 samples/AvaloniaControlCatalog/ImmutableBitmapImpl.cs create mode 100644 samples/AvaloniaControlCatalog/LineGeometryImpl.cs create mode 100644 samples/AvaloniaControlCatalog/MainView.xaml create mode 100644 samples/AvaloniaControlCatalog/MainView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/MainWindow.xaml create mode 100644 samples/AvaloniaControlCatalog/MainWindow.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/MiniMvvm/MiniCommand.cs create mode 100644 samples/AvaloniaControlCatalog/MiniMvvm/PropertyChangedExtensions.cs create mode 100644 samples/AvaloniaControlCatalog/MiniMvvm/ViewModelBase.cs create mode 100644 samples/AvaloniaControlCatalog/Models/CatalogTheme.cs create mode 100644 samples/AvaloniaControlCatalog/Models/Person.cs create mode 100644 samples/AvaloniaControlCatalog/Models/StateData.cs create mode 100644 samples/AvaloniaControlCatalog/NavHeaderItem.xaml create mode 100644 samples/AvaloniaControlCatalog/NuGet.Config create mode 100644 samples/AvaloniaControlCatalog/Pages/AcceleratorPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/AcceleratorPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/AcrylicPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/AcrylicPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/AdornerLayerPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/AdornerLayerPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/AutoCompleteBoxPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/BitmapCachePage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/BitmapCachePage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/BorderPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/BorderPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ButtonSpinnerPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ButtonSpinnerPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ButtonsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ButtonsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CanvasPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CanvasPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselDemoPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselDemoPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CareCompanionAppPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CareCompanionAppPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselCustomizationPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselCustomizationPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselDataBindingPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselDataBindingPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselGalleryAppPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselGalleryAppPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselGesturesPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselGesturesPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselGettingStartedPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselGettingStartedPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselMultiItemPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselMultiItemPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageCustomizationPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageCustomizationPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageDataTemplatePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageDataTemplatePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageEventsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageEventsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageFirstLookPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageFirstLookPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageGesturePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageGesturePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPagePerformancePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPagePerformancePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageSelectionPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageSelectionPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageTransitionsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselPageTransitionsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselTransitionsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselTransitionsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselVerticalPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/CarouselVerticalPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryMainPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryMainPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryShowcasePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryShowcasePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CheckBoxPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CheckBoxPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ClipboardPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ClipboardPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ColorPickerPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ColorPickerPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ComboBoxPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ComboBoxPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarCustomizationPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarCustomizationPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarDynamicOverflowPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarDynamicOverflowPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarEventsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarEventsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarFirstLookPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarFirstLookPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarKeyboardPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarKeyboardPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarLabelPositionPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarLabelPositionPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarOverflowPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarOverflowPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarTogglePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBar/CommandBarTogglePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBarPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CommandBarPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CompositionPage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CompositionPage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ConnectedAnimationDemoPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ConnectedAnimationDemoPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ContainerQueryPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ContainerQueryPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentDemoPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentDemoPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageCommandBarPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageCommandBarPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageCustomizationPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageCustomizationPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageEventsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageEventsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageFirstLookPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageFirstLookPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPagePerformancePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPagePerformancePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageSafeAreaPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageSafeAreaPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ContextFlyoutPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ContextFlyoutPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ContextMenuPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ContextMenuPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CursorPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CursorPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CustomDrawing.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/CustomDrawing.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/CustomDrawingExampleControl.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DataValidationPage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DataValidationPage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DateTimePickerPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DateTimePickerPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DialogsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DialogsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DragAndDropPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DragAndDropPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerDemoPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerDemoPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageBreakpointPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageBreakpointPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomFlyoutPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomFlyoutPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomizationPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomizationPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageEventsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageEventsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageFirstLookPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageFirstLookPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageNavigationPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageNavigationPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPagePerformancePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPagePerformancePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageRtlPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageRtlPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageTransitionsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageTransitionsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerAppPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerAppPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerCommunityView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerCommunityView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerHabitsView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerHabitsView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerHomeView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerHomeView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerStatsView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerStatsView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernAppPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernAppPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernDiscoverView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernDiscoverView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernMyTripsView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernMyTripsView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernProfileView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernProfileView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernSettingsView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernSettingsView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ExpanderPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ExpanderPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/FlyoutsPage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/FlyoutsPage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/FocusPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/FocusPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/GesturePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/GesturePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchRotationPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchRotationPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchZoomPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchZoomPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/Gestures/GesturePullPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/Gestures/GesturePullPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/Gestures/GestureSwipePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/Gestures/GestureSwipePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/HeaderedContentPage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/HeaderedContentPage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ImagePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ImagePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/LabelsPage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/LabelsPage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/LayoutTransformControlPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/LayoutTransformControlPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ListBoxPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ListBoxPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/MenuPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/MenuPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NativeEmbedPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NativeEmbedPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationDemoHelper.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationDemoPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationDemoPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixAppPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixAppPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixDetailView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixDetailView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixHomeView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixHomeView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixSearchView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixSearchView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderHomeScrollView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderHomeScrollView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderProfileScrollView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderProfileScrollView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirAppPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirAppPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirDishDetailView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirDishDetailView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirMenuView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirMenuView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirProfileView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirProfileView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirReservationsView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirReservationsView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageAppearancePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageAppearancePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageAttachedMethodsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageAttachedMethodsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageBackButtonPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageBackButtonPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageCurvedHeaderPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageCurvedHeaderPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageEventsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageEventsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageFirstLookPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageFirstLookPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageGesturePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageGesturePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageInteractiveHeaderPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageInteractiveHeaderPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageModalPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageModalPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageModalTransitionsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageModalTransitionsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageMvvmNavigation.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageMvvmPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageMvvmPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageMvvmPageFactory.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageMvvmViewModels.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPagePassDataPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPagePassDataPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPagePerformancePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPagePerformancePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageScrollAwarePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageScrollAwarePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageStackPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageStackPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageTitlePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageTitlePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageToolbarPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageToolbarPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageTransitionsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageTransitionsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseAppPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseAppPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseHomeView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseHomeView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseLoginView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseLoginView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseProfileView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseProfileView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutDetailView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutDetailView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutsView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutsView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingAppPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingAppPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingDetailView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingDetailView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingFavoritesView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingFavoritesView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingGamesView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingGamesView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingHomeView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingHomeView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingProfileView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingProfileView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingSearchView.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingSearchView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/CompositeTransition.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/FadeThroughTransition.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/PageSlideTransition.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/ParallaxSlideTransition.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NavigationPerformanceMonitorHelper.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NotificationsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NotificationsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/NumericUpDownPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/NumericUpDownPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/OpenGl/GlPageKnobs.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/OpenGl/GlPageKnobs.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/OpenGl/OpenGlContent.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/OpenGl/OpenGlFbo.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/OpenGlPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/OpenGlPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCarouselPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCarouselPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomButtonThemesPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomButtonThemesPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomColorsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomColorsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomTemplatesPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomTemplatesPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerEventsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerEventsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerGettingStartedPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerGettingStartedPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerLargeCollectionPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerLargeCollectionPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPagerPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/PipsPagerPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PlatformInfoPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/PlatformInfoPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PointerCanvas.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PointerContactsTab.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/PointersPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/PointersPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ProgressBarPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ProgressBarPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/RadioButtonPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/RadioButtonPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/RefreshContainerPage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/RefreshContainerPage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/RelativePanelPage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/RelativePanelPage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ScreenPage.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ScrollViewerPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ScrollViewerPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/SliderPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/SliderPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/SplitViewPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/SplitViewPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabControlPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabControlPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabStripPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabStripPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedDemoPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedDemoPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/CenteredTabPanel.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/FluidNavBar/FluidNavBar.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/FluidNavBar/FluidNavItem.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageCollectionPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageCollectionPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageCustomTabBarPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageCustomTabBarPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageCustomizationPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageCustomizationPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageDataTemplatePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageDataTemplatePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageDisabledTabsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageDisabledTabsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageEventsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageEventsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFabPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFabPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFirstLookPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFirstLookPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFluidNavPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFluidNavPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageGesturePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageGesturePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageKeyboardPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageKeyboardPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPagePerformancePage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPagePerformancePage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPagePlacementPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPagePlacementPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageProgrammaticPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageProgrammaticPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageTransitionsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageTransitionsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithDrawerPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithDrawerPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithNavigationPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithNavigationPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TableViewPage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TableViewPage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TextBlockPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TextBlockPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TextBoxPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TextBoxPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ThemePage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ThemePage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ToggleSwitchPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ToggleSwitchPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ToolTipPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ToolTipPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TransitioningContentControlPage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TransitioningContentControlPage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/Transitions/CardStackPageTransition.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/Transitions/WaveRevealPageTransition.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/TreeViewPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/TreeViewPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/ViewboxPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/ViewboxPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/WindowCustomizationsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/WindowCustomizationsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/teapot.bin create mode 100644 samples/AvaloniaControlCatalog/PlatformRenderInterface.cs create mode 100644 samples/AvaloniaControlCatalog/Program.cs create mode 100644 samples/AvaloniaControlCatalog/RectangleGeometryImpl.cs create mode 100644 samples/AvaloniaControlCatalog/RegionImpl.cs create mode 100644 samples/AvaloniaControlCatalog/RenderTargetBitmapImpl.cs create mode 100644 samples/AvaloniaControlCatalog/RenderTargetImpl.cs create mode 100644 samples/AvaloniaControlCatalog/ScrollPage.xaml create mode 100644 samples/AvaloniaControlCatalog/StreamGeometryImpl.cs create mode 100644 samples/AvaloniaControlCatalog/TextShaperImpl.cs create mode 100644 samples/AvaloniaControlCatalog/TransformedGeometryImpl.cs create mode 100644 samples/AvaloniaControlCatalog/TransparentStyles.axaml create mode 100644 samples/AvaloniaControlCatalog/TransparentStyles.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/ApplicationViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/ComboBoxPageViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/ContextPageViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/CursorPageViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/DataValidationViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/ExpanderPageViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/ListBoxPageViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/MainWindowViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/MainWindowViewModel_PageList.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/MenuItemViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/MenuPageViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/NotificationViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/PlatformInformationViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/RefreshContainerViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/SplitViewPageViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/TabControlPageViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/TableViewPageViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/TreeViewPageViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/Views/CustomNotificationView.xaml create mode 100644 samples/AvaloniaControlCatalog/Views/CustomNotificationView.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/WebGPULayerImpl.cs create mode 100644 samples/AvaloniaControlCatalog/WebGPURenderTargetImpl.cs create mode 100644 samples/AvaloniaControlCatalog/WriteableBitmapImpl.cs create mode 100644 samples/AvaloniaControlCatalog/app.manifest diff --git a/.gitignore b/.gitignore index 4e61c3c8..b3124ebd 100644 --- a/.gitignore +++ b/.gitignore @@ -232,5 +232,3 @@ SixLabors.Shapes.Coverage.xml .codex-* .claude /plans/ - -samples/Avalonia** diff --git a/ImageSharp.Drawing.All.sln b/ImageSharp.Drawing.All.sln index f82b7951..8c2b44c3 100644 --- a/ImageSharp.Drawing.All.sln +++ b/ImageSharp.Drawing.All.sln @@ -318,10 +318,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Png", "Png", "{E1C42A6F-913 tests\Images\Input\Png\zlib-overflow.png = tests\Images\Input\Png\zlib-overflow.png EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Drawing.Tests", "tests\ImageSharp.Drawing.Tests\ImageSharp.Drawing.Tests.csproj", "{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Drawing.Benchmarks", "tests\ImageSharp.Drawing.Benchmarks\ImageSharp.Drawing.Benchmarks.csproj", "{59804113-1DD4-4F80-8D06-35FF71652508}" -EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SharedInfrastructure", "shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.shproj", "{68A8CC40-6AED-4E96-B524-31B1158FDEEA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{23859314-5693-4E6C-BE5C-80A433439D2A}" @@ -329,22 +325,102 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ .github\workflows\build-and-test.yml = .github\workflows\build-and-test.yml EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Drawing.WebGPU", "src\ImageSharp.Drawing.WebGPU\ImageSharp.Drawing.WebGPU.csproj", "{061582C2-658F-40AE-A978-7D74A4EB2C0A}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Drawing.WebGPU.ShaderGen", "src\ImageSharp.Drawing.WebGPU.ShaderGen\ImageSharp.Drawing.WebGPU.ShaderGen.csproj", "{C7606104-5D58-4670-912C-3F336606B02D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Drawing.ManualBenchmarks", "tests\ImageSharp.Drawing.ManualBenchmarks\ImageSharp.Drawing.ManualBenchmarks.csproj", "{70193989-E587-451A-AF34-4C74FEE5DA5A}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebGPUWindowDemo", "samples\WebGPUWindowDemo\WebGPUWindowDemo.csproj", "{999D90DE-6940-4693-9F80-701F2D5DB77A}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DrawShapesWithImageSharp", "samples\DrawShapesWithImageSharp\DrawShapesWithImageSharp.csproj", "{43A3CA46-2675-4AD4-88FC-DDC2E657F0B7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebGPUExternalSurfaceDemo", "samples\WebGPUExternalSurfaceDemo\WebGPUExternalSurfaceDemo.csproj", "{F0AAE591-A44E-48A5-8C74-395BDF98F646}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaControlCatalog", "samples\AvaloniaControlCatalog\AvaloniaControlCatalog.csproj", "{333F3D4F-D251-5601-4851-307FF0F13D23}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SixLabors.Fonts", "..\Fonts\src\SixLabors.Fonts\SixLabors.Fonts.csproj", "{4A922B77-34EC-EA6A-8E96-8353C8FA0640}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PolygonClipper", "..\PolygonClipper\src\PolygonClipper\PolygonClipper.csproj", "{5EC089DC-69F7-643B-93BB-293D199F5F74}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "temp", "temp", "{29F3AA1B-6D75-C75D-9183-749A65CD24A7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Controls.ColorPicker", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj", "{38CA36F4-AD3A-4B37-AB75-9DEF57219A42}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Base", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Base\Avalonia.Base.csproj", "{9AFBFA31-F636-4685-A8B9-4F623139C01B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevAnalyzers", "..\..\JimBobSquarePants\Avalonia\src\tools\DevAnalyzers\DevAnalyzers.csproj", "{AA365C58-287B-47CE-8EDA-6FA3BF10574E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Analyzers.CSharp", "..\..\JimBobSquarePants\Avalonia\src\tools\Avalonia.Analyzers.CSharp\Avalonia.Analyzers.CSharp.csproj", "{80A082A3-4FC7-4AD4-AFB6-503B3033B32D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Analyzers.CodeFixes.CSharp", "..\..\JimBobSquarePants\Avalonia\src\tools\Avalonia.Analyzers.CodeFixes.CSharp\Avalonia.Analyzers.CodeFixes.CSharp.csproj", "{7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Analyzers.VisualBasic", "..\..\JimBobSquarePants\Avalonia\src\tools\Avalonia.Analyzers.VisualBasic\Avalonia.Analyzers.VisualBasic.csproj", "{E78916D9-1240-4C13-BEC3-242AFF8BFB84}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevGenerators", "..\..\JimBobSquarePants\Avalonia\src\tools\DevGenerators\DevGenerators.csproj", "{F253A0A3-3202-45AE-A2F5-30098F8B3BB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Build.Tasks", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj", "{7BD98865-305F-428F-BDCE-10311B96174A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Remote.Protocol", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Remote.Protocol\Avalonia.Remote.Protocol.csproj", "{3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Markup.Xaml", "..\..\JimBobSquarePants\Avalonia\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj", "{2956B3D9-23A0-4818-A12D-178C15F5F3D9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Controls", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Controls\Avalonia.Controls.csproj", "{505BA299-AAE0-4258-934F-5CF9B6DC4EAC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Markup", "..\..\JimBobSquarePants\Avalonia\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj", "{D3730B8F-1568-4CAD-8617-0BA565E435D7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Desktop", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Desktop\Avalonia.Desktop.csproj", "{3EADAED3-1360-4944-9434-DE3359DE2233}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32", "..\..\JimBobSquarePants\Avalonia\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj", "{B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia", "..\..\JimBobSquarePants\Avalonia\packages\Avalonia\Avalonia.csproj", "{7A7E13F8-68AE-4EE3-AA58-410EB33125E9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Generators", "..\..\JimBobSquarePants\Avalonia\src\tools\Avalonia.Generators\Avalonia.Generators.csproj", "{5DA79C9E-5625-4D88-9D42-674D27538B74}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DesignerSupport", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj", "{B7840472-147D-49DA-9AC4-0EEB5E9F27FC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.OpenGL", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.OpenGL\Avalonia.OpenGL.csproj", "{0CFDCBC5-32FD-458B-A487-D5E9551BADF9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Metal", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Metal\Avalonia.Metal.csproj", "{45053362-1DBC-4EAD-8B4C-51B6E56F60F6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Vulkan", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Vulkan\Avalonia.Vulkan.csproj", "{ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Dialogs", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj", "{1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.MicroCom", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.MicroCom\Avalonia.MicroCom.csproj", "{93A496DB-F661-49E0-97C5-4680D92163ED}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Automation", "..\..\JimBobSquarePants\Avalonia\src\Windows\Avalonia.Win32.Automation\Avalonia.Win32.Automation.csproj", "{F7F47D90-331B-4D44-BB01-5CE31A2D2532}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Skia", "..\..\JimBobSquarePants\Avalonia\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj", "{F0AA05AC-508E-4D13-B416-7D080A6ED5B0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Native", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Native\Avalonia.Native.csproj", "{14E80B26-EDD6-468F-BC51-350DD9C83100}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.X11", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.X11\Avalonia.X11.csproj", "{1AC23054-F30F-48A9-B750-1D60BDCFE3FD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.FreeDesktop", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.FreeDesktop\Avalonia.FreeDesktop.csproj", "{F1FD356A-0712-4379-AF87-11BB6F4D554B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.FreeDesktop.AtSpi", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.FreeDesktop.AtSpi\Avalonia.FreeDesktop.AtSpi.csproj", "{E241F16B-FE81-4107-8C9F-6BFA029C7934}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DBus.Generators", "..\..\JimBobSquarePants\Avalonia\src\tools\Avalonia.DBus.Generators\Avalonia.DBus.Generators.csproj", "{B1749E6E-EF88-4C46-89BC-CF1BF7A46851}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.HarfBuzz", "..\..\JimBobSquarePants\Avalonia\src\HarfBuzz\Avalonia.HarfBuzz\Avalonia.HarfBuzz.csproj", "{A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Fonts.Inter", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj", "{099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Themes.Fluent", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{F28E672C-DBA2-496E-8784-E6F17CE261D5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Themes.Simple", "..\..\JimBobSquarePants\Avalonia\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj", "{5483EE45-0838-46C4-B59B-5B52F7C7D975}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp", "..\ImageSharp\src\ImageSharp\ImageSharp.csproj", "{FE71764E-E7BD-B378-8A4C-86929A28AD25}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DrawingBackendBenchmark", "samples\DrawingBackendBenchmark\DrawingBackendBenchmark.csproj", "{B5CF5531-624F-9F49-46BD-937FF4062F54}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebGPUExternalSurfaceDemo", "samples\WebGPUExternalSurfaceDemo\WebGPUExternalSurfaceDemo.csproj", "{97619DEB-226C-EB67-140D-0C7E67B2F7D0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebGPUWindowDemo", "samples\WebGPUWindowDemo\WebGPUWindowDemo.csproj", "{4570F31D-D00F-6A80-DB7C-74826CBD15AE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Drawing.WebGPU", "src\ImageSharp.Drawing.WebGPU\ImageSharp.Drawing.WebGPU.csproj", "{638FC2AE-7FC1-6527-E051-C6B34803F08D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Drawing.Tests", "tests\ImageSharp.Drawing.Tests\ImageSharp.Drawing.Tests.csproj", "{D73D1783-E5F5-5094-6D3E-D77B45A51B4A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Drawing.Benchmarks", "tests\ImageSharp.Drawing.Benchmarks\ImageSharp.Drawing.Benchmarks.csproj", "{78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Drawing.ManualBenchmarks", "tests\ImageSharp.Drawing.ManualBenchmarks\ImageSharp.Drawing.ManualBenchmarks.csproj", "{62B1D30A-935F-819E-A227-A7FFF2E12CC1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -367,42 +443,6 @@ Global {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|x64.Build.0 = Release|Any CPU {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|x86.ActiveCfg = Release|Any CPU {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|x86.Build.0 = Release|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|x64.ActiveCfg = Debug|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|x64.Build.0 = Debug|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|x86.ActiveCfg = Debug|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|x86.Build.0 = Debug|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|Any CPU.Build.0 = Release|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|x64.ActiveCfg = Release|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|x64.Build.0 = Release|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|x86.ActiveCfg = Release|Any CPU - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|x86.Build.0 = Release|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Debug|Any CPU.Build.0 = Debug|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Debug|x64.ActiveCfg = Debug|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Debug|x64.Build.0 = Debug|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Debug|x86.ActiveCfg = Debug|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Debug|x86.Build.0 = Debug|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Release|Any CPU.ActiveCfg = Release|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Release|Any CPU.Build.0 = Release|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Release|x64.ActiveCfg = Release|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Release|x64.Build.0 = Release|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Release|x86.ActiveCfg = Release|Any CPU - {59804113-1DD4-4F80-8D06-35FF71652508}.Release|x86.Build.0 = Release|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Debug|x64.ActiveCfg = Debug|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Debug|x64.Build.0 = Debug|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Debug|x86.ActiveCfg = Debug|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Debug|x86.Build.0 = Debug|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Release|Any CPU.Build.0 = Release|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Release|x64.ActiveCfg = Release|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Release|x64.Build.0 = Release|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Release|x86.ActiveCfg = Release|Any CPU - {061582C2-658F-40AE-A978-7D74A4EB2C0A}.Release|x86.Build.0 = Release|Any CPU {C7606104-5D58-4670-912C-3F336606B02D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C7606104-5D58-4670-912C-3F336606B02D}.Debug|Any CPU.Build.0 = Debug|Any CPU {C7606104-5D58-4670-912C-3F336606B02D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -415,30 +455,6 @@ Global {C7606104-5D58-4670-912C-3F336606B02D}.Release|x64.Build.0 = Release|Any CPU {C7606104-5D58-4670-912C-3F336606B02D}.Release|x86.ActiveCfg = Release|Any CPU {C7606104-5D58-4670-912C-3F336606B02D}.Release|x86.Build.0 = Release|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Debug|x64.ActiveCfg = Debug|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Debug|x64.Build.0 = Debug|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Debug|x86.ActiveCfg = Debug|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Debug|x86.Build.0 = Debug|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Release|Any CPU.Build.0 = Release|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Release|x64.ActiveCfg = Release|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Release|x64.Build.0 = Release|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Release|x86.ActiveCfg = Release|Any CPU - {70193989-E587-451A-AF34-4C74FEE5DA5A}.Release|x86.Build.0 = Release|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Debug|x64.ActiveCfg = Debug|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Debug|x64.Build.0 = Debug|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Debug|x86.ActiveCfg = Debug|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Debug|x86.Build.0 = Debug|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Release|Any CPU.Build.0 = Release|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Release|x64.ActiveCfg = Release|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Release|x64.Build.0 = Release|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Release|x86.ActiveCfg = Release|Any CPU - {999D90DE-6940-4693-9F80-701F2D5DB77A}.Release|x86.Build.0 = Release|Any CPU {43A3CA46-2675-4AD4-88FC-DDC2E657F0B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {43A3CA46-2675-4AD4-88FC-DDC2E657F0B7}.Debug|Any CPU.Build.0 = Debug|Any CPU {43A3CA46-2675-4AD4-88FC-DDC2E657F0B7}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -451,18 +467,450 @@ Global {43A3CA46-2675-4AD4-88FC-DDC2E657F0B7}.Release|x64.Build.0 = Release|Any CPU {43A3CA46-2675-4AD4-88FC-DDC2E657F0B7}.Release|x86.ActiveCfg = Release|Any CPU {43A3CA46-2675-4AD4-88FC-DDC2E657F0B7}.Release|x86.Build.0 = Release|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Debug|x64.ActiveCfg = Debug|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Debug|x64.Build.0 = Debug|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Debug|x86.ActiveCfg = Debug|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Debug|x86.Build.0 = Debug|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Release|Any CPU.Build.0 = Release|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Release|x64.ActiveCfg = Release|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Release|x64.Build.0 = Release|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Release|x86.ActiveCfg = Release|Any CPU - {F0AAE591-A44E-48A5-8C74-395BDF98F646}.Release|x86.Build.0 = Release|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Debug|x64.ActiveCfg = Debug|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Debug|x64.Build.0 = Debug|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Debug|x86.ActiveCfg = Debug|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Debug|x86.Build.0 = Debug|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Release|Any CPU.Build.0 = Release|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Release|x64.ActiveCfg = Release|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Release|x64.Build.0 = Release|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Release|x86.ActiveCfg = Release|Any CPU + {333F3D4F-D251-5601-4851-307FF0F13D23}.Release|x86.Build.0 = Release|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|x64.ActiveCfg = Debug|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|x64.Build.0 = Debug|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|x86.ActiveCfg = Debug|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|x86.Build.0 = Debug|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|Any CPU.Build.0 = Release|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|x64.ActiveCfg = Release|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|x64.Build.0 = Release|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|x86.ActiveCfg = Release|Any CPU + {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|x86.Build.0 = Release|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|x64.ActiveCfg = Debug|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|x64.Build.0 = Debug|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|x86.ActiveCfg = Debug|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|x86.Build.0 = Debug|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|Any CPU.Build.0 = Release|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|x64.ActiveCfg = Release|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|x64.Build.0 = Release|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|x86.ActiveCfg = Release|Any CPU + {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|x86.Build.0 = Release|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Debug|x64.ActiveCfg = Debug|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Debug|x64.Build.0 = Debug|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Debug|x86.ActiveCfg = Debug|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Debug|x86.Build.0 = Debug|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Release|Any CPU.Build.0 = Release|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Release|x64.ActiveCfg = Release|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Release|x64.Build.0 = Release|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Release|x86.ActiveCfg = Release|Any CPU + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42}.Release|x86.Build.0 = Release|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Debug|x64.ActiveCfg = Debug|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Debug|x64.Build.0 = Debug|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Debug|x86.ActiveCfg = Debug|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Debug|x86.Build.0 = Debug|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Release|Any CPU.Build.0 = Release|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Release|x64.ActiveCfg = Release|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Release|x64.Build.0 = Release|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Release|x86.ActiveCfg = Release|Any CPU + {9AFBFA31-F636-4685-A8B9-4F623139C01B}.Release|x86.Build.0 = Release|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Debug|x64.ActiveCfg = Debug|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Debug|x64.Build.0 = Debug|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Debug|x86.ActiveCfg = Debug|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Debug|x86.Build.0 = Debug|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Release|Any CPU.Build.0 = Release|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Release|x64.ActiveCfg = Release|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Release|x64.Build.0 = Release|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Release|x86.ActiveCfg = Release|Any CPU + {AA365C58-287B-47CE-8EDA-6FA3BF10574E}.Release|x86.Build.0 = Release|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Debug|x64.ActiveCfg = Debug|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Debug|x64.Build.0 = Debug|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Debug|x86.ActiveCfg = Debug|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Debug|x86.Build.0 = Debug|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Release|Any CPU.Build.0 = Release|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Release|x64.ActiveCfg = Release|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Release|x64.Build.0 = Release|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Release|x86.ActiveCfg = Release|Any CPU + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D}.Release|x86.Build.0 = Release|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Debug|x64.ActiveCfg = Debug|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Debug|x64.Build.0 = Debug|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Debug|x86.ActiveCfg = Debug|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Debug|x86.Build.0 = Debug|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Release|Any CPU.Build.0 = Release|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Release|x64.ActiveCfg = Release|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Release|x64.Build.0 = Release|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Release|x86.ActiveCfg = Release|Any CPU + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED}.Release|x86.Build.0 = Release|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Debug|x64.ActiveCfg = Debug|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Debug|x64.Build.0 = Debug|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Debug|x86.ActiveCfg = Debug|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Debug|x86.Build.0 = Debug|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Release|Any CPU.Build.0 = Release|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Release|x64.ActiveCfg = Release|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Release|x64.Build.0 = Release|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Release|x86.ActiveCfg = Release|Any CPU + {E78916D9-1240-4C13-BEC3-242AFF8BFB84}.Release|x86.Build.0 = Release|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Debug|x64.ActiveCfg = Debug|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Debug|x64.Build.0 = Debug|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Debug|x86.ActiveCfg = Debug|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Debug|x86.Build.0 = Debug|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Release|Any CPU.Build.0 = Release|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Release|x64.ActiveCfg = Release|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Release|x64.Build.0 = Release|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Release|x86.ActiveCfg = Release|Any CPU + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8}.Release|x86.Build.0 = Release|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Debug|x64.ActiveCfg = Debug|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Debug|x64.Build.0 = Debug|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Debug|x86.ActiveCfg = Debug|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Debug|x86.Build.0 = Debug|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Release|Any CPU.Build.0 = Release|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Release|x64.ActiveCfg = Release|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Release|x64.Build.0 = Release|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Release|x86.ActiveCfg = Release|Any CPU + {7BD98865-305F-428F-BDCE-10311B96174A}.Release|x86.Build.0 = Release|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Debug|x64.ActiveCfg = Debug|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Debug|x64.Build.0 = Debug|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Debug|x86.ActiveCfg = Debug|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Debug|x86.Build.0 = Debug|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Release|Any CPU.Build.0 = Release|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Release|x64.ActiveCfg = Release|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Release|x64.Build.0 = Release|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Release|x86.ActiveCfg = Release|Any CPU + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86}.Release|x86.Build.0 = Release|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Debug|x64.ActiveCfg = Debug|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Debug|x64.Build.0 = Debug|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Debug|x86.ActiveCfg = Debug|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Debug|x86.Build.0 = Debug|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Release|Any CPU.Build.0 = Release|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Release|x64.ActiveCfg = Release|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Release|x64.Build.0 = Release|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Release|x86.ActiveCfg = Release|Any CPU + {2956B3D9-23A0-4818-A12D-178C15F5F3D9}.Release|x86.Build.0 = Release|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Debug|x64.ActiveCfg = Debug|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Debug|x64.Build.0 = Debug|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Debug|x86.ActiveCfg = Debug|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Debug|x86.Build.0 = Debug|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Release|Any CPU.Build.0 = Release|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Release|x64.ActiveCfg = Release|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Release|x64.Build.0 = Release|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Release|x86.ActiveCfg = Release|Any CPU + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC}.Release|x86.Build.0 = Release|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Debug|x64.ActiveCfg = Debug|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Debug|x64.Build.0 = Debug|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Debug|x86.ActiveCfg = Debug|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Debug|x86.Build.0 = Debug|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Release|Any CPU.Build.0 = Release|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Release|x64.ActiveCfg = Release|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Release|x64.Build.0 = Release|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Release|x86.ActiveCfg = Release|Any CPU + {D3730B8F-1568-4CAD-8617-0BA565E435D7}.Release|x86.Build.0 = Release|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Debug|x64.ActiveCfg = Debug|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Debug|x64.Build.0 = Debug|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Debug|x86.ActiveCfg = Debug|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Debug|x86.Build.0 = Debug|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Release|Any CPU.Build.0 = Release|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Release|x64.ActiveCfg = Release|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Release|x64.Build.0 = Release|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Release|x86.ActiveCfg = Release|Any CPU + {3EADAED3-1360-4944-9434-DE3359DE2233}.Release|x86.Build.0 = Release|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Debug|x64.ActiveCfg = Debug|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Debug|x64.Build.0 = Debug|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Debug|x86.ActiveCfg = Debug|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Debug|x86.Build.0 = Debug|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Release|Any CPU.Build.0 = Release|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Release|x64.ActiveCfg = Release|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Release|x64.Build.0 = Release|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Release|x86.ActiveCfg = Release|Any CPU + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D}.Release|x86.Build.0 = Release|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Debug|x64.ActiveCfg = Debug|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Debug|x64.Build.0 = Debug|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Debug|x86.ActiveCfg = Debug|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Debug|x86.Build.0 = Debug|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Release|Any CPU.Build.0 = Release|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Release|x64.ActiveCfg = Release|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Release|x64.Build.0 = Release|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Release|x86.ActiveCfg = Release|Any CPU + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9}.Release|x86.Build.0 = Release|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Debug|x64.ActiveCfg = Debug|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Debug|x64.Build.0 = Debug|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Debug|x86.ActiveCfg = Debug|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Debug|x86.Build.0 = Debug|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Release|Any CPU.Build.0 = Release|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Release|x64.ActiveCfg = Release|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Release|x64.Build.0 = Release|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Release|x86.ActiveCfg = Release|Any CPU + {5DA79C9E-5625-4D88-9D42-674D27538B74}.Release|x86.Build.0 = Release|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Debug|x64.ActiveCfg = Debug|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Debug|x64.Build.0 = Debug|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Debug|x86.ActiveCfg = Debug|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Debug|x86.Build.0 = Debug|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Release|Any CPU.Build.0 = Release|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Release|x64.ActiveCfg = Release|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Release|x64.Build.0 = Release|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Release|x86.ActiveCfg = Release|Any CPU + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC}.Release|x86.Build.0 = Release|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Debug|x64.ActiveCfg = Debug|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Debug|x64.Build.0 = Debug|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Debug|x86.ActiveCfg = Debug|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Debug|x86.Build.0 = Debug|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Release|Any CPU.Build.0 = Release|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Release|x64.ActiveCfg = Release|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Release|x64.Build.0 = Release|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Release|x86.ActiveCfg = Release|Any CPU + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9}.Release|x86.Build.0 = Release|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Debug|x64.ActiveCfg = Debug|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Debug|x64.Build.0 = Debug|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Debug|x86.ActiveCfg = Debug|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Debug|x86.Build.0 = Debug|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Release|Any CPU.Build.0 = Release|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Release|x64.ActiveCfg = Release|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Release|x64.Build.0 = Release|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Release|x86.ActiveCfg = Release|Any CPU + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6}.Release|x86.Build.0 = Release|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Debug|x64.ActiveCfg = Debug|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Debug|x64.Build.0 = Debug|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Debug|x86.ActiveCfg = Debug|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Debug|x86.Build.0 = Debug|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Release|Any CPU.Build.0 = Release|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Release|x64.ActiveCfg = Release|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Release|x64.Build.0 = Release|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Release|x86.ActiveCfg = Release|Any CPU + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB}.Release|x86.Build.0 = Release|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Debug|x64.ActiveCfg = Debug|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Debug|x64.Build.0 = Debug|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Debug|x86.ActiveCfg = Debug|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Debug|x86.Build.0 = Debug|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Release|Any CPU.Build.0 = Release|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Release|x64.ActiveCfg = Release|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Release|x64.Build.0 = Release|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Release|x86.ActiveCfg = Release|Any CPU + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171}.Release|x86.Build.0 = Release|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Debug|x64.ActiveCfg = Debug|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Debug|x64.Build.0 = Debug|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Debug|x86.ActiveCfg = Debug|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Debug|x86.Build.0 = Debug|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Release|Any CPU.Build.0 = Release|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Release|x64.ActiveCfg = Release|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Release|x64.Build.0 = Release|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Release|x86.ActiveCfg = Release|Any CPU + {93A496DB-F661-49E0-97C5-4680D92163ED}.Release|x86.Build.0 = Release|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Debug|x64.ActiveCfg = Debug|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Debug|x64.Build.0 = Debug|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Debug|x86.ActiveCfg = Debug|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Debug|x86.Build.0 = Debug|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Release|Any CPU.Build.0 = Release|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Release|x64.ActiveCfg = Release|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Release|x64.Build.0 = Release|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Release|x86.ActiveCfg = Release|Any CPU + {F7F47D90-331B-4D44-BB01-5CE31A2D2532}.Release|x86.Build.0 = Release|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Debug|x64.ActiveCfg = Debug|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Debug|x64.Build.0 = Debug|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Debug|x86.ActiveCfg = Debug|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Debug|x86.Build.0 = Debug|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Release|Any CPU.Build.0 = Release|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Release|x64.ActiveCfg = Release|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Release|x64.Build.0 = Release|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Release|x86.ActiveCfg = Release|Any CPU + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0}.Release|x86.Build.0 = Release|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Debug|x64.ActiveCfg = Debug|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Debug|x64.Build.0 = Debug|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Debug|x86.ActiveCfg = Debug|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Debug|x86.Build.0 = Debug|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Release|Any CPU.Build.0 = Release|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Release|x64.ActiveCfg = Release|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Release|x64.Build.0 = Release|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Release|x86.ActiveCfg = Release|Any CPU + {14E80B26-EDD6-468F-BC51-350DD9C83100}.Release|x86.Build.0 = Release|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Debug|x64.ActiveCfg = Debug|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Debug|x64.Build.0 = Debug|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Debug|x86.ActiveCfg = Debug|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Debug|x86.Build.0 = Debug|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Release|Any CPU.Build.0 = Release|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Release|x64.ActiveCfg = Release|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Release|x64.Build.0 = Release|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Release|x86.ActiveCfg = Release|Any CPU + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD}.Release|x86.Build.0 = Release|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Debug|x64.ActiveCfg = Debug|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Debug|x64.Build.0 = Debug|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Debug|x86.ActiveCfg = Debug|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Debug|x86.Build.0 = Debug|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Release|Any CPU.Build.0 = Release|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Release|x64.ActiveCfg = Release|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Release|x64.Build.0 = Release|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Release|x86.ActiveCfg = Release|Any CPU + {F1FD356A-0712-4379-AF87-11BB6F4D554B}.Release|x86.Build.0 = Release|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Debug|x64.ActiveCfg = Debug|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Debug|x64.Build.0 = Debug|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Debug|x86.ActiveCfg = Debug|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Debug|x86.Build.0 = Debug|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Release|Any CPU.Build.0 = Release|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Release|x64.ActiveCfg = Release|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Release|x64.Build.0 = Release|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Release|x86.ActiveCfg = Release|Any CPU + {E241F16B-FE81-4107-8C9F-6BFA029C7934}.Release|x86.Build.0 = Release|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Debug|x64.ActiveCfg = Debug|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Debug|x64.Build.0 = Debug|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Debug|x86.ActiveCfg = Debug|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Debug|x86.Build.0 = Debug|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Release|Any CPU.Build.0 = Release|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Release|x64.ActiveCfg = Release|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Release|x64.Build.0 = Release|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Release|x86.ActiveCfg = Release|Any CPU + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851}.Release|x86.Build.0 = Release|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Debug|x64.ActiveCfg = Debug|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Debug|x64.Build.0 = Debug|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Debug|x86.ActiveCfg = Debug|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Debug|x86.Build.0 = Debug|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Release|Any CPU.Build.0 = Release|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Release|x64.ActiveCfg = Release|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Release|x64.Build.0 = Release|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Release|x86.ActiveCfg = Release|Any CPU + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E}.Release|x86.Build.0 = Release|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Debug|x64.ActiveCfg = Debug|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Debug|x64.Build.0 = Debug|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Debug|x86.ActiveCfg = Debug|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Debug|x86.Build.0 = Debug|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Release|Any CPU.Build.0 = Release|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Release|x64.ActiveCfg = Release|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Release|x64.Build.0 = Release|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Release|x86.ActiveCfg = Release|Any CPU + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D}.Release|x86.Build.0 = Release|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Debug|x64.ActiveCfg = Debug|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Debug|x64.Build.0 = Debug|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Debug|x86.ActiveCfg = Debug|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Debug|x86.Build.0 = Debug|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Release|Any CPU.Build.0 = Release|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Release|x64.ActiveCfg = Release|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Release|x64.Build.0 = Release|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Release|x86.ActiveCfg = Release|Any CPU + {F28E672C-DBA2-496E-8784-E6F17CE261D5}.Release|x86.Build.0 = Release|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Debug|x64.ActiveCfg = Debug|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Debug|x64.Build.0 = Debug|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Debug|x86.ActiveCfg = Debug|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Debug|x86.Build.0 = Debug|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Release|Any CPU.Build.0 = Release|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Release|x64.ActiveCfg = Release|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Release|x64.Build.0 = Release|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Release|x86.ActiveCfg = Release|Any CPU + {5483EE45-0838-46C4-B59B-5B52F7C7D975}.Release|x86.Build.0 = Release|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Debug|x64.ActiveCfg = Debug|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Debug|x64.Build.0 = Debug|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Debug|x86.ActiveCfg = Debug|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Debug|x86.Build.0 = Debug|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Release|Any CPU.Build.0 = Release|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Release|x64.ActiveCfg = Release|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Release|x64.Build.0 = Release|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Release|x86.ActiveCfg = Release|Any CPU + {FE71764E-E7BD-B378-8A4C-86929A28AD25}.Release|x86.Build.0 = Release|Any CPU {B5CF5531-624F-9F49-46BD-937FF4062F54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B5CF5531-624F-9F49-46BD-937FF4062F54}.Debug|Any CPU.Build.0 = Debug|Any CPU {B5CF5531-624F-9F49-46BD-937FF4062F54}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -475,6 +923,78 @@ Global {B5CF5531-624F-9F49-46BD-937FF4062F54}.Release|x64.Build.0 = Release|Any CPU {B5CF5531-624F-9F49-46BD-937FF4062F54}.Release|x86.ActiveCfg = Release|Any CPU {B5CF5531-624F-9F49-46BD-937FF4062F54}.Release|x86.Build.0 = Release|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Debug|x64.Build.0 = Debug|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Debug|x86.Build.0 = Debug|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Release|Any CPU.Build.0 = Release|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Release|x64.ActiveCfg = Release|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Release|x64.Build.0 = Release|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Release|x86.ActiveCfg = Release|Any CPU + {97619DEB-226C-EB67-140D-0C7E67B2F7D0}.Release|x86.Build.0 = Release|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Debug|x64.ActiveCfg = Debug|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Debug|x64.Build.0 = Debug|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Debug|x86.ActiveCfg = Debug|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Debug|x86.Build.0 = Debug|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Release|Any CPU.Build.0 = Release|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Release|x64.ActiveCfg = Release|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Release|x64.Build.0 = Release|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Release|x86.ActiveCfg = Release|Any CPU + {4570F31D-D00F-6A80-DB7C-74826CBD15AE}.Release|x86.Build.0 = Release|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Debug|x64.ActiveCfg = Debug|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Debug|x64.Build.0 = Debug|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Debug|x86.ActiveCfg = Debug|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Debug|x86.Build.0 = Debug|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Release|Any CPU.Build.0 = Release|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Release|x64.ActiveCfg = Release|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Release|x64.Build.0 = Release|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Release|x86.ActiveCfg = Release|Any CPU + {638FC2AE-7FC1-6527-E051-C6B34803F08D}.Release|x86.Build.0 = Release|Any CPU + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Debug|x64.ActiveCfg = Debug|x64 + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Debug|x64.Build.0 = Debug|x64 + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Debug|x86.ActiveCfg = Debug|x86 + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Debug|x86.Build.0 = Debug|x86 + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Release|Any CPU.Build.0 = Release|Any CPU + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Release|x64.ActiveCfg = Release|x64 + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Release|x64.Build.0 = Release|x64 + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Release|x86.ActiveCfg = Release|x86 + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A}.Release|x86.Build.0 = Release|x86 + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Debug|x64.ActiveCfg = Debug|Any CPU + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Debug|x64.Build.0 = Debug|Any CPU + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Debug|x86.ActiveCfg = Debug|Any CPU + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Debug|x86.Build.0 = Debug|Any CPU + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Release|Any CPU.Build.0 = Release|Any CPU + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Release|x64.ActiveCfg = Release|Any CPU + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Release|x64.Build.0 = Release|Any CPU + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Release|x86.ActiveCfg = Release|Any CPU + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3}.Release|x86.Build.0 = Release|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Debug|x64.ActiveCfg = Debug|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Debug|x64.Build.0 = Debug|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Debug|x86.ActiveCfg = Debug|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Debug|x86.Build.0 = Debug|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Release|Any CPU.Build.0 = Release|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Release|x64.ActiveCfg = Release|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Release|x64.Build.0 = Release|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Release|x86.ActiveCfg = Release|Any CPU + {62B1D30A-935F-819E-A227-A7FFF2E12CC1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -497,25 +1017,65 @@ Global {6458AFCB-A159-47D5-8F2B-50C95C0915E0} = {DB21FED7-E8CB-4B00-9EB2-9144D32A590A} {39F5197B-CF6C-41A5-9739-7F97E78BB104} = {6458AFCB-A159-47D5-8F2B-50C95C0915E0} {E1C42A6F-913B-4A7B-B1A8-2BB62843B254} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} - {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} - {59804113-1DD4-4F80-8D06-35FF71652508} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {68A8CC40-6AED-4E96-B524-31B1158FDEEA} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {23859314-5693-4E6C-BE5C-80A433439D2A} = {1799C43E-5C54-4A8F-8D64-B1475241DB0D} - {061582C2-658F-40AE-A978-7D74A4EB2C0A} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {C7606104-5D58-4670-912C-3F336606B02D} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} - {70193989-E587-451A-AF34-4C74FEE5DA5A} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} - {999D90DE-6940-4693-9F80-701F2D5DB77A} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} {43A3CA46-2675-4AD4-88FC-DDC2E657F0B7} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} - {F0AAE591-A44E-48A5-8C74-395BDF98F646} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} + {333F3D4F-D251-5601-4851-307FF0F13D23} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} + {4A922B77-34EC-EA6A-8E96-8353C8FA0640} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} + {5EC089DC-69F7-643B-93BB-293D199F5F74} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} + {38CA36F4-AD3A-4B37-AB75-9DEF57219A42} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {9AFBFA31-F636-4685-A8B9-4F623139C01B} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {AA365C58-287B-47CE-8EDA-6FA3BF10574E} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {80A082A3-4FC7-4AD4-AFB6-503B3033B32D} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {7237DC3A-C8C5-49C1-A9CB-00D93E8F6CED} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {E78916D9-1240-4C13-BEC3-242AFF8BFB84} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {F253A0A3-3202-45AE-A2F5-30098F8B3BB8} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {7BD98865-305F-428F-BDCE-10311B96174A} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {3CCFA7A8-ED2D-4E85-9024-9FCF5EC63C86} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {2956B3D9-23A0-4818-A12D-178C15F5F3D9} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {505BA299-AAE0-4258-934F-5CF9B6DC4EAC} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {D3730B8F-1568-4CAD-8617-0BA565E435D7} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {3EADAED3-1360-4944-9434-DE3359DE2233} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {B8D27D9E-1D57-4F89-9CCE-B1B5E947947D} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {7A7E13F8-68AE-4EE3-AA58-410EB33125E9} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {5DA79C9E-5625-4D88-9D42-674D27538B74} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {B7840472-147D-49DA-9AC4-0EEB5E9F27FC} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {0CFDCBC5-32FD-458B-A487-D5E9551BADF9} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {45053362-1DBC-4EAD-8B4C-51B6E56F60F6} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {ABD471D5-4CDF-48E8-BD19-0CC2AA4868EB} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {1C3FDE70-832F-41FB-A9A4-4DD8DBBBF171} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {93A496DB-F661-49E0-97C5-4680D92163ED} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {F7F47D90-331B-4D44-BB01-5CE31A2D2532} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {F0AA05AC-508E-4D13-B416-7D080A6ED5B0} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {14E80B26-EDD6-468F-BC51-350DD9C83100} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {1AC23054-F30F-48A9-B750-1D60BDCFE3FD} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {F1FD356A-0712-4379-AF87-11BB6F4D554B} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {E241F16B-FE81-4107-8C9F-6BFA029C7934} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {B1749E6E-EF88-4C46-89BC-CF1BF7A46851} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {A45F0090-68EA-4FC5-AAEB-6D71CBB1483E} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {099273DB-F00E-43C2-BCAE-DA7A1C3AB49D} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {F28E672C-DBA2-496E-8784-E6F17CE261D5} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {5483EE45-0838-46C4-B59B-5B52F7C7D975} = {29F3AA1B-6D75-C75D-9183-749A65CD24A7} + {FE71764E-E7BD-B378-8A4C-86929A28AD25} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {B5CF5531-624F-9F49-46BD-937FF4062F54} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} + {97619DEB-226C-EB67-140D-0C7E67B2F7D0} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} + {4570F31D-D00F-6A80-DB7C-74826CBD15AE} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} + {638FC2AE-7FC1-6527-E051-C6B34803F08D} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} + {D73D1783-E5F5-5094-6D3E-D77B45A51B4A} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} + {78BC7FFA-DFB0-B9ED-03C7-3A59E499B4B3} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} + {62B1D30A-935F-819E-A227-A7FFF2E12CC1} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution - shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{061582c2-658f-40ae-a978-7d74a4eb2c0a}*SharedItemsImports = 5 shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{2e33181e-6e28-4662-a801-e2e7dc206029}*SharedItemsImports = 5 + ..\Fonts\shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{4a922b77-34ec-ea6a-8e96-8353c8fa0640}*SharedItemsImports = 5 + ..\PolygonClipper\shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{5ec089dc-69f7-643b-93bb-293d199f5f74}*SharedItemsImports = 5 + shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{638fc2ae-7fc1-6527-e051-c6b34803f08d}*SharedItemsImports = 5 shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13 + ..\ImageSharp\shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{fe71764e-e7bd-b378-8a4c-86929a28ad25}*SharedItemsImports = 5 EndGlobalSection GlobalSection(Performance) = preSolution HasPerformanceSessions = true diff --git a/samples/AvaloniaControlCatalog/App.xaml b/samples/AvaloniaControlCatalog/App.xaml new file mode 100644 index 00000000..42f35c72 --- /dev/null +++ b/samples/AvaloniaControlCatalog/App.xaml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + #33000000 + #99000000 + #FFE6E6E6 + #FF000000 + + + #33FFFFFF + #99FFFFFF + #FF1F1F1F + #FFFFFFFF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/App.xaml.cs b/samples/AvaloniaControlCatalog/App.xaml.cs new file mode 100644 index 00000000..159e4ebf --- /dev/null +++ b/samples/AvaloniaControlCatalog/App.xaml.cs @@ -0,0 +1,175 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using Avalonia.Platform; +using Avalonia.Styling; +using Avalonia.Themes.Simple; +using Avalonia.Themes.Fluent; +using ControlCatalog.Models; +using ControlCatalog.ViewModels; + +namespace ControlCatalog +{ + public class App : Application + { + private readonly Styles _themeStylesContainer = new(); + private FluentTheme? _fluentTheme; + private SimpleTheme? _simpleTheme; + private IStyle? _colorPickerFluent, _colorPickerSimple; + + public App() + { + DataContext = new ApplicationViewModel(); + } + + public override void Initialize() + { + Styles.Add(_themeStylesContainer); + + AvaloniaXamlLoader.Load(this); + + _fluentTheme = (FluentTheme)Resources["FluentTheme"]!; + _simpleTheme = (SimpleTheme)Resources["SimpleTheme"]!; + _colorPickerFluent = (IStyle)Resources["ColorPickerFluent"]!; + _colorPickerSimple = (IStyle)Resources["ColorPickerSimple"]!; + + SetCatalogThemes(CatalogTheme.Fluent); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) + { + desktopLifetime.MainWindow = new MainWindow { DataContext = new MainWindowViewModel() }; + + if (int.TryParse(Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_EXIT_AFTER_MS"), out int exitDelay) && exitDelay > 0) + { + // Test-only hook: let the real desktop lifetime render normally, then shut it down + // without a human closing the window. + desktopLifetime.MainWindow.Opened += async (_, _) => + { + await Task.Delay(exitDelay); + desktopLifetime.Shutdown(); + }; + } + } + else if (ApplicationLifetime is IActivityApplicationLifetime singleViewFactoryApplicationLifetime) + { + singleViewFactoryApplicationLifetime.MainViewFactory = () => new PageNavigationHost() + { + Page = new MainView { DataContext = new MainWindowViewModel() } + }; + } + else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) + { + singleViewLifetime.MainView = new PageNavigationHost() + { + Page = new MainView { DataContext = new MainWindowViewModel() } + }; + } + + if (this.TryGetFeature() is { } activatableApplicationLifetime) + { + activatableApplicationLifetime.Activated += (sender, args) => + Console.WriteLine($"App activated: {args.Kind}"); + activatableApplicationLifetime.Deactivated += (sender, args) => + Console.WriteLine($"App deactivated: {args.Kind}"); + } + + base.OnFrameworkInitializationCompleted(); + } + + public void OnDockNewWindowClicked(object? sender, EventArgs e) + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime) + { + var window = new MainWindow(); + window.Show(); + } + } + + public void OnDockShowMainWindowClicked(object? sender, EventArgs e) + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) + { + desktopLifetime.MainWindow?.Activate(); + } + } + + private int _dockMenuItemCount; + + public void OnDockAddItemClicked(object? sender, EventArgs e) + { + var dockMenu = NativeDock.GetMenu(this); + if (dockMenu is not null) + { + _dockMenuItemCount++; + var item = new NativeMenuItem($"New item {_dockMenuItemCount}"); + item.Click += (_, _) => + { + dockMenu.Items.Remove(item); + }; + dockMenu.Items.Insert(0, item); + } + } + + private CatalogTheme _prevTheme; + public static CatalogTheme CurrentTheme => ((App)Current!)._prevTheme; + public static void SetCatalogThemes(CatalogTheme theme) + { + var app = (App)Current!; + var prevTheme = app._prevTheme; + app._prevTheme = theme; + var shouldReopenWindow = prevTheme != theme; + + if (app._themeStylesContainer.Count == 0) + { + app._themeStylesContainer.Add(new Style()); + app._themeStylesContainer.Add(new Style()); + app._themeStylesContainer.Add(new Style()); + } + + if (theme == CatalogTheme.Fluent) + { + app._themeStylesContainer[0] = app._fluentTheme!; + app._themeStylesContainer[1] = app._colorPickerFluent!; + } + else if (theme == CatalogTheme.Simple) + { + app._themeStylesContainer[0] = app._simpleTheme!; + app._themeStylesContainer[1] = app._colorPickerSimple!; + } + + if (shouldReopenWindow) + { + if (app.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) + { + var oldWindow = desktopLifetime.MainWindow; + var newWindow = new MainWindow() + { + DataContext = new MainWindowViewModel() + }; + desktopLifetime.MainWindow = newWindow; + newWindow.Show(); + oldWindow?.Close(); + } + else if (app.ApplicationLifetime is IActivityApplicationLifetime singleViewFactoryApplicationLifetime) + { + singleViewFactoryApplicationLifetime.MainViewFactory = () => new PageNavigationHost() + { + Page = new MainView { DataContext = new MainWindowViewModel() } + }; + } + else if (app.ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) + { + singleViewLifetime.MainView = new PageNavigationHost() + { + Page = new MainView { DataContext = new MainWindowViewModel() } + }; + } + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/Assets/CurvedHeader/avatar.jpg b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/avatar.jpg new file mode 100644 index 00000000..ddcf4395 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/avatar.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72bc7dc39313ed42adb7ccc430b4083e2d4c91b4433a7eca0c620fdb64892316 +size 270846 diff --git a/samples/AvaloniaControlCatalog/Assets/CurvedHeader/featured.jpg b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/featured.jpg new file mode 100644 index 00000000..bfc31e4b --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/featured.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b86c986b58e751da873fb022a9c3fbc8608c5884db65dca711866f3b63b59a3 +size 342297 diff --git a/samples/AvaloniaControlCatalog/Assets/CurvedHeader/product1.jpg b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/product1.jpg new file mode 100644 index 00000000..4842769e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/product1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3b4ab4d1fe41bc08f45170c393f9c7bf0dab08e3f9334ec781a48a25f6926e0 +size 392467 diff --git a/samples/AvaloniaControlCatalog/Assets/CurvedHeader/product2.jpg b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/product2.jpg new file mode 100644 index 00000000..0fab3111 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/product2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:328cf4e51f1aa7c85263d7f643f949fbdf026a02267d09a9549a9b724ce260cb +size 311991 diff --git a/samples/AvaloniaControlCatalog/Assets/CurvedHeader/product3.jpg b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/product3.jpg new file mode 100644 index 00000000..ef7100e6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/product3.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:041d41538d29d7b8dc60c4f004eb6c2214a50367bfe00ef65255ce8209d61dd2 +size 314528 diff --git a/samples/AvaloniaControlCatalog/Assets/CurvedHeader/update1.jpg b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/update1.jpg new file mode 100644 index 00000000..fbb14a94 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/update1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b91f15899decfc3ee76c1a81a3046690fd6e311d99967ec8a4cc3f3de71eda22 +size 201420 diff --git a/samples/AvaloniaControlCatalog/Assets/CurvedHeader/update2.jpg b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/update2.jpg new file mode 100644 index 00000000..e66023e5 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/update2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1705f35eb2edd8b7a8e244a44488670f54288910277e8b48671837aa02e4a18 +size 202337 diff --git a/samples/AvaloniaControlCatalog/Assets/CurvedHeader/update3.jpg b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/update3.jpg new file mode 100644 index 00000000..cf1c0277 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/CurvedHeader/update3.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0540dc8d5d03e5043ff4c976a4946efbcb01a4f7c3e0c4f6005dbf87630be478 +size 219094 diff --git a/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Bold.ttf b/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Bold.ttf new file mode 100644 index 00000000..0a35698d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Bold.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da4f442e66843990825ed4757e27ad3442cad83f9844cc503e8ece85e00f77f2 +size 290916 diff --git a/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-BoldItalic.ttf b/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-BoldItalic.ttf new file mode 100644 index 00000000..1dcbaab5 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-BoldItalic.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4413cbf92a508e6a6eb0a089ee9bcf1b94f0b366ea755c5c64b6d4755f160c40 +size 103200 diff --git a/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Italic.ttf b/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Italic.ttf new file mode 100644 index 00000000..5f1badfb --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Italic.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dac7846e443ec58c0802cd6fa55836060a62d8a7448c0bccff5242bac9e71d0e +size 103828 diff --git a/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Regular.ttf b/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Regular.ttf new file mode 100644 index 00000000..68410ff1 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Fonts/SourceSansPro-Regular.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71d10a86b4c54a5a9c0c8b467e53ac67d79edb96c956e4e9f65a7074dfb9992a +size 293516 diff --git a/samples/AvaloniaControlCatalog/Assets/Fonts/WenQuanYiMicroHei-01.ttf b/samples/AvaloniaControlCatalog/Assets/Fonts/WenQuanYiMicroHei-01.ttf new file mode 100644 index 00000000..d70ce051 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Fonts/WenQuanYiMicroHei-01.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e2297609126d20a5c3f35eb0ed8ac8bc7e4a2290a820f4055c0703d2c598912 +size 4505016 diff --git a/samples/AvaloniaControlCatalog/Assets/Icon.png b/samples/AvaloniaControlCatalog/Assets/Icon.png new file mode 100644 index 00000000..26522b1f --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1821e7bd48a6f407bddcbc1fd789132a3d5b6a5b787951114e3360b4bd650c96 +size 14349 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/avatar.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/avatar.jpg new file mode 100644 index 00000000..baeae257 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/avatar.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c48b24b7af0a316393b9492b04f26c3716efb385f222cb1f3098a3225e5fd3d4 +size 22590 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/dest_alps.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/dest_alps.jpg new file mode 100644 index 00000000..db24b89b --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/dest_alps.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:942940764a67ff522ab558510d0fc28d8c6d8139d36ad12f0150ca80061e5911 +size 40061 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/dest_forest.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/dest_forest.jpg new file mode 100644 index 00000000..3034a1fa --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/dest_forest.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13210c53e74791192cd5f0a08e525acbb84a4fe01634af2a37c26d4440c13335 +size 57483 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/dest_norway.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/dest_norway.jpg new file mode 100644 index 00000000..d6e6bde5 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/dest_norway.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d35bf25fec4fe7e043daf0a2db45fd60022d6ac30f822de04476e9b75a1613b6 +size 26764 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/exp_angkor.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/exp_angkor.jpg new file mode 100644 index 00000000..33d774d8 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/exp_angkor.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67d40972a68c877c5caa83ede7e9f9f740262b2f66f643c61f37bff5455e7ffd +size 78618 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/exp_tokyo.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/exp_tokyo.jpg new file mode 100644 index 00000000..271b47c2 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/exp_tokyo.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:386207dd4f978585f3eab30b99e26edd7d809c739a65d6e6b21c6044d36c1899 +size 104781 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_alpine.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_alpine.jpg new file mode 100644 index 00000000..6a5363ec --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_alpine.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a952107d9fcdc461dd40dd185e661813766c6f03dee7dfa1727e85973e15b0c +size 39021 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_bay.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_bay.jpg new file mode 100644 index 00000000..e96394b4 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_bay.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae12c0a8c2580d98ed4ed4fea5b156f7849081199e1b2716f3c8abb4e986de0f +size 35928 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_city.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_city.jpg new file mode 100644 index 00000000..a7b42af8 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_city.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9d399a312edfc37e9b485c02b11cb3cd5c8eae45c6638966a7a65fe7f8c5218 +size 38189 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_paris.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_paris.jpg new file mode 100644 index 00000000..a0a71973 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_paris.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea917b0df0e1dfb0125f782dd86942eec48f81da011cf0c69719efbf050dac75 +size 16808 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_tropical.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_tropical.jpg new file mode 100644 index 00000000..c1af3469 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_tropical.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2885affa5a5400330e5ddbf2a88b4a5ffedc38e7cd7a5d3c4988c84869d8b481 +size 23530 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_venice.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_venice.jpg new file mode 100644 index 00000000..8331f302 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/gallery_venice.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d80c97e65cf610c562ebda65357356aa6d4d47c32e36e7a77212449aeb9a457c +size 32633 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/story1.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/story1.jpg new file mode 100644 index 00000000..024fa15b --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/story1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04fe12f3211d8413bcf576839355460357081c5c29b3ee3c8767d9008a00aff9 +size 13068 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/story2.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/story2.jpg new file mode 100644 index 00000000..ce3d2d40 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/story2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c81284b88d9cc65af2e35d286dbff0c72da487672358e332be8499c2a174ffe +size 10385 diff --git a/samples/AvaloniaControlCatalog/Assets/ModernApp/story3.jpg b/samples/AvaloniaControlCatalog/Assets/ModernApp/story3.jpg new file mode 100644 index 00000000..a491aa12 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/ModernApp/story3.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5fec94c831245620f3bfc82a34d62482668359418dd4c72ba8b0f16671ae521 +size 9262 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/cast1.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/cast1.jpg new file mode 100644 index 00000000..a82fd4de --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/cast1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f085e47346df78b2d8d7fc3202c94f0a05f51c94669842f1deaca61edf3513a0 +size 383729 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/cast2.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/cast2.jpg new file mode 100644 index 00000000..52efa377 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/cast2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97f9c47d312a7a20d6f816c1f1aa12553d889cdcc812895c6de133e5c4163d3f +size 384405 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/continue1.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/continue1.jpg new file mode 100644 index 00000000..28e76e59 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/continue1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8864dd8af0a41fecdcd10c9f3dd7f46fd40835eaceec84d9c3bdb469b908c1de +size 243330 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/hero.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/hero.jpg new file mode 100644 index 00000000..d19b19d2 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/hero.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1d19998f473a10f475fb09dbc9333846e33926df134cae53fbb26ef12dfa065 +size 365476 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/morelike1.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/morelike1.jpg new file mode 100644 index 00000000..5a74f00e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/morelike1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:232f551a9920a0df27e3f63e6c1bb0284b32654873bbdb6e0fd01d2b03fa289e +size 386942 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/search1.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/search1.jpg new file mode 100644 index 00000000..fe0ac989 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/search1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e49187f3a3d58250c0340654ef62986c5bcf71cc2bc7ccf8e9f464d06d29843 +size 365280 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/toprated1.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/toprated1.jpg new file mode 100644 index 00000000..9c0594bf --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/toprated1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:379bed75f5e9fdcf81e0be3b8ce244097b3f941945c64bd4f793501062b92ab8 +size 323000 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/toprated2.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/toprated2.jpg new file mode 100644 index 00000000..bc2ac63a --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/toprated2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6027435c1abaf0a84f7badf3b496589c2eb12211b86506273c374a3da6fc4328 +size 193691 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/toprated3.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/toprated3.jpg new file mode 100644 index 00000000..fc909227 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/toprated3.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81bd4874b15875b3297f59958b67e76fe0cc36c84a3c3e55f1ec72466ef0de01 +size 276190 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/toprated4.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/toprated4.jpg new file mode 100644 index 00000000..428d1837 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/toprated4.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b64cedeb1b6e382eb48c4ac46667fccf8fcdca4de7a754b6b6e9afed5bac0c17 +size 338325 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/trending1.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/trending1.jpg new file mode 100644 index 00000000..275d2095 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/trending1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f48ab966356c739a134dfdf518572722d655a1c8ff19d16349f93dd4c5da574 +size 354912 diff --git a/samples/AvaloniaControlCatalog/Assets/Movies/trending2.jpg b/samples/AvaloniaControlCatalog/Assets/Movies/trending2.jpg new file mode 100644 index 00000000..0714cc90 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Movies/trending2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c4ae07bf3716d1131c59766518d74e50e0a6150f9a018c74fde92b39b786470 +size 360307 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/cat_hiit.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/cat_hiit.jpg new file mode 100644 index 00000000..6cb7b1dc --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/cat_hiit.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d2255faa3104c9dbe66b1d5a0a27b9eecac836b322fdf30f43d84d798bc0904 +size 363012 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/cat_strength.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/cat_strength.jpg new file mode 100644 index 00000000..7619e560 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/cat_strength.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98eff7032787cf75e5c01968070ca3a18b4167d8124e7dde37ab5ee883e9206f +size 328458 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/cat_yoga.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/cat_yoga.jpg new file mode 100644 index 00000000..99d8a442 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/cat_yoga.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:273226cb81451cfed6405f3f337dfd5a1a90f806052c1c5b849fcc31c9ed2bb9 +size 220062 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/ex_bench.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/ex_bench.jpg new file mode 100644 index 00000000..dff9c638 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/ex_bench.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b6c1e389d2e8a9b3bf8638629ccc6be5dda989fa5aeeb7fbac3b15c347aaf3e +size 348183 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/ex_deadlifts.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/ex_deadlifts.jpg new file mode 100644 index 00000000..1bf715e1 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/ex_deadlifts.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:729e1e7ab3cad488f8c15055bd2bc46dbf4df378235d09b17b6faf345535dcae +size 350168 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/ex_overhead.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/ex_overhead.jpg new file mode 100644 index 00000000..ba6c56f2 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/ex_overhead.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d05add74876aa9913d0668ba14814def56385cc6d5a71ef32e2947aa8cf35e4 +size 318869 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/ex_pullups.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/ex_pullups.jpg new file mode 100644 index 00000000..d73d2714 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/ex_pullups.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c61f8680ec76d0a6dce17194f90960bd071f3e0eb9f4e5c1fa44ad42e08d5fee +size 259525 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/ex_squats.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/ex_squats.jpg new file mode 100644 index 00000000..db4673a6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/ex_squats.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4c0ba6f7dfecb0f725199f27771fa143626fd4af82c2267a3e6a4e033d4c2bc +size 349045 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/profile_avatar.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/profile_avatar.jpg new file mode 100644 index 00000000..e878e284 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/profile_avatar.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:099c45bf78acddb5e3706c08c78570b2216bc287bee4c9551331ef7755985a87 +size 234819 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/rec_fullbody.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/rec_fullbody.jpg new file mode 100644 index 00000000..1edfdab7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/rec_fullbody.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f74e1316bc51221749ebb06bdfbd6e7a291a3cd2bf27482ff8927a1fe7722ff +size 299718 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/rec_mobility.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/rec_mobility.jpg new file mode 100644 index 00000000..8a197c6f --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/rec_mobility.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80ea995c7241abeda718538e4d39e99c905c561c6b646a536c75cac3968b942a +size 378365 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/rec_powercore.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/rec_powercore.jpg new file mode 100644 index 00000000..35621156 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/rec_powercore.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4c4fbf95b31719a0c1aa149635e17ae1165e93a4bae19774c0dd628025bdc5f +size 333866 diff --git a/samples/AvaloniaControlCatalog/Assets/Pulse/workout_hero.jpg b/samples/AvaloniaControlCatalog/Assets/Pulse/workout_hero.jpg new file mode 100644 index 00000000..6736801e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Pulse/workout_hero.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9cce5e1a6dba98d1517ecba88a22b14c9974a775f83190ecfd700b1a09e2996 +size 357270 diff --git a/samples/AvaloniaControlCatalog/Assets/Restaurant/dish1.jpg b/samples/AvaloniaControlCatalog/Assets/Restaurant/dish1.jpg new file mode 100644 index 00000000..47c7c3e4 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Restaurant/dish1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e53d7c95d20decc8fd69432d737f49955d9aad6c4b5e97dbfa8f7d14034e3b2 +size 63977 diff --git a/samples/AvaloniaControlCatalog/Assets/Restaurant/dish2.jpg b/samples/AvaloniaControlCatalog/Assets/Restaurant/dish2.jpg new file mode 100644 index 00000000..7da1fd38 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Restaurant/dish2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0e26e64efacd609c7c47b5f46f2238f5eeb69a94374c1d57b2bc773962f9596 +size 19720 diff --git a/samples/AvaloniaControlCatalog/Assets/Restaurant/dish3.jpg b/samples/AvaloniaControlCatalog/Assets/Restaurant/dish3.jpg new file mode 100644 index 00000000..0e33613e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Restaurant/dish3.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5fdc8a579038c204a04c44e6828f89e3c18026537258283d46a416fcadc4256a +size 56364 diff --git a/samples/AvaloniaControlCatalog/Assets/Restaurant/dish4.jpg b/samples/AvaloniaControlCatalog/Assets/Restaurant/dish4.jpg new file mode 100644 index 00000000..42b17c21 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Restaurant/dish4.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9854911823e06db37e4ab09de2946c46ffadd035735a9e4ac907b0ed1a6ec92c +size 46057 diff --git a/samples/AvaloniaControlCatalog/Assets/Restaurant/featured_dish.jpg b/samples/AvaloniaControlCatalog/Assets/Restaurant/featured_dish.jpg new file mode 100644 index 00000000..3756a344 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Restaurant/featured_dish.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c73b185ddc65fef06e932526f145e2d94ce3b2421b3e75ecca24d54568274e9f +size 68998 diff --git a/samples/AvaloniaControlCatalog/Assets/Restaurant/user_avatar.jpg b/samples/AvaloniaControlCatalog/Assets/Restaurant/user_avatar.jpg new file mode 100644 index 00000000..ce3d2d40 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Restaurant/user_avatar.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c81284b88d9cc65af2e35d286dbff0c72da487672358e332be8499c2a174ffe +size 10385 diff --git a/samples/AvaloniaControlCatalog/Assets/RetroGaming/cyber_city.jpg b/samples/AvaloniaControlCatalog/Assets/RetroGaming/cyber_city.jpg new file mode 100644 index 00000000..e931dc47 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/RetroGaming/cyber_city.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87d6f01e3c0216f1b87b165fd30a45bd58ca01efd38e779eacf09e9acfd771f6 +size 354653 diff --git a/samples/AvaloniaControlCatalog/Assets/RetroGaming/dungeon_bit.jpg b/samples/AvaloniaControlCatalog/Assets/RetroGaming/dungeon_bit.jpg new file mode 100644 index 00000000..4c2c3016 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/RetroGaming/dungeon_bit.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1879f78ca91253fd12e66e9f10b1fe5943471f1c84561086dd4ebb930e26ef20 +size 468907 diff --git a/samples/AvaloniaControlCatalog/Assets/RetroGaming/forest_spirit.jpg b/samples/AvaloniaControlCatalog/Assets/RetroGaming/forest_spirit.jpg new file mode 100644 index 00000000..36c5957d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/RetroGaming/forest_spirit.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e03ea60a9425aa35d8daea7336eb8ba6eb5a6388bc7c4b918480a1e8153f66ad +size 426054 diff --git a/samples/AvaloniaControlCatalog/Assets/RetroGaming/hero.jpg b/samples/AvaloniaControlCatalog/Assets/RetroGaming/hero.jpg new file mode 100644 index 00000000..deeb1959 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/RetroGaming/hero.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a12f0998ac595661b6eb9b0db10d2c8b0dc94036a9ac8979aabef0907bde8517 +size 220826 diff --git a/samples/AvaloniaControlCatalog/Assets/RetroGaming/neon_ninja.jpg b/samples/AvaloniaControlCatalog/Assets/RetroGaming/neon_ninja.jpg new file mode 100644 index 00000000..240ae5ea --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/RetroGaming/neon_ninja.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbd118a09032ee0e8e095199f65a8230ffadb301f02799308d8dc9221f617f38 +size 303206 diff --git a/samples/AvaloniaControlCatalog/Assets/RetroGaming/neon_racer.jpg b/samples/AvaloniaControlCatalog/Assets/RetroGaming/neon_racer.jpg new file mode 100644 index 00000000..78723686 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/RetroGaming/neon_racer.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eae91ec8b75d3910700ec06ba95da8dbef9e6390414db02466e1e8c8c12bf359 +size 398687 diff --git a/samples/AvaloniaControlCatalog/Assets/RetroGaming/pixel_quest.jpg b/samples/AvaloniaControlCatalog/Assets/RetroGaming/pixel_quest.jpg new file mode 100644 index 00000000..ea0352cb --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/RetroGaming/pixel_quest.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c14d6c36f708f5182d97ef13f5f93ab38d976ab69700d89936eede18f5041ea4 +size 318451 diff --git a/samples/AvaloniaControlCatalog/Assets/RetroGaming/space_voids.jpg b/samples/AvaloniaControlCatalog/Assets/RetroGaming/space_voids.jpg new file mode 100644 index 00000000..9eaa32bd --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/RetroGaming/space_voids.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04404c6cb7b46ac40b5e5d939328c00d81952ccd29d8f766f038f7bb5b15e47b +size 346663 diff --git a/samples/AvaloniaControlCatalog/Assets/Sanctuary/city_bg.jpg b/samples/AvaloniaControlCatalog/Assets/Sanctuary/city_bg.jpg new file mode 100644 index 00000000..84094687 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Sanctuary/city_bg.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a7289d08dfd44e1f9b1b9165492924d34600e14330086c3bf311c0d3ccaff429 +size 416179 diff --git a/samples/AvaloniaControlCatalog/Assets/Sanctuary/forest_bg.jpg b/samples/AvaloniaControlCatalog/Assets/Sanctuary/forest_bg.jpg new file mode 100644 index 00000000..5cc52e30 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Sanctuary/forest_bg.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:84201ad5c3993927ff95093312f194401f85359298bb8dcc0061d9a6d9576c2b +size 460289 diff --git a/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_arctic_silence.jpg b/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_arctic_silence.jpg new file mode 100644 index 00000000..11ced44e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_arctic_silence.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5507dcbeb97f469087eb4b66f4e9de9dff988628cab77c9f74922ea39d2d14fd +size 468065 diff --git a/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_deep_forest.jpg b/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_deep_forest.jpg new file mode 100644 index 00000000..944c463a --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_deep_forest.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2d5c7b9f823005cd5245ede7f3587cc00d6cf1ccb113f5aece0aebf4b017711 +size 537150 diff --git a/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_desert_sands.jpg b/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_desert_sands.jpg new file mode 100644 index 00000000..eea7a888 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_desert_sands.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb636651ef45fc72bb98c324f556cc89589527d0f277e405b192f09c7b833c1b +size 331737 diff --git a/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_hero.jpg b/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_hero.jpg new file mode 100644 index 00000000..72e22e63 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Sanctuary/main_hero.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49646d05d73283e485c1cff68bab10090815e65b30798d51b376c57c8042dd76 +size 466219 diff --git a/samples/AvaloniaControlCatalog/Assets/Sanctuary/mountain_bg.jpg b/samples/AvaloniaControlCatalog/Assets/Sanctuary/mountain_bg.jpg new file mode 100644 index 00000000..d61797ad --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/Sanctuary/mountain_bg.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:605e9595791622ee51d12f84411c8d6ccae372d134b6fe8d9d3276bbde5c0ae0 +size 416532 diff --git a/samples/AvaloniaControlCatalog/Assets/avalonia-32.png b/samples/AvaloniaControlCatalog/Assets/avalonia-32.png new file mode 100644 index 00000000..f896a9f2 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/avalonia-32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6d9f3f015b19fd537e192fdfc0e8069ea5dcd983ddd2cdf7213f3638bff82d0 +size 1322 diff --git a/samples/AvaloniaControlCatalog/Assets/delicate-arch-896885_640.jpg b/samples/AvaloniaControlCatalog/Assets/delicate-arch-896885_640.jpg new file mode 100644 index 00000000..e1f825cf --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/delicate-arch-896885_640.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f015d396bc046aab2849a62d5c30842be3423ddc4ff58d0b2b07d7f9898b0dc +size 95812 diff --git a/samples/AvaloniaControlCatalog/Assets/github_icon.png b/samples/AvaloniaControlCatalog/Assets/github_icon.png new file mode 100644 index 00000000..41e45765 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/github_icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3c7853d2d976abc126cb5f533d0080ea03436bd05c6f0bfcd57c1efdff69683 +size 17223 diff --git a/samples/AvaloniaControlCatalog/Assets/hirsch-899118_640.jpg b/samples/AvaloniaControlCatalog/Assets/hirsch-899118_640.jpg new file mode 100644 index 00000000..c3254eb4 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/hirsch-899118_640.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37545dd5cf7e85f58cc55dc0e271cabaf15cb4a8ccdadb352122095615b74c63 +size 216662 diff --git a/samples/AvaloniaControlCatalog/Assets/image1.jpg b/samples/AvaloniaControlCatalog/Assets/image1.jpg new file mode 100644 index 00000000..29ab7de1 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/image1.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89852956751255d12625b89737fbc97faa9b4cc3c8cf40f28983491ad0ef8e17 +size 71581 diff --git a/samples/AvaloniaControlCatalog/Assets/image2.jpg b/samples/AvaloniaControlCatalog/Assets/image2.jpg new file mode 100644 index 00000000..75c97f00 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/image2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e0f13d195f3daf0d313cb408b3895bf6fd95324a4fdc4523c718bbe15b4bfa50 +size 53850 diff --git a/samples/AvaloniaControlCatalog/Assets/image3.jpg b/samples/AvaloniaControlCatalog/Assets/image3.jpg new file mode 100644 index 00000000..6b70c394 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/image3.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64f5c61afcaca7e4a5d2b32967acce91f8a6976c2eeb1848efb5bd6556412b51 +size 54199 diff --git a/samples/AvaloniaControlCatalog/Assets/image4.jpg b/samples/AvaloniaControlCatalog/Assets/image4.jpg new file mode 100644 index 00000000..d7bd96d1 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/image4.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2c4c6245efa1bcade1d6819be4fc932fd5a78076e009faa54008d8f91a5071a +size 97298 diff --git a/samples/AvaloniaControlCatalog/Assets/image5.jpg b/samples/AvaloniaControlCatalog/Assets/image5.jpg new file mode 100644 index 00000000..3bfd6f62 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/image5.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcaecb19b5907e37e3728f935390f8744c739df7d0233d7d5688db2d33b5809c +size 69100 diff --git a/samples/AvaloniaControlCatalog/Assets/image6.jpg b/samples/AvaloniaControlCatalog/Assets/image6.jpg new file mode 100644 index 00000000..3d7239a9 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/image6.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c81db4378d3cf4d5f871c59824763014cc8dc88e9adedbd734ae35cecc8e03da +size 109665 diff --git a/samples/AvaloniaControlCatalog/Assets/image7.jpg b/samples/AvaloniaControlCatalog/Assets/image7.jpg new file mode 100644 index 00000000..818793ee --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/image7.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:137a9d41c4e59c420711cfd49090621cf3c0f300e7c3429046a5c783083a7ebc +size 61519 diff --git a/samples/AvaloniaControlCatalog/Assets/maple-leaf-888807_640.jpg b/samples/AvaloniaControlCatalog/Assets/maple-leaf-888807_640.jpg new file mode 100644 index 00000000..b892ddfc --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/maple-leaf-888807_640.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b9f04697c2afc982323df5fcc3804e9a832da24bc213057f7258b0f8915c2f9 +size 113622 diff --git a/samples/AvaloniaControlCatalog/Assets/test_icon.ico b/samples/AvaloniaControlCatalog/Assets/test_icon.ico new file mode 100644 index 00000000..b023aa24 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Assets/test_icon.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df9262e2176d830bfbef5b5e1dfa12f91c89e8ecb764eee366c3352df371d665 +size 176111 diff --git a/samples/AvaloniaControlCatalog/AvaloniaControlCatalog.csproj b/samples/AvaloniaControlCatalog/AvaloniaControlCatalog.csproj new file mode 100644 index 00000000..62e50786 --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaControlCatalog.csproj @@ -0,0 +1,99 @@ + + + net10.0-windows + WinExe + true + + default + enable + ControlCatalog + AvaloniaControlCatalog + true + true + false + $(NoWarn);AVA3001 + ..\..\..\..\JimBobSquarePants\Avalonia + $(MSBuildProjectDirectory)\$(LocalAvaloniaRoot)\build\avalonia.snk + $(MSBuildProjectDirectory)\NuGet.Config + $(MSBuildProjectDirectory)\$(LocalAvaloniaRoot)\src\Avalonia.Build.Tasks\bin\Release\netstandard2.0\Avalonia.Build.Tasks.dll + true + false + true + true + app.manifest + + + + + + + + + + %(Filename) + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MSBuild:Compile + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/AnonymousObserverNonGenericHelper.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/AnonymousObserverNonGenericHelper.cs new file mode 100644 index 00000000..134c67b2 --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/AnonymousObserverNonGenericHelper.cs @@ -0,0 +1,13 @@ +using System; + +namespace Avalonia.Reactive; + +/// +/// Contains fields for that aren't using generic arguments. +/// Separated to avoid unnecessary generic instantiations. +/// +internal static class AnonymousObserverNonGenericHelper +{ + public static readonly Action ThrowsOnError = ex => throw ex; + public static readonly Action NoOpCompleted = () => { }; +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyBindingObservable.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyBindingObservable.cs new file mode 100644 index 00000000..fd68381d --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyBindingObservable.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using Avalonia.Data; + +namespace Avalonia.Reactive +{ + internal class AvaloniaPropertyBindingObservable : LightweightObservableBase>, IDescription + { + private readonly WeakReference _target; + private readonly AvaloniaProperty _property; + private readonly Func? _converter; + private BindingValue _value = BindingValue.Unset; + + public AvaloniaPropertyBindingObservable( + AvaloniaObject target, + AvaloniaProperty property, + Func? converter = null) + { + _target = new WeakReference(target); + _property = property; + _converter = converter; + } + + public string Description => $"{_target.GetType().Name}.{_property.Name}"; + + protected override void Initialize() + { + if (_target.TryGetTarget(out var target)) + { + if (_converter is { } converter) + { + var unconvertedValue = (TSource)target.GetValue(_property)!; + _value = converter(unconvertedValue); + target.PropertyChanged += PropertyChanged_WithConversion; + } + else + { + _value = (TResult)target.GetValue(_property)!; + target.PropertyChanged += PropertyChanged; + } + } + } + + protected override void Deinitialize() + { + if (_target.TryGetTarget(out var target)) + { + if (_converter is not null) + { + target.PropertyChanged -= PropertyChanged_WithConversion; + } + else + { + target.PropertyChanged -= PropertyChanged; + } + } + } + + protected override void Subscribed(IObserver> observer, bool first) + { + if (_value.Type != BindingValueType.UnsetValue) + { + observer.OnNext(_value); + } + } + + private void PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if (e.Property == _property) + { + if (e is AvaloniaPropertyChangedEventArgs typedArgs) + { + PublishValue(e.Sender.GetValue(typedArgs.Property)); + } + else + { + PublishUntypedValue(e.Sender.GetValue(e.Property)); + } + } + } + + private void PropertyChanged_WithConversion(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if (e.Property == _property) + { + if (e is AvaloniaPropertyChangedEventArgs typedArgs) + { + var newValueRaw = e.Sender.GetValue(typedArgs.Property); + + var newValue = _converter!(newValueRaw); + + PublishValue(newValue); + } + else + { + var newValue = e.Sender.GetValue(e.Property); + + if (newValue is TSource source) + { + newValue = _converter!(source); + } + + PublishUntypedValue(newValue); + } + } + } + + private void PublishValue(TResult newValue) + { + if (!_value.HasValue || !EqualityComparer.Default.Equals(newValue, _value.Value)) + { + _value = newValue; + PublishNext(_value); + } + } + + private void PublishUntypedValue(object? newValue) + { + if (!Equals(newValue, _value)) + { + _value = (TResult)newValue!; + PublishNext(_value); + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyChangedObservable.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyChangedObservable.cs new file mode 100644 index 00000000..fa876a89 --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyChangedObservable.cs @@ -0,0 +1,46 @@ +using System; + +namespace Avalonia.Reactive +{ + internal class AvaloniaPropertyChangedObservable : + LightweightObservableBase, + IDescription + { + private readonly WeakReference _target; + private readonly AvaloniaProperty _property; + + public AvaloniaPropertyChangedObservable( + AvaloniaObject target, + AvaloniaProperty property) + { + _target = new WeakReference(target); + _property = property; + } + + public string Description => $"{_target.GetType().Name}.{_property.Name}"; + + protected override void Initialize() + { + if (_target.TryGetTarget(out var target)) + { + target.PropertyChanged += PropertyChanged; + } + } + + protected override void Deinitialize() + { + if (_target.TryGetTarget(out var target)) + { + target.PropertyChanged -= PropertyChanged; + } + } + + private void PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if (e.Property == _property) + { + PublishNext(e); + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyObservable.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyObservable.cs new file mode 100644 index 00000000..0d40fa96 --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/AvaloniaPropertyObservable.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using Avalonia.Data; + +namespace Avalonia.Reactive +{ + internal class AvaloniaPropertyObservable : LightweightObservableBase, IDescription + { + private readonly WeakReference _target; + private readonly AvaloniaProperty _property; + private readonly Func? _converter; + private Optional _value; + + public AvaloniaPropertyObservable( + AvaloniaObject target, + AvaloniaProperty property, + Func? converter = null) + { + _target = new WeakReference(target); + _property = property; + _converter = converter; + } + + public string Description => $"{_target.GetType().Name}.{_property.Name}"; + + protected override void Initialize() + { + if (_target.TryGetTarget(out var target)) + { + if (_converter is { } converter) + { + var unconvertedValue = (TSource)target.GetValue(_property)!; + _value = converter(unconvertedValue); + target.PropertyChanged += PropertyChanged_WithConversion; + } + else + { + _value = (TResult)target.GetValue(_property)!; + target.PropertyChanged += PropertyChanged; + } + } + } + + protected override void Deinitialize() + { + if (_target.TryGetTarget(out var target)) + { + if (_converter is not null) + { + target.PropertyChanged -= PropertyChanged_WithConversion; + } + else + { + target.PropertyChanged -= PropertyChanged; + } + } + + _value = default; + } + + protected override void Subscribed(IObserver observer, bool first) + { + if (_value.HasValue) + observer.OnNext(_value.Value); + } + + private void PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if (e.Property == _property) + { + TResult newValue; + + if (e is AvaloniaPropertyChangedEventArgs typed) + { + newValue = AvaloniaObjectExtensions.GetValue(e.Sender, typed.Property); + } + else + { + newValue = (TResult)e.Sender.GetValue(e.Property)!; + } + + PublishNewValue(newValue); + } + } + + private void PropertyChanged_WithConversion(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if (e.Property == _property) + { + TSource newValueRaw; + + if (e is AvaloniaPropertyChangedEventArgs typed) + { + newValueRaw = AvaloniaObjectExtensions.GetValue(e.Sender, typed.Property); + } + else + { + newValueRaw = (TSource)e.Sender.GetValue(e.Property)!; + } + + var newValue = _converter!(newValueRaw); + + PublishNewValue(newValue); + } + } + + private void PublishNewValue(TResult newValue) + { + if (!_value.HasValue || + !EqualityComparer.Default.Equals(newValue, _value.Value)) + { + _value = newValue; + PublishNext(_value.Value!); + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/CombinedSubject.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/CombinedSubject.cs new file mode 100644 index 00000000..dc615313 --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/CombinedSubject.cs @@ -0,0 +1,23 @@ +using System; + +namespace Avalonia.Reactive; + +internal class CombinedSubject : IAvaloniaSubject +{ + private readonly IObserver _observer; + private readonly IObservable _observable; + + public CombinedSubject(IObserver observer, IObservable observable) + { + _observer = observer; + _observable = observable; + } + + public void OnCompleted() => _observer.OnCompleted(); + + public void OnError(Exception error) => _observer.OnError(error); + + public void OnNext(T value) => _observer.OnNext(value); + + public IDisposable Subscribe(IObserver observer) => _observable.Subscribe(observer); +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/CompositeDisposable.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/CompositeDisposable.cs new file mode 100644 index 00000000..4952686a --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/CompositeDisposable.cs @@ -0,0 +1,427 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace Avalonia.Reactive; + +internal sealed class CompositeDisposable : ICollection, IDisposable +{ + private readonly object _gate = new object(); + private bool _disposed; + private List _disposables; + private int _count; + private const int ShrinkThreshold = 64; + + /// + /// Initializes a new instance of the class with the specified number of disposables. + /// + /// The number of disposables that the new CompositeDisposable can initially store. + /// is less than zero. + public CompositeDisposable(int capacity) + { + if (capacity < 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity)); + } + + _disposables = new List(capacity); + } + + /// + /// Initializes a new instance of the class from a group of disposables. + /// + /// Disposables that will be disposed together. + /// is null. + /// Any of the disposables in the collection is null. + public CompositeDisposable(params IDisposable[] disposables) + { + if (disposables == null) + { + throw new ArgumentNullException(nameof(disposables)); + } + + _disposables = ToList(disposables); + + // _count can be read by other threads and thus should be properly visible + // also releases the _disposables contents so it becomes thread-safe + Volatile.Write(ref _count, _disposables.Count); + } + + /// + /// Initializes a new instance of the class from a group of disposables. + /// + /// Disposables that will be disposed together. + /// is null. + /// Any of the disposables in the collection is null. + public CompositeDisposable(IList disposables) + { + if (disposables == null) + { + throw new ArgumentNullException(nameof(disposables)); + } + + _disposables = ToList(disposables); + + // _count can be read by other threads and thus should be properly visible + // also releases the _disposables contents so it becomes thread-safe + Volatile.Write(ref _count, _disposables.Count); + } + + private static List ToList(IEnumerable disposables) + { + var capacity = disposables switch + { + IDisposable[] a => a.Length, + ICollection c => c.Count, + _ => 12 + }; + + var list = new List(capacity); + + // do the copy and null-check in one step to avoid a + // second loop for just checking for null items + foreach (var d in disposables) + { + if (d == null) + { + throw new ArgumentException("Disposables can't contain null", nameof(disposables)); + } + + list.Add(d); + } + + return list; + } + + /// + /// Gets the number of disposables contained in the . + /// + public int Count => Volatile.Read(ref _count); + + /// + /// Adds a disposable to the or disposes the disposable if the is disposed. + /// + /// Disposable to add. + /// is null. + public void Add(IDisposable item) + { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + lock (_gate) + { + if (!_disposed) + { + _disposables.Add(item); + + // If read atomically outside the lock, it should be written atomically inside + // the plain read on _count is fine here because manipulation always happens + // from inside a lock. + Volatile.Write(ref _count, _count + 1); + return; + } + } + + item.Dispose(); + } + + /// + /// Removes and disposes the first occurrence of a disposable from the . + /// + /// Disposable to remove. + /// true if found; false otherwise. + /// is null. + public bool Remove(IDisposable item) + { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + lock (_gate) + { + // this composite was already disposed and if the item was in there + // it has been already removed/disposed + if (_disposed) + { + return false; + } + + // + // List doesn't shrink the size of the underlying array but does collapse the array + // by copying the tail one position to the left of the removal index. We don't need + // index-based lookup but only ordering for sequential disposal. So, instead of spending + // cycles on the Array.Copy imposed by Remove, we use a null sentinel value. We also + // do manual Swiss cheese detection to shrink the list if there's a lot of holes in it. + // + + // read fields as infrequently as possible + var current = _disposables; + + var i = current.IndexOf(item); + if (i < 0) + { + // not found, just return + return false; + } + + current[i] = null; + + if (current.Capacity > ShrinkThreshold && _count < current.Capacity / 2) + { + var fresh = new List(current.Capacity / 2); + + foreach (var d in current) + { + if (d != null) + { + fresh.Add(d); + } + } + + _disposables = fresh; + } + + // make sure the Count property sees an atomic update + Volatile.Write(ref _count, _count - 1); + } + + // if we get here, the item was found and removed from the list + // just dispose it and report success + + item.Dispose(); + + return true; + } + + /// + /// Disposes all disposables in the group and removes them from the group. + /// + public void Dispose() + { + List? currentDisposables = null; + + lock (_gate) + { + if (!_disposed) + { + currentDisposables = _disposables; + + // nulling out the reference is faster no risk to + // future Add/Remove because _disposed will be true + // and thus _disposables won't be touched again. + _disposables = null!; // NB: All accesses are guarded by _disposed checks. + + Volatile.Write(ref _count, 0); + Volatile.Write(ref _disposed, true); + } + } + + if (currentDisposables != null) + { + foreach (var d in currentDisposables) + { + d?.Dispose(); + } + } + } + + /// + /// Removes and disposes all disposables from the , but does not dispose the . + /// + public void Clear() + { + IDisposable?[] previousDisposables; + + lock (_gate) + { + // disposed composites are always clear + if (_disposed) + { + return; + } + + var current = _disposables; + + previousDisposables = current.ToArray(); + current.Clear(); + + Volatile.Write(ref _count, 0); + } + + foreach (var d in previousDisposables) + { + d?.Dispose(); + } + } + + /// + /// Determines whether the contains a specific disposable. + /// + /// Disposable to search for. + /// true if the disposable was found; otherwise, false. + /// is null. + public bool Contains(IDisposable item) + { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + lock (_gate) + { + if (_disposed) + { + return false; + } + + return _disposables.Contains(item); + } + } + + /// + /// Copies the disposables contained in the to an array, starting at a particular array index. + /// + /// Array to copy the contained disposables to. + /// Target index at which to copy the first disposable of the group. + /// is null. + /// is less than zero. -or - is larger than or equal to the array length. + public void CopyTo(IDisposable[] array, int arrayIndex) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (arrayIndex < 0 || arrayIndex >= array.Length) + { + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + } + + lock (_gate) + { + // disposed composites are always empty + if (_disposed) + { + return; + } + + if (arrayIndex + _count > array.Length) + { + // there is not enough space beyond arrayIndex + // to accommodate all _count disposables in this composite + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + } + + var i = arrayIndex; + + foreach (var d in _disposables) + { + if (d != null) + { + array[i++] = d; + } + } + } + } + + /// + /// Always returns false. + /// + public bool IsReadOnly => false; + + /// + /// Returns an enumerator that iterates through the . + /// + /// An enumerator to iterate over the disposables. + public IEnumerator GetEnumerator() + { + lock (_gate) + { + if (_disposed || _count == 0) + { + return EmptyEnumerator; + } + + // the copy is unavoidable but the creation + // of an outer IEnumerable is avoidable + return new CompositeEnumerator(_disposables.ToArray()); + } + } + + /// + /// Returns an enumerator that iterates through the . + /// + /// An enumerator to iterate over the disposables. + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + /// Gets a value that indicates whether the object is disposed. + /// + public bool IsDisposed => Volatile.Read(ref _disposed); + + /// + /// An empty enumerator for the + /// method to avoid allocation on disposed or empty composites. + /// + private static readonly CompositeEnumerator EmptyEnumerator = + new CompositeEnumerator(Array.Empty()); + + /// + /// An enumerator for an array of disposables. + /// + private sealed class CompositeEnumerator : IEnumerator + { + private readonly IDisposable?[] _disposables; + private int _index; + + public CompositeEnumerator(IDisposable?[] disposables) + { + _disposables = disposables; + _index = -1; + } + + public IDisposable Current => _disposables[_index]!; // NB: _index is only advanced to non-null positions. + + object IEnumerator.Current => _disposables[_index]!; + + public void Dispose() + { + // Avoid retention of the referenced disposables + // beyond the lifecycle of the enumerator. + // Not sure if this happens by default to + // generic array enumerators though. + var disposables = _disposables; + Array.Clear(disposables, 0, disposables.Length); + } + + public bool MoveNext() + { + var disposables = _disposables; + + for (;;) + { + var idx = ++_index; + + if (idx >= disposables.Length) + { + return false; + } + + // inlined that filter for null elements + if (disposables[idx] != null) + { + return true; + } + } + } + + public void Reset() + { + _index = -1; + } + } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/Disposable.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/Disposable.cs new file mode 100644 index 00000000..17b0d01d --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/Disposable.cs @@ -0,0 +1,98 @@ +using System; +using System.Threading; + +namespace Avalonia.Reactive; + +/// +/// Provides a set of static methods for creating objects. +/// +internal static class Disposable +{ + /// + /// Represents a disposable that does nothing on disposal. + /// + private sealed class EmptyDisposable : IDisposable + { + public static readonly EmptyDisposable Instance = new(); + + private EmptyDisposable() + { + } + + public void Dispose() + { + // no op + } + } + + internal sealed class AnonymousDisposable : IDisposable + { + private volatile Action? _dispose; + public AnonymousDisposable(Action dispose) + { + _dispose = dispose; + } + public bool IsDisposed => _dispose == null; + public void Dispose() + { + Interlocked.Exchange(ref _dispose, null)?.Invoke(); + } + } + + internal sealed class AnonymousDisposable : IDisposable + { + private TState _state; + private volatile Action? _dispose; + + public AnonymousDisposable(TState state, Action dispose) + { + _state = state; + _dispose = dispose; + } + + public bool IsDisposed => _dispose == null; + public void Dispose() + { + Interlocked.Exchange(ref _dispose, null)?.Invoke(_state); + _state = default!; + } + } + + /// + /// Gets the disposable that does nothing when disposed. + /// + public static IDisposable Empty => EmptyDisposable.Instance; + + /// + /// Creates a disposable object that invokes the specified action when disposed. + /// + /// Action to run during the first call to . The action is guaranteed to be run at most once. + /// The disposable object that runs the given action upon disposal. + /// is null. + public static IDisposable Create(Action dispose) + { + if (dispose == null) + { + throw new ArgumentNullException(nameof(dispose)); + } + + return new AnonymousDisposable(dispose); + } + + /// + /// Creates a disposable object that invokes the specified action when disposed. + /// + /// The state to be passed to the action. + /// Action to run during the first call to . The action is guaranteed to be run at most once. + /// The disposable object that runs the given action upon disposal. + /// is null. + public static IDisposable Create(TState state, Action dispose) + { + if (dispose == null) + { + throw new ArgumentNullException(nameof(dispose)); + } + + return new AnonymousDisposable(state, dispose); + } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/DisposableMixin.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/DisposableMixin.cs new file mode 100644 index 00000000..478312fa --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/DisposableMixin.cs @@ -0,0 +1,37 @@ +using System; +using Avalonia.Reactive; + +namespace Avalonia.Reactive; + +/// +/// Extension methods associated with the IDisposable interface. +/// +internal static class DisposableMixin +{ + /// + /// Ensures the provided disposable is disposed with the specified . + /// + /// + /// The type of the disposable. + /// + /// + /// The disposable we are going to want to be disposed by the CompositeDisposable. + /// + /// + /// The to which will be added. + /// + /// + /// The disposable. + /// + public static T DisposeWith(this T item, CompositeDisposable compositeDisposable) + where T : IDisposable + { + if (compositeDisposable is null) + { + throw new ArgumentNullException(nameof(compositeDisposable)); + } + + compositeDisposable.Add(item); + return item; + } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/IAvaloniaSubject.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/IAvaloniaSubject.cs new file mode 100644 index 00000000..272e2190 --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/IAvaloniaSubject.cs @@ -0,0 +1,8 @@ +using System; + +namespace Avalonia.Reactive; + +internal interface IAvaloniaSubject : IObserver, IObservable /*, ISubject */ +{ + +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/LightweightObservableBase.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/LightweightObservableBase.cs new file mode 100644 index 00000000..0688a4ed --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/LightweightObservableBase.cs @@ -0,0 +1,237 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Threading; + +namespace Avalonia.Reactive +{ + /// + /// Lightweight base class for observable implementations. + /// + /// The observable type. + /// + /// ObservableBase{T} is rather heavyweight in terms of allocations and memory + /// usage. This class provides a more lightweight base for some internal observable types + /// in the Avalonia framework. + /// + internal abstract class LightweightObservableBase : IObservable + { + private Exception? _error; + private List>? _observers = new List>(); + + public bool HasObservers => _observers?.Count > 0; + + public IDisposable Subscribe(IObserver observer) + { + _ = observer ?? throw new ArgumentNullException(nameof(observer)); + + //Dispatcher.UIThread.VerifyAccess(); + + var first = false; + + for (; ; ) + { + if (Volatile.Read(ref _observers) == null) + { + if (_error != null) + { + observer.OnError(_error); + } + else + { + observer.OnCompleted(); + } + + return Disposable.Empty; + } + + lock (this) + { + if (_observers == null) + { + continue; + } + + first = _observers.Count == 0; + _observers.Add(observer); + break; + } + } + + if (first) + { + Initialize(); + } + + Subscribed(observer, first); + + return new RemoveObserver(this, observer); + } + + void Remove(IObserver observer) + { + if (Volatile.Read(ref _observers) != null) + { + lock (this) + { + var observers = _observers; + + if (observers != null) + { + observers.Remove(observer); + + if (observers.Count == 0) + { + observers.TrimExcess(); + Deinitialize(); + } + } + } + } + } + + sealed class RemoveObserver : IDisposable + { + LightweightObservableBase? _parent; + + IObserver? _observer; + + public RemoveObserver(LightweightObservableBase parent, IObserver observer) + { + _parent = parent; + Volatile.Write(ref _observer, observer); + } + + public void Dispose() + { + var observer = _observer; + Interlocked.Exchange(ref _parent, null)?.Remove(observer!); + _observer = null; + } + } + + protected abstract void Initialize(); + protected abstract void Deinitialize(); + + protected void PublishNext(T value) + { + if (Volatile.Read(ref _observers) != null) + { + IObserver[]? observers = null; + int count = 0; + + // Optimize for the common case of 1/2/3 observers. + IObserver? observer0 = null; + IObserver? observer1 = null; + IObserver? observer2 = null; + lock (this) + { + if (_observers == null) + { + return; + } + + count = _observers.Count; + switch (count) + { + case 3: + observer0 = _observers[0]; + observer1 = _observers[1]; + observer2 = _observers[2]; + break; + case 2: + observer0 = _observers[0]; + observer1 = _observers[1]; + break; + case 1: + observer0 = _observers[0]; + break; + case 0: + return; + default: + { + observers = ArrayPool>.Shared.Rent(count); + _observers.CopyTo(observers); + break; + } + } + } + + if (observer0 != null) + { + observer0.OnNext(value); + observer1?.OnNext(value); + observer2?.OnNext(value); + } + else if (observers != null) + { + for(int i = 0; i < count; i++) + { + observers[i].OnNext(value); + // Avoid memory leak by clearing the reference. + observers[i] = null!; + } + + ArrayPool>.Shared.Return(observers); + } + } + } + + protected void PublishCompleted() + { + if (Volatile.Read(ref _observers) != null) + { + IObserver[] observers; + + lock (this) + { + if (_observers == null) + { + return; + } + observers = _observers.ToArray(); + Volatile.Write(ref _observers, null); + } + + foreach (var observer in observers) + { + observer.OnCompleted(); + } + + Deinitialize(); + } + } + + protected void PublishError(Exception error) + { + if (Volatile.Read(ref _observers) != null) + { + + IObserver[] observers; + + lock (this) + { + if (_observers == null) + { + return; + } + + _error = error; + observers = _observers.ToArray(); + Volatile.Write(ref _observers, null); + } + + foreach (var observer in observers) + { + observer.OnError(error); + } + + Deinitialize(); + } + } + + protected virtual void Subscribed(IObserver observer, bool first) + { + } + } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/LightweightSubject.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/LightweightSubject.cs new file mode 100644 index 00000000..5663fd66 --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/LightweightSubject.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using Avalonia.Threading; + +namespace Avalonia.Reactive; + +internal class LightweightSubject : LightweightObservableBase, IAvaloniaSubject +{ + public void OnCompleted() + { + PublishCompleted(); + } + + public void OnError(Exception error) + { + PublishError(error); + } + + public void OnNext(T value) + { + PublishNext(value); + } + + protected override void Initialize() { } + + protected override void Deinitialize() { } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/Observable.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/Observable.cs new file mode 100644 index 00000000..4cffdaa6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/Observable.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Avalonia.Reactive.Operators; +using Avalonia.Threading; + +namespace Avalonia.Reactive; + +/// +/// Provides common observable methods as a replacement for the Rx framework. +/// +internal static class Observable +{ + public static IObservable Create(Func, IDisposable> subscribe) + { + return new CreateWithDisposableObservable(subscribe); + } + + public static IDisposable Subscribe(this IObservable source, Action action) + { + return source.Subscribe(new AnonymousObserver(action)); + } + + public static IObservable Select(this IObservable source, Func selector) + { + return Create(obs => + { + return source.Subscribe(new AnonymousObserver( + input => + { + TResult value; + try + { + value = selector(input); + } + catch (Exception ex) + { + obs.OnError(ex); + return; + } + + obs.OnNext(value); + }, obs.OnError, obs.OnCompleted)); + }); + } + + public static IObservable StartWith(this IObservable source, TSource value) + { + return Create(obs => + { + obs.OnNext(value); + return source.Subscribe(obs); + }); + } + + public static IObservable Where(this IObservable source, Func predicate) + { + return Create(obs => + { + return source.Subscribe(new AnonymousObserver( + input => + { + bool shouldRun; + try + { + shouldRun = predicate(input); + } + catch (Exception ex) + { + obs.OnError(ex); + return; + } + if (shouldRun) + { + obs.OnNext(input); + } + }, obs.OnError, obs.OnCompleted)); + }); + } + + public static IObservable Switch( + this IObservable> sources) + { + return new Switch(sources); + } + + public static IObservable CombineLatest( + this IObservable first, IObservable second, + Func resultSelector) + { + return new CombineLatest(first, second, resultSelector); + } + + public static IObservable CombineLatest( + this IEnumerable> inputs) + { + return new CombineLatest(inputs, items => items); + } + + public static IObservable Skip(this IObservable source, int skipCount) + { + if (skipCount <= 0) + { + throw new ArgumentException("Skip count must be bigger than zero", nameof(skipCount)); + } + + return Create(obs => + { + var remaining = skipCount; + return source.Subscribe(new AnonymousObserver( + input => + { + if (remaining <= 0) + { + obs.OnNext(input); + } + else + { + remaining--; + } + }, obs.OnError, obs.OnCompleted)); + }); + } + + public static IObservable Take(this IObservable source, int takeCount) + { + if (takeCount <= 0) + { + return Empty(); + } + + return Create(obs => + { + var remaining = takeCount; + IDisposable? sub = null; + sub = source.Subscribe(new AnonymousObserver( + input => + { + if (remaining > 0) + { + --remaining; + obs.OnNext(input); + + if (remaining == 0) + { + sub?.Dispose(); + obs.OnCompleted(); + } + } + }, obs.OnError, obs.OnCompleted)); + return sub; + }); + } + + public static IObservable FromEventPattern(Action addHandler, Action removeHandler) + { + return Create(observer => + { + var handler = new Action(observer.OnNext); + var converted = new EventHandler((_, args) => handler(args)); + addHandler(converted); + + return Disposable.Create(() => removeHandler(converted)); + }); + } + + public static IObservable FromEventPattern(Action> addHandler, Action> removeHandler) where T : EventArgs + { + return Create(observer => + { + var handler = new Action(observer.OnNext); + var converted = new EventHandler((_, args) => handler(args)); + addHandler(converted); + + return Disposable.Create(() => removeHandler(converted)); + }); + } + + public static IObservable Return(T value) + { + return new ReturnImpl(value); + } + + public static IObservable Empty() + { + return EmptyImpl.Instance; + } + + /// + /// Returns an observable that fires once with the specified value and never completes. + /// + /// The type of the value. + /// The value. + /// The observable. + public static IObservable SingleValue(T value) + { + return new SingleValueImpl(value); + } + + private sealed class SingleValueImpl : IObservable + { + private readonly T _value; + + public SingleValueImpl(T value) + { + _value = value; + } + public IDisposable Subscribe(IObserver observer) + { + observer.OnNext(_value); + return Disposable.Empty; + } + } + + private sealed class ReturnImpl : IObservable + { + private readonly T _value; + + public ReturnImpl(T value) + { + _value = value; + } + public IDisposable Subscribe(IObserver observer) + { + observer.OnNext(_value); + observer.OnCompleted(); + return Disposable.Empty; + } + } + + internal sealed class EmptyImpl : IObservable + { + internal static readonly IObservable Instance = new EmptyImpl(); + + private EmptyImpl() { } + + public IDisposable Subscribe(IObserver observer) + { + observer.OnCompleted(); + return Disposable.Empty; + } + } + + private sealed class CreateWithDisposableObservable : IObservable + { + private readonly Func, IDisposable> _subscribe; + + public CreateWithDisposableObservable(Func, IDisposable> subscribe) + { + _subscribe = subscribe; + } + + public IDisposable Subscribe(IObserver observer) + { + return _subscribe(observer); + } + } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/CombineLatest.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/CombineLatest.cs new file mode 100644 index 00000000..aa95ceec --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/CombineLatest.cs @@ -0,0 +1,374 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace Avalonia.Reactive.Operators; + +// Code based on https://github.com/dotnet/reactive/blob/main/Rx.NET/Source/src/System.Reactive/Linq/Observable/CombineLatest.cs + +internal sealed class CombineLatest : IObservable +{ + private readonly IObservable _first; + private readonly IObservable _second; + private readonly Func _resultSelector; + + public CombineLatest(IObservable first, IObservable second, + Func resultSelector) + { + _first = first; + _second = second; + _resultSelector = resultSelector; + } + + public IDisposable Subscribe(IObserver observer) + { + var sink = new _(_resultSelector, observer); + sink.Run(_first, _second); + return sink; + } + + internal sealed class _ : IdentitySink + { + private readonly Func _resultSelector; + private readonly object _gate = new object(); + + public _(Func resultSelector, IObserver observer) + : base(observer) + { + _resultSelector = resultSelector; + _firstDisposable = null!; + _secondDisposable = null!; + } + + private IDisposable _firstDisposable; + private IDisposable _secondDisposable; + + public void Run(IObservable first, IObservable second) + { + var fstO = new FirstObserver(this); + var sndO = new SecondObserver(this); + + fstO.SetOther(sndO); + sndO.SetOther(fstO); + + _firstDisposable = first.Subscribe(fstO); + _secondDisposable = second.Subscribe(sndO); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _firstDisposable.Dispose(); + _secondDisposable.Dispose(); + } + + base.Dispose(disposing); + } + + private sealed class FirstObserver : IObserver + { + private readonly _ _parent; + private SecondObserver _other; + + public FirstObserver(_ parent) + { + _parent = parent; + _other = default!; // NB: Will be set by SetOther. + } + + public void SetOther(SecondObserver other) { _other = other; } + + public bool HasValue { get; private set; } + public TFirst? Value { get; private set; } + public bool Done { get; private set; } + + public void OnNext(TFirst value) + { + lock (_parent._gate) + { + HasValue = true; + Value = value; + + if (_other.HasValue) + { + TResult res; + try + { + res = _parent._resultSelector(value, _other.Value!); + } + catch (Exception ex) + { + _parent.ForwardOnError(ex); + return; + } + + _parent.ForwardOnNext(res); + } + else if (_other.Done) + { + _parent.ForwardOnCompleted(); + } + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent.ForwardOnError(error); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + Done = true; + + if (_other.Done) + { + _parent.ForwardOnCompleted(); + } + else + { + _parent._firstDisposable.Dispose(); + } + } + } + } + + private sealed class SecondObserver : IObserver + { + private readonly _ _parent; + private FirstObserver _other; + + public SecondObserver(_ parent) + { + _parent = parent; + _other = default!; // NB: Will be set by SetOther. + } + + public void SetOther(FirstObserver other) { _other = other; } + + public bool HasValue { get; private set; } + public TSecond? Value { get; private set; } + public bool Done { get; private set; } + + public void OnNext(TSecond value) + { + lock (_parent._gate) + { + HasValue = true; + Value = value; + + if (_other.HasValue) + { + TResult res; + try + { + res = _parent._resultSelector(_other.Value!, value); + } + catch (Exception ex) + { + _parent.ForwardOnError(ex); + return; + } + + _parent.ForwardOnNext(res); + } + else if (_other.Done) + { + _parent.ForwardOnCompleted(); + } + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + _parent.ForwardOnError(error); + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + Done = true; + + if (_other.Done) + { + _parent.ForwardOnCompleted(); + } + else + { + _parent._secondDisposable.Dispose(); + } + } + } + } + } +} + +internal sealed class CombineLatest : IObservable +{ + private readonly IEnumerable> _sources; + private readonly Func _resultSelector; + + public CombineLatest(IEnumerable> sources, Func resultSelector) + { + _sources = sources; + _resultSelector = resultSelector; + } + + public IDisposable Subscribe(IObserver observer) + { + var sink = new _(_resultSelector, observer); + sink.Run(_sources); + return sink; + } + + internal sealed class _ : IdentitySink + { + private readonly object _gate = new object(); + private readonly Func _resultSelector; + + public _(Func resultSelector, IObserver observer) + : base(observer) + { + _resultSelector = resultSelector; + + // NB: These will be set in Run before getting used. + _hasValue = null!; + _values = null!; + _isDone = null!; + _subscriptions = null!; + } + + private bool[] _hasValue; + private bool _hasValueAll; + private TSource[] _values; + private bool[] _isDone; + private IDisposable[] _subscriptions; + + public void Run(IEnumerable> sources) + { + var srcs = sources.ToArray(); + + var N = srcs.Length; + + _hasValue = new bool[N]; + _hasValueAll = false; + + _values = new TSource[N]; + + _isDone = new bool[N]; + + _subscriptions = new IDisposable[N]; + + for (var i = 0; i < N; i++) + { + var j = i; + + var o = new SourceObserver(this, j); + _subscriptions[j] = o; + + o.Disposable = srcs[j].Subscribe(o); + } + + SetUpstream(new CompositeDisposable(_subscriptions)); + } + + private void OnNext(int index, TSource value) + { + lock (_gate) + { + _values[index] = value; + + _hasValue[index] = true; + + if (_hasValueAll || (_hasValueAll = _hasValue.All(v => v))) + { + TResult res; + try + { + res = _resultSelector(_values); + } + catch (Exception ex) + { + ForwardOnError(ex); + return; + } + + ForwardOnNext(res); + } + else if (_isDone.Where((_, i) => i != index).All(d => d)) + { + ForwardOnCompleted(); + } + } + } + + private new void OnError(Exception error) + { + lock (_gate) + { + ForwardOnError(error); + } + } + + private void OnCompleted(int index) + { + lock (_gate) + { + _isDone[index] = true; + + if (_isDone.All(d => d)) + { + ForwardOnCompleted(); + } + else + { + _subscriptions[index].Dispose(); + } + } + } + + private sealed class SourceObserver : IObserver, IDisposable + { + private readonly _ _parent; + private readonly int _index; + + public SourceObserver(_ parent, int index) + { + _parent = parent; + _index = index; + } + + public IDisposable? Disposable { get; set; } + + public void OnNext(TSource value) + { + _parent.OnNext(_index, value); + } + + public void OnError(Exception error) + { + _parent.OnError(error); + } + + public void OnCompleted() + { + _parent.OnCompleted(_index); + } + + public void Dispose() + { + Disposable?.Dispose(); + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/Sink.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/Sink.cs new file mode 100644 index 00000000..7acb5833 --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/Sink.cs @@ -0,0 +1,111 @@ +using System; +using System.Threading; + +namespace Avalonia.Reactive.Operators; + +// Code based on https://github.com/dotnet/reactive/blob/main/Rx.NET/Source/src/System.Reactive/Internal/Sink.cs + +internal abstract class Sink : IDisposable +{ + private IDisposable? _upstream; + private readonly IObserver _observer; + + protected Sink(IObserver observer) + { + _observer = observer; + } + + public void Dispose() + { + Dispose(true); + } + + /// + /// Override this method to dispose additional resources. + /// The method is guaranteed to be called at most once. + /// + /// If true, the method was called from . + protected virtual void Dispose(bool disposing) + { + //Calling base.Dispose(true) is not a proper disposal, so we can omit the assignment here. + //Sink is internal so this can pretty much be enforced. + //_observer = NopObserver.Instance; + + _upstream?.Dispose(); + } + + public void ForwardOnNext(TTarget value) + { + _observer.OnNext(value); + } + + public void ForwardOnCompleted() + { + _observer.OnCompleted(); + Dispose(); + } + + public void ForwardOnError(Exception error) + { + _observer.OnError(error); + Dispose(); + } + + protected void SetUpstream(IDisposable upstream) + { + _upstream = upstream; + } + + protected void DisposeUpstream() + { + _upstream?.Dispose(); + } +} + +internal abstract class Sink : Sink, IObserver +{ + protected Sink(IObserver observer) : base(observer) + { + } + + public virtual void Run(IObservable source) + { + SetUpstream(source.Subscribe(this)); + } + + public abstract void OnNext(TSource value); + + public virtual void OnError(Exception error) => ForwardOnError(error); + + public virtual void OnCompleted() => ForwardOnCompleted(); + + public IObserver GetForwarder() => new _(this); + + private sealed class _ : IObserver + { + private readonly Sink _forward; + + public _(Sink forward) + { + _forward = forward; + } + + public void OnNext(TTarget value) => _forward.ForwardOnNext(value); + + public void OnError(Exception error) => _forward.ForwardOnError(error); + + public void OnCompleted() => _forward.ForwardOnCompleted(); + } +} + +internal abstract class IdentitySink : Sink +{ + protected IdentitySink(IObserver observer) : base(observer) + { + } + + public override void OnNext(T value) + { + ForwardOnNext(value); + } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/Switch.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/Switch.cs new file mode 100644 index 00000000..f0ca9e3b --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/Operators/Switch.cs @@ -0,0 +1,145 @@ +using System; + +namespace Avalonia.Reactive.Operators; + +// Code based on https://github.com/dotnet/reactive/blob/main/Rx.NET/Source/src/System.Reactive/Linq/Observable/Switch.cs + +internal sealed class Switch : IObservable +{ + private readonly IObservable> _sources; + + public Switch(IObservable> sources) + { + _sources = sources; + } + + public IDisposable Subscribe(IObserver observer) + { + return _sources.Subscribe(new _(observer)); + } + + internal sealed class _ : Sink, TSource> + { + private readonly object _gate = new object(); + + public _(IObserver observer) + : base(observer) + { + } + + private IDisposable? _innerSerialDisposable; + private bool _isStopped; + private ulong _latest; + private bool _hasLatest; + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _innerSerialDisposable?.Dispose(); + } + + base.Dispose(disposing); + } + + public override void OnNext(IObservable value) + { + ulong id; + + lock (_gate) + { + id = unchecked(++_latest); + _hasLatest = true; + } + + var innerObserver = new InnerObserver(this, id); + + _innerSerialDisposable?.Dispose(); + _innerSerialDisposable = innerObserver; + innerObserver.Disposable = value.Subscribe(innerObserver); + } + + public override void OnError(Exception error) + { + lock (_gate) + { + ForwardOnError(error); + } + } + + public override void OnCompleted() + { + lock (_gate) + { + DisposeUpstream(); + + _isStopped = true; + if (!_hasLatest) + { + ForwardOnCompleted(); + } + } + } + + private sealed class InnerObserver : IObserver, IDisposable + { + private readonly _ _parent; + private readonly ulong _id; + + public InnerObserver(_ parent, ulong id) + { + _parent = parent; + _id = id; + } + + public IDisposable? Disposable { get; set; } + + public void OnNext(TSource value) + { + lock (_parent._gate) + { + if (_parent._latest == _id) + { + _parent.ForwardOnNext(value); + } + } + } + + public void OnError(Exception error) + { + lock (_parent._gate) + { + Dispose(); + + if (_parent._latest == _id) + { + _parent.ForwardOnError(error); + } + } + } + + public void OnCompleted() + { + lock (_parent._gate) + { + Dispose(); + + if (_parent._latest == _id) + { + _parent._hasLatest = false; + + if (_parent._isStopped) + { + _parent.ForwardOnCompleted(); + } + } + } + } + + public void Dispose() + { + Disposable?.Dispose(); + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/AvaloniaReactive/SingleSubscriberObservableBase.cs b/samples/AvaloniaControlCatalog/AvaloniaReactive/SingleSubscriberObservableBase.cs new file mode 100644 index 00000000..10d42214 --- /dev/null +++ b/samples/AvaloniaControlCatalog/AvaloniaReactive/SingleSubscriberObservableBase.cs @@ -0,0 +1,78 @@ +using System; +using Avalonia.Threading; + +namespace Avalonia.Reactive +{ + internal abstract class SingleSubscriberObservableBase : IObservable, IDisposable + { + private Exception? _error; + private IObserver? _observer; + private bool _completed; + + public IDisposable Subscribe(IObserver observer) + { + _ = observer ?? throw new ArgumentNullException(nameof(observer)); + Dispatcher.UIThread.VerifyAccess(); + + if (_observer != null) + { + throw new InvalidOperationException("The observable can only be subscribed once."); + } + + if (_error != null) + { + observer.OnError(_error); + } + else if (_completed) + { + observer.OnCompleted(); + } + else + { + _observer = observer; + Subscribed(); + } + + return this; + } + + public virtual void Dispose() + { + Unsubscribed(); + _observer = null; + } + + protected abstract void Unsubscribed(); + + protected void PublishNext(T value) + { + _observer?.OnNext(value); + } + + protected void PublishCompleted() + { + _completed = true; + + if (_observer != null) + { + _observer.OnCompleted(); + Unsubscribed(); + _observer = null; + } + } + + protected void PublishError(Exception error) + { + _error = error; + + if (_observer != null) + { + _observer.OnError(error); + Unsubscribed(); + _observer = null; + } + } + + protected abstract void Subscribed(); + } +} diff --git a/samples/AvaloniaControlCatalog/CombinedGeometryImpl.cs b/samples/AvaloniaControlCatalog/CombinedGeometryImpl.cs new file mode 100644 index 00000000..1148cc19 --- /dev/null +++ b/samples/AvaloniaControlCatalog/CombinedGeometryImpl.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Avalonia.Media; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Processing; + +namespace AvaloniaControlCatalog; + +/// +/// Avalonia combined geometry implementation backed by ImageSharp path clipping. +/// +internal sealed class CombinedGeometryImpl : GeometryImpl +{ + /// + /// Creates a combined geometry from two ImageSharp-backed geometries. + /// + /// The geometry combine mode. + /// The first geometry. + /// The second geometry. + /// The combined geometry. + public static CombinedGeometryImpl Create(GeometryCombineMode combineMode, GeometryImpl first, GeometryImpl second) + => new(CreatePaths(combineMode, first, second)); + + private CombinedGeometryImpl((IPath StrokePath, IPath? FillPath, IntersectionRule FillRule) paths) + : base(paths.StrokePath, paths.FillPath, paths.FillRule) + { + } + + private static (IPath StrokePath, IPath? FillPath, IntersectionRule FillRule) CreatePaths( + GeometryCombineMode combineMode, + GeometryImpl first, + GeometryImpl second) + { + BooleanOperation operation = combineMode.ToBooleanOperation(); + + IPath strokePath = first.StrokePath.Clip(operation, first.FillRule, second.StrokePath); + IPath? fillPath = null; + + if (first.FillPath is not null && second.FillPath is not null) + { + fillPath = ReferenceEquals(first.FillPath, first.StrokePath) && ReferenceEquals(second.FillPath, second.StrokePath) + ? strokePath + : first.FillPath.Clip(operation, first.FillRule, second.FillPath); + } + + return (strokePath, fillPath, first.FillRule); + } +} diff --git a/samples/AvaloniaControlCatalog/Converter/DegToRadConverter.cs b/samples/AvaloniaControlCatalog/Converter/DegToRadConverter.cs new file mode 100644 index 00000000..b062bcb6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Converter/DegToRadConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using Avalonia.Data.Converters; + +namespace ControlCatalog.Converter +{ + public class DegToRadConverter : IValueConverter + { + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is double rad) + { + return rad * 180.0d / Math.PI; + } + return 0.0d; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is double deg) + { + return deg / 180.0d * Math.PI; + } + return 0.0d; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Converter/GdpToBrushConverter.cs b/samples/AvaloniaControlCatalog/Converter/GdpToBrushConverter.cs new file mode 100644 index 00000000..d5d6f5cd --- /dev/null +++ b/samples/AvaloniaControlCatalog/Converter/GdpToBrushConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Globalization; +using Avalonia; +using Avalonia.Data.Converters; +using Avalonia.Media; +using Avalonia.Media.Immutable; + +namespace ControlCatalog.Converter; + +public sealed class GdpToBrushConverter : IValueConverter +{ + private readonly ImmutableSolidColorBrush _orangeBrush = new(Colors.Orange, 0.6); + private readonly ImmutableSolidColorBrush _yellowBrush = new(Colors.Yellow, 0.6); + private readonly ImmutableSolidColorBrush _greenBrush = new(Colors.LightGreen, 0.6); + + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is not int gdp) + return AvaloniaProperty.UnsetValue; + + return gdp switch + { + <= 5000 => _orangeBrush, + <= 10000 => _yellowBrush, + _ => _greenBrush + }; + } + + object IValueConverter.ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + => throw new NotSupportedException(); +} diff --git a/samples/AvaloniaControlCatalog/Converter/HexConverter.cs b/samples/AvaloniaControlCatalog/Converter/HexConverter.cs new file mode 100644 index 00000000..31cce5ba --- /dev/null +++ b/samples/AvaloniaControlCatalog/Converter/HexConverter.cs @@ -0,0 +1,34 @@ +using System; +using System.Globalization; +using Avalonia; +using Avalonia.Data.Converters; + +namespace ControlCatalog.Converter; + +public class HexConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + var str = value?.ToString(); + if (str == null) + return AvaloniaProperty.UnsetValue; + if (int.TryParse(str, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int x)) + return (decimal)x; + return AvaloniaProperty.UnsetValue; + + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + try + { + if (value is decimal d) + return ((int)d).ToString("X8"); + return AvaloniaProperty.UnsetValue; + } + catch + { + return AvaloniaProperty.UnsetValue; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Converter/MathSubtractConverter.cs b/samples/AvaloniaControlCatalog/Converter/MathSubtractConverter.cs new file mode 100644 index 00000000..6f3c99ab --- /dev/null +++ b/samples/AvaloniaControlCatalog/Converter/MathSubtractConverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; + +namespace ControlCatalog.Converter; + +public class MathSubtractConverter : IValueConverter +{ + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is double dv && parameter is double dp) + { + return dv - dp; + } + return double.NaN; + } + + public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } +} diff --git a/samples/AvaloniaControlCatalog/Converter/TableViewColumnWidthConverter.cs b/samples/AvaloniaControlCatalog/Converter/TableViewColumnWidthConverter.cs new file mode 100644 index 00000000..e9f976f6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Converter/TableViewColumnWidthConverter.cs @@ -0,0 +1,27 @@ +using System; +using System.Globalization; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Data.Converters; + +namespace ControlCatalog.Converter; + +public sealed class TableViewColumnWidthConverter : IValueConverter +{ + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is bool useStarSize && + parameter is string stringParameter && + double.TryParse(stringParameter, NumberStyles.Number, CultureInfo.InvariantCulture, out var baseWidth)) + { + return useStarSize ? + new GridLength(baseWidth, GridUnitType.Star) : + new GridLength(baseWidth * 100, GridUnitType.Pixel); + } + + return AvaloniaProperty.UnsetValue; + } + + object? IValueConverter.ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + => throw new NotSupportedException(); +} diff --git a/samples/AvaloniaControlCatalog/DecoratedWindow.xaml b/samples/AvaloniaControlCatalog/DecoratedWindow.xaml new file mode 100644 index 00000000..804eebfb --- /dev/null +++ b/samples/AvaloniaControlCatalog/DecoratedWindow.xaml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Title + + + + + + + + + + + + Hello world! + + + + None + BorderOnly + Full + + + CanResize + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/DecoratedWindow.xaml.cs b/samples/AvaloniaControlCatalog/DecoratedWindow.xaml.cs new file mode 100644 index 00000000..bb87d982 --- /dev/null +++ b/samples/AvaloniaControlCatalog/DecoratedWindow.xaml.cs @@ -0,0 +1,45 @@ +using Avalonia.Controls; +using Avalonia.Input; + +namespace ControlCatalog +{ + public partial class DecoratedWindow : Window + { + public DecoratedWindow() + { + InitializeComponent(); + TitleBar.PointerPressed += (i, e) => + { + BeginMoveDrag(e); + }; + SetupSide(Left, StandardCursorType.LeftSide, WindowEdge.West); + SetupSide(Right, StandardCursorType.RightSide, WindowEdge.East); + SetupSide(Top, StandardCursorType.TopSide, WindowEdge.North); + SetupSide(Bottom, StandardCursorType.BottomSide, WindowEdge.South); + SetupSide(TopLeft, StandardCursorType.TopLeftCorner, WindowEdge.NorthWest); + SetupSide(TopRight, StandardCursorType.TopRightCorner, WindowEdge.NorthEast); + SetupSide(BottomLeft, StandardCursorType.BottomLeftCorner, WindowEdge.SouthWest); + SetupSide(BottomRight, StandardCursorType.BottomRightCorner, WindowEdge.SouthEast); + MinimizeButton.Click += delegate + { this.WindowState = WindowState.Minimized; }; + MaximizeButton.Click += delegate + { + WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; + }; + CloseButton.Click += delegate + { + Close(); + }; + } + + private void SetupSide(Control ctl, StandardCursorType cursor, WindowEdge edge) + { + ctl.Cursor = new Cursor(cursor); + ctl.PointerPressed += (i, e) => + { + if (WindowState == WindowState.Normal) + BeginResizeDrag(edge, e); + }; + } + } +} diff --git a/samples/AvaloniaControlCatalog/DrawingBackendMode.cs b/samples/AvaloniaControlCatalog/DrawingBackendMode.cs new file mode 100644 index 00000000..366f40b2 --- /dev/null +++ b/samples/AvaloniaControlCatalog/DrawingBackendMode.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace AvaloniaControlCatalog; + +/// +/// Selects the rendering backend used by the ImageSharp.Drawing Avalonia sample. +/// +internal enum DrawingBackendMode +{ + /// + /// Uses WebGPU when Avalonia exposes a compatible native surface; otherwise uses the CPU framebuffer. + /// + Auto, + + /// + /// Uses the CPU framebuffer renderer. + /// + Cpu, + + /// + /// Requires a WebGPU-compatible native surface. + /// + WebGpu +} diff --git a/samples/AvaloniaControlCatalog/DrawingContextImpl.cs b/samples/AvaloniaControlCatalog/DrawingContextImpl.cs new file mode 100644 index 00000000..a1d2187a --- /dev/null +++ b/samples/AvaloniaControlCatalog/DrawingContextImpl.cs @@ -0,0 +1,1810 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics; +using System.Numerics; +using System.Text; +using Avalonia; +using Avalonia.Platform; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.Drawing.Processing.Backends; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; +using AvaloniaEdgeMode = Avalonia.Media.EdgeMode; +using AvaloniaRenderOptions = Avalonia.Media.RenderOptions; +using AvaloniaTextOptions = Avalonia.Media.TextOptions; +using AvaloniaTextRenderingMode = Avalonia.Media.TextRenderingMode; +using BoxShadow = Avalonia.Media.BoxShadow; + +namespace AvaloniaControlCatalog; + +/// +/// Avalonia drawing context implementation backed by an ImageSharp.Drawing canvas. +/// +internal sealed class DrawingContextImpl : IDrawingContextImpl, IDrawingContextImplWithEffects, IDrawingContextWithAcrylicLikeSupport +{ + private const float AcrylicBlurSigma = 10F; + + private readonly ILockedFramebuffer? framebuffer; + private readonly Image? image; + private readonly WebGPURenderTarget? renderTarget; + private readonly WebGPUSurfaceFrame? surfaceFrame; + private readonly DrawingCanvas canvas; + private readonly bool useWebGpuBackend; + private readonly bool ownsCanvas; + private readonly int traceId; + private readonly StringBuilder? trace; + private readonly Stack transforms = new(); + private readonly Stack renderOptions = new(); + private readonly Stack textOptions = new(); + private readonly Stack opacities = new(); + private readonly Stack opacityLayers = new(); + private readonly Stack<(Brush? Brush, RectangleF Bounds, Rectangle LayerBounds, float Opacity, bool HasLayer)> opacityMasks = new(); + private readonly Stack effects = new(); + private readonly List brushImages = []; + private readonly AvaloniaVector dpi; + private readonly bool ownsImage; + private static int nextTraceId; + private static bool environmentTraceConsumed; + private int traceOperationIndex; + private int canvasStateDepth; + private AvaloniaRenderOptions currentRenderOptions; + private AvaloniaTextOptions currentTextOptions; + private double opacity = 1; + private bool transientResourcesDisposed; + + /// + /// Selects the drawing backend used by top-level render targets. + /// + public static DrawingBackendMode BackendMode { get; set; } = DrawingBackendMode.Auto; + + /// + /// When set, the next framebuffer render is saved to a PNG in the temp folder for debugging, then the + /// flag resets so exactly one frame is captured on demand (no per-frame disk spam). Flip it from the + /// debugger or wire it to an input to grab the rendered framebuffer and inspect rendering vs present. + /// + public static bool DebugSaveNextFrame { get; set; } = false; + + /// + /// When set, the next drawing context writes a text trace of clips, clears, layers, and geometry + /// draws to the temp folder, then resets. Enable it when a frame needs operation-level diagnostics. + /// + public static bool DebugTraceNextFrame { get; set; } = false; + + private static readonly DrawingTextCache TextCache = new(); + + /// + /// Initializes a drawing context over an Avalonia framebuffer lock. + /// + /// The locked framebuffer to draw into. + public DrawingContextImpl(ILockedFramebuffer framebuffer) + : this(WrapFramebuffer(framebuffer), framebuffer.Dpi, true) + { + this.framebuffer = framebuffer; + + this.Trace(() => $"Framebuffer size={framebuffer.Size.Width}x{framebuffer.Size.Height} rowBytes={framebuffer.RowBytes} format={framebuffer.Format} alpha={framebuffer.AlphaFormat}"); + } + + /// + /// Initializes a drawing context over an existing ImageSharp image. + /// + /// The ImageSharp image to draw into. + /// The image DPI. + public DrawingContextImpl(Image image, AvaloniaVector dpi) + : this(image, dpi, false) + { + } + + /// + /// Initializes a drawing context over an acquired WebGPU surface frame. + /// + /// The acquired surface frame that will present on disposal. + /// The frame DPI. + /// The frame size in pixels. + public DrawingContextImpl(WebGPUSurfaceFrame surfaceFrame, AvaloniaVector dpi, PixelSize pixelSize) + { + this.surfaceFrame = surfaceFrame; + this.canvas = surfaceFrame.Canvas; + this.dpi = dpi; + this.useWebGpuBackend = true; + this.ownsCanvas = false; + + if (ShouldTraceContext(ownsImage: false, pixelSize.Width, pixelSize.Height)) + { + this.traceId = Interlocked.Increment(ref nextTraceId); + this.trace = new StringBuilder(); + this.Trace(() => $"Begin size={pixelSize.Width}x{pixelSize.Height} dpi={dpi.X},{dpi.Y} backend=WebGPU ownsImage=false"); + } + } + + /// + /// Initializes a drawing context over a WebGPU offscreen render target. + /// + /// The WebGPU render target that receives layer drawing commands. + /// The render target DPI. + /// The render target size in pixels. + public DrawingContextImpl(WebGPURenderTarget renderTarget, AvaloniaVector dpi, PixelSize pixelSize) + : this(renderTarget, dpi, pixelSize, ownsCanvas: true) + { + } + + /// + /// Initializes a drawing context over a WebGPU offscreen render target. + /// + /// The WebGPU render target that receives layer drawing commands. + /// The render target DPI. + /// The render target size in pixels. + /// A value indicating whether this context owns the canvas lifetime. + internal DrawingContextImpl(WebGPURenderTarget renderTarget, AvaloniaVector dpi, PixelSize pixelSize, bool ownsCanvas) + { + this.renderTarget = renderTarget; + this.canvas = renderTarget.CreateCanvas(new DrawingOptions(), TextCache); + this.dpi = dpi; + this.useWebGpuBackend = true; + this.ownsCanvas = ownsCanvas; + + if (ShouldTraceContext(ownsImage: false, pixelSize.Width, pixelSize.Height)) + { + this.traceId = Interlocked.Increment(ref nextTraceId); + this.trace = new StringBuilder(); + this.Trace(() => $"Begin size={pixelSize.Width}x{pixelSize.Height} dpi={dpi.X},{dpi.Y} backend=WebGPU-layer ownsImage=false"); + } + } + + private DrawingContextImpl(Image image, AvaloniaVector dpi, bool ownsImage) + { + this.image = image; + this.dpi = dpi; + this.ownsImage = ownsImage; + this.useWebGpuBackend = false; + this.ownsCanvas = true; + + if (ShouldTraceContext(ownsImage, image.Width, image.Height)) + { + this.traceId = Interlocked.Increment(ref nextTraceId); + this.trace = new StringBuilder(); + this.Trace(() => $"Begin size={image.Width}x{image.Height} dpi={dpi.X},{dpi.Y} backend=CPU ownsImage={ownsImage}"); + } + + this.canvas = image.Frames.RootFrame.CreateCanvas(Configuration.Default, new DrawingOptions(), TextCache); + } + + /// + public Matrix Transform { get; set; } = Matrix.Identity; + + /// + /// Gets the drawing canvas used by this context. + /// + internal DrawingCanvas Canvas => this.canvas; + + /// + public void Clear(Avalonia.Media.Color color) + { + this.Trace(() => $"Clear color={Describe(color)}"); + this.canvas.Clear(Brushes.Solid(color.ToColor())); + } + + /// + public void DrawBitmap(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect) + { + IDrawableBitmapImpl bitmap = (IDrawableBitmapImpl)source; + double imageOpacity = opacity * this.opacity; + Rectangle imageSourceRect = sourceRect.ToRectangle(); + RectangleF imageDestinationRect = destRect.ToRectangleF(); + bool isUpscaling = imageDestinationRect.Width > imageSourceRect.Width || imageDestinationRect.Height > imageSourceRect.Height; + var sampler = this.currentRenderOptions.BitmapInterpolationMode.ToResampler(isUpscaling); + PixelAlphaCompositionMode alphaCompositionMode = this.currentRenderOptions.BitmapBlendingMode.ToAlphaCompositionMode(); + + this.Trace(() => $"DrawBitmap image={bitmap.PixelSize.Width}x{bitmap.PixelSize.Height} opacity={imageOpacity:0.###} source={Describe(sourceRect)} dest={Describe(destRect)} transform={Describe(this.Transform)}"); + bitmap.Draw(this, imageSourceRect, imageDestinationRect, sampler, (float)imageOpacity, alphaCompositionMode); + } + + /// + /// Draws an ImageSharp image into this context using the supplied bitmap draw state. + /// + /// The image to draw. + /// The source rectangle in image pixels. + /// The destination rectangle in drawing coordinates. + /// The resampler used when scaling the image. + /// The opacity applied to the image draw. + /// The alpha composition mode used to blend the image with the destination. + public void DrawImage( + Image image, + Rectangle sourceRect, + RectangleF destinationRect, + IResampler sampler, + float opacity, + PixelAlphaCompositionMode alphaCompositionMode) + { + DrawingOptions options = this.CreateDrawingOptions(IntersectionRule.EvenOdd); + options.GraphicsOptions.AlphaCompositionMode = alphaCompositionMode; + this.SaveCanvasState(options, $"image alpha={alphaCompositionMode}"); + + if (opacity >= 1) + { + this.canvas.DrawImage(image, sourceRect, destinationRect, sampler); + this.RestoreCanvasState("DrawImage state"); + return; + } + + Rectangle layerBounds = ToEnclosingRectangle(destinationRect); + if (layerBounds.Width <= 0 || layerBounds.Height <= 0) + { + this.RestoreCanvasState("DrawImage state"); + return; + } + + this.SaveCanvasLayer( + new GraphicsOptions + { + BlendPercentage = opacity, + AlphaCompositionMode = alphaCompositionMode + }, + layerBounds, + "DrawImage opacity"); + + this.canvas.DrawImage(image, sourceRect, destinationRect, sampler); + this.RestoreCanvasState("DrawImage opacity layer"); + this.RestoreCanvasState("DrawImage state"); + } + + /// + public void DrawBitmap(IBitmapImpl source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect) + { + this.Trace(() => $"DrawBitmap opacityMask={DescribeBrush(opacityMask)} maskRect={Describe(opacityMaskRect)} dest={Describe(destRect)} transform={Describe(this.Transform)}"); + this.PushOpacityMask(opacityMask, opacityMaskRect); + this.DrawBitmap(source, 1, new Rect(((IDrawableBitmapImpl)source).PixelSize.ToSize(1)), destRect); + this.PopOpacityMask(); + } + + /// + public void DrawLine(IPen? pen, Avalonia.Point p1, Avalonia.Point p2) + { + Rect targetRect = new Rect(p1, p2).Normalize(); + Pen? strokePen = this.CreatePen(pen, targetRect); + + if (strokePen is not null) + { + this.PushDrawingState(IntersectionRule.EvenOdd, this.GetBrushOpacity(pen?.Brush)); + this.canvas.DrawLine(strokePen, p1.ToPointF(), p2.ToPointF()); + this.RestoreCanvasState("DrawLine state"); + } + } + + /// + public void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry) + { + GeometryImpl geometryImpl = (GeometryImpl)geometry; + Rect fillBounds = geometryImpl.GetRenderBounds(null); + Rect strokeBounds = geometryImpl.GetRenderBounds(pen); + + this.Trace(() => $"DrawGeometry fillRule={geometryImpl.FillRule} fillBounds={Describe(fillBounds)} strokeBounds={Describe(strokeBounds)} brush={DescribeBrush(brush)} pen={DescribePen(pen)} transform={Describe(this.Transform)}"); + + Brush? fillBrush = this.CreateBrush(brush, fillBounds); + + if (fillBrush is not null && geometryImpl.FillPath is not null) + { + this.FillPath(fillBrush, geometryImpl.FillPath, geometryImpl.FillRule, this.GetBrushOpacity(brush)); + } + + Pen? strokePen = this.CreatePen(pen, strokeBounds); + + if (strokePen is not null) + { + IntersectionRule strokeRule = geometryImpl.FillRule; + this.Trace(() => $"StrokePath fillRule={strokeRule} pathBounds={Describe(geometryImpl.StrokePath.Bounds)} strokeBounds={Describe(strokeBounds)} transform={Describe(this.Transform)}"); + + this.PushDrawingState(strokeRule, this.GetBrushOpacity(pen?.Brush)); + this.canvas.Draw(strokePen, geometryImpl.StrokePath); + this.RestoreCanvasState("DrawGeometry stroke state"); + } + } + + /// + public void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect, BoxShadows boxShadows = default) + { + this.Trace(() => $"DrawRectangle rect={Describe(rect.Rect)} rounded={rect.IsRounded} brush={DescribeBrush(brush)} pen={DescribePen(pen)} shadows={boxShadows.Count} transform={Describe(this.Transform)}"); + + IPath path = rect.IsRounded + ? rect.ToRoundedRectanglePath() + : new RectanglePolygon(rect.Rect.ToRectangleF()); + + this.DrawOuterBoxShadows(rect, path, boxShadows); + + // Inset shadows paint above the fill and below the stroke, matching the Skia backend, + // so the fill and stroke split around them when any are present. + if (boxShadows.HasInsetShadows) + { + this.DrawGeometry(brush, null, new StreamGeometryImpl(path, path, IntersectionRule.EvenOdd)); + this.DrawInnerBoxShadows(rect, path, boxShadows); + this.DrawGeometry(null, pen, new StreamGeometryImpl(path, path, IntersectionRule.EvenOdd)); + return; + } + + this.DrawGeometry(brush, pen, new StreamGeometryImpl(path, path, IntersectionRule.EvenOdd)); + } + + /// + public void DrawRectangle(Avalonia.Media.IExperimentalAcrylicMaterial material, RoundedRect rect) + { + if (rect.Rect.Height <= 0 || rect.Rect.Width <= 0) + { + return; + } + + this.Trace(() => $"DrawAcrylicRectangle rect={Describe(rect.Rect)} rounded={rect.IsRounded} background={material.BackgroundSource} material={Describe(material.MaterialColor)} tint={Describe(material.TintColor)} transform={Describe(this.Transform)}"); + + IPath path = rect.IsRounded + ? rect.ToRoundedRectanglePath() + : new RectanglePolygon(rect.Rect.ToRectangleF()); + + // The acrylic effect frosts and tints whatever lies beneath the shape and replaces the + // region with the result: the backdrop write-back carries Src semantics, which is the one + // thing Skia's Digger mode changes on its paint. Under a transparent window the backdrop + // is empty and the region becomes the bare tint, exactly Skia's material; over in-app + // content the material is genuinely frosted, which Skia fakes with a noise tile. + Color materialTint = FlattenAcrylicTint(material.MaterialColor, material.TintColor); + this.SaveCanvasLayer( + new GraphicsOptions(), + path, + new WebGPUBackdropAcrylicLayerEffect(AcrylicBlurSigma, materialTint), + this.CreateDrawingOptions(IntersectionRule.EvenOdd), + "Acrylic rectangle"); + + this.RestoreCanvasState("Acrylic rectangle layer"); + } + + /// + /// Flattens the acrylic material and tint into the single colour whose source-over composite + /// equals tint over material. + /// + /// + /// Skia's acrylic paint composes two constant colour shaders, tint source-over material, per + /// pixel. Because source-over is associative, tint over (material over backdrop) equals + /// (tint over material) over backdrop, so the pair collapses to one Porter-Duff over of the two + /// constants: the weighted sum below is the premultiplied blend and the division by the result + /// alpha converts back to the straight colour the acrylic effect's matrix expects. + /// + private static Color FlattenAcrylicTint(AvaloniaColor material, AvaloniaColor tint) + { + float materialAlpha = material.A / 255F; + float tintAlpha = tint.A / 255F; + float alpha = tintAlpha + (materialAlpha * (1F - tintAlpha)); + if (alpha <= 0F) + { + return Color.Transparent; + } + + byte Flatten(byte tintChannel, byte materialChannel) + => (byte)Math.Round(((tintChannel * tintAlpha) + (materialChannel * materialAlpha * (1F - tintAlpha))) / alpha); + + return new AvaloniaColor( + (byte)Math.Round(alpha * 255F), + Flatten(tint.R, material.R), + Flatten(tint.G, material.G), + Flatten(tint.B, material.B)).ToColor(); + } + + /// + public void DrawRegion(IBrush? brush, IPen? pen, IPlatformRenderInterfaceRegion region) + { + RegionImpl regionImpl = (RegionImpl)region; + + this.Trace(() => $"DrawRegion empty={regionImpl.IsEmpty} bounds={Describe(regionImpl.Bounds)} rects={Describe(regionImpl.Rects)} brush={DescribeBrush(brush)} pen={DescribePen(pen)} transform={Describe(this.Transform)}"); + + if (regionImpl.IsEmpty) + { + return; + } + + IPath path = regionImpl.ToPath(); + + this.DrawGeometry(brush, pen, new StreamGeometryImpl(path, path, IntersectionRule.NonZero)); + } + + /// + public void DrawEllipse(IBrush? brush, IPen? pen, Rect rect) + { + IPath path = new EllipsePolygon(rect.Center.ToPointF(), rect.Size.ToSizeF()); + this.DrawGeometry(brush, pen, new StreamGeometryImpl(path, path, IntersectionRule.EvenOdd)); + } + + /// + public void DrawGlyphRun(IBrush? foreground, IGlyphRunImpl glyphRun) + { + if (glyphRun is not GlyphRunImpl glyphRunImpl) + { + this.Trace(() => $"DrawGlyphRun unsupported={glyphRun.GetType().Name}"); + return; + } + + ReadOnlyMemory glyphIds = glyphRunImpl.GlyphIds; + if (glyphIds.IsEmpty) + { + this.Trace(() => $"DrawGlyphRun empty bounds={Describe(glyphRunImpl.Bounds)} transform={Describe(this.Transform)}"); + return; + } + + Brush? brush = this.CreateBrush(foreground, glyphRunImpl.Bounds); + + this.Trace(() => $"DrawGlyphRun glyphs={glyphIds.Length} bounds={Describe(glyphRunImpl.Bounds)} foreground={DescribeBrush(foreground)} transform={Describe(this.Transform)}"); + + if (brush is null) + { + return; + } + + // Start with the currently pushed TextOptions, then apply the same RenderOptions + // fallback shape as the Skia backend before creating the glyph rendering options. + AvaloniaTextOptions effectiveTextOptions = this.currentTextOptions; + AvaloniaRenderOptions effectiveRenderOptions = this.currentRenderOptions; + +#pragma warning disable CS0618 + if (effectiveTextOptions.TextRenderingMode == AvaloniaTextRenderingMode.Unspecified && + effectiveRenderOptions.TextRenderingMode != AvaloniaTextRenderingMode.Unspecified) + { + effectiveTextOptions = effectiveTextOptions with + { + TextRenderingMode = effectiveRenderOptions.TextRenderingMode + }; + } +#pragma warning restore CS0618 + + // If TextRenderingMode is still unspecified, derive it from EdgeMode as Skia does. + if (effectiveTextOptions.TextRenderingMode == AvaloniaTextRenderingMode.Unspecified) + { + effectiveTextOptions = effectiveTextOptions with + { + TextRenderingMode = effectiveRenderOptions.EdgeMode == AvaloniaEdgeMode.Aliased + ? AvaloniaTextRenderingMode.Alias + : AvaloniaTextRenderingMode.SubpixelAntialias + }; + } + + bool antialias = effectiveTextOptions.TextRenderingMode != AvaloniaTextRenderingMode.Alias; + + // Keep glyph-level font option mapping on the glyph run, matching Skia's + // GlyphRunImpl.CreateFont ownership. + RichGlyphOptions options = glyphRunImpl.CreateGlyphOptions(effectiveTextOptions); + + this.PushDrawingState(IntersectionRule.NonZero, antialias, this.GetBrushOpacity(foreground)); + this.canvas.DrawText(glyphIds.Span, glyphRunImpl.GlyphOrigins.Span, options, brush, pen: null); + this.RestoreCanvasState("DrawGlyphRun state"); + } + + /// + public IDrawingContextLayerImpl CreateLayer(PixelSize size) + { + if (this.useWebGpuBackend) + { + if (this.surfaceFrame is not null) + { + WebGPURenderTarget layerTarget = this.surfaceFrame.CreateRenderTarget(size.Width, size.Height); + + return new WebGPULayerImpl(layerTarget, size, this.dpi); + } + + if (this.renderTarget is not null) + { + WebGPURenderTarget layerTarget = this.renderTarget.CreateRenderTarget(size.Width, size.Height); + + return new WebGPULayerImpl(layerTarget, size, this.dpi); + } + + throw new InvalidOperationException("The WebGPU drawing context has no target for layer allocation."); + } + + return new RenderTargetBitmapImpl(size, this.dpi); + } + + /// + /// Copies a CPU-backed layer into this context. + /// + /// The source image. + /// The layer size in pixels. + internal void Blit(Image source, PixelSize size) + { + using DrawingCanvas sourceCanvas = source.Frames.RootFrame.CreateCanvas(Configuration.Default, new DrawingOptions(), TextCache); + this.canvas.CopyPixelsFrom( + sourceCanvas, + new Rectangle(0, 0, size.Width, size.Height), + new SixLabors.ImageSharp.Point(0, 0)); + } + + /// + /// Copies a layer canvas into this context. + /// + /// The source canvas. + /// The layer size in pixels. + internal void Blit(DrawingCanvas source, PixelSize size) + { + this.canvas.CopyPixelsFrom( + source, + new Rectangle(0, 0, size.Width, size.Height), + new SixLabors.ImageSharp.Point(0, 0)); + } + + /// + public void PushClip(Rect clip) + { + this.Trace(() => $"PushClip rect={Describe(clip)} transform={Describe(this.Transform)}"); + this.SaveTransform(); + this.PushDrawingState(IntersectionRule.EvenOdd, antialias: false); + this.canvas.Clip(new RectanglePolygon(clip.ToRectangleF())); + } + + /// + public void PushClip(RoundedRect clip) + { + this.Trace(() => $"PushClip roundedRect={Describe(clip.Rect)} rounded={clip.IsRounded} transform={Describe(this.Transform)}"); + this.SaveTransform(); + this.PushDrawingState(IntersectionRule.EvenOdd); + this.canvas.Clip(clip.ToRoundedRectanglePath()); + } + + /// + public void PushClip(IPlatformRenderInterfaceRegion region) + { + RegionImpl regionImpl = (RegionImpl)region; + + this.Trace(() => $"PushClip region empty={regionImpl.IsEmpty} bounds={Describe(regionImpl.Bounds)} rects={Describe(regionImpl.Rects)} transform={Describe(this.Transform)}"); + this.SaveTransform(); + Matrix transform = this.Transform; + this.Transform = Matrix.Identity; + + // Avalonia's Skia backend uses SkCanvas.ClipRegion here. Skia documents region clips as + // device-space and aliased, so keep the dirty-region edge hard instead of blending stale + // pixels at the redraw boundary. + this.PushDrawingState(IntersectionRule.NonZero, antialias: false); + this.canvas.Clip(regionImpl.ToPath()); + this.Transform = transform; + } + + /// + public void PopClip() + { + this.Trace(() => "PopClip"); + this.RestoreCanvasState("Clip state"); + this.RestoreTransform(); + } + + /// + public void PushLayer(Rect bounds) + { + this.Trace(() => $"PushLayer bounds={Describe(bounds)} transform={Describe(this.Transform)}"); + this.SaveTransform(); + this.SaveCanvasLayer(new GraphicsOptions(), ToEnclosingRectangle(bounds), this.CreateDrawingOptions(IntersectionRule.EvenOdd), "PushLayer"); + } + + /// + public void PopLayer() + { + this.Trace(() => "PopLayer"); + this.RestoreCanvasState("Layer"); + this.RestoreTransform(); + } + + /// + public void PushOpacity(double opacity, Rect? bounds) + { + this.Trace(() => $"PushOpacity opacity={opacity:0.###} bounds={(bounds.HasValue ? Describe(bounds.Value) : "")} current={this.opacity:0.###} transform={Describe(this.Transform)}"); + this.opacities.Push(this.opacity); + + bool useOpacityLayer = this.currentRenderOptions.RequiresFullOpacityHandling == true; + this.opacityLayers.Push(useOpacityLayer); + + if (useOpacityLayer) + { + float layerOpacity = (float)(this.opacity * opacity); + Rectangle layerBounds = bounds is not null ? ToEnclosingRectangle(bounds.Value) : this.canvas.Bounds; + + // Some controls require opacity to be applied to the composed result rather than to each + // child brush. Match Skia by isolating those controls in a layer and fading the layer when + // it is restored. + this.opacity = 1; + this.SaveCanvasLayer( + new GraphicsOptions { BlendPercentage = layerOpacity }, + layerBounds, + this.CreateDrawingOptions(IntersectionRule.EvenOdd), + "PushOpacity full opacity layer"); + + return; + } + + this.opacity *= opacity; + } + + /// + public void PopOpacity() + { + this.Trace(() => $"PopOpacity current={this.opacity:0.###}"); + if (this.opacityLayers.Pop()) + { + this.RestoreCanvasState("Opacity layer"); + } + + this.opacity = this.opacities.Pop(); + } + + /// + public void PushOpacityMask(IBrush mask, Rect bounds) + { + this.SaveTransform(); + + Brush? maskBrush = this.CreateBrush(mask, bounds); + Rectangle layerBounds = ToEnclosingRectangle(bounds); + bool hasLayer = maskBrush is not null && layerBounds.Width > 0 && layerBounds.Height > 0; + + this.opacityMasks.Push((maskBrush, bounds.ToRectangleF(), layerBounds, this.GetBrushOpacity(mask), hasLayer)); + + if (hasLayer) + { + // Establish the active transform on the canvas state so the layer (and the content drawn + // into it) is positioned correctly, then isolate the masked content in its own layer. + this.PushDrawingState(IntersectionRule.EvenOdd); + this.SaveCanvasLayer(new GraphicsOptions(), layerBounds, "OpacityMask content"); + } + } + + /// + public void PopOpacityMask() + { + (Brush? brush, RectangleF maskBounds, Rectangle layerBounds, float maskOpacity, bool hasLayer) = this.opacityMasks.Pop(); + + if (hasLayer) + { + // Draw the mask into a DstIn layer so, on composite, it keeps the content layer's colour + // but scales its alpha by the mask's alpha; the content then fades wherever the mask is + // transparent. Both layers and the saved state are unwound here. + this.SaveCanvasLayer( + new GraphicsOptions { AlphaCompositionMode = PixelAlphaCompositionMode.DestIn }, + layerBounds, + this.CreateDrawingOptions(IntersectionRule.EvenOdd, maskOpacity), + "OpacityMask mask"); + + this.canvas.Fill(brush!, new RectanglePolygon(maskBounds)); + this.RestoreCanvasState("OpacityMask mask layer"); + this.RestoreCanvasState("OpacityMask content layer"); + this.RestoreCanvasState("OpacityMask state"); + } + + this.RestoreTransform(); + } + + /// + public void PushEffect(AvaloniaRect? effectClipRect, IEffect effect) + { + this.Trace(() => $"PushEffect clip={(effectClipRect.HasValue ? Describe(effectClipRect.Value) : "")} effect={effect.GetType().Name} transform={Describe(this.Transform)}"); + + LayerEffect? layerEffect = this.CreateLayerEffect(effect); + this.effects.Push(layerEffect is not null); + + if (layerEffect is not null) + { + // Content drawn between push and pop renders into an isolated effect layer; the canvas + // applies the effect to the layer's content when the layer is restored. The ambient + // opacity rides the layer composite so the effect output fades with the content, and is + // reset inside the layer to avoid applying it twice, matching PushOpacity. + Rectangle bounds = effectClipRect is not null + ? ToEnclosingRectangle(effectClipRect.Value) + : this.canvas.Bounds; + + float layerOpacity = (float)this.opacity; + this.opacities.Push(this.opacity); + this.opacity = 1; + + this.SaveCanvasLayer( + new GraphicsOptions { BlendPercentage = layerOpacity }, + bounds, + layerEffect, + this.CreateDrawingOptions(IntersectionRule.EvenOdd), + "Effect content"); + } + } + + /// + public void PopEffect() + { + this.Trace(() => "PopEffect"); + + if (this.effects.Pop()) + { + this.RestoreCanvasState("Effect layer"); + this.opacity = this.opacities.Pop(); + } + } + + /// + /// Resolves an Avalonia effect into a canvas with device-space + /// parameters, mirroring the Skia backend's effect-to-image-filter mapping. Unsupported effect + /// types and degenerate blurs resolve to and draw unaffected. + /// + /// The effect to resolve. + /// The resolved effect, or . + private LayerEffect? CreateLayerEffect(IEffect effect) + { + // Blur sigma and shadow offsets act on the layer's pixels after the transform has been + // applied to the geometry, so both are mapped through the current transform the same way + // Skia's image filters follow the canvas matrix. + AvaloniaMatrix transform = this.Transform; + float scale = (float)Math.Sqrt(Math.Abs((transform.M11 * transform.M22) - (transform.M12 * transform.M21))); + + if (effect is IBlurEffect blur) + { + if (blur.Radius <= 0) + { + return null; + } + + return new WebGPUGaussianBlurLayerEffect(BlurRadiusToSigma(blur.Radius) * scale); + } + + if (effect is IDropShadowEffect drop) + { + float sigma = drop.BlurRadius > 0 ? BlurRadiusToSigma(drop.BlurRadius) * scale : 0F; + + // The offset is a direction vector, so it maps through the transform without translation. + SixLabors.ImageSharp.Point offset = new( + (int)Math.Round((drop.OffsetX * transform.M11) + (drop.OffsetY * transform.M21)), + (int)Math.Round((drop.OffsetX * transform.M12) + (drop.OffsetY * transform.M22))); + + // The colour's own alpha and the effect opacity scale the silhouette's alpha; the + // ambient opacity rides the effect layer's composite instead, matching the Skia + // backend's opacity-save-layer path. + byte alpha = (byte)Math.Clamp(drop.Color.A * drop.Opacity, 0D, 255D); + Color color = new AvaloniaColor(alpha, drop.Color.R, drop.Color.G, drop.Color.B).ToColor(); + + return new WebGPUDropShadowLayerEffect(offset, sigma, color); + } + + return null; + } + + /// + public void PushGeometryClip(IGeometryImpl clip) + { + this.SaveTransform(); + + GeometryImpl geometry = (GeometryImpl)clip; + this.Trace(() => $"PushGeometryClip fillRule={geometry.FillRule} pathBounds={Describe(geometry.FillPath?.Bounds ?? RectangleF.Empty)} transform={Describe(this.Transform)}"); + this.PushDrawingState(geometry.FillRule); + this.canvas.Clip(geometry.FillPath ?? EmptyPath.ClosedPath); + } + + /// + public void PopGeometryClip() => this.PopClip(); + + /// + public void PushRenderOptions(RenderOptions renderOptions) + { + this.renderOptions.Push(this.currentRenderOptions); + this.currentRenderOptions = this.currentRenderOptions.MergeWith(renderOptions); + this.TraceState($"PushRenderOptions requiresFullOpacity={this.currentRenderOptions.RequiresFullOpacityHandling}"); + } + + /// + public void PopRenderOptions() + { + this.currentRenderOptions = this.renderOptions.Pop(); + this.TraceState($"PopRenderOptions requiresFullOpacity={this.currentRenderOptions.RequiresFullOpacityHandling}"); + } + + /// + public void PushTextOptions(AvaloniaTextOptions textOptions) + { + this.textOptions.Push(this.currentTextOptions); + this.currentTextOptions = this.currentTextOptions.MergeWith(textOptions); + this.TraceState($"PushTextOptions rendering={this.currentTextOptions.TextRenderingMode}"); + } + + /// + public void PopTextOptions() + { + this.currentTextOptions = this.textOptions.Pop(); + this.TraceState($"PopTextOptions rendering={this.currentTextOptions.TextRenderingMode}"); + } + + /// + public object? GetFeature(Type t) => null; + + /// + public void Dispose() + { + if (this.surfaceFrame is not null) + { + // Surface frames own presentation. Disposing the frame flushes its canvas and presents the + // acquired swapchain texture, matching Skia's direct platform-drawable session shape. + this.surfaceFrame.Dispose(); + } + else + { + // Layer contexts leave their canvas alive for the owning layer's blit. Other contexts dispose the + // canvas here so queued commands render into their target. + if (this.ownsCanvas) + { + this.canvas.Dispose(); + } + else + { + this.canvas.Flush(); + } + } + + // On-demand capture of the rendered framebuffer (the actual window surface, not layers). Splits + // "holes are in the rendered image" from "holes appear only at present/display". + if (DebugSaveNextFrame && this.framebuffer is not null && this.image is not null) + { + DebugSaveNextFrame = false; + string path = System.IO.Path.Combine( + GetCaptureDirectory(), + $"imagesharp-frame-{DateTime.Now:yyyyMMdd-HHmmss-fff}.png"); + this.image.SaveAsPng(path); + } + + if (ShouldSaveContextImage() && this.image is not null) + { + string path = System.IO.Path.Combine( + GetCaptureDirectory(), + $"imagesharp-context-{DateTime.Now:yyyyMMdd-HHmmss-fff}-{this.traceId}-{(this.useWebGpuBackend ? "webgpu" : "cpu")}-{(this.ownsImage ? "owned" : "borrowed")}.png"); + this.image.SaveAsPng(path); + } + + if (this.trace is not null) + { + string path = System.IO.Path.Combine( + GetCaptureDirectory(), + $"imagesharp-trace-{DateTime.Now:yyyyMMdd-HHmmss-fff}-{this.traceId}.log"); + File.WriteAllText(path, this.trace.ToString()); + Debug.WriteLine($"ImageSharp drawing trace saved to {path}"); + } + + if (this.ownsCanvas || this.surfaceFrame is not null) + { + this.DisposeTransientResources(); + } + + this.framebuffer?.Dispose(); + + if (this.ownsImage && this.image is not null) + { + this.image.Dispose(); + } + } + + /// + /// Disposes temporary images referenced by queued draw commands after those commands have rendered. + /// + internal void DisposeTransientResources() + { + if (this.transientResourcesDisposed) + { + return; + } + + for (int i = 0; i < this.brushImages.Count; i++) + { + this.brushImages[i].Dispose(); + } + + this.transientResourcesDisposed = true; + } + + /// + /// Converts an Avalonia brush into an ImageSharp brush for the supplied target bounds. + /// + private Brush? CreateBrush(IBrush? brush, Rect targetRect) + { + if (brush is null) + { + return null; + } + + if (brush is ISolidColorBrush solid) + { + return Brushes.Solid(solid.Color.ToColor()); + } + + if (brush is IGradientBrush gradient) + { + // Brushes are built in local/context space; the DrawingCanvas applies the active + // transform to the brush, so the backend must not pre-transform it here. + return CreateGradientBrush(gradient, targetRect); + } + + if (brush is IImageBrush imageBrush) + { + return this.CreateImageBrush(imageBrush, targetRect); + } + + if (brush is Avalonia.Media.ISceneBrush sceneBrush) + { + using Avalonia.Media.ISceneBrushContent? content = sceneBrush.CreateContent(); + + return content is null + ? null + : this.CreateSceneBrushContent(content, targetRect); + } + + if (brush is Avalonia.Media.ISceneBrushContent sceneBrushContent) + { + return this.CreateSceneBrushContent(sceneBrushContent, targetRect); + } + + return null; + } + + /// + /// Gets the blend amount for a brush draw from Avalonia's brush opacity and active context opacity. + /// + private float GetBrushOpacity(IBrush? brush) + => (float)((brush?.Opacity ?? 1) * this.opacity); + + /// + /// Wraps the Avalonia framebuffer memory so the CPU canvas can render directly into it. + /// + private static unsafe Image WrapFramebuffer(ILockedFramebuffer framebuffer) + { + int rowByteCount = framebuffer.Size.Width * 4; + int bufferByteCount = (framebuffer.RowBytes * (framebuffer.Size.Height - 1)) + rowByteCount; + + return Image.WrapMemory( + framebuffer.Address.ToPointer(), + bufferByteCount, + framebuffer.Size.Width, + framebuffer.Size.Height, + framebuffer.RowBytes); + } + + /// + /// Saves the canvas state with the current transform, clip, and fill-rule options. + /// + /// + /// Save snapshots the current state and inherits the active clip; the canvas applies the active + /// transform to drawn paths and clips itself, so nothing is pre-transformed here. + /// + private void PushDrawingState(IntersectionRule fillRule) + => this.SaveCanvasState(this.CreateDrawingOptions(fillRule), $"fillRule={fillRule}"); + + /// + /// Saves a canvas state and records diagnostic stack depth when tracing is enabled. + /// + private void SaveCanvasState(DrawingOptions options, string reason) + { + this.canvas.Save(options); + this.canvasStateDepth++; + this.TraceState($"Save {reason}"); + } + + /// + /// Saves a compositing layer and records diagnostic stack depth when tracing is enabled. + /// + private void SaveCanvasLayer(GraphicsOptions layerOptions, Rectangle bounds, string reason) + { + this.canvas.SaveLayer(layerOptions, bounds); + this.canvasStateDepth++; + this.TraceState( + $"SaveLayer {reason} bounds={Describe(bounds)} blend={layerOptions.BlendPercentage:0.###} alpha={layerOptions.AlphaCompositionMode}"); + } + + /// + /// Saves a compositing layer with explicit drawing options and records diagnostic stack depth. + /// + private void SaveCanvasLayer(GraphicsOptions layerOptions, Rectangle bounds, DrawingOptions options, string reason) + { + this.canvas.SaveLayer(layerOptions, bounds, options); + this.canvasStateDepth++; + this.TraceState( + $"SaveLayer {reason} bounds={Describe(bounds)} blend={layerOptions.BlendPercentage:0.###} alpha={layerOptions.AlphaCompositionMode}"); + } + + /// + /// Saves an effect compositing layer and records diagnostic stack depth when tracing is enabled. + /// + private void SaveCanvasLayer(GraphicsOptions layerOptions, Rectangle bounds, LayerEffect effect, DrawingOptions options, string reason) + { + this.canvas.SaveLayer(layerOptions, bounds, effect, options); + this.canvasStateDepth++; + this.TraceState( + $"SaveLayer {reason} effect={effect.GetType().Name} bounds={Describe(bounds)} blend={layerOptions.BlendPercentage:0.###} alpha={layerOptions.AlphaCompositionMode}"); + } + + /// + /// Saves an effect compositing layer over a path region and records diagnostic stack depth + /// when tracing is enabled. + /// + private void SaveCanvasLayer(GraphicsOptions layerOptions, IPath region, LayerEffect effect, DrawingOptions options, string reason) + { + this.canvas.SaveLayer(layerOptions, region, effect, options); + this.canvasStateDepth++; + this.TraceState( + $"SaveLayer {reason} effect={effect.GetType().Name} bounds={Describe(region.Bounds)} blend={layerOptions.BlendPercentage:0.###} alpha={layerOptions.AlphaCompositionMode}"); + } + + /// + /// Restores a canvas state and records diagnostic stack depth when tracing is enabled. + /// + private void RestoreCanvasState(string reason) + { + this.canvas.Restore(); + this.canvasStateDepth--; + this.TraceState($"Restore {reason}"); + } + + /// + /// Saves the canvas state with the current transform, clip, fill-rule options, and draw opacity. + /// + private void PushDrawingState(IntersectionRule fillRule, float blendPercentage) + => this.SaveCanvasState(this.CreateDrawingOptions(fillRule, blendPercentage), $"fillRule={fillRule} blend={blendPercentage:0.###}"); + + /// + /// Saves the canvas state with an explicit antialiasing value. + /// + private void PushDrawingState(IntersectionRule fillRule, bool antialias, float blendPercentage = 1) + { + DrawingOptions options = this.CreateDrawingOptions(fillRule, blendPercentage); + options.GraphicsOptions.Antialias = antialias; + + this.SaveCanvasState(options, $"fillRule={fillRule} antialias={antialias} blend={blendPercentage:0.###}"); + } + + /// + /// Creates ImageSharp drawing options from the active Avalonia drawing state. + /// + private DrawingOptions CreateDrawingOptions(IntersectionRule fillRule) + => new() + { + Transform = this.Transform.ToMatrix4x4(), + IntersectionRule = fillRule + }; + + /// + /// Creates ImageSharp drawing options with Avalonia opacity mapped to brush blend amount. + /// + private DrawingOptions CreateDrawingOptions(IntersectionRule fillRule, float blendPercentage) + { + DrawingOptions options = this.CreateDrawingOptions(fillRule); + options.GraphicsOptions.BlendPercentage = blendPercentage; + + return options; + } + + /// + /// Saves the current transform before Avalonia pushes a nested drawing state. + /// + private void SaveTransform() + { + this.transforms.Push(this.Transform); + this.TraceState("SaveTransform"); + } + + /// + /// Restores the transform saved by the matching push operation. + /// + private void RestoreTransform() + { + this.Transform = this.transforms.Pop(); + this.TraceState("RestoreTransform"); + } + + /// + /// Converts an Avalonia pen into an ImageSharp pen for the supplied target bounds. + /// + private Pen? CreatePen(IPen? pen, Rect targetRect) + { + if (pen is null || pen.Thickness <= 0) + { + return null; + } + + Brush? brush = this.CreateBrush(pen.Brush, targetRect); + + return brush is null ? null : pen.ToPen(brush); + } + + /// + /// Draws non-inset rectangle shadows before the rectangle fill/stroke. + /// + private void DrawOuterBoxShadows(RoundedRect rect, IPath contentPath, BoxShadows boxShadows) + { + if (boxShadows.Count == 0) + { + return; + } + + foreach (BoxShadow shadow in boxShadows) + { + if (shadow == default || shadow.Color.A == 0 || shadow.IsInset) + { + continue; + } + + this.DrawOuterBoxShadow(rect, contentPath, shadow); + } + } + + /// + /// Draws inset rectangle shadows after the rectangle fill and before its stroke. + /// + private void DrawInnerBoxShadows(RoundedRect rect, IPath contentPath, BoxShadows boxShadows) + { + foreach (BoxShadow shadow in boxShadows) + { + if (shadow == default || shadow.Color.A == 0 || !shadow.IsInset) + { + continue; + } + + this.DrawInnerBoxShadow(rect, contentPath, shadow); + } + } + + /// + /// Draws one inset shadow. Gaussian blur is linear and preserves constants, so the + /// blurred complement of the hole equals one minus the blurred hole mask: the content is + /// filled with the shadow color and the blurred hole mask is punched out of it with a + /// DestOut layer composite. No complement geometry padded by the blur reach is needed, + /// because one minus the mask saturates on its own away from the hole, and the technique + /// works for any content geometry the clip can express. + /// + private void DrawInnerBoxShadow(RoundedRect rect, IPath contentPath, BoxShadow shadow) + { + if (rect.Rect.Width <= 0 || rect.Rect.Height <= 0) + { + return; + } + + // The hole the light shines through: the content deflated by the spread and shifted + // by the offset. Everything outside it, inside the content, is in shadow. + RoundedRect holeRect = TranslateRoundedRect( + rect.Deflate(shadow.Spread, shadow.Spread), + shadow.OffsetX, + shadow.OffsetY); + + this.PushDrawingState(IntersectionRule.EvenOdd); + this.canvas.Clip(ClipOperation.Intersection, contentPath); + this.SaveCanvasLayer(new GraphicsOptions { BlendPercentage = (float)this.opacity }, ToEnclosingRectangle(rect.Rect), "InnerBoxShadow"); + this.canvas.Fill(Brushes.Solid(shadow.Color.ToColor()), contentPath); + + // A spread large enough to consume the content leaves no hole; the whole interior + // stays in shadow. + if (holeRect.Rect.Width > 0 && holeRect.Rect.Height > 0) + { + IPath holePath = holeRect.IsRounded + ? holeRect.ToRoundedRectanglePath() + : new RectanglePolygon(holeRect.Rect.ToRectangleF()); + + // The hole mask must be opaque so the composite removes the shadow entirely + // inside the hole; the shadow color's own alpha applies to the shadow, not here. + // The hole is drawn through a blur effect layer so the mask feathers before the + // DestOut composite; a zero blur passes through unchanged. + this.SaveCanvasLayer( + new GraphicsOptions { AlphaCompositionMode = PixelAlphaCompositionMode.DestOut }, + ToEnclosingRectangle(holeRect.Rect), + new WebGPUGaussianBlurLayerEffect(BlurRadiusToSigma(shadow.Blur)), + this.CreateDrawingOptions(IntersectionRule.EvenOdd), + "InnerBoxShadow hole"); + + this.canvas.Fill(Brushes.Solid(Color.Black), holePath); + this.RestoreCanvasState("InnerBoxShadow hole layer"); + } + + this.RestoreCanvasState("InnerBoxShadow layer"); + this.RestoreCanvasState("InnerBoxShadow intersection clip"); + } + + /// + /// Draws one non-inset shadow. + /// + private void DrawOuterBoxShadow(RoundedRect rect, IPath contentPath, BoxShadow shadow) + { + RoundedRect shadowRect = TranslateRoundedRect( + rect.Inflate(shadow.Spread, shadow.Spread), + shadow.OffsetX, + shadow.OffsetY); + + IPath shadowPath = shadowRect.IsRounded + ? shadowRect.ToRoundedRectanglePath() + : new RectanglePolygon(shadowRect.Rect.ToRectangleF()); + + if (shadow.Blur > 0) + { + // The shadow geometry fills a blur effect layer; the canvas blurs it when the layer is + // restored, and the layer's blend carries the ambient opacity. + this.PushDrawingState(IntersectionRule.EvenOdd); + this.canvas.Clip(ClipOperation.Difference, contentPath); + this.SaveCanvasLayer( + new GraphicsOptions { BlendPercentage = (float)this.opacity }, + ToEnclosingRectangle(shadowRect.Rect), + new WebGPUGaussianBlurLayerEffect(BlurRadiusToSigma(shadow.Blur)), + this.CreateDrawingOptions(IntersectionRule.EvenOdd), + "OuterBoxShadow blur"); + + this.canvas.Fill(Brushes.Solid(shadow.Color.ToColor()), shadowPath); + this.RestoreCanvasState("OuterBoxShadow blur layer"); + this.RestoreCanvasState("OuterBoxShadow difference clip"); + + return; + } + + this.FillPath(Brushes.Solid(shadow.Color.ToColor()), new ComplexPolygon(shadowPath, contentPath), IntersectionRule.EvenOdd, (float)this.opacity); + } + + /// + /// Converts an Avalonia blur radius to the sigma used by ImageSharp's Gaussian blur. + /// + private static float BlurRadiusToSigma(double radius) + => radius <= 0 ? 0 : (float)radius / 3F; + + /// + /// Converts a floating-point Avalonia rectangle to a pixel rectangle that fully contains it. + /// + private static Rectangle ToEnclosingRectangle(Rect rect) + { + int left = (int)Math.Floor(rect.Left); + int top = (int)Math.Floor(rect.Top); + int right = (int)Math.Ceiling(rect.Right); + int bottom = (int)Math.Ceiling(rect.Bottom); + + return new Rectangle(left, top, right - left, bottom - top); + } + + /// + /// Converts a floating-point ImageSharp rectangle to a pixel rectangle that fully contains it. + /// + private static Rectangle ToEnclosingRectangle(RectangleF rect) + { + int left = (int)Math.Floor(rect.Left); + int top = (int)Math.Floor(rect.Top); + int right = (int)Math.Ceiling(rect.Right); + int bottom = (int)Math.Ceiling(rect.Bottom); + + return new Rectangle(left, top, right - left, bottom - top); + } + + /// + /// Translates a rounded rectangle without changing its corner radii. + /// + private static RoundedRect TranslateRoundedRect(RoundedRect rect, double offsetX, double offsetY) + => new( + rect.Rect.Translate(new AvaloniaVector(offsetX, offsetY)), + rect.RadiiTopLeft, + rect.RadiiTopRight, + rect.RadiiBottomRight, + rect.RadiiBottomLeft); + + /// + /// Fills a path using the caller-supplied fill rule. + /// + private void FillPath(Brush brush, IPath path, IntersectionRule fillRule, float blendPercentage = 1) + { + this.Trace(() => $"FillPath fillRule={fillRule} pathBounds={Describe(path.Bounds)} transform={Describe(this.Transform)}"); + this.PushDrawingState(fillRule, blendPercentage); + this.canvas.Fill(brush, path); + this.RestoreCanvasState("FillPath state"); + } + + /// + /// Converts an Avalonia gradient brush into the matching ImageSharp gradient brush. + /// + private static Brush? CreateGradientBrush(IGradientBrush gradient, Rect targetRect) + { + ColorStop[] stops = ToColorStops(gradient.GradientStops); + GradientRepetitionMode repetitionMode = gradient.SpreadMethod.ToGradientRepetitionMode(); + ILinearGradientBrush? linear = gradient as ILinearGradientBrush; + + if (linear is not null) + { + PointF start = linear.StartPoint.ToPixels(targetRect).ToPointF(); + PointF end = linear.EndPoint.ToPixels(targetRect).ToPointF(); + + return new LinearGradientBrush(start, end, repetitionMode, stops); + } + + IRadialGradientBrush? radial = gradient as IRadialGradientBrush; + + if (radial is not null) + { + PointF center = radial.Center.ToPixels(targetRect).ToPointF(); + PointF origin = radial.GradientOrigin.ToPixels(targetRect).ToPointF(); + float radiusX = (float)radial.RadiusX.ToValue(targetRect.Width); + float radiusY = (float)radial.RadiusY.ToValue(targetRect.Height); + + if (origin == center && radiusX != radiusY) + { + return new EllipticGradientBrush( + center, + new PointF(center.X + radiusX, center.Y), + radiusY / radiusX, + repetitionMode, + stops); + } + + return origin == center + ? new RadialGradientBrush(center, radiusX, repetitionMode, stops) + : new RadialGradientBrush(origin, 0, center, radiusX, repetitionMode, stops); + } + + IConicGradientBrush? conic = gradient as IConicGradientBrush; + + if (conic is not null) + { + PointF center = conic.Center.ToPixels(targetRect).ToPointF(); + float startAngle = (float)(90 - conic.Angle); + + return new SweepGradientBrush(center, startAngle, startAngle + 360, repetitionMode, stops); + } + + return null; + } + + /// + /// Converts an Avalonia image brush into an ImageSharp tiled image brush. + /// + private ImageBrush? CreateImageBrush(IImageBrush brush, Rect targetRect) + { + IDrawableBitmapImpl? bitmap = brush.Source?.GetBitmap() as IDrawableBitmapImpl; + + if (bitmap is null) + { + return null; + } + + // Skia feeds TileBrushCalculator the bitmap size in the current drawing DPI, then draws + // from source pixels into that logical size. Keep the same split here: layout in Avalonia + // DIPs, source sampling through the bitmap implementation. + Avalonia.Size sourceSize = bitmap.PixelSize.ToSizeWithDpi(this.dpi); + return this.CreateTileBrush(bitmap, sourceSize, brush, targetRect); + } + + /// + /// Converts an Avalonia scene brush into an ImageSharp tiled image brush. + /// + private ImageBrush? CreateSceneBrushContent(Avalonia.Media.ISceneBrushContent content, Rect targetRect) + { + Rect contentRect = content.Rect; + int width = (int)Math.Ceiling(contentRect.Width); + int height = (int)Math.Ceiling(contentRect.Height); + + if (width < 1 || height < 1) + { + return null; + } + + Image sourceImage = new(WriteableBitmapImpl.ImageConfiguration, width, height); + + using (DrawingContextImpl context = new(sourceImage, this.dpi)) + { + // Scene brushes render from their own content bounds, not from the bounds of the + // visual being painted: + // + // +----------------------+ target visual + // | | + // | +-----+----+ | content bounds + // | | | | | + // | +-----+ | | + // | +--+ | | + // | +--+ | | + // +----------------------+ + // + // Translate non-zero content bounds back to the intermediate origin, matching Skia's + // VisualBrush path before the generated image is tiled over the target box. + Matrix? transform = contentRect.TopLeft == default + ? null + : Matrix.CreateTranslation(-contentRect.X, -contentRect.Y); + + context.PushRenderOptions(this.currentRenderOptions); + context.Clear(Avalonia.Media.Colors.Transparent); + content.Render(context, transform); + context.PopRenderOptions(); + } + + using WriteableBitmapImpl sourceBitmap = new(sourceImage, this.dpi); + + return this.CreateTileBrush(sourceBitmap, contentRect.Size, content.Brush, targetRect); + } + + /// + /// Converts a source bitmap and Avalonia tile brush settings into an ImageSharp tiled image brush. + /// + private ImageBrush? CreateTileBrush( + IDrawableBitmapImpl sourceBitmap, + Avalonia.Size sourceSize, + ITileBrush brush, + Rect targetRect) + { + TileBrushLayout layout = CalculateTileBrushLayout(brush, sourceSize, targetRect); + + if (!layout.IsValid) + { + return null; + } + + Image tileImage = new(WriteableBitmapImpl.ImageConfiguration, layout.IntermediateWidth, layout.IntermediateHeight); + + using (DrawingContextImpl context = new(tileImage, this.dpi)) + { + context.Clear(Avalonia.Media.Colors.Transparent); + context.PushClip(layout.IntermediateClip); + context.PushRenderOptions(this.currentRenderOptions); + context.Transform = layout.IntermediateTransform; + context.DrawBitmap(sourceBitmap, 1, new Rect(sourceBitmap.PixelSize.ToSize(1)), new Rect(sourceSize)); + context.PopRenderOptions(); + context.PopClip(); + } + + this.brushImages.Add(tileImage); + + (WrapMode wrapX, WrapMode wrapY) = ToWrapModes(brush.TileMode); + return new ImageBrush(tileImage, tileImage.Bounds, layout.Offset, wrapX, wrapY); + } + + /// + /// Maps Avalonia tile modes to ImageSharp wrap modes. + /// + private static (WrapMode X, WrapMode Y) ToWrapModes(TileMode tileMode) + => tileMode switch + { + TileMode.Tile => (WrapMode.Repeat, WrapMode.Repeat), + TileMode.FlipX => (WrapMode.Mirror, WrapMode.Repeat), + TileMode.FlipY => (WrapMode.Repeat, WrapMode.Mirror), + TileMode.FlipXY => (WrapMode.Mirror, WrapMode.Mirror), + _ => (WrapMode.None, WrapMode.None), + }; + + /// + /// Converts Avalonia gradient stops to ImageSharp color stops. + /// + private static ColorStop[] ToColorStops(IReadOnlyList gradientStops) + { + ColorStop[] stops = new ColorStop[gradientStops.Count]; + + for (int i = 0; i < gradientStops.Count; i++) + { + IGradientStop stop = gradientStops[i]; + stops[i] = new ColorStop((float)stop.Offset, stop.Color.ToColor()); + } + + return stops; + } + + /// + /// Calculates the ImageSharp tile image that represents Avalonia's tile brush viewbox/viewport rules. + /// + private static TileBrushLayout CalculateTileBrushLayout(ITileBrush brush, Avalonia.Size sourceSize, Rect targetRect) + { + Rect sourceRect = brush.SourceRect.ToPixels(sourceSize); + Avalonia.Size targetSize = targetRect.Size; + Rect destinationRect = brush.DestinationRect.ToPixels(targetSize); + + if (sourceRect.Width <= 0 || sourceRect.Height <= 0 || destinationRect.Width <= 0 || destinationRect.Height <= 0) + { + return default; + } + + AvaloniaVector scale = Avalonia.Media.MediaExtensions.CalculateScaling( + brush.Stretch, + destinationRect.Size, + sourceRect.Size); + + Avalonia.Size scaledSize = new(sourceRect.Width * scale.X, sourceRect.Height * scale.Y); + AvaloniaVector translate = CalculateTileTranslate( + brush.AlignmentX, + brush.AlignmentY, + scaledSize, + destinationRect.Size); + + bool isTiled = brush.TileMode != TileMode.None; + Avalonia.Size intermediateSize = isTiled ? destinationRect.Size : targetSize; + Matrix intermediateTransform = + Matrix.CreateTranslation(-sourceRect.Position) * + Matrix.CreateScale(scale) * + Matrix.CreateTranslation(translate); + + Rect intermediateClip; + + if (isTiled) + { + intermediateClip = new Rect(destinationRect.Size); + } + else + { + intermediateClip = destinationRect; + intermediateTransform *= Matrix.CreateTranslation(destinationRect.Position); + } + + int intermediateWidth = Math.Max(1, (int)Math.Ceiling(intermediateSize.Width)); + int intermediateHeight = Math.Max(1, (int)Math.Ceiling(intermediateSize.Height)); + + // Relative viewports are anchored to the painted target. Absolute viewports are anchored + // in drawing coordinates, so subtract the target origin before ImageSharp adds it back + // when creating the brush renderer for the painted shape. + double offsetX = isTiled ? destinationRect.X : 0; + double offsetY = isTiled ? destinationRect.Y : 0; + + if (brush.DestinationRect.Unit == RelativeUnit.Absolute) + { + offsetX -= targetRect.X; + offsetY -= targetRect.Y; + } + + SixLabors.ImageSharp.Point offset = new((int)Math.Floor(offsetX), (int)Math.Floor(offsetY)); + return new TileBrushLayout(intermediateClip, intermediateTransform, intermediateWidth, intermediateHeight, offset); + } + + /// + /// Calculates Avalonia tile alignment translation for the scaled source size. + /// + private static AvaloniaVector CalculateTileTranslate(AlignmentX alignmentX, AlignmentY alignmentY, Avalonia.Size sourceSize, Avalonia.Size destinationSize) + { + double x = alignmentX switch + { + AlignmentX.Center => (destinationSize.Width - sourceSize.Width) / 2, + AlignmentX.Right => destinationSize.Width - sourceSize.Width, + _ => 0 + }; + + double y = alignmentY switch + { + AlignmentY.Center => (destinationSize.Height - sourceSize.Height) / 2, + AlignmentY.Bottom => destinationSize.Height - sourceSize.Height, + _ => 0 + }; + + return new AvaloniaVector(x, y); + } + + /// + /// Holds the baked tile image placement required by an Avalonia tile brush. + /// + private readonly struct TileBrushLayout + { + /// + /// Initializes a new instance of the struct. + /// + /// The tile-space clip applied while drawing the source image. + /// The tile-space transform applied while drawing the source image. + /// The baked tile image width. + /// The baked tile image height. + /// The offset that anchors the baked tile in drawing coordinates. + public TileBrushLayout( + Rect intermediateClip, + Matrix intermediateTransform, + int intermediateWidth, + int intermediateHeight, + SixLabors.ImageSharp.Point offset) + { + this.IntermediateClip = intermediateClip; + this.IntermediateTransform = intermediateTransform; + this.IntermediateWidth = intermediateWidth; + this.IntermediateHeight = intermediateHeight; + this.Offset = offset; + this.IsValid = true; + } + + /// + /// Gets a value indicating whether the layout contains drawable tile geometry. + /// + public bool IsValid { get; } + + /// + /// Gets the tile-space clip applied while drawing the source image. + /// + public Rect IntermediateClip { get; } + + /// + /// Gets the tile-space transform applied while drawing the source image. + /// + public Matrix IntermediateTransform { get; } + + /// + /// Gets the baked tile image width. + /// + public int IntermediateWidth { get; } + + /// + /// Gets the baked tile image height. + /// + public int IntermediateHeight { get; } + + /// + /// Gets the offset that anchors the baked tile in drawing coordinates. + /// + public SixLabors.ImageSharp.Point Offset { get; } + } + + /// + /// Writes a trace entry when tracing is enabled for this context. + /// + private void Trace(Func message) + { + if (this.trace is null) + { + return; + } + + this.trace.Append(this.traceOperationIndex++).Append(": ").AppendLine(message()); + } + + /// + /// Writes the current sample and canvas state stack depths to the active diagnostic trace. + /// + private void TraceState(string action) + { + if (this.trace is null) + { + return; + } + + this.trace + .Append(this.traceOperationIndex++) + .Append(": ") + .Append(action) + .Append(" canvasDepth=") + .Append(this.canvasStateDepth) + .Append(" transformDepth=") + .Append(this.transforms.Count) + .Append(" opacityDepth=") + .Append(this.opacities.Count) + .Append(" opacityLayerDepth=") + .Append(this.opacityLayers.Count) + .Append(" opacityMaskDepth=") + .Append(this.opacityMasks.Count) + .Append(" renderOptionsDepth=") + .Append(this.renderOptions.Count) + .Append(" textOptionsDepth=") + .Append(this.textOptions.Count) + .Append(" opacity=") + .Append(this.opacity.ToString("0.###")) + .Append(" transform=") + .AppendLine(Describe(this.Transform)); + } + + /// + /// Determines whether the next drawing context should capture operation-level diagnostics. + /// + private static bool ShouldTraceContext(bool ownsImage, int width, int height) + { + if (DebugTraceNextFrame || DebugSaveNextFrame) + { + DebugTraceNextFrame = false; + return true; + } + + if (ownsImage && Environment.GetEnvironmentVariable("IMAGESHARP_DRAWING_TRACE_FRAMEBUFFER_CONTEXTS") == "1") + { + return true; + } + + if (width >= 256 && height >= 256 && Environment.GetEnvironmentVariable("IMAGESHARP_DRAWING_TRACE_LARGE_CONTEXTS") == "1") + { + return true; + } + + if (Environment.GetEnvironmentVariable("IMAGESHARP_DRAWING_TRACE_ALL_CONTEXTS") == "1") + { + return true; + } + + if (!environmentTraceConsumed && Environment.GetEnvironmentVariable("IMAGESHARP_DRAWING_TRACE_NEXT_FRAME") == "1") + { + environmentTraceConsumed = true; + return true; + } + + return false; + } + + /// + /// Determines whether every drawing context should save its rendered image. + /// + private static bool ShouldSaveContextImage() + => Environment.GetEnvironmentVariable("IMAGESHARP_DRAWING_SAVE_ALL_CONTEXTS") == "1"; + + /// + /// Gets the directory used for on-demand image and trace captures. + /// + private static string GetCaptureDirectory() + { + string? directory = Environment.GetEnvironmentVariable("IMAGESHARP_DRAWING_CAPTURE_DIRECTORY"); + + if (string.IsNullOrWhiteSpace(directory)) + { + return System.IO.Path.GetTempPath(); + } + + Directory.CreateDirectory(directory); + + return directory; + } + + /// + /// Formats an Avalonia color for diagnostic traces. + /// + private static string Describe(Avalonia.Media.Color color) + => $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}"; + + /// + /// Formats an Avalonia rectangle for diagnostic traces. + /// + private static string Describe(Rect rect) + => $"({rect.X:0.###},{rect.Y:0.###},{rect.Width:0.###},{rect.Height:0.###})"; + + /// + /// Formats an ImageSharp rectangle for diagnostic traces. + /// + private static string Describe(RectangleF rect) + => $"({rect.X:0.###},{rect.Y:0.###},{rect.Width:0.###},{rect.Height:0.###})"; + + /// + /// Formats an ImageSharp pixel rectangle for diagnostic traces. + /// + private static string Describe(Rectangle rect) + => $"({rect.X},{rect.Y},{rect.Width},{rect.Height})"; + + /// + /// Formats a pixel rectangle for diagnostic traces. + /// + private static string Describe(LtrbPixelRect rect) + => $"({rect.Left},{rect.Top},{rect.Right},{rect.Bottom})"; + + /// + /// Formats a list of pixel rectangles for diagnostic traces. + /// + private static string Describe(IList rects) + { + StringBuilder builder = new(); + builder.Append(rects.Count).Append('['); + + for (int i = 0; i < rects.Count; i++) + { + if (i > 0) + { + builder.Append(','); + } + + builder.Append(Describe(rects[i])); + } + + return builder.Append(']').ToString(); + } + + /// + /// Formats an Avalonia transform matrix for diagnostic traces. + /// + private static string Describe(Matrix matrix) + => $"[{matrix.M11:0.###},{matrix.M12:0.###},{matrix.M21:0.###},{matrix.M22:0.###},{matrix.M31:0.###},{matrix.M32:0.###}]"; + + /// + /// Formats an Avalonia brush for diagnostic traces. + /// + private static string DescribeBrush(IBrush? brush) + { + if (brush is null) + { + return ""; + } + + if (brush is ISolidColorBrush solid) + { + return $"Solid({Describe(solid.Color)}, opacity={brush.Opacity:0.###})"; + } + + return $"{brush.GetType().Name}(opacity={brush.Opacity:0.###})"; + } + + /// + /// Formats an Avalonia pen for diagnostic traces. + /// + private static string DescribePen(IPen? pen) + { + if (pen is null) + { + return ""; + } + + return $"Pen(thickness={pen.Thickness:0.###}, brush={DescribeBrush(pen.Brush)})"; + } +} diff --git a/samples/AvaloniaControlCatalog/EllipseGeometryImpl.cs b/samples/AvaloniaControlCatalog/EllipseGeometryImpl.cs new file mode 100644 index 00000000..fd459953 --- /dev/null +++ b/samples/AvaloniaControlCatalog/EllipseGeometryImpl.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Avalonia; +using SixLabors.ImageSharp.Drawing; + +namespace AvaloniaControlCatalog; + +/// +/// Avalonia ellipse geometry implementation backed by an ImageSharp ellipse path. +/// +internal sealed class EllipseGeometryImpl : GeometryImpl +{ + /// + /// Initializes a new ellipse geometry. + /// + /// The ellipse bounds. + public EllipseGeometryImpl(Rect rect) + : this(CreatePath(rect)) + { + } + + private EllipseGeometryImpl(IPath path) + : base(path, path, IntersectionRule.EvenOdd) + { + } + + private static IPath CreatePath(Rect rect) + => rect.Width <= 0 || rect.Height <= 0 + ? EmptyPath.ClosedPath + : new EllipsePolygon(rect.Center.ToPointF(), rect.Size.ToSizeF()); +} diff --git a/samples/AvaloniaControlCatalog/FontManagerImpl.cs b/samples/AvaloniaControlCatalog/FontManagerImpl.cs new file mode 100644 index 00000000..72fcb9ff --- /dev/null +++ b/samples/AvaloniaControlCatalog/FontManagerImpl.cs @@ -0,0 +1,297 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using Avalonia.Media; +using Avalonia.Media.Fonts; +using Avalonia.Platform; +using SixLabors.Fonts.Unicode; +using SixLaborsTag = SixLabors.Fonts.Tables.AdvancedTypographic.Tag; +using SixLaborsColorFontSupport = SixLabors.Fonts.ColorFontSupport; +using SixLaborsFontCollection = SixLabors.Fonts.FontCollection; +using SixLaborsFontDescription = SixLabors.Fonts.FontDescription; +using SixLaborsFontFamily = SixLabors.Fonts.FontFamily; +using SixLaborsFontMatch = SixLabors.Fonts.FontMatch; +using SixLaborsFontMetrics = SixLabors.Fonts.FontMetrics; +using SixLaborsFontStyle = SixLabors.Fonts.FontStyle; +using SixLaborsSystemFonts = SixLabors.Fonts.SystemFonts; + +namespace AvaloniaControlCatalog; + +/// +/// Avalonia font manager implementation backed by SixLabors.Fonts. +/// +internal sealed class FontManagerImpl : IFontManagerImpl +{ + private const SixLaborsColorFontSupport FallbackColorFontSupport = + SixLaborsColorFontSupport.ColrV0 | SixLaborsColorFontSupport.ColrV1 | SixLaborsColorFontSupport.Svg; + + /// + /// Initializes a new instance of the class. + /// + public FontManagerImpl() + { + } + + /// + public string GetDefaultFontFamilyName() + => SixLaborsSystemFonts.GetDefaultFamilyName(); + + /// + public string[] GetInstalledFontFamilyNames(bool checkForUpdates = false) + => SixLaborsSystemFonts.GetFamilyNames(checkForUpdates); + + /// + public bool TryMatchCharacter(int codepoint, FontStyle style, FontWeight weight, FontStretch stretch, string? familyName, System.Globalization.CultureInfo? culture, [NotNullWhen(true)] out IPlatformTypeface? platformTypeface) + { + CodePoint codePoint = new(codepoint); + SixLaborsFontStyle imageSharpStyle = ToImageSharpStyle(style, weight); + + if (SixLaborsSystemFonts.TryMatchCharacter(codePoint, imageSharpStyle, familyName, culture, out SixLaborsFontMatch match) + && this.TryCreatePlatformTypeface(match, out platformTypeface)) + { + return true; + } + + platformTypeface = null; + return false; + } + + /// + public bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight, FontStretch stretch, [NotNullWhen(true)] out IPlatformTypeface? platformTypeface) + { + if (!SixLaborsSystemFonts.TryGet(familyName, out SixLaborsFontFamily family)) + { + platformTypeface = null; + return false; + } + + SixLaborsFontStyle requestedStyle = ToImageSharpStyle(style, weight); + SixLaborsFontStyle resolvedStyle = requestedStyle; + + if (!family.TryGetMetrics(resolvedStyle, out _)) + { + resolvedStyle = family.TryGetMetrics(SixLaborsFontStyle.Regular, out _) + ? SixLaborsFontStyle.Regular + : family.GetAvailableStyles().Span[0]; + } + + FontSimulations fontSimulations = FontSimulations.None; + + // Avalonia records only the styles missing from the resolved face. SixLabors.Fonts then + // synthesizes those styles while retaining the metrics and tables of that resolved face. + if ((requestedStyle & SixLaborsFontStyle.Bold) != 0 && (resolvedStyle & SixLaborsFontStyle.Bold) == 0) + { + fontSimulations |= FontSimulations.Bold; + } + + if ((requestedStyle & SixLaborsFontStyle.Italic) != 0 && (resolvedStyle & SixLaborsFontStyle.Italic) == 0) + { + fontSimulations |= FontSimulations.Oblique; + } + + return this.TryCreatePlatformTypeface(family, resolvedStyle, fontSimulations, out platformTypeface); + } + + /// + public bool TryCreateGlyphTypeface(Stream stream, FontSimulations fontSimulations, [NotNullWhen(true)] out IPlatformTypeface? platformTypeface) + { + try + { + SixLaborsFontCollection collection = new(); + SixLaborsFontDescription description; + SixLaborsFontFamily family = collection.Add(stream, out description); + family.TryGetMetrics(description.Style, out SixLaborsFontMetrics? metrics); + + platformTypeface = new PlatformTypeface( + description.FontFamilyInvariantCulture, + ToAvaloniaStyle(description.Style), + ToAvaloniaWeight(description.Style), + FontStretch.Normal, + metrics!, + family, + description.Style, + fontSimulations); + + return true; + } + catch + { + platformTypeface = null; + return false; + } + } + + /// + public bool TryGetFamilyTypefaces(string familyName, [NotNullWhen(true)] out IReadOnlyList? familyTypefaces) + { + if (!SixLaborsSystemFonts.TryGet(familyName, out SixLaborsFontFamily family)) + { + familyTypefaces = null; + return false; + } + + ReadOnlySpan styles = family.GetAvailableStyles().Span; + Typeface[] typefaces = new Typeface[styles.Length]; + + for (int i = 0; i < styles.Length; i++) + { + SixLaborsFontStyle faceStyle = styles[i]; + typefaces[i] = new Typeface(familyName, ToAvaloniaStyle(faceStyle), ToAvaloniaWeight(faceStyle), FontStretch.Normal); + } + + familyTypefaces = typefaces; + return true; + } + + private static FontStyle ToAvaloniaStyle(SixLaborsFontStyle style) + => (style & SixLaborsFontStyle.Italic) == SixLaborsFontStyle.Italic ? FontStyle.Italic : FontStyle.Normal; + + private static FontWeight ToAvaloniaWeight(SixLaborsFontStyle style) + => (style & SixLaborsFontStyle.Bold) == SixLaborsFontStyle.Bold ? FontWeight.Bold : FontWeight.Normal; + + private static SixLaborsFontStyle ToImageSharpStyle(FontStyle style, FontWeight weight) + { + SixLaborsFontStyle result = (int)weight >= 600 ? SixLaborsFontStyle.Bold : SixLaborsFontStyle.Regular; + + if (style == FontStyle.Italic || style == FontStyle.Oblique) + { + result |= SixLaborsFontStyle.Italic; + } + + return result; + } + + private bool TryCreatePlatformTypeface(SixLaborsFontMatch match, [NotNullWhen(true)] out IPlatformTypeface? platformTypeface) + => this.TryCreatePlatformTypeface(match.Family, match.Style, FontSimulations.None, out platformTypeface); + + private bool TryCreatePlatformTypeface( + SixLaborsFontFamily family, + SixLaborsFontStyle style, + FontSimulations fontSimulations, + [NotNullWhen(true)] out IPlatformTypeface? platformTypeface) + { + if (!family.TryGetMetrics(style, out SixLabors.Fonts.FontMetrics? metrics)) + { + platformTypeface = null; + return false; + } + + platformTypeface = new PlatformTypeface( + metrics.Description.FontFamilyInvariantCulture, + ToAvaloniaStyle(metrics.Description.Style), + ToAvaloniaWeight(metrics.Description.Style), + FontStretch.Normal, + metrics, + family, + metrics.Description.Style, + fontSimulations); + + return true; + } +} + +/// +/// Avalonia platform typeface wrapper for a SixLabors.Fonts face. +/// +internal sealed class PlatformTypeface : IPlatformTypeface +{ + private readonly SixLaborsFontMetrics fontMetrics; + private readonly SixLaborsFontFamily fontFamily; + private readonly SixLaborsFontStyle fontStyle; + + /// + /// Initializes a new platform typeface wrapper. + /// + /// The Avalonia family name. + /// The Avalonia font style. + /// The Avalonia font weight. + /// The Avalonia font stretch. + /// The SixLabors font metrics. + /// The SixLabors font family. + /// The SixLabors font style. + /// The requested font simulations. + public PlatformTypeface( + string familyName, + FontStyle style, + FontWeight weight, + FontStretch stretch, + SixLaborsFontMetrics fontMetrics, + SixLaborsFontFamily fontFamily, + SixLaborsFontStyle fontStyle, + FontSimulations fontSimulations = FontSimulations.None) + { + this.FamilyName = familyName; + this.Style = style; + this.Weight = weight; + this.Stretch = stretch; + this.FontSimulations = fontSimulations; + this.fontMetrics = fontMetrics; + this.fontStyle = fontStyle; + this.fontFamily = fontFamily; + } + + /// + public string FamilyName { get; } + + /// + public FontWeight Weight { get; } + + /// + public FontStyle Style { get; } + + /// + public FontStretch Stretch { get; } + + /// + public FontSimulations FontSimulations { get; } + + /// + /// Creates a SixLabors font for the exact face this typeface wraps, at the given size. + /// + /// The requested font size. + /// The created SixLabors font. + public SixLabors.Fonts.Font CreateFont(float size) + { + SixLaborsFontStyle requestedStyle = this.fontStyle; + + // Avalonia resolves a real face first and records any missing bold or oblique style as a + // simulation. SixLabors.Fonts performs those simulations from the requested style while + // retaining the resolved face metrics, so include the simulation flags in that request. + if ((this.FontSimulations & FontSimulations.Bold) != 0) + { + requestedStyle |= SixLaborsFontStyle.Bold; + } + + if ((this.FontSimulations & FontSimulations.Oblique) != 0) + { + requestedStyle |= SixLaborsFontStyle.Italic; + } + + return this.fontFamily.CreateFont(size, requestedStyle); + } + + /// + public bool TryGetStream([NotNullWhen(true)] out Stream? stream) + { + try + { + stream = this.fontMetrics.OpenStream(); + return true; + } + catch + { + stream = null; + return false; + } + } + + /// + public bool TryGetTable(OpenTypeTag tag, out ReadOnlyMemory table) + => this.fontMetrics.TryGetTableData(new SixLaborsTag((uint)tag), out table); + + /// + public void Dispose() + { + } +} diff --git a/samples/AvaloniaControlCatalog/GeometryGroupImpl.cs b/samples/AvaloniaControlCatalog/GeometryGroupImpl.cs new file mode 100644 index 00000000..4e38600a --- /dev/null +++ b/samples/AvaloniaControlCatalog/GeometryGroupImpl.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Avalonia.Media; +using Avalonia.Platform; +using SixLabors.ImageSharp.Drawing; + +namespace AvaloniaControlCatalog; + +/// +/// Avalonia geometry group implementation backed by combined ImageSharp paths. +/// +internal sealed class GeometryGroupImpl : GeometryImpl +{ + /// + /// Initializes a new geometry group. + /// + /// The fill rule for the group fill path. + /// The child geometries in the group. + public GeometryGroupImpl(FillRule fillRule, IReadOnlyList children) + : this(CreatePaths(fillRule, children)) + { + } + + private GeometryGroupImpl((IPath StrokePath, IPath? FillPath, IntersectionRule FillRule) paths) + : base(paths.StrokePath, paths.FillPath, paths.FillRule) + { + } + + private static (IPath StrokePath, IPath? FillPath, IntersectionRule FillRule) CreatePaths(FillRule fillRule, IReadOnlyList children) + { + List strokePaths = new(children.Count); + List fillPaths = new(children.Count); + + for (int i = 0; i < children.Count; i++) + { + GeometryImpl geometry = (GeometryImpl)children[i]; + strokePaths.Add(geometry.StrokePath); + + if (geometry.FillPath is not null) + { + fillPaths.Add(geometry.FillPath); + } + } + + return ( + new ComplexPolygon(strokePaths), + fillPaths.Count == 0 ? null : new ComplexPolygon(fillPaths), + fillRule.ToIntersectionRule()); + } +} diff --git a/samples/AvaloniaControlCatalog/GeometryImpl.cs b/samples/AvaloniaControlCatalog/GeometryImpl.cs new file mode 100644 index 00000000..0fd12e9f --- /dev/null +++ b/samples/AvaloniaControlCatalog/GeometryImpl.cs @@ -0,0 +1,280 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using Avalonia; +using Avalonia.Media; +using Avalonia.Platform; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Processing; + +namespace AvaloniaControlCatalog; + +/// +/// Base Avalonia geometry implementation backed by ImageSharp paths. +/// +internal abstract class GeometryImpl : IGeometryImpl +{ + /// + /// Initializes a new geometry from ImageSharp stroke and fill paths. + /// + /// The path used for stroke operations. + /// The path used for fill operations. + /// The fill rule used for the fill path. + protected GeometryImpl(IPath strokePath, IPath? fillPath, IntersectionRule fillRule) + => this.SetPaths(strokePath, fillPath, fillRule); + + /// + /// Gets the stroke path used as the primary geometry path. + /// + public IPath Path => this.StrokePath; + + /// + /// Gets the path used for stroke operations. + /// + public IPath StrokePath { get; private set; } = EmptyPath.OpenPath; + + /// + /// Gets the path used for fill operations. + /// + public IPath? FillPath { get; private set; } = EmptyPath.OpenPath; + + /// + /// Gets the fill rule used for the fill path. + /// + public IntersectionRule FillRule { get; private set; } + + /// + public Rect Bounds { get; private set; } + + /// + public double ContourLength { get; private set; } + + private LinearGeometry strokeGeometry = EmptyPath.OpenPath.ToLinearGeometry(Vector2.One); + + private LinearGeometry? fillGeometry = EmptyPath.OpenPath.ToLinearGeometry(Vector2.One); + + private StrokeCache strokeCache; + + /// + /// Replaces the stroke and fill path with the same path. + /// + /// The replacement path. + protected void SetPath(IPath path) + => this.SetPaths(path, path, this.FillRule); + + /// + /// Replaces the geometry paths and cached path data. + /// + /// The path used for stroke operations. + /// The path used for fill operations. + /// The fill rule used for . + protected void SetPaths(IPath strokePath, IPath? fillPath, IntersectionRule fillRule) + { + this.StrokePath = strokePath; + this.FillPath = fillPath; + this.FillRule = fillRule; + this.strokeGeometry = strokePath.ToLinearGeometry(Vector2.One); + this.fillGeometry = fillPath is null + ? null + : ReferenceEquals(fillPath, strokePath) ? this.strokeGeometry : fillPath.ToLinearGeometry(Vector2.One); + this.Bounds = this.strokeGeometry.Info.Bounds.ToAvaloniaRect(); + this.ContourLength = this.strokeGeometry.ComputeLength(); + this.strokeCache = default; + } + + /// + public Rect GetRenderBounds(IPen? pen) + { + Rect bounds = this.Bounds; + if (this.fillGeometry is not null) + { + bounds = bounds.Union(this.fillGeometry.Info.Bounds.ToAvaloniaRect()); + } + + if (pen is not null) + { + LinearGeometry geometry = this.GetStrokeGeometry(pen); + bounds = bounds.Union(geometry.Info.Bounds.ToAvaloniaRect()); + } + + return bounds; + } + + /// + public IGeometryImpl GetWidenedGeometry(IPen pen) + { + IPath path = this.StrokePath.CreateStrokePath(pen); + + return new StreamGeometryImpl(path, path, this.FillRule); + } + + /// + public bool FillContains(AvaloniaPoint point) + => this.fillGeometry is not null && this.fillGeometry.Contains(point.ToPointF(), this.FillRule); + + /// + public IGeometryImpl? Intersect(IGeometryImpl geometry) + => CombinedGeometryImpl.Create(GeometryCombineMode.Intersect, this, (GeometryImpl)geometry); + + /// + public bool StrokeContains(IPen? pen, AvaloniaPoint point) + => pen is not null && this.GetStrokeGeometry(pen).Contains(point.ToPointF(), IntersectionRule.NonZero); + + /// + public ITransformedGeometryImpl WithTransform(Matrix transform) + => new TransformedGeometryImpl(this, transform); + + /// + public bool TryGetPointAtDistance(double distance, out AvaloniaPoint point) + { + if (!this.TryGetPointAndTangentAtDistance(distance, out point, out _)) + { + point = default; + return false; + } + + return true; + } + + /// + public bool TryGetPointAndTangentAtDistance(double distance, out AvaloniaPoint point, out AvaloniaPoint tangent) + { + if (this.ContourLength <= 0) + { + point = default; + tangent = default; + return false; + } + + if (!this.StrokePath.TryGetPathPointAtDistance((float)distance, Vector2.One, out PathPoint segment)) + { + point = default; + tangent = default; + return false; + } + + point = new AvaloniaPoint(segment.Point.X, segment.Point.Y); + tangent = new AvaloniaPoint(segment.Tangent.X, segment.Tangent.Y); + return true; + } + + /// + public bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, [NotNullWhen(true)] out IGeometryImpl? segmentGeometry) + { + if (stopDistance <= startDistance || this.ContourLength <= 0) + { + segmentGeometry = null; + return false; + } + + if (!this.strokeGeometry.TryGetSegment((float)startDistance, (float)stopDistance, startOnBeginFigure, out IPath path)) + { + segmentGeometry = null; + return false; + } + + segmentGeometry = new StreamGeometryImpl(path, null, this.FillRule); + return true; + } + + private LinearGeometry GetStrokeGeometry(IPen pen) + { + LinearGeometry? cachedGeometry = this.strokeCache.Geometry; + if (cachedGeometry is not null && this.strokeCache.Matches(pen)) + { + return cachedGeometry; + } + + LinearGeometry geometry = this.StrokePath.CreateStrokePath(pen).ToLinearGeometry(Vector2.One); + this.strokeCache = new StrokeCache(pen, geometry); + + return geometry; + } + + private struct StrokeCache + { + private double thickness; + private PenLineCap lineCap; + private PenLineJoin lineJoin; + private double miterLimit; + private double dashOffset; + private double[]? dashes; + + /// + /// Initializes cached widened-stroke geometry for a pen. + /// + /// The pen used to create the widened stroke. + /// The widened stroke geometry. + public StrokeCache(IPen pen, LinearGeometry geometry) + { + this.thickness = pen.Thickness; + this.lineCap = pen.LineCap; + this.lineJoin = pen.LineJoin; + this.miterLimit = pen.MiterLimit; + this.dashOffset = pen.DashStyle?.Offset ?? 0; + this.dashes = CopyDashes(pen.DashStyle?.Dashes); + this.Geometry = geometry; + } + + /// + /// Gets the cached widened stroke geometry. + /// + public LinearGeometry? Geometry { get; } + + /// + /// Returns whether the cached geometry was created for the supplied pen. + /// + /// The pen to compare. + /// when the pen matches the cache key. + public bool Matches(IPen pen) + => this.thickness == pen.Thickness && + this.lineCap == pen.LineCap && + this.lineJoin == pen.LineJoin && + this.miterLimit == pen.MiterLimit && + this.dashOffset == (pen.DashStyle?.Offset ?? 0) && + DashesEqual(this.dashes, pen.DashStyle?.Dashes); + + private static double[]? CopyDashes(IReadOnlyList? source) + { + if (source is null || source.Count == 0) + { + return null; + } + + double[] result = new double[source.Count]; + for (int i = 0; i < result.Length; i++) + { + result[i] = source[i]; + } + + return result; + } + + private static bool DashesEqual(double[]? left, IReadOnlyList? right) + { + if (left is null || left.Length == 0) + { + return right is null || right.Count == 0; + } + + if (right is null || left.Length != right.Count) + { + return false; + } + + for (int i = 0; i < left.Length; i++) + { + if (left[i] != right[i]) + { + return false; + } + } + + return true; + } + } + +} diff --git a/samples/AvaloniaControlCatalog/GlobalUsings.cs b/samples/AvaloniaControlCatalog/GlobalUsings.cs new file mode 100644 index 00000000..4493c042 --- /dev/null +++ b/samples/AvaloniaControlCatalog/GlobalUsings.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +// Avalonia type aliases shared across the ImageSharp.Drawing rendering backend so the ImageSharp +// types (Brush, Pen, Color, etc.) can be used bare while Avalonia's same-named types stay explicit. +global using AvaloniaColor = Avalonia.Media.Color; +global using AvaloniaMatrix = Avalonia.Matrix; +global using AvaloniaPoint = Avalonia.Point; +global using AvaloniaRect = Avalonia.Rect; +global using AvaloniaRoundedRect = Avalonia.RoundedRect; +global using AvaloniaSize = Avalonia.Size; +global using AvaloniaVector = Avalonia.Vector; +global using AlignmentX = Avalonia.Media.AlignmentX; +global using AlignmentY = Avalonia.Media.AlignmentY; +global using BoxShadows = Avalonia.Media.BoxShadows; +global using FillRule = Avalonia.Media.FillRule; +global using GeometryCombineMode = Avalonia.Media.GeometryCombineMode; +global using GradientSpreadMethod = Avalonia.Media.GradientSpreadMethod; +global using IBlurEffect = Avalonia.Media.IBlurEffect; +global using IBrush = Avalonia.Media.IBrush; +global using IConicGradientBrush = Avalonia.Media.IConicGradientBrush; +global using IDropShadowEffect = Avalonia.Media.IDropShadowEffect; +global using IEffect = Avalonia.Media.IEffect; +global using IGradientBrush = Avalonia.Media.IGradientBrush; +global using IGradientStop = Avalonia.Media.IGradientStop; +global using IImageBrush = Avalonia.Media.IImageBrush; +global using ILinearGradientBrush = Avalonia.Media.ILinearGradientBrush; +global using IPen = Avalonia.Media.IPen; +global using IRadialGradientBrush = Avalonia.Media.IRadialGradientBrush; +global using ISolidColorBrush = Avalonia.Media.ISolidColorBrush; +global using PenLineCap = Avalonia.Media.PenLineCap; +global using PenLineJoin = Avalonia.Media.PenLineJoin; +global using ITileBrush = Avalonia.Media.ITileBrush; +global using RenderOptions = Avalonia.Media.RenderOptions; +global using TileMode = Avalonia.Media.TileMode; diff --git a/samples/AvaloniaControlCatalog/GlyphRunImpl.cs b/samples/AvaloniaControlCatalog/GlyphRunImpl.cs new file mode 100644 index 00000000..124bb2f7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/GlyphRunImpl.cs @@ -0,0 +1,243 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using Avalonia; +using Avalonia.Media; +using Avalonia.Media.TextFormatting; +using Avalonia.Platform; +using SixLabors.Fonts; +using SixLabors.ImageSharp.Drawing.Processing; +using AvaloniaTextHintingMode = Avalonia.Media.TextHintingMode; +using AvaloniaTextOptions = Avalonia.Media.TextOptions; + +namespace AvaloniaControlCatalog; + +/// +/// Avalonia glyph run implementation backed by SixLabors.Fonts glyph ids and positions. +/// +internal sealed class GlyphRunImpl : IGlyphRunImpl +{ + /// + /// Positioned per-glyph metrics measured once at construction. Bounds derive from the + /// entries and scans them for decoration skipping. + /// + private readonly ReadOnlyMemory glyphMetrics; + + /// + /// Memoized result. Avalonia retains glyph runs and redraws + /// decorations with the same limits every paint, so one slot covers the realistic pattern. + /// Reference assignment keeps the read-check-publish sequence safe without locking. + /// + private IntersectionsCache? intersectionsCache; + + /// + /// The hinting mode the run was most recently drawn with; intersections are computed from + /// the same hinted geometry so decoration gaps align with the rendered outlines. + /// + private HintingMode hintingMode = HintingMode.Standard; + + /// + /// Initializes a new glyph run wrapper. + /// + /// The Avalonia glyph typeface. + /// The font rendering em size. + /// The positioned glyph information from Avalonia shaping. + /// The glyph-run baseline origin. + /// The SixLabors font face used to render the run. + public GlyphRunImpl(GlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin, Font font) + { + this.GlyphTypeface = glyphTypeface; + this.FontRenderingEmSize = fontRenderingEmSize; + this.BaselineOrigin = baselineOrigin; + this.Font = font; + + ushort[] glyphIds = new ushort[glyphInfos.Count]; + Vector2[] origins = new Vector2[glyphInfos.Count]; + + double penX = 0; + for (int i = 0; i < glyphInfos.Count; i++) + { + GlyphInfo glyph = glyphInfos[i]; + Avalonia.Vector offset = glyph.GlyphOffset; + + glyphIds[i] = glyph.GlyphIndex; + origins[i] = new Vector2((float)(baselineOrigin.X + penX + offset.X), (float)(baselineOrigin.Y + offset.Y)); + + penX += glyph.GlyphAdvance; + } + + this.GlyphIds = glyphIds; + this.GlyphOrigins = origins; + + // Measure directly from the per-glyph metrics cache; the entries match the boxes + // ImageSharp reports when rendering the same positioned run with the same options. + this.glyphMetrics = TextMeasurer.GetGlyphMetrics( + this.GlyphIds.Span, + this.GlyphOrigins.Span, + new GlyphOptions + { + Font = font, + TextBaseline = TextBaseline.Alphabetic, + ColorFontSupport = ColorFontSupport.ColrV0 | ColorFontSupport.ColrV1 | ColorFontSupport.Svg + }); + + FontRectangle measured = FontRectangle.Empty; + bool hasBounds = false; + foreach (SixLabors.Fonts.GlyphMetrics entry in this.glyphMetrics.Span) + { + if (entry.Bounds == FontRectangle.Empty) + { + continue; + } + + measured = hasBounds ? FontRectangle.Union(measured, entry.Bounds) : entry.Bounds; + hasBounds = true; + } + + this.Bounds = !hasBounds + ? new Rect(baselineOrigin.X, baselineOrigin.Y, penX, 0) + : new Rect(measured.X, measured.Y, measured.Width, measured.Height).Inflate(1); + } + + /// + public GlyphTypeface GlyphTypeface { get; } + + /// + /// Gets the exact SixLabors font face the run was shaped against. + /// + public Font Font { get; } + + /// + public double FontRenderingEmSize { get; } + + /// + public Point BaselineOrigin { get; } + + /// + public Rect Bounds { get; } + + /// + /// Gets the glyph identifiers used by ImageSharp. + /// + public ReadOnlyMemory GlyphIds { get; } + + /// + /// Gets the absolute glyph origins used by ImageSharp. + /// + public ReadOnlyMemory GlyphOrigins { get; } + + /// + /// Creates ImageSharp rich glyph options from the current Avalonia text options. + /// + /// The effective Avalonia text options. + /// The ImageSharp glyph options. + public RichGlyphOptions CreateGlyphOptions(AvaloniaTextOptions textOptions) + { + // Track the hinting mode the run is drawn with so outline-derived measurements + // (decoration intersections) use the same hinted geometry. + this.hintingMode = textOptions.TextHintingMode switch + { + AvaloniaTextHintingMode.None => HintingMode.None, + AvaloniaTextHintingMode.Light => HintingMode.Standard, + AvaloniaTextHintingMode.Strong => HintingMode.Standard, + _ => HintingMode.Standard + }; + + return new RichGlyphOptions + { + Font = this.Font, + HintingMode = this.hintingMode, + + // Avalonia positions glyph runs by their baseline origins, so the run must be + // anchored by the alphabetic baseline rather than the line-box default. + TextBaseline = TextBaseline.Alphabetic, + ColorFontSupport = ColorFontSupport.ColrV0 | ColorFontSupport.ColrV1 | ColorFontSupport.Svg + }; + } + + /// + /// + /// Computed from the exact outline geometry via the Fonts intersection API, matching the + /// precision of the Skia backend's SKTextBlob.GetIntercepts, so text decorations + /// break tightly around descender strokes. + /// + public IReadOnlyList GetIntersections(float lowerLimit, float upperLimit) + { + HintingMode currentHintingMode = this.hintingMode; + IntersectionsCache? cache = this.intersectionsCache; + if (cache is not null + && cache.LowerLimit == lowerLimit + && cache.UpperLimit == upperLimit + && cache.HintingMode == currentHintingMode) + { + return cache.Intersections; + } + + // The limits arrive relative to the baseline; the run origins are absolute. + float bandTop = (float)this.BaselineOrigin.Y + lowerLimit; + float bandBottom = (float)this.BaselineOrigin.Y + upperLimit; + + IReadOnlyList intersections = TextMeasurer.GetIntersections( + this.GlyphIds.Span, + this.GlyphOrigins.Span, + new GlyphOptions + { + Font = this.Font, + HintingMode = currentHintingMode, + TextBaseline = TextBaseline.Alphabetic, + ColorFontSupport = ColorFontSupport.ColrV0 | ColorFontSupport.ColrV1 | ColorFontSupport.Svg + }, + bandTop, + bandBottom).ToArray(); + + this.intersectionsCache = new IntersectionsCache(lowerLimit, upperLimit, currentHintingMode, intersections); + return intersections; + } + + /// + public void Dispose() + { + } + + /// + /// One memoized intersections result for a limit band. + /// + private sealed class IntersectionsCache + { + /// + /// Initializes a new instance of the class. + /// + /// The band's lower limit relative to the baseline. + /// The band's upper limit relative to the baseline. + /// The hinting mode the intersections were computed with. + /// The merged, x-sorted interval pairs. + public IntersectionsCache(float lowerLimit, float upperLimit, HintingMode hintingMode, IReadOnlyList intersections) + { + this.LowerLimit = lowerLimit; + this.UpperLimit = upperLimit; + this.HintingMode = hintingMode; + this.Intersections = intersections; + } + + /// + /// Gets the hinting mode the intersections were computed with. + /// + public HintingMode HintingMode { get; } + + /// + /// Gets the band's lower limit relative to the baseline. + /// + public float LowerLimit { get; } + + /// + /// Gets the band's upper limit relative to the baseline. + /// + public float UpperLimit { get; } + + /// + /// Gets the merged, x-sorted interval pairs. + /// + public IReadOnlyList Intersections { get; } + } +} diff --git a/samples/AvaloniaControlCatalog/IDrawableBitmapImpl.cs b/samples/AvaloniaControlCatalog/IDrawableBitmapImpl.cs new file mode 100644 index 00000000..ef86b7e3 --- /dev/null +++ b/samples/AvaloniaControlCatalog/IDrawableBitmapImpl.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Avalonia.Platform; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Transforms; + +namespace AvaloniaControlCatalog; + +/// +/// Bitmap implementation that can draw itself into an ImageSharp-backed drawing context. +/// +internal interface IDrawableBitmapImpl : IBitmapImpl +{ + /// + /// Draws this bitmap into the supplied drawing context. + /// + /// The drawing context receiving the bitmap. + /// The source rectangle in bitmap pixels. + /// The destination rectangle in drawing coordinates. + /// The resampler used when scaling the bitmap. + /// The opacity applied to the bitmap draw. + /// The alpha composition mode used to blend the bitmap with the destination. + void Draw( + DrawingContextImpl context, + Rectangle sourceRect, + RectangleF destinationRect, + IResampler sampler, + float opacity, + PixelAlphaCompositionMode alphaCompositionMode); +} diff --git a/samples/AvaloniaControlCatalog/Icons.cs b/samples/AvaloniaControlCatalog/Icons.cs new file mode 100644 index 00000000..58d0e0b7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Icons.cs @@ -0,0 +1,194 @@ +namespace ControlCatalog +{ + internal static class Icons + { + // Layers/Composition + public const string Layers = "M12,16L19.36,10.27L21,9L12,2L3,9L4.63,10.27M12,18.54L4.62,12.81L3,14.07L12,21.07L21,14.07L19.37,12.8"; + + // Keyboard + public const string Keyboard = "M19,10H17V8H19M19,13H17V11H19M16,10H14V8H16M16,13H14V11H16M16,17H8V15H16M7,10H5V8H7M7,13H5V11H7M8,11H10V13H8M8,8H10V10H8M11,11H13V13H11M11,8H13V10H11M20,5H4C2.89,5 2,5.89 2,7V17A2,2 0 0,0 4,19H20A2,2 0 0,0 22,17V7C22,5.89 21.1,5 20,5Z"; + + // Blur/Acrylic + public const string Blur = "M14.5,10C16.43,10 18,8.43 18,6.5C18,4.57 16.43,3 14.5,3C12.57,3 11,4.57 11,6.5C11,8.43 12.57,10 14.5,10M14.5,4A2.5,2.5 0 0,1 17,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,4M8.5,14C10.43,14 12,12.43 12,10.5C12,8.57 10.43,7 8.5,7C6.57,7 5,8.57 5,10.5C5,12.43 6.57,14 8.5,14M8.5,8A2.5,2.5 0 0,1 11,10.5A2.5,2.5 0 0,1 8.5,13A2.5,2.5 0 0,1 6,10.5A2.5,2.5 0 0,1 8.5,8M14.5,16A4.5,4.5 0 0,0 10,20.5A4.5,4.5 0 0,0 14.5,25A4.5,4.5 0 0,0 19,20.5A4.5,4.5 0 0,0 14.5,16M14.5,24A3.5,3.5 0 0,1 11,20.5A3.5,3.5 0 0,1 14.5,17A3.5,3.5 0 0,1 18,20.5A3.5,3.5 0 0,1 14.5,24Z"; + + // Adorner/Sparkle + public const string Sparkle = "M12,1L9,9L1,12L9,15L12,23L15,15L23,12L15,9"; + + // Text/AutoComplete + public const string TextInput = "M20,21H4V3H20V21M6,9H14V7H6V9M6,13H18V11H6V13M6,17H14V15H6V17Z"; + + // Border/Square + public const string Border = "M4,4H20V20H4V4M6,8V6H8V8H6M6,12V10H8V12H6M6,16V14H8V16H6M6,20V18H8V20H6M10,20V18H12V20H10M14,20V18H16V20H14M18,20V18H20V20H18M18,16V14H20V16H18M18,12V10H20V12H18M18,8V6H20V8H18M14,8V6H16V8H14M10,8V6H12V8H10Z"; + + // Lightning/Cache + public const string Lightning = "M11,21H7L13,3H17L14,10H20L11,21Z"; + + // Cursor Click/Button + public const string CursorClick = "M10.76,8.69A0.76,0.76 0 0,0 10,9.45V20.9C10,21.32 10.34,21.66 10.76,21.66C10.95,21.66 11.11,21.6 11.24,21.5L13.15,19.95L14.81,23.57C14.94,23.84 15.21,24 15.5,24C15.62,24 15.74,23.97 15.85,23.92L17.28,23.26C17.69,23.07 17.86,22.59 17.66,22.17L16,18.54L18.44,18.19C18.87,18.13 19.1,17.73 18.91,17.37L12.1,8.89C11.95,8.76 11.79,8.69 10.76,8.69M15,10V8H20V10H15M13.83,4.76L16.66,1.93L18.07,3.34L15.24,6.17L13.83,4.76M10,0V5H8V0H10M3.93,14.66L6.76,11.83L8.17,13.24L5.34,16.07L3.93,14.66M3.93,3.34L5.34,1.93L8.17,4.76L6.76,6.17L3.93,3.34M7,10H2V8H7V10Z"; + + // Spinner/ButtonSpinner + public const string Spinner = "M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z"; + + // Calendar + public const string Calendar = "M19,19H5V8H19M16,1V3H8V1H6V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3H18V1M9,15H7V13H9V15M13,15H11V13H13V15M17,15H15V13H17V15M9,11H7V9H9V11M13,11H11V9H13V11M17,11H15V9H17V11Z"; + + // Canvas/Artboard + public const string Canvas = "M17.5,12A1.5,1.5 0 0,1 16,10.5A1.5,1.5 0 0,1 17.5,9A1.5,1.5 0 0,1 19,10.5A1.5,1.5 0 0,1 17.5,12M14.5,8A1.5,1.5 0 0,1 13,6.5A1.5,1.5 0 0,1 14.5,5A1.5,1.5 0 0,1 16,6.5A1.5,1.5 0 0,1 14.5,8M9.5,8A1.5,1.5 0 0,1 8,6.5A1.5,1.5 0 0,1 9.5,5A1.5,1.5 0 0,1 11,6.5A1.5,1.5 0 0,1 9.5,8M6.5,12A1.5,1.5 0 0,1 5,10.5A1.5,1.5 0 0,1 6.5,9A1.5,1.5 0 0,1 8,10.5A1.5,1.5 0 0,1 6.5,12M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A1.5,1.5 0 0,0 13.5,19.5C13.5,19.11 13.35,18.76 13.11,18.5C12.88,18.23 12.73,17.88 12.73,17.5A1.5,1.5 0 0,1 14.23,16H16A5,5 0 0,0 21,11C21,6.58 16.97,3 12,3Z"; + + // Command/Terminal + public const string Terminal = "M20,19V7H4V19H20M20,3A2,2 0 0,1 22,5V19A2,2 0 0,1 20,21H4A2,2 0 0,1 2,19V5C2,3.89 2.9,3 4,3H20M13,17V15H18V17H13M9.58,13L5.57,9H8.4L11.7,12.3C12.09,12.69 12.09,13.33 11.7,13.72L8.42,17H5.59L9.58,13Z"; + + // Slides/Carousel + public const string Slides = "M2,6H6V18H2V6M7,6H17V18H7V6M18,6H22V18H18V6Z"; + + // Checkbox + public const string Checkbox = "M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M10,17L5,12L6.41,10.58L10,14.17L17.59,6.58L19,8"; + + // Clipboard + public const string Clipboard = "M19,3H14.82C14.4,1.84 13.3,1 12,1C10.7,1 9.6,1.84 9.18,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M12,3A1,1 0 0,1 13,4A1,1 0 0,1 12,5A1,1 0 0,1 11,4A1,1 0 0,1 12,3"; + + // Palette/Color + public const string Palette = "M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A1.5,1.5 0 0,0 13.5,19.5C13.5,19.11 13.35,18.76 13.11,18.5C12.88,18.23 12.73,17.88 12.73,17.5A1.5,1.5 0 0,1 14.23,16H16A5,5 0 0,0 21,11C21,6.58 16.97,3 12,3M6.5,12A1.5,1.5 0 0,1 5,10.5A1.5,1.5 0 0,1 6.5,9A1.5,1.5 0 0,1 8,10.5A1.5,1.5 0 0,1 6.5,12M9.5,8A1.5,1.5 0 0,1 8,6.5A1.5,1.5 0 0,1 9.5,5A1.5,1.5 0 0,1 11,6.5A1.5,1.5 0 0,1 9.5,8M14.5,8A1.5,1.5 0 0,1 13,6.5A1.5,1.5 0 0,1 14.5,5A1.5,1.5 0 0,1 16,6.5A1.5,1.5 0 0,1 14.5,8M17.5,12A1.5,1.5 0 0,1 16,10.5A1.5,1.5 0 0,1 17.5,9A1.5,1.5 0 0,1 19,10.5A1.5,1.5 0 0,1 17.5,12Z"; + + // Dropdown/ComboBox + public const string Dropdown = "M7,10L12,15L17,10H7Z"; + + // Container/Box + public const string Container = "M2,2H8V4H16V2H22V8H20V16H22V22H16V20H8V22H2V16H4V8H2V2M16,8V6H8V8H6V16H8V18H16V16H18V8H16M4,4V6H6V4H4M18,4V6H20V4H18M4,18V20H6V18H4M18,18V20H20V18H18Z"; + + // Document/Page + public const string Document = "M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"; + + // Menu/Context + public const string Menu = "M3,6H21V8H3V6M3,11H21V13H3V11M3,16H21V18H3V16Z"; + + // Cursor/Pointer + public const string Cursor = "M13.64,21.97C13.14,22.21 12.54,22 12.31,21.5L10.13,16.76L7.62,18.78C7.45,18.92 7.24,19 7,19A1,1 0 0,1 6,18V3A1,1 0 0,1 7,2C7.24,2 7.47,2.09 7.64,2.23L7.65,2.22L19.14,11.86C19.57,12.22 19.39,12.92 18.83,13.03L14.83,13.78L17,18.5C17.24,19 17,19.59 16.5,19.83L13.64,21.97Z"; + + // Brush/Drawing + public const string Brush = "M20.71,4.63L19.37,3.29C19,2.9 18.35,2.9 17.96,3.29L9,12.25L11.75,15L20.71,6.04C21.1,5.65 21.1,5 20.71,4.63M7,14A3,3 0 0,0 4,17C4,18.31 2.84,19 2,19C2.92,20.22 4.5,21 6,21A4,4 0 0,0 10,17A3,3 0 0,0 7,14Z"; + + // Grid/Table + public const string Grid = "M10,4V8H14V4H10M16,4V8H20V4H16M16,10V14H20V10H16M16,16V20H20V16H16M14,20V16H10V20H14M8,20V16H4V20H8M8,14V10H4V14H8M8,8V4H4V8H8M10,14H14V10H10V14Z"; + + // Shield/Validation + public const string Shield = "M10,17L6,13L7.41,11.59L10,14.17L16.59,7.58L18,9M12,1L3,5V11C3,16.55 6.84,21.74 12,23C17.16,21.74 21,16.55 21,11V5L12,1Z"; + + // Clock + public const string Clock = "M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M16.2,16.2L11,13V7H12.5V12.2L17,14.9L16.2,16.2Z"; + + // Dialog/Message + public const string Dialog = "M20,2H4A2,2 0 0,0 2,4V22L6,18H20A2,2 0 0,0 22,16V4A2,2 0 0,0 20,2Z"; + + // DragDrop/Move + public const string DragDrop = "M13,6V11H18V7.75L22.25,12L18,16.25V13H13V18H16.25L12,22.25L7.75,18H11V13H6V16.25L1.75,12L6,7.75V11H11V6H7.75L12,1.75L16.25,6H13Z"; + + // Sidebar/Drawer + public const string Drawer = "M3,2H21A1,1 0 0,1 22,3V21A1,1 0 0,1 21,22H3A1,1 0 0,1 2,21V3A1,1 0 0,1 3,2M10,4V20H20V4H10M4,4V20H8V4H4Z"; + + // Expand + public const string Expand = "M10,21V19H6.41L10.91,14.5L9.5,13.09L5,17.59V14H3V21H10M14.5,10.91L19,6.41V10H21V3H14V5H17.59L13.09,9.5L14.5,10.91Z"; + + // Flyout/Popup + public const string Flyout = "M19,19H5V5H19M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M7,9H10V7H7V9M7,13H17V11H7V13M7,17H14V15H7V17Z"; + + // Target/Focus + public const string Target = "M12,8A4,4 0 0,0 8,12A4,4 0 0,0 12,16A4,4 0 0,0 16,12A4,4 0 0,0 12,8M3.05,13H1V11H3.05C3.5,6.83 6.83,3.5 11,3.05V1H13V3.05C17.17,3.5 20.5,6.83 20.95,11H23V13H20.95C20.5,17.17 17.17,20.5 13,20.95V23H11V20.95C6.83,20.5 3.5,17.17 3.05,13M12,5A7,7 0 0,0 5,12A7,7 0 0,0 12,19A7,7 0 0,0 19,12A7,7 0 0,0 12,5Z"; + + // Gesture/Touch + public const string Gesture = "M5,15A2,2 0 0,0 3,17V19H7V17A2,2 0 0,0 5,15M5,11A6,6 0 0,0 -1,17H1A4,4 0 0,1 5,13V11M5,7C0.03,7 -4,11.03 -4,16V17H-2V16C-2,12.13 1.13,9 5,9V7M20.5,9.5L12,18L8.5,14.5L7.08,15.91L12,20.84L21.92,10.91L20.5,9.5Z"; + + // Image/Photo + public const string Image = "M8.5,13.5L11,16.5L14.5,12L19,18H5M21,19V5C21,3.89 20.1,3 19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19Z"; + + // Tag/Label + public const string Tag = "M5.5,7A1.5,1.5 0 0,1 4,5.5A1.5,1.5 0 0,1 5.5,4A1.5,1.5 0 0,1 7,5.5A1.5,1.5 0 0,1 5.5,7M21.41,11.58L12.41,2.58C12.05,2.22 11.55,2 11,2H4C2.89,2 2,2.89 2,4V11C2,11.55 2.22,12.05 2.59,12.41L11.58,21.41C11.95,21.77 12.45,22 13,22C13.55,22 14.05,21.77 14.41,21.41L21.41,14.41C21.78,14.05 22,13.55 22,13C22,12.45 21.77,11.95 21.41,11.58Z"; + + // Transform/Rotate + public const string Transform = "M16.89,15.5L18.31,16.89C19.21,15.73 19.76,14.39 19.93,13H17.91C17.77,13.87 17.43,14.72 16.89,15.5M13,17.9V19.92C14.39,19.75 15.74,19.21 16.9,18.31L15.46,16.87C14.71,17.41 13.87,17.76 13,17.9M19.93,11C19.76,9.61 19.21,8.27 18.31,7.11L16.89,8.53C17.43,9.28 17.77,10.13 17.91,11M15.55,5.55L11,1V5.07C7.06,5.56 4,8.92 4,12.95C4,17.18 7.32,20.56 11.46,20.93C11.64,20.95 11.82,20.97 12,20.97C12.18,20.97 12.36,20.95 12.54,20.93C12.69,20.92 12.83,20.89 13,20.87V18.9C12.82,18.92 12.65,18.95 12.47,18.95C12.32,18.96 12.16,18.97 12,18.97C8.42,18.97 5.53,16.12 5.53,12.58C5.53,9.36 7.88,6.67 11,6.15V10L15.55,5.55Z"; + + // List + public const string List = "M7,5H21V7H7V5M7,13V11H21V13H7M4,4.5A1.5,1.5 0 0,1 5.5,6A1.5,1.5 0 0,1 4,7.5A1.5,1.5 0 0,1 2.5,6A1.5,1.5 0 0,1 4,4.5M4,10.5A1.5,1.5 0 0,1 5.5,12A1.5,1.5 0 0,1 4,13.5A1.5,1.5 0 0,1 2.5,12A1.5,1.5 0 0,1 4,10.5M7,19V17H21V19H7M4,16.5A1.5,1.5 0 0,1 5.5,18A1.5,1.5 0 0,1 4,19.5A1.5,1.5 0 0,1 2.5,18A1.5,1.5 0 0,1 4,16.5Z"; + + // Bell/Notification + public const string Bell = "M21,19V20H3V19L5,17V11C5,7.9 7.03,5.17 10,4.29C10,4.19 10,4.1 10,4A2,2 0 0,1 12,2A2,2 0 0,1 14,4C14,4.1 14,4.19 14,4.29C16.97,5.17 19,7.9 19,11V17L21,19M14,21A2,2 0 0,1 12,23A2,2 0 0,1 10,21"; + + // Number/Hash + public const string Number = "M5.41,21L6.12,17H2.12L2.47,15H6.47L7.53,9H3.53L3.88,7H7.88L8.59,3H10.59L9.88,7H15.88L16.59,3H18.59L17.88,7H21.88L21.53,9H17.53L16.47,15H20.47L20.12,17H16.12L15.41,21H13.41L14.12,17H8.12L7.41,21H5.41M9.47,9L8.41,15H14.41L15.47,9H9.47Z"; + + // Cube/3D + public const string Cube = "M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L5,8.09V15.91L12,19.85L19,15.91V8.09L12,4.15Z"; + + // Dots/Pagination + public const string Dots = "M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"; + + // Info/Information + public const string Info = "M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"; + + // Progress/Loading + public const string Progress = "M13,2.03V2.05L13,4.05C17.39,4.59 20.5,8.58 19.96,12.97C19.5,16.61 16.64,19.5 13,19.93V21.93C18.5,21.38 22.5,16.5 21.95,11C21.5,6.25 17.73,2.5 13,2.03M11,2.06C9.05,2.25 7.19,3 5.67,4.26L7.1,5.74C8.22,4.84 9.57,4.26 11,4.06V2.06M4.26,5.67C3,7.19 2.25,9.04 2.05,11H4.05C4.24,9.58 4.8,8.23 5.69,7.1L4.26,5.67M2.06,13C2.26,14.96 3.03,16.81 4.27,18.33L5.69,16.9C4.81,15.77 4.24,14.42 4.06,13H2.06M7.1,18.37L5.67,19.74C7.18,21 9.04,21.79 11,22V20C9.58,19.82 8.23,19.25 7.1,18.37Z"; + + // Radio + public const string Radio = "M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,7A5,5 0 0,0 7,12A5,5 0 0,0 12,17A5,5 0 0,0 17,12A5,5 0 0,0 12,7Z"; + + // Refresh + public const string Refresh = "M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z"; + + // Layout/Grid + public const string Layout = "M19,11H13V5H19M19,19H13V13H19M11,11H5V5H11M11,19H5V13H11M3,3V21H21V3"; + + // Scroll + public const string Scroll = "M15,20H9V12H4.16L12,4.16L19.84,12H15V20Z"; + + // Slider/Tune + public const string Slider = "M7,5H21V7H7V5M7,13V11H21V13H7M4,4.5A1.5,1.5 0 0,1 5.5,6A1.5,1.5 0 0,1 4,7.5A1.5,1.5 0 0,1 2.5,6A1.5,1.5 0 0,1 4,4.5M4,10.5A1.5,1.5 0 0,1 5.5,12A1.5,1.5 0 0,1 4,13.5A1.5,1.5 0 0,1 2.5,12A1.5,1.5 0 0,1 4,10.5M7,19V17H21V19H7M4,16.5A1.5,1.5 0 0,1 5.5,18A1.5,1.5 0 0,1 4,19.5A1.5,1.5 0 0,1 2.5,18A1.5,1.5 0 0,1 4,16.5Z"; + + // Split/SplitView + public const string Split = "M18,4H6C4.89,4 4,4.89 4,6V18A2,2 0 0,0 6,20H18A2,2 0 0,0 20,18V6C20,4.89 19.1,4 18,4M18,6V18H13V6H18Z"; + + // Tab + public const string Tab = "M21,3H3C1.89,3 1,3.89 1,5V19A2,2 0 0,0 3,21H21C22.1,21 23,20.1 23,19V5C23,3.89 22.1,3 21,3M21,19H3V5H13V9H21V19Z"; + + // Theme/Palette + public const string Theme = "M12,18V6A6,6 0 0,1 18,12A6,6 0 0,1 12,18M20,15.31L23.31,12L20,8.69V4H15.31L12,0.69L8.69,4H4V8.69L0.69,12L4,15.31V20H8.69L12,23.31L15.31,20H20V15.31Z"; + + // Toggle/Switch + public const string Toggle = "M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z"; + + // Tooltip/Info bubble + public const string Tooltip = "M4,2H20A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H13.9L10.2,21.71C10,21.9 9.75,22 9.5,22V22H9A1,1 0 0,1 8,21V18H4A2,2 0 0,1 2,16V4A2,2 0 0,1 4,2M11,15H13V13H11V15M11,11H13V5H11V11Z"; + + // Animation/Transition + public const string Transition = "M2,5.27L3.28,4L20,20.72L18.73,22L12.73,16H7V19L3,15L7,11V14H9.73L5,9.27V14H3V5.27M21,9L17,5V8H14V10H17V13L21,9M17,17V14H15V17H17Z"; + + // Tree/Hierarchy + public const string Tree = "M15,20A1,1 0 0,0 16,19V4H8V19A1,1 0 0,0 9,20H2V22H22V20H15Z"; + + // Viewbox/Resize + public const string Viewbox = "M5,15H3V19A2,2 0 0,0 5,21H9V19H5M5,5H9V3H5A2,2 0 0,0 3,5V9H5M19,3H15V5H19V9H21V5A2,2 0 0,0 19,3M19,19H15V21H19A2,2 0 0,0 21,19V15H19"; + + // Puzzle/Embed + public const string Puzzle = "M20.5,11H19V7C19,5.89 18.1,5 17,5H13V3.5A2.5,2.5 0 0,0 10.5,1A2.5,2.5 0 0,0 8,3.5V5H4A2,2 0 0,0 2,7V10.8H3.5C5.04,10.8 6.2,11.96 6.2,13.5C6.2,15.04 5.04,16.2 3.5,16.2H2V20A2,2 0 0,0 4,22H7.8V20.5C7.8,18.96 8.96,17.8 10.5,17.8C12.04,17.8 13.2,18.96 13.2,20.5V22H17A2,2 0 0,0 19,20V16H20.5A2.5,2.5 0 0,0 23,13.5A2.5,2.5 0 0,0 20.5,11Z"; + + // Window/Application + public const string Window = "M4,4H20V20H4V4M6,8V18H18V8H6Z"; + + // Header/Title + public const string Header = "M3,4H5V10H9V4H11V18H9V12H5V18H3V4M13,8H15.31L15.63,5H17.63L17.31,8H19.31L19.63,5H21.63L21.31,8H23V10H21.1L20.9,12H23V14H20.69L20.37,17H18.37L18.69,14H16.69L16.37,17H14.37L14.69,14H13V12H14.9L15.1,10H13V8M17.1,10L16.9,12H18.9L19.1,10H17.1Z"; + + // Monitor/Screen + public const string Monitor = "M21,16H3V4H21M21,2H3C1.89,2 1,2.89 1,4V16A2,2 0 0,0 3,18H10V20H8V22H16V20H14V18H21A2,2 0 0,0 23,16V4C23,2.89 22.1,2 21,2Z"; + + // Navigation/Compass + public const string Navigation = "M12,2L4.5,20.29L5.21,21L12,18L18.79,21L19.5,20.29L12,2Z"; + + // OpenGL/3D Box (same as Cube but outline variant) + public const string Cube3D = "M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L6.04,7.5L12,10.85L17.96,7.5L12,4.15M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V9.21L13,12.58V19.29L19,15.91Z"; + + // Horizontal dots/Pager + public const string HorizontalDots = "M16,12A2,2 0 0,1 18,10A2,2 0 0,1 20,12A2,2 0 0,1 18,14A2,2 0 0,1 16,12M10,12A2,2 0 0,1 12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12M4,12A2,2 0 0,1 6,10A2,2 0 0,1 8,12A2,2 0 0,1 6,14A2,2 0 0,1 4,12Z"; + + // Slider tune/adjust + public const string Tune = "M3,17V19H9V17H3M3,5V7H13V5H3M13,21V19H21V17H13V15H11V21H13M7,9V11H3V13H7V15H9V9H7M21,13V11H11V13H21M15,9H17V7H21V5H17V3H15V9Z"; + } +} diff --git a/samples/AvaloniaControlCatalog/ImageSharpDrawingAppBuilderExtensions.cs b/samples/AvaloniaControlCatalog/ImageSharpDrawingAppBuilderExtensions.cs new file mode 100644 index 00000000..a4c67297 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ImageSharpDrawingAppBuilderExtensions.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Avalonia; +using Avalonia.Platform; + +namespace AvaloniaControlCatalog; + +/// +/// Provides application builder extensions for the ImageSharp.Drawing sample renderer. +/// +internal static class ImageSharpDrawingAppBuilderExtensions +{ + /// + /// Registers the ImageSharp.Drawing rendering and SixLabors.Fonts text shaping + /// subsystems with the application builder. + /// + /// The application builder. + /// The supplied . + public static AppBuilder UseImageSharpDrawing(this AppBuilder builder) + => builder + .UseRenderingSubsystem(PlatformRenderInterface.Initialize, "ImageSharp.Drawing") + .UseTextShapingSubsystem( + () => AvaloniaLocator.CurrentMutable.Bind().ToConstant(new TextShaperImpl()), + "SixLabors.Fonts"); +} diff --git a/samples/AvaloniaControlCatalog/ImageSharpExtensions.cs b/samples/AvaloniaControlCatalog/ImageSharpExtensions.cs new file mode 100644 index 00000000..1e611df7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ImageSharpExtensions.cs @@ -0,0 +1,328 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.IO.Compression; +using System.Numerics; +using Avalonia.Media.Imaging; +using SixLabors.Fonts; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; + +namespace AvaloniaControlCatalog; + +/// +/// Conversions between Avalonia and ImageSharp primitive types. +/// +internal static class ImageSharpExtensions +{ + /// + /// Saves an ImageSharp image using the format and settings specified by Avalonia encoder options. + /// + /// The image to save. + /// The stream to which the encoded image is written. + /// The encoder options that select the output format and its settings. + public static void Save(this Image image, Stream stream, BitmapEncoderOptions options) + { + ImageEncoder encoder = options switch + { + PngBitmapEncoderOptions pngOptions => new PngEncoder + { + CompressionLevel = pngOptions.CompressionLevel switch + { + CompressionLevel.Fastest => PngCompressionLevel.BestSpeed, + CompressionLevel.NoCompression => PngCompressionLevel.NoCompression, + CompressionLevel.SmallestSize => PngCompressionLevel.BestCompression, + _ => PngCompressionLevel.DefaultCompression + } + }, + JpegBitmapEncoderOptions jpegOptions => new JpegEncoder + { + Quality = Math.Clamp(jpegOptions.Quality, 1, 100) + }, + _ => throw new ArgumentOutOfRangeException(nameof(options), options, "Unknown encoder options type") + }; + + image.Save(stream, encoder); + } + + /// + /// Converts an Avalonia colour to an ImageSharp colour with the supplied opacity applied. + /// + /// The Avalonia colour. + /// The opacity multiplier. + /// The converted ImageSharp colour. + public static Color ToColor(this AvaloniaColor color, double opacity = 1) + { + byte alpha = (byte)Math.Clamp(color.A * opacity, 0, 255); + return Color.FromPixel(new Bgra32(color.R, color.G, color.B, alpha)); + } + + /// + /// Converts ImageSharp metadata resolution to Avalonia DPI. + /// + /// The ImageSharp image metadata. + /// The converted Avalonia DPI. + public static AvaloniaVector ToDpi(this ImageMetadata metadata) + => metadata.ResolutionUnits switch + { + PixelResolutionUnit.PixelsPerCentimeter => new AvaloniaVector(metadata.HorizontalResolution * 2.54, metadata.VerticalResolution * 2.54), + PixelResolutionUnit.PixelsPerMeter => new AvaloniaVector(metadata.HorizontalResolution * 0.0254, metadata.VerticalResolution * 0.0254), + PixelResolutionUnit.PixelsPerInch => new AvaloniaVector(metadata.HorizontalResolution, metadata.VerticalResolution), + _ => new AvaloniaVector(ImageMetadata.DefaultHorizontalResolution, ImageMetadata.DefaultVerticalResolution) + }; + + /// + /// Converts an Avalonia point to an ImageSharp point. + /// + /// The Avalonia point. + /// The converted ImageSharp point. + public static PointF ToPointF(this AvaloniaPoint point) => new((float)point.X, (float)point.Y); + + /// + /// Converts an Avalonia size to an ImageSharp size. + /// + /// The Avalonia size. + /// The converted ImageSharp size. + public static SizeF ToSizeF(this AvaloniaSize size) => new((float)size.Width, (float)size.Height); + + /// + /// Converts an Avalonia rectangle to an ImageSharp rectangle. + /// + /// The Avalonia rectangle. + /// The converted ImageSharp rectangle. + public static RectangleF ToRectangleF(this AvaloniaRect rect) => new((float)rect.X, (float)rect.Y, (float)rect.Width, (float)rect.Height); + + /// + /// Converts an ImageSharp rectangle to an Avalonia rectangle. + /// + /// The ImageSharp rectangle. + /// The converted Avalonia rectangle. + public static AvaloniaRect ToAvaloniaRect(this RectangleF rect) => new(rect.X, rect.Y, rect.Width, rect.Height); + + /// + /// Converts an Avalonia fill rule to the equivalent ImageSharp intersection rule. + /// + /// The Avalonia fill rule. + /// The converted ImageSharp intersection rule. + public static IntersectionRule ToIntersectionRule(this FillRule fillRule) + => fillRule == FillRule.NonZero ? IntersectionRule.NonZero : IntersectionRule.EvenOdd; + + /// + /// Converts an Avalonia bitmap interpolation mode to the closest ImageSharp resampler. + /// + /// The Avalonia interpolation mode. + /// Whether the image is being enlarged. + /// The converted ImageSharp resampler. + public static IResampler ToResampler(this BitmapInterpolationMode interpolationMode, bool isUpscaling) + => interpolationMode switch + { + BitmapInterpolationMode.None => KnownResamplers.NearestNeighbor, + BitmapInterpolationMode.HighQuality when isUpscaling => KnownResamplers.MitchellNetravali, + _ => KnownResamplers.Triangle + }; + + /// + /// Converts an Avalonia bitmap blending mode to an ImageSharp alpha composition mode. + /// + /// The Avalonia bitmap blending mode. + /// The converted ImageSharp alpha composition mode. + public static PixelAlphaCompositionMode ToAlphaCompositionMode(this BitmapBlendingMode blendingMode) + => blendingMode switch + { + BitmapBlendingMode.Source => PixelAlphaCompositionMode.Src, + BitmapBlendingMode.SourceAtop => PixelAlphaCompositionMode.SrcAtop, + BitmapBlendingMode.SourceIn => PixelAlphaCompositionMode.SrcIn, + BitmapBlendingMode.SourceOut => PixelAlphaCompositionMode.SrcOut, + BitmapBlendingMode.Destination => PixelAlphaCompositionMode.Dest, + BitmapBlendingMode.DestinationAtop => PixelAlphaCompositionMode.DestAtop, + BitmapBlendingMode.DestinationOver => PixelAlphaCompositionMode.DestOver, + BitmapBlendingMode.DestinationIn => PixelAlphaCompositionMode.DestIn, + BitmapBlendingMode.DestinationOut => PixelAlphaCompositionMode.DestOut, + BitmapBlendingMode.Xor => PixelAlphaCompositionMode.Xor, + + // TODO: Implement missing modes. Hue etc. + _ => PixelAlphaCompositionMode.SrcOver + }; + + /// + /// Converts an Avalonia matrix to an ImageSharp-compatible matrix. + /// + /// The Avalonia matrix. + /// The converted matrix. + public static Matrix4x4 ToMatrix4x4(this AvaloniaMatrix matrix) + => new( + (float)matrix.M11, + (float)matrix.M12, + 0, + 0, + (float)matrix.M21, + (float)matrix.M22, + 0, + 0, + 0, + 0, + 1, + 0, + (float)matrix.M31, + (float)matrix.M32, + 0, + 1); + + /// + /// Converts an Avalonia rectangle to an integer ImageSharp rectangle. + /// + /// The Avalonia rectangle. + /// The converted ImageSharp rectangle. + public static Rectangle ToRectangle(this AvaloniaRect r) => (Rectangle)r.ToRectangleF(); + + /// + /// Converts an Avalonia rounded rectangle to an ImageSharp rounded rectangle polygon. + /// + /// The Avalonia rounded rectangle. + /// The converted ImageSharp rounded rectangle polygon. + public static RoundedRectanglePolygon ToRoundedRectanglePath(this AvaloniaRoundedRect rect) + => new( + rect.Rect.ToRectangleF(), + new SizeF((float)rect.RadiiTopLeft.X, (float)rect.RadiiTopLeft.Y), + new SizeF((float)rect.RadiiTopRight.X, (float)rect.RadiiTopRight.Y), + new SizeF((float)rect.RadiiBottomRight.X, (float)rect.RadiiBottomRight.Y), + new SizeF((float)rect.RadiiBottomLeft.X, (float)rect.RadiiBottomLeft.Y)); + + /// + /// Converts an Avalonia gradient spread method to an ImageSharp gradient repetition mode. + /// + /// The Avalonia spread method. + /// The converted ImageSharp repetition mode. + public static GradientRepetitionMode ToGradientRepetitionMode(this GradientSpreadMethod spreadMethod) + => spreadMethod switch + { + GradientSpreadMethod.Reflect => GradientRepetitionMode.Reflect, + GradientSpreadMethod.Repeat => GradientRepetitionMode.Repeat, + _ => GradientRepetitionMode.None + }; + + /// + /// Converts an Avalonia geometry combine mode to an ImageSharp boolean operation. + /// + /// The Avalonia combine mode. + /// The converted ImageSharp boolean operation. + public static BooleanOperation ToBooleanOperation(this GeometryCombineMode combineMode) + => combineMode switch + { + GeometryCombineMode.Intersect => BooleanOperation.Intersection, + GeometryCombineMode.Xor => BooleanOperation.Xor, + GeometryCombineMode.Exclude => BooleanOperation.Difference, + _ => BooleanOperation.Union + }; + + /// + /// Converts an Avalonia pen to ImageSharp stroke options. + /// + /// The Avalonia pen. + /// The converted ImageSharp stroke options. + public static StrokeOptions ToStrokeOptions(this IPen pen) + => new() + { + MiterLimit = pen.MiterLimit, + LineCap = pen.LineCap switch + { + PenLineCap.Round => LineCap.Round, + PenLineCap.Square => LineCap.Square, + _ => LineCap.Butt + }, + LineJoin = pen.LineJoin switch + { + PenLineJoin.Round => LineJoin.Round, + PenLineJoin.Bevel => LineJoin.Bevel, + _ => LineJoin.Miter + } + }; + + /// + /// Converts an Avalonia dash style to an ImageSharp stroke pattern. + /// + /// The Avalonia pen. + /// The converted stroke pattern. + public static float[] ToStrokePattern(this IPen pen) + { + IReadOnlyList? dashes = pen.DashStyle?.Dashes; + if (dashes is null || dashes.Count < 2) + { + return []; + } + + float[] pattern = new float[dashes.Count]; + for (int i = 0; i < pattern.Length; i++) + { + pattern[i] = (float)dashes[i]; + } + + return pattern; + } + + /// + /// Converts an Avalonia dash offset to an ImageSharp stroke pattern offset. + /// + /// The Avalonia pen. + /// The converted stroke pattern offset. + public static float ToStrokePatternOffset(this IPen pen) + => pen.DashStyle is null ? 0 : (float)pen.DashStyle.Offset; + + /// + /// Creates a stroked ImageSharp path from the supplied source path and Avalonia pen. + /// + /// The source ImageSharp path. + /// The Avalonia pen. + /// The stroked ImageSharp path. + public static IPath CreateStrokePath(this IPath path, IPen pen) + { + float[] pattern = pen.ToStrokePattern(); + StrokeOptions options = pen.ToStrokeOptions(); + + return pattern.Length >= 2 + ? path.GenerateOutline((float)pen.Thickness, pattern, pen.ToStrokePatternOffset(), options) + : path.GenerateOutline((float)pen.Thickness, options); + } + + /// + /// Converts an Avalonia pen and resolved brush to an ImageSharp pen. + /// + /// The Avalonia pen. + /// The resolved ImageSharp brush. + /// The converted ImageSharp pen. + public static Pen ToPen(this IPen pen, Brush brush) + { + float[] pattern = pen.ToStrokePattern(); + PenOptions options = new(brush, (float)pen.Thickness, pattern) + { + StrokePatternOffset = pen.ToStrokePatternOffset(), + StrokeOptions = pen.ToStrokeOptions() + }; + + return pattern.Length >= 2 ? new PatternPen(options) : new SolidPen(options); + } + + /// + /// Creates an ImageSharp font for the supplied family name and size. + /// + /// The font family name. + /// The font size. + /// The created ImageSharp font. + public static Font ToFont(this string familyName, float size) + { + if (SystemFonts.TryGet(familyName, out FontFamily family)) + { + return family.CreateFont(size); + } + + return SystemFonts.CreateFont("Segoe UI", size); + } +} diff --git a/samples/AvaloniaControlCatalog/ImmutableBitmapImpl.cs b/samples/AvaloniaControlCatalog/ImmutableBitmapImpl.cs new file mode 100644 index 00000000..e7e6716d --- /dev/null +++ b/samples/AvaloniaControlCatalog/ImmutableBitmapImpl.cs @@ -0,0 +1,274 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Transforms; + +namespace AvaloniaControlCatalog; + +/// +/// ImageSharp-backed immutable bitmap implementation for the sample Avalonia renderer. +/// +internal sealed unsafe class ImmutableBitmapImpl : IDrawableBitmapImpl, IReadableBitmapImpl +{ + private readonly object sync = new(); + private readonly PixelFormat format; + private readonly AlphaFormat alphaFormat; + + /// + /// Initializes a new immutable bitmap from an encoded image stream. + /// + /// The encoded image stream. + public ImmutableBitmapImpl(Stream stream) + : this(SixLabors.ImageSharp.Image.Load(WriteableBitmapImpl.ImageDecoderOptions, stream)) + { + } + + /// + /// Initializes a new immutable bitmap by resizing an existing immutable bitmap. + /// + /// The source bitmap. + /// The destination size in pixels. + /// The interpolation mode used when scaling. + public ImmutableBitmapImpl(ImmutableBitmapImpl source, PixelSize destinationSize, BitmapInterpolationMode interpolationMode) + { + Image image = new(WriteableBitmapImpl.ImageConfiguration, destinationSize.Width, destinationSize.Height); + this.Image = image; + this.Dpi = source.Dpi; + this.PixelSize = destinationSize; + this.format = PixelFormat.Bgra8888; + this.alphaFormat = Avalonia.Platform.AlphaFormat.Premul; + + bool isUpscaling = destinationSize.Width > source.PixelSize.Width || destinationSize.Height > source.PixelSize.Height; + IResampler sampler = interpolationMode.ToResampler(isUpscaling); + Rectangle sourceRect = new(0, 0, source.PixelSize.Width, source.PixelSize.Height); + RectangleF destinationRect = new(0, 0, destinationSize.Width, destinationSize.Height); + + using DrawingContextImpl context = new(image, this.Dpi); + source.Draw(context, sourceRect, destinationRect, sampler, 1, PixelAlphaCompositionMode.Src); + } + + /// + /// Initializes a new immutable bitmap decoded to fit the specified width or height. + /// + /// The encoded image stream. + /// The target width or height. + /// Whether is a width. + /// The interpolation mode used if the decoder resizes. + public ImmutableBitmapImpl(Stream stream, int decodeSize, bool horizontal, BitmapInterpolationMode interpolationMode) + : this( + SixLabors.ImageSharp.Image.Load( + new() + { + Configuration = WriteableBitmapImpl.ImageConfiguration, + TargetSize = horizontal + ? new SixLabors.ImageSharp.Size(decodeSize, 0) + : new SixLabors.ImageSharp.Size(0, decodeSize), + Sampler = interpolationMode.ToResampler(isUpscaling: false) + }, + stream)) + { + } + + /// + /// Initializes a new immutable bitmap from a copy of external pixel data. + /// + /// The bitmap size in pixels. + /// The bitmap DPI. + /// The byte stride between rows. + /// The source pixel format. + /// The source alpha format. + /// The source pixel data. + public ImmutableBitmapImpl(PixelSize size, Vector dpi, int stride, PixelFormat format, AlphaFormat alphaFormat, IntPtr data) + : this(CloneExternalPixels(size, stride, format, alphaFormat, data), dpi, format, alphaFormat) + { + } + + /// + /// Initializes a new immutable bitmap wrapping an existing ImageSharp image. + /// + /// The ImageSharp image backing the bitmap. + public ImmutableBitmapImpl(Image image) + : this(image, image.Metadata.ToDpi()) + { + } + + /// + /// Initializes a new immutable bitmap wrapping an existing ImageSharp image. + /// + /// The ImageSharp image backing the bitmap. + /// The bitmap DPI. + public ImmutableBitmapImpl(Image image, Vector dpi) + : this(image, dpi, PixelFormat.Bgra8888, Avalonia.Platform.AlphaFormat.Premul) + { + } + + /// + /// Initializes a new immutable bitmap with its stored pixel and alpha formats. + /// + /// The ImageSharp image backing the bitmap. + /// The bitmap DPI. + /// The stored pixel format. + /// The stored alpha format. + private ImmutableBitmapImpl(Image image, Vector dpi, PixelFormat format, AlphaFormat alphaFormat) + { + this.Image = image; + this.Dpi = dpi; + this.PixelSize = new PixelSize(image.Width, image.Height); + this.format = format; + this.alphaFormat = alphaFormat; + } + + /// + /// Gets the ImageSharp pixels drawn for this bitmap. + /// + public Image Image { get; } + + /// + public Vector Dpi { get; } + + /// + public PixelSize PixelSize { get; } + + /// + public int Version { get; } = 1; + + /// + public PixelFormat? Format => this.format; + + /// + public AlphaFormat? AlphaFormat => this.alphaFormat; + + /// + public void Draw( + DrawingContextImpl context, + Rectangle sourceRect, + RectangleF destinationRect, + IResampler sampler, + float opacity, + PixelAlphaCompositionMode alphaCompositionMode) + => context.DrawImage(this.Image, sourceRect, destinationRect, sampler, opacity, alphaCompositionMode); + + /// + public ILockedFramebuffer Lock() => new BitmapFramebuffer(this); + + /// + public void Save(Stream stream, BitmapEncoderOptions options) => this.Image.Save(stream, options); + + /// + public void Dispose() => this.Image.Dispose(); + + /// + /// Copies external pixel memory into an ImageSharp image. + /// + private static Image CloneExternalPixels(PixelSize size, int stride, PixelFormat format, AlphaFormat alphaFormat, IntPtr data) + { + if (format == PixelFormats.Bgra8888) + { + return alphaFormat == Avalonia.Platform.AlphaFormat.Premul + ? CopyExternalPixels(size, stride, data) + : CopyExternalPixels(size, stride, data); + } + + if (format == PixelFormats.Rgba8888) + { + return alphaFormat == Avalonia.Platform.AlphaFormat.Premul + ? CopyExternalPixels(size, stride, data) + : CopyExternalPixels(size, stride, data); + } + + // Avalonia's Rgb565 and ImageSharp's Bgr565 share the same packed 5-6-5 byte layout. + return CopyExternalPixels(size, stride, data); + } + + /// + /// Copies external rows into owned ImageSharp storage without changing their pixel representation. + /// + /// The ImageSharp pixel type matching the supplied Avalonia format. + /// The bitmap size in pixels. + /// The signed byte stride between source rows. + /// The address of the first logical source row. + /// The owned image containing the copied bytes. + private static Image CopyExternalPixels(PixelSize size, int stride, IntPtr data) + where TPixel : unmanaged, IPixel + { + Image image = new(WriteableBitmapImpl.ImageConfiguration, size.Width, size.Height); + + for (int y = 0; y < size.Height; y++) + { + Span destinationRow = MemoryMarshal.AsBytes(image.DangerousGetPixelRowMemory(y).Span); + + // Avalonia addresses the first logical row even for bottom-up buffers. Advancing by the + // signed stride therefore preserves logical row order without reinterpreting any pixels. + ReadOnlySpan sourceRow = new((byte*)data + (y * stride), destinationRow.Length); + sourceRow.CopyTo(destinationRow); + } + + return image; + } + + /// + /// Framebuffer view over the bitmap's own contiguous ImageSharp pixel buffer. + /// + private sealed class BitmapFramebuffer : ILockedFramebuffer, IImageVisitor + { + private readonly ImmutableBitmapImpl parent; + private MemoryHandle handle; + + /// + /// Initializes a new framebuffer lock. + /// + /// The bitmap owning the pixel buffer. + public BitmapFramebuffer(ImmutableBitmapImpl parent) + { + this.parent = parent; + Monitor.Enter(parent.sync); + parent.Image.AcceptVisitor(this); + } + + /// + public void Visit(Image image) + where TPixel : unmanaged, IPixel + { + if (!image.DangerousTryGetSinglePixelMemory(out Memory memory)) + { + Monitor.Exit(this.parent.sync); + throw new InvalidOperationException("ImageSharp bitmap buffers must be contiguous to lock them as Avalonia framebuffers."); + } + + this.handle = memory.Pin(); + } + + /// + public IntPtr Address => (IntPtr)this.handle.Pointer; + + /// + public PixelSize Size => this.parent.PixelSize; + + /// + public int RowBytes => ((this.parent.format.BitsPerPixel * this.parent.PixelSize.Width) + 7) / 8; + + /// + public Vector Dpi => this.parent.Dpi; + + /// + public PixelFormat Format => this.parent.format; + + /// + public AlphaFormat AlphaFormat => this.parent.alphaFormat; + + /// + public void Dispose() + { + this.handle.Dispose(); + Monitor.Exit(this.parent.sync); + } + } +} diff --git a/samples/AvaloniaControlCatalog/LineGeometryImpl.cs b/samples/AvaloniaControlCatalog/LineGeometryImpl.cs new file mode 100644 index 00000000..2b2da6aa --- /dev/null +++ b/samples/AvaloniaControlCatalog/LineGeometryImpl.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Avalonia; +using SixLabors.ImageSharp.Drawing; + +namespace AvaloniaControlCatalog; + +/// +/// Avalonia line geometry implementation backed by an ImageSharp open path. +/// +internal sealed class LineGeometryImpl : GeometryImpl +{ + /// + /// Initializes a new line geometry. + /// + /// The line start point. + /// The line end point. + public LineGeometryImpl(Point p1, Point p2) + : base(new PathBuilder().AddLine(p1.ToPointF(), p2.ToPointF()).Build(), null, IntersectionRule.EvenOdd) + { + } +} diff --git a/samples/AvaloniaControlCatalog/MainView.xaml b/samples/AvaloniaControlCatalog/MainView.xaml new file mode 100644 index 00000000..f7b2658b --- /dev/null +++ b/samples/AvaloniaControlCatalog/MainView.xaml @@ -0,0 +1,161 @@ + + + + + + + 20 + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + BorderOnly + Full + + + + + Default + Light + Dark + + + + + Fluent + Simple + + + + + None + Transparent + Blur + AcrylicBlur + Mica + + + + + LeftToRight + RightToLeft + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/MainView.xaml.cs b/samples/AvaloniaControlCatalog/MainView.xaml.cs new file mode 100644 index 00000000..0f738320 --- /dev/null +++ b/samples/AvaloniaControlCatalog/MainView.xaml.cs @@ -0,0 +1,375 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Media; +using Avalonia.Media.Immutable; +using Avalonia.Styling; +using Avalonia.Threading; +using Avalonia.VisualTree; +using ControlCatalog.Models; +using ControlCatalog.ViewModels; +using AvaloniaCalendar = Avalonia.Controls.Calendar; + +namespace ControlCatalog +{ + public partial class MainView : DrawerPage + { + private readonly TransparentStyles _transparentStyles = new(); + + public MainView() + { + InitializeComponent(); + + Loaded += MainView_Loaded; + Unloaded += MainView_Unloaded; + } + + private const double WideBreakpoint = 1008; + private const double NarrowBreakpoint = 640; + + protected override Type StyleKeyOverride => typeof(MainView); + + private void MainView_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + { + if (DataContext == null) + return; + + SizeChanged += OnDrawerSizeChanged; + UpdateAdaptiveLayout(); + } + + private void MainView_Unloaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + { + SizeChanged -= OnDrawerSizeChanged; + _lastAppliedMode = null; + } + + private SplitViewDisplayMode? _lastAppliedMode; + private bool _updatingLayout; + + private void OnDrawerSizeChanged(object? sender, SizeChangedEventArgs e) + { + if (e.WidthChanged) + UpdateAdaptiveLayout(); + } + + private void UpdateAdaptiveLayout() + { + if (_updatingLayout || DataContext == null) + return; + + var width = Bounds.Width; + if (width <= 0) + return; + + SplitViewDisplayMode targetMode; + if (width >= WideBreakpoint) + targetMode = SplitViewDisplayMode.Inline; + else if (width >= NarrowBreakpoint) + targetMode = SplitViewDisplayMode.CompactInline; + else + targetMode = SplitViewDisplayMode.Overlay; + + if (_lastAppliedMode == targetMode) + return; + + _updatingLayout = true; + try + { + _lastAppliedMode = targetMode; + ViewModel.DisplayMode = targetMode; + + if (targetMode == SplitViewDisplayMode.Inline) + ViewModel.IsDrawerOpened = true; + else if (targetMode == SplitViewDisplayMode.Overlay) + ViewModel.IsDrawerOpened = false; + } + finally + { + _updatingLayout = false; + } + } + + private void Themes_SelectionChanged(object? sender, SelectionChangedEventArgs e) + { + if (e.AddedItems.Count > 0 && e.AddedItems[0] is CatalogTheme theme) + { + App.SetCatalogThemes(theme); + } + } + + private void ThemeVariants_SelectionChanged(object? sender, SelectionChangedEventArgs e) + { + if (Application.Current is { } app && e.AddedItems.Count > 0 && e.AddedItems[0] is ThemeVariant themeVariant) + { + app.RequestedThemeVariant = themeVariant; + } + } + + private void FlowDirection_SelectionChanged(object? sender, SelectionChangedEventArgs e) + { + if (TopLevel.GetTopLevel(this) is { } topLevel && e.AddedItems.Count > 0 && e.AddedItems[0] is FlowDirection flowDirection) + { + topLevel.FlowDirection = flowDirection; + } + } + + private void Decorations_SelectionChanged(object? sender, SelectionChangedEventArgs e) + { + if (TopLevel.GetTopLevel(this) is Window window && e.AddedItems.Count > 0 && e.AddedItems[0] is WindowDecorations systemDecorations) + { + window.WindowDecorations = systemDecorations; + } + } + + private void TransparencyLevels_SelectionChanged(object? sender, SelectionChangedEventArgs e) + { + if (TopLevel.GetTopLevel(this) is { } topLevel && e.AddedItems.Count > 0 && e.AddedItems[0] is WindowTransparencyLevel transparencyLevel) + { + topLevel.TransparencyLevelHint = [transparencyLevel]; + + if (topLevel.ActualTransparencyLevel != WindowTransparencyLevel.None && + transparencyLevel != WindowTransparencyLevel.None) + { + topLevel.Background = new ImmutableSolidColorBrush(Colors.Gray, 0.2); + if (!topLevel.Styles.Contains(_transparentStyles)) + topLevel.Styles.Add(_transparentStyles); + } + else + { + topLevel.Background = null; + topLevel.Styles.Remove(_transparentStyles); + } + } + } + + protected override void OnDataContextChanged(EventArgs e) + { + base.OnDataContextChanged(e); + + if (ViewModel != null) + { + ViewModel.Navigator = NavPage; + } + } + + internal MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext!; + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + if (DataContext == null) + return; + + UpdateAdaptiveLayout(); + + var topLevel = TopLevel.GetTopLevel(this)!; + if (topLevel is Window window) + ViewModel.SelectedDecorationIndex = (int)window.WindowDecorations; + + var insets = topLevel.InsetsManager; + if (insets != null) + { + // In real life application these events should be unsubscribed to avoid memory leaks. + ViewModel.SafeAreaPadding = insets.SafeAreaPadding; + insets.SafeAreaChanged += (sender, args) => + { + ViewModel.SafeAreaPadding = insets.SafeAreaPadding; + }; + + ViewModel.DisplayEdgeToEdge = insets.DisplayEdgeToEdgePreference; + ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true; + + ViewModel.PropertyChanged += async (sender, args) => + { + if (args.PropertyName == nameof(ViewModel.DisplayEdgeToEdge)) + { + insets.DisplayEdgeToEdgePreference = ViewModel.DisplayEdgeToEdge; + } + else if (args.PropertyName == nameof(ViewModel.IsSystemBarVisible)) + { + insets.IsSystemBarVisible = ViewModel.IsSystemBarVisible; + } + + // Give the OS some time to apply new values and refresh the view model. + await Task.Delay(100); + ViewModel.DisplayEdgeToEdge = insets.DisplayEdgeToEdgePreference; + ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true; + }; + } + + int startupPageIndex = 0; + string? startupPage = Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_PAGE"); + + if (!string.IsNullOrWhiteSpace(startupPage)) + { + for (int i = 0; i < ViewModel.Pages.Count; i++) + { + if (string.Equals(ViewModel.Pages[i].Header, startupPage, StringComparison.OrdinalIgnoreCase)) + { + startupPageIndex = i; + break; + } + } + } + + ViewModel.SelectedPageIndex = startupPageIndex; + + string? navigateToPage = Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_NAVIGATE_TO_PAGE"); + + if (!string.IsNullOrWhiteSpace(navigateToPage)) + { + // Test-only hook: changing selection after layout exercises the same dirty-region + // repaint path as clicking a navigation item in the drawer. + Dispatcher.UIThread.Post(async () => + { + await Task.Delay(350); + + string[] navigationSequence = navigateToPage.Split( + ',', + StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + foreach (string pageName in navigationSequence) + { + for (int i = 0; i < ViewModel.Pages.Count; i++) + { + if (string.Equals(ViewModel.Pages[i].Header, pageName, StringComparison.OrdinalIgnoreCase)) + { + ListBox? navigationList = this.GetVisualDescendants() + .OfType() + .FirstOrDefault(x => ReferenceEquals(x.ItemsSource, ViewModel.Pages)); + + Console.Error.WriteLine($"ControlCatalog navigation atTicks={Stopwatch.GetTimestamp()} page={pageName}"); + + if (navigationList is not null) + { + navigationList.SelectedIndex = i; + } + else + { + ViewModel.SelectedPageIndex = i; + } + + if (Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_KEEP_DRAWER_OPEN") == "1") + { + ViewModel.IsDrawerOpened = true; + } + + break; + } + } + + // Leave enough time for the selected page's layout and ordered flushes to + // finish before the next selection measures a distinct navigation. + await Task.Delay(1000); + } + }, DispatcherPriority.Loaded); + } + + if (double.TryParse( + Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_SCROLL_Y"), + NumberStyles.Float, + CultureInfo.InvariantCulture, + out double scrollY)) + { + // Test-only hook: scrolling after layout captures the same dirty-region path that a + // user scroll exercises, while keeping the sample's normal startup path untouched. + Dispatcher.UIThread.Post(async () => + { + await Task.Delay(250); + + ScrollViewer? scrollViewer = this.GetVisualDescendants() + .OfType() + .Where(x => x.Extent.Height > x.Viewport.Height && x.Viewport.Height > 0) + .OrderByDescending(x => x.Viewport.Width) + .ThenByDescending(x => x.Extent.Height - x.Viewport.Height) + .FirstOrDefault(); + + if (scrollViewer is not null) + { + int stepCount = int.TryParse( + Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_SCROLL_STEPS"), + out int parsedStepCount) + ? Math.Max(1, parsedStepCount) + : 1; + + for (int i = 1; i <= stepCount; i++) + { + scrollViewer.Offset = new Vector(scrollViewer.Offset.X, scrollY * i / stepCount); + await Task.Delay(80); + } + } + }, DispatcherPriority.Loaded); + } + + if (int.TryParse( + Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_CALENDAR_SCROLL_MONTHS"), + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int calendarScrollMonths)) + { + // Test-only hook: Calendar navigation invalidates the CalendarItem internals, which + // exercises a different dirty-region path than scrolling the page viewport. + Dispatcher.UIThread.Post(async () => + { + await Task.Delay(250); + + AvaloniaCalendar[] calendars = this.GetVisualDescendants() + .OfType() + .Where(x => x.IsVisible) + .ToArray(); + + int calendarStart = 0; + int calendarEnd = calendars.Length; + + if (int.TryParse( + Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_CALENDAR_SCROLL_INDEX"), + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out int calendarIndex)) + { + calendarStart = calendarIndex; + calendarEnd = calendarIndex + 1; + } + + using Pointer pointer = new(Pointer.GetNextFreeId(), PointerType.Mouse, true); + PointerPointProperties properties = new(RawInputModifiers.None, PointerUpdateKind.Other); + int direction = calendarScrollMonths < 0 ? 1 : -1; + int steps = Math.Abs(calendarScrollMonths); + + for (int i = 0; i < steps; i++) + { + for (int j = calendarStart; j < calendarEnd; j++) + { + AvaloniaCalendar calendar = calendars[j]; + Point position = calendar.TranslatePoint( + new Point(calendar.Bounds.Width / 2, calendar.Bounds.Height / 2), + topLevel) ?? default; + + PointerWheelEventArgs args = new( + calendar, + pointer, + topLevel, + position, + (ulong)Environment.TickCount64, + properties, + KeyModifiers.None, + new Vector(0, direction)); + + calendar.RaiseEvent(args); + } + + await Task.Delay(80); + } + }, DispatcherPriority.Loaded); + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/MainWindow.xaml b/samples/AvaloniaControlCatalog/MainWindow.xaml new file mode 100644 index 00000000..d839689d --- /dev/null +++ b/samples/AvaloniaControlCatalog/MainWindow.xaml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/MainWindow.xaml.cs b/samples/AvaloniaControlCatalog/MainWindow.xaml.cs new file mode 100644 index 00000000..d43536a0 --- /dev/null +++ b/samples/AvaloniaControlCatalog/MainWindow.xaml.cs @@ -0,0 +1,74 @@ +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; + +namespace ControlCatalog +{ + public partial class MainWindow : Window + { + private NativeMenu? _recentMenu; + + public MainWindow() + { + InitializeComponent(); + + if (TryGetStartupSize(out Size startupSize)) + { + Width = startupSize.Width; + Height = startupSize.Height; + } + + _recentMenu = ((NativeMenu.GetMenu(this)?.Items[0] as NativeMenuItem)?.Menu?.Items[2] as NativeMenuItem)?.Menu; + } + + public static string MenuQuitHeader => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "Quit Avalonia" : "E_xit"; + + public static KeyGesture MenuQuitGesture => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? + new KeyGesture(Key.Q, KeyModifiers.Meta) : + new KeyGesture(Key.F4, KeyModifiers.Alt); + + public void OnOpenClicked(object sender, EventArgs args) + { + _recentMenu?.Items.Insert(0, new NativeMenuItem("Item " + (_recentMenu.Items.Count + 1))); + } + + public void OnCloseClicked(object sender, EventArgs args) + { + Close(); + } + + /// + /// Reads the optional startup window size used by automated renderer captures. + /// + /// The parsed startup size. + /// when a valid startup size was provided. + private static bool TryGetStartupSize(out Size size) + { + size = default; + string? value = Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_WINDOW_SIZE"); + + if (string.IsNullOrWhiteSpace(value)) + { + return false; + } + + string[] parts = value.Split('x', 'X', ','); + if (parts.Length != 2) + { + return false; + } + + if (!double.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out double width) || + !double.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out double height)) + { + return false; + } + + size = new Size(width, height); + return true; + } + } +} diff --git a/samples/AvaloniaControlCatalog/MiniMvvm/MiniCommand.cs b/samples/AvaloniaControlCatalog/MiniMvvm/MiniCommand.cs new file mode 100644 index 00000000..57f6d6b4 --- /dev/null +++ b/samples/AvaloniaControlCatalog/MiniMvvm/MiniCommand.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace MiniMvvm +{ + public sealed class MiniCommand : MiniCommand, ICommand + { + private readonly Action? _cb; + private readonly Func? _acb; + private bool _busy; + + public MiniCommand(Action cb) + { + _cb = cb; + } + + public MiniCommand(Func cb) + { + _acb = cb; + } + + private bool Busy + { + get => _busy; + set + { + _busy = value; + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + } + + + public override event EventHandler? CanExecuteChanged; + + public override bool CanExecute(object? parameter) => !_busy; + + public override async void Execute(object? parameter) + { + if(Busy) + return; + try + { + Busy = true; + if (_cb != null) + _cb((T)parameter!); + else + await _acb!((T)parameter!); + } + finally + { + Busy = false; + } + } + } + + public abstract class MiniCommand : ICommand + { + public static MiniCommand Create(Action cb) => new MiniCommand(_ => cb()); + public static MiniCommand Create(Action cb) => new MiniCommand(cb); + public static MiniCommand CreateFromTask(Func cb) => new MiniCommand(_ => cb()); + + public abstract bool CanExecute(object? parameter); + public abstract void Execute(object? parameter); + public abstract event EventHandler? CanExecuteChanged; + } +} diff --git a/samples/AvaloniaControlCatalog/MiniMvvm/PropertyChangedExtensions.cs b/samples/AvaloniaControlCatalog/MiniMvvm/PropertyChangedExtensions.cs new file mode 100644 index 00000000..cc551d61 --- /dev/null +++ b/samples/AvaloniaControlCatalog/MiniMvvm/PropertyChangedExtensions.cs @@ -0,0 +1,110 @@ +using System; +using System.ComponentModel; +using System.Linq.Expressions; +using System.Reflection; +using Avalonia.Reactive; + +namespace MiniMvvm +{ + public static class PropertyChangedExtensions + { + class PropertyObservable : IObservable + { + private readonly INotifyPropertyChanged _target; + private readonly PropertyInfo _info; + + public PropertyObservable(INotifyPropertyChanged target, PropertyInfo info) + { + _target = target; + _info = info; + } + + class Subscription : IDisposable + { + private readonly INotifyPropertyChanged _target; + private readonly PropertyInfo _info; + private readonly IObserver _observer; + + public Subscription(INotifyPropertyChanged target, PropertyInfo info, IObserver observer) + { + _target = target; + _info = info; + _observer = observer; + _target.PropertyChanged += OnPropertyChanged; + _observer.OnNext((T)_info.GetValue(_target)!); + } + + private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == _info.Name) + _observer.OnNext((T)_info.GetValue(_target)!); + } + + public void Dispose() + { + _target.PropertyChanged -= OnPropertyChanged; + _observer.OnCompleted(); + } + } + + public IDisposable Subscribe(IObserver observer) + { + return new Subscription(_target, _info, observer); + } + } + + public static IObservable WhenAnyValue(this TModel model, + Expression> expr) where TModel : INotifyPropertyChanged + { + var l = (LambdaExpression)expr; + var ma = (MemberExpression)l.Body; + var prop = (PropertyInfo)ma.Member; + return new PropertyObservable(model, prop); + } + + public static IObservable WhenAnyValue(this TModel model, + Expression> v1, + Func cb + ) where TModel : INotifyPropertyChanged + { + return model.WhenAnyValue(v1).Select(cb); + } + + public static IObservable WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2, + Func cb + ) where TModel : INotifyPropertyChanged => + Observable.CombineLatest( + model.WhenAnyValue(v1), + model.WhenAnyValue(v2), + cb); + + public static IObservable> WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2 + ) where TModel : INotifyPropertyChanged => + model.WhenAnyValue(v1, v2, (a1, a2) => (a1, a2)); + + public static IObservable WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2, + Expression> v3, + Func cb + ) where TModel : INotifyPropertyChanged => + model.WhenAnyValue(v1) + .CombineLatest( + model.WhenAnyValue(v2), + (l, r) => (l, r)) + .CombineLatest( + model.WhenAnyValue(v3), + (t, r) => cb(t.l, t.r, r)); + + public static IObservable> WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2, + Expression> v3 + ) where TModel : INotifyPropertyChanged => + model.WhenAnyValue(v1, v2, v3, (a1, a2, a3) => (a1, a2, a3)); + } +} diff --git a/samples/AvaloniaControlCatalog/MiniMvvm/ViewModelBase.cs b/samples/AvaloniaControlCatalog/MiniMvvm/ViewModelBase.cs new file mode 100644 index 00000000..1b2f3b2c --- /dev/null +++ b/samples/AvaloniaControlCatalog/MiniMvvm/ViewModelBase.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace MiniMvvm +{ + public class ViewModelBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler? PropertyChanged; + + protected bool RaiseAndSetIfChanged(ref T field, T value, [CallerMemberName] string? propertyName = null) + { + if (!EqualityComparer.Default.Equals(field, value)) + { + field = value; + RaisePropertyChanged(propertyName); + return true; + } + return false; + } + + protected void RaisePropertyChanged([CallerMemberName] string? propertyName = null) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} diff --git a/samples/AvaloniaControlCatalog/Models/CatalogTheme.cs b/samples/AvaloniaControlCatalog/Models/CatalogTheme.cs new file mode 100644 index 00000000..79b3182d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Models/CatalogTheme.cs @@ -0,0 +1,8 @@ +namespace ControlCatalog.Models +{ + public enum CatalogTheme + { + Fluent, + Simple + } +} diff --git a/samples/AvaloniaControlCatalog/Models/Person.cs b/samples/AvaloniaControlCatalog/Models/Person.cs new file mode 100644 index 00000000..99bc5025 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Models/Person.cs @@ -0,0 +1,125 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using Avalonia.Media; + +namespace ControlCatalog.Models +{ + public class Person : INotifyDataErrorInfo, INotifyPropertyChanged + { + string _firstName = string.Empty; + string _lastName = string.Empty; + bool _isBanned; + private int _age; + + public string FirstName + { + get => _firstName; + set + { + _firstName = value; + if (string.IsNullOrWhiteSpace(value)) + SetError(nameof(FirstName), "First Name Required"); + else + SetError(nameof(FirstName), null); + + OnPropertyChanged(nameof(FirstName)); + } + + } + + public string LastName + { + get => _lastName; + set + { + _lastName = value; + if (string.IsNullOrWhiteSpace(value)) + SetError(nameof(LastName), "Last Name Required"); + else + SetError(nameof(LastName), null); + + OnPropertyChanged(nameof(LastName)); + } + } + + public bool IsBanned + { + get => _isBanned; + set + { + _isBanned = value; + + OnPropertyChanged(nameof(_isBanned)); + } + } + + + /// + /// Gets or sets the age of the person + /// + public int Age + { + get => _age; + set + { + _age = value; + OnPropertyChanged(nameof(Age)); + } + } + + Dictionary> _errorLookup = new Dictionary>(); + + void SetError(string propertyName, string? error) + { + if (string.IsNullOrEmpty(error)) + { + if (_errorLookup.Remove(propertyName)) + OnErrorsChanged(propertyName); + } + else + { + if (_errorLookup.TryGetValue(propertyName, out var errorList)) + { + errorList.Clear(); + errorList.Add(error!); + } + else + { + var errors = new List { error! }; + _errorLookup.Add(propertyName, errors); + } + + OnErrorsChanged(propertyName); + } + } + + public bool HasErrors => _errorLookup.Count > 0; + + public event EventHandler? ErrorsChanged; + public event PropertyChangedEventHandler? PropertyChanged; + + void OnErrorsChanged(string propertyName) + { + ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); + } + void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + public IEnumerable GetErrors(string? propertyName) + { + if (propertyName is { } && _errorLookup.TryGetValue(propertyName, out var errorList)) + return errorList; + else + return Array.Empty(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Models/StateData.cs b/samples/AvaloniaControlCatalog/Models/StateData.cs new file mode 100644 index 00000000..a7ea655c --- /dev/null +++ b/samples/AvaloniaControlCatalog/Models/StateData.cs @@ -0,0 +1,20 @@ +namespace ControlCatalog.Models; + +public class StateData +{ + public string Name { get; private set; } + public string Abbreviation { get; private set; } + public string Capital { get; private set; } + + public StateData(string name, string abbreviation, string capital) + { + Name = name; + Abbreviation = abbreviation; + Capital = capital; + } + + public override string ToString() + { + return Name; + } +} diff --git a/samples/AvaloniaControlCatalog/NavHeaderItem.xaml b/samples/AvaloniaControlCatalog/NavHeaderItem.xaml new file mode 100644 index 00000000..6867661e --- /dev/null +++ b/samples/AvaloniaControlCatalog/NavHeaderItem.xaml @@ -0,0 +1,101 @@ + + + + + + #99FFFFFF + #FF1F1F1F + #FF000000 + #FF171717 + + + #99000000 + #FFE6E6E6 + #FFFFFFFF + #FFF2F2F2 + + + 36 + 1 1 1 1 #2000, 0 0 1 1 #2fff + + + + Disabled + + Test + + Test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/NuGet.Config b/samples/AvaloniaControlCatalog/NuGet.Config new file mode 100644 index 00000000..026df9e6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/NuGet.Config @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/AcceleratorPage.xaml b/samples/AvaloniaControlCatalog/Pages/AcceleratorPage.xaml new file mode 100644 index 00000000..62fcf0d7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/AcceleratorPage.xaml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Accelerator Support + + + + + This is tab 1 content + + This is tab 1 content + + This is tab 1 content + + + + + This is tab 2 content + + + + + + This is tab 4 content + + + This is fab 5 content + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/AcceleratorPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/AcceleratorPage.xaml.cs new file mode 100644 index 00000000..40afacc0 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/AcceleratorPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class AcceleratorPage : ContentPage + { + public AcceleratorPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/AcrylicPage.xaml b/samples/AvaloniaControlCatalog/Pages/AcrylicPage.xaml new file mode 100644 index 00000000..4a68fbfa --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/AcrylicPage.xaml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/AcrylicPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/AcrylicPage.xaml.cs new file mode 100644 index 00000000..adb5b057 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/AcrylicPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class AcrylicPage : ContentPage + { + public AcrylicPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/AdornerLayerPage.xaml b/samples/AvaloniaControlCatalog/Pages/AdornerLayerPage.xaml new file mode 100644 index 00000000..35907c7e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/AdornerLayerPage.xaml @@ -0,0 +1,76 @@ + + + + Rotation + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/AdornerLayerPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/AdornerLayerPage.xaml.cs new file mode 100644 index 00000000..5ee8d444 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/AdornerLayerPage.xaml.cs @@ -0,0 +1,34 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class AdornerLayerPage : ContentPage + { + private Control? _adorner; + + public AdornerLayerPage() + { + InitializeComponent(); + } + + private void RemoveAdorner_OnClick(object? sender, RoutedEventArgs e) + { + var adorner = AdornerLayer.GetAdorner(AdornerButton); + if (adorner is { }) + { + _adorner = adorner; + } + AdornerLayer.SetAdorner(AdornerButton, null); + } + + private void AddAdorner_OnClick(object? sender, RoutedEventArgs e) + { + if (_adorner is { }) + { + AdornerLayer.SetAdorner(AdornerButton, _adorner); + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/AutoCompleteBoxPage.xaml b/samples/AvaloniaControlCatalog/Pages/AutoCompleteBoxPage.xaml new file mode 100644 index 00000000..4edb00b7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/AutoCompleteBoxPage.xaml @@ -0,0 +1,83 @@ + + + + A control into which the user can input text + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs new file mode 100644 index 00000000..1624fc56 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Data.Converters; +using Avalonia.LogicalTree; +using ControlCatalog.Models; + +namespace ControlCatalog.Pages +{ + public partial class AutoCompleteBoxPage : ContentPage + { + private static StateData[] BuildAllStates() + { + return new StateData[] + { + new StateData("Alabama","AL","Montgomery"), + new StateData("Alaska","AK","Juneau"), + new StateData("Arizona","AZ","Phoenix"), + new StateData("Arkansas","AR","Little Rock"), + new StateData("California","CA","Sacramento"), + new StateData("Colorado","CO","Denver"), + new StateData("Connecticut","CT","Hartford"), + new StateData("Delaware","DE","Dover"), + new StateData("Florida","FL","Tallahassee"), + new StateData("Georgia","GA","Atlanta"), + new StateData("Hawaii","HI","Honolulu"), + new StateData("Idaho","ID","Boise"), + new StateData("Illinois","IL","Springfield"), + new StateData("Indiana","IN","Indianapolis"), + new StateData("Iowa","IA","Des Moines"), + new StateData("Kansas","KS","Topeka"), + new StateData("Kentucky","KY","Frankfort"), + new StateData("Louisiana","LA","Baton Rouge"), + new StateData("Maine","ME","Augusta"), + new StateData("Maryland","MD","Annapolis"), + new StateData("Massachusetts","MA","Boston"), + new StateData("Michigan","MI","Lansing"), + new StateData("Minnesota","MN","St. Paul"), + new StateData("Mississippi","MS","Jackson"), + new StateData("Missouri","MO","Jefferson City"), + new StateData("Montana","MT","Helena"), + new StateData("Nebraska","NE","Lincoln"), + new StateData("Nevada","NV","Carson City"), + new StateData("New Hampshire","NH","Concord"), + new StateData("New Jersey","NJ","Trenton"), + new StateData("New Mexico","NM","Santa Fe"), + new StateData("New York","NY","Albany"), + new StateData("North Carolina","NC","Raleigh"), + new StateData("North Dakota","ND","Bismarck"), + new StateData("Ohio","OH","Columbus"), + new StateData("Oklahoma","OK","Oklahoma City"), + new StateData("Oregon","OR","Salem"), + new StateData("Pennsylvania","PA","Harrisburg"), + new StateData("Rhode Island","RI","Providence"), + new StateData("South Carolina","SC","Columbia"), + new StateData("South Dakota","SD","Pierre"), + new StateData("Tennessee","TN","Nashville"), + new StateData("Texas","TX","Austin"), + new StateData("Utah","UT","Salt Lake City"), + new StateData("Vermont","VT","Montpelier"), + new StateData("Virginia","VA","Richmond"), + new StateData("Washington","WA","Olympia"), + new StateData("West Virginia","WV","Charleston"), + new StateData("Wisconsin","WI","Madison"), + new StateData("Wyoming","WY","Cheyenne"), + }; + } + public StateData[] States { get; private set; } + + private static LinkedList[] BuildAllSentences() + { + return new string[] + { + "Hello world", + "No this is Patrick", + "Never gonna give you up", + "How does one patch KDE2 under FreeBSD" + } + .Select(x => new LinkedList(x.Split(' '))) + .ToArray(); + } + public LinkedList[] Sentences { get; private set; } + + public AutoCompleteBoxPage() + { + InitializeComponent(); + + States = BuildAllStates(); + Sentences = BuildAllSentences(); + + foreach (AutoCompleteBox box in GetAllAutoCompleteBox().Where(x => x.Name != "CustomAutocompleteBox")) + { + box.ItemsSource = States; + } + + var converter = new FuncMultiValueConverter(parts => + { + return String.Format("{0} ({1})", parts.ToArray()); + }); + var binding = new MultiBinding { Converter = converter }; + binding.Bindings.Add(CompiledBinding.Create(s => s.Name)); + binding.Bindings.Add(CompiledBinding.Create(s => s.Abbreviation)); + + MultiBindingBox.ValueMemberBinding = binding; + + AsyncBox.AsyncPopulator = PopulateAsync; + + CustomAutocompleteBox.ItemsSource = Sentences.SelectMany(x => x); + CustomAutocompleteBox.TextFilter = LastWordContains; + CustomAutocompleteBox.TextSelector = AppendWord; + } + private IEnumerable GetAllAutoCompleteBox() + { + return + this.GetLogicalDescendants() + .OfType(); + } + + private static bool StringContains(string str, string? query) + { + if (query == null) + return false; + return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0; + } + private async Task> PopulateAsync(string? searchText, CancellationToken cancellationToken) + { + await Task.Delay(TimeSpan.FromSeconds(1.5), cancellationToken); + + return + States.Where(data => StringContains(data.Name, searchText) || StringContains(data.Capital, searchText)) + .ToList(); + } + + private bool LastWordContains(string? searchText, string? item) + { + var words = searchText?.Split(' ') ?? Array.Empty(); + var options = Sentences.Select(x => x.First) + .ToArray?>(); + for (var i = 0; i < words.Length; ++i) + { + var word = words[i]; + for (var j = 0; word is { } && j < options.Length; ++j) + { + if (options[i] is { } option) + { + if (i == words.Length - 1) + { + options[j] = option.Value.ToLower().Contains(word.ToLower()) ? option : null; + } + else + { + options[j] = option.Value.Equals(word, StringComparison.InvariantCultureIgnoreCase) ? option.Next : null; + } + } + } + } + + return options.Any(x => x != null && x.Value == item); + } + private string AppendWord(string? text, string? item) + { + if (item is { }) + { + string[] parts = text?.Split(' ') ?? Array.Empty(); + if (parts.Length == 0) + return item; + + parts[parts.Length - 1] = item; + return string.Join(" ", parts); + } + return string.Empty; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/BitmapCachePage.axaml b/samples/AvaloniaControlCatalog/Pages/BitmapCachePage.axaml new file mode 100644 index 00000000..4298f29e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/BitmapCachePage.axaml @@ -0,0 +1,49 @@ + + + + + + + + + + Render at scale + + Scale + + Enable clear type + + Snap to device pixels + Subpixel offset X + + + + + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/BitmapCachePage.axaml.cs b/samples/AvaloniaControlCatalog/Pages/BitmapCachePage.axaml.cs new file mode 100644 index 00000000..be7cf7a7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/BitmapCachePage.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace ControlCatalog.Pages; + +public partial class BitmapCachePage : ContentPage +{ + public BitmapCachePage() + { + InitializeComponent(); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/BorderPage.xaml b/samples/AvaloniaControlCatalog/Pages/BorderPage.xaml new file mode 100644 index 00000000..297e3fcf --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/BorderPage.xaml @@ -0,0 +1,65 @@ + + + + + + + A control which decorates a child with a border and background + + + + Border + + + Background And CenterBorder + + + Background And InnerBorderEdge + + + Background And OuterBorderEdge + + + Rounded Corners + + + Rounded Corners + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/BorderPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/BorderPage.xaml.cs new file mode 100644 index 00000000..71aebf24 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/BorderPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class BorderPage : ContentPage + { + public BorderPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ButtonSpinnerPage.xaml b/samples/AvaloniaControlCatalog/Pages/ButtonSpinnerPage.xaml new file mode 100644 index 00000000..c21a772c --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ButtonSpinnerPage.xaml @@ -0,0 +1,34 @@ + + + + The ButtonSpinner control allows you to add button spinners to any element and then respond to the Spin event to manipulate that element. + + + AllowSpin + ShowButtonSpinner + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ButtonSpinnerPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ButtonSpinnerPage.xaml.cs new file mode 100644 index 00000000..28f4b200 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ButtonSpinnerPage.xaml.cs @@ -0,0 +1,49 @@ +using System; +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class ButtonSpinnerPage : ContentPage + { + public ButtonSpinnerPage() + { + InitializeComponent(); + } + + public void OnSpin(object sender, SpinEventArgs e) + { + var spinner = (ButtonSpinner)sender; + + if (spinner.Content is TextBlock txtBox) + { + int value = Array.IndexOf(_mountains, txtBox.Text); + if (e.Direction == SpinDirection.Increase) + value++; + else + value--; + + if (value < 0) + value = _mountains.Length - 1; + else if (value >= _mountains.Length) + value = 0; + + txtBox.Text = _mountains[value]; + } + + } + + private readonly string[] _mountains = new[] + { + "Everest", + "K2 (Mount Godwin Austen)", + "Kangchenjunga", + "Lhotse", + "Makalu", + "Cho Oyu", + "Dhaulagiri", + "Manaslu", + "Nanga Parbat", + "Annapurna" + }; + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ButtonsPage.xaml b/samples/AvaloniaControlCatalog/Pages/ButtonsPage.xaml new file mode 100644 index 00000000..695ae285 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ButtonsPage.xaml @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A standard button control + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A button control with multiple states: checked, unchecked or indeterminate. + + + + + + + + + + + + + + A button control that raises its Click event repeatedly when it is pressed and held. + + + + + + + + + + + + + + + + A button control that functions as a navigateable hyperlink. + + + + + + + + + + + + + + + + + + + + + + A button with an added drop-down chevron to visually indicate it has a flyout with additional actions. + + + + + + + + + + + + Disabled + + + + + + + + + A button with primary and secondary parts that can each be pressed separately. The primary part behaves like a Button and the secondary part opens a flyout. + + + + + + + + + + + + + + + + + + + + + + + + A button with primary and secondary parts that can each be pressed separately. The primary part behaves like a ToggleButton with two states and the secondary part opens a flyout. + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ButtonsPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ButtonsPage.xaml.cs new file mode 100644 index 00000000..611fc762 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ButtonsPage.xaml.cs @@ -0,0 +1,21 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class ButtonsPage : ContentPage + { + private int repeatButtonClickCount = 0; + + public ButtonsPage() + { + InitializeComponent(); + } + + public void OnRepeatButtonClick(object? sender, RoutedEventArgs args) + { + repeatButtonClickCount++; + RepeatButtonTextBlock.Text = $"Repeat Button: {repeatButtonClickCount}"; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml b/samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml new file mode 100644 index 00000000..769dbc0d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml @@ -0,0 +1,56 @@ + + + A control for selecting dates with a calendar drop-down + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml.cs new file mode 100644 index 00000000..0e4fcbd9 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml.cs @@ -0,0 +1,23 @@ +using System; +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class CalendarDatePickerPage : ContentPage + { + public CalendarDatePickerPage() + { + InitializeComponent(); + + DatePicker1.SelectedDate = DateTime.Today; + DatePicker2.SelectedDate = DateTime.Today.AddDays(10); + DatePicker3.SelectedDate = DateTime.Today.AddDays(20); + DatePicker5.SelectedDate = DateTime.Today; + + DatePicker4.TemplateApplied += (s, e) => + { + DatePicker4.BlackoutDates?.AddDatesInPast(); + }; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml b/samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml new file mode 100644 index 00000000..99727e91 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml @@ -0,0 +1,53 @@ + + + A calendar control for selecting dates + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml.cs new file mode 100644 index 00000000..d9118326 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml.cs @@ -0,0 +1,20 @@ +using System; +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class CalendarPage : ContentPage + { + public CalendarPage() + { + InitializeComponent(); + + var today = DateTime.Today; + DisplayDatesCalendar.DisplayDateStart = today.AddDays(-25); + DisplayDatesCalendar.DisplayDateEnd = today.AddDays(25); + + BlackoutDatesCalendar.BlackoutDates.AddDatesInPast(); + BlackoutDatesCalendar.BlackoutDates.Add(new CalendarDateRange(today.AddDays(6))); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/CanvasPage.xaml b/samples/AvaloniaControlCatalog/Pages/CanvasPage.xaml new file mode 100644 index 00000000..a9e34218 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CanvasPage.xaml @@ -0,0 +1,38 @@ + + + A panel which lays out its children by explicit coordinates + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CanvasPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/CanvasPage.xaml.cs new file mode 100644 index 00000000..b94aabca --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CanvasPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class CanvasPage : ContentPage + { + public CanvasPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/CarouselDemoPage.xaml b/samples/AvaloniaControlCatalog/Pages/CarouselDemoPage.xaml new file mode 100644 index 00000000..792441dd --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CarouselDemoPage.xaml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CarouselDemoPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/CarouselDemoPage.xaml.cs new file mode 100644 index 00000000..0d59d296 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CarouselDemoPage.xaml.cs @@ -0,0 +1,91 @@ +using System; +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class CarouselDemoPage : ContentPage + { + private static readonly (string Group, string Title, string Description, Func Factory)[] Demos = + { + // Overview + ("Overview", "First Look", + "Basic CarouselPage with three pages and page indicator.", + () => new CarouselPageFirstLookPage()), + + // Populate + ("Populate", "Data Templates", + "Bind CarouselPage to an ObservableCollection, add or remove pages at runtime, and switch the page template.", + () => new CarouselPageDataTemplatePage()), + + // Appearance + ("Appearance", "Customization", + "Switch slide direction between horizontal and vertical with PageSlide. Page indicator dots update on each selection.", + () => new CarouselPageCustomizationPage()), + + // Features + ("Features", "Page Transitions", + "Animate page switches with CrossFade or PageSlide.", + () => new CarouselPageTransitionsPage()), + ("Features", "Programmatic Selection", + "Jump to any page programmatically with SelectedIndex and respond to SelectionChanged events.", + () => new CarouselPageSelectionPage()), + ("Features", "Gesture & Keyboard", + "Swipe left/right to navigate pages. Toggle IsGestureEnabled and IsKeyboardNavigationEnabled.", + () => new CarouselPageGesturePage()), + ("Features", "Events", + "SelectionChanged, NavigatedTo, and NavigatedFrom events. Swipe or navigate to see the live event log.", + () => new CarouselPageEventsPage()), + + // Performance + ("Performance", "Performance Monitor", + "Track page count, live page instances, and managed heap size. Observe how GC reclaims memory after removing pages.", + () => new CarouselPagePerformancePage()), + + // Showcases + ("Showcases", "Sanctuary", + "Travel discovery app with 3 full-screen immersive pages. Each page has a real background photo, gradient overlay, and themed content. Built as a 1:1 replica of a Stitch design.", + () => new SanctuaryShowcasePage()), + ("Showcases", "Care Companion", + "Healthcare onboarding with CarouselPage (3 pages), then a TabbedPage patient dashboard. Skip or complete onboarding to navigate to the dashboard via RemovePage.", + () => new CareCompanionAppPage()), + + // Carousel (ItemsControl) demos + ("Carousel", "Getting Started", + "Basic Carousel with image items and previous/next navigation buttons.", + () => new CarouselGettingStartedPage()), + ("Carousel", "Transitions", + "Configure page transitions: PageSlide, CrossFade, 3D Rotation, or None.", + () => new CarouselTransitionsPage()), + ("Carousel", "Customization", + "Adjust orientation and transition type to tailor the carousel layout.", + () => new CarouselCustomizationPage()), + ("Carousel", "Gestures & Keyboard", + "Navigate items via swipe gesture and arrow keys. Toggle each input mode on and off.", + () => new CarouselGesturesPage()), + ("Carousel", "Vertical Orientation", + "Carousel with Orientation set to Vertical, navigated with Up/Down keys, swipe, or buttons.", + () => new CarouselVerticalPage()), + ("Carousel", "Multi-Item Peek", + "Adjust ViewportFraction to show multiple items simultaneously with adjacent cards peeking.", + () => new CarouselMultiItemPage()), + ("Carousel", "Data Binding", + "Bind Carousel to an ObservableCollection and add, remove, or shuffle items at runtime.", + () => new CarouselDataBindingPage()), + ("Carousel", "Curated Gallery", + "Editorial art gallery app with DrawerPage navigation, hero Carousel with PipsPager dots, and a horizontal peek carousel for collection highlights.", + () => new CarouselGalleryAppPage()), + }; + + public CarouselDemoPage() + { + InitializeComponent(); + Loaded += OnLoaded; + } + + private async void OnLoaded(object? sender, RoutedEventArgs e) + { + await SampleNav.PushAsync(NavigationDemoHelper.CreateGalleryHomePage(SampleNav, Demos), null); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/CarouselPage.xaml b/samples/AvaloniaControlCatalog/Pages/CarouselPage.xaml new file mode 100644 index 00000000..bdb40b90 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CarouselPage.xaml @@ -0,0 +1,119 @@ + + + A swipeable items control that can reveal adjacent pages with ViewportFraction. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Transition + + None + Page Slide + Cross Fade + Rotate 3D + Card Stack + Wave Reveal + Composite (Slide + Fade) + + + Orientation + + Horizontal + Vertical + + + Viewport Fraction + + + + 1.00 + + + + + + + + + Wrap Selection + Swipe Enabled + + + + + + + + Total Items: + 0 + + + Selected Index: + 0 + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CarouselPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/CarouselPage.xaml.cs new file mode 100644 index 00000000..c4a6ffa1 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CarouselPage.xaml.cs @@ -0,0 +1,149 @@ +using System; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using ControlCatalog.Pages.Transitions; + +namespace ControlCatalog.Pages +{ + public partial class CarouselPage : ContentPage + { + public CarouselPage() + { + InitializeComponent(); + + left.Click += (s, e) => carousel.Previous(); + right.Click += (s, e) => carousel.Next(); + transition.SelectionChanged += TransitionChanged; + orientation.SelectionChanged += TransitionChanged; + viewportFraction.ValueChanged += ViewportFractionChanged; + + wrapSelection.IsChecked = carousel.WrapSelection; + wrapSelection.IsCheckedChanged += (s, e) => + { + carousel.WrapSelection = wrapSelection.IsChecked ?? false; + UpdateButtonState(); + }; + + swipeEnabled.IsChecked = carousel.IsSwipeEnabled; + swipeEnabled.IsCheckedChanged += (s, e) => + { + carousel.IsSwipeEnabled = swipeEnabled.IsChecked ?? false; + }; + + carousel.PropertyChanged += (s, e) => + { + if (e.Property == SelectingItemsControl.SelectedIndexProperty) + { + UpdateButtonState(); + } + else if (e.Property == Carousel.ViewportFractionProperty) + { + UpdateViewportFractionDisplay(); + } + }; + + carousel.ViewportFraction = viewportFraction.Value; + UpdateButtonState(); + UpdateViewportFractionDisplay(); + } + + private void UpdateButtonState() + { + itemsCountIndicator.Text = carousel.ItemCount.ToString(); + selectedIndexIndicator.Text = carousel.SelectedIndex.ToString(); + + var wrap = carousel.WrapSelection; + left.IsEnabled = wrap || carousel.SelectedIndex > 0; + right.IsEnabled = wrap || carousel.SelectedIndex < carousel.ItemCount - 1; + } + + private void ViewportFractionChanged(object? sender, RangeBaseValueChangedEventArgs e) + { + carousel.ViewportFraction = Math.Round(e.NewValue, 2); + UpdateViewportFractionDisplay(); + } + + private void UpdateViewportFractionDisplay() + { + var value = carousel.ViewportFraction; + viewportFractionIndicator.Text = value.ToString("0.00"); + + var pagesInView = 1d / value; + viewportFractionHint.Text = value >= 1d + ? "1.00 shows a single full page." + : $"{pagesInView:0.##} pages fit in view. Try 0.80 for peeking or 0.33 for three full items."; + } + + private void TransitionChanged(object? sender, SelectionChangedEventArgs e) + { + var isVertical = orientation.SelectedIndex == 1; + var axis = isVertical ? PageSlide.SlideAxis.Vertical : PageSlide.SlideAxis.Horizontal; + + switch (transition.SelectedIndex) + { + case 0: + carousel.PageTransition = null; + break; + case 1: + carousel.PageTransition = new PageSlide(TimeSpan.FromSeconds(0.25), axis); + break; + case 2: + carousel.PageTransition = new CrossFade(TimeSpan.FromSeconds(0.25)); + break; + case 3: + carousel.PageTransition = new Rotate3DTransition(TimeSpan.FromSeconds(0.5), axis); + break; + case 4: + carousel.PageTransition = new CardStackPageTransition(TimeSpan.FromSeconds(0.5), axis); + break; + case 5: + carousel.PageTransition = new WaveRevealPageTransition(TimeSpan.FromSeconds(0.8), axis); + break; + case 6: + carousel.PageTransition = new CompositePageTransition + { + PageTransitions = + { + new PageSlide(TimeSpan.FromSeconds(0.25), axis), + new CrossFade(TimeSpan.FromSeconds(0.25)), + } + }; + break; + } + + UpdateLayoutForOrientation(isVertical); + } + + private void UpdateLayoutForOrientation(bool isVertical) + { + if (isVertical) + { + Grid.SetColumn(left, 1); + Grid.SetRow(left, 0); + Grid.SetColumn(right, 1); + Grid.SetRow(right, 2); + + left.Padding = new Thickness(20, 10); + right.Padding = new Thickness(20, 10); + + leftArrow.RenderTransform = new Avalonia.Media.RotateTransform(90); + rightArrow.RenderTransform = new Avalonia.Media.RotateTransform(90); + } + else + { + Grid.SetColumn(left, 0); + Grid.SetRow(left, 1); + Grid.SetColumn(right, 2); + Grid.SetRow(right, 1); + + left.Padding = new Thickness(10, 20); + right.Padding = new Thickness(10, 20); + + leftArrow.RenderTransform = null; + rightArrow.RenderTransform = null; + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/CarouselPage/CareCompanionAppPage.xaml b/samples/AvaloniaControlCatalog/Pages/CarouselPage/CareCompanionAppPage.xaml new file mode 100644 index 00000000..a11a1ea6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CarouselPage/CareCompanionAppPage.xaml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CarouselPage/CareCompanionAppPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/CarouselPage/CareCompanionAppPage.xaml.cs new file mode 100644 index 00000000..16f1ba0d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CarouselPage/CareCompanionAppPage.xaml.cs @@ -0,0 +1,1069 @@ +using System; +using System.Collections.ObjectModel; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Primitives; +using Avalonia.Data; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Styling; +using AvaCarouselPage = Avalonia.Controls.CarouselPage; + +namespace ControlCatalog.Pages; + +public partial class CareCompanionAppPage : UserControl +{ + static readonly Color Primary = Color.Parse("#137fec"); + static readonly Color PrimaryDark = Color.Parse("#0a5bb5"); + static readonly Color PrimaryLight = Color.Parse("#e0f0ff"); + static readonly Color BgLight = Color.Parse("#f6f7f8"); + static readonly Color TextDark = Color.Parse("#111827"); + static readonly Color TextMuted = Color.Parse("#64748b"); + static readonly Color CardBg = Colors.White; + static readonly Color SuccessGreen = Color.Parse("#10b981"); + static readonly Color WarningAmber = Color.Parse("#f59e0b"); + + NavigationPage? _navPage; + AvaCarouselPage? _onboarding; + ScrollViewer? _infoPanel; + + public CareCompanionAppPage() + { + InitializeComponent(); + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + _infoPanel = this.FindControl("InfoPanel"); + UpdateInfoVisibility(); + + _navPage = this.FindControl("NavPage"); + if (_navPage == null) return; + + _onboarding = BuildOnboardingCarousel(); + _ = _navPage.PushAsync(_onboarding); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == BoundsProperty) + UpdateInfoVisibility(); + } + + void UpdateInfoVisibility() + { + if (_infoPanel != null) + _infoPanel.IsVisible = Bounds.Width >= 650; + } + + static TextBlock Txt(string text, double size, FontWeight weight, Color color, + double opacity = 1, TextAlignment align = TextAlignment.Left, + TextWrapping wrap = TextWrapping.NoWrap) + => new TextBlock + { + Text = text, + FontSize = size, + FontWeight = weight, + Foreground = new SolidColorBrush(color), + Opacity = opacity, + TextAlignment = align, + TextWrapping = wrap, + }; + + static Button StyledButton(object content, IBrush bg, IBrush fg, double height, + CornerRadius radius, Thickness margin = default, double fontSize = 14, + FontWeight fontWeight = FontWeight.SemiBold, + IBrush? border = null, Thickness borderThick = default) + { + var btn = new Button + { + Content = content, + Background = bg, + Foreground = fg, + Height = height, + CornerRadius = radius, + Margin = margin, + Padding = new Thickness(16, 0), + HorizontalContentAlignment = HorizontalAlignment.Center, + VerticalContentAlignment = VerticalAlignment.Center, + BorderBrush = border, + BorderThickness = borderThick, + }; + + var over = new Style(x => x.OfType + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryMainPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryMainPage.xaml.cs new file mode 100644 index 00000000..05a4097a --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryMainPage.xaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class SanctuaryMainPage : UserControl +{ + public SanctuaryMainPage() + { + InitializeComponent(); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryShowcasePage.xaml b/samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryShowcasePage.xaml new file mode 100644 index 00000000..50864e5e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryShowcasePage.xaml @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryShowcasePage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryShowcasePage.xaml.cs new file mode 100644 index 00000000..be913706 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CarouselPage/SanctuaryShowcasePage.xaml.cs @@ -0,0 +1,70 @@ +using System.Linq; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.VisualTree; + +namespace ControlCatalog.Pages; + +public partial class SanctuaryShowcasePage : UserControl +{ + public SanctuaryShowcasePage() + { + InitializeComponent(); + } + + private void OnPage1CTA(object? sender, RoutedEventArgs e) + { + DemoCarousel.SelectedIndex = 1; + } + + private void OnPage2CTA(object? sender, RoutedEventArgs e) + { + DemoCarousel.SelectedIndex = 2; + } + + private async void OnPage3CTA(object? sender, RoutedEventArgs e) + { + var nav = this.FindAncestorOfType(); + if (nav == null) + return; + + var carouselWrapper = nav.NavigationStack.LastOrDefault(); + + var headerGrid = new Grid { ColumnDefinitions = new ColumnDefinitions("*, Auto") }; + headerGrid.Children.Add(new TextBlock + { + Text = "Sanctuary", + VerticalAlignment = VerticalAlignment.Center + }); + var closeIcon = Geometry.Parse( + "M4.397 4.397a1 1 0 0 1 1.414 0L12 10.585l6.19-6.188a1 1 0 0 1 1.414 1.414L13.413 12l6.19 6.189a1 1 0 0 1-1.414 1.414L12 13.413l-6.189 6.19a1 1 0 0 1-1.414-1.414L10.585 12 4.397 5.811a1 1 0 0 1 0-1.414z"); + var closeBtn = new Button + { + Content = new PathIcon { Data = closeIcon }, + Background = Brushes.Transparent, + BorderThickness = new Thickness(0), + Padding = new Thickness(8, 4), + VerticalAlignment = VerticalAlignment.Center + }; + Grid.SetColumn(closeBtn, 1); + headerGrid.Children.Add(closeBtn); + closeBtn.Click += async (_, _) => await nav.PopAsync(null); + + var mainPage = new ContentPage + { + Header = headerGrid, + Content = new SanctuaryMainPage() + }; + NavigationPage.SetHasBackButton(mainPage, false); + + await nav.PushAsync(mainPage); + + if (carouselWrapper != null) + { + nav.RemovePage(carouselWrapper); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/CheckBoxPage.xaml b/samples/AvaloniaControlCatalog/Pages/CheckBoxPage.xaml new file mode 100644 index 00000000..4f9c5944 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CheckBoxPage.xaml @@ -0,0 +1,29 @@ + + + A check box control + + + + _Unchecked + _Checked + _Indeterminate + Disabled + + + Three State: Unchecked + Three State: Checked + Three State: Indeterminate + Three State: Disabled + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CheckBoxPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/CheckBoxPage.xaml.cs new file mode 100644 index 00000000..b63d7084 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CheckBoxPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class CheckBoxPage : ContentPage + { + public CheckBoxPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ClipboardPage.xaml b/samples/AvaloniaControlCatalog/Pages/ClipboardPage.xaml new file mode 100644 index 00000000..86e99b93 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ClipboardPage.xaml @@ -0,0 +1,31 @@ + + + Example of ClipboardPage capabilities + + + + + + + + + + + + Precise dirty rects + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CompositionPage.axaml.cs b/samples/AvaloniaControlCatalog/Pages/CompositionPage.axaml.cs new file mode 100644 index 00000000..8f5d75f4 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CompositionPage.axaml.cs @@ -0,0 +1,357 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Threading; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Media; +using Avalonia.Media.Immutable; +using Avalonia.Rendering.Composition; +using Avalonia.Rendering.Composition.Animations; +using Avalonia.VisualTree; +using Math = System.Math; + +namespace ControlCatalog.Pages; + +public partial class CompositionPage : TabbedPage +{ + private ImplicitAnimationCollection? _implicitAnimations; + private CompositionCustomVisual? _customVisual; + private CompositionSolidColorVisual? _solidVisual; + + public CompositionPage() + { + InitializeComponent(); + AttachAnimatedSolidVisual(SolidVisualHost); + AttachCustomVisual(CustomVisualHost); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + Items.ItemsSource = CreateColorItems(); + } + + private static List CreateColorItems() + { + var list = new List(); + + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 255, 185, 0))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 231, 72, 86))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 120, 215))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 153, 188))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 122, 117, 116))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 118, 118, 118))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 255, 141, 0))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 232, 17, 35))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 99, 177))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 45, 125, 154))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 93, 90, 88))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 76, 74, 72))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 247, 99, 12))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 234, 0, 94))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 142, 140, 216))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 183, 195))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 104, 118, 138))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 105, 121, 126))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 202, 80, 16))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 195, 0, 82))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 107, 105, 214))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 3, 131, 135))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 81, 92, 107))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 74, 84, 89))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 218, 59, 1))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 227, 0, 140))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 135, 100, 184))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 178, 148))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 86, 124, 115))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 100, 124, 100))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 239, 105, 80))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 191, 0, 119))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 116, 77, 169))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 1, 133, 116))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 72, 104, 96))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 82, 94, 84))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 209, 52, 56))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 194, 57, 179))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 177, 70, 194))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 0, 204, 106))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 73, 130, 5))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 132, 117, 69))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 255, 67, 67))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 154, 0, 137))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 136, 23, 152))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 16, 137, 62))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 16, 124, 16))); + list.Add(new CompositionPageColorItem(Color.FromArgb(255, 126, 115, 95))); + + return list; + } + + private void EnsureImplicitAnimations() + { + if (_implicitAnimations == null) + { + var compositor = ElementComposition.GetElementVisual(this)!.Compositor; + + var offsetAnimation = compositor.CreateVector3KeyFrameAnimation(); + offsetAnimation.Target = "Offset"; + offsetAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue"); + offsetAnimation.Duration = TimeSpan.FromMilliseconds(400); + + var rotationAnimation = compositor.CreateScalarKeyFrameAnimation(); + rotationAnimation.Target = "RotationAngle"; + rotationAnimation.InsertKeyFrame(.5f, 0.160f); + rotationAnimation.InsertKeyFrame(1f, 0f); + rotationAnimation.Duration = TimeSpan.FromMilliseconds(400); + + var animationGroup = compositor.CreateAnimationGroup(); + animationGroup.Add(offsetAnimation); + animationGroup.Add(rotationAnimation); + + _implicitAnimations = compositor.CreateImplicitAnimationCollection(); + _implicitAnimations["Offset"] = animationGroup; + } + } + + public static void SetEnableAnimations(Border border, bool value) + { + var page = border.FindAncestorOfType(); + if (page == null) + { + border.AttachedToVisualTree += delegate { SetEnableAnimations(border, true); }; + return; + } + + if (ElementComposition.GetElementVisual(page) == null) + return; + + page.EnsureImplicitAnimations(); + if (border.GetVisualParent() is Visual visualParent + && ElementComposition.GetElementVisual(visualParent) is CompositionVisual compositionVisual) + { + compositionVisual.ImplicitAnimations = page._implicitAnimations; + } + } + + void AttachAnimatedSolidVisual(Visual v) + { + void Update() + { + if(_solidVisual == null) + return; + _solidVisual.Size = new (v.Bounds.Width / 3, v.Bounds.Height / 3); + _solidVisual.Offset = new (v.Bounds.Width / 3, v.Bounds.Height / 3, 0); + } + v.AttachedToVisualTree += delegate + { + var compositor = ElementComposition.GetElementVisual(v)?.Compositor; + if(compositor == null || _solidVisual?.Compositor == compositor) + return; + _solidVisual = compositor.CreateSolidColorVisual(); + ElementComposition.SetElementChildVisual(v, _solidVisual); + _solidVisual.Color = Colors.Red; + var animation = _solidVisual.Compositor.CreateColorKeyFrameAnimation(); + animation.InsertKeyFrame(0, Colors.Red); + animation.InsertKeyFrame(0.5f, Colors.Blue); + animation.InsertKeyFrame(1, Colors.Green); + animation.Duration = TimeSpan.FromSeconds(5); + animation.IterationBehavior = AnimationIterationBehavior.Forever; + animation.Direction = PlaybackDirection.Alternate; + _solidVisual.StartAnimation("Color", animation); + + _solidVisual.AnchorPoint = new (0, 0); + + var scale = _solidVisual.Compositor.CreateVector3KeyFrameAnimation(); + scale.Duration = TimeSpan.FromSeconds(5); + scale.IterationBehavior = AnimationIterationBehavior.Forever; + scale.InsertKeyFrame(0, new Vector3(1, 1, 0)); + scale.InsertKeyFrame(0.5f, new Vector3(1.5f, 1.5f, 0)); + scale.InsertKeyFrame(1, new Vector3(1, 1, 0)); + + _solidVisual.StartAnimation("Scale", scale); + + var center = + _solidVisual.Compositor.CreateExpressionAnimation( + "Vector3(this.Target.Size.X * 0.5, this.Target.Size.Y * 0.5, 1)"); + _solidVisual.StartAnimation("CenterPoint", center); + Update(); + }; + v.PropertyChanged += (_, a) => + { + if (a.Property == BoundsProperty) + Update(); + }; + } + + void AttachCustomVisual(Visual v) + { + void Update() + { + if (_customVisual == null) + return; + var h = (float)Math.Min(v.Bounds.Height, v.Bounds.Width / 3); + _customVisual.Size = new (v.Bounds.Width, h); + _customVisual.Offset = new (0, (v.Bounds.Height - h) / 2, 0); + } + v.AttachedToVisualTree += delegate + { + var compositor = ElementComposition.GetElementVisual(v)?.Compositor; + if(compositor == null || _customVisual?.Compositor == compositor) + return; + _customVisual = compositor.CreateCustomVisual(new CustomVisualHandler()); + ElementComposition.SetElementChildVisual(v, _customVisual); + _customVisual.SendHandlerMessage(CustomVisualHandler.StartMessage); + PreciseDirtyRectsCheckboxCustomVisualChanged(this, new()); + Update(); + }; + + v.PropertyChanged += (_, a) => + { + if (a.Property == BoundsProperty) + Update(); + }; + } + + class CustomVisualHandler : CompositionCustomVisualHandler + { + private TimeSpan _animationElapsed; + private TimeSpan? _lastServerTime; + private bool _running; + private bool _preciseDirtyRects; + + public static readonly object StopMessage = new(), + StartMessage = new(), + UsePreciseDirtyRects = new(), + UseNonPreciseDirtyRects = new(); + + private List<(Point center, double size, ImmutableSolidColorBrush brush)> _ellipses = new(); + + void UpdateRects() + { + if (_running) + { + if (_lastServerTime.HasValue) _animationElapsed += (CompositionNow - _lastServerTime.Value); + _lastServerTime = CompositionNow; + } + + _ellipses.Clear(); + + const int cnt = 20; + var maxPointSizeX = EffectiveSize.X / (cnt * 1.6); + var maxPointSizeY = EffectiveSize.Y / 4; + var pointSize = Math.Min(maxPointSizeX, maxPointSizeY); + var animationLength = TimeSpan.FromSeconds(4); + var animationStage = _animationElapsed.TotalSeconds / animationLength.TotalSeconds; + + var sinOffset = Math.Cos(_animationElapsed.TotalSeconds) * 1.5; + + for (var c = 0; c < cnt; c++) + { + var stage = (animationStage + (double)c / cnt) % 1; + var colorStage = + (animationStage + (Math.Sin(_animationElapsed.TotalSeconds * 2) + 1) / 2 + (double)c / cnt) % 1; + var posX = (EffectiveSize.X + pointSize * 3) * stage - pointSize; + var posY = (EffectiveSize.Y - pointSize) * (1 + Math.Sin(stage * 3.14 * 3 + sinOffset)) / 2 + pointSize / 2; + var opacity = Math.Sin(stage * 3.14); + + _ellipses.Add((new Point(posX, posY), pointSize / 2, new ImmutableSolidColorBrush(Color.FromArgb( + 255, + (byte)(255 - 255 * colorStage), + (byte)(255 * Math.Abs(0.5 - colorStage) * 2), + (byte)(255 * colorStage) + ), opacity))); + } + } + + public override void OnRender(ImmediateDrawingContext drawingContext) + { + if (_ellipses.Count == 0) + UpdateRects(); + + foreach(var e in _ellipses) + drawingContext.DrawEllipse(e.brush, null, e.center, e.size, e.size); + } + + public override void OnMessage(object message) + { + if (message == StartMessage) + { + _running = true; + _lastServerTime = null; + RegisterForNextAnimationFrameUpdate(); + } + else if (message == StopMessage) + _running = false; + else if (message == UsePreciseDirtyRects) + _preciseDirtyRects = true; + else if (message == UseNonPreciseDirtyRects) + _preciseDirtyRects = false; + } + + void InvalidateCurrentEllipseRects() + { + foreach (var e in _ellipses) + Invalidate(new Rect(e.center.X - e.size, e.center.Y - e.size, e.size * 2, e.size * 2)); + } + + public override void OnAnimationFrameUpdate() + { + if (_running) + { + if (_preciseDirtyRects) + InvalidateCurrentEllipseRects(); + else + Invalidate(); + UpdateRects(); + if(_preciseDirtyRects) + InvalidateCurrentEllipseRects(); + RegisterForNextAnimationFrameUpdate(); + } + } + } + + private void ButtonThreadSleep(object? sender, RoutedEventArgs e) + { + Thread.Sleep(10000); + } + + private void ButtonStartCustomVisual(object? sender, RoutedEventArgs e) + { + _customVisual?.SendHandlerMessage(CustomVisualHandler.StartMessage); + } + + private void ButtonStopCustomVisual(object? sender, RoutedEventArgs e) + { + _customVisual?.SendHandlerMessage(CustomVisualHandler.StopMessage); + } + + private void PreciseDirtyRectsCheckboxCustomVisualChanged(object sender, RoutedEventArgs e) + { + _customVisual?.SendHandlerMessage(PreciseDirtyRectsCheckboxCustomVisual?.IsChecked == true + ? CustomVisualHandler.UsePreciseDirtyRects + : CustomVisualHandler.UseNonPreciseDirtyRects); + } +} + +public class CompositionPageColorItem +{ + public Color Color { get; private set; } + + public SolidColorBrush ColorBrush + { + get { return new SolidColorBrush(Color); } + } + + public String ColorHexValue + { + get { return Color.ToString().Substring(3).ToUpperInvariant(); } + } + + public CompositionPageColorItem(Color color) + { + Color = color; + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ConnectedAnimationDemoPage.xaml b/samples/AvaloniaControlCatalog/Pages/ConnectedAnimationDemoPage.xaml new file mode 100644 index 00000000..ca4a85af --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ConnectedAnimationDemoPage.xaml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ConnectedAnimationDemoPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ConnectedAnimationDemoPage.xaml.cs new file mode 100644 index 00000000..5d666730 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ConnectedAnimationDemoPage.xaml.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; + +namespace ControlCatalog.Pages +{ + public partial class ConnectedAnimationDemoPage : ContentPage + { + private static readonly (string Group, string Title, string Description, Func Factory)[] Demos = []; + + public ConnectedAnimationDemoPage() + { + InitializeComponent(); + Loaded += OnLoaded; + } + + private async void OnLoaded(object? sender, RoutedEventArgs e) + { + await SampleNav.PushAsync(CreateHomePage(), null); + } + + private ContentPage CreateHomePage() + { + var stack = new StackPanel + { + Margin = new Avalonia.Thickness(12), + Spacing = 16 + }; + + var groups = new Dictionary(); + var groupOrder = new List(); + + foreach (var (group, title, description, factory) in Demos) + { + if (!groups.ContainsKey(group)) + { + groups[group] = new WrapPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Left + }; + groupOrder.Add(group); + } + + var demoFactory = factory; + var demoTitle = title; + + var card = new Button + { + Width = 200, + MinHeight = 80, + Margin = new Avalonia.Thickness(0, 0, 8, 8), + VerticalAlignment = VerticalAlignment.Top, + HorizontalContentAlignment = HorizontalAlignment.Left, + VerticalContentAlignment = VerticalAlignment.Top, + Padding = new Avalonia.Thickness(12, 8), + Content = new StackPanel + { + Spacing = 4, + Children = + { + new TextBlock + { + Text = title, + FontSize = 13, + FontWeight = FontWeight.SemiBold, + TextWrapping = TextWrapping.Wrap + }, + new TextBlock + { + Text = description, + FontSize = 11, + Opacity = 0.6, + TextWrapping = TextWrapping.Wrap + } + } + } + }; + + card.Click += async (s, ev) => + { + var headerGrid = new Grid { ColumnDefinitions = new ColumnDefinitions("*, Auto") }; + headerGrid.Children.Add(new TextBlock + { + Text = demoTitle, + VerticalAlignment = VerticalAlignment.Center + }); + var closeBtn = new Button + { + Content = new PathIcon + { + Data = Geometry.Parse("M4.397 4.397a1 1 0 0 1 1.414 0L12 10.585l6.19-6.188a1 1 0 0 1 1.414 1.414L13.413 12l6.19 6.189a1 1 0 0 1-1.414 1.414L12 13.413l-6.189 6.19a1 1 0 0 1-1.414-1.414L10.585 12 4.397 5.811a1 1 0 0 1 0-1.414z") + }, + Background = Brushes.Transparent, + BorderThickness = new Avalonia.Thickness(0), + Padding = new Avalonia.Thickness(8, 4), + VerticalAlignment = VerticalAlignment.Center + }; + Grid.SetColumn(closeBtn, 1); + headerGrid.Children.Add(closeBtn); + closeBtn.Click += async (_, _) => await SampleNav.PopAsync(null); + + var page = new ContentPage + { + Header = headerGrid, + Content = demoFactory(), + HorizontalContentAlignment = HorizontalAlignment.Stretch, + VerticalContentAlignment = VerticalAlignment.Stretch + }; + NavigationPage.SetHasBackButton(page, false); + await SampleNav.PushAsync(page, null); + }; + + groups[group].Children.Add(card); + } + + foreach (var groupName in groupOrder) + { + stack.Children.Add(new TextBlock + { + Text = groupName, + FontSize = 13, + FontWeight = FontWeight.SemiBold, + Margin = new Avalonia.Thickness(0, 0, 0, 4), + Opacity = 0.6 + }); + stack.Children.Add(groups[groupName]); + } + + var homePage = new ContentPage + { + Content = new ScrollViewer { Content = stack }, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + VerticalContentAlignment = VerticalAlignment.Stretch + }; + + NavigationPage.SetHasNavigationBar(homePage, false); + return homePage; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ContainerQueryPage.xaml b/samples/AvaloniaControlCatalog/Pages/ContainerQueryPage.xaml new file mode 100644 index 00000000..96f7a875 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ContainerQueryPage.xaml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ContainerQueryPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ContainerQueryPage.xaml.cs new file mode 100644 index 00000000..b4372300 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ContainerQueryPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class ContainerQueryPage : ContentPage + { + public ContainerQueryPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ContentDemoPage.xaml b/samples/AvaloniaControlCatalog/Pages/ContentDemoPage.xaml new file mode 100644 index 00000000..bcf0ca67 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ContentDemoPage.xaml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ContentDemoPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ContentDemoPage.xaml.cs new file mode 100644 index 00000000..b47dde07 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ContentDemoPage.xaml.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; + +namespace ControlCatalog.Pages +{ + public partial class ContentDemoPage : ContentPage + { + private static readonly (string Group, string Title, string Description, Func Factory)[] Demos = + { + // Overview + ("Overview", "First Look", "Basic ContentPage with header, content, and background inside a NavigationPage.", () => new ContentPageFirstLookPage()), + + // Appearance + ("Appearance", "Customization", "Adjust content alignment, background color, and padding to understand how ContentPage adapts its layout.", () => new ContentPageCustomizationPage()), + + // Features + ("Features", "CommandBar", "Attach a CommandBar to the top or bottom of a ContentPage. Add and remove items at runtime.", () => new ContentPageCommandBarPage()), + ("Features", "Safe Area", "Understand how AutomaticallyApplySafeAreaPadding absorbs platform insets.", () => new ContentPageSafeAreaPage()), + ("Features", "Events", "Observe page lifecycle events: NavigatedTo, NavigatedFrom, and Navigating.", () => new ContentPageEventsPage()), + + // Performance + ("Performance", "Performance Monitor", "Push ContentPages of varying weight (50 KB to 2 MB) and observe live instance count and managed heap. Pop pages and force GC to confirm memory is released.", () => new ContentPagePerformancePage()), + }; + + public ContentDemoPage() + { + InitializeComponent(); + Loaded += OnLoaded; + } + + private async void OnLoaded(object? sender, RoutedEventArgs e) + { + await SampleNav.PushAsync(CreateHomePage(), null); + } + + private ContentPage CreateHomePage() + { + var stack = new StackPanel + { + Margin = new Avalonia.Thickness(12), + Spacing = 16 + }; + + var groups = new Dictionary(); + var groupOrder = new List(); + + foreach (var (group, title, description, factory) in Demos) + { + if (!groups.ContainsKey(group)) + { + groups[group] = new WrapPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Left + }; + groupOrder.Add(group); + } + + var demoFactory = factory; + var demoTitle = title; + + var card = new Button + { + Width = 170, + MinHeight = 80, + Margin = new Avalonia.Thickness(0, 0, 8, 8), + VerticalAlignment = VerticalAlignment.Top, + HorizontalContentAlignment = HorizontalAlignment.Left, + VerticalContentAlignment = VerticalAlignment.Top, + Padding = new Avalonia.Thickness(12, 8), + Content = new StackPanel + { + Spacing = 4, + Children = + { + new TextBlock + { + Text = title, + FontSize = 13, + FontWeight = FontWeight.SemiBold, + TextWrapping = TextWrapping.Wrap + }, + new TextBlock + { + Text = description, + FontSize = 11, + Opacity = 0.6, + TextWrapping = TextWrapping.Wrap + } + } + } + }; + + card.Click += async (s, e) => + { + var headerGrid = new Grid { ColumnDefinitions = new ColumnDefinitions("*, Auto") }; + headerGrid.Children.Add(new TextBlock + { + Text = demoTitle, + VerticalAlignment = VerticalAlignment.Center + }); + var closeBtn = new Button + { + Content = new PathIcon + { + Data = Geometry.Parse("M4.397 4.397a1 1 0 0 1 1.414 0L12 10.585l6.19-6.188a1 1 0 0 1 1.414 1.414L13.413 12l6.19 6.189a1 1 0 0 1-1.414 1.414L12 13.413l-6.189 6.19a1 1 0 0 1-1.414-1.414L10.585 12 4.397 5.811a1 1 0 0 1 0-1.414z") + }, + Background = Brushes.Transparent, + BorderThickness = new Avalonia.Thickness(0), + Padding = new Avalonia.Thickness(8, 4), + VerticalAlignment = VerticalAlignment.Center + }; + Grid.SetColumn(closeBtn, 1); + headerGrid.Children.Add(closeBtn); + closeBtn.Click += async (_, _) => await SampleNav.PopAsync(null); + + var page = new ContentPage + { + Header = headerGrid, + Content = demoFactory(), + HorizontalContentAlignment = HorizontalAlignment.Stretch, + VerticalContentAlignment = VerticalAlignment.Stretch + }; + NavigationPage.SetHasBackButton(page, false); + await SampleNav.PushAsync(page, null); + }; + + groups[group].Children.Add(card); + } + + foreach (var groupName in groupOrder) + { + stack.Children.Add(new TextBlock + { + Text = groupName, + FontSize = 13, + FontWeight = FontWeight.SemiBold, + Margin = new Avalonia.Thickness(0, 0, 0, 4), + Opacity = 0.6 + }); + stack.Children.Add(groups[groupName]); + } + + var homePage = new ContentPage + { + Content = new ScrollViewer { Content = stack }, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + VerticalContentAlignment = VerticalAlignment.Stretch + }; + + NavigationPage.SetHasNavigationBar(homePage, false); + + return homePage; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageCommandBarPage.xaml b/samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageCommandBarPage.xaml new file mode 100644 index 00000000..0a753d25 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ContentPage/ContentPageCommandBarPage.xaml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Hello world + + + Inner context flyout + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ContextFlyoutPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ContextFlyoutPage.xaml.cs new file mode 100644 index 00000000..f25b9341 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ContextFlyoutPage.xaml.cs @@ -0,0 +1,59 @@ +using System; +using System.ComponentModel; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public partial class ContextFlyoutPage : ContentPage + { + public ContextFlyoutPage() + { + InitializeComponent(); + DataContext = new ContextPageViewModel(); + } + + private ContextPageViewModel? _model; + protected override void OnDataContextChanged(EventArgs e) + { + if (_model != null) + _model.View = null; + _model = DataContext as ContextPageViewModel; + if (_model != null) + _model.View = this; + + base.OnDataContextChanged(e); + } + + private void ContextFlyoutPage_Closing(object? sender, CancelEventArgs e) + { + e.Cancel = CancelCloseCheckBox?.IsChecked ?? false; + } + + private void ContextFlyoutPage_Opening(object? sender, EventArgs e) + { + if (e is CancelEventArgs cancelArgs) + { + cancelArgs.Cancel = CancelCloseCheckBox?.IsChecked ?? false; + } + } + + private void CloseFlyout(object? sender, RoutedEventArgs e) + { + TextBox.ContextFlyout?.Hide(); + } + + public void CustomContextRequested(object? sender, ContextRequestedEventArgs e) + { + if (sender is Border border && border.Child is TextBlock textBlock) + { + textBlock.Text = e.TryGetPosition(border, out var point) + ? $"Context was requested with pointer at: {point.X:N0}, {point.Y:N0}" + : "Context was requested without pointer"; + e.Handled = true; + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ContextMenuPage.xaml b/samples/AvaloniaControlCatalog/Pages/ContextMenuPage.xaml new file mode 100644 index 00000000..7e2c1a22 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ContextMenuPage.xaml @@ -0,0 +1,100 @@ + + + A right click menu that can be applied to any control. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ContextMenuPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ContextMenuPage.xaml.cs new file mode 100644 index 00000000..86ce8071 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ContextMenuPage.xaml.cs @@ -0,0 +1,51 @@ +using System; +using System.ComponentModel; +using Avalonia.Controls; +using Avalonia.Input; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public partial class ContextMenuPage : ContentPage + { + public ContextMenuPage() + { + InitializeComponent(); + DataContext = new ContextPageViewModel(); + } + + private ContextPageViewModel? _model; + protected override void OnDataContextChanged(EventArgs e) + { + if (_model != null) + _model.View = null; + _model = DataContext as ContextPageViewModel; + if (_model != null) + _model.View = this; + + base.OnDataContextChanged(e); + } + + private void ContextFlyoutPage_Closing(object? sender, CancelEventArgs e) + { + e.Cancel = CancelCloseCheckBox?.IsChecked ?? false; + } + + private void ContextFlyoutPage_Opening(object? sender, CancelEventArgs e) + { + e.Cancel = CancelOpenCheckBox?.IsChecked ?? false; + } + + public void CustomContextRequested(object? sender, ContextRequestedEventArgs e) + { + if (sender is Border border && border.Child is TextBlock textBlock) + { + textBlock.Text = e.TryGetPosition(border, out var point) + ? $"Context was requested with pointer at: {point.X:N0}, {point.Y:N0}" + : "Context was requested without pointer"; + e.Handled = true; + } + + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/CursorPage.xaml b/samples/AvaloniaControlCatalog/Pages/CursorPage.xaml new file mode 100644 index 00000000..ae20193c --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CursorPage.xaml @@ -0,0 +1,31 @@ + + + + Defines a cursor (mouse pointer) + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CursorPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/CursorPage.xaml.cs new file mode 100644 index 00000000..88699ac6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CursorPage.xaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public partial class CursorPage : ContentPage + { + public CursorPage() + { + InitializeComponent(); + DataContext = new CursorPageViewModel(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/CustomDrawing.xaml b/samples/AvaloniaControlCatalog/Pages/CustomDrawing.xaml new file mode 100644 index 00000000..f1cb2dab --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/CustomDrawing.xaml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DialogsPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DialogsPage.xaml.cs new file mode 100644 index 00000000..616e3d4f --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DialogsPage.xaml.cs @@ -0,0 +1,519 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Security; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Dialogs; +using Avalonia.Layout; +using Avalonia.Platform.Storage; + +namespace ControlCatalog.Pages +{ + public partial class DialogsPage : ContentPage + { + public DialogsPage() + { + InitializeComponent(); + + IStorageFolder? lastSelectedDirectory = null; + IStorageItem? lastSelectedItem = null; + bool ignoreTextChanged = false; + + var results = PickerLastResults; + var resultsVisible = PickerLastResultsVisible; + var bookmarkContainer = BookmarkContainer; + var openedFileContent = OpenedFileContent; + var openMultiple = OpenMultiple; + var currentFolderBox = CurrentFolderBox; + var useSuggestedFilter = UseSuggestedFilter; + var suggestedFilterSelector = SuggestedFilterSelector; + + currentFolderBox.TextChanged += async (sender, args) => + { + if (ignoreTextChanged) return; + + if (Enum.TryParse(currentFolderBox.Text, true, out var folderEnum)) + { + lastSelectedDirectory = await GetStorageProvider().TryGetWellKnownFolderAsync(folderEnum); + } + else if (!string.IsNullOrWhiteSpace(currentFolderBox.Text)) + { + if (!Uri.TryCreate(currentFolderBox.Text, UriKind.Absolute, out var folderLink)) + { + Uri.TryCreate("file://" + currentFolderBox.Text, UriKind.Absolute, out folderLink); + } + + if (folderLink is not null) + { + try + { + lastSelectedDirectory = await GetStorageProvider().TryGetFolderFromPathAsync(folderLink); + } + catch (SecurityException) + { + + } + } + } + }; + + + List? BuildFileTypes() + { + var selectedItem = (FilterSelector.SelectedItem as ComboBoxItem)?.Content + ?? "None"; + + var binLogType = new FilePickerFileType("Binary Log") + { + Patterns = new[] { "*.binlog", "*.buildlog" }, + MimeTypes = new[] { "application/binlog", "application/buildlog" }, + AppleUniformTypeIdentifiers = new[] { "public.data" } + }; + + return selectedItem switch + { + "All + TXT + BinLog" => new List + { + FilePickerFileTypes.All, FilePickerFileTypes.TextPlain, binLogType + }, + "Binlog" => new List { binLogType }, + "TXT extension only" => new List + { + new("TXT") { Patterns = FilePickerFileTypes.TextPlain.Patterns } + }, + "TXT mime only" => new List + { + new("TXT") { MimeTypes = FilePickerFileTypes.TextPlain.MimeTypes } + }, + "TXT apple type id only" => new List + { + new("TXT") + { + AppleUniformTypeIdentifiers = + FilePickerFileTypes.TextPlain.AppleUniformTypeIdentifiers + } + }, + _ => null + }; + } + + List? GetFileTypes() + { + var types = BuildFileTypes(); + UpdateSuggestedFilterSelector(types); + return types; + } + + void UpdateSuggestedFilterSelector(IReadOnlyList? types) + { + var previouslySelected = (suggestedFilterSelector.SelectedItem as ComboBoxItem)?.Tag as FilePickerFileType; + suggestedFilterSelector.Items.Clear(); + suggestedFilterSelector.Items.Add(new ComboBoxItem { Content = "First filter", Tag = null }); + + var desiredIndex = 0; + if (types is { Count: > 0 }) + { + for (var i = 0; i < types.Count; i++) + { + var type = types[i]; + var item = new ComboBoxItem { Content = type.Name, Tag = type }; + suggestedFilterSelector.Items.Add(item); + + if (previouslySelected is not null && ReferenceEquals(previouslySelected, type)) + { + desiredIndex = i + 1; + } + } + } + + suggestedFilterSelector.SelectedIndex = desiredIndex; + } + + FilePickerFileType? GetSuggestedFileType(IReadOnlyList? types) + { + if (useSuggestedFilter.IsChecked == true && types is { Count: > 0 }) + { + if (suggestedFilterSelector.SelectedItem is ComboBoxItem { Tag: FilePickerFileType selectedType } + && types.Any(t => ReferenceEquals(t, selectedType))) + { + return selectedType; + } + + return types.FirstOrDefault(); + } + + return null; + } + + void UpdateSuggestedFilterSelectorState() => + suggestedFilterSelector.IsEnabled = useSuggestedFilter.IsChecked == true; + + useSuggestedFilter.IsCheckedChanged += (_, _) => UpdateSuggestedFilterSelectorState(); + UpdateSuggestedFilterSelectorState(); + + FilterSelector.SelectionChanged += (_, _) => UpdateSuggestedFilterSelector(BuildFileTypes()); + UpdateSuggestedFilterSelector(BuildFileTypes()); + + DecoratedWindow.Click += delegate + { + new DecoratedWindow().Show(); + }; + DecoratedWindowDialog.Click += delegate + { + _ = new DecoratedWindow().ShowDialog(GetWindow()); + }; + Dialog.Click += delegate + { + var window = CreateSampleWindow(); + window.Height = 200; + _ = window.ShowDialog(GetWindow()); + }; + DialogNoTaskbar.Click += delegate + { + var window = CreateSampleWindow(); + window.Height = 200; + window.ShowInTaskbar = false; + _ = window.ShowDialog(GetWindow()); + }; + OwnedWindow.Click += delegate + { + var window = CreateSampleWindow(); + + window.Show(GetWindow()); + }; + + OwnedWindowNoTaskbar.Click += delegate + { + var window = CreateSampleWindow(); + + window.ShowInTaskbar = false; + + window.Show(GetWindow()); + }; + + OpenFilePicker.Click += async delegate + { + var fileTypes = GetFileTypes(); + var result = await GetStorageProvider().OpenFilePickerAsync(new FilePickerOpenOptions() + { + Title = "Open file", + FileTypeFilter = fileTypes, + SuggestedFileType = GetSuggestedFileType(fileTypes), + SuggestedFileName = "FileName", + SuggestedStartLocation = lastSelectedDirectory, + AllowMultiple = openMultiple.IsChecked == true + }); + + await SetPickerResult(result); + }; + SaveFilePicker.Click += async delegate + { + var fileTypes = GetFileTypes(); + var suggestedType = GetSuggestedFileType(fileTypes); + var file = await GetStorageProvider().SaveFilePickerAsync(new FilePickerSaveOptions() + { + Title = "Save file", + FileTypeChoices = fileTypes, + SuggestedFileType = suggestedType, + SuggestedStartLocation = lastSelectedDirectory, + SuggestedFileName = "FileName", + ShowOverwritePrompt = true + }); + + if (file is not null) + { + try + { + // Sync disposal of StreamWriter is not supported on WASM + await using var stream = await file.OpenWriteAsync(); + await using var writer = new System.IO.StreamWriter(stream); + await writer.WriteLineAsync(openedFileContent.Text); + + SetFolder(await file.GetParentAsync()); + } + catch (Exception ex) + { + openedFileContent.Text = ex.ToString(); + } + } + + await SetPickerResult(file is null ? null : new[] { file }); + }; + SaveFilePickerWithResult.Click += async delegate + { + var saveFileTypes = new[] { FilePickerFileTypes.Json, FilePickerFileTypes.Xml }; + var result = await GetStorageProvider().SaveFilePickerWithResultAsync(new FilePickerSaveOptions() + { + Title = "Save file", + FileTypeChoices = saveFileTypes, + SuggestedFileType = GetSuggestedFileType(saveFileTypes), + SuggestedStartLocation = lastSelectedDirectory, + SuggestedFileName = "FileName", + ShowOverwritePrompt = true + }); + + try + { + if (result.File is { } file) + { + // Sync disposal of StreamWriter is not supported on WASM + await using var stream = await file.OpenWriteAsync(); + await using var writer = new System.IO.StreamWriter(stream); + if (result.SelectedFileType == FilePickerFileTypes.Xml) + { + await writer.WriteLineAsync("Test"); + } + else + { + await writer.WriteLineAsync("""{ "sample": "Test" }"""); + } + + SetFolder(await result.File.GetParentAsync()); + } + } + catch (Exception ex) + { + openedFileContent.Text = ex.ToString(); + } + + await SetPickerResult(result.File is null ? null : new[] { result.File }, result.SelectedFileType); + }; + OpenFolderPicker.Click += async delegate + { + var folders = await GetStorageProvider().OpenFolderPickerAsync(new FolderPickerOpenOptions() + { + Title = "Folder file", + SuggestedStartLocation = lastSelectedDirectory, + SuggestedFileName = "FileName", + AllowMultiple = openMultiple.IsChecked == true + }); + + await SetPickerResult(folders); + }; + OpenFileFromBookmark.Click += async delegate + { + var file = bookmarkContainer.Text is not null + ? await GetStorageProvider().OpenFileBookmarkAsync(bookmarkContainer.Text) + : null; + + await SetPickerResult(file is null ? null : new[] { file }); + }; + OpenFolderFromBookmark.Click += async delegate + { + var folder = bookmarkContainer.Text is not null + ? await GetStorageProvider().OpenFolderBookmarkAsync(bookmarkContainer.Text) + : null; + + await SetPickerResult(folder is null ? null : new[] { folder }); + }; + + LaunchUri.Click += async delegate + { + var statusBlock = LaunchStatus; + if (Uri.TryCreate(UriToLaunch.Text, UriKind.Absolute, out var uri)) + { + var result = await TopLevel.GetTopLevel(this)!.Launcher.LaunchUriAsync(uri); + statusBlock.Text = "LaunchUriAsync returned " + result; + } + else + { + statusBlock.Text = "Can't parse the Uri"; + } + }; + + LaunchFile.Click += async delegate + { + var statusBlock = LaunchStatus; + if (lastSelectedItem is not null) + { + var result = await TopLevel.GetTopLevel(this)!.Launcher.LaunchFileAsync(lastSelectedItem); + statusBlock.Text = "LaunchFileAsync returned " + result; + } + else + { + statusBlock.Text = "Please select any file or folder first"; + } + }; + + void SetFolder(IStorageFolder? folder) + { + ignoreTextChanged = true; + lastSelectedDirectory = folder; + lastSelectedItem = folder; + currentFolderBox.Text = folder?.Path is { IsAbsoluteUri: true } abs ? abs.LocalPath : folder?.Path?.ToString(); + ignoreTextChanged = false; + } + async Task SetPickerResult(IReadOnlyCollection? items, FilePickerFileType? selectedType = null) + { + items ??= Array.Empty(); + bookmarkContainer.Text = items.FirstOrDefault(f => f.CanBookmark) is { } f ? await f.SaveBookmarkAsync() : "Can't bookmark"; + var mappedResults = new List(); + + string resultText = ""; + if (items.FirstOrDefault() is IStorageItem item) + { + resultText += item is IStorageFile ? "File:" : "Folder:"; + resultText += Environment.NewLine; + + var props = await item.GetBasicPropertiesAsync(); + resultText += @$"Size: {props.Size} + DateCreated: {props.DateCreated} + DateModified: {props.DateModified} + CanBookmark: {item.CanBookmark} + "; + if (item is IStorageFile file) + { + resultText += @$" + Content: + "; + + try + { + resultText += await ReadTextFromFile(file, 500); + } + catch (Exception ex) + { + resultText += ex.ToString(); + } + } + + if (item is IStorageFolder storageFolder) + { + SetFolder(storageFolder); + } + else + { + var parent = await item.GetParentAsync(); + SetFolder(parent); + if (parent is not null) + { + mappedResults.Add(FullPathOrName(parent)); + } + } + + foreach (var selectedItem in items) + { + mappedResults.Add("+> " + FullPathOrName(selectedItem)); + if (selectedItem is IStorageFolder folder) + { + await foreach (var innerItem in folder.GetItemsAsync()) + { + mappedResults.Add("++> " + FullPathOrName(innerItem)); + } + } + } + lastSelectedItem = item; + } + + if (selectedType is not null) + { + resultText += Environment.NewLine + "Selected type: " + selectedType.Name; + } + + openedFileContent.Text = resultText; + results.ItemsSource = mappedResults; + resultsVisible.IsVisible = mappedResults.Any(); + } + } + + internal static async Task ReadTextFromFile(IStorageFile file, int length) + { + await using var stream = await file.OpenReadAsync(); + using var reader = new System.IO.StreamReader(stream); + + // 4GB file test, shouldn't load more than 10000 chars into a memory. + var buffer = ArrayPool.Shared.Rent(length); + try + { + var charsRead = await reader.ReadAsync(buffer, 0, length); + return new string(buffer, 0, charsRead); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + var openedFileContent = OpenedFileContent; + try + { + var storageProvider = GetStorageProvider(); + openedFileContent.Text = $@"CanOpen: {storageProvider.CanOpen} +CanSave: {storageProvider.CanSave} +CanPickFolder: {storageProvider.CanPickFolder}"; + } + catch (Exception ex) + { + openedFileContent.Text = "Storage provider is not available: " + ex.Message; + } + } + + private Window CreateSampleWindow() + { + Button button; + Button dialogButton; + + var window = new Window + { + Height = 200, + Width = 200, + Content = new StackPanel + { + Spacing = 4, + Children = + { + new TextBlock { Text = "Hello world!" }, + (button = new Button + { + HorizontalAlignment = HorizontalAlignment.Center, + Content = "Click to close", + IsDefault = true + }), + (dialogButton = new Button + { + HorizontalAlignment = HorizontalAlignment.Center, + Content = "Dialog", + IsDefault = false + }) + } + }, + WindowStartupLocation = WindowStartupLocation.CenterOwner + }; + + button.Click += (_, __) => window.Close(); + dialogButton.Click += (_, __) => + { + var dialog = CreateSampleWindow(); + dialog.Height = 200; + dialog.ShowDialog(window); + }; + + return window; + } + + private IStorageProvider GetStorageProvider() + { + var forceManaged = ForceManaged.IsChecked ?? false; + return forceManaged + ? new ManagedStorageProvider(GetWindow()) // NOTE: In your production App use 'AppBuilder.UseManagedSystemDialogs()' + : GetTopLevel().StorageProvider; + } + + private static string FullPathOrName(IStorageItem? item) + { + if (item is null) return "(null)"; + return item.Path is { IsAbsoluteUri: true } path ? path.ToString() : item.Name; + } + + Window GetWindow() => TopLevel.GetTopLevel(this) as Window ?? throw new NullReferenceException("Invalid Owner"); + TopLevel GetTopLevel() => TopLevel.GetTopLevel(this) ?? throw new NullReferenceException("Invalid Owner"); + } +#pragma warning restore CS0618 // Type or member is obsolete +} diff --git a/samples/AvaloniaControlCatalog/Pages/DragAndDropPage.xaml b/samples/AvaloniaControlCatalog/Pages/DragAndDropPage.xaml new file mode 100644 index 00000000..7300f8c0 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DragAndDropPage.xaml @@ -0,0 +1,61 @@ + + + + + + + + Example of Drag+Drop capabilities + + + + + Drag Me (text) + + + Drag Me (files) + + + Drag Me (bitmap) + + + Drag Me (custom) + + + + + + Drop some text, files, bitmap or custom format here (Copy) + + + Drop some text or custom format here (Move) + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DragAndDropPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DragAndDropPage.xaml.cs new file mode 100644 index 00000000..d9fe1af1 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DragAndDropPage.xaml.cs @@ -0,0 +1,174 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Markup.Xaml; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; + +using Avalonia.Platform.Storage; + +namespace ControlCatalog.Pages +{ + public partial class DragAndDropPage : ContentPage + { + private readonly DataFormat _customFormat = + DataFormat.CreateStringApplicationFormat("xxx-avalonia-controlcatalog-custom"); + + public DragAndDropPage() + { + InitializeComponent(); + + int textCount = 0; + + SetupDnd( + "Text", + d => d.Add(DataTransferItem.Create(DataFormat.Text, $"Text was dragged {++textCount} times")), + DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link); + + SetupDnd( + "Custom", + d => d.Add(DataTransferItem.Create(_customFormat, "Test123")), + DragDropEffects.Copy | DragDropEffects.Move); + + SetupDnd( + "Files", + async d => + { + var currentFile = Environment.ProcessPath; + + if (File.Exists(currentFile) && + TopLevel.GetTopLevel(this) is { } topLevel && + await topLevel.StorageProvider.TryGetFileFromPathAsync(currentFile) is { } storageFile) + { + d.Add(DataTransferItem.Create(DataFormat.File, storageFile)); + } + }, + DragDropEffects.Copy); + + SetupDnd( + "Bitmap", + d => d.Add(DataTransferItem.Create(DataFormat.Bitmap, new Bitmap(AssetLoader.Open(new Uri("avares://ControlCatalog/Assets/image1.jpg"))))), + DragDropEffects.Copy); + + AddHandlers(CopyTarget, DragDropEffects.Copy); + AddHandlers(MoveTarget, DragDropEffects.Move); + } + + private void SetupDnd(string suffix, Action factory, DragDropEffects effects) => + SetupDnd( + suffix, + o => + { + factory(o); + return Task.CompletedTask; + }, + effects); + + private void SetupDnd(string suffix, Func factory, DragDropEffects effects) + { + var dragMe = this.Get("DragMe" + suffix); + var dragState = this.Get("DragState" + suffix); + + async void DoDrag(object? sender, PointerPressedEventArgs e) + { + var dragData = new DataTransfer(); + await factory(dragData); + + var result = await DragDrop.DoDragDropAsync(e, dragData, effects); + switch (result) + { + case DragDropEffects.Move: + dragState.Text = "Data was moved"; + break; + case DragDropEffects.Copy: + dragState.Text = "Data was copied"; + break; + case DragDropEffects.Link: + dragState.Text = "Data was linked"; + break; + case DragDropEffects.None: + dragState.Text = "The drag operation was canceled"; + break; + default: + dragState.Text = "Unknown result"; + break; + } + } + + dragMe.PointerPressed += DoDrag; + } + + private void AddHandlers(Control target, DragDropEffects allowedEffects) + { + DragDrop.AddDragEnterHandler(target, DragOver); + DragDrop.AddDragOverHandler(target, DragOver); + DragDrop.AddDropHandler(target, Drop); + + void DragOver(object? sender, DragEventArgs e) + { + e.DragEffects &= allowedEffects; + + // Only allow if the dragged data contains text or filenames. + if (!e.DataTransfer.Contains(DataFormat.Text) + && !e.DataTransfer.Contains(DataFormat.File) + && !e.DataTransfer.Contains(DataFormat.Bitmap) + && !e.DataTransfer.Contains(_customFormat)) + e.DragEffects = DragDropEffects.None; + } + + async void Drop(object? sender, DragEventArgs e) + { + e.DragEffects &= allowedEffects; + + if (e.DragEffects == DragDropEffects.None) + return; + + if (e.DataTransfer.Contains(DataFormat.Text)) + { + DropState.Content = e.DataTransfer.TryGetText(); + } + else if (e.DataTransfer.Contains(DataFormat.File)) + { + var files = e.DataTransfer.TryGetFiles() ?? []; + var contentStr = ""; + + foreach (var item in files) + { + if (item is IStorageFile file) + { + var content = await DialogsPage.ReadTextFromFile(file, 500); + contentStr += $"File {item.Name}:{Environment.NewLine}{content}{Environment.NewLine}{Environment.NewLine}"; + } + else if (item is IStorageFolder folder) + { + var childrenCount = 0; + await foreach (var _ in folder.GetItemsAsync()) + { + childrenCount++; + } + contentStr += $"Folder {item.Name}: items {childrenCount}{Environment.NewLine}{Environment.NewLine}"; + } + } + DropState.Content = contentStr; + } + else if (e.DataTransfer.Contains(DataFormat.Bitmap)) + { + var bitmap = e.DataTransfer.TryGetValue(DataFormat.Bitmap); + DropState.Content = new Image + { + Source = bitmap, Width = 400, Height = 300, Stretch = Stretch.Uniform + }; + } + else if (e.DataTransfer.Contains(_customFormat)) + { + DropState.Content = "Custom: " + e.DataTransfer.TryGetValue(_customFormat); + } + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerDemoPage.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerDemoPage.xaml new file mode 100644 index 00000000..f1edd0bc --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerDemoPage.xaml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerDemoPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerDemoPage.xaml.cs new file mode 100644 index 00000000..27eb93ff --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerDemoPage.xaml.cs @@ -0,0 +1,76 @@ +using System; +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class DrawerDemoPage : ContentPage + { + private static readonly (string Group, string Title, string Description, Func Factory)[] Demos = + { + // Overview + ("Overview", "First Look", "Basic DrawerPage with a navigation drawer, menu items, and detail content.", + () => new DrawerPageFirstLookPage()), + + // Features + ("Features", "Navigation", + "Master-detail pattern: select a drawer menu item to navigate the detail via a NavigationPage with hamburger-to-back-button transition.", + () => new DrawerPageNavigationPage()), + ("Features", "Compact Rail", + "CompactOverlay and CompactInline layout modes: a narrow icon rail is always visible and expands on open. Adjust rail width and open pane width.", + () => new DrawerPageCompactPage()), + ("Features", "Breakpoint", + "DrawerBreakpointLength: below the threshold the drawer switches to Overlay mode automatically. Resize the window or adjust the slider to see the layout switch in real time.", + () => new DrawerPageBreakpointPage()), + ("Features", "Events", + "Opened, Closing, and Closed drawer events plus NavigatedTo and NavigatedFrom page lifecycle events. Enable 'Cancel next close' to prevent the drawer from closing.", + () => new DrawerPageEventsPage()), + ("Features", "RTL Layout", "Right-to-left layout: drawer opens from the right edge with mirrored gestures.", + () => new DrawerPageRtlPage()), + + // Appearance + ("Appearance", "Customization", + "Customize drawer behavior, layout mode, length, colors, header and footer.", + () => new DrawerPageCustomizationPage()), + ("Appearance", "Custom Flyout", + "Dark overlay menu with staggered item animations and CrossFade page transitions on the detail NavigationPage.", + () => new DrawerPageCustomFlyoutPage()), + ("Appearance", "Transitions", + "Configure the detail NavigationPage transition. Choose CrossFade, PageSlide, or CompositePageTransition to animate detail page changes.", + () => new DrawerPageTransitionsPage()), + + // Performance + ("Performance", "Performance Monitor", + "Track detail page swaps, live page instances, and managed heap size. Observe how GC reclaims memory after swapping pages.", + () => new DrawerPagePerformancePage()), + + // Showcases + ("Showcases", "AvaloniaFlix", + "Streaming app with DrawerPage wrapping NavigationPage. Hamburger auto-injected at root, back arrow on detail, and dark themed flyout menu.", + () => new AvaloniaFlixAppPage()), + ("Showcases", "L'Avenir Restaurant", + "Restaurant app with DrawerPage as the root container, NavigationPage for detail navigation, and TabbedPage bottom tabs for Menu, Reservations, and Profile.", + () => new LAvenirAppPage()), + ("Showcases", "EcoTracker", + "Sustainability tracker with CompactInline drawer, eco leaf hamburger icon, crossfade compact/open menu transitions, and green-themed Home, Stats, Habits, and Community pages.", + () => new EcoTrackerAppPage()), + ("Showcases", "ModernApp", + "Travel social app using a top-placement DrawerPage. A slide-down nav pane gives access to Discover, My Trips, Profile, and Settings. Features destination cards, story circles, an experience feed, a stats profile, and a travel gallery.", + () => new ModernAppPage()), + ("Showcases", "Controls Gallery App", + "Controls gallery app using DrawerPage CompactInline mode. Dark Fluent palette, accent pill selection indicator, search box that fades in when open, expandable category groups, and Settings pinned to the footer.", + () => new ControlsGalleryAppPage()), + }; + + public DrawerDemoPage() + { + InitializeComponent(); + Loaded += OnLoaded; + } + + private async void OnLoaded(object? sender, RoutedEventArgs e) + { + await SampleNav.PushAsync(NavigationDemoHelper.CreateGalleryHomePage(SampleNav, Demos), null); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml new file mode 100644 index 00000000..e0ce3ca0 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml @@ -0,0 +1,400 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml.cs new file mode 100644 index 00000000..6c771f30 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml.cs @@ -0,0 +1,464 @@ +using System; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; + +namespace ControlCatalog.Pages; + +public partial class ControlsGalleryAppPage : UserControl +{ + static readonly Color Accent = Color.Parse("#60CDFF"); + static readonly Color ContentBg = Color.Parse("#141414"); + static readonly Color CardBg = Color.Parse("#1F1F1F"); + static readonly Color BorderCol = Color.Parse("#2EFFFFFF"); + static readonly Color TextCol = Color.Parse("#FFFFFF"); + static readonly Color TextSec = Color.Parse("#C8FFFFFF"); + static readonly Color TextMuted = Color.Parse("#80FFFFFF"); + + DrawerPage? _drawer; + NavigationPage? _detailNav; + Button? _selectedBtn; + TextBox? _searchBox; + ContentPage? _preSearchPage; + bool _isSearching; + + public ControlsGalleryAppPage() + { + InitializeComponent(); + + _drawer = this.FindControl("NavDrawer"); + _detailNav = this.FindControl("DetailNav"); + _selectedBtn = this.FindControl + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageBreakpointPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageBreakpointPage.xaml.cs new file mode 100644 index 00000000..0da2eb2e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageBreakpointPage.xaml.cs @@ -0,0 +1,84 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class DrawerPageBreakpointPage : UserControl + { + private bool _isLoaded; + + public DrawerPageBreakpointPage() + { + InitializeComponent(); + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + _isLoaded = true; + DemoDrawer.PropertyChanged += OnDrawerPropertyChanged; + UpdateStatus(); + } + + protected override void OnUnloaded(RoutedEventArgs e) + { + base.OnUnloaded(e); + DemoDrawer.PropertyChanged -= OnDrawerPropertyChanged; + } + + private void OnDrawerPropertyChanged(object? sender, Avalonia.AvaloniaPropertyChangedEventArgs e) + { + if (e.Property == DrawerPage.BoundsProperty) + UpdateStatus(); + } + + private void OnBreakpointChanged(object? sender, RangeBaseValueChangedEventArgs e) + { + if (!_isLoaded) + return; + var value = (int)e.NewValue; + DemoDrawer.DrawerBreakpointLength = value; + BreakpointText.Text = value.ToString(); + UpdateStatus(); + } + + private void OnLayoutChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.DrawerLayoutBehavior = LayoutCombo.SelectedIndex switch + { + 0 => DrawerLayoutBehavior.Split, + 1 => DrawerLayoutBehavior.CompactInline, + 2 => DrawerLayoutBehavior.CompactOverlay, + _ => DrawerLayoutBehavior.Split + }; + UpdateStatus(); + } + + private void OnMenuItemClick(object? sender, RoutedEventArgs e) + { + if (!_isLoaded || sender is not Button button) + return; + var item = button.Tag?.ToString() ?? "Home"; + DetailTitleText.Text = item; + DetailPage.Header = item; + if (DemoDrawer.DrawerLayoutBehavior != DrawerLayoutBehavior.Split) + DemoDrawer.IsOpen = false; + } + + private void UpdateStatus() + { + var isVertical = DemoDrawer.DrawerPlacement == DrawerPlacement.Top || + DemoDrawer.DrawerPlacement == DrawerPlacement.Bottom; + var length = isVertical ? DemoDrawer.Bounds.Height : DemoDrawer.Bounds.Width; + var breakpoint = DemoDrawer.DrawerBreakpointLength; + WidthText.Text = $"{(isVertical ? "Height" : "Width")}: {(int)length} px"; + var isOverlay = breakpoint > 0 && length > 0 && length < breakpoint; + ModeText.Text = isOverlay ? + "Mode: Overlay (below breakpoint)" : + $"Mode: {DemoDrawer.DrawerLayoutBehavior} (above breakpoint)"; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml new file mode 100644 index 00000000..fc349726 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml.cs new file mode 100644 index 00000000..b1d84fb5 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml.cs @@ -0,0 +1,78 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class DrawerPageCompactPage : UserControl + { + private bool _isLoaded; + + public DrawerPageCompactPage() + { + InitializeComponent(); + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + _isLoaded = true; + DemoDrawer.Opened += OnDrawerStatusChanged; + DemoDrawer.Closed += OnDrawerStatusChanged; + } + + protected override void OnUnloaded(RoutedEventArgs e) + { + base.OnUnloaded(e); + DemoDrawer.Opened -= OnDrawerStatusChanged; + DemoDrawer.Closed -= OnDrawerStatusChanged; + } + + private void OnDrawerStatusChanged(object? sender, System.EventArgs e) => UpdateStatus(); + + private void OnLayoutChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.DrawerLayoutBehavior = LayoutCombo.SelectedIndex switch + { + 0 => DrawerLayoutBehavior.CompactOverlay, + 1 => DrawerLayoutBehavior.CompactInline, + _ => DrawerLayoutBehavior.CompactOverlay + }; + } + + private void OnCompactLengthChanged(object? sender, RangeBaseValueChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.CompactDrawerLength = e.NewValue; + CompactLengthText.Text = ((int)e.NewValue).ToString(); + } + + private void OnDrawerLengthChanged(object? sender, RangeBaseValueChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.DrawerLength = e.NewValue; + DrawerLengthText.Text = ((int)e.NewValue).ToString(); + } + + private void OnMenuItemClick(object? sender, RoutedEventArgs e) + { + if (!_isLoaded) + return; + if (sender is not Button button) + return; + var item = button.Tag?.ToString() ?? "Home"; + DetailTitleText.Text = item; + DetailPage.Header = item; + DemoDrawer.IsOpen = false; + } + + private void UpdateStatus() + { + StatusText.Text = $"Drawer: {(DemoDrawer.IsOpen ? "Open" : "Closed")}"; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomFlyoutPage.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomFlyoutPage.xaml new file mode 100644 index 00000000..11f584c3 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomFlyoutPage.xaml @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomFlyoutPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomFlyoutPage.xaml.cs new file mode 100644 index 00000000..c05d92ec --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomFlyoutPage.xaml.cs @@ -0,0 +1,146 @@ +using System; +using Avalonia.Controls; +using Avalonia.Controls.Shapes; +using Avalonia.Interactivity; +using Avalonia.Media; +using Avalonia.Threading; + +namespace ControlCatalog.Pages +{ + public partial class DrawerPageCustomFlyoutPage : UserControl + { + private Ellipse? _bubble1; + private Ellipse? _bubble2; + private DispatcherTimer? _bubbleTimer; + private double _bubblePhase; + + public DrawerPageCustomFlyoutPage() + { + InitializeComponent(); + + _bubble1 = this.FindControl("Bubble1"); + _bubble2 = this.FindControl("Bubble2"); + + DrawerPageControl.PropertyChanged += (_, args) => + { + if (args.Property == DrawerPage.IsOpenProperty) + OnDrawerOpenChanged((bool)args.NewValue!); + }; + + _ = DetailNav.PushAsync(BuildDetailPage("Home"), null); + } + + private Control[] MenuItems => + new Control[] { MenuItem1, MenuItem2, MenuItem3, MenuItem4, MenuItem5, FooterRow }; + + private void OnDrawerOpenChanged(bool isOpen) + { + if (isOpen) + { + StartBubbles(); + + foreach (var item in MenuItems) + { + item.Opacity = 1.0; + if (item.RenderTransform is TranslateTransform tt) + tt.Y = 0; + } + } + else + { + StopBubbles(); + + foreach (var item in MenuItems) + { + var savedItemT = item.Transitions; + item.Transitions = null; + item.Opacity = 0.0; + item.Transitions = savedItemT; + + if (item.RenderTransform is TranslateTransform tt) + { + var savedTT = tt.Transitions; + tt.Transitions = null; + tt.Y = 25; + tt.Transitions = savedTT; + } + } + } + } + + private void StartBubbles() + { + if (_bubbleTimer != null) return; + _bubblePhase = 0; + _bubbleTimer = new DispatcherTimer(DispatcherPriority.Render) + { + Interval = TimeSpan.FromMilliseconds(16) + }; + _bubbleTimer.Tick += OnBubbleTick; + _bubbleTimer.Start(); + } + + private void StopBubbles() + { + if (_bubbleTimer == null) return; + _bubbleTimer.Stop(); + _bubbleTimer.Tick -= OnBubbleTick; + _bubbleTimer = null; + + if (_bubble1 != null) _bubble1.RenderTransform = null; + if (_bubble2 != null) _bubble2.RenderTransform = null; + } + + private void OnBubbleTick(object? sender, EventArgs e) + { + _bubblePhase += 0.012; + + if (_bubble1 != null) + _bubble1.RenderTransform = new TranslateTransform( + x: Math.Sin(_bubblePhase * 0.65) * 10, + y: Math.Sin(_bubblePhase) * 14); + + if (_bubble2 != null) + _bubble2.RenderTransform = new TranslateTransform( + x: Math.Sin(_bubblePhase * 0.45 + 1.8) * 7, + y: Math.Cos(_bubblePhase * 0.85 + 0.6) * 10); + } + + private async void OnMenuItemClick(object? sender, RoutedEventArgs e) + { + if (sender is not Button button) return; + var tag = button.Tag?.ToString() ?? "Home"; + + DrawerPageControl.IsOpen = false; + + await DetailNav.ReplaceAsync(BuildDetailPage(tag), null); + } + + private static ContentPage BuildDetailPage(string section) + { + var (iconPath, body) = section switch + { + "Home" => + ("M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z", + "Welcome back! Here is your dashboard with recent activity, quick actions, and personalized content."), + "Explore" => + ("M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z", + "Discover new places, trending topics, and recommended content tailored to your interests."), + "Messages" => + ("M20,8L12,13L4,8V6L12,11L20,6M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4Z", + "Your conversations and notifications. Stay connected with the people who matter."), + "Profile" => + ("M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z", + "View and edit your profile, manage privacy settings, and control your account preferences."), + "Settings" => + ("M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.04 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.68 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.04 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z", + "Configure application preferences, notifications, and privacy options."), + _ => ("M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2Z", "") + }; + + var page = NavigationDemoHelper.MakeSectionPage(section, iconPath, section, body, 0); + NavigationPage.SetHasNavigationBar(page, false); + return page; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomizationPage.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomizationPage.xaml new file mode 100644 index 00000000..3adffed5 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomizationPage.xaml @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomizationPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomizationPage.xaml.cs new file mode 100644 index 00000000..d9482523 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageCustomizationPage.xaml.cs @@ -0,0 +1,326 @@ +using System.Linq; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Input.GestureRecognizers; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; + +namespace ControlCatalog.Pages +{ + public partial class DrawerPageCustomizationPage : UserControl + { + private bool _isLoaded; + + private static readonly string[] _iconPaths = + { + // 0 - 3 lines (default hamburger) + "M3 17h18a1 1 0 0 1 .117 1.993L21 19H3a1 1 0 0 1-.117-1.993L3 17h18H3Zm0-6 18-.002a1 1 0 0 1 .117 1.993l-.117.007L3 13a1 1 0 0 1-.117-1.993L3 11l18-.002L3 11Zm0-6h18a1 1 0 0 1 .117 1.993L21 7H3a1 1 0 0 1-.117-1.993L3 5h18H3Z", + // 1 - 2 lines + "M3,13H21V11H3M3,6V8H21V6", + // 2 - 4 squares + "M3,11H11V3H3M3,21H11V13H3M13,21H21V13H13M13,3V11H21V3", + }; + + public DrawerPageCustomizationPage() + { + InitializeComponent(); + EnableMouseSwipeGesture(DemoDrawer); + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + _isLoaded = true; + } + + private void OnToggleDrawer(object? sender, RoutedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.IsOpen = !DemoDrawer.IsOpen; + } + + private void OnBehaviorChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.DrawerBehavior = BehaviorCombo.SelectedIndex switch + { + 0 => DrawerBehavior.Auto, + 1 => DrawerBehavior.Flyout, + 2 => DrawerBehavior.Locked, + 3 => DrawerBehavior.Disabled, + _ => DrawerBehavior.Auto + }; + } + + private void OnLayoutChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.DrawerLayoutBehavior = LayoutCombo.SelectedIndex switch + { + 0 => DrawerLayoutBehavior.Overlay, + 1 => DrawerLayoutBehavior.Split, + 2 => DrawerLayoutBehavior.CompactOverlay, + 3 => DrawerLayoutBehavior.CompactInline, + _ => DrawerLayoutBehavior.Overlay + }; + } + + private void OnPlacementChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.DrawerPlacement = PlacementCombo.SelectedIndex switch + { + 1 => DrawerPlacement.Right, + 2 => DrawerPlacement.Top, + 3 => DrawerPlacement.Bottom, + _ => DrawerPlacement.Left + }; + } + + private void OnGestureToggled(object? sender, RoutedEventArgs e) + { + if (!_isLoaded) + return; + if (sender is CheckBox check) + DemoDrawer.IsGestureEnabled = check.IsChecked == true; + } + + private void OnDrawerLengthChanged(object? sender, RangeBaseValueChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.DrawerLength = e.NewValue; + DrawerLengthText.Text = ((int)e.NewValue).ToString(); + } + + private void OnDrawerBgChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.DrawerBackground = DrawerBgCombo.SelectedIndex switch + { + 1 => new SolidColorBrush(Colors.SlateBlue), + 2 => new SolidColorBrush(Colors.DarkCyan), + 3 => new SolidColorBrush(Colors.DarkRed), + 4 => new SolidColorBrush(Colors.DarkGreen), + _ => null + }; + } + + private void OnHeaderBgChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.DrawerHeaderBackground = HeaderBgCombo.SelectedIndex switch + { + 1 => new SolidColorBrush(Colors.DodgerBlue), + 2 => new SolidColorBrush(Colors.Orange), + 3 => new SolidColorBrush(Colors.Teal), + 4 => new SolidColorBrush(Colors.Purple), + _ => null + }; + } + + private void OnFooterBgChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.DrawerFooterBackground = FooterBgCombo.SelectedIndex switch + { + 1 => new SolidColorBrush(Colors.DimGray), + 2 => new SolidColorBrush(Colors.DarkSlateBlue), + 3 => new SolidColorBrush(Colors.DarkOliveGreen), + 4 => new SolidColorBrush(Colors.Maroon), + _ => null + }; + } + + private void OnIconChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.DrawerIcon = new PathIcon { Data = Geometry.Parse(_iconPaths[IconCombo.SelectedIndex]) }; + } + + private void OnBackdropChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + DemoDrawer.BackdropBrush = BackdropCombo.SelectedIndex switch + { + 1 => new SolidColorBrush(Color.FromArgb(102, 0, 0, 0)), + 2 => new SolidColorBrush(Color.FromArgb(179, 0, 0, 0)), + 3 => new SolidColorBrush(Color.FromArgb(102, 255, 255, 255)), + _ => null + }; + } + + private void OnShowHeaderToggled(object? sender, RoutedEventArgs e) + { + if (!_isLoaded) + return; + if (ShowHeaderCheck.IsChecked == true) + DemoDrawer.DrawerHeader = HeaderTemplateCombo.SelectedIndex == 0 ? DrawerHeaderBorder : (object)"My Application"; + else + DemoDrawer.DrawerHeader = null; + } + + private void OnShowFooterToggled(object? sender, RoutedEventArgs e) + { + if (!_isLoaded) + return; + if (ShowFooterCheck.IsChecked == true) + { + DemoDrawer.DrawerFooter = FooterTemplateCombo.SelectedIndex switch + { + 1 => (object)"v12.0", + 2 => (object)"Avalonia", + _ => DrawerFooterBorder + }; + } + else + { + DemoDrawer.DrawerFooter = null; + } + } + + private void OnHeaderTemplateChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + + switch (HeaderTemplateCombo.SelectedIndex) + { + case 1: + DemoDrawer.DrawerHeader = "My Application"; + DemoDrawer.DrawerHeaderTemplate = new FuncDataTemplate((data, _) => + new Border + { + Padding = new Avalonia.Thickness(16), + Child = new StackPanel + { + Spacing = 2, + Children = + { + new TextBlock { Text = data, FontSize = 18, FontWeight = FontWeight.SemiBold, Foreground = Brushes.White }, + new TextBlock { Text = "Navigation", FontSize = 12, Foreground = Brushes.White, Opacity = 0.7 } + } + } + }); + break; + + case 2: + DemoDrawer.DrawerHeader = "My Application"; + DemoDrawer.DrawerHeaderTemplate = new FuncDataTemplate((data, _) => + { + var initial = data?.Length > 0 ? data[0].ToString().ToUpperInvariant() : "?"; + var avatar = new Border + { + Width = 40, + Height = 40, + CornerRadius = new Avalonia.CornerRadius(20), + Background = new SolidColorBrush(Color.Parse("#1976D2")), + Child = new TextBlock + { + Text = initial, + FontSize = 18, + FontWeight = FontWeight.Bold, + Foreground = Brushes.White, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center + } + }; + var label = new TextBlock { Text = data, FontSize = 14, FontWeight = FontWeight.SemiBold, VerticalAlignment = VerticalAlignment.Center }; + var row = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 10 }; + row.Children.Add(avatar); + row.Children.Add(label); + return new Border { Padding = new Avalonia.Thickness(12), Child = row }; + }); + break; + + default: + DemoDrawer.DrawerHeader = DrawerHeaderBorder; + DemoDrawer.DrawerHeaderTemplate = null; + break; + } + } + + private void OnFooterTemplateChanged(object? sender, SelectionChangedEventArgs e) + { + if (!_isLoaded) + return; + + switch (FooterTemplateCombo.SelectedIndex) + { + case 1: + DemoDrawer.DrawerFooter = "v12.0"; + DemoDrawer.DrawerFooterTemplate = new FuncDataTemplate((data, _) => + new Border + { + Padding = new Avalonia.Thickness(12, 8), + Child = new Border + { + Padding = new Avalonia.Thickness(8, 4), + CornerRadius = new Avalonia.CornerRadius(4), + Background = new SolidColorBrush(Color.Parse("#1976D2")), + Child = new TextBlock { Text = data, FontSize = 11, Foreground = Brushes.White, FontWeight = FontWeight.SemiBold } + } + }); + break; + + case 2: + DemoDrawer.DrawerFooter = "Avalonia"; + DemoDrawer.DrawerFooterTemplate = new FuncDataTemplate((data, _) => + { + var icon = new PathIcon + { + Width = 14, + Height = 14, + Data = Geometry.Parse("M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"), + Opacity = 0.5 + }; + var label = new TextBlock { Text = data, FontSize = 12, Opacity = 0.6, VerticalAlignment = VerticalAlignment.Center }; + var row = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 6 }; + row.Children.Add(icon); + row.Children.Add(label); + return new Border { Padding = new Avalonia.Thickness(14, 10), Child = row }; + }); + break; + + default: + DemoDrawer.DrawerFooter = DrawerFooterBorder; + DemoDrawer.DrawerFooterTemplate = null; + break; + } + } + + private void OnMenuItemClick(object? sender, RoutedEventArgs e) + { + if (!_isLoaded) + return; + if (sender is not Button button) return; + var item = button.Tag?.ToString() ?? "Home"; + + DetailTitleText.Text = item; + + if (DemoDrawer.DrawerBehavior != DrawerBehavior.Locked) + DemoDrawer.IsOpen = false; + } + + private static void EnableMouseSwipeGesture(Control control) + { + var recognizer = control.GestureRecognizers + .OfType() + .FirstOrDefault(); + + if (recognizer is not null) + recognizer.IsMouseEnabled = true; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageEventsPage.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageEventsPage.xaml new file mode 100644 index 00000000..92a20558 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageEventsPage.xaml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageEventsPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageEventsPage.xaml.cs new file mode 100644 index 00000000..02148dc4 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageEventsPage.xaml.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; + +namespace ControlCatalog.Pages +{ + public partial class DrawerPageEventsPage : UserControl + { + private readonly Dictionary _sectionPages; + + public DrawerPageEventsPage() + { + InitializeComponent(); + + _sectionPages = new Dictionary + { + ["Home"] = CreateSectionPage("Home"), + ["Profile"] = CreateSectionPage("Profile"), + ["Settings"] = CreateSectionPage("Settings"), + }; + + foreach (var (name, page) in _sectionPages) + { + var label = name; + page.NavigatedTo += (_, _) => Log($"{label}: NavigatedTo"); + page.NavigatedFrom += (_, _) => Log($"{label}: NavigatedFrom"); + } + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + DemoDrawer.Opened += OnDrawerOpened; + DemoDrawer.Closing += OnClosing; + DemoDrawer.Closed += OnDrawerClosed; + // Set Content here so the initial NavigatedTo events fire + // (VisualRoot is null in the constructor, which suppresses lifecycle events). + DemoDrawer.Content = _sectionPages["Home"]; + } + + protected override void OnUnloaded(RoutedEventArgs e) + { + base.OnUnloaded(e); + DemoDrawer.Opened -= OnDrawerOpened; + DemoDrawer.Closing -= OnClosing; + DemoDrawer.Closed -= OnDrawerClosed; + } + + private void OnDrawerOpened(object? sender, System.EventArgs e) => Log("Opened"); + private void OnDrawerClosed(object? sender, System.EventArgs e) => Log("Closed"); + + private void OnToggle(object? sender, RoutedEventArgs e) + { + DemoDrawer.IsOpen = !DemoDrawer.IsOpen; + } + + private void OnClosing(object? sender, DrawerClosingEventArgs e) + { + if (CancelCheck.IsChecked == true) + { + e.Cancel = true; + CancelCheck.IsChecked = false; + Log("Closing \u2192 cancelled"); + } + else + { + Log("Closing"); + } + } + + private void OnSelectSection(object? sender, RoutedEventArgs e) + { + if (sender is not Button btn) return; + var section = btn.Tag?.ToString() ?? "Home"; + + if (!_sectionPages.TryGetValue(section, out var page)) return; + if (ReferenceEquals(DemoDrawer.Content, page)) + { + DemoDrawer.IsOpen = false; + return; + } + + Log($"\u2192 {section}"); + DemoDrawer.Content = page; + DemoDrawer.IsOpen = false; + } + + private void OnClearLog(object? sender, RoutedEventArgs e) + { + EventLog.Text = string.Empty; + } + + private void Log(string message) + { + EventLog.Text = $"{message}\n{EventLog.Text}"; + } + + private static ContentPage CreateSectionPage(string header) => new ContentPage + { + Header = header, + Content = new StackPanel + { + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + Spacing = 8, + Children = + { + new TextBlock + { + Text = header, + FontSize = 24, + FontWeight = FontWeight.SemiBold, + HorizontalAlignment = HorizontalAlignment.Center, + }, + new TextBlock + { + Text = "Tap a drawer item to navigate.\nWatch the event log in the panel.", + TextWrapping = TextWrapping.Wrap, + Opacity = 0.6, + TextAlignment = TextAlignment.Center, + FontSize = 13, + } + } + } + }; + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageFirstLookPage.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageFirstLookPage.xaml new file mode 100644 index 00000000..e257137e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageFirstLookPage.xaml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageRtlPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageRtlPage.xaml.cs new file mode 100644 index 00000000..36454ef8 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageRtlPage.xaml.cs @@ -0,0 +1,57 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Media; + +namespace ControlCatalog.Pages +{ + public partial class DrawerPageRtlPage : UserControl + { + public DrawerPageRtlPage() + { + InitializeComponent(); + } + + private void OnRtlToggled(object? sender, RoutedEventArgs e) + { + if (DemoDrawer == null) return; + DemoDrawer.FlowDirection = RtlCheckBox.IsChecked == true + ? FlowDirection.RightToLeft + : FlowDirection.LeftToRight; + } + + private void OnPlacementChanged(object? sender, SelectionChangedEventArgs e) + { + if (DemoDrawer == null) return; + DemoDrawer.DrawerPlacement = PlacementCombo.SelectedIndex switch + { + 0 => DrawerPlacement.Left, + 1 => DrawerPlacement.Right, + _ => DrawerPlacement.Left + }; + } + + private void OnToggleDrawer(object? sender, RoutedEventArgs e) + { + if (DemoDrawer == null) return; + DemoDrawer.IsOpen = !DemoDrawer.IsOpen; + } + + private void OnMenuItemClick(object? sender, RoutedEventArgs e) + { + if (sender is not Button button) return; + var item = button.Tag?.ToString() ?? "Home"; + + DetailTitleText.Text = item; + DetailDescriptionText.Text = item switch + { + "Home" => "Toggle RTL to see the drawer flip to the right edge.\nGestures are mirrored: drag from right edge to open, drag right to close.", + "Profile" => "View and edit your profile information here.", + "Messages" => "Your messages and notifications appear here.", + "Settings" => "Configure application preferences and options.", + _ => $"Content for {item}" + }; + + DemoDrawer.IsOpen = false; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageTransitionsPage.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageTransitionsPage.xaml new file mode 100644 index 00000000..9776163b --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageTransitionsPage.xaml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageTransitionsPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageTransitionsPage.xaml.cs new file mode 100644 index 00000000..23634c17 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/DrawerPageTransitionsPage.xaml.cs @@ -0,0 +1,94 @@ +using System; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Media; + +namespace ControlCatalog.Pages +{ + public partial class DrawerPageTransitionsPage : UserControl + { + private string _selectedTransition = "None"; + + public DrawerPageTransitionsPage() + { + InitializeComponent(); + Loaded += OnLoaded; + } + + private async void OnLoaded(object? sender, RoutedEventArgs e) + { + // Null out the default transition — OnTransitionChanged runs during init before DetailNav exists. + DetailNav.PageTransition = null; + await DetailNav.PushAsync(BuildPage("Home", _selectedTransition), null); + } + + private void OnTransitionChanged(object? sender, SelectionChangedEventArgs e) + { + if (DetailNav == null) return; + + _selectedTransition = TransitionCombo.SelectedIndex switch + { + 1 => "CrossFade", + 2 => "PageSlide (H)", + 3 => "PageSlide (V)", + 4 => "Composite (Slide + Fade)", + _ => "None" + }; + + DetailNav.PageTransition = TransitionCombo.SelectedIndex switch + { + 1 => new CrossFade(TimeSpan.FromMilliseconds(300)), + 2 => new PageSlide(TimeSpan.FromMilliseconds(300), PageSlide.SlideAxis.Horizontal), + 3 => new PageSlide(TimeSpan.FromMilliseconds(300), PageSlide.SlideAxis.Vertical), + 4 => new CompositePageTransition + { + PageTransitions = + { + new PageSlide(TimeSpan.FromMilliseconds(300), PageSlide.SlideAxis.Horizontal), + new CrossFade(TimeSpan.FromMilliseconds(300)) + } + }, + _ => null + }; + } + + private async void OnSectionClick(object? sender, RoutedEventArgs e) + { + if (sender is not Button button) return; + var section = button.Tag?.ToString() ?? "Home"; + + DemoDrawer.IsOpen = false; + + await DetailNav.ReplaceAsync(BuildPage(section, _selectedTransition)); + } + + private static ContentPage BuildPage(string section, string transitionName) + { + var (iconPath, body) = section switch + { + "Home" => + ("M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z", + "Your dashboard with recent activity, quick actions, and personalized content."), + "Explore" => + ("M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z", + "Discover new places, trending topics, and recommended content tailored to your interests."), + "Messages" => + ("M20,8L12,13L4,8V6L12,11L20,6M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4Z", + "Your conversations and notifications. Stay connected with the people who matter."), + "Profile" => + ("M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z", + "View and edit your profile, manage privacy settings, and control your account preferences."), + "Settings" => + ("M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.04 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.68 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.04 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z", + "Configure application preferences, notifications, and privacy options."), + _ => ("M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2Z", "") + }; + + var page = NavigationDemoHelper.MakeSectionPage(section, iconPath, section, body, 0, $"Transition: {transitionName}"); + NavigationPage.SetHasNavigationBar(page, false); + return page; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerAppPage.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerAppPage.xaml new file mode 100644 index 00000000..22320fbc --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerAppPage.xaml @@ -0,0 +1,302 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerAppPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerAppPage.xaml.cs new file mode 100644 index 00000000..49b13b4d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerAppPage.xaml.cs @@ -0,0 +1,319 @@ +using System; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; + +namespace ControlCatalog.Pages; + +public partial class EcoTrackerAppPage : UserControl +{ + static readonly Color Primary = Color.Parse("#2E7D32"); + static readonly Color Accent = Color.Parse("#4CAF50"); + static readonly Color BgLight = Color.Parse("#F1F8E9"); + static readonly Color TextDark = Color.Parse("#1A2E1C"); + static readonly Color TextMuted = Color.Parse("#90A4AE"); + + const string LeafPath = + "M12 3C9 6 6 9 6 13C6 17.4 8.7 21 12 22C15.3 21 18 17.4 18 13C18 9 15 6 12 3Z"; + + NavigationPage? _navPage; + DrawerPage? _drawerPage; + ScrollViewer? _infoPanel; + Button? _selectedBtn; + + public EcoTrackerAppPage() + { + InitializeComponent(); + + _infoPanel = this.FindControl("InfoPanel"); + _navPage = this.FindControl("NavPage"); + _drawerPage = this.FindControl("DrawerPageControl"); + _selectedBtn = this.FindControl + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerHomeView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerHomeView.xaml.cs new file mode 100644 index 00000000..3f36a4d4 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerHomeView.xaml.cs @@ -0,0 +1,14 @@ +using System; +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages; + +public partial class EcoTrackerHomeView : UserControl +{ + public Action? TreeDetailRequested { get; set; } + + public EcoTrackerHomeView() => InitializeComponent(); + + void OnHeroClick(object? sender, RoutedEventArgs e) => TreeDetailRequested?.Invoke(); +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerStatsView.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerStatsView.xaml new file mode 100644 index 00000000..82a5e938 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerStatsView.xaml @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerStatsView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerStatsView.xaml.cs new file mode 100644 index 00000000..90aa0206 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/EcoTrackerStatsView.xaml.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class EcoTrackerStatsView : UserControl +{ + public EcoTrackerStatsView() => InitializeComponent(); +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernAppPage.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernAppPage.xaml new file mode 100644 index 00000000..77a0e329 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernAppPage.xaml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernAppPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernAppPage.xaml.cs new file mode 100644 index 00000000..f0d4d7d6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernAppPage.xaml.cs @@ -0,0 +1,118 @@ +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Media; + +namespace ControlCatalog.Pages; + +public partial class ModernAppPage : UserControl +{ + // Palette + static readonly Color Primary = Color.Parse("#0dccf2"); + static readonly Color BgLight = Color.Parse("#f5f8f8"); + + static IBrush BgBrush => new SolidColorBrush(BgLight); + + DrawerPage? _drawerPage; + NavigationPage? _navPage; + ScrollViewer? _infoPanel; + TextBlock? _pageTitle; + Button? _selectedNavBtn; + + public ModernAppPage() + { + InitializeComponent(); + + _infoPanel = this.FindControl("InfoPanel"); + _drawerPage = this.FindControl("DrawerPageControl"); + _navPage = this.FindControl("NavPage"); + _pageTitle = this.FindControl("PageTitle"); + + if (_navPage != null) + NavigateToDiscover(); + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + UpdateInfoPanelVisibility(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == BoundsProperty) + UpdateInfoPanelVisibility(); + } + + void UpdateInfoPanelVisibility() + { + if (_infoPanel != null) + _infoPanel.IsVisible = Bounds.Width >= 640; + } + + void OnNavClick(object? sender, RoutedEventArgs e) + { + if (sender is not Button btn) return; + SelectNavButton(btn); + _drawerPage!.IsOpen = false; + switch (btn.Tag?.ToString()) + { + case "Discover": NavigateToDiscover(); break; + case "MyTrips": NavigateToMyTrips(); break; + case "Profile": NavigateToProfile(); break; + case "Settings": NavigateToSettings(); break; + } + } + + void OnCloseDrawer(object? sender, RoutedEventArgs e) + { + if (_drawerPage != null) _drawerPage.IsOpen = false; + } + + void SelectNavButton(Button btn) + { + if (_selectedNavBtn != null) + _selectedNavBtn.Background = Brushes.Transparent; + _selectedNavBtn = btn; + btn.Background = new SolidColorBrush(Color.Parse("#1A0dccf2")); + } + + async Task Navigate(ContentPage page) + { + if (_navPage == null) return; + NavigationPage.SetHasBackButton(page, false); + NavigationPage.SetHasNavigationBar(page, false); + await _navPage.PopToRootAsync(); + await _navPage.PushAsync(page); + } + + async void NavigateToDiscover() + { + if (_pageTitle != null) _pageTitle.Text = "Discover"; + SelectNavButton(this.FindControl + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernProfileView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernProfileView.xaml.cs new file mode 100644 index 00000000..ae2cdb94 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernProfileView.xaml.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class ModernProfileView : UserControl +{ + public ModernProfileView() => InitializeComponent(); +} diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernSettingsView.xaml b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernSettingsView.xaml new file mode 100644 index 00000000..a4ee8cd8 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernSettingsView.xaml @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernSettingsView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernSettingsView.xaml.cs new file mode 100644 index 00000000..d50ade3d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/DrawerPage/ModernSettingsView.xaml.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class ModernSettingsView : UserControl +{ + public ModernSettingsView() => InitializeComponent(); +} diff --git a/samples/AvaloniaControlCatalog/Pages/ExpanderPage.xaml b/samples/AvaloniaControlCatalog/Pages/ExpanderPage.xaml new file mode 100644 index 00000000..322da30a --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ExpanderPage.xaml @@ -0,0 +1,76 @@ + + + Expands to show nested content + + + + + Expanded content + + + + + Expanded content + + + + + Expanded content + + + + + Expanded content + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/FlyoutsPage.axaml.cs b/samples/AvaloniaControlCatalog/Pages/FlyoutsPage.axaml.cs new file mode 100644 index 00000000..d6f54df0 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/FlyoutsPage.axaml.cs @@ -0,0 +1,94 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Primitives.PopupPositioning; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class FlyoutsPage : ContentPage + { + public FlyoutsPage() + { + InitializeComponent(); + + AttachedFlyoutPanel.DoubleTapped += Afp_DoubleTapped; + + SetXamlTexts(); + } + + private void Afp_DoubleTapped(object? sender, RoutedEventArgs e) + { + if (sender is Panel p) + { + FlyoutBase.ShowAttachedFlyout(p); + } + } + + private void SetXamlTexts() + { + var bfxt = ButtonFlyoutXamlText; + bfxt.Text = ""; + + var mfxt = this.MenuFlyoutXamlText; + mfxt.Text = ""; + + var afxt = this.AttachedFlyoutXamlText; + afxt.Text = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n\n In DoubleTapped handler:\n" + + "FlyoutBase.ShowAttachedFlyout(AttachedFlyoutPanel);"; + + var sfxt = this.SharedFlyoutXamlText; + sfxt.Text = "Declare a flyout in Resources:\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n\n\n" + + "Then attach the flyout where you want it:\n" + + " + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/FocusPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/FocusPage.xaml.cs new file mode 100644 index 00000000..18aae471 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/FocusPage.xaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace ControlCatalog.Pages; + +public partial class FocusPage : ContentPage +{ + public FocusPage() + { + AvaloniaXamlLoader.Load(this); + } +} + diff --git a/samples/AvaloniaControlCatalog/Pages/GesturePage.xaml b/samples/AvaloniaControlCatalog/Pages/GesturePage.xaml new file mode 100644 index 00000000..da9ed5f8 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/GesturePage.xaml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/GesturePage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/GesturePage.xaml.cs new file mode 100644 index 00000000..56c09573 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/GesturePage.xaml.cs @@ -0,0 +1,39 @@ +using System; +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class GesturePage : ContentPage + { + private static readonly (string Group, string Title, string Description, Func Factory)[] Demos = + { + ("Touch / Pen", "Pull Gesture", + "Press and drag from colored border zones. A green ball tracks the pull delta and springs back on release.", + () => new GesturePullPage()), + + ("Multi Touch", "Pinch / Zoom", + "Pinch to scale an image using composition visuals. Scroll to pan when zoomed in.", + () => new GesturePinchZoomPage()), + + ("Multi Touch", "Pinch / Rotation", + "Pinch to rotate a rectangle. The Angle property from the pinch event drives a RotateTransform.", + () => new GesturePinchRotationPage()), + + ("Touch / Pen / Mouse", "Swipe Gesture", + "Swipe horizontally or vertically. Configure direction, threshold, and mouse support. Shows live delta, velocity, and direction.", + () => new GestureSwipePage()), + }; + + public GesturePage() + { + InitializeComponent(); + Loaded += OnLoaded; + } + + private async void OnLoaded(object? sender, RoutedEventArgs e) + { + await SampleNav.PushAsync(NavigationDemoHelper.CreateGalleryHomePage(SampleNav, Demos), null); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchRotationPage.xaml b/samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchRotationPage.xaml new file mode 100644 index 00000000..b46af74c --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchRotationPage.xaml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchRotationPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchRotationPage.xaml.cs new file mode 100644 index 00000000..55d5e619 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchRotationPage.xaml.cs @@ -0,0 +1,33 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; + +namespace ControlCatalog.Pages +{ + public partial class GesturePinchRotationPage : UserControl + { + private bool _isInit; + + public GesturePinchRotationPage() + { + InitializeComponent(); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + if (_isInit) + { + return; + } + + _isInit = true; + + RotationGesture.AddHandler(InputElement.PinchEvent, (s, e) => + { + AngleSlider.Value = e.Angle; + }); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchZoomPage.xaml b/samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchZoomPage.xaml new file mode 100644 index 00000000..6932741e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/Gestures/GesturePinchZoomPage.xaml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/LabelsPage.axaml.cs b/samples/AvaloniaControlCatalog/Pages/LabelsPage.axaml.cs new file mode 100644 index 00000000..b26112b3 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/LabelsPage.axaml.cs @@ -0,0 +1,35 @@ +using Avalonia.Controls; +using ControlCatalog.Models; + +namespace ControlCatalog.Pages +{ + public partial class LabelsPage : ContentPage + { + private Person? _person; + + public LabelsPage() + { + CreateDefaultPerson(); + this.InitializeComponent(); + } + + private void CreateDefaultPerson() + { + DataContext = _person = new Person + { + FirstName = "John", + LastName = "Doe", + IsBanned = true, + }; + } + + public void DoSave() + { + + } + public void DoCancel() + { + CreateDefaultPerson(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/LayoutTransformControlPage.xaml b/samples/AvaloniaControlCatalog/Pages/LayoutTransformControlPage.xaml new file mode 100644 index 00000000..d12bbfc3 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/LayoutTransformControlPage.xaml @@ -0,0 +1,28 @@ + + + + Rotation + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/LayoutTransformControlPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/LayoutTransformControlPage.xaml.cs new file mode 100644 index 00000000..e3815238 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/LayoutTransformControlPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class LayoutTransformControlPage : ContentPage + { + public LayoutTransformControlPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ListBoxPage.xaml b/samples/AvaloniaControlCatalog/Pages/ListBoxPage.xaml new file mode 100644 index 00000000..f73a4c18 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ListBoxPage.xaml @@ -0,0 +1,51 @@ + + + + + + + + + + Hosts a collection of ListBoxItem. + Each 5th item is highlighted with nth-child(5n+3) and nth-last-child(5n+4) rules. + + + Multiple + Toggle + AlwaysSelected + AutoScrollToSelectedItem + WrapSelection + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ListBoxPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ListBoxPage.xaml.cs new file mode 100644 index 00000000..208b5cce --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ListBoxPage.xaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public partial class ListBoxPage : ContentPage + { + public ListBoxPage() + { + InitializeComponent(); + DataContext = new ListBoxPageViewModel(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/MenuPage.xaml b/samples/AvaloniaControlCatalog/Pages/MenuPage.xaml new file mode 100644 index 00000000..d9fb5d81 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/MenuPage.xaml @@ -0,0 +1,99 @@ + + + Exported menu fallback + (Should be only visible on platforms without desktop-global menu bar) + + A window menu + + + + Defined in XAML + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dyanamically generated + + + + + + + + + Mixed + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/MenuPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/MenuPage.xaml.cs new file mode 100644 index 00000000..56a229a5 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/MenuPage.xaml.cs @@ -0,0 +1,28 @@ +using System; +using Avalonia.Controls; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public partial class MenuPage : ContentPage + { + public MenuPage() + { + InitializeComponent(); + DataContext = new MenuPageViewModel(); + } + + private MenuPageViewModel? _model; + protected override void OnDataContextChanged(EventArgs e) + { + if (_model != null) + _model.View = null; + _model = DataContext as MenuPageViewModel; + if (_model != null) + _model.View = this; + + base.OnDataContextChanged(e); + } + + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NativeEmbedPage.xaml b/samples/AvaloniaControlCatalog/Pages/NativeEmbedPage.xaml new file mode 100644 index 00000000..71d622ba --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NativeEmbedPage.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + Text + + + Tooltip + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NativeEmbedPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NativeEmbedPage.xaml.cs new file mode 100644 index 00000000..1cd1f78c --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NativeEmbedPage.xaml.cs @@ -0,0 +1,77 @@ +using System; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Platform; + +namespace ControlCatalog.Pages +{ + public partial class NativeEmbedPage : ContentPage + { + public NativeEmbedPage() + { + InitializeComponent(); + } + + public async void ShowPopupDelay(object sender, RoutedEventArgs args) + { + await Task.Delay(3000); + ShowPopup(sender, args); + } + + public void ShowPopup(object sender, RoutedEventArgs args) + { + new ContextMenu() + { + Items = + { + new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" } + } + }.Open((Control)sender); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == BoundsProperty) + { + var isMobile = change.GetNewValue().Width < 1200; + FirstPanel.Classes.Set("mobile", isMobile); + SecondPanel.Classes.Set("mobile", isMobile); + } + } + } + + public class EmbedSample : NativeControlHost + { + public static INativeDemoControl? Implementation { get; set; } + + static EmbedSample() + { + + } + + public bool IsSecond { get; set; } + + protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) + { + return Implementation?.CreateControl(IsSecond, parent, () => base.CreateNativeControlCore(parent)) + ?? base.CreateNativeControlCore(parent); + } + + protected override void DestroyNativeControlCore(IPlatformHandle control) + { + base.DestroyNativeControlCore(control); + } + } + + public interface INativeDemoControl + { + /// Used to specify which control should be displayed as a demo + /// + /// + IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationDemoHelper.cs b/samples/AvaloniaControlCatalog/Pages/NavigationDemoHelper.cs new file mode 100644 index 00000000..22e52b6f --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationDemoHelper.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections.Generic; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Layout; +using Avalonia.Media; + +namespace ControlCatalog.Pages +{ + /// + /// Shared helpers for ControlCatalog demo pages. + /// + internal static class NavigationDemoHelper + { + /// + /// Pastel background brushes cycled by page index. + /// + internal static readonly IBrush[] PageBrushes = + { + new SolidColorBrush(Color.Parse("#BBDEFB")), + new SolidColorBrush(Color.Parse("#C8E6C9")), + new SolidColorBrush(Color.Parse("#FFE0B2")), + new SolidColorBrush(Color.Parse("#E1BEE7")), + new SolidColorBrush(Color.Parse("#FFCDD2")), + new SolidColorBrush(Color.Parse("#B2EBF2")), + }; + + internal static IBrush GetPageBrush(int index) => + PageBrushes[((index % PageBrushes.Length) + PageBrushes.Length) % PageBrushes.Length]; + + /// + /// Creates a simple demo ContentPage with a centered title and subtitle. + /// + internal static ContentPage MakePage(string header, string body, int colorIndex) => + new ContentPage + { + Header = header, + Background = GetPageBrush(colorIndex), + Content = new StackPanel + { + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + Spacing = 8, + Children = + { + new TextBlock + { + Text = header, + FontSize = 20, + FontWeight = FontWeight.SemiBold, + HorizontalAlignment = HorizontalAlignment.Center + }, + new TextBlock + { + Text = body, + FontSize = 13, + Opacity = 0.7, + TextWrapping = TextWrapping.Wrap, + TextAlignment = TextAlignment.Center, + MaxWidth = 260 + } + } + }, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + VerticalContentAlignment = VerticalAlignment.Stretch + }; + + /// + /// Creates a demo ContentPage with an icon, title, body, and hint text + /// (used by DrawerPage detail pages). + /// + internal static ContentPage MakeSectionPage( + string header, string iconData, string title, string body, + int colorIndex, string? hint = null) + { + var panel = new StackPanel { Margin = new Thickness(24, 20), Spacing = 12 }; + + panel.Children.Add(new PathIcon + { + Width = 48, + Height = 48, + Data = Geometry.Parse(iconData), + Foreground = new SolidColorBrush(Color.Parse("#0078D4")) + }); + panel.Children.Add(new TextBlock + { + Text = title, + FontSize = 26, + FontWeight = FontWeight.Bold + }); + panel.Children.Add(new TextBlock + { + Text = body, + FontSize = 14, + Opacity = 0.8, + TextWrapping = TextWrapping.Wrap + }); + panel.Children.Add(new Separator { Margin = new Thickness(0, 8) }); + + if (hint != null) + { + panel.Children.Add(new TextBlock + { + Text = hint, + FontSize = 12, + Opacity = 0.45, + FontStyle = FontStyle.Italic, + TextWrapping = TextWrapping.Wrap + }); + } + + return new ContentPage + { + Header = header, + Background = GetPageBrush(colorIndex), + Content = new ScrollViewer { Content = panel }, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + VerticalContentAlignment = VerticalAlignment.Stretch + }; + } + + private static readonly Geometry CloseIcon = Geometry.Parse( + "M4.397 4.397a1 1 0 0 1 1.414 0L12 10.585l6.19-6.188a1 1 0 0 1 1.414 1.414L13.413 12l6.19 6.189a1 1 0 0 1-1.414 1.414L12 13.413l-6.189 6.19a1 1 0 0 1-1.414-1.414L10.585 12 4.397 5.811a1 1 0 0 1 0-1.414z"); + + /// + /// Builds the demo gallery home page for NavigationPage/TabbedPage/DrawerPage demo registries. + /// + internal static ContentPage CreateGalleryHomePage( + NavigationPage nav, + (string Group, string Title, string Description, Func Factory)[] demos) + { + var stack = new StackPanel { Margin = new Thickness(12), Spacing = 16 }; + + var groups = new Dictionary(); + var groupOrder = new List(); + + foreach (var (group, title, description, factory) in demos) + { + if (!groups.ContainsKey(group)) + { + groups[group] = new WrapPanel + { + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Left + }; + groupOrder.Add(group); + } + + var demoFactory = factory; + var demoTitle = title; + + var card = new Button + { + Width = 170, + MinHeight = 80, + Margin = new Thickness(0, 0, 8, 8), + VerticalAlignment = VerticalAlignment.Top, + HorizontalContentAlignment = HorizontalAlignment.Left, + VerticalContentAlignment = VerticalAlignment.Top, + Padding = new Thickness(12, 8), + Content = new StackPanel + { + Spacing = 4, + Children = + { + new TextBlock + { + Text = title, + FontSize = 13, + FontWeight = FontWeight.SemiBold, + TextWrapping = TextWrapping.Wrap + }, + new TextBlock + { + Text = description, + FontSize = 11, + Opacity = 0.6, + TextWrapping = TextWrapping.Wrap + } + } + } + }; + + card.Click += async (_, _) => + { + var headerGrid = new Grid { ColumnDefinitions = new ColumnDefinitions("*, Auto") }; + headerGrid.Children.Add(new TextBlock + { + Text = demoTitle, + VerticalAlignment = VerticalAlignment.Center + }); + var closeBtn = new Button + { + Content = new PathIcon { Data = CloseIcon }, + Background = Brushes.Transparent, + BorderThickness = new Thickness(0), + Padding = new Thickness(8, 4), + VerticalAlignment = VerticalAlignment.Center + }; + Grid.SetColumn(closeBtn, 1); + headerGrid.Children.Add(closeBtn); + closeBtn.Click += async (_, _) => await nav.PopAsync(null); + + var page = new ContentPage + { + Header = headerGrid, + Content = demoFactory(), + HorizontalContentAlignment = HorizontalAlignment.Stretch, + VerticalContentAlignment = VerticalAlignment.Stretch + }; + NavigationPage.SetHasBackButton(page, false); + await nav.PushAsync(page, null); + }; + + groups[group].Children.Add(card); + } + + foreach (var groupName in groupOrder) + { + stack.Children.Add(new TextBlock + { + Text = groupName, + FontSize = 13, + FontWeight = FontWeight.SemiBold, + Margin = new Thickness(0, 0, 0, 4), + Opacity = 0.6 + }); + stack.Children.Add(groups[groupName]); + } + + var homePage = new ContentPage + { + Content = new ScrollViewer { Content = stack }, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + VerticalContentAlignment = VerticalAlignment.Stretch + }; + NavigationPage.SetHasNavigationBar(homePage, false); + return homePage; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationDemoPage.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationDemoPage.xaml new file mode 100644 index 00000000..0f7536b6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationDemoPage.xaml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationDemoPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationDemoPage.xaml.cs new file mode 100644 index 00000000..7ca41ab8 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationDemoPage.xaml.cs @@ -0,0 +1,94 @@ +using System; +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class NavigationDemoPage : ContentPage + { + private static readonly (string Group, string Title, string Description, Func Factory)[] Demos = + { + // Overview + ("Overview", "First Look", "Basic NavigationPage with push/pop navigation and back button support.", + () => new NavigationPageFirstLookPage()), + ("Overview", "Modal Navigation", "Push and pop modal pages that appear on top of the navigation stack.", + () => new NavigationPageModalPage()), + ("Overview", "Navigation Events", + "Subscribe to Pushed, Popped, PoppedToRoot, ModalPushed, and ModalPopped events.", + () => new NavigationPageEventsPage()), + + // Appearance + ("Appearance", "Bar Customization", + "Customize the navigation bar background, foreground, shadow, and visibility.", + () => new NavigationPageAppearancePage()), + ("Appearance", "Header", + "Set page header content: a string, icon, or any custom control in the navigation bar.", + () => new NavigationPageTitlePage()), + + // Data + ("Data", "Pass Data", "Pass data during navigation via constructor arguments or DataContext.", + () => new NavigationPagePassDataPage()), + ("Data", "MVVM Navigation", + "Keep navigation decisions in view models by routing NavigationPage push and pop operations through a small INavigationService.", + () => new NavigationPageMvvmPage()), + + // Features + ("Features", "Attached Methods", + "Per-page navigation bar and back button control via static attached methods.", + () => new NavigationPageAttachedMethodsPage()), + ("Features", "Back Button", "Customize, hide, or intercept the back button.", + () => new NavigationPageBackButtonPage()), + ("Features", "CommandBar", + "Add, remove and position CommandBar items inside the navigation bar or as a bottom bar.", + () => new NavigationPageToolbarPage()), + ("Features", "Transitions", + "Configure page transitions: PageSlide, Parallax Slide, CrossFade, Fade Through, and more.", + () => new NavigationPageTransitionsPage()), + ("Features", "Modal Transitions", "Configure modal transition: PageSlide from bottom, CrossFade, or None.", + () => new NavigationPageModalTransitionsPage()), + ("Features", "Stack Management", "Remove or insert pages anywhere in the navigation stack at runtime.", + () => new NavigationPageStackPage()), + ("Features", "Interactive Header", + "Build a header with a title and live search box that filters page content in real time.", + () => new NavigationPageInteractiveHeaderPage()), + ("Features", "Back Swipe Gesture", "Swipe from the left edge to interactively pop the current page.", + () => new NavigationPageGesturePage()), + ("Features", "Scroll-Aware Bar", + "Hide the navigation bar on downward scroll and reveal it on upward scroll.", + () => new NavigationPageScrollAwarePage()), + + // Performance + ("Performance", "Performance Monitor", + "Track stack depth, live page instances, and managed heap size. Observe how memory is reclaimed after popping pages.", + () => new NavigationPagePerformancePage()), + + // Showcases + ("Showcases", "Pulse Fitness", + "Login flow with RemovePage, TabbedPage dashboard with bottom tabs, and NavigationPage push for workout detail.", + () => new PulseAppPage()), + ("Showcases", "L'Avenir", + "Restaurant app with DrawerPage flyout menu, TabbedPage bottom tabs, and NavigationPage push for dish detail.", + () => new LAvenirAppPage()), + ("Showcases", "AvaloniaFlix", + "Streaming app with dark NavigationPage, hidden nav bar on home, and custom bar tint on movie detail pages.", + () => new AvaloniaFlixAppPage()), + ("Showcases", "Retro Gaming", + "Arcade-style app with NavigationPage header, TabbedPage bottom tabs with CenteredTabPanel, and game detail push.", + () => new RetroGamingAppPage()), + ("Showcases", "Curved Header", + "Shop app with dome-bottomed white header on home (nav bar hidden) and blue curved header on detail (BarLayoutBehavior.Overlay).", + () => new NavigationPageCurvedHeaderPage()), + }; + + public NavigationDemoPage() + { + InitializeComponent(); + Loaded += OnLoaded; + } + + private async void OnLoaded(object? sender, RoutedEventArgs e) + { + await SampleNav.PushAsync(NavigationDemoHelper.CreateGalleryHomePage(SampleNav, Demos), null); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixAppPage.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixAppPage.xaml new file mode 100644 index 00000000..6e209c8b --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixAppPage.xaml @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixAppPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixAppPage.xaml.cs new file mode 100644 index 00000000..a2ae34dd --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixAppPage.xaml.cs @@ -0,0 +1,191 @@ +using System; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; + +namespace ControlCatalog.Pages; + +public partial class AvaloniaFlixAppPage : UserControl +{ + NavigationPage? _detailNav; + ScrollViewer? _infoPanel; + + public AvaloniaFlixAppPage() + { + InitializeComponent(); + + _detailNav = this.FindControl("DetailNav"); + if (_detailNav != null) + { + _detailNav.ModalTransition = new PageSlide(TimeSpan.FromMilliseconds(300), PageSlide.SlideAxis.Vertical); + + var homeView = new AvaloniaFlixHomeView(); + homeView.MovieSelected = title => PushDetailPage(title); + homeView.SearchRequested = () => _ = PushSearchPageAsync(); + + var homePage = new ContentPage + { + Content = homeView, + Background = Brushes.Transparent, + Header = BuildHomeHeader(), + }; + NavigationPage.SetTopCommandBar(homePage, BuildHomeCommandBar()); + _ = _detailNav.PushAsync(homePage); + } + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + + _infoPanel = this.FindControl("InfoPanel"); + UpdateInfoPanelVisibility(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == BoundsProperty) + UpdateInfoPanelVisibility(); + } + + void UpdateInfoPanelVisibility() + { + if (_infoPanel != null) + _infoPanel.IsVisible = Bounds.Width >= 650; + } + + TextBlock BuildHomeHeader() => new TextBlock + { + Text = "AVALONIAFLIX", + Foreground = new SolidColorBrush(Color.Parse("#E50914")), + FontSize = 18, + FontWeight = Avalonia.Media.FontWeight.Black, + VerticalAlignment = VerticalAlignment.Center, + }; + + StackPanel BuildHomeCommandBar() + { + var cmdBar = new StackPanel + { + Orientation = Orientation.Horizontal, + Spacing = 12, + VerticalAlignment = VerticalAlignment.Center, + Margin = new Thickness(0, 0, 8, 0), + }; + var searchBtn = new Button { Padding = new Thickness(4) }; + searchBtn.Classes.Add("flixTransparent"); + searchBtn.Click += OnSearchClick; + searchBtn.Content = new PathIcon + { + Width = 20, Height = 20, Foreground = Brushes.White, + Data = Avalonia.Media.Geometry.Parse("M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z"), + }; + cmdBar.Children.Add(searchBtn); + cmdBar.Children.Add(new Border + { + Width = 30, Height = 30, CornerRadius = new CornerRadius(4), + Background = new SolidColorBrush(Color.Parse("#333333")), + Child = new TextBlock + { + Text = "JD", FontSize = 10, FontWeight = Avalonia.Media.FontWeight.Bold, + Foreground = Brushes.White, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + }, + }); + return cmdBar; + } + + async void PushDetailPage(string title) + { + if (_detailNav == null) return; + + var detailView = new AvaloniaFlixDetailView(title); + + var headerTitle = new TextBlock + { + Text = title, FontSize = 17, FontWeight = Avalonia.Media.FontWeight.Bold, + Foreground = Brushes.White, VerticalAlignment = VerticalAlignment.Center, + }; + + var shareBtnContent = new PathIcon + { + Width = 20, Height = 20, Foreground = Brushes.White, + Data = Avalonia.Media.Geometry.Parse("M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.35C15.11,18.56 15.08,18.78 15.08,19C15.08,20.61 16.39,21.92 18,21.92C19.61,21.92 20.92,20.61 20.92,19C20.92,17.39 19.61,16.08 18,16.08Z"), + }; + var shareBtn = new Button { Padding = new Thickness(8), Content = shareBtnContent }; + shareBtn.Classes.Add("flixTransparent"); + + var bookmarkBtnContent = new PathIcon + { + Width = 20, Height = 20, Foreground = Brushes.White, + Data = Avalonia.Media.Geometry.Parse("M17,3H7A2,2 0 0,0 5,5V21L12,18L19,21V5C19,3.89 18.1,3 17,3Z"), + }; + var bookmarkBtn = new Button { Padding = new Thickness(8), Content = bookmarkBtnContent }; + bookmarkBtn.Classes.Add("flixTransparent"); + + var detailCmdBar = new StackPanel + { + Orientation = Orientation.Horizontal, Spacing = 8, + VerticalAlignment = VerticalAlignment.Center, + }; + detailCmdBar.Children.Add(shareBtn); + detailCmdBar.Children.Add(bookmarkBtn); + + var detailPage = new ContentPage + { + Content = detailView, + Background = Brushes.Transparent, + Header = headerTitle, + }; + NavigationPage.SetTopCommandBar(detailPage, detailCmdBar); + + await _detailNav.PushAsync(detailPage); + + var drawer = this.FindControl("DrawerPageControl"); + if (drawer is { IsOpen: true }) + drawer.IsOpen = false; + } + + async void OnSearchClick(object? sender, RoutedEventArgs e) + { + await PushSearchPageAsync(); + } + + async Task PushSearchPageAsync() + { + if (_detailNav == null) return; + + var searchView = new AvaloniaFlixSearchView(); + searchView.CloseRequested = async () => await (_detailNav?.PopModalAsync() ?? Task.CompletedTask); + searchView.MovieSelected = async title => + { + if (_detailNav != null && _detailNav.ModalStack.Count > 0) + await _detailNav.PopModalAsync(); + PushDetailPage(title); + }; + + var searchPage = new ContentPage + { + Content = searchView, + Background = new SolidColorBrush(Color.Parse("#0A0A0A")), + }; + NavigationPage.SetHasNavigationBar(searchPage, false); + + await (_detailNav?.PushModalAsync(searchPage) ?? Task.CompletedTask); + } + + void OnMenuItemClick(object? sender, RoutedEventArgs e) + { + var drawer = this.FindControl("DrawerPageControl"); + if (drawer != null) + drawer.IsOpen = false; + + _ = _detailNav?.PopToRootAsync(); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixDetailView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixDetailView.xaml new file mode 100644 index 00000000..e6f53066 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixDetailView.xaml @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixDetailView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixDetailView.xaml.cs new file mode 100644 index 00000000..659fea52 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixDetailView.xaml.cs @@ -0,0 +1,60 @@ +using System; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; + +namespace ControlCatalog.Pages; + +public partial class AvaloniaFlixDetailView : UserControl +{ + static readonly string[] MovieAssets = + { + "avares://ControlCatalog/Assets/Movies/trending1.jpg", + "avares://ControlCatalog/Assets/Movies/trending2.jpg", + "avares://ControlCatalog/Assets/Movies/toprated1.jpg", + "avares://ControlCatalog/Assets/Movies/toprated2.jpg", + "avares://ControlCatalog/Assets/Movies/toprated3.jpg", + "avares://ControlCatalog/Assets/Movies/toprated4.jpg", + "avares://ControlCatalog/Assets/Movies/continue1.jpg", + "avares://ControlCatalog/Assets/Movies/morelike1.jpg", + "avares://ControlCatalog/Assets/Movies/search1.jpg", + "avares://ControlCatalog/Assets/Movies/hero.jpg", + "avares://ControlCatalog/Assets/Movies/cast1.jpg", + "avares://ControlCatalog/Assets/Movies/cast2.jpg", + }; + + public AvaloniaFlixDetailView() => InitializeComponent(); + + public AvaloniaFlixDetailView(string movieTitle) + { + InitializeComponent(); + + HeroTitleLabel.Text = movieTitle; + + var rng = new Random(movieTitle.GetHashCode()); + int imgIdx = Math.Abs(movieTitle.GetHashCode()) % MovieAssets.Length; + + string year = (2020 + rng.Next(6)).ToString(); + string rating = $"{6.5 + rng.NextDouble() * 3.0:F1}/10"; + int mins = 90 + rng.Next(60); + string duration = $"{mins / 60}h {mins % 60}m"; + + YearLabel.Text = year; + RatingLabel.Text = rating; + DurationLabel.Text = duration; + + try + { + var uri = new Uri(MovieAssets[imgIdx]); + HeroBg.Background = new ImageBrush(new Bitmap(AssetLoader.Open(uri))) + { + Stretch = Stretch.UniformToFill, + }; + } + catch + { + HeroBg.Background = new SolidColorBrush(Color.Parse("#111111")); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixHomeView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixHomeView.xaml new file mode 100644 index 00000000..4f186e91 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixHomeView.xaml @@ -0,0 +1,630 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixHomeView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixHomeView.xaml.cs new file mode 100644 index 00000000..81702bf6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixHomeView.xaml.cs @@ -0,0 +1,21 @@ +using System; +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages; + +public partial class AvaloniaFlixHomeView : UserControl +{ + public Action? MovieSelected { get; set; } + public Action? SearchRequested { get; set; } + + public AvaloniaFlixHomeView() => InitializeComponent(); + + void OnMovieClick(object? sender, RoutedEventArgs e) + { + string title = "Cyber Dune"; + if (sender is Button btn && btn.Tag is string tag) + title = tag; + MovieSelected?.Invoke(title); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixSearchView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixSearchView.xaml new file mode 100644 index 00000000..d4c53652 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixSearchView.xaml @@ -0,0 +1,302 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixSearchView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixSearchView.xaml.cs new file mode 100644 index 00000000..aac3069b --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/AvaloniaFlixSearchView.xaml.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.VisualTree; + +namespace ControlCatalog.Pages; + +public partial class AvaloniaFlixSearchView : UserControl +{ + public Action? CloseRequested { get; set; } + public Action? MovieSelected { get; set; } + + public AvaloniaFlixSearchView() => InitializeComponent(); + + void OnCloseClick(object? sender, RoutedEventArgs e) + { + if (CloseRequested != null) + { + CloseRequested(); + } + else + { + var nav = this.FindAncestorOfType(); + _ = nav?.PopModalAsync() ?? Task.CompletedTask; + } + } + + void OnMovieClick(object? sender, RoutedEventArgs e) + { + string title = "Neon Horizon"; + if (sender is Button btn && btn.Tag is string tag) + title = tag; + MovieSelected?.Invoke(title); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderHomeScrollView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderHomeScrollView.xaml new file mode 100644 index 00000000..8810c577 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderHomeScrollView.xaml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderHomeScrollView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderHomeScrollView.xaml.cs new file mode 100644 index 00000000..c8910584 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderHomeScrollView.xaml.cs @@ -0,0 +1,16 @@ +using System; +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages; + +public partial class CurvedHeaderHomeScrollView : UserControl +{ + public Action? NavigateRequested { get; set; } + + public CurvedHeaderHomeScrollView() => InitializeComponent(); + + void OnShopNowClick(object? sender, RoutedEventArgs e) => NavigateRequested?.Invoke(); + + void OnProductClick(object? sender, RoutedEventArgs e) => NavigateRequested?.Invoke(); +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderProfileScrollView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderProfileScrollView.xaml new file mode 100644 index 00000000..ecfeb1bb --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/CurvedHeaderProfileScrollView.xaml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirAppPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirAppPage.xaml.cs new file mode 100644 index 00000000..3ccdcaef --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirAppPage.xaml.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Shapes; +using Avalonia.Controls.Templates; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Styling; + +namespace ControlCatalog.Pages; + +public partial class LAvenirAppPage : UserControl +{ + static readonly Color Primary = Color.Parse("#4b2bee"); + static readonly Color BgDark = Color.Parse("#131022"); + static readonly Color BgLight = Color.Parse("#f6f6f8"); + static readonly Color TextDark = Color.Parse("#1e293b"); + static readonly Color TextMuted = Color.Parse("#94a3b8"); + static readonly Color BorderLight = Color.Parse("#e2e8f0"); + + NavigationPage? _navPage; + DrawerPage? _drawerPage; + ScrollViewer? _infoPanel; + + public LAvenirAppPage() + { + InitializeComponent(); + + _navPage = this.FindControl("NavPage"); + _drawerPage = this.FindControl("DrawerPageControl"); + + if (_navPage != null) + _ = _navPage.PushAsync(BuildMenuTabbedPage()); + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + + _infoPanel = this.FindControl("InfoPanel"); + UpdateInfoPanelVisibility(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == BoundsProperty) + UpdateInfoPanelVisibility(); + } + + void UpdateInfoPanelVisibility() + { + if (_infoPanel != null) + _infoPanel.IsVisible = Bounds.Width >= 650; + } + + TabbedPage BuildMenuTabbedPage() + { + var tp = new TabbedPage + { + Background = new SolidColorBrush(BgLight), + TabPlacement = TabPlacement.Bottom, + PageTransition = new PageSlide(TimeSpan.FromMilliseconds(200)), + }; + tp.Resources["TabItemHeaderFontSize"] = 12.0; + tp.Resources["TabbedPageTabStripBackground"] = Brushes.White; + tp.Resources["TabbedPageTabStripBorderThickness"] = new Thickness(0, 1, 0, 0); + tp.Resources["TabbedPageTabStripBorderBrush"] = new SolidColorBrush(BorderLight); + tp.Resources["TabbedPageTabItemHeaderForegroundSelected"] = new SolidColorBrush(Primary); + tp.Resources["TabbedPageTabItemHeaderForegroundUnselected"] = new SolidColorBrush(TextMuted); + + tp.IndicatorTemplate = new FuncDataTemplate((_, _) => + new Ellipse + { + Width = 5, Height = 5, + Margin = new Thickness(0, 10, 0, 0), + HorizontalAlignment = HorizontalAlignment.Center, + Fill = new SolidColorBrush(Primary), + }); + + tp.Header = new TextBlock + { + Text = "L'Avenir", + FontSize = 18, + FontWeight = FontWeight.Bold, + Foreground = new SolidColorBrush(TextDark), + VerticalAlignment = VerticalAlignment.Center, + TextAlignment = TextAlignment.Center, + }; + + NavigationPage.SetTopCommandBar(tp, new Button + { + Width = 40, + Height = 40, + CornerRadius = new CornerRadius(12), + Background = Brushes.Transparent, + Foreground = new SolidColorBrush(TextDark), + Padding = new Thickness(8), + BorderThickness = new Thickness(0), + Content = new PathIcon + { + Data = Geometry.Parse("M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z"), + Width = 18, + Height = 18, + }, + VerticalAlignment = VerticalAlignment.Center, + }); + + var menuView = new LAvenirMenuView(); + menuView.DishSelected = PushDishDetail; + + var menuPage = new ContentPage + { + Content = menuView, + Background = new SolidColorBrush(BgLight), + Header = "Menu", + Icon = new PathIcon { Data = Geometry.Parse("M11 9H9V2H7v7H5V2H3v7c0 2.12 1.66 3.84 3.75 3.97V22h2.5v-9.03C11.34 12.84 13 11.12 13 9V2h-2v7zm5-3v8h2.5v8H21V2c-2.76 0-5 2.24-5 4z") }, + }; + + var reservationsPage = new ContentPage + { + Content = new LAvenirReservationsView(), + Background = new SolidColorBrush(BgLight), + Header = "Reservations", + Icon = new PathIcon { Data = Geometry.Parse("M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM9 10H7v2h2v-2zm4 0h-2v2h2v-2zm4 0h-2v2h2v-2z") }, + }; + + var profilePage = new ContentPage + { + Content = new LAvenirProfileView(), + Background = new SolidColorBrush(BgLight), + Header = "Profile", + Icon = new PathIcon { Data = Geometry.Parse("M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z") }, + }; + + tp.Pages = new ObservableCollection { menuPage, reservationsPage, profilePage }; + return tp; + } + + async void PushDishDetail(string name, string price, string description, string imageFile) + { + if (_navPage == null) return; + + var detail = new ContentPage + { + Content = new LAvenirDishDetailView(name, price, description, imageFile), + Background = new SolidColorBrush(BgDark), + Header = name, + }; + NavigationPage.SetBottomCommandBar(detail, BuildFloatingBar(price)); + + _navPage.Background = new SolidColorBrush(BgDark); + _navPage.Resources["NavigationBarBackground"] = new SolidColorBrush(BgDark); + _navPage.Resources["NavigationBarForeground"] = Brushes.White; + + detail.NavigatedFrom += (_, _) => + { + if (_navPage != null) + { + _navPage.Background = new SolidColorBrush(BgLight); + _navPage.Resources["NavigationBarBackground"] = new SolidColorBrush(BgLight); + _navPage.Resources["NavigationBarForeground"] = new SolidColorBrush(TextDark); + } + }; + + await _navPage.PushAsync(detail); + } + + Border BuildFloatingBar(string price) + { + var bar = new Border + { + CornerRadius = new CornerRadius(16), + Background = new SolidColorBrush(Color.FromArgb(178, BgDark.R, BgDark.G, BgDark.B)), + BorderBrush = new SolidColorBrush(Color.FromArgb(51, 255, 255, 255)), + BorderThickness = new Thickness(1), + Padding = new Thickness(16, 12), + Margin = new Thickness(16, 8, 16, 8), + }; + + var barGrid = new Grid { ColumnDefinitions = new ColumnDefinitions("*,Auto") }; + + var info = new StackPanel { VerticalAlignment = VerticalAlignment.Center }; + info.Children.Add(new TextBlock + { + Text = "Add to Order", + FontSize = 14, + FontWeight = FontWeight.Bold, + Foreground = Brushes.White, + }); + info.Children.Add(new TextBlock + { + Text = price, + FontSize = 12, + FontWeight = FontWeight.Medium, + Foreground = new SolidColorBrush(TextMuted), + }); + barGrid.Children.Add(info); + + var addBtn = new Button + { + Content = "Add", + Width = 80, + Height = 40, + CornerRadius = new CornerRadius(10), + Background = new SolidColorBrush(Primary), + Foreground = Brushes.White, + FontWeight = FontWeight.Bold, + FontSize = 14, + HorizontalContentAlignment = HorizontalAlignment.Center, + VerticalContentAlignment = VerticalAlignment.Center, + }; + var hoverStyle = new Style(x => x.OfType + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirReservationsView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirReservationsView.xaml.cs new file mode 100644 index 00000000..5ea6d72c --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/LAvenirReservationsView.xaml.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class LAvenirReservationsView : UserControl +{ + public LAvenirReservationsView() => InitializeComponent(); +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageAppearancePage.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageAppearancePage.xaml new file mode 100644 index 00000000..f519b418 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/NavigationPageAppearancePage.xaml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseHomeView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseHomeView.xaml.cs new file mode 100644 index 00000000..5f98cee0 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseHomeView.xaml.cs @@ -0,0 +1,25 @@ +using System; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages; + +public partial class PulseHomeView : UserControl +{ + public Action? WorkoutDetailRequested { get; set; } + + public PulseHomeView() => InitializeComponent(); + + void OnRecCard1Pressed(object? sender, PointerPressedEventArgs e) => + WorkoutDetailRequested?.Invoke(); + + void OnRecCard2Pressed(object? sender, PointerPressedEventArgs e) => + WorkoutDetailRequested?.Invoke(); + + void OnRecCard3Pressed(object? sender, PointerPressedEventArgs e) => + WorkoutDetailRequested?.Invoke(); + + void OnPlayButtonClicked(object? sender, RoutedEventArgs e) => + WorkoutDetailRequested?.Invoke(); +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseLoginView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseLoginView.xaml new file mode 100644 index 00000000..23090904 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseLoginView.xaml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutDetailView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutDetailView.xaml.cs new file mode 100644 index 00000000..7999d3d7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutDetailView.xaml.cs @@ -0,0 +1,15 @@ +using System; +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages; + +public partial class PulseWorkoutDetailView : UserControl +{ + public Action? BackRequested { get; set; } + + public PulseWorkoutDetailView() => InitializeComponent(); + + void OnBackClicked(object? sender, RoutedEventArgs e) => + BackRequested?.Invoke(); +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutsView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutsView.xaml new file mode 100644 index 00000000..c1f9659b --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutsView.xaml @@ -0,0 +1,290 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutsView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutsView.xaml.cs new file mode 100644 index 00000000..fec95be3 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/PulseWorkoutsView.xaml.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class PulseWorkoutsView : UserControl +{ + public PulseWorkoutsView() => InitializeComponent(); +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingAppPage.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingAppPage.xaml new file mode 100644 index 00000000..23278161 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingAppPage.xaml @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingAppPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingAppPage.xaml.cs new file mode 100644 index 00000000..6e194cf1 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingAppPage.xaml.cs @@ -0,0 +1,306 @@ +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Media; + +namespace ControlCatalog.Pages; + +public partial class RetroGamingAppPage : UserControl +{ + static readonly Color BgColor = Color.Parse("#120a1f"); + static readonly Color SurfaceColor = Color.Parse("#2d1b4e"); + static readonly Color CyanColor = Color.Parse("#00ffff"); + static readonly Color YellowColor = Color.Parse("#ffff00"); + static readonly Color MutedColor = Color.Parse("#7856a8"); + static readonly Color TextColor = Color.Parse("#e0d0ff"); + + NavigationPage? _nav; + ScrollViewer? _infoPanel; + + public RetroGamingAppPage() + { + InitializeComponent(); + + _nav = this.FindControl("RetroNav"); + if (_nav != null) + _ = _nav.PushAsync(BuildHomePage()); + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + + _infoPanel = this.FindControl("InfoPanel"); + UpdateInfoPanelVisibility(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == BoundsProperty) + UpdateInfoPanelVisibility(); + } + + void UpdateInfoPanelVisibility() + { + if (_infoPanel != null) + _infoPanel.IsVisible = Bounds.Width >= 650; + } + + ContentPage BuildHomePage() + { + var page = new ContentPage { Background = new SolidColorBrush(BgColor) }; + page.Header = BuildPixelArcadeLogo(); + NavigationPage.SetTopCommandBar(page, BuildNavBarRight()); + + var panel = new Panel(); + panel.Children.Add(BuildHomeTabbedPage()); + panel.Children.Add(BuildSearchFab()); + + page.Content = panel; + return page; + } + + static Control BuildPixelArcadeLogo() + { + var row = new StackPanel + { + Orientation = Avalonia.Layout.Orientation.Horizontal, + Spacing = 10, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, + }; + + var iconPanel = new Grid { Width = 36, Height = 30 }; + iconPanel.Children.Add(new Border + { + Width = 36, Height = 20, CornerRadius = new CornerRadius(3), + Background = new SolidColorBrush(Color.Parse("#cc44dd")), + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Bottom, + }); + iconPanel.Children.Add(new Border + { + Width = 9, Height = 9, + Background = new SolidColorBrush(SurfaceColor), + HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Bottom, + Margin = new Thickness(4, 0, 0, 6), + }); + iconPanel.Children.Add(new Border + { + Width = 9, Height = 9, + Background = new SolidColorBrush(SurfaceColor), + HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Bottom, + Margin = new Thickness(0, 0, 4, 6), + }); + row.Children.Add(iconPanel); + + var textStack = new StackPanel { Spacing = 1 }; + textStack.Children.Add(new TextBlock + { + Text = "PIXEL", + FontFamily = new FontFamily("Courier New, monospace"), + FontSize = 14, FontWeight = FontWeight.Bold, + Foreground = new SolidColorBrush(YellowColor), LineHeight = 16, + }); + textStack.Children.Add(new TextBlock + { + Text = "ARCADE", + FontFamily = new FontFamily("Courier New, monospace"), + FontSize = 14, FontWeight = FontWeight.Bold, + Foreground = new SolidColorBrush(YellowColor), LineHeight = 16, + }); + row.Children.Add(textStack); + return row; + } + + static Control BuildNavBarRight() + { + var row = new StackPanel + { + Orientation = Avalonia.Layout.Orientation.Horizontal, + Spacing = 10, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, + Margin = new Thickness(0, 0, 8, 0), + }; + row.Children.Add(new PathIcon + { + Width = 16, Height = 16, + Foreground = new SolidColorBrush(TextColor), + Data = Geometry.Parse("M12 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"), + }); + var avatar = new Border + { + Width = 26, Height = 26, + CornerRadius = new CornerRadius(0), + ClipToBounds = true, + Background = new SolidColorBrush(SurfaceColor), + BorderBrush = new SolidColorBrush(MutedColor), + BorderThickness = new Thickness(1), + }; + avatar.Child = new TextBlock + { + Text = "P1", + FontFamily = new FontFamily("Courier New, monospace"), + FontSize = 7, FontWeight = FontWeight.Bold, + Foreground = new SolidColorBrush(CyanColor), + HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, + }; + row.Children.Add(avatar); + return row; + } + + TabbedPage BuildHomeTabbedPage() + { + var tp = new TabbedPage + { + Background = new SolidColorBrush(BgColor), + TabPlacement = TabPlacement.Bottom, + PageTransition = new PageSlide(TimeSpan.FromMilliseconds(250)), + }; + tp.Resources["TabItemHeaderFontSize"] = 12.0; + tp.Resources["TabbedPageTabStripBackground"] = new SolidColorBrush(SurfaceColor); + tp.Resources["TabbedPageTabItemHeaderForegroundSelected"] = new SolidColorBrush(Color.Parse("#ad2bee")); + tp.Resources["TabbedPageTabItemHeaderForegroundUnselected"] = new SolidColorBrush(MutedColor); + + var homeView = new RetroGamingHomeView(); + homeView.GameSelected = PushDetailPage; + + var homeTab = new ContentPage + { + Header = "Home", + Icon = new PathIcon { Data = Geometry.Parse("M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z") }, + Background = new SolidColorBrush(BgColor), + Content = homeView, + }; + + var gamesView = new RetroGamingGamesView(); + gamesView.GameSelected = PushDetailPage; + + var gamesTab = new ContentPage + { + Header = "Games", + Icon = new PathIcon { Data = Geometry.Parse("M7.97,16L5,19C4.67,19.3 4.23,19.5 3.75,19.5A1.75,1.75 0 0,1 2,17.75V17.5L3,10.12C3.21,7.81 5.14,6 7.5,6H16.5C18.86,6 20.79,7.81 21,10.12L22,17.5V17.75A1.75,1.75 0 0,1 20.25,19.5C19.77,19.5 19.33,19.3 19,19L16.03,16H7.97M7,9V11H5V13H7V15H9V13H11V11H9V9H7M14.5,12A1.5,1.5 0 0,0 13,13.5A1.5,1.5 0 0,0 14.5,15A1.5,1.5 0 0,0 16,13.5A1.5,1.5 0 0,0 14.5,12M17.5,9A1.5,1.5 0 0,0 16,10.5A1.5,1.5 0 0,0 17.5,12A1.5,1.5 0 0,0 19,10.5A1.5,1.5 0 0,0 17.5,9Z") }, + Background = new SolidColorBrush(BgColor), + Content = gamesView, + }; + + var favTab = new ContentPage + { + Header = "Favorites", + Icon = new PathIcon { Data = Geometry.Parse("M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z") }, + Background = new SolidColorBrush(BgColor), + Content = new RetroGamingFavoritesView(), + }; + + var profileTab = new ContentPage + { + Header = "Profile", + Icon = new PathIcon { Data = Geometry.Parse("M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z") }, + Background = new SolidColorBrush(BgColor), + Content = new RetroGamingProfileView(), + }; + + tp.Pages = new ObservableCollection { homeTab, gamesTab, favTab, profileTab }; + return tp; + } + + Control BuildSearchFab() + { + var fab = new Button + { + Width = 50, Height = 50, + CornerRadius = new CornerRadius(0), + Background = new SolidColorBrush(YellowColor), + Padding = new Thickness(0), + }; + fab.Classes.Add("retro-fab"); + fab.Content = new PathIcon + { + Width = 22, Height = 22, + Foreground = new SolidColorBrush(BgColor), + Data = Geometry.Parse("M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z"), + }; + fab.Click += (_, _) => _ = _nav?.PushModalAsync(BuildSearchModal()); + + return new Border + { + HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Bottom, + Margin = new Thickness(0, 0, 0, 35), + BoxShadow = new BoxShadows(new BoxShadow + { + Blur = 10, Spread = 1, + Color = Color.FromArgb(140, 255, 255, 0), + }), + Child = fab, + }; + } + + ContentPage BuildSearchModal() + { + var page = new ContentPage { Background = new SolidColorBrush(BgColor) }; + + var searchView = new RetroGamingSearchView(); + searchView.CloseRequested = () => _ = _nav?.PopModalAsync(); + searchView.GameSelected = async title => + { + await (_nav?.PopModalAsync() ?? System.Threading.Tasks.Task.CompletedTask); + PushDetailPage(title); + }; + + page.Content = searchView; + return page; + } + + async void PushDetailPage(string gameTitle) + { + if (_nav == null) return; + + var detailView = new RetroGamingDetailView(gameTitle); + + var page = new ContentPage + { + Background = new SolidColorBrush(BgColor), + Content = detailView, + }; + + NavigationPage.SetBarLayoutBehavior(page, BarLayoutBehavior.Overlay); + page.NavigatedTo += (_, _) => { if (_nav != null) _nav.Resources["NavigationBarBackground"] = Brushes.Transparent; }; + page.NavigatedFrom += (_, _) => { if (_nav != null) _nav.Resources["NavigationBarBackground"] = new SolidColorBrush(SurfaceColor); }; + + var cmdBar = new StackPanel + { + Orientation = Avalonia.Layout.Orientation.Horizontal, + Spacing = 4, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, + Margin = new Thickness(0, 0, 8, 0), + }; + var heartBtn = new Button(); + heartBtn.Classes.Add("retro-icon-btn"); + heartBtn.Content = new PathIcon + { + Width = 16, Height = 16, + Foreground = new SolidColorBrush(Color.Parse("#ad2bee")), + Data = Geometry.Parse("M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"), + }; + var shareBtn = new Button(); + shareBtn.Classes.Add("retro-icon-btn"); + shareBtn.Content = new PathIcon + { + Width = 16, Height = 16, + Foreground = new SolidColorBrush(TextColor), + Data = Geometry.Parse("M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.35C15.11,18.56 15.08,18.78 15.08,19C15.08,20.61 16.39,21.92 18,21.92C19.61,21.92 20.92,20.61 20.92,19C20.92,17.39 19.61,16.08 18,16.08Z"), + }; + cmdBar.Children.Add(heartBtn); + cmdBar.Children.Add(shareBtn); + NavigationPage.SetTopCommandBar(page, cmdBar); + + await _nav.PushAsync(page); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingDetailView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingDetailView.xaml new file mode 100644 index 00000000..718e8137 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingDetailView.xaml @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingDetailView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingDetailView.xaml.cs new file mode 100644 index 00000000..cf49850b --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingDetailView.xaml.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; + +namespace ControlCatalog.Pages; + +public partial class RetroGamingDetailView : UserControl +{ + static readonly Dictionary GameAssets = new() + { + { "Cyber Ninja 2084", "hero.jpg" }, + { "Pixel Quest", "pixel_quest.jpg" }, + { "Neon Racer", "neon_racer.jpg" }, + { "Dungeon Bit", "dungeon_bit.jpg" }, + { "Forest Spirit", "forest_spirit.jpg" }, + { "Cyber City", "cyber_city.jpg" }, + { "Neon Ninja", "neon_ninja.jpg" }, + { "Space Voids", "space_voids.jpg" }, + }; + + public RetroGamingDetailView() => InitializeComponent(); + + public RetroGamingDetailView(string gameTitle) + { + InitializeComponent(); + + DetailTitleText.Text = gameTitle.ToUpperInvariant(); + + var filename = GameAssets.TryGetValue(gameTitle, out var f) ? f + : (GameAssets.TryGetValue("Neon Ninja", out var fb) ? fb : null); + + if (filename != null) + { + try + { + var uri = new Uri($"avares://ControlCatalog/Assets/RetroGaming/{filename}"); + using var stream = AssetLoader.Open(uri); + var bmp = new Bitmap(stream); + DetailHeroImageBorder.Background = new ImageBrush(bmp) + { + Stretch = Stretch.UniformToFill, + }; + } + catch + { + SetFallbackBackground(); + } + } + else + { + SetFallbackBackground(); + } + } + + void SetFallbackBackground() + { + var grad = new LinearGradientBrush + { + StartPoint = new Avalonia.RelativePoint(0, 0, Avalonia.RelativeUnit.Relative), + EndPoint = new Avalonia.RelativePoint(1, 1, Avalonia.RelativeUnit.Relative), + }; + grad.GradientStops.Add(new GradientStop(Avalonia.Media.Color.Parse("#3d2060"), 0)); + grad.GradientStops.Add(new GradientStop(Avalonia.Media.Color.Parse("#120a1f"), 1)); + DetailHeroImageBorder.Background = grad; + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingFavoritesView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingFavoritesView.xaml new file mode 100644 index 00000000..ef11d4e7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingFavoritesView.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingFavoritesView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingFavoritesView.xaml.cs new file mode 100644 index 00000000..fd67902a --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingFavoritesView.xaml.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class RetroGamingFavoritesView : UserControl +{ + public RetroGamingFavoritesView() => InitializeComponent(); +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingGamesView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingGamesView.xaml new file mode 100644 index 00000000..a4e9f403 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingGamesView.xaml @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingGamesView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingGamesView.xaml.cs new file mode 100644 index 00000000..6676b792 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingGamesView.xaml.cs @@ -0,0 +1,39 @@ +using System; +using Avalonia.Controls; +using Avalonia.Layout; + +namespace ControlCatalog.Pages; + +public partial class RetroGamingGamesView : UserControl +{ + public Action? GameSelected { get; set; } + + public RetroGamingGamesView() + { + InitializeComponent(); + + GameCyberNinjaBtn.Click += (_, _) => GameSelected?.Invoke("Cyber Ninja 2084"); + GameNeonRacerBtn.Click += (_, _) => GameSelected?.Invoke("Neon Racer"); + GameDungeonBitBtn.Click += (_, _) => GameSelected?.Invoke("Dungeon Bit"); + GameForestSpiritBtn.Click += (_, _) => GameSelected?.Invoke("Forest Spirit"); + GamePixelQuestBtn.Click += (_, _) => GameSelected?.Invoke("Pixel Quest"); + GameSpaceVoidsBtn.Click += (_, _) => GameSelected?.Invoke("Space Voids"); + GameCyberCityBtn.Click += (_, _) => GameSelected?.Invoke("Cyber City"); + + GamesGrid.SizeChanged += OnGridSizeChanged; + } + + void OnGridSizeChanged(object? sender, SizeChangedEventArgs e) + { + const double defaultWidth = 145; + var available = GamesGrid.Bounds.Width; + if (available <= 0) return; + + bool singleColumn = available < defaultWidth * 2; + foreach (var child in GamesGrid.Children) + { + if (child is Button btn && btn.Content is Border card) + card.Width = singleColumn ? available : defaultWidth; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingHomeView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingHomeView.xaml new file mode 100644 index 00000000..bf5d1125 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingHomeView.xaml @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingHomeView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingHomeView.xaml.cs new file mode 100644 index 00000000..d1693f3c --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingHomeView.xaml.cs @@ -0,0 +1,22 @@ +using System; +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class RetroGamingHomeView : UserControl +{ + public Action? GameSelected { get; set; } + + public RetroGamingHomeView() + { + InitializeComponent(); + + HeroPlayBtn.Click += (_, _) => GameSelected?.Invoke("Cyber Ninja 2084"); + ContinuePixelQuestBtn.Click += (_, _) => GameSelected?.Invoke("Pixel Quest"); + ContinueSpaceVoidsBtn.Click += (_, _) => GameSelected?.Invoke("Space Voids"); + NewReleaseNeonRacerBtn.Click += (_, _) => GameSelected?.Invoke("Neon Racer"); + NewReleaseDungeonBitBtn.Click += (_, _) => GameSelected?.Invoke("Dungeon Bit"); + NewReleaseForestSpiritBtn.Click += (_, _) => GameSelected?.Invoke("Forest Spirit"); + NewReleaseCyberCityBtn.Click += (_, _) => GameSelected?.Invoke("Cyber City"); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingProfileView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingProfileView.xaml new file mode 100644 index 00000000..17d7ff2d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingProfileView.xaml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingProfileView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingProfileView.xaml.cs new file mode 100644 index 00000000..f2284bdc --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingProfileView.xaml.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class RetroGamingProfileView : UserControl +{ + public RetroGamingProfileView() => InitializeComponent(); +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingSearchView.xaml b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingSearchView.xaml new file mode 100644 index 00000000..92389efe --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingSearchView.xaml @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingSearchView.xaml.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingSearchView.xaml.cs new file mode 100644 index 00000000..c5db19c0 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/RetroGamingSearchView.xaml.cs @@ -0,0 +1,24 @@ +using System; +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class RetroGamingSearchView : UserControl +{ + public Action? CloseRequested { get; set; } + public Action? GameSelected { get; set; } + + public RetroGamingSearchView() + { + InitializeComponent(); + + CloseBtn.Click += (_, _) => CloseRequested?.Invoke(); + SearchCyberNinjaBtn.Click += (_, _) => GameSelected?.Invoke("Cyber Ninja 2084"); + SearchNeonRacerBtn.Click += (_, _) => GameSelected?.Invoke("Neon Racer"); + SearchDungeonBitBtn.Click += (_, _) => GameSelected?.Invoke("Dungeon Bit"); + SearchForestSpiritBtn.Click += (_, _) => GameSelected?.Invoke("Forest Spirit"); + SearchPixelQuestBtn.Click += (_, _) => GameSelected?.Invoke("Pixel Quest"); + SearchSpaceVoidsBtn.Click += (_, _) => GameSelected?.Invoke("Space Voids"); + SearchCyberCityBtn.Click += (_, _) => GameSelected?.Invoke("Cyber City"); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/CompositeTransition.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/CompositeTransition.cs new file mode 100644 index 00000000..0cddd58e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/CompositeTransition.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Animation.Easings; +using Avalonia.Media; +using Avalonia.Styling; +using Avalonia.VisualTree; + +namespace ControlCatalog.Pages +{ + /// + /// Example custom IPageTransition: horizontal slide combined with cross-fade. + /// Both pages slide and fade simultaneously for a smooth blended effect. + /// + public class CompositeTransition : IPageTransition + { + public CompositeTransition() { } + + public CompositeTransition(TimeSpan duration) + { + Duration = duration; + } + + public TimeSpan Duration { get; set; } = TimeSpan.FromMilliseconds(300); + public Easing TransitionEasing { get; set; } = new LinearEasing(); + + public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + return; + + var tasks = new List(); + var parent = GetVisualParent(from, to); + var distance = parent.Bounds.Width > 0 ? parent.Bounds.Width : 500d; + + if (from != null) + { + var anim = new Avalonia.Animation.Animation + { + FillMode = FillMode.Forward, + Easing = TransitionEasing, + Duration = Duration, + Children = + { + new KeyFrame + { + Cue = new Cue(0d), + Setters = + { + new Setter(TranslateTransform.XProperty, 0d), + new Setter(Visual.OpacityProperty, 1d) + } + }, + new KeyFrame + { + Cue = new Cue(1d), + Setters = + { + new Setter(TranslateTransform.XProperty, forward ? -distance : distance), + new Setter(Visual.OpacityProperty, 0d) + } + } + } + }; + tasks.Add(anim.RunAsync(from, cancellationToken)); + } + + if (to != null) + { + to.IsVisible = true; + + var anim = new Avalonia.Animation.Animation + { + FillMode = FillMode.Forward, + Easing = TransitionEasing, + Duration = Duration, + Children = + { + new KeyFrame + { + Cue = new Cue(0d), + Setters = + { + new Setter(TranslateTransform.XProperty, forward ? distance : -distance), + new Setter(Visual.OpacityProperty, 0d) + } + }, + new KeyFrame + { + Cue = new Cue(1d), + Setters = + { + new Setter(TranslateTransform.XProperty, 0d), + new Setter(Visual.OpacityProperty, 1d) + } + } + } + }; + tasks.Add(anim.RunAsync(to, cancellationToken)); + } + + await Task.WhenAll(tasks); + + if (from != null && !cancellationToken.IsCancellationRequested) + from.IsVisible = false; + } + + private static Visual GetVisualParent(Visual? from, Visual? to) + { + var p1 = (from ?? to)!.GetVisualParent(); + if (from != null && to != null && + !ReferenceEquals(from.GetVisualParent(), to.GetVisualParent())) + throw new ArgumentException("Transition elements have different parents."); + return p1 ?? throw new ArgumentException("Transition elements have no parent."); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/FadeThroughTransition.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/FadeThroughTransition.cs new file mode 100644 index 00000000..ece9b1a1 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/FadeThroughTransition.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Animation.Easings; +using Avalonia.Media; +using Avalonia.Styling; + +namespace ControlCatalog.Pages +{ + /// + /// Example custom IPageTransition: a "fade through" with scale. + /// The outgoing page fades out while scaling down; the incoming page fades in while + /// scaling up, producing a smooth depth-aware transition. + /// + public class FadeThroughTransition : IPageTransition + { + public FadeThroughTransition() { } + + public FadeThroughTransition(TimeSpan duration) + { + Duration = duration; + } + + public TimeSpan Duration { get; set; } = TimeSpan.FromMilliseconds(300); + public Easing FadeEasing { get; set; } = new CubicEaseOut(); + + public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + return; + + var tasks = new List(); + + if (from != null) + { + from.RenderTransformOrigin = RelativePoint.Center; + from.RenderTransform = new ScaleTransform(1, 1); + } + + if (to != null) + { + to.RenderTransformOrigin = RelativePoint.Center; + to.RenderTransform = new ScaleTransform(1, 1); + to.Opacity = 0; + } + + if (from != null) + { + var outAnim = new Avalonia.Animation.Animation + { + FillMode = FillMode.Forward, + Easing = FadeEasing, + Duration = Duration, + Children = + { + new KeyFrame + { + Cue = new Cue(0d), + Setters = + { + new Setter(Visual.OpacityProperty, 1d), + new Setter(ScaleTransform.ScaleXProperty, 1d), + new Setter(ScaleTransform.ScaleYProperty, 1d) + } + }, + new KeyFrame + { + Cue = new Cue(1d), + Setters = + { + new Setter(Visual.OpacityProperty, 0d), + new Setter(ScaleTransform.ScaleXProperty, forward ? 0.92 : 1.08), + new Setter(ScaleTransform.ScaleYProperty, forward ? 0.92 : 1.08) + } + } + } + }; + tasks.Add(outAnim.RunAsync(from, cancellationToken)); + } + + if (to != null) + { + to.IsVisible = true; + + var inAnim = new Avalonia.Animation.Animation + { + FillMode = FillMode.Forward, + Easing = FadeEasing, + Duration = Duration, + Children = + { + new KeyFrame + { + Cue = new Cue(0d), + Setters = + { + new Setter(Visual.OpacityProperty, 0d), + new Setter(ScaleTransform.ScaleXProperty, forward ? 1.08 : 0.92), + new Setter(ScaleTransform.ScaleYProperty, forward ? 1.08 : 0.92) + } + }, + new KeyFrame + { + Cue = new Cue(1d), + Setters = + { + new Setter(Visual.OpacityProperty, 1d), + new Setter(ScaleTransform.ScaleXProperty, 1d), + new Setter(ScaleTransform.ScaleYProperty, 1d) + } + } + } + }; + tasks.Add(inAnim.RunAsync(to, cancellationToken)); + } + + await Task.WhenAll(tasks); + + if (to != null && !cancellationToken.IsCancellationRequested) + { + to.Opacity = 1; + to.RenderTransform = null; + } + + if (from != null) + { + if (!cancellationToken.IsCancellationRequested) + from.IsVisible = false; + from.Opacity = 1; + from.RenderTransform = null; + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/PageSlideTransition.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/PageSlideTransition.cs new file mode 100644 index 00000000..dfbe8478 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/PageSlideTransition.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Animation.Easings; +using Avalonia.Media; +using Avalonia.Styling; +using Avalonia.VisualTree; + +namespace ControlCatalog.Pages +{ + /// + /// Example custom IPageTransition: a directional page slide. + /// Both pages slide together in the specified axis direction. + /// Demonstrates how to implement a custom horizontal or vertical slide from scratch. + /// + public class PageSlideTransition : IPageTransition + { + public enum SlideAxis { Horizontal, Vertical } + + public PageSlideTransition() { } + + public PageSlideTransition(TimeSpan duration, SlideAxis axis = SlideAxis.Horizontal) + { + Duration = duration; + Axis = axis; + } + + public TimeSpan Duration { get; set; } = TimeSpan.FromMilliseconds(300); + public SlideAxis Axis { get; set; } = SlideAxis.Horizontal; + public Easing SlideEasing { get; set; } = new LinearEasing(); + + public Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) => + Axis == SlideAxis.Horizontal + ? StartAxis(from, to, forward, cancellationToken, TranslateTransform.XProperty, () => GetVisualParent(from, to).Bounds.Width) + : StartAxis(from, to, forward, cancellationToken, TranslateTransform.YProperty, () => GetVisualParent(from, to).Bounds.Height); + + private async Task StartAxis( + Visual? from, Visual? to, bool forward, CancellationToken cancellationToken, + Avalonia.AvaloniaProperty prop, Func getDistance) + { + if (cancellationToken.IsCancellationRequested) + return; + var tasks = new List(); + var distance = getDistance() is > 0 and var d ? d : 500d; + + if (from != null) + { + var anim = new Avalonia.Animation.Animation + { + FillMode = FillMode.Forward, + Easing = SlideEasing, + Duration = Duration, + Children = + { + new KeyFrame { Cue = new Cue(0d), Setters = { new Setter(prop, 0d) } }, + new KeyFrame { Cue = new Cue(1d), Setters = { new Setter(prop, forward ? -distance : distance) } } + } + }; + tasks.Add(anim.RunAsync(from, cancellationToken)); + } + + if (to != null) + { + to.IsVisible = true; + var anim = new Avalonia.Animation.Animation + { + FillMode = FillMode.Forward, + Easing = SlideEasing, + Duration = Duration, + Children = + { + new KeyFrame { Cue = new Cue(0d), Setters = { new Setter(prop, forward ? distance : -distance) } }, + new KeyFrame { Cue = new Cue(1d), Setters = { new Setter(prop, 0d) } } + } + }; + tasks.Add(anim.RunAsync(to, cancellationToken)); + } + + await Task.WhenAll(tasks); + + if (from != null && !cancellationToken.IsCancellationRequested) + from.IsVisible = false; + } + + private static Visual GetVisualParent(Visual? from, Visual? to) + { + var p1 = (from ?? to)!.GetVisualParent(); + if (from != null && to != null && + !ReferenceEquals(from.GetVisualParent(), to.GetVisualParent())) + throw new ArgumentException("Transition elements have different parents."); + return p1 ?? throw new ArgumentException("Transition elements have no parent."); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/ParallaxSlideTransition.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/ParallaxSlideTransition.cs new file mode 100644 index 00000000..7326c6e9 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPage/Transitions/ParallaxSlideTransition.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Animation.Easings; +using Avalonia.Media; +using Avalonia.Styling; +using Avalonia.VisualTree; + +namespace ControlCatalog.Pages +{ + /// + /// Example custom IPageTransition: a parallax slide. + /// The incoming page slides full-width from the right while the outgoing page shifts ~30% + /// to the left with a subtle opacity fade, producing a depth-layered push effect. + /// + public class ParallaxSlideTransition : IPageTransition + { + public ParallaxSlideTransition() { } + + public ParallaxSlideTransition(TimeSpan duration) + { + Duration = duration; + } + + public TimeSpan Duration { get; set; } = TimeSpan.FromMilliseconds(350); + public Easing SlideEasing { get; set; } = new CubicEaseOut(); + + public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + return; + + var tasks = new List(); + var parent = GetVisualParent(from, to); + var distance = parent.Bounds.Width > 0 ? parent.Bounds.Width : 500d; + + if (from != null) + { + var anim = new Avalonia.Animation.Animation + { + FillMode = FillMode.Forward, + Easing = SlideEasing, + Duration = Duration, + Children = + { + new KeyFrame + { + Cue = new Cue(0d), + Setters = + { + new Setter(TranslateTransform.XProperty, 0d), + new Setter(Visual.OpacityProperty, 1d) + } + }, + new KeyFrame + { + Cue = new Cue(1d), + Setters = + { + new Setter(TranslateTransform.XProperty, forward ? -distance * 0.3 : distance), + new Setter(Visual.OpacityProperty, forward ? 0.7 : 1d) + } + } + } + }; + tasks.Add(anim.RunAsync(from, cancellationToken)); + } + + if (to != null) + { + to.IsVisible = true; + + var anim = new Avalonia.Animation.Animation + { + FillMode = FillMode.Forward, + Easing = SlideEasing, + Duration = Duration, + Children = + { + new KeyFrame + { + Cue = new Cue(0d), + Setters = + { + new Setter(TranslateTransform.XProperty, forward ? distance : -distance * 0.3), + new Setter(Visual.OpacityProperty, forward ? 1d : 0.7) + } + }, + new KeyFrame + { + Cue = new Cue(1d), + Setters = + { + new Setter(TranslateTransform.XProperty, 0d), + new Setter(Visual.OpacityProperty, 1d) + } + } + } + }; + tasks.Add(anim.RunAsync(to, cancellationToken)); + } + + await Task.WhenAll(tasks); + + if (from != null && !cancellationToken.IsCancellationRequested) + from.IsVisible = false; + } + + private static Visual GetVisualParent(Visual? from, Visual? to) + { + var p1 = (from ?? to)!.GetVisualParent(); + if (from != null && to != null && + !ReferenceEquals(from.GetVisualParent(), to.GetVisualParent())) + throw new ArgumentException("Transition elements have different parents."); + return p1 ?? throw new ArgumentException("Transition elements have no parent."); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NavigationPerformanceMonitorHelper.cs b/samples/AvaloniaControlCatalog/Pages/NavigationPerformanceMonitorHelper.cs new file mode 100644 index 00000000..3178c35f --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NavigationPerformanceMonitorHelper.cs @@ -0,0 +1,275 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Threading; + +namespace ControlCatalog.Pages +{ + /// + /// Shared helpers for the performance-monitor demo pages + /// (NavigationPage, TabbedPage, DrawerPage, ContentPage). + /// + internal sealed class NavigationPerformanceMonitorHelper + { + internal static readonly IBrush PositiveDeltaBrush = new SolidColorBrush(Color.Parse("#D32F2F")); + internal static readonly IBrush NegativeDeltaBrush = new SolidColorBrush(Color.Parse("#388E3C")); + internal static readonly IBrush ZeroDeltaBrush = new SolidColorBrush(Color.Parse("#757575")); + internal static readonly IBrush CurrentBorderBrush = new SolidColorBrush(Color.Parse("#0078D4")); + internal static readonly IBrush DefaultBorderBrush = new SolidColorBrush(Color.Parse("#CCCCCC")); + + private readonly List> _trackedPages = new(); + private double _previousHeapMB; + private DispatcherTimer? _autoRefreshTimer; + + internal readonly Stopwatch OpStopwatch = new(); + internal int TotalCreated; + + /// + /// Track a newly-created page via WeakReference and increment TotalCreated. + /// + internal void TrackPage(Page page) + { + TotalCreated++; + _trackedPages.Add(new WeakReference(page)); + } + + /// + /// Count live (not yet GC'd) tracked page instances. + /// + internal int CountLiveInstances() + { + int alive = 0; + for (int i = _trackedPages.Count - 1; i >= 0; i--) + { + if (_trackedPages[i].TryGetTarget(out _)) + alive++; + else + _trackedPages.RemoveAt(i); + } + return alive; + } + + /// + /// Update heap and delta text blocks. Call from RefreshAll(). + /// + internal void UpdateHeapDelta(TextBlock heapText, TextBlock deltaText) + { + var heapMB = GC.GetTotalMemory(false) / (1024.0 * 1024.0); + heapText.Text = $"Managed Heap: {heapMB:##0.0} MB"; + + var delta = heapMB - _previousHeapMB; + if (Math.Abs(delta) < 0.05) + { + deltaText.Text = "(no change)"; + deltaText.Foreground = ZeroDeltaBrush; + } + else + { + var sign = delta > 0 ? "+" : ""; + deltaText.Text = $"({sign}{delta:0.0} MB)"; + deltaText.Foreground = delta > 0 ? PositiveDeltaBrush : NegativeDeltaBrush; + } + _previousHeapMB = heapMB; + } + + /// + /// Initialize previous heap baseline. + /// + internal void InitHeap() + { + _previousHeapMB = GC.GetTotalMemory(false) / (1024.0 * 1024.0); + } + + /// + /// Stop the stopwatch and write elapsed ms to the given TextBlock. + /// + internal void StopMetrics(TextBlock lastOpText) + { + if (!OpStopwatch.IsRunning) return; + OpStopwatch.Stop(); + lastOpText.Text = $"Last Op: {OpStopwatch.ElapsedMilliseconds} ms"; + } + + /// + /// Force full GC, then invoke the refresh callback. + /// + internal void ForceGC(Action refresh) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + refresh(); + } + + /// + /// Start a 2-second auto-refresh timer. + /// + internal void StartAutoRefresh(Action refresh) + { + if (_autoRefreshTimer != null) return; + _autoRefreshTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) }; + _autoRefreshTimer.Tick += (_, _) => refresh(); + _autoRefreshTimer.Start(); + } + + /// + /// Stop the auto-refresh timer. + /// + internal void StopAutoRefresh() + { + _autoRefreshTimer?.Stop(); + _autoRefreshTimer = null; + } + + /// + /// Toggle auto-refresh based on a CheckBox. + /// + internal void OnAutoRefreshChanged(CheckBox check, Action refresh) + { + if (check.IsChecked == true) + StartAutoRefresh(refresh); + else + StopAutoRefresh(); + } + + /// + /// Append a timestamped log entry to a StackPanel inside a ScrollViewer. + /// + internal void LogOperation(string action, string detail, + StackPanel logPanel, ScrollViewer logScroll, string? extraInfo = null) + { + var heapMB = GC.GetTotalMemory(false) / (1024.0 * 1024.0); + var timing = OpStopwatch.ElapsedMilliseconds; + var extra = extraInfo != null ? $" {extraInfo}," : ""; + + logPanel.Children.Add(new TextBlock + { + Text = $"{DateTime.Now:HH:mm:ss} [{action}] {detail} —{extra} heap {heapMB:##0.0} MB, {timing} ms", + FontSize = 10, + FontFamily = new FontFamily("Cascadia Mono,Consolas,Menlo,monospace"), + Padding = new Thickness(6, 2), + TextTrimming = TextTrimming.CharacterEllipsis, + }); + logScroll.ScrollToEnd(); + } + + /// + /// Build a tracked ContentPage with a 50 KB dummy allocation. + /// + internal ContentPage BuildTrackedPage(string title, int index, int allocBytes = 51200) + { + var page = NavigationDemoHelper.MakePage(title, + $"Stack position #{index}\nPush more pages ...", index); + page.Tag = new byte[allocBytes]; + TrackPage(page); + return page; + } + + /// + /// Create a reusable stack/history row (badge + title + label). + /// + internal static (Border Container, Border Badge, TextBlock IndexText, + TextBlock TitleText, TextBlock BadgeText) CreateStackRow() + { + var indexText = new TextBlock + { + FontSize = 10, FontWeight = FontWeight.SemiBold, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + }; + var badge = new Border + { + Width = 22, Height = 22, + CornerRadius = new CornerRadius(11), + VerticalAlignment = VerticalAlignment.Center, + Child = indexText, + }; + var titleText = new TextBlock + { + VerticalAlignment = VerticalAlignment.Center, + TextTrimming = TextTrimming.CharacterEllipsis, + Margin = new Thickness(6, 0, 0, 0), + }; + var badgeText = new TextBlock + { + FontSize = 10, Opacity = 0.5, + VerticalAlignment = VerticalAlignment.Center, + Margin = new Thickness(4, 0, 0, 0), + IsVisible = false, + }; + + var row = new DockPanel(); + row.Children.Add(badge); + row.Children.Add(titleText); + row.Children.Add(badgeText); + + var container = new Border + { + CornerRadius = new CornerRadius(6), + Padding = new Thickness(8, 6), + Child = row, + }; + + return (container, badge, indexText, titleText, badgeText); + } + + /// + /// Update a stack row with page data. + /// + internal static void UpdateStackRow( + (Border Container, Border Badge, TextBlock IndexText, + TextBlock TitleText, TextBlock BadgeText) row, + int stackIndex, string title, bool isCurrent, bool isRoot) + { + row.Badge.Background = NavigationDemoHelper.GetPageBrush(stackIndex); + row.IndexText.Text = (stackIndex + 1).ToString(); + row.TitleText.Text = title; + row.TitleText.FontWeight = isCurrent ? FontWeight.SemiBold : FontWeight.Normal; + + string? label = isCurrent ? "current" : (isRoot ? "root" : null); + row.BadgeText.Text = label ?? ""; + row.BadgeText.IsVisible = label != null; + + row.Container.BorderBrush = isCurrent ? CurrentBorderBrush : DefaultBorderBrush; + row.Container.BorderThickness = new Thickness(isCurrent ? 2 : 1); + } + + /// + /// Sync a StackPanel of stack rows with data, growing/shrinking the row cache as needed. + /// + internal static void RefreshStackPanel( + StackPanel panel, + List<(Border Container, Border Badge, TextBlock IndexText, + TextBlock TitleText, TextBlock BadgeText)> rowCache, + IReadOnlyList stack, Page? currentPage) + { + int count = stack.Count; + + while (rowCache.Count < count) + rowCache.Add(CreateStackRow()); + + while (panel.Children.Count > count) + panel.Children.RemoveAt(panel.Children.Count - 1); + while (panel.Children.Count < count) + panel.Children.Add(rowCache[panel.Children.Count].Container); + + for (int displayIdx = 0; displayIdx < count; displayIdx++) + { + int stackIdx = count - 1 - displayIdx; + var page = stack[stackIdx]; + bool isCurrent = ReferenceEquals(page, currentPage); + bool isRoot = stackIdx == 0; + + var row = rowCache[displayIdx]; + if (!ReferenceEquals(panel.Children[displayIdx], row.Container)) + panel.Children[displayIdx] = row.Container; + + UpdateStackRow(row, stackIdx, page.Header?.ToString() ?? "(untitled)", isCurrent, isRoot); + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/NotificationsPage.xaml b/samples/AvaloniaControlCatalog/Pages/NotificationsPage.xaml new file mode 100644 index 00000000..61d8afde --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/NotificationsPage.xaml @@ -0,0 +1,25 @@ + + + TopLevel bound notification manager. + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/OpenGlPage.xaml.cs new file mode 100644 index 00000000..76a79290 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/OpenGlPage.xaml.cs @@ -0,0 +1,82 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Interactivity; +using Avalonia.OpenGL; +using Avalonia.OpenGL.Controls; +using Avalonia.Rendering.Composition; +using ControlCatalog.Pages.OpenGl; + +// ReSharper disable StringLiteralTypo + +namespace ControlCatalog.Pages +{ + public partial class OpenGlPage : ContentPage + { + public OpenGlPage() + { + InitializeComponent(); + GL.Init(Knobs); + + AttachedToVisualTree += delegate + { + if (TopLevel.GetTopLevel(this) is Window) + Snapshot.IsVisible = true; + }; + } + + private async void SnapshotClick(object sender, RoutedEventArgs e) + { + var v = ElementComposition.GetElementVisual(this)!; + var snap = await v.Compositor.CreateCompositionVisualSnapshot(v, 1.5); + await new Window() + { + Content = new ScrollViewer() + { + HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, + VerticalScrollBarVisibility = ScrollBarVisibility.Auto, + Content = new Image() + { + Source = snap + } + } + }.ShowDialog((Window)TopLevel.GetTopLevel(this)!); + } + } + + public class OpenGlPageControl : OpenGlControlBase + { + private OpenGlContent _content = new(); + private GlPageKnobs? _knobs; + + public void Init(GlPageKnobs knobs) + { + _knobs = knobs; + _knobs.PropertyChanged += KnobsPropertyChanged; + } + + private void KnobsPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs change) + { + if (change.Property == GlPageKnobs.YawProperty + || change.Property == GlPageKnobs.RollProperty + || change.Property == GlPageKnobs.PitchProperty + || change.Property == GlPageKnobs.DiscoProperty) + RequestNextFrameRendering(); + } + + protected override unsafe void OnOpenGlInit(GlInterface GL) => _content.Init(GL, GlVersion); + + protected override void OnOpenGlDeinit(GlInterface GL) => _content.Deinit(GL); + + protected override unsafe void OnOpenGlRender(GlInterface gl, int fb) + { + if (_knobs == null) + return; + _content.OnOpenGlRender(gl, fb, new PixelSize((int)Bounds.Width, (int)Bounds.Height), + _knobs.Yaw, _knobs.Pitch, _knobs.Roll, _knobs.Disco); + if (_knobs.Disco > 0.01) + RequestNextFrameRendering(); + _knobs?.Info = _content.Info; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCarouselPage.xaml b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCarouselPage.xaml new file mode 100644 index 00000000..b75b5c37 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCarouselPage.xaml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCarouselPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCarouselPage.xaml.cs new file mode 100644 index 00000000..f42bb10c --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCarouselPage.xaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class PipsPagerCarouselPage : UserControl +{ + public PipsPagerCarouselPage() + { + InitializeComponent(); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomButtonThemesPage.xaml b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomButtonThemesPage.xaml new file mode 100644 index 00000000..3e32f253 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomButtonThemesPage.xaml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomButtonThemesPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomButtonThemesPage.xaml.cs new file mode 100644 index 00000000..26682b9d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomButtonThemesPage.xaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class PipsPagerCustomButtonThemesPage : UserControl +{ + public PipsPagerCustomButtonThemesPage() + { + InitializeComponent(); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomColorsPage.xaml b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomColorsPage.xaml new file mode 100644 index 00000000..260536d7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomColorsPage.xaml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomColorsPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomColorsPage.xaml.cs new file mode 100644 index 00000000..a9276f11 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomColorsPage.xaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class PipsPagerCustomColorsPage : UserControl +{ + public PipsPagerCustomColorsPage() + { + InitializeComponent(); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomTemplatesPage.xaml b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomTemplatesPage.xaml new file mode 100644 index 00000000..fe748b24 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomTemplatesPage.xaml @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomTemplatesPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomTemplatesPage.xaml.cs new file mode 100644 index 00000000..cce9e6c5 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerCustomTemplatesPage.xaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class PipsPagerCustomTemplatesPage : UserControl +{ + public PipsPagerCustomTemplatesPage() + { + InitializeComponent(); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerEventsPage.xaml b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerEventsPage.xaml new file mode 100644 index 00000000..a69c1016 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerEventsPage.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerEventsPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerEventsPage.xaml.cs new file mode 100644 index 00000000..d9716539 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerEventsPage.xaml.cs @@ -0,0 +1,29 @@ +using System.Collections.ObjectModel; +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class PipsPagerEventsPage : UserControl +{ + private readonly ObservableCollection _events = new(); + + public PipsPagerEventsPage() + { + InitializeComponent(); + + EventLog.ItemsSource = _events; + + EventPager.PropertyChanged += (_, e) => + { + if (e.Property != PipsPager.SelectedPageIndexProperty) + return; + + var newIndex = (int)e.NewValue!; + StatusText.Text = $"Selected: {newIndex}"; + _events.Insert(0, $"SelectedPageIndex changed to {newIndex}"); + + if (_events.Count > 20) + _events.RemoveAt(_events.Count - 1); + }; + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerGettingStartedPage.xaml b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerGettingStartedPage.xaml new file mode 100644 index 00000000..5eead2fb --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerGettingStartedPage.xaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerGettingStartedPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerGettingStartedPage.xaml.cs new file mode 100644 index 00000000..80a1569f --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerGettingStartedPage.xaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class PipsPagerGettingStartedPage : UserControl +{ + public PipsPagerGettingStartedPage() + { + InitializeComponent(); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerLargeCollectionPage.xaml b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerLargeCollectionPage.xaml new file mode 100644 index 00000000..5cc416d4 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerLargeCollectionPage.xaml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerLargeCollectionPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerLargeCollectionPage.xaml.cs new file mode 100644 index 00000000..2dc936b5 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPager/PipsPagerLargeCollectionPage.xaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages; + +public partial class PipsPagerLargeCollectionPage : UserControl +{ + public PipsPagerLargeCollectionPage() + { + InitializeComponent(); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPagerPage.xaml b/samples/AvaloniaControlCatalog/Pages/PipsPagerPage.xaml new file mode 100644 index 00000000..aee21a7d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPagerPage.xaml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/PipsPagerPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/PipsPagerPage.xaml.cs new file mode 100644 index 00000000..5a3b142f --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PipsPagerPage.xaml.cs @@ -0,0 +1,54 @@ +using System; +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class PipsPagerPage : ContentPage + { + private static readonly (string Group, string Title, string Description, Func Factory)[] Demos = + { + ("Getting Started", "First Look", + "Default PipsPager with horizontal and vertical orientation, with and without navigation buttons.", + () => new PipsPagerGettingStartedPage()), + + ("Features", "Carousel Integration", + "Bind SelectedPageIndex to a Carousel's SelectedIndex for two-way synchronized page navigation.", + () => new PipsPagerCarouselPage()), + ("Features", "Large Collections", + "Use MaxVisiblePips to limit visible indicators when the page count is large. Pips scroll automatically.", + () => new PipsPagerLargeCollectionPage()), + ("Features", "Events", + "Monitor SelectedPageIndex changes to react to user navigation.", + () => new PipsPagerEventsPage()), + + ("Appearance", "Custom Colors", + "Override pip indicator colors using resource keys for normal, selected, and hover states.", + () => new PipsPagerCustomColorsPage()), + ("Appearance", "Custom Button Themes", + "Replace the default chevron navigation buttons with custom button themes.", + () => new PipsPagerCustomButtonThemesPage()), + ("Appearance", "Custom Templates", + "Override pip item templates to create squares, pills, numbers, or any custom shape.", + () => new PipsPagerCustomTemplatesPage()), + + ("Showcases", "Care Companion", + "A health care onboarding flow using PipsPager as the page indicator for a CarouselPage.", + () => new CareCompanionAppPage()), + ("Showcases", "Sanctuary", + "A travel discovery app using PipsPager as the page indicator for a CarouselPage.", + () => new SanctuaryShowcasePage()), + }; + + public PipsPagerPage() + { + InitializeComponent(); + Loaded += OnLoaded; + } + + private async void OnLoaded(object? sender, RoutedEventArgs e) + { + await SampleNav.PushAsync(NavigationDemoHelper.CreateGalleryHomePage(SampleNav, Demos), null); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PlatformInfoPage.xaml b/samples/AvaloniaControlCatalog/Pages/PlatformInfoPage.xaml new file mode 100644 index 00000000..f86d53b7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PlatformInfoPage.xaml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/PlatformInfoPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/PlatformInfoPage.xaml.cs new file mode 100644 index 00000000..db1bbef4 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PlatformInfoPage.xaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public partial class PlatformInfoPage : ContentPage + { + public PlatformInfoPage() + { + InitializeComponent(); + DataContext = new PlatformInformationViewModel(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PointerCanvas.cs b/samples/AvaloniaControlCatalog/Pages/PointerCanvas.cs new file mode 100644 index 00000000..1311a1a1 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PointerCanvas.cs @@ -0,0 +1,236 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Media; +using Avalonia.Threading; + +namespace ControlCatalog.Pages; + +public class PointerCanvas : Control +{ + private readonly Stopwatch _stopwatch = Stopwatch.StartNew(); + private int _events; + private IDisposable? _statusUpdated; + private Dictionary _pointers = new(); + private PointerPointProperties? _lastProperties; + private PointerUpdateKind? _lastNonOtherUpdateKind; + class PointerPoints + { + struct CanvasPoint + { + public IBrush? Brush; + public Point Point; + public double Radius; + public double? Pressure; + } + + readonly CanvasPoint[] _points = new CanvasPoint[1000]; + int _index; + + public void Render(DrawingContext context, bool drawPoints) + { + CanvasPoint? prev = null; + for (var c = 0; c < _points.Length; c++) + { + var i = (c + _index) % _points.Length; + var pt = _points[i]; + var pressure = (pt.Pressure ?? prev?.Pressure ?? 0.5); + var thickness = pressure * 10; + var radius = pressure * pt.Radius; + + if (drawPoints) + { + if (pt.Brush != null) + { + context.DrawEllipse(pt.Brush, null, pt.Point, radius, radius); + } + } + else + { + if (prev.HasValue && prev.Value.Brush != null && pt.Brush != null + && prev.Value.Pressure != null && pt.Pressure != null) + { + var linePen = new Pen(Brushes.Black, thickness, null, PenLineCap.Round, PenLineJoin.Round); + context.DrawLine(linePen, prev.Value.Point, pt.Point); + } + } + prev = pt; + } + + } + + void AddPoint(Point pt, IBrush brush, double radius, float? pressure = null) + { + _points[_index] = new CanvasPoint { Point = pt, Brush = brush, Radius = radius, Pressure = pressure }; + _index = (_index + 1) % _points.Length; + } + + public void HandleEvent(PointerEventArgs e, Visual v) + { + e.Handled = true; + e.PreventGestureRecognition(); + var currentPoint = e.GetCurrentPoint(v); + if (e.RoutedEvent == PointerPressedEvent) + AddPoint(currentPoint.Position, Brushes.Green, 10); + else if (e.RoutedEvent == PointerReleasedEvent) + AddPoint(currentPoint.Position, Brushes.Red, 10); + else + { + var pts = e.GetIntermediatePoints(v); + for (var c = 0; c < pts.Count; c++) + { + var pt = pts[c]; + AddPoint(pt.Position, c == pts.Count - 1 ? Brushes.Blue : Brushes.Black, + c == pts.Count - 1 ? 5 : 2, pt.Properties.Pressure); + } + } + } + } + + private int _threadSleep; + public static readonly DirectProperty ThreadSleepProperty = + AvaloniaProperty.RegisterDirect(nameof(ThreadSleep), c => c.ThreadSleep, (c, v) => c.ThreadSleep = v); + + public int ThreadSleep + { + get => _threadSleep; + set => SetAndRaise(ThreadSleepProperty, ref _threadSleep, value); + } + + private bool _drawOnlyPoints; + public static readonly DirectProperty DrawOnlyPointsProperty = + AvaloniaProperty.RegisterDirect(nameof(DrawOnlyPoints), c => c.DrawOnlyPoints, (c, v) => c.DrawOnlyPoints = v); + + public bool DrawOnlyPoints + { + get => _drawOnlyPoints; + set => SetAndRaise(DrawOnlyPointsProperty, ref _drawOnlyPoints, value); + } + + private string? _status; + public static readonly DirectProperty StatusProperty = + AvaloniaProperty.RegisterDirect(nameof(Status), c => c.Status, (c, v) => c.Status = v, + defaultBindingMode: Avalonia.Data.BindingMode.TwoWay); + + public string? Status + { + get => _status; + set => SetAndRaise(StatusProperty, ref _status, value); + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + _statusUpdated = DispatcherTimer.Run(() => + { + if (_stopwatch.Elapsed.TotalMilliseconds > 250) + { + Status = $@"Events per second: {(_events / _stopwatch.Elapsed.TotalSeconds)} +PointerUpdateKind: {_lastProperties?.PointerUpdateKind} +Last PointerUpdateKind != Other: {_lastNonOtherUpdateKind} +IsLeftButtonPressed: {_lastProperties?.IsLeftButtonPressed} +IsRightButtonPressed: {_lastProperties?.IsRightButtonPressed} +IsMiddleButtonPressed: {_lastProperties?.IsMiddleButtonPressed} +IsXButton1Pressed: {_lastProperties?.IsXButton1Pressed} +IsXButton2Pressed: {_lastProperties?.IsXButton2Pressed} +IsBarrelButtonPressed: {_lastProperties?.IsBarrelButtonPressed} +IsEraser: {_lastProperties?.IsEraser} +IsInverted: {_lastProperties?.IsInverted} +Pressure: {_lastProperties?.Pressure} +XTilt: {_lastProperties?.XTilt} +YTilt: {_lastProperties?.YTilt} +Twist: {_lastProperties?.Twist}"; + _stopwatch.Restart(); + _events = 0; + } + + return true; + }, TimeSpan.FromMilliseconds(10)); + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + + _statusUpdated?.Dispose(); + } + + void HandleEvent(PointerEventArgs e) + { + _events++; + + if (_threadSleep != 0) + { + Thread.Sleep(_threadSleep); + } + InvalidateVisual(); + + var lastPointer = e.GetCurrentPoint(this); + _lastProperties = lastPointer.Properties; + + if (_lastProperties?.PointerUpdateKind != PointerUpdateKind.Other) + { + _lastNonOtherUpdateKind = _lastProperties?.PointerUpdateKind; + } + + if (e.RoutedEvent == PointerReleasedEvent && e.Pointer.Type == PointerType.Touch) + { + _pointers.Remove(e.Pointer.Id); + return; + } + + if (e.Pointer.Type != PointerType.Pen + || lastPointer.Properties.Pressure > 0) + { + if (!_pointers.TryGetValue(e.Pointer.Id, out var pt)) + _pointers[e.Pointer.Id] = pt = new PointerPoints(); + pt.HandleEvent(e, this); + } + } + + public override void Render(DrawingContext context) + { + context.FillRectangle(Brushes.White, Bounds); + foreach (var pt in _pointers.Values) + pt.Render(context, _drawOnlyPoints); + base.Render(context); + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + if (e.ClickCount == 2) + { + _pointers.Clear(); + InvalidateVisual(); + return; + } + + HandleEvent(e); + base.OnPointerPressed(e); + } + + protected override void OnPointerMoved(PointerEventArgs e) + { + HandleEvent(e); + base.OnPointerMoved(e); + } + + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + HandleEvent(e); + base.OnPointerReleased(e); + } + + protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) + { + _lastProperties = null; + base.OnPointerCaptureLost(e); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PointerContactsTab.cs b/samples/AvaloniaControlCatalog/Pages/PointerContactsTab.cs new file mode 100644 index 00000000..323ac685 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PointerContactsTab.cs @@ -0,0 +1,109 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Linq; + +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Media; +using Avalonia.Media.Immutable; + +namespace ControlCatalog.Pages; + +public class PointerContactsTab : Control +{ + class PointerInfo + { + public Point Point { get; set; } + public Color Color { get; set; } + } + + private static Color[] AllColors = new[] + { + Colors.Aqua, + Colors.Beige, + Colors.Chartreuse, + Colors.Coral, + Colors.Fuchsia, + Colors.Crimson, + Colors.Lavender, + Colors.Orange, + Colors.Orchid, + Colors.ForestGreen, + Colors.SteelBlue, + Colors.PapayaWhip, + Colors.PaleVioletRed, + Colors.Goldenrod, + Colors.Maroon, + Colors.Moccasin, + Colors.Navy, + Colors.Wheat, + Colors.Violet, + Colors.Sienna, + Colors.Indigo, + Colors.Honeydew + }; + + private Dictionary _pointers = new Dictionary(); + + public PointerContactsTab() + { + ClipToBounds = true; + } + + void UpdatePointer(PointerEventArgs e) + { + if (!_pointers.TryGetValue(e.Pointer, out var info)) + { + if (e.RoutedEvent == PointerMovedEvent) + return; + var colors = AllColors.Except(_pointers.Values.Select(c => c.Color)).ToArray(); + var color = colors[new Random().Next(0, colors.Length - 1)]; + _pointers[e.Pointer] = info = new PointerInfo { Color = color }; + } + + info.Point = e.GetPosition(this); + InvalidateVisual(); + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + UpdatePointer(e); + e.Pointer.Capture(this); + e.Handled = true; + e.PreventGestureRecognition(); + base.OnPointerPressed(e); + } + + protected override void OnPointerMoved(PointerEventArgs e) + { + UpdatePointer(e); + e.Handled = true; + base.OnPointerMoved(e); + } + + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + _pointers.Remove(e.Pointer); + e.Handled = true; + InvalidateVisual(); + } + + protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) + { + _pointers.Remove(e.Pointer); + InvalidateVisual(); + } + + public override void Render(DrawingContext context) + { + context.FillRectangle(Brushes.Transparent, new Rect(default, Bounds.Size)); + foreach (var pt in _pointers.Values) + { + var brush = new ImmutableSolidColorBrush(pt.Color); + + context.DrawEllipse(brush, null, pt.Point, 75, 75); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/PointersPage.xaml b/samples/AvaloniaControlCatalog/Pages/PointersPage.xaml new file mode 100644 index 00000000..2f958b90 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PointersPage.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Capture 1 + + + Capture 2 + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/PointersPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/PointersPage.xaml.cs new file mode 100644 index 00000000..388c8fa3 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/PointersPage.xaml.cs @@ -0,0 +1,59 @@ +using System; +using Avalonia.Controls; +using Avalonia.Input; + +namespace ControlCatalog.Pages; + +public partial class PointersPage : TabbedPage +{ + public PointersPage() + { + InitializeComponent(); + } + + private void Border_PointerUpdated(object? sender, PointerEventArgs e) + { + if (sender is Border border && border.Child is TextBlock textBlock) + { + var position = e.GetPosition(border); + textBlock.Text = @$"Type: {e.Pointer.Type} +Captured: {e.Pointer.Captured == sender} +PointerId: {e.Pointer.Id} +Position: {(int)position.X} {(int)position.Y}"; + e.Handled = true; + } + } + + private void Border_PointerCaptureLost(object? sender, PointerCaptureLostEventArgs e) + { + if (sender is Border border && border.Child is TextBlock textBlock) + { + textBlock.Text = @$"Type: {e.Pointer.Type} +Captured: {e.Pointer.Captured == sender} +PointerId: {e.Pointer.Id} +Position: ??? ???"; + e.Handled = true; + + } + } + + private void Border_PointerReleased(object? sender, PointerReleasedEventArgs e) + { + if (e.Pointer.Captured == sender) + { + e.Pointer.Capture(null); + e.Handled = true; + } + else if (e.Pointer.Captured is not null) + { + throw new InvalidOperationException("How?"); + } + } + + private void Border_PointerPressed(object? sender, PointerPressedEventArgs e) + { + e.Pointer.Capture(sender as Border); + e.Handled = true; + e.PreventGestureRecognition(); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ProgressBarPage.xaml b/samples/AvaloniaControlCatalog/Pages/ProgressBarPage.xaml new file mode 100644 index 00000000..3b80b5a6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ProgressBarPage.xaml @@ -0,0 +1,43 @@ + + + A progress bar control + + + Maximum + + + + Minimum + + + + Progress Text Format + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ProgressBarPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ProgressBarPage.xaml.cs new file mode 100644 index 00000000..244161a6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ProgressBarPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class ProgressBarPage : ContentPage + { + public ProgressBarPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/RadioButtonPage.xaml b/samples/AvaloniaControlCatalog/Pages/RadioButtonPage.xaml new file mode 100644 index 00000000..efb63ea1 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/RadioButtonPage.xaml @@ -0,0 +1,41 @@ + + + Allows the selection of a single option of many + + + + _Option 1 + O_ption 2 + Op_tion 3 + Disabled + + + Three States: Option 1 + Three States: Option 2 + Three States: Option 3 + Disabled + + + Group A: Option 1 + Group A: Disabled + Group B: Option 1 + Group B: Option 3 + + + Group A: Option 2 + Group B: Option 2 + Group B: Option 4 + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/RadioButtonPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/RadioButtonPage.xaml.cs new file mode 100644 index 00000000..8199a7ca --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/RadioButtonPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class RadioButtonPage : ContentPage + { + public RadioButtonPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/RefreshContainerPage.axaml b/samples/AvaloniaControlCatalog/Pages/RefreshContainerPage.axaml new file mode 100644 index 00000000..a905e4ef --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/RefreshContainerPage.axaml @@ -0,0 +1,44 @@ + + + + + + Used pull direction: + + + + TopToBottom + LeftToRight + RightToLeft + BottomToTop + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/RefreshContainerPage.axaml.cs b/samples/AvaloniaControlCatalog/Pages/RefreshContainerPage.axaml.cs new file mode 100644 index 00000000..2c886fa9 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/RefreshContainerPage.axaml.cs @@ -0,0 +1,28 @@ +using Avalonia.Controls; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public partial class RefreshContainerPage : ContentPage + { + private RefreshContainerViewModel _viewModel; + + public RefreshContainerPage() + { + InitializeComponent(); + + _viewModel = new RefreshContainerViewModel(); + + DataContext = _viewModel; + } + + private async void RefreshContainerPage_RefreshRequested(object? sender, RefreshRequestedEventArgs e) + { + var deferral = e.GetDeferral(); + + await _viewModel.AddToTop(); + + deferral.Complete(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/RelativePanelPage.axaml b/samples/AvaloniaControlCatalog/Pages/RelativePanelPage.axaml new file mode 100644 index 00000000..ccc39bef --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/RelativePanelPage.axaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/RelativePanelPage.axaml.cs b/samples/AvaloniaControlCatalog/Pages/RelativePanelPage.axaml.cs new file mode 100644 index 00000000..3c59deb7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/RelativePanelPage.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class RelativePanelPage : ContentPage + { + public RelativePanelPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ScreenPage.cs b/samples/AvaloniaControlCatalog/Pages/ScreenPage.cs new file mode 100644 index 00000000..97911529 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ScreenPage.cs @@ -0,0 +1,174 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Net.Http.Headers; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Platform; +using Avalonia.Rendering; +using Avalonia.Threading; + +namespace ControlCatalog.Pages +{ + public class ScreenPage : ContentPage + { + private double _leftMost; + private double _topMost; + private IBrush _primaryBrush = SolidColorBrush.Parse("#FF0078D7"); + private IBrush _defaultBrush = Brushes.LightGray; + private IPen _activePen = new Pen(Brushes.Black); + private IPen _defaultPen = new Pen(Brushes.DarkGray); + + public ScreenPage() + { + var button = new Button(); + button.Content = "Request ScreenDetails"; + button.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top; + button.Click += async (sender, args) => + { + var success = TopLevel.GetTopLevel(this)!.Screens is { } screens ? + await screens.RequestScreenDetails() : + false; + button.Content = "Request ScreenDetails: " + (success ? "Granted" : "Denied"); + }; + Content = button; + } + + protected override bool BypassFlowDirectionPolicies => true; + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + var topLevel = TopLevel.GetTopLevel(this); + if (topLevel is Window w) + { + w.PositionChanged += (_, _) => InvalidateVisual(); + } + + if (topLevel?.Screens is { } screens) + { + screens.Changed += (_, _) => + { + Console.WriteLine("Screens Changed"); + InvalidateVisual(); + }; + } + } + + public override void Render(DrawingContext context) + { + base.Render(context); + double beginOffset = (Content as Visual)?.Bounds.Height + 10 ?? 0; + + var topLevel = TopLevel.GetTopLevel(this)!; + if (topLevel.Screens is not { } screens) + { + var formattedText = CreateFormattedText("Current platform doesn't support Screens API."); + context.DrawText(formattedText, new Point(15, 15 + beginOffset)); + return; + } + + var activeScreen = screens.ScreenFromTopLevel(topLevel); + double maxBottom = 0; + + for (int i = 0; i + + + Allows for horizontal and vertical content scrolling. Supports snapping on touch and pointer wheel scrolling. + + + + + + + + + + + + + + + + + + + + + + + + + + + Scrollviewer can snap supported content both vertically and horizontally. Snapping occurs from scrolling with touch or pen. + + + + + + + + + + + + + + + + Vertical Snapping + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Horizontal Snapping + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ScrollViewerPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ScrollViewerPage.xaml.cs new file mode 100644 index 00000000..cfdd5bab --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ScrollViewerPage.xaml.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using MiniMvvm; + +namespace ControlCatalog.Pages +{ + public class ScrollViewerPageViewModel : ViewModelBase + { + private bool _allowAutoHide; + private bool _enableInertia; + private ScrollBarVisibility _horizontalScrollVisibility; + private ScrollBarVisibility _verticalScrollVisibility; + private SnapPointsType _snapPointsType; + private SnapPointsAlignment _snapPointsAlignment; + private bool _areSnapPointsRegular; + + public ScrollViewerPageViewModel() + { + AvailableVisibility = new List + { + ScrollBarVisibility.Auto, + ScrollBarVisibility.Visible, + ScrollBarVisibility.Hidden, + ScrollBarVisibility.Disabled, + }; + + AvailableSnapPointsType = new List() + { + SnapPointsType.None, + SnapPointsType.Mandatory, + SnapPointsType.MandatorySingle + }; + + AvailableSnapPointsAlignment = new List() + { + SnapPointsAlignment.Near, + SnapPointsAlignment.Center, + SnapPointsAlignment.Far, + }; + + HorizontalScrollVisibility = ScrollBarVisibility.Auto; + VerticalScrollVisibility = ScrollBarVisibility.Auto; + AllowAutoHide = true; + EnableInertia = true; + } + + public bool AllowAutoHide + { + get => _allowAutoHide; + set => this.RaiseAndSetIfChanged(ref _allowAutoHide, value); + } + + public bool EnableInertia + { + get => _enableInertia; + set => this.RaiseAndSetIfChanged(ref _enableInertia, value); + } + + public ScrollBarVisibility HorizontalScrollVisibility + { + get => _horizontalScrollVisibility; + set => this.RaiseAndSetIfChanged(ref _horizontalScrollVisibility, value); + } + + public ScrollBarVisibility VerticalScrollVisibility + { + get => _verticalScrollVisibility; + set => this.RaiseAndSetIfChanged(ref _verticalScrollVisibility, value); + } + + public List AvailableVisibility { get; } + + public bool AreSnapPointsRegular + { + get => _areSnapPointsRegular; + set => this.RaiseAndSetIfChanged(ref _areSnapPointsRegular, value); + } + + public SnapPointsType SnapPointsType + { + get => _snapPointsType; + set => this.RaiseAndSetIfChanged(ref _snapPointsType, value); + } + + public SnapPointsAlignment SnapPointsAlignment + { + get => _snapPointsAlignment; + set => this.RaiseAndSetIfChanged(ref _snapPointsAlignment, value); + } + public List AvailableSnapPointsType { get; } + public List AvailableSnapPointsAlignment { get; } + } + + public partial class ScrollViewerPage : TabbedPage + { + public ScrollViewerPage() + { + InitializeComponent(); + + DataContext = new ScrollViewerPageViewModel(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/SliderPage.xaml b/samples/AvaloniaControlCatalog/Pages/SliderPage.xaml new file mode 100644 index 00000000..445564cb --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/SliderPage.xaml @@ -0,0 +1,84 @@ + + + A control that lets the user select from a range of values by moving a Thumb control along a Track. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/SliderPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/SliderPage.xaml.cs new file mode 100644 index 00000000..f65f7143 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/SliderPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class SliderPage : ContentPage + { + public SliderPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/SplitViewPage.xaml b/samples/AvaloniaControlCatalog/Pages/SplitViewPage.xaml new file mode 100644 index 00000000..144688e3 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/SplitViewPage.xaml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + Left + Right + Top + Bottom + + + + + + Inline + CompactInline + Overlay + CompactOverlay + + + + + CatalogChromeMediumColor + Red + Blue + Green + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/SplitViewPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/SplitViewPage.xaml.cs new file mode 100644 index 00000000..4917a13c --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/SplitViewPage.xaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public partial class SplitViewPage : ContentPage + { + public SplitViewPage() + { + InitializeComponent(); + DataContext = new SplitViewPageViewModel(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TabControlPage.xaml b/samples/AvaloniaControlCatalog/Pages/TabControlPage.xaml new file mode 100644 index 00000000..e7aa18ea --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabControlPage.xaml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + This is the first page in the TabControl. + + + + + + This is the second page in the TabControl. + + + + + You should not see this. + + + + + + + + + + + + + + + + + + + + + + Tab Placement: + + Left + Bottom + Right + Top + + Set TabItem.ContentTemplate + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/TabControlPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/TabControlPage.xaml.cs new file mode 100644 index 00000000..366b5216 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabControlPage.xaml.cs @@ -0,0 +1,47 @@ +using System; +using Avalonia.Controls; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public partial class TabControlPage : ContentPage + { + public TabControlPage() + { + InitializeComponent(); + + DataContext = new TabControlPageViewModel + { + Tabs = new[] + { + new TabControlPageViewModelItem + { + Header = "Arch", + Text = "This is the first templated tab page.", + Image = LoadBitmap("avares://ControlCatalog/Assets/delicate-arch-896885_640.jpg"), + }, + new TabControlPageViewModelItem + { + Header = "Leaf", + Text = "This is the second templated tab page.", + Image = LoadBitmap("avares://ControlCatalog/Assets/maple-leaf-888807_640.jpg"), + }, + new TabControlPageViewModelItem + { + Header = "Disabled", + Text = "You should not see this.", + IsEnabled = false, + }, + }, + TabPlacement = Dock.Top, + }; + } + + private static Bitmap LoadBitmap(string uri) + { + return new Bitmap(AssetLoader.Open(new Uri(uri))); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TabStripPage.xaml b/samples/AvaloniaControlCatalog/Pages/TabStripPage.xaml new file mode 100644 index 00000000..f4a47af3 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabStripPage.xaml @@ -0,0 +1,35 @@ + + + A control which displays a selectable strip of tabs + + + + Defined in XAML + + Item 1 + Item 2 + Disabled + + + + + Dynamically generated + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/TabStripPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/TabStripPage.xaml.cs new file mode 100644 index 00000000..d8301098 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabStripPage.xaml.cs @@ -0,0 +1,33 @@ +using Avalonia.Controls; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public partial class TabStripPage : ContentPage + { + public TabStripPage() + { + InitializeComponent(); + + DataContext = new TabControlPageViewModel + { + Tabs = new[] + { + new TabControlPageViewModelItem() + { + Header = "Item 1", + }, + new TabControlPageViewModelItem + { + Header = "Item 2", + }, + new TabControlPageViewModelItem + { + Header = "Disabled", + IsEnabled = false, + }, + } + }; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TabbedDemoPage.xaml b/samples/AvaloniaControlCatalog/Pages/TabbedDemoPage.xaml new file mode 100644 index 00000000..58a3b4fe --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabbedDemoPage.xaml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/TabbedDemoPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/TabbedDemoPage.xaml.cs new file mode 100644 index 00000000..88692c97 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabbedDemoPage.xaml.cs @@ -0,0 +1,96 @@ +using System; +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class TabbedDemoPage : ContentPage + { + private static readonly (string Group, string Title, string Description, Func Factory)[] Demos = + { + // Overview + ("Overview", "First Look", + "Basic TabbedPage with three tabs, tab placement selector, and selection status.", + () => new TabbedPageFirstLookPage()), + + // Populate + ("Populate", "Page Collection", + "Populate a TabbedPage by adding ContentPage objects directly to the Pages collection.", + () => new TabbedPageCollectionPage()), + ("Populate", "Data Templates", + "Bind TabbedPage to an ObservableCollection, add or remove tabs at runtime, and switch the page template.", + () => new TabbedPageDataTemplatePage()), + + // Appearance + ("Appearance", "Tab Customization", + "Customize tab placement, bar background, selected and unselected tab colors.", + () => new TabbedPageCustomizationPage()), + ("Appearance", "Custom Tab Bar", + "VYNTRA-style custom tab bar with floating pill, brand colours, and system-adaptive theme using only resource overrides and styles.", + () => new TabbedPageCustomTabBarPage()), + ("Appearance", "FAB Tab Bar", + "Social-media-style bottom nav with a central floating action button that triggers a command, not a tab.", + () => new TabbedPageFabPage()), + ("Appearance", "Fluid Nav Bar", + "Inspired by the Flutter fluid_nav_bar vignette. Color themes with animated indicator and icons.", + () => new TabbedPageFluidNavPage()), + + // Features + ("Features", "Programmatic Selection", + "Preset the initial tab with SelectedIndex, jump to any tab programmatically, and respond to SelectionChanged events.", + () => new TabbedPageProgrammaticPage()), + ("Features", "Placement", "Switch the tab bar between Top, Bottom, Left, and Right placements.", + () => new TabbedPagePlacementPage()), + ("Features", "Page Transitions", + "Animate tab switches with CrossFade, PageSlide, or composite transitions.", + () => new TabbedPageTransitionsPage()), + ("Features", "Keyboard Navigation", + "Keyboard shortcuts to navigate between tabs, with a toggle to enable or disable.", + () => new TabbedPageKeyboardPage()), + ("Features", "Swipe Gestures", + "Swipe left/right (Top/Bottom) or up/down (Left/Right) to navigate. Toggle IsGestureEnabled.", + () => new TabbedPageGesturePage()), + ("Features", "Events", + "SelectionChanged, NavigatedTo, and NavigatedFrom events. Switch tabs to see the live event log.", + () => new TabbedPageEventsPage()), + ("Features", "Disabled Tabs", + "IsTabEnabled attached property: disable individual tabs so they cannot be selected.", + () => new TabbedPageDisabledTabsPage()), + + // Performance + ("Performance", "Performance Monitor", + "Track tab count, live page instances, and managed heap size. Observe how GC reclaims memory after removing tabs.", + () => new TabbedPagePerformancePage()), + + // Composition + ("Composition", "With NavigationPage", + "Embed a NavigationPage inside each TabbedPage tab for drill-down navigation.", + () => new TabbedPageWithNavigationPage()), + ("Composition", "With DrawerPage", + "Combine TabbedPage with DrawerPage: a global navigation drawer sits over tabbed content.", + () => new TabbedPageWithDrawerPage()), + + // Showcases + ("Showcases", "Pulse Fitness", + "Fitness app with bottom TabbedPage navigation, NavigationPage drill-down inside tabs, and workout detail screens.", + () => new PulseAppPage()), + ("Showcases", "L'Avenir Restaurant", + "Restaurant app with DrawerPage root, NavigationPage detail, and TabbedPage bottom tabs for Menu, Reservations, and Profile.", + () => new LAvenirAppPage()), + ("Showcases", "Retro Gaming", + "Arcade-style app with NavigationPage header, TabbedPage bottom tabs with CenteredTabPanel, and game detail push.", + () => new RetroGamingAppPage()), + }; + + public TabbedDemoPage() + { + InitializeComponent(); + Loaded += OnLoaded; + } + + private async void OnLoaded(object? sender, RoutedEventArgs e) + { + await SampleNav.PushAsync(NavigationDemoHelper.CreateGalleryHomePage(SampleNav, Demos), null); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TabbedPage/CenteredTabPanel.cs b/samples/AvaloniaControlCatalog/Pages/TabbedPage/CenteredTabPanel.cs new file mode 100644 index 00000000..6ca5e0ec --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabbedPage/CenteredTabPanel.cs @@ -0,0 +1,63 @@ +using System; +using Avalonia; +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + /// + /// A custom panel that arranges N children in N+1 equally-sized slots, leaving the + /// middle slot empty. Intended for tab bars that need a central action button + /// overlaid on the gap. + /// + /// For N children the split is ⌊N/2⌋ items on the left and ⌈N/2⌉ on the right. + /// Example – 4 tabs: [0][1][ gap ][2][3] (5 equal columns, center is free). + /// + public class CenteredTabPanel : Panel + { + protected override Size MeasureOverride(Size availableSize) + { + int count = Children.Count; + if (count == 0) + return default; + + int slots = count + 1; + bool infiniteWidth = double.IsInfinity(availableSize.Width); + double slotWidth = infiniteWidth ? 60.0 : availableSize.Width / slots; + + double maxHeight = 0; + foreach (var child in Children) + { + child.Measure(new Size(slotWidth, availableSize.Height)); + maxHeight = Math.Max(maxHeight, child.DesiredSize.Height); + } + + // When given finite width, fill it. When infinite (inside a ScrollViewer), + // return a small positive width so the parent allocates real space. + double desiredWidth = infiniteWidth ? slotWidth * slots : availableSize.Width; + if (double.IsNaN(maxHeight) || double.IsInfinity(maxHeight)) + maxHeight = 0; + + return new Size(desiredWidth, maxHeight); + } + + protected override Size ArrangeOverride(Size finalSize) + { + int count = Children.Count; + if (count == 0) + return finalSize; + + int slots = count + 1; + double slotW = finalSize.Width / slots; + int leftCount = count / 2; // items placed to the left of the gap + + for (int i = 0; i < count; i++) + { + // Skip the center slot (leftCount), reserved for the FAB. + int slot = i < leftCount ? i : i + 1; + Children[i].Arrange(new Rect(slot * slotW, 0, slotW, finalSize.Height)); + } + + return finalSize; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TabbedPage/FluidNavBar/FluidNavBar.cs b/samples/AvaloniaControlCatalog/Pages/TabbedPage/FluidNavBar/FluidNavBar.cs new file mode 100644 index 00000000..a68e57c9 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabbedPage/FluidNavBar/FluidNavBar.cs @@ -0,0 +1,620 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Media; +using Avalonia.Platform; +using Avalonia.Rendering.SceneGraph; +using Avalonia.Skia; +using Avalonia.Threading; +using SkiaSharp; + +namespace ControlCatalog.Pages +{ + /// + /// A fluid navigation bar that replicates the Flutter fluid_nav_bar vignette. + /// The bar background has a bezier "dip" that travels to the selected tab. + /// Each icon is drawn progressively using SKPathMeasure for the fill animation. + /// + public class FluidNavBar : Control, Avalonia.Rendering.ICustomHitTest + { + internal const double NominalHeight = 56.0; + internal const double CircleRadius = 25.0; + internal const double ActiveFloat = 16.0; // px the circle rises + internal const double IconDrawScale = 0.9; // icon scale within circle + internal const double ScaleCurveScale = 0.50; + internal const double FloatLinearPIn = 0.28; + internal const double FillLinearPIn = 0.25; + internal const double XAnimDuration = 0.620; // s — X bump travel + internal const double YDipDuration = 0.300; // s — dip down + internal const double YBounceDelay = 0.500; // s — wait before bounce + internal const double YBounceDuration = 1.200; // s — elastic bounce up + internal const double FloatUpDuration = 1.666; // s — circle rising + internal const double FloatDownDuration = 0.833; // s — circle falling + + public static readonly StyledProperty> ItemsProperty = + AvaloniaProperty.Register>( + nameof(Items), new List()); + + public static readonly StyledProperty SelectedIndexProperty = + AvaloniaProperty.Register(nameof(SelectedIndex), 0); + + public static readonly StyledProperty BarColorProperty = + AvaloniaProperty.Register(nameof(BarColor), Colors.White); + + public static readonly StyledProperty ButtonColorProperty = + AvaloniaProperty.Register(nameof(ButtonColor), Colors.White); + + public static readonly StyledProperty ActiveIconColorProperty = + AvaloniaProperty.Register(nameof(ActiveIconColor), Colors.Black); + + public static readonly StyledProperty InactiveIconColorProperty = + AvaloniaProperty.Register( + nameof(InactiveIconColor), Color.FromArgb(140, 120, 120, 120)); + + private double _xCurrent = -1; // -1 = not yet initialised + private double _lastWidth = -1; // tracks width changes for resize correction + private double _xStart, _xTarget, _xAnimStartSec; + private double _yValue = 1.0; // 0 = deepest dip, 1 = flat + private double _yDipStartSec; + private bool _yBounceStarted; + private double _yBounceStartSec; + + // per-item (length = Items.Count after OnItemsChanged) + private double[] _floatProgress = Array.Empty(); + private double[] _floatStartSec = Array.Empty(); + private bool[] _floatGoingUp = Array.Empty(); + + // Parsed Skia paths — owned here, disposed on detach / items change + private SKPath?[] _parsedPaths = Array.Empty(); + + private DispatcherTimer? _animTimer; + private readonly Stopwatch _clock = Stopwatch.StartNew(); + private bool _animating; + + public IList Items + { + get => GetValue(ItemsProperty); + set => SetValue(ItemsProperty, value); + } + + public int SelectedIndex + { + get => GetValue(SelectedIndexProperty); + set => SetValue(SelectedIndexProperty, value); + } + + public Color BarColor + { + get => GetValue(BarColorProperty); + set => SetValue(BarColorProperty, value); + } + + public Color ButtonColor + { + get => GetValue(ButtonColorProperty); + set => SetValue(ButtonColorProperty, value); + } + + public Color ActiveIconColor + { + get => GetValue(ActiveIconColorProperty); + set => SetValue(ActiveIconColorProperty, value); + } + + public Color InactiveIconColor + { + get => GetValue(InactiveIconColorProperty); + set => SetValue(InactiveIconColorProperty, value); + } + + public event EventHandler? SelectionChanged; + + public FluidNavBar() + { + ClipToBounds = false; + Height = NominalHeight; + Cursor = new Cursor(StandardCursorType.Hand); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ItemsProperty) + OnItemsChanged(); + else if (change.Property == SelectedIndexProperty) + OnSelectedIndexChanged(change.GetOldValue(), change.GetNewValue()); + else if (change.Property == BarColorProperty + || change.Property == ButtonColorProperty + || change.Property == ActiveIconColorProperty + || change.Property == InactiveIconColorProperty) + InvalidateVisual(); + } + + public bool HitTest(Point point) + { + return point.X >= 0 && point.X <= Bounds.Width + && point.Y >= -(ActiveFloat + CircleRadius) + && point.Y <= Bounds.Height; + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + + var n = Items?.Count ?? 0; + if (n == 0 || Bounds.Width <= 0) return; + + var pos = e.GetPosition(this); + var index = (int)(pos.X / (Bounds.Width / n)); + index = Math.Clamp(index, 0, n - 1); + + if (index != SelectedIndex) + { + SetCurrentValue(SelectedIndexProperty, index); + SelectionChanged?.Invoke(this, index); + } + + e.Handled = true; + } + + protected override Size MeasureOverride(Size availableSize) + { + var w = double.IsPositiveInfinity(availableSize.Width) ? 300 : availableSize.Width; + return new Size(w, NominalHeight); + } + + protected override Size ArrangeOverride(Size finalSize) + { + var w = finalSize.Width; + if (w > 0) + { + if (_xCurrent < 0 || _lastWidth < 0) + { + // First layout — snap everything to the current selection + _xCurrent = IndexToX(SelectedIndex, w); + _xTarget = _xCurrent; + _xStart = _xCurrent; + } + else if (Math.Abs(w - _lastWidth) > 0.5) + { + // Width changed (resize) — scale pixel positions proportionally + // so the bump stays over the correct slot + var ratio = w / _lastWidth; + _xCurrent = _xCurrent * ratio; + _xStart = _xStart * ratio; + _xTarget = IndexToX(SelectedIndex, w); + InvalidateVisual(); + } + _lastWidth = w; + } + return new Size(w > 0 ? w : 300, NominalHeight); + } + + public override void Render(DrawingContext context) + { + var w = Bounds.Width; + var h = Bounds.Height; + + if (w <= 0 || h <= 0 || Items == null || Items.Count == 0) return; + + var n = Items.Count; + + // Initialise _xCurrent if layout didn't run yet + if (_xCurrent < 0) + { + _xCurrent = IndexToX(SelectedIndex, w); + _xTarget = _xCurrent; + } + + // Snapshot per-item animation state for this frame + var slotCenters = new double[n]; + var floatOffsets = new double[n]; + var scaleYValues = new double[n]; + var fillAmounts = new double[n]; + + for (int i = 0; i < n; i++) + slotCenters[i] = IndexToX(i, w); + + for (int i = 0; i < n; i++) + { + var p = i < _floatProgress.Length ? _floatProgress[i] : (i == SelectedIndex ? 1.0 : 0.0); + var goUp = i < _floatGoingUp.Length ? _floatGoingUp[i] : (i == SelectedIndex); + + // Float offset — uses LinearPoint(0.28, 0) to delay start, then elastic/quintic easing + var linearP = LinearPoint(p, FloatLinearPIn, 0.0); + var floatEased = goUp ? ElasticOut(linearP, 0.38) : EaseInQuint(linearP); + floatOffsets[i] = ActiveFloat * floatEased; + + // Scale Y squish via CenteredElastic curves + var centered = goUp ? CenteredElasticOut(p, 0.6) : CenteredElasticIn(p, 0.6); + scaleYValues[i] = 0.75 + centered * ScaleCurveScale; + + // Icon fill — LinearPoint(0.25, 1.0) adds a slight draw delay vs float + fillAmounts[i] = LinearPoint(p, FillLinearPIn, 1.0); + } + + // Clamp scaleY to sane range to avoid SVG-transform oddities + for (int i = 0; i < n; i++) + scaleYValues[i] = Math.Max(0.1, Math.Min(1.5, scaleYValues[i])); + + var op = new FluidNavBarRenderOp( + new Rect(0, -(ActiveFloat + CircleRadius), w, h + ActiveFloat + CircleRadius), + (float)w, (float)h, + (float)_xCurrent, (float)_yValue, + slotCenters, floatOffsets, scaleYValues, fillAmounts, + _parsedPaths, + BarColor, ButtonColor, ActiveIconColor, InactiveIconColor); + + context.Custom(op); + } + + private void OnItemsChanged() + { + foreach (var p in _parsedPaths) p?.Dispose(); + + var n = Items?.Count ?? 0; + _parsedPaths = new SKPath?[n]; + _floatProgress = new double[n]; + _floatStartSec = new double[n]; + _floatGoingUp = new bool[n]; + + for (int i = 0; i < n; i++) + { + var svg = Items![i].SvgPath; + if (!string.IsNullOrEmpty(svg)) + _parsedPaths[i] = SKPath.ParseSvgPathData(svg); + } + + var sel = Math.Clamp(SelectedIndex, 0, Math.Max(0, n - 1)); + for (int i = 0; i < n; i++) + { + _floatProgress[i] = i == sel ? 1.0 : 0.0; + _floatGoingUp[i] = i == sel; + } + + _xCurrent = -1; // force re-init on next arrange/render + InvalidateVisual(); + } + + private void OnSelectedIndexChanged(int oldIndex, int newIndex) + { + var n = _floatProgress.Length; + if (n == 0) return; + + newIndex = Math.Clamp(newIndex, 0, n - 1); + oldIndex = Math.Clamp(oldIndex, 0, n - 1); + if (oldIndex == newIndex) return; + + var now = _clock.Elapsed.TotalSeconds; + + // X: slide bump from old to new position + if (_xCurrent < 0 && Bounds.Width > 0) + _xCurrent = IndexToX(oldIndex, Bounds.Width); + + _xStart = _xCurrent; + _xTarget = Bounds.Width > 0 ? IndexToX(newIndex, Bounds.Width) : _xStart; + _xAnimStartSec = now; + + // Y: dip then elastic bounce + _yValue = 1.0; + _yDipStartSec = now; + _yBounceStarted = false; + + // Per-button float + _floatGoingUp[oldIndex] = false; + _floatStartSec[oldIndex] = now; + _floatGoingUp[newIndex] = true; + _floatStartSec[newIndex] = now; + + StartAnimation(); + } + + private void StartAnimation() + { + if (_animating) return; + _animating = true; + _animTimer = new DispatcherTimer( + TimeSpan.FromSeconds(1.0 / 60.0), + DispatcherPriority.Render, + OnAnimTick); + _animTimer.Start(); + } + + private void StopAnimation() + { + _animTimer?.Stop(); + _animTimer = null; + _animating = false; + } + + private void OnAnimTick(object? sender, EventArgs e) + { + var now = _clock.Elapsed.TotalSeconds; + var anyActive = false; + + var xElapsed = now - _xAnimStartSec; + if (xElapsed < XAnimDuration) + { + _xCurrent = _xStart + (_xTarget - _xStart) * (xElapsed / XAnimDuration); + anyActive = true; + } + else + { + _xCurrent = _xTarget; + } + + var yDipElapsed = now - _yDipStartSec; + if (yDipElapsed < YDipDuration) + { + _yValue = 1.0 - yDipElapsed / YDipDuration; + anyActive = true; + } + else + { + _yValue = 0.0; + + if (!_yBounceStarted && yDipElapsed >= YBounceDelay) + { + _yBounceStarted = true; + _yBounceStartSec = now; + } + + if (_yBounceStarted) + { + var bt = now - _yBounceStartSec; + if (bt < YBounceDuration) + { + _yValue = ElasticOut(bt / YBounceDuration, 0.38); + anyActive = true; + } + else + { + _yValue = 1.0; + } + } + } + + for (int i = 0; i < _floatProgress.Length; i++) + { + var elapsed = now - _floatStartSec[i]; + var duration = _floatGoingUp[i] ? FloatUpDuration : FloatDownDuration; + if (elapsed < duration) + { + var t = elapsed / duration; + _floatProgress[i] = _floatGoingUp[i] ? t : 1.0 - t; + anyActive = true; + } + else + { + _floatProgress[i] = _floatGoingUp[i] ? 1.0 : 0.0; + } + } + + InvalidateVisual(); + + if (!anyActive) + StopAnimation(); + } + + private double IndexToX(int index, double width) + { + var n = Items?.Count ?? 1; + if (n <= 0) n = 1; + return (index + 0.5) * (width / n); + } + + + internal static double ElasticOut(double t, double period = 0.4) + { + if (t <= 0) return 0; + if (t >= 1) return 1; + var s = period / 4.0; + return Math.Pow(2.0, -10.0 * t) * Math.Sin((t - s) * 2.0 * Math.PI / period) + 1.0; + } + + private static double CenteredElasticOut(double t, double period = 0.4) + { + return Math.Pow(2.0, -10.0 * t) * Math.Sin(t * 2.0 * Math.PI / period) + 0.5; + } + + private static double CenteredElasticIn(double t, double period = 0.4) + { + return -Math.Pow(2.0, 10.0 * (t - 1.0)) * Math.Sin((t - 1.0) * 2.0 * Math.PI / period) + 0.5; + } + + internal static double LinearPoint(double x, double pIn, double pOut) + { + if (pIn <= 0) return pOut; + var lowerScale = pOut / pIn; + var upperScale = (1.0 - pOut) / (1.0 - pIn); + var upperOff = 1.0 - upperScale; + return x < pIn ? x * lowerScale : x * upperScale + upperOff; + } + + private static double EaseInQuint(double t) => t * t * t * t * t; + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + StopAnimation(); + } + + private sealed class FluidNavBarRenderOp : ICustomDrawOperation + { + private readonly float _w, _h, _xCenter, _normY; + private readonly double[] _slots, _floatOff, _scaleY, _fill; + private readonly SKPath?[] _paths; + private readonly Color _bar, _btn, _active, _inactive; + + public Rect Bounds { get; } + + public FluidNavBarRenderOp( + Rect bounds, + float w, float h, + float xCenter, float normY, + double[] slots, double[] floatOff, double[] scaleY, double[] fill, + SKPath?[] paths, + Color bar, Color btn, Color active, Color inactive) + { + Bounds = bounds; + _w = w; _h = h; + _xCenter = xCenter; _normY = normY; + _slots = slots; _floatOff = floatOff; + _scaleY = scaleY; _fill = fill; + _paths = paths; + _bar = bar; _btn = btn; + _active = active; _inactive = inactive; + } + + public bool HitTest(Point p) => false; + public bool Equals(ICustomDrawOperation? other) => false; + public void Dispose() { } + + public void Render(ImmediateDrawingContext context) + { + var lease = context.TryGetFeature(); + if (lease == null) return; + + using var l = lease.Lease(); + var canvas = l.SkCanvas; + + int save = canvas.Save(); + try + { + DrawBackground(canvas); + for (int i = 0; i < _slots.Length; i++) + DrawButton(canvas, i); + } + finally + { + canvas.RestoreToCount(save); + } + } + + private void DrawBackground(SKCanvas canvas) + { + const float rTop = 54f, rBot = 44f; + const float hcTop = 0.6f, hcBot = 0.5f; + const float pcTop = 0.35f, pcBot = 0.85f; + const float tY = -10f, bY = 54f; + const float tD = 0f, bD = 6f; + + float norm = (float)(LinearPoint(_normY, 0.5, 2.0) / 2.0); + + float r = Lerp(rTop, rBot, norm); + float anchr = Lerp(r * hcTop, r * hcBot, (float)LinearPoint(norm, 0.5, 0.75)); + float dipc = Lerp(r * pcTop, r * pcBot, (float)LinearPoint(norm, 0.5, 0.80)); + float y = Lerp(tY, bY, (float)LinearPoint(norm, 0.2, 0.70)); + float dist = Lerp(tD, bD, (float)LinearPoint(norm, 0.5, 0.00)); + float x0 = _xCenter - dist / 2f; + float x1 = _xCenter + dist / 2f; + + using var path = new SKPath(); + path.MoveTo(0, 0); + path.LineTo(x0 - r, 0); + path.CubicTo(x0 - r + anchr, 0, x0 - dipc, y, x0, y); + path.LineTo(x1, y); + path.CubicTo(x1 + dipc, y, x1 + r - anchr, 0, x1 + r, 0); + path.LineTo(_w, 0); + path.LineTo(_w, _h); + path.LineTo(0, _h); + path.Close(); + + using var paint = new SKPaint { Color = ToSK(_bar), IsAntialias = true }; + canvas.DrawPath(path, paint); + } + + private void DrawButton(SKCanvas canvas, int i) + { + var cx = (float)_slots[i]; + var cy = _h / 2f; + var fo = (float)_floatOff[i]; + var sy = (float)_scaleY[i]; + var fa = (float)_fill[i]; + + const float r = (float)CircleRadius; + + // Circle — just translated up, not scaled + using var cp = new SKPaint { Color = ToSK(_btn), IsAntialias = true }; + canvas.DrawCircle(cx, cy - fo, r, cp); + + // Icon + if (i < _paths.Length && _paths[i] != null) + DrawIcon(canvas, _paths[i]!, cx, cy - fo, sy, fa); + } + + private void DrawIcon(SKCanvas canvas, SKPath path, float cx, float cy, + float scaleY, float fillAmount) + { + const float s = (float)IconDrawScale; + + int save = canvas.Save(); + canvas.Translate(cx, cy); + canvas.Scale(s, s * scaleY); + + // Grey background stroke (full path, unselected look) + using var bg = new SKPaint + { + Style = SKPaintStyle.Stroke, + StrokeWidth = 2.4f, + StrokeCap = SKStrokeCap.Round, + StrokeJoin = SKStrokeJoin.Round, + Color = ToSK(_inactive), + IsAntialias = true + }; + canvas.DrawPath(path, bg); + + // Foreground stroke, trimmed progressively with SKPathMeasure + if (fillAmount > 0f) + { + using var fg = new SKPaint + { + Style = SKPaintStyle.Stroke, + StrokeWidth = 2.4f, + StrokeCap = SKStrokeCap.Round, + StrokeJoin = SKStrokeJoin.Round, + Color = ToSK(_active), + IsAntialias = true + }; + DrawTrimmedPath(canvas, path, fillAmount, fg); + } + + canvas.RestoreToCount(save); + } + + // Iterates all contours and draws each trimmed to fillAmount of its length. + // Direct port of Flutter's extractPartialPath behavior. + private static void DrawTrimmedPath(SKCanvas canvas, SKPath path, + float fillAmount, SKPaint paint) + { + using var measure = new SKPathMeasure(path, false); + do + { + var len = measure.Length; + if (len <= 0f) continue; + + using var seg = new SKPath(); + if (measure.GetSegment(0f, len * fillAmount, seg, true)) + canvas.DrawPath(seg, paint); + } + while (measure.NextContour()); + } + + private static float Lerp(float a, float b, float t) => a + (b - a) * t; + + private static double LinearPoint(double x, double pIn, double pOut) + { + if (pIn <= 0) return pOut; + var lo = pOut / pIn; + var hi = (1.0 - pOut) / (1.0 - pIn); + return x < pIn ? x * lo : x * hi + (1.0 - hi); + } + + private static SKColor ToSK(Color c) => new SKColor(c.R, c.G, c.B, c.A); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TabbedPage/FluidNavBar/FluidNavItem.cs b/samples/AvaloniaControlCatalog/Pages/TabbedPage/FluidNavBar/FluidNavItem.cs new file mode 100644 index 00000000..e2c98005 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabbedPage/FluidNavBar/FluidNavItem.cs @@ -0,0 +1,14 @@ +namespace ControlCatalog.Pages +{ + public class FluidNavItem + { + public string SvgPath { get; } + public string Label { get; } + + public FluidNavItem(string svgPath, string label) + { + SvgPath = svgPath; + Label = label; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageCollectionPage.xaml b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageCollectionPage.xaml new file mode 100644 index 00000000..80e63a08 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageCollectionPage.xaml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFabPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFabPage.xaml.cs new file mode 100644 index 00000000..b52bfd4d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFabPage.xaml.cs @@ -0,0 +1,45 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Media; + +namespace ControlCatalog.Pages +{ + public partial class TabbedPageFabPage : UserControl + { + private static readonly StreamGeometry FeedGeometry = + StreamGeometry.Parse("M12.9942 2.79444C12.4118 2.30208 11.5882 2.30208 11.0058 2.79444L3.50582 9.39444C3.18607 9.66478 3 10.0634 3 10.4828V20.25C3 20.9404 3.55964 21.5 4.25 21.5H8.25C8.94036 21.5 9.5 20.9404 9.5 20.25V14.75C9.5 14.6119 9.61193 14.5 9.75 14.5H14.25C14.3881 14.5 14.5 14.6119 14.5 14.75V20.25C14.5 20.9404 15.0596 21.5 15.75 21.5H19.75C20.4404 21.5 21 20.9404 21 20.25V10.4828C21 10.0634 20.8139 9.66478 20.4942 9.39444L12.9942 2.79444Z"); + private static readonly StreamGeometry DiscoverGeometry = + StreamGeometry.Parse("M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm4.24 5.76-3.03 6.55-6.55 3.03L9.69 10.8l6.55-3.04zM12 13.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"); + private static readonly StreamGeometry AlertsGeometry = + StreamGeometry.Parse("M12 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"); + private static readonly StreamGeometry ProfileGeometry = + StreamGeometry.Parse("M12 2C9.243 2 7 4.243 7 7s2.243 5 5 5 5-2.243 5-5-2.243-5-5-5zM12 14c-5.523 0-10 3.582-10 8a1 1 0 001 1h18a1 1 0 001-1c0-4.418-4.477-8-10-8z"); + + private int _postCount; + + public TabbedPageFabPage() + { + InitializeComponent(); + SetupIcons(); + + FabButton.Click += OnFabClicked; + TriggerFabButton.Click += OnFabClicked; + } + + private void SetupIcons() + { + FeedPage.Icon = new PathIcon { Data = FeedGeometry }; + DiscoverPage.Icon = new PathIcon { Data = DiscoverGeometry }; + AlertsPage.Icon = new PathIcon { Data = AlertsGeometry }; + ProfilePage.Icon = new PathIcon { Data = ProfileGeometry }; + } + + private void OnFabClicked(object? sender, RoutedEventArgs e) + { + _postCount++; + StatusText.Text = _postCount == 1 + ? "Post created! Check your feed." + : $"{_postCount} posts created!"; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFirstLookPage.xaml b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFirstLookPage.xaml new file mode 100644 index 00000000..1dabb6ba --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageFirstLookPage.xaml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithDrawerPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithDrawerPage.xaml.cs new file mode 100644 index 00000000..88f688d7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithDrawerPage.xaml.cs @@ -0,0 +1,114 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; + +namespace ControlCatalog.Pages +{ + public partial class TabbedPageWithDrawerPage : UserControl + { + public TabbedPageWithDrawerPage() + { + InitializeComponent(); + Loaded += (_, _) => ShowSection("Home"); + } + + private void OnSectionSelected(object? sender, RoutedEventArgs e) + { + if (sender is Button btn && btn.Tag is string section) + { + ShowSection(section); + DemoDrawer.IsOpen = false; + } + } + + private void ShowSection(string section) + { + SectionHost.Content = section switch + { + "Home" => CreateHomeTabbed(), + _ => CreatePlainPage(section) + }; + } + + private static Control CreatePlainPage(string section) + { + var (subtitle, icon) = section switch + { + "Explore" => ("Discover new content.", "📍"), + "Favorites" => ("Items you've saved.", "❤"), + _ => (string.Empty, string.Empty) + }; + + return new StackPanel + { + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + Spacing = 8, + Children = + { + new TextBlock + { + Text = section, + FontSize = 22, + FontWeight = FontWeight.SemiBold, + HorizontalAlignment = HorizontalAlignment.Center + }, + new TextBlock + { + Text = subtitle, + FontSize = 13, + Opacity = 0.7, + TextWrapping = TextWrapping.Wrap, + TextAlignment = TextAlignment.Center, + MaxWidth = 300 + } + } + }; + } + + private static TabbedPage CreateHomeTabbed() => new() + { + TabPlacement = TabPlacement.Bottom, + Pages = new[] + { + new ContentPage + { + Header = "Featured", + Content = new TextBlock + { + Text = "Featured content", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + FontSize = 18, + Opacity = 0.7 + } + }, + new ContentPage + { + Header = "Recent", + Content = new TextBlock + { + Text = "Recent activity", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + FontSize = 18, + Opacity = 0.7 + } + }, + new ContentPage + { + Header = "Popular", + Content = new TextBlock + { + Text = "Popular right now", + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + FontSize = 18, + Opacity = 0.7 + } + } + } + }; + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithNavigationPage.xaml b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithNavigationPage.xaml new file mode 100644 index 00000000..22878ec3 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithNavigationPage.xaml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithNavigationPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithNavigationPage.xaml.cs new file mode 100644 index 00000000..2b7a5a3a --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TabbedPage/TabbedPageWithNavigationPage.xaml.cs @@ -0,0 +1,100 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Layout; +using Avalonia.Media; + +namespace ControlCatalog.Pages +{ + public partial class TabbedPageWithNavigationPage : UserControl + { + private bool _initialized; + + public TabbedPageWithNavigationPage() + { + InitializeComponent(); + Loaded += OnLoaded; + } + + private async void OnLoaded(object? sender, RoutedEventArgs e) + { + if (_initialized) + return; + + _initialized = true; + await BrowseNav.PushAsync(CreateListPage("Browse", "Items", BrowseNav), null); + await SearchNav.PushAsync(CreateListPage("Search", "Results", SearchNav), null); + await AccountNav.PushAsync(CreateListPage("Account", "Options", AccountNav), null); + } + + private static ContentPage CreateListPage(string tabName, string listTitle, NavigationPage nav) + { + var list = new ListBox + { + Margin = new Avalonia.Thickness(8), + Items = + { + $"{listTitle} item 1", + $"{listTitle} item 2", + $"{listTitle} item 3", + $"{listTitle} item 4", + $"{listTitle} item 5" + } + }; + + list.SelectionChanged += async (_, args) => + { + if (args.AddedItems.Count == 0) return; + var item = args.AddedItems[0]?.ToString() ?? string.Empty; + list.SelectedItem = null; + + var detail = new ContentPage + { + Header = item, + Content = new StackPanel + { + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + Spacing = 8, + Children = + { + new TextBlock + { + Text = item, + FontSize = 20, + FontWeight = FontWeight.SemiBold, + HorizontalAlignment = HorizontalAlignment.Center + }, + new TextBlock + { + Text = $"Detail view for \"{item}\" in the {tabName} tab.", + FontSize = 13, + Opacity = 0.7, + TextWrapping = TextWrapping.Wrap, + TextAlignment = Avalonia.Media.TextAlignment.Center, + MaxWidth = 280 + } + } + } + }; + + await nav.PushAsync(detail, nav.PageTransition); + }; + + var page = new ContentPage + { + Header = tabName, + Content = list, + HorizontalContentAlignment = HorizontalAlignment.Stretch, + VerticalContentAlignment = VerticalAlignment.Stretch + }; + NavigationPage.SetHasNavigationBar(page, false); + return page; + } + + private void OnPlacementChanged(object? sender, SelectionChangedEventArgs e) + { + if (DemoTabs == null) return; + DemoTabs.TabPlacement = PlacementCombo.SelectedIndex == 0 ? TabPlacement.Top : TabPlacement.Bottom; + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TableViewPage.axaml b/samples/AvaloniaControlCatalog/Pages/TableViewPage.axaml new file mode 100644 index 00000000..bd730eb6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TableViewPage.axaml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/TableViewPage.axaml.cs b/samples/AvaloniaControlCatalog/Pages/TableViewPage.axaml.cs new file mode 100644 index 00000000..af753dd7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TableViewPage.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages; + +public partial class TableViewPage : ContentPage +{ + public TableViewPage() + { + InitializeComponent(); + DataContext = new TableViewPageViewModel(); + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TextBlockPage.xaml b/samples/AvaloniaControlCatalog/Pages/TextBlockPage.xaml new file mode 100644 index 00000000..3418cffe --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TextBlockPage.xaml @@ -0,0 +1,141 @@ + + + A control that can display text + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is a + TextBlock + with several + Span elements, + + using a variety of styles + . + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/TextBlockPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/TextBlockPage.xaml.cs new file mode 100644 index 00000000..d10d255a --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TextBlockPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class TextBlockPage : ContentPage + { + public TextBlockPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TextBoxPage.xaml b/samples/AvaloniaControlCatalog/Pages/TextBoxPage.xaml new file mode 100644 index 00000000..9b5a412f --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TextBoxPage.xaml @@ -0,0 +1,105 @@ + + + + + + + + + + Custom context flyout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/TextBoxPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/TextBoxPage.xaml.cs new file mode 100644 index 00000000..3c38a05c --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TextBoxPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class TextBoxPage : ContentPage + { + public TextBoxPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ThemePage.axaml b/samples/AvaloniaControlCatalog/Pages/ThemePage.axaml new file mode 100644 index 00000000..ccc5769f --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ThemePage.axaml @@ -0,0 +1,80 @@ + + + + + + + Black + + + White + + + #ffe5ea + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Nested ToolTips + + + + ToolTip replaced on the fly + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ToolTipPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ToolTipPage.xaml.cs new file mode 100644 index 00000000..e436407d --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ToolTipPage.xaml.cs @@ -0,0 +1,42 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives.PopupPositioning; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public partial class ToolTipPage : ContentPage + { + public ToolTipPage() + { + InitializeComponent(); + } + + private void ToolTipOpening(object? sender, CancelRoutedEventArgs args) + { + ((Control)args.Source!).SetValue(ToolTip.TipProperty, "New tip set from ToolTipOpening."); + } + + public void CustomPlacementCallback(CustomPopupPlacement placement) + { + var r = new Random().Next(); + + placement.Anchor = (r % 4) switch + { + 1 => PopupAnchor.Top, + 2 => PopupAnchor.Left, + 3 => PopupAnchor.Right, + _ => PopupAnchor.Bottom, + }; + placement.Gravity = (r % 4) switch + { + 1 => PopupGravity.Top, + 2 => PopupGravity.Left, + 3 => PopupGravity.Right, + _ => PopupGravity.Bottom, + }; + placement.Offset = new Point(r % 20, r % 20); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/TransitioningContentControlPage.axaml b/samples/AvaloniaControlCatalog/Pages/TransitioningContentControlPage.axaml new file mode 100644 index 00000000..5435ed27 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TransitioningContentControlPage.axaml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + 8 + + + + + The TransitioningContentControl control allows you to show a page transition whenever the Content changes. + + + + + + + + + + + + + + + + + + + + + + + + + Single + Multiple + Toggle + AlwaysSelected + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/TreeViewPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/TreeViewPage.xaml.cs new file mode 100644 index 00000000..d03590fe --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/TreeViewPage.xaml.cs @@ -0,0 +1,14 @@ +using Avalonia.Controls; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public partial class TreeViewPage : ContentPage + { + public TreeViewPage() + { + InitializeComponent(); + DataContext = new TreeViewPageViewModel(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/ViewboxPage.xaml b/samples/AvaloniaControlCatalog/Pages/ViewboxPage.xaml new file mode 100644 index 00000000..3848e405 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ViewboxPage.xaml @@ -0,0 +1,54 @@ + + + + + A control used to scale single child. + + + + + + + + + + + + + + + + + + + + + + Uniform + UniformToFill + Fill + None + + + + + + + + Both + DownOnly + UpOnly + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/ViewboxPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/ViewboxPage.xaml.cs new file mode 100644 index 00000000..f7b1ea3f --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/ViewboxPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class ViewboxPage : ContentPage + { + public ViewboxPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/WindowCustomizationsPage.xaml b/samples/AvaloniaControlCatalog/Pages/WindowCustomizationsPage.xaml new file mode 100644 index 00000000..5114dd16 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/WindowCustomizationsPage.xaml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/WindowCustomizationsPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/WindowCustomizationsPage.xaml.cs new file mode 100644 index 00000000..2ee8677e --- /dev/null +++ b/samples/AvaloniaControlCatalog/Pages/WindowCustomizationsPage.xaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace ControlCatalog.Pages +{ + public partial class WindowCustomizationsPage : ContentPage + { + public WindowCustomizationsPage() + { + InitializeComponent(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/Pages/teapot.bin b/samples/AvaloniaControlCatalog/Pages/teapot.bin new file mode 100644 index 0000000000000000000000000000000000000000..589eeb912dca4141b6c4d55a767446fe3aad67cf GIT binary patch literal 15464 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 literal 0 HcmV?d00001 diff --git a/samples/AvaloniaControlCatalog/PlatformRenderInterface.cs b/samples/AvaloniaControlCatalog/PlatformRenderInterface.cs new file mode 100644 index 00000000..271678b8 --- /dev/null +++ b/samples/AvaloniaControlCatalog/PlatformRenderInterface.cs @@ -0,0 +1,315 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System; +using System.IO; +using System.Numerics; +using Avalonia; +using Avalonia.Media; +using Avalonia.Media.Fonts; +using Avalonia.Media.Imaging; +using Avalonia.Media.TextFormatting; +using Avalonia.Platform; +using Avalonia.Platform.Surfaces; +using SixLabors.Fonts; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.Drawing.Processing.Backends; +using SixLabors.ImageSharp.Drawing.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; +using AvaloniaGlyphRun = Avalonia.Media.GlyphRun; + +namespace AvaloniaControlCatalog; + +/// +/// Avalonia rendering platform implementation backed by ImageSharp.Drawing. +/// +internal sealed class PlatformRenderInterface : IPlatformRenderInterface +{ + /// + /// Registers the ImageSharp.Drawing platform renderer and font manager with Avalonia. + /// + private static readonly object WebGpuLogGate = new(); + + private static readonly string WebGpuLogPath = + System.IO.Path.Combine(AppContext.BaseDirectory, "webgpu-errors.log"); + + public static void Initialize() + { + // Surface native WebGPU validation/device-lost errors instead of letting them be + // silently swallowed. Without this the GPU backend can corrupt the surface or lose + // the device with no diagnostic, which reads as a mysterious hang or blanked output. + // + // This is a WinExe with no attached console, and the callback can fire from a native + // thread immediately before the process dies, so the only reliable sink is a file that + // is opened, written, flushed, and closed on every call. Debug.WriteLine is kept for the + // case where a debugger is attached (run in Debug so the wgpu validation layer is active). + File.WriteAllText(WebGpuLogPath, $"WebGPU error log started {DateTime.Now:O}{Environment.NewLine}"); + WebGPUEnvironment.UncapturedError = static (type, message) => AppendWebGpuLog($"[WebGPU {type}] {message}"); + + // The uncaptured-error callback above does NOT catch the validation error that loses the + // device (the binding panics on the failing wgpuQueueSubmit before it routes through us). + // wgpu's own log callback DOES surface that root error, so wire it at Warn level. + try + { + WebGPUEnvironment.EnableNativeLogging(AppendWebGpuLog); + } + catch (Exception ex) + { + AppendWebGpuLog($"failed to wire wgpu log callback: {ex.Message}"); + } + + PlatformRenderInterface platform = new(); + + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(platform) + .Bind().ToConstant(new FontManagerImpl()); + } + + private static void AppendWebGpuLog(string message) + { + string line = $"{DateTime.Now:HH:mm:ss.fff} {message}"; + System.Diagnostics.Debug.WriteLine(line); + try + { + lock (WebGpuLogGate) + { + File.AppendAllText(WebGpuLogPath, line + Environment.NewLine); + } + } + catch (IOException) + { + // Never let a logging failure escape the native callback boundary. + } + } + + /// + public bool SupportsIndividualRoundRects => true; + + /// + public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; + + /// + public PixelFormat DefaultPixelFormat => PixelFormat.Bgra8888; + + /// + public bool SupportsRegions => true; + + /// + public IGeometryImpl CreateEllipseGeometry(Rect rect) + => new EllipseGeometryImpl(rect); + + /// + public IGeometryImpl CreateLineGeometry(AvaloniaPoint p1, AvaloniaPoint p2) + => new LineGeometryImpl(p1, p2); + + /// + public IGeometryImpl CreateRectangleGeometry(Rect rect) => new RectangleGeometryImpl(rect); + + /// + public IStreamGeometryImpl CreateStreamGeometry() => new StreamGeometryImpl(); + + /// + public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) + => new GeometryGroupImpl(fillRule, children); + + /// + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, IGeometryImpl g1, IGeometryImpl g2) + => CombinedGeometryImpl.Create(combineMode, (GeometryImpl)g1, (GeometryImpl)g2); + + /// + public IGeometryImpl BuildGlyphRunGeometry(AvaloniaGlyphRun glyphRun) + { + SixLabors.Fonts.Font font = ((PlatformTypeface)glyphRun.GlyphTypeface.PlatformTypeface).CreateFont((float)glyphRun.FontRenderingEmSize); + GlyphRunImpl glyphRunImpl = new(glyphRun.GlyphTypeface, glyphRun.FontRenderingEmSize, glyphRun.GlyphInfos, glyphRun.BaselineOrigin, font); + RichGlyphOptions options = new() + { + Font = font, + HintingMode = HintingMode.None, + + // Avalonia positions glyph runs by their baseline origins, so the run must be + // anchored by the alphabetic baseline rather than the line-box default. + TextBaseline = TextBaseline.Alphabetic, + ColorFontSupport = ColorFontSupport.ColrV0 | ColorFontSupport.ColrV1 | ColorFontSupport.Svg + }; + + ComplexPolygon glyphPath = new(TextBuilder.GeneratePaths(glyphRunImpl.GlyphIds.Span, glyphRunImpl.GlyphOrigins.Span, options)); + + return new StreamGeometryImpl(glyphPath, glyphPath, IntersectionRule.NonZero); + } + + /// + public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, AvaloniaVector dpi) + => new RenderTargetBitmapImpl(size, dpi); + + /// + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, AvaloniaVector dpi, PixelFormat format, AlphaFormat alphaFormat) + => new WriteableBitmapImpl(size, dpi, format, alphaFormat); + + /// + public IBitmapImpl LoadBitmap(string fileName) + { + using FileStream stream = File.OpenRead(fileName); + + return this.LoadBitmap(stream); + } + + /// + public IBitmapImpl LoadBitmap(Stream stream) + => new ImmutableBitmapImpl(stream); + + /// + public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + => new WriteableBitmapImpl(stream, width, horizontal: true, interpolationMode); + + /// + public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + => new WriteableBitmapImpl(stream, height, horizontal: false, interpolationMode); + + /// + public IWriteableBitmapImpl LoadWriteableBitmap(string fileName) + { + using FileStream stream = File.OpenRead(fileName); + + return this.LoadWriteableBitmap(stream); + } + + /// + public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream) + => new WriteableBitmapImpl(stream); + + /// + public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + => new ImmutableBitmapImpl(stream, width, horizontal: true, interpolationMode); + + /// + public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + => new ImmutableBitmapImpl(stream, height, horizontal: false, interpolationMode); + + /// + public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) + { + ImmutableBitmapImpl? bitmap = bitmapImpl as ImmutableBitmapImpl; + + if (bitmap is not null) + { + return new ImmutableBitmapImpl(bitmap, destinationSize, interpolationMode); + } + + throw new InvalidOperationException("Invalid source bitmap type."); + } + + /// + public unsafe IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, AvaloniaVector dpi, int stride) + => new ImmutableBitmapImpl(size, dpi, stride, format, alphaFormat, data); + + /// + public IGlyphRunImpl CreateGlyphRun(GlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, AvaloniaPoint baselineOrigin) + { + // Resolve the exact SixLabors face this run was shaped against (via the public PlatformTypeface) + // so the glyph ids line up - mirroring how the Skia backend carries the SKTypeface. + SixLabors.Fonts.Font font = ((PlatformTypeface)glyphTypeface.PlatformTypeface).CreateFont((float)fontRenderingEmSize); + + return new GlyphRunImpl(glyphTypeface, fontRenderingEmSize, glyphInfos, baselineOrigin, font); + } + + /// + public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext? graphicsApiContext) + => new PlatformRenderInterfaceContext(graphicsApiContext is not null); + + /// + public bool IsSupportedBitmapPixelFormat(PixelFormat format) + => format == PixelFormats.Rgb565 + || format == PixelFormats.Bgra8888 + || format == PixelFormats.Rgba8888; + + /// + public IPlatformRenderInterfaceRegion CreateRegion() => new RegionImpl(); +} + +/// +/// ImageSharp.Drawing render-context implementation used by Avalonia render targets and layers. +/// +internal sealed class PlatformRenderInterfaceContext : IPlatformRenderInterfaceContext +{ + private readonly bool hasPlatformGraphicsContext; + private readonly WebGPUSurfaceSession webGpuSurfaceSession = new(); + private bool useWebGpuBackend; + + /// + /// Initializes a new backend context. + /// + /// Whether Avalonia created a platform graphics context for this renderer. + public PlatformRenderInterfaceContext(bool hasPlatformGraphicsContext) + => this.hasPlatformGraphicsContext = hasPlatformGraphicsContext; + + /// + public bool IsLost => this.webGpuSurfaceSession.IsLost; + + /// + public IReadOnlyDictionary PublicFeatures { get; } = new Dictionary(); + + /// + public PixelSize? MaxOffscreenRenderTargetPixelSize => null; + + /// + public IRenderTarget CreateRenderTarget(IEnumerable surfaces) + { + bool useWebGpu = DrawingContextImpl.BackendMode == DrawingBackendMode.WebGpu + || (DrawingContextImpl.BackendMode == DrawingBackendMode.Auto && this.hasPlatformGraphicsContext); + + if (useWebGpu) + { + foreach (IPlatformRenderSurface surface in surfaces) + { + if (surface is INativePlatformHandleSurface nativeSurface && + WebGPURenderTargetImpl.TryCreate(this.webGpuSurfaceSession, nativeSurface, out WebGPURenderTargetImpl? renderTarget)) + { + this.useWebGpuBackend = true; + return renderTarget; + } + } + + if (DrawingContextImpl.BackendMode == DrawingBackendMode.WebGpu) + { + throw new NotSupportedException("WebGPU rendering requires an Avalonia native surface with enough platform handles to create a WebGPU surface."); + } + } + + foreach (IPlatformRenderSurface surface in surfaces) + { + if (surface is IFramebufferPlatformSurface framebufferSurface) + { + this.useWebGpuBackend = false; + return new RenderTargetImpl(framebufferSurface.CreateFramebufferRenderTarget()); + } + } + + throw new NotSupportedException("ImageSharp.Drawing sample renderer requires a framebuffer surface."); + } + + /// + public IDrawingContextLayerImpl CreateOffscreenRenderTarget(PixelSize pixelSize, AvaloniaVector scaling, bool enableTextAntialiasing) + { + AvaloniaVector dpi = scaling * 96; + if (this.useWebGpuBackend) + { + return new WebGPULayerImpl(this.webGpuSurfaceSession, pixelSize, dpi); + } + + return new RenderTargetBitmapImpl(pixelSize, dpi); + } + + /// + public object? TryGetFeature(Type featureType) => null; + + /// + public void Dispose() + { + this.webGpuSurfaceSession.Dispose(); + } + +} diff --git a/samples/AvaloniaControlCatalog/Program.cs b/samples/AvaloniaControlCatalog/Program.cs new file mode 100644 index 00000000..6510d7da --- /dev/null +++ b/samples/AvaloniaControlCatalog/Program.cs @@ -0,0 +1,56 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System; +using Avalonia; +using ControlCatalog; +using SixLabors.ImageSharp; + +namespace AvaloniaControlCatalog; + +internal static class Program +{ + [STAThread] + public static void Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + + private static AppBuilder BuildAvaloniaApp() + { + DrawingContextImpl.BackendMode = Environment.GetEnvironmentVariable("IMAGESHARP_DRAWING_BACKEND")?.ToLowerInvariant() switch + { + "cpu" => DrawingBackendMode.Cpu, + "webgpu" => DrawingBackendMode.WebGpu, + _ => DrawingBackendMode.Auto + }; + + // DrawingContextImpl.BackendMode = DrawingBackendMode.Cpu; + + if (int.TryParse(Environment.GetEnvironmentVariable("IMAGESHARP_DRAWING_MAX_PARALLELISM"), out int maxParallelism)) + { + Configuration.Default.MaxDegreeOfParallelism = maxParallelism; + } + + AppBuilder builder = AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont(); + + if (DrawingContextImpl.BackendMode == DrawingBackendMode.Cpu) + { + // This sample-only override must run before Avalonia creates the backend context; + // otherwise Win32 creates a platform graphics context and the top-level surfaces + // represent the GPU/composited path instead of the framebuffer path we need to test. + builder = builder.With(new Win32PlatformOptions + { + RenderingMode = [Win32RenderingMode.Software] + }); + } + + // Temporary backend switch for visual comparison: ISD_BACKEND=skia uses Avalonia's default (Skia). + if (!string.Equals(Environment.GetEnvironmentVariable("ISD_BACKEND"), "skia", StringComparison.OrdinalIgnoreCase)) + { + builder = builder.UseImageSharpDrawing(); + } + + return builder; + } +} diff --git a/samples/AvaloniaControlCatalog/RectangleGeometryImpl.cs b/samples/AvaloniaControlCatalog/RectangleGeometryImpl.cs new file mode 100644 index 00000000..bf87fab9 --- /dev/null +++ b/samples/AvaloniaControlCatalog/RectangleGeometryImpl.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Avalonia; +using SixLabors.ImageSharp.Drawing; + +namespace AvaloniaControlCatalog; + +/// +/// Avalonia rectangle geometry implementation backed by an ImageSharp rectangle path. +/// +internal sealed class RectangleGeometryImpl : GeometryImpl +{ + /// + /// Initializes a new rectangle geometry. + /// + /// The rectangle bounds. + public RectangleGeometryImpl(Rect rect) + : this(new RectanglePolygon(rect.ToRectangleF())) + { + } + + private RectangleGeometryImpl(IPath path) + : base(path, path, IntersectionRule.EvenOdd) + { + } +} diff --git a/samples/AvaloniaControlCatalog/RegionImpl.cs b/samples/AvaloniaControlCatalog/RegionImpl.cs new file mode 100644 index 00000000..1f32d0c4 --- /dev/null +++ b/samples/AvaloniaControlCatalog/RegionImpl.cs @@ -0,0 +1,106 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Avalonia; +using Avalonia.Platform; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; + +namespace AvaloniaControlCatalog; + +/// +/// ImageSharp-backed region implementation for Avalonia clipping and hit testing. +/// +internal sealed class RegionImpl : IPlatformRenderInterfaceRegion +{ + private readonly Region region = new(); + private readonly List rects = []; + private bool rectsValid = true; + + /// + public bool IsEmpty => this.region.IsEmpty; + + /// + public LtrbPixelRect Bounds + { + get + { + Rectangle bounds = this.region.Bounds; + return new() + { + Left = bounds.Left, + Top = bounds.Top, + Right = bounds.Right, + Bottom = bounds.Bottom + }; + } + } + + /// + public IList Rects + { + get + { + if (this.rectsValid) + { + return this.rects; + } + + this.rects.Clear(); + IReadOnlyList rectangles = this.region.Rectangles; + for (int i = 0; i < rectangles.Count; i++) + { + Rectangle rectangle = rectangles[i]; + this.rects.Add(new LtrbPixelRect + { + Left = rectangle.Left, + Top = rectangle.Top, + Right = rectangle.Right, + Bottom = rectangle.Bottom + }); + } + + this.rectsValid = true; + return this.rects; + } + } + + /// + public void AddRect(LtrbPixelRect rect) + { + this.region.Add(Rectangle.FromLTRB(rect.Left, rect.Top, rect.Right, rect.Bottom)); + this.rectsValid = false; + } + + /// + public void Reset() + { + this.region.Clear(); + this.rects.Clear(); + this.rectsValid = true; + } + + /// + public bool Intersects(LtrbRect rect) + => this.region.Intersects( + Rectangle.FromLTRB( + (int)rect.Left, + (int)rect.Top, + (int)Math.Ceiling(rect.Right), + (int)Math.Ceiling(rect.Bottom))); + + /// + public bool Contains(AvaloniaPoint pt) + => this.region.Contains((int)pt.X, (int)pt.Y); + + /// + /// Converts the current region to an ImageSharp path. + /// + /// The ImageSharp path representing the region. + public IPath ToPath() => this.region.ToPath(); + + /// + public void Dispose() + { + } +} diff --git a/samples/AvaloniaControlCatalog/RenderTargetBitmapImpl.cs b/samples/AvaloniaControlCatalog/RenderTargetBitmapImpl.cs new file mode 100644 index 00000000..adb350fa --- /dev/null +++ b/samples/AvaloniaControlCatalog/RenderTargetBitmapImpl.cs @@ -0,0 +1,106 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Avalonia; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using Avalonia.Platform.Surfaces; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Transforms; + +namespace AvaloniaControlCatalog; + +/// +/// ImageSharp-backed render-target bitmap implementation for offscreen layers and render-target bitmaps. +/// +internal sealed class RenderTargetBitmapImpl : IRenderTargetBitmapImpl, IDrawingContextLayerImpl, IFramebufferPlatformSurface, IDrawableBitmapImpl +{ + private readonly WriteableBitmapImpl bitmap; + private readonly RenderTargetImpl renderTarget; + + /// + /// Initializes a new render-target bitmap. + /// + /// The bitmap size in pixels. + /// The bitmap DPI. + public RenderTargetBitmapImpl(PixelSize size, Vector dpi) + { + this.bitmap = new WriteableBitmapImpl(size, dpi); + this.renderTarget = new RenderTargetImpl(this.CreateFramebufferRenderTarget()); + } + + /// + /// Gets the ImageSharp pixels drawn for this render target. + /// + public Image Image => this.bitmap.Image; + + /// + public Vector Dpi => this.bitmap.Dpi; + + /// + public PixelSize PixelSize => this.bitmap.PixelSize; + + /// + public int Version => this.bitmap.Version; + + /// + public PixelFormat? Format => this.bitmap.Format; + + /// + public AlphaFormat? AlphaFormat => this.bitmap.AlphaFormat; + + /// + public bool CanBlit => true; + + /// + public bool IsCorrupted => false; + + /// + public void Blit(IDrawingContextImpl context) + { + DrawingContextImpl drawingContext = (DrawingContextImpl)context; + drawingContext.Blit(this.Image, this.PixelSize); + } + + /// + public void Draw( + DrawingContextImpl context, + Rectangle sourceRect, + RectangleF destinationRect, + IResampler sampler, + float opacity, + PixelAlphaCompositionMode alphaCompositionMode) + => context.DrawImage(this.Image, sourceRect, destinationRect, sampler, opacity, alphaCompositionMode); + + /// + public IDrawingContextImpl CreateDrawingContext() + => this.renderTarget.CreateDrawingContext( + new IRenderTarget.RenderTargetSceneInfo(this.PixelSize, this.Dpi.X / 96D, Avalonia.Rendering.Composition.CompositionTransparencyLevel.None), + out _); + + /// + public ILockedFramebuffer Lock() => this.bitmap.Lock(); + + /// + public IFramebufferRenderTarget CreateFramebufferRenderTarget() + => new FuncFramebufferRenderTarget( + (IRenderTarget.RenderTargetSceneInfo _, out FramebufferLockProperties properties) => + { + // Dirty-rect rendering clears and redraws only the active clip, so pixels outside + // that clip must remain from the previous lock until the layer is copied out. + properties = new FramebufferLockProperties(PreviousFrameIsRetained: true); + return this.Lock(); + }, + retainsFrameContents: true); + + /// + public void Save(Stream stream, BitmapEncoderOptions options) => this.bitmap.Save(stream, options); + + /// + public void Dispose() + { + this.renderTarget.Dispose(); + this.bitmap.Dispose(); + } +} diff --git a/samples/AvaloniaControlCatalog/RenderTargetImpl.cs b/samples/AvaloniaControlCatalog/RenderTargetImpl.cs new file mode 100644 index 00000000..ea0449e7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/RenderTargetImpl.cs @@ -0,0 +1,58 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Avalonia.Platform; +using Avalonia.Platform.Surfaces; + +namespace AvaloniaControlCatalog; + +/// +/// Framebuffer render target for the ImageSharp.Drawing sample renderer. +/// +internal sealed class RenderTargetImpl : IRenderTarget +{ + private IFramebufferRenderTarget? framebufferRenderTarget; + + /// + /// Initializes a new framebuffer render target wrapper. + /// + /// The Avalonia framebuffer render target. + public RenderTargetImpl(IFramebufferRenderTarget framebufferRenderTarget) + => this.framebufferRenderTarget = framebufferRenderTarget; + + /// + public RenderTargetProperties Properties => new() + { + IsSuitableForDirectRendering = true, + RetainsPreviousFrameContents = this.framebufferRenderTarget?.RetainsFrameContents == true + }; + + /// + public PlatformRenderTargetState PlatformRenderTargetState + => this.framebufferRenderTarget?.State ?? PlatformRenderTargetState.Disposed; + + /// + public IDrawingContextImpl CreateDrawingContext(IRenderTarget.RenderTargetSceneInfo sceneInfo, out RenderTargetDrawingContextProperties properties) + { + if (this.framebufferRenderTarget is null) + { + throw new ObjectDisposedException(nameof(RenderTargetImpl)); + } + + ILockedFramebuffer framebuffer = this.framebufferRenderTarget.Lock(sceneInfo, out FramebufferLockProperties lockProperties); + + properties = new RenderTargetDrawingContextProperties + { + PreviousFrameIsRetained = lockProperties.PreviousFrameIsRetained + }; + + return new DrawingContextImpl(framebuffer); + } + + /// + public void Dispose() + { + this.framebufferRenderTarget?.Dispose(); + this.framebufferRenderTarget = null; + } +} diff --git a/samples/AvaloniaControlCatalog/ScrollPage.xaml b/samples/AvaloniaControlCatalog/ScrollPage.xaml new file mode 100644 index 00000000..6d5ecb81 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ScrollPage.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/StreamGeometryImpl.cs b/samples/AvaloniaControlCatalog/StreamGeometryImpl.cs new file mode 100644 index 00000000..64de0db7 --- /dev/null +++ b/samples/AvaloniaControlCatalog/StreamGeometryImpl.cs @@ -0,0 +1,216 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Avalonia; +using Avalonia.Media; +using Avalonia.Platform; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; + +namespace AvaloniaControlCatalog; + +/// +/// Mutable stream geometry implementation backed by ImageSharp path builders. +/// +internal sealed class StreamGeometryImpl : GeometryImpl, IStreamGeometryImpl +{ + private IPath strokePath = EmptyPath.OpenPath; + private IPath? fillPath = EmptyPath.OpenPath; + + /// + /// Initializes an empty stream geometry. + /// + public StreamGeometryImpl() + : base(EmptyPath.OpenPath, EmptyPath.OpenPath, IntersectionRule.EvenOdd) + { + } + + internal StreamGeometryImpl(IPath strokePath, IPath? fillPath, IntersectionRule fillRule) + : base(strokePath, fillPath, fillRule) + { + this.strokePath = strokePath; + this.fillPath = fillPath; + } + + /// + public IStreamGeometryImpl Clone() => new StreamGeometryImpl(this.strokePath, this.fillPath, this.FillRule); + + /// + public IStreamGeometryContextImpl Open() => new StreamGeometryImplContext(this); + + private void SetStreamPaths(IPath strokePath, IPath? fillPath, IntersectionRule fillRule) + { + this.strokePath = strokePath; + this.fillPath = fillPath; + this.SetPaths(strokePath, fillPath, fillRule); + } + + /// + /// Stream geometry context that records Avalonia path commands into ImageSharp path builders. + /// + private sealed class StreamGeometryImplContext : IStreamGeometryContextImpl + { + private readonly StreamGeometryImpl owner; + private readonly PathBuilder strokeBuilder = new(); + private readonly PathBuilder fillBuilder = new(); + private PointF startPoint; + private bool isFilled; + private bool isFigureBroken; + private bool hasFill; + private IntersectionRule fillRule = IntersectionRule.EvenOdd; + + /// + /// Initializes a new stream geometry context. + /// + /// The stream geometry updated when the context is disposed. + public StreamGeometryImplContext(StreamGeometryImpl owner) + => this.owner = owner; + + /// + public void ArcTo(AvaloniaPoint point, AvaloniaSize size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection, bool isStroked = true) + { + PointF endPoint = point.ToPointF(); + bool sweep = sweepDirection == SweepDirection.Clockwise; + + // Avalonia's rotationAngle is nominally radians, but the Skia backend passes the raw value + // straight to SKPath.ArcTo, whose x-axis rotation is degrees - so callers (e.g. the rounded + // rectangle builder, which passes PiOver2) are effectively calibrated against a degrees-valued + // axis rotation. Match Skia by forwarding the value unchanged rather than converting to degrees. + // See https://github.com/AvaloniaUI/Avalonia/issues/21445. + float rotation = (float)rotationAngle; + + if (isStroked) + { + this.strokeBuilder.ArcTo((float)size.Width, (float)size.Height, rotation, isLargeArc, sweep, endPoint); + } + else + { + this.BreakStrokeFigure(endPoint); + } + + if (this.isFilled) + { + this.fillBuilder.ArcTo((float)size.Width, (float)size.Height, rotation, isLargeArc, sweep, endPoint); + } + } + + /// + public void BeginFigure(AvaloniaPoint startPoint, bool isFilled = true) + { + this.startPoint = startPoint.ToPointF(); + this.isFilled = isFilled; + this.isFigureBroken = false; + this.strokeBuilder.MoveTo(this.startPoint); + + if (isFilled) + { + this.hasFill = true; + this.fillBuilder.MoveTo(this.startPoint); + } + } + + /// + public void CubicBezierTo(AvaloniaPoint controlPoint1, AvaloniaPoint controlPoint2, AvaloniaPoint endPoint, bool isStroked = true) + { + PointF imageEndPoint = endPoint.ToPointF(); + + if (isStroked) + { + this.strokeBuilder.CubicBezierTo( + controlPoint1.ToPointF(), + controlPoint2.ToPointF(), + imageEndPoint); + } + else + { + this.BreakStrokeFigure(imageEndPoint); + } + + if (this.isFilled) + { + this.fillBuilder.CubicBezierTo( + controlPoint1.ToPointF(), + controlPoint2.ToPointF(), + imageEndPoint); + } + } + + /// + public void QuadraticBezierTo(AvaloniaPoint controlPoint, AvaloniaPoint endPoint, bool isStroked = true) + { + PointF imageEndPoint = endPoint.ToPointF(); + + if (isStroked) + { + this.strokeBuilder.QuadraticBezierTo(controlPoint.ToPointF(), imageEndPoint); + } + else + { + this.BreakStrokeFigure(imageEndPoint); + } + + if (this.isFilled) + { + this.fillBuilder.QuadraticBezierTo(controlPoint.ToPointF(), imageEndPoint); + } + } + + /// + public void LineTo(AvaloniaPoint point, bool isStroked = true) + { + PointF imagePoint = point.ToPointF(); + + if (isStroked) + { + this.strokeBuilder.LineTo(imagePoint); + } + else + { + this.BreakStrokeFigure(imagePoint); + } + + if (this.isFilled) + { + this.fillBuilder.LineTo(imagePoint); + } + } + + /// + public void EndFigure(bool isClosed) + { + if (isClosed) + { + if (this.isFigureBroken) + { + this.strokeBuilder.LineTo(this.startPoint); + this.isFigureBroken = false; + } + else + { + this.strokeBuilder.CloseFigure(); + } + + if (this.isFilled) + { + this.fillBuilder.CloseFigure(); + } + } + } + + /// + public void SetFillRule(FillRule fillRule) + { + this.fillRule = fillRule.ToIntersectionRule(); + } + + /// + public void Dispose() + => this.owner.SetStreamPaths(this.strokeBuilder.Build(), this.hasFill ? this.fillBuilder.Build() : null, this.fillRule); + + private void BreakStrokeFigure(PointF point) + { + this.isFigureBroken = true; + this.strokeBuilder.MoveTo(point); + } + } +} diff --git a/samples/AvaloniaControlCatalog/TextShaperImpl.cs b/samples/AvaloniaControlCatalog/TextShaperImpl.cs new file mode 100644 index 00000000..5a945225 --- /dev/null +++ b/samples/AvaloniaControlCatalog/TextShaperImpl.cs @@ -0,0 +1,185 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Collections.Concurrent; +using System.Globalization; +using Avalonia; +using Avalonia.Media; +using Avalonia.Media.TextFormatting; +using Avalonia.Media.TextFormatting.Unicode; +using Avalonia.Platform; +using SixLaborsFont = SixLabors.Fonts.Font; +using SixLaborsShapedGlyph = SixLabors.Fonts.ShapedGlyph; +using SixLaborsTag = SixLabors.Fonts.Tables.AdvancedTypographic.Tag; +using SixLaborsTextDirection = SixLabors.Fonts.TextDirection; +using SixLaborsTextShaper = SixLabors.Fonts.TextShaper; +using SixLaborsTextShapingBuffer = SixLabors.Fonts.TextShapingBuffer; + +namespace AvaloniaControlCatalog; + +/// +/// Avalonia text shaper implementation backed by SixLabors.Fonts. +/// +internal sealed class TextShaperImpl : ITextShaperImpl +{ + [ThreadStatic] + private static SixLaborsTextShapingBuffer? shapingBuffer; + + /// + public ShapedBuffer ShapeText(ReadOnlyMemory text, TextShaperOptions options) + { + if (text.Length == 0) + { + return new ShapedBuffer(text, 0, options.GlyphTypeface, options.FontRenderingEmSize, options.BidiLevel); + } + + GlyphTypeface glyphTypeface = options.GlyphTypeface; + + if (glyphTypeface.TextShaperTypeface is not TextShaperTypeface shaperTypeface) + { + throw new NotSupportedException("The provided GlyphTypeface is not supported by this text shaper."); + } + + ReadOnlySpan textSpan = text.Span; + + // A trailing break character must keep its cluster for caret navigation while + // rendering as nothing, and a trailing carriage return and line feed pair + // shares one cluster anchored at the carriage return. + int trailingBreakStart = -1; + if (new Codepoint(textSpan[^1]).IsBreakChar) + { + trailingBreakStart = textSpan.Length > 1 && textSpan[^2] == '\r' && textSpan[^1] == '\n' + ? textSpan.Length - 2 + : textSpan.Length - 1; + } + + SixLaborsTextShapingBuffer buffer = shapingBuffer ??= new SixLaborsTextShapingBuffer(); + buffer.Add(textSpan); + buffer.TextDirection = (options.BidiLevel & 1) == 0 + ? SixLaborsTextDirection.LeftToRight + : SixLaborsTextDirection.RightToLeft; + buffer.Language = options.Culture ?? CultureInfo.CurrentCulture; + + SixLaborsFont font = shaperTypeface.GetFont((float)options.FontRenderingEmSize); + SixLaborsTag[] features = GetFeatures(options); + + if (features.Length > 0) + { + SixLaborsTextShaper.ShapeRun(font, buffer, features); + } + else + { + SixLaborsTextShaper.ShapeRun(font, buffer); + } + + int bufferLength = buffer.Count; + ShapedBuffer shapedBuffer = new(text, bufferLength, glyphTypeface, options.FontRenderingEmSize, options.BidiLevel); + + ushort invisibleGlyph = trailingBreakStart >= 0 ? glyphTypeface.CharacterToGlyphMap[' '] : (ushort)0; + + for (int i = 0; i < bufferLength; i++) + { + ref readonly SixLaborsShapedGlyph shapedGlyph = ref buffer[i]; + + ushort glyphIndex = shapedGlyph.GlyphId; + int glyphCluster = shapedGlyph.StringIndex; + double glyphAdvance = shapedGlyph.AdvanceWidth + options.LetterSpacing; + + // Shaping offsets are Y-up while Avalonia positions glyphs Y-down. + Vector glyphOffset = new(shapedGlyph.Offset.X, -shapedGlyph.Offset.Y); + + if (trailingBreakStart >= 0 && glyphCluster >= trailingBreakStart) + { + glyphIndex = invisibleGlyph; + glyphCluster = trailingBreakStart; + glyphAdvance = options.LetterSpacing; + glyphOffset = default; + } + + shapedBuffer[i] = new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset); + } + + return shapedBuffer; + } + + /// + public ITextShaperTypeface CreateTypeface(GlyphTypeface glyphTypeface) + { + if (glyphTypeface.PlatformTypeface is not PlatformTypeface platformTypeface) + { + throw new NotSupportedException("The provided GlyphTypeface is not supported by this text shaper."); + } + + return new TextShaperTypeface(platformTypeface); + } + + /// + /// Maps the requested font features to shaping feature tags. + /// + /// The text shaper options. + /// The feature tags to turn on. + private static SixLaborsTag[] GetFeatures(TextShaperOptions options) + { + if (options.FontFeatures is null || options.FontFeatures.Count == 0) + { + return []; + } + + // Shaping accepts whole-run feature enablement only, so disabled features and + // sub-run feature ranges cannot be expressed and are not forwarded. + int enabled = 0; + for (int i = 0; i < options.FontFeatures.Count; i++) + { + if (options.FontFeatures[i].Value != 0) + { + enabled++; + } + } + + if (enabled == 0) + { + return []; + } + + SixLaborsTag[] features = new SixLaborsTag[enabled]; + int index = 0; + for (int i = 0; i < options.FontFeatures.Count; i++) + { + FontFeature fontFeature = options.FontFeatures[i]; + if (fontFeature.Value != 0) + { + features[index++] = SixLaborsTag.Parse(fontFeature.Tag); + } + } + + return features; + } +} + +/// +/// Text shaper typeface caching the SixLabors fonts created for a platform typeface. +/// +internal sealed class TextShaperTypeface : ITextShaperTypeface +{ + private readonly PlatformTypeface platformTypeface; + private readonly ConcurrentDictionary fonts = new(); + + /// + /// Initializes a new instance of the class. + /// + /// The platform typeface to shape with. + public TextShaperTypeface(PlatformTypeface platformTypeface) => this.platformTypeface = platformTypeface; + + /// + /// Gets the SixLabors font for this typeface at the given size. + /// + /// The requested font size. + /// The font. + public SixLaborsFont GetFont(float size) + => this.fonts.GetOrAdd(size, static (requestedSize, typeface) => typeface.CreateFont(requestedSize), this.platformTypeface); + + /// + public void Dispose() + { + } +} diff --git a/samples/AvaloniaControlCatalog/TransformedGeometryImpl.cs b/samples/AvaloniaControlCatalog/TransformedGeometryImpl.cs new file mode 100644 index 00000000..0070e516 --- /dev/null +++ b/samples/AvaloniaControlCatalog/TransformedGeometryImpl.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using Avalonia; +using Avalonia.Media; +using Avalonia.Platform; +using SixLabors.ImageSharp.Drawing; + +namespace AvaloniaControlCatalog; + +/// +/// Geometry wrapper that preserves the original source geometry and applied transform. +/// +internal sealed class TransformedGeometryImpl : GeometryImpl, ITransformedGeometryImpl +{ + /// + /// Initializes a new transformed geometry wrapper. + /// + /// The untransformed source geometry. + /// The transform applied to . + public TransformedGeometryImpl(GeometryImpl source, Matrix transform) + : this(source, transform, transform.ToMatrix4x4()) + { + } + + private TransformedGeometryImpl(GeometryImpl source, Matrix transform, Matrix4x4 matrix) + : this(source, transform, matrix, source.StrokePath.Transform(matrix)) + { + } + + private TransformedGeometryImpl(GeometryImpl source, Matrix transform, Matrix4x4 matrix, IPath strokePath) + : base( + strokePath, + ReferenceEquals(source.StrokePath, source.FillPath) ? strokePath : source.FillPath?.Transform(matrix), + source.FillRule) + { + this.SourceGeometry = source; + this.Transform = transform; + } + + /// + public IGeometryImpl SourceGeometry { get; } + + /// + public Matrix Transform { get; } +} diff --git a/samples/AvaloniaControlCatalog/TransparentStyles.axaml b/samples/AvaloniaControlCatalog/TransparentStyles.axaml new file mode 100644 index 00000000..adf92446 --- /dev/null +++ b/samples/AvaloniaControlCatalog/TransparentStyles.axaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/TransparentStyles.axaml.cs b/samples/AvaloniaControlCatalog/TransparentStyles.axaml.cs new file mode 100644 index 00000000..85310f4b --- /dev/null +++ b/samples/AvaloniaControlCatalog/TransparentStyles.axaml.cs @@ -0,0 +1,5 @@ +using Avalonia.Styling; + +namespace ControlCatalog; + +public class TransparentStyles : Styles; diff --git a/samples/AvaloniaControlCatalog/ViewModels/ApplicationViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/ApplicationViewModel.cs new file mode 100644 index 00000000..509feca5 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/ApplicationViewModel.cs @@ -0,0 +1,26 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class ApplicationViewModel : ViewModelBase + { + public ApplicationViewModel() + { + ExitCommand = MiniCommand.Create(() => + { + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime) + { + lifetime.Shutdown(); + } + }); + + RestoreDefault = MiniCommand.Create(() => { }); + } + + public MiniCommand ExitCommand { get; } + + public MiniCommand RestoreDefault { get; } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/ComboBoxPageViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/ComboBoxPageViewModel.cs new file mode 100644 index 00000000..7d415e5d --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/ComboBoxPageViewModel.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using Avalonia.Controls; +using Avalonia.Controls.Selection; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class ComboBoxPageViewModel : ViewModelBase + { + private bool _wrapSelection; + private string _textValue = string.Empty; + private IdAndName? _selectedItem = null; + + public bool WrapSelection + { + get => _wrapSelection; + set => this.RaiseAndSetIfChanged(ref _wrapSelection, value); + } + + public string TextValue + { + get => _textValue; + set => this.RaiseAndSetIfChanged(ref _textValue, value); + } + + public IdAndName? SelectedItem + { + get => _selectedItem; + set => this.RaiseAndSetIfChanged(ref _selectedItem, value); + } + + public ObservableCollection Values { get; set; } = new ObservableCollection + { + new IdAndName(){ Id = "Id 1", Name = "Name 1", SearchText = "A" }, + new IdAndName(){ Id = "Id 2", Name = "Name 2", SearchText = "B" }, + new IdAndName(){ Id = "Id 3", Name = "Name 3", SearchText = "C" }, + new IdAndName(){ Id = "Id 4", Name = "Name 4", SearchText = "D" }, + new IdAndName(){ Id = "Id 5", Name = "Name 5", SearchText = "E" }, + }; + } + + public class IdAndName + { + public string? Id { get; set; } + public string? Name { get; set; } + public string? SearchText { get; set; } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/ContextPageViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/ContextPageViewModel.cs new file mode 100644 index 00000000..03156f69 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/ContextPageViewModel.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.VisualTree; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class ContextPageViewModel + { + public Control? View { get; set; } + public ContextPageViewModel() + { + OpenCommand = MiniCommand.CreateFromTask(Open); + SaveCommand = MiniCommand.Create(Save); + OpenRecentCommand = MiniCommand.Create(OpenRecent); + + MenuItems = new[] + { + new MenuItemViewModel { Header = "_Open...", Command = OpenCommand }, + new MenuItemViewModel { Header = "Save", Command = SaveCommand }, + new MenuItemViewModel { Header = "-" }, + new MenuItemViewModel + { + Header = "Recent", + Items = new[] + { + new MenuItemViewModel + { + Header = "File1.txt", + Command = OpenRecentCommand, + CommandParameter = @"c:\foo\File1.txt" + }, + new MenuItemViewModel + { + Header = "File2.txt", + Command = OpenRecentCommand, + CommandParameter = @"c:\foo\File2.txt" + }, + } + }, + }; + } + + public IReadOnlyList MenuItems { get; set; } + public MiniCommand OpenCommand { get; } + public MiniCommand SaveCommand { get; } + public MiniCommand OpenRecentCommand { get; } + + public async Task Open() + { + var window = TopLevel.GetTopLevel(View) as Window; + if (window == null) + return; + + var result = await window.StorageProvider.OpenFilePickerAsync(new Avalonia.Platform.Storage.FilePickerOpenOptions() { AllowMultiple = true }); + + foreach (var file in result) + { + System.Diagnostics.Debug.WriteLine($"Opened: {file.Name}"); + } + } + + public void Save() + { + System.Diagnostics.Debug.WriteLine("Save"); + } + + public void OpenRecent(string path) + { + System.Diagnostics.Debug.WriteLine($"Open recent: {path}"); + } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/CursorPageViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/CursorPageViewModel.cs new file mode 100644 index 00000000..dfcdd436 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/CursorPageViewModel.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia; +using Avalonia.Input; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class CursorPageViewModel : ViewModelBase + { + public CursorPageViewModel() + { + StandardCursors = Enum.GetValues(typeof(StandardCursorType)) + .Cast() + .Select(x => new StandardCursorModel(x)) + .ToList(); + + var s = AssetLoader.Open(new Uri("avares://ControlCatalog/Assets/avalonia-32.png")); + var bitmap = new Bitmap(s); + CustomCursor = new Cursor(bitmap, new PixelPoint(16, 16)); + } + + public IEnumerable StandardCursors { get; } + + public Cursor CustomCursor { get; } + } + + public class StandardCursorModel + { + public StandardCursorModel(StandardCursorType type) + { + Type = type; + Cursor = new Cursor(type); + } + + public StandardCursorType Type { get; } + + public Cursor Cursor { get; } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/DataValidationViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/DataValidationViewModel.cs new file mode 100644 index 00000000..3b2c3769 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/DataValidationViewModel.cs @@ -0,0 +1,45 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using MiniMvvm; + +namespace ControlCatalog.ViewModels; + +public class DataValidationViewModel : ViewModelBase +{ + private string? _DataAnnotationsSample; + + [Required] + [EmailAddress] + [MinLength(5)] + public string? DataAnnotationsSample + { + get => _DataAnnotationsSample; + set => RaiseAndSetIfChanged(ref _DataAnnotationsSample, value); + } + + public Func Converter { get; } = new Func(o => + { + return $"Error: {o}"; + }); + + + private string? _ExceptionInsideSetterSample; + + public string? ExceptionInsideSetterSample + { + get => _ExceptionInsideSetterSample; + set + { + if (value is null || value.Length < 5) + throw new ArgumentOutOfRangeException(nameof(value), "Give me 5 or more letter please :-)"); + + RaiseAndSetIfChanged(ref _ExceptionInsideSetterSample, value); + } + } + + public Func ExceptionConverter { get; } = new Func(o => + { + return o is Exception ex ? $"Huh, there was an Exception: {ex.Message}" : "Something went really wrong!"; + }); +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/ExpanderPageViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/ExpanderPageViewModel.cs new file mode 100644 index 00000000..aa7f710e --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/ExpanderPageViewModel.cs @@ -0,0 +1,27 @@ +using Avalonia; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class ExpanderPageViewModel : ViewModelBase + { + private object _cornerRadius = AvaloniaProperty.UnsetValue; + private bool _rounded; + + public object CornerRadius + { + get => _cornerRadius; + private set => RaiseAndSetIfChanged(ref _cornerRadius, value); + } + + public bool Rounded + { + get => _rounded; + set + { + if (RaiseAndSetIfChanged(ref _rounded, value)) + CornerRadius = _rounded ? new CornerRadius(25) : AvaloniaProperty.UnsetValue; + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/ListBoxPageViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/ListBoxPageViewModel.cs new file mode 100644 index 00000000..a1dd17af --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/ListBoxPageViewModel.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using Avalonia.Controls; +using Avalonia.Controls.Selection; +using ControlCatalog.Pages; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class ListBoxPageViewModel : ViewModelBase + { + private bool _multiple; + private bool _toggle; + private bool _alwaysSelected; + private bool _autoScrollToSelectedItem = true; + private bool _wrapSelection; + private int _counter; + private IObservable _selectionMode; + + public ListBoxPageViewModel() + { + Items = new ObservableCollection(Enumerable.Range(1, 10000).Select(i => GenerateItem())); + + Selection = new SelectionModel(); + Selection.Select(1); + + _selectionMode = this.WhenAnyValue( + x => x.Multiple, + x => x.Toggle, + x => x.AlwaysSelected, + (m, t, a) => + (m ? Avalonia.Controls.SelectionMode.Multiple : 0) | + (t ? Avalonia.Controls.SelectionMode.Toggle : 0) | + (a ? Avalonia.Controls.SelectionMode.AlwaysSelected : 0)); + + AddItemCommand = MiniCommand.Create(() => + { + var item = GenerateItem(); + Items.Add(item); + Selection.Clear(); + Selection.Select(Items.Count - 1); + }); + + RemoveItemCommand = MiniCommand.Create(() => + { + var items = Selection.SelectedItems.ToList(); + + foreach (var item in items) + { + Items.Remove(item!); + } + }); + + SelectRandomItemCommand = MiniCommand.Create(() => + { + var random = new Random(); + + using (Selection.BatchUpdate()) + { + Selection.Clear(); + Selection.Select(random.Next(Items.Count - 1)); + } + }); + } + + public ObservableCollection Items { get; } + public SelectionModel Selection { get; } + public IObservable SelectionMode => _selectionMode; + + public bool Multiple + { + get => _multiple; + set => this.RaiseAndSetIfChanged(ref _multiple, value); + } + + public bool Toggle + { + get => _toggle; + set => this.RaiseAndSetIfChanged(ref _toggle, value); + } + + public bool AlwaysSelected + { + get => _alwaysSelected; + set => this.RaiseAndSetIfChanged(ref _alwaysSelected, value); + } + + public bool AutoScrollToSelectedItem + { + get => _autoScrollToSelectedItem; + set => this.RaiseAndSetIfChanged(ref _autoScrollToSelectedItem, value); + } + + public bool WrapSelection + { + get => _wrapSelection; + set => this.RaiseAndSetIfChanged(ref _wrapSelection, value); + } + + public MiniCommand AddItemCommand { get; } + public MiniCommand RemoveItemCommand { get; } + public MiniCommand SelectRandomItemCommand { get; } + + private ItemModel GenerateItem() => new ItemModel(_counter++); + } + + /// + /// An Item model for the + /// + public class ItemModel + { + /// + /// Creates a new ItemModel with the given ID + /// + /// The ID to display + public ItemModel(int id) + { + ID = id; + } + + /// + /// The ID of this Item + /// + public int ID { get; } + + public override string ToString() + { + return $"Item {ID}"; + } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/MainWindowViewModel.cs new file mode 100644 index 00000000..5d14cca1 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,150 @@ +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Dialogs; +using System; +using System.ComponentModel.DataAnnotations; +using Avalonia; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + partial class MainWindowViewModel : ViewModelBase + { + private WindowState _windowState; + private WindowState[] _windowStates = Array.Empty(); + private bool _extendClientAreaEnabled; + private double _titleBarHeight; + private bool _isSystemBarVisible; + private bool _displayEdgeToEdge; + private Thickness _safeAreaPadding; + private bool _canResize; + private bool _canMinimize; + private bool _canMaximize; + private int _selectedDecorationIndex; + + public MainWindowViewModel() + { + AboutCommand = MiniCommand.CreateFromTask(async () => + { + var dialog = new AboutAvaloniaDialog(); + + if ((App.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow is { } mainWindow) + { + await dialog.ShowDialog(mainWindow); + } + }); + ExitCommand = MiniCommand.Create(() => + { + (App.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.Shutdown(); + }); + + WindowState = WindowState.Normal; + + WindowStates = new WindowState[] + { + WindowState.Minimized, + WindowState.Normal, + WindowState.Maximized, + WindowState.FullScreen, + }; + + TitleBarHeight = -1; + CanResize = true; + CanMinimize = true; + CanMaximize = true; + + Filter(); + } + + public bool ExtendClientAreaEnabled + { + get { return _extendClientAreaEnabled; } + set { RaiseAndSetIfChanged(ref _extendClientAreaEnabled, value); } + } + + public double TitleBarHeight + { + get { return _titleBarHeight; } + set { RaiseAndSetIfChanged(ref _titleBarHeight, value); } + } + + public WindowState WindowState + { + get { return _windowState; } + set { RaiseAndSetIfChanged(ref _windowState, value); } + } + + public WindowState[] WindowStates + { + get { return _windowStates; } + set { RaiseAndSetIfChanged(ref _windowStates, value); } + } + + public bool IsSystemBarVisible + { + get { return _isSystemBarVisible; } + set { RaiseAndSetIfChanged(ref _isSystemBarVisible, value); } + } + + public bool DisplayEdgeToEdge + { + get { return _displayEdgeToEdge; } + set { RaiseAndSetIfChanged(ref _displayEdgeToEdge, value); } + } + + public Thickness SafeAreaPadding + { + get { return _safeAreaPadding; } + set { RaiseAndSetIfChanged(ref _safeAreaPadding, value); } + } + + public bool CanResize + { + get { return _canResize; } + set { RaiseAndSetIfChanged(ref _canResize, value); } + } + + public bool CanMinimize + { + get { return _canMinimize; } + set { RaiseAndSetIfChanged(ref _canMinimize, value); } + } + + public bool CanMaximize + { + get { return _canMaximize; } + set { RaiseAndSetIfChanged(ref _canMaximize, value); } + } + + public int SelectedDecorationIndex + { + get { return _selectedDecorationIndex; } + set { RaiseAndSetIfChanged(ref _selectedDecorationIndex, value); } + } + + public MiniCommand AboutCommand { get; } + + public MiniCommand ExitCommand { get; } + + private DateTime? _validatedDateExample; + + /// + /// A required DateTime which should demonstrate validation for the DateTimePicker + /// + [Required] + public DateTime? ValidatedDateExample + { + get => _validatedDateExample; + set => RaiseAndSetIfChanged(ref _validatedDateExample, value); + } + + public Win32Properties.WindowCornerPreference[] Win32WindowCornerPreferences { get; } = + Enum.GetValues(); + + public Win32Properties.WindowCornerPreference Win32WindowCornerPreference + { + get; + set { RaiseAndSetIfChanged(ref field, value); } + } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/MainWindowViewModel_PageList.cs b/samples/AvaloniaControlCatalog/ViewModels/MainWindowViewModel_PageList.cs new file mode 100644 index 00000000..89d20de6 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/MainWindowViewModel_PageList.cs @@ -0,0 +1,248 @@ +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Dialogs; +using System; +using System.ComponentModel.DataAnnotations; +using Avalonia; +using MiniMvvm; +using Avalonia.Collections; +using ControlCatalog.Pages; +using System.Globalization; +using System.Text; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; + +namespace ControlCatalog.ViewModels +{ + partial class MainWindowViewModel + { + private int _selectedPageIndex; + private bool _isDrawerOpened = true; + private bool _ignoreListChange = false; + private string? _query = ""; + private PageItem? _currentItem; + private SplitViewDisplayMode _displayMode; + + private List _items = new() + { + new PageItem("Composition", () => new CompositionPage(), Icons.Layers), + new PageItem("Accelerator", () => new AcceleratorPage(), Icons.Keyboard), + new PageItem("Acrylic", () => new AcrylicPage(), Icons.Blur), + new PageItem("AdornerLayer", () => new AdornerLayerPage(), Icons.Sparkle), + new PageItem("AutoCompleteBox",() => new AutoCompleteBoxPage(), Icons.TextInput), + new PageItem("Border",() => new BorderPage(), Icons.Border), + new PageItem("BitmapCache",() => new BitmapCachePage(), Icons.Lightning), + new PageItem("Buttons",() => new ButtonsPage(), Icons.CursorClick), + new PageItem("ButtonSpinner",() => new ButtonSpinnerPage(), Icons.Spinner), + new PageItem("Calendar",() => new CalendarPage(), Icons.Calendar), + new PageItem("Canvas",() => new CanvasPage(), Icons.Canvas), + new PageItem("CommandBar",() => new CommandBarPage(), Icons.Terminal), + new PageItem("Carousel",() => new Pages.CarouselPage(), Icons.Slides), + new PageItem("CarouselPage",() => new CarouselDemoPage(), Icons.Slides), + new PageItem("CheckBox",() => new CheckBoxPage(), Icons.Checkbox), + new PageItem("Clipboard",() => new ClipboardPage(), Icons.Clipboard), + new PageItem("ColorPicker",() => new ColorPickerPage(), Icons.Palette), + new PageItem("ComboBox",() => new ComboBoxPage(), Icons.Dropdown), + new PageItem("Container Queries",() => new ContainerQueryPage(), Icons.Container), + new PageItem("ContentPage",() => new ContentDemoPage(), Icons.Document), + new PageItem("ContextFlyout",() => new ContextFlyoutPage(), Icons.Menu), + new PageItem("ContextMenu",() => new ContextMenuPage(), Icons.Menu), + new PageItem("Cursor",() => new CursorPage(), Icons.Cursor), + new PageItem("Custom Drawing",() => new CustomDrawing(), Icons.Brush), + new PageItem("Data Validation",() => new DataValidationPage(), Icons.Shield), + new PageItem("Date/Time Picker",() => new DateTimePickerPage(), Icons.Clock), + new PageItem("CalendarDatePicker",() => new CalendarDatePickerPage(), Icons.Calendar), + new PageItem("Dialogs",() => new DialogsPage(), Icons.Dialog), + new PageItem("Drag+Drop",() => new DragAndDropPage(), Icons.DragDrop), + new PageItem("DrawerPage",() => new DrawerDemoPage(), Icons.Drawer), + new PageItem("Expander",() => new ExpanderPage(), Icons.Expand), + new PageItem("Flyouts",() => new FlyoutsPage(), Icons.Flyout), + new PageItem("Focus",() => new FocusPage(), Icons.Target), + new PageItem("Gestures",() => new GesturePage(), Icons.Gesture), + new PageItem("Image",() => new ImagePage(), Icons.Image), + new PageItem("Label",() => new LabelsPage(), Icons.Tag), + new PageItem("LayoutTransformControl",() => new LayoutTransformControlPage(), Icons.Transform), + new PageItem("ListBox",() => new ListBoxPage(), Icons.List), + new PageItem("Menu",() => new MenuPage(), Icons.Menu), + new PageItem("NavigationPage",() => new NavigationDemoPage(), Icons.Navigation), + new PageItem("Notifications",() => new NotificationsPage(), Icons.Bell), + new PageItem("NumericUpDown",() => new NumericUpDownPage(), Icons.Number), + new PageItem("OpenGL",() => new OpenGlPage(), Icons.Cube3D), + new PageItem("OpenGL Lease",() => new OpenGlLeasePage(), Icons.Cube3D), + new PageItem("PipsPager",() => new PipsPagerPage(), Icons.HorizontalDots), + new PageItem("Platform Information",() => new PlatformInfoPage(), Icons.Info), + new PageItem("Pointers",() => new PointersPage(), Icons.Cursor), + new PageItem("ProgressBar",() => new ProgressBarPage(), Icons.Progress), + new PageItem("RadioButton",() => new RadioButtonPage(), Icons.Radio), + new PageItem("RefreshContainer",() => new RefreshContainerPage(), Icons.Refresh), + new PageItem("RelativePanel",() => new RelativePanelPage(), Icons.Layout), + new PageItem("ScrollViewer",() => new ScrollViewerPage(), Icons.Scroll), + new PageItem("Slider",() => new SliderPage(), Icons.Tune), + new PageItem("SplitView",() => new SplitViewPage(), Icons.Split), + new PageItem("TabbedPage",() => new TabbedDemoPage(), Icons.Tab), + new PageItem("TabControl",() => new TabControlPage(), Icons.Tab), + new PageItem("TabStrip",() => new TabStripPage(), Icons.Tab), + new PageItem("TableView",() => new TableViewPage(), Icons.Grid), + new PageItem("TextBox",() => new TextBoxPage(), Icons.TextInput), + new PageItem("TextBlock",() => new TextBlockPage(), Icons.TextInput), + new PageItem("Theme Variants",() => new ThemePage(), Icons.Theme), + new PageItem("ToggleSwitch",() => new ToggleSwitchPage(), Icons.Toggle), + new PageItem("ToolTip",() => new ToolTipPage(), Icons.Tooltip), + new PageItem("TransitioningContentControl",() => new TransitioningContentControlPage(), Icons.Transition), + new PageItem("TreeView",() => new TreeViewPage(), Icons.Tree), + new PageItem("Viewbox",() => new ViewboxPage(), Icons.Viewbox), + new PageItem("Native Embed",() => new NativeEmbedPage(), Icons.Puzzle), + new PageItem("Window Customizations",() => new WindowCustomizationsPage(), Icons.Window), + new PageItem("HeaderedContentControl",() => new HeaderedContentPage(), Icons.Header), + new PageItem("Screens",() => new ScreenPage(), Icons.Monitor), + }; + + public AvaloniaList Pages { get; } = new AvaloniaList(); + + public void Filter(string? query = "") + { + try + { + _ignoreListChange = true; + Pages.Clear(); + + if (string.IsNullOrWhiteSpace(query)) + { + Pages.AddRange(_items); + } + else + { + var querySearchKey = PageItem.CreateSearchKey(query); + + if (querySearchKey.Length == 0) + { + Pages.AddRange(_items); + } + else + { + foreach (var item in _items) + { + if (item.MatchesSearch(querySearchKey)) + { + Pages.Add(item); + } + } + } + } + } + finally + { + _ignoreListChange = false; + if (_currentItem != null) + { + var newIndex = Pages.IndexOf(_currentItem); + if (newIndex != -1) + { + SelectedPageIndex = newIndex; + } + } + } + } + + public INavigation? Navigator { get; internal set; } + + public int SelectedPageIndex + { + get { return _selectedPageIndex; } + set + { + RaiseAndSetIfChanged(ref _selectedPageIndex, value); + + if (!_ignoreListChange) + { + NavigateTo(_selectedPageIndex); + + if (DisplayMode == SplitViewDisplayMode.CompactOverlay || DisplayMode == SplitViewDisplayMode.Overlay) + IsDrawerOpened = false; + } + } + } + + public bool IsDrawerOpened + { + get { return _isDrawerOpened; } + set { RaiseAndSetIfChanged(ref _isDrawerOpened, value); } + } + + public SplitViewDisplayMode DisplayMode + { + get { return _displayMode; } + set { RaiseAndSetIfChanged(ref _displayMode, value); } + } + + public string? Query + { + get { return _query; } + set + { + RaiseAndSetIfChanged(ref _query, value); + + Filter(value); + } + } + + private async void NavigateTo(int pageIndex) + { + if (pageIndex < 0 || pageIndex >= Pages.Count || Navigator is null) + return; + + var item = Pages[pageIndex]; + + if (item != null) + { + var view = item.Factory(); + if (view is Page page && view.GetType() != Navigator.NavigationStack.LastOrDefault()?.GetType()) + { + _currentItem = item; + await Navigator.ReplaceAsync(page); + } + } + } + } + + class PageItem(string header, Func factory, string? iconData = null) + { + public string Header { get; } = header; + public Func Factory { get; } = factory; + public string? IconData { get; } = iconData; + private string SearchKey { get; } = CreateSearchKey(header); + + public bool IsVisible { get; set; } = true; + + public bool MatchesSearch(string searchKey) + { + return SearchKey.Contains(searchKey, StringComparison.Ordinal); + } + + public static string CreateSearchKey(string value) + { + var normalizedValue = value.Normalize(NormalizationForm.FormKD); + var builder = new StringBuilder(normalizedValue.Length); + + foreach (var c in normalizedValue) + { + var category = CharUnicodeInfo.GetUnicodeCategory(c); + + if (category is UnicodeCategory.NonSpacingMark or + UnicodeCategory.SpacingCombiningMark or + UnicodeCategory.EnclosingMark) + { + continue; + } + + if (char.IsLetterOrDigit(c)) + { + builder.Append(char.ToUpperInvariant(c)); + } + } + + return builder.ToString(); + } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/MenuItemViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/MenuItemViewModel.cs new file mode 100644 index 00000000..190bb4c8 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/MenuItemViewModel.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Windows.Input; + +namespace ControlCatalog.ViewModels +{ + public class MenuItemViewModel + { + public string? Header { get; set; } + public ICommand? Command { get; set; } + public object? CommandParameter { get; set; } + public IList? Items { get; set; } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/MenuPageViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/MenuPageViewModel.cs new file mode 100644 index 00000000..f6b406d0 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/MenuPageViewModel.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.VisualTree; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class MenuPageViewModel + { + public Control? View { get; set; } + public MenuPageViewModel() + { + OpenCommand = MiniCommand.CreateFromTask(Open); + SaveCommand = MiniCommand.Create(Save); + OpenRecentCommand = MiniCommand.Create(OpenRecent); + + var recentItems = new[] + { + new MenuItemViewModel + { + Header = "File1.txt", + Command = OpenRecentCommand, + CommandParameter = @"c:\foo\File1.txt" + }, + new MenuItemViewModel + { + Header = "File2.txt", + Command = OpenRecentCommand, + CommandParameter = @"c:\foo\File2.txt" + }, + }; + + RecentItems = recentItems; + MenuItems = new[] + { + new MenuItemViewModel + { + Header = "_File", + Items = new[] + { + new MenuItemViewModel { Header = "O_pen...", Command = OpenCommand }, + new MenuItemViewModel { Header = "Save", Command = SaveCommand }, + new MenuItemViewModel { Header = "-" }, + new MenuItemViewModel + { + Header = "Recent", + Items = recentItems + }, + } + }, + new MenuItemViewModel + { + Header = "_Edit", + Items = new[] + { + new MenuItemViewModel { Header = "_Copy" }, + new MenuItemViewModel { Header = "_Paste" }, + } + } + }; + } + + public IReadOnlyList MenuItems { get; set; } + public IReadOnlyList RecentItems { get; set; } + public MiniCommand OpenCommand { get; } + public MiniCommand SaveCommand { get; } + public MiniCommand OpenRecentCommand { get; } + + public async Task Open() + { + var window = TopLevel.GetTopLevel(View) as Window; + if (window == null) + return; + var result = await window.StorageProvider.OpenFilePickerAsync(new Avalonia.Platform.Storage.FilePickerOpenOptions() { AllowMultiple = true }); + + if (result != null) + { + foreach (var file in result) + { + System.Diagnostics.Debug.WriteLine($"Opened: {file.Name}"); + } + } + } + + public void Save() + { + System.Diagnostics.Debug.WriteLine("Save"); + } + + public void OpenRecent(string path) + { + System.Diagnostics.Debug.WriteLine($"Open recent: {path}"); + } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/NotificationViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/NotificationViewModel.cs new file mode 100644 index 00000000..40af6e03 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/NotificationViewModel.cs @@ -0,0 +1,44 @@ +using Avalonia.Controls.Notifications; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class NotificationViewModel + { + public WindowNotificationManager? NotificationManager { get; set; } + + public NotificationViewModel() + { + ShowCustomManagedNotificationCommand = MiniCommand.Create(() => + { + NotificationManager?.Show(new NotificationViewModel() { Title = "Hey There!", Message = "Did you know that Avalonia now supports Custom In-Window Notifications?" , NotificationManager = NotificationManager}, NotificationType.Warning); + }); + + ShowManagedNotificationCommand = MiniCommand.Create(() => + { + NotificationManager?.Show(new Avalonia.Controls.Notifications.Notification("Welcome", "Avalonia now supports Notifications.", NotificationType.Information)); + }); + + YesCommand = MiniCommand.Create(() => + { + NotificationManager?.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today.")); + }); + + NoCommand = MiniCommand.Create(() => + { + NotificationManager?.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today. To find out more visit...")); + }); + } + + public string? Title { get; set; } + public string? Message { get; set; } + + public MiniCommand YesCommand { get; } + + public MiniCommand NoCommand { get; } + + public MiniCommand ShowCustomManagedNotificationCommand { get; } + + public MiniCommand ShowManagedNotificationCommand { get; } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/PlatformInformationViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/PlatformInformationViewModel.cs new file mode 100644 index 00000000..21c87bab --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/PlatformInformationViewModel.cs @@ -0,0 +1,58 @@ +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Platform; +using MiniMvvm; + +namespace ControlCatalog.ViewModels; + +public class PlatformInformationViewModel : ViewModelBase +{ + public PlatformInformationViewModel() + { + /* NOTE: + * ------------ + * The below API is not meant to be used in production Apps. + * If you need to consume this info, please use: + * - OperatingSystem ( https://learn.microsoft.com/en-us/dotnet/api/system.operatingsystem | if .NET 5 or greater) + * - or RuntimeInformation ( https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.runtimeinformation ) + */ + + var runtimeInfo = AvaloniaLocator.Current.GetService()?.GetRuntimeInfo(); + + if (runtimeInfo is { } info) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))) + { + if (info.IsDesktop) + { + PlatformInfo = "Platform: Desktop (browser)"; + } + else if (info.IsMobile) + { + PlatformInfo = "Platform: Mobile (browser)"; + } + else + { + PlatformInfo = "Platform: Unknown (browser) - please report"; + } + } + else + { + if (info.IsDesktop) + { + PlatformInfo = "Platform: Desktop (native)"; + } + else if (info.IsMobile) + { + PlatformInfo = "Platform: Mobile (native)"; + } + else + { + PlatformInfo = "Platform: Unknown (native) - please report"; + } + } + } + } + + public string? PlatformInfo { get; } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/RefreshContainerViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/RefreshContainerViewModel.cs new file mode 100644 index 00000000..aa15d775 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/RefreshContainerViewModel.cs @@ -0,0 +1,25 @@ +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using Avalonia.Controls.Notifications; +using ControlCatalog.Pages; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class RefreshContainerViewModel : ViewModelBase + { + public ObservableCollection Items { get; } + + public RefreshContainerViewModel() + { + Items = new ObservableCollection(Enumerable.Range(1, 200).Select(i => $"Item {i}")); + } + + public async Task AddToTop() + { + await Task.Delay(3000); + Items.Insert(0, $"Item {200 - Items.Count}"); + } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/SplitViewPageViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/SplitViewPageViewModel.cs new file mode 100644 index 00000000..9664331a --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/SplitViewPageViewModel.cs @@ -0,0 +1,54 @@ +using System; +using Avalonia.Controls; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class SplitViewPageViewModel : ViewModelBase + { + private int _displayMode = 3; //CompactOverlay + private int _placement = 0; //Left + + public int Placement + { + get => _placement; + set + { + this.RaiseAndSetIfChanged(ref _placement, value); + this.RaisePropertyChanged(nameof(PanePlacement)); + } + } + + public int DisplayMode + { + get => _displayMode; + set + { + this.RaiseAndSetIfChanged(ref _displayMode, value); + this.RaisePropertyChanged(nameof(CurrentDisplayMode)); + } + } + + public SplitViewPanePlacement PanePlacement => + _placement switch + { + 0 => SplitViewPanePlacement.Left, + 1 => SplitViewPanePlacement.Right, + 2 => SplitViewPanePlacement.Top, + 3 => SplitViewPanePlacement.Bottom, + _ => SplitViewPanePlacement.Left + }; + + public SplitViewDisplayMode CurrentDisplayMode + { + get + { + if (Enum.IsDefined(typeof(SplitViewDisplayMode), _displayMode)) + { + return (SplitViewDisplayMode)_displayMode; + } + return SplitViewDisplayMode.CompactOverlay; + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/TabControlPageViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/TabControlPageViewModel.cs new file mode 100644 index 00000000..b2334625 --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/TabControlPageViewModel.cs @@ -0,0 +1,26 @@ +using Avalonia.Controls; +using Avalonia.Media.Imaging; +using MiniMvvm; + +namespace ControlCatalog.ViewModels; + +public class TabControlPageViewModel : ViewModelBase +{ + private Dock _tabPlacement; + + public TabControlPageViewModelItem[]? Tabs { get; set; } + + public Dock TabPlacement + { + get { return _tabPlacement; } + set { this.RaiseAndSetIfChanged(ref _tabPlacement, value); } + } +} + +public class TabControlPageViewModelItem +{ + public string? Header { get; set; } + public string? Text { get; set; } + public Bitmap? Image { get; set; } + public bool IsEnabled { get; set; } = true; +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/TableViewPageViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/TableViewPageViewModel.cs new file mode 100644 index 00000000..1a969cee --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/TableViewPageViewModel.cs @@ -0,0 +1,246 @@ +using System.Collections.ObjectModel; +using MiniMvvm; + +namespace ControlCatalog.ViewModels; + +public sealed class TableViewPageViewModel : ViewModelBase +{ + public ObservableCollection Countries { get; } = + [ + new("Afghanistan", "ASIA (EX. NEAR EAST)", 31056997, 647500, 700), + new("Albania", "EASTERN EUROPE", 3581655, 28748, 4500), + new("Algeria", "NORTHERN AFRICA", 32930091, 2381740, 6000), + new("American Samoa", "OCEANIA", 57794, 199, 8000), + new("Andorra", "WESTERN EUROPE", 71201, 468, 19000), + new("Angola", "SUB-SAHARAN AFRICA", 12127071, 1246700, 1900), + new("Anguilla", "LATIN AMER. & CARIB", 13477, 102, 8600), + new("Antigua & Barbuda", "LATIN AMER. & CARIB", 69108, 443, 11000), + new("Argentina", "LATIN AMER. & CARIB", 39921833, 2766890, 11200), + new("Armenia", "C.W. OF IND. STATES", 2976372, 29800, 3500), + new("Aruba", "LATIN AMER. & CARIB", 71891, 193, 28000), + new("Australia", "OCEANIA", 20264082, 7686850, 29000), + new("Austria", "WESTERN EUROPE", 8192880, 83870, 30000), + new("Azerbaijan", "C.W. OF IND. STATES", 7961619, 86600, 3400), + new("The Bahamas", "LATIN AMER. & CARIB", 303770, 13940, 16700), + new("Bahrain", "NEAR EAST", 698585, 665, 16900), + new("Bangladesh", "ASIA (EX. NEAR EAST)", 147365352, 144000, 1900), + new("Barbados", "LATIN AMER. & CARIB", 279912, 431, 15700), + new("Belarus", "C.W. OF IND. STATES", 10293011, 207600, 6100), + new("Belgium", "WESTERN EUROPE", 10379067, 30528, 29100), + new("Belize", "LATIN AMER. & CARIB", 287730, 22966, 4900), + new("Benin", "SUB-SAHARAN AFRICA", 7862944, 112620, 1100), + new("Bermuda", "NORTHERN AMERICA", 65773, 53, 36000), + new("Bhutan", "ASIA (EX. NEAR EAST)", 2279723, 47000, 1300), + new("Bolivia", "LATIN AMER. & CARIB", 8989046, 1098580, 2400), + new("Bosnia & Herzegovina", "EASTERN EUROPE", 4498976, 51129, 6100), + new("Botswana", "SUB-SAHARAN AFRICA", 1639833, 600370, 9000), + new("Brazil", "LATIN AMER. & CARIB", 188078227, 8511965, 7600), + new("British Virgin Is.", "LATIN AMER. & CARIB", 23098, 153, 16000), + new("Brunei", "ASIA (EX. NEAR EAST)", 379444, 5770, 18600), + new("Bulgaria", "EASTERN EUROPE", 7385367, 110910, 7600), + new("Burkina Faso", "SUB-SAHARAN AFRICA", 13902972, 274200, 1100), + new("Burma", "ASIA (EX. NEAR EAST)", 47382633, 678500, 1800), + new("Burundi", "SUB-SAHARAN AFRICA", 8090068, 27830, 600), + new("Cambodia", "ASIA (EX. NEAR EAST)", 13881427, 181040, 1900), + new("Cameroon", "SUB-SAHARAN AFRICA", 17340702, 475440, 1800), + new("Canada", "NORTHERN AMERICA", 33098932, 9984670, 29800), + new("Cape Verde", "SUB-SAHARAN AFRICA", 420979, 4033, 1400), + new("Cayman Islands", "LATIN AMER. & CARIB", 45436, 262, 35000), + new("Central African Rep.", "SUB-SAHARAN AFRICA", 4303356, 622984, 1100), + new("Chad", "SUB-SAHARAN AFRICA", 9944201, 1284000, 1200), + new("Chile", "LATIN AMER. & CARIB", 16134219, 756950, 9900), + new("China", "ASIA (EX. NEAR EAST)", 1313973713, 9596960, 5000), + new("Colombia", "LATIN AMER. & CARIB", 43593035, 1138910, 6300), + new("Comoros", "SUB-SAHARAN AFRICA", 690948, 2170, 700), + new("Congo, Dem.Rep.", "SUB - SAHARAN AFRICA", 62660551, 2345410, 700), + new("Congo, Repub.of the", "SUB - SAHARAN AFRICA", 3702314, 342000, 700), + new("Cook Islands", "OCEANIA", 21388, 240, 5000), + new("Costa Rica", "LATIN AMER. & CARIB", 4075261, 51100, 9100), + new("Cote d'Ivoire", "SUB-SAHARAN AFRICA", 17654843, 322460, 1400), + new("Croatia", "EASTERN EUROPE", 4494749, 56542, 10600), + new("Cuba", "LATIN AMER. & CARIB", 11382820, 110860, 2900), + new("Cyprus", "NEAR EAST", 784301, 9250, 19200), + new("Czech Republic", "EASTERN EUROPE", 10235455, 78866, 15700), + new("Denmark", "WESTERN EUROPE", 5450661, 43094, 31100), + new("Djibouti", "SUB-SAHARAN AFRICA", 486530, 23000, 1300), + new("Dominica", "LATIN AMER. & CARIB", 68910, 754, 5400), + new("Dominican Republic", "LATIN AMER. & CARIB", 9183984, 48730, 6000), + new("East Timor", "ASIA (EX. NEAR EAST)", 1062777, 15007, 500), + new("Ecuador", "LATIN AMER. & CARIB", 13547510, 283560, 3300), + new("Egypt", "NORTHERN AFRICA", 78887007, 1001450, 4000), + new("El Salvador", "LATIN AMER. & CARIB", 6822378, 21040, 4800), + new("Equatorial Guinea", "SUB-SAHARAN AFRICA", 540109, 28051, 2700), + new("Eritrea", "SUB-SAHARAN AFRICA", 4786994, 121320, 700), + new("Estonia", "BALTICS", 1324333, 45226, 12300), + new("Ethiopia", "SUB-SAHARAN AFRICA", 74777981, 1127127, 700), + new("Faroe Islands", "WESTERN EUROPE", 47246, 1399, 22000), + new("Fiji", "OCEANIA", 905949, 18270, 5800), + new("Finland", "WESTERN EUROPE", 5231372, 338145, 27400), + new("France", "WESTERN EUROPE", 60876136, 547030, 27600), + new("French Guiana", "LATIN AMER. & CARIB", 199509, 91000, 8300), + new("French Polynesia", "OCEANIA", 274578, 4167, 17500), + new("Gabon", "SUB-SAHARAN AFRICA", 1424906, 267667, 5500), + new("Gambia, The", "SUB - SAHARAN AFRICA", 1641564, 11300, 1700), + new("Gaza Strip", "NEAR EAST", 1428757, 360, 600), + new("Georgia", "C.W. OF IND. STATES", 4661473, 69700, 2500), + new("Germany", "WESTERN EUROPE", 82422299, 357021, 27600), + new("Ghana", "SUB-SAHARAN AFRICA", 22409572, 239460, 2200), + new("Gibraltar", "WESTERN EUROPE", 27928, 7, 17500), + new("Greece", "WESTERN EUROPE", 10688058, 131940, 20000), + new("Greenland", "NORTHERN AMERICA", 56361, 2166086, 20000), + new("Grenada", "LATIN AMER. & CARIB", 89703, 344, 5000), + new("Guadeloupe", "LATIN AMER. & CARIB", 452776, 1780, 8000), + new("Guam", "OCEANIA", 171019, 541, 21000), + new("Guatemala", "LATIN AMER. & CARIB", 12293545, 108890, 4100), + new("Guernsey", "WESTERN EUROPE", 65409, 78, 20000), + new("Guinea", "SUB-SAHARAN AFRICA", 9690222, 245857, 2100), + new("Guinea-Bissau", "SUB-SAHARAN AFRICA", 1442029, 36120, 800), + new("Guyana", "LATIN AMER. & CARIB", 767245, 214970, 4000), + new("Haiti", "LATIN AMER. & CARIB", 8308504, 27750, 1600), + new("Honduras", "LATIN AMER. & CARIB", 7326496, 112090, 2600), + new("Hong Kong", "ASIA (EX. NEAR EAST)", 6940432, 1092, 28800), + new("Hungary", "EASTERN EUROPE", 9981334, 93030, 13900), + new("Iceland", "WESTERN EUROPE", 299388, 103000, 30900), + new("India", "ASIA (EX. NEAR EAST)", 1095351995, 3287590, 2900), + new("Indonesia", "ASIA (EX. NEAR EAST)", 245452739, 1919440, 3200), + new("Iran", "ASIA (EX. NEAR EAST)", 68688433, 1648000, 7000), + new("Iraq", "NEAR EAST", 26783383, 437072, 1500), + new("Ireland", "WESTERN EUROPE", 4062235, 70280, 29600), + new("Isle of Man", "WESTERN EUROPE", 75441, 572, 21000), + new("Israel", "NEAR EAST", 6352117, 20770, 19800), + new("Italy", "WESTERN EUROPE", 58133509, 301230, 26700), + new("Jamaica", "LATIN AMER. & CARIB", 2758124, 10991, 3900), + new("Japan", "ASIA (EX. NEAR EAST)", 127463611, 377835, 28200), + new("Jersey", "WESTERN EUROPE", 91084, 116, 24800), + new("Jordan", "NEAR EAST", 5906760, 92300, 4300), + new("Kazakhstan", "C.W. OF IND. STATES", 15233244, 2717300, 6300), + new("Kenya", "SUB-SAHARAN AFRICA", 34707817, 582650, 1000), + new("Kiribati", "OCEANIA", 105432, 811, 800), + new("North Korea", "ASIA(EX.NEAR EAST)", 23113019, 120540, 1300), + new("South Korea", "ASIA(EX.NEAR EAST)", 48846823, 98480, 17800), + new("Kuwait", "NEAR EAST", 2418393, 17820, 19000), + new("Kyrgyzstan", "C.W. OF IND. STATES", 5213898, 198500, 1600), + new("Laos", "ASIA (EX. NEAR EAST)", 6368481, 236800, 1700), + new("Latvia", "BALTICS", 2274735, 64589, 10200), + new("Lebanon", "NEAR EAST", 3874050, 10400, 4800), + new("Lesotho", "SUB-SAHARAN AFRICA", 2022331, 30355, 3000), + new("Liberia", "SUB-SAHARAN AFRICA", 3042004, 111370, 1000), + new("Libya", "NORTHERN AFRICA", 5900754, 1759540, 6400), + new("Liechtenstein", "WESTERN EUROPE", 33987, 160, 25000), + new("Lithuania", "BALTICS", 3585906, 65200, 11400), + new("Luxembourg", "WESTERN EUROPE", 474413, 2586, 55100), + new("Macau", "ASIA (EX. NEAR EAST)", 453125, 28, 19400), + new("Macedonia", "EASTERN EUROPE", 2050554, 25333, 6700), + new("Madagascar", "SUB-SAHARAN AFRICA", 18595469, 587040, 800), + new("Malawi", "SUB-SAHARAN AFRICA", 13013926, 118480, 600), + new("Malaysia", "ASIA (EX. NEAR EAST)", 24385858, 329750, 9000), + new("Maldives", "ASIA (EX. NEAR EAST)", 359008, 300, 3900), + new("Mali", "SUB-SAHARAN AFRICA", 11716829, 1240000, 900), + new("Malta", "WESTERN EUROPE", 400214, 316, 17700), + new("Marshall Islands", "OCEANIA", 60422, 11854, 1600), + new("Martinique", "LATIN AMER. & CARIB", 436131, 1100, 14400), + new("Mauritania", "SUB-SAHARAN AFRICA", 3177388, 1030700, 1800), + new("Mauritius", "SUB-SAHARAN AFRICA", 1240827, 2040, 11400), + new("Mayotte", "SUB-SAHARAN AFRICA", 201234, 374, 2600), + new("Mexico", "LATIN AMER. & CARIB", 107449525, 1972550, 9000), + new("Micronesia, Fed.St.", "OCEANIA", 108004, 702, 2000), + new("Moldova", "C.W. OF IND. STATES", 4466706, 33843, 1800), + new("Monaco", "WESTERN EUROPE", 32543, 2, 27000), + new("Mongolia", "ASIA (EX. NEAR EAST)", 2832224, 1564116, 1800), + new("Montserrat", "LATIN AMER. & CARIB", 9439, 102, 3400), + new("Morocco", "NORTHERN AFRICA", 33241259, 446550, 4000), + new("Mozambique", "SUB-SAHARAN AFRICA", 19686505, 801590, 1200), + new("Namibia", "SUB-SAHARAN AFRICA", 2044147, 825418, 7200), + new("Nauru", "OCEANIA", 13287, 21, 5000), + new("Nepal", "ASIA (EX. NEAR EAST)", 28287147, 147181, 1400), + new("Netherlands", "WESTERN EUROPE", 16491461, 41526, 28600), + new("Netherlands Antilles", "LATIN AMER. & CARIB", 221736, 960, 11400), + new("New Caledonia", "OCEANIA", 219246, 19060, 15000), + new("New Zealand", "OCEANIA", 4076140, 268680, 21600), + new("Nicaragua", "LATIN AMER. & CARIB", 5570129, 129494, 2300), + new("Niger", "SUB-SAHARAN AFRICA", 12525094, 1267000, 800), + new("Nigeria", "SUB-SAHARAN AFRICA", 131859731, 923768, 900), + new("N. Mariana Islands", "OCEANIA", 82459, 477, 12500), + new("Norway", "WESTERN EUROPE", 4610820, 323802, 37800), + new("Oman", "NEAR EAST", 3102229, 212460, 13100), + new("Pakistan", "ASIA (EX. NEAR EAST)", 165803560, 803940, 2100), + new("Palau", "OCEANIA", 20579, 458, 9000), + new("Panama", "LATIN AMER. & CARIB", 3191319, 78200, 6300), + new("Papua New Guinea", "OCEANIA", 5670544, 462840, 2200), + new("Paraguay", "LATIN AMER. & CARIB", 6506464, 406750, 4700), + new("Peru", "LATIN AMER. & CARIB", 28302603, 1285220, 5100), + new("Philippines", "ASIA (EX. NEAR EAST)", 89468677, 300000, 4600), + new("Poland", "EASTERN EUROPE", 38536869, 312685, 11100), + new("Portugal", "WESTERN EUROPE", 10605870, 92391, 18000), + new("Puerto Rico", "LATIN AMER. & CARIB", 3927188, 13790, 16800), + new("Qatar", "NEAR EAST", 885359, 11437, 21500), + new("Reunion", "SUB-SAHARAN AFRICA", 787584, 2517, 5800), + new("Romania", "EASTERN EUROPE", 22303552, 237500, 7000), + new("Russia", "C.W. OF IND. STATES", 142893540, 17075200, 8900), + new("Rwanda", "SUB-SAHARAN AFRICA", 8648248, 26338, 1300), + new("Saint Helena", "SUB-SAHARAN AFRICA", 7502, 413, 2500), + new("Saint Kitts & Nevis", "LATIN AMER. & CARIB", 39129, 261, 8800), + new("Saint Lucia", "LATIN AMER. & CARIB", 168458, 616, 5400), + new("St Pierre & Miquelon", "NORTHERN AMERICA", 7026, 242, 6900), + new("Saint Vincent and the Grenadines", "LATIN AMER. & CARIB", 117848, 389, 2900), + new("Samoa", "OCEANIA", 176908, 2944, 5600), + new("San Marino", "WESTERN EUROPE", 29251, 61, 34600), + new("Sao Tome & Principe", "SUB-SAHARAN AFRICA", 193413, 1001, 1200), + new("Saudi Arabia", "NEAR EAST", 27019731, 1960582, 11800), + new("Senegal", "SUB-SAHARAN AFRICA", 11987121, 196190, 1600), + new("Serbia", "EASTERN EUROPE", 9396411, 88361, 2200), + new("Seychelles", "SUB-SAHARAN AFRICA", 81541, 455, 7800), + new("Sierra Leone", "SUB-SAHARAN AFRICA", 6005250, 71740, 500), + new("Singapore", "ASIA (EX. NEAR EAST)", 4492150, 693, 23700), + new("Slovakia", "EASTERN EUROPE", 5439448, 48845, 13300), + new("Slovenia", "EASTERN EUROPE", 2010347, 20273, 19000), + new("Solomon Islands", "OCEANIA", 552438, 28450, 1700), + new("Somalia", "SUB-SAHARAN AFRICA", 8863338, 637657, 500), + new("South Africa", "SUB-SAHARAN AFRICA", 44187637, 1219912, 10700), + new("Spain", "WESTERN EUROPE", 40397842, 504782, 22000), + new("Sri Lanka", "ASIA (EX. NEAR EAST)", 20222240, 65610, 3700), + new("Sudan", "SUB-SAHARAN AFRICA", 41236378, 2505810, 1900), + new("Suriname", "LATIN AMER. & CARIB", 439117, 163270, 4000), + new("Swaziland", "SUB-SAHARAN AFRICA", 1136334, 17363, 4900), + new("Sweden", "WESTERN EUROPE", 9016596, 449964, 26800), + new("Switzerland", "WESTERN EUROPE", 7523934, 41290, 32700), + new("Syria", "NEAR EAST", 18881361, 185180, 3300), + new("Taiwan", "ASIA (EX. NEAR EAST)", 23036087, 35980, 23400), + new("Tajikistan", "C.W. OF IND. STATES", 7320815, 143100, 1000), + new("Tanzania", "SUB-SAHARAN AFRICA", 37445392, 945087, 600), + new("Thailand", "ASIA (EX. NEAR EAST)", 64631595, 514000, 7400), + new("Togo", "SUB-SAHARAN AFRICA", 5548702, 56785, 1500), + new("Tonga", "OCEANIA", 114689, 748, 2200), + new("Trinidad & Tobago", "LATIN AMER. & CARIB", 1065842, 5128, 9500), + new("Tunisia", "NORTHERN AFRICA", 10175014, 163610, 6900), + new("Turkey", "NEAR EAST", 70413958, 780580, 6700), + new("Turkmenistan", "C.W. OF IND. STATES", 5042920, 488100, 5800), + new("Turks & Caicos Is", "LATIN AMER. & CARIB", 21152, 430, 9600), + new("Tuvalu", "OCEANIA", 11810, 26, 1100), + new("Uganda", "SUB-SAHARAN AFRICA", 28195754, 236040, 1400), + new("Ukraine", "C.W. OF IND. STATES", 46710816, 603700, 5400), + new("United Arab Emirates", "NEAR EAST", 2602713, 82880, 23200), + new("United Kingdom", "WESTERN EUROPE", 60609153, 244820, 27700), + new("United States", "NORTHERN AMERICA", 298444215, 9631420, 37800), + new("Uruguay", "LATIN AMER. & CARIB", 3431932, 176220, 12800), + new("Uzbekistan", "C.W. OF IND. STATES", 27307134, 447400, 1700), + new("Vanuatu", "OCEANIA", 208869, 12200, 2900), + new("Venezuela", "LATIN AMER. & CARIB", 25730435, 912050, 4800), + new("Vietnam", "ASIA (EX. NEAR EAST)", 84402966, 329560, 2500), + new("Virgin Islands", "LATIN AMER. & CARIB", 108605, 1910, 17200), + new("Wallis and Futuna", "OCEANIA", 16025, 274, 3700), + new("West Bank", "NEAR EAST", 2460492, 5860, 800), + new("Yemen", "NEAR EAST", 21456188, 527970, 800), + new("Zambia", "SUB-SAHARAN AFRICA", 11502010, 752614, 800), + new("Zimbabwe", "SUB-SAHARAN AFRICA", 12236805, 390580, 1900) + ]; + + public sealed class Country(string name, string region, int population, int area, int gdp) + { + public string Name { get; set; } = name; + public string Region { get; set; } = region; + public int Population { get; set; } = population; + public int Area { get; set; } = area; // Square miles + public int GDP { get; set; } = gdp; + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs new file mode 100644 index 00000000..5bad6c4b --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs @@ -0,0 +1,317 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using Avalonia.Styling; +using Avalonia.VisualTree; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class TransitioningContentControlPageViewModel : ViewModelBase + { + public TransitioningContentControlPageViewModel() + { + var images = new string[] + { + "delicate-arch-896885_640.jpg", + "hirsch-899118_640.jpg", + "maple-leaf-888807_640.jpg" + }; + + foreach (var image in images) + { + var path = $"avares://ControlCatalog/Assets/{image}"; + Images.Add(new Bitmap(AssetLoader.Open(new Uri(path)))); + } + + SetupTransitions(); + + _SelectedTransition = PageTransitions[1]; + _SelectedImage = Images[0]; + } + + public List PageTransitions { get; } = new List(); + + public List Images { get; } = new List(); + + + private Bitmap _SelectedImage; + private bool _Reversed; + + /// + /// Gets or Sets the selected image + /// + public Bitmap SelectedImage + { + get { return _SelectedImage; } + set { this.RaiseAndSetIfChanged(ref _SelectedImage, value); } + } + + + private PageTransition _SelectedTransition; + + /// + /// Gets or sets the transition to play + /// + public PageTransition SelectedTransition + { + get { return _SelectedTransition; } + set { this.RaiseAndSetIfChanged(ref _SelectedTransition, value); } + } + + + + private bool _ClipToBounds; + + /// + /// Gets or sets if the content should be clipped to bounds + /// + public bool ClipToBounds + { + get { return _ClipToBounds; } + set { this.RaiseAndSetIfChanged(ref _ClipToBounds, value); } + } + + + private int _Duration = 500; + + /// + /// Gets or Sets the duration + /// + public int Duration + { + get { return _Duration; } + set + { + this.RaiseAndSetIfChanged(ref _Duration , value); + SetupTransitions(); + } + } + + /// + /// Gets or sets a value indicating whether the animation is reversed. + /// + public bool Reversed + { + get => _Reversed; + set => this.RaiseAndSetIfChanged(ref _Reversed, value); + } + + private void SetupTransitions() + { + if (PageTransitions.Count == 0) + { + PageTransitions.AddRange(new[] + { + new PageTransition("None"), + new PageTransition("CrossFade"), + new PageTransition("Slide horizontally"), + new PageTransition("Slide vertically"), + new PageTransition("Composite"), + new PageTransition("Custom") + }); + } + + PageTransitions[1].Transition = new CrossFade(TimeSpan.FromMilliseconds(Duration)); + PageTransitions[2].Transition = new PageSlide(TimeSpan.FromMilliseconds(Duration), PageSlide.SlideAxis.Horizontal); + PageTransitions[3].Transition = new PageSlide(TimeSpan.FromMilliseconds(Duration), PageSlide.SlideAxis.Vertical); + + var compositeTransition = new CompositePageTransition(); + compositeTransition.PageTransitions.Add(PageTransitions[1].Transition!); + compositeTransition.PageTransitions.Add(PageTransitions[2].Transition!); + compositeTransition.PageTransitions.Add(PageTransitions[3].Transition!); + PageTransitions[4].Transition = compositeTransition; + + PageTransitions[5].Transition = new CustomTransition(TimeSpan.FromMilliseconds(Duration)); + } + + public void NextImage() + { + Reversed = false; + var index = Images.IndexOf(SelectedImage) + 1; + + if (index >= Images.Count) + { + index = 0; + } + + SelectedImage = Images[index]; + } + + public void PrevImage() + { + Reversed = true; + var index = Images.IndexOf(SelectedImage) - 1; + + if (index < 0) + { + index = Images.Count-1; + } + + SelectedImage = Images[index]; + } + } + + public class PageTransition : ViewModelBase + { + public PageTransition(string displayTitle) + { + DisplayTitle = displayTitle; + } + + public string DisplayTitle { get; } + + + private IPageTransition? _Transition; + + /// + /// Gets or sets the transition + /// + public IPageTransition? Transition + { + get { return _Transition; } + set { this.RaiseAndSetIfChanged(ref _Transition, value); } + } + + public override string ToString() + { + return DisplayTitle; + } + + } + + public class CustomTransition : IPageTransition + { + /// + /// Initializes a new instance of the class. + /// + public CustomTransition() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The duration of the animation. + public CustomTransition(TimeSpan duration) + { + Duration = duration; + } + + /// + /// Gets the duration of the animation. + /// + public TimeSpan Duration { get; set; } + + public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + var tasks = new List(); + var parent = GetVisualParent(from, to); + var scaleProperty = ScaleTransform.ScaleYProperty; + + if (from != null) + { + var animation = new Animation + { + Children = + { + new KeyFrame + { + Setters = { new Setter { Property = scaleProperty, Value = 1d } }, + Cue = new Cue(0d) + }, + new KeyFrame + { + Setters = + { + new Setter + { + Property = scaleProperty, + Value = 0d + } + }, + Cue = new Cue(1d) + } + }, + Duration = Duration + }; + tasks.Add(animation.RunAsync(from, cancellationToken)); + } + + if (to != null) + { + to.IsVisible = true; + var animation = new Animation + { + Children = + { + new KeyFrame + { + Setters = + { + new Setter + { + Property = scaleProperty, + Value = 0d + } + }, + Cue = new Cue(0d) + }, + new KeyFrame + { + Setters = { new Setter { Property = scaleProperty, Value = 1d } }, + Cue = new Cue(1d) + } + }, + Duration = Duration + }; + tasks.Add(animation.RunAsync(to, cancellationToken)); + } + + await Task.WhenAll(tasks); + + if (from != null && !cancellationToken.IsCancellationRequested) + { + from.IsVisible = false; + } + } + + /// + /// Gets the common visual parent of the two control. + /// + /// The from control. + /// The to control. + /// The common parent. + /// + /// The two controls do not share a common parent. + /// + /// + /// Any one of the parameters may be null, but not both. + /// + private static Visual GetVisualParent(Visual? from, Visual? to) + { + var p1 = (from ?? to)!.GetVisualParent(); + var p2 = (to ?? from)!.GetVisualParent(); + + if (p1 != null && p2 != null && p1 != p2) + { + throw new ArgumentException("Controls for PageSlide must have same parent."); + } + + return p1 ?? throw new InvalidOperationException("Cannot determine visual parent."); + } + } +} diff --git a/samples/AvaloniaControlCatalog/ViewModels/TreeViewPageViewModel.cs b/samples/AvaloniaControlCatalog/ViewModels/TreeViewPageViewModel.cs new file mode 100644 index 00000000..7b24f61e --- /dev/null +++ b/samples/AvaloniaControlCatalog/ViewModels/TreeViewPageViewModel.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using Avalonia.Controls; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class TreeViewPageViewModel : ViewModelBase + { + private readonly Node _root; + private SelectionMode _selectionMode; + + public TreeViewPageViewModel() + { + _root = new Node(); + + Items = _root.Children; + SelectedItems = new ObservableCollection(); + + AddItemCommand = MiniCommand.Create(AddItem); + RemoveItemCommand = MiniCommand.Create(RemoveItem); + SelectRandomItemCommand = MiniCommand.Create(SelectRandomItem); + } + + public ObservableCollection Items { get; } + public ObservableCollection SelectedItems { get; } + public MiniCommand AddItemCommand { get; } + public MiniCommand RemoveItemCommand { get; } + public MiniCommand SelectRandomItemCommand { get; } + + public SelectionMode SelectionMode + { + get => _selectionMode; + set + { + SelectedItems.Clear(); + this.RaiseAndSetIfChanged(ref _selectionMode, value); + } + } + + private void AddItem() + { + var parentItem = SelectedItems.Count > 0 ? (Node)SelectedItems[0] : _root; + parentItem.AddItem(); + } + + private void RemoveItem() + { + while (SelectedItems.Count > 0) + { + var lastItem = SelectedItems[0]; + RecursiveRemove(Items, lastItem); + } + + bool RecursiveRemove(ObservableCollection items, Node selectedItem) + { + if (items.Remove(selectedItem)) + { + return true; + } + + foreach (Node item in items) + { + if (item.AreChildrenInitialized && RecursiveRemove(item.Children, selectedItem)) + { + return true; + } + } + + return false; + } + } + + private void SelectRandomItem() + { + var random = new Random(); + var depth = random.Next(4); + var indexes = Enumerable.Range(0, depth).Select(x => random.Next(10)); + var node = _root; + + foreach (var i in indexes) + { + node = node.Children[i]; + } + + SelectedItems.Clear(); + SelectedItems.Add(node); + } + + public class Node + { + private ObservableCollection? _children; + private int _childIndex = 10; + + public Node() + { + Header = "Item"; + } + + public Node(Node parent, int index) + { + Parent = parent; + Header = parent.Header + ' ' + index; + } + + public Node? Parent { get; } + public string Header { get; } + public bool AreChildrenInitialized => _children != null; + public ObservableCollection Children => _children ??= CreateChildren(); + public void AddItem() => Children.Add(new Node(this, _childIndex++)); + public void RemoveItem(Node child) => Children.Remove(child); + public override string ToString() => Header; + + private ObservableCollection CreateChildren() + { + return new ObservableCollection( + Enumerable.Range(0, 10).Select(i => new Node(this, i))); + } + } + } +} diff --git a/samples/AvaloniaControlCatalog/Views/CustomNotificationView.xaml b/samples/AvaloniaControlCatalog/Views/CustomNotificationView.xaml new file mode 100644 index 00000000..f06ea029 --- /dev/null +++ b/samples/AvaloniaControlCatalog/Views/CustomNotificationView.xaml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/Pages/DialogsPage.xaml.cs b/samples/AvaloniaControlCatalog/Pages/DialogsPage.xaml.cs index 616e3d4f..626f6310 100644 --- a/samples/AvaloniaControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/AvaloniaControlCatalog/Pages/DialogsPage.xaml.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; -using Avalonia.Dialogs; using Avalonia.Layout; using Avalonia.Platform.Storage; @@ -498,13 +497,7 @@ private Window CreateSampleWindow() return window; } - private IStorageProvider GetStorageProvider() - { - var forceManaged = ForceManaged.IsChecked ?? false; - return forceManaged - ? new ManagedStorageProvider(GetWindow()) // NOTE: In your production App use 'AppBuilder.UseManagedSystemDialogs()' - : GetTopLevel().StorageProvider; - } + private IStorageProvider GetStorageProvider() => GetTopLevel().StorageProvider; private static string FullPathOrName(IStorageItem? item) { diff --git a/samples/AvaloniaControlCatalog/Program.cs b/samples/AvaloniaControlCatalog/Program.cs index 6510d7da..859a31b2 100644 --- a/samples/AvaloniaControlCatalog/Program.cs +++ b/samples/AvaloniaControlCatalog/Program.cs @@ -5,6 +5,7 @@ using Avalonia; using ControlCatalog; using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing.Renderers.Avalonia; namespace AvaloniaControlCatalog; @@ -16,15 +17,13 @@ public static void Main(string[] args) private static AppBuilder BuildAvaloniaApp() { - DrawingContextImpl.BackendMode = Environment.GetEnvironmentVariable("IMAGESHARP_DRAWING_BACKEND")?.ToLowerInvariant() switch + DrawingBackendMode backendMode = Environment.GetEnvironmentVariable("IMAGESHARP_DRAWING_BACKEND")?.ToLowerInvariant() switch { "cpu" => DrawingBackendMode.Cpu, "webgpu" => DrawingBackendMode.WebGpu, _ => DrawingBackendMode.Auto }; - // DrawingContextImpl.BackendMode = DrawingBackendMode.Cpu; - if (int.TryParse(Environment.GetEnvironmentVariable("IMAGESHARP_DRAWING_MAX_PARALLELISM"), out int maxParallelism)) { Configuration.Default.MaxDegreeOfParallelism = maxParallelism; @@ -34,7 +33,7 @@ private static AppBuilder BuildAvaloniaApp() .UsePlatformDetect() .WithInterFont(); - if (DrawingContextImpl.BackendMode == DrawingBackendMode.Cpu) + if (backendMode == DrawingBackendMode.Cpu) { // This sample-only override must run before Avalonia creates the backend context; // otherwise Win32 creates a platform graphics context and the top-level surfaces @@ -48,7 +47,7 @@ private static AppBuilder BuildAvaloniaApp() // Temporary backend switch for visual comparison: ISD_BACKEND=skia uses Avalonia's default (Skia). if (!string.Equals(Environment.GetEnvironmentVariable("ISD_BACKEND"), "skia", StringComparison.OrdinalIgnoreCase)) { - builder = builder.UseImageSharpDrawing(); + builder = builder.UseImageSharpDrawing(backendMode); } return builder; diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index b1d51c34..89c9d7a5 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -17,30 +17,29 @@ 3.0 - 1 - + - <_WebGpuVersionPrefix>$(WebGpuVersionMajor).$(MinVerMinor).$(MinVerPatch) - <_WebGpuPreReleaseSuffix> - <_WebGpuPreReleaseSuffix Condition="'$(MinVerPreRelease)' != ''">-$(MinVerPreRelease) - <_WebGpuBuildMetadataSuffix> - <_WebGpuBuildMetadataSuffix Condition="'$(MinVerBuildMetadata)' != ''">+$(MinVerBuildMetadata) - - $(WebGpuVersionMajor).0.0.0 - $(WebGpuVersionMajor).$(MinVerMinor).$(MinVerPatch).0 - $(_WebGpuVersionPrefix)$(_WebGpuPreReleaseSuffix) + <_IndependentPackageVersionPrefix>$(IndependentPackageVersionMajor).$(MinVerMinor).$(MinVerPatch) + <_IndependentPackagePreReleaseSuffix> + <_IndependentPackagePreReleaseSuffix Condition="'$(MinVerPreRelease)' != ''">-$(MinVerPreRelease) + <_IndependentPackageBuildMetadataSuffix> + <_IndependentPackageBuildMetadataSuffix Condition="'$(MinVerBuildMetadata)' != ''">+$(MinVerBuildMetadata) + + $(IndependentPackageVersionMajor).0.0.0 + $(IndependentPackageVersionMajor).$(MinVerMinor).$(MinVerPatch).0 + $(_IndependentPackageVersionPrefix)$(_IndependentPackagePreReleaseSuffix) $(Version) - $(Version)$(_WebGpuBuildMetadataSuffix) + $(Version)$(_IndependentPackageBuildMetadataSuffix) diff --git a/samples/AvaloniaControlCatalog/CombinedGeometryImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/CombinedGeometryImpl.cs similarity index 96% rename from samples/AvaloniaControlCatalog/CombinedGeometryImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/CombinedGeometryImpl.cs index 1148cc19..c3d2fad0 100644 --- a/samples/AvaloniaControlCatalog/CombinedGeometryImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/CombinedGeometryImpl.cs @@ -5,7 +5,7 @@ using SixLabors.ImageSharp.Drawing; using SixLabors.ImageSharp.Drawing.Processing; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Avalonia combined geometry implementation backed by ImageSharp path clipping. diff --git a/samples/AvaloniaControlCatalog/DrawingBackendMode.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/DrawingBackendMode.cs similarity index 82% rename from samples/AvaloniaControlCatalog/DrawingBackendMode.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/DrawingBackendMode.cs index 366f40b2..f1fc2f5c 100644 --- a/samples/AvaloniaControlCatalog/DrawingBackendMode.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/DrawingBackendMode.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// -/// Selects the rendering backend used by the ImageSharp.Drawing Avalonia sample. +/// Selects the rendering backend used by the ImageSharp.Drawing Avalonia renderer. /// -internal enum DrawingBackendMode +public enum DrawingBackendMode { /// /// Uses WebGPU when Avalonia exposes a compatible native surface; otherwise uses the CPU framebuffer. diff --git a/samples/AvaloniaControlCatalog/DrawingContextImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/DrawingContextImpl.cs similarity index 97% rename from samples/AvaloniaControlCatalog/DrawingContextImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/DrawingContextImpl.cs index a1d2187a..bb785e61 100644 --- a/samples/AvaloniaControlCatalog/DrawingContextImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/DrawingContextImpl.cs @@ -19,7 +19,7 @@ using AvaloniaTextRenderingMode = Avalonia.Media.TextRenderingMode; using BoxShadow = Avalonia.Media.BoxShadow; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Avalonia drawing context implementation backed by an ImageSharp.Drawing canvas. @@ -181,7 +181,7 @@ private DrawingContextImpl(Image image, AvaloniaVector dpi, bool ownsIm internal DrawingCanvas Canvas => this.canvas; /// - public void Clear(Avalonia.Media.Color color) + public void Clear(global::Avalonia.Media.Color color) { this.Trace(() => $"Clear color={Describe(color)}"); this.canvas.Clear(Brushes.Solid(color.ToColor())); @@ -261,7 +261,7 @@ public void DrawBitmap(IBitmapImpl source, IBrush opacityMask, Rect opacityMaskR } /// - public void DrawLine(IPen? pen, Avalonia.Point p1, Avalonia.Point p2) + public void DrawLine(IPen? pen, AvaloniaPoint p1, AvaloniaPoint p2) { Rect targetRect = new Rect(p1, p2).Normalize(); Pen? strokePen = this.CreatePen(pen, targetRect); @@ -328,7 +328,7 @@ public void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect, BoxShadows } /// - public void DrawRectangle(Avalonia.Media.IExperimentalAcrylicMaterial material, RoundedRect rect) + public void DrawRectangle(global::Avalonia.Media.IExperimentalAcrylicMaterial material, RoundedRect rect) { if (rect.Rect.Height <= 0 || rect.Rect.Width <= 0) { @@ -919,16 +919,16 @@ internal void DisposeTransientResources() return this.CreateImageBrush(imageBrush, targetRect); } - if (brush is Avalonia.Media.ISceneBrush sceneBrush) + if (brush is global::Avalonia.Media.ISceneBrush sceneBrush) { - using Avalonia.Media.ISceneBrushContent? content = sceneBrush.CreateContent(); + using global::Avalonia.Media.ISceneBrushContent? content = sceneBrush.CreateContent(); return content is null ? null : this.CreateSceneBrushContent(content, targetRect); } - if (brush is Avalonia.Media.ISceneBrushContent sceneBrushContent) + if (brush is global::Avalonia.Media.ISceneBrushContent sceneBrushContent) { return this.CreateSceneBrushContent(sceneBrushContent, targetRect); } @@ -1355,14 +1355,14 @@ private void FillPath(Brush brush, IPath path, IntersectionRule fillRule, float // Skia feeds TileBrushCalculator the bitmap size in the current drawing DPI, then draws // from source pixels into that logical size. Keep the same split here: layout in Avalonia // DIPs, source sampling through the bitmap implementation. - Avalonia.Size sourceSize = bitmap.PixelSize.ToSizeWithDpi(this.dpi); + AvaloniaSize sourceSize = bitmap.PixelSize.ToSizeWithDpi(this.dpi); return this.CreateTileBrush(bitmap, sourceSize, brush, targetRect); } /// /// Converts an Avalonia scene brush into an ImageSharp tiled image brush. /// - private ImageBrush? CreateSceneBrushContent(Avalonia.Media.ISceneBrushContent content, Rect targetRect) + private ImageBrush? CreateSceneBrushContent(global::Avalonia.Media.ISceneBrushContent content, Rect targetRect) { Rect contentRect = content.Rect; int width = (int)Math.Ceiling(contentRect.Width); @@ -1396,7 +1396,7 @@ private void FillPath(Brush brush, IPath path, IntersectionRule fillRule, float : Matrix.CreateTranslation(-contentRect.X, -contentRect.Y); context.PushRenderOptions(this.currentRenderOptions); - context.Clear(Avalonia.Media.Colors.Transparent); + context.Clear(global::Avalonia.Media.Colors.Transparent); content.Render(context, transform); context.PopRenderOptions(); } @@ -1411,7 +1411,7 @@ private void FillPath(Brush brush, IPath path, IntersectionRule fillRule, float /// private ImageBrush? CreateTileBrush( IDrawableBitmapImpl sourceBitmap, - Avalonia.Size sourceSize, + AvaloniaSize sourceSize, ITileBrush brush, Rect targetRect) { @@ -1426,7 +1426,7 @@ private void FillPath(Brush brush, IPath path, IntersectionRule fillRule, float using (DrawingContextImpl context = new(tileImage, this.dpi)) { - context.Clear(Avalonia.Media.Colors.Transparent); + context.Clear(global::Avalonia.Media.Colors.Transparent); context.PushClip(layout.IntermediateClip); context.PushRenderOptions(this.currentRenderOptions); context.Transform = layout.IntermediateTransform; @@ -1473,10 +1473,10 @@ private static ColorStop[] ToColorStops(IReadOnlyList gradientSto /// /// Calculates the ImageSharp tile image that represents Avalonia's tile brush viewbox/viewport rules. /// - private static TileBrushLayout CalculateTileBrushLayout(ITileBrush brush, Avalonia.Size sourceSize, Rect targetRect) + private static TileBrushLayout CalculateTileBrushLayout(ITileBrush brush, AvaloniaSize sourceSize, Rect targetRect) { Rect sourceRect = brush.SourceRect.ToPixels(sourceSize); - Avalonia.Size targetSize = targetRect.Size; + AvaloniaSize targetSize = targetRect.Size; Rect destinationRect = brush.DestinationRect.ToPixels(targetSize); if (sourceRect.Width <= 0 || sourceRect.Height <= 0 || destinationRect.Width <= 0 || destinationRect.Height <= 0) @@ -1484,12 +1484,12 @@ private static TileBrushLayout CalculateTileBrushLayout(ITileBrush brush, Avalon return default; } - AvaloniaVector scale = Avalonia.Media.MediaExtensions.CalculateScaling( + AvaloniaVector scale = global::Avalonia.Media.MediaExtensions.CalculateScaling( brush.Stretch, destinationRect.Size, sourceRect.Size); - Avalonia.Size scaledSize = new(sourceRect.Width * scale.X, sourceRect.Height * scale.Y); + AvaloniaSize scaledSize = new(sourceRect.Width * scale.X, sourceRect.Height * scale.Y); AvaloniaVector translate = CalculateTileTranslate( brush.AlignmentX, brush.AlignmentY, @@ -1497,7 +1497,7 @@ private static TileBrushLayout CalculateTileBrushLayout(ITileBrush brush, Avalon destinationRect.Size); bool isTiled = brush.TileMode != TileMode.None; - Avalonia.Size intermediateSize = isTiled ? destinationRect.Size : targetSize; + AvaloniaSize intermediateSize = isTiled ? destinationRect.Size : targetSize; Matrix intermediateTransform = Matrix.CreateTranslation(-sourceRect.Position) * Matrix.CreateScale(scale) * @@ -1537,7 +1537,7 @@ private static TileBrushLayout CalculateTileBrushLayout(ITileBrush brush, Avalon /// /// Calculates Avalonia tile alignment translation for the scaled source size. /// - private static AvaloniaVector CalculateTileTranslate(AlignmentX alignmentX, AlignmentY alignmentY, Avalonia.Size sourceSize, Avalonia.Size destinationSize) + private static AvaloniaVector CalculateTileTranslate(AlignmentX alignmentX, AlignmentY alignmentY, AvaloniaSize sourceSize, AvaloniaSize destinationSize) { double x = alignmentX switch { @@ -1723,7 +1723,7 @@ private static string GetCaptureDirectory() /// /// Formats an Avalonia color for diagnostic traces. /// - private static string Describe(Avalonia.Media.Color color) + private static string Describe(global::Avalonia.Media.Color color) => $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}"; /// diff --git a/samples/AvaloniaControlCatalog/EllipseGeometryImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/EllipseGeometryImpl.cs similarity index 93% rename from samples/AvaloniaControlCatalog/EllipseGeometryImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/EllipseGeometryImpl.cs index fd459953..fa09244e 100644 --- a/samples/AvaloniaControlCatalog/EllipseGeometryImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/EllipseGeometryImpl.cs @@ -4,7 +4,7 @@ using Avalonia; using SixLabors.ImageSharp.Drawing; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Avalonia ellipse geometry implementation backed by an ImageSharp ellipse path. diff --git a/samples/AvaloniaControlCatalog/FontManagerImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/FontManagerImpl.cs similarity index 99% rename from samples/AvaloniaControlCatalog/FontManagerImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/FontManagerImpl.cs index 72fcb9ff..32a66cd6 100644 --- a/samples/AvaloniaControlCatalog/FontManagerImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/FontManagerImpl.cs @@ -16,7 +16,7 @@ using SixLaborsFontStyle = SixLabors.Fonts.FontStyle; using SixLaborsSystemFonts = SixLabors.Fonts.SystemFonts; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Avalonia font manager implementation backed by SixLabors.Fonts. diff --git a/samples/AvaloniaControlCatalog/GeometryGroupImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/GeometryGroupImpl.cs similarity index 96% rename from samples/AvaloniaControlCatalog/GeometryGroupImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/GeometryGroupImpl.cs index 4e38600a..e910a3a1 100644 --- a/samples/AvaloniaControlCatalog/GeometryGroupImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/GeometryGroupImpl.cs @@ -5,7 +5,7 @@ using Avalonia.Platform; using SixLabors.ImageSharp.Drawing; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Avalonia geometry group implementation backed by combined ImageSharp paths. diff --git a/samples/AvaloniaControlCatalog/GeometryImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/GeometryImpl.cs similarity index 90% rename from samples/AvaloniaControlCatalog/GeometryImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/GeometryImpl.cs index 0fd12e9f..1baac130 100644 --- a/samples/AvaloniaControlCatalog/GeometryImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/GeometryImpl.cs @@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Drawing; using SixLabors.ImageSharp.Drawing.Processing; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Base Avalonia geometry implementation backed by ImageSharp paths. @@ -115,6 +115,37 @@ public IGeometryImpl GetWidenedGeometry(IPen pen) public bool FillContains(AvaloniaPoint point) => this.fillGeometry is not null && this.fillGeometry.Contains(point.ToPointF(), this.FillRule); + /// + public IntersectionResult GetFillIntersectionResult(IGeometryImpl geometry) + { + if (geometry is not GeometryImpl other || this.FillPath is null || other.FillPath is null) + { + return IntersectionResult.Empty; + } + + Region region = new(this.FillPath, this.FillRule); + Region otherRegion = new(other.FillPath, other.FillRule); + + // Containment is tested before intersection so equal regions produce FullyInside + // rather than FullyContains. + if (region.Contains(otherRegion)) + { + return IntersectionResult.FullyInside; + } + + if (otherRegion.Contains(region)) + { + return IntersectionResult.FullyContains; + } + + if (region.Intersects(otherRegion)) + { + return IntersectionResult.Intersects; + } + + return IntersectionResult.Empty; + } + /// public IGeometryImpl? Intersect(IGeometryImpl geometry) => CombinedGeometryImpl.Create(GeometryCombineMode.Intersect, this, (GeometryImpl)geometry); diff --git a/src/ImageSharp.Drawing.Renderers.Avalonia/GlobalUsings.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/GlobalUsings.cs new file mode 100644 index 00000000..d2ace35f --- /dev/null +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/GlobalUsings.cs @@ -0,0 +1,36 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +// Avalonia type aliases shared across the ImageSharp.Drawing Avalonia renderer so the ImageSharp +// types (Brush, Pen, Color, etc.) can be used bare while Avalonia's same-named types stay explicit. +global using AvaloniaColor = global::Avalonia.Media.Color; +global using AvaloniaMatrix = global::Avalonia.Matrix; +global using AvaloniaPixelFormats = global::Avalonia.Platform.PixelFormats; +global using AvaloniaPoint = global::Avalonia.Point; +global using AvaloniaRect = global::Avalonia.Rect; +global using AvaloniaRoundedRect = global::Avalonia.RoundedRect; +global using AvaloniaSize = global::Avalonia.Size; +global using AvaloniaVector = global::Avalonia.Vector; +global using AlignmentX = global::Avalonia.Media.AlignmentX; +global using AlignmentY = global::Avalonia.Media.AlignmentY; +global using BoxShadows = global::Avalonia.Media.BoxShadows; +global using FillRule = global::Avalonia.Media.FillRule; +global using GeometryCombineMode = global::Avalonia.Media.GeometryCombineMode; +global using GradientSpreadMethod = global::Avalonia.Media.GradientSpreadMethod; +global using IBlurEffect = global::Avalonia.Media.IBlurEffect; +global using IBrush = global::Avalonia.Media.IBrush; +global using IConicGradientBrush = global::Avalonia.Media.IConicGradientBrush; +global using IDropShadowEffect = global::Avalonia.Media.IDropShadowEffect; +global using IEffect = global::Avalonia.Media.IEffect; +global using IGradientBrush = global::Avalonia.Media.IGradientBrush; +global using IGradientStop = global::Avalonia.Media.IGradientStop; +global using IImageBrush = global::Avalonia.Media.IImageBrush; +global using ILinearGradientBrush = global::Avalonia.Media.ILinearGradientBrush; +global using IPen = global::Avalonia.Media.IPen; +global using IRadialGradientBrush = global::Avalonia.Media.IRadialGradientBrush; +global using ISolidColorBrush = global::Avalonia.Media.ISolidColorBrush; +global using PenLineCap = global::Avalonia.Media.PenLineCap; +global using PenLineJoin = global::Avalonia.Media.PenLineJoin; +global using ITileBrush = global::Avalonia.Media.ITileBrush; +global using RenderOptions = global::Avalonia.Media.RenderOptions; +global using TileMode = global::Avalonia.Media.TileMode; diff --git a/samples/AvaloniaControlCatalog/GlyphRunImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/GlyphRunImpl.cs similarity index 97% rename from samples/AvaloniaControlCatalog/GlyphRunImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/GlyphRunImpl.cs index 124bb2f7..eb3ddebd 100644 --- a/samples/AvaloniaControlCatalog/GlyphRunImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/GlyphRunImpl.cs @@ -11,7 +11,7 @@ using AvaloniaTextHintingMode = Avalonia.Media.TextHintingMode; using AvaloniaTextOptions = Avalonia.Media.TextOptions; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Avalonia glyph run implementation backed by SixLabors.Fonts glyph ids and positions. @@ -45,7 +45,7 @@ internal sealed class GlyphRunImpl : IGlyphRunImpl /// The positioned glyph information from Avalonia shaping. /// The glyph-run baseline origin. /// The SixLabors font face used to render the run. - public GlyphRunImpl(GlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin, Font font) + public GlyphRunImpl(GlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, AvaloniaPoint baselineOrigin, Font font) { this.GlyphTypeface = glyphTypeface; this.FontRenderingEmSize = fontRenderingEmSize; @@ -59,7 +59,7 @@ public GlyphRunImpl(GlyphTypeface glyphTypeface, double fontRenderingEmSize, IRe for (int i = 0; i < glyphInfos.Count; i++) { GlyphInfo glyph = glyphInfos[i]; - Avalonia.Vector offset = glyph.GlyphOffset; + AvaloniaVector offset = glyph.GlyphOffset; glyphIds[i] = glyph.GlyphIndex; origins[i] = new Vector2((float)(baselineOrigin.X + penX + offset.X), (float)(baselineOrigin.Y + offset.Y)); @@ -112,7 +112,7 @@ public GlyphRunImpl(GlyphTypeface glyphTypeface, double fontRenderingEmSize, IRe public double FontRenderingEmSize { get; } /// - public Point BaselineOrigin { get; } + public AvaloniaPoint BaselineOrigin { get; } /// public Rect Bounds { get; } diff --git a/samples/AvaloniaControlCatalog/IDrawableBitmapImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/IDrawableBitmapImpl.cs similarity index 95% rename from samples/AvaloniaControlCatalog/IDrawableBitmapImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/IDrawableBitmapImpl.cs index ef86b7e3..eaa690fc 100644 --- a/samples/AvaloniaControlCatalog/IDrawableBitmapImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/IDrawableBitmapImpl.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Bitmap implementation that can draw itself into an ImageSharp-backed drawing context. diff --git a/src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharp.Drawing.Renderers.Avalonia.csproj b/src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharp.Drawing.Renderers.Avalonia.csproj new file mode 100644 index 00000000..4d522eb0 --- /dev/null +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharp.Drawing.Renderers.Avalonia.csproj @@ -0,0 +1,42 @@ + + + + SixLabors.ImageSharp.Drawing.Renderers.Avalonia + SixLabors.ImageSharp.Drawing.Renderers.Avalonia + SixLabors.ImageSharp.Drawing.Renderers.Avalonia + SixLabors.ImageSharp.Drawing.Renderers.Avalonia + + 1 + sixlabors.imagesharp.drawing.128.png + LICENSE + https://github.com/SixLabors/ImageSharp.Drawing/ + $(RepositoryUrl) + ImageSharp Drawing Avalonia Graphics Renderer Shapes Paths Text + An ImageSharp.Drawing renderer for Avalonia applications. + Debug;Release + net8.0 + enable + Nullable + false + true + true + $(NoWarn);AVA3001 + 12.2.999-cibuild0068621-alpha + https://nuget-feed-all.avaloniaui.net/v3/index.json;$(RestoreAdditionalProjectSources) + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaControlCatalog/ImageSharpDrawingAppBuilderExtensions.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharpDrawingAppBuilderExtensions.cs similarity index 62% rename from samples/AvaloniaControlCatalog/ImageSharpDrawingAppBuilderExtensions.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharpDrawingAppBuilderExtensions.cs index a4c67297..4f00a45a 100644 --- a/samples/AvaloniaControlCatalog/ImageSharpDrawingAppBuilderExtensions.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharpDrawingAppBuilderExtensions.cs @@ -4,23 +4,30 @@ using Avalonia; using Avalonia.Platform; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// -/// Provides application builder extensions for the ImageSharp.Drawing sample renderer. +/// Provides application builder extensions for the ImageSharp.Drawing renderer. /// -internal static class ImageSharpDrawingAppBuilderExtensions +public static class ImageSharpDrawingAppBuilderExtensions { /// /// Registers the ImageSharp.Drawing rendering and SixLabors.Fonts text shaping /// subsystems with the application builder. /// /// The application builder. + /// The rendering backend selection. /// The supplied . - public static AppBuilder UseImageSharpDrawing(this AppBuilder builder) - => builder + public static AppBuilder UseImageSharpDrawing(this AppBuilder builder, DrawingBackendMode backendMode) + { + // Avalonia registers one rendering subsystem for the application, so the backend selection + // must be fixed before Avalonia creates any platform rendering contexts. + DrawingContextImpl.BackendMode = backendMode; + + return builder .UseRenderingSubsystem(PlatformRenderInterface.Initialize, "ImageSharp.Drawing") .UseTextShapingSubsystem( () => AvaloniaLocator.CurrentMutable.Bind().ToConstant(new TextShaperImpl()), "SixLabors.Fonts"); + } } diff --git a/samples/AvaloniaControlCatalog/ImageSharpExtensions.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharpExtensions.cs similarity index 99% rename from samples/AvaloniaControlCatalog/ImageSharpExtensions.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharpExtensions.cs index 1e611df7..7280ab02 100644 --- a/samples/AvaloniaControlCatalog/ImageSharpExtensions.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharpExtensions.cs @@ -16,7 +16,7 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Conversions between Avalonia and ImageSharp primitive types. diff --git a/samples/AvaloniaControlCatalog/ImmutableBitmapImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/ImmutableBitmapImpl.cs similarity index 95% rename from samples/AvaloniaControlCatalog/ImmutableBitmapImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/ImmutableBitmapImpl.cs index e7e6716d..38041f6e 100644 --- a/samples/AvaloniaControlCatalog/ImmutableBitmapImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/ImmutableBitmapImpl.cs @@ -11,7 +11,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// ImageSharp-backed immutable bitmap implementation for the sample Avalonia renderer. @@ -43,8 +43,8 @@ public ImmutableBitmapImpl(ImmutableBitmapImpl source, PixelSize destinationSize this.Image = image; this.Dpi = source.Dpi; this.PixelSize = destinationSize; - this.format = PixelFormat.Bgra8888; - this.alphaFormat = Avalonia.Platform.AlphaFormat.Premul; + this.format = AvaloniaPixelFormats.Bgra8888; + this.alphaFormat = global::Avalonia.Platform.AlphaFormat.Premul; bool isUpscaling = destinationSize.Width > source.PixelSize.Width || destinationSize.Height > source.PixelSize.Height; IResampler sampler = interpolationMode.ToResampler(isUpscaling); @@ -106,7 +106,7 @@ public ImmutableBitmapImpl(Image image) /// The ImageSharp image backing the bitmap. /// The bitmap DPI. public ImmutableBitmapImpl(Image image, Vector dpi) - : this(image, dpi, PixelFormat.Bgra8888, Avalonia.Platform.AlphaFormat.Premul) + : this(image, dpi, AvaloniaPixelFormats.Bgra8888, global::Avalonia.Platform.AlphaFormat.Premul) { } @@ -170,16 +170,16 @@ public void Draw( /// private static Image CloneExternalPixels(PixelSize size, int stride, PixelFormat format, AlphaFormat alphaFormat, IntPtr data) { - if (format == PixelFormats.Bgra8888) + if (format == AvaloniaPixelFormats.Bgra8888) { - return alphaFormat == Avalonia.Platform.AlphaFormat.Premul + return alphaFormat == global::Avalonia.Platform.AlphaFormat.Premul ? CopyExternalPixels(size, stride, data) : CopyExternalPixels(size, stride, data); } - if (format == PixelFormats.Rgba8888) + if (format == AvaloniaPixelFormats.Rgba8888) { - return alphaFormat == Avalonia.Platform.AlphaFormat.Premul + return alphaFormat == global::Avalonia.Platform.AlphaFormat.Premul ? CopyExternalPixels(size, stride, data) : CopyExternalPixels(size, stride, data); } diff --git a/samples/AvaloniaControlCatalog/LineGeometryImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/LineGeometryImpl.cs similarity index 83% rename from samples/AvaloniaControlCatalog/LineGeometryImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/LineGeometryImpl.cs index 2b2da6aa..0a2d959f 100644 --- a/samples/AvaloniaControlCatalog/LineGeometryImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/LineGeometryImpl.cs @@ -4,7 +4,7 @@ using Avalonia; using SixLabors.ImageSharp.Drawing; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Avalonia line geometry implementation backed by an ImageSharp open path. @@ -16,7 +16,7 @@ internal sealed class LineGeometryImpl : GeometryImpl /// /// The line start point. /// The line end point. - public LineGeometryImpl(Point p1, Point p2) + public LineGeometryImpl(AvaloniaPoint p1, AvaloniaPoint p2) : base(new PathBuilder().AddLine(p1.ToPointF(), p2.ToPointF()).Build(), null, IntersectionRule.EvenOdd) { } diff --git a/samples/AvaloniaControlCatalog/PlatformRenderInterface.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/PlatformRenderInterface.cs similarity index 97% rename from samples/AvaloniaControlCatalog/PlatformRenderInterface.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/PlatformRenderInterface.cs index 271678b8..34415261 100644 --- a/samples/AvaloniaControlCatalog/PlatformRenderInterface.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/PlatformRenderInterface.cs @@ -22,7 +22,7 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; using AvaloniaGlyphRun = Avalonia.Media.GlyphRun; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Avalonia rendering platform implementation backed by ImageSharp.Drawing. @@ -93,7 +93,7 @@ private static void AppendWebGpuLog(string message) public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; /// - public PixelFormat DefaultPixelFormat => PixelFormat.Bgra8888; + public PixelFormat DefaultPixelFormat => AvaloniaPixelFormats.Bgra8888; /// public bool SupportsRegions => true; @@ -222,9 +222,9 @@ public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsCon /// public bool IsSupportedBitmapPixelFormat(PixelFormat format) - => format == PixelFormats.Rgb565 - || format == PixelFormats.Bgra8888 - || format == PixelFormats.Rgba8888; + => format == AvaloniaPixelFormats.Rgb565 + || format == AvaloniaPixelFormats.Bgra8888 + || format == AvaloniaPixelFormats.Rgba8888; /// public IPlatformRenderInterfaceRegion CreateRegion() => new RegionImpl(); diff --git a/samples/AvaloniaControlCatalog/RectangleGeometryImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/RectangleGeometryImpl.cs similarity index 91% rename from samples/AvaloniaControlCatalog/RectangleGeometryImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/RectangleGeometryImpl.cs index bf87fab9..e4145b28 100644 --- a/samples/AvaloniaControlCatalog/RectangleGeometryImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/RectangleGeometryImpl.cs @@ -4,7 +4,7 @@ using Avalonia; using SixLabors.ImageSharp.Drawing; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Avalonia rectangle geometry implementation backed by an ImageSharp rectangle path. diff --git a/samples/AvaloniaControlCatalog/RegionImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/RegionImpl.cs similarity index 97% rename from samples/AvaloniaControlCatalog/RegionImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/RegionImpl.cs index 1f32d0c4..4ef69115 100644 --- a/samples/AvaloniaControlCatalog/RegionImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/RegionImpl.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.Drawing; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// ImageSharp-backed region implementation for Avalonia clipping and hit testing. diff --git a/samples/AvaloniaControlCatalog/RenderTargetBitmapImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/RenderTargetBitmapImpl.cs similarity index 95% rename from samples/AvaloniaControlCatalog/RenderTargetBitmapImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/RenderTargetBitmapImpl.cs index adb350fa..e2b9896c 100644 --- a/samples/AvaloniaControlCatalog/RenderTargetBitmapImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/RenderTargetBitmapImpl.cs @@ -9,7 +9,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// ImageSharp-backed render-target bitmap implementation for offscreen layers and render-target bitmaps. @@ -76,7 +76,7 @@ public void Draw( /// public IDrawingContextImpl CreateDrawingContext() => this.renderTarget.CreateDrawingContext( - new IRenderTarget.RenderTargetSceneInfo(this.PixelSize, this.Dpi.X / 96D, Avalonia.Rendering.Composition.CompositionTransparencyLevel.None), + new IRenderTarget.RenderTargetSceneInfo(this.PixelSize, this.Dpi.X / 96D, global::Avalonia.Rendering.Composition.CompositionTransparencyLevel.None), out _); /// diff --git a/samples/AvaloniaControlCatalog/RenderTargetImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/RenderTargetImpl.cs similarity index 97% rename from samples/AvaloniaControlCatalog/RenderTargetImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/RenderTargetImpl.cs index ea0449e7..0cd1b492 100644 --- a/samples/AvaloniaControlCatalog/RenderTargetImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/RenderTargetImpl.cs @@ -4,7 +4,7 @@ using Avalonia.Platform; using Avalonia.Platform.Surfaces; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Framebuffer render target for the ImageSharp.Drawing sample renderer. diff --git a/samples/AvaloniaControlCatalog/StreamGeometryImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/StreamGeometryImpl.cs similarity index 99% rename from samples/AvaloniaControlCatalog/StreamGeometryImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/StreamGeometryImpl.cs index 64de0db7..edd0ae2c 100644 --- a/samples/AvaloniaControlCatalog/StreamGeometryImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/StreamGeometryImpl.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.Drawing; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Mutable stream geometry implementation backed by ImageSharp path builders. diff --git a/samples/AvaloniaControlCatalog/TextShaperImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/TextShaperImpl.cs similarity index 97% rename from samples/AvaloniaControlCatalog/TextShaperImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/TextShaperImpl.cs index 5a945225..246b9bd6 100644 --- a/samples/AvaloniaControlCatalog/TextShaperImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/TextShaperImpl.cs @@ -15,7 +15,7 @@ using SixLaborsTextShaper = SixLabors.Fonts.TextShaper; using SixLaborsTextShapingBuffer = SixLabors.Fonts.TextShapingBuffer; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Avalonia text shaper implementation backed by SixLabors.Fonts. @@ -96,7 +96,7 @@ public ShapedBuffer ShapeText(ReadOnlyMemory text, TextShaperOptions optio glyphOffset = default; } - shapedBuffer[i] = new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset); + shapedBuffer[i] = new global::Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset); } return shapedBuffer; diff --git a/samples/AvaloniaControlCatalog/TransformedGeometryImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/TransformedGeometryImpl.cs similarity index 96% rename from samples/AvaloniaControlCatalog/TransformedGeometryImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/TransformedGeometryImpl.cs index 0070e516..e196d2de 100644 --- a/samples/AvaloniaControlCatalog/TransformedGeometryImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/TransformedGeometryImpl.cs @@ -7,7 +7,7 @@ using Avalonia.Platform; using SixLabors.ImageSharp.Drawing; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// Geometry wrapper that preserves the original source geometry and applied transform. diff --git a/samples/AvaloniaControlCatalog/WebGPULayerImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPULayerImpl.cs similarity index 95% rename from samples/AvaloniaControlCatalog/WebGPULayerImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/WebGPULayerImpl.cs index b8a60b6d..fb5720c0 100644 --- a/samples/AvaloniaControlCatalog/WebGPULayerImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPULayerImpl.cs @@ -10,7 +10,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// WebGPU-backed Avalonia drawing layer for the ImageSharp.Drawing sample renderer. @@ -62,10 +62,10 @@ public WebGPULayerImpl(WebGPURenderTarget renderTarget, PixelSize size, Vector d public bool IsCorrupted => false; /// - public PixelFormat? Format => PixelFormat.Bgra8888; + public PixelFormat? Format => AvaloniaPixelFormats.Bgra8888; /// - public AlphaFormat? AlphaFormat => Avalonia.Platform.AlphaFormat.Premul; + public AlphaFormat? AlphaFormat => global::Avalonia.Platform.AlphaFormat.Premul; /// public bool HasRenderContextAffinity => true; diff --git a/samples/AvaloniaControlCatalog/WebGPURenderTargetImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPURenderTargetImpl.cs similarity index 98% rename from samples/AvaloniaControlCatalog/WebGPURenderTargetImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/WebGPURenderTargetImpl.cs index 9eb9dc01..4078704a 100644 --- a/samples/AvaloniaControlCatalog/WebGPURenderTargetImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPURenderTargetImpl.cs @@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Drawing.Processing.Backends; using ImageSharpSize = SixLabors.ImageSharp.Size; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// WebGPU render target bound directly to an Avalonia native window surface. diff --git a/samples/AvaloniaControlCatalog/WriteableBitmapImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/WriteableBitmapImpl.cs similarity index 94% rename from samples/AvaloniaControlCatalog/WriteableBitmapImpl.cs rename to src/ImageSharp.Drawing.Renderers.Avalonia/WriteableBitmapImpl.cs index f4c818be..f14b200e 100644 --- a/samples/AvaloniaControlCatalog/WriteableBitmapImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/WriteableBitmapImpl.cs @@ -11,7 +11,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; -namespace AvaloniaControlCatalog; +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; /// /// ImageSharp-backed writeable bitmap implementation for the sample Avalonia renderer. @@ -116,10 +116,10 @@ private WriteableBitmapImpl(Image image) public int Version => this.version; /// - public PixelFormat? Format => PixelFormats.Bgra8888; + public PixelFormat? Format => AvaloniaPixelFormats.Bgra8888; /// - public AlphaFormat? AlphaFormat => Avalonia.Platform.AlphaFormat.Premul; + public AlphaFormat? AlphaFormat => global::Avalonia.Platform.AlphaFormat.Premul; /// public void Draw( @@ -186,10 +186,10 @@ public BitmapFramebuffer(WriteableBitmapImpl parent) public Vector Dpi => this.parent.Dpi; /// - public PixelFormat Format => PixelFormats.Bgra8888; + public PixelFormat Format => AvaloniaPixelFormats.Bgra8888; /// - public AlphaFormat AlphaFormat => Avalonia.Platform.AlphaFormat.Premul; + public AlphaFormat AlphaFormat => global::Avalonia.Platform.AlphaFormat.Premul; /// public void Dispose() diff --git a/src/ImageSharp.Drawing.WebGPU/ImageSharp.Drawing.WebGPU.csproj b/src/ImageSharp.Drawing.WebGPU/ImageSharp.Drawing.WebGPU.csproj index 1d76c205..ba66f647 100644 --- a/src/ImageSharp.Drawing.WebGPU/ImageSharp.Drawing.WebGPU.csproj +++ b/src/ImageSharp.Drawing.WebGPU/ImageSharp.Drawing.WebGPU.csproj @@ -5,6 +5,8 @@ SixLabors.ImageSharp.Drawing.WebGPU SixLabors.ImageSharp.Drawing.Processing.Backends SixLabors.ImageSharp.Drawing.WebGPU + + 1 sixlabors.imagesharp.drawing.128.png LICENSE https://github.com/SixLabors/ImageSharp.Drawing/ diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 16b60759..ec46721c 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -45,7 +45,7 @@ - + @@ -53,11 +53,11 @@ Enable when referencing the local imagesharp source to prevent missing global usings for the ImageSharp namespaces. --> - + diff --git a/src/ImageSharp.Drawing/Region.cs b/src/ImageSharp.Drawing/Region.cs index 72a1e920..839fc461 100644 --- a/src/ImageSharp.Drawing/Region.cs +++ b/src/ImageSharp.Drawing/Region.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Drawing.Helpers; namespace SixLabors.ImageSharp.Drawing; @@ -17,8 +18,8 @@ namespace SixLabors.ImageSharp.Drawing; /// public sealed class Region { - // The canonical model is the same shape used by SkRegion: a sorted set of horizontal - // Y bands, where each band owns sorted, non-overlapping X intervals. This preserves + // The canonical model is a sorted set of horizontal Y bands, where each band owns + // sorted, non-overlapping X intervals. This preserves // disjoint islands, L shapes, holes, and stair-step edges without collapsing anything // to the bounding rectangle. private readonly List bands = []; @@ -45,6 +46,331 @@ public Region() public Region(Rectangle rectangle) : this() => this.Add(rectangle); + /// + /// Initializes a new instance of the class containing the integer coverage of the specified path. + /// + /// The path whose filled area is added to the region. + /// The rule used to determine the filled area of the path. + /// The resulting region contains non-antialiased integer coverage. + public Region(IPath path, IntersectionRule intersectionRule) + : this() + { + RectangleF pathBounds = path.Bounds; + + // The finite integer clip ceilings each edge independently to match the path-region + // conversion contract; deriving right and bottom from rounded sizes can widen the clip. + int clipLeft = (int)MathF.Ceiling(pathBounds.Left); + int clipTop = (int)MathF.Ceiling(pathBounds.Top); + int clipRight = (int)MathF.Ceiling(pathBounds.Right); + int clipBottom = (int)MathF.Ceiling(pathBounds.Bottom); + + if (clipLeft >= clipRight || clipTop >= clipBottom) + { + return; + } + + LinearGeometry geometry = path.ToLinearGeometry(Vector2.One); + ReadOnlySpan contours = geometry.GetContours(); + int maximumEdgeCount = geometry.Info.PointCount; + + if (maximumEdgeCount == 0) + { + return; + } + + const int stackEdgeBufferSizeInBytes = 512; + int stackEdgeCapacity = stackEdgeBufferSizeInBytes / Unsafe.SizeOf(); + + // Each stored point can contribute at most one edge after the contour is implicitly closed. + // The fixed byte budget bounds per-call stack use while keeping small paths allocation-free; + // larger paths receive one exact, constructor-local array that dies with the conversion. + Span edges = maximumEdgeCount <= stackEdgeCapacity + ? stackalloc RegionEdge[maximumEdgeCount] + : new RegionEdge[maximumEdgeCount]; + + int edgeCount = 0; + + for (int i = 0; i < contours.Length; i++) + { + LinearContour contour = contours[i]; + + if (contour.PointCount < 2) + { + continue; + } + + ReadOnlySpan points = geometry.GetContourPoints(contour); + + // Filled contours are implicitly closed. Starting with the final point emits the + // closing edge without copying the contour or appending a duplicate endpoint. + PointF previous = points[^1]; + + for (int p = 0; p < points.Length; p++) + { + PointF current = points[p]; + + // Edge endpoints use signed 26.6 coordinates. The current X crossing and its + // per-row delta use signed 16.16 coordinates so every scanline advances by one add. + long x0 = (long)(previous.X * 64F); + long y0 = (long)(previous.Y * 64F); + long x1 = (long)(current.X * 64F); + long y1 = (long)(current.Y * 64F); + previous = current; + + int winding = 1; + + if (y0 > y1) + { + long temporary = x0; + x0 = x1; + x1 = temporary; + + temporary = y0; + y0 = y1; + y1 = temporary; + winding = -1; + } + + // Adding one half selects rows by their pixel centres. The bottom row is + // exclusive so adjoining edges contribute their shared vertex exactly once. + int top = (int)((y0 + 32) >> 6); + int bottom = (int)((y1 + 32) >> 6); + + if (top == bottom || top >= clipBottom || bottom <= clipTop) + { + continue; + } + + long slope = ((x1 - x0) << 16) / (y1 - y0); + long distanceToFirstCentre = ((long)top << 6) + 32 - y0; + long x = (x0 + ((slope * distanceToFirstCentre) >> 16)) << 10; + int firstY = Math.Max(top, clipTop); + int lastY = Math.Min(bottom - 1, clipBottom - 1); + + // Advance from the edge's natural first row to the clipped first row in 16.16 + // units. Converting before subtraction prevents the row distance from wrapping. + x += slope * ((long)firstY - top); + + edges[edgeCount++] = new RegionEdge + { + FirstY = firstY, + LastY = lastY, + X = x, + DxDy = slope, + Winding = winding, + Previous = -1, + Next = -1 + }; + } + } + + if (edgeCount == 0) + { + return; + } + + edges[..edgeCount].Sort(); + + // The sorted list contains active edges followed by edges for future rows. Relinking + // crossings in place avoids a second edge-sized order buffer. + for (int i = 0; i < edgeCount; i++) + { + edges[i].Previous = i - 1; + edges[i].Next = i + 1 < edgeCount ? i + 1 : -1; + } + + int activeHead = 0; + int firstFutureEdge = 0; + int y = edges[0].FirstY; + int windingMask = intersectionRule == IntersectionRule.EvenOdd ? 1 : -1; + bool hasBounds = false; + int regionLeft = 0; + int regionTop = 0; + int regionRight = 0; + int regionBottom = 0; + + while (activeHead >= 0 && y < clipBottom) + { + // When no edge spans the vertical gap, skip directly to the next populated row. + if (activeHead == firstFutureEdge && y < edges[firstFutureEdge].FirstY) + { + y = edges[firstFutureEdge].FirstY; + } + + // Future edges are ordered by their first row and initial X. Move only the edges + // beginning on this row into active X order; existing active edges were restored + // to that order while walking the preceding row. + int newEdge = firstFutureEdge; + + while (newEdge >= 0 && edges[newEdge].FirstY <= y) + { + int nextNewEdge = edges[newEdge].Next; + MoveEdgeBackward(edges, newEdge, ref activeHead); + newEdge = nextNewEdge; + } + + RegionBand? previousBand = this.bands.Count > 0 && this.bands[^1].Bottom == y + ? this.bands[^1] + : null; + + RegionBand? rowBand = null; + int matchedIntervalCount = 0; + int winding = 0; + long intervalLeft = 0; + int activeEdge = activeHead; + + while (activeEdge >= 0 && edges[activeEdge].FirstY <= y) + { + // All crossings that round to one integer boundary are one transition. Grouping + // them makes equal-X edge order irrelevant and removes zero-width intermediate spans. + long crossingX = (edges[activeEdge].X + 32768) >> 16; + int windingDelta = 0; + + do + { + int currentEdge = activeEdge; + int nextActiveEdge = edges[currentEdge].Next; + windingDelta += edges[currentEdge].Winding; + + if (edges[currentEdge].LastY == y) + { + int previousActiveEdge = edges[currentEdge].Previous; + + if (previousActiveEdge >= 0) + { + edges[previousActiveEdge].Next = nextActiveEdge; + } + else + { + activeHead = nextActiveEdge; + } + + if (nextActiveEdge >= 0) + { + edges[nextActiveEdge].Previous = previousActiveEdge; + } + } + else + { + edges[currentEdge].X += edges[currentEdge].DxDy; + MoveEdgeBackward(edges, currentEdge, ref activeHead); + } + + activeEdge = nextActiveEdge; + } + while (activeEdge >= 0 && + edges[activeEdge].FirstY <= y && + ((edges[activeEdge].X + 32768) >> 16) == crossingX); + + bool wasInside = (winding & windingMask) != 0; + winding += windingDelta; + bool isInside = (winding & windingMask) != 0; + + if (!wasInside && isInside) + { + intervalLeft = crossingX; + continue; + } + + if (!wasInside || isInside) + { + continue; + } + + // Clamp in the wide type and reject a wholly clipped span before narrowing. + // A surviving endpoint is therefore guaranteed to fit the integer clip. + long clippedLeft = Math.Max(intervalLeft, clipLeft); + long clippedRight = Math.Min(crossingX, clipRight); + + if (clippedLeft >= clippedRight) + { + continue; + } + + int left = (int)clippedLeft; + int right = (int)clippedRight; + + if (!hasBounds) + { + regionLeft = left; + regionTop = y; + regionRight = right; + hasBounds = true; + } + else + { + regionLeft = Math.Min(regionLeft, left); + regionRight = Math.Max(regionRight, right); + } + + regionBottom = y + 1; + + // Delay allocating a row band while its intervals still match the preceding + // band. On the first difference, copy only the already-matched prefix that + // must become part of the new canonical band. + if (rowBand is null && + previousBand is not null && + matchedIntervalCount < previousBand.Intervals.Count && + previousBand.Intervals[matchedIntervalCount].Left == left && + previousBand.Intervals[matchedIntervalCount].Right == right) + { + matchedIntervalCount++; + continue; + } + + if (rowBand is null) + { + rowBand = new RegionBand(y, y + 1); + + if (previousBand is not null && matchedIntervalCount > 0) + { + rowBand.Intervals.EnsureCapacity(previousBand.Intervals.Count); + + for (int i = 0; i < matchedIntervalCount; i++) + { + rowBand.Intervals.Add(previousBand.Intervals[i]); + } + } + } + + rowBand.Intervals.Add(new Interval(left, right)); + } + + firstFutureEdge = activeEdge; + + if (rowBand is not null) + { + this.bands.Add(rowBand); + } + else if (previousBand is not null && matchedIntervalCount == previousBand.Intervals.Count) + { + // Identical consecutive rows share the existing interval storage. + previousBand.Bottom = y + 1; + } + else if (previousBand is not null && matchedIntervalCount > 0) + { + // A shorter row can differ only after its matching prefix has ended. + RegionBand shorterBand = new(y, y + 1); + shorterBand.Intervals.EnsureCapacity(matchedIntervalCount); + + for (int i = 0; i < matchedIntervalCount; i++) + { + shorterBand.Intervals.Add(previousBand.Intervals[i]); + } + + this.bands.Add(shorterBand); + } + + y++; + } + + if (hasBounds) + { + this.bounds = Rectangle.FromLTRB(regionLeft, regionTop, regionRight, regionBottom); + this.rectanglesValid = false; + } + } + /// /// Initializes a new instance of the class containing the same area as the specified region. /// @@ -228,6 +554,80 @@ public bool Contains(int x, int y) return false; } + /// + /// Returns a value indicating whether this region contains the specified region. + /// + /// The region to test. + /// if this region contains all of ; otherwise, . + public bool Contains(Region region) + { + // Empty regions contain no area, while the bounds test rejects containment before scanning the canonical bands. + if (this.IsEmpty || region.IsEmpty || !this.bounds.Contains(region.bounds)) + { + return false; + } + + // Bands are ordered and non-overlapping, so the candidate containing band never needs to move backwards. + int firstContainingBandIndex = 0; + for (int i = 0; i < region.bands.Count; i++) + { + RegionBand requiredBand = region.bands[i]; + while (firstContainingBandIndex < this.bands.Count && this.bands[firstContainingBandIndex].Bottom <= requiredBand.Top) + { + firstContainingBandIndex++; + } + + int containingBandIndex = firstContainingBandIndex; + int coveredTop = requiredBand.Top; + + // Every vertical portion of the required band must be covered without a gap. + while (coveredTop < requiredBand.Bottom) + { + if (containingBandIndex >= this.bands.Count) + { + return false; + } + + RegionBand containingBand = this.bands[containingBandIndex]; + if (containingBand.Top > coveredTop) + { + return false; + } + + // Intervals are also ordered and non-overlapping, allowing a monotonic scan within the overlapping bands. + int containingIntervalIndex = 0; + for (int j = 0; j < requiredBand.Intervals.Count; j++) + { + Interval required = requiredBand.Intervals[j]; + while (containingIntervalIndex < containingBand.Intervals.Count + && containingBand.Intervals[containingIntervalIndex].Right <= required.Left) + { + containingIntervalIndex++; + } + + if (containingIntervalIndex >= containingBand.Intervals.Count) + { + return false; + } + + Interval containing = containingBand.Intervals[containingIntervalIndex]; + + // A required interval is contained only when one interval covers its complete horizontal extent. + if (containing.Left > required.Left || containing.Right < required.Right) + { + return false; + } + } + + // Continue at the first uncovered scanline when the required band spans multiple containing bands. + coveredTop = Math.Min(containingBand.Bottom, requiredBand.Bottom); + containingBandIndex++; + } + } + + return true; + } + /// /// Returns a value indicating whether the region intersects the specified rectangle. /// @@ -279,6 +679,80 @@ public bool Intersects(Rectangle rectangle) return false; } + /// + /// Returns a value indicating whether this region intersects the specified region. + /// + /// The region to test. + /// if the regions have area in common; otherwise, . + public bool Intersects(Region region) + { + // Touching bounds have no shared area, so they can be rejected before scanning the canonical bands. + if (this.IsEmpty || region.IsEmpty + || this.bounds.Right <= region.bounds.Left + || region.bounds.Right <= this.bounds.Left + || this.bounds.Bottom <= region.bounds.Top + || region.bounds.Bottom <= this.bounds.Top) + { + return false; + } + + // Both band lists are ordered and non-overlapping, enabling a linear two-pointer vertical sweep. + int firstBandIndex = 0; + int secondBandIndex = 0; + while (firstBandIndex < this.bands.Count && secondBandIndex < region.bands.Count) + { + RegionBand firstBand = this.bands[firstBandIndex]; + RegionBand secondBand = region.bands[secondBandIndex]; + if (firstBand.Bottom <= secondBand.Top) + { + firstBandIndex++; + continue; + } + + if (secondBand.Bottom <= firstBand.Top) + { + secondBandIndex++; + continue; + } + + // The bands overlap vertically, so scan their ordered intervals for a horizontal overlap. + int firstIntervalIndex = 0; + int secondIntervalIndex = 0; + while (firstIntervalIndex < firstBand.Intervals.Count && secondIntervalIndex < secondBand.Intervals.Count) + { + Interval first = firstBand.Intervals[firstIntervalIndex]; + Interval second = secondBand.Intervals[secondIntervalIndex]; + if (first.Right <= second.Left) + { + firstIntervalIndex++; + continue; + } + + if (second.Right <= first.Left) + { + secondIntervalIndex++; + continue; + } + + return true; + } + + // Advance every band ending at this boundary so the sweep continues beyond the tested vertical overlap. + int overlappingBottom = Math.Min(firstBand.Bottom, secondBand.Bottom); + if (firstBand.Bottom == overlappingBottom) + { + firstBandIndex++; + } + + if (secondBand.Bottom == overlappingBottom) + { + secondBandIndex++; + } + } + + return false; + } + /// /// Intersects this region with the specified rectangle. /// @@ -437,8 +911,8 @@ public IPath ToPath() /// The path describing the region boundary. private IPath BuildBoundaryPath() { - // Match SkRegion's boundary export shape: rectangles are first represented as - // opposing vertical edges, then linked into closed contours around the region + // Rectangles are first represented as opposing vertical edges, then linked into + // closed contours around the region // boundary. Shared internal edges cancel because the rectangle list is already // normalized into non-overlapping bands/intervals. List edges = new(this.rectangles.Count * 2); @@ -813,6 +1287,57 @@ private static bool IntervalsEqual(List first, List second) return true; } + /// + /// Moves an edge backwards through the linked crossing order when its X position precedes its current predecessor. + /// + /// The edge storage containing the linked order. + /// The edge whose X position may have moved backwards. + /// The first edge in crossing order. + private static void MoveEdgeBackward(Span edges, int edgeIndex, ref int head) + { + int previousIndex = edges[edgeIndex].Previous; + + if (previousIndex < 0 || edges[previousIndex].X <= edges[edgeIndex].X) + { + return; + } + + // Unlink the edge before searching backwards through the already-sorted prefix. + int nextIndex = edges[edgeIndex].Next; + edges[previousIndex].Next = nextIndex; + + if (nextIndex >= 0) + { + edges[nextIndex].Previous = previousIndex; + } + + int insertionPredecessor = previousIndex; + + while (insertionPredecessor >= 0 && edges[insertionPredecessor].X > edges[edgeIndex].X) + { + insertionPredecessor = edges[insertionPredecessor].Previous; + } + + if (insertionPredecessor < 0) + { + edges[edgeIndex].Previous = -1; + edges[edgeIndex].Next = head; + edges[head].Previous = edgeIndex; + head = edgeIndex; + return; + } + + int insertionSuccessor = edges[insertionPredecessor].Next; + edges[edgeIndex].Previous = insertionPredecessor; + edges[edgeIndex].Next = insertionSuccessor; + edges[insertionPredecessor].Next = edgeIndex; + + if (insertionSuccessor >= 0) + { + edges[insertionSuccessor].Previous = edgeIndex; + } + } + /// /// Converts one rectangle to its boundary path. /// @@ -848,6 +1373,58 @@ public Interval(int left, int right) public int Right { get; } } + /// + /// Represents one non-horizontal path edge during integer scan conversion. + /// + private struct RegionEdge : IComparable + { + /// + /// Gets or sets the first scanline crossed by the edge. + /// + public int FirstY { get; set; } + + /// + /// Gets or sets the last scanline crossed by the edge. + /// + public int LastY { get; set; } + + /// + /// Gets or sets the current crossing position in signed 16.16 fixed-point units. + /// + public long X { get; set; } + + /// + /// Gets or sets the signed 16.16 X advance for one scanline. + /// + public long DxDy { get; set; } + + /// + /// Gets or sets the winding contribution made when the edge is crossed. + /// + public int Winding { get; set; } + + /// + /// Gets or sets the preceding edge index in active crossing order. + /// + public int Previous { get; set; } + + /// + /// Gets or sets the following edge index in active crossing order. + /// + public int Next { get; set; } + + /// + /// Compares edges by their first scanline and initial crossing position. + /// + /// The edge to compare with this edge. + /// A value indicating the relative scan order of the edges. + public readonly int CompareTo(RegionEdge other) + { + int y = this.FirstY.CompareTo(other.FirstY); + return y != 0 ? y : this.X.CompareTo(other.X); + } + } + /// /// Represents one Y band with common X interval coverage. /// diff --git a/tests/ImageSharp.Drawing.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Drawing.Tests/TestUtilities/ImageProviders/FileProvider.cs index 2eee9796..f7c8db0b 100644 --- a/tests/ImageSharp.Drawing.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Drawing.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -6,9 +6,8 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using IOPath = System.IO.Path; - using Xunit.Abstractions; +using IOPath = System.IO.Path; namespace SixLabors.ImageSharp.Drawing.Tests; From 9eedc750f01ee4923a0d087f470b04b88ea82b5b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 22 Aug 2026 22:28:09 +1000 Subject: [PATCH 3/8] Update catalog pages from upstream --- samples/AvaloniaControlCatalog/App.xaml.cs | 11 - .../AvaloniaControlCatalog/MainView.xaml.cs | 174 +----------- .../AvaloniaControlCatalog/MainWindow.xaml | 1 + .../AvaloniaControlCatalog/MainWindow.xaml.cs | 39 --- .../Pages/CalendarDatePickerPage.xaml | 3 + .../Pages/CalendarPage.xaml | 11 + .../Pages/CompositionPage.axaml | 263 ++++++++++++++---- .../Pages/CompositionPage.axaml.cs | 179 +++++++++++- .../Pages/NumericUpDownPage.xaml.cs | 2 +- .../Pages/PlatformSettingsPage.xaml | 136 +++++++++ .../Pages/PlatformSettingsPage.xaml.cs | 30 ++ .../Pages/RefreshContainerPage.axaml | 1 + .../Pages/RefreshContainerPage.axaml.cs | 7 + .../Pages/TextBlockPage.xaml | 23 +- .../Pages/TextBoxPage.xaml | 5 +- .../Pages/WindowCustomizationsPage.xaml | 59 +++- .../Pages/WrapPanelPage.axaml | 120 ++++++++ .../Pages/WrapPanelPage.axaml.cs | 13 + .../AvaloniaControlCatalog/ScrollPage.xaml | 2 +- .../ViewModels/CursorPageViewModel.cs | 3 +- .../ViewModels/DataValidationViewModel.cs | 2 + .../ViewModels/MainWindowViewModel.cs | 52 ++++ .../MainWindowViewModel_PageList.cs | 2 + .../ViewModels/PlatformSettingsViewModel.cs | 80 ++++++ .../ViewModels/RefreshContainerViewModel.cs | 2 - .../ViewModels/WrapPanelPageViewModel.cs | 105 +++++++ 26 files changed, 1010 insertions(+), 315 deletions(-) create mode 100644 samples/AvaloniaControlCatalog/Pages/PlatformSettingsPage.xaml create mode 100644 samples/AvaloniaControlCatalog/Pages/PlatformSettingsPage.xaml.cs create mode 100644 samples/AvaloniaControlCatalog/Pages/WrapPanelPage.axaml create mode 100644 samples/AvaloniaControlCatalog/Pages/WrapPanelPage.axaml.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/PlatformSettingsViewModel.cs create mode 100644 samples/AvaloniaControlCatalog/ViewModels/WrapPanelPageViewModel.cs diff --git a/samples/AvaloniaControlCatalog/App.xaml.cs b/samples/AvaloniaControlCatalog/App.xaml.cs index 159e4ebf..71e4113a 100644 --- a/samples/AvaloniaControlCatalog/App.xaml.cs +++ b/samples/AvaloniaControlCatalog/App.xaml.cs @@ -43,17 +43,6 @@ public override void OnFrameworkInitializationCompleted() if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) { desktopLifetime.MainWindow = new MainWindow { DataContext = new MainWindowViewModel() }; - - if (int.TryParse(Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_EXIT_AFTER_MS"), out int exitDelay) && exitDelay > 0) - { - // Test-only hook: let the real desktop lifetime render normally, then shut it down - // without a human closing the window. - desktopLifetime.MainWindow.Opened += async (_, _) => - { - await Task.Delay(exitDelay); - desktopLifetime.Shutdown(); - }; - } } else if (ApplicationLifetime is IActivityApplicationLifetime singleViewFactoryApplicationLifetime) { diff --git a/samples/AvaloniaControlCatalog/MainView.xaml.cs b/samples/AvaloniaControlCatalog/MainView.xaml.cs index 0f738320..d4a45a12 100644 --- a/samples/AvaloniaControlCatalog/MainView.xaml.cs +++ b/samples/AvaloniaControlCatalog/MainView.xaml.cs @@ -1,20 +1,12 @@ using System; -using System.Diagnostics; -using System.Globalization; -using System.Linq; using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; -using Avalonia.Input; -using Avalonia.Input.Raw; using Avalonia.Media; using Avalonia.Media.Immutable; using Avalonia.Styling; -using Avalonia.Threading; -using Avalonia.VisualTree; using ControlCatalog.Models; using ControlCatalog.ViewModels; -using AvaloniaCalendar = Avalonia.Controls.Calendar; namespace ControlCatalog { @@ -205,171 +197,7 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) }; } - int startupPageIndex = 0; - string? startupPage = Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_PAGE"); - - if (!string.IsNullOrWhiteSpace(startupPage)) - { - for (int i = 0; i < ViewModel.Pages.Count; i++) - { - if (string.Equals(ViewModel.Pages[i].Header, startupPage, StringComparison.OrdinalIgnoreCase)) - { - startupPageIndex = i; - break; - } - } - } - - ViewModel.SelectedPageIndex = startupPageIndex; - - string? navigateToPage = Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_NAVIGATE_TO_PAGE"); - - if (!string.IsNullOrWhiteSpace(navigateToPage)) - { - // Test-only hook: changing selection after layout exercises the same dirty-region - // repaint path as clicking a navigation item in the drawer. - Dispatcher.UIThread.Post(async () => - { - await Task.Delay(350); - - string[] navigationSequence = navigateToPage.Split( - ',', - StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - - foreach (string pageName in navigationSequence) - { - for (int i = 0; i < ViewModel.Pages.Count; i++) - { - if (string.Equals(ViewModel.Pages[i].Header, pageName, StringComparison.OrdinalIgnoreCase)) - { - ListBox? navigationList = this.GetVisualDescendants() - .OfType() - .FirstOrDefault(x => ReferenceEquals(x.ItemsSource, ViewModel.Pages)); - - Console.Error.WriteLine($"ControlCatalog navigation atTicks={Stopwatch.GetTimestamp()} page={pageName}"); - - if (navigationList is not null) - { - navigationList.SelectedIndex = i; - } - else - { - ViewModel.SelectedPageIndex = i; - } - - if (Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_KEEP_DRAWER_OPEN") == "1") - { - ViewModel.IsDrawerOpened = true; - } - - break; - } - } - - // Leave enough time for the selected page's layout and ordered flushes to - // finish before the next selection measures a distinct navigation. - await Task.Delay(1000); - } - }, DispatcherPriority.Loaded); - } - - if (double.TryParse( - Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_SCROLL_Y"), - NumberStyles.Float, - CultureInfo.InvariantCulture, - out double scrollY)) - { - // Test-only hook: scrolling after layout captures the same dirty-region path that a - // user scroll exercises, while keeping the sample's normal startup path untouched. - Dispatcher.UIThread.Post(async () => - { - await Task.Delay(250); - - ScrollViewer? scrollViewer = this.GetVisualDescendants() - .OfType() - .Where(x => x.Extent.Height > x.Viewport.Height && x.Viewport.Height > 0) - .OrderByDescending(x => x.Viewport.Width) - .ThenByDescending(x => x.Extent.Height - x.Viewport.Height) - .FirstOrDefault(); - - if (scrollViewer is not null) - { - int stepCount = int.TryParse( - Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_SCROLL_STEPS"), - out int parsedStepCount) - ? Math.Max(1, parsedStepCount) - : 1; - - for (int i = 1; i <= stepCount; i++) - { - scrollViewer.Offset = new Vector(scrollViewer.Offset.X, scrollY * i / stepCount); - await Task.Delay(80); - } - } - }, DispatcherPriority.Loaded); - } - - if (int.TryParse( - Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_CALENDAR_SCROLL_MONTHS"), - NumberStyles.Integer, - CultureInfo.InvariantCulture, - out int calendarScrollMonths)) - { - // Test-only hook: Calendar navigation invalidates the CalendarItem internals, which - // exercises a different dirty-region path than scrolling the page viewport. - Dispatcher.UIThread.Post(async () => - { - await Task.Delay(250); - - AvaloniaCalendar[] calendars = this.GetVisualDescendants() - .OfType() - .Where(x => x.IsVisible) - .ToArray(); - - int calendarStart = 0; - int calendarEnd = calendars.Length; - - if (int.TryParse( - Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_CALENDAR_SCROLL_INDEX"), - NumberStyles.Integer, - CultureInfo.InvariantCulture, - out int calendarIndex)) - { - calendarStart = calendarIndex; - calendarEnd = calendarIndex + 1; - } - - using Pointer pointer = new(Pointer.GetNextFreeId(), PointerType.Mouse, true); - PointerPointProperties properties = new(RawInputModifiers.None, PointerUpdateKind.Other); - int direction = calendarScrollMonths < 0 ? 1 : -1; - int steps = Math.Abs(calendarScrollMonths); - - for (int i = 0; i < steps; i++) - { - for (int j = calendarStart; j < calendarEnd; j++) - { - AvaloniaCalendar calendar = calendars[j]; - Point position = calendar.TranslatePoint( - new Point(calendar.Bounds.Width / 2, calendar.Bounds.Height / 2), - topLevel) ?? default; - - PointerWheelEventArgs args = new( - calendar, - pointer, - topLevel, - position, - (ulong)Environment.TickCount64, - properties, - KeyModifiers.None, - new Vector(0, direction)); - - calendar.RaiseEvent(args); - } - - await Task.Delay(80); - } - }, DispatcherPriority.Loaded); - } + ViewModel.SelectedPageIndex = 0; } } } diff --git a/samples/AvaloniaControlCatalog/MainWindow.xaml b/samples/AvaloniaControlCatalog/MainWindow.xaml index d839689d..308b29c5 100644 --- a/samples/AvaloniaControlCatalog/MainWindow.xaml +++ b/samples/AvaloniaControlCatalog/MainWindow.xaml @@ -12,6 +12,7 @@ CanResize="{Binding CanResize}" CanMinimize="{Binding CanMinimize}" CanMaximize="{Binding CanMaximize}" + WindowDrawnDecorations.TitleBarDecorations="{Binding TitleBarDecorations}" Win32Properties.WindowCornerPreference="{Binding Win32WindowCornerPreference}" x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}" x:DataType="vm:MainWindowViewModel"> diff --git a/samples/AvaloniaControlCatalog/MainWindow.xaml.cs b/samples/AvaloniaControlCatalog/MainWindow.xaml.cs index d43536a0..ca040df5 100644 --- a/samples/AvaloniaControlCatalog/MainWindow.xaml.cs +++ b/samples/AvaloniaControlCatalog/MainWindow.xaml.cs @@ -1,7 +1,5 @@ using System; -using System.Globalization; using System.Runtime.InteropServices; -using Avalonia; using Avalonia.Controls; using Avalonia.Input; @@ -15,12 +13,6 @@ public MainWindow() { InitializeComponent(); - if (TryGetStartupSize(out Size startupSize)) - { - Width = startupSize.Width; - Height = startupSize.Height; - } - _recentMenu = ((NativeMenu.GetMenu(this)?.Items[0] as NativeMenuItem)?.Menu?.Items[2] as NativeMenuItem)?.Menu; } @@ -39,36 +31,5 @@ public void OnCloseClicked(object sender, EventArgs args) { Close(); } - - /// - /// Reads the optional startup window size used by automated renderer captures. - /// - /// The parsed startup size. - /// when a valid startup size was provided. - private static bool TryGetStartupSize(out Size size) - { - size = default; - string? value = Environment.GetEnvironmentVariable("IMAGESHARP_CONTROL_CATALOG_WINDOW_SIZE"); - - if (string.IsNullOrWhiteSpace(value)) - { - return false; - } - - string[] parts = value.Split('x', 'X', ','); - if (parts.Length != 2) - { - return false; - } - - if (!double.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out double width) || - !double.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out double height)) - { - return false; - } - - size = new Size(width, height); - return true; - } } } diff --git a/samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml b/samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml index 769dbc0d..c00af6c8 100644 --- a/samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml +++ b/samples/AvaloniaControlCatalog/Pages/CalendarDatePickerPage.xaml @@ -49,6 +49,9 @@ + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml b/samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml index 99727e91..44ab767d 100644 --- a/samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml +++ b/samples/AvaloniaControlCatalog/Pages/CalendarPage.xaml @@ -48,6 +48,17 @@ + + + + + # + + + diff --git a/samples/AvaloniaControlCatalog/Pages/CompositionPage.axaml b/samples/AvaloniaControlCatalog/Pages/CompositionPage.axaml index a3e9b047..0e4fcf2a 100644 --- a/samples/AvaloniaControlCatalog/Pages/CompositionPage.axaml +++ b/samples/AvaloniaControlCatalog/Pages/CompositionPage.axaml @@ -1,61 +1,204 @@ - - - - - - - - - - - - - - - - - - - - - - - - - Resize me - - - - - - - - - - - - - - - - - - Precise dirty rects - - - - + + + + + + + + + + + + + + + + + + + + + + + + + Resize me + + + + + + + + + + + + + + + + + + + Precise dirty rects + + + + + + + + + + + + + + + + + + + + + + - private HintingMode hintingMode = HintingMode.Standard; + private HintingMode hintingMode = HintingMode.Full; /// /// Initializes a new glyph run wrapper. @@ -78,6 +78,7 @@ public GlyphRunImpl(GlyphTypeface glyphTypeface, double fontRenderingEmSize, IRe new GlyphOptions { Font = font, + HintingMode = HintingMode.Full, TextBaseline = TextBaseline.Alphabetic, ColorFontSupport = ColorFontSupport.ColrV0 | ColorFontSupport.ColrV1 | ColorFontSupport.Svg }); @@ -140,8 +141,8 @@ public RichGlyphOptions CreateGlyphOptions(AvaloniaTextOptions textOptions) { AvaloniaTextHintingMode.None => HintingMode.None, AvaloniaTextHintingMode.Light => HintingMode.Standard, - AvaloniaTextHintingMode.Strong => HintingMode.Standard, - _ => HintingMode.Standard + AvaloniaTextHintingMode.Strong => HintingMode.Full, + _ => HintingMode.Full }; return new RichGlyphOptions From f9b4ef7139d1c6d02dacadb13447869077184d29 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 22 Aug 2026 22:53:53 +1000 Subject: [PATCH 5/8] Update ImageSharp.Drawing.All.sln --- ImageSharp.Drawing.All.sln | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/ImageSharp.Drawing.All.sln b/ImageSharp.Drawing.All.sln index ba011499..bac39415 100644 --- a/ImageSharp.Drawing.All.sln +++ b/ImageSharp.Drawing.All.sln @@ -333,10 +333,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DrawShapesWithImageSharp", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaControlCatalog", "samples\AvaloniaControlCatalog\AvaloniaControlCatalog.csproj", "{333F3D4F-D251-5601-4851-307FF0F13D23}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SixLabors.Fonts", "..\Fonts\src\SixLabors.Fonts\SixLabors.Fonts.csproj", "{4A922B77-34EC-EA6A-8E96-8353C8FA0640}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PolygonClipper", "..\PolygonClipper\src\PolygonClipper\PolygonClipper.csproj", "{5EC089DC-69F7-643B-93BB-293D199F5F74}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DrawingBackendBenchmark", "samples\DrawingBackendBenchmark\DrawingBackendBenchmark.csproj", "{B5CF5531-624F-9F49-46BD-937FF4062F54}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebGPUExternalSurfaceDemo", "samples\WebGPUExternalSurfaceDemo\WebGPUExternalSurfaceDemo.csproj", "{97619DEB-226C-EB67-140D-0C7E67B2F7D0}" @@ -411,30 +407,6 @@ Global {333F3D4F-D251-5601-4851-307FF0F13D23}.Release|x64.Build.0 = Release|Any CPU {333F3D4F-D251-5601-4851-307FF0F13D23}.Release|x86.ActiveCfg = Release|Any CPU {333F3D4F-D251-5601-4851-307FF0F13D23}.Release|x86.Build.0 = Release|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|x64.ActiveCfg = Debug|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|x64.Build.0 = Debug|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|x86.ActiveCfg = Debug|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Debug|x86.Build.0 = Debug|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|Any CPU.Build.0 = Release|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|x64.ActiveCfg = Release|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|x64.Build.0 = Release|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|x86.ActiveCfg = Release|Any CPU - {4A922B77-34EC-EA6A-8E96-8353C8FA0640}.Release|x86.Build.0 = Release|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|x64.ActiveCfg = Debug|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|x64.Build.0 = Debug|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|x86.ActiveCfg = Debug|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Debug|x86.Build.0 = Debug|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|Any CPU.Build.0 = Release|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|x64.ActiveCfg = Release|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|x64.Build.0 = Release|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|x86.ActiveCfg = Release|Any CPU - {5EC089DC-69F7-643B-93BB-293D199F5F74}.Release|x86.Build.0 = Release|Any CPU {B5CF5531-624F-9F49-46BD-937FF4062F54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B5CF5531-624F-9F49-46BD-937FF4062F54}.Debug|Any CPU.Build.0 = Debug|Any CPU {B5CF5531-624F-9F49-46BD-937FF4062F54}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -558,8 +530,6 @@ Global {C7606104-5D58-4670-912C-3F336606B02D} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {43A3CA46-2675-4AD4-88FC-DDC2E657F0B7} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} {333F3D4F-D251-5601-4851-307FF0F13D23} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} - {4A922B77-34EC-EA6A-8E96-8353C8FA0640} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} - {5EC089DC-69F7-643B-93BB-293D199F5F74} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {B5CF5531-624F-9F49-46BD-937FF4062F54} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} {97619DEB-226C-EB67-140D-0C7E67B2F7D0} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} {4570F31D-D00F-6A80-DB7C-74826CBD15AE} = {CC2DDE4D-58D9-4333-BBEE-B4F36E0A4070} @@ -574,8 +544,6 @@ Global EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{2e33181e-6e28-4662-a801-e2e7dc206029}*SharedItemsImports = 5 - ..\Fonts\shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{4a922b77-34ec-ea6a-8e96-8353c8fa0640}*SharedItemsImports = 5 - ..\PolygonClipper\shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{5ec089dc-69f7-643b-93bb-293d199f5f74}*SharedItemsImports = 5 shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{638fc2ae-7fc1-6527-e051-c6b34803f08d}*SharedItemsImports = 5 shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13 EndGlobalSection From fb07c1ba8048eb68d51b8cdb008e218982fb34fb Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 23 Aug 2026 12:27:48 +1000 Subject: [PATCH 6/8] Add X11 WebGPU surface support to the Avalonia renderer Open our own Xlib Display and pair it with the XID Avalonia exposes, since window ids are valid across connections to the same server. --- .../WebGPURenderTargetImpl.cs | 34 ++++++++++++++++--- .../Xlib.cs | 30 ++++++++++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 src/ImageSharp.Drawing.Renderers.Avalonia/Xlib.cs diff --git a/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPURenderTargetImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPURenderTargetImpl.cs index 4078704a..990730f6 100644 --- a/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPURenderTargetImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPURenderTargetImpl.cs @@ -18,6 +18,7 @@ internal sealed class WebGPURenderTargetImpl : IRenderTarget { private readonly WebGPUSurfaceHost host; private readonly WebGPUSurfaceSession session; + private readonly nint x11Display; private WebGPUExternalSurface? surface; private WebGPUCompositeAlphaMode alphaMode; private bool isDisposed; @@ -31,9 +32,9 @@ internal sealed class WebGPURenderTargetImpl : IRenderTarget /// when the native surface can be used by WebGPU. public static bool TryCreate(WebGPUSurfaceSession session, INativePlatformHandleSurface nativeSurface, [NotNullWhen(true)] out WebGPURenderTargetImpl? renderTarget) { - if (TryCreateSurfaceHost(nativeSurface, out WebGPUSurfaceHost host)) + if (TryCreateSurfaceHost(nativeSurface, out WebGPUSurfaceHost host, out nint x11Display)) { - renderTarget = new WebGPURenderTargetImpl(session, host); + renderTarget = new WebGPURenderTargetImpl(session, host, x11Display); return true; } @@ -41,14 +42,16 @@ public static bool TryCreate(WebGPUSurfaceSession session, INativePlatformHandle return false; } - private WebGPURenderTargetImpl(WebGPUSurfaceSession session, WebGPUSurfaceHost host) + private WebGPURenderTargetImpl(WebGPUSurfaceSession session, WebGPUSurfaceHost host, nint x11Display) { this.session = session; this.host = host; + this.x11Display = x11Display; } - private static bool TryCreateSurfaceHost(INativePlatformHandleSurface nativeSurface, out WebGPUSurfaceHost host) + private static bool TryCreateSurfaceHost(INativePlatformHandleSurface nativeSurface, out WebGPUSurfaceHost host, out nint x11Display) { + x11Display = 0; switch (nativeSurface.HandleDescriptor) { case "HWND": @@ -56,6 +59,20 @@ private static bool TryCreateSurfaceHost(INativePlatformHandleSurface nativeSurf host = WebGPUSurfaceHost.Win32(nativeSurface.Handle, hinstance); return true; + case "XID": + // Avalonia exposes the window id but keeps its Display* internal. Window ids are valid + // on any connection to the same server, so open our own connection and keep it open for + // the lifetime of the render target; the WebGPU surface is created against it. + x11Display = Xlib.XOpenDisplay(null); + if (x11Display == 0) + { + host = default; + return false; + } + + host = WebGPUSurfaceHost.X11(x11Display, (nuint)nativeSurface.Handle); + return true; + case "SurfaceView": host = WebGPUSurfaceHost.Android(nativeSurface.Handle); return true; @@ -66,7 +83,7 @@ private static bool TryCreateSurfaceHost(INativePlatformHandleSurface nativeSurf default: // Avalonia currently exposes several GPU-capable platform surfaces through GL/Metal/D3D - // abstractions rather than full native window descriptors. X11, Wayland, and NSView require + // abstractions rather than full native window descriptors. Wayland and NSView require // additional upstream handle metadata before they can be mapped to Silk.NET WebGPU hosts here. host = default; return false; @@ -137,6 +154,13 @@ public void Dispose() } this.surface?.Dispose(); + + // The surface is gone, so the X connection it was created on can close. + if (this.x11Display != 0) + { + Xlib.XCloseDisplay(this.x11Display); + } + this.isDisposed = true; } } diff --git a/src/ImageSharp.Drawing.Renderers.Avalonia/Xlib.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/Xlib.cs new file mode 100644 index 00000000..835ac3c3 --- /dev/null +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/Xlib.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; + +/// +/// The Xlib entry points needed to open a connection to the X server that Avalonia's window lives on. +/// Avalonia keeps its own Display* internal, and window ids are valid across connections, so the +/// renderer opens a second connection and pairs it with the window id Avalonia exposes. +/// +internal static partial class Xlib +{ + /// + /// Opens a connection to the X server. Pass to use the DISPLAY environment variable. + /// + /// The display name, or for the default display. + /// The Display*, or when no connection could be made. + [LibraryImport("libX11.so.6", StringMarshalling = StringMarshalling.Utf8)] + public static partial nint XOpenDisplay(string? displayName); + + /// + /// Closes a connection opened by . + /// + /// The Display* to close. + /// Zero on success. + [LibraryImport("libX11.so.6")] + public static partial int XCloseDisplay(nint display); +} From 9a15171522805a480bcf27341bd17e0701a37335 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 23 Aug 2026 13:29:25 +1000 Subject: [PATCH 7/8] Keep CPU output visible on Win32 and use the window's HINSTANCE Select Win32 software rendering from the renderer initializer when the application has not configured Win32PlatformOptions: always in Cpu mode, and in Auto mode when the WebGPU probe fails. Composited windows do not display framebuffer output. Removes the sample-only override. Use GetModuleHandle(null) for the HWND surface, the module Avalonia registers its window class against. --- samples/AvaloniaControlCatalog/Program.cs | 11 ---- ...ageSharp.Drawing.Renderers.Avalonia.csproj | 1 + .../Kernel32.cs | 21 ++++++++ .../PlatformRenderInterface.cs | 51 +++++++++++++++++++ .../WebGPURenderTargetImpl.cs | 6 +-- 5 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 src/ImageSharp.Drawing.Renderers.Avalonia/Kernel32.cs diff --git a/samples/AvaloniaControlCatalog/Program.cs b/samples/AvaloniaControlCatalog/Program.cs index 859a31b2..32d62a86 100644 --- a/samples/AvaloniaControlCatalog/Program.cs +++ b/samples/AvaloniaControlCatalog/Program.cs @@ -33,17 +33,6 @@ private static AppBuilder BuildAvaloniaApp() .UsePlatformDetect() .WithInterFont(); - if (backendMode == DrawingBackendMode.Cpu) - { - // This sample-only override must run before Avalonia creates the backend context; - // otherwise Win32 creates a platform graphics context and the top-level surfaces - // represent the GPU/composited path instead of the framebuffer path we need to test. - builder = builder.With(new Win32PlatformOptions - { - RenderingMode = [Win32RenderingMode.Software] - }); - } - // Temporary backend switch for visual comparison: ISD_BACKEND=skia uses Avalonia's default (Skia). if (!string.Equals(Environment.GetEnvironmentVariable("ISD_BACKEND"), "skia", StringComparison.OrdinalIgnoreCase)) { diff --git a/src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharp.Drawing.Renderers.Avalonia.csproj b/src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharp.Drawing.Renderers.Avalonia.csproj index 4d522eb0..936a521c 100644 --- a/src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharp.Drawing.Renderers.Avalonia.csproj +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/ImageSharp.Drawing.Renderers.Avalonia.csproj @@ -32,6 +32,7 @@ + diff --git a/src/ImageSharp.Drawing.Renderers.Avalonia/Kernel32.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/Kernel32.cs new file mode 100644 index 00000000..e6d9fd17 --- /dev/null +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/Kernel32.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Drawing.Renderers.Avalonia; + +/// +/// The Win32 entry point needed to identify the module that owns Avalonia's windows. +/// +internal static partial class Kernel32 +{ + /// + /// Gets the handle of a loaded module. Pass for the process executable, + /// which is the module Avalonia registers its window classes against. + /// + /// The module name, or for the process executable. + /// The module handle (HINSTANCE). + [LibraryImport("kernel32.dll", EntryPoint = "GetModuleHandleW", StringMarshalling = StringMarshalling.Utf16)] + public static partial nint GetModuleHandle(string? moduleName); +} diff --git a/src/ImageSharp.Drawing.Renderers.Avalonia/PlatformRenderInterface.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/PlatformRenderInterface.cs index 34415261..6f4901fa 100644 --- a/src/ImageSharp.Drawing.Renderers.Avalonia/PlatformRenderInterface.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/PlatformRenderInterface.cs @@ -62,6 +62,11 @@ public static void Initialize() AppendWebGpuLog($"failed to wire wgpu log callback: {ex.Message}"); } + if (OperatingSystem.IsWindows()) + { + EnsureWin32SoftwareRenderingForFramebuffer(); + } + PlatformRenderInterface platform = new(); AvaloniaLocator.CurrentMutable @@ -69,6 +74,52 @@ public static void Initialize() .Bind().ToConstant(new FontManagerImpl()); } + /// + /// Keeps CPU rendering visible on Windows. Win32 creates GPU-composited windows whenever a GPU + /// platform is available, and pixels drawn by the CPU into those windows are never shown. When + /// this renderer will draw with the CPU and the application did not configure + /// itself, select software rendering so the windows keep a + /// redirection surface. Avalonia runs this initializer after the application's options and before + /// the windowing subsystem, which is the only point where both facts are known. + /// + private static void EnsureWin32SoftwareRenderingForFramebuffer() + { + if (AvaloniaLocator.Current.GetService() is not null) + { + return; + } + + bool usesFramebuffer; + switch (DrawingContextImpl.BackendMode) + { + case DrawingBackendMode.Cpu: + usesFramebuffer = true; + break; + + case DrawingBackendMode.Auto: + WebGPUEnvironmentError probe = WebGPUEnvironment.ProbeAvailability(); + usesFramebuffer = probe != WebGPUEnvironmentError.Success; + if (usesFramebuffer) + { + AppendWebGpuLog($"WebGPU unavailable ({probe}); using the CPU framebuffer with Win32 software rendering."); + } + + break; + + default: + usesFramebuffer = false; + break; + } + + if (usesFramebuffer) + { + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Win32PlatformOptions + { + RenderingMode = [Win32RenderingMode.Software] + }); + } + } + private static void AppendWebGpuLog(string message) { string line = $"{DateTime.Now:HH:mm:ss.fff} {message}"; diff --git a/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPURenderTargetImpl.cs b/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPURenderTargetImpl.cs index 990730f6..ad62d60f 100644 --- a/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPURenderTargetImpl.cs +++ b/src/ImageSharp.Drawing.Renderers.Avalonia/WebGPURenderTargetImpl.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; using Avalonia; using Avalonia.Platform; using Avalonia.Rendering.Composition; @@ -55,8 +54,9 @@ private static bool TryCreateSurfaceHost(INativePlatformHandleSurface nativeSurf switch (nativeSurface.HandleDescriptor) { case "HWND": - nint hinstance = Marshal.GetHINSTANCE(typeof(WebGPURenderTargetImpl).Module); - host = WebGPUSurfaceHost.Win32(nativeSurface.Handle, hinstance); + // Avalonia registers its window classes against the process executable, so that is + // the module that owns this window. The managed assembly's module would be the wrong one. + host = WebGPUSurfaceHost.Win32(nativeSurface.Handle, Kernel32.GetModuleHandle(null)); return true; case "XID": From 9fd10ffc01ddadfa1520a9ddc71814cbb58ad29c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 22 Oct 2026 11:34:55 +1000 Subject: [PATCH 8/8] Store the test fonts as Git LFS pointers The .gitattributes rules already route these fonts through LFS. The pointers match the objects on main. --- .../TestFonts/NettoOffc.ttf | Bin 89796 -> 130 bytes .../TestFonts/NotoColorEmoji-Regular.ttf | Bin 25096376 -> 133 bytes .../TestFonts/NotoSansKR-Regular.otf | Bin 4744692 -> 132 bytes .../TestFonts/NotoSerifKR-Regular.otf | Bin 7417376 -> 132 bytes .../TestFonts/OpenSans-Regular.ttf | Bin 122076 -> 131 bytes .../TestFonts/SixLaborsSampleAB.woff | Bin 1352 -> 129 bytes .../TestFonts/TwemojiMozilla.ttf | Bin 1437964 -> 132 bytes .../TestFonts/WendyOne-Regular.ttf | Bin 22772 -> 130 bytes .../TestFonts/icomoon-events.ttf | Bin 22580 -> 130 bytes .../TestFonts/malgun.ttf | Bin 9595100 -> 132 bytes .../TestFonts/me_quran_volt_newmet.ttf | Bin 459976 -> 131 bytes .../TestFonts/whitney-book.ttf | Bin 61140 -> 130 bytes 12 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/ImageSharp.Drawing.Tests/TestFonts/NettoOffc.ttf b/tests/ImageSharp.Drawing.Tests/TestFonts/NettoOffc.ttf index b1b2ac7310827df54895a3aad2b716d4c4e302ab..f9a717662fb4c326180b060fd8292313eb584ae4 100644 GIT binary patch literal 130 zcmWN{K^DUh5CG7l^BB8Q&o&>gGM3wV zriJ=jje|*DTJ)CXs1Ypg9xwtiZ~?!fcD!Ybh74(lIq5(E2?}oz?`Q(8PLn|o*0S*d ML5%ie>4OU>{^Uw03IG5A literal 89796 zcmeFadAuA&xi?7NWdgOLIPnYfUE&G1ObVP z;DU(j^;L1Znu`b_aY02u6bxKMR0LF1R76FLsAT5%eX4qvy{n0>E)hZyooW-nT)CXPTjfvg0bgK+~55&V~#sdz4YQdd$Tja z*x=i6fAsVV&e-YR{NZuN`uE@+7oD;Fq6^qXEP(Iz;JS6jxtE{5Hu2Eaj17O4v0v>v zbH~nIm+)1Wp@40S#UDHKwCy|ou+sH2JUfN^$Ie8C`+%#0^NVrbcIM8DcX|7UpJ7ai zqe4^XoOa=P4_|)YF2)lhj8(pW?)j%~?;ShiB$VgFxF^4J`>qQd1^yd6e+!<^pSOMI zX&>3U>pvNv#i{cT7o305#RoRrdKcq!e?onxF1YZt3p%3VSMWT_!?rNOo#(CYA76j{ zn@%>S{>l=L=a9qho%_v;biD8J&C%+WwSse(;{lxdm_oVO@SNj^wE|IiPoXWSAqbNrnd6v!cPMoPOr^2x*S2Fg;8J0g= zJO!|P!>T;LhvomNID9C>_Z>fEPv=FM0BXwp`69!q;$_>#6aN6j&bP9w__gc=sJC?99b*~{z%USi|CgDvO(&W3PJW8Y`Spv zVei8}jD4K$#~wm@47)~0{s8WIjO|j&%uu3i4gVDzSH^IB4*P>BLp#n_vo-8VoS(-M z{3RS8L;e%kkHtQQ>mD2z;uyrWpRf{tnz@v9Y*z6xze8aLe}(z+K0p60v-k$&x3Tm1 zCOkuBDY!IF94{0fGC|*i}ehf_*Xe zDE1`XgX2jk+tH3yY>J=B7NZV?&o;un$h(nk=5G-A9>Mi#>{C=1+_M3@#v`EJjqLTv zUxQsd@5r$Z$9l03@cZ#B;r&&1#{lpKS_nE2E$kh1@L{j8Cu_gQ{ugB%vl?_DTD0z7 z@CTg}En0S>1<@K(JS*@{xIbbCemev$hz>0~=wM*)WxuZd3H!_ZWn8b*foRdP3mnin z(V}%HS`f`}#Ji5=e@9*WnB~~S#vMBDCmtd?)_DeW!@Er2a?ak@tN5a321>S z>>bd2MDslD{IBfe4&crCZP23^*RdmiJ9fh{%sOx!bYy;{0NyseF`Aa!dPvV$Um1wj8py9NNM-nRL%~zHOX4xJYvxPTtD5 zok$7VPMG7pd}Ft^@pZV5Z#-_}wvAi1CyvLJ?tad<;ka#Ee9K9E3nlq6+qQ4pg52$N zBDW#&rY(GITlY3ThAZ2*@sm#WfeNSsXCI619J@BufIO1WdCO(ixTp%(6`F?<~i%fO(b!EU-Lakriq$ zvl1%;wy_dmJ8J_hvv$A=E7$%8UaSCivJSv5)(P0nx@rem59tQWAK^#KmB z{@P#JAR7Q2VuOIgYzT0K4cA^`qXLex5v0f2DBuD%1~|dSYky%2*#f{xwh(ZNO#&_g ze330?Q-ITK5#S743^>cCYky`-*bLxOHVe3nEdiWkO97X&WzY>P*c{+Wwj6L3TLHM5 zt*reK+Itn?TDBVSD7FUhXm%9fF@P_yb?j)sW7#o)>)ATM4eZ$3AJ}nhJ>ci|z+8v##bn*dK@o1q`KuoD2cvJ(MMW+wq|V_N{Xv#qt?u~XQ| zfTyx;fIHZBz|+_%wcoPS*{OhMupNMBveN+1Vy6S1&CaMj$IfAA0-npx0^G^Y20Rb& zH|%_N4&VjsT)@|}oq!jz^8hbm=huGCE@l@1Ucz1vcqzLO@G^E$?N`t_7Xx0-E&;rP zT?+UHb{XIs*{<5N>`m-)z&Eoi0N=vi0C**PW9^sht?W&JSFtw(Ud`SD_%?PW;5F>6 zwO_Dn*;Rnov8w^!&fW%iJ-ep%bGDmZ3-}Ip9pF3J+X3Gt;0^5h+B59kY&YPI>>YqN zv3CN#hrO%zGdQ2mBDbx%M;m zVfF#QkFZ++KgvD`_%U`X;K$jAYEQC%V;=_m1p5f!|6(5n{3QEW?WgQh?Bjr+X8#6w z8~X&{XW0J&{4D!q?I-MW>{EcBXP*ZA0=o_H-vNKjzQ{fU_$BsPz&-49fd9ch4|qHK zLhT9mW%loYcd#!4euaGr@J_a;_9J!|`wzgq>~_Gr*_Q$Lu{!|wv#-=1XJ2J^0)CC% z1^9Kg7w{YG?%EI8J!~J~H`#u`Z?Ufeew%#_@Lu-y+GFfH>>Gghv3mgTXWs;TfPJg> zpJ->k4fr6t7w~)RJAmI8@F8|z?NRnHyC3ie>;b?7?7M)Eum@|8ut(YV0C6wiW9%Wo zAF_vQ2iW862Y^3f2LPX7j{yFdJqq{}_Mf#Mu%EKW0H0((1pFC$9PlajquRslY4!x* zGwjEJKW9Gy`~~|d;4fiCJcJhSXMn$APXYd#Jq`F9_Dt>j>^b&xz~8c80RE2s67cr| zKF^-5eUJTt{R;2}*a6>Te`LP_{1bbw_8|K+`z_#$?00~FVZR4_i9HYaS6Fc0We3>{ zfPZ6u1bmtO3Gnai&$S0&iMHq)H>140RV8C0xfQu=L!|C#P-HHQ8#i_X5F4_nx3Z*uTw>~cBqM~~nJyb@O^%jtFEc>$1cJ8;eE^?LC%Z63PYsbCWy zz$e5JUzOM8eRN%(+YoP5Tn?`nMWDCShSyU4u*o_wkBX-p4l0HxKnJfoU~i}07Kp(H&GoZx+814igYHcs5Q;Go-`iVv5OcDS9QmcR-A z@i;Pkzytv*x*Isb=e$0jth-wli|Q<~juLpV$ui?hO`OOAG;l^Z@p-9cuYwa~E6fwX zTj^_fi$*x{xixzOWr>J~;Y8I1PVjxdhuFlexaxGGPfMZ!4a6%Rj{@%SQb|3?a8o(J z3~u^I(H;1AxCBnz5+_tlhZB{jR{0xqB5Z{x&=yNT0)6S)bLc<@zv z+6|l$1hw&u;FCbGjuX{e z$B8TjL640SO48F{1-DO>m@?=^UAGpNqh7HGWUf-tZM- zeED%I0iM!K4`hdm81UgSP|xL%WfagsCxiwth1=_OfIIwNuLu8bFF1>Ef=8*U9*+~% zbhsTVJqY&kxJW8cK)h095jq4;0PsN}3&4pg>+VshM1l{XV`#%yu*ovxiYnehIH9*S zHelP~ajGiH0?ZQ?T5uxnaxlMvr|6r&e9(z(T;1o**1P2}U3$ z!A9TpI+z;5Q@DVWK#1xZ@LKlf_4+_$WYjl26^!_R6Ws38RiFmd@}Nxh8*H2e1R6X( zR72ndMa8?si@*$CA83gZ;#;5raDt6Au$wqW;Dic;%WfT?@#y#%l@Bii)HEIMrA?!o z@CK*TFEb=g00jm-5_fc6+*k+rwA1U-HNm{}VxijvMjAK?gdsZgHBde*=v~1$UDe6Wz0f%Bk zgNCQDxddnzu2IDvEiz${ydz>e8P#(Wk{Sz^g%{! zoDeO24yIXnEpUQTXcqLkPxnRb4JZNQqbl_cPlgkIMuO(|1vN0d*AM%Fih^pROg<0| z#nS8NPB$>2it9erZ{QvX2yOt=APGJ~iz!}D8rVzNfewQbrRSlDf^INskQ5P6;-?nO zX9Ro;jP%%_2BOuQME)Osf z5ZCvr$yd0bZEbGWb0acfm-se`mrq{3fOs^LVge(*tDlcUE zrTJuvLXnOPh%ewPbOEK1I55P4uG^)Tn8T-Csyh_)d+}Kzw=FoqXI0h7f(bkYWZ}e2 zLR+h*KTTUIl~z?vN49}oZoo-Ar!lZF$`uVlOZfvXzf04g`2rdWq)`C_Y6z!#K-CPF z4=0AM>9}ZUAzbrnJ|G%J52( zjkf}&cuzQs7B*mMIeXIrMiALl6}jQbRLNjqVZUy~Lr@C=9Sx=|l1>Mg3m{+t4F=E> zbo+J93L1J)*R-G>0rVMo6tz;J`8BA|fZIZ@=2M}wLxg#+s(buYN3X8N{2)q9B~Dd! z13c%~qOn+js%{ujff}lFFeox~$W<_?>4vzVS%Piox=vq!a@Odzs;0ViZ!CfmXeyDM zKB?L`p&N7;v+{Tf=)y^);Drv0>Lq*A^@8VF-D^K&?sgPc=M#sv|NI zz(gPsAWp>xp{-RtmPlyQe2R-AQJq85b}+;Nn308z3br7%ZSxVngDqeLJcch3)m0GB z5VQtA*f^mZ3^$7wq11saoWx3KO^movwl~8Fg(;hE6dRt*cLmXks{u2ZjzTTyChP|@ zXyQLe(CtRSz(Qs)6f{C1j~X;$p%9dSVT8>DDg$OPLz)rP3S=a1OdVo&vgXnyC2wrkOIk>@8?oAhMZ|WB|mIg>Dl# z323I7i9$>1CfdpnUSw|`GWdX#P%sRfggpV%h=s#u*ffH`2})JR31|kKfG=&FfPu6q z2rO`d!kC1UbRcNzY2XC<#gsTnWimESQVpC~gbVr#Z36#c2PViw5OH`oL1y0Olhr|g67R3gfL~+kyIB6dUGc?b7I8;cWsSbuchQ}@w z$P@z6P`IRJg{%-T>ea($DvFb^8H$9{AgE>q!j=&X86i^*g?zrCH$_wi7l(`l6oR1| zVV{OKg`u2t8W=Ta=wVGW!eKuYMJSWYnbM@p3htmfN24Mq4E-C4pcE1ZDIrVL_}F|T z4`5(lSj}flpf*TiVBnL6jT3=Y4@-CBtsoFiGrg)B4rjvy_7)CD6Ua`BI|<;)uJI^? zpqbG~dm5TM6h(s>s~a@YC>1ao&c&iuJQ9v2d`85|CgPDeN}Y)2QDi-4L}RFx8L_lT z#P1LJ3K7eSXr>l1(%aYqeH1Yf`wl{m9P{)pCAKnWrtBDraTEp42Lig;OW5XKfT8;wQ^!QQCq1f=`(2^tr3tP!V zPZ5n`Bnu`cEeOe!y6JIu%dHW2uZ9PQ=0~+?i)WG*AP?#)nX+PFotP1i z!^%<1^lGS|gjGZxEW=7_A$psUw7P)$NS9?LL-=4)w?d(KM|W3D*iY$BQ6#E!HY0M9 z=?u1bDkb@-9HApMEH=%Ol8@q%K*H#*#KIsRgdCe;SvF2YCH$;?5^sfEpcoyC3?rHB zOe~>oc6LcJnJb`*W$bdplVdAV41yL(r~2E_D8|xiRL!JBnFy*XK)jO8B(upxCKs?$ ziE=KR%BB;^e7Xzt3}wUVY&4OKCKJJADxk#zokV3b0=?4)LPpF;S`WiyOq=OwZy1c) z8;zu`a5SAZq9|o|Z(kxSwjNO?s&hUkGSb;RHeg2bQAd=HbUh{cC@uIXmW%~b!M?5p z*f>UF5RF1cY@CRCsH|fePXSpdM)#6nFrDs6ub^%D@)fCcp$&I+iaQD5$%Sik%rLBI zHalF=p?|Z1c)d(ivOoYJ)s@es3+Yt8s6{fVu3{lu$feSyY%l0(<*jTUl#ZuUW;&zk z39W~y9EyiBu?h$o3&pZVB%94;L)qA1B%O^9#$wq>B$mwvW07dOe`qL4RnO=9MX{(c zEfov2Wea3irE@uPA>9p*NueRvw2b7Vtl*{@%vW=j>^)g$gCfajKODlyZHB}>K0UZ~1UNlR<(MPcLt7E!=b zDLc^KRwx&8Z51P4%nek^#d0y1uarhm_E>wg)Sk)}()p~FFBrj$F<8hK@{weukm^Nc zl96P|jN?tENHMh_UMQv)q!N(cRIwON#p8vsiHU5x*v17SsNLwO2!pCru3!UVq!~Vx zrXthzVnHCWl&2>^ETd>mjO3C?mk4bsD~0$Ov6YIbgvN%BLH8R>hGLAax2#fWyts+B zjT<)=OC7za;;^VO0X(+ERjYA<&7n5lAU zytk{;RcUMMsVpqFwwUy0KAvjTPYj2BZ`r<3v=#U<)>(p#y;`mGb0Cu~?(7s^gx#H?QWcd@`TS7xU%(Kz?CEWzWB_FryqU#2Tz~=^p2-qdFt;^ z{rRasJ@vb%e)H5%pZdmApLyz2Pc8rH5;vlkQG@?)H@6}>xA@SJ-=e58xz&&VswDDB z2jaY4%#EmG#P}iNOhu%bh6pnQk!2<#%EE{wiy(q5hRCr5BF0kSh72OaaxBjZhzKho z6098&U=>7ubt2-c8S+h!sLn!c$S%bBT!46~mk{xEHuoYH=~nJSq_2x9!fO-LOT2~>m=>v$axew7Q&mtz}w}@2v zHM^S!5Q+0$#F_jBaVLM~8rRwLh^6`sB31r~h^TiWHtJo7dU`janr>n@AbRO$)bM?X zP5KC;l)l9tLTt_J5qEO{@itc=!iFMoP#M;ot->`WvuU)g3Ez8^uYC2Bd z+PjzWo_v1ptYv%nHk>Fu$ml8{<><*T-{WXszJ60_bAESz_o^Mc^UL#RZr`!T*)EQ_ zaN6$8{rNp?!=|$UkK0t(GqX9>NS?NN^FqAGN$DWzsu3VPdGqY@Ss!+(!?YU>&rakv8OBFV6#$E14Nm1fAofWss>_M5`UAXR* zZ`rVE&rE6$+q`=>y>r8+Qen@PyLYE{qaOD8UUttB8Jr!NF=J-{6L<$OF}IgrxehPG zp;SmwMyXILpq!hR;oH8RH5)d~p`?Y)y@c~_(4ON@>E`<=2A>k&Kw=a4cmZS~4$67E z5roB$%-RR7>SUg7yhPR#)Z4ax(;iNB+QVwj*bZD7~Hy@S^d%?w{P!bUC_d1my4 z(O-}47<+mAjSJ!n-Z{Z0-n@`4T)psp3!j^unY?f6+^H8A?ON0(%7<5db@jT{&#!s=+M%_NA5}i;(xVuIvmjROTBF2Lgxw+ChE@bvOns<%jB+E6|4uEk)_2jNXwtN*UdfhA`5~rUF6N!se!>*AFuRrAz!g9h%+`i(x>$EKF8?D zVr5*)U4~~lE`n^OB5p1fbtPg&b3E&aSi0g7`Q_1HR92UKs-=6~#pUHyD?*vFwKLt5 zGn2iGOT^zDvsrkla5+2M86WISbad=Yk9NfCmwk$5I8DDEb_7F9rs9=++&`-2I!4>t zCVSGZQ%EGXf!t10)hq`b2m2OaIUymta0D>+y^ z0!82!%&sA&i_{=eMUm=6s!Iq!C+N7kA^o;&H>H0e;eBBXWpb2}Ymu-el(9rgSQ(tt zD?SiM3ibdb%nurwpwX-g;n5~cMw2O9W>c`sO(+c$HiZex!i0Tc!lW<}>2JbfHDRTi zuzgKfqbB&rLFxBGqrJ1U{S11pdfTgH zDic~T;P)N6NW+-753y_tW^^1>&I8?NAf*>dDJ??8)`Oyc$f6(VB9N69Qb=T2fmB_W z#)xdO7Q)U`or~#?#gfu7JY5kY=R#^NQc!)mqg3ouMn|C8#w%#E7AwO;S%`{62xb%t z!HI>(C8i7>BUK2Q;yP(Oc)jreq>lf@>KR>Jt}GkQVepVg|Y5@cOuo>QL#>obmYRB;boP{!P2nVHodek(UnMa zO%zBuJM2g|AuIgXU)xwBHxa2Iau0>V8Uhl~TE1`IA$ug2{!b``g{bU~%@`TnK zEv0BQ>Uf*iJ~o@fBU`^ld)Ly;k!~HYoyUIRP|-SX+)wSVkki=&4i@y7gfxnfyrxq71`&<%Gv z@c&Jp=^7my8+Bn!$#M=34GlWO@A3O~{QR;@f3d^sw{)Lp%OmHVe_*S}r$t0tupH(6 zGs>G}C+~;IDllxE-T3r_Nc@CMt}Q6MPnbI@6s?L5v5GcVMaNb}2UJB@P(^1}#q<;v z-B%UUJkTA7D96F6Y0^Dml-oh&w%b7zdvv5sJO$Q~s`%R0@*0*ORd|a}ly>WZ^Ur%^ z3zass;}@4+_H$9@A-U?Wa4XqZd%}E(fskcd6lVwEBIr^h`m`G!g zeIvzrQNy;WH<8kGDe*E1=^Ypu)9+}ce)?^ax^H8gJ^W2deKV}br7DhYN%}HcB{fY|6eIZ0MSX#X-Be2uokFuy>YV?HbyzsBg@%LxQVGmbx#ma~z z(cnDL;12Ma$=d6fo~I!Kgy{fb8m*0O5qj!19p@LxHojV=s)ilPe+aX|bLdiW6j4D6 z%`fECr-(oeqyN)^!bY)ofJ}8=ABAQ{s}H-LbbU^w$V}~QwC+JFNbq0ieCj?W8!Eme zRXpq+q2tZEa<7b3%B7;mjb2F%P2@P*$R~3X#|#Xvo5@!epx4Grx^R z4!<7bcL|-bWC0qQ@x4(w$~U=towyAL@s9u^u6 zrbWGcg5{~TghlBnhz>#N1T*e^CDb-MSz6fcck7nw>s{U1y>ciWuwsFqai>m0!=|qC znb?ZXh0C>&(=dIeGp417mUpLyd)kfaZ%F7+mm%QuTd2!YcGiBFSPB|)>i7hZA`4J* z&RnBb8LCyLrB-&&O7xrRl105E>ba7=-43j~f%Uzt7kQJSrVY7U45I*CGQX~F51G}( zQ+E3#w7YGEmmW64P$8M&augVtEFsFu^80(&bPxvm$^#R*iKF`mjxEy&tJj;G?ObwH zymLXGuOaPX6?+qe5m$()8q*0Q{p*sc6UTekj3F|>6{N^QBY5<5dZ^uQ=P`4a{|Y#& zuq{XG&p1d(`ZL$ipYyuGt@<->>z=0m%;Vu0UZT&_q2pm`4tX5IN9xi`5tU^9%#1s} zj>yrZx;}a>>CtQBs5t*|%+j6S>hkL;>w25sOHr45;UAe`m+WN=p&8=n z<-J1#^~+EJ8t0jy_<^(VLh5x52rd}HwGknE z?XN=B#R{xyG6N;g!;vy98NHiFfCDf;zc)X&vUBj*vJa}+@5#<~rTR-4=ju-_THyDY z@wCOmRLknn!p@ar`F))$Mst#uVW+7_+Xhm@cp#E?#Y`TzQp5;94`)YKbWqhXTlygX z9_ZT*kNo9(*%Dx-2m2D#Ug}KhJQ9XVz)lY;)h*7JHZU{KQ`3qW>gx;&%ounyEAUZ5 z4#v$O6@;xRAr%rceS`~P3`ldPo6D{x8D`WZ02vj5D_ue7LmI}T{CoMaRh^xy#&V11 zt_u|rViqzmaYjw*rw+#B$))YMl%I=~22vO-x2(4QRBE6U5m?HNuIPN==Ji_~P9>~r zVXnBAZJb9$jXN+W{vdxG z^vYp$Wd-~EUbYg{S_K=tAH)m`{gpwgT_AlWsHlN%8r_4m;{aK18gl^s`_RbA)ZGvW zqOb;XEJdnbFWY6+?B{x^US4Spku;OKq!%t`dHaPjMV8Us=sJ@ec#)!>DC%EVYt$VY z8z%=tqcc=N7l)h-vNx1f!bhC*OL|5N6KlJB)=m^gd)m9Rqsu!wmXG9m-k<0#hQrWn zNqJ0?W{j4HGxm{R0PWVbWh!c;K2H6LDE5)XSoRpC_C~;^BHBtA@M1^~XIs(K@LVRqLzKWdO zzi9b&W)YE?Q5BP5)VO|HKjdRcndHM3q5N_oA9XpwkxvTwaD)&b$hqR2$)7P3OOoIW zcdQ)E{VPdPuxbPBM%djdB&A{XMF%($vfljY6=Q z>3bn)8M0JGFOPaHY5HbbI$mGJgbufSY1!fC^jqs6Ji@M4(v9zOtVnT?Kgz54W5Ji(TDmC%GoEC}$;}Yg2TN?+W%u;U{T!XR@%FmadmW#ISJLm>y5@6{C|{2#bA%WF z<2(Ma7I9AwoNL6B`i~q+SP%PAZ;f4z{>{ZWrhw_Dg--?|7cMP)sa5fm+7CPH6Gs2% zMFVntGkrCwA8IQ0)Dw8ySnY43uF+;)b*h@)QdfmC6j@g<669M%pH-4`yq9Wg9@u&zQM0XovyGRQonN_s%yD^gx+03`0J^ix*0O4Lbx{WWecFu zlGqnO#zK&~5ZZ0nZBRI5R1S#LM5B%Brb2bM$X?SP-OIZ1Ku$;(JOa|%Kz~k-F_6b= z8r>18o9eb2HBrKcLtU}RkL{ig+%US8h|gM)P`)!>T#&-_nNWE7>hjXTOrbEVYW_Mu zy+GJrm>Uld1$&e6?t*1#4inw=d}eTIxpPHg(3cAmPw%SjP)#(1W{Zj1LV!s*tr?KCO-S&_yfrkX| zg~wqMZ(2jRqYjkl`HvC)#t47=(dclvQ>hdf2txBsN^1-#i~@zTnPlpQMfOoa9>8@zKp_s8HM{Y3io9c?#n1X7lr#W3g06-)RsUsH57}LNgMlc zFe9`U8c!Mr!tG3ou+i=E)LRd9xs=m``huC_B;wP0CW{-=gG<`mX9p2!78%dO!*8ge zwi$%RRhFJGJbc2^%F+#1F^RalqSaIRvYv27=_z_TK~JRzXWOgS=NBd7S(jo3bj#(h zWQrSl`DOGhz2hV!32OnbK%6kv5QEP#2D+ueS2Ok2^fl}x8_Nm8azZw*U`RAQ&TinM z&@%8LR75On``ybGl@IeNHwlg@~yMVwD^ZozZv{MLC-e(Njn3FvSJD7z7` zs~)-(Ni7mkmAUGd@c{>7E*O6n_Dmen9DVE^ds#n-J^*_ni;8H1y8YmX`uO1h(qW8j z(CA^Gkh0U}~vt|(-! z-Yaql)1VCVI4lPyy197;4KIGEb8ax*UG80w8C~5QOAoe1rY7FDdvay0GM2kSjzJ~| z=DK^2N+vgs_pKgI^LK^d8J$@Chw7a(%TfbvR`u^d2})OQ?|a$O{ou&MeVcZ(-L#7A zu7R-jsPztsdXW8}67>k%Cau)=)Tbk39|kP>2FB`5k{~iXW8+Jf>@2S+88BjfuH+bw;KDoRQm1&^I=6g9hujnbBGLZ>*AvUpAu^W}fifv>JUOh@HXD@CRJ1AzvA zzX1I-%FcoACMA#7Q$_`hp^VW3*pVaUo1z288{CkhQm0x<-R$e^hq|wqI)zeq3cd&- zH6+U4@Q%WdgR0mJe|V429!qQy5B`~ps1ol8r*y4!VlVif1mw7)*9#NL8=Wm4(}(St z!qPM!Ap)d82tDQwnvj7%l9X8F)d)M)MBl(Me?4ZPFF>(0;49Dw>svxYUjv&RILKiq zhXgrxy{J7FXoFN3I3wSdDUNjsDKzk5yDgjAz&3H48O`pnt>$fmRCtYuH>H~Ag% zh6+Syi7GOM`Q6ngh{&|_jz(&A^#_!vyn|r%w>(D2LzfW5i)B68O{k|vQ5OH*aT=7Z zK2B4QtXNb3r1Hs@PjIXycaV4B{Ta5X`F_M~0cTAe@*3V=3W^ZiU9at7jxT6PbPRt% z-=;=5n8oz6i0013VjUS%ju=&4?MnvJSfs+#%#b^j>!d!Lq%Zlc@@(gkGN}J+8ML{% zNxh~}+SJRE0A!_o3(7G};6hj0uv3RiDN#>)SS^o_+t$7iRYAo7CZbus>Omd{F9-d6 zw)zC2)~4m3c?I2m&0qal^@lm_Z<3|t>@h8F z#^_Axu6`sM<)7n!9R%kB8T1}rAP3P;zqsG>}t4mQE!S6qTm+~w1EdY z0=gZwr3fKd2}ord^`58Q|7*@_nt$X|sdE;B5(JMO!C5hWn0lxPu8Nrnm3LC@;wDDV zl-@(n*DDt}^fM(wCx_l(##IqcDE%6w15Ma|un*c$yAbv^)KF?coTF}tML^xik&_ZB z>S?q$YO0YyXe}*5tH_W`i8=<-uOWL`?ZSrvQ^ypB5r+I;WxS*|J$?G=)7S_u@9ti? zk|x&?{E5uwK6zBu(1kVC7gy79H82Oe2ReytS_|5Jr_}BV)QhycjP2UX5-1qDrzjY4 zEo@z+$W!LUJx%RCPjA`mEOd7S01cX2oQ#Zkg+CZ zee}cy6Jrxwt|KyiZ>C&qZ(n?ka)XsxFtO!?vGMAkMAN;Yt+;r$y}kNpQJ?YJN0d`Q zrwsbt_mg!lZ8@^VX&A*t=ct8_TI@rmU82)1-K%7k6Nm0)gG8|uZWTf{FNURQq0=i; z%rES68YC;CW};(PM8~dJ@7NWgw~Of5716OPqGMM?$F7KuT@f9-B06?ObnJ@g*cI{B zB06?O5tl;9?iQGW|F4w=Obhmar_r->%o{=bVY=V4*0gKA06x(xy1G}b>h6+| zd*jyh>2=1a{_XT>HRK(3gj7AGC@a=PP!068V>-N06> z6@bb=2noSDzx7f45{$fle{YajA8uB#HqIgXMqTWv=K8AIVw~>V(z}Gqint7?I9WmG|#@9WV9wY&ORUuB_z6et zP^&LsX@;Q772+4=bBnS=W|8J^aI6k{P>d1IviD1G=@iODep1P;G>ArAIfYV|MaqS1 zgD86$$EL@09*^2?9MMz|XOiVWy0V+a1wzS_Ga>TwXd@;>yHRMQX6^eRr81HYZD51p zs8e=W1RGk9x|bC<*`sfj4^i-1abj)P@Y>R=1+JAA9^Ku0e8&+XZ~Pn)wAMM>8xQMp zAp3}5wf+?YiHLbbsGGp03%L9Vcr?mp_ruIMye~Shy|-PD0z=f>0X0bcfm(`F2vd{T zGQecB#`i^lDZhy{&!@hQ}`rw^JHX2x)|^r4hE4X4hf=0QTFk z--Z2t>~`2%8ewbjl~LGQ-KZq5xllK@NsTBUIEmobPiUx)wJ(bF^h6e61RK%$vj321dU$L%Z?jsqbF`UZ8$YP$KCop> z3=R`J+L16YUThyQR)3-r2RIL5LcDYDT~25*i}OeQnY3TI6;2%8($quWP~cV!8#q0* zOH6{~!uRwS(5Vx?K3mt%YoX`$QRa(lmlZhlQ>8#t^AA0(v+0dZ$06MO+Wut2iyVv~Ry% z4e4Rmy$`r7Bjo3$>Mw(q;q&rwK7_S@uyjWCIgV+`hU%$4T>UPVcZ!gMmH6x_)F;V~ z+m8ZB`QL}A3#{-18r17JkN4&gy_NTTP2RJ~)Jh#&Ej)U!Gi+GC&wpNo1vu}8KCt)# zev-$J8TQo&`Iy&-I5=h8%E2+ypcf*#+=<*xsX& zao##s>fv@rWy*}X`N`F9h+&EgaZ>#_UqRoFnE{`&7$X%Hr4D^7q#G39FhHTAls$AZFsMKoYz7K25$oAd*fPNnx>uUTRJj5FxFH-&Y_W=sr+a(w02 z1o0h;Z@LpSYKd>M8||j(+qwV(1n#MehTSJDS6=S`-AyNVI4s>Caw;FdS|DLnJ#;bF z+p@553-v|L=P^;2P8s_n(o8V`(Bj0(H_u zjwC@#f8{n&r#^lL|0(JeMH3AR7Yd)^4}~sh_HC%~#R6vFL&&`hV1P(K$I=P}!uYLl z*DWgCl&-J%!y*4yur`o4=u*!4s}T+Oy*wQZR-g5%1{T>N*|lotD@JV(dJ~7os@dZ* zHW!Qv^50D)5d{}S5VC)fGRLzhvw^-YxC`ZVA#TpbcCOw5zZy%1_`T;CG{mOt_J;xy z*DMw|3ef<&g0<@pavDc0W7PT$d)X*nVqzZ!p6fAW6tyqy7P8zb2V^W+uW0$mf*EVr za&`=Va~4Q*Bv5i;1$07(!{&Z+o7BB!Gz3D56s;D;4$~k=xfv}ayvA^gw+?ym_lNYh zHI)+wdU|5Z`z~BKkX?|$f*h7vTH9Wl9!M{{u-q5-c;bEKg$pK)xGSJ_S5_|XjD)9q zdri}28eugW3YRC!ZwV%IhLKNUF$;y_r}>wG(*&w~C29*?(70O#^>m}=ZnXIYQFH1& z(byZJY$4Dc4IIp~;_N%C7}naYH$qr21YxcRdXI z;v2^ng`(9jnv2GW4=FC}E#QGp^8ZupF)53>rnM!zS^PF?D-h>O(H<1h0-qib(1ujq zvqfX_^~hE-H0z=BcHAoo4u-mhk^{>+=N2Qp%j<~`C&v~o8cUAEydJom7teJr8%Xk% zRMspa^Foe*zG&=IV~el^U`UVnvZ;Zk_?aK#Un={@YgsD5?ro)=4{swn?L)WN$4U*_$*yrs#wU;g7PCiL(a?a$ zD>RK@+!Tn3JclYKPdP5PFiG?yhQ4)bY;r+tDi7LGEkAVJsX=Zyk{iQ+V^hkV``Z^uj+8rq?{!rUP-Zi&sHA(2vj3dQiHi^j4ODGyP(xS~vC zCN>}}VD8-1*w~_9{c6G3l3>6=bT$ov0%8M7j995NiB0fTA*ZDKitHpgn@$!lggT`f z{?2)=(!5ZG)|o5B9TdnY$M-1g70Oh^?AXjqGMBL@Z#BFf%PG_RmSa&Dcei3Qw zi-?#C#@I>`w+BmAx!aTLskN!@AVS)7EAWqjZe6hUZhzx^;` zivFW%NA<}@-R>-!5;u|$_=(Ad**a+}YfE|vI;pdLu7D=YC^QXp^4iJ0T`kFYOlKiN#MZMK)C|`LOL1=42O!a`xGO}%AA4lCa zv`#b4(TBRGwxju*HkeK9W{sM@XX0!ju6DU(n69(|kF6K4DCd?Y`Ol~f)k#sf<5F_U zsaTansXdQhQTqbQ6i1WrDrK_e>ov+`^DJ1jrA&x_hD|DZ@6>B;E!`GTy0(!V1wBUe zKoA`}Vo9IX?{>1~zds_-D$4RKY_&*vaX<~9-hw*SPe%+hh94CQ} zM6)Mm4ilD5TUj=zfV{ho^#e5ep?>#K^frS2KmS-DU2sJ8=KUA}H`Fiv+6RWIkB=bk z?A?MV{x8S7>YtaROozlMq?9-1OVCqe?86urg{);DSz>H}=6hl%T_HWo6bLT;AtZVI zLJk)++85hwXri!-q^YZ=ZEbo*>=ww@UQGcUrbmXQ0Frie32kk`)nmsLH)Kyl7&p%B zsDl`J8=A7HoD)o!BMbY;`C}yW=As3JgP>ks(*?&zZdJL2*>04@H$}>sV6loR0T^c9 zZl*%MaP=H<@-aQ`!fHH`Xgt-wxUKpf%7lKm^%U@yWoOEFcC+X`Zfa)~MCTOLI&pHbI(ac~O;?_~=G}XG;q+%5P5( zt?2GvF_f+!m(5O2&eDEY_ln_kdU!?mb@ha6a>MJ&|p474N)MXPQKBe_;F8j-po9^*ls_XZLQtHfJ!pNha4(F)+X8^vNb3?TU%fqKhM^fSN~R9gF>97ts(1AT0`2& zhg%rrpstH;3&F<^GbxT)A-g*h6SffnP1!Ia60ji-iEh4ft~4A%7?Bh9Vh`w^p}9my z#yHKRWvk{PQc;9*!x(Htqe&CqF0QZxi{*ep6ER$Yn3e<+Lw8(~8*a0_ibbO`TyeA) zsvnn10<)qaTc@E+6YSsQ{0xdsp_v)==rf8ll>A4%P3qS)qm~@FuM4>}%GaE?VTY_Y zC9R!QX1i3GL`w2H5h2={B@H6v4U4=q)c7#+_5nxr)P#K>Z2Wrsx|}dd9rfl2LSz0; z-O*!@Ae1X_D~%wuu+To4#1tR5r+>o8CV~ZXYwvixd$L%W$soQ6OZLY{v!$_I{$*ow zG&j@+@2Eejn~tbAm=VJ@fgi` zDVr0UR){^2&_q*fKcE>lMbpabq$g{s)%d3#kF=NU4$tUvqPMjF+mmY1t40h>&*;<( zkd5jd(Fgc$zTIgWfv}=DthQk&0r>3>@-GVB+#yFgn_~?WyGHXigRpEd%N9C`#v0~D znlvjiZ%U`_krY};2Iw_L38crRX_JVKDL~Hh>nOWXw)x`&&9co4)3?V5ns>}I_l_JN zhzP-<@d25l@c~MqRsQdf53Juo6^b~oQ7{S;cAR~hJl%1&1JiXP$~ADS<0^k^^}QGm z(D*%WCnmnC)pzpqX*O{6CVnwSM|7vVI)lpF@lX`6M6@Y2#|P}l+-B|O)xW8^ZA;8{ z>Ae0@8dL|v-5xq=3H9$c$N3{*GMMRV z^>Th~eN5{66U#zaB%Zqv#7T02MGE;lux>yK8s09mEsagww>p%_QS~pz2c$<^Ms0d* z7er~f8T0VK3&MCyn{iL|FMp*pW`p|q#pEWAn~0}+r|mE%Ki~QMSoqWj*)jXc7lcBR zONU0%$top(Pzp1qa>y@&tnjkad^U<*CKn5bkW$Kr6a}TyjSl9M^vA?}F))PQ8X_FY zW|Izaa{bW+PaL2yWgdKgAL}g=%NBbv|0qjk&i9lunNm-l(pk(y`etd^sbFSyNO297 zzD+q)i~jt#%hS>5O!mBzV4S@8t;znYPJi>&v$N}K04^MRn42f|pk%COLXGIb6DS%X%?-_Fs(}-_9Tz)&5WmmC9Jn$x&nR-XButgNKiG>F%5L|mMRg`ZzN+L ziem-qT?Lx`67u{n*29|Tc(uncB?)C$S~xH~hq*C+S)X#Up-xx+uswO{KB8>3Qm5}F z(2KXhZWv{7W6PLen%B{^y+|r3?v45YGBTLL3dsHxls&9Nby)rQF48X|j3*i0bv z*sHA|)U<7FJ&B(evF-kYQ*(1uRQyApQQ+@X+%&> z*CVK=(fZKRQ`2~88WB{}h@hHA1l2SmsHPD?HH`?WX+%&>BZ6vLL{LpP_>NretH5)! zG}mbrzP<|5y9yqeRcH%WVXo6E%yn9Y_=i=P>$D1UomOG4(<;n$T2-Ixvb@CL+X z^`VT%kf(tH1Zn*(FZOY`jut{UC_-mZh}ANr;QS>OS!p!wq~OJxKCpXpaxf~^Xyrwx z$Yax@pU<1mIX_jkd6fCqN&1V(}0j&?5fE1G;}%d?H8h$Il`}{HH|VYfARFMQ}`F5;PX9;rQL} zgPi67OtM>ArlFh|Pi?B4^dsk~kshJm z^$3mzqzo%9u$=f3&FLTpr%~0Rc}oId9D7tY5R<$av>>}2SSCQUrQmYAQRQOEmCK2Z zFBkBn+nk{wMKi#SXgNQ9V)M$oFQfEj#OB)x@S+E?7iRSl-fxBxpizB__v>W^52FB&$U*V@X({u*8S!<2QS?XBzT4^qLZpCjn_v`BxJAevkS zzYl&1|98-DIo8R2AC}(3$ChHJCHU&SWx_J)=VL@aDUY+IIO`E-G-|yR_pZXR2gl3pv$*9?u06-GRl? zH3P>kj7^vF3%c-o8vclGI`LCA$wDYSq%G;5O805~U9*WU;bT_V(aO1eDR57bC1f>_ zE0g9MyRnBT;=s0J$h;Fe+U^*?!FJ5Q-99Sk)?M-SBS;I&2y8r~mB%~nc!J7l;5Iolv^Ps+6csny!c*5V8#hiAt{ z1JaG@`MPrC{Cv6UX1%3pHs1DvGIs0&X03d3_0^|LOq_D{>NWDXX7PFJ`}^0Qx7a?e zT(>0FV`*XhmQ^y{+1uNhPT&{$@LPy(t0%T(oga^`IqVe!8_vhG8wLh8;2r007~tEM z923y-D<`_k?CUtavp48eO#GIROAoAD0u(LgH}E`gNo)RHEXNfo#+}9$)dSdxWZ-SA z^#^p&Xes%SXwsFOLrg3GHOCW6WXzQ0JlIjl2CW@P(-=!^RA{ggodJetCfF_`!jF~rHlyE8^XGp(snD}onLis z;9n1rKtmW5;nNBJ9fu^=DcKy5dG*{mpYMFt)GYVur@O`mOVFs6A?9dHw`XqhD2$^IBwIOa=Ze;%x(Ma(#XoO^THw27`t=RMal;_f`Lc zOd7sJOn0+>O1eB1{({h$n5g`JwGNiEA(Ls8bO9Fkd-r}AaB_Xf{W#($P(?dOQD)S^ zp|(%1Cq!%R%4Ke1K(B&y>40qgj;4B1@IE=6MN837{MFwwm!j#d+z%DYsj-B>w}Z_n z&Y8j*Xvq$eSYEQtWdz5FKrhTdXfZ#aNNL9Ge<9ZV6MZ54kW$1rp?@}KMwhwpBRc-6 z(NuEDz+plNp=`+U&SU%gkKM^+-r(h8+Eaft8XtAyUn@ghuUpp|f* z2hL^ei?BmuutPD#v<2oP#X%;pcVeHwz6$$h>}O%W9Q*ayKZyOa*zd+pk^D5!4c!am zlOvo)cZ409fgPHG9h!k1nnBZ;fgPHG9h!k1nt>gffgPHG9h!k1nyK5N8Q7tC1KvtH zygBcrsl(+m4pJ5<UZU@mmVrksXU9Iu=Q^3rVnM5`39N$0CW2MG_s0BsvyJ zbS#qSSR{egBsv!OMIA&(&*uheZ=syf-hREgj!Jtp+CH2q^!FEv{r!9oowQzDI8Z1I z&`vbOx*s6q57Cd+(6KsIMy$(4glPmU3(lvSwm0laJJ9yk{6Es$LeZIIT-7~6Iv%G$ zo-&RZ7>RcKYWHlg_$(=dBYH#)KXpm2a3T7EyUEJBk$CAwO6?|wB9%ex#7ok0q;r|F znT#_mV9dLe`_M8kmn$;J_0MSCXzHXXlbm%ycL=~d>M{?;iVJyfs zCR!SXx@UnFMV4-&?YiZ;-XF@QLirF@me9>)Pi%B1shxc(d5(Aqg9N@WYwcFL;PyE~ zJ{UNjnbE?Sc>{i<(DoTlVF?~u^Lm2TdnA9?n9x+q zj$jakG#p%Wc5P^s4C)E(9{I{>$+H%rwVBFp@#+;5b|2A-0vyRcs?V}0mp)9I#G9(1 zs%Z;90BeYYzIiq~Dvr7#_a9UdR5fK-06)~FQC2G4UFe#kFuoa0e-GpMX_BQjK-bwvUp zId6Ca{I2(azuRa|Aaxj)(5*e3#TYUx#E=vX_f~6dwmEzWxaBzoW26upTPdc_VZ1! z*(o-d*0H3uR9jd7*g3i{}UdK0HUmvJE2_9<9Y#8e4KQNwSbw z&u=4z*+8bOERb6^3cFwd)nRM)tvV1YJGS2%v;I1t1y8i!n(zogdp~oSg+npY6NeAG9a6|3*V)lhINj$i+_<^8Z+KB8jfWU^u{ z%*{IA=c_(`M8=$s!6o0jZt!6y4vg5K0kihV9KxT2V#t+00DnO=$H%bSun4{=Q2qB7 z;=ofU@JPLl0UX!iIDz8?^agB7yC2f*(b-EXHC-%oWG2qFn29#7;bgT9NqYnf8T-*8 z=TJw4d~&QL@S#B*J8;aw{W5_Ut(CgO_N6q|>OOo~+xn7>Xpp4Bt`Gi}>XH8~%ZU<( zl{JU0;;nQL&z*-AOSXCND~-x=tFWrCnJ2D0bk{uAyz{VyynB~Yldu#$dpGx4Sn%Eb z+gW;W7QfsZRNCgVG11^34v&zX#h?LVBZ&qizJzUt%9Gxfc3d7jIV|wr#CcP)Y>dtl z&SzV2-keWk-?57Buzy7X{#kSisYzvM=4qmiG#qIiei#Te7w^@DRK)oGH>`nq3#(sy zBntW7*EASDdN^_BtAgsiWKn$SRV<3#WKk%GQ%Lar0Q?pu;CuqVL~D_J=C;aY&z{AI_+qoB%<0HYq*PU*twK(E{$32 zN4tUFc{t46X_`Q!5~blIOmYn-Idq~l$u%^G8ccExIzWS1YcR<*nB*Evat$WA29sQa zNlrZ-4JJAIJX@OPlMH)Vg36qLN*#;iO5z!^Q>b_Mzim=cYXzCK?c#qe%JbOj#{Ugl zOj-ReCH(5cxb44hv{iS#rsQusOiO?VY5qe2W6U&;wt$^TIu(oWVxIsS%RpnfA@!tz zJLxJ&I0`-j9R_hK7{H#U(=^KL@qQR8(uY>3g|y8vNG{juKO*EG#3E6qF)!Rmjtb8o z#pqi)y#uSn<+|b&`j6I_LjT1warq_q$x9e#i8%i#t)fjPN%f{+Un1U92xAg}Y0xA9 zu}s|Z_>eChd>t8U>#>xah(e%s#~fE7$U6rw!b;Rg)aL(euco0sPn1O;JV zPtU$k7zXS9Y#rNlAmaZ0#yaPqP5a^_ejL&|QE%uAVY(-Vi!Iitnrk+#y*p;-@PN#S zxNdGf9$^yMKG+?9A|$AvkGIKP{_c<>#RxI&;)(^PpO<$?2m+VDn|F|(Y%;j$ye=o* zyFyEw_uDBW6cOKkGUnxWf-*);t*Wif9nu|><4$V;u6wOp8XZz#h`tk|N9~G0)opb@ z!n$W;`}cRhjVn~y&9$m(W0A_id=3MZKA74yO-3eCdC2U361HHLRUE6B992|+BGz!EA%Y%Gl_koy;z;5DV3T= zM_Xly4UTwc*KA^~)N?xFWB2E*_d5Gl(QZ6aMN^2L2RcV+u4Dlrrs;Nff0{C)2bMV= zTtGl44*|p4kKl(%SIRm2)KiVw>dU6czbq1;wytWeZ=@PWt;4s?qJYyK>@_|5*kh5V zwR^?*&BL%f6cc0cIRVYpo*|;?tniE2CUv8K<`GJnvn{i}A8lVf=@uz=Ekv@2d&!zf zFel8Rk}*`vuzPe&M&&hw(N21Fgzl~bHG!xLF5>KQ&`{U5bOyqpHfALGD(fSaY};(SXEruOO-1svwhFo$%zG}z}feYQ=55pB$y z2*rZ4Z#&SReX*&OXSYqUj{i-NMl-y-e*vf zP^Q29$}$C@OrpnYS*F(ddRv(S9J-8SnGFAI(Nhe~U!Y7ESf+TT@Dk}Hc4A`L)6KbD zZL<4F(M?lf?Zn=WWi-Y+aka7eYn1b}aVgrf3j~#ZmsnCMrR?g<7HGV4X&uPwSn(7|rg z4>P84ZpB*M4LlYF`Snk`l>VTDsHgj*aAHNc8VvQCkitJv&|tb>NTEIH=vF5Z1-wkGmPP6hD>O%I{_R5ko7@ibC6m z(yVy2T{k(_6-llVg?Fxvs&>mZUEH;sDnbW`n_7CR)RszFpjBr)R<<4~)%HDDe(f-J zcI?g4Fu%oUDmtG37A>rjT&HN!O33Me7SA}0*%zTjW{Xk0H7#s0orEvs%;3fN!oItG z$$TNpXmNy-*IC|Zb7ts(D)mlt#YJdS<}`b>rq57`?lcUA7Wxe91bv48`hFw9IR@e* zG%|OVMx9W^wBMyJqJ_B#^_I3f__*l(Z7Ii|Ol&xSisH>us94kqD)#yHR21rE<3sgz zXQ|lEtlj~y?RT2zFG8(FPBXu(!E=;s*!{eZ5!!l=rW?xck57n{-PQwao31uXd8-wv z>ayP37v}Km*VE>G{RSGfZ`eS~`1jVsw}qeA`VF*bM<(JvVX(bWXrIozi7E=%WjX_>Jk1zYsVhP=V%f0AlssU4rmV(%Xd+aUeq(8TN z2b8ENVv*4uTZz56%O`Qp#A%E?Sj&?K$0G%OW#O zO@ZNIAw961GRQexgFoioZrDS97B=~ypxxS~CurpC^)uVX?@p_Y{~@oR+OU%ern4Ex zkd}_edad3+ohI#eyn%k_Fk@>{aC@FX4Rcz|*#Bo~C`(<@pLc?Wttb0-Xt={+roRXc z(^|~*jx@|up4V)+?R+Sf+|GPxx7q&h@u3XdVj(8l_D;~zHbi3E!M+324mBTik`OLJ z)9FqUf>H0^OVPj6v1i|)`CPD5R-`Cr%G%|o{~l$9sO^of(_n_0P zcoF(8aGDiiREc%VM;JdNunvVchJUyBR&9CT9rX{^2eL4m17UY-RD{;rlD53MIx0>& z@bGFl*Q#G?e)ctASI=p@E-{A04zkr6_iY$RyoU4Nf5%6+ypDlVBL-U+F<`wI*v-Vi z@;U~V*DV0j$_%j+0gUW<==&C*Q88^nrU4y`2GW*nzgvweNH8*+hW ztg4;V6gzsHbI`0JsF~KBS%@rxewZZA`*fH6ws;+N;<&%}RfgEyz_{)S{c6@SPJ|Y% z<3@Pzzr0uPL4iRDeFk<2ZT?GPYp}Tjpm4hW6+22CI*(!VR(MEtuBR_@xR3<1ZAs%!{&$tova%){$YTl@TK53kw^ ziQ7#D(t-tIkR+;xOLhC|B*R>S8`uU1xu%mYVGV;F1)Tp)^b}`oq-Q^&XTM)f&(`wH zCD3!3gN)Nzdb&Cesx9d`LOfw!rt{}zm%yJR{~i1(t9S`6Oq%u=n)Vl(inWv|ReuGi zwk_dY0)4M_l6pEzz-zl+vlC|Qip1Wf3cPTbBIvRneC z<2qL5c9!g;+R?1Iv>1o`!`2DZAEi@&)Fn`#XXwbGzhNX6C*MMS(T@-NRn#}qGE=h# z)K4Vw)Xt^&iW;(I4FxRok9fp0dOYNtOMjVqbwXsJ035vJHSRA~b#2pJ2IdZyY@;}5& zZ>%y|R~u=m7ZGn)ES}|x{&Fv5yP?bxEth>H%97DK%fXeX9>CXb&}w&Ptq<^abN1aR ziY6<|i2=>KwG<{$$)Z3x39sskmuKNO&5dD>?KcI#Nx@_f?lOEQ;CsC0%0bgzX-8w| zR4R!YUsyEfVOLH&c&JYhnkFm^>tK2Ua*OX~kL=qkw>wR)aQZ-9*TQ#p)~K-Xk!Ep! zpE0ea_xtowVOdv;vq%JoeA(s&zgLksjD&;j#zF@N+sX0q$@GigqmP4I{I2l>#i3%B zDbng2e_*f8;%qTNbf02#EdJt$h~2?O{l#ZU<76my`=bQO&>hHgC>tml12vIe4w?Q6 zkMbRw9>H-*2hQeu^={khsQ-xe9neP@!`e4E^7p-J7-es(Z+h{~-dBNldhrsIfloiM zi5%*bsG}WuXYX$tjefOvc16R~a%l{R0Yb9A2Wxcl66MmDmUT65tzRvp%{3dF1 z*$pG@>}x$a=r3~gC2?Mx67klLRHyS>f&KyI>cskAndYCT5TeAqi0Znf}ZztC~aF^Df-<>;?0pV)1&e7a9lYBoAH;E`Qnw)mqRK(R zI@`Gq8Jquwvdj63&||D9f3*{|`tj)YRwA|nr?q0?7R9v%Gibr9 z^m&f*7AYen{-|gq`rzZJk>~?4MPyU)>FuQyQEHVSj9MyBPig0W2iqi8yPhIN=9Cnf zQ}kxhMOb@=ZVzUsiGG@#hhD`ny+4(l;!?c7{(4s>g{eHKRwbpm>=>r2FGN>q3(FE3 zhIpjb7z_PW{6Y7f5Ld%O?M~h3lhPWxg|R(ad0vk$^!G>zyTXV_b%B9>e7gCGD^!m} zpZKV0Ztln$8XpqwJ!qijntfnP*X#k_UGq&D1{{s?h;;LDF$Taz72Ft^QjI}A!5*=R zIr$4x99*|!46(*$iowCuW%?w8b{U9qxIM4>_UM|w&a1w-#nkp)#5AD~*spbv582nZ z#}w#{S^^tw-?=-2A`oAY!)UEAjnr=Nt8?<2Y~ReE#2{ zhuD*G{MDC3oFgtuk5&?72lV)(gXq{HJ??g#S6b2|U!npOprFDZvKx9a{;=L3b5#Qs4~*ql9U$bj6E zjl+b{u*%6(oXSPd}S}!)bO-5&g zLLAS7A}|Rlc&gGheX*TWIr3J0dZDL-Oa z^zJe&ov~-UJhBj`*WImmL~##h1gTH>3@p4oEZOh9uw?(H=Y0{8Mz=dtpTZm+4Z9TB zTguwfZk3V&Iypyx$h1*Tw@R_giMksidxoP!B{|Ti$updc;)Tj|@aSMv(q>o)r>XgO zvZdWBOvE9FNxA)y5iPe=A=fef#s6ge~ zdfTGM0;fs01FE#KWr;Dz&KoJ%NU4y6XpYqB)h2cFepBxdn;tpxrcE3d?HCS2Z;P$4 zwg|GX)kM(&IojIh0(yF6y8BvZM zqvOySh86-U$H$@AZ;+Ue;u&C@*lCI)K=b2l^=za?eH70|nvWwl@P(WRm+$WQ7B7tH zt+~R!*w(d8_A%4ZQyXbx_b{a`>b1bH?pWw+fnP)LYWUp4%oL8bRx;-vO;ceF0cj2E z&?>%{>=S9BgAl#9jR;?Z%;rJ8inIVidQ8aHF}~89-b!aUSo9Y=e~69aB&!_p+c-ql zz%nZ(XiD&Jrz=5tc9+Z^|PGhBA%h+14 zWYZt%U6jEuA#eF(1MCcVF;V&5ZZM=4V_ow+ATheSg3ss;olK+eI@iiV#E~U?F+ADvF425%I#$1#=4LfabXgh6Uk8DJ_jrf)wH&3L+ z5n?*)fx0lQ#fZfyD8Hf;!$#2--9ryrWp6*b0$Qhlz5VnIyOwS@-9c!q*T z|Gje6Gp5MC({b_v=Ow21jWp?=C%&OUVY3=61Z;XFHMMwnD*g>GPECyrld}~(Jy94c z0WclCP^h1ckn2V?UC_R@b-{Orf1uB0VYh#{3f{>_gJ`y&*sX`nrPzLEAT^x)G!*n# z;EKQsUcl!tKEBgn&-+&-KEuZVzhz<_ARbBOB#f347qXhE z7%n{qQROr~x^J{x%n7-e9ddC}KNm#Eg>ST6_(scxZ?s(aM$3h7v|RW`%SGSHg>ST6 z#z7IAf+8?wF1`nKWK-av)D>Txx($KVY(P`nTK~#cpKD=6kUcsqVobKxw`lg5h_KPw zy5+zDYeY@8hjU!+W2wj!&*2R)E%8$F*<#&nsR6d@Mxr?5@fj@p3z`wIsKV*aME93ujo|>7%bL}% zt}wUy@y#$K1)`f~;((Kg&t0t}oBKUcX#wHA-$k^iWI4OENE>VFFPA~((XZ0cdldan%$*sesknWja3Gtcq8x;2GRC293pLK zQN=nJk5YtI=O((eD3u1Mwz;&pmFi8cn*?&3c6MSZM*l<%hBN;YrzcvkYdShb(a!Oa zwoX&1PDe*NI8$NlsS9~_4_Nf0puUgsArdpl5UZ@_BwIL6wa$_9G~YY2G>AbT=0Yq_ zm8sJ@Te_>dlw@p^1&RX0+|;X^_ER6W5PvjnR)@*lxW%cp!vcLg4Sk^+GfC=~mxKKL zgN+_eZEAqjqo}u`pGm#Ow5pfApIoe7(rmJOz`B9|Qz#YaHPpO3XbL`CT)?(#*iN*- z8``L!bNoQVD5v_t%P!Vs-y05rUk6!hr;9)K5n9(U!TL*%gs2n?GKLWax7Hs<`Nd)BiujQ&nFu1XWT@gVIc(yo`$Ftky%o9!x7W2PqW5llq#v38OAUQhGi-B%Wd$!{ zvF3k&Hy&ybSxl^B5tmpG`6FeHKqkuEk*%w_Jp)$5!|hw9A}$KzS)IX)BhWh`hN2E! z$=JQnzbKKw&=}c%nCY_-$Bbm+l#j6_?``j>E`bSNzMdYgUI9Uo(LRYid&GK0$0YKT#Izp6qEL$h!z^_&=oM!>BkqIu$?8eQD7Hw)PDj+S9q+{${?yKXi$UoP zKAwVi`-A3kLfwG3${~K?x(I1C@EVxG8urFLtkvHO5R)y)Ymk?(U$EB{hqPfdeAxMU z4RAt5ZF<}=q`8Fe496RN@g~yNJ|>{jR_RFZI8Ba6atyaR?$xSINASEww&zI=_&mE5 zi08E`cGJ%enx>Y~4AiFE41=3j<9Q)y5qsa}Gwo;TtR1~=HRo5I=;+c{nr<=7YQ7Ke zYP;<_;c(`6zq08SL(`uObDLM<-R;He(szYuvG1s|Y8{5^ZBZJ*tTn**k z{{G&-taf#A4~{g9`{gT)A6Z}|u}K@CG-}o^UeIZYUM2}N)>he?Qz)^3>4DW!4@@Q6 ziBVY$i6#R}N84)JECRXN!RE+bYP}kE_04WnSJmHx@eoS|Sw@RL2@KC#L>_-EaiMJ0 z+<*IH>E(|lu0NKz{#fGrV~OjJC9Xe~xc*q;`eTXfk0q`@Na&9xu0NN!a>7-V9I!S( z=?k&Qg~Y~?sq`YPsWPB96{$enNvCR@3-fu~W}dd%b+^SqoE#kK@0;8`F>%1a#KgoT z-+)Nd)gly(%+2qbH`<&zI!D@;u<)}{Q6*AnK<}$^PoVtaKDiZrxlzY zF@3CI)G<+TAyXT-zBDV_Zw%laeonLy&e5N2%SUknNA+sb>c$MM!8J> zns}!>V28xA-C%}}Kzz1o--fL&9)I*kxOjvu$G0ea5v>~UxwqwebPc&(e%Av-V7Y?# z=%HThZ_As?x7c20*p6rW+Fym&$l%djqdW!OPcUQ3h&t965{T9R3&ecNrXpR<5yeKR zs%FVI0tu(jE=NE>6~aY&CD2 z%nqa=)e+1RWzW-TZY#SJ)%9rCZ8d{lc0Dj{JMChN%Hu%WR!#MK$1Nwtbtt_?_vRXP zB-=5`CeezwMUnKxKV%y%YAc9iE6;D$%2Je$T3yQ0k_+IV8Eel*(6OtsbT{T6$5{3< zDoM;4e_PZwr|2e5+hI;)mE#7WSlentlN?vmh9&d{ZS5^*xv&(p#axrYh(hV7_0qNL zWh=&>QlFJ+;N{{8B3A!SvDGhHX7*@@ zncZoh$?3GH?kmE6D$7Y2&=qI1;)6luWpNHXf@UzIt@i>5{Z4b#R$!0CD_e{=EoU(LQvS;W4&5(f( z;HWlpSQg`WvqlG0`U$I<)yI0&Ej7)Wlc?KW4Yr2DFzXpulA^3G%fRox&?ZT2*CEOT zWu`i7Hx@GnOi>~aA{Glak|dRdl(_uZjZ3YL;71|Vmw7{w8j6&5F6dgDd}v=+>0`_! zVXH|FY^4u6_R2dt;~;sil3T74g}fHdFr9CNS{~$vZX>23Ukl!-O40I--=LN>k zl~{7_=pVr{+B>KZ>``-D$H#S!hbwb<>u1Ut4}(0@x}3MU%@YD$f6>Tf+A~Wbr#^x{ z5@45`oMu*iq0jDW3tgwTK%ROnc29gmj7?1*VfZpU!~GifUzWfZzlR6Qjrbkvk9fyu z6>)n-7zrg`hk{RHz%TNhgl{<*+*kh|Y8$8Z?JE1UuA4owyI@}f50p-ugp>r;)ttOS z{?ypp4eafPAKlO#ZeVXWu(uo7+YRjP2KIIXd%J%U~e}od%JGoj5)l@p%-VUHBZq z#~05}!gHnCVg@k;-9`@kIVc-xPKKtnO$yco&P1axSarXeQVec(v^l~a<^IX=u#B<2 zb6_E0@{b>$ICtQn-ZIDb)^O0kxrxK$JzyE&X4pc}Q4BJggpq zJVU|;4ywT95j0ce_IZN_hK0BWdUyvTl4nNPkQDwRfaXnxMZ^iiK{Ru|@}NB`;clo8 z4LHOs&D9er(PB2h_&`y5a^q3F1=2cRG_f+9&}?l8q6_Mxhn)xPHi-JLYqy5S)i%+? z4`lVnuDOI@7xSV9O!(GRnx1kHN~qki$s#l!$+Su)ngNM*hf)_;Hcs$`{pxXve>FjZ zA{zoGdYOW~CQ8&gP&B}0-aN#K50*a-H4+gmz{l6W=~=m6b=evew6*DHrhf;QmLA9c z2D&$`mkjXo|F6dolZ^j;dW?s4nIhV5Z|)Ss)NUcQ8FlqiY)W93Az8A&G?M{pP1qwQRXTL5B06^Kmqy$gFX@#)rIu&&{H@a zV6_;tHIlX2FzSXG=8_irD_;~U;lL3QL@9Q`i5XrjiH#u`Q-)E z6V^;W(=RWOW`^RX8r`1Fp?sI^#t8%i6e=>Q`e1C1wn@EC<+Q8$27Sm&v;8yB}@9IpuJExVm5(?{ibaGcq7UxZ7=Pa3InRIAWuXp!^BmeLGfT2 z16q{PzJsC@SGPk$85?jzu;K~|YL;W@4cZn<3m^B^BCENioS|3>hw}7fd2Tk`!T$Nn z3+j!`Mlq$pEz5^^4C2wz8|AaMv6GrYCnn0Jt(b*w?g~O!OZ!?IR&Qs_P=PjvyidK^ zULZdcT3EW{K#gn;?XHmx#pnw;Y#-&A zsUB`RCdv+sS%|F>ij*~mw{fyC|Id%mD`d@QQkshkC=@$Mg#$+D1szTG^OtT8pkId-T4_2RNq|h3}TS@jOl4p}!)-eyr>R2h}+4e_YwDUagk2 z($zRBNYtyXx|*M?sv3DO`uk7D;n)a}6$fV+y*ca(Uc0Z*-7>piD@=rPPe=?hK0-Lw zM#fOOjYU{lkzd5N1kn%^;hibiABVf9JmJzMV2V&>*g<2cuV1K9eb2?s7qNkxHW?oA z$7wmgpkI7MRK`>brACo5&G3r40mLtajn5aFHJw)`Muyj5XZUw~u%POKLU@Vb0(}h3 z5b-I-=W={z<8wVetMIuSpU3cd5g#8cro#MQQeeTi8d zv06@Sbk82~Ik+py6x2=b>K0^5>K6HfVk-3E~)vkvH7W8 zf}`UCTK#=ltLxE*s!{=6@>-)eWKQ%qKMYv9OhqoU_wn5@P*FMm!H5H`AcoptsvO-! z1R{XAuD4o>h-T=x_;;1A5St#BloKOd6`N+bQM|PZAEm|qk{y$q6e>L=Ef#OZt9_K; zp?_^cn~p-OXa|wR=ZhSam17vPdm`EraY!NF6Xn3HCkldJkdkdO{Du@FawF9PsX;8J zZvT@3UU$=t@O8IiF|Zl#WmVm42fnxf@8~l1o5<1WF?kVzai-|(KseYBc8?gH9-S8z z5Fd;TS0Aj<-7kKLT9%L)65#D06^1yVz8>*OA%R~05n=8gSa3G9|C6+t^}KAhGOq!; zKsO~4U0hb+P^BYf9V2wjxP6SUD|C0`Hmk-3O^Mxo5e`K|;t4KK#kXj=<ts3^W zd{r$E2oCV{oFcqYE=+Nwgo5;(WcwTZk&Vi~$MY>ov==9j>dx%!wX`eqUj%7H&W0_b z62?(bxA@RUY8;D|jkoSv+1=cIwHS_A)|E(+)MK=N-TX}6UQKVSW(08c zb%UnGFG$L%hPz6abp4|-#Me8}<%S!?2ss41b?uZ_w=?LdyfDU7V%jKgcTLjnm6xuS z919p%E$XCWdAEDG`i6LWsph7)yY+}O8d%GFt1-mSODiX~8I6C`N{=_H%Dc+z zYL1%L(fpKS)z|;5Yw)F>v6!D^KgvYCnLb15TPSv^(ys4oz{nlMK%%k+(o)(CrP+*x zX;Zc=)PlX$;6yjKoo=DN5q>+}-FNy$MEbpQ?|t{l^4Z5*&G+^VZ#wMb?c>vQI6Pb( zr<$71HhqD)N&KaVj2ws2ph&rAH(q1iCkp~QN80p~?X55D4dkf#7Z-hE1%d@9QZfp{ z<)v&i#G?mUJ6@h_1mrpF9VALdqJxjdbje7Utpe*Hsz*jiq;B2XiLAmC_i?QLxE*Z{ zhswTvR5V;?`-TQabYCRBYY~^UDcUN>&lG1hOB@_&jt_Bjj|d2G>mD&)*g-c2c!g}v zLtX}(Qy^(qq2V5#hPVEYQcx$Doq2u>!P+NMPb-lO?GeXthY;tNMS|!f(o2-2BZ}%) zAFe1~{te)?yyio|e4M>ADh0%%=A%eo-h3Q5l~~Ul3lK(2%`1U%&1*2FCg5II_}|(J ztYo^1SWT=U))MQ8D}i3T%Zqn;@h&e#?($OPE-yvy@>1k3FGcS1Qsgdg%&8{Kutt>7 zN0|=vNBStxwRt@!b00ksn z(0mqHNEA8*C{p(TMd}{NHwN;JfqY{i``kw{XGyg&6KSbO@gmw_WZY4fKe3ZDG_wFI?CGI0iX%o?c zf)+e82|RopmVl}abSWB!Ut|V^f_0M2TPXXs_MMRHA)GiIrL9fFb&jJI9`OMMs&mkuR zE$Mj^}46NT4!&h4@p zb6(~37C!j|^G^~_HGhNhe@4nQg4;8d(@6J3pE?VaF}WvuR!`930@4K-6MAAy{tj5g zYbuFV#A;#C9$5*+0VNU5G}~T*wK71FaRTV9!e|eXwWTBi6JJS z_4AbOczYgt$5Eg}V$Ku4`@j@p59VXn0y!dYV#X@gDB93KU#u3N!*NMEogy~6q z&J?Ca4#@*O-vnMkypj}{#(U@Su5zZA^U6x*R1vF*HN;wC9dTvz9eCO**6vo;>o(%; z#5;&<(6V{TpO{|DE7uX%6E_ey5;qa=;g$Ci?<3w%tS4nQGyNb-{t$5sOZzZ!EAbJQ z?NQeKG1mQY)@vKnPw<*2iBExZ^OUDaNs-?2lxO&s=Xm{&=AFpd$-AEC^)HZayO?fZ z{%+zP;$GrD-n*Y=6Im-yd6~~Y$dbRpI=;#)kATDTFpo=%zeaqW_nsi0B%b4Q&a*}r z@ZR1WoqD4$oJCqjp5BmG1!d&v4S7{iMxNe~SI^2!iRa{l=Z^yenT|#oz<)%U>+>-S zd zITM(k$aD#DQnMJ1OkruS1g-LwX-v=KujRz$M3Fu6QENewJ@Qd&L6JT3QES1Kpl!bL zN7m&|;$6hm#I>x^I^ufb2I5BICZcc}EH(JXhlpE<4->Z%A0a+Ue2n-waU1ao;*-Ru zAO+YK+mibj)e}t42j>=bF z=anazf0FnaOLCfcj+}6w`4@nYrNCVU;FGh!K&BHgau$F$egdYUcNBm(PA;&9>!;#lH1;&|c&;zVKzaT0M#^H+G<6{uqYW$6O) zMu9SqzsicBKv_<#WNE92)x;WNEwPTc616S>Z%AG4B;G|_O>=(Y?jy?Tpn$7`0_7E!_EqK_AqrO%kShw56U;eDJjXUY&-4XgA$g#X zV?rUvfkKW0g&3t`@T-gig?LgIpwPb%a`JaT=~IP@^r=Ec`c$DJeX3BAK2?bF2+Dg4 z6?rdKN@$BByz3}X#-$?6#gdkBsR*N-1(?VyWkp#8-rEM0b!8FCBbbLW6rntVQl27k z-A>?8)Tjva<{{v4rbjS6mN=CE5soX01W zv$V^3S0&3_MXV;)5NnBb#FdzpiZEl!b7T!$1iA^z8ny^@6O=V<5$GoPAZzpxaSQQb z;#T4##7Bvbu{@6xw-IHgD^g^pE5i8W4-`%-0;dUOb})Y@QD(Oy!oa_Tlh2+bMrZ4p^{h*!L4$m;tfrBBj{{%b@9K!SvriWs~Ivuj-Y2a|?4`+Tc zIPDNN%L0jmQQBf^8;Zdp-ytn)#$sgzQ6#HkWh`+VQPz^h%0!~{`eOF_V)puC_WEM> z`eOF_V)puC^m@5V)_TQ?to4c&S?d*}Ed-xJ$%`p<6ti85(Jm(BJjXZg;5D)mELLPC zSd4ZNl$BsH+C@+#iek`Q(w~8{#i+I5In?no*8MWpeKbt3y%0U;N>F>WvY&W>cm(-l zU<=@iNQ*+xvAlj9)8m*P&-8evConyM=@Qbjg!C-oIA20~mXMw$q-P1~S;Ccg38`12 zl%UKd*s$~mPQ}|xlxcW-3CGeBQniFsEg@A)NYxTjwS-hHAyrFA)e=&*gj6jdRZGCn z-{EOOw-VB=gmfz*-AYKe5>l*0d7bb53_Mr@x(N#1CZS*d1bm$+bLAxbY66Xk8&v!5p*=uD_Dcxe~uN z!1i5qjMK4VIs_Dr=ya?-1w|7$oxNr{dd+d9MJqU+TEThfBWIO)c$%Q-Rp#Ld4M5Q- z&chQVrwGrShkhjKdZsNz(Ylmlj6Dt%J!CoWD(7A0)I*l@?d5!9Ip4UDI>Uw387@cq zxU!tJUd~!C=hK$649oen<$T(5Jf{IyPUrRYOk0T33M+8^aiElW1+QO$>m@DPsug(e zaix+P$VzG;D>++LQm0YL`Jxgt*I8U6Gesq5ib|{LR?c>Z?a zP*Av%8puj&ASEvi_HD%PTkwWwk(s#uFE)}o5F zsA4UuSc@vwqKdVsVlApziz?QlinXX>Evi_HD%PTkwWwk(s#uFE)}jjIfRtQnQN>zR zvli8?MKxHLOJqYf-~m)UXyctVIoLQNvo)uog9}MGb3F!&=m^7B#Fz4Qo-uTGX%> zHLOJqYf-~m)UXyctVIoLQNvo)uog9}MJ;Pl%Uaa37PYKJEo)KBTGX-@wX8)gYf;Nu z)Up<}tVJzrQOjDu{sY`w%Uaa37PYKJEo)KBTGX-@wJd)v%U{d#*RtfbEO{+UUdxi# zvgEZac`Zv`$CB5vW5cRKfH?i;Z=%g{#PlY`Cp}o=6{tU zn*UWS+bWjrkC1h}uzC@^i@2J&1~T(;1sWraY->OPNy}cq8myNEtBJA~umsf zgY~kYjIL`qx~`>$VJ%1RwS3xI(Ds6|7PS2dDEiN}c>Y(}x6 zb-aEZuV2UO*Yo=Iyna2eU(f5;^ZNC?em$>W&+9kv`VG8(1Fzq}>o@TF4ZMB>uiwDy zH}d+8ynZ9E$0-w(eo@WGO}u^+uiwP$H}U#S)Vge<)@2j5 zE}N)z*+i|&CTd+a>CY5>?HtrtfF^ex~ce|3|T7Bx|C2 zu8HcoCaT9Z7Uam9sGdEd9%bH!w9It%>=*Um!!Ah6ny4OJAt-C2dh~?`psb1NxhATI zgeGZO6V*dMBPeU4dPUYm^^n;FWldC%9waDhqI&cod6%q->ai*pl$o)fYodCr%H;{N zCaTA(T<((fPCduBdah;a(F>$JvX-ewFA$WqOg(ympsZ!;(I4eGvX-fbq$enAnR>2e z>M1kVb1hTPwM;$NGWC%C{<2f z_4QoK)T8eo$FHJAsmBN)HIlVVJtQ<~7g@{HE3%e(fHZl4G&iNO{@oybAUMqm~#-L@mA#^Mq@$QGdjr8_#k-iIMT9bbP!x8 zD0@Z+(Q9r7%AV0d^qu`c*)uwbUb6-$dqxK_0-puSp3y;2NuDWtMhCGLmK@nLI*7Hf zpzIkP#9CNT_KXgKe*|UE=%6BdMh6wyGdifqp3y;!UGf&$Gdc(!5R^TmgWv%{*)uu_ z9uSm0ql4f9LD@4p2p$lWJ)?tYFG1NeItU(+>t)aAAX;DYWzXm!#{X}CvS)M<<9`EC z_KXg3C3P4*^a5eGB`(g?-<`zHcF)Tgc}Y_I(Sv-NL?aVc)l~?_1dSE$sUi_Ie9@ zy@kEr!d`D-ueY$*TiEL@?C%!#cMJQwh5g;a{%&D^x3Irk*xxPe?-urV3;Vl;{oTU; zZef47u)kZ_-!1I#7WPdG`=*8c-NOEEVSl%2)gv<4{asT1fIk|V3I6PQy3Wi@vK>ma#C)^R7mZ%37r zyz3m=_w$-7SSu9Lj$6z3=Q9lJ9kWz{M{1kXV zQ1s`gzypG!KR*Q?5Zuix_Yn6I_Yq&^uQI})Li<*rza@b3pP?>-Rm5sy4Y8J3M_dUM z%M=q(=Hk!UAF-2z(Ez&~sOjfuuR}mtSAGsYk?Uo({yA2Da#t0xnpi`uCDsvF5=BG! zIoemAAXX@!E9W3xHnNo)*~*P<t&0 z8`;W@Y~@C_awA*0k*(awR&E5RZO6UR%8hL0Mo2!AmR4?r~+{jjLWGgqal^fC1 zKgWBel^fC11*Mf6*{>Vf%BM*^c$`9u!)pLh`p9Y4@igmrnsq$QI-X`7PqU7vS;y0? z<7qza40+%TdEg9r;0$@-40+%TdEg9r;0$@-40+%TdEg9r;EW!p66I`o@2#%y!R0DMbC2{Pmr|edCsHc zXO#xq8|SHt4tMX?KjVYxT4Ttp8ru)1J8Z zO+76hH;(FQAEdw3(|$^d>V|t%7n~V_AErz_jk6r;BYGN6E?{q}U1L;ws}?=&qL>VF zK2`2@Rr(r2^t782WSF3*-Ie)_XuF;Y+aDdUaB zdOAQc8Sm+7Hs{9nGtfHbkcFeWc&agkAAl9B+5Gzl&|2?T};Ue4}o0pMm z&MD-Ng|Pz{&z?IoHp^_z;)U`w?T*=tVoPHy7M9K|n^(GUPHg$L7b^}*YcI#Fii!mV znVCzMF3q?WHIPrnjPiMxmYrF)cy4Lt+}ShA<}WJCm{l=vE{sad_@56~+=G=;Wj@Zn zl!1X_p~9_DZou~fWigm*F7g-Q#9Wy&6M55dIwltEHvtNA`7XzO75HlwQVWn%%)iT& zh4?PTFSB_?DW5w`l%?2C7>#FD;D0%;AA`d&*D5oV@yNRl@0*Le7PeWQ0$knJosw^@ zNn5{9!jl)G23kpC(HSyeI%9?@O`*e`k{*cn&qfKPo>`<#mctvZC3SpLo?6=ZNqjX#CGmA7_0O2$f|CjQA2HvQ(i1j-Iv@J)O{*zZ{qSji= zWs+NF;P3gMOc^LG@12KSN2*yVi4jH2-|-Lg#sDqV5><)r0cUpT^xq?2iq_na|;1@e$Y; z8i{>{(O}=P$~Z9Z1Z5(+%_NNBQ?PR}6_J3h#7@Um%GJs>N~vnZYPA|O(_WnZ!Yq$H z^V^jBaI|cT>Z07Lx?=wNlk%1FwQ>i}kFLac`w`}Z`*AYwTjd+&JFIt~#LDF`Jhn|o zW8MWOdmW@b0=f7t<=2yAEZLy^O%Z8( zJ5~)RMGk~i_c_k69aTP6K2ZLle5iZ^-$5U%?y86Csd}m2s*mc6<4yi*fO3x-s0OLQ z%CpLIIPvxZ)^tq-U8cLuR^p_NX0R`@OH~ZrEHIQi7~;1~~WgGyAxCXulX-T@~{+ zZeFQws*75gXHZ=>k2jAsr#a+AJs5LK6#9q!9|N9T1df^uj;z3^ulzU1wtSvTz*hrr zd&S-F^Scfmy?N;s>C47!t_Z2 z_szbx!m3iH30dA|Pd8(Ui(9avWI|S$IYj=znJvu3vnR~HZhjhO^%)sispb^q=;SK~wW ztgO8zC3M6UnU}4eQ@8r-+=)|i&%D#?s_CP~kIVV}%Qs#dz3Rw^Jr4cO>$|*T8!J|S zx8|wsJD04#@YskMLxyF{@m@3fveetpO&M|9d+sMA(*9NX_>^OB4E#F%rNtlJvF-35 zqc_bR>9H>6qYu8k?$d=Y7r%V}HASBu-xvDOjAgS;H$IteFoKO9T5-s{;($5F%>(S< z>guklF3ILZbAt76vsxRjOCjYm7A?qFf~Hu63W{_B7BH4X2diqciveOrw*1xACC%K! zoV+=4b3$#Le&dXVb6eb(S+QtQ`i#;HXzKhY|_L4b9 z{tk2jPizS`2WI($tYiVVi4z87rT28CGFZu2sjAA@S1vn}aLdOheG^{(DB_t13=eFr zO!uh@S&`>e>6f;BYFO#cqpnRWKe(`Z^>dRV`fvSW&A#6G+ZXq$DA+jWWM0GFt6p0- zFnarohd19b`I8^tpXo9%ceMIW_Jd(ZMm?T%_^VHAM;dyMs#^By&l5iR;uE*J54Wtp z|0~5e^VUsY>~ZhbZ}0$5 z_nl=?344aWJNT9_9(c=Q*kO8W;^EMTZn^S@>-$byxO`>U{b_?9d~L#;%lmxSqhQyV zwaUZuysD=CaMgrw!k?S$zu@3c-#)o+vtQUpheB_;#^Rqkzt-i#7vC&PddKyZb2(RB z^nm4&jro_r78KyR7^8j`2eWegDC{;l5d0T9M4W;x>n#Fm*syh&fnD>=EoU zxonZh(y@a|7nEh0%t4ZkFr9-JFPvUFf645*bIYvl;T7yQaaQTlin6R|bEI769c=~5Ev$~q2we}0uf0hos$l8DPZkvssnq%*_@@KZS+x|z!ib^%kkt)(% zD!#h5%+k5p5zst%n-n}CD(P6P~W-aoa zoc_kLoa}W$cL%-r(X7nJKRfaAt+)3$cw4u+Yxh=7nOwejf6?QKb<+->&=Y|d16mj+Dn7dA1_3gQvE-c?R zUA^<$QQjZD9XILGb#FYia>-NY8hpP#IqKNvpJr`-%2fDV-GSZui1C4W#nE3mVhHbO zBZi0B4ZRzyNAm#5>*`Wq?rrY1IcIavT625jhQ=$7tTJc-M-k!F=%35{(-Kp#MS3aTsHEQG= z+i#sR|6`AohpsyE+Mj|ycNy>d<*+0i1KEDk@~6?8-iwE9+>9QZ6WW65`SpC4%{AC}%Ra|y$@92?>Uv5=m^VJUBxT9G# z&zuW_poeA*np?VPQEYbXSXoTZh#k9dHkPp0x9qX80~S}zDqlFe;(E@gIp(~qUiPjj zf96`d=87GiEP*}^LQ5E?iI=n>-`MoRgISi3asE26wkT-CEH>Wy=bRfj{`QaFjT#=@{mg+GH~cWJ?A*JL z7k+zw?Y*f#eRb`734j0o+xq!k@_X%h`=KQsF>y%=b9=8&I$mA;ROGD>{L;NLEx_FP z`)$dMOGk{maQ2OVouBw#`Aa9}dEI&J=*&B2Jexi`>AM=grga>Qr!$n-OEX!bw?g79W2(Rk6GHy`ZtY1S@M*wF1W=EA!2h}EgKtc?IcDlm{FR|?k{r*x__wI zWPJ-{23?>r8e7T|7zhI0Jhj3NgtPpo#Em6C&MZ0Gf6G6M2i$e{y{q;-oe}-o zviKjOy7>R$i&=Ym9$VS_qkr^!ch!!5&s46zuw`n)KQj{F{pcojK6Hp%+|~pR_w4^e zRQUDR#V35S`~6SHJ{9?`--n-Mta#&=hvtqSvwCsKtqpNs{pHMn#Va#slq`>#ICb6S zuN})-@zT{-eRso7bERt#M3Eo$A#8d2o#lJyWbdl`I=cVA#_t-`s>J61qa%p6)X2tw z!hf>VFhgRo4~*|S42*+^ScN8!V7Gx~3+K+BZ_RMGWh9v6gb%$;kyhR2qS(drXU0w_ zzqVp&>B2JVHM9Dfi{vWr;M~EpubW)~b!YM5SnF7q9y@9_mdcB8hyAumrE_P`q$C!* zq-Rz*rEnK-lmEoB8M9&smn|q=STPT4>MZ^J#tz@#;UdTK2e*MIrnpvK=eg{OWr251 z$qrocQsa&h?+lCTb$HIBr^+|}zUP#uVz%6V#aHE*ZAzJb^3J-s+e{B#bJe6Hdov!X zKk7dk?i(6O;R}CbCqjf5519BUhKv!CbAhSYWu{V(u^9wyR4% z7K^!?vo~keX6jPNpb3MrvA{6rq&p2jwx2i({d)AYkt2Zz3U^E*c;6{QZ# z&%QJJi8G1YJ*)Eam!7}pp7oDEId0iIStE_6;eU?Isx)F`G(KZM#E2D^|D&UxA;4)g zZ=q@0ykZZgWlvq%l&ogC8zELhYh~_Y)BboGvwR%b#>E`v@Rv7rS9Ycf0b5#iT%Imj zK`sH&Z|ptt{-y`NIlJRunLS24INChI;dY;_B6Gp!gj?dE$XWpP)qJRNuV>1>x1sNG zJP>qCKreaUK0f58z0L33mar^)M#2q4Pyg-iuR|~YyWAo|M2+m}3%(ev*8r@lRATpQYJ+dtnQwc@FFp6NCF>34^$j{j#; z_NI+}D@J|U_tiJzCw<~Q_pS+dO<4Q4wbPCbeX_3J-{tIG$p^xw^!o8=zbV77e|2-@ zsd3AK$3{P(4`y~ExMfwUb1}Zn5=Of2KcYr!LwV=v=9=nfG(@;6o2NlsvNee@%iH#c zyNaf?{o!rp5UB=N4$T<3`(fAGQJ)KCrBL{u3I%eDY<37*cbn|_m wzCPovx!>!5CFhCuiyg)hGlT_8 PP{izCmd0yB8&LcJIkP8R literal 25096376 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%0A?3QQO_+9htYQIaKUFCIiL-}^H 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

CA5tjz@s@&LdI+CN(r{Kx$N=s(Z71pmEPcGbpM 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%0ArM(=677~*kL%Y^==Wkero8PLlb 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

}{@r4asW*jTgY)#&cj-;~C;d8&7F&XFPE$ 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�IybM~w%b=ya40@f$Sl=`n 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