// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #pragma once #include "Alignment.hpp" // for Alignment #include "Axes.hpp" // for Axis #include #include "Layout.hpp" // for LayoutHorizontalPolicy, LayoutVertic... #include "Margins.hpp" // for Padding, Margins #include "core/BoundingBox.hpp" // for BoundingBox, BoundingBox::(anonymous) #include // for uint32_t, int32_t, uint16_t #include // for function #include // for list #include // for unique_ptr #include // for move #include #include namespace gui { class InputEvent; } namespace gui { class Navigation; } // namespace gui namespace gui { class Timer; } namespace gui { enum class ItemType { ITEM = 0, RECT = 1, LABEL, LIST, LIST_ITEM, SPAN_ITEM, TEXT, IMAGE, LAYOUT, VBOX, HBOX }; class Item { private: /// list of timers so that item could have it's timers in itself std::list> timers; public: // flag that informs whether item has a focus bool focus; /// pointer to the child item that has focus Item *focusItem = nullptr; /// item type of the widget - could be used to check what type item we are working on /// right now unused except Text, where it calles callback for gui::BoxLayout which does nothing ItemType type = ItemType::ITEM; // pointer to the parent Item Item *parent = nullptr; // list of items that have the same parent. std::list children; enum class Area { Min, Normal, Draw, Max, }; /// actual bounding box of the item. This is in coordinates of the parent widget. BoundingBox widgetArea; /// bounding box of the item minimum size, BoundingBox widgetMinimumArea; /// bounding box of the item maximal size, BoundingBox widgetMaximumArea; // bounding box used for drawing. This is in coordinates of window BoundingBox drawArea; // drawableArea would be more accurate // maximal bounding box size auto area(Area which = Area::Normal) -> BoundingBox & { switch (which) { case Area::Min: return widgetMinimumArea; case Area::Normal: return widgetArea; case Area::Draw: return drawArea; case Area::Max: return widgetMaximumArea; default: return widgetArea; } } Padding padding; Margins margins; Alignment alignment; /// radius of corner, default 0 short radius = 0; /// flag that defines if item is active /// if false -> than it shouldn't be used with onInput, navigation etc. bool activeItem = true; /// flag that defines whether widget is visible (this is - should be rendered) bool visible; // policy for changing vertical size if Item is placed inside layout LayoutVerticalPolicy verticalPolicy; // policy for changing horizontal size if Item is placed inside layout LayoutHorizontalPolicy horizontalPolicy; // Maximum height to which Layout base widget can scale current widget uint16_t maxHeight; // Maximum width to which Layout base widget can scale current widget uint16_t maxWidth; /// @defgroup callbacks Item callback functions /// callback functors are meant to emulate signal <-> slot actions where you bind element instance to in code /// defined lambda function all Items have functions corresponding to callback /// 1. if you wish to create new item which does something new in action function - override it /// 2. if you wish to call function on item - set callback for it /// @attention all callbacks return true if handled, return true means end of event processing, i.e. if you /// handle enter in inputCallback, then activatedCallback shouldn't be called. /// @{ /// called when item looses/gains focus std::function focusChangedCallback; /// called when item has dimensions changed /// @note should be part of widgetArea std::function dimensionChangedCallback; /// called when item is activated, this is enter is pressed /// @param `this` item std::function activatedCallback; /// callback when any key is pressed /// @param `this` item /// @param `InputEvent` std::function inputCallback; /// callback when timer is called on Item and onTimer is executed /// @param `this` item /// @param `timer` which triggered this callback std::function timerCallback = nullptr; /// callback on navigation, called when item passes navigation to handle by it's children /// @attention when child handles navigation it should return true, so that parent won't perform action for that std::function itemNavigation = nullptr; /// @} /// @defgroup focus functions handling focus /// @{ bool handleNavigation(const InputEvent inputEvent); /// sets/resets focus on `this` Item and runs focusChangedCallback for it bool setFocus(bool state); /// sets/resets child with focus in `this` Item /// runs focusChangedCallback on item which changes /// @attention focusItem is just a pointer, might crash if item with focus was removed void setFocusItem(Item *item); /// gettter for focus item /// @attention focusItem is just a pointer, might crash if item with focus was removed Item *getFocusItem() const; /// @} /// @defgroup callbackCallers functions which should call functors from callbacks group /// @{ /// called from setFocus, does nothing (which means it doesn't call focusChangedCallback virtual bool onFocus(bool state); /// called when `this` Item was pressed with enter (middle key on phone action keys), used for callback input /// handling calls activatedCallback /// @param[in]: data unused virtual bool onActivated(void *data); /// called when any key is pressed, before onActivated , after focus /// calls: inputCallback virtual bool onInput(const InputEvent &inputEvent); /// (should be) called each time when dimension of element was changed, added and used only with ListView /// calls: none, inconsistent api /// @note TODO should be fixed so that api would be consistent virtual bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim); /// called on Timer event in application, triggeres timerCallback /// @param timer timer element which triggered this action virtual bool onTimer(Timer &timer); /// @} /// function called to add child to item /// similar approach to QT, adding widget will always succeed. virtual void addWidget(Item *item); /// function to remove child item from element /// it's recursive for all elements underneath /// @attention It doesn't call `delete` please remove item after calling this function virtual bool removeWidget(Item *item); /// call removeWidget on item and delete on item virtual bool erase(Item *item); /// remove all children and destroy them virtual void erase(); /// sets `visible` flag virtual void setVisible(bool value); virtual void setArea(BoundingBox area); void setAreaInAxis(Axis axis, uint32_t posOnAxis, uint32_t posOnOrthogonalAxis, uint32_t sizeOnAxis, uint32_t sizeOnOrthogonalAxis); /// sets position of element - this is sets area().x and area().y of item /// calls onDimensionChanged callback & updateDrawArea for item /// @attention should be bind to area virtual void setPosition(const short &x, const short &y); virtual void setPosition(const short &val, Axis axis); [[nodiscard]] uint16_t getSize(Axis axis) const; [[nodiscard]] uint16_t getPosition(Axis axis) const; virtual void setMargins(const Margins &value); [[nodiscard]] Margins getMargins(); virtual void setPadding(const Padding &value); [[nodiscard]] Padding getPadding() const; virtual void setAlignment(const Alignment &value); [[nodiscard]] Alignment &getAlignment(); [[nodiscard]] Alignment getAlignment(Axis axis); [[nodiscard]] virtual uint16_t getAxisAlignmentValue(Axis axis, uint16_t itemSize); /// @defgroup size_range_setters Named the same way that are in QT minimum/maximum sizes setters /// /// All setters: /// 1. only sets range in which normal area can be calculated in layouts /// 2. doesn't trigger any callbacks /// @note we can consider calling callback when setMinimum/Maximum exceeds normal size /// @{ void setMaximumSize(uint32_t val, Axis axis); void setMaximumWidth(uint32_t w); void setMaximumHeight(uint32_t h); void setMaximumSize(uint32_t w, uint32_t h); void setMinimumSize(uint32_t val, Axis axis); void setMinimumSize(uint32_t w, uint32_t h); void setMinimumWidth(uint32_t w); void setMinimumHeight(uint32_t h); /// @} /// requests bigger size from parent if parent available /// if no parent available - sets size /// @return true if handled positively virtual auto requestSize(unsigned short request_w, unsigned short request_h) -> Size final; /// handle for layouts to implement to resize on demand ( i.e. when it needs to expand after addition/removal of /// chars ) /// /// by default items do not resize of it's childrem so it's safe for them to pass handleRequestSize /// straight to setSize. Layout manages size of it's item in range { Area::Min <= size <= Area::Max } so for /// that case layout should to i.e. store request size and than handle resizes appropriately /// i.e. Text => text->requestSize => layout->storeRequest => layout use it in resizes /// with this both: /// 1. user setSize /// 2. resize requests from UI element /// should be handled without infinite loop on resize ( item->setSize -> notify Layout -> layout: item->setSize /// ) /// /// @return bool requested size granted {w,h} virtual auto handleRequestResize(const Item *, unsigned short request_w, unsigned short request_h) -> Size; /// sets size of item virtual void setSize(const unsigned short w, const unsigned short h); void setSize(uint32_t val, Axis axis); /// used in ListView to position element sets area() = WidgetArea(params) /// calls onDimensionChanged & updateDrwArea /// @attention should be bind to area virtual void setBoundingBox(const BoundingBox &new_box); /// entry function to create commands to execute in renderer to draw on screen /// @note we should consider lazy evaluation prior to drawing on screen, rather than on each resize of elements /// @return list of commands for renderer to draw elements on screen virtual std::list buildDrawList() final; /// Implementation of DrawList per Item to be drawn on screen /// This is called from buildDrawList before children elements are added /// should be = 0; virtual void buildDrawListImplementation(std::list &commands) {} std::function &)> preBuildDrawListHook = nullptr; std::function &)> postBuildDrawListHook = nullptr; /// sets radius of Item box /// @note this should be moved to Rect virtual void setRadius(int value); /// get next navigation item in NavigationDirection virtual Item *getNavigationItem(NavigationDirection direction); /// set next navigation item in NavigationDirection virtual void setNavigationItem(NavigationDirection direction, Item *item); /// clear next navigation item in NavigationDirection virtual void clearNavigationItem(gui::NavigationDirection direction); Item(); Item(Item &) = delete; virtual ~Item(); /// @defgroup inconsistent inconsistent size/offset accessors and setters /// all this elements should be checked for naming/use consistency /// possibly all of that should be handled via area() (and area should have callback pinned from Item on resize /// @{ void setX(const int32_t x); void setY(const int32_t y); [[nodiscard]] int32_t getX() const { return (widgetArea.x); } [[nodiscard]] int32_t getY() const { return (widgetArea.y); } [[nodiscard]] uint32_t getWidth() const { return (widgetArea.w); } [[nodiscard]] uint32_t getHeight() const { return (widgetArea.h); } /// helper function to show where widget ends in x axis [[nodiscard]] int32_t offset_w() const { return getWidth() + widgetArea.x; } /// helper function to show where widget ends in y axis [[nodiscard]] int32_t offset_h() const { return getHeight() + widgetArea.y; } [[nodiscard]] int32_t getOffset(Axis axis) const { return this->widgetArea.size(axis) + this->widgetArea.pos(axis); }; /// @} /// adds timer to gui item /// this is needed so that timer for element would live as long as element lives void attachTimer(std::unique_ptr &&timer) { timers.emplace_back(std::move(timer)); } /// remove timer from item and as a result - destory it void detachTimer(Timer &timer); virtual void accept(GuiVisitor &visitor); protected: /// On change of position or size this method will recalculate visible part of the widget /// considering widgets hierarchy and calculate absolute position of drawing primitives. virtual void updateDrawArea(); virtual void buildChildrenDrawList(std::list &commands) final; /// Pointer to navigation object. It is added when object is set for one of the directions gui::Navigation *navigationDirections = nullptr; }; NavigationDirection inputToNavigation(const InputEvent &evt); bool isInputNavigation(const InputEvent &evt); } /* namespace gui */