diff --git a/Include/ssGUI/DataClasses/Hierarchy.hpp b/Include/ssGUI/DataClasses/Hierarchy.hpp index 74759510..fce4b237 100644 --- a/Include/ssGUI/DataClasses/Hierarchy.hpp +++ b/Include/ssGUI/DataClasses/Hierarchy.hpp @@ -125,18 +125,18 @@ namespace ssGUI virtual void NotifyAndRemoveOnObjectDestroyEventCallbackIfExist(); - void AddChild(ssGUI::GUIObject* guiObject, bool compositeChild); - void AddChild( ssGUI::GUIObject* guiObject, - ssGUI::Enums::AlignmentHorizontal horizontalAlignment, - ssGUI::Enums::AlignmentVertical verticalAlignment, - bool compositeChild); + void SetChildParent(ssGUI::GUIObject* guiObject, bool compositeChild); + void SetChildParent(ssGUI::GUIObject* guiObject, + ssGUI::Enums::AlignmentHorizontal horizontalAlignment, + ssGUI::Enums::AlignmentVertical verticalAlignment, + bool compositeChild); - void AddChildWithWrapper(ssGUI::GUIObject* guiObject, bool compositeChild); + void SetChildParentWithWrapper(ssGUI::GUIObject* guiObject, bool compositeChild); - void AddChildWithWrapper( ssGUI::GUIObject* guiObject, - ssGUI::Enums::AlignmentHorizontal horizontalAlignment, - ssGUI::Enums::AlignmentVertical verticalAlignment, - bool compositeChild); + void SetChildParentWithWrapper( ssGUI::GUIObject* guiObject, + ssGUI::Enums::AlignmentHorizontal horizontalAlignment, + ssGUI::Enums::AlignmentVertical verticalAlignment, + bool compositeChild); public: Hierarchy(); @@ -175,7 +175,7 @@ namespace ssGUI ssGUI_BASE_CHECK_RETURN_NULL(); auto* guiObject = ssGUI::Factory::Create(); - AddChild(guiObject, compositeChild); + SetChildParent(guiObject, compositeChild); return guiObject; } @@ -189,7 +189,7 @@ namespace ssGUI ssGUI_BASE_CHECK_RETURN_NULL(); auto* guiObject = ssGUI::Factory::Create(); - AddChild(guiObject, horizontalAlignment, verticalAlignment, compositeChild); + SetChildParent(guiObject, horizontalAlignment, verticalAlignment, compositeChild); return guiObject; } @@ -201,7 +201,7 @@ namespace ssGUI ssGUI_BASE_CHECK_RETURN_NULL(); auto* guiObject = ssGUI::Factory::Create(); - AddChildWithWrapper(guiObject, compositeChild); + SetChildParentWithWrapper(guiObject, compositeChild); return guiObject; } @@ -215,7 +215,7 @@ namespace ssGUI ssGUI_BASE_CHECK_RETURN_NULL(); auto* guiObject = ssGUI::Factory::Create(); - AddChildWithWrapper(guiObject, horizontalAlignment, verticalAlignment, compositeChild); + SetChildParentWithWrapper(guiObject, horizontalAlignment, verticalAlignment, compositeChild); return guiObject; } @@ -303,7 +303,7 @@ namespace ssGUI //function: AddChildAfter //Same as above but with extension added template - T* AddChildAfter( ssGUI::GUIObject* target, + T* AddChildAfter( ssGUI::GUIObject* target, ssGUI::Enums::AlignmentHorizontal horizontalAlignment, ssGUI::Enums::AlignmentVertical verticalAlignment, bool compositeChild = false) @@ -336,7 +336,7 @@ namespace ssGUI //function: AddChildAfterWithWrapper //Same as above but with extension added template - T* AddChildAfterWithWrapper( ssGUI::GUIObject* target, + T* AddChildAfterWithWrapper( ssGUI::GUIObject* target, ssGUI::Enums::AlignmentHorizontal horizontalAlignment, ssGUI::Enums::AlignmentVertical verticalAlignment, bool compositeChild = false) @@ -526,11 +526,11 @@ namespace ssGUI virtual std::vector GetListOfChildren() const; //function: HasChildRecursively - //Returns true if the searchChild is an recursive child of this GUI Object + //Returns true if the searchChild is an recursive child (including this GUI Object) of this GUI Object virtual bool HasChildRecursively(ssGUI::GUIObject* searchChild) const; //function: HasParentRecursively - //Returns true if the searchChild is an recursive parent of this GUI Object + //Returns true if the searchChild is an recursive parent (including this GUI Object) of this GUI Object. virtual bool HasParentRecursively(ssGUI::GUIObject* searchParent) const; //function: Internal_AddChild diff --git a/Include/ssGUI/DataClasses/TabEventInfo.hpp b/Include/ssGUI/DataClasses/TabEventInfo.hpp new file mode 100644 index 00000000..b0d15ab6 --- /dev/null +++ b/Include/ssGUI/DataClasses/TabEventInfo.hpp @@ -0,0 +1,29 @@ +#ifndef SSGUI_TAB_EVENT_INFO_HPP +#define SSGUI_TAB_EVENT_INFO_HPP + +//namespace: ssGUI +namespace ssGUI +{ + class GUIObject; + + class Tab; + + //struct: ssGUI::TabEventInfo + //Custom event info for NEW_TAB_CONTENT_ADDED, TAB_CONTENT_SWITCHED and TAB_CONTENT_UNTABBED + //events + struct TabEventInfo + { + //var: TabAreaContainer + //Pointer to the GUI Object that has the tab area extension attached + ssGUI::GUIObject* TabAreaContainer = nullptr; + + //var: Tab + //Pointer to the tab that is related to the event. + //For TAB_CONTENT_UNTABBED event, this will be nullptr. + //For TAB_CONTENT_SWITCHED event, this can be nullptr if no tab is selected. + ssGUI::Tab* Tab = nullptr; + }; + +} + +#endif \ No newline at end of file diff --git a/Include/ssGUI/Enums/Direction.hpp b/Include/ssGUI/Enums/Direction.hpp new file mode 100644 index 00000000..51a3fa49 --- /dev/null +++ b/Include/ssGUI/Enums/Direction.hpp @@ -0,0 +1,98 @@ +#ifndef SSGUI_DIRECTION_HPP +#define SSGUI_DIRECTION_HPP + +#include "ssGUI/HelperClasses/EnumToStringMacro.hpp" +#include +#include + +namespace ssGUI +{ + + //namespace: ssGUI::Enums + namespace Enums + { + /*enum: Direction + Bitfiled type that represents a combination of directions. + + TOP - Top + RIGHT - Right + BOTTOM - Bottom + LEFT - Left + COUNT - Count + */ + enum class Direction : uint8_t + { + TOP = 1 << 0, + RIGHT = 1 << 1, + BOTTOM = 1 << 2, + LEFT = 1 << 3, + COUNT = 1 << 4 + }; + + namespace + { + inline std::string Internal_DirectionToString(Direction anchor) + { + static_assert((uint8_t)Direction::COUNT == 1 << 4, "ToString"); + switch(anchor) + { + RETURN_ENUM_STRING(Direction::TOP); + RETURN_ENUM_STRING(Direction::RIGHT); + RETURN_ENUM_STRING(Direction::BOTTOM); + RETURN_ENUM_STRING(Direction::LEFT); + RETURN_ENUM_STRING(Direction::COUNT); + } + + return ""; + } + } + + inline ssGUI::Enums::Direction operator|(ssGUI::Enums::Direction a, + ssGUI::Enums::Direction b) + { + return static_cast(static_cast(a) | + static_cast(b)); + }; + + inline ssGUI::Enums::Direction operator&(ssGUI::Enums::Direction a, + ssGUI::Enums::Direction b) + { + return static_cast(static_cast(a) & + static_cast(b)); + }; + + inline bool operator==(ssGUI::Enums::Direction a, ssGUI::Enums::Direction b) + { + return (static_cast(a & b) == static_cast(b)); + }; + + inline bool operator!=(ssGUI::Enums::Direction a, ssGUI::Enums::Direction b) + { + return !(a == b); + }; + + //function: DirectionToString + inline std::string DirectionToString(Direction direction) + { + static_assert((uint8_t)Direction::COUNT == 1 << 4, "ToString"); + + std::string curString; + for(int i = 0; i < 4; ++i) + { + std::string returnString = + Internal_DirectionToString(direction & (Direction)(1 << i)); + if(!returnString.empty()) + curString += returnString + ", "; + } + + //Remove last comma + if(!curString.empty()) + curString.erase(curString.begin() + curString.size() - 2); + + return curString; + } + } + +} + +#endif \ No newline at end of file diff --git a/Include/ssGUI/Enums/EventType.hpp b/Include/ssGUI/Enums/EventType.hpp index b1798613..5d0ec012 100644 --- a/Include/ssGUI/Enums/EventType.hpp +++ b/Include/ssGUI/Enums/EventType.hpp @@ -80,6 +80,15 @@ namespace ssGUI will be pointer to struct. has no effect. + NEW_TAB_CONTENT_ADDED - Triggered *after* there's a new tab content added to TabArea extension. The content being added will be the source for triggering this event callback. + will be pointer to struct. + + TAB_CONTENT_SWITCHED - Triggered *after* a new tab content is switched. The new tab content will be the source for triggering this event callback. + will be pointer to struct. + + TAB_CONTENT_UNTABBED - Triggered *after* the tabbed content is untabbed. The untabbed content will be the source for triggering this event callback. + will be pointer to struct. + COUNT - Count SCROLLBAR_VALUE_CHANGED - Same as @@ -126,6 +135,10 @@ namespace ssGUI OBJECT_DOCKED, EXTERNAL_OBJECT_DOCKED, + NEW_TAB_CONTENT_ADDED, + TAB_CONTENT_SWITCHED, + TAB_CONTENT_UNTABBED, + COUNT, SCROLLBAR_VALUE_CHANGED = SLIDER_VALUE_CHANGED, @@ -136,7 +149,7 @@ namespace ssGUI //function: EventTypeToString inline std::string EventTypeToString(EventType event) { - static_assert((int)EventType::COUNT == 33, "ToString"); + static_assert((int)EventType::COUNT == 36, "ToString"); switch(event) { RETURN_ENUM_STRING(EventType::NONE); @@ -177,6 +190,10 @@ namespace ssGUI RETURN_ENUM_STRING(EventType::OBJECT_DOCKED); RETURN_ENUM_STRING(EventType::EXTERNAL_OBJECT_DOCKED); + RETURN_ENUM_STRING(EventType::NEW_TAB_CONTENT_ADDED); + RETURN_ENUM_STRING(EventType::TAB_CONTENT_SWITCHED); + RETURN_ENUM_STRING(EventType::TAB_CONTENT_UNTABBED); + RETURN_ENUM_STRING(EventType::COUNT); } diff --git a/Include/ssGUI/Enums/GUIObjectType.hpp b/Include/ssGUI/Enums/GUIObjectType.hpp index 2c34a34a..26c0bde4 100644 --- a/Include/ssGUI/Enums/GUIObjectType.hpp +++ b/Include/ssGUI/Enums/GUIObjectType.hpp @@ -71,7 +71,8 @@ namespace Enums IMAGE_CANVAS = 1 << 15, STANDARD_SLIDER = 1 << 16, STANDARD_CHECKBOX = 1 << 17, - COUNT = 1 << 18 + TAB = 1 << 18, + COUNT = 1 << 19 }; inline ssGUI::Enums::GUIObjectType operator|(ssGUI::Enums::GUIObjectType a, ssGUI::Enums::GUIObjectType b) @@ -96,9 +97,9 @@ namespace Enums namespace { - inline std::string InternalGUIObjectTypeToString(GUIObjectType guiObjectType) + inline std::string Internal_GUIObjectTypeToString(GUIObjectType guiObjectType) { - static_assert((int)GUIObjectType::COUNT == 1 << 18, "ToString"); + static_assert((int)GUIObjectType::COUNT == 1 << 19, "ToString"); switch(guiObjectType) { RETURN_ENUM_STRING(GUIObjectType::WINDOW); @@ -119,6 +120,7 @@ namespace Enums RETURN_ENUM_STRING(GUIObjectType::IMAGE_CANVAS); RETURN_ENUM_STRING(GUIObjectType::STANDARD_SLIDER); RETURN_ENUM_STRING(GUIObjectType::STANDARD_CHECKBOX); + RETURN_ENUM_STRING(GUIObjectType::TAB); RETURN_ENUM_STRING(GUIObjectType::COUNT); } @@ -129,12 +131,12 @@ namespace Enums //function: GUIObjectTypeToString inline std::string GUIObjectTypeToString(GUIObjectType guiObjectType) { - static_assert((int)GUIObjectType::COUNT == 1 << 18, "ToString"); + static_assert((int)GUIObjectType::COUNT == 1 << 19, "ToString"); std::string curString; - for(int i = 0; i < 19; ++i) + for(int i = 0; i < 20; ++i) { - std::string returnString = InternalGUIObjectTypeToString(guiObjectType & (GUIObjectType)(1 << i)); + std::string returnString = Internal_GUIObjectTypeToString(guiObjectType & (GUIObjectType)(1 << i)); if(!returnString.empty()) curString += returnString + ", "; } diff --git a/Include/ssGUI/Extensions/TabArea.hpp b/Include/ssGUI/Extensions/TabArea.hpp new file mode 100644 index 00000000..30124736 --- /dev/null +++ b/Include/ssGUI/Extensions/TabArea.hpp @@ -0,0 +1,478 @@ +#ifndef SSGUI_TAB_AREA_HPP +#define SSGUI_TAB_AREA_HPP + +#include "ssGUI/Extensions/Extension.hpp" +#include "ssGUI/GUIObjectClasses/CompositeClasses/Tab.hpp" + +#include + +namespace ssGUI +{ + //namespace: ssGUI::Extensions + namespace Extensions + { + /* clang-format off */ + + /*class: ssGUI::Extensions::TabArea + Allows GUI Objects with to be tabbed under this extension + + Variables & Constructor: + ============================== C++ ============================== + protected: + //See + ssGUI::GUIObject* Container; + + //See + bool Enabled; + + //See + float TabBarHeight; + + //See + float NewTabWidth; + + //See + ObjectsReferences CurrentObjectsReferences; + + //See + ssGUIObjectIndex TabBar; + + //See + ssGUIObjectIndex CurrentTabContent; + + //(Internal variable) Used for detecting which tab is clicked + ssGUIObjectIndex LastClickedTab; + + //See + ssGUIObjectIndex ContentsHolder; + + //(Internal variable) Used for holding all the tabs + ssGUIObjectIndex TabsHolder; + + using TabId = ssGUIObjectIndex; + using TabContentId = ssGUIObjectIndex; + + //(Internal variable) Used for mapping tabs and tabs contents + std::unordered_map TabsInfos; + + //(Internal variable) Object for displaying tab preview + ssGUIObjectIndex TabPreview; + + //See + glm::u8vec4 PreviewColor; + + //See + bool OverrideTabPreviewSize; + + //See + glm::vec2 TabPreviewOverrideSize; + + //See + glm::u8vec4 NewTabColor; + + //See + static ssGUI::GUIObject* DefaultTabBarObject; + + //See + static glm::u8vec4 DefaultTabBarColor; + + //See + static ssGUI::Tab* DefaultTabObject; + + //See + static glm::u8vec4 DefaultTabColor; + + //See + static glm::u8vec4 DefaultPreviewColor; + ================================================================= + ============================== C++ ============================== + TabArea::TabArea() : + Container(nullptr), + Enabled(true), + TabBarHeight(25), + NewTabWidth(100), + CurrentObjectsReferences(), + TabBar(-1), + CurrentTabContent(-1), + LastClickedTab(-1), + ContentsHolder(-1), + TabsHolder(-1), + TabsInfos(), + TabPreview(-1), + PreviewColor(DefaultPreviewColor), + OverrideTabPreviewSize(false), + TabPreviewOverrideSize(), + NewTabColor(DefaultTabColor) + { + } + ================================================================= + */ + + /* clang-format on */ + class TabArea : public Extension + { + public: + friend class ssGUI::Factory; + + private: + TabArea& operator=(const TabArea& other); + + protected: + //See + ssGUI::GUIObject* Container; + + //See + bool Enabled; + + //See + float TabBarHeight; + + //See + float NewTabWidth; + + //See + ObjectsReferences CurrentObjectsReferences; + + //See + ssGUIObjectIndex TabBar; + + //See + ssGUIObjectIndex CurrentTabContent; + + //(Internal variable) Used for detecting which tab is clicked + ssGUIObjectIndex LastClickedTab; + + //See + ssGUIObjectIndex ContentsHolder; + + //(Internal variable) Used for holding all the tabs + ssGUIObjectIndex TabsHolder; + + using TabId = ssGUIObjectIndex; + using TabContentId = ssGUIObjectIndex; + + //(Internal variable) Used for mapping tabs and tabs contents + std::unordered_map TabsInfos; + + //(Internal variable) Object for displaying tab preview + ssGUIObjectIndex TabPreview; + + //See + glm::u8vec4 PreviewColor; + + //See + bool OverrideTabPreviewSize; + + //See + glm::vec2 TabPreviewOverrideSize; + + //See + glm::u8vec4 NewTabColor; + + //See + static ssGUI::GUIObject* DefaultTabBarObject; + + //See + static glm::u8vec4 DefaultTabBarColor; + + //See + static ssGUI::Tab* DefaultTabObject; + + //See + static glm::u8vec4 DefaultTabColor; + + //See + static glm::u8vec4 DefaultPreviewColor; + + TabArea(); + virtual ~TabArea() override; + TabArea(const TabArea& other); + + static void* operator new(size_t size) + { + return ::operator new(size); + }; + + static void* operator new[](size_t size) + { + return ::operator new(size); + }; + + static void operator delete(void* p) + { + free(p); + }; + + static void operator delete[](void* p) + { + free(p); + }; + + virtual void Initialize(); + + virtual bool IsTabContentExist(ssGUI::GUIObject* content) const; + + using TabInfoIterator = std::unordered_map::iterator; + virtual TabInfoIterator GetMouseHoveredTab(glm::ivec2 mousePos); + + virtual void DrawTabPreview(TabId tab, glm::vec2 mousePos); + + virtual void DrawTabPreview(); + + virtual void DiscardTabPreview(); + + virtual void ValidateTabsAndOrders(); + + virtual ssGUI::GUIObject* GetFirstLayoutChild(ssGUI::GUIObject* parent); + + virtual ssGUI::GUIObject* GetLastLayoutChild(ssGUI::GUIObject* parent); + + virtual ssGUI::Tab* AddTab(ssGUIObjectIndex contentId, + ssGUI::Tab* tabToUse = nullptr); + + virtual void UpdateOrientationsForContentsAndPreviews(); + + virtual bool TabbedIfNeeded(ssGUI::Backend::BackendSystemInputInterface* + inputInterface, + ssGUI::InputStatus& inputStatus, + ssGUI::InputStatus& lastInputStatus, + bool isMouseInsideTabBar, + glm::ivec2 mousePos); + + virtual void SetLastClickedTab(ssGUI::GUIObject* lastTab); + + virtual bool SanityCheck() const; + + virtual bool CanContentBeTabbed(ssGUI::GUIObject* contentToCheck, + ssGUI::GUIObject* contentTopLevelParent) const; + + virtual void AddDeletionToCloseButton(ssGUI::Tab* tab); + + virtual void ConstructRenderInfo() override; + virtual void ConstructRenderInfo(ssGUI::Backend::BackendDrawingInterface* + drawingInterface, + ssGUI::GUIObject* mainWindow, + glm::vec2 mainWindowPositionOffset) override; + + public: + //==================================================================== + //Group: Constants + //==================================================================== + + //string: EXTENSION_NAME + static const std::string EXTENSION_NAME; + + //string: ListenerKey + static const std::string ListenerKey; + + //==================================================================== + //Group: Tab Preview + //==================================================================== + + //function: SetOverrideTabPreviewSize + //By default, the tab preview size is half of the tab size. + //If override is true, the tab preview size will be OverrideTabPreviewSize. + virtual void SetOverrideTabPreviewSize(bool override, glm::vec2 size = glm::vec2()); + + //function: IsOverrideTabPreviewSize + virtual bool IsOverrideTabPreviewSize() const; + + //function: GetOverrideTabPreviewSize + virtual glm::vec2 GetOverrideTabPreviewSize() const; + + //function: SetTabPreviewColor + virtual void SetTabPreviewColor(glm::u8vec4 color); + + //function: GetTabPreviewColor + virtual glm::u8vec4 GetTabPreviewColor() const; + + //function: SetDefaultPreviewColor + static void SetDefaultPreviewColor(glm::u8vec4 color); + + //function: GetDefaultPreviewColor + static glm::u8vec4 GetDefaultPreviewColor(); + + //==================================================================== + //Group: Tab Bar + //==================================================================== + + //function: GetTabBar + virtual ssGUI::GUIObject* GetTabBar() const; + + //function: SetTabBarHeight + virtual void SetTabBarHeight(float height); + + //function: GetTabBarHeight + virtual float GetTabBarHeight() const; + + //function: SetTabBarColor + virtual void SetTabBarColor(glm::u8vec4 color); + + //function: GetTabBarColor + virtual glm::u8vec4 GetTabBarColor() const; + + //function: SetTabBarHorizontal + virtual void SetTabBarHorizontal(bool horizontal); + + //function: IsTabBarHorizontal + virtual bool IsTabBarHorizontal() const; + + //function: SetDefaultTabBarObject + static void SetDefaultTabBarObject(ssGUI::GUIObject* defaultTabBar); + + //function: GetDefaultTabBarObject + static ssGUI::GUIObject* GetDefaultTabBarObject(); + + //function: SetDefaultTabBarColor + static void SetDefaultTabBarColor(glm::u8vec4 color); + + //function: GetDefaultTabBarColor + static glm::u8vec4 GetDefaultTabBarColor(); + + //==================================================================== + //Group: Tab appearance and order + //==================================================================== + + //function: SetNewTabWidth + virtual void SetNewTabWidth(float width); + + //function: GetNewTabWidth + virtual float GetNewTabWidth() const; + + //function: SetReverseTabsOrder + virtual void SetReverseTabsOrder(bool reverse); + + //function: IsReverseTabsOrder + virtual bool IsReverseTabsOrder() const; + + //function: SetNewTabColor + virtual void SetNewTabColor(glm::u8vec4 color); + + //function: GetNewTabColor + virtual glm::u8vec4 GetNewTabColor() const; + + //function: SetDefaultTabObject + static void SetDefaultTabObject(ssGUI::Tab* defaultTab); + + //function: GetDefaultTabObject + static ssGUI::Tab* GetDefaultTabObject(); + + //function: SetDefaultTabColor + static void SetDefaultTabColor(glm::u8vec4 color); + + //function: GetDefaultTabColor + static glm::u8vec4 GetDefaultTabColor(); + + //==================================================================== + //Group: Tab Content + //==================================================================== + + //function: AddContent + virtual ssGUI::Tab* AddContent(ssGUI::GUIObject* contentToRegister, + ssGUI::Tab* tabToUse = nullptr); + + //function: AddContentToFront + virtual ssGUI::Tab* AddContentToFront(ssGUI::GUIObject* contentToRegister, + ssGUI::Tab* tabToUse = nullptr); + + //function: AddContentToEnd + virtual ssGUI::Tab* AddContentToEnd(ssGUI::GUIObject* contentToRegister, + ssGUI::Tab* tabToUse = nullptr); + + //function: AddContentBefore + virtual ssGUI::Tab* AddContentBefore(ssGUI::GUIObject* contentToRegister, + ssGUI::GUIObject* contentBefore, + ssGUI::Tab* tabToUse = nullptr); + + //function: AddContentAfter + virtual ssGUI::Tab* AddContentAfter(ssGUI::GUIObject* contentToRegister, + ssGUI::GUIObject* contentAfter, + ssGUI::Tab* tabToUse = nullptr); + + //function: RemoveContent + virtual void RemoveContent(ssGUI::GUIObject* contentToRemove); + + //function: GetTabFromContent + virtual ssGUI::Tab* GetTabFromContent(ssGUI::GUIObject* registeredContent) const; + + //function: GetTabContentsHolder + virtual ssGUI::GUIObject* GetTabContentsHolder() const; + + //function: MoveContentBefore + virtual void MoveContentBefore(ssGUI::GUIObject* contentToMove, + ssGUI::GUIObject* contentBefore); + + //function: MoveContentFront + virtual void MoveContentFront(ssGUI::GUIObject* contentToMove); + + //function: MoveContentEnd + virtual void MoveContentEnd(ssGUI::GUIObject* contentToMove); + + //function: MoveContentAfter + virtual void MoveContentAfter(ssGUI::GUIObject* contentToMove, + ssGUI::GUIObject* contentAfter); + + //function: SwitchContent + virtual void SwitchContent(ssGUI::GUIObject* registeredContent); + + //function: GetCurrentTab + virtual ssGUI::Tab* GetCurrentTab() const; + + //function: GetCurrentTabContent + virtual ssGUI::GUIObject* GetCurrentTabContent() const; + + //==================================================================== + //Group: Overrides + //==================================================================== + + //Override from Extension + //function: SetEnabled + //See + virtual void SetEnabled(bool enabled) override; + + //function: IsEnabled + //See + virtual bool IsEnabled() const override; + + //function: Internal_Update + //See + virtual void Internal_Update(bool isPreUpdate, + ssGUI::Backend::BackendSystemInputInterface* + inputInterface, + ssGUI::InputStatus& currentInputStatus, + ssGUI::InputStatus& lastInputStatus, + ssGUI::GUIObject* mainWindow) override; + + //function: Internal_Draw + //See + virtual void Internal_Draw(bool isPreRender, + ssGUI::Backend::BackendDrawingInterface* + drawingInterface, + ssGUI::GUIObject* mainWindow, + glm::vec2 mainWindowPositionOffset) override; + + //function: GetExtensionName + //See + virtual std::string GetExtensionName() const override; + + //function: BindToObject + //See + virtual void BindToObject(ssGUI::GUIObject* bindObj) override; + + //function: Copy + //See + virtual void Copy(const ssGUI::Extensions::Extension* extension) override; + + //function: Internal_GetObjectsReferences + //See + virtual ObjectsReferences* Internal_GetObjectsReferences() override; + + //function: Clone + //See + virtual TabArea* Clone() override; + }; + } + +} + +#endif \ No newline at end of file diff --git a/Include/ssGUI/Extensions/Tabbable.hpp b/Include/ssGUI/Extensions/Tabbable.hpp new file mode 100644 index 00000000..405c8f8d --- /dev/null +++ b/Include/ssGUI/Extensions/Tabbable.hpp @@ -0,0 +1,188 @@ +#ifndef SSGUI_TABBABLE_HPP +#define SSGUI_TABBABLE_HPP + +#include "ssGUI/Extensions/Extension.hpp" + +namespace ssGUI +{ + +//namespace: ssGUI::Extensions +namespace Extensions +{ + /* clang-format off */ + + /*class: ssGUI::Extensions::Tabbable + Allows the attached object to be tabbed under + + Variables & Constructor: + ============================== C++ ============================== + protected: + //See + ssGUI::GUIObject* Container; + + //See + bool Enabled; + + //See + ssGUIObjectIndex TabAreaObject; + + //See + ObjectsReferences CurrentObjectsReferences; + + //See + ssGUIObjectIndex TopLevelParent; + + //See + bool Untabbable; + + //See + glm::vec2 LastMouseDragBeginPosition; + ================================================================= + ============================== C++ ============================== + Tabbable::Tabbable() : + Container(nullptr), + Enabled(true), + TabAreaObject(-1), + CurrentObjectsReferences(), + TopLevelParent(-1), + Untabbable(false), + LastMouseDragBeginPosition() + { + } + ================================================================= + */ + + /* clang-format on */ + class Tabbable : public Extension + { + public: + friend class ssGUI::Factory; + + private: + Tabbable& operator=(Tabbable const& other); + + protected: + //See + ssGUI::GUIObject* Container; + + //See + bool Enabled; + + //See + ssGUIObjectIndex TabAreaObject; + + //See + ObjectsReferences CurrentObjectsReferences; + + //See + ssGUIObjectIndex TopLevelParent; + + //See + bool Untabbable; + + //See + glm::vec2 LastMouseDragBeginPosition; + + Tabbable(); + virtual ~Tabbable() override; + Tabbable(Tabbable const& other); + static void* operator new(size_t size) {return ::operator new(size);}; + static void* operator new[](size_t size) {return ::operator new(size);}; + static void operator delete(void* p) {free(p);}; + static void operator delete[](void* p) {free(p);}; + + virtual void ConstructRenderInfo() override; + virtual void ConstructRenderInfo( ssGUI::Backend::BackendDrawingInterface* drawingInterface, + ssGUI::GUIObject* mainWindow, + glm::vec2 mainWindowPositionOffset) override; + + public: + //==================================================================== + //Group: Constants + //==================================================================== + + //string: EXTENSION_NAME + static const std::string EXTENSION_NAME; + + //==================================================================== + //Group: Tabbable + //==================================================================== + + //function: SetTopLevelParent + virtual void SetTopLevelParent(ssGUI::GUIObject* topLevelParent); + + //function: GetTopLevelParent + virtual ssGUI::GUIObject* GetTopLevelParent() const; + + //function: SetUntabbable + virtual void SetUntabbable(bool untabbable); + + //function: IsUntabbable + virtual bool IsUntabbable() const; + + //function: GetLastMouseDragBeginPosition + virtual glm::vec2 GetLastMouseDragBeginPosition() const; + + //function: SetLastMouseDragBeginPosition + virtual void SetLastMouseDragBeginPosition(glm::vec2 pos); + + //function: GetTabAreaObject + ssGUI::GUIObject* GetTabAreaObject() const; + + //function: SetTabAreaObject + void SetTabAreaObject(ssGUI::GUIObject* tabArea); + + + //==================================================================== + //Group: Overrides + //==================================================================== + + //Override from Extension + //function: SetEnabled + //See + virtual void SetEnabled(bool enabled) override; + + //function: IsEnabled + //See + virtual bool IsEnabled() const override; + + //function: Internal_Update + //See + virtual void Internal_Update( bool isPreUpdate, + ssGUI::Backend::BackendSystemInputInterface* inputInterface, + ssGUI::InputStatus& currentInputStatus, + ssGUI::InputStatus& lastInputStatus, + ssGUI::GUIObject* mainWindow) override; + + //function: Internal_Draw + //See + virtual void Internal_Draw( bool isPreRender, + ssGUI::Backend::BackendDrawingInterface* drawingInterface, + ssGUI::GUIObject* mainWindow, + glm::vec2 mainWindowPositionOffset) override; + + //function: GetExtensionName + //See + virtual std::string GetExtensionName() const override; + + //function: BindToObject + //See + virtual void BindToObject(ssGUI::GUIObject* bindObj) override; + + //function: Copy + //See + virtual void Copy(const ssGUI::Extensions::Extension* extension) override; + + //function: Internal_GetObjectsReferences + //See + virtual ObjectsReferences* Internal_GetObjectsReferences() override; + + //function: Clone + //See + virtual Tabbable* Clone() override; + }; +} + +} + +#endif \ No newline at end of file diff --git a/Include/ssGUI/GUIObjectClasses/CompositeClasses/Tab.hpp b/Include/ssGUI/GUIObjectClasses/CompositeClasses/Tab.hpp new file mode 100644 index 00000000..650bd5ce --- /dev/null +++ b/Include/ssGUI/GUIObjectClasses/CompositeClasses/Tab.hpp @@ -0,0 +1,208 @@ +#ifndef SSGUI_TAB_HPP +#define SSGUI_TAB_HPP + +#include "ssGUI/Enums/Direction.hpp" +#include "ssGUI/GUIObjectClasses/CompositeClasses/StandardButton.hpp" + +//namespace: ssGUI +namespace ssGUI +{ + /* clang-format off */ + /*class: Tab + A tab GUI object that is normally generated by + + Variables & Constructor: + ============================== C++ ============================== + protected: + //See + glm::u8vec4 TabColor; + + //See + glm::u8vec4 SelectedColor; + + //See + glm::u8vec4 UnderlineColor; + + //See + float UnderlineThickness; + + //See + ssGUI::Enums::Direction UnderlineDirection; + + //See + ssGUIObjectIndex CloseButton; + + //(Internal variable) Shape Ids for the close button + int CloseButtonShapeIds[2]; + + //See + ssGUIObjectIndex ContentObject; + + //See + bool Selected = false; + + //(Internal variable) Default size for close button + static glm::vec2 DefaultCloseButtonSize; + ================================================================= + ============================== C++ ============================== + Tab::Tab() : + StandardButton(), + TabColor(100, 100, 100, 255), + SelectedColor(127, 127, 127, 255), + UnderlineColor(255, 255, 255, 127), + UnderlineThickness(3), + UnderlineDirection(ssGUI::Enums::Direction::BOTTOM), + CloseButton(-1), + CloseButtonShapeIds{-1, -1}, + ContentObject(-1), + Selected(false) + { + GetExtension()->ClearAllPreferredSizeMultiplier(); + RemoveExtension(); + RemoveExtension(); + auto* boxShadow = GetExtension(); + boxShadow->SetSizeOffset(glm::vec2(boxShadow->GetSizeOffset().x, 0)); + + GetButtonTextObject()->SetNewTextFontSize(14); + + InitializeCloseButton(); + } + ================================================================= + */ + /* clang-format on */ + class Tab : public StandardButton + { + private: + Tab& operator=(const Tab& other) = default; + + protected: + //See + glm::u8vec4 TabColor; + + //See + glm::u8vec4 SelectedColor; + + //See + glm::u8vec4 UnderlineColor; + + //See + float UnderlineThickness; + + //See + ssGUI::Enums::Direction UnderlineDirection; + + //See + ssGUIObjectIndex CloseButton; + + //(Internal variable) Shape Ids for the close button + int CloseButtonShapeIds[2]; + + //See + ssGUIObjectIndex ContentObject; + + //See + bool Selected = false; + + //(Internal variable) Default size for close button + static glm::vec2 DefaultCloseButtonSize; + + Tab(const Tab& other); + + virtual void SetCloseSymbolColor(glm::u8vec4 color); + + virtual void InitializeCloseButton(); + + virtual void MainLogic(ssGUI::Backend::BackendSystemInputInterface* inputInterface, + ssGUI::InputStatus& currentInputStatus, + ssGUI::InputStatus& lastInputStatus, + ssGUI::GUIObject* mainWindow) override; + + public: + //==================================================================== + //Group: Constants + //==================================================================== + + //string: ListenerKey + static const std::string ListenerKey; + + Tab(); + virtual ~Tab() override; + + //==================================================================== + //Group: Methods + //==================================================================== + //function: SetAssociatedContent + virtual void SetAssociatedContent(ssGUI::GUIObject* content); + + //function: GetAssociatedContent + virtual ssGUI::GUIObject* GetAssociatedContent() const; + + //function: SetTabColor + virtual void SetTabColor(glm::u8vec4 color); + + //function: GetTabColor + virtual glm::u8vec4 GetTabColor() const; + + //function: SetSelectedColor + virtual void SetSelectedColor(glm::u8vec4 color); + + //function: GetSelectedColor + virtual glm::u8vec4 GetSelectedColor() const; + + //function: SetUnderlineColor + virtual void SetUnderlineColor(glm::u8vec4 color); + + //function: GetUnderlineColor + virtual glm::u8vec4 GetUnderlineColor() const; + + //function: SetUnderlineDirection + virtual void SetUnderlineDirection(ssGUI::Enums::Direction direction); + + //function: GetUnderlineDirection + virtual ssGUI::Enums::Direction GetUnderlineDirection() const; + + //function: SetUnderlineThickness + virtual void SetUnderlineThickness(float thickness); + + //function: GetUnderlineThickness + virtual float GetUnderlineThickness() const; + + //function: SetCloseButton + virtual void SetCloseButton(ssGUI::Button* button); + + //function: GetCloseButton + virtual ssGUI::Button* GetCloseButton() const; + + //function: SelectTab + virtual void SelectTab(bool select); + + //function: IsTabSelected + virtual bool IsTabSelected() const; + + //function: SetTabIcon + virtual void SetTabIcon(ssGUI::Image* icon); + + //function: GetTabIcon + virtual ssGUI::Image* GetTabIcon() const; + + //function: SetTabTitleObject + virtual void SetTabTitleObject(ssGUI::Text* title); + + //function: GetTabTitleObject + virtual ssGUI::Text* GetTabTitleObject() const; + + //==================================================================== + //Group: Overrides + //==================================================================== + + //function: GetType + //See + virtual ssGUI::Enums::GUIObjectType GetType() const override; + + //function: Clone + //See + virtual Tab* Clone(bool cloneChildren) override; + }; +} + +#endif \ No newline at end of file diff --git a/Src/Examples/Playground/TabbingPlayground.cpp b/Src/Examples/Playground/TabbingPlayground.cpp new file mode 100644 index 00000000..8ef0dca8 --- /dev/null +++ b/Src/Examples/Playground/TabbingPlayground.cpp @@ -0,0 +1,114 @@ + +#include "ssGUI/Extensions/AdvancedPosition.hpp" +#include "ssGUI/Extensions/Layout.hpp" +#include "ssGUI/Extensions/TabArea.hpp" +#include "ssGUI/Extensions/Tabbable.hpp" +#include "ssGUI/GUIObjectClasses/CompositeClasses/StandardWindow.hpp" +#include "ssGUI/HeaderGroups/StandardGroup.hpp" + +#include + +//Layout example +int main() +{ + //Create the main window + ssGUI::MainWindow mainWindow; + mainWindow.SetSize(glm::vec2(450, 650)); + + ssGUI::Window* content1 = mainWindow.AddChild(); + ssGUI::Window* content2 = mainWindow.AddChild(); + + content1->AddExtension(); + content2->AddExtension(); + content1->SetTitlebarColor(glm::u8vec4(255, 127, 127, 255)); + ssGUI::Text* testText = + content1->AddChild(ssGUI::Enums::AlignmentHorizontal::CENTER, + ssGUI::Enums::AlignmentVertical::CENTER); + testText->SetText("Hello World!"); + + content2->SetTitlebarColor(glm::u8vec4(127, 255, 127, 255)); + + ssGUI::Window window; + + window.SetPosition(glm::vec2(25, 25)); + window.SetSize(glm::vec2(300, 600)); + //window.SetMinSize(glm::vec2(300, 600)); + + window.SetParent(&mainWindow); + window.AddExtension(); + //window.GetExtension()->SetReverseTabsOrder(true); + + ssGUI::Tab* tab = window.GetExtension()->AddContent(content1); + tab->SetTabColor(glm::u8vec4(255, 127, 127, 255)); + tab->SetSelectedColor(glm::u8vec4(255, 127, 127, 255)); + + ssGUI::Tab* tab2 = window.GetExtension()->AddContent(content2); + tab2->SetTabColor(glm::u8vec4(127, 255, 127, 255)); + tab2->SetSelectedColor(glm::u8vec4(127, 255, 127, 255)); + + //tab->SetTabTitleObject(nullptr); + //tab2->SetTabTitleObject(nullptr); + //window.GetExtension()->SetTabBarHorizontal(true); + + //window.AddExtension(); + // window.GetExtension()->SetPadding(5); + window.SetBackgroundColor(glm::u8vec4(180, 180, 180, 255)); + +#if 0 + ssGUI::StandardWindow window2; + window2.SetBackgroundColor(glm::u8vec4(100, 100, 100, 255)); + window2.SetTitlebarColor(glm::u8vec4(255, 127, 127, 255)); + //window2.AddExtension(); + window2.SetPosition(window2.GetPosition() + glm::vec2(5, 5)); + window2.AddExtension(); + window2.SetParent(&mainWindow); +#endif + +#if 0 + ssGUI::StandardWindow window3; + window3.SetTitlebarColor(glm::u8vec4(127, 255, 127, 255)); + window3.SetBackgroundColor(glm::u8vec4(100, 100, 100, 255)); + window3.AddExtension(); + window3.SetPosition(window2.GetPosition() + glm::vec2(5, 5)); + //window3.AddExtension(); + + ssGUI::StandardWindow window4; + window4.SetTitlebarColor(glm::u8vec4(127, 127, 255, 255)); + window4.SetBackgroundColor(glm::u8vec4(100, 100, 100, 255)); + window4.AddExtension(); + window4.SetPosition(window3.GetPosition() + glm::vec2(5, 5)); + + ssGUI::StandardWindow window5; + // window5.SetTitlebarColor(glm::u8vec4(127, 127, 127, 255)); + window5.SetBackgroundColor(glm::u8vec4(100, 100, 100, 255)); + window5.AddExtension(); + window5.SetPosition(window4.GetPosition() + glm::vec2(5, 5)); +#endif + + //window3.SetParent(&mainWindow); + //window4.SetParent(&mainWindow); + //window5.SetParent(&mainWindow); + + //Create the GUIManager, add the main window and start running + ssGUI::ssGUIManager guiManager; + + guiManager.AddPostGUIUpdateEventListener( + [&]() + { + //ssLOG_LINE("content1->GetResizeType(): " << + //ssGUI::Enums::ResizeTypeToString(content1->GetResizeType())); + //ssLOG_LINE("content2->GetResizeType(): " << + //ssGUI::Enums::ResizeTypeToString(content2->GetResizeType())); + + //ssLOG_LINE("window: " << &window); + //ssLOG_LINE("window.GetMinSize(): " << window.GetMinSize()); + //ssLOG_LINE("window.GetMaxSize(): " << window.GetMaxSize()); + //ssLOG_LINE("window.GetExtension()->IsCoverFullLength(): " + //<< window.GetExtension()->IsCoverFullLength()); + }); + + guiManager.AddRootGUIObject((ssGUI::GUIObject*)&mainWindow); + + guiManager.StartRunning(); + return 0; +} diff --git a/Src/ssGUI/Backend/X11_OpenGL3_3/BackendMainWindowX11_OpenGL3_3.cpp b/Src/ssGUI/Backend/X11_OpenGL3_3/BackendMainWindowX11_OpenGL3_3.cpp index 38499138..48fdc673 100644 --- a/Src/ssGUI/Backend/X11_OpenGL3_3/BackendMainWindowX11_OpenGL3_3.cpp +++ b/Src/ssGUI/Backend/X11_OpenGL3_3/BackendMainWindowX11_OpenGL3_3.cpp @@ -781,23 +781,24 @@ namespace Backend glm::ivec2 BackendMainWindowX11_OpenGL3_3::GetWindowPosition() const { - XWindowAttributes attr; - if(!XGetWindowAttributes(WindowDisplay, WindowId, &attr)) - { - ssGUI_WARNING(ssGUI_BACKEND_TAG, "Failed to XGetWindowAttributes"); - return glm::ivec2(0, 0); - } + #if 0 + XWindowAttributes attr; + if(!XGetWindowAttributes(WindowDisplay, WindowId, &attr)) + { + ssGUI_WARNING(ssGUI_BACKEND_TAG, "Failed to XGetWindowAttributes"); + return glm::ivec2(0, 0); + } + #endif //https://stackoverflow.com/a/23940869/7519584 - int x, y; + int renderPosX, renderPosY; Window curWindow = WindowId; XTranslateCoordinates(WindowDisplay, WindowId, RootWindow(WindowDisplay, DefaultScreen(WindowDisplay)), - 0, 0, &x, &y, &curWindow ); + 0, 0, &renderPosX, &renderPosY, &curWindow ); int top, right, left, bot; GetWindowDecor(top, right, bot, left); - - return glm::ivec2(x - attr.x - left, y - attr.y - top); + return glm::ivec2(renderPosX - left, renderPosY - top); } glm::ivec2 BackendMainWindowX11_OpenGL3_3::GetPositionOffset() const diff --git a/Src/ssGUI/DataClasses/Hierarchy.cpp b/Src/ssGUI/DataClasses/Hierarchy.cpp index f15bcbb5..1afc9726 100644 --- a/Src/ssGUI/DataClasses/Hierarchy.cpp +++ b/Src/ssGUI/DataClasses/Hierarchy.cpp @@ -47,34 +47,34 @@ namespace ssGUI } } - void Hierarchy::AddChild(ssGUI::GUIObject* guiObject,bool compositeChild) + void Hierarchy::SetChildParent(ssGUI::GUIObject* guiObject, bool compositeChild) { guiObject->SetParent(CurrentObject, compositeChild); } - void Hierarchy::AddChild( ssGUI::GUIObject* guiObject, - ssGUI::Enums::AlignmentHorizontal horizontalAlignment, - ssGUI::Enums::AlignmentVertical verticalAlignment, - bool compositeChild) + void Hierarchy::SetChildParent( ssGUI::GUIObject* guiObject, + ssGUI::Enums::AlignmentHorizontal horizontalAlignment, + ssGUI::Enums::AlignmentVertical verticalAlignment, + bool compositeChild) { - AddChild(guiObject, compositeChild); + SetChildParent(guiObject, compositeChild); auto* ap = guiObject->AddExtension(); ap->SetAlignment(horizontalAlignment, verticalAlignment); } - void Hierarchy::AddChildWithWrapper(ssGUI::GUIObject* guiObject, bool compositeChild) + void Hierarchy::SetChildParentWithWrapper(ssGUI::GUIObject* guiObject, bool compositeChild) { auto* wrapper = ssGUI::Create(); - AddChild(wrapper, compositeChild); - wrapper->AddChild(guiObject, compositeChild); + SetChildParent(wrapper, compositeChild); + wrapper->SetChildParent(guiObject, compositeChild); } - void Hierarchy::AddChildWithWrapper(ssGUI::GUIObject* guiObject, - ssGUI::Enums::AlignmentHorizontal horizontalAlignment, - ssGUI::Enums::AlignmentVertical verticalAlignment, - bool compositeChild) + void Hierarchy::SetChildParentWithWrapper( ssGUI::GUIObject* guiObject, + ssGUI::Enums::AlignmentHorizontal horizontalAlignment, + ssGUI::Enums::AlignmentVertical verticalAlignment, + bool compositeChild) { - AddChildWithWrapper(guiObject, compositeChild); + SetChildParentWithWrapper(guiObject, compositeChild); auto* ap = guiObject->AddExtension(); ap->SetAlignment(horizontalAlignment, verticalAlignment); } diff --git a/Src/ssGUI/Extensions/CMakeLists.txt b/Src/ssGUI/Extensions/CMakeLists.txt index 6d0111b1..44bef337 100644 --- a/Src/ssGUI/Extensions/CMakeLists.txt +++ b/Src/ssGUI/Extensions/CMakeLists.txt @@ -15,4 +15,6 @@ target_sources(ssGUI PRIVATE "${CMAKE_CURRENT_LIST_DIR}/TemplateExtension.cpp" "${CMAKE_CURRENT_LIST_DIR}/DockableV2.cpp" "${CMAKE_CURRENT_LIST_DIR}/DockingArea.cpp" + "${CMAKE_CURRENT_LIST_DIR}/TabArea.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Tabbable.cpp" ) \ No newline at end of file diff --git a/Src/ssGUI/Extensions/DockableV2.cpp b/Src/ssGUI/Extensions/DockableV2.cpp index 29a2ab2c..1a87f749 100644 --- a/Src/ssGUI/Extensions/DockableV2.cpp +++ b/Src/ssGUI/Extensions/DockableV2.cpp @@ -567,7 +567,10 @@ namespace Extensions void DockableV2::SetTopLevelParent(ssGUI::GUIObject* topLevelParent) { if(topLevelParent == nullptr) + { TopLevelParent = -1; + return; + } TopLevelParent = CurrentObjectsReferences.AddObjectReference(topLevelParent); } diff --git a/Src/ssGUI/Extensions/TabArea.cpp b/Src/ssGUI/Extensions/TabArea.cpp new file mode 100644 index 00000000..3679debd --- /dev/null +++ b/Src/ssGUI/Extensions/TabArea.cpp @@ -0,0 +1,1663 @@ +#include "ssGUI/Extensions/TabArea.hpp" +#include "ssGUI/DataClasses/TabEventInfo.hpp" +#include "ssGUI/Extensions/AdvancedPosition.hpp" +#include "ssGUI/Extensions/AdvancedSize.hpp" +#include "ssGUI/Extensions/Layout.hpp" +#include "ssGUI/Extensions/Tabbable.hpp" +#include "ssGUI/GUIObjectClasses/CompositeClasses/Tab.hpp" +#include "ssGUI/GUIObjectClasses/GUIObject.hpp" +#include "ssGUI/GUIObjectClasses/MainWindow.hpp" +#include "ssGUI/GUIObjectClasses/Widget.hpp" +#include "ssGUI/ssGUITags.hpp" + +#include + +namespace ssGUI +{ + namespace Extensions + { + ssGUI::GUIObject* TabArea::DefaultTabBarObject = nullptr; + glm::u8vec4 TabArea::DefaultTabBarColor = glm::u8vec4(127, 127, 127, 255); + ssGUI::Tab* TabArea::DefaultTabObject = nullptr; + glm::u8vec4 TabArea::DefaultTabColor = glm::u8vec4(100, 100, 100, 255); + glm::u8vec4 TabArea::DefaultPreviewColor = glm::u8vec4(255, 255, 255, 127); + + TabArea::TabArea() : + Container(nullptr), + Enabled(true), + TabBarHeight(25), + NewTabWidth(100), + CurrentObjectsReferences(), + TabBar(-1), + CurrentTabContent(-1), + LastClickedTab(-1), + ContentsHolder(-1), + TabsHolder(-1), + TabsInfos(), + TabPreview(-1), + PreviewColor(DefaultPreviewColor), + OverrideTabPreviewSize(false), + TabPreviewOverrideSize(), + NewTabColor(DefaultTabColor) + { + } + + TabArea::~TabArea() + { + } + + TabArea::TabArea(const TabArea& other) + { + Container = nullptr; + Copy(&other); + } + + void TabArea::Initialize() + { + if(Container == nullptr) + return; + + //Create layout if there isn't one + if(!Container->IsExtensionExist()) + Container->AddExtension(); //->SetCoverFullLength(false); + + Container->GetExtension()->SetSpacing(0); + + //Tab bar creation + ssGUI::Widget* tabBar; + + if(DefaultTabBarObject != nullptr) + { + tabBar = static_cast(DefaultTabBarObject->Clone(true)); + tabBar->SetParent(Container); + } + else + { + tabBar = Container->AddChild(true); + tabBar->SetBackgroundColor(DefaultTabBarColor); + } + + tabBar->SetUserCreated(false); + tabBar->SetMaxSize(glm::vec2(9999, TabBarHeight)); + tabBar->AddExtension(); + Container->MoveChildToFirst(tabBar); + + TabBar = CurrentObjectsReferences.AddObjectReference(tabBar); + + //Tabs Holder + ssGUI::GUIObject* tabsHolder = tabBar->AddChild(true); + tabsHolder->SetUserCreated(false); + auto* tabsHolderLayout = tabsHolder->AddExtension(); + tabsHolderLayout->SetCoverFullLength(false); + tabsHolderLayout->SetHorizontalLayout(true); + TabsHolder = CurrentObjectsReferences.AddObjectReference(tabsHolder); + + //Contents Holder + ssGUI::GUIObject* contentsHolder = Container->AddChild(true); + contentsHolder->SetUserCreated(false); + contentsHolder->AddExtension(); + ContentsHolder = CurrentObjectsReferences.AddObjectReference(contentsHolder); + + //Create a preview object that we can use + auto* tabPreview = tabsHolder->AddChild(true); + tabPreview->SetUserCreated(false); + tabPreview->SetBlockInput(false); + tabPreview->AddTag(ssGUI::Tags::FLOATING); + tabPreview->AddExtension() + ->SetAlignment(ssGUI::Enums::AlignmentHorizontal::CENTER, + ssGUI::Enums::AlignmentVertical::CENTER); + + tabPreview->AddExtension()->SetSizingPercentage(1, 1); + + tabPreview->SetBackgroundColor(PreviewColor); + tabPreview->SetEnabled(false); + TabPreview = CurrentObjectsReferences.AddObjectReference(tabPreview); + + UpdateOrientationsForContentsAndPreviews(); + } + + bool TabArea::IsTabContentExist(ssGUI::GUIObject* content) const + { + ssGUIObjectIndex objectIndex = CurrentObjectsReferences.GetObjectIndex(content); + + if(objectIndex == -1) + return false; + + return TabsInfos.find(objectIndex) != TabsInfos.end(); + } + + TabArea::TabInfoIterator TabArea::GetMouseHoveredTab(glm::ivec2 mousePos) + { + for(auto tabIt = TabsInfos.begin(); tabIt != TabsInfos.end(); ++tabIt) + { + ssGUI::GUIObject* tabObj = + CurrentObjectsReferences.GetObjectReference(tabIt->second); + + if(tabObj == nullptr) + continue; + + if(mousePos.x >= tabObj->GetGlobalPosition().x && + mousePos.x < tabObj->GetGlobalPosition().x + tabObj->GetSize().x && + mousePos.y >= tabObj->GetGlobalPosition().y && + mousePos.y < tabObj->GetGlobalPosition().y + tabObj->GetSize().y) + { + return tabIt; + } + } + + return TabsInfos.end(); + } + + void TabArea::DrawTabPreview(TabId tab, glm::vec2 mousePos) + { + if(CurrentObjectsReferences.GetObjectReference(tab) == nullptr) + return; + + ssGUI::GUIObject* tabObj = CurrentObjectsReferences.GetObjectReference(tab); + ssGUI::GUIObject* previewObject = + CurrentObjectsReferences.GetObjectReference(TabPreview); + + previewObject->SetEnabled(true); + + bool isHoverLeftOrTop = + IsTabBarHorizontal() ? + mousePos.x < tabObj->GetGlobalPosition().x + tabObj->GetSize().x / 2.f : + mousePos.y < tabObj->GetGlobalPosition().y + tabObj->GetSize().y / 2.f; + + //Set Preview parent + previewObject->SetParent(CurrentObjectsReferences.GetObjectReference(TabsHolder)); + + if(!previewObject->IsExtensionExist() || + !previewObject->IsExtensionExist()) + { + ssGUI_ERROR(ssGUI_EXT_TAG, + "Preview object does not have advanced size or advanced position"); + ssLOG_EXIT_PROGRAM(); + return; + } + + //Disable advanced size and advanced position + previewObject->GetExtension()->SetEnabled(false); + previewObject->GetExtension()->SetEnabled(false); + + //Set preview size + if(OverrideTabPreviewSize) + previewObject->SetSize(TabPreviewOverrideSize); + else + { + if(IsTabBarHorizontal()) + previewObject->SetSize(glm::vec2(tabObj->GetSize().x / 2.f, TabBarHeight)); + else + previewObject->SetSize(glm::vec2(TabBarHeight, tabObj->GetSize().y / 2.f)); + } + + //Tab preview position left/top hover tab + if(isHoverLeftOrTop) + previewObject->SetGlobalPosition(tabObj->GetGlobalPosition()); + //Tab preview position right/bottom hover tab + else + { + glm::vec2 tabWidthOffset; + + if(OverrideTabPreviewSize) + { + tabWidthOffset = + IsTabBarHorizontal() ? + glm::vec2(tabObj->GetSize().x - TabPreviewOverrideSize.x, + TabBarHeight / 2.f - TabPreviewOverrideSize.y / 2.f) : + glm::vec2(TabBarHeight / 2.f - TabPreviewOverrideSize.x / 2.f, + tabObj->GetSize().y - TabPreviewOverrideSize.x); + } + else + { + tabWidthOffset = IsTabBarHorizontal() ? + glm::vec2(tabObj->GetSize().x / 2.f, 0) : + glm::vec2(0, tabObj->GetSize().y / 2.f); + } + + previewObject->SetGlobalPosition(tabObj->GetGlobalPosition() + tabWidthOffset); + } + } + + void TabArea::DrawTabPreview() + { + ssGUI::GUIObject* previewObject = + CurrentObjectsReferences.GetObjectReference(TabPreview); + + if(!previewObject->IsExtensionExist() || + !previewObject->IsExtensionExist()) + { + ssGUI_ERROR(ssGUI_EXT_TAG, + "Preview object does not have advanced size or advanced position"); + ssLOG_EXIT_PROGRAM(); + return; + } + + previewObject->SetEnabled(true); + + //Set preview parent + previewObject->SetParent(CurrentObjectsReferences.GetObjectReference(ContentsHolder)); + + //Enable advanced size and advanced position + previewObject->GetExtension()->SetEnabled(true); + previewObject->GetExtension()->SetEnabled(true); + } + + void TabArea::DiscardTabPreview() + { + CurrentObjectsReferences.GetObjectReference(TabPreview)->SetEnabled(false); + } + + void TabArea::ValidateTabsAndOrders() + { + if(!SanityCheck()) + { + ssGUI_ERROR(ssGUI_EXT_TAG, "Sanity check failed"); + ssLOG_EXIT_PROGRAM(); + return; + } + + ssGUI::GUIObject* tabsHolder = CurrentObjectsReferences.GetObjectReference(TabsHolder); + ssGUI::GUIObject* contentsHolder = + CurrentObjectsReferences.GetObjectReference(ContentsHolder); + + contentsHolder->StashChildrenIterator(); + tabsHolder->StashChildrenIterator(); + + contentsHolder->MoveChildrenIteratorToFirst(); + tabsHolder->MoveChildrenIteratorToFirst(); + + ssGUI::GUIObject* lastTab = nullptr; + ssGUI::GUIObject* lastChild = nullptr; + + //For each child + while(!contentsHolder->IsChildrenIteratorEnd()) + { + //Skip if it is not a tab content + if(!contentsHolder->GetCurrentChild() + ->IsExtensionExist()) + { + contentsHolder->MoveChildrenIteratorNext(); + continue; + } + + ssGUIObjectIndex currentChildIndex = + CurrentObjectsReferences.GetObjectIndex(contentsHolder->GetCurrentChild()); + + //If it is a tab content, but not registered + if(TabsInfos.find(currentChildIndex) == TabsInfos.end()) + { + //If this is the first child, add the current child as a tab content to the + //front + if(lastChild == nullptr) + lastTab = AddContentToFront(contentsHolder->GetCurrentChild()); + //Otherwise, add it as a tab content tab bar after the last tab + else + lastTab = AddContentAfter(contentsHolder->GetCurrentChild(), lastTab); + } + //If it is a tab content and registered + else + { + //Get the next valid tab + while(!tabsHolder->IsChildrenIteratorEnd() && + tabsHolder->GetCurrentChild()->GetType() != + ssGUI::Enums::GUIObjectType::TAB) + { + tabsHolder->MoveChildrenIteratorNext(); + } + + ssGUI::GUIObject* currentContentTab = + CurrentObjectsReferences + .GetObjectReference(TabsInfos.at(currentChildIndex)); + ssGUI::GUIObject* currentTab = tabsHolder->GetCurrentChild(); + + //If the tab we are currently referencing is invalid, create a new tab for this + //content + if(currentContentTab == nullptr) + { + currentContentTab = AddTab(currentChildIndex); + TabsInfos.at(currentChildIndex) = + CurrentObjectsReferences.AddObjectReference(currentContentTab); + } + //Otherwise, check if the tab is in the tab bar and parent the tab content if it + //is not + else if(currentContentTab->GetParent() != tabsHolder) + currentContentTab->SetParent(tabsHolder); + + //If we can't find the current tab, move the content tab to after the last tab + if(currentTab == nullptr || currentTab != currentContentTab) + { + //If lastTab is nullptr, that means this is the first tab, move the tab to + //first + if(lastTab == nullptr) + tabsHolder->MoveChildToFirst(currentContentTab); + //Otherwise, move the tab after the last tab + else + tabsHolder->MoveChildAfterTargetChild(currentContentTab, lastTab); + + ssGUI_ERROR(ssGUI_EXT_TAG, "Tab registered but not found"); + } + + lastTab = currentContentTab; + } + + lastChild = contentsHolder->GetCurrentChild(); + + contentsHolder->MoveChildrenIteratorNext(); + tabsHolder->MoveChildrenIteratorToChild(lastTab); + tabsHolder->MoveChildrenIteratorNext(); + } + + contentsHolder->PopChildrenIterator(); + tabsHolder->PopChildrenIterator(); + } + + ssGUI::GUIObject* TabArea::GetFirstLayoutChild(ssGUI::GUIObject* parent) + { + std::vector children = parent->GetListOfChildren(); + + for(int i = 0; i < children.size(); ++i) + { + if(!children.at(i)->HasTag(ssGUI::Tags::FLOATING) && + !children.at(i)->HasTag(ssGUI::Tags::OVERLAY) && children.at(i)->IsEnabled()) + { + return children.at(i); + } + } + + return nullptr; + } + + ssGUI::GUIObject* TabArea::GetLastLayoutChild(ssGUI::GUIObject* parent) + { + std::vector children = parent->GetListOfChildren(); + + for(int i = children.size(); i >= 0; --i) + { + if(!children.at(i)->HasTag(ssGUI::Tags::FLOATING) && + !children.at(i)->HasTag(ssGUI::Tags::OVERLAY) && children.at(i)->IsEnabled()) + { + return children.at(i); + } + } + + return nullptr; + } + + ssGUI::Tab* TabArea::AddTab(ssGUIObjectIndex contentId, ssGUI::Tab* tabToUse) + { + if(!SanityCheck()) + { + ssGUI_ERROR(ssGUI_EXT_TAG, "Sanity check failed"); + ssLOG_EXIT_PROGRAM(); + return nullptr; + } + + ssGUI::Tab* tab = nullptr; + + if(DefaultTabObject != nullptr) + tabToUse = DefaultTabObject; + + if(tabToUse == nullptr) + { + tab = + CurrentObjectsReferences.GetObjectReference(TabsHolder)->AddChild(); + tab->SetTabColor(NewTabColor); + tab->GetTabTitleObject()->SetText("Tab"); + } + else + { + if(tabToUse->IsEventCallbackExist(ssGUI::Enums::EventType::BUTTON_STATE_CHANGED) && + tabToUse->GetAssociatedContent() != nullptr && + tabToUse->GetAssociatedContent() + ->IsExtensionExist()) + { + ssGUI::GUIObject* previousTabArea = + tabToUse->GetAssociatedContent() + ->GetExtension() + ->GetTabAreaObject(); + + //Remove event callback from old tab area if it exists + if(previousTabArea != nullptr) + { + tabToUse->GetEventCallback(ssGUI::Enums::EventType::BUTTON_STATE_CHANGED) + ->RemoveEventListener(ListenerKey, previousTabArea); + } + } + + tab = tabToUse; + } + + ssGUIObjectIndex tabAreaContainerId = + tab->GetEventCallback(ssGUI::Enums::EventType::BUTTON_STATE_CHANGED) + ->AddObjectReference(Container); + + tab->GetEventCallback(ssGUI::Enums::EventType::BUTTON_STATE_CHANGED) + ->AddEventListener(ListenerKey, + Container, + [tabAreaContainerId](EventInfo& info) + { + //Set LastClickedTab to the current tab when it is clicked + ssGUI::Tab* currentTab = + static_cast(info.Container); + if(currentTab->GetButtonState() == + ssGUI::Enums::ButtonState::ON_CLICK) + { + ssGUI::GUIObject* tabAreaContainer = + info.References + ->GetObjectReference(tabAreaContainerId); + if(tabAreaContainer == nullptr || + !tabAreaContainer + ->IsExtensionExist()) + { + info.DeleteCurrentListener = true; + return; + } + + tabAreaContainer + ->GetExtension() + ->SetLastClickedTab(info.Container); + } + }); + + tab->SetUserCreated(false); + + if(tabToUse == nullptr) + { + if(IsTabBarHorizontal()) + tab->SetSize(glm::vec2(NewTabWidth, TabBarHeight)); + else + tab->SetSize(glm::vec2(TabBarHeight, NewTabWidth)); + } + + tab->SetAssociatedContent(CurrentObjectsReferences.GetObjectReference(contentId)); + ssGUIObjectIndex tabIndex = CurrentObjectsReferences.AddObjectReference(tab); + AddDeletionToCloseButton(tab); + return tab; + } + + void TabArea::UpdateOrientationsForContentsAndPreviews() + { + bool tabBarHorizontal = IsTabBarHorizontal(); + float minLength = 0; + + if(!SanityCheck()) + { + ssLOG_EXIT_PROGRAM(); + return; + } + + if(tabBarHorizontal) + { + CurrentObjectsReferences.GetObjectReference(TabsHolder) + ->SetMinSize(glm::vec2(25, TabBarHeight)); + CurrentObjectsReferences.GetObjectReference(TabsHolder) + ->SetMaxSize(glm::vec2(9999, TabBarHeight)); + } + else + { + CurrentObjectsReferences.GetObjectReference(TabsHolder) + ->SetMinSize(glm::vec2(TabBarHeight, 25)); + CurrentObjectsReferences.GetObjectReference(TabsHolder) + ->SetMaxSize(glm::vec2(TabBarHeight, 9999)); + } + } + + bool TabArea::TabbedIfNeeded(ssGUI::Backend::BackendSystemInputInterface* + inputInterface, + ssGUI::InputStatus& currentInputStatus, + ssGUI::InputStatus& lastInputStatus, + bool isMouseInsideTabBar, + glm::ivec2 mousePos) + { +#define DISCARD_AND_RETURN() \ + do \ + { \ + DiscardTabPreview(); \ + return true; \ + } \ + while(0) + + //Check if there's any external tabs being dragged + if(lastInputStatus.CurrentDragData.GetDragDataType() != + ssGUI::Enums::DragDataType::GUI_OBJECT || + lastInputStatus.CurrentDragData.IsIntercepted()) + { + return false; + } + + ssGUI::GUIObject* draggedObj = + lastInputStatus.CurrentDragData.GetDragData(); + + //Check if mouse input is being block by external object + if(currentInputStatus.MouseInputBlockedData.GetBlockDataType() == + ssGUI::Enums::BlockDataType::GUI_OBJECT && + !currentInputStatus.MouseInputBlockedData.GetBlockData() + ->HasParentRecursively(Container) && + !currentInputStatus.MouseInputBlockedData.GetBlockData() + ->HasParentRecursively(draggedObj)) + { + return false; + } + + //Check if it is possible to tab current dragged object to container + if(!draggedObj->IsExtensionExist() || + !CanContentBeTabbed(draggedObj, + draggedObj->GetExtension() + ->GetTopLevelParent())) + { + return false; + } + + //Check if the mouse is on any of the tabs + if(isMouseInsideTabBar) + { + auto hoverIt = GetMouseHoveredTab(mousePos); + + if(hoverIt != TabsInfos.end() && + CurrentObjectsReferences.GetObjectReference(hoverIt->first) != draggedObj && + CurrentObjectsReferences.GetObjectReference(hoverIt->first) != nullptr && + CurrentObjectsReferences.GetObjectReference(hoverIt->second) != nullptr) + { + //Check if it is mouse up + if(inputInterface->IsButtonOrKeyUp(ssGUI::Enums::MouseButton::LEFT)) + { + //If draggedObj parent has TabArea, call remove content + ssGUI::GUIObject* tabAreaObject = + draggedObj->GetExtension() + ->GetTabAreaObject(); + ssGUI::Tab* tabClone = nullptr; + if(tabAreaObject != nullptr && + tabAreaObject->IsExtensionExist()) + { + tabClone = tabAreaObject->GetExtension() + ->GetTabFromContent(draggedObj) + ->Clone(true); + + tabAreaObject->GetExtension() + ->RemoveContent(draggedObj); + } + + //Add it and intercept it + ssGUI::GUIObject* tabObj = + CurrentObjectsReferences.GetObjectReference(hoverIt->second); + + ssGUI::GUIObject* tabsHolder = + CurrentObjectsReferences.GetObjectReference(TabsHolder); + if(tabsHolder == nullptr || + !tabsHolder->IsExtensionExist()) + { + ssGUI_ERROR(ssGUI_EXT_TAG, "Invalid tabs holder"); + ssLOG_EXIT_PROGRAM(); + return false; + } + + bool addBefore = true; + addBefore = + IsTabBarHorizontal() ? + mousePos.x < + tabObj->GetGlobalPosition().x + tabObj->GetSize().x / 2.f : + mousePos.y < + tabObj->GetGlobalPosition().y + tabObj->GetSize().y / 2.f; + + addBefore = tabsHolder->GetExtension() + ->IsReverseOrder() ? + !addBefore : + addBefore; + + if(addBefore) + AddContentBefore(draggedObj, + CurrentObjectsReferences + .GetObjectReference(hoverIt->first), + tabClone); + else + AddContentAfter(draggedObj, + CurrentObjectsReferences + .GetObjectReference(hoverIt->first), + tabClone); + + lastInputStatus.CurrentDragData.SetInterceptor(Container); + DISCARD_AND_RETURN(); + } + //Otherwise we should show the preview + else + { + DrawTabPreview(hoverIt->second, mousePos); + return true; + } + } + } + //Otherwise if outside of tab bar + else + { + //If it is not inside container, return false + if(mousePos.x < Container->GetGlobalPosition().x || + mousePos.x >= Container->GetGlobalPosition().x + Container->GetSize().x || + mousePos.y < Container->GetGlobalPosition().y || + mousePos.y >= Container->GetGlobalPosition().y + Container->GetSize().y) + { + return false; + } + + //Check if the object being dragged is one of our contents, if so discard + if(IsTabContentExist(draggedObj)) + DISCARD_AND_RETURN(); + + //Check if it is mouse up + if(inputInterface->IsButtonOrKeyUp(ssGUI::Enums::MouseButton::LEFT)) + { + //If mouse up, add to end of tab bar + //If draggedObj parent has TabArea, call remove content + ssGUI::GUIObject* tabAreaObject = + draggedObj->GetExtension()->GetTabAreaObject(); + ssGUI::Tab* tabClone = nullptr; + if(tabAreaObject != nullptr && + tabAreaObject->IsExtensionExist()) + { + tabClone = tabAreaObject->GetExtension() + ->GetTabFromContent(draggedObj) + ->Clone(true); + + tabAreaObject->GetExtension() + ->RemoveContent(draggedObj); + } + + AddContent(draggedObj, tabClone); + lastInputStatus.CurrentDragData.SetInterceptor(Container); + DISCARD_AND_RETURN(); + } + //Otherwise show preview + else + { + DrawTabPreview(); + return true; + } + } + + return false; + } + + void TabArea::SetLastClickedTab(ssGUI::GUIObject* lastTab) + { + if(Container == nullptr) + return; + + LastClickedTab = CurrentObjectsReferences.GetObjectIndex(lastTab); + } + + bool TabArea::SanityCheck() const + { + if(Container == nullptr || ContentsHolder < 0 || TabsHolder < 0 || + CurrentObjectsReferences.GetObjectReference(ContentsHolder) == nullptr || + CurrentObjectsReferences.GetObjectReference(TabsHolder) == nullptr || + !CurrentObjectsReferences.GetObjectReference(ContentsHolder) + ->IsExtensionExist() || + !CurrentObjectsReferences.GetObjectReference(TabsHolder) + ->IsExtensionExist()) + { + ssGUI_ERROR(ssGUI_EXT_TAG, "Sanity check failed"); + return false; + } + + return true; + } + + bool TabArea::CanContentBeTabbed(ssGUI::GUIObject* contentToCheck, + ssGUI::GUIObject* contentTopLevelParent) const + { + //If top level parent exists for the content + if(contentTopLevelParent != nullptr) + { + //If top level parent is not this object or container's recursive parent, return + if(contentTopLevelParent != Container && + !Container->HasParentRecursively(contentTopLevelParent)) + { + return false; + } + } + //Otherwise, check if we are under the same main window + else + { + ssGUI::MainWindow* containerMainWindow = nullptr; + ssGUI::GUIObject* currentParent = Container; + + while(currentParent != nullptr && + currentParent->GetType() != ssGUI::Enums::GUIObjectType::MAIN_WINDOW) + { + currentParent = Container->GetParent(); + } + + if(currentParent != nullptr) + containerMainWindow = static_cast(currentParent); + else + { + //This should not happen, but if it does, exit + ssGUI_ERROR(ssGUI_EXT_TAG, "We are not under Main Window, this is not handled"); + ssLOG_EXIT_PROGRAM(); + return false; + } + + if(!containerMainWindow->HasChildRecursively(contentToCheck)) + return false; + } + + return true; + } + + void TabArea::AddDeletionToCloseButton(ssGUI::Tab* tab) + { + if(tab->GetCloseButton() == nullptr) + return; + + auto* ecb = tab->GetCloseButton() + ->AddEventCallback(ssGUI::Enums::EventType::BUTTON_STATE_CHANGED); + + ssGUIObjectIndex containerId = ecb->AddObjectReference(Container); + ssGUIObjectIndex tabId = ecb->AddObjectReference(tab); + ecb->AddEventListener(ListenerKey, + Container, + [containerId, tabId](ssGUI::EventInfo& info) + { + if(info.References->GetObjectReference(containerId) == + nullptr || + !info.References->GetObjectReference(containerId) + ->IsExtensionExist() || + info.References->GetObjectReference(tabId) == nullptr || + info.References->GetObjectReference(tabId) + ->GetAssociatedContent() == nullptr) + { + info.DeleteCurrentListener = true; + return; + } + + ssGUI::Button* closeButton = + static_cast(info.Container); + ssGUI::Tab* tab = + info.References->GetObjectReference(tabId); + ssGUI::GUIObject* tabContent = tab->GetAssociatedContent(); + + if(closeButton->GetButtonState() == + ssGUI::Enums::ButtonState::ON_CLICK) + { + info.References->GetObjectReference(containerId) + ->GetExtension() + ->RemoveContent(tabContent); + + tabContent->Delete(); + tab->Delete(); + info.DeleteCurrentListener = true; + } + }); + } + + void TabArea::ConstructRenderInfo() + { + } + + void TabArea::ConstructRenderInfo(ssGUI::Backend::BackendDrawingInterface* drawingInterface, + ssGUI::GUIObject* mainWindow, + glm::vec2 mainWindowPositionOffset) + { + } + + const std::string TabArea::EXTENSION_NAME = "Tab Area"; + const std::string TabArea::ListenerKey = "Tab Area"; + + void TabArea::SetOverrideTabPreviewSize(bool override, glm::vec2 size) + { + OverrideTabPreviewSize = override; + TabPreviewOverrideSize = size; + } + + bool TabArea::IsOverrideTabPreviewSize() const + { + return OverrideTabPreviewSize; + } + + glm::vec2 TabArea::GetOverrideTabPreviewSize() const + { + return TabPreviewOverrideSize; + } + + void TabArea::SetTabPreviewColor(glm::u8vec4 color) + { + PreviewColor = color; + } + + glm::u8vec4 TabArea::GetTabPreviewColor() const + { + return PreviewColor; + } + + void TabArea::SetDefaultPreviewColor(glm::u8vec4 color) + { + DefaultPreviewColor = color; + } + + glm::u8vec4 TabArea::GetDefaultPreviewColor() + { + return DefaultPreviewColor; + } + + ssGUI::GUIObject* TabArea::GetTabBar() const + { + if(!SanityCheck()) + return nullptr; + + return CurrentObjectsReferences.GetObjectReference(TabBar); + } + + void TabArea::SetTabBarHeight(float height) + { + TabBarHeight = height; + } + + float TabArea::GetTabBarHeight() const + { + return TabBarHeight; + } + + void TabArea::SetTabBarColor(glm::u8vec4 color) + { + if(!SanityCheck()) + return; + + CurrentObjectsReferences.GetObjectReference(TabBar)->SetBackgroundColor(color); + } + + glm::u8vec4 TabArea::GetTabBarColor() const + { + return CurrentObjectsReferences.GetObjectReference(TabBar)->GetBackgroundColor(); + } + + void TabArea::SetTabBarHorizontal(bool horizontal) + { + if(!SanityCheck()) + { + ssLOG_EXIT_PROGRAM(); + return; + } + + ssGUI::GUIObject* tabsHolder = CurrentObjectsReferences.GetObjectReference(TabsHolder); + ssGUI::GUIObject* contentsHolder = + CurrentObjectsReferences.GetObjectReference(ContentsHolder); + ssGUI::GUIObject* tabBar = CurrentObjectsReferences.GetObjectReference(TabBar); + + tabsHolder->GetExtension()->SetHorizontalLayout(horizontal); + tabBar->GetExtension()->SetHorizontalLayout(horizontal); + Container->GetExtension()->SetHorizontalLayout(!horizontal); + contentsHolder->GetExtension() + ->SetHorizontalLayout(!horizontal); + + UpdateOrientationsForContentsAndPreviews(); + } + + bool TabArea::IsTabBarHorizontal() const + { + if(!SanityCheck()) + { + ssLOG_EXIT_PROGRAM(); + return true; + } + + return CurrentObjectsReferences.GetObjectReference(TabsHolder) + ->GetExtension() + ->IsHorizontalLayout(); + } + + void TabArea::SetDefaultTabBarObject(ssGUI::GUIObject* defaultTabBar) + { + DefaultTabBarObject = defaultTabBar; + } + + ssGUI::GUIObject* TabArea::GetDefaultTabBarObject() + { + return DefaultTabBarObject; + } + + void TabArea::SetDefaultTabBarColor(glm::u8vec4 color) + { + DefaultTabBarColor = color; + } + + glm::u8vec4 TabArea::GetDefaultTabBarColor() + { + return DefaultTabBarColor; + } + + void TabArea::SetNewTabWidth(float width) + { + NewTabWidth = width; + } + + float TabArea::GetNewTabWidth() const + { + return NewTabWidth; + } + + void TabArea::SetReverseTabsOrder(bool reverse) + { + if(!SanityCheck()) + return; + + CurrentObjectsReferences.GetObjectReference(TabsHolder) + ->GetExtension() + ->SetReverseOrder(reverse); + } + + bool TabArea::IsReverseTabsOrder() const + { + if(!SanityCheck()) + return false; + + return CurrentObjectsReferences.GetObjectReference(TabsHolder) + ->GetExtension() + ->IsReverseOrder(); + } + + void TabArea::SetNewTabColor(glm::u8vec4 color) + { + NewTabColor = color; + } + + glm::u8vec4 TabArea::GetNewTabColor() const + { + return NewTabColor; + } + + void TabArea::SetDefaultTabObject(ssGUI::Tab* defaultTab) + { + DefaultTabObject = defaultTab; + } + + ssGUI::Tab* TabArea::GetDefaultTabObject() + { + return DefaultTabObject; + } + + void TabArea::SetDefaultTabColor(glm::u8vec4 color) + { + DefaultTabColor = color; + } + + glm::u8vec4 TabArea::GetDefaultTabColor() + { + return DefaultTabColor; + } + + ssGUI::Tab* TabArea::AddContent(ssGUI::GUIObject* contentToRegister, ssGUI::Tab* tabToUse) + { + if(!SanityCheck() || contentToRegister == nullptr) + return nullptr; + + //If the content is already registered and the tab is valid, return the tab + if(IsTabContentExist(contentToRegister) && + GetTabFromContent(contentToRegister) != nullptr) + return GetTabFromContent(contentToRegister); + + //Remove invalid tab if content is registered but tab is invalid + if(GetTabFromContent(contentToRegister) == nullptr) + RemoveContent(contentToRegister); + + //Add tabbable extension to content, it will do nothing if it already exist + auto* tabbableExt = contentToRegister->AddExtension(); + + //If the content's top level parent outside/below the container, abort + if(!CanContentBeTabbed(contentToRegister, tabbableExt->GetTopLevelParent())) + return nullptr; + + //Set content parent + ssGUI::GUIObject* contentsHolder = + CurrentObjectsReferences.GetObjectReference(ContentsHolder); + + ssGUIObjectIndex contentId = + CurrentObjectsReferences.AddObjectReference(contentToRegister); + ssGUI::Tab* tab = AddTab(contentId, tabToUse); + TabsInfos[contentId] = CurrentObjectsReferences.AddObjectReference(tab); + + contentToRegister->GetExtension() + ->SetTabAreaObject(Container); + contentToRegister->SetParent(contentsHolder); + + //Call event callback + ssGUI::TabEventInfo info; + info.Tab = tab; + info.TabAreaContainer = Container; + + if(Container->IsEventCallbackExist(ssGUI::Enums::EventType::NEW_TAB_CONTENT_ADDED)) + { + Container->GetEventCallback(ssGUI::Enums::EventType::NEW_TAB_CONTENT_ADDED) + ->Notify(contentToRegister, &info); + } + + //Switch to newly registered content + SwitchContent(contentToRegister); + + contentsHolder->GetExtension()->SetCoverFullLength(true); + return tab; + } + + ssGUI::Tab* TabArea::AddContentToFront(ssGUI::GUIObject* contentToRegister, + ssGUI::Tab* tabToUse) + { + ssGUI::Tab* tab = AddContent(contentToRegister, tabToUse); + MoveContentFront(contentToRegister); + return tab; + } + + ssGUI::Tab* TabArea::AddContentToEnd(ssGUI::GUIObject* contentToRegister, + ssGUI::Tab* tabToUse) + { + ssGUI::Tab* tab = AddContent(contentToRegister, tabToUse); + MoveContentEnd(contentToRegister); + return tab; + } + + ssGUI::Tab* TabArea::AddContentBefore(ssGUI::GUIObject* contentToRegister, + ssGUI::GUIObject* contentBefore, + ssGUI::Tab* tabToUse) + { + ssGUI::Tab* tab = AddContent(contentToRegister, tabToUse); + MoveContentBefore(contentToRegister, contentBefore); + return tab; + } + + ssGUI::Tab* TabArea::AddContentAfter(ssGUI::GUIObject* contentToRegister, + ssGUI::GUIObject* contentAfter, + ssGUI::Tab* tabToUse) + { + ssGUI::Tab* tab = AddContent(contentToRegister, tabToUse); + MoveContentAfter(contentToRegister, contentAfter); + return tab; + } + + void TabArea::RemoveContent(ssGUI::GUIObject* contentToRemove) + { + if(!SanityCheck()) + { + ssLOG_EXIT_PROGRAM(); + return; + } + + if(contentToRemove == nullptr || + !contentToRemove->IsExtensionExist()) + return; + + ssGUIObjectIndex contentIndex = + CurrentObjectsReferences.GetObjectIndex(contentToRemove); + + if(contentIndex < 0) + return; + + auto foundIt = TabsInfos.find(contentIndex); + + if(foundIt == TabsInfos.end()) + return; + + contentToRemove->GetExtension()->SetTabAreaObject(nullptr); + + ssGUI::GUIObject* tabObj = CurrentObjectsReferences.GetObjectReference(foundIt->second); + + if(tabObj != nullptr) + tabObj->Delete(); + + TabsInfos.erase(foundIt); + + if(TabsInfos.empty()) + { + ssGUI::GUIObject* contentsHolder = + CurrentObjectsReferences.GetObjectReference(ContentsHolder); + contentsHolder->GetExtension() + ->SetCoverFullLength(false); + } + } + + ssGUI::Tab* TabArea::GetTabFromContent(ssGUI::GUIObject* registeredContent) const + { + if(Container == nullptr || registeredContent == nullptr) + return nullptr; + + ssGUIObjectIndex contentIndex = + CurrentObjectsReferences.GetObjectIndex(registeredContent); + + if(contentIndex < 0) + return nullptr; + + auto foundIt = TabsInfos.find(contentIndex); + + if(foundIt == TabsInfos.end()) + return nullptr; + + return CurrentObjectsReferences.GetObjectReference(foundIt->second); + } + + ssGUI::GUIObject* TabArea::GetTabContentsHolder() const + { + return CurrentObjectsReferences.GetObjectReference(ContentsHolder); + } + + void TabArea::MoveContentBefore(ssGUI::GUIObject* contentToMove, + ssGUI::GUIObject* contentBefore) + { + if(!SanityCheck()) + { + ssLOG_EXIT_PROGRAM(); + return; + } + + if(contentToMove == nullptr || contentBefore == nullptr) + return; + + if(!IsTabContentExist(contentToMove) || !IsTabContentExist(contentBefore)) + return; + + ssGUI::GUIObject* tabToMove = GetTabFromContent(contentToMove); + ssGUI::GUIObject* tabBefore = GetTabFromContent(contentBefore); + + if(tabToMove == nullptr || tabBefore == nullptr) + { + ssGUI_ERROR(ssGUI_EXT_TAG, "Tab object not found"); + return; + } + + ssGUI::GUIObject* contentsHolder = + CurrentObjectsReferences.GetObjectReference(ContentsHolder); + ssGUI::GUIObject* tabsHolder = CurrentObjectsReferences.GetObjectReference(TabsHolder); + + contentsHolder->MoveChildBeforeTargetChild(contentToMove, contentBefore); + tabsHolder->MoveChildBeforeTargetChild(tabToMove, tabBefore); + } + + void TabArea::MoveContentFront(ssGUI::GUIObject* contentToMove) + { + if(!SanityCheck()) + { + ssLOG_EXIT_PROGRAM(); + return; + } + + if(contentToMove == nullptr || !IsTabContentExist(contentToMove)) + return; + + ssGUI::GUIObject* contentsHolder = + CurrentObjectsReferences.GetObjectReference(ContentsHolder); + ssGUI::GUIObject* tabsHolder = CurrentObjectsReferences.GetObjectReference(TabsHolder); + + contentsHolder->MoveChildToFirst(contentToMove); + + ssGUI::GUIObject* tabToMove = GetTabFromContent(contentToMove); + + if(tabToMove == nullptr) + { + ssGUI_ERROR(ssGUI_EXT_TAG, "Tab object not found"); + return; + } + + tabsHolder->MoveChildToFirst(tabToMove); + } + + void TabArea::MoveContentEnd(ssGUI::GUIObject* contentToMove) + { + if(!SanityCheck()) + { + ssLOG_EXIT_PROGRAM(); + return; + } + + if(contentToMove == nullptr || !IsTabContentExist(contentToMove)) + return; + + ssGUI::GUIObject* contentsHolder = + CurrentObjectsReferences.GetObjectReference(ContentsHolder); + ssGUI::GUIObject* tabsHolder = CurrentObjectsReferences.GetObjectReference(TabsHolder); + + contentsHolder->MoveChildToLast(contentToMove); + + ssGUI::GUIObject* tabToMove = GetTabFromContent(contentToMove); + + if(tabToMove == nullptr) + { + ssGUI_ERROR(ssGUI_EXT_TAG, "Tab object not found"); + return; + } + + tabsHolder->MoveChildToLast(tabToMove); + } + + void TabArea::MoveContentAfter(ssGUI::GUIObject* contentToMove, + ssGUI::GUIObject* contentAfter) + { + if(!SanityCheck()) + { + ssLOG_EXIT_PROGRAM(); + return; + } + + if(contentToMove == nullptr || contentAfter == nullptr || + !IsTabContentExist(contentToMove) || !IsTabContentExist(contentAfter)) + { + return; + } + + ssGUI::GUIObject* tabToMove = GetTabFromContent(contentToMove); + ssGUI::GUIObject* tabAfter = GetTabFromContent(contentAfter); + + if(tabToMove == nullptr || tabAfter == nullptr) + { + ssGUI_ERROR(ssGUI_EXT_TAG, "Tab object not found"); + return; + } + + ssGUI::GUIObject* contentsHolder = + CurrentObjectsReferences.GetObjectReference(ContentsHolder); + ssGUI::GUIObject* tabsHolder = CurrentObjectsReferences.GetObjectReference(TabsHolder); + + contentsHolder->MoveChildAfterTargetChild(contentToMove, contentAfter); + tabsHolder->MoveChildAfterTargetChild(tabToMove, tabAfter); + } + + void TabArea::SwitchContent(ssGUI::GUIObject* registeredContent) + { + if(Container == nullptr) + return; + + ssGUIObjectIndex contentId = -1; + ssGUIObjectIndex originalContentId = CurrentTabContent; + + auto foundIt = TabsInfos.end(); + + if(registeredContent != nullptr) + { + contentId = CurrentObjectsReferences.GetObjectIndex(registeredContent); + + if(contentId < 0) + return; + + foundIt = TabsInfos.find(contentId); + + if(foundIt == TabsInfos.end()) + return; + } + + CurrentTabContent = contentId; + + //Disable the rest of the tabs + for(auto currentIt = TabsInfos.begin(); currentIt != TabsInfos.end(); ++currentIt) + { + ssGUI::GUIObject* contentObj = + CurrentObjectsReferences.GetObjectReference(currentIt->first); + + if(contentObj == nullptr) + continue; + + contentObj->SetEnabled(false); + + ssGUI::Tab* curTab = + CurrentObjectsReferences.GetObjectReference(currentIt->second); + if(curTab != nullptr) + curTab->SelectTab(false); + } + + //If we switch to no content (i.e. registeredContent == nullptr), trigger event callback + //and return here + ssGUI::TabEventInfo info; + info.Tab = nullptr; + info.TabAreaContainer = Container; + + if(contentId < 0) + { + if(Container->IsEventCallbackExist(ssGUI::Enums::EventType::TAB_CONTENT_SWITCHED)) + { + Container->GetEventCallback(ssGUI::Enums::EventType::TAB_CONTENT_SWITCHED) + ->Notify(registeredContent, &info); + } + + return; + } + + //Enable and switch to the tab we want to switch to + registeredContent->SetEnabled(true); + + if(GetCurrentTab() != nullptr) + GetCurrentTab()->SelectTab(true); + + //Trigger event callback + info.Tab = GetCurrentTab(); + info.TabAreaContainer = Container; + + if(Container->IsEventCallbackExist(ssGUI::Enums::EventType::TAB_CONTENT_SWITCHED)) + { + Container->GetEventCallback(ssGUI::Enums::EventType::TAB_CONTENT_SWITCHED) + ->Notify(registeredContent, &info); + } + + if(registeredContent + ->IsEventCallbackExist(ssGUI::Enums::EventType::TAB_CONTENT_SWITCHED)) + { + registeredContent->GetEventCallback(ssGUI::Enums::EventType::TAB_CONTENT_SWITCHED) + ->Notify(registeredContent, &info); + } + + if(originalContentId >= 0) + { + ssGUI::GUIObject* originalContentObj = + CurrentObjectsReferences.GetObjectReference(originalContentId); + + if(originalContentId != CurrentTabContent && originalContentObj != nullptr && + originalContentObj + ->IsEventCallbackExist(ssGUI::Enums::EventType::TAB_CONTENT_SWITCHED)) + { + originalContentObj + ->GetEventCallback(ssGUI::Enums::EventType::TAB_CONTENT_SWITCHED) + ->Notify(registeredContent, &info); + } + } + } + + ssGUI::Tab* TabArea::GetCurrentTab() const + { + if(CurrentTabContent < 0) + return nullptr; + + auto tabIt = TabsInfos.find(CurrentTabContent); + + if(tabIt == TabsInfos.end()) + return nullptr; + + return CurrentObjectsReferences.GetObjectReference(tabIt->second); + } + + ssGUI::GUIObject* TabArea::GetCurrentTabContent() const + { + if(CurrentTabContent < 0) + return nullptr; + + auto tabIt = TabsInfos.find(CurrentTabContent); + + if(tabIt == TabsInfos.end()) + return nullptr; + + return CurrentObjectsReferences.GetObjectReference(tabIt->first); + } + + bool TabArea::IsEnabled() const + { + return Enabled; + } + + void TabArea::SetEnabled(bool enabled) + { + Enabled = enabled; + } + +#undef DISCARD_AND_RETURN +#define DISCARD_AND_RETURN() \ + do \ + { \ + DiscardTabPreview(); \ + return; \ + } \ + while(0) + + void TabArea::Internal_Update(bool isPreUpdate, + ssGUI::Backend::BackendSystemInputInterface* inputInterface, + ssGUI::InputStatus& currentInputStatus, + ssGUI::InputStatus& lastInputStatus, + ssGUI::GUIObject* mainWindow) + { + ssGUI_LOG_FUNC(); + + //This is function is executed twice, one before the Container GUI object update and one + //after. You can use the isPreUpdate variable to decide when to execute the extension + //update + if(isPreUpdate || Container == nullptr || Container->GetParent() == nullptr) + return; + + //Need to perform clean up if this is disabled. For example delete any objects created + //by this extension + if(!Enabled) + DISCARD_AND_RETURN(); + + //Perform order check + ValidateTabsAndOrders(); + + if(!SanityCheck()) + return; + +#if 0 + ssLOG_LINE("Container: " << Container); + ssLOG_LINE("Container->GetMinSize(): " << Container->GetMinSize()); + ssLOG_LINE("Container->GetMaxSize(): " << Container->GetMaxSize()); + ssLOG_LINE( "Container->GetExtension()->IsCoverFullLength(): " << + Container->GetExtension()->IsCoverFullLength()); + ssLOG_LINE( "Container->GetExtension()->IsHorizontalLayout(): " << + Container->GetExtension()->IsHorizontalLayout()); + + + ssGUI::GUIObject* contentsHolderObj = CurrentObjectsReferences.GetObjectReference(ContentsHolder); + + ssLOG_LINE("contentsHolderObj: " << contentsHolderObj); + ssLOG_LINE("contentsHolderObj->GetMinSize(): " << contentsHolderObj->GetMinSize()); + ssLOG_LINE("contentsHolderObj->GetMaxSize(): " << contentsHolderObj->GetMaxSize()); + ssLOG_LINE( "contentsHolderObj->GetExtension()->IsCoverFullLength(): " << + contentsHolderObj->GetExtension()->IsCoverFullLength()); + ssLOG_LINE( "contentsHolderObj->GetExtension()->IsHorizontalLayout(): " << + contentsHolderObj->GetExtension()->IsHorizontalLayout()); +#endif + + //Check if the mouse is on any of the tabs + auto* mainWindowBackend = + static_cast(mainWindow)->GetBackendWindowInterface(); + + glm::ivec2 mousePos = inputInterface->GetCurrentMousePosition(mainWindowBackend); + + auto* tabBar = CurrentObjectsReferences.GetObjectReference(TabBar); + if(tabBar == nullptr) + { + ssGUI_ERROR(ssGUI_EXT_TAG, "Tab bar missing"); + DISCARD_AND_RETURN(); + } + + glm::vec2 tabBarMaxBound = tabBar->GetGlobalPosition() + tabBar->GetSize(); + + bool isMouseInsideTabBar = + mousePos.x >= tabBar->GetGlobalPosition().x && mousePos.x < tabBarMaxBound.x && + mousePos.y >= tabBar->GetGlobalPosition().y && mousePos.y < tabBarMaxBound.y; + + //Check if the mouse is dragging + ssGUI::Enums::MouseButton leftMouseButton = ssGUI::Enums::MouseButton::LEFT; + + bool mouseInput = + inputInterface->IsButtonOrKeyPressExistCurrentFrame(leftMouseButton) || + inputInterface->IsButtonOrKeyPressExistLastFrame(leftMouseButton); + + //Check if we are dragging one of our tab content + ssGUI::DragData& lastDragData = lastInputStatus.CurrentDragData; + + bool ourTabBeingDragged = + lastDragData.GetDragDataType() == ssGUI::Enums::DragDataType::GUI_OBJECT && + !lastDragData.IsIntercepted() && + IsTabContentExist(lastDragData.GetDragData()); + + if(ourTabBeingDragged && mouseInput) + { + //If so, continue setting it as being dragged and set the cursor + currentInputStatus.CurrentDragData + .SetDragData(lastInputStatus.CurrentDragData.GetDragData()); + + inputInterface->SetCursorType(ssGUI::Enums::CursorType::MOVE); + } + + //Check if any of our tabs are being tabbed by another tab + if(TabbedIfNeeded(inputInterface, + currentInputStatus, + lastInputStatus, + isMouseInsideTabBar, + mousePos)) + { + return; + } + + //If left mouse button up for our tab for last frame and it is not tabbed against + //anything, + // untab it if allowed + auto hoverIt = GetMouseHoveredTab(mousePos); + + //Tab is still being counted as dragged when mouse up (no mouse input for that frame) + //Therefore, if the tab is being dragged last frame and + //there's no mouse input last frame, + //that means it was mouse button up last frame for the tab. + if(!mouseInput && ourTabBeingDragged && hoverIt == TabsInfos.end()) + { + ssGUI::GUIObject* draggedContent = lastDragData.GetDragData(); + + if(draggedContent->IsExtensionExist() && + !draggedContent->GetExtension()->IsUntabbable()) + { + //Remove the content and position it on the mouse + RemoveContent(draggedContent); + ssGUI::GUIObject* topLevelParent = + lastDragData.GetDragData() + ->GetExtension() + ->GetTopLevelParent(); + + glm::vec2 mouseDownPos = + lastDragData.GetDragData() + ->GetExtension() + ->GetLastMouseDragBeginPosition(); + + glm::vec2 mouseWindowPosDelta = + draggedContent->GetGlobalPosition() - mouseDownPos; + + if(topLevelParent == nullptr) + draggedContent->SetParent(mainWindow); + else + draggedContent->SetParent(topLevelParent); + + draggedContent->SetGlobalPosition(glm::vec2(mousePos) + mouseWindowPosDelta); + + //Trigger event callbacks + ssGUI::TabEventInfo info; + info.Tab = nullptr; + info.TabAreaContainer = Container; + if(Container + ->IsEventCallbackExist(ssGUI::Enums::EventType::TAB_CONTENT_UNTABBED)) + { + Container->GetEventCallback(ssGUI::Enums::EventType::TAB_CONTENT_UNTABBED) + ->Notify(draggedContent, &info); + } + + if(draggedContent + ->IsEventCallbackExist(ssGUI::Enums::EventType::TAB_CONTENT_UNTABBED)) + { + draggedContent + ->GetEventCallback(ssGUI::Enums::EventType::TAB_CONTENT_UNTABBED) + ->Notify(draggedContent, &info); + } + + DISCARD_AND_RETURN(); + } + } + + if(LastClickedTab < 0) + DISCARD_AND_RETURN(); + + //Check last clicked tab if it is being clicked, if so switch to the content + ssGUI::Tab* lastClickedTab = + CurrentObjectsReferences.GetObjectReference(LastClickedTab); + + if(lastClickedTab == nullptr || + lastClickedTab->GetButtonState() != ssGUI::Enums::ButtonState::ON_CLICK) + DISCARD_AND_RETURN(); + + ssGUI::GUIObject* content = lastClickedTab->GetAssociatedContent(); + auto contentIt = TabsInfos.find(CurrentObjectsReferences.GetObjectIndex(content)); + + if(contentIt == TabsInfos.end() || + CurrentObjectsReferences.GetObjectReference(contentIt->first) == nullptr) + DISCARD_AND_RETURN(); + + ssGUI::GUIObject* contentObj = + CurrentObjectsReferences.GetObjectReference(contentIt->first); + SwitchContent(contentObj); + + //Set the drag object + currentInputStatus.CurrentDragData.SetDragData(contentObj); + + //If content is window, get and tabbable extension and set last mouse drag begin + //position + if(contentObj->GetType() == ssGUI::Enums::GUIObjectType::WINDOW && + contentObj->IsExtensionExist()) + { + static_cast(contentObj) + ->GetExtension() + ->SetLastMouseDragBeginPosition(mousePos); + } + + //Update content size + CurrentObjectsReferences.GetObjectReference(ContentsHolder) + ->GetExtension() + ->ForceUpdateLayout(inputInterface, + currentInputStatus, + lastInputStatus, + mainWindow); + + DISCARD_AND_RETURN(); + } + + void TabArea::Internal_Draw(bool isPreRender, + ssGUI::Backend::BackendDrawingInterface* drawingInterface, + ssGUI::GUIObject* mainWindow, + glm::vec2 mainWindowPositionOffset) + { + } + + std::string TabArea::GetExtensionName() const + { + return EXTENSION_NAME; + } + + void TabArea::BindToObject(ssGUI::GUIObject* bindObj) + { + Container = bindObj; + + Initialize(); + } + + void TabArea::Copy(const ssGUI::Extensions::Extension* extension) + { + if(extension->GetExtensionName() != EXTENSION_NAME) + return; + + auto* original = static_cast(extension); + + Enabled = original->Enabled; + TabBarHeight = original->TabBarHeight; + NewTabWidth = original->NewTabWidth; + CurrentObjectsReferences = original->CurrentObjectsReferences; + TabBar = original->TabBar; + CurrentTabContent = original->CurrentTabContent; + LastClickedTab = -1; + ContentsHolder = original->ContentsHolder; + TabsHolder = original->TabsHolder; + TabsInfos = original->TabsInfos; + TabPreview = original->TabPreview; + PreviewColor = original->PreviewColor; + OverrideTabPreviewSize = original->OverrideTabPreviewSize; + TabPreviewOverrideSize = original->TabPreviewOverrideSize; + NewTabColor = original->NewTabColor; + } + + ObjectsReferences* TabArea::Internal_GetObjectsReferences() + { + return &CurrentObjectsReferences; + } + + TabArea* TabArea::Clone() + { + TabArea* temp = new TabArea(*this); + return temp; + } + } + +} \ No newline at end of file diff --git a/Src/ssGUI/Extensions/Tabbable.cpp b/Src/ssGUI/Extensions/Tabbable.cpp new file mode 100644 index 00000000..567a8bda --- /dev/null +++ b/Src/ssGUI/Extensions/Tabbable.cpp @@ -0,0 +1,198 @@ +#include "ssGUI/Extensions/Tabbable.hpp" +#include "ssGUI/GUIObjectClasses/GUIObject.hpp" +#include "ssGUI/GUIObjectClasses/MainWindow.hpp" + +namespace ssGUI +{ + namespace Extensions + { + Tabbable::Tabbable() : + Container(nullptr), + Enabled(true), + TabAreaObject(-1), + CurrentObjectsReferences(), + TopLevelParent(-1), + Untabbable(false), + LastMouseDragBeginPosition() + { + } + + Tabbable::~Tabbable() + { + } + + Tabbable::Tabbable(const Tabbable& other) + { + Container = nullptr; + Copy(&other); + } + + void Tabbable::ConstructRenderInfo() + { + } + + void Tabbable::ConstructRenderInfo(ssGUI::Backend::BackendDrawingInterface* + drawingInterface, + ssGUI::GUIObject* mainWindow, + glm::vec2 mainWindowPositionOffset) + { + } + + const std::string Tabbable::EXTENSION_NAME = "Tabbable"; + + void Tabbable::SetTopLevelParent(ssGUI::GUIObject* topLevelParent) + { + if(topLevelParent == nullptr) + { + TopLevelParent = -1; + return; + } + + TopLevelParent = CurrentObjectsReferences.AddObjectReference(topLevelParent); + } + + ssGUI::GUIObject* Tabbable::GetTopLevelParent() const + { + if(TopLevelParent < 0) + return nullptr; + + return CurrentObjectsReferences.GetObjectReference(TopLevelParent); + } + + void Tabbable::SetUntabbable(bool untabbable) + { + Untabbable = untabbable; + } + + bool Tabbable::IsUntabbable() const + { + return Untabbable; + } + + glm::vec2 Tabbable::GetLastMouseDragBeginPosition() const + { + return LastMouseDragBeginPosition; + } + + void Tabbable::SetLastMouseDragBeginPosition(glm::vec2 pos) + { + LastMouseDragBeginPosition = pos; + } + + ssGUI::GUIObject* Tabbable::GetTabAreaObject() const + { + if(TabAreaObject < 0) + return nullptr; + + return CurrentObjectsReferences.GetObjectReference(TabAreaObject); + } + + void Tabbable::SetTabAreaObject(ssGUI::GUIObject* tabArea) + { + if(tabArea == nullptr) + { + TabAreaObject = -1; + return; + } + + TabAreaObject = CurrentObjectsReferences.AddObjectReference(tabArea); + } + + bool Tabbable::IsEnabled() const + { + return Enabled; + } + + void Tabbable::SetEnabled(bool enabled) + { + Enabled = enabled; + } + + void Tabbable::Internal_Update(bool isPreUpdate, + ssGUI::Backend::BackendSystemInputInterface* inputInterface, + ssGUI::InputStatus& currentInputStatus, + ssGUI::InputStatus& lastInputStatus, + ssGUI::GUIObject* mainWindow) + { + ssGUI_LOG_FUNC(); + +#define DISCARD_AND_RETURN() \ + do \ + { \ + return; \ + } \ + while(0) + + //This is function is executed twice, one before the Container GUI object update and one + //after. You can use the isPreUpdate variable to decide when to execute the extension + //update + if(isPreUpdate || Container == nullptr || Container->GetParent() == nullptr || !Enabled) + return; + + if(Container->GetType() != ssGUI::Enums::GUIObjectType::WINDOW) + DISCARD_AND_RETURN(); + + { + auto* window = static_cast(Container); + + if(window->GetWindowDragState() == ssGUI::Enums::WindowDragState::STARTED) + { + auto* mainWindowBackend = + static_cast(mainWindow)->GetBackendWindowInterface(); + LastMouseDragBeginPosition = + inputInterface->GetCurrentMousePosition(mainWindowBackend); + } + + //If the window is being dragged / finished being dragged, set the input status + if(window->GetWindowDragState() == ssGUI::Enums::WindowDragState::DRAGGING || + window->GetWindowDragState() == ssGUI::Enums::WindowDragState::ENDED) + { + currentInputStatus.CurrentDragData.SetDragData(Container); + } + } + } + + void Tabbable::Internal_Draw(bool isPreRender, + ssGUI::Backend::BackendDrawingInterface* drawingInterface, + ssGUI::GUIObject* mainWindow, + glm::vec2 mainWindowPositionOffset) + { + } + + std::string Tabbable::GetExtensionName() const + { + return EXTENSION_NAME; + } + + void Tabbable::BindToObject(ssGUI::GUIObject* bindObj) + { + Container = bindObj; + } + + void Tabbable::Copy(const ssGUI::Extensions::Extension* extension) + { + if(extension->GetExtensionName() != EXTENSION_NAME) + return; + + auto* original = static_cast(extension); + Enabled = original->IsEnabled(); + TabAreaObject = original->TabAreaObject; + CurrentObjectsReferences = original->CurrentObjectsReferences; + TopLevelParent = original->TopLevelParent; + Untabbable = original->Untabbable; + LastMouseDragBeginPosition = original->LastMouseDragBeginPosition; + } + + ObjectsReferences* Tabbable::Internal_GetObjectsReferences() + { + return &CurrentObjectsReferences; + } + + Tabbable* Tabbable::Clone() + { + Tabbable* temp = new Tabbable(*this); + return temp; + } + } + +} \ No newline at end of file diff --git a/Src/ssGUI/GUIObjectClasses/CompositeClasses/CMakeLists.txt b/Src/ssGUI/GUIObjectClasses/CompositeClasses/CMakeLists.txt index 94584904..50b69945 100644 --- a/Src/ssGUI/GUIObjectClasses/CompositeClasses/CMakeLists.txt +++ b/Src/ssGUI/GUIObjectClasses/CompositeClasses/CMakeLists.txt @@ -8,4 +8,5 @@ target_sources(ssGUI PRIVATE "${CMAKE_CURRENT_LIST_DIR}/ImageCanvas.cpp" "${CMAKE_CURRENT_LIST_DIR}/StandardSlider.cpp" "${CMAKE_CURRENT_LIST_DIR}/StandardCheckbox.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Tab.cpp" ) \ No newline at end of file diff --git a/Src/ssGUI/GUIObjectClasses/CompositeClasses/Tab.cpp b/Src/ssGUI/GUIObjectClasses/CompositeClasses/Tab.cpp new file mode 100644 index 00000000..55d4f230 --- /dev/null +++ b/Src/ssGUI/GUIObjectClasses/CompositeClasses/Tab.cpp @@ -0,0 +1,372 @@ +#include "ssGUI/GUIObjectClasses/CompositeClasses/Tab.hpp" +#include "ssGUI/Extensions/AdvancedPosition.hpp" +#include "ssGUI/Extensions/BoxShadow.hpp" +#include "ssGUI/Extensions/Layout.hpp" +#include "ssGUI/Extensions/Outline.hpp" +#include "ssGUI/Extensions/RoundedCorners.hpp" +#include "ssGUI/Extensions/Shape.hpp" + +namespace ssGUI +{ + glm::vec2 Tab::DefaultCloseButtonSize = glm::vec2(15, 15); + + Tab::Tab(const Tab& other) : StandardButton(other) + { + TabColor = other.TabColor; + SelectedColor = other.SelectedColor; + UnderlineColor = other.UnderlineColor; + UnderlineThickness = other.UnderlineThickness; + UnderlineDirection = other.UnderlineDirection; + CloseButton = other.CloseButton; + CloseButtonShapeIds[0] = other.CloseButtonShapeIds[0]; + CloseButtonShapeIds[1] = other.CloseButtonShapeIds[1]; + ContentObject = other.ContentObject; + Selected = other.Selected; + } + + void Tab::SetCloseSymbolColor(glm::u8vec4 color) + { + ssGUI::GUIObject* closeButton = CurrentObjectsReferences.GetObjectReference(CloseButton); + + if(closeButton == nullptr) + return; + + auto* closeButtonShape = closeButton->GetExtension(); + + if(closeButtonShape == nullptr) + return; + + std::vector* colorVec = + closeButtonShape->GetAdditionalShapeColorsWithID(CloseButtonShapeIds[0]); + + for(int i = 0; i < colorVec->size(); ++i) + colorVec->at(i) = color; + + colorVec = closeButtonShape->GetAdditionalShapeColorsWithID(CloseButtonShapeIds[1]); + for(int i = 0; i < colorVec->size(); ++i) + colorVec->at(i) = color; + } + + void Tab::InitializeCloseButton() + { + //Create Close button + auto* closeButton = AddChildWithWrapper(true); + closeButton->AddExtension(); + closeButton->GetParent()->SetMaxSize(glm::vec2(DefaultCloseButtonSize.x * 1.5f, + closeButton->GetParent()->GetMaxSize().y)); + + closeButton->SetMinSize(DefaultCloseButtonSize); + closeButton->SetMaxSize(DefaultCloseButtonSize); + + ssGUI::EventCallback* ecb = + closeButton->GetEventCallback(ssGUI::Enums::EventType::BUTTON_STATE_CHANGED); + + ecb->ClearEventListeners(); + closeButton->RemoveExtension(); + + closeButton->SetButtonColor(glm::u8vec4(255, 255, 255, 0)); + + auto* closeSymbolShape = closeButton->AddExtension(); + CloseButtonShapeIds[0] = closeSymbolShape->AddAdditionalLine(); + CloseButtonShapeIds[1] = closeSymbolShape->AddAdditionalLine(); + glm::u8vec4 closeSymbolColor = GetAdaptiveTextColor(); + + closeSymbolShape->SetAdditionalLine(CloseButtonShapeIds[0], + glm::vec2(5.f, 5.f), + glm::vec2(10.f, 10.f), + 2, + 2, + closeSymbolColor, + closeSymbolColor, + false); + + closeSymbolShape->SetAdditionalLine(CloseButtonShapeIds[1], + glm::vec2(10.f, 5.f), + glm::vec2(5.f, 10.f), + 2, + 2, + closeSymbolColor, + closeSymbolColor, + false); + + ssGUIObjectIndex tabId = ecb->AddObjectReference(this); + ecb->AddEventListener(ListenerKey, + this, + [tabId](ssGUI::EventInfo& info) + { + ssGUI::Tab* tab = + info.References->GetObjectReference(tabId); + + if(tab == nullptr) + { + info.DeleteCurrentListener = true; + return; + } + + ssGUI::Button* closeButton = + static_cast(info.Container); + glm::u8vec4 closeButtonColor = closeButton->GetButtonColor(); + glm::u8vec4 closeSymbolColor = tab->GetAdaptiveTextColor(); + + static_assert((int)ssGUI::Enums::ButtonState::COUNT == 6, + "Make sure this is updated"); + switch(closeButton->GetButtonState()) + { + case ssGUI::Enums::ButtonState::NORMAL: + closeButtonColor.a = 0; + break; + case ssGUI::Enums::ButtonState::HOVER: + closeButtonColor.a = 100; + break; + case ssGUI::Enums::ButtonState::ON_CLICK: + case ssGUI::Enums::ButtonState::CLICKING: + closeButtonColor.a = 50; + break; + case ssGUI::Enums::ButtonState::CLICKED: + case ssGUI::Enums::ButtonState::NOT_INTERACTABLE: + closeButtonColor.a = 0; + break; + } + + closeButton->SetBackgroundColor(closeButtonColor); + tab->SetCloseSymbolColor(closeSymbolColor); + }); + + closeButton->NotifyButtonEventCallbackManually(); + CloseButton = CurrentObjectsReferences.AddObjectReference(closeButton); + } + + void Tab::MainLogic(ssGUI::Backend::BackendSystemInputInterface* inputInterface, + ssGUI::InputStatus& currentInputStatus, + ssGUI::InputStatus& lastInputStatus, + ssGUI::GUIObject* mainWindow) + { + StandardButton::MainLogic(inputInterface, currentInputStatus, lastInputStatus, mainWindow); + + ssGUI::Button* closeButton = + (ssGUI::Button*)CurrentObjectsReferences.GetObjectReference(CloseButton); + + if(currentInputStatus.MouseInputBlockedData.GetBlockDataType() == + ssGUI::Enums::BlockDataType::GUI_OBJECT && + currentInputStatus.MouseInputBlockedData.GetBlockData() == closeButton) + { + SetButtonState(ssGUI::Enums::ButtonState::HOVER); + } + } + + const std::string Tab::ListenerKey = "Tab"; + + Tab::Tab() : + StandardButton(), + TabColor(100, 100, 100, 255), + SelectedColor(127, 127, 127, 255), + UnderlineColor(255, 255, 255, 127), + UnderlineThickness(3), + UnderlineDirection(ssGUI::Enums::Direction::BOTTOM), + CloseButton(-1), + CloseButtonShapeIds{-1, -1}, + ContentObject(-1), + Selected(false) + { + GetExtension()->ClearAllPreferredSizeMultiplier(); + RemoveExtension(); + RemoveExtension(); + auto* boxShadow = GetExtension(); + boxShadow->SetSizeOffset(glm::vec2(boxShadow->GetSizeOffset().x, 0)); + + GetButtonTextObject()->SetNewTextFontSize(14); + + InitializeCloseButton(); + } + + Tab::~Tab() + { + } + + void Tab::SetAssociatedContent(ssGUI::GUIObject* content) + { + ContentObject = CurrentObjectsReferences.AddObjectReference(content); + } + + ssGUI::GUIObject* Tab::GetAssociatedContent() const + { + if(ContentObject < 0) + return nullptr; + + return CurrentObjectsReferences.GetObjectReference(ContentObject); + } + + void Tab::SetTabColor(glm::u8vec4 color) + { + TabColor = color; + if(!Selected) + SetButtonColor(TabColor); + + glm::u8vec4 closeButtonColor = GetAdaptiveTextColor(); + SetCloseSymbolColor(closeButtonColor); + } + + glm::u8vec4 Tab::GetTabColor() const + { + return TabColor; + } + + void Tab::SetSelectedColor(glm::u8vec4 color) + { + SelectedColor = color; + if(Selected) + SetButtonColor(SelectedColor); + + glm::u8vec4 closeButtonColor = GetAdaptiveTextColor(); + SetCloseSymbolColor(closeButtonColor); + } + + glm::u8vec4 Tab::GetSelectedColor() const + { + return SelectedColor; + } + + void Tab::SetUnderlineColor(glm::u8vec4 color) + { + UnderlineColor = color; + } + + glm::u8vec4 Tab::GetUnderlineColor() const + { + return UnderlineColor; + } + + void Tab::SetUnderlineDirection(ssGUI::Enums::Direction direction) + { + UnderlineDirection = direction; + } + + ssGUI::Enums::Direction Tab::GetUnderlineDirection() const + { + return UnderlineDirection; + } + + void Tab::SetUnderlineThickness(float thickness) + { + UnderlineThickness = thickness; + } + + float Tab::GetUnderlineThickness() const + { + return UnderlineThickness; + } + + void Tab::SetCloseButton(ssGUI::Button* button) + { + ssGUI::GUIObject* closeButtonObj = nullptr; + + if(CloseButton >= 0) + closeButtonObj = CurrentObjectsReferences.GetObjectReference(CloseButton); + + if(button == closeButtonObj) + return; + + if(closeButtonObj != nullptr) + closeButtonObj->Delete(); + + if(button == nullptr) + { + CloseButton = -1; + return; + } + + button->SetParent(this); + CloseButton = CurrentObjectsReferences.AddObjectReference(button); + } + + ssGUI::Button* Tab::GetCloseButton() const + { + if(CloseButton < 0) + return nullptr; + + return (ssGUI::Button*)CurrentObjectsReferences.GetObjectReference(CloseButton); + } + + void Tab::SelectTab(bool select) + { + Selected = select; + + if(Selected) + SetButtonColor(SelectedColor); + else + SetButtonColor(TabColor); + + glm::u8vec4 closeButtonColor = GetAdaptiveTextColor(); + SetCloseSymbolColor(closeButtonColor); + + //Add border extension if not exists + auto* border = AddExtension(); + + border->SetInnerBorder(true); + + border->ShowBorderTop(false); + border->ShowBorderRight(false); + border->ShowBorderLeft(false); + border->ShowBorderBottom(false); + + if(select) + { + if(UnderlineDirection == ssGUI::Enums::Direction::TOP) + border->ShowBorderTop(true); + else if(UnderlineDirection == ssGUI::Enums::Direction::RIGHT) + border->ShowBorderRight(true); + else if(UnderlineDirection == ssGUI::Enums::Direction::BOTTOM) + border->ShowBorderBottom(true); + else if(UnderlineDirection == ssGUI::Enums::Direction::LEFT) + border->ShowBorderLeft(true); + } + + border->SetBorderColor(UnderlineColor); + border->SetBorderWidth(UnderlineThickness); + } + + bool Tab::IsTabSelected() const + { + return Selected; + } + + void Tab::SetTabIcon(ssGUI::Image* icon) + { + StandardButton::SetButtonIconObject(icon); + } + + ssGUI::Image* Tab::GetTabIcon() const + { + return StandardButton::GetButtonIconObject(); + } + + void Tab::SetTabTitleObject(ssGUI::Text* title) + { + StandardButton::SetButtonTextObject(title); + } + + ssGUI::Text* Tab::GetTabTitleObject() const + { + return StandardButton::GetButtonTextObject(); + } + + ssGUI::Enums::GUIObjectType Tab::GetType() const + { + return ssGUI::Enums::GUIObjectType::TAB | Button::GetType(); + } + + Tab* Tab::Clone(bool cloneChildren) + { + ssGUI_LOG_FUNC(); + Tab* temp = new Tab(*this); + CloneExtensionsAndEventCallbacks(temp); + + if(cloneChildren) + { + if(CloneChildren(this, temp) == nullptr) + return nullptr; + } + + return temp; + } +} \ No newline at end of file