'gaze' library for "reactive" wxWidgets development with MVC

Do you like to promote your wxWidgets based application or component!? Post it here and let's see what the critics have to say. Also, if you found that ONE wx component the world needs to know about, put it here for future reference.
Post Reply
AmadeusK525
Experienced Solver
Experienced Solver
Posts: 61
Joined: Wed Aug 19, 2020 12:04 am

'gaze' library for "reactive" wxWidgets development with MVC

Post by AmadeusK525 »

Hey all, I developed a tiny and messy gaze library to help me with making my wxWidgets apps more decoupled by following an MVC architecture (https://en.wikipedia.org/wiki/Model%E2% ... controller). It's a single header files that let's you declare "watchable" data sources (simple pub/sub mechanism).

I use it to have "ViewModel" classes, which contain plain data, and I can update the data whenever I want and be sure that the UI will reflect it regardless. For example, if I have some data that I need to fetch remotely, I'll open up a thread for it and "fire and forget". When it's finished loading, it'll set the data and the UI will be updated. For this to work I use some helper classes to wrap wxWidgets components. For example:

Code: Select all

class StaticTextWatcher : public wxStaticText, public gaze::watcher {
public:
    StaticTextWatcher() {}
    StaticTextWatcher(
        wxWindow* parent,
        wxWindowID id,
        gaze::source<wxString>* label,
        const wxPoint& pos = wxDefaultPosition,
        const wxSize& size = wxDefaultSize,
        long style = 0,
        const wxString& name = wxASCII_STR(wxStaticTextNameStr)
    ) {
        this->Create(parent, id, label, pos, size, style, name);
    }

    void Create(
        wxWindow* parent,
        wxWindowID id,
        gaze::source<wxString>* label,
        const wxPoint& pos = wxDefaultPosition,
        const wxSize& size = wxDefaultSize,
        long style = 0,
        const wxString& name = wxASCII_STR(wxStaticTextNameStr)
    ) {
        wxStaticText::Create(parent, id, label->get(), pos, size, style, name);
        this->sLabel = this->watch_assign(label);
    }

protected:
    virtual void subject_updated(const gaze::subject* subj) override {
        this->CallAfter([this, subj]() { // Use CallAfter to ensure main thread
            if (subj == this->sLabel) {
                this->SetLabel(this->sLabel->get());
                this->Layout();
                this->Update();
                this->Refresh();
            }
        });
    }

private:
    const gaze::source<wxString>* sLabel;
};
I can now have reactive static texts anywhere I want in my code and just pass in a gaze::source<wxString> to it. For example:

Code: Select all

this->sLabel = gaze::source<wxString>{"initial value"};

StaticTextWatcher* statText = new StaticTextWatcher(this, -1, &this->sLabel);

std::thread([this]() {
    std::this_thread::sleep_for(std::chrono::seconds(5)); // Pretend this is fetching data from the internet
    this->sLabel.set("new value! I just changed!"); // Will trigger the UI update automatically
}).detach();
And bam. Simple concept, simple code, simple architecture. I hope this helps people choose an architecture to structure their apps in, since I've seen a lot of people asking for tips on this (and I did too, since I started messing with wxWidgets before ever landing a Software Development job so I hadn't worked on any real products up until then and was completely self-taught haha).

MVC structure I use:

Code: Select all

class ViewModel {
public:
    gaze::source<std::string> sSomeText{""};
    gaze::source<std::string> sOtherText{""};
    
    void doSomethingWithData() {
        sSomeText.set("yahoo");
    }
}

class Controller {
public:
    void OnButtonPressed() {
        viewModel.doSomethingWithData();
    }
private:
    ViewModel* viewModel;
}

class SomeView : public wxPanel {
public:
    void BindEvents() {
        button->Bind(wxEVT_BUTTON, 	[this]() { this->controller->OnButtonPressed(); });
    }

private:
    Controller* controller;
    
    wxButton* button;
}
Oh and here's the gaze source code: https://github.com/AmadeusK525/gaze

Hope this is helpful!
Post Reply