Refactoring, bug fixes, and implementing styling data.

This commit is contained in:
2025-02-14 15:29:00 -05:00
parent 9cf78742ae
commit f772369686
19 changed files with 262 additions and 139 deletions

View File

@@ -16,10 +16,10 @@
#include <Color4.hpp>
#include "JGL/types/RenderTarget.h"
#include <JUI/DefaultStyle.hpp>
namespace JUI
{
enum class BorderMode
{
Outline, /// As BorderWidth increases, the border grows outward. The dimensions of the widgets contents do not change.
@@ -72,9 +72,9 @@ namespace JUI
enum BorderMode border_mode = BorderMode::Middle;
bool mouse_press_debounce{};
bool mouse_inside_debounce{};
float border_width = 1.f;
Color4 bg_color = {128,128,128, 255};
Color4 border_color = {192, 192, 192, 0};
float border_width = Style::BorderLineWidth;
Color4 bg_color = Style::BackgroundColor;
Color4 border_color = Style::BorderColor;
bool clips_descendants = false; // Controls if child objects can render outside of their parent's rectangle bounds.
float corner_rounding_radius = 0.f; // Curves the rectangle corners by N degrees.
};

View File

@@ -22,6 +22,7 @@
#include <ReWindow/types/Key.h>
#include <JUI/Tween.hpp>
#include <JUI/JUI.hpp>
#include "JUI/DefaultStyle.hpp"
using namespace JGL;
@@ -334,14 +335,14 @@ namespace JUI {
float rotation = 0;
std::string name;
bool selected = false;
UDim pad_left = 0_px;
UDim pad_right = 0_px;
UDim pad_top = 0_px;
UDim pad_bottom = 0_px;
UDim margin_left = 0_px;
UDim margin_right = 0_px;
UDim margin_top = 0_px;
UDim margin_bottom = 0_px;
UDim pad_left = Style::BasePadding;
UDim pad_right = Style::BasePadding;
UDim pad_top = Style::BasePadding;
UDim pad_bottom = Style::BasePadding;
UDim margin_left = Style::BasePadding;
UDim margin_right = Style::BasePadding;
UDim margin_top = Style::BasePadding;
UDim margin_bottom = Style::BasePadding;
Vector2 anchor_point = {0.f, 0.f};
bool visible = true;
Widget* next = nullptr;

View File

@@ -0,0 +1,60 @@
#pragma once
#include <Color4.hpp>
#include <Colors.hpp>
#include <JUI/UDim.hpp>
namespace JUI {
namespace DefaultStyle {
const Color4 BackgroundColor = {128, 128, 128};
const Color4 BorderColor = {192, 192, 192};
const float BorderLineWidth = 1.f;
const UDim BasePadding = 0_px;
namespace Button {
const Color4 BaseBackgroundColor = Colors::White;
const Color4 BaseBorderColor = Colors::Black;
const Color4 HoverBackgroundColor = Colors::Blues::SkyBlue;
const Color4 HoverBorderColor = Colors::Blues::DarkSlateBlue;
const Color4 PressBackgroundColor = Colors::Blues::DarkSlateBlue;
const Color4 PressBorderColor = Colors::Blues::SkyBlue;
const Color4 DisableBackgroundColor = Colors::Gray;
const Color4 DisableBorderColor = Colors::Gray;
}
namespace Image
{
//const
}
namespace Text {
namespace H1 {
}
namespace H2 {
}
}
namespace Collapsible {
const UDim HeaderHeight = 16_px;
}
#pragma endregion
#pragma region Text Defaults
#pragma endregion
}
namespace ExperimentalStyle {
}
namespace Style = DefaultStyle;
}

View File

@@ -108,14 +108,15 @@ namespace JUI
//void SetTooltip(const std::string &content, float delay = 0.2f) override;
protected:
bool disabled = false;
Color4 hover_bg = Colors::Blues::SkyBlue;
Color4 hover_border = Colors::Blues::DarkSlateBlue;
Color4 pressed_bg = Colors::Blues::DarkSlateBlue;
Color4 pressed_border = Colors::Blues::SkyBlue;;
Color4 disabled_bg = Colors::Gray;
Color4 disabled_border = Colors::Gray;
Color4 base_bg = Colors::White;
Color4 base_border = Colors::Black;
Color4 hover_bg = Style::Button::HoverBackgroundColor;
Color4 hover_border = Style::Button::HoverBorderColor;
Color4 pressed_bg = Style::Button::PressBackgroundColor;
Color4 pressed_border = Style::Button::PressBorderColor;
Color4 disabled_bg = Style::Button::DisableBackgroundColor;
Color4 disabled_border = Style::Button::DisableBorderColor;
Color4 base_bg = Style::Button::BaseBackgroundColor;
Color4 base_border = Style::Button::BaseBorderColor;
void UpdateVisualState();
};

View File

@@ -1,5 +1,44 @@
/// 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) 2025 Redacted Software
/// This work is dedicated to the public domain.
/// @file Collapsible.cpp
/// @desc A rectangular widget with a clickable header that enables hiding and showing the contents.
/// @edit 2025-02-14
#pragma once
namespace JUI
{
#include <JUI/Widgets/Rect.hpp>
#include <JUI/Widgets/TextButton.hpp>
namespace JUI {
class Collapsible : public Rect {
public:
Collapsible();
explicit Collapsible(Widget* parent);
TextButton* Header();
Rect* ContentBox();
void Collapse();
void Expand();
[[nodiscard]] bool Collapsed() const;
UDim HeaderHeight() const;
void HeaderHeight(const UDim& value);
void Title(const std::string& value);
std::string Title() const;
protected:
TextButton* header = nullptr;
Rect* content_box = nullptr;
TextRect* lil_arrow = nullptr;
bool collapsed = false;
UDim2 saved_size;
UDim header_height = Style::Collapsible::HeaderHeight;
};
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <JUI/Base/Widget.hpp>
namespace JUI {
enum class Orientation { HORIZONTAL, VERTICAL };
/// Fills space, and renders a single line through it, based on the given orientation.
class Separator : public Widget {
public:
Separator() : Widget()
{
}
explicit Separator(Widget* parent)
{
}
protected:
private:
};
}

View File

@@ -11,6 +11,7 @@
#pragma once
#include <set>
#include <JUI/Mixins/Clickable.hpp>
#include "TextRect.hpp"
@@ -42,11 +43,7 @@ namespace JUI {
void SetInputBuffer(const std::string& value);
void ClearInputBuffer();
// TODO: Implement selection of part of input text.
Color4 GetSelectionColor() const;
void SetSelectionColor(const Color4& color);
bool HasSelection() const;
std::string GetSelectedText() const;
[[nodiscard]] bool HasFocus() const;
void SetFocused(bool focused);
@@ -61,7 +58,15 @@ namespace JUI {
void GrabFocus();
void DropFocus();
[[nodiscard]] std::vector<char> GetBlacklist() const;
[[nodiscard]] std::set<std::string> GetBlacklist() const;
void SetBlacklist(const std::set<std::string>& value);
void AddToBlacklist(const std::string& value);
// TODO: Implement selection of part of input text.
Color4 GetSelectionColor() const;
void SetSelectionColor(const Color4& color);
bool HasSelection() const;
std::string GetSelectedText() const;
protected:
bool clear_text_on_return = true;
bool drop_focus_on_return = false;
@@ -77,7 +82,7 @@ namespace JUI {
std::string autocomplete_text = "Hello World";
bool hide_autocomplete_on_select = true;
bool autocomplete_text_enabled = true;
std::vector<char> blacklist;
std::set<std::string> blacklist;
private:
};
}

View File

@@ -27,6 +27,7 @@
#include <ReWindow/types/Window.h>
#include <ReWindow/Logger.h>
#include "JUI/Widgets/NineSlice.hpp"
#include <JUI/Widgets/Collapsible.hpp>
JUI::Scene* scene;
JGL::Texture* sample_texture;
@@ -175,6 +176,10 @@ JUI::Scene* CreateScene() {
input_form->SetContent("");
input_form->SetTextSize(14);
auto* collapsible = new Collapsible(column_layout);
collapsible->Size({100_percent, 200_px});
auto* other_window = new JUI::Window(root);
other_window->Position({10_percent, 10_percent});
other_window->Size({30_percent, 25_percent});

4
src/JUI/DefaultStyle.cpp Normal file
View File

@@ -0,0 +1,4 @@
#include <JUI/DefaultStyle.hpp>
namespace DefaultStyle
{ }

View File

@@ -2,8 +2,7 @@
#include <JUI/Widgets/Button.hpp>
#include <jlog/Logger.hpp>
namespace JUI
{
namespace JUI {
Button::Button(): Rect(), Clickable() {
BGColor(BaseBGColor());
BorderColor(BaseBorderColor());
@@ -13,15 +12,11 @@ namespace JUI
this->Parent(parent);
};
void Button::OnClick(const Vector2& mouse_pos, const MouseButton& btn)
{
void Button::OnClick(const Vector2& mouse_pos, const MouseButton& btn) {
if (disabled)
return;
Clickable::OnClick(mouse_pos, btn);
//BGColor(PressedBGColor());
//BorderColor(PressedBorderColor());
}
void Button::OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &btn,
@@ -31,14 +26,9 @@ namespace JUI
return;
Clickable::OnRelease(mouse_pos, btn, still_hovering);
//BGColor(BaseBGColor());
//BorderColor(BaseBorderColor());
}
void Button::UpdateVisualState()
{
void Button::UpdateVisualState() {
if (Disabled()) {
BGColor(DisabledBGColor());
BorderColor(DisabledBorderColor());
@@ -117,8 +107,6 @@ namespace JUI
if (Disabled() || IsClicked())
return;
//BGColor(HoveredBGColor());
//BorderColor(HoveredBorderColor());
}
void Button::OnExit(const Vector2 &MousePos) {
@@ -126,9 +114,6 @@ namespace JUI
if (Disabled() || IsClicked())
return;
//BGColor(BaseBGColor());
//BorderColor(BaseBorderColor());
}
Color4 Button::HoveredBGColor() const { return hover_bg; }
@@ -168,7 +153,6 @@ namespace JUI
HoveredBorderColor(hover);
PressedBorderColor(pressed);
DisabledBGColor(disabled);
UpdateVisualState();
}
@@ -176,19 +160,7 @@ namespace JUI
BaseBGColor(base);
HoveredBGColor(hover);
PressedBGColor(pressed);
DisabledBGColor(disabled);
DisabledBGColor(disabled);\
UpdateVisualState();
}
//void Button::SetTooltip(const std::string &content, float delay) {
//tooltip_limit = delay;
// TODO: Verify this is okay to do:
//tooltip = new Tooltip(this);
//tooltip->SetContent(content);
//tooltip->ZIndex(5);
//tooltip->Visible(false);
//}
}

View File

@@ -1,9 +1,7 @@
#include <JUI/Widgets/Checkbox.hpp>
namespace JUI
{
namespace JUI {
Checkbox::Checkbox() : Button(), CheckboxBase() { }
@@ -23,13 +21,9 @@ namespace JUI
void Checkbox::InnerDraw() {
Rect::InnerDraw();
if (checked)
{
//J2D::Begin();
if (checked) {
Vector2 check_padding = {2, 2};
RectBase::Draw(check_color, check_color, GetAbsolutePosition()+check_padding, GetAbsoluteSize()-(check_padding*2));
//J2D::End();
}
}
}

View File

@@ -0,0 +1,67 @@
#include <JUI/Widgets/Collapsible.hpp>
namespace JUI {
Collapsible::Collapsible() : Rect() {
header = new TextButton(this);
header->Size({100_percent, header_height});
header->SetContent("Collapsible");
header->BaseBGColor(Colors::Gray);
header->BGColor(Colors::Gray);
header->Center();
//header->
header->OnClickEvent += [this] (auto a, auto b) {
if (Collapsed())
Expand();
else
Collapse();
};
lil_arrow = new TextRect(header);
lil_arrow->Size({header_height, header_height});
lil_arrow->BGColor(Colors::Transparent);
lil_arrow->SetContent("/\\");
content_box = new Rect(this);
content_box->BGColor(Colors::Transparent);
content_box->BorderColor(Colors::Transparent);
content_box->SetBorderWidth(0);
content_box->Position({0_px, header_height});
content_box->Size({100_percent, 100_percent - header_height});
}
void Collapsible::Collapse() {
lil_arrow->SetContent("\\/");
content_box->Visible(false);
saved_size = Size();
TweenSize(UDim2(saved_size.X, header_height));
collapsed = true;
}
void Collapsible::Expand() {
lil_arrow->SetContent("/\\");
content_box->Visible(true);
TweenSize(saved_size);
collapsed = false;
}
bool Collapsible::Collapsed() const { return collapsed; }
Collapsible::Collapsible(Widget *parent) : Collapsible() {
this->Parent(parent);
}
TextButton *Collapsible::Header() { return header; }
Rect *Collapsible::ContentBox() {return content_box; }
UDim Collapsible::HeaderHeight() const { return header_height;}
void Collapsible::HeaderHeight(const UDim &value) { header_height = value; }
void Collapsible::Title(const std::string &value) {
header->SetContent(value);
}
std::string Collapsible::Title() const { return header->GetContent(); }
}

View File

@@ -8,8 +8,6 @@ JUI::ImageRect::ImageRect(JUI::Widget *parent) : ImageRect() {
void JUI::ImageRect::Update(float delta) {
Rect::Update(delta);
}
void JUI::ImageRect::Draw() {

View File

@@ -6,8 +6,7 @@ namespace JUI
ListLayout::ListLayout(Widget* parent) : ListLayout() {}
VerticalListLayout::VerticalListLayout() : ListLayout() {}
VerticalListLayout::VerticalListLayout(Widget* parent) : VerticalListLayout()
{
VerticalListLayout::VerticalListLayout(Widget* parent) : VerticalListLayout() {
this->Parent(parent);
}
@@ -61,10 +60,6 @@ namespace JUI
Parent(parent);
}
void HorizontalListLayout::ApplyLayout() {
// TODO: Implement widget.LayoutOrder and sort by that number.

View File

@@ -1,8 +1,7 @@
#include <JUI/Widgets/NineSlice.hpp>
#include <JGL/JGL.h>
namespace JUI
{
namespace JUI {
AABB2D JUI::NineSliceRect::TopLeftQuad() const { return top_left_quad; }
AABB2D NineSliceRect::TopRightQuad() const { return top_right_quad; }
@@ -42,10 +41,7 @@ namespace JUI
void NineSliceRect::Draw() {
Rect::Draw();
//JGL::J2D::Begin();
Vector2 abs_pos = GetAbsolutePosition();
Vector2 abs_size = GetAbsoluteSize();
// Draw Top-Left Quad.
@@ -58,7 +54,6 @@ namespace JUI
Vector2 tr_computed_pos = abs_pos + Vector2(abs_size.x - tr_quad.maxPoint.x, 0);
JGL::J2D::DrawPartialSprite(texture, tr_computed_pos, tr_quad.minPoint, tr_quad.maxPoint);
// Draw Bottom Left Quad
auto bl_quad = BottomLeftQuad();
Vector2 bl_computed_pos = abs_pos + Vector2(0, abs_size.y - bl_quad.maxPoint.y);
@@ -69,8 +64,6 @@ namespace JUI
Vector2 br_computed_pos = abs_pos + abs_size - br_quad.maxPoint;
JGL::J2D::DrawPartialSprite(texture, br_computed_pos, br_quad.minPoint, br_quad.maxPoint);
// Draw Top-Quad.
Vector2 t_computed_pos = abs_pos + Vector2(tl_quad.maxPoint.x, 0);
auto t_quad = TopQuad();
@@ -91,24 +84,17 @@ namespace JUI
float l_scaling = abs_height_minus_corners / l_quad.maxPoint.y;
JGL::J2D::DrawPartialSprite(texture, l_computed_pos, l_quad.minPoint, l_quad.maxPoint, 0, {0, 0}, {1, l_scaling});
// Draw Right Quad
auto r_quad = RightQuad();
Vector2 r_computed_pos = abs_pos + Vector2(abs_size.x - tr_quad.maxPoint.x, tr_quad.maxPoint.y);
float r_scaling = abs_height_minus_corners / r_quad.maxPoint.y;
JGL::J2D::DrawPartialSprite(texture, r_computed_pos, r_quad.minPoint, r_quad.maxPoint, 0, {0,0}, {1, r_scaling});
// Draw Center Quad
auto c_quad = CenterQuad();
Vector2 c_computed_pos = abs_pos + tl_quad.maxPoint;
Vector2 c_scaling = Vector2(abs_width_minus_corners, abs_height_minus_corners) / c_quad.maxPoint;
JGL::J2D::DrawPartialSprite(texture, c_computed_pos, c_quad.minPoint, c_quad.maxPoint, 0, {0,0}, c_scaling);
//JGL::J2D::End();
}
NineSliceRect::NineSliceRect() : Rect(), ImageBase() {}
@@ -116,6 +102,5 @@ namespace JUI
NineSliceRect::NineSliceRect(Widget *parent) : Rect(parent), ImageBase() {}
NineSliceRect::NineSliceRect(Widget *parent, JGL::Texture *texture) : Rect(parent), ImageBase(texture) {}
}

View File

@@ -1,41 +1,18 @@
#include <JUI/Mixins/Toggleable.hpp>
#include <JUI/Widgets/RadioButton.hpp>
namespace JUI
{
namespace JUI {
RadioButton::RadioButton() : Button(), Toggleable() {}
RadioButton::RadioButton(Widget* parent) : RadioButton() {
this->Parent(parent);
}
/*
void RadioButton::OnToggleOn() {
OnToggleOnEvent.Invoke();
}
void RadioButton::OnToggleOff() {
OnToggleOffEvent.Invoke();
}
*/
void RadioButton::OnClick(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &btn) {
Button::OnClick(mouse_pos, btn);
Toggleable::Update();
/*
toggle = !toggle;
if (toggle) {
OnToggleOn();
} else {
OnToggleOff();
}
*/
}
void RadioButton::Update(float delta)
{
void RadioButton::Update(float delta) {
Button::Update(delta);
}
}

View File

@@ -10,7 +10,6 @@ namespace JUI {
}
void Rect::PreDraw()
{
Vector2 abs_pos = GetAbsolutePosition();
@@ -42,24 +41,17 @@ namespace JUI {
}
}
void Rect::InnerDraw()
{
//J2D::Begin();
void Rect::InnerDraw() {
RectBase::Draw(GetAbsolutePosition(), GetAbsoluteSize());
//J2D::End();
// Draw Child Elements with scissor clipping still active
//Widget::DrawChildWidgets();
}
void Rect::Update(float delta) {
if (IsMouseInside() && !mouse_inside_debounce)
{
if (IsMouseInside() && !mouse_inside_debounce) {
MouseEnter.Invoke(last_known_mouse_pos);
mouse_inside_debounce = true;
}
if (!IsMouseInside() && mouse_inside_debounce)
{
if (!IsMouseInside() && mouse_inside_debounce) {
MouseExit.Invoke(last_known_mouse_pos);
mouse_inside_debounce = false;
}
@@ -86,7 +78,6 @@ namespace JUI {
void Rect::Draw() {
Widget::Draw();
}
}

View File

@@ -4,10 +4,7 @@
using namespace JUI;
void ScrollingRect::RecomputeRenderTarget() {
//
//Rect::PreDraw();
InnerDraw();
//Rect::PostDraw();
}
void ScrollingRect::Draw() {
@@ -16,15 +13,12 @@ void ScrollingRect::Draw() {
// Q: How do we know when something has changed?
RecomputeRenderTarget();
//J2D::DrawRenderTarget(canvas, GetAbsolutePosition());
J2D::DrawPartialRenderTarget(canvas, GetAbsolutePosition(), {0, 0}, Vector2(GetAbsoluteSize().x, GetAbsoluteSize().y));
bool canvas_larger_than_widget = scroll_size > GetAbsoluteSize().y;
float ratio = scroll_size / GetAbsoluteSize().y;
if (vertical_scrollbar_enabled && canvas_larger_than_widget)
{
//std::cout << "Ratio " << ratio << std::endl;
if (vertical_scrollbar_enabled && canvas_larger_than_widget) {
Vector2 vscroll_pos = GetAbsolutePosition() + Vector2(GetAbsoluteSize().x-scrollbar_width, 0);
Vector2 vscroll_size = {scrollbar_width, GetAbsoluteSize().y-scrollbar_width};
J2D::FillRect(Colors::LightGray, vscroll_pos, vscroll_size);
@@ -35,7 +29,6 @@ void ScrollingRect::Draw() {
Vector2 lil_box_pos = GetAbsolutePosition() + GetAbsoluteSize() - lil_box_size;
J2D::FillRect(Colors::DarkGray, lil_box_pos, lil_box_size);
}
}
Vector2 ScrollingRect::CanvasPosition() const {

View File

@@ -183,6 +183,10 @@ namespace JUI {
return;
}
if (blacklist.contains(key.Mnemonic))
{
// TODO: Validate results.
}
if (key == Keys::Backspace) {
if (cursor_position > 0) {
@@ -219,7 +223,7 @@ namespace JUI {
void TextInputForm::DropFocusOnReturn(bool value) { drop_focus_on_return = value;}
std::vector<char> TextInputForm::GetBlacklist() const { return blacklist;}
std::set<std::string> TextInputForm::GetBlacklist() const { return blacklist;}
std::string TextInputForm::InputBuffer() const { return input_buffer;}
@@ -227,4 +231,12 @@ namespace JUI {
void TextInputForm::ClearInputBuffer() { SetInputBuffer(""); }
void TextInputForm::SetBlacklist(const std::set<std::string> &value) {
blacklist = value;
}
void TextInputForm::AddToBlacklist(const std::string& value) {
blacklist.insert(value);
}
}