From 98fa85cf0bb86a28ddb4f14d3e9c80abc55018a5 Mon Sep 17 00:00:00 2001 From: Nikola Ducak Date: Thu, 28 Nov 2024 11:28:50 +0100 Subject: [PATCH] fix: empty screen on startup --- source/view/annual_view.cpp | 9 +++++++-- source/view/calendar_component.cpp | 20 ++++++++++++-------- source/view/calendar_component.hpp | 28 ++++++++++++++++++++++------ test/calendar_component_test.cpp | 28 +++++++++++++++++++++++----- 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/source/view/annual_view.cpp b/source/view/annual_view.cpp index 1ac412b..b36c7ad 100644 --- a/source/view/annual_view.cpp +++ b/source/view/annual_view.cpp @@ -29,11 +29,16 @@ using namespace ftxui; AnnualView::AnnualView(const std::chrono::year_month_day &today, bool sundayStart, unsigned recentEventsWindow) : m_screen{ScreenInteractive::Fullscreen()}, - m_calendarButtons{Calendar::make(m_screen, today, makeCalendarOptions(today, sundayStart))}, + m_calendarButtons{Calendar::make(ScreenSizeProvider::makeDefault(), today, + makeCalendarOptions(today, sundayStart))}, m_tagsMenu{makeTagsMenu()}, m_sectionsMenu{makeSectionsMenu()}, m_rootComponent{makeFullUIComponent()}, m_recentEventsWindow{recentEventsWindow} {} -void AnnualView::run() { m_screen.Loop(m_rootComponent); } +void AnnualView::run() { + // Caps-log expects the terminal to be at least 80x24 + Terminal::SetFallbackSize(Dimensions{/*dimx=*/80, /*dimy=*/24}); + m_screen.Loop(m_rootComponent); +} void AnnualView::stop() { m_screen.ExitLoopClosure()(); } diff --git a/source/view/calendar_component.cpp b/source/view/calendar_component.cpp index a321359..d0715e3 100644 --- a/source/view/calendar_component.cpp +++ b/source/view/calendar_component.cpp @@ -5,6 +5,8 @@ #include "view/ftxui_ext/extended_containers.hpp" #include +#include +#include namespace caps_log::view { using namespace ftxui; @@ -20,9 +22,9 @@ namespace { */ class MonthComponentArranger { public: - static Element arrange(const Screen &screen, const Components &monthComponents, + static Element arrange(const Dimensions screenDimensions, const Components &monthComponents, std::chrono::year displayedYear) { - const auto availableMonthColumns = computeNumberOfMonthsPerRow(screen); + const auto availableMonthColumns = computeNumberOfMonthsPerRow(screenDimensions); const auto renderData = arrangeMonthsInCalendar(monthComponents, availableMonthColumns); return window(text(std::to_string(static_cast(displayedYear))), vbox(renderData) | frame) | @@ -40,8 +42,8 @@ class MonthComponentArranger { /** * @brief Computes the number of months that can be displayed in a row. */ - static int computeNumberOfMonthsPerRow(const Screen &screen) { - int availableMonthColumns = screen.dimx() / kCharsPerMonthComponet; + static int computeNumberOfMonthsPerRow(const Dimensions &screenDimensions) { + int availableMonthColumns = screenDimensions.dimx / kCharsPerMonthComponet; // prevent spliting 12 months into 2 rows of unequal elements if (availableMonthColumns == 5) { // NOLINT availableMonthColumns = 4; @@ -70,9 +72,10 @@ class MonthComponentArranger { } // namespace -Calendar::Calendar(const Screen &screen, const std::chrono::year_month_day &today, - CalendarOption option) - : m_screen{&screen}, m_option(std::move(option)), m_today(today), +Calendar::Calendar(std::unique_ptr screenSizeProvider, + const std::chrono::year_month_day &today, CalendarOption option) + : m_screenSizeProvider{std::move(screenSizeProvider)}, m_option(std::move(option)), + m_today(today), // TODO: SetActiveChild does nothing, this is the only // way to focus a specific date on startup. Investigate. m_selectedMonthComponentIdx{(static_cast(static_cast(today.month()))) - 1}, @@ -130,7 +133,8 @@ Component Calendar::createYear(std::chrono::year year) { const auto container = ftxui_ext::AnyDir(monthComponents, &m_selectedMonthComponentIdx); return Renderer(container, [this, monthComponents]() { - return MonthComponentArranger::arrange(*m_screen, monthComponents, m_displayedYear); + return MonthComponentArranger::arrange(m_screenSizeProvider->getScreenSize(), + monthComponents, m_displayedYear); }); } diff --git a/source/view/calendar_component.hpp b/source/view/calendar_component.hpp index e6d1789..27cef0a 100644 --- a/source/view/calendar_component.hpp +++ b/source/view/calendar_component.hpp @@ -15,8 +15,24 @@ struct CalendarOption { bool sundayStart = false; }; +class ScreenSizeProvider { + public: + virtual ~ScreenSizeProvider() = default; + virtual ftxui::Dimensions getScreenSize() const = 0; + + static std::unique_ptr makeDefault() { + // Caps-log runs only in full screen mode, so we can use the terminal size as the screen + // size + class DefaultScreenSizeProvider : public ScreenSizeProvider { + public: + ftxui::Dimensions getScreenSize() const override { return ftxui::Terminal::Size(); } + }; + return std::make_unique(); + } +}; + class Calendar : public ftxui::ComponentBase { - const ftxui::Screen *m_screen; + std::unique_ptr m_screenSizeProvider; CalendarOption m_option; std::chrono::year_month_day m_today; ftxui::Component m_root; @@ -27,8 +43,8 @@ class Calendar : public ftxui::ComponentBase { std::chrono::year m_displayedYear; public: - Calendar(const ftxui::Screen &screen, const std::chrono::year_month_day &today, - CalendarOption option = {}); + Calendar(std::unique_ptr ScreenSizeProvider, + const std::chrono::year_month_day &today, CalendarOption option = {}); bool OnEvent(ftxui::Event event) override; ftxui::Element Render() override; @@ -40,9 +56,9 @@ class Calendar : public ftxui::ComponentBase { * @brief A utility factory method to create a shared pointer to a Calendar instance. This is * useful as FTXUI works with shared pointers to ComponentBase instances. */ - static inline auto make(const ftxui::Screen &screen, const std::chrono::year_month_day &today, - CalendarOption option = {}) { - return std::make_shared(screen, today, option); + static inline auto make(std::unique_ptr screenSizeProvider, + const std::chrono::year_month_day &today, CalendarOption option = {}) { + return std::make_shared(std::move(screenSizeProvider), today, option); } private: diff --git a/test/calendar_component_test.cpp b/test/calendar_component_test.cpp index e76b52c..ffdc731 100644 --- a/test/calendar_component_test.cpp +++ b/test/calendar_component_test.cpp @@ -18,6 +18,16 @@ std::string readFile(const std::string &path) { buffer << ifs.rdbuf(); return buffer.str(); } + +class MockScreenSizeProvider : public caps_log::view::ScreenSizeProvider { + public: + MockScreenSizeProvider(ftxui::Dimensions size) : m_size{size} {} + ftxui::Dimensions getScreenSize() const override { return m_size; } + + private: + ftxui::Dimensions m_size; +}; + } // namespace TEST(CalnedarComponentTest, Render) { @@ -37,13 +47,16 @@ TEST(CalnedarComponentTest, Render) { }; // NOLINTEND for (const auto &data : testData) { - ftxui::Screen screen{data.screen_width, 41}; // NOLINT - caps_log::view::Calendar calendar{screen, {2024y, std::chrono::January, 1d}}; // NOLINT - + ftxui::Dimensions dimensions{data.screen_width, 41}; // NOLINT + ftxui::Screen screen{data.screen_width, 41}; // NOLINT + auto sizeProvider = std::make_unique(dimensions); + caps_log::view::Calendar calendar{std::move(sizeProvider), + {2024y, std::chrono::January, 1d}}; // NOLINT ftxui::Render(screen, calendar.Render()); const auto screenRender = screen.ToString(); std::string expectedOutput = readFile(filePath(data)); - EXPECT_EQ(screenRender, expectedOutput) << "Got: " << screenRender; + EXPECT_EQ(screenRender, expectedOutput) << "Expected" << expectedOutput << "\n\n" + << "Got: " << screenRender; } } @@ -52,15 +65,20 @@ TEST(CalnedarComponentTest, EventHandling) { std::chrono::year_month_day next{2024y, std::chrono::January, 2d}; // NOLINT std::chrono::year_month_day next_month{2024y, std::chrono::February, 1d}; // NOLINT ftxui::Screen screen{184, 41}; // NOLINT - caps_log::view::Calendar calendar{screen, start}; // NOLINT + auto sizeProvider = std::make_unique(ftxui::Dimensions{184, 41}); + + caps_log::view::Calendar calendar{std::move(sizeProvider), start}; + calendar.OnEvent(ftxui::Event::ArrowRight); EXPECT_EQ(calendar.getFocusedDate(), next) << "Got date: " << caps_log::utils::date::formatToString(calendar.getFocusedDate()); + calendar.OnEvent(ftxui::Event::ArrowDown); calendar.OnEvent(ftxui::Event::ArrowDown); calendar.OnEvent(ftxui::Event::ArrowDown); calendar.OnEvent(ftxui::Event::ArrowDown); calendar.OnEvent(ftxui::Event::ArrowDown); + EXPECT_EQ(calendar.getFocusedDate(), next_month) << "Got date: " << caps_log::utils::date::formatToString(calendar.getFocusedDate()); }