Clickable and Hoverable classes created, Button class rewritten to use them, Window class WIP, demo radio button is just default button behavior now.

This commit is contained in:
2024-07-31 12:58:36 -04:00
parent a28048b19b
commit 34a9fe728b
7 changed files with 171 additions and 192 deletions

View File

@@ -21,6 +21,11 @@
namespace JUI
{
enum class MouseBtn {
Left = 1,
Middle = 2,
Right = 3
};
using namespace J3ML::Math;
using namespace J3ML::LinearAlgebra;
@@ -195,6 +200,8 @@ namespace JUI
/// In a well-formed JUI menu, this **should** always be a Scene.
Widget* GetFamilyTreeRoot() const;
bool IsMouseInside() const;
public:
/// Renders the widget to the current OpenGL Context using JGL.
/// The user should call this on their Scene instances only. JUI will handle the rest.
@@ -211,13 +218,14 @@ namespace JUI
/// This is designed in such a way that the end-user can plug this into their existing code.
/// The user should call this on their Scene instances only. JUI will handle the rest.
/// See ReWindowIntegrationDemo for an example.
virtual void ObserveMouseInput(int btn, bool pressed);
virtual void ObserveMouseInput(MouseBtn btn, bool pressed);
protected:
void DrawChildWidgets();
void UpdateChildWidgets(float delta);
protected:
bool lmb_state;
bool prev_lmb_state;
MouseBtn mbtn;
bool mb_state;
bool prev_mb_state;
//int last_known_mouse_button;
//bool last_known_mouse_button_state;
Vector2 last_known_mouse_pos;
@@ -242,9 +250,40 @@ namespace JUI
Widget* prev = nullptr;
};
class Hoverable {
public:
Event<Vector2> OnHoverEvent;
Event<Vector2> OnExitEvent;
public:
bool IsHovered() const { return hovered; };
public:
virtual void OnHover(const Vector2& MousePos) {
OnHoverEvent.Invoke(MousePos);
};
virtual void OnExit(const Vector2& MousePos) {
OnExitEvent.Invoke(MousePos);
};
protected:
bool hovered;
bool hover_debounce;
};
class Clickable
{
public:
Event<Vector2, MouseBtn> OnClickEvent;
Event<Vector2, MouseBtn, bool> OnReleaseEvent;
public:
bool IsClicked() const { return clicked; };
public:
virtual void OnClick(const Vector2& MousePos, const MouseBtn& MouseButton) {
OnClickEvent.Invoke(MousePos, MouseButton);
};
virtual void OnRelease(const Vector2& MousePos, const MouseBtn& MouseButton, bool MouseStillOver) {
OnReleaseEvent.Invoke(MousePos, MouseButton, MouseStillOver);
};
protected:
bool clicked = false;
bool click_debounce = false;
};
}

View File

@@ -14,28 +14,15 @@ namespace JUI
Disabled
};
enum class MouseBtn {
Left = 1,
Middle = 2,
Right = 3
};
class Button : public Widget, public RectBase, public Clickable
//class Button : public Widget, public RectBase, public Clickable, public Hoverable
class Button : public Widget, public RectBase, public Clickable, public Hoverable
{
public:
Event<Vector2> OnHoverEvent;
Event<Vector2> OnExitEvent;
Event<Vector2> OnPressEvent;
Event<Vector2, bool> OnReleaseEvent;
public:
Button();
explicit Button(Widget* parent);
~Button() override {};
// TODO: This is duplicated between here and Rect widget
// Ideally, it would be in RectBase, or better yet
// the Clickable mixin class for all input-related shit.
bool IsMouseInside() const;
public:
// TODO: These suffice for now as a proof-of-concept
// But I want more sophisticated style-animation support
@@ -50,8 +37,6 @@ namespace JUI
Color4 GetPressedBorderColor() const;
Color4 GetDisabledBorderColor() const;
bool IsHovered() const;
bool IsPressed() const;
bool IsEnabled() const;
@@ -60,26 +45,5 @@ namespace JUI
void Update(float delta) override;
void GotoHoverColor();
#pragma region Overrides
virtual void OnHover(const Vector2& MousePos) {
OnHoverEvent.Invoke(MousePos);
};
virtual void OnExit(const Vector2& MousePos) {
OnExitEvent.Invoke(MousePos);
};
virtual void OnPress(const Vector2& MousePos) {
//BGColor(GetPressedBGColor());
//BorderColor(GetPressedBorderColor());
OnPressEvent.Invoke(MousePos);
};
virtual void OnRelease(const Vector2& MousePos, bool MouseStillOver) {
OnReleaseEvent.Invoke(MousePos, MouseStillOver);
};
#pragma endregion
protected:
bool hover_debounce;
bool is_pressed;
private:
};
}

View File

@@ -6,6 +6,7 @@
#include "JUI/Base/RectBase.hpp"
#include "JUI/Widgets/Rect.hpp"
#include "JUI/Widgets/Text.hpp"
#include "JUI/Widgets/Button.hpp"
namespace JUI
{
@@ -26,7 +27,7 @@ namespace JUI
class Dockable {};
class Window : public Widget, public RectBase, public Draggable, public Dockable
class Window : public Widget, RectBase, public Clickable, public Draggable, public Dockable
{
public:
Window();
@@ -42,7 +43,18 @@ namespace JUI
Text* GetTitleInstance();
Rect* GetTopbarInstance();
Rect* GetViewportInstance();
protected:
JUI::Rect* Topbar;
JUI::Rect* Viewport;
JUI::Text* TitleLabel;
std::string title = "JUI Window";
bool resizable = true;
bool resizing = false;
bool draggable = true;
bool dockable = false;
int titlebar_height = 16;
Vector2 max_size;
Vector2 min_size;
};
}

View File

@@ -13,76 +13,12 @@
#include <JGL/JGL.h>
#include "JUI/Widgets/Rect.hpp"
#include "JUI/Widgets/Button.hpp"
#include "JUI/Widgets/Window.hpp"
#include "JUI/Widgets/Scene.hpp"
#include "JUI/Widgets/Text.hpp"
#include <rewindow/types/window.h>
#include <jlog/jlog.hpp>
class CoolButton : public JUI::Button {
public:
CoolButton();
CoolButton(Widget* parent);
~CoolButton() override {};
void Update(float delta);
void OnHover(const Vector2& MousePos);
void OnExit(const Vector2& MousePos);
void OnPress(const Vector2& MousePos);
void OnRelease(const Vector2& MousePos, bool MouseStillOver);
};
CoolButton::CoolButton(): Button() {}
CoolButton::CoolButton(Widget *parent): Button(parent) {};
void CoolButton::Update(float delta) {
if (IsHovered() && !hover_debounce) {
OnHover(last_known_mouse_pos);
hover_debounce = true;
}
if (!IsHovered() && hover_debounce) {
OnExit(last_known_mouse_pos);
hover_debounce = false;
}
if (hover_debounce && (lmb_state == true && prev_lmb_state != true)) {
if (!is_pressed) {
OnPress(last_known_mouse_pos);
is_pressed = true;
} else {
OnRelease(last_known_mouse_pos, lmb_state);
is_pressed = false;
}
}
//if (IsPressed() && (lmb_state == true && prev_lmb_state == true)) {
// OnRelease(last_known_mouse_pos, IsHovered());
// is_pressed = false;
//}
prev_lmb_state = lmb_state;
}
void CoolButton::OnHover(const Vector2& MousePos) {
DEBUG("Hovered CoolButton")
OnHoverEvent.Invoke(MousePos);
}
void CoolButton::OnExit(const Vector2& MousePos) {
DEBUG("Exited CoolButton")
OnExitEvent.Invoke(MousePos);
}
void CoolButton::OnPress(const Vector2& MousePos) {
DEBUG("Pressed CoolButton")
OnPressEvent.Invoke(MousePos);
}
void CoolButton::OnRelease(const Vector2& MousePos, bool MouseStillOver) {
DEBUG("Released CoolButton")
OnReleaseEvent.Invoke(MousePos, MouseStillOver);
}
JGL::Font FreeSans;
JUI::Scene* scene;
@@ -124,7 +60,7 @@ JUI::Scene* CreateScene() {
// Button //
CoolButton* button_element = new CoolButton(root);
Button* button_element = new Button(root);
button_element->SetName("BobJim");
button_element->BGColor({0,0,64});
button_element->SetSize({0, 0, 0.1f, 0.1f});
@@ -135,10 +71,10 @@ JUI::Scene* CreateScene() {
DEBUG(std::format("Just testing shit {} {}", bpos.X.Scale, bpos.Y.Scale));
//exit(1);
button_element->SetPosition({bpos.X.Pixels, bpos.Y.Pixels, bpos.X.Scale - 0.2f, 0}); // I don't know how to use sx and sy - maxine
button_element->OnPressEvent += [rect_element] (Vector2 MouseCoords) {
button_element->OnClickEvent += [rect_element] (Vector2 MouseCoords, JUI::MouseBtn mbtn) {
rect_element->BGColor({64, 0, 0});
};
button_element->OnReleaseEvent += [rect_element] (Vector2 MouseCoords, bool MouseStillOver) {
button_element->OnReleaseEvent += [rect_element] (Vector2 MouseCoords, JUI::MouseBtn mbtn, bool MouseStillOver) {
rect_element->BGColor({0, 64, 0});
};
Text* btntext = new Text(button_element);
@@ -148,6 +84,10 @@ JUI::Scene* CreateScene() {
btntext->SetTextColor({255, 0, 0});
// End Button //
// Window //
JUI::Window win_element = JUI::Window();
// End Window //
root->SetViewportSize(800, 600);
@@ -236,22 +176,22 @@ int main()
window->OnMouseButtonUpEvent += [&] (MouseButtonUpEvent e) {
/// Invalid operands to binary expression 'MouseButton' and 'const MouseButton'
if (e.Button == MouseButtons::Left)
scene->ObserveMouseInput(1, false);
scene->ObserveMouseInput(JUI::MouseBtn::Left, false);
if (e.Button == MouseButtons::Middle)
scene->ObserveMouseInput(2, false);
scene->ObserveMouseInput(JUI::MouseBtn::Middle, false);
if (e.Button == MouseButtons::Right)
scene->ObserveMouseInput(3, false);
scene->ObserveMouseInput(JUI::MouseBtn::Right, false);
};
window->OnMouseButtonDownEvent += [&] (MouseButtonDownEvent e) {
DEBUG(std::format("FUCK {} {}", e.Button.ButtonIndex, MouseButtons::Left.ButtonIndex))
if (e.Button == MouseButtons::Left)
scene->ObserveMouseInput(1, true);
scene->ObserveMouseInput(JUI::MouseBtn::Left, true);
if (e.Button == MouseButtons::Middle)
scene->ObserveMouseInput(2, true);
scene->ObserveMouseInput(JUI::MouseBtn::Middle, true);
if (e.Button == MouseButtons::Right)
scene->ObserveMouseInput(3, true);
scene->ObserveMouseInput(JUI::MouseBtn::Right, true);
};
window->OnMouseButtonDownEvent += [&] (MouseButtonDownEvent e) {};

View File

@@ -265,12 +265,21 @@ namespace JUI {
return parent->GetFamilyTreeRoot();
}
void Widget::ObserveMouseInput(int btn, bool pressed) {
if (btn == 1)
lmb_state = pressed;
bool Widget::IsMouseInside() const {
float x = last_known_mouse_pos.x;
float y = last_known_mouse_pos.y;
auto pos = GetAbsolutePosition();
auto size = GetAbsoluteSize();
DEBUG(std::format("BTN {}", btn))
DEBUG(std::format("PRESSED {}", pressed))
if (x > pos.x && y > pos.y && x < pos.x + size.x && y < pos.y + size.y) {
return true;
}
return false;
}
void Widget::ObserveMouseInput(MouseBtn btn, bool pressed) {
mbtn = btn;
mb_state = pressed;
for (Widget* child : children)
{

View File

@@ -6,34 +6,10 @@ namespace JUI
{
Button::Button(): Widget() {}
Button::Button(Widget *parent): Button() {
Button::Button(Widget *parent): Clickable() {
this->SetParent(parent);
};
// We need to like make this generic as part of RectBase
bool Button::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) {
DEBUG("IM A BUTTON")
return true;
}
return false;
}
bool Button::IsPressed() const {
return is_pressed;
}
bool Button::IsHovered() const {
return IsMouseInside();
}
void Button::Draw() {
if (!visible)
return;
@@ -57,7 +33,6 @@ namespace JUI
glEnable(GL_SCISSOR_TEST);
}
RectBase::Draw(abs_pos, abs_size);
// Draw Child Elements with scissor clipping still active
@@ -77,6 +52,8 @@ namespace JUI
}
void Button::Update(float delta) {
hovered = IsMouseInside();
if (IsHovered() && !hover_debounce) {
OnHover(last_known_mouse_pos);
hover_debounce = true;
@@ -87,49 +64,18 @@ namespace JUI
hover_debounce = false;
}
if (hover_debounce && (lmb_state == true && prev_lmb_state != true)) {
OnPress(last_known_mouse_pos);
is_pressed = true;
if (hover_debounce && (mbtn == JUI::MouseBtn::Left && mb_state == true && prev_mb_state != true)) {
clicked = true;
if (!click_debounce) {
OnClick(last_known_mouse_pos, mbtn);
click_debounce = true;
} else {
OnRelease(last_known_mouse_pos, mbtn, mb_state);
click_debounce = false;
}
}
if (IsPressed() && (lmb_state != true && prev_lmb_state == true)) {
OnRelease(last_known_mouse_pos, IsHovered());
is_pressed = false;
}
prev_lmb_state = lmb_state;
prev_mb_state = mb_state;
}
/*
void Button::Update(float delta) {
if (IsMouseInside() && !mouse_inside_debounce)
{
MouseEnter.Invoke(last_known_mouse_pos);
//DEBUG(std::format("mb state {}", last_known_mouse_button_state))
mouse_inside_debounce = true;
}
if (!IsMouseInside() && mouse_inside_debounce)
{
MouseExit.Invoke(last_known_mouse_pos);
mouse_inside_debounce = false;
}
if (IsMouseInside() && (lmb_state == true && prev_lmb_state != true)) {
DEBUG("PRESSED LOL");
Vector2 dummy = {0, 0};
MousePress.Invoke(dummy);
is_pressed = true;
}
if (IsPressed() && (lmb_state != true && prev_lmb_state == true)) {
DEBUG("PRESSED KEK");
//Vector2 dummy = {0, 0};
MouseRelease.Invoke(last_known_mouse_pos, IsMouseInside());
is_pressed = false;
}
prev_lmb_state = lmb_state;
}
*/
}

View File

@@ -1,7 +1,76 @@
#include "JUI/Widgets/Window.hpp"
#include "jlog/jlog.hpp"
namespace JUI
{
Window::Window() : Widget(), Clickable(), RectBase(), Draggable(), Dockable() {
this->SetPosition({200, 200, 0, 0});
this->SetSize({400, 200, 0, 0});
this->BGColor({0,0,0,0,});
this->OnClickEvent += [&] (Vector2 dummy, MouseBtn dummy2) {
if (draggable)
DEBUG("Window draggable")
if (resizable)
DEBUG("Window resizable")
};
this->OnReleaseEvent += [&] (Vector2 dummy, MouseBtn dummy2, bool dummy3) {};
Viewport = new Rect(this);
Viewport->SetName("Viewport");
Viewport->BGColor({64,64,64, 255});
Viewport->SetSize({100_percent, 100_percent});
Viewport->SetPosition({0_percent, 0_percent});
// TODO: Viewport->SetAnchorPoint({0.f, 0.f});
Viewport->BorderColor({128, 128, 128, 255});
Viewport->SetBorderWidth(1);
// TODO: Viewport->SetBorderRadius(0);
Topbar = new Rect(Viewport);
Topbar->SetPosition({0_px, 0_px});
Topbar->SetSize({100_percent, 20_px});
Topbar->BGColor({128, 128, 128, 255});
Topbar->BorderColor({128, 128, 128, 255});
Topbar->SetBorderWidth(1);
TitleLabel = new Text(Topbar);
TitleLabel->Center();
TitleLabel->SetContent(title);
// TODO: auto* list = new HorizontalListLayout(Topbar);
// TODO: exit_btn
// TODO: fs_btn
}
Vector2 Window::GetMinSize() const { return min_size; }
Vector2 Window::GetMaxSize() const { return max_size; }
Vector2 Window::GetCurrentSize() const { return this->GetAbsoluteSize(); }
std::string Window::GetTitle() const { return title; }
bool Window::IsDraggable() const { return draggable; }
bool Window::IsDockable() const { return dockable; }
bool Window::IsResizable() const { return resizable; }
Text *Window::GetTitleInstance() { return TitleLabel; }
Rect *Window::GetTopbarInstance() { return Topbar; }
Rect *Window::GetViewportInstance() { return Viewport; }
}