452 lines
14 KiB
C++
452 lines
14 KiB
C++
/// 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 main.cpp
|
|
/// @desc Demo Program Entry Point
|
|
/// @edit 2024-07-11
|
|
|
|
#include <iostream>
|
|
#include <JGL/JGL.h>
|
|
#include <JUI/Widgets/Rect.hpp>
|
|
#include <JUI/Widgets/RadioButton.hpp>
|
|
#include <JUI/Widgets/Window.hpp>
|
|
#include <JUI/Widgets/Scene.hpp>
|
|
#include <JUI/Widgets/Text.hpp>
|
|
#include <JUI/Widgets/ListLayout.hpp>
|
|
#include <JUI/Widgets/TextRect.hpp>
|
|
#include <JUI/Widgets/Image.hpp>
|
|
#include <JUI/Widgets/Slider.hpp>
|
|
#include <JUI/Widgets/ScrollingRect.hpp>
|
|
#include <JUI/Widgets/UtilityBar.hpp>
|
|
#include <JUI/Widgets/Checkbox.hpp>
|
|
#include <JUI/Widgets/TextInputForm.hpp>
|
|
#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;
|
|
JGL::Texture* slicer;
|
|
JUI::VerticalListLayout* list;
|
|
JUI::ScrollingRect* scroller;
|
|
JUI::TextRect* widget_count;
|
|
|
|
int count_descendants(JUI::Widget* w) {
|
|
return w->GetDescendants().size();
|
|
}
|
|
|
|
JUI::Scene* CreateScene() {
|
|
using namespace JUI;
|
|
|
|
auto *root = new Scene();
|
|
|
|
|
|
/*root->DescendantAdded += [&] (auto* node) {
|
|
widget_count->SetContent("Widgets: " + count_descendants(root));
|
|
};
|
|
|
|
root->DescendantRemoved += [&] (auto* node) {
|
|
widget_count->SetContent("Widgets: " + count_descendants(root));
|
|
};*/
|
|
|
|
auto* nineslice_demo_window = new JUI::Window(root);
|
|
nineslice_demo_window->Name("NineSlice Demo Window");
|
|
nineslice_demo_window->CornerRounding(10);
|
|
nineslice_demo_window->Size({50_percent, 50_percent});
|
|
nineslice_demo_window->SetTitle("9-Slice Demo");
|
|
nineslice_demo_window->Visible(false);
|
|
nineslice_demo_window->TitlebarHeight(12);
|
|
|
|
auto* topbar = new UtilityBar(root);
|
|
topbar->ZIndex(3);
|
|
auto* file = topbar->AddButton("Demos");
|
|
|
|
auto* file_tt = new JUI::Tooltip(file);
|
|
file_tt->SetContent("Tooltip");
|
|
|
|
file->OnClickEvent += [&, root, nineslice_demo_window] (Vector2 pos, JUI::MouseButton btn)
|
|
{
|
|
auto* ctx_menu = new ContextMenu(root);
|
|
ctx_menu->Position(UDim2(0,20,0,0));
|
|
auto* open_nineslice = ctx_menu->AddItem("9-Slice Widget Demo");
|
|
|
|
open_nineslice->OnClickEvent += [&, nineslice_demo_window] (Vector2 pos, JUI::MouseButton btn) {
|
|
nineslice_demo_window->Visible(true);
|
|
};
|
|
|
|
auto* open_scroll = ctx_menu->AddItem("Scroll Widget Demo");
|
|
|
|
open_scroll->OnClickEvent += [&] (Vector2 pos, JUI::MouseButton btn) {};
|
|
ctx_menu->AddItem("");
|
|
ctx_menu->ZIndex(3);
|
|
};
|
|
|
|
topbar->AddButton("Edit");
|
|
auto* view = topbar->AddButton("View");
|
|
|
|
topbar->AddButton("Help");
|
|
|
|
widget_count = new JUI::TextRect(topbar);
|
|
widget_count->Size(UDim2(100,20,0,0));
|
|
widget_count->AnchorPoint({1, 0});
|
|
widget_count->Position({100_percent, 0_percent});
|
|
widget_count->BGColor(Colors::Transparent);
|
|
widget_count->SetTextColor(Colors::Black);
|
|
widget_count->BorderColor(Colors::Transparent);
|
|
widget_count->BorderWidth(0);
|
|
widget_count->Center();
|
|
widget_count->AlignRight();
|
|
|
|
//auto* horizontal = new HorizontalListLayout(root);
|
|
//horizontal->ZIndex(1);
|
|
|
|
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* column_layout = new VerticalListLayout(column_rect);
|
|
|
|
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, 20, 0.32f, 0});
|
|
button->SetTextColor(Colors::Black);
|
|
button->SetContent("Button");
|
|
button->AlignLeft();
|
|
//button->Padding(5_px);
|
|
|
|
auto* tt2 = new JUI::Tooltip(button);
|
|
tt2->SetContent("Test 123");
|
|
|
|
auto* button2 = new TextButton(btn_h_layout1);
|
|
//button2->Position({5, 105, 0, 0});
|
|
button2->Size({0, 20, 0.32f, 0});
|
|
button2->SetTextColor(Colors::Black);
|
|
button2->SetContent("Button");
|
|
button2->AlignCenterHorizontally();
|
|
|
|
auto* button3 = new TextButton(btn_h_layout1);
|
|
//button2->Position({5, 105, 0, 0});
|
|
button3->Size({0, 20, 0.32f, 0});
|
|
button3->SetTextColor(Colors::Black);
|
|
button3->SetContent("Button");
|
|
button3->AlignRight();
|
|
|
|
auto* btn_container2 = new Rect(column_layout);
|
|
btn_container2->Size({100_percent, 24_px});
|
|
btn_container2->BGColor(Colors::DarkGray);
|
|
|
|
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->BorderWidth(0);
|
|
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(column_layout);
|
|
input_form->Size({0,24, 1, 0});
|
|
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});
|
|
other_window->SetTitle("Another Window");
|
|
|
|
Tween* t = other_window->TweenPosition({50_percent, 50_percent}, {.time = 5});
|
|
|
|
t->Completed += [] () { std::cout << "Tween type test!!" << std::endl; };
|
|
|
|
scroller = new JUI::ScrollingRect(other_window->ViewportInstance());
|
|
scroller->Size({100_percent, 100_percent});
|
|
scroller->BGColor(Colors::Reds::LightCoral);
|
|
|
|
list = new JUI::VerticalListLayout(scroller);
|
|
list->LayoutOrder(JUI::LayoutOrder::V::BOTTOM);
|
|
|
|
//nineslice_demo_window->Padding(1_px);
|
|
// End Window //
|
|
|
|
auto* nineslice = new JUI::NineSliceRect(nineslice_demo_window);
|
|
nineslice->Content(slicer);
|
|
nineslice->Size({100_percent, 100_percent});
|
|
nineslice->BGColor(Colors::Transparent);
|
|
nineslice->TopLeftQuad({{0,0},{96,96}});
|
|
nineslice->TopRightQuad({{384-96,0},{96,96}});
|
|
nineslice->BottomLeftQuad({ {0, 378-96}, {96, 96}});
|
|
nineslice->BottomRightQuad({ {384-96, 378-96}, {96, 96}});
|
|
|
|
nineslice->TopQuad({ {96, 0}, {192, 96} });
|
|
nineslice->BottomQuad({ {96, 378-96}, {192, 96} });
|
|
nineslice->RightQuad({{384-(96), 96}, {96, 378-(96*2)}});
|
|
nineslice->LeftQuad({{0, 96}, {96, 378-(96*2)}});
|
|
|
|
nineslice->CenterQuad({{96, 96}, {384-(96*2), 378-(96*2)}});
|
|
|
|
auto darkie = new JUI::Image(nineslice_demo_window->ViewportInstance(), sample_texture);
|
|
darkie->FitImageToParent(true);
|
|
darkie->Color({255,255,255,128});
|
|
|
|
auto list = new VerticalListLayout(nineslice_demo_window->ViewportInstance());
|
|
list->Padding(10_px);
|
|
|
|
TextRect* a = new TextRect(list);
|
|
a->SetTextSize(16);
|
|
a->Size({0, 20, 1, 0});
|
|
//a->FitText(true);
|
|
a->Center();
|
|
a->SetContent("This is a virtual window.");
|
|
|
|
|
|
TextRect* b = new TextRect(list);
|
|
b->SetTextSize(16);
|
|
b->SetContent("You can drag it around via left-click, and resize via right-click. Isn't that cool??");
|
|
//b->FitText(true);
|
|
b->Size({0, 20, 1, 0});
|
|
b->Center();
|
|
|
|
root->SetViewportSize({800, 600});
|
|
|
|
return root;
|
|
}
|
|
|
|
float scale = 1.f;
|
|
float accum = 0;
|
|
int iter = 0;
|
|
|
|
class JUIDevelopmentTestWindow : public ReWindow::OpenGLWindow {
|
|
public:
|
|
|
|
void initGL() {
|
|
auto size = GetSize();
|
|
auto vec_size = Vector2i(size.x, size.y);
|
|
bool result = JGL::Init(vec_size, 0.f, 0.f);
|
|
JGL::Update(vec_size);
|
|
glClearColor(0.f, 0.f, 0.f, 0.f);
|
|
|
|
// TODO: Delete when we update to the next release of JGL
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // NOTE: This MUST be called for text rendering to work properly!!!
|
|
}
|
|
|
|
void Update(float elapsed)
|
|
{
|
|
|
|
widget_count->SetContent(std::format("Widgets: {}", count_descendants(scene)));
|
|
|
|
using namespace JUI::UDimLiterals;
|
|
|
|
accum += elapsed;
|
|
|
|
scene->Update(elapsed);
|
|
|
|
if (accum > 1.f)
|
|
{
|
|
iter--;
|
|
accum = 0.f;
|
|
auto* text = new JUI::TextRect(list);
|
|
text->Size({50_percent, 20_px});
|
|
text->ZIndex(iter);
|
|
text->LayoutOrder(-iter);
|
|
text->SetContent(std::format("{} Sampled Delta: {}ms", -iter, Math::Floor(elapsed*1000.f)));
|
|
scroller->scroll_size += 20;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void Draw()
|
|
{
|
|
scene->Draw();
|
|
}
|
|
|
|
JUIDevelopmentTestWindow(const std::string& title, int w, int h) : ReWindow::OpenGLWindow(title, w, h, 2, 1) {}
|
|
void OnRefresh(float elapsed) override {
|
|
Update(elapsed);
|
|
auto size = GetSize();
|
|
Vector2i vSize = Vector2i(size.x, size.y);
|
|
JGL::Update(vSize);
|
|
scene->SetViewportSize(Vector2(vSize));
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
Draw();
|
|
|
|
this->SwapBuffers();
|
|
}
|
|
|
|
|
|
|
|
bool OnResizeRequest(const ReWindow::WindowResizeRequestEvent& e) override {
|
|
std::cout << "RESIZED:" << e.Size.x << ", " << e.Size.y << std::endl;
|
|
return true;
|
|
}
|
|
|
|
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &) override
|
|
{
|
|
|
|
}
|
|
|
|
void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent &) override
|
|
{
|
|
|
|
}
|
|
|
|
void OnMouseMove(const ReWindow::MouseMoveEvent &) override
|
|
{
|
|
|
|
}
|
|
|
|
|
|
void OnKeyDown(const ReWindow::KeyDownEvent &) override {
|
|
|
|
}
|
|
|
|
void OnMouseWheel(const ReWindow::MouseWheelEvent &w) override {
|
|
scale += w.WheelMovement * 0.125f;
|
|
scene->GlobalUIScale({scale, scale});
|
|
}
|
|
};
|
|
|
|
|
|
void inspect_widget(JUI::Widget* w, int depth = 1) {
|
|
std::cout << std::setw(depth*4);
|
|
std::cout << w->Name() << std::endl;
|
|
std::cout << std::setw(0);
|
|
depth++;
|
|
for (auto* child : w->GetChildren())
|
|
{
|
|
inspect_widget(child, depth);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int main() {
|
|
using namespace ReWindow;
|
|
// TODO: Find out new jlog api for silencing specific loggers.
|
|
|
|
ReWindow::Logger::Debug.EnableConsole(false);
|
|
|
|
auto* window = new JUIDevelopmentTestWindow("Test Window", 800, 600);
|
|
//window->SetRenderer(RenderingAPI::OPENGL);
|
|
window->Open();
|
|
window->initGL();
|
|
window->SetFullscreen(false);
|
|
window->SetVsyncEnabled(false);
|
|
window->SetResizable(true);
|
|
|
|
JGL::Update({800, 600});
|
|
|
|
sample_texture = new JGL::Texture("assets/ld.png");
|
|
slicer = new JGL::Texture("assets/9slice.png");
|
|
scene = CreateScene();
|
|
|
|
inspect_widget(scene);
|
|
|
|
|
|
window->OnResizeRequestEvent += [&] (ReWindow::WindowResizeRequestEvent e){
|
|
Vector2i size = Vector2i(e.Size.x, e.Size.y);//window->getLastKnownResize();
|
|
scene->SetViewportSize(Vector2(size));
|
|
std::cout << size.x << "," << size.y << std::endl;
|
|
JGL::Update(size);
|
|
};
|
|
|
|
window->OnMouseMoveEvent += [&] (MouseMoveEvent e)
|
|
{
|
|
scene->ObserveMouseMovement(Vector2(e.Position.x, e.Position.y));
|
|
};
|
|
|
|
window->OnMouseButtonUpEvent += [&] (MouseButtonUpEvent e) {
|
|
/// Invalid operands to binary expression 'MouseButton' and 'const MouseButton'
|
|
if (e.Button == MouseButtons::Left)
|
|
scene->ObserveMouseInput(JUI::MouseButton::Left, false);
|
|
if (e.Button == MouseButtons::Middle)
|
|
scene->ObserveMouseInput(JUI::MouseButton::Middle, false);
|
|
if (e.Button == MouseButtons::Right)
|
|
scene->ObserveMouseInput(JUI::MouseButton::Right, false);
|
|
};
|
|
|
|
window->OnMouseButtonDownEvent += [&] (MouseButtonDownEvent e) {
|
|
if (e.Button == MouseButtons::Left)
|
|
scene->ObserveMouseInput(JUI::MouseButton::Left, true);
|
|
if (e.Button == MouseButtons::Middle)
|
|
scene->ObserveMouseInput(JUI::MouseButton::Middle, true);
|
|
if (e.Button == MouseButtons::Right)
|
|
scene->ObserveMouseInput(JUI::MouseButton::Right, true);
|
|
};
|
|
|
|
window->OnMouseButtonDownEvent += [&] (MouseButtonDownEvent e) {};
|
|
|
|
window->OnKeyDownEvent += [&] (KeyDownEvent e) {
|
|
scene->ObserveKeyInput(e.key, true);
|
|
};
|
|
|
|
window->OnKeyUpEvent += [&] (KeyUpEvent e) {
|
|
scene->ObserveKeyInput(e.key, false);
|
|
};
|
|
|
|
while (window->IsAlive()) {
|
|
//window->PollEvents();
|
|
window->ManagedRefresh();
|
|
}
|
|
return 0;
|
|
}
|