mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
Added UIProperty to easily bind values with UI elements (very basic initial implementation).
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
#include <eepp/ui/models/widgettreemodel.hpp>
|
||||
|
||||
#include <eepp/ui/uidatabind.hpp>
|
||||
#include <eepp/ui/uiproperty.hpp>
|
||||
|
||||
#include <eepp/ui/undostack.hpp>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <eepp/system/log.hpp>
|
||||
#include <eepp/ui/uiwidget.hpp>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
@@ -23,12 +23,33 @@ template <typename T> class UIDataBind {
|
||||
};
|
||||
|
||||
static Converter converterDefault() {
|
||||
return Converter( []( const UIDataBind<T>*, T& val,
|
||||
const std::string& str ) { return String::fromString( val, str ); },
|
||||
[]( const UIDataBind<T>*, std::string& str, const T& val ) {
|
||||
str = String::toString( val );
|
||||
return true;
|
||||
} );
|
||||
return Converter(
|
||||
[]( const UIDataBind<T>* databind, T& val, const std::string& str ) {
|
||||
if constexpr ( std::is_same_v<T, std::string> ) {
|
||||
str = val;
|
||||
return true;
|
||||
} else if constexpr ( std::is_same_v<T, bool> ) {
|
||||
val = StyleSheetProperty( databind->getPropertyDefinition(), str ).asBool();
|
||||
return true;
|
||||
} else {
|
||||
return String::fromString( val, str );
|
||||
}
|
||||
},
|
||||
[]( const UIDataBind<T>*, std::string& str, const T& val ) {
|
||||
if constexpr ( std::is_same_v<T, std::string> ||
|
||||
std::is_same_v<T, std::string_view> ) {
|
||||
str = val;
|
||||
} else if constexpr ( std::is_same_v<T, double> ) {
|
||||
str = String::fromDouble( val );
|
||||
} else if constexpr ( std::is_same_v<T, float> ) {
|
||||
str = String::fromFloat( val );
|
||||
} else if constexpr ( std::is_same_v<T, bool> ) {
|
||||
str = val ? "true" : "false";
|
||||
} else {
|
||||
str = String::toString( val );
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
}
|
||||
|
||||
static Converter converterString() {
|
||||
@@ -56,7 +77,7 @@ template <typename T> class UIDataBind {
|
||||
}
|
||||
|
||||
static std::unique_ptr<UIDataBind<T>>
|
||||
New( T* t, const std::set<UIWidget*>& widgets,
|
||||
New( T* t, const std::unordered_set<UIWidget*>& widgets,
|
||||
const Converter& converter = UIDataBind<T>::converterDefault(),
|
||||
const std::string& valueKey = "value",
|
||||
const Event::EventType& eventType = Event::OnValueChange ) {
|
||||
@@ -74,7 +95,7 @@ template <typename T> class UIDataBind {
|
||||
|
||||
UIDataBind() {}
|
||||
|
||||
UIDataBind( T* t, const std::set<UIWidget*>& widgets,
|
||||
UIDataBind( T* t, const std::unordered_set<UIWidget*>& widgets,
|
||||
const Converter& converter = UIDataBind<T>::converterDefault(),
|
||||
const std::string& valueKey = "value",
|
||||
const Event::EventType& eventType = Event::OnValueChange ) {
|
||||
@@ -88,7 +109,7 @@ template <typename T> class UIDataBind {
|
||||
init( t, { widget }, converter, valueKey, eventType );
|
||||
}
|
||||
|
||||
void init( T* t, const std::set<UIWidget*>& widgets,
|
||||
void init( T* t, const std::unordered_set<UIWidget*>& widgets,
|
||||
const Converter& converter = UIDataBind<T>::converterDefault(),
|
||||
const std::string& valueKey = "value",
|
||||
const Event::EventType& eventType = Event::OnValueChange ) {
|
||||
@@ -103,6 +124,8 @@ template <typename T> class UIDataBind {
|
||||
}
|
||||
|
||||
void set( const T& t ) {
|
||||
if ( t == *data )
|
||||
return;
|
||||
inSetValue = true;
|
||||
*data = t;
|
||||
setValueChange();
|
||||
@@ -111,6 +134,17 @@ template <typename T> 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 <typename T> class UIDataBind {
|
||||
|
||||
std::function<void( const T& newVal )> onValueChangeCb;
|
||||
|
||||
const std::set<UIWidget*>& getWidgets() const { return widgets; }
|
||||
const std::unordered_set<UIWidget*>& getWidgets() const { return widgets; }
|
||||
|
||||
protected:
|
||||
T* data{ nullptr };
|
||||
std::set<UIWidget*> widgets;
|
||||
std::unordered_set<UIWidget*> widgets;
|
||||
std::unordered_map<UIWidget*, Uint32> valueCbs;
|
||||
std::unordered_map<UIWidget*, Uint32> closeCbs;
|
||||
bool inSetValue{ false };
|
||||
@@ -215,7 +249,7 @@ class UIDataBindBool {
|
||||
using Ptr = std::unique_ptr<UIDataBind<bool>>;
|
||||
|
||||
static Ptr
|
||||
New( bool* t, const std::set<UIWidget*>& widgets,
|
||||
New( bool* t, const std::unordered_set<UIWidget*>& widgets,
|
||||
const UIDataBind<bool>::Converter& converter = UIDataBind<bool>::converterBool(),
|
||||
const std::string& valueKey = "value" ) {
|
||||
return UIDataBind<bool>::New( t, widgets, converter, valueKey );
|
||||
@@ -233,7 +267,7 @@ class UIDataBindString {
|
||||
public:
|
||||
using Ptr = std::unique_ptr<UIDataBind<std::string>>;
|
||||
|
||||
static Ptr New( std::string* t, const std::set<UIWidget*>& widgets,
|
||||
static Ptr New( std::string* t, const std::unordered_set<UIWidget*>& widgets,
|
||||
const UIDataBind<std::string>::Converter& converter =
|
||||
UIDataBind<std::string>::converterString(),
|
||||
const std::string& valueKey = "text",
|
||||
|
||||
74
include/eepp/ui/uiproperty.hpp
Normal file
74
include/eepp/ui/uiproperty.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <eepp/ui/uidatabind.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
template <typename T> class UIProperty {
|
||||
public:
|
||||
UIProperty() {}
|
||||
|
||||
UIProperty( T defaultValue, UIWidget* widget,
|
||||
const UIDataBind<T>::Converter& converter = UIDataBind<T>::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<UIWidget*>& widgets = {},
|
||||
const UIDataBind<T>::Converter& converter = UIDataBind<T>::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<UIWidget*>& widgets = {},
|
||||
const UIDataBind<T>::Converter& converter = UIDataBind<T>::converterDefault(),
|
||||
const std::string& valueKey = "value",
|
||||
const Event::EventType& eventType = Event::OnValueChange ) :
|
||||
mBindedData( &mValue, widgets, converter, valueKey, eventType ) {}
|
||||
|
||||
UIProperty( UIWidget* widget,
|
||||
const UIDataBind<T>::Converter& converter = UIDataBind<T>::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<T>& 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<void( const T& newVal )>& fn ) {
|
||||
mBindedData.onValueChangeCb = fn;
|
||||
return *this;
|
||||
}
|
||||
|
||||
UIProperty& changed( std::function<void( const T& newVal )>&& fn ) {
|
||||
mBindedData.onValueChangeCb = std::move( fn );
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
T mValue{};
|
||||
UIDataBind<T> mBindedData;
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
@@ -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<PropertyId> 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;
|
||||
|
||||
@@ -11,26 +11,9 @@ EE_MAIN_FUNC int main( int, char** ) {
|
||||
<TextView text="Fahrenheit" layout_height="match_parent" padding="0dp 4dp 0dp 4dp" enabled="false" />
|
||||
</hbox>
|
||||
)xml" );
|
||||
UITextInput* celsiusInput = hbox->find<UITextInput>( "celsius_input" );
|
||||
UITextInput* fahrenheitInput = hbox->find<UITextInput>( "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<double> celsius( hbox->find( "celsius_input" )->setFocus()->asType<UIWidget>() );
|
||||
UIProperty<double> fahrenheit( hbox->find<UITextInput>( "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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user