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:
@@ -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;
|
||||
};
|
||||
}
|
||||
|
@@ -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:
|
||||
};
|
||||
}
|
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
88
main.cpp
88
main.cpp
@@ -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) {};
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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;
|
||||
}
|
||||
*/
|
||||
}
|
@@ -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; }
|
||||
}
|
Reference in New Issue
Block a user