diff --git a/.ecode/project_build.json b/.ecode/project_build.json index f2c49d429..023c47260 100644 --- a/.ecode/project_build.json +++ b/.ecode/project_build.json @@ -252,6 +252,42 @@ "command": "${project_root}/bin/eepp-7guis-cells-debug", "name": "eepp-7guis-cells-debug", "working_dir": "${project_root}/bin" + }, + { + "args": "", + "command": "${project_root}/bin/eepp-7guis-temperature-converter-debug", + "name": "eepp-7guis-temperature-converter-debug", + "working_dir": "${project_root}/bin" + }, + { + "args": "", + "command": "${project_root}/bin/eepp-7guis-circle-drawer-debug", + "name": "eepp-7guis-circle-drawer-debug", + "working_dir": "${project_root}/bin" + }, + { + "args": "", + "command": "${project_root}/bin/eepp-7guis-counter-debug", + "name": "eepp-7guis-counter-debug", + "working_dir": "${project_root}/bin" + }, + { + "args": "", + "command": "${project_root}/bin/eepp-7guis-crud-debug", + "name": "eepp-7guis-crud-debug", + "working_dir": "${project_root}/bin" + }, + { + "args": "", + "command": "${project_root}/bin/eepp-7guis-flight-booker-debug", + "name": "eepp-7guis-flight-booker-debug", + "working_dir": "${project_root}/bin" + }, + { + "args": "", + "command": "${project_root}/bin/eepp-7guis-timer-debug", + "name": "eepp-7guis-timer-debug", + "working_dir": "${project_root}/bin" } ], "var": { diff --git a/include/eepp/ui.hpp b/include/eepp/ui.hpp index 2b2e111e6..1a789eea8 100644 --- a/include/eepp/ui.hpp +++ b/include/eepp/ui.hpp @@ -86,6 +86,7 @@ #include #include +#include #include diff --git a/include/eepp/ui/uidatabind.hpp b/include/eepp/ui/uidatabind.hpp index 06d0c6ac9..ca59ca319 100644 --- a/include/eepp/ui/uidatabind.hpp +++ b/include/eepp/ui/uidatabind.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include namespace EE { namespace UI { @@ -23,12 +23,33 @@ template class UIDataBind { }; static Converter converterDefault() { - return Converter( []( const UIDataBind*, T& val, - const std::string& str ) { return String::fromString( val, str ); }, - []( const UIDataBind*, std::string& str, const T& val ) { - str = String::toString( val ); - return true; - } ); + return Converter( + []( const UIDataBind* databind, T& val, const std::string& str ) { + if constexpr ( std::is_same_v ) { + str = val; + return true; + } else if constexpr ( std::is_same_v ) { + val = StyleSheetProperty( databind->getPropertyDefinition(), str ).asBool(); + return true; + } else { + return String::fromString( val, str ); + } + }, + []( const UIDataBind*, std::string& str, const T& val ) { + if constexpr ( std::is_same_v || + std::is_same_v ) { + str = val; + } else if constexpr ( std::is_same_v ) { + str = String::fromDouble( val ); + } else if constexpr ( std::is_same_v ) { + str = String::fromFloat( val ); + } else if constexpr ( std::is_same_v ) { + str = val ? "true" : "false"; + } else { + str = String::toString( val ); + } + return true; + } ); } static Converter converterString() { @@ -56,7 +77,7 @@ template class UIDataBind { } static std::unique_ptr> - New( T* t, const std::set& widgets, + New( T* t, const std::unordered_set& widgets, const Converter& converter = UIDataBind::converterDefault(), const std::string& valueKey = "value", const Event::EventType& eventType = Event::OnValueChange ) { @@ -74,7 +95,7 @@ template class UIDataBind { UIDataBind() {} - UIDataBind( T* t, const std::set& widgets, + UIDataBind( T* t, const std::unordered_set& widgets, const Converter& converter = UIDataBind::converterDefault(), const std::string& valueKey = "value", const Event::EventType& eventType = Event::OnValueChange ) { @@ -88,7 +109,7 @@ template class UIDataBind { init( t, { widget }, converter, valueKey, eventType ); } - void init( T* t, const std::set& widgets, + void init( T* t, const std::unordered_set& widgets, const Converter& converter = UIDataBind::converterDefault(), const std::string& valueKey = "value", const Event::EventType& eventType = Event::OnValueChange ) { @@ -103,6 +124,8 @@ template class UIDataBind { } void set( const T& t ) { + if ( t == *data ) + return; inSetValue = true; *data = t; setValueChange(); @@ -111,6 +134,17 @@ template class UIDataBind { onValueChangeCb( t ); } + void set( T&& t ) { + if ( t == *data ) + return; + inSetValue = true; + *data = std::move( t ); + setValueChange(); + inSetValue = false; + if ( onValueChangeCb ) + onValueChangeCb( t ); + } + const T& get() const { return *data; } void reset() { @@ -151,11 +185,11 @@ template class UIDataBind { std::function onValueChangeCb; - const std::set& getWidgets() const { return widgets; } + const std::unordered_set& getWidgets() const { return widgets; } protected: T* data{ nullptr }; - std::set widgets; + std::unordered_set widgets; std::unordered_map valueCbs; std::unordered_map closeCbs; bool inSetValue{ false }; @@ -215,7 +249,7 @@ class UIDataBindBool { using Ptr = std::unique_ptr>; static Ptr - New( bool* t, const std::set& widgets, + New( bool* t, const std::unordered_set& widgets, const UIDataBind::Converter& converter = UIDataBind::converterBool(), const std::string& valueKey = "value" ) { return UIDataBind::New( t, widgets, converter, valueKey ); @@ -233,7 +267,7 @@ class UIDataBindString { public: using Ptr = std::unique_ptr>; - static Ptr New( std::string* t, const std::set& widgets, + static Ptr New( std::string* t, const std::unordered_set& widgets, const UIDataBind::Converter& converter = UIDataBind::converterString(), const std::string& valueKey = "text", diff --git a/include/eepp/ui/uiproperty.hpp b/include/eepp/ui/uiproperty.hpp new file mode 100644 index 000000000..66b8352a9 --- /dev/null +++ b/include/eepp/ui/uiproperty.hpp @@ -0,0 +1,74 @@ +#include + +namespace EE { namespace UI { + +template class UIProperty { + public: + UIProperty() {} + + UIProperty( T defaultValue, UIWidget* widget, + const UIDataBind::Converter& converter = UIDataBind::converterDefault(), + const std::string& valueKey = "value", + const Event::EventType& eventType = Event::OnValueChange ) : + mValue( std::move( defaultValue ) ), + mBindedData( &mValue, widget, converter, valueKey, eventType ) {} + + UIProperty( T defaultValue, const std::unordered_set& widgets = {}, + const UIDataBind::Converter& converter = UIDataBind::converterDefault(), + const std::string& valueKey = "value", + const Event::EventType& eventType = Event::OnValueChange ) : + mValue( std::move( defaultValue ) ), + mBindedData( &mValue, widgets, converter, valueKey, eventType ) {} + + UIProperty( const std::unordered_set& widgets = {}, + const UIDataBind::Converter& converter = UIDataBind::converterDefault(), + const std::string& valueKey = "value", + const Event::EventType& eventType = Event::OnValueChange ) : + mBindedData( &mValue, widgets, converter, valueKey, eventType ) {} + + UIProperty( UIWidget* widget, + const UIDataBind::Converter& converter = UIDataBind::converterDefault(), + const std::string& valueKey = "value", + const Event::EventType& eventType = Event::OnValueChange ) : + mBindedData( &mValue, widget, converter, valueKey, eventType ) {} + + void operator=( const T& newVal ) { mBindedData.set( newVal ); } + + void operator=( T&& newVal ) { mBindedData.set( std::move( newVal ) ); } + + const T& value() const { return mBindedData.get(); } + + const UIDataBind& databind() const { return mBindedData; } + + UIProperty& connect( UIWidget* widget ) { + mBindedData.bind( widget ); + return *this; + } + + UIProperty& disconnect( UIWidget* widget ) { + mBindedData.unbind( widget ); + return *this; + } + + const T& operator*() const noexcept { return value(); } + + const T* operator->() const noexcept { return &value(); } + + operator const T&() const noexcept { return value(); } + + UIProperty& changed( const std::function& fn ) { + mBindedData.onValueChangeCb = fn; + return *this; + } + + UIProperty& changed( std::function&& fn ) { + mBindedData.onValueChangeCb = std::move( fn ); + return *this; + } + + protected: + T mValue{}; + UIDataBind mBindedData; +}; + +}} // namespace EE::UI diff --git a/src/eepp/ui/uitextview.cpp b/src/eepp/ui/uitextview.cpp index 96a404f24..15a993368 100644 --- a/src/eepp/ui/uitextview.cpp +++ b/src/eepp/ui/uitextview.cpp @@ -687,6 +687,7 @@ bool UITextView::applyProperty( const StyleSheetProperty& attribute ) { return false; switch ( attribute.getPropertyDefinition()->getPropertyId() ) { + case PropertyId::Value: case PropertyId::Text: setText( getTranslatorString( attribute.value() ) ); break; @@ -786,6 +787,7 @@ std::string UITextView::getPropertyString( const PropertyDefinition* propertyDef return ""; switch ( propertyDef->getPropertyId() ) { + case PropertyId::Value: case PropertyId::Text: return getText().toUtf8(); case PropertyId::TextTransform: @@ -829,21 +831,14 @@ std::string UITextView::getPropertyString( const PropertyDefinition* propertyDef std::vector UITextView::getPropertiesImplemented() const { auto props = UIWidget::getPropertiesImplemented(); - auto local = { PropertyId::Text, - PropertyId::TextTransform, - PropertyId::Color, - PropertyId::TextShadowColor, - PropertyId::TextShadowOffset, - PropertyId::SelectionColor, - PropertyId::SelectionBackColor, - PropertyId::FontFamily, - PropertyId::FontSize, - PropertyId::FontStyle, - PropertyId::Wordwrap, - PropertyId::TextStrokeWidth, - PropertyId::TextStrokeColor, - PropertyId::TextSelection, - PropertyId::TextAlign, + auto local = { PropertyId::Value, PropertyId::Text, + PropertyId::TextTransform, PropertyId::Color, + PropertyId::TextShadowColor, PropertyId::TextShadowOffset, + PropertyId::SelectionColor, PropertyId::SelectionBackColor, + PropertyId::FontFamily, PropertyId::FontSize, + PropertyId::FontStyle, PropertyId::Wordwrap, + PropertyId::TextStrokeWidth, PropertyId::TextStrokeColor, + PropertyId::TextSelection, PropertyId::TextAlign, PropertyId::TextOverflow }; props.insert( props.end(), local.begin(), local.end() ); return props; diff --git a/src/examples/7guis/temperature_converter/temperature_converter.cpp b/src/examples/7guis/temperature_converter/temperature_converter.cpp index a3d8dd0b4..2eea9eb17 100644 --- a/src/examples/7guis/temperature_converter/temperature_converter.cpp +++ b/src/examples/7guis/temperature_converter/temperature_converter.cpp @@ -11,26 +11,9 @@ EE_MAIN_FUNC int main( int, char** ) { )xml" ); - UITextInput* celsiusInput = hbox->find( "celsius_input" ); - UITextInput* fahrenheitInput = hbox->find( "fahrenheit_input" ); - const auto f2c = []( double f ) { return ( f - 32 ) * 5 / 9; }; - const auto c2f = []( double c ) { return c * 9 / 5 + 32; }; - bool converting = false; - const auto convert = [&]( bool fromCelsius ) { - if ( converting ) // Only process input value change and skip value change from setText - return; - BoolScopedOp op( converting, true ); - auto sourceInput = fromCelsius ? celsiusInput : fahrenheitInput; - double val; - if ( !String::fromString( val, sourceInput->getText() ) ) - return; - auto str( String::fromDouble( fromCelsius ? c2f( val ) : f2c( val ) ) ); - if ( fromCelsius ) - fahrenheitInput->setText( str ); - else - celsiusInput->setText( str ); - }; - celsiusInput->setFocus()->on( Event::OnValueChange, [&convert]( auto ) { convert( true ); } ); - fahrenheitInput->on( Event::OnValueChange, [&convert]( auto ) { convert( false ); } ); + UIProperty celsius( hbox->find( "celsius_input" )->setFocus()->asType() ); + UIProperty fahrenheit( hbox->find( "fahrenheit_input" ) ); + celsius.changed( [&fahrenheit]( auto c ) { fahrenheit = c * 9 / 5 + 32; } ); + fahrenheit.changed( [&celsius]( auto f ) { celsius = ( f - 32 ) * 5 / 9; } ); return app.run(); }