From f09ba5ffd53bf7d28e0dc516c00a8f69ca7efae9 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 28 Sep 2023 08:29:55 +1000 Subject: Use bindey for databinding instead of hand rolling ui updates --- lib/bindey/README.md | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 lib/bindey/README.md (limited to 'lib/bindey/README.md') diff --git a/lib/bindey/README.md b/lib/bindey/README.md new file mode 100644 index 00000000..0ef0e62b --- /dev/null +++ b/lib/bindey/README.md @@ -0,0 +1,116 @@ +# bindey + +Everyone knows Model-View-ViewModel is the best architecture, but how can we realize it in C++ applications with minimal overhead, and no complicated framework impositions? + +`bindey` provides the basic building block of MVVM -- an observable "Property" and a databinding mechanism. + +## Property Usage + +At minimum, `bindey::property` can allow you to avoid writing getters and setters. Consider this example: + +``` +#include + +using namespace bindey; + +class Person +{ +public: + property name; + property age; +}; +``` +Then we can use it like this: +``` +Person p; +p.name("Kevin"); +p.age(666); + +auto thatDudesName = p.name(); +auto ageIsJustANumber = p.age(); +``` + +`property` default initializes its value with `{}`, and of course allows initialization. +``` +Person::Person() +: name("Default Name") +, age(0) +{} +``` +## Data Binding +`bindey` provides a simple binding mechanism to connect a "source" `property` to an arbitrary object. This base signature is +``` +template +binding bind( property& from, To& to ); +``` +And a specialization for `property` to `property` binding of the same type is provided. +``` +template +binding bind( property& from, property& to ) +{ + return from.onChanged( [&]( const auto& newValue ) { to( newValue ); } ); +} +``` + +### Writing Your Own Bindings +Where this becomes fun is when you get to reduce boilerplate. For example, assume a `Button` class from some UI Framework. +``` +struct Button +{ + void setText(const std::string& text) + { + this->text = text; + } + + std::string text; +}; +``` +To make your life better, simply implement a template speciailization in the `bindey` namespace. +``` +namespace bindey +{ +template <> +binding bind( property& from, Button& to ) +{ + return from.onChanged( [&]( const auto& newValue ){ to.setText( newValue ); } ); +} +} // namespace bindey +``` +Then, bind your property to the button as needed: +``` +bindey::property name; +... +Button someButton; +... +bindey::bind( name, someButton ); +``` + +### Binding Lifetimes +The result of a call to `bind` is a `bindey::binding` object. If this return value is discarded, then the binding's lifetime is coupled to the `property`'s. + +Otherwise, this token can be used to disconnect the binding as needed, the easiest way is to capture it in a `scoped_binding` object. + +For example, if your binding involves objects who's lifetime you do not control, you should certainly capture the binding to avoid crashes. +``` +struct GreatObj +{ + GreatObj(Button* b) + { + mSomeButton = b; + mButtonBinding = bindey::bind( name, *mSomeButton ); + } + + void updateButton(Button* newB) + { + mSomeButton = nullptr; + mButtonBinding = {}; // disconnect from old button + if( newB != nullptr ) + { + mSomeButton = newB; + mButtonBinding = bindey::bind( name, *mSomeButton ); + } + } + + bindey::scoped_binding mButtonBinding; +}; +``` -- cgit v1.2.3