2020-01-18 09:38:21 +01:00
/*
2021-03-28 11:16:33 +02:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2022-02-26 09:09:45 -07:00
* Copyright ( c ) 2022 , the SerenityOS developers .
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2019-01-20 04:49:48 +01:00
# pragma once
2022-12-04 18:02:33 +00:00
# include <AK/DeprecatedString.h>
2020-02-14 22:29:06 +01:00
# include <AK/Forward.h>
2020-09-15 21:33:37 +02:00
# include <AK/HashMap.h>
2019-08-17 11:26:19 +02:00
# include <AK/IntrusiveList.h>
2019-09-22 00:17:53 +02:00
# include <AK/Noncopyable.h>
# include <AK/NonnullRefPtrVector.h>
2021-05-19 14:35:34 +02:00
# include <AK/OwnPtr.h>
2022-03-16 18:26:15 -06:00
# include <AK/StringView.h>
2020-07-26 17:16:35 +02:00
# include <AK/TypeCasts.h>
2019-01-20 04:49:48 +01:00
# include <AK/Weakable.h>
2020-02-14 22:29:06 +01:00
# include <LibCore/Forward.h>
2020-09-15 21:33:37 +02:00
# include <LibCore/Property.h>
2019-08-18 20:36:37 +02:00
2020-02-02 12:34:39 +01:00
namespace Core {
2022-12-28 20:35:16 +00:00
# define REGISTER_ABSTRACT_CORE_OBJECT(namespace_, class_name) \
namespace Core { \
namespace Registration { \
Core : : ObjectClassRegistration registration_ # # class_name ( # namespace_ " :: " # class_name # # sv , [ ] ( ) { return Error : : from_string_literal ( " Attempted to construct an abstract object. " ) ; } ) ; \
} \
2021-10-25 20:20:36 -05:00
}
2022-12-28 20:35:16 +00:00
# define REGISTER_CORE_OBJECT(namespace_, class_name) \
namespace Core { \
namespace Registration { \
Core : : ObjectClassRegistration registration_ # # class_name ( # namespace_ " :: " # class_name # # sv , [ ] ( ) { return namespace_ : : class_name : : try_create ( ) ; } ) ; \
} \
2021-04-04 15:40:34 -06:00
}
class ObjectClassRegistration {
AK_MAKE_NONCOPYABLE ( ObjectClassRegistration ) ;
AK_MAKE_NONMOVABLE ( ObjectClassRegistration ) ;
public :
2022-12-28 20:35:16 +00:00
ObjectClassRegistration ( StringView class_name , Function < ErrorOr < NonnullRefPtr < Object > > ( ) > factory , ObjectClassRegistration * parent_class = nullptr ) ;
2022-02-26 09:09:45 -07:00
~ ObjectClassRegistration ( ) = default ;
2021-04-04 15:40:34 -06:00
2022-03-16 18:26:15 -06:00
StringView class_name ( ) const { return m_class_name ; }
2022-04-01 20:58:27 +03:00
ObjectClassRegistration const * parent_class ( ) const { return m_parent_class ; }
2022-12-28 20:35:16 +00:00
ErrorOr < NonnullRefPtr < Object > > construct ( ) const { return m_factory ( ) ; }
2022-04-01 20:58:27 +03:00
bool is_derived_from ( ObjectClassRegistration const & base_class ) const ;
2021-04-04 15:40:34 -06:00
2022-04-01 20:58:27 +03:00
static void for_each ( Function < void ( ObjectClassRegistration const & ) > ) ;
static ObjectClassRegistration const * find ( StringView class_name ) ;
2021-04-04 15:40:34 -06:00
private :
2021-08-28 02:38:23 -06:00
StringView m_class_name ;
2022-12-28 20:35:16 +00:00
Function < ErrorOr < NonnullRefPtr < Object > > ( ) > m_factory ;
2021-04-04 15:40:34 -06:00
ObjectClassRegistration * m_parent_class { nullptr } ;
} ;
2021-05-13 22:42:11 +02:00
class InspectorServerConnection ;
2020-03-05 14:40:47 +01:00
2019-12-29 15:58:07 +01:00
enum class TimerShouldFireWhenNotVisible {
No = 0 ,
Yes
} ;
2022-11-14 10:38:36 -05:00
# define C_OBJECT(klass) \
public : \
2022-10-17 00:06:11 +02:00
virtual StringView class_name ( ) const override \
{ \
return # klass # # sv ; \
} \
2022-11-14 10:38:36 -05:00
template < typename Klass = klass , class . . . Args > \
static NonnullRefPtr < klass > construct ( Args & & . . . args ) \
{ \
return adopt_ref ( * new Klass ( : : forward < Args > ( args ) . . . ) ) ; \
} \
template < typename Klass = klass , class . . . Args > \
static ErrorOr < NonnullRefPtr < klass > > try_create ( Args & & . . . args ) \
{ \
return adopt_nonnull_ref_or_enomem ( new ( nothrow ) Klass ( : : forward < Args > ( args ) . . . ) ) ; \
2019-09-21 10:13:34 +02:00
}
2019-12-29 15:58:07 +01:00
2022-10-17 00:06:11 +02:00
# define C_OBJECT_ABSTRACT(klass) \
public : \
virtual StringView class_name ( ) const override \
{ \
return # klass # # sv ; \
}
2019-07-25 19:49:28 +02:00
2020-02-02 12:34:39 +01:00
class Object
: public RefCounted < Object >
, public Weakable < Object > {
// NOTE: No C_OBJECT macro for Core::Object itself.
2019-09-22 00:17:53 +02:00
2020-08-26 21:52:24 +02:00
AK_MAKE_NONCOPYABLE ( Object ) ;
AK_MAKE_NONMOVABLE ( Object ) ;
2021-04-16 16:33:24 +04:30
IntrusiveListNode < Object > m_all_objects_list_node ;
2019-08-17 11:26:19 +02:00
2020-10-16 16:04:19 -04:00
public :
2020-02-02 12:34:39 +01:00
virtual ~ Object ( ) ;
2019-01-20 04:49:48 +01:00
2022-03-16 18:26:15 -06:00
virtual StringView class_name ( ) const = 0 ;
2019-01-20 04:49:48 +01:00
2022-09-30 17:59:41 +02:00
template < typename T >
bool fast_is ( ) const = delete ;
virtual bool is_widget ( ) const { return false ; }
2022-12-04 18:02:33 +00:00
DeprecatedString const & name ( ) const { return m_name ; }
void set_name ( DeprecatedString name ) { m_name = move ( name ) ; }
2019-07-10 20:33:53 +02:00
2020-02-02 12:34:39 +01:00
NonnullRefPtrVector < Object > & children ( ) { return m_children ; }
2022-04-01 20:58:27 +03:00
NonnullRefPtrVector < Object > const & children ( ) const { return m_children ; }
2019-01-20 04:49:48 +01:00
2019-05-27 03:52:19 +02:00
template < typename Callback >
void for_each_child ( Callback callback )
{
2019-09-22 00:17:53 +02:00
for ( auto & child : m_children ) {
if ( callback ( child ) = = IterationDecision : : Break )
2019-05-27 03:52:19 +02:00
return ;
}
}
2019-05-28 11:53:16 +02:00
template < typename T , typename Callback >
2022-10-17 00:06:11 +02:00
void for_each_child_of_type ( Callback callback )
requires IsBaseOf < Object , T > ;
2021-01-01 00:46:51 -07:00
template < typename T >
2022-12-04 18:02:33 +00:00
T * find_child_of_type_named ( DeprecatedString const & )
2022-10-17 00:06:11 +02:00
requires IsBaseOf < Object , T > ;
2021-01-01 00:46:51 -07:00
template < typename T >
2022-12-04 18:02:33 +00:00
T * find_descendant_of_type_named ( DeprecatedString const & )
2022-10-17 00:06:11 +02:00
requires IsBaseOf < Object , T > ;
2019-05-27 04:18:24 +02:00
2022-04-01 20:58:27 +03:00
bool is_ancestor_of ( Object const & ) const ;
2019-09-20 20:37:31 +02:00
2020-02-02 12:34:39 +01:00
Object * parent ( ) { return m_parent ; }
2022-04-01 20:58:27 +03:00
Object const * parent ( ) const { return m_parent ; }
2019-01-20 04:49:48 +01:00
2019-12-29 15:58:07 +01:00
void start_timer ( int ms , TimerShouldFireWhenNotVisible = TimerShouldFireWhenNotVisible : : No ) ;
2019-01-31 17:31:23 +01:00
void stop_timer ( ) ;
bool has_timer ( ) const { return m_timer_id ; }
2019-01-20 04:49:48 +01:00
2021-11-24 13:09:51 +01:00
ErrorOr < void > try_add_child ( Object & ) ;
2020-02-02 12:34:39 +01:00
void add_child ( Object & ) ;
void insert_child_before ( Object & new_child , Object & before_child ) ;
void remove_child ( Object & ) ;
2020-12-27 17:14:44 +01:00
void remove_all_children ( ) ;
2019-01-20 04:49:48 +01:00
2021-03-28 11:16:33 +02:00
void set_event_filter ( Function < bool ( Core : : Event & ) > ) ;
2019-03-19 00:01:02 +01:00
void dump_tree ( int indent = 0 ) ;
2021-08-30 10:43:28 +00:00
void deferred_invoke ( Function < void ( ) > ) ;
2019-04-07 14:36:10 +02:00
2021-02-25 21:10:47 +01:00
void save_to ( JsonObject & ) ;
2020-09-15 21:33:37 +02:00
2022-12-04 18:02:33 +00:00
bool set_property ( DeprecatedString const & name , JsonValue const & value ) ;
JsonValue property ( DeprecatedString const & name ) const ;
HashMap < DeprecatedString , NonnullOwnPtr < Property > > const & properties ( ) const { return m_properties ; }
2019-08-18 20:36:37 +02:00
2021-09-09 16:30:59 +04:30
static IntrusiveList < & Object : : m_all_objects_list_node > & all_objects ( ) ;
2019-08-17 11:26:19 +02:00
2020-02-02 12:34:39 +01:00
void dispatch_event ( Core : : Event & , Object * stay_within = nullptr ) ;
2019-09-20 20:37:31 +02:00
2019-09-22 00:41:01 +02:00
void remove_from_parent ( )
{
if ( m_parent )
m_parent - > remove_child ( * this ) ;
}
2020-02-23 07:13:09 +01:00
template < class T , class . . . Args >
2020-03-04 19:07:55 +01:00
inline T & add ( Args & & . . . args )
2020-02-23 07:13:09 +01:00
{
2020-03-04 19:07:55 +01:00
auto child = T : : construct ( forward < Args > ( args ) . . . ) ;
add_child ( * child ) ;
return child ;
2020-02-23 07:13:09 +01:00
}
2021-11-24 13:09:51 +01:00
template < class T , class . . . Args >
inline ErrorOr < NonnullRefPtr < T > > try_add ( Args & & . . . args )
{
auto child = TRY ( T : : try_create ( forward < Args > ( args ) . . . ) ) ;
TRY ( try_add_child ( * child ) ) ;
return child ;
}
2019-12-29 15:58:07 +01:00
virtual bool is_visible_for_timer_purposes ( ) const ;
2020-03-05 14:40:47 +01:00
bool is_being_inspected ( ) const { return m_inspector_count ; }
2021-05-13 22:42:11 +02:00
void increment_inspector_count ( Badge < InspectorServerConnection > ) ;
void decrement_inspector_count ( Badge < InspectorServerConnection > ) ;
2020-03-05 14:40:47 +01:00
2019-03-15 16:12:06 +01:00
protected :
2021-01-01 16:02:16 +01:00
explicit Object ( Object * parent = nullptr ) ;
2019-07-25 19:49:28 +02:00
2022-12-04 18:02:33 +00:00
void register_property ( DeprecatedString const & name , Function < JsonValue ( ) > getter , Function < bool ( JsonValue const & ) > setter = nullptr ) ;
2020-09-15 21:33:37 +02:00
2021-03-28 11:24:22 +02:00
virtual void event ( Core : : Event & ) ;
2020-02-02 12:34:39 +01:00
virtual void timer_event ( TimerEvent & ) ;
virtual void custom_event ( CustomEvent & ) ;
2019-01-20 04:49:48 +01:00
2019-07-27 09:31:46 +02:00
// NOTE: You may get child events for children that are not yet fully constructed!
2020-02-02 12:34:39 +01:00
virtual void child_event ( ChildEvent & ) ;
2019-07-27 09:31:46 +02:00
2020-07-26 17:16:35 +02:00
virtual void did_begin_inspection ( ) { }
virtual void did_end_inspection ( ) { }
2020-03-05 14:40:47 +01:00
2019-03-15 16:12:06 +01:00
private :
2020-02-02 12:34:39 +01:00
Object * m_parent { nullptr } ;
2022-12-04 18:02:33 +00:00
DeprecatedString m_name ;
2019-01-31 17:31:23 +01:00
int m_timer_id { 0 } ;
2020-03-05 14:40:47 +01:00
unsigned m_inspector_count { 0 } ;
2022-12-04 18:02:33 +00:00
HashMap < DeprecatedString , NonnullOwnPtr < Property > > m_properties ;
2020-02-02 12:34:39 +01:00
NonnullRefPtrVector < Object > m_children ;
2021-03-28 11:16:33 +02:00
Function < bool ( Core : : Event & ) > m_event_filter ;
2019-01-20 04:49:48 +01:00
} ;
2019-05-27 04:06:01 +02:00
}
2019-05-27 04:18:24 +02:00
2020-10-15 21:07:36 +02:00
template < >
2021-01-09 01:00:22 +01:00
struct AK : : Formatter < Core : : Object > : AK : : Formatter < FormatString > {
2022-04-01 20:58:27 +03:00
ErrorOr < void > format ( FormatBuilder & builder , Core : : Object const & value )
2021-01-09 01:00:22 +01:00
{
2022-07-11 17:32:29 +00:00
return AK : : Formatter < FormatString > : : format ( builder , " {}({}) " sv , value . class_name ( ) , & value ) ;
2021-01-09 01:00:22 +01:00
}
2020-10-15 21:07:36 +02:00
} ;
2020-07-26 17:16:35 +02:00
namespace Core {
2019-05-27 04:18:24 +02:00
template < typename T , typename Callback >
2022-10-17 00:06:11 +02:00
inline void Object : : for_each_child_of_type ( Callback callback )
requires IsBaseOf < Object , T >
2021-01-01 00:46:51 -07:00
{
for_each_child ( [ & ] ( auto & child ) {
2021-04-17 23:01:24 +02:00
if ( is < T > ( child ) )
return callback ( static_cast < T & > ( child ) ) ;
2021-01-01 00:46:51 -07:00
return IterationDecision : : Continue ;
} ) ;
}
template < typename T >
2022-12-04 18:02:33 +00:00
T * Object : : find_child_of_type_named ( DeprecatedString const & name )
2022-10-17 00:06:11 +02:00
requires IsBaseOf < Object , T >
2021-01-01 00:46:51 -07:00
{
T * found_child = nullptr ;
for_each_child_of_type < T > ( [ & ] ( auto & child ) {
if ( child . name ( ) = = name ) {
found_child = & child ;
return IterationDecision : : Break ;
}
return IterationDecision : : Continue ;
} ) ;
return found_child ;
}
template < typename T >
2022-12-04 18:02:33 +00:00
T * Object : : find_descendant_of_type_named ( DeprecatedString const & name )
2022-10-17 00:06:11 +02:00
requires IsBaseOf < Object , T >
2019-05-27 04:18:24 +02:00
{
2021-04-17 23:01:24 +02:00
if ( is < T > ( * this ) & & this - > name ( ) = = name ) {
return static_cast < T * > ( this ) ;
}
2021-01-01 00:46:51 -07:00
T * found_child = nullptr ;
2019-05-28 11:53:16 +02:00
for_each_child ( [ & ] ( auto & child ) {
2021-01-01 00:46:51 -07:00
found_child = child . template find_descendant_of_type_named < T > ( name ) ;
if ( found_child )
return IterationDecision : : Break ;
2019-05-27 04:18:24 +02:00
return IterationDecision : : Continue ;
} ) ;
2021-01-01 00:46:51 -07:00
return found_child ;
2019-05-27 04:18:24 +02:00
}
2019-07-14 10:59:26 +02:00
2020-09-15 21:33:37 +02:00
# define REGISTER_INT_PROPERTY(property_name, getter, setter) \
register_property ( \
property_name , \
[ this ] { return this - > getter ( ) ; } , \
[ this ] ( auto & value ) { \
this - > setter ( value . template to_number < int > ( ) ) ; \
return true ; \
} ) ;
# define REGISTER_BOOL_PROPERTY(property_name, getter, setter) \
register_property ( \
property_name , \
[ this ] { return this - > getter ( ) ; } , \
[ this ] ( auto & value ) { \
this - > setter ( value . to_bool ( ) ) ; \
return true ; \
} ) ;
# define REGISTER_STRING_PROPERTY(property_name, getter, setter) \
register_property ( \
property_name , \
[ this ] { return this - > getter ( ) ; } , \
[ this ] ( auto & value ) { \
2022-12-06 01:12:49 +00:00
this - > setter ( value . to_deprecated_string ( ) ) ; \
2020-09-15 21:33:37 +02:00
return true ; \
} ) ;
# define REGISTER_READONLY_STRING_PROPERTY(property_name, getter) \
register_property ( \
property_name , \
[ this ] { return this - > getter ( ) ; } , \
2021-01-10 16:29:28 -07:00
{ } ) ;
2020-09-15 21:33:37 +02:00
2022-12-08 10:59:30 -05:00
# define REGISTER_WRITE_ONLY_STRING_PROPERTY(property_name, setter) \
register_property ( \
property_name , \
{ } , \
[ this ] ( auto & value ) { \
this - > setter ( value . to_deprecated_string ( ) ) ; \
return true ; \
} ) ;
2022-06-29 00:39:08 +02:00
# define REGISTER_READONLY_SIZE_PROPERTY(property_name, getter) \
register_property ( \
property_name , \
[ this ] { \
auto size = this - > getter ( ) ; \
JsonArray size_array ; \
size_array . append ( size . width ( ) ) ; \
size_array . append ( size . height ( ) ) ; \
return size_array ; \
} , \
{ } ) ;
2022-12-21 16:51:33 +00:00
# define REGISTER_RECT_PROPERTY(property_name, getter, setter) \
register_property ( \
property_name , \
[ this ] { \
auto rect = this - > getter ( ) ; \
JsonObject rect_object ; \
rect_object . set ( " x " sv , rect . x ( ) ) ; \
rect_object . set ( " y " sv , rect . y ( ) ) ; \
rect_object . set ( " width " sv , rect . width ( ) ) ; \
rect_object . set ( " height " sv , rect . height ( ) ) ; \
return rect_object ; \
} , \
[ this ] ( auto & value ) { \
Gfx : : IntRect rect ; \
if ( value . is_object ( ) ) { \
rect . set_x ( value . as_object ( ) . get_i32 ( " x " sv ) . value_or ( 0 ) ) ; \
rect . set_y ( value . as_object ( ) . get_i32 ( " y " sv ) . value_or ( 0 ) ) ; \
rect . set_width ( value . as_object ( ) . get_i32 ( " width " sv ) . value_or ( 0 ) ) ; \
rect . set_height ( value . as_object ( ) . get_i32 ( " height " sv ) . value_or ( 0 ) ) ; \
} else if ( value . is_array ( ) & & value . as_array ( ) . size ( ) = = 4 ) { \
rect . set_x ( value . as_array ( ) [ 0 ] . to_i32 ( ) ) ; \
rect . set_y ( value . as_array ( ) [ 1 ] . to_i32 ( ) ) ; \
rect . set_width ( value . as_array ( ) [ 2 ] . to_i32 ( ) ) ; \
rect . set_height ( value . as_array ( ) [ 3 ] . to_i32 ( ) ) ; \
} else { \
return false ; \
} \
setter ( rect ) ; \
\
return true ; \
2020-09-15 21:33:37 +02:00
} ) ;
2022-03-24 15:56:09 +01:00
# define REGISTER_SIZE_PROPERTY(property_name, getter, setter) \
register_property ( \
property_name , \
[ this ] { \
auto size = this - > getter ( ) ; \
JsonArray size_array ; \
size_array . append ( size . width ( ) ) ; \
size_array . append ( size . height ( ) ) ; \
return size_array ; \
} , \
[ this ] ( auto & value ) { \
if ( ! value . is_array ( ) ) \
return false ; \
Gfx : : IntSize size ; \
size . set_width ( value . as_array ( ) [ 0 ] . to_i32 ( ) ) ; \
size . set_height ( value . as_array ( ) [ 1 ] . to_i32 ( ) ) ; \
setter ( size ) ; \
return true ; \
2020-09-15 21:33:37 +02:00
} ) ;
2020-12-28 12:58:21 +01:00
# define REGISTER_ENUM_PROPERTY(property_name, getter, setter, EnumType, ...) \
register_property ( \
property_name , \
[ this ] ( ) - > JsonValue { \
struct { \
EnumType enum_value ; \
2022-12-04 18:02:33 +00:00
DeprecatedString string_value ; \
2020-12-28 12:58:21 +01:00
} options [ ] = { __VA_ARGS__ } ; \
auto enum_value = getter ( ) ; \
for ( size_t i = 0 ; i < array_size ( options ) ; + + i ) { \
auto & option = options [ i ] ; \
if ( enum_value = = option . enum_value ) \
return option . string_value ; \
} \
return JsonValue ( ) ; \
} , \
[ this ] ( auto & value ) { \
struct { \
EnumType enum_value ; \
2022-12-04 18:02:33 +00:00
DeprecatedString string_value ; \
2020-12-28 12:58:21 +01:00
} options [ ] = { __VA_ARGS__ } ; \
2021-03-18 20:01:09 +03:00
if ( ! value . is_string ( ) ) \
return false ; \
2020-12-28 12:58:21 +01:00
auto string_value = value . as_string ( ) ; \
for ( size_t i = 0 ; i < array_size ( options ) ; + + i ) { \
auto & option = options [ i ] ; \
if ( string_value = = option . string_value ) { \
setter ( option . enum_value ) ; \
return true ; \
} \
} \
return false ; \
} )
2020-12-28 20:45:04 +01:00
# define REGISTER_TEXT_ALIGNMENT_PROPERTY(property_name, getter, setter) \
REGISTER_ENUM_PROPERTY ( \
property_name , getter , setter , Gfx : : TextAlignment , \
{ Gfx : : TextAlignment : : Center , " Center " } , \
2021-05-21 01:03:02 +01:00
{ Gfx : : TextAlignment : : CenterLeft , " CenterLeft " } , \
2020-12-28 20:45:04 +01:00
{ Gfx : : TextAlignment : : CenterRight , " CenterRight " } , \
2022-03-25 12:30:48 +01:00
{ Gfx : : TextAlignment : : TopCenter , " TopCenter " } , \
2021-05-21 01:03:02 +01:00
{ Gfx : : TextAlignment : : TopLeft , " TopLeft " } , \
2020-12-28 20:45:04 +01:00
{ Gfx : : TextAlignment : : TopRight , " TopRight " } , \
2022-03-25 12:30:48 +01:00
{ Gfx : : TextAlignment : : BottomCenter , " BottomCenter " } , \
2021-05-21 01:03:02 +01:00
{ Gfx : : TextAlignment : : BottomLeft , " BottomLeft " } , \
2020-12-28 20:45:04 +01:00
{ Gfx : : TextAlignment : : BottomRight , " BottomRight " } )
2021-03-22 19:51:06 +00:00
# define REGISTER_FONT_WEIGHT_PROPERTY(property_name, getter, setter) \
REGISTER_ENUM_PROPERTY ( \
property_name , getter , setter , unsigned , \
{ Gfx : : FontWeight : : Thin , " Thin " } , \
{ Gfx : : FontWeight : : ExtraLight , " ExtraLight " } , \
{ Gfx : : FontWeight : : Light , " Light " } , \
{ Gfx : : FontWeight : : Regular , " Regular " } , \
{ Gfx : : FontWeight : : Medium , " Medium " } , \
{ Gfx : : FontWeight : : SemiBold , " SemiBold " } , \
{ Gfx : : FontWeight : : Bold , " Bold " } , \
{ Gfx : : FontWeight : : ExtraBold , " ExtraBold " } , \
{ Gfx : : FontWeight : : Black , " Black " } , \
{ Gfx : : FontWeight : : ExtraBlack , " ExtraBlack " } )
2021-07-29 20:06:55 +00:00
# define REGISTER_TEXT_WRAPPING_PROPERTY(property_name, getter, setter) \
REGISTER_ENUM_PROPERTY ( \
property_name , getter , setter , Gfx : : TextWrapping , \
{ Gfx : : TextWrapping : : Wrap , " Wrap " } , \
{ Gfx : : TextWrapping : : DontWrap , " DontWrap " } )
2020-02-02 12:34:39 +01:00
}