diff --git a/include/JUI/Base/Widget.hpp b/include/JUI/Base/Widget.hpp index c035de7..fa99fcd 100644 --- a/include/JUI/Base/Widget.hpp +++ b/include/JUI/Base/Widget.hpp @@ -129,6 +129,8 @@ namespace JUI { [[nodiscard]] virtual Vector2 GetAbsolutePosition() const; [[nodiscard]] virtual float GetAbsoluteRotation() const; + [[nodiscard]] virtual Vector2 GetAbsoluteCentroid() const; + /// Returns the parent of this widget, or a nullptr if there is no parent. /// @see GetFamilyTreeRoot(). Widget* GetParent() const; diff --git a/include/JUI/Mixins/Focusable.hpp b/include/JUI/Mixins/Focusable.hpp new file mode 100644 index 0000000..9e024e3 --- /dev/null +++ b/include/JUI/Mixins/Focusable.hpp @@ -0,0 +1,62 @@ +/// Josh's User Interface Library +/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets. +/// Developed and Maintained by Josh O'Leary @ Redacted Software. +/// Special Thanks to William Tomasine II and Maxine Hayes. +/// (c) 2024 Redacted Software +/// This work is dedicated to the public domain. + +/// @file Resizable.hpp +/// @desc Resizable Mixin Helper class +/// @edit 2024-10-11 + +#pragma once + +#include +#include + +namespace JUI +{ + /// A mixin helper class that enables a widget to be resized by the mouse. + class Focusable { + public: + Event<> OnGrabFocus; + Event<> OnDropFocus; + + [[nodiscard]] bool Focused() const; + + [[nodiscard]] bool IsFocused() const { return focused;} + [[nodiscard]] bool HasFocus() const { return focused;} + virtual void GrabFocus() { SetFocused(true);} + virtual void DropFocus(const Vector2& point) { + if (do_focus_cooldown_hack && focus_hack_cooldown > time_focused) + return; + + SetFocused(false); + } + virtual void SetFocused(bool value) { + focused = value; + if (do_focus_cooldown_hack && value) time_focused = 0; + } + + virtual void Update(float delta) { + if (do_focus_cooldown_hack) + time_focused += delta; + } + + void DoFocusHackCooldown(bool value) { do_focus_cooldown_hack = value; } + bool DoFocusHackCooldown() const { return do_focus_cooldown_hack; } + + float FocusHackCooldown() const { return focus_hack_cooldown; } + void FocusHackCooldown(float value) { + focus_hack_cooldown = value; + } + + protected: + bool focused = false; + float time_focused = 0; + + bool do_focus_cooldown_hack = false; + float focus_hack_cooldown = 0.1f; + }; +} + diff --git a/include/JUI/UDim2.hpp b/include/JUI/UDim2.hpp index 0dba31f..4da1760 100644 --- a/include/JUI/UDim2.hpp +++ b/include/JUI/UDim2.hpp @@ -17,7 +17,6 @@ namespace JUI { - using namespace J3ML::LinearAlgebra; /// Funky Coordinate system purpose-built for screen positioning @@ -26,6 +25,17 @@ namespace JUI /// @see UDim.hpp class UDim2 { + public: // Constants + static const UDim2 Center; + static const UDim2 TopLeft; + static const UDim2 BottomRight; + static const UDim2 BottomLeft; + static const UDim2 TopRight; + + static const UDim2 TopCenter; + static const UDim2 LeftCenter; + static const UDim2 BottomCenter; + static const UDim2 RightCenter; public: // Properties UDim X; UDim Y; diff --git a/include/JUI/Widgets/Anchor.hpp b/include/JUI/Widgets/Anchor.hpp new file mode 100644 index 0000000..9beeeea --- /dev/null +++ b/include/JUI/Widgets/Anchor.hpp @@ -0,0 +1,88 @@ +#pragma once +#include + + +namespace JUI { + + /// An invisible, point-like widget which is used to attach decorator-type widgets at a given point. + /// For example: The CurveSegment Widget uses two Anchors for it's endpoints. + class Anchor : public Widget { + public: + Anchor() : Widget() { + + } + explicit Anchor(Widget* parent) : Anchor() { + Parent(parent); + }; + explicit Anchor(Widget* parent, const UDim2& position) : Anchor(parent) { + Position(position); + Size({0_px, 0_px}); + } + + void Update(float elapsed) override { + + } + + void Draw() override { + Color4 debug_color = Colors::Red; + float debug_size = 4; + + J2D::FillCircle(debug_color, GetAbsolutePosition(), debug_size, 5); + } + + Vector2 Point() { return GetAbsolutePosition(); } + protected: + private: + }; + + class Decorator : public Widget { + public: + Decorator() : Widget(){ + + } + explicit Decorator(Widget* parent) : Decorator() { + Parent(parent); + } + + }; + + class ArrowDecorator : public Decorator { + public: + explicit ArrowDecorator(Widget* parent, Anchor* a, Anchor* b) : Decorator() { + Parent(parent); + origin = a; + goal = b; + } + + + void Draw() override { + using namespace J3ML; + Color4 line_color = Colors::Green; + float line_size = 4; + + J2D::DrawLine(line_color, origin->Point(), goal->Point(), line_size); + + Vector2 lookback_dir = goal->Point() - origin->Point(); + + Rotation angle = lookback_dir.AimedAngle(); + + angle += 10_degrees; + + Vector2 fan_vector_left = (Math::Cos(angle), Math::Sin(angle)); + + Vector2 tri_polys[3] = { + origin->Point(), + + }; + + J2D::FillTriangle(line_color); + + //J2D::FillTriangle(); + } + protected: + Anchor* origin; + Anchor* goal; + private: + + }; +} diff --git a/include/JUI/Widgets/FileDialog.hpp b/include/JUI/Widgets/FileDialog.hpp index 734c335..ee686dc 100644 --- a/include/JUI/Widgets/FileDialog.hpp +++ b/include/JUI/Widgets/FileDialog.hpp @@ -41,7 +41,6 @@ namespace JUI auto* rect = new JUI::TextRect(file_layout); rect->Size({100_percent, 20_px}); - std::string entry_type = entry.is_directory() ? "directory" : "file"; std::string perms = stringify(entry.status().permissions()); std::string type = stringify(entry.status().type()); diff --git a/main.cpp b/main.cpp index e82b16d..9ab26b6 100644 --- a/main.cpp +++ b/main.cpp @@ -34,6 +34,7 @@ #include #include +#include "JUI/Widgets/Anchor.hpp" #include "JUI/Widgets/ColorPicker.hpp" #include "JUI/Widgets/FileDialog.hpp" #include "JUI/Widgets/FpsGraph.hpp" @@ -160,7 +161,9 @@ JUI::UtilityBar* CreateUtilityBar(JUI::Widget* root) { demos->AddButton("Scroll Widget Demo", [&] {}); - demos->AddButton("Command Line", [&]{ console->Toggle();}); + demos->AddButton("Command Line", [&] { + console->Toggle(); + }); demos->AddButton("File Dialog", [&] {CreateFileDialog();}); @@ -176,6 +179,13 @@ JUI::UtilityBar* CreateUtilityBar(JUI::Widget* root) { berries->AddButton("Blueberries"); } } + + auto* ptA = new JUI::Anchor(topbar, {10_px, 20_px}); + + auto* ptB = new JUI::Anchor(demos, UDim2::Center); + + auto* line = new JUI::ArrowDecorator(topbar, ptA, ptB); + } auto* edit = topbar->AddButton("Edit"); @@ -552,18 +562,6 @@ JUI::Scene* CreateScene() { auto* widget_list = CreateWidgetList(root); - //auto* horizontal = new HorizontalListLayout(root); - //horizontal->ZIndex(1); - - - - //auto* separator_a = new Rect(radio_btn_set_layout()); - - - - //nineslice_demo_window->Padding(1_px); - // End Window // - root->SetViewportSize({800, 600}); diff --git a/src/JUI/Base/Widget.cpp b/src/JUI/Base/Widget.cpp index e51be31..e9237f9 100644 --- a/src/JUI/Base/Widget.cpp +++ b/src/JUI/Base/Widget.cpp @@ -147,6 +147,10 @@ float Widget::GetAbsoluteRotation() const { return rotation; } +Vector2 Widget::GetAbsoluteCentroid() const { + return GetAbsolutePosition() + (GetAbsoluteSize() / 2.f); +} + std::vector Widget::GetChildren() { return children; } std::vector Widget::GetDescendants() { diff --git a/src/JUI/Mixins/Focusable.cpp b/src/JUI/Mixins/Focusable.cpp new file mode 100644 index 0000000..b0ab1ca --- /dev/null +++ b/src/JUI/Mixins/Focusable.cpp @@ -0,0 +1,3 @@ +#include + +bool JUI::Focusable::Focused() const { return focused; } diff --git a/src/JUI/UDim2.cpp b/src/JUI/UDim2.cpp index 0224201..6c84412 100644 --- a/src/JUI/UDim2.cpp +++ b/src/JUI/UDim2.cpp @@ -1,5 +1,18 @@ #include +const JUI::UDim2 JUI::UDim2::Center {50_percent, 50_percent}; + +const JUI::UDim2 JUI::UDim2::TopLeft {0_percent, 0_percent}; +const JUI::UDim2 JUI::UDim2::BottomRight {100_percent, 100_percent}; +const JUI::UDim2 JUI::UDim2::BottomLeft {0_percent, 100_percent}; +const JUI::UDim2 JUI::UDim2::TopRight {100_percent, 0_percent}; + +const JUI::UDim2 JUI::UDim2::TopCenter {50_percent, 0_percent}; +const JUI::UDim2 JUI::UDim2::LeftCenter {0_percent, 50_percent}; +const JUI::UDim2 JUI::UDim2::BottomCenter {50_percent, 100_percent}; + +const JUI::UDim2 JUI::UDim2::RightCenter {100_percent, 50_percent}; + JUI::UDim2::UDim2() { //X = {0, 0.f}; //Y = {0, 0.f};