From 924a6323a1845bda3491d4255f162752619b4576 Mon Sep 17 00:00:00 2001 From: bog Date: Mon, 9 Oct 2023 19:31:21 +0200 Subject: [PATCH] :sparkles: modes can now print stuff on the screen. --- Makefile | 3 ++ meson.build | 3 ++ src/Mode.cpp | 8 ++++- src/Mode.hpp | 9 ++++- src/Pywiq.cpp | 25 ++++++++++---- src/Pywiq.hpp | 6 +++- src/Term.cpp | 37 ++++++++++++++++++++ src/Term.hpp | 7 +++- src/UI_Node.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++++++ src/UI_Node.hpp | 49 +++++++++++++++++++++++++++ src/VBoxUI.cpp | 27 +++++++++++++++ src/VBoxUI.hpp | 24 +++++++++++++ src/View.cpp | 12 +++++++ src/View.hpp | 27 +++++++++++++++ src/commons.hpp | 13 +++++++ src/main.cpp | 17 +++++++--- tests/Executor.cpp | 15 +++++++-- 17 files changed, 350 insertions(+), 16 deletions(-) create mode 100644 src/UI_Node.cpp create mode 100644 src/UI_Node.hpp create mode 100644 src/VBoxUI.cpp create mode 100644 src/VBoxUI.hpp create mode 100644 src/View.cpp create mode 100644 src/View.hpp diff --git a/Makefile b/Makefile index b1de79e..55d27ac 100644 --- a/Makefile +++ b/Makefile @@ -10,5 +10,8 @@ tests: build install: tests meson install -C build +install-no-test: build + meson install -C build + doc: doxygen diff --git a/meson.build b/meson.build index 2b6e304..14b0fbf 100644 --- a/meson.build +++ b/meson.build @@ -25,6 +25,9 @@ pwq_lib = static_library('pywiq', # View # ---- + 'src/UI_Node.cpp', + 'src/VBoxUI.cpp', + 'src/View.cpp', # Command # ------- diff --git a/src/Mode.cpp b/src/Mode.cpp index 62b6dd5..e5d783e 100644 --- a/src/Mode.cpp +++ b/src/Mode.cpp @@ -7,9 +7,11 @@ namespace pwq { /*explicit*/ Mode::Mode(std::string const& name, Pywiq& pywiq, + std::shared_ptr ui, std::shared_ptr executor) : m_name { name } , m_pywiq { pywiq } + , m_ui { ui } , m_executor { executor } { } @@ -28,6 +30,11 @@ namespace pwq return m_executor; } + /*virtual*/ void Mode::update(KeyMod const& keymod) + { + m_executor->update(keymod); + } + void Mode::bind_action(std::vector keys, std::shared_ptr action) { @@ -36,5 +43,4 @@ namespace pwq Binding binding {keys, action}; m_executor->bind(binding); } - } diff --git a/src/Mode.hpp b/src/Mode.hpp index be23b61..3e44d66 100644 --- a/src/Mode.hpp +++ b/src/Mode.hpp @@ -2,7 +2,8 @@ #define pwq_MODE_HPP #include "commons.hpp" -#include "src/KeyMod.hpp" +#include "KeyMod.hpp" +#include "UI_Node.hpp" namespace pwq { @@ -19,13 +20,18 @@ namespace pwq public: explicit Mode(std::string const& name, Pywiq& pywiq, + std::shared_ptr ui, std::shared_ptr executor); + virtual ~Mode(); Pywiq& pywiq(); + std::weak_ptr ui() const { return m_ui; } std::weak_ptr executor(); + virtual void update(KeyMod const& keymod); + void bind_action(std::vector keys, std::shared_ptr action); @@ -35,6 +41,7 @@ namespace pwq private: std::string m_name; Pywiq& m_pywiq; + std::shared_ptr m_ui; std::shared_ptr m_executor; std::vector> m_actions; }; diff --git a/src/Pywiq.cpp b/src/Pywiq.cpp index cf60b92..98b38ea 100644 --- a/src/Pywiq.cpp +++ b/src/Pywiq.cpp @@ -1,14 +1,22 @@ #include "Pywiq.hpp" +#include "UI_Node.hpp" #include "Controller.hpp" +#include "View.hpp" #include "KeyMod.hpp" #include "Mode.hpp" #include "Executor.hpp" namespace pwq { - /*explicit*/ Pywiq::Pywiq(Controller& controller) + /*explicit*/ Pywiq::Pywiq(Controller& controller, View& view) : m_controller { controller } + , m_view { view } + , m_ui { std::make_shared(nullptr)} + { + auto sz = m_view.size(); + + m_ui->reshape(0, 0, sz.first, sz.second); } /*virtual*/ Pywiq::~Pywiq() @@ -17,20 +25,25 @@ namespace pwq void Pywiq::add_mode(std::shared_ptr mode) { + if (!m_modes.empty()) + { + m_ui->rem_child(m_modes.back()->ui().lock()); + } + m_modes.push_back(mode); + m_ui->add_child(m_modes.back()->ui().lock()); } bool Pywiq::exec() { + m_view.clear_screen(); + m_view.draw(*m_ui); + KeyMod keymod = m_controller.wait_keymod(); for (auto mode: m_modes) { - if (auto executor=mode->executor().lock(); - executor) - { - executor->update(keymod); - } + mode->update(keymod); } return m_running; diff --git a/src/Pywiq.hpp b/src/Pywiq.hpp index c619689..94734e4 100644 --- a/src/Pywiq.hpp +++ b/src/Pywiq.hpp @@ -7,6 +7,8 @@ namespace pwq { class Mode; class Controller; + class View; + class UI_Node; /** * Manage the editor modes. @@ -14,7 +16,7 @@ namespace pwq class Pywiq { public: - explicit Pywiq(Controller& controller); + explicit Pywiq(Controller& controller, View& view); virtual ~Pywiq(); void add_mode(std::shared_ptr mode); @@ -24,7 +26,9 @@ namespace pwq private: Controller& m_controller; + View& m_view; std::vector> m_modes; + std::shared_ptr m_ui; bool m_running = true; }; } diff --git a/src/Term.cpp b/src/Term.cpp index 982517a..d0f4fb5 100644 --- a/src/Term.cpp +++ b/src/Term.cpp @@ -81,4 +81,41 @@ namespace pwq return *km; } + + std::pair Term::size() const /*override*/ + { + int x, y; + getmaxyx(stdscr, y, x); + + return {x, y}; + } + + void Term::clear_screen() /*override*/ + { + clear(); + } + + void Term::draw(UI_Node const& ui) /*override*/ + { + if (auto txt=ui.text(); + txt) + { + int len = std::min((int) txt->size(), ui.global_size().first) + - ui.global_pos().first; + + if (len > 0) + { + std::wstring text = txt->substr(0, len); + + mvprintw(ui.global_pos().second, + ui.global_pos().first, + "%ls", text.c_str()); + } + } + + for (size_t i=0; i size() const override; + void clear_screen() override; + void draw(UI_Node const& ui) override; + private: std::unordered_map m_mapping; }; diff --git a/src/UI_Node.cpp b/src/UI_Node.cpp new file mode 100644 index 0000000..fa3438f --- /dev/null +++ b/src/UI_Node.cpp @@ -0,0 +1,84 @@ +#include "UI_Node.hpp" +#include + +namespace pwq +{ + /*explicit*/ UI_Node::UI_Node(std::shared_ptr parent) + : m_parent { parent } + { + } + + /*virtual*/ UI_Node::~UI_Node() + { + } + + std::pair UI_Node::global_pos() const + { + if (!m_parent) + { + return pos(); + } + + return {m_parent->global_pos().first + pos().first, + m_parent->global_pos().second + pos().second}; + } + + std::pair UI_Node::global_size() const + { + if (!m_parent) + { + return size(); + } + + return {m_parent->global_size().first + size().first, + m_parent->global_size().second + size().second}; + } + + void UI_Node::set_text(std::wstring const& text) + { + m_text = text; + } + + void UI_Node::reshape(int x, int y, int w, int h) + { + m_x = x; + m_y = y; + m_w = w; + m_h = h; + + for (size_t i=0; i, size_t) + { + } + + std::weak_ptr UI_Node::child(size_t index) const + { + PWQ_ASSERT(index < count(), + "cannot access ui node child at index '", + std::to_string(index), "'"); + + return m_children[index]; + } + + void UI_Node::add_child(std::shared_ptr node) + { + m_children.push_back(node); + place(node, m_children.size() - 1); + } + + void UI_Node::rem_child(std::shared_ptr node) + { + auto itr = std::remove_if(std::begin(m_children), + std::end(m_children), + [&](auto child){ + return child.get() == node.get(); + }); + + m_children.erase(itr, std::end(m_children)); + } +} diff --git a/src/UI_Node.hpp b/src/UI_Node.hpp new file mode 100644 index 0000000..e27a3b0 --- /dev/null +++ b/src/UI_Node.hpp @@ -0,0 +1,49 @@ +#ifndef pwq_UI_NODE_HPP +#define pwq_UI_NODE_HPP + +#include "commons.hpp" + +namespace pwq +{ + /** + * Represents an element of the ui tree drawn by a view. + * @see View + **/ + class UI_Node + { + public: + explicit UI_Node(std::shared_ptr parent); + virtual ~UI_Node(); + + std::pair pos() const { return {m_x, m_y}; } + std::pair size() const { return {m_w, m_h}; } + + size_t count() const { return m_children.size(); } + std::optional text() const { return m_text; } + + std::pair global_pos() const; + std::pair global_size() const; + + void set_text(std::wstring const& text); + + std::weak_ptr child(size_t index) const; + + virtual void add_child(std::shared_ptr node); + virtual void reshape(int x, int y, int w, int h); + virtual void place(std::shared_ptr node, size_t index); + + void rem_child(std::shared_ptr node); + + protected: + std::shared_ptr m_parent; + int m_x = 0; + int m_y = 0; + int m_w = 0; + int m_h = 0; + + std::optional m_text; + std::vector> m_children; + }; +} + +#endif diff --git a/src/VBoxUI.cpp b/src/VBoxUI.cpp new file mode 100644 index 0000000..93f58ab --- /dev/null +++ b/src/VBoxUI.cpp @@ -0,0 +1,27 @@ +#include "VBoxUI.hpp" + +namespace pwq +{ + /*explicit*/ VBoxUI::VBoxUI(std::shared_ptr parent) + : UI_Node(parent) + { + } + + /*virtual*/ VBoxUI::~VBoxUI() + { + } + + void VBoxUI::place(std::shared_ptr node, size_t index) + { + int const x = node->pos().first; + int y = node->pos().second;; + + if (index > 0) + { + y = m_children[index - 1]->pos().second + + m_children[index - 1]->size().second; + } + + node->reshape(x, y, m_w, node->size().second); + } +} diff --git a/src/VBoxUI.hpp b/src/VBoxUI.hpp new file mode 100644 index 0000000..a7bcd86 --- /dev/null +++ b/src/VBoxUI.hpp @@ -0,0 +1,24 @@ +#ifndef pwq_VBOXUI_HPP +#define pwq_VBOXUI_HPP + +#include "commons.hpp" +#include "UI_Node.hpp" + +namespace pwq +{ + /** + * Arrange its children verticaly. + * @see UI_Node + **/ + class VBoxUI: public UI_Node + { + public: + explicit VBoxUI(std::shared_ptr parent); + virtual ~VBoxUI(); + + virtual void place(std::shared_ptr node, size_t index); + private: + }; +} + +#endif diff --git a/src/View.cpp b/src/View.cpp new file mode 100644 index 0000000..8ce0dce --- /dev/null +++ b/src/View.cpp @@ -0,0 +1,12 @@ +#include "View.hpp" + +namespace pwq +{ + /*explicit*/ View::View() + { + } + + /*virtual*/ View::~View() + { + } +} diff --git a/src/View.hpp b/src/View.hpp new file mode 100644 index 0000000..819d9d4 --- /dev/null +++ b/src/View.hpp @@ -0,0 +1,27 @@ +#ifndef pwq_VIEW_HPP +#define pwq_VIEW_HPP + +#include "commons.hpp" +#include "UI_Node.hpp" + +namespace pwq +{ + /** + * Interface used to draw ui nodes. + * @see UI_Node + **/ + class View + { + public: + explicit View(); + virtual ~View(); + + virtual void clear_screen() = 0; + virtual void draw(UI_Node const& node) = 0; + virtual std::pair size() const = 0; + + private: + }; +} + +#endif diff --git a/src/commons.hpp b/src/commons.hpp index 056f480..8176344 100644 --- a/src/commons.hpp +++ b/src/commons.hpp @@ -22,4 +22,17 @@ NAME (std::string const& what): std::runtime_error(what) {} \ } +#define PWQ_ASSERT(COND, ...) \ + if ( !(COND) ) \ + { \ + std::vector msgs = {__VA_ARGS__}; \ + \ + for (auto const& msg: msgs) \ + { \ + std::cerr << msg; \ + } \ + \ + abort(); \ + } + #endif diff --git a/src/main.cpp b/src/main.cpp index 273bae3..3da9cc0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,16 +5,25 @@ #include "Executor.hpp" #include "Term.hpp" #include "actions/Quit.hpp" +#include "VBoxUI.hpp" int main(int, char**) { pwq::Term term; - pwq::Pywiq pywiq {term}; + pwq::Pywiq pywiq {term, term}; auto executor = std::make_shared(); - auto default_mode = std::make_shared("default", - pywiq, - executor); + + auto default_ui = std::make_shared(nullptr); + std::wstring msg = L"Hello World!"; + default_ui->reshape(0, 0, msg.size(), 1); + default_ui->set_text(msg); + + auto default_mode = std::make_shared + ("default", + pywiq, + default_ui, + executor); auto quit = default_mode->make_action(); diff --git a/tests/Executor.cpp b/tests/Executor.cpp index c8b80f8..953eef8 100644 --- a/tests/Executor.cpp +++ b/tests/Executor.cpp @@ -3,6 +3,7 @@ #include "../src/Action.hpp" #include "../src/Binding.hpp" #include "../src/Executor.hpp" +#include "../src/View.hpp" #include "../src/Controller.hpp" #include "../src/Pywiq.hpp" @@ -12,6 +13,12 @@ struct MockController : public Controller { virtual KeyMod wait_keymod() override { return KeyMod(L"a"); } }; +struct MockView : public View { + virtual void clear_screen() override {} + virtual void draw(UI_Node const&) override { } + virtual std::pair size() const override { return {0, 0}; } +}; + class ExecutorTest { public: @@ -21,8 +28,12 @@ public: protected: std::shared_ptr exec; MockController m_ctrl; - Pywiq m_pywiq = Pywiq(m_ctrl); - Mode m_mode = Mode("executor_test", m_pywiq, exec); + MockView m_view; + Pywiq m_pywiq = Pywiq(m_ctrl, m_view); + Mode m_mode = Mode("executor_test", + m_pywiq, + std::make_shared(nullptr), + exec); }; struct ActionMock: public Action