|
#include <list> |
|
#include <functional> |
|
#include <iostream> |
|
#include <string> |
|
|
|
namespace Core |
|
{ |
|
class Object {}; |
|
|
|
namespace Collections |
|
{ |
|
} |
|
|
|
namespace Functional |
|
{ |
|
template<typename ...Args> |
|
class ISlot : public Object |
|
{ |
|
public: |
|
virtual void operator()(Args&&... args) = 0; |
|
}; |
|
|
|
template<typename ...Args> |
|
class Slot : public ISlot<Args...> |
|
{ |
|
|
|
std::function<void(Args...)> Function; |
|
|
|
public: |
|
Slot(std::function<void(Args...)>&& function) |
|
{ |
|
Function = std::move(function); |
|
} |
|
|
|
void operator()(Args&&... args) |
|
{ |
|
Function(std::forward<Args>(std::move(args))...); |
|
} |
|
}; |
|
} |
|
|
|
namespace Events |
|
{ |
|
template<typename ...Args> |
|
class Signal: public Object |
|
{ |
|
|
|
public: |
|
|
|
typedef Functional::Slot<Args...> SlotType; |
|
|
|
Signal() = default; |
|
|
|
void Connect(SlotType&& slot) |
|
{ |
|
Slots.push_back(slot); |
|
} |
|
|
|
void Emit(Args&&... data) |
|
{ |
|
for (auto & slot : Slots) |
|
{ |
|
slot(std::forward<Args>(data)...); |
|
} |
|
} |
|
|
|
private: |
|
std::list<SlotType> Slots; |
|
}; |
|
} |
|
|
|
namespace MVC |
|
{ |
|
|
|
class INotifyPropertyChanged |
|
{ |
|
public: |
|
typedef Events::Signal<std::string> SignalType; |
|
SignalType PropertyChanged; |
|
}; |
|
|
|
class ModelBase : public Object |
|
{ |
|
}; |
|
|
|
class ModelViewBase : public Object, public INotifyPropertyChanged |
|
{ |
|
public: |
|
}; |
|
|
|
class Component : public Object |
|
{ |
|
|
|
}; |
|
|
|
class ViewBase : public Component |
|
{ |
|
public: |
|
ModelViewBase& DataContext; |
|
|
|
ViewBase(decltype(DataContext)& dataContext) : DataContext(dataContext) {} |
|
|
|
virtual void InitializeComponent() = 0; |
|
}; |
|
|
|
class ListViewBase : public ViewBase |
|
{ |
|
|
|
}; |
|
|
|
class ListItemViewBase : public ViewBase |
|
{ |
|
}; |
|
|
|
class ListItemView : public ListItemViewBase |
|
{ |
|
protected: |
|
ModelViewBase& DataContext; |
|
}; |
|
|
|
} |
|
|
|
namespace App |
|
{ |
|
class TargetModel : MVC::ModelBase |
|
{ |
|
public: |
|
double Latitude; |
|
double Longitude; |
|
double Elevation; |
|
}; |
|
|
|
class TargetModelView : private TargetModel, public MVC::ModelViewBase |
|
{ |
|
public: |
|
|
|
#define CREATE_PROPERTY_WRAPPER(PropertyType, PropertyName) \ |
|
void Set##PropertyName(PropertyType _##PropertyName) \ |
|
{ \ |
|
this->PropertyName = _##PropertyName; \ |
|
PropertyChanged.Emit(#PropertyName); \ |
|
} \ |
|
\ |
|
double Get##PropertyName() \ |
|
{ \ |
|
return PropertyName; \ |
|
} |
|
|
|
CREATE_PROPERTY_WRAPPER(double, Latitude) |
|
CREATE_PROPERTY_WRAPPER(double, Longitude) |
|
CREATE_PROPERTY_WRAPPER(double, Elevation) |
|
|
|
}; |
|
|
|
class TargetView : public MVC::ViewBase, public MVC::ModelViewBase |
|
{ |
|
|
|
decltype(TargetModelView::PropertyChanged)::SlotType OnDataContextPropertyChanged; |
|
|
|
public: |
|
|
|
TargetView() : MVC::ViewBase((MVC::ModelViewBase&)*this), OnDataContextPropertyChanged([&](std::string s){PrintAll();}) |
|
{ |
|
|
|
} |
|
|
|
TargetView(TargetModelView& dataContext) : MVC::ViewBase(dataContext), OnDataContextPropertyChanged([&](std::string s){PrintAll();}) |
|
{ |
|
|
|
} |
|
|
|
void InitializeComponent() |
|
{ |
|
TargetModelView& modelView = static_cast<TargetModelView&>(DataContext); |
|
OnDataContextPropertyChanged = decltype(OnDataContextPropertyChanged)([&](std::string property){ PrintAll(); }); |
|
modelView.PropertyChanged.Connect(std::move(OnDataContextPropertyChanged)); |
|
} |
|
|
|
void PrintAll() |
|
{ |
|
TargetModelView& modelView = static_cast<TargetModelView&>(DataContext); |
|
std::cout << "Latitude " << modelView.GetLatitude() << std::endl; |
|
std::cout << "Longitude " << modelView.GetLongitude() << std::endl; |
|
std::cout << "Elevation " << modelView.GetElevation() << std::endl; |
|
} |
|
}; |
|
} |
|
} |
|
|
|
int main() |
|
{ |
|
Core::App::TargetModelView targetModelView; |
|
Core::App::TargetView targetView(targetModelView); |
|
targetView.InitializeComponent(); |
|
|
|
((Core::App::TargetModelView&) targetView.DataContext).SetLatitude(1.0); |
|
((Core::App::TargetModelView&) targetView.DataContext).SetLongitude(2.0); |
|
((Core::App::TargetModelView&) targetView.DataContext).SetElevation(3.0); |
|
targetModelView.SetLatitude(2.0); |
|
} |
The result of this code is (by example)
Latitude 1
Longitude 6.95335e-310
Elevation 6.95335e-310
Latitude 1
Longitude 2
Elevation 6.95335e-310
Latitude 1
Longitude 2
Elevation 3
Latitude 2
Longitude 2
Elevation 3
For every property changed done in the ModelView,
the View Component, who is listening to all property
chages, print all information about the DataContext.