blob: 8e65591b8a9d227468033dddb0017f2766c1e80c [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_VR_DATABINDING_BINDING_H_
#define CHROME_BROWSER_VR_DATABINDING_BINDING_H_
#include <memory>
#include <optional>
#include "base/functional/bind.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/vr/databinding/binding_base.h"
namespace vr {
// This class represents a one-way binding that propagates a change from a
// source/model property to a sink/view. This is inappropriate for use in the
// case of, say, an editor view like a text field where changes must also be
// propagated back to the model.
//
// IMPORTANT: it is assumed that a Binding instance will outlive the model to
// which it is bound. This class in not appropriate for use with models that
// come and go in the lifetime of the application.
template <typename T>
class Binding : public BindingBase {
public:
#ifndef NDEBUG
Binding(const base::RepeatingCallback<T()>& getter,
const std::string& getter_text,
const base::RepeatingCallback<void(const T&)>& setter,
const std::string& setter_text)
: getter_(getter),
setter_(setter),
getter_text_(getter_text),
setter_text_(setter_text) {}
Binding(const base::RepeatingCallback<T()>& getter,
const std::string& getter_text,
const base::RepeatingCallback<void(const std::optional<T>&,
const T&)>& setter,
const std::string& setter_text)
: getter_(getter),
historic_setter_(setter),
getter_text_(getter_text),
setter_text_(setter_text) {}
#else
Binding(const base::RepeatingCallback<T()>& getter,
const base::RepeatingCallback<void(const T&)>& setter)
: getter_(getter), setter_(setter) {}
Binding(const base::RepeatingCallback<T()>& getter,
const base::RepeatingCallback<void(const std::optional<T>&,
const T&)>& setter)
: getter_(getter), historic_setter_(setter) {}
#endif
Binding(const Binding&) = delete;
Binding& operator=(const Binding&) = delete;
~Binding() override = default;
// This function will check if the getter is producing a different value than
// when it was last polled. If so, it will pass that value to the provided
// setter. NB: this assumes that T is copyable.
bool Update() override {
T current_value = getter_.Run();
if (last_value_ && current_value == last_value_.value())
return false;
if (setter_)
setter_.Run(current_value);
if (historic_setter_)
historic_setter_.Run(last_value_, current_value);
last_value_ = current_value;
return true;
}
std::string ToString() override {
#ifndef NDEBUG
if (getter_text_.empty() && setter_text_.empty())
return "";
return base::StringPrintf("%s => %s", getter_text_.c_str(),
setter_text_.c_str());
#else
return "";
#endif
}
private:
base::RepeatingCallback<T()> getter_;
base::RepeatingCallback<void(const T&)> setter_;
base::RepeatingCallback<void(const std::optional<T>&, const T&)>
historic_setter_;
std::optional<T> last_value_;
#ifndef NDEBUG
std::string getter_text_;
std::string setter_text_;
#endif
};
// These macros are sugar for constructing a simple binding. It is meant to make
// setting up bindings a little less painful, but it is not meant to handle all
// cases. If you need to do something more complex (eg, convert type T before
// it is propagated), you should use the constructor directly.
//
// For example:
//
// struct MyModel { int source; };
// struct MyView {
// int sink;
// int awesomeness;
// void SetAwesomeness(int new_awesomeness) {
// awesomeness = new_awesomeness;
// }
// };
//
// MyModel m;
// m.source = 20;
//
// MyView v;
// v.sink = 10;
// v.awesomeness = 30;
//
// auto binding = VR_BIND(int, MyModel, &m, source, MyView, &v, sink = value);
//
// Or, equivalently:
//
// auto binding = VR_BIND_FIELD(int, MyModel, &m, source, MyView, &v, sink);
//
// If your view has a setter, you may find VR_BIND_FUNC handy:
//
// auto binding =
// VR_BIND_FUNC(int, MyModel, &m, source, MyView, &v, SetAwesomeness);
//
#ifndef NDEBUG
#define VR_BIND(T, M, m, Get, V, v, Set) \
std::make_unique<Binding<T>>( \
base::BindRepeating([](M* model) { return Get; }, base::Unretained(m)), \
#Get, \
base::BindRepeating([](V* view, T const& value) { Set; }, \
base::Unretained(v)), \
#Set)
#else
#define VR_BIND(T, M, m, Get, V, v, Set) \
std::make_unique<Binding<T>>( \
base::BindRepeating([](M* model) { return Get; }, base::Unretained(m)), \
base::BindRepeating([](V* view, T const& value) { Set; }, \
base::Unretained(v)))
#endif
#define VR_BIND_FUNC(T, M, m, Get, V, v, f) \
VR_BIND(T, M, m, Get, V, v, view->f(value))
#define VR_BIND_FIELD(T, M, m, Get, V, v, f) \
VR_BIND(T, M, m, Get, V, v, view->f = value)
#ifndef NDEBUG
#define VR_BIND_LAMBDA(...) base::BindRepeating(__VA_ARGS__), #__VA_ARGS__
#else
#define VR_BIND_LAMBDA(...) base::BindRepeating(__VA_ARGS__)
#endif
} // namespace vr
#endif // CHROME_BROWSER_VR_DATABINDING_BINDING_H_