MCRO
C++23 utilities for Unreal Engine.
Loading...
Searching...
No Matches
Slate.h
Go to the documentation of this file.
1/** @noop License Comment
2 * @file
3 * @copyright
4 * This Source Code is subject to the terms of the Mozilla Public License, v2.0.
5 * If a copy of the MPL was not distributed with this file You can obtain one at
6 * https://mozilla.org/MPL/2.0/
7 *
8 * @author David Mórász
9 * @date 2025
10 */
11
12#pragma once
13
14#include "CoreMinimal.h"
15#include "Widgets/SWidget.h"
16#include "Widgets/SBoxPanel.h"
17#include "Layout/Visibility.h"
18#include "SlotBase.h"
19#include "Mcro/FunctionTraits.h"
21#include "Mcro/Range/Views.h"
22
23/**
24 * @brief
25 * Extra functionalities for general Slate programming chores, including enhancements of the Slate declarative syntax
26 */
27namespace Mcro::Slate
28{
29 using namespace Mcro::FunctionTraits;
30
31 /** @brief Constraining given type to a Slate widget */
32 template <typename T>
33 concept CWidget = CDerivedFrom<T, SWidget>;
34
35 /** @brief Constraining given type to a slot of a widget */
36 template <typename T>
37 concept CSlot = CDerivedFrom<T, FSlotBase>;
38
39 /** @brief Constraining given type to either a slot or a widget */
40 template <typename T>
42
43 /** @brief Constraining given type to the arguments of either a widget or a slot */
44 template <typename T>
45 concept CWidgetOrSlotArguments = CSameAsDecayed<T, typename std::decay_t<T>::WidgetArgsType>;
46
47 /** @brief Constraining given type to the arguments of a widget */
48 template <typename T>
49 concept CWidgetArguments = requires(typename std::decay_t<T>::WidgetType& t) { t; };
50
51 /** @brief Constraining given type to the arguments of a slot */
52 template <typename T>
53 concept CSlotArguments = CDerivedFrom<T, FSlotBase::FSlotArguments>
54 && requires(T& args)
55 {
56 args.GetSlot();
57 }
58 ;
59
60 /** @brief Constraining given type to a widget which can receive slots */
61 template <typename T>
62 concept CWidgetWithSlots = requires(typename T::FSlot& t) { t; };
63
64 template <typename T>
66 && CDerivedFrom<T, SBoxPanel>
67 && requires(T&& t, typename T::FScopedWidgetSlotArguments& slotArgs)
68 {
69 { t.AddSlot() } -> CSameAsDecayed<typename T::FScopedWidgetSlotArguments>;
70 }
71 ;
72
73 template <typename T>
75
76 template <CWidget T>
77 struct TArgumentsOf_Struct<T>
78 {
79 using Type = typename T::FArguments;
80 };
81
82 template <CSlot T>
84 {
85 using Type = typename T::FSlotArguments;
86 };
87
88 /** @brief Get the type of arguments from either a widget or a slot type (FArguments or FSlotArguments) */
89 template <typename T>
91
92 /**
93 * @brief
94 * An attribute block functor which takes in reference of FArguments or FSlotArguments and returns the
95 * same reference but presumably setting some Slate attributes before that. This is useful for modularizing the
96 * Slate declarative syntax.
97 *
98 * Use `TAttributeBlock` alias for your functions for better convenience
99 */
100 template <CWidgetOrSlotArguments T>
102 {
103 using Function = TFunction<T&(T&)>;
104
105 template <CConvertibleToDecayed<Function> Arg>
106 TAttributeBlockFunctor(Arg&& function) : Storage(FWD(function)) {}
107
109
110 T& operator () (T& args) const
111 {
112 return Storage(args);
113 }
114
115 /**
116 * @brief
117 * The "append attribute block" operator which allows pre-defined "blocks of slate attributes" naturally fit
118 * inside the Slate declarative syntax. Traditionally repeated structures in Slate were expressed as either
119 * explicit mutations on widgets after they were created or as entirely separate compound widgets. Either way
120 * breaks the flow of the declarative syntax and makes using Slate sometimes pretty clunky. This operator aims
121 * to make widget composition more comfortable.
122 *
123 * @tparam Arguments Right hand side FArguments or FSlotArguments
124 * @param args r-value reference right hand side FArguments or FSlotArguments
125 * @param attributes An attribute block function
126 * @return The same reference as args or a new slot if that has been added inside the attribute block
127 */
128 friend T& operator / (TIdentity_T<T>&& args, TAttributeBlockFunctor const& attributes)
129 {
130 return attributes(args);
131 }
132
133 /**
134 * @brief
135 * The "append attribute block" operator which allows pre-defined "blocks of slate attributes" naturally fit
136 * inside the Slate declarative syntax. Traditionally repeated structures in Slate were expressed as either
137 * explicit mutations on widgets after they were created or as entirely separate compound widgets. Either way
138 * breaks the flow of the declarative syntax and makes using Slate sometimes pretty clunky. This operator aims
139 * to make widget composition more comfortable.
140 *
141 * @tparam Arguments Right hand side FArguments or FSlotArguments
142 * @param args l-value reference right hand side FArguments or FSlotArguments
143 * @param attributes An attribute block function
144 * @return The same reference as args or a new slot if that has been added inside the attribute block
145 */
146 friend T& operator / (T& args, TAttributeBlockFunctor const& attributes)
147 {
148 return attributes(args);
149 }
150 };
151
152 /**
153 * @brief
154 * An attribute block functor which takes in reference of FArguments or FSlotArguments and returns the
155 * same reference but presumably setting some Slate attributes before that. This is useful for modularizing the
156 * Slate declarative syntax.
157 */
158 template <CWidgetOrSlot T>
160
161 /** @brief An attribute block which does nothing */
162 template <CWidgetOrSlot T>
163 TAttributeBlock<T> InertAttributeBlock = [](TArgumentsOf<T>& args) -> auto& { return args; };
164
165 /**
166 * @brief Add multiple slots at the same time with the declarative syntax derived from an input data array.
167 *
168 * @code
169 * void SMyWidget::Construct(const FArguments& args)
170 * {
171 * using namespace Mcro::Slate;
172 *
173 * ChildSlot
174 * [
175 * SNew(SVerticalBox)
176 * + TSlots(args._DataArray, [](const FMyData& data)
177 * {
178 * FText dataText = FText::FromString(data.ToString());
179 * return MoveTemp(SVerticalBox::Slot()
180 * . HAlign(HAlign_Fill)
181 * . AutoHeight()
182 * [ SNew(STextBlock).Text(dataText) ];
183 * );
184 * })
185 * + SVerticalBox::Slot()
186 * . HAlign(HAlign_Fill)
187 * . AutoHeight()
188 * [
189 * SNew(STextBlock)
190 * . Text(INVTEXT_"Footer after the list of data")
191 * ]
192 * ];
193 * }
194 * @endcode
195 */
196 template <
197 CRangeMember Range,
198 CFunctionLike Transform,
199 CFunctionLike OnEmpty = TUniqueFunction<TFunction_Return<Transform>()>,
200 CSlotArguments = TFunction_Return<Transform>
201 >
202 requires (TFunction_ArgCount<Transform> == 1)
203 struct TSlots
204 {
205 TSlots(Range const& range, Transform&& transform, TOptional<OnEmpty>&& onEmpty = {})
206 : RangeRef(const_cast<Range&>(range))
207 , TransformStorage(MoveTemp(transform))
208 , OnEmptyStorage(MoveTemp(onEmpty))
209 {}
210 TSlots(Range& range, Transform&& transform, TOptional<OnEmpty>&& onEmpty = {})
211 : RangeRef(range)
212 , TransformStorage(MoveTemp(transform))
213 , OnEmptyStorage(MoveTemp(onEmpty))
214 {}
215
216 TSlots(TSlots const&) = delete;
217 TSlots(TSlots&& o) noexcept
218 : RangeRef(o.RangeRef)
219 , TransformStorage(MoveTemp(o.TransformStorage))
220 , OnEmptyStorage(MoveTemp(o.OnEmptyStorage))
221 {
222 }
223
224 TSlots& operator=(TSlots const&) = delete;
225 TSlots& operator=(TSlots&& o) noexcept
226 {
227 if (this == &o)
228 return *this;
229 RangeRef = o.RangeRef;
230 TransformStorage = MoveTemp(o.TransformStorage);
231 OnEmptyStorage = MoveTemp(o.OnEmptyStorage);
232 return *this;
233 }
234
235 template <CWidgetArguments Arguments>
236 void Append(Arguments& args)
237 {
238 using namespace Mcro::Range;
239 if (IteratorEquals(RangeRef.begin(), RangeRef.end()) && OnEmptyStorage.IsSet())
240 {
241 args + OnEmptyStorage.GetValue()();
242 return;
243 }
244
245 for (auto it = RangeRef.begin(); !IteratorEquals(it, RangeRef.end()); ++it)
246 args + TransformStorage(*it);
247 }
248
249 /** @copydoc TSlots */
250 template <CWidgetArguments Arguments>
251 friend Arguments& operator + (Arguments&& args, TSlots&& slots)
252 {
253 slots.Append(args);
254 return args;
255 }
256
257 private:
258 Range& RangeRef;
259 Transform TransformStorage;
260 TOptional<OnEmpty> OnEmptyStorage;
261 };
262
263 /**
264 * @brief Create widget slots from ranges of tuples via a transformation with structured binding arguments.
265 * @see Mcro::Range::TransformTuple
266 *
267 * For example:
268 * @code
269 * void SMyWidget::Construct(const FArguments& args)
270 * {
271 * using namespace Mcro::Slate;
272 *
273 * ChildSlot
274 * [
275 * SNew(SVerticalBox)
276 * + SlotsFromTuples(args._DataMap, [](const FString& key, int32 value)
277 * {
278 * return MoveTemp(SVerticalBox::Slot()
279 * . HAlign(HAlign_Fill)
280 * . AutoHeight()
281 * [
282 * SNew(SHorizontalBox)
283 * + SHorizontalBox::Slot() [ SNew(STextBlock).Text(AsText(key)) ]
284 * + SHorizontalBox::Slot() [ SNew(STextBlock).Text(AsText(value)) ]
285 * ];
286 * );
287 * })
288 * + SVerticalBox::Slot()
289 * . HAlign(HAlign_Fill)
290 * . AutoHeight()
291 * [
292 * SNew(STextBlock)
293 * . Text(INVTEXT_"Footer after the list of data")
294 * ]
295 * ];
296 * }
297 * @endcode
298 */
299 template <
300 CFunctionLike Transform,
302 CFunctionLike OnEmpty = TUniqueFunction<TFunction_Return<Transform>()>,
303 CSlotArguments = TFunction_Return<Transform>
304 >
305 requires (TFunction_ArgCount<Transform> >= 1)
306 auto SlotsFromTuples(Range&& range, Transform&& transform, TOptional<OnEmpty>&& onEmpty = {})
307 {
308 using namespace Mcro::Range;
309 using Tuple = TRangeElementType<Range>;
310
311 return TSlots(FWD(range), [transform](Tuple const& tuple)
312 {
313 return InvokeWithTuple(transform, tuple);
314 }, FWD(onEmpty));
315 }
316
317 /** @brief Convenience function for typing less when widget visibility depends on a boolean */
318 MCRO_API EVisibility IsVisible(bool visible, EVisibility hiddenState = EVisibility::Collapsed);
319}
#define FWD(...)
Shorten forwarding expression with this macro so one may not need to specify explicit type.
Definition Macros.h:100
Constraining given type to the arguments of a slot
Definition Slate.h:53
Constraining given type to a slot of a widget.
Definition Slate.h:37
Constraining given type to the arguments of a widget
Definition Slate.h:49
Constraining given type to the arguments of either a widget or a slot.
Definition Slate.h:45
Constraining given type to either a slot or a widget.
Definition Slate.h:41
Constraining given type to a widget which can receive slots.
Definition Slate.h:62
Constraining given type to a Slate widget.
Definition Slate.h:33
typename TFunctionTraits< std::decay_t< T > >::Return TFunction_Return
Shorthand for getting a function return type.
constexpr size_t TFunction_ArgCount
Shorthand for getting a function argument count.
TFunction_Return< Function > InvokeWithTuple(Function &&function, Tuple &&arguments)
A clone of std::apply for Unreal, STL and RangeV3 tuples which also supports function pointers.
bool IteratorEquals(L const &l, R const &r)
Definition Concepts.h:45
TIteratorElementType< decltype(DeclVal< T >().begin())> TRangeElementType
return a range's associated content type determined by dereferencing their iterator.
Definition Concepts.h:161
Extra functionalities for general Slate programming chores, including enhancements of the Slate decla...
Definition Slate.h:28
MCRO_API EVisibility IsVisible(bool visible, EVisibility hiddenState=EVisibility::Collapsed)
Convenience function for typing less when widget visibility depends on a boolean.
auto SlotsFromTuples(Range &&range, Transform &&transform, TOptional< OnEmpty > &&onEmpty={})
Create widget slots from ranges of tuples via a transformation with structured binding arguments.
Definition Slate.h:306
typename TArgumentsOf_Struct< T >::Type TArgumentsOf
Get the type of arguments from either a widget or a slot type (FArguments or FSlotArguments)
Definition Slate.h:90
TAttributeBlock< T > InertAttributeBlock
An attribute block which does nothing.
Definition Slate.h:163
typename T::FArguments Type
Definition Slate.h:79
An attribute block functor which takes in reference of FArguments or FSlotArguments and returns the s...
Definition Slate.h:102
TAttributeBlockFunctor(Arg &&function)
Definition Slate.h:106
friend T & operator/(TIdentity_T< T > &&args, TAttributeBlockFunctor const &attributes)
The "append attribute block" operator which allows pre-defined "blocks of slate attributes" naturally...
Definition Slate.h:128
T & operator()(T &args) const
Definition Slate.h:110
TFunction< T &(T &)> Function
Definition Slate.h:103
Add multiple slots at the same time with the declarative syntax derived from an input data array.
Definition Slate.h:204
TSlots(TSlots const &)=delete
void Append(Arguments &args)
Definition Slate.h:236
TSlots(TSlots &&o) noexcept
Definition Slate.h:217
TSlots(Range const &range, Transform &&transform, TOptional< OnEmpty > &&onEmpty={})
Definition Slate.h:205
TSlots & operator=(TSlots const &)=delete
TSlots & operator=(TSlots &&o) noexcept
Definition Slate.h:225
TSlots(Range &range, Transform &&transform, TOptional< OnEmpty > &&onEmpty={})
Definition Slate.h:210