mirror of https://github.com/PCSX2/pcsx2.git
335 lines
11 KiB
C
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
|