~aleteoryx/muditaos

b77af1696e6f4cbd6bc15b67d88f2d4314b8e356 — Onufry Pajaczek 3 years ago 017ebc2
[BH-1542] Fix TimeFixedWidget encapsulation

*DigitsContainer extracted as testable module
*Getters removed from TimeFixedWidget
A module-apps/apps-common/widgets/DigitsContainer.hpp => module-apps/apps-common/widgets/DigitsContainer.hpp +62 -0
@@ 0,0 1,62 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Label.hpp>

namespace gui
{
    struct DimensionsParams
    {
        std::uint32_t mainBoxHeight;
        std::uint32_t mainBoxWidth;
        std::uint32_t digitMaxWidth;
        std::uint32_t colonWidth;
        std::uint32_t minusWidth;
        std::uint32_t leftBoxWidth;
        std::uint32_t rightBoxWidth;
    };

    template <size_t N> struct DigitsContainer
    {
        std::array<Label *, N> digits;
        void setMinutesBox(std::uint32_t minutes, DimensionsParams params);
        void setSecondsBox(std::uint32_t seconds, DimensionsParams params);
    };
    template <size_t N> void setDigits(std::string text, const DigitsContainer<N> &container, DimensionsParams params)
    {
        std::for_each(std::crbegin(container.digits),
                      std::crend(container.digits),
                      [text = std::move(text), &params](Label *label) mutable {
                          if (text.empty()) {
                              label->setText("");
                              label->setSize(0, 0);
                          }
                          else {
                              label->setText(std::string{text.back()});
                              label->setSize(params.digitMaxWidth, params.mainBoxHeight);
                              text.pop_back();
                          }
                      });
    }

    template <size_t N> void DigitsContainer<N>::setMinutesBox(std::uint32_t minutes, DimensionsParams params)
    {
        auto rangeGuard = 1000;
        minutes %= rangeGuard;

        setDigits(std::to_string(minutes), *this, params);
    }

    template <size_t N> void DigitsContainer<N>::setSecondsBox(std::uint32_t seconds, DimensionsParams params)
    {
        auto rangeGuard = 100;
        seconds %= rangeGuard;

        bool shouldBeDisplayedAsOneDigit = seconds < 10;
        std::string text = shouldBeDisplayedAsOneDigit ? "0" + std::to_string(seconds) : std::to_string(seconds);

        setDigits(std::move(text), *this, params);
    }
} // namespace gui

M module-apps/apps-common/widgets/TimeFixedWidget.cpp => module-apps/apps-common/widgets/TimeFixedWidget.cpp +9 -47
@@ 60,11 60,11 @@ namespace gui
        attachLabelToBox(leftBox.minus, leftBox.box);
        setMinus();

        for (auto &digit : leftBox.digits) {
        for (auto &digit : leftBox.container.digits) {
            attachLabelToBox(digit, leftBox.box);
        }

        for (auto &digit : rightBox.digits) {
        for (auto &digit : rightBox.container.digits) {
            attachLabelToBox(digit, rightBox.box);
        }



@@ 91,63 91,25 @@ namespace gui

    void TimeFixedWidget::setMinutesBox(std::uint32_t minutes)
    {
        auto rangeGuard = 1000;
        minutes %= rangeGuard;

        setDigits(std::to_string(minutes), leftBox.digits);

        leftBox.container.setMinutesBox(minutes, getDimensions());
        leftBox.box->resizeItems();
    }

    void TimeFixedWidget::setSecondsBox(std::uint32_t seconds)
    {
        auto rangeGuard = 100;
        seconds %= rangeGuard;

        bool shouldBeDisplayedAsOneDigit = seconds < 10;
        std::string text = shouldBeDisplayedAsOneDigit ? "0" + std::to_string(seconds) : std::to_string(seconds);

        setDigits(std::move(text), rightBox.digits);
    }

    const TimeFixedWidget::LeftBox &TimeFixedWidget::getLeftBox()
    {
        return leftBox;
    }

    const TimeFixedWidget::RightBox &TimeFixedWidget::getRightBox()
    {
        return rightBox;
    }

    template <size_t N> void TimeFixedWidget::setDigits(std::string &&text, const DigitsContainer<N> &digits) const
    {
        const auto dimensions = getDimensions();

        std::for_each(
            std::crbegin(digits), std::crend(digits), [text = std::move(text), &dimensions](Label *label) mutable {
                if (text.empty()) {
                    label->setText("");
                    label->setSize(0, 0);
                }
                else {
                    label->setText(std::string{text.back()});
                    label->setSize(dimensions.digitMaxWidth, dimensions.mainBoxHeight);
                    text.pop_back();
                }
            });
        rightBox.container.setSecondsBox(seconds, getDimensions());
    }

    void TimeFixedWidget::setFontAndDimensions(const UTF8 &fontName) const
    {
        leftBox.minus->setFont(fontName);
        for (auto &digit : leftBox.digits) {
        for (auto &digit : leftBox.container.digits) {
            digit->setFont(fontName);
        }

        colon->setFont(fontName);

        for (auto &digit : rightBox.digits) {
        for (auto &digit : rightBox.container.digits) {
            digit->setFont(fontName);
        }



@@ 162,13 124,13 @@ namespace gui
        rightBox.box->setSize(params.rightBoxWidth, params.mainBoxHeight);

        leftBox.minus->setSize(params.minusWidth, params.mainBoxHeight);
        for (auto &digit : leftBox.digits) {
        for (auto &digit : leftBox.container.digits) {
            digit->setSize(params.digitMaxWidth, params.mainBoxHeight);
        }

        colon->setSize(params.colonWidth, params.mainBoxHeight);

        for (auto &digit : rightBox.digits) {
        for (auto &digit : rightBox.container.digits) {
            digit->setSize(params.digitMaxWidth, params.mainBoxHeight);
        }
    }


@@ 184,7 146,7 @@ namespace gui
        info.colonWidth    = leftBox.minus->getTextFormat().getFont()->getPixelWidth(colonSign);
        info.minusWidth    = leftBox.minus->getTextFormat().getFont()->getPixelWidth(minusSign);

        info.leftBoxWidth  = (info.digitMaxWidth * leftBox.digits.size()) + info.minusWidth;
        info.leftBoxWidth  = (info.digitMaxWidth * leftBox.container.digits.size()) + info.minusWidth;
        info.rightBoxWidth = info.mainBoxWidth - info.minusWidth - info.leftBoxWidth;

        return info;

M module-apps/apps-common/widgets/TimeFixedWidget.hpp => module-apps/apps-common/widgets/TimeFixedWidget.hpp +5 -18
@@ 3,6 3,7 @@

#pragma once

#include "DigitsContainer.hpp"
#include <Text.hpp>
#include <BoxLayout.hpp>
#include "widgets/DateWidget.hpp"


@@ 10,33 11,21 @@

namespace gui
{
    struct DimensionsParams
    {
        std::uint32_t mainBoxHeight;
        std::uint32_t mainBoxWidth;
        std::uint32_t digitMaxWidth;
        std::uint32_t colonWidth;
        std::uint32_t minusWidth;
        std::uint32_t leftBoxWidth;
        std::uint32_t rightBoxWidth;
    };

    class TimeFixedWidget : public Rect
    {
      public:
        template <size_t N> using DigitsContainer = std::array<Label *, N>;

        struct LeftBox
        {
            HBox *box    = nullptr;
            Label *minus = nullptr;
            DigitsContainer<3> digits{nullptr};
            DigitsContainer<3> container{};
        };

        struct RightBox
        {
            HBox *box = nullptr;
            DigitsContainer<2> digits{nullptr};
            DigitsContainer<2> container{};
        };

        TimeFixedWidget(Item *parent,


@@ 50,16 39,13 @@ namespace gui
        void setSecondsBox(std::uint32_t second);
        void setFontAndDimensions(const UTF8 &fontName) const;

        const LeftBox &getLeftBox();
        const RightBox &getRightBox();
        DimensionsParams getDimensions() const;

      private:
        void attachLabelToBox(Label *&label, HBox *&box) const;
        void setMinus() const;
        void setColon() const;
        void setDimensions(DimensionsParams &&params) const;
        DimensionsParams getDimensions() const;
        template <size_t N> void setDigits(std::string &&text, const DigitsContainer<N> &digits) const;

        bool minusVisible = false;
        HBox *mainBox     = nullptr;


@@ 68,4 54,5 @@ namespace gui
        LeftBox leftBox;
        RightBox rightBox;
    };

} /* namespace gui */

M module-apps/tests/CMakeLists.txt => module-apps/tests/CMakeLists.txt +1 -1
@@ 25,7 25,7 @@ add_gtest_executable(
    NAME
        apps-common-widgets
    SRCS
        widgets/test-TimeFixedWidget.cpp
        widgets/test-DigitsContainer.cpp
    LIBS
       apps-common
       gui-mock

R module-apps/tests/widgets/test-TimeFixedWidget.cpp => module-apps/tests/widgets/test-DigitsContainer.cpp +25 -50
@@ 10,27 10,10 @@

namespace
{
    constexpr auto width = style::bell_base_layout::outer_layouts_w;
    constexpr auto height =
        style::bell_base_layout::outer_layouts_h - gui::bgSoundsStyle::progress::bottomDescTopMargin;

    class TimeFixedWidgetTest : public ::testing::Test
    template <size_t N> void checkDisplayedText(std::string text, const gui::DigitsContainer<N> &container)
    {
      public:
        TimeFixedWidgetTest()
        {
            mockup::fontManager();
            widget = new gui::TimeFixedWidget(&item, 0, 0, width, height);
        }

        gui::Item item{};
        gui::TimeFixedWidget *widget;
    };

    template <size_t N>
    void checkDisplayedText(std::string text, const gui::TimeFixedWidget::DigitsContainer<N> &digits)
    {
        std::for_each(std::crbegin(digits), std::crend(digits), [&text](gui::Label *label) {
        std::for_each(std::crbegin(container.digits), std::crend(container.digits), [&text](gui::Label *label) {
            if (text.empty()) {
                EXPECT_STREQ((label->getText()).c_str(), "");
            }


@@ 43,35 26,23 @@ namespace
        EXPECT_TRUE(text.empty());
    }

} // namespace

TEST_F(TimeFixedWidgetTest, initilization)
{
    EXPECT_NE(widget, nullptr);
}

TEST_F(TimeFixedWidgetTest, minusNotVisable)
{
    widget->setFontAndDimensions(style::window::font::verybig);
    EXPECT_EQ(widget->getLeftBox().minus->getText(), "");
}

TEST(TimeFixedWidgetNonFixtureTest, minusVisable)
{
    mockup::fontManager();
    template <size_t N> auto initContainer(gui::HBox &box) -> gui::DigitsContainer<N>
    {
        auto container = gui::DigitsContainer<N>{};
        for (auto &digit : container.digits) {

    auto item   = gui::Item{};
    auto widget = new gui::TimeFixedWidget(&item, 0, 0, width, height, true);
            digit = new gui::Label(&box, 0, 0, 0, 0);
        }
        return container;
    }

    widget->setFontAndDimensions(style::window::font::verybig);
    EXPECT_EQ(widget->getLeftBox().minus->getText(), "-");
}
} // namespace

TEST_F(TimeFixedWidgetTest, setMinutesBox)
TEST(DigitsContainerTest, setMinutesBox)
{
    using namespace std::string_literals;
    mockup::fontManager();

    widget->setFontAndDimensions(style::window::font::verybig);
    std::unordered_map<int, std::string> inputAndExpectedOutput{{123, "123"s},
                                                                {999, "999"s},
                                                                {9999, "999"s},


@@ 80,23 51,27 @@ TEST_F(TimeFixedWidgetTest, setMinutesBox)
                                                                {10, "10"s},
                                                                {1, "1"s},
                                                                {0, "0"s}};
    gui::HBox box{};
    auto container = initContainer<3>(box);

    for (const auto &param : inputAndExpectedOutput) {
        widget->setMinutesBox(param.first);
        checkDisplayedText(param.second, widget->getLeftBox().digits);
    for (auto &param : inputAndExpectedOutput) {
        container.setMinutesBox(param.first, gui::DimensionsParams{});
        checkDisplayedText(param.second, container);
    }
}

TEST_F(TimeFixedWidgetTest, setSecondsBox)
TEST(DigitsContainerTest, setSecondsBox)
{
    using namespace std::string_literals;
    mockup::fontManager();

    widget->setFontAndDimensions(style::window::font::verybig);
    std::unordered_map<int, std::string> inputAndExpectedOutput{
        {123, "23"s}, {999, "99"s}, {9999, "99"s}, {1233433, "33"s}, {99, "99"s}, {10, "10"s}, {1, "01"s}, {0, "00"s}};
    gui::HBox box{};
    auto container = initContainer<2>(box);

    for (const auto &param : inputAndExpectedOutput) {
        widget->setSecondsBox(param.first);
        checkDisplayedText(param.second, widget->getRightBox().digits);
    for (auto &param : inputAndExpectedOutput) {
        container.setSecondsBox(param.first, gui::DimensionsParams{});
        checkDisplayedText(param.second, container);
    }
}