From 3973eededce02205cd8bfe767dbd5ac718a85c25 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Mon, 27 Apr 2026 00:41:41 +0700 Subject: [PATCH 1/2] Add an MVVM example --- src/ap/CMakeLists.txt | 2 +- src/ap/README.md | 18 +++---- src/ap/mvc/IObserver.h | 1 + src/ap/mvc/model/SharedData.cpp | 2 +- src/ap/mvvm/CMakeLists.txt | 12 +++++ src/ap/mvvm/IObserver.h | 8 +++ src/ap/mvvm/model/SharedData.cpp | 22 ++++++++ src/ap/mvvm/model/SharedData.h | 20 +++++++ src/ap/mvvm/mvvm_ap.cpp | 73 ++++++++++++++++++++++++++ src/ap/mvvm/view/DisplayWidget.cpp | 33 ++++++++++++ src/ap/mvvm/view/DisplayWidget.h | 22 ++++++++ src/ap/mvvm/view/EditorWidget.cpp | 37 +++++++++++++ src/ap/mvvm/view/EditorWidget.h | 21 ++++++++ src/ap/mvvm/viewmodel/SharedDataVM.cpp | 32 +++++++++++ src/ap/mvvm/viewmodel/SharedDataVM.h | 31 +++++++++++ 15 files changed, 323 insertions(+), 11 deletions(-) create mode 100644 src/ap/mvvm/CMakeLists.txt create mode 100644 src/ap/mvvm/IObserver.h create mode 100644 src/ap/mvvm/model/SharedData.cpp create mode 100644 src/ap/mvvm/model/SharedData.h create mode 100644 src/ap/mvvm/mvvm_ap.cpp create mode 100644 src/ap/mvvm/view/DisplayWidget.cpp create mode 100644 src/ap/mvvm/view/DisplayWidget.h create mode 100644 src/ap/mvvm/view/EditorWidget.cpp create mode 100644 src/ap/mvvm/view/EditorWidget.h create mode 100644 src/ap/mvvm/viewmodel/SharedDataVM.cpp create mode 100644 src/ap/mvvm/viewmodel/SharedDataVM.h diff --git a/src/ap/CMakeLists.txt b/src/ap/CMakeLists.txt index 7e9ae84..c6e1842 100644 --- a/src/ap/CMakeLists.txt +++ b/src/ap/CMakeLists.txt @@ -18,4 +18,4 @@ target_sources(ap target_link_libraries(ap PRIVATE gtk4-settings) add_subdirectory(mvc) -# add_subdirectory(mvvm) \ No newline at end of file +add_subdirectory(mvvm) \ No newline at end of file diff --git a/src/ap/README.md b/src/ap/README.md index 22aaebd..f757537 100644 --- a/src/ap/README.md +++ b/src/ap/README.md @@ -45,13 +45,13 @@ View → Controller → Model ### 3. GTK4 - [Refer](https://docs.gtk.org/gtk4/getting_started.html) -### 4. Examples -### 4.1. simple_ap -- Cos: - - Quick, Simple -- Pos: - - Dependency: e.g. what happen when we delete Gtk::Label m_labelMonitorA; - - Scalability: - - Reusability: +### 4. Trade-offs: MVC vs MVVM -### 4.2. mvc_ap \ No newline at end of file +| Aspect | MVC | MVVM | +|---------------------|---------------------------------------------------------------------|----------------------------------------------------------------------| +| **Complexity** | Lower — **Controller** is a thin pass-through | Slightly higher — **ViewModel** adds an extra layer | +| **Coupling** | Views know both **Controller** and **Model** (e.g., for initial data) | **Views** know only the **ViewModel** | +| **Testability** | Controller is testable, but **Views** are still tied to **Model** for reads | **ViewModel** is fully testable without GTK; Views are pure UI | +| **Scalability** | Adding fields requires updating **Model**, **Controller**, and all **Views** | Adding fields requires updating **Model** and **ViewModel**; Views update bindings only | +| **Observer wiring** | Manual — Container wires each **View** to the **Model** | Self-contained — **Views** register via **ViewModel**; container stays clean| +| **UI logic leakage**| Risk - Views may call `model_->getData()` directly | Eliminated - Views use `viewModel_->getCurrentText()` only | \ No newline at end of file diff --git a/src/ap/mvc/IObserver.h b/src/ap/mvc/IObserver.h index 60c9baf..15ced7e 100644 --- a/src/ap/mvc/IObserver.h +++ b/src/ap/mvc/IObserver.h @@ -1,5 +1,6 @@ #pragma once +#include class IObserver { public: virtual ~IObserver() = default; diff --git a/src/ap/mvc/model/SharedData.cpp b/src/ap/mvc/model/SharedData.cpp index 0f44a39..7fcb1dd 100644 --- a/src/ap/mvc/model/SharedData.cpp +++ b/src/ap/mvc/model/SharedData.cpp @@ -9,7 +9,7 @@ void SharedData::setData(const std::string& data) { } void SharedData::notifyObservers() { - for (auto o : observers_) { + for (auto *o : observers_) { o->onDataChanged(this->data_); } } diff --git a/src/ap/mvvm/CMakeLists.txt b/src/ap/mvvm/CMakeLists.txt new file mode 100644 index 0000000..2e68a5e --- /dev/null +++ b/src/ap/mvvm/CMakeLists.txt @@ -0,0 +1,12 @@ +add_executable(mvvm_ap) + +target_sources(mvvm_ap + PRIVATE + mvvm_ap.cpp + model/SharedData.cpp + viewmodel/SharedDataVM.cpp + view/EditorWidget.cpp + view/DisplayWidget.cpp +) + +target_link_libraries(mvvm_ap PRIVATE gtk4-settings) \ No newline at end of file diff --git a/src/ap/mvvm/IObserver.h b/src/ap/mvvm/IObserver.h new file mode 100644 index 0000000..15ced7e --- /dev/null +++ b/src/ap/mvvm/IObserver.h @@ -0,0 +1,8 @@ +#pragma once + +#include +class IObserver { + public: + virtual ~IObserver() = default; + virtual void onDataChanged(const std::string& newData) = 0; +}; \ No newline at end of file diff --git a/src/ap/mvvm/model/SharedData.cpp b/src/ap/mvvm/model/SharedData.cpp new file mode 100644 index 0000000..c45efd7 --- /dev/null +++ b/src/ap/mvvm/model/SharedData.cpp @@ -0,0 +1,22 @@ +#include "SharedData.h" + +SharedData::SharedData() : data_{"Initial Data"} {} + +void SharedData::setData(const std::string& data) { + this->data_ = data; + notifyObservers(); +} + +void SharedData::notifyObservers() { + for (auto* o : observers_) { + o->onDataChanged(this->data_); + } +} +void SharedData::addObserver(IObserver* obs) { + if (obs != nullptr) + observers_.push_back(obs); +} + +std::string SharedData::getData() const { + return data_; +} \ No newline at end of file diff --git a/src/ap/mvvm/model/SharedData.h b/src/ap/mvvm/model/SharedData.h new file mode 100644 index 0000000..26a3cbe --- /dev/null +++ b/src/ap/mvvm/model/SharedData.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include "../IObserver.h" + +class SharedData { + public: + SharedData(); + + void setData(const std::string& data); + std::string getData() const; + + void addObserver(IObserver* obs); + + private: + void notifyObservers(); + + std::string data_; + std::vector observers_; +}; \ No newline at end of file diff --git a/src/ap/mvvm/mvvm_ap.cpp b/src/ap/mvvm/mvvm_ap.cpp new file mode 100644 index 0000000..ddf2599 --- /dev/null +++ b/src/ap/mvvm/mvvm_ap.cpp @@ -0,0 +1,73 @@ +#include +#include +#include "model/SharedData.h" +#include "view/DisplayWidget.h" +#include "view/EditorWidget.h" +#include "viewmodel/SharedDataVM.h" + +/** + * @brief ContainerWindow wires up the MVVM triad: + * Key differences from MVC's ContainerWindow: + * 1. No Controller is created. + * 2. No manual addObserver() calls, each View self-registers with the ViewModel during construction. + * 3. Views receive a shared_ptr, never a Model pointer. + */ +class ContainerWindow : public Gtk::Window { + public: + ContainerWindow(); + + private: + // Main Layout + Gtk::Box mainLayout_; + Gtk::Box topRowLayout_; // Horizontal arrangement (2 Displays side-by-side) + + // Model & ViewModel are shared + // View hold a shared_ptr to the ViewModel + std::shared_ptr dataModel_; + std::shared_ptr viewModel_; + + // Views + std::unique_ptr editorView_; + std::unique_ptr displayViewLeft_; + std::unique_ptr displayViewRight_; +}; + +ContainerWindow::ContainerWindow() + : mainLayout_(Gtk::Orientation::VERTICAL), + topRowLayout_(Gtk::Orientation::HORIZONTAL) { + set_title("MVC Integrated Demo"); + set_default_size(600, 400); + + // Step 1 – construct the Model. + dataModel_ = std::make_shared(); + + // Step 2 – construct the ViewModel; it subscribes to the Model internally. + viewModel_ = std::make_shared(dataModel_); + + // Step 3 – construct Views, passing only the ViewModel. + editorView_ = std::make_unique(viewModel_); + displayViewLeft_ = std::make_unique("ZONE 2: MONITOR A (Blue)", + "blue", viewModel_); + displayViewRight_ = std::make_unique("ZONE 3: MONITOR B (Red)", + "red", viewModel_); + + dataModel_->addObserver(editorView_.get()); + dataModel_->addObserver(displayViewLeft_.get()); + dataModel_->addObserver(displayViewRight_.get()); + + // Layout, unchanged from MVC + displayViewLeft_->set_hexpand(true); + displayViewRight_->set_hexpand(true); + topRowLayout_.append(*displayViewLeft_); + topRowLayout_.append(*displayViewRight_); + + editorView_->set_vexpand(false); + mainLayout_.append(topRowLayout_); + mainLayout_.append(*editorView_); + set_child(mainLayout_); +} + +int main(int argc, char* argv[]) { + auto app = Gtk::Application::create("org.gtkmm.example.singlemvvm"); + return app->make_window_and_run(argc, argv); +} \ No newline at end of file diff --git a/src/ap/mvvm/view/DisplayWidget.cpp b/src/ap/mvvm/view/DisplayWidget.cpp new file mode 100644 index 0000000..3fc696f --- /dev/null +++ b/src/ap/mvvm/view/DisplayWidget.cpp @@ -0,0 +1,33 @@ +#include "DisplayWidget.h" + +DisplayWidget::DisplayWidget(const std::string& title, const std::string& color, + std::shared_ptr vm) + : Gtk::Box(Gtk::Orientation::VERTICAL), + color_(color), + innerBox_(Gtk::Orientation::VERTICAL), + view_model_(std::move(vm)) { + frame_.set_label(title); + frame_.set_margin(10); + + // get from the ViewModel's current state + updateLabel(view_model_->getCurrentText()); + + innerBox_.append(labelData_); + innerBox_.set_margin(20); + + frame_.set_child(innerBox_); + this->append(frame_); + + // self-register: now the VM will push future update to this View + view_model_->addObserver(this); +} + +void DisplayWidget::updateLabel(const std::string& text) { + std::string markup = "" + text + ""; + labelData_.set_markup(markup); +} + +void DisplayWidget::onDataChanged(const std::string& newData) { + updateLabel(newData); +} \ No newline at end of file diff --git a/src/ap/mvvm/view/DisplayWidget.h b/src/ap/mvvm/view/DisplayWidget.h new file mode 100644 index 0000000..1b99be0 --- /dev/null +++ b/src/ap/mvvm/view/DisplayWidget.h @@ -0,0 +1,22 @@ +#include +#include +#include "../IObserver.h" +#include "../viewmodel/SharedDataVM.h" + +class DisplayWidget : public Gtk::Box, public IObserver { + public: + DisplayWidget(const std::string& title, const std::string& color, + std::shared_ptr vm); + + void onDataChanged(const std::string& newData) override; + + private: + void updateLabel(const std::string& text); + + std::string color_; + Gtk::Frame frame_; + Gtk::Box innerBox_; + Gtk::Label labelData_; + + std::shared_ptr view_model_; +}; \ No newline at end of file diff --git a/src/ap/mvvm/view/EditorWidget.cpp b/src/ap/mvvm/view/EditorWidget.cpp new file mode 100644 index 0000000..c33671f --- /dev/null +++ b/src/ap/mvvm/view/EditorWidget.cpp @@ -0,0 +1,37 @@ +#include "EditorWidget.h" + +#include + +EditorWidget::EditorWidget(std::shared_ptr vm) + : Gtk::Box(Gtk::Orientation::VERTICAL), + innerBox_(Gtk::Orientation::VERTICAL), + view_model_(std::move(vm)) { + frame_.set_label("ZONE 1: EDITOR (Input View)"); + frame_.set_margin(10); + + labelTitle_.set_text("Enter new data:"); + entry_.set_text(view_model_->getCurrentText()); + button_.set_label("Broadcast Update"); + + innerBox_.append(labelTitle_); + innerBox_.append(entry_); + innerBox_.append(button_); + innerBox_.set_margin(15); + innerBox_.set_spacing(10); + + frame_.set_child(innerBox_); + this->append(frame_); + + // MVVM binding: user action -> ViewModel command + button_.signal_clicked().connect( + [this]() { view_model_->submitText(entry_.get_text()); }); + + // Self-register + view_model_->addObserver(this); +} + +void EditorWidget::onDataChanged(const std::string& newData) { + if (entry_.get_text() != Glib::ustring(newData)) { + entry_.set_text(newData); + } +} \ No newline at end of file diff --git a/src/ap/mvvm/view/EditorWidget.h b/src/ap/mvvm/view/EditorWidget.h new file mode 100644 index 0000000..dc93814 --- /dev/null +++ b/src/ap/mvvm/view/EditorWidget.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include +#include "../IObserver.h" +#include "../viewmodel/SharedDataVM.h" + +class EditorWidget : public Gtk::Box, public IObserver { + public: + explicit EditorWidget(std::shared_ptr vm); + + void onDataChanged(const std::string& newData) override; + + private: + Gtk::Frame frame_; + Gtk::Box innerBox_; + Gtk::Label labelTitle_; + Gtk::Entry entry_; + Gtk::Button button_; + + std::shared_ptr view_model_; +}; \ No newline at end of file diff --git a/src/ap/mvvm/viewmodel/SharedDataVM.cpp b/src/ap/mvvm/viewmodel/SharedDataVM.cpp new file mode 100644 index 0000000..8679a7a --- /dev/null +++ b/src/ap/mvvm/viewmodel/SharedDataVM.cpp @@ -0,0 +1,32 @@ +#include "SharedDataVM.h" + +SharedDataVM::SharedDataVM(std::shared_ptr model) + : model_(std::move(model)) { + model_->addObserver(this); +} + +void SharedDataVM::submitText(const std::string& text) { + if (text.empty()) + return; + + model_->setData(text); +} + +std::string SharedDataVM::getCurrentText() const { + return model_->getData(); +} + +void SharedDataVM::addObserver(IObserver* obs) { + if (obs != nullptr) + view_observers_.push_back(obs); +} + +void SharedDataVM::onDataChanged(const std::string& newData) { + notifyObservers(newData); +} + +void SharedDataVM::notifyObservers(const std::string& data) { + for (auto* obs : view_observers_) { + obs->onDataChanged(data); + } +} \ No newline at end of file diff --git a/src/ap/mvvm/viewmodel/SharedDataVM.h b/src/ap/mvvm/viewmodel/SharedDataVM.h new file mode 100644 index 0000000..c7089de --- /dev/null +++ b/src/ap/mvvm/viewmodel/SharedDataVM.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include "../IObserver.h" +#include "../model/SharedData.h" + +/** + * @brief The single mediator between the Model and all Views + * + * Responsibilities: + * - Subscribes to the Model + * - Exposes a command that Views call on user actions + * - Maintains its own observer list so View subcribe to the ViewModel instead of the Model. View never touch Model + */ +class SharedDataVM : public IObserver { + public: + explicit SharedDataVM(std::shared_ptr model); + + void submitText(const std::string& text); + + std::string getCurrentText() const; + + void addObserver(IObserver* obs); + + private: + void onDataChanged(const std::string& newData) override; + void notifyObservers(const std::string& data); + + std::shared_ptr model_; // SharedData model + std::vector view_observers_; // Views +}; \ No newline at end of file From c41c80fed8ad193c386ddadd8ba642c60178a8da Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Mon, 27 Apr 2026 00:54:46 +0700 Subject: [PATCH 2/2] Fix mvvm --- src/ap/mvc/model/SharedData.cpp | 2 +- src/ap/mvc/mvc_ap.cpp | 4 +++- src/ap/mvvm/model/SharedData.cpp | 10 +++++----- src/ap/mvvm/model/SharedData.h | 4 +++- src/ap/mvvm/mvvm_ap.cpp | 12 +++++------- src/ap/mvvm/view/DisplayWidget.cpp | 10 +++++----- src/ap/mvvm/view/DisplayWidget.h | 6 ++++-- src/ap/mvvm/view/EditorWidget.cpp | 4 ++-- src/ap/mvvm/view/EditorWidget.h | 4 +++- src/ap/mvvm/viewmodel/SharedDataVM.cpp | 12 ++++++------ src/ap/mvvm/viewmodel/SharedDataVM.h | 6 ++++-- 11 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/ap/mvc/model/SharedData.cpp b/src/ap/mvc/model/SharedData.cpp index 7fcb1dd..853a0c4 100644 --- a/src/ap/mvc/model/SharedData.cpp +++ b/src/ap/mvc/model/SharedData.cpp @@ -9,7 +9,7 @@ void SharedData::setData(const std::string& data) { } void SharedData::notifyObservers() { - for (auto *o : observers_) { + for (auto* o : observers_) { o->onDataChanged(this->data_); } } diff --git a/src/ap/mvc/mvc_ap.cpp b/src/ap/mvc/mvc_ap.cpp index e6cb20c..4da5d0c 100644 --- a/src/ap/mvc/mvc_ap.cpp +++ b/src/ap/mvc/mvc_ap.cpp @@ -5,6 +5,7 @@ #include "view/DisplayWidget.h" #include "view/EditorWidget.h" +namespace mvc { class ContainerWindow : public Gtk::Window { public: ContainerWindow(); @@ -67,8 +68,9 @@ ContainerWindow::ContainerWindow() mainLayout_.append(*editorView_); // Add bottom row set_child(mainLayout_); } +} // namespace mvc int main(int argc, char* argv[]) { auto app = Gtk::Application::create("org.gtkmm.example.singlemvc"); - return app->make_window_and_run(argc, argv); + return app->make_window_and_run(argc, argv); } \ No newline at end of file diff --git a/src/ap/mvvm/model/SharedData.cpp b/src/ap/mvvm/model/SharedData.cpp index c45efd7..36a9554 100644 --- a/src/ap/mvvm/model/SharedData.cpp +++ b/src/ap/mvvm/model/SharedData.cpp @@ -1,22 +1,22 @@ #include "SharedData.h" -SharedData::SharedData() : data_{"Initial Data"} {} +mvvm::SharedData::SharedData() : data_{"Initial Data"} {} -void SharedData::setData(const std::string& data) { +void mvvm::SharedData::setData(const std::string& data) { this->data_ = data; notifyObservers(); } -void SharedData::notifyObservers() { +void mvvm::SharedData::notifyObservers() { for (auto* o : observers_) { o->onDataChanged(this->data_); } } -void SharedData::addObserver(IObserver* obs) { +void mvvm::SharedData::addObserver(IObserver* obs) { if (obs != nullptr) observers_.push_back(obs); } -std::string SharedData::getData() const { +std::string mvvm::SharedData::getData() const { return data_; } \ No newline at end of file diff --git a/src/ap/mvvm/model/SharedData.h b/src/ap/mvvm/model/SharedData.h index 26a3cbe..603cdd5 100644 --- a/src/ap/mvvm/model/SharedData.h +++ b/src/ap/mvvm/model/SharedData.h @@ -3,6 +3,7 @@ #include #include "../IObserver.h" +namespace mvvm { class SharedData { public: SharedData(); @@ -17,4 +18,5 @@ class SharedData { std::string data_; std::vector observers_; -}; \ No newline at end of file +}; +} // namespace mvvm \ No newline at end of file diff --git a/src/ap/mvvm/mvvm_ap.cpp b/src/ap/mvvm/mvvm_ap.cpp index ddf2599..ba03f62 100644 --- a/src/ap/mvvm/mvvm_ap.cpp +++ b/src/ap/mvvm/mvvm_ap.cpp @@ -5,12 +5,13 @@ #include "view/EditorWidget.h" #include "viewmodel/SharedDataVM.h" +namespace mvvm { /** * @brief ContainerWindow wires up the MVVM triad: * Key differences from MVC's ContainerWindow: * 1. No Controller is created. * 2. No manual addObserver() calls, each View self-registers with the ViewModel during construction. - * 3. Views receive a shared_ptr, never a Model pointer. + * 3. Views receive a shared_ptr, never a Model pointer. */ class ContainerWindow : public Gtk::Window { public: @@ -35,7 +36,7 @@ class ContainerWindow : public Gtk::Window { ContainerWindow::ContainerWindow() : mainLayout_(Gtk::Orientation::VERTICAL), topRowLayout_(Gtk::Orientation::HORIZONTAL) { - set_title("MVC Integrated Demo"); + set_title("MVVM Integrated Demo"); set_default_size(600, 400); // Step 1 – construct the Model. @@ -51,10 +52,6 @@ ContainerWindow::ContainerWindow() displayViewRight_ = std::make_unique("ZONE 3: MONITOR B (Red)", "red", viewModel_); - dataModel_->addObserver(editorView_.get()); - dataModel_->addObserver(displayViewLeft_.get()); - dataModel_->addObserver(displayViewRight_.get()); - // Layout, unchanged from MVC displayViewLeft_->set_hexpand(true); displayViewRight_->set_hexpand(true); @@ -66,8 +63,9 @@ ContainerWindow::ContainerWindow() mainLayout_.append(*editorView_); set_child(mainLayout_); } +} // namespace mvvm int main(int argc, char* argv[]) { auto app = Gtk::Application::create("org.gtkmm.example.singlemvvm"); - return app->make_window_and_run(argc, argv); + return app->make_window_and_run(argc, argv); } \ No newline at end of file diff --git a/src/ap/mvvm/view/DisplayWidget.cpp b/src/ap/mvvm/view/DisplayWidget.cpp index 3fc696f..d18ad9b 100644 --- a/src/ap/mvvm/view/DisplayWidget.cpp +++ b/src/ap/mvvm/view/DisplayWidget.cpp @@ -1,9 +1,9 @@ #include "DisplayWidget.h" -DisplayWidget::DisplayWidget(const std::string& title, const std::string& color, - std::shared_ptr vm) +mvvm::DisplayWidget::DisplayWidget(const std::string& title, std::string color, + std::shared_ptr vm) : Gtk::Box(Gtk::Orientation::VERTICAL), - color_(color), + color_(std::move(color)), innerBox_(Gtk::Orientation::VERTICAL), view_model_(std::move(vm)) { frame_.set_label(title); @@ -22,12 +22,12 @@ DisplayWidget::DisplayWidget(const std::string& title, const std::string& color, view_model_->addObserver(this); } -void DisplayWidget::updateLabel(const std::string& text) { +void mvvm::DisplayWidget::updateLabel(const std::string& text) { std::string markup = "" + text + ""; labelData_.set_markup(markup); } -void DisplayWidget::onDataChanged(const std::string& newData) { +void mvvm::DisplayWidget::onDataChanged(const std::string& newData) { updateLabel(newData); } \ No newline at end of file diff --git a/src/ap/mvvm/view/DisplayWidget.h b/src/ap/mvvm/view/DisplayWidget.h index 1b99be0..33864fb 100644 --- a/src/ap/mvvm/view/DisplayWidget.h +++ b/src/ap/mvvm/view/DisplayWidget.h @@ -3,9 +3,10 @@ #include "../IObserver.h" #include "../viewmodel/SharedDataVM.h" +namespace mvvm { class DisplayWidget : public Gtk::Box, public IObserver { public: - DisplayWidget(const std::string& title, const std::string& color, + DisplayWidget(const std::string& title, std::string color, std::shared_ptr vm); void onDataChanged(const std::string& newData) override; @@ -19,4 +20,5 @@ class DisplayWidget : public Gtk::Box, public IObserver { Gtk::Label labelData_; std::shared_ptr view_model_; -}; \ No newline at end of file +}; +} // namespace mvvm \ No newline at end of file diff --git a/src/ap/mvvm/view/EditorWidget.cpp b/src/ap/mvvm/view/EditorWidget.cpp index c33671f..7607262 100644 --- a/src/ap/mvvm/view/EditorWidget.cpp +++ b/src/ap/mvvm/view/EditorWidget.cpp @@ -2,7 +2,7 @@ #include -EditorWidget::EditorWidget(std::shared_ptr vm) +mvvm::EditorWidget::EditorWidget(std::shared_ptr vm) : Gtk::Box(Gtk::Orientation::VERTICAL), innerBox_(Gtk::Orientation::VERTICAL), view_model_(std::move(vm)) { @@ -30,7 +30,7 @@ EditorWidget::EditorWidget(std::shared_ptr vm) view_model_->addObserver(this); } -void EditorWidget::onDataChanged(const std::string& newData) { +void mvvm::EditorWidget::onDataChanged(const std::string& newData) { if (entry_.get_text() != Glib::ustring(newData)) { entry_.set_text(newData); } diff --git a/src/ap/mvvm/view/EditorWidget.h b/src/ap/mvvm/view/EditorWidget.h index dc93814..e9d9c5f 100644 --- a/src/ap/mvvm/view/EditorWidget.h +++ b/src/ap/mvvm/view/EditorWidget.h @@ -4,6 +4,7 @@ #include "../IObserver.h" #include "../viewmodel/SharedDataVM.h" +namespace mvvm { class EditorWidget : public Gtk::Box, public IObserver { public: explicit EditorWidget(std::shared_ptr vm); @@ -18,4 +19,5 @@ class EditorWidget : public Gtk::Box, public IObserver { Gtk::Button button_; std::shared_ptr view_model_; -}; \ No newline at end of file +}; +} // namespace mvvm \ No newline at end of file diff --git a/src/ap/mvvm/viewmodel/SharedDataVM.cpp b/src/ap/mvvm/viewmodel/SharedDataVM.cpp index 8679a7a..2e4e8db 100644 --- a/src/ap/mvvm/viewmodel/SharedDataVM.cpp +++ b/src/ap/mvvm/viewmodel/SharedDataVM.cpp @@ -1,31 +1,31 @@ #include "SharedDataVM.h" -SharedDataVM::SharedDataVM(std::shared_ptr model) +mvvm::SharedDataVM::SharedDataVM(std::shared_ptr model) : model_(std::move(model)) { model_->addObserver(this); } -void SharedDataVM::submitText(const std::string& text) { +void mvvm::SharedDataVM::submitText(const std::string& text) { if (text.empty()) return; model_->setData(text); } -std::string SharedDataVM::getCurrentText() const { +std::string mvvm::SharedDataVM::getCurrentText() const { return model_->getData(); } -void SharedDataVM::addObserver(IObserver* obs) { +void mvvm::SharedDataVM::addObserver(IObserver* obs) { if (obs != nullptr) view_observers_.push_back(obs); } -void SharedDataVM::onDataChanged(const std::string& newData) { +void mvvm::SharedDataVM::onDataChanged(const std::string& newData) { notifyObservers(newData); } -void SharedDataVM::notifyObservers(const std::string& data) { +void mvvm::SharedDataVM::notifyObservers(const std::string& data) { for (auto* obs : view_observers_) { obs->onDataChanged(data); } diff --git a/src/ap/mvvm/viewmodel/SharedDataVM.h b/src/ap/mvvm/viewmodel/SharedDataVM.h index c7089de..44ac352 100644 --- a/src/ap/mvvm/viewmodel/SharedDataVM.h +++ b/src/ap/mvvm/viewmodel/SharedDataVM.h @@ -4,13 +4,14 @@ #include "../IObserver.h" #include "../model/SharedData.h" +namespace mvvm { /** * @brief The single mediator between the Model and all Views * * Responsibilities: * - Subscribes to the Model * - Exposes a command that Views call on user actions - * - Maintains its own observer list so View subcribe to the ViewModel instead of the Model. View never touch Model + * - Maintains its own observer list so View subscribe to the ViewModel instead of the Model. View never touch Model */ class SharedDataVM : public IObserver { public: @@ -28,4 +29,5 @@ class SharedDataVM : public IObserver { std::shared_ptr model_; // SharedData model std::vector view_observers_; // Views -}; \ No newline at end of file +}; +} // namespace mvvm \ No newline at end of file