Awesome collaborative changes.

This commit is contained in:
2025-02-10 21:07:31 -05:00
parent 24e38b9ec3
commit 883eabb5e3
13 changed files with 702 additions and 477 deletions

View File

@@ -61,9 +61,8 @@ namespace JUI {
/// This event triggers right before this widget gets deallocated.
Event<Widget *> Destroying;
public:
Tween* TweenPositionTo(const UDim2& goal, TweenInfo params = {});
Tween* TweenSizeTo(const UDim2& goal, TweenInfo params = {});
Tween* TweenPosition(const UDim2& goal, TweenInfo params = {});
Tween* TweenSize(const UDim2& goal, TweenInfo params = {});
@@ -142,6 +141,9 @@ namespace JUI {
void AnchorPoint(const Vector2 &point);
Tween* TweenAnchorPoint(const Vector2& goal, TweenInfo info = {});
#pragma region Padding
/// Returns the padding factor on the left of this widget.
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @see PaddingLeft(), class UDim.
@@ -159,6 +161,8 @@ namespace JUI {
/// @see PaddingBottom(), class UDim.
[[nodiscard]] UDim PaddingBottom() const;
/// Sets the padding factor on the left of this widget.
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @see PaddingLeft(), class UDim.
@@ -175,6 +179,7 @@ namespace JUI {
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @see PaddingLeft(), class UDim.
void PaddingBottom(const UDim &pad_bottom);
/// Sets the padding factor on the four respective sides of this widget.
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @param left
@@ -187,6 +192,16 @@ namespace JUI {
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
void Padding(const UDim& padding);
Tween* TweenPaddingLeft(const UDim &goal, TweenInfo info = {});
Tween* TweenPaddingRight(const UDim &goal, TweenInfo info = {});
Tween* TweenPaddingTop(const UDim &goal, TweenInfo info = {});
Tween* TweenPaddingBottom(const UDim &goal, TweenInfo info = {});
Tween* TweenPadding(const UDim& goalLeft, const UDim& goalTop, const UDim& goalRight, const UDim& goalBottom, TweenInfo info = {});
Tween* TweenPadding(const UDim& goal, TweenInfo info = {});
#pragma endregion
#pragma region Margin
/// Returns the margin factor on the left of this widget.
[[nodiscard]] UDim MarginLeft() const;
/// Returns the margin factor on the top of this widget.
@@ -220,6 +235,16 @@ namespace JUI {
/// @see Margin(const UDim&, const UDim&, const UDim&, const UDim&).
void Margin(const UDim& margin);
Tween* TweenMarginLeft(const UDim &goal, TweenInfo info = {});
Tween* TweenMarginTop(const UDim &goal, TweenInfo info = {});
Tween* TweenMarginBottom(const UDim &goal, TweenInfo info = {});
Tween* TweenMarginRight(const UDim &goal, TweenInfo info = {});
Tween* TweenMargin(const UDim& goalLeft, const UDim& goalTop, const UDim& goalRight, const UDim& goalBottom, TweenInfo info = {});
Tween* TweenMargin(const UDim& goal, TweenInfo info = {});
#pragma endregion
/// Returns this widgets mnemonic name.
/// Widgets can optionally be assigned a name that can be used to retrieve it from a widget tree node.
/// @see Name().

View File

@@ -90,9 +90,7 @@ namespace JUI
/// A class that represents an animation-in-action.
class Tween {
public:
Tween(TweenTickFunc tick_func) {
this->tick_func = tick_func;
}
Tween(TweenTickFunc tick_func);
Tween(TweenTickFunc tick_func, TweenInfo info) {
this->tick_func = tick_func;

View File

@@ -13,92 +13,44 @@
#include <JUI/Base/Widget.hpp>
#include <JUI/Widgets/Rect.hpp>
#include "JUI/Mixins/Clickable.hpp"
#include "JUI/Mixins/Hoverable.hpp"
#include "Button.hpp"
#include "JUI/Base/ImageBase.hpp"
#include <JUI/Mixins/Clickable.hpp>
#include <JUI/Mixins/Hoverable.hpp>
#include <JUI/Widgets/Button.hpp>
#include <JUI/Base/ImageBase.hpp>
#include <JUI/Widgets/ImageButton.hpp>
namespace JUI
{
class Checkbox : public Button {
// TODO: Find a nice way to implement a checkmark.
class CheckboxBase {
public:
Checkbox() : Button() { }
explicit Checkbox(Widget *parent) : Checkbox()
{
this->Parent(parent);
}
void Update(float delta) override
{
Button::Update(delta);
}
void OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &bnt, bool still_hovering) override
{
Button::OnRelease(mouse_pos, bnt, still_hovering);
checked = !checked;
}
void InnerDraw() override
{
Rect::InnerDraw();
if (checked)
{
//J2D::Begin();
Vector2 check_padding = {2, 2};
RectBase::Draw(check_color, check_color, GetAbsolutePosition()+check_padding, GetAbsoluteSize()-(check_padding*2));
//J2D::End();
}
}
void Draw() override
{
if (!visible)
return;
//J2D::Begin();
Vector2 abs_pos = GetAbsolutePosition();
Vector2 abs_size = GetAbsoluteSize();
auto root_size = GetFamilyTreeRoot()->GetAbsoluteSize();
GLint *old_scissor_bounds;
bool clip_was_enabled;
if (clips_descendants) {
clip_was_enabled = glIsEnabled(GL_SCISSOR_TEST);
if (clip_was_enabled)
glGetIntegerv(GL_SCISSOR_BOX, old_scissor_bounds);
float presumed_screen_height = 600;
glScissor(abs_pos.x, presumed_screen_height-abs_size.y-abs_pos.y, abs_size.x, abs_size.y);
glEnable(GL_SCISSOR_TEST);
}
RectBase::Draw(abs_pos, abs_size);
// Draw Child Elements with scissor clipping still active
Widget::Draw();
// Returns clip to previous state
if (clips_descendants)
{
//glScissor(old_scissor_bounds[0], old_scissor_bounds[1], old_scissor_bounds[2], old_scissor_bounds[3]);
if (!clip_was_enabled) {}
glDisable(GL_SCISSOR_TEST);
}
//J2D::End();
}
[[nodiscard]] Color4 CheckedColor() const { return check_color; }
void CheckedColor(const Color4& color) { check_color = color; }
bool IsChecked() const { return checked;}
void SetChecked(bool value) { checked = value; }
Tween* TweenCheckedColor(const Color4& goal, TweenInfo info = {});
protected:
bool checked;
Color4 check_color = Colors::Red;
};
class Checkbox : public Button, public CheckboxBase {
public:
Checkbox();
explicit Checkbox(Widget *parent);
void Update(float delta) override;
void OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &bnt, bool still_hovering) override;
void InnerDraw() override;
protected:
private:
};
class ImageCheckbox : public ImageButton, public CheckboxBase {
public:
protected:
private:
};
}

View File

@@ -1,3 +1,5 @@
#pragma once
#include <JUI/Base/ImageBase.hpp>
#include <JUI/Widgets/Button.hpp>
@@ -10,8 +12,8 @@ public:
void Update(float delta) override;
void Draw() override;
public:
ImageButton() : ImageBase(), Button() {}
explicit ImageButton(Widget* parent) : ImageButton() { Parent(parent); }
ImageButton();
explicit ImageButton(Widget* parent);
public:
~ImageButton() override = default;
};

View File

@@ -18,8 +18,12 @@
namespace JUI
{
class ImageRect: public Rect, public ImageBase
{
class ImageRect: public Rect, public ImageBase {
public:
ImageRect();
explicit ImageRect(Widget* parent);
void Update(float delta) override;
void Draw() override;
};
}

View File

@@ -15,6 +15,11 @@ namespace JUI {
class ScrollingRect;
}
// TODO: Fix direction of ScrollBar itself
// TODO: MouseWheel scroll, and ScrollBar clickable
// TODO: Clamp range of ScrollBar
/// A Rectangle Widget which has a larger renderable area than the visible area.
/// This allows user-controlled scrolling of the viewable content.
class JUI::ScrollingRect : public Rect {
@@ -45,8 +50,7 @@ public:
void CanvasSize(const Vector2i& new_size);
void InnerDraw() override;
void Draw() override;
void Update(float delta) override
{
void Update(float delta) override {
//scroll += delta*5;
//canvas->Resize(Vector2i(GetAbsoluteSize().x, GetAbsoluteSize().y));
Rect::Update(delta);

View File

@@ -79,48 +79,98 @@ JUI::Scene* CreateScene() {
//auto* horizontal = new HorizontalListLayout(root);
//horizontal->ZIndex(1);
auto* sizer_1 = new Rect(root);
//sizer_1->ZIndex(4);
sizer_1->Size({0,-24,0.2f, 1.f});
sizer_1->Position({0, 24, 0, 0});
sizer_1->BGColor(Colors::Grays::Gainsboro);
auto* column_rect = new Rect(root);
//column_rect->ZIndex(4);
column_rect->Size({0, -24, 0.2f, 1.f});
column_rect->Position({0, 24, 0, 0});
column_rect->BGColor(Colors::Grays::Gainsboro);
auto* s1_vert = new VerticalListLayout(sizer_1);
auto* column_layout = new VerticalListLayout(column_rect);
auto* button = new TextButton(s1_vert);
auto* btn_container1 = new Rect(column_layout);
btn_container1->Size({100_percent, 24_px});
auto* btn_h_layout1 = new HorizontalListLayout(btn_container1);
btn_h_layout1->Padding({2_px});
auto* button = new TextButton(btn_h_layout1);
//button->Position({5, 105, 0, 0});
button->Size({0, 35, 1, 0});
button->Size({0, 20, 0.32f, 0});
button->SetTextColor(Colors::Black);
button->SetContent("Button");
button->AlignLeft();
button->Padding(5_px);
//button->Padding(5_px);
auto* tt2 = new JUI::Tooltip(button);
tt2->SetContent("Test 123");
auto* button2 = new TextButton(s1_vert);
auto* button2 = new TextButton(btn_h_layout1);
//button2->Position({5, 105, 0, 0});
button2->Size({0, 35, 1, 0});
button2->Size({0, 20, 0.32f, 0});
button2->SetTextColor(Colors::Black);
button2->SetContent("Button");
button2->AlignCenterHorizontally();
auto* button3 = new TextButton(s1_vert);
auto* button3 = new TextButton(btn_h_layout1);
//button2->Position({5, 105, 0, 0});
button3->Size({0, 35, 1, 0});
button3->Size({0, 20, 0.32f, 0});
button3->SetTextColor(Colors::Black);
button3->SetContent("Button");
button3->AlignRight();
auto* checkbox_container = new Rect(s1_vert);
checkbox_container->Size({0, 35, 1, 0});
auto* btn_container2 = new Rect(column_layout);
btn_container2->Size({100_percent, 24_px});
auto* btn_h_layout2 = new HorizontalListLayout(btn_container2);
btn_h_layout2->Padding({2_px});
auto* button4 = new TextButton(btn_h_layout2);
//button->Position({5, 105, 0, 0});
button4->Size({0, 20, 0.32f, 0});
button4->SetTextColor(Colors::Black);
button4->SetContent("Button");
button4->AlignLeft();
//button4->CornerRounding(4);
auto* button5 = new TextButton(btn_h_layout2);
//button2->Position({5, 105, 0, 0});
button5->Size({0, 20, 0.32f, 0});
button5->SetTextColor(Colors::Black);
button5->SetContent("Button");
button5->AlignCenterHorizontally();
//button5->CornerRounding(4);
auto* button6 = new TextButton(btn_h_layout2);
//button2->Position({5, 105, 0, 0});
button6->Size({0, 20, 0.32f, 0});
button6->SetTextColor(Colors::Black);
button6->SetContent("Button");
button6->AlignRight();
//button6->CornerRounding(4);
auto* checkbox_container = new Rect(column_layout);
checkbox_container->Size({0, 24, 1, 0});
auto* checkbox_horiz = new HorizontalListLayout(checkbox_container);
checkbox_horiz->Padding(2_px);
auto* label = new TextRect(checkbox_horiz);
label->SetContent("Checkboxes");
label->AutoFitSizeToText(true);
auto* check1 = new Checkbox(checkbox_horiz);
check1->Size({20_px, 20_px});
auto* check2 = new Checkbox(checkbox_horiz);
check2->Size({20_px, 20_px});
check2->CheckedColor(Colors::Blue);
check2->CornerRounding(7);
auto* check3 = new Checkbox(checkbox_horiz);
check3->Size({20_px, 20_px});
check3->CheckedColor(Colors::Oranges::Coral);
check3->CornerRounding(7);
auto* input_form = new TextInputForm(s1_vert);
auto* input_form = new TextInputForm(column_layout);
input_form->Size({0,30, 1, 0});
input_form->SetContent("");
input_form->SetTextSize(14);
@@ -130,7 +180,7 @@ JUI::Scene* CreateScene() {
other_window->Size({30_percent, 25_percent});
other_window->SetTitle("Another Window");
Tween* t = other_window->TweenPositionTo({50_percent, 50_percent}, {.time = 5});
Tween* t = other_window->TweenPosition({50_percent, 50_percent}, {.time = 5});
t->Completed += [] () { std::cout << "This Dick" << std::endl; };

View File

@@ -2,180 +2,169 @@
#include <jlog/Logger.hpp>
#include <J3ML/Geometry/AABB2D.hpp>
namespace JUI {
using namespace JUI;
Widget::Widget() {
name = "Widget";
children = std::vector<Widget *>();
}
Widget::Widget() {
name = "Widget";
children = std::vector<Widget *>();
}
Widget::Widget(Widget* parent) : Widget() {
this->Parent(parent);
}
Widget::Widget(Widget* parent) : Widget()
{
this->Parent(parent);
}
void Widget::Parent(Widget* newParent) {
// hold a reference to this so it doesn't get collected as we're working.
Widget* oldParent = this->parent;
void Widget::Parent(Widget* newParent) {
// hold a reference to this so it doesn't get collected as we're working.
Widget* oldParent = this->parent;
// New parent is old parent, do nothing, maybe raise a warning.
if (newParent == oldParent)
return;
// New parent is old parent, do nothing, maybe raise a warning.
if (newParent == oldParent)
return;
// Don't allow for an instance to be parented to itself
if (this == newParent)
throw std::runtime_error("Cannot parent a widget to itself.");
// You're trying to break the linearity of the object hierarchy by creating a circle in the graph.
if (this->IsAncestorOf(newParent))
throw std::runtime_error("Cannot create circular reference.");
// Don't allow for an instance to be parented to itself
if (this == newParent)
throw std::runtime_error("Cannot parent a widget to itself.");
// You're trying to break the linearity of the object hierarchy by creating a circle in the graph.
if (this->IsAncestorOf(newParent))
throw std::runtime_error("Cannot create circular reference.");
for (Widget* ancestor: this->GetAncestors()) {
if (oldParent && !ancestor->IsAncestorOf(newParent) && newParent != ancestor) {
ancestor->DescendantRemoved(this);
for (Widget* descendant : this->GetDescendants()) {
ancestor->DescendantRemoved(descendant);
}
}
}
// Remove ourselves from our parent (if we have one)
if (this->parent) {
// this->parent->ChildRemoved(this)
std::erase(this->parent->children, this);
}
// Update our old parent to the new one
this->parent = newParent;
// If our parent is set to nullptr, we can't update it's vector of children
if (!newParent) return;
// Add ourselves to our new parent's list of children
newParent->children.emplace_back(this);
//newParent->ChildAdded(this);
for (Widget* ancestor : this->GetAncestors()) {
if (!oldParent || (!oldParent->IsDescendantOf(newParent) && oldParent != ancestor)) {
// Don't fire unless an instance is actually a new descendant
ancestor->DescendantAdded(this);
for (Widget* descendant: this->GetDescendants()) {
ancestor->DescendantAdded(descendant);
}
}
for (Widget* ancestor: this->GetAncestors()) {
if (oldParent && !ancestor->IsAncestorOf(newParent) && newParent != ancestor) {
ancestor->DescendantRemoved(this);
for (Widget* descendant : this->GetDescendants())
ancestor->DescendantRemoved(descendant);
}
}
bool Widget::IsDescendantOf(Widget* ancestor) {
if (ancestor == nullptr) return false;
// Remove ourselves from our parent (if we have one)
if (this->parent)
std::erase(this->parent->children, this);
Widget* instance = this;
while (instance->GetParent()) {
instance = instance->GetParent();
if (instance == ancestor)
return true;
// Update our old parent to the new one
this->parent = newParent;
// If our parent is set to nullptr, we can't update it's vector of children
if (!newParent) return;
// Add ourselves to our new parent's list of children
newParent->children.emplace_back(this);
//newParent->ChildAdded(this);
for (Widget* ancestor : this->GetAncestors()) {
if (!oldParent || (!oldParent->IsDescendantOf(newParent) && oldParent != ancestor)) {
// Don't fire unless an instance is actually a new descendant
ancestor->DescendantAdded(this);
for (Widget* descendant: this->GetDescendants())
ancestor->DescendantAdded(descendant);
}
return false;
}
}
bool Widget::IsAncestorOf(Widget* descendant) {
if (descendant == nullptr) return false;
Widget* instance = descendant;
while (instance->GetParent()) {
instance = instance->GetParent();
if (instance == this)
return true;
}
return false;
bool Widget::IsDescendantOf(Widget* ancestor) {
if (ancestor == nullptr) return false;
Widget* instance = this;
while (instance->GetParent()) {
instance = instance->GetParent();
if (instance == ancestor)
return true;
}
return false;
}
Vector2 Widget::AnchorPoint() const {
return anchor_point;
bool Widget::IsAncestorOf(Widget* descendant) {
if (descendant == nullptr) return false;
Widget* instance = descendant;
while (instance->GetParent()) {
instance = instance->GetParent();
if (instance == this)
return true;
}
return false;
}
UDim Widget::PaddingLeft() const { return pad_left;}
UDim Widget::PaddingTop() const { return pad_top;}
UDim Widget::PaddingRight() const { return pad_right;}
UDim Widget::PaddingBottom() const { return pad_bottom;}
Vector2 Widget::AnchorPoint() const {
return anchor_point;
}
void Widget::PaddingLeft (const UDim& pad_left) { this->pad_left = pad_left; }
void Widget::PaddingTop (const UDim& pad_top) { this->pad_top = pad_top; }
void Widget::PaddingRight (const UDim& pad_right) { this->pad_right = pad_right; }
void Widget::PaddingBottom(const UDim& pad_bottom) { this->pad_bottom = pad_bottom; }
UDim Widget::PaddingLeft() const { return pad_left;}
UDim Widget::PaddingTop() const { return pad_top;}
UDim Widget::PaddingRight() const { return pad_right;}
UDim Widget::PaddingBottom() const { return pad_bottom;}
void Widget::Padding(const UDim &left, const UDim &top, const UDim &right, const UDim &bottom) {
PaddingLeft(left);
PaddingTop(top);
PaddingRight(right);
PaddingBottom(bottom);
void Widget::PaddingLeft (const UDim& value) { this->pad_left = value; }
void Widget::PaddingTop (const UDim& pad_top) { this->pad_top = pad_top; }
void Widget::PaddingRight (const UDim& pad_right) { this->pad_right = pad_right; }
void Widget::PaddingBottom(const UDim& pad_bottom) { this->pad_bottom = pad_bottom; }
void Widget::Padding(const UDim &left, const UDim &top, const UDim &right, const UDim &bottom) {
PaddingLeft(left);
PaddingTop(top);
PaddingRight(right);
PaddingBottom(bottom);
}
void Widget::Padding(const UDim &padding) {
Padding(padding, padding, padding, padding);
}
UDim Widget::MarginLeft () const { return margin_left;}
UDim Widget::MarginTop () const { return margin_top;}
UDim Widget::MarginRight () const { return margin_right;}
UDim Widget::MarginBottom() const { return margin_bottom;}
void Widget::MarginLeft(const UDim& ml) { margin_left = ml;}
void Widget::MarginTop(const UDim& mt) { margin_top = mt;}
void Widget::MarginRight(const UDim& mr) { margin_right = mr;}
void Widget::MarginBottom(const UDim& mb) { margin_bottom = mb;}
void Widget::Margin(const UDim &left, const UDim &top, const UDim &right, const UDim &bottom) {
MarginLeft(left);
MarginTop(top);
MarginRight(right);
MarginBottom(bottom);
}
void Widget::Margin(const UDim &margin) {
Margin(margin, margin, margin, margin);
}
std::string Widget::Name() const { return name; }
void Widget::Name(const std::string& new_name) { name = new_name;}
bool Widget::IsVisible() const { return visible; }
void Widget::Visible(bool enabled) { visible = enabled;}
Widget* Widget::GetParent() const { return parent; }
UDim2 Widget::Position() const { return position; }
void Widget::Position(const UDim2& pos) { position = pos; }
UDim2 Widget::Size() const { return size;}
void Widget::Size(const UDim2& s) { size = s; }
float Widget::GetAbsoluteRotation() const {
// TODO: implement rotation correctly!!
return rotation;
}
std::vector<Widget *> Widget::GetChildren() { return children; }
std::vector<Widget *> Widget::GetDescendants() {
std::vector<Widget*> descendants;
for (auto& child: this->GetChildren()) {
descendants.push_back(child);
std::vector<Widget*> recursiveDescendants = child->GetDescendants();
descendants.insert(descendants.end(), recursiveDescendants.begin(), recursiveDescendants.end());
}
return descendants;
}
void Widget::Padding(const UDim &padding) {
Padding(padding, padding, padding, padding);
}
UDim Widget::MarginLeft () const { return margin_left;}
UDim Widget::MarginTop () const { return margin_top;}
UDim Widget::MarginRight () const { return margin_right;}
UDim Widget::MarginBottom() const { return margin_bottom;}
void Widget::MarginLeft(const UDim& ml) { margin_left = ml;}
void Widget::MarginTop(const UDim& mt) { margin_top = mt;}
void Widget::MarginRight(const UDim& mr) { margin_right = mr;}
void Widget::MarginBottom(const UDim& mb) { margin_bottom = mb;}
void Widget::Margin(const UDim &left, const UDim &top, const UDim &right, const UDim &bottom) {
MarginLeft(left);
MarginTop(top);
MarginRight(right);
MarginBottom(bottom);
}
void Widget::Margin(const UDim &margin) {
Margin(margin, margin, margin, margin);
}
std::string Widget::Name() const { return name; }
void Widget::Name(const std::string& new_name) { name = new_name;}
bool Widget::IsVisible() const { return visible; }
void Widget::Visible(bool enabled) { visible = enabled;}
Widget* Widget::GetParent() const {
return parent;
}
UDim2 Widget::Position() const { return position; }
void Widget::Position(const UDim2& pos) { position = pos; }
UDim2 Widget::Size() const { return size;}
void Widget::Size(const UDim2& s) { size = s; }
float Widget::GetAbsoluteRotation() const {
// TODO: implement rotation correctly!!
return rotation;
}
std::vector<Widget *> Widget::GetChildren() { return children; }
std::vector<Widget *> Widget::GetDescendants() {
std::vector<Widget*> descendants;
for (auto& child: this->GetChildren()) {
descendants.push_back(child);
std::vector<Widget*> recursiveDescendants = child->GetDescendants();
descendants.insert(descendants.end(), recursiveDescendants.begin(), recursiveDescendants.end());
}
return descendants;
}
std::vector<Widget*> Widget::GetAncestors() {
std::vector<Widget*> ancestors;
for (Widget *ancestor = this->parent; ancestor; ancestor = ancestor->parent) {
ancestors.push_back(ancestor);
}
return ancestors;
}
std::vector<Widget*> Widget::GetAncestors() {
std::vector<Widget*> ancestors;
for (Widget *ancestor = this->parent; ancestor; ancestor = ancestor->parent)
ancestors.push_back(ancestor);
return ancestors;
}
/*float Widget::GetAbsoluteMarginLeft() {}
float Widget::GetAbsoluteMarginRight() {}
@@ -188,259 +177,412 @@ namespace JUI {
Vector2 Widget::GetAbsolutePaddingTopLeft() const {
auto parent_abs_size = this->GetParent()->GetAbsoluteSize();
Vector2 Widget::GetAbsolutePaddingTopLeft() const {
auto parent_abs_size = this->GetParent()->GetAbsoluteSize();
UDim padding_h = parent->PaddingLeft();
UDim padding_v = parent->PaddingTop();
UDim padding_h = parent->PaddingLeft();
UDim padding_v = parent->PaddingTop();
float padding_x = padding_h.Pixels + (padding_h.Scale * parent_abs_size.x);
float padding_y = padding_v.Pixels + (padding_v.Scale * parent_abs_size.y);
float padding_x = padding_h.Pixels + (padding_h.Scale * parent_abs_size.x);
float padding_y = padding_v.Pixels + (padding_v.Scale * parent_abs_size.y);
return {padding_x, padding_y};
}
return {padding_x, padding_y};
}
Vector2 Widget::GetAbsoluteMarginTopLeft()
{
// TODO: Implement correctly.
Vector2 Widget::GetAbsoluteMarginTopLeft() {
// TODO: Implement correctly.
return {0,0};
}
Vector2 Widget::GetAbsolutePaddingBottomRight() const {
UDim padding_h = parent->PaddingLeft() + parent->PaddingRight();
float final_pad_x = padding_h.Pixels + (padding_h.Scale * parent->GetAbsoluteSize().x);
UDim padding_v = parent->PaddingTop() + parent->PaddingBottom();
float final_pad_y = padding_v.Pixels + (padding_v.Scale * parent->GetAbsoluteSize().y);
Vector2 padding = {final_pad_x, final_pad_y};
return padding;
}
Vector2 Widget::GetAbsoluteMarginBottomRight() {
// TODO: Implement correctly.
return {0,0};
}
Vector2 Widget::GetAbsolutePosition() const {
if (this->parent == nullptr)
return {0,0};
}
Vector2 Widget::GetAbsolutePaddingBottomRight() const {
UDim padding_h = parent->PaddingLeft() + parent->PaddingRight();
float final_pad_x = padding_h.Pixels + (padding_h.Scale * parent->GetAbsoluteSize().x);
auto child_pos_scale = this->Position().GetScale();
auto child_pos_pixels = this->Position().GetPixels();
auto parent_abs_size = this->GetParent()->GetAbsoluteSize();
auto parent_abs_pos = this->GetParent()->GetAbsolutePosition();
UDim padding_v = parent->PaddingTop() + parent->PaddingBottom();
float final_pad_y = padding_v.Pixels + (padding_v.Scale * parent->GetAbsoluteSize().y);
auto abs_size = GetAbsoluteSize();
Vector2 padding = {final_pad_x, final_pad_y};
auto anchor_offset = abs_size * AnchorPoint();
return padding;
}
Vector2 padding_offset = GetAbsolutePaddingTopLeft();
Vector2 Widget::GetAbsoluteMarginBottomRight()
{
// TODO: Implement correctly.
return {0,0};
}
Vector2 Widget::GetAbsolutePosition() const {
if (this->parent == nullptr)
return {0,0};
auto child_pos_scale = this->Position().GetScale();
auto child_pos_pixels = this->Position().GetPixels();
auto parent_abs_size = this->GetParent()->GetAbsoluteSize();
auto parent_abs_pos = this->GetParent()->GetAbsolutePosition();
auto abs_size = GetAbsoluteSize();
auto anchor_offset = abs_size * AnchorPoint();
Vector2 padding_offset = GetAbsolutePaddingTopLeft();
Vector2 absolute_position =
Vector2 absolute_position =
parent_abs_pos + child_pos_pixels + (parent_abs_size * child_pos_scale) + padding_offset;
return absolute_position - anchor_offset;
}
Vector2 Widget::GetAbsoluteSize() const {
if (this->GetParent() == nullptr)
return {0,0};
Vector2 child_size_scale = this->Size().GetScale();
Vector2 child_size_px = this->Size().GetPixels();
Vector2 parent_abs_size = this->GetParent()->GetAbsoluteSize();
Vector2 pad_size_reduction = GetAbsolutePaddingBottomRight();
Vector2 abs_size = child_size_px + (parent_abs_size * child_size_scale) - pad_size_reduction;
return abs_size;
return absolute_position - anchor_offset;
}
Vector2 Widget::GetAbsoluteSize() const {
if (this->GetParent() == nullptr)
return {0,0};
Vector2 child_size_scale = this->Size().GetScale();
Vector2 child_size_px = this->Size().GetPixels();
Vector2 parent_abs_size = this->GetParent()->GetAbsoluteSize();
Vector2 pad_size_reduction = GetAbsolutePaddingBottomRight();
Vector2 abs_size = child_size_px + (parent_abs_size * child_size_scale) - pad_size_reduction;
return abs_size;
}
std::optional<Widget*> Widget::FindFirstChild(const std::string& search_name) {
for (auto& child : children) {
if (child->Name() == search_name)
return child;
}
return std::nullopt;
}
std::optional<Widget*> Widget::FindFirstChild(const std::string& search_name) {
for (auto& child : children) {
if (child->Name() == search_name)
return child;
}
return std::nullopt;
}
void Widget::Draw() {
if (!visible)
return;
PreDraw();
InnerDraw();
PostDraw();
DrawChildWidgets();
}
void Widget::UpdateTweens(float elapsed) {
for (Tween* t: tweens)
t->Update(elapsed);
// TODO: Remove tweens if "dead"
std::ranges::remove_if(tweens.begin(), tweens.end(), [](Tween* t){
return t->HasCompleted();
});
}
void Widget::Update(float delta) {
UpdateChildWidgets(delta);
UpdateTweens(delta);
}
struct {
bool operator()(Widget* a, Widget* b) const { return a->ZIndex() < b->ZIndex();}
}
zIndexSort;
void Widget::DrawChildWidgets() {
std::sort(children.begin(), children.end(), zIndexSort);
for (auto child : children)
child->Draw();
}
void Widget::UpdateChildWidgets(float delta) {
for (auto child : children)
child->Update(delta);
}
Widget* Widget::GetFamilyTreeRoot() const {
// This is stupid, Fix ASAP!
auto parent = this->GetParent();
if (parent->GetParent() == nullptr)
return parent;
return parent->GetFamilyTreeRoot();
}
bool Widget::IsMouseInside() const {
float x = last_known_mouse_pos.x;
float y = last_known_mouse_pos.y;
auto pos = GetAbsolutePosition();
auto size = GetAbsoluteSize();
if (x > pos.x && y > pos.y && x < pos.x + size.x && y < pos.y + size.y)
return true;
return false;
}
void Widget::ObserveMouseInput(MouseButton btn, bool pressed) {
mbtn = btn;
mb_state = pressed;
for (Widget* child : children)
child->ObserveMouseInput(btn, pressed);
}
void Widget::ObserveMouseMovement(const Vector2 &latest_known_pos) {
last_known_mouse_pos = latest_known_pos;
for (Widget* child: children)
child->ObserveMouseMovement(latest_known_pos);
}
void Widget::ObserveKeyInput(Key key, bool pressed) {
for (Widget* child : children)
child->ObserveKeyInput(key, pressed);
}
void Widget::Draw() {
if (!visible)
return;
bool Widget::IsAncestorOf(Widget *descendant) const {
if (descendant == nullptr)
return false;
PreDraw();
InnerDraw();
PostDraw();
DrawChildWidgets();
}
void Widget::UpdateTweens(float elapsed)
{
for (Tween* t: tweens) {
t->Update(elapsed);
}
// TODO: Remove tweens if "dead"
std::ranges::remove_if(tweens.begin(), tweens.end(), [](Tween* t){
return t->HasCompleted();
});
}
void Widget::Update(float delta) {
UpdateChildWidgets(delta);
UpdateTweens(delta);
}
struct {
bool operator()(Widget* a, Widget* b) const { return a->ZIndex() < b->ZIndex();}
} zIndexSort;
void Widget::DrawChildWidgets() {
std::sort(children.begin(), children.end(), zIndexSort);
for (auto child : children) {
child->Draw();
}
}
void Widget::UpdateChildWidgets(float delta) {
for (auto child : children) {
child->Update(delta);
}
}
Widget* Widget::GetFamilyTreeRoot() const {
// This is stupid, Fix ASAP!
auto parent = this->GetParent();
if (parent->GetParent() == nullptr)
return parent;
return parent->GetFamilyTreeRoot();
}
bool Widget::IsMouseInside() const {
float x = last_known_mouse_pos.x;
float y = last_known_mouse_pos.y;
auto pos = GetAbsolutePosition();
auto size = GetAbsoluteSize();
if (x > pos.x && y > pos.y && x < pos.x + size.x && y < pos.y + size.y) {
Widget* instance = descendant;
while (instance->GetParent()) {
instance = instance->GetParent();
if (instance == this)
return true;
}
}
return false;
}
bool Widget::IsDescendantOf(Widget *ancestor) const {
if (ancestor == nullptr)
return false;
const Widget *instance = this;
while (instance->GetParent()) {
instance = instance->GetParent();
if (instance == ancestor)
return true;
}
return false;
}
void Widget::ObserveMouseInput(MouseButton btn, bool pressed) {
mbtn = btn;
mb_state = pressed;
Widget *Widget::Add(Widget *newChild) {
newChild->Parent(this);
return newChild;
}
for (Widget* child : children)
{
child->ObserveMouseInput(btn, pressed);
}
}
void Widget::AnchorPoint(const Vector2& point) {
anchor_point = point;
}
void Widget::ObserveMouseMovement(const Vector2 &latest_known_pos) {
int Widget::ZIndex() const { return zindex;}
last_known_mouse_pos = latest_known_pos;
void Widget::ZIndex(int new_zindex) { zindex = new_zindex; }
for (Widget* child : children)
{
child->ObserveMouseMovement(latest_known_pos);
}
}
AABB2D Widget::GetActualRenderBounds() const {
return {GetAbsolutePosition(), GetAbsoluteSize()};
}
void Widget::ObserveKeyInput(Key key, bool pressed) {
for (Widget* child : children) {
child->ObserveKeyInput(key, pressed);
}
}
void Widget::SetViewportSize(const Vector2 &vps) {
viewport_size = vps;
for(auto& child : children)
child->SetViewportSize(viewport_size);
}
bool Widget::IsAncestorOf(Widget *descendant) const {
if (descendant == nullptr)
return false;
Widget *instance = descendant;
while (instance->GetParent()) {
instance = instance->GetParent();
if (instance == this)
return true;
}
return false;
}
AABB2D Widget::AbsoluteBounds() const {
return {GetAbsolutePosition(), GetAbsoluteSize()};
}
bool Widget::IsDescendantOf(Widget *ancestor) const {
if (ancestor == nullptr)
return false;
const Widget *instance = this;
while (instance->GetParent()) {
instance = instance->GetParent();
if (instance == ancestor)
return true;
}
return false;
}
Tween* Widget::TweenPosition(const UDim2& goal, TweenInfo params) {
UDim2 start_position = this->Position();
TweenTickFunc updateTillGoalReached = [this, start_position, goal] (float elapsed, float progress) mutable {
UDim2 pos = start_position.Lerp(goal, progress);
Position(pos);
};
Widget *Widget::Add(Widget *newChild) {
newChild->Parent(this);
return newChild;
}
Tween* t = new Tween(updateTillGoalReached, params);
tweens.push_back(t);
return t;
}
void Widget::AnchorPoint(const Vector2& point) {
anchor_point = point;
}
Tween* Widget::TweenSize(const UDim2& goal, TweenInfo params) {
UDim2 start_size = this->Size();
TweenTickFunc updateTillGoalReached = [this, start_size, goal] (float elapsed, float progress) mutable {
UDim2 step_size = start_size.Lerp(goal, progress);
Size(step_size);
};
int Widget::ZIndex() const { return zindex;}
Tween* t = new Tween(updateTillGoalReached, params);
tweens.push_back(t);
return t;
}
void Widget::ZIndex(int new_zindex) { zindex = new_zindex; }
Tween* Widget::TweenPaddingLeft(const JUI::UDim& goal, JUI::TweenInfo info) {
//UDum2
UDim start_padding = this->PaddingLeft();
TweenTickFunc updateTillGoalReached = [this, start_padding, goal] (float elapsed, float progress) mutable {
UDim step_padding = start_padding.Lerp(goal, progress);
PaddingLeft(step_padding);
};
AABB2D Widget::GetActualRenderBounds() const {
return {GetAbsolutePosition(), GetAbsoluteSize()};
}
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
void Widget::SetViewportSize(const Vector2 &vps) {
viewport_size = vps;
return t;
}
for(auto& child : children)
{
child->SetViewportSize(viewport_size);
}
}
Tween* Widget::TweenPaddingRight(const JUI::UDim& goal, JUI::TweenInfo info) {
//UDum2
UDim start_padding = this->PaddingRight();
TweenTickFunc updateTillGoalReached = [this, start_padding, goal] (float elapsed, float progress) mutable {
UDim step_padding = start_padding.Lerp(goal, progress);
PaddingRight(step_padding);
};
AABB2D Widget::AbsoluteBounds() const {
return {GetAbsolutePosition(), GetAbsoluteSize()};
}
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}
Tween *Widget::TweenPositionTo(const UDim2 &goal, TweenInfo params) {
UDim2 start_position = this->Position();
TweenTickFunc updateTillGoalReached = [this, start_position, goal] (float elapsed, float progress) mutable {
UDim2 pos = start_position.Lerp(goal, progress);
Position(pos);
};
Tween* t = new Tween(updateTillGoalReached, params);
tweens.push_back(t);
return t;
}
Tween* Widget::TweenPaddingTop(const JUI::UDim& goal, JUI::TweenInfo info) {
//UDum2
UDim start_padding = this->PaddingTop();
TweenTickFunc updateTillGoalReached = [this, start_padding, goal] (float elapsed, float progress) mutable {
UDim step_padding = start_padding.Lerp(goal, progress);
PaddingTop(step_padding);
};
Tween *Widget::TweenSizeTo(const UDim2 &goal, TweenInfo info) {
UDim2 start_size = this->Size();
TweenTickFunc updateTillGoalReached = [this, start_size, goal] (float elapsed, float progress) mutable {
UDim2 step_size = start_size.Lerp(goal, progress);
Size(step_size);
};
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}
Tween* Widget::TweenPaddingBottom(const JUI::UDim& goal, JUI::TweenInfo info) {
//UDum2
UDim start_padding = this->PaddingBottom();
TweenTickFunc updateTillGoalReached = [this, start_padding, goal] (float elapsed, float progress) mutable {
UDim step_padding = start_padding.Lerp(goal, progress);
PaddingBottom(step_padding);
};
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}
Tween *Widget::TweenPadding(const JUI::UDim &gl, const JUI::UDim &gt, const JUI::UDim &gr,
const JUI::UDim &gb, JUI::TweenInfo info) {
UDim spl = this->PaddingLeft();
UDim spr = this->PaddingRight();
UDim spt = this->PaddingTop();
UDim spb = this->PaddingBottom();
TweenTickFunc updateTillGoalReached = [this, spl, spr, spt, spb, gl, gt, gr, gb] (float elapsed, float progress) mutable {
UDim step_l = spl.Lerp(gl, progress);
UDim step_r = spr.Lerp(gr, progress);
UDim step_b = spb.Lerp(gb, progress);
UDim step_t = spt.Lerp(gt, progress);
Padding(step_l, step_r, step_b, step_t);
};
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}
Tween *Widget::TweenPadding(const JUI::UDim &goal, JUI::TweenInfo info) {
UDim start_padding = this->PaddingTop(); // TODO: Determine which side is most appropriate as start_point.
TweenTickFunc updateTillGoalReached = [this, start_padding, goal] (float elapsed, float progress) mutable {
UDim step_padding = start_padding.Lerp(goal, progress);
Padding(step_padding);
};
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}
Tween *Widget::TweenMargin(const JUI::UDim &gl, const JUI::UDim &gt, const JUI::UDim &gr,
const JUI::UDim &gb, JUI::TweenInfo info) {
UDim spl = this->MarginLeft();
UDim spr = this->MarginRight();
UDim spt = this->MarginTop();
UDim spb = this->MarginBottom();
TweenTickFunc updateTillGoalReached = [this, spl, spr, spt, spb, gl, gt, gr, gb] (float elapsed, float progress) mutable {
UDim step_l = spl.Lerp(gl, progress);
UDim step_r = spr.Lerp(gr, progress);
UDim step_b = spb.Lerp(gb, progress);
UDim step_t = spt.Lerp(gt, progress);
Margin(step_l, step_r, step_b, step_t);
};
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}
Tween *Widget::TweenMargin(const JUI::UDim &goal, JUI::TweenInfo info) {
UDim start_Margin = this->MarginTop(); // TODO: Determine which side is most appropriate as start_point.
TweenTickFunc updateTillGoalReached = [this, start_Margin, goal] (float elapsed, float progress) mutable {
UDim step_Margin = start_Margin.Lerp(goal, progress);
Margin(step_Margin);
};
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}
Tween* Widget::TweenMarginLeft(const JUI::UDim& goal, JUI::TweenInfo info) {
UDim start = this->MarginLeft();
TweenTickFunc updateTillGoalReached = [this, start, goal] (float elapsed, float progress) mutable {
UDim step = start.Lerp(goal, progress);
MarginLeft(step);
};
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}
Tween* Widget::TweenMarginRight(const JUI::UDim& goal, JUI::TweenInfo info) {
UDim start = this->MarginRight();
TweenTickFunc updateTillGoalReached = [this, start, goal] (float elapsed, float progress) mutable {
UDim step = start.Lerp(goal, progress);
MarginRight(step);
};
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}
Tween* Widget::TweenMarginTop(const JUI::UDim& goal, JUI::TweenInfo info) {
UDim start = this->MarginTop();
TweenTickFunc updateTillGoalReached = [this, start, goal] (float elapsed, float progress) mutable {
UDim step = start.Lerp(goal, progress);
MarginTop(step);
};
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}
Tween* Widget::TweenMarginBottom(const JUI::UDim& goal, JUI::TweenInfo info) {
UDim start = this->MarginBottom();
TweenTickFunc updateTillGoalReached = [this, start, goal] (float elapsed, float progress) mutable {
UDim step = start.Lerp(goal, progress);
MarginBottom(step);
};
Tween* t = new Tween(updateTillGoalReached, info);
tweens.push_back(t);
return t;
}

View File

@@ -195,4 +195,8 @@ namespace JUI
bool Tween::HasCompleted() const { return completed; }
void Tween::Start() { progress = 0; Resume(); }
Tween::Tween(TweenTickFunc tick_func) {
this->tick_func = tick_func;
}
}

View File

@@ -5,4 +5,31 @@ namespace JUI
{
Checkbox::Checkbox() : Button(), CheckboxBase() { }
Checkbox::Checkbox(Widget *parent) : Checkbox()
{
this->Parent(parent);
}
void Checkbox::Update(float delta) {
Button::Update(delta);
}
void Checkbox::OnRelease(const Vector2 &mouse_pos, const MouseButton &bnt, bool still_hovering) {
Button::OnRelease(mouse_pos, bnt, still_hovering);
checked = !checked;
}
void Checkbox::InnerDraw() {
Rect::InnerDraw();
if (checked)
{
//J2D::Begin();
Vector2 check_padding = {2, 2};
RectBase::Draw(check_color, check_color, GetAbsolutePosition()+check_padding, GetAbsoluteSize()-(check_padding*2));
//J2D::End();
}
}
}

View File

@@ -8,3 +8,7 @@ void JUI::ImageButton::Draw() {
Button::Draw();
ImageBase::Draw(GetAbsolutePosition()+GetAbsolutePaddingTopLeft(), GetAbsoluteSize()-GetAbsolutePaddingBottomRight());
}
JUI::ImageButton::ImageButton() : ImageBase(), Button() {}
JUI::ImageButton::ImageButton(JUI::Widget *parent) : ImageButton() { Parent(parent); }

View File

@@ -1,2 +1,16 @@
#include <JUI/Widgets/ImageRect.hpp>
JUI::ImageRect::ImageRect() : Rect(), ImageBase() {}
JUI::ImageRect::ImageRect(JUI::Widget *parent) : ImageRect() {
Parent(parent);
}
void JUI::ImageRect::Update(float delta) {
Rect::Update(delta);
}
void JUI::ImageRect::Draw() {
Rect::Draw();
ImageBase::Draw(GetAbsolutePosition()+GetAbsolutePaddingTopLeft(), GetAbsoluteSize()-GetAbsolutePaddingBottomRight());
}

View File

@@ -11,7 +11,6 @@ namespace JUI
this->Parent(parent);
}
struct {
bool operator()(Widget* a, Widget* b) const { return a->LayoutOrder() > b->LayoutOrder();}
} layoutOrderSort;