guiwidget.h File Reference

Go to the documentation of this file. Source: include/ffw/gui/guiwidget.h

/* This file is part of FineFramework project */
#ifndef FFW_GUI_WIDGET
#define FFW_GUI_WIDGET

#include <cstdint>
#include <functional>
#include <vector>
#include "constants.h"
#include "guifont.h"
#include "guistyle.h"

namespace ffw {
    class GuiWindow;
    class GuiWidget;
    class GuiTheme;
    enum class GuiEventType {
        ACTION = 0x1,
        //SELECTED = 0x2,
        //CHANGED = 0x4,
        //SLIDED = 0x8,
        SIZE = 0x10,
        INNER = 0x20,
        POSITION = 0x40,
        HOVER = 0x80,
        FOCUS = 0x100,
        STATE = 0x200,
        INPUT = 0x400,
        KEY = 0x800,
        MOUSEBTN = 0x1000,
        ALL = 0x7FFFFFFF,
    };

    enum class GuiFocusType {
        DEFAULT = 0,
        STICKY,
        DROP,
        TOGGLE,
    };

    enum class GuiOrientation {
        FIXED,
        VERTICAL,
        HORIZONTAL
    };

    class GuiEvent {
    public:
        union Data {
            struct ActionData {
                int value;
            } action;

            struct SizeData {
                float width;
                float height;
            } size;

            struct InnerData {
                float width;
                float height;
            } inner;

            struct PosData {
                float x;
                float y;
            } pos;

            struct HoverData {
                bool gained;
            } hover;

            struct FocusData {
                bool gained;
            } focus;

            struct StateData {
                bool disabled;
            } state;

            struct InputData {
                unsigned int chr;
            } input;

            struct InputKey {
                ffw::Key key;
                ffw::KeyMode mode;
            } key;

            struct MousebtnData {
                ffw::MouseButton button;
                ffw::MouseMode mode;
            } mousebtn;
        };

        GuiEvent(GuiWidget* widget, GuiEventType type, Data data):widget(widget),type(type),data(data) {
        }

        GuiWidget* widget = nullptr;
        GuiEventType type = GuiEventType::ACTION;
        Data data;
    };
    struct GuiCallback {
        std::function<void(GuiEvent)> func = nullptr;
        bool now = false;
        GuiEventType type = GuiEventType::ALL;

        template<class T>
        static GuiCallback make(void (T::*memfuncptr)(GuiEvent), T* instance, GuiEventType type = GuiEventType::ALL, bool instant = false) {
            GuiCallback callback = GuiCallback();
            callback.func = std::bind(memfuncptr, instance, std::placeholders::_1);
            callback.now = instant;
            callback.type = type;
            return callback;
        }
        static GuiCallback make(const std::function<void(GuiEvent)>& function, GuiEventType type = GuiEventType::ALL, bool instant = false) {
            GuiCallback callback = GuiCallback();
            callback.func = function;
            callback.now = instant;
            callback.type = type;
            return callback;
        }
        template<typename F>
        static GuiCallback make(const F& lambda, GuiEventType type = GuiEventType::ALL, bool instant = false) {
            GuiCallback callback = GuiCallback();
            callback.func = std::function<void(GuiEvent)>(lambda);
            callback.now = instant;
            callback.type = type;
            return callback;
        }
    };
    class GuiUserInput {
    public:
        ffw::MouseButton mousebtn;
        ffw::MouseMode mousemode;
        unsigned int chr;
        ffw::Key key;
        ffw::KeyMode keymode;
        mutable ffw::Pointf scroll;
    };
    class FFW_API GuiWidget {
    public:
        struct Style {
            GuiStyle normal;
            GuiStyle hover;
            GuiStyle active;
            GuiStyle disabled;
            GuiDefaults defaults;
        };
        struct Flags {
            unsigned int hover: 1;
            unsigned int focus: 1;
            unsigned int disabled: 1;
            unsigned int hidden: 1;
            unsigned int wrap: 1;
            unsigned int render: 1;
            unsigned int invalidate: 1;
            unsigned int ignore: 1;
            unsigned int rearrange: 1;
            unsigned int first: 1;
            unsigned int eventpos: 1;
            unsigned int eventsize: 1;
            unsigned int eventinner: 1;
            unsigned int eventdisabled: 1;
            unsigned int eventfocus: 1;
            unsigned int eventhover: 1;
        };
        GuiWidget(GuiWindow* context);
        GuiWidget(const GuiWidget& other) = delete;
        GuiWidget(GuiWidget&& other);
        void swap(GuiWidget& other);
        virtual ~GuiWidget();
        inline void setSize(GuiUnits width, GuiUnits height) {
            setSize(Point<GuiUnits>(width, height));
        }
        void setSize(const Point<GuiUnits>& s);
        void setPos(const Point<GuiUnits>& pos) {
            setPos(pos.x, pos.y);
        }
        void setPos(GuiUnits posx, GuiUnits posy);
        const Point<GuiUnits>& getPos() const;
        const Point<GuiUnits>& getSize() const;
        ffw::Pointf getVisibleContentSize() const;
        ffw::Pointf getVisibleContentPos() const;
        inline ffw::Pointf getInnerContentSize() const {
            return totalSize;
        }
        inline ffw::Pointf getInnerContentPos() const {
            return getVisibleContentPos() + offset;
        }
        ffw::Pointf getAbsolutePos() const;
        void setOffset(const ffw::Pointf off);
        const ffw::Pointf& getOffset() const;
        void setPadding(GuiUnits top, GuiUnits right, GuiUnits bottom, GuiUnits left);
        inline void setPadding(GuiUnits all) {
            setPadding(all, all, all, all);
        }
        inline void setPadding(const GuiStyle::Padding& pad) {
            setPadding(pad[0], pad[1], pad[2], pad[3]);
        }
        void setMargin(GuiUnits top, GuiUnits right, GuiUnits bottom, GuiUnits left);
        inline void setMargin(GuiUnits all) {
            setMargin(all, all, all, all);
        }
        inline void setMargin(const GuiStyle::Margin& mar) {
            setMargin(mar[0], mar[1], mar[2], mar[3]);
        }
        inline const GuiStyle::Padding& getPadding() const {
            return padding;
        }
        inline const GuiStyle::Margin& getMargin() const {
            return margin;
        }
        inline GuiUnits getPadding(int side) const {
            return padding[side];
        }
        inline GuiUnits getMargin(int side) const {
            return margin[side];
        }
        inline void setMarginTop(GuiUnits val) {
            setMargin(val, margin[1], margin[2], margin[3]);
        }
        inline void setMarginRight(GuiUnits val) {
            setMargin(margin[0], val, margin[2], margin[3]);
        }
        inline void setMarginBottom(GuiUnits val) {
            setMargin(margin[0], margin[1], val, margin[3]);
        }
        inline void setMarginLeft(GuiUnits val) {
            setMargin(margin[0], margin[1], margin[2], val);
        }
        inline void setPaddingTop(GuiUnits val) {
            setPadding(val, padding[1], padding[2], padding[3]);
        }
        inline void setPaddingRight(GuiUnits val) {
            setPadding(padding[0], val, padding[2], padding[3]);
        }
        inline void setPaddingBottom(GuiUnits val) {
            setPadding(padding[0], padding[1], val, padding[3]);
        }
        inline void setPaddingLeft(GuiUnits val) {
            setPadding(padding[0], padding[1], padding[2], val);
        }
        float getPaddingInPixels(int side) const;
        float getMarginInPixels(int side) const;
        void setAlign(GuiAlign align);
        GuiAlign getAlign() const;
        void setWrap(bool wrap);
        inline bool getWrap() const {
            return flags.wrap;
        }
        void setID(unsigned long id);
        void setRef(std::string ref);
        unsigned long getID() const;
        inline const std::string& getRef() const {
            return ref;
        }
        void update(const ffw::Pointf& parentpos, const ffw::Pointf& parentsize, const GuiUserInput& input, ffw::Pointf mousepos, bool mouseout);
        void render(const ffw::Pointf& clippos, const ffw::Pointf& clipsize, const ffw::Pointf& off, bool clear);
        bool shouldRedraw() const;
        void redraw();
        void invalidate();
        void setParent(GuiWidget* parent);
        const GuiWidget* getParent() const;
        inline bool hasHover() const {
            return flags.hover;
        }
        inline bool hasFocus() const {
            return flags.focus;
        }
        void setFocus(bool f);
        void setHover(bool h);
        void setDisabled(bool d);
        void setHidden(bool h);
        void setIgnoreUserInput(bool d);
        inline void hide() {
            setHidden(true);
        }
        inline void show() {
            setHidden(false);
        }
        inline bool isHidden() const {
            return flags.hidden;
        }
        inline bool isDisabled() const {
            return flags.disabled;
        }
        void setFont(const GuiFont* font);
        const GuiFont* getFont() const;
        const GuiFont* getCurrentFont() const;
        inline const Pointi getMousePos() const {
            return mouseOld;
        }
        virtual ffw::Pointf getMinimumWrapSize() = 0;
        ffw::Pointf getOuterWrapSize() {
            auto s = getMinimumWrapSize();
            s.x += getPaddingInPixels(1) + getPaddingInPixels(3) + getMarginInPixels(1) + getMarginInPixels(3);
            s.y += getPaddingInPixels(0) + getPaddingInPixels(2) + getMarginInPixels(0) + getMarginInPixels(2);
            return s;
        }
        template<class T>
        T* findByID(unsigned long id_){
            if(typeid(*this).hash_code() == typeid(T).hash_code() && id == id_)return dynamic_cast<T*>(this);
            else if(widgets.size() > 0){
                for(auto& w : widgets){
                    T* test = w->findByID<T>(id_);
                    if(test != NULL)return test;
                }
            }
            return NULL;
        }
        template<class T>
        void addEventCallback(void (T::*memfuncptr)(GuiEvent), T* instance, GuiEventType type = GuiEventType::ALL, bool now = false){
            eventCallbacks.push_back(GuiCallback::make(memfuncptr, instance, type, now));
        }
        void addEventCallback(const std::function<void(GuiEvent)>& function, GuiEventType type = GuiEventType::ALL, bool now = false) {
            eventCallbacks.push_back(GuiCallback::make(function, type, now));
        }
        template<typename F>
        void addEventCallback(const F& lambda, GuiEventType type = GuiEventType::ALL, bool now = false) {
            eventCallbacks.push_back(GuiCallback::make(lambda, type, now));
        }
        void setTheme(const GuiTheme* theme, bool defaults = false);
        void setStyleGroup(const GuiWidget::Style* style);
        inline const GuiWidget::Style* getStyleGroup() {
            return widgetStyle;
        }
        inline const ffw::GuiStyle* getCurrentStyle() const {
            return getCurrentStyle(widgetStyle);
        }
        const ffw::GuiStyle* getCurrentStyle(const GuiWidget::Style* group) const;
        inline const ffw::Pointf& getRealSize() const {
            return sizeReal;
        }
        inline ffw::Pointf getRealSizeWithMargin() const {
            return ffw::Pointf(
                sizeReal.x + getMarginInPixels(1) + getMarginInPixels(3),
                sizeReal.y + getMarginInPixels(0) + getMarginInPixels(2)
            );
        }
        inline const ffw::Pointf& getRealPos() const {
            return posReal;
        }
        inline void setLineHeight(float height) {
            lineHeight = height;
            redraw();
        }
        inline float getLineHeight() const {
            return lineHeight;
        }
        inline void setDefaults(const ffw::GuiDefaults* def) {
            if (def != NULL) {
                size.set(def->size.x, def->size.y);
                padding = def->padding;
                margin = def->margin;
                align = def->align;
            }
        }
        inline GuiWindow* getWindow() {
            return context;
        }
        inline const GuiWindow* getWindow() const  {
            return context;
        }
        inline void setToggleFocus() {
            focusType = GuiFocusType::TOGGLE;
        }
        inline void setStickyFocus() {
            focusType = GuiFocusType::STICKY;
        }
        inline void setDropFocus() {
            focusType = GuiFocusType::DROP;
        }
        inline void setDefaultFocus() {
            focusType = GuiFocusType::DEFAULT;
        }
        inline GuiFocusType getFocusType() const {
            return focusType;
        }
        inline const std::vector<GuiWidget*>& getAllWidgets() const {
            return widgets;
        }
        void pushEvent(GuiEventType type, GuiEvent::Data data);
        GuiWidget& operator = (const GuiWidget& other) = delete;
        GuiWidget& operator = (GuiWidget&& other);
    protected:
        void traverseBackground(const ffw::Pointf& pos, const ffw::Pointf& size);
        GuiWidget* addWidgetInternal(GuiWidget* widget);
        GuiWidget* addWidgetAfterInternal(const GuiWidget* previous, GuiWidget* widget);
        GuiWidget* addWidgetBeforeInternal(const GuiWidget* next, GuiWidget* widget);
        void deleteWidgetsInternal();
        bool deleteSingleWidgetInternal(const GuiWidget* widget);
        void setOrientationInternal(GuiOrientation orientation);
        void recalculatePos();
        void recalculateSize(GuiWidget* ptr = NULL);
        void renderComponent(const ffw::Pointf& pos, const ffw::Pointf& size, const ffw::GuiStyle* group);
        virtual void eventRender(const ffw::Pointf& contentoffset, const ffw::Pointf& contentsize) = 0;
        virtual void eventPos(const ffw::Pointf& pos);
        virtual void eventSize(const ffw::Pointf& size);
        virtual void eventHover(bool gained);
        virtual void eventFocus(bool gained);
        virtual void eventMouse(const ffw::Pointf& mousePos);
        virtual bool eventScroll(const ffw::Pointf& scroll);
        virtual void eventMouseButton(ffw::MouseButton button, ffw::MouseMode mode);
        virtual void eventText(uint32_t chr);
        virtual void eventKey(ffw::Key key, ffw::KeyMode mode);
        virtual void eventDisabled(bool disabled);
        virtual void eventThemeChanged(const GuiTheme* theme, bool defaults = false) = 0;
        GuiWindow* context;
        const GuiWidget::Style* widgetStyle;
        inline const Flags& getFlags() const {
            return flags;
        }
    private:
        void checkEvents();
        void arrangeWidgetsVertical(GuiWidget* ptr = nullptr);
        void arrangeWidgetsHorizontal(GuiWidget* ptr = nullptr);
        void arrangeWidgetsFixed(GuiWidget* ptr = nullptr);
        void realignWidgetsHorizontally(size_t start, size_t end, const ffw::Pointf& contentSize);
        void realignWidgetsHorizontallyEnd(size_t start, size_t end, const ffw::Pointf& contentSize, const ffw::Pointf& totalSize);
        void realignWidgetsVertically(size_t start, size_t end, const ffw::Pointf& contentSize);
        void realignWidgetsVerticallyEnd(size_t start, size_t end, const ffw::Pointf& contentSize, const ffw::Pointf& totalSize);
        ffw::Pointf sizeReal;
        ffw::Pointf posReal;
        ffw::GuiUnits2D size;
        ffw::GuiUnits2D pos;
        ffw::Pointf mouseOld;
        ffw::Pointf offset;
        ffw::Pointf totalSize;
        GuiOrientation orientation;
        GuiAlign align;
        std::vector<GuiWidget*> widgets;
        GuiStyle::Margin margin;
        GuiStyle::Padding padding;
        GuiFocusType focusType;
        float lineHeight;
        const GuiFont* widgetfont;
        unsigned long id;
        std::string ref;
        GuiWidget* parent;
        std::vector<GuiCallback> eventCallbacks;
        Flags flags;
    };
}

#endif