-
Notifications
You must be signed in to change notification settings - Fork 201
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CircleMask view example to NativeModuleSample (#996)
## Description This PR adds a `CircleMask` native view to the `NativeModuleSample`. ### Why So we have an example of implementing the same native view for both Paper (using Xaml) and Fabric (using Composition). ## Screenshots | New Arch | Old Arch | |:-:|:-:| | ![image](https://github.com/user-attachments/assets/0cd5d355-f970-4fbf-9d25-237efed14e1b) | ![image](https://github.com/user-attachments/assets/b707c136-0e5f-4336-9940-01e87bbb7c8f) | --------- Co-authored-by: Andrew Coates <30809111+acoates-ms@users.noreply.github.com>
- Loading branch information
1 parent
24a6f6c
commit 9046ff5
Showing
11 changed files
with
486 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
samples/NativeModuleSample/cpp-lib/src/CircleMaskNativeComponent.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; | ||
import type { ViewProps } from 'react-native'; | ||
|
||
interface CircleMaskProps extends ViewProps {} | ||
|
||
export default codegenNativeComponent<CircleMaskProps>('CircleMask'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
159 changes: 159 additions & 0 deletions
159
samples/NativeModuleSample/cpp-lib/windows/NativeModuleSample/CircleMask.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
#include "pch.h" | ||
|
||
#include "CircleMask.h" | ||
|
||
namespace winrt::NativeModuleSample::implementation | ||
{ | ||
|
||
void RegisterCircleMaskNativeComponent( | ||
winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept | ||
{ | ||
#ifdef RNW_NEW_ARCH | ||
NativeModuleSampleCodegen::RegisterCircleMaskNativeComponent<CircleMaskComponentView>(packageBuilder, [](const winrt::Microsoft::ReactNative::Composition::IReactCompositionViewComponentBuilder& /*builder*/) { | ||
// Once 0.76 includes SetViewFeatures - enable this code: | ||
/* | ||
// Turn off default border handling, as it overrides the Clip property of the visual | ||
builder.SetViewFeatures(winrt::Microsoft::ReactNative::Composition::ComponentViewFeatures::Default & ~winrt::Microsoft::ReactNative::Composition::ComponentViewFeatures::NativeBorder); | ||
*/ | ||
}); | ||
#else | ||
packageBuilder.AddViewManager(L"CircleMaskViewManager", []() { return winrt::make<CircleMaskViewManager>(); }); | ||
#endif | ||
} | ||
|
||
#ifdef RNW_NEW_ARCH | ||
|
||
winrt::Microsoft::UI::Composition::Visual CircleMaskComponentView::CreateVisual(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept | ||
{ | ||
auto compositor = view.as<winrt::Microsoft::ReactNative::Composition::ComponentView>().Compositor(); | ||
|
||
m_visual = compositor.CreateSpriteVisual(); | ||
|
||
auto ellipseGeometry = compositor.CreateEllipseGeometry(); | ||
auto clip = compositor.CreateGeometricClip(); | ||
clip.Geometry(ellipseGeometry); | ||
m_visual.Clip(clip); | ||
|
||
return m_visual; | ||
} | ||
|
||
void CircleMaskComponentView::Initialize(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept | ||
{ | ||
m_layoutMetricChangedRevoker = view.LayoutMetricsChanged( | ||
winrt::auto_revoke, | ||
[wkThis = get_weak()]( | ||
const winrt::IInspectable &/*sender*/, const winrt::Microsoft::ReactNative::LayoutMetricsChangedArgs& args) { | ||
if (auto strongThis = wkThis.get()) { | ||
// Once 0.76 includes SetViewFeatures - remove this code, since the core border code will not override our clip: | ||
{ | ||
auto compositor = strongThis->m_visual.Compositor(); | ||
auto ellipseGeometry = compositor.CreateEllipseGeometry(); | ||
auto clip = compositor.CreateGeometricClip(); | ||
clip.Geometry(ellipseGeometry); | ||
strongThis->m_visual.Clip(clip); | ||
} | ||
// End of workaround for not being able to disable core clipping code | ||
|
||
|
||
auto ellipseGeometry = strongThis->m_visual.Clip().as<winrt::Microsoft::UI::Composition::CompositionGeometricClip>().Geometry().as<winrt::Microsoft::UI::Composition::CompositionEllipseGeometry>(); | ||
winrt::Windows::Foundation::Numerics::float2 radius = {args.NewLayoutMetrics().Frame.Width * args.NewLayoutMetrics().PointScaleFactor / 2, args.NewLayoutMetrics().Frame.Height * args.NewLayoutMetrics().PointScaleFactor / 2}; | ||
ellipseGeometry.Center(radius); | ||
ellipseGeometry.Radius(radius); | ||
} | ||
}); | ||
} | ||
|
||
#else | ||
|
||
// IViewManager | ||
winrt::hstring CircleMaskViewManager::Name() noexcept | ||
{ | ||
return L"CircleMask"; | ||
} | ||
|
||
winrt::Windows::UI::Xaml::FrameworkElement CircleMaskViewManager::CreateView() noexcept | ||
{ | ||
auto const &view = winrt::Windows::UI::Xaml::Controls::Border(); | ||
|
||
auto const &binding = winrt::Windows::UI::Xaml::Data::Binding(); | ||
binding.Source(view); | ||
binding.Path(winrt::Windows::UI::Xaml::PropertyPath(L"Height")); | ||
binding.Converter(HeightToCornerRadiusConverter::Instance()); | ||
|
||
view.SetBinding(winrt::Windows::UI::Xaml::Controls::Border::CornerRadiusProperty(), binding); | ||
|
||
return view; | ||
} | ||
|
||
// IViewManagerWithChildren | ||
|
||
void CircleMaskViewManager::AddView( | ||
winrt::Windows::UI::Xaml::FrameworkElement const &parent, | ||
winrt::Windows::UI::Xaml::UIElement const &child, | ||
int64_t /*index*/) noexcept | ||
{ | ||
if (auto const &border = parent.try_as<winrt::Windows::UI::Xaml::Controls::Border>()) { | ||
border.Child(child); | ||
} | ||
} | ||
|
||
void CircleMaskViewManager::RemoveAllChildren(winrt::Windows::UI::Xaml::FrameworkElement const &parent) noexcept | ||
{ | ||
if (auto const &border = parent.try_as<winrt::Windows::UI::Xaml::Controls::Border>()) { | ||
border.Child(nullptr); | ||
} | ||
} | ||
|
||
void CircleMaskViewManager::RemoveChildAt(winrt::Windows::UI::Xaml::FrameworkElement const &parent, int64_t /*index*/) noexcept | ||
{ | ||
if (auto const &border = parent.try_as<winrt::Windows::UI::Xaml::Controls::Border>()) { | ||
border.Child(nullptr); | ||
} | ||
} | ||
|
||
void CircleMaskViewManager::ReplaceChild( | ||
winrt::Windows::UI::Xaml::FrameworkElement const &parent, | ||
winrt::Windows::UI::Xaml::UIElement const & /*oldChild*/, | ||
winrt::Windows::UI::Xaml::UIElement const &newChild) noexcept | ||
{ | ||
if (auto const &border = parent.try_as<winrt::Windows::UI::Xaml::Controls::Border>()) { | ||
border.Child(newChild); | ||
} | ||
} | ||
|
||
winrt::Windows::Foundation::IInspectable HeightToCornerRadiusConverter::Convert( | ||
winrt::Windows::Foundation::IInspectable const &value, | ||
winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/, | ||
winrt::Windows::Foundation::IInspectable const & /*parameter*/, | ||
winrt::hstring const & /*language*/) noexcept | ||
{ | ||
double d = winrt::unbox_value<double>(value); | ||
|
||
if (isnan(d)) { | ||
d = 0.0; | ||
} | ||
|
||
return winrt::box_value(winrt::Windows::UI::Xaml::CornerRadiusHelper::FromUniformRadius(d)); | ||
} | ||
|
||
winrt::Windows::Foundation::IInspectable HeightToCornerRadiusConverter::ConvertBack( | ||
winrt::Windows::Foundation::IInspectable const &value, | ||
winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/, | ||
winrt::Windows::Foundation::IInspectable const & /*parameter*/, | ||
winrt::hstring const & /*language*/) noexcept | ||
{ | ||
return value; | ||
} | ||
|
||
winrt::Windows::UI::Xaml::Data::IValueConverter HeightToCornerRadiusConverter::Instance() noexcept | ||
{ | ||
static auto const &instance = winrt::make<HeightToCornerRadiusConverter>(); | ||
return instance; | ||
}; | ||
|
||
#endif // #ifndef RNW_NEW_ARCH | ||
|
||
} // namespace winrt::NativeModuleSample::implementation |
97 changes: 97 additions & 0 deletions
97
samples/NativeModuleSample/cpp-lib/windows/NativeModuleSample/CircleMask.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
#pragma once | ||
|
||
#include "pch.h" | ||
|
||
#ifdef RNW_NEW_ARCH | ||
|
||
#include "codegen/react/components/NativeModuleSampleSpec/CircleMask.g.h" | ||
|
||
#include <winrt/Microsoft.ReactNative.Composition.Experimental.h> | ||
|
||
#else | ||
|
||
#include <winrt/Windows.UI.Xaml.Controls.h> | ||
#include <winrt/Windows.UI.Xaml.Data.h> | ||
|
||
#endif | ||
|
||
namespace winrt::NativeModuleSample::implementation | ||
{ | ||
|
||
void RegisterCircleMaskNativeComponent( | ||
winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept; | ||
|
||
#ifdef RNW_NEW_ARCH | ||
|
||
struct CircleMaskComponentView : winrt::implements<CircleMaskComponentView, winrt::IInspectable>, | ||
NativeModuleSampleCodegen::BaseCircleMask<CircleMaskComponentView> | ||
{ | ||
winrt::Microsoft::UI::Composition::Visual CreateVisual(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept override; | ||
void Initialize(const winrt::Microsoft::ReactNative::ComponentView &/*view*/) noexcept override; | ||
|
||
private: | ||
winrt::Microsoft::ReactNative::ComponentView::LayoutMetricsChanged_revoker m_layoutMetricChangedRevoker; | ||
winrt::Microsoft::UI::Composition::SpriteVisual m_visual{nullptr}; | ||
}; | ||
|
||
#else | ||
|
||
struct CircleMaskViewManager : winrt::implements< | ||
CircleMaskViewManager, | ||
winrt::Microsoft::ReactNative::IViewManager, | ||
winrt::Microsoft::ReactNative::IViewManagerWithChildren> | ||
{ | ||
public: | ||
CircleMaskViewManager(){} | ||
|
||
// IViewManager | ||
winrt::hstring Name() noexcept; | ||
|
||
winrt::Windows::UI::Xaml::FrameworkElement CreateView() noexcept; | ||
|
||
// IViewManagerWithChildren | ||
|
||
void AddView( | ||
winrt::Windows::UI::Xaml::FrameworkElement const &parent, | ||
winrt::Windows::UI::Xaml::UIElement const &child, | ||
int64_t /*index*/) noexcept; | ||
|
||
void RemoveAllChildren(winrt::Windows::UI::Xaml::FrameworkElement const &parent) noexcept; | ||
|
||
void RemoveChildAt(winrt::Windows::UI::Xaml::FrameworkElement const &parent, int64_t /*index*/) noexcept; | ||
|
||
void ReplaceChild( | ||
winrt::Windows::UI::Xaml::FrameworkElement const &parent, | ||
winrt::Windows::UI::Xaml::UIElement const & /*oldChild*/, | ||
winrt::Windows::UI::Xaml::UIElement const &newChild) noexcept; | ||
}; | ||
|
||
struct HeightToCornerRadiusConverter | ||
: winrt::implements<HeightToCornerRadiusConverter, winrt::Windows::UI::Xaml::Data::IValueConverter> | ||
{ | ||
public: | ||
HeightToCornerRadiusConverter(){} | ||
|
||
winrt::Windows::Foundation::IInspectable Convert( | ||
winrt::Windows::Foundation::IInspectable const &value, | ||
winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/, | ||
winrt::Windows::Foundation::IInspectable const & /*parameter*/, | ||
winrt::hstring const & /*language*/) noexcept; | ||
|
||
winrt::Windows::Foundation::IInspectable ConvertBack( | ||
winrt::Windows::Foundation::IInspectable const &value, | ||
winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/, | ||
winrt::Windows::Foundation::IInspectable const & /*parameter*/, | ||
winrt::hstring const & /*language*/) noexcept; | ||
|
||
static winrt::Windows::UI::Xaml::Data::IValueConverter Instance() noexcept; | ||
|
||
// IValueConverter | ||
}; | ||
|
||
#endif // #ifdef RNW_NEW_ARCH | ||
|
||
} // namespace winrt::NativeModuleSample::implementation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.