pcsx2/3rdparty/winwil/include/wil/cppwinrt_authoring.h

335 lines
11 KiB
C++

//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
//! @file
//! Helpers that make authoring C++/WinRT components easier.
namespace wil
{
#ifndef __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED
/// @cond
#define __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED
namespace details
{
template <typename T>
struct single_threaded_property_storage
{
T m_value{};
single_threaded_property_storage() = default;
single_threaded_property_storage(const T& value) : m_value(value)
{
}
operator T&()
{
return m_value;
}
operator T const&() const
{
return m_value;
}
template <typename Q>
auto operator=(Q&& q)
{
m_value = wistd::forward<Q>(q);
return *this;
}
};
} // namespace details
/// @endcond
template <typename T>
struct single_threaded_property
: std::conditional_t<std::is_scalar_v<T> || std::is_final_v<T>, wil::details::single_threaded_property_storage<T>, T>
{
single_threaded_property() = default;
template <typename... TArgs>
single_threaded_property(TArgs&&... value) : base_type(std::forward<TArgs>(value)...)
{
}
using base_type =
std::conditional_t<std::is_scalar_v<T> || std::is_final_v<T>, wil::details::single_threaded_property_storage<T>, T>;
T operator()() const
{
return *this;
}
// This is the only setter exposed. We don't expose `operator()(Q&& q)`,
// since that is what C++/WinRT uses to implement public setters. Since
// single_threaded_property is intended for readonly properties, we
// don't want to expose that.
//
// To set the value of this property *internally* (within your
// implementation), use this `operator=`:
//
// MyProperty = 42;
// // MyProperty(42); // won't work
//
// For settable properties, use single_threaded_rw_property<T> instead.
template <typename Q>
auto& operator=(Q&& q)
{
static_cast<base_type&>(*this) = std::forward<Q>(q);
return *this;
}
};
template <typename T>
struct single_threaded_rw_property : single_threaded_property<T>
{
using base_type = single_threaded_property<T>;
template <typename... TArgs>
single_threaded_rw_property(TArgs&&... value) : base_type(std::forward<TArgs>(value)...)
{
}
using base_type::operator();
// needed in lieu of deducing-this
template <typename Q>
auto& operator()(Q&& q)
{
return *this = std::forward<Q>(q);
}
// needed in lieu of deducing-this
template <typename Q>
auto& operator=(Q&& q)
{
base_type::operator=(std::forward<Q>(q));
return *this;
}
};
#endif // __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED
#if (!defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H)) || \
defined(WIL_DOXYGEN) // WinRT / XAML helpers
/// @cond
#define __WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION
namespace details
{
template <typename T>
struct event_base
{
winrt::event_token operator()(const T& handler)
{
return m_handler.add(handler);
}
auto operator()(const winrt::event_token& token) noexcept
{
return m_handler.remove(token);
}
template <typename... TArgs>
auto invoke(TArgs&&... args)
{
return m_handler(std::forward<TArgs>(args)...);
}
private:
winrt::event<T> m_handler;
};
} // namespace details
/// @endcond
/**
* @brief A default event handler that maps to
* [Windows.Foundation.EventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.eventhandler-1).
* @tparam T The event data type.
*/
template <typename T>
struct untyped_event : wil::details::event_base<winrt::Windows::Foundation::EventHandler<T>>
{
};
/**
* @brief A default event handler that maps to
* [Windows.Foundation.TypedEventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.typedeventhandler-2).
* @tparam T The event data type.
* @details Usage example:
* @code
* // In IDL, this corresponds to:
* // event Windows.Foundation.TypedEventHandler<ModalPage, String> OkClicked;
* wil::typed_event<MarkupSample::ModalPage, winrt::hstring> OkClicked;
* @endcode
*/
template <typename TSender, typename TArgs>
struct typed_event : wil::details::event_base<winrt::Windows::Foundation::TypedEventHandler<TSender, TArgs>>
{
};
#endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H)
#if (!defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H))) || \
defined(WIL_DOXYGEN) // INotifyPropertyChanged helpers
/// @cond
#define __WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA
namespace details
{
#ifdef WINRT_Microsoft_UI_Xaml_Data_H
using Xaml_Data_PropertyChangedEventHandler = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler;
using Xaml_Data_PropertyChangedEventArgs = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventArgs;
#elif defined(WINRT_Windows_UI_Xaml_Data_H)
using Xaml_Data_PropertyChangedEventHandler = winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler;
using Xaml_Data_PropertyChangedEventArgs = winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs;
#endif
} // namespace details
/// @endcond
/**
* @brief Helper base class to inherit from to have a simple implementation of
* [INotifyPropertyChanged](https://docs.microsoft.com/uwp/api/windows.ui.xaml.data.inotifypropertychanged).
* @tparam T CRTP type
* @details When you declare your class, make this class a base class and pass your class as a template parameter:
* @code
* struct MyPage : MyPageT<MyPage>, wil::notify_property_changed_base<MyPage>
* {
* wil::single_threaded_notifying_property<int> MyInt;
* MyPage() : INIT_NOTIFYING_PROPERTY(MyInt, 42) { }
* // or
* WIL_NOTIFYING_PROPERTY(int, MyInt, 42);
* };
* @endcode
*/
template <typename T, typename Xaml_Data_PropertyChangedEventHandler = wil::details::Xaml_Data_PropertyChangedEventHandler, typename Xaml_Data_PropertyChangedEventArgs = wil::details::Xaml_Data_PropertyChangedEventArgs>
struct notify_property_changed_base
{
using Type = T;
auto PropertyChanged(Xaml_Data_PropertyChangedEventHandler const& value)
{
return m_propertyChanged.add(value);
}
void PropertyChanged(winrt::event_token const& token)
{
m_propertyChanged.remove(token);
}
Type& self()
{
return *static_cast<Type*>(this);
}
/**
* @brief Raises a property change notification event
* @param name The name of the property
* @return
* @details Usage example\n
* C++
* @code
* void MyPage::DoSomething()
* {
* // modify MyInt
* // MyInt = ...
*
* // now send a notification to update the bound UI elements
* RaisePropertyChanged(L"MyInt");
* }
* @endcode
*/
auto RaisePropertyChanged(std::wstring_view name)
{
return m_propertyChanged(self(), Xaml_Data_PropertyChangedEventArgs{name});
}
protected:
winrt::event<Xaml_Data_PropertyChangedEventHandler> m_propertyChanged;
};
/**
* @brief Implements a property type with notifications
* @tparam T the property type
* @details Use the INIT_NOTIFY_PROPERTY macro to initialize this property in your class constructor. This will set up the
* right property name, and bind it to the `notify_property_changed_base` implementation.
*/
template <typename T, typename Xaml_Data_PropertyChangedEventHandler = wil::details::Xaml_Data_PropertyChangedEventHandler, typename Xaml_Data_PropertyChangedEventArgs = wil::details::Xaml_Data_PropertyChangedEventArgs>
struct single_threaded_notifying_property : single_threaded_rw_property<T>
{
using Type = T;
using base_type = single_threaded_rw_property<T>;
using base_type::operator();
template <typename Q>
auto& operator()(Q&& q)
{
return *this = std::forward<Q>(q);
}
template <typename Q>
auto& operator=(Q&& q)
{
if (q != this->operator()())
{
static_cast<base_type&>(*this) = std::forward<Q>(q);
if (auto strong = m_sender.get(); (m_npc != nullptr) && (strong != nullptr))
{
(*m_npc)(strong, Xaml_Data_PropertyChangedEventArgs{m_name});
}
}
return *this;
}
template <typename... TArgs>
single_threaded_notifying_property(
winrt::event<Xaml_Data_PropertyChangedEventHandler>* npc,
const winrt::Windows::Foundation::IInspectable& sender,
std::wstring_view name,
TArgs&&... args) :
single_threaded_rw_property<T>(std::forward<TArgs...>(args)...), m_name(name), m_npc(npc), m_sender(sender)
{
}
single_threaded_notifying_property(const single_threaded_notifying_property&) = default;
single_threaded_notifying_property(single_threaded_notifying_property&&) = default;
std::wstring_view Name() const noexcept
{
return m_name;
}
private:
std::wstring_view m_name;
winrt::event<Xaml_Data_PropertyChangedEventHandler>* m_npc;
winrt::weak_ref<winrt::Windows::Foundation::IInspectable> m_sender;
};
/**
* @def WIL_NOTIFYING_PROPERTY
* @brief use this to stamp out a property that calls RaisePropertyChanged upon changing its value. This is a zero-storage
* alternative to wil::single_threaded_notifying_property<T>.
* @details You can pass an initializer list for the initial property value in the variadic arguments to this macro.
*/
#define WIL_NOTIFYING_PROPERTY(type, name, ...) \
type m_##name{__VA_ARGS__}; \
auto name() const noexcept \
{ \
return m_##name; \
} \
auto& name(type value) \
{ \
if (m_##name != value) \
{ \
m_##name = std::move(value); \
RaisePropertyChanged(L"" #name); \
} \
return *this; \
}
/**
* @def INIT_NOTIFYING_PROPERTY
* @brief use this to initialize a wil::single_threaded_notifying_property in your class constructor.
*/
#define INIT_NOTIFYING_PROPERTY(NAME, VALUE) NAME(&m_propertyChanged, *this, L"" #NAME, VALUE)
#endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H))
} // namespace wil