Working on ContextMenu & UtilityBar

This commit is contained in:
2025-04-29 15:44:01 -05:00
parent 06f27b2436
commit 2fed2ea760
6 changed files with 173 additions and 141 deletions

View File

@@ -93,13 +93,13 @@ namespace JUI {
auto* btn = AddButton(name);
// TODO: Is this memory safe at all?
btn->OnClickEvent += [&] (auto a, auto b) mutable {
btn->OnClickEvent += [&, callback] (auto a, auto b) mutable {
callback();
};
return btn;
}
std::pair<TextButton*, ContextMenu*> AddSubmenu(const std::string& name);
ContextMenu* AddSubmenu(const std::string& name);
/// Sets whether the widget will automatically hide itself when the mouse leaves its bounding box.

View File

@@ -16,6 +16,9 @@ namespace JUI
{
class UtilityBar;
// TODO: Automatically adjust to make this widget's width that of the widest subcomponent button.
/// A horizontal toolbar widget that is designed to be used at the top of windows, and subwindow widgets.
/// Convenience functions are provided for adding tool buttons and sub-menus.
/// @see class ContextMenu
@@ -31,6 +34,12 @@ namespace JUI
TextButton* AddButton(const std::string& name);
TextButton* AddButton(const std::string& name, const std::function<void()>& callback) {
auto* button = AddButton(name);
button->OnClickEvent += [&, callback] (auto a, auto b) mutable { callback();};
return button;
}
protected:
//std::vector<TextButton*> buttons;

270
main.cpp
View File

@@ -30,111 +30,135 @@
#include <JUI/Widgets/Collapsible.hpp>
#include <JUI/Widgets/CommandLine.hpp>
using namespace JUI;
float ui_scale = 1.f;
float accum = 0;
int iter = 0;
JUI::Scene* scene;
JGL::Texture* sample_texture;
JGL::Texture* slicer;
JUI::VerticalListLayout* list;
JUI::Window* scroller_window;
JUI::ScrollingRect* scroller;
JUI::TextRect* widget_count;
JUI::CommandLine* console;
JUI::Window* nineslice_demo_window;
JUI::UtilityBar* topbar;
int count_descendants(JUI::Widget* w) {
return w->GetDescendants().size();
}
JUI::Scene* CreateScene() {
using namespace JUI;
JUI::Window* CreateInfoboxWindow(JUI::Widget* root) {
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);
/// Constructs, applies layout, and returns, a JUI::Window which demonstrates the NineSliceRect capability.
/// @param root The Widget to parent the NineSliceRect to.
JUI::Window* CreateNinesliceWindow(JUI::Widget* root) {
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);
nineslice_demo_window->Close();
nineslice_demo_window->TitlebarHeight(12); {
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->Center();
a->SetContent("This is a virtual window.");
}
TextRect* b = new TextRect(list); {
b->SetTextSize(16); b->Size({0, 20, 1, 0}); b->Center();
b->SetContent("You can drag it around via left-click, and resize via right-click. Isn't that cool??");
}
}
}
}
return nineslice_demo_window;
}
console = new JUI::CommandLine(root);
console->Close();
JUI::UtilityBar* CreateUtilityBar(JUI::Widget* root) {
auto* topbar = new UtilityBar(root); {
topbar->ZIndex(3);
JUI::UILogs.OnLog += [&] (const std::string& msg, Color4 c){ console->Log(msg, c);};
// TODO: Make it so that when you mouse over another option in the *parent* menu, it closes any open submenus.
auto* demos = topbar->AddSubmenu("Demos");
{
demos->AddButton("9-Slice Widget Demo",
[&] mutable { nineslice_demo_window->Toggle(); });
auto* topbar = new UtilityBar(root);
topbar->ZIndex(3);
demos->AddButton("Scroll Widget Demo", [&] mutable {});
auto* demos = topbar->AddSubmenu("Demos");
demos->AddButton("Command Line",
[&] mutable{ console->Toggle();});
demos->AddButton("9-Slice Widget Demo",
[&] mutable { nineslice_demo_window->Toggle(); });
auto* test_sub = demos->AddSubmenu("Submenu"); {
test_sub->AddButton("Apples");
test_sub->AddButton("Bananas");
test_sub->AddButton("Oranges");
}
}
demos->AddButton("Scroll Widget Demo", [&] mutable {});
demos->AddButton("Command Line",
[&] mutable{ console->Toggle();});
auto* edit = topbar->AddButton("Edit");
auto* view = topbar->AddSubmenu("View"); {
view->AddButton("Increase UI Scale 5%", [&]mutable {
ui_scale += 0.05f;
});
view->AddButton("Decrease UI Scale 5%", [&] mutable{
ui_scale -= 0.05f;
});
}
//auto* file_tt = new JUI::Tooltip(file);
//file_tt->SetContent("Tooltip");
auto* help = topbar->AddButton("About", [&] mutable {
/*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);
};
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* open_scroll = ctx_menu->AddItem("Scroll Widget Demo");
}
return topbar;
}
open_scroll->OnClickEvent += [&] (Vector2 pos, JUI::MouseButton btn) {};
//ctx_menu->AddItem("");
auto* open_console = ctx_menu->AddItem("Command Line");
open_console->OnClickEvent += [&] (Vector2 pos, JUI::MouseButton btn)mutable {
console->Open();
};
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);
/// Constructs, styles, and returns a JUI::Rect which contains a sample of each widget in action.
JUI::Rect* CreateWidgetList(JUI::Widget* root) {
auto* widget_list = 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);
widget_list->Size({0, -24, 0.2f, 1.f});
widget_list->Position({0, 24, 0, 0});
widget_list->BGColor(Colors::Grays::Gainsboro);
auto* column_layout = new VerticalListLayout(column_rect);
auto* column_layout = new VerticalListLayout(widget_list);
auto* btn_container1 = new Rect(column_layout);
btn_container1->Size({100_percent, 24_px});
@@ -264,73 +288,75 @@ JUI::Scene* CreateScene() {
radio_c_label->SetContent("C ");
radio_c_label->SetTextSize(12);
//auto* separator_a = new Rect(radio_btn_set_layout());
return widget_list;
}
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");
JUI::Window* CreateScrollDemoWindow(Widget* root) {
auto* scroller_demo = new JUI::Window(root);
scroller_demo->Position({10_percent, 10_percent});
scroller_demo->Size({30_percent, 25_percent});
scroller_demo->SetTitle("ScrollingRect Demonstration");
Tween* t = other_window->TweenPosition({50_percent, 50_percent}, {.time = 5});
Tween* t = scroller_demo->TweenPosition({50_percent, 50_percent}, {.time = 5});
t->Completed += [] () { std::cout << "Tween type test!!" << std::endl; };
scroller = new JUI::ScrollingRect(other_window->ViewportInstance());
scroller = new JUI::ScrollingRect(scroller_demo->ViewportInstance());
scroller->Size({100_percent, 100_percent});
scroller->BGColor(Colors::Reds::LightCoral);
list = new JUI::VerticalListLayout(scroller);
list->LayoutOrder(JUI::LayoutOrder::V::BOTTOM);
return scroller_demo;
}
JUI::Window* CreateAnimationDemoWindow() {
return nullptr;
}
JUI::Scene* CreateScene() {
auto *root = new Scene();
topbar = CreateUtilityBar(root);
nineslice_demo_window = CreateNinesliceWindow(root);
scroller_window = CreateScrollDemoWindow(root);
console = new JUI::CommandLine(root); {
console->Close();
JUI::UILogs.OnLog += [&] (const std::string& msg, Color4 c){ console->Log(msg, c);};
}
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 //
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:
@@ -356,6 +382,8 @@ public:
accum += elapsed;
scene->Update(elapsed);
/// As a demo, change the scene's UI scale.
scene->GlobalUIScale({ui_scale, ui_scale});
if (accum > 1.f) {
iter--;
@@ -425,13 +453,13 @@ public:
}
void OnMouseWheel(const ReWindow::MouseWheelEvent &w) override {
scale += w.WheelMovement * 0.125f;
ui_scale += w.WheelMovement * 0.125f;
if (scene->ObserveMouseWheel(w.WheelMovement))
return;
/// As a demo, change the scene's UI scale.
scene->GlobalUIScale({scale, scale});
scene->GlobalUIScale({ui_scale, ui_scale});
}
};

View File

@@ -38,12 +38,20 @@ namespace JUI {
return line_item;
}
std::pair<TextButton *, ContextMenu *> ContextMenu::AddSubmenu(const std::string &name) {
ContextMenu * ContextMenu::AddSubmenu(const std::string &name) {
auto* btn = AddButton(name);
// TODO: For auto-hiding, check if the widget is over a submenu as well.
// TODO: Duplicated code with UtilityBar.cpp
auto* submenu = new JUI::ContextMenu(this);
submenu->AnchorPoint({1, 0});
auto* submenu = new JUI::ContextMenu(btn);
submenu->AnchorPoint({0, 0});
submenu->Position({100_percent, 0_percent});
btn->OnClickEvent += [submenu] (auto a, auto b) mutable {
submenu->Visible(true);
};
return submenu;
}
void ContextMenu::CloseOnHoverLoss(bool value) { close_on_hover_loss = value;}

View File

@@ -25,29 +25,16 @@ namespace JUI {
ContextMenu * UtilityBar::AddSubmenu(const std::string &name) {
auto* btn = AddButton(name);
//buttons.push_back(btn);
auto* submenu = new JUI::ContextMenu(this);
//submenu->AnchorPoint({0, 1});
// TODO: Fix AnchorPoint behavior!!
submenu->Position({0_percent, 100_percent});
submenu->Visible(true);
//submenus.push_back(submenu);
btn->OnClickEvent += [submenu] (auto a, auto b) mutable {
submenu->Visible(true);
};
//conn.Invoke({}, MouseButton::Left);
// TODO: We seem to have duplicate events
// Hoverable::OnExitEvent
// RectBase::MouseExit
//submenu->MouseExit += [&] (auto) mutable {
// submenu->Visible(false);
//};
return submenu;
}

View File

@@ -226,8 +226,8 @@ namespace JUI {
RectBase::Draw(this->GetAbsolutePosition(), this->GetAbsoluteSize());
Widget::Draw();
J2D::DrawString(Colors::Black, std::format("hover: {}", hovered), this->GetAbsolutePosition().x, this->GetAbsolutePosition().y, 1.f, 10);
J2D::DrawString(Colors::Black, std::format("mbutton: {}", mb_state), this->GetAbsolutePosition().x, this->GetAbsolutePosition().y+10, 1.f, 10);
//J2D::DrawString(Colors::Black, std::format("hover: {}", hovered), this->GetAbsolutePosition().x, this->GetAbsolutePosition().y, 1.f, 10);
//J2D::DrawString(Colors::Black, std::format("mbutton: {}", mb_state), this->GetAbsolutePosition().x, this->GetAbsolutePosition().y+10, 1.f, 10);
}
void Window::SetTitle(const std::string &title) {