Started work on Context Menu
This commit is contained in:
parent
4bbf6a9b98
commit
99886e8c62
9 changed files with 251 additions and 14 deletions
|
|
@ -84,6 +84,7 @@ list(APPEND OGFX_SOURCE_FILES
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/Window.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/Window.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/WindowOutputHandler.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/WindowOutputHandler.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/WidgetManager.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/WidgetManager.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/ContextMenu.cpp
|
||||||
|
|
||||||
# gui/widgets
|
# gui/widgets
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/Widget.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/Widget.cpp
|
||||||
|
|
|
||||||
131
src/ogfx/gui/ContextMenu.cpp
Normal file
131
src/ogfx/gui/ContextMenu.cpp
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
OmniaFramework - A collection of useful functionality
|
||||||
|
Copyright (C) 2025 OmniaX-Dev
|
||||||
|
|
||||||
|
This file is part of OmniaFramework.
|
||||||
|
|
||||||
|
OmniaFramework is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
OmniaFramework is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ContextMenu.hpp"
|
||||||
|
#include "../render/BasicRenderer.hpp"
|
||||||
|
#include "Window.hpp"
|
||||||
|
|
||||||
|
namespace ogfx
|
||||||
|
{
|
||||||
|
namespace gui
|
||||||
|
{
|
||||||
|
void ContextMenu::Entry::update_size(BasicRenderer2D& gfx, i32 fontSize)
|
||||||
|
{
|
||||||
|
size = { 0, 0 };
|
||||||
|
for (auto& entry : submenus)
|
||||||
|
{
|
||||||
|
auto s = gfx.getStringDimensions(entry.text, fontSize);
|
||||||
|
if (s.x > size.x)
|
||||||
|
size.x = s.x;
|
||||||
|
size.y += s.y;
|
||||||
|
entry.update_size(gfx, fontSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextMenu& ContextMenu::create(void)
|
||||||
|
{
|
||||||
|
setSize(130, 300);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextMenu::draw(BasicRenderer2D& gfx)
|
||||||
|
{
|
||||||
|
gfx.outlinedRect(*this, Colors::Crimson, Colors::Black, 2);
|
||||||
|
f32 y = 0;
|
||||||
|
for (auto& entry : m_data.entries)
|
||||||
|
{
|
||||||
|
Vec2 entryPos = getPosition() + Vec2 { m_padding.x, y + m_padding.y };
|
||||||
|
Rectangle rect = { entryPos - Vec2 { m_padding.x, 0 }, getw(), m_entryHeight };
|
||||||
|
if (rect.contains(m_mousePos, true))
|
||||||
|
gfx.outlinedRect(rect, Colors::Chocolate, Colors::White, 1);
|
||||||
|
gfx.drawString(entry.text, entryPos, Colors::Black, getFontSize());
|
||||||
|
y += m_entryHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextMenu::onMouseReleased(const Event& event)
|
||||||
|
{
|
||||||
|
Vec2 mousePos { event.mouse->position_x, event.mouse->position_y };
|
||||||
|
if (contains(mousePos, true))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
event.handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextMenu::onMouseMoved(const Event& event)
|
||||||
|
{
|
||||||
|
Vec2 mousePos { event.mouse->position_x, event.mouse->position_y };
|
||||||
|
if (contains(mousePos, true))
|
||||||
|
{
|
||||||
|
m_mousePos = mousePos;
|
||||||
|
}
|
||||||
|
event.handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextMenu::show(void)
|
||||||
|
{
|
||||||
|
m_visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextMenu::show(const Vec2& pos)
|
||||||
|
{
|
||||||
|
setPosition(pos);
|
||||||
|
m_visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextMenu::hide(void)
|
||||||
|
{
|
||||||
|
m_visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextMenu::setInstance(const Instance& instance)
|
||||||
|
{
|
||||||
|
m_data = instance;
|
||||||
|
update_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextMenu::setFontSize(i32 size)
|
||||||
|
{
|
||||||
|
m_fontSize = size;
|
||||||
|
update_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextMenu::update_size(void)
|
||||||
|
{
|
||||||
|
setSize(0, 0);
|
||||||
|
auto& gfx = m_window.getGFX();
|
||||||
|
for (auto& entry : m_data.entries)
|
||||||
|
{
|
||||||
|
auto s = gfx.getStringDimensions(entry.text, getFontSize());
|
||||||
|
if (s.x > getw())
|
||||||
|
setw(s.x);
|
||||||
|
addh(s.y);
|
||||||
|
m_entryHeight = s.y;
|
||||||
|
entry.update_size(gfx, getFontSize());
|
||||||
|
}
|
||||||
|
addSize({ m_padding.x + m_padding.w, m_padding.y + m_padding.h });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/ogfx/gui/ContextMenu.hpp
Normal file
78
src/ogfx/gui/ContextMenu.hpp
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
OmniaFramework - A collection of useful functionality
|
||||||
|
Copyright (C) 2025 OmniaX-Dev
|
||||||
|
|
||||||
|
This file is part of OmniaFramework.
|
||||||
|
|
||||||
|
OmniaFramework is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
OmniaFramework is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ogfx/gui/widgets/Widget.hpp>
|
||||||
|
|
||||||
|
namespace ogfx
|
||||||
|
{
|
||||||
|
namespace gui
|
||||||
|
{
|
||||||
|
class ContextMenu : public Rectangle
|
||||||
|
{
|
||||||
|
public: struct Entry
|
||||||
|
{
|
||||||
|
inline Entry(const String& t, const stdvec<Entry>& sub = {}) { text = t; }
|
||||||
|
String text { "" };
|
||||||
|
stdvec<Entry> submenus;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vec2 size;
|
||||||
|
void update_size(BasicRenderer2D& gfx, i32 fontSize);
|
||||||
|
|
||||||
|
friend class ContextMenu;
|
||||||
|
};
|
||||||
|
public: struct Instance
|
||||||
|
{
|
||||||
|
stdvec<Entry> entries;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
inline ContextMenu(WindowCore& window) : m_window(window) { create(); }
|
||||||
|
ContextMenu& create(void);
|
||||||
|
void draw(BasicRenderer2D& gfx);
|
||||||
|
void onMouseReleased(const Event& event);
|
||||||
|
void onMouseMoved(const Event& event);
|
||||||
|
void show(void);
|
||||||
|
void show(const Vec2& pos);
|
||||||
|
void hide(void);
|
||||||
|
void setInstance(const Instance& instance);
|
||||||
|
void setFontSize(i32 size);
|
||||||
|
|
||||||
|
inline bool isVisible(void) const { return m_visible; }
|
||||||
|
inline i32 getFontSize(void) const { return m_fontSize; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void update_size(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
WindowCore& m_window;
|
||||||
|
bool m_visible { false };
|
||||||
|
Instance m_data;
|
||||||
|
f32 m_entryHeight { 0 };
|
||||||
|
Rectangle m_padding { 10, 10, 10, 10 };
|
||||||
|
Vec2 m_mousePos;
|
||||||
|
|
||||||
|
i32 m_fontSize { 18 };
|
||||||
|
|
||||||
|
friend class Instance;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -495,11 +495,11 @@ namespace ogfx
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
MouseEventData::eButton button = MouseEventData::eButton::None;
|
MouseEventData::eButton button = MouseEventData::eButton::None;
|
||||||
switch (btn)
|
switch (event.button.button)
|
||||||
{
|
{
|
||||||
case SDL_BUTTON_MASK(1): button = MouseEventData::eButton::Left; break;
|
case SDL_BUTTON_LEFT: button = MouseEventData::eButton::Left; break;
|
||||||
case SDL_BUTTON_MASK(2): button = MouseEventData::eButton::Middle; break;
|
case SDL_BUTTON_MIDDLE: button = MouseEventData::eButton::Middle; break;
|
||||||
case SDL_BUTTON_MASK(3): button = MouseEventData::eButton::Right; break;
|
case SDL_BUTTON_RIGHT: button = MouseEventData::eButton::Right; break;
|
||||||
default: button = MouseEventData::eButton::None; break;
|
default: button = MouseEventData::eButton::None; break;
|
||||||
}
|
}
|
||||||
MouseEventData mmd(*this, mx, my, button);
|
MouseEventData mmd(*this, mx, my, button);
|
||||||
|
|
@ -629,13 +629,6 @@ namespace ogfx
|
||||||
else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP)
|
else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP)
|
||||||
{
|
{
|
||||||
MouseEventData mmd = get_mouse_state(event);
|
MouseEventData mmd = get_mouse_state(event);
|
||||||
switch (event.button.button)
|
|
||||||
{
|
|
||||||
case SDL_BUTTON_MASK(1): mmd.button = MouseEventData::eButton::Left; break;
|
|
||||||
case SDL_BUTTON_MASK(2): mmd.button = MouseEventData::eButton::Middle; break;
|
|
||||||
case SDL_BUTTON_MASK(3): mmd.button = MouseEventData::eButton::Right; break;
|
|
||||||
default: mmd.button = MouseEventData::eButton::None; break;
|
|
||||||
}
|
|
||||||
ostd::SignalHandler::emitSignal(ostd::BuiltinSignals::MouseReleased, ostd::Signal::Priority::RealTime, mmd);
|
ostd::SignalHandler::emitSignal(ostd::BuiltinSignals::MouseReleased, ostd::Signal::Priority::RealTime, mmd);
|
||||||
}
|
}
|
||||||
else if (event.type == SDL_EVENT_KEY_DOWN)
|
else if (event.type == SDL_EVENT_KEY_DOWN)
|
||||||
|
|
@ -877,6 +870,8 @@ namespace ogfx
|
||||||
m_rootWidget.__update();
|
m_rootWidget.__update();
|
||||||
m_rootWidget.__draw(m_gfx);
|
m_rootWidget.__draw(m_gfx);
|
||||||
onRedraw(m_gfx);
|
onRedraw(m_gfx);
|
||||||
|
if (m_cmenu.isVisible())
|
||||||
|
m_cmenu.draw(m_gfx);
|
||||||
|
|
||||||
if (isTooltipShown())
|
if (isTooltipShown())
|
||||||
{
|
{
|
||||||
|
|
@ -938,6 +933,8 @@ namespace ogfx
|
||||||
{
|
{
|
||||||
evt.mouse = &(ogfx::MouseEventData&)signal.userData;
|
evt.mouse = &(ogfx::MouseEventData&)signal.userData;
|
||||||
evt.__original_signal_id = ostd::BuiltinSignals::MouseMoved;
|
evt.__original_signal_id = ostd::BuiltinSignals::MouseMoved;
|
||||||
|
if (m_cmenu.isVisible())
|
||||||
|
m_cmenu.onMouseMoved(evt);
|
||||||
m_rootWidget.__onMouseMoved(evt);
|
m_rootWidget.__onMouseMoved(evt);
|
||||||
}
|
}
|
||||||
else if (signal.ID == ostd::BuiltinSignals::MouseScrolled)
|
else if (signal.ID == ostd::BuiltinSignals::MouseScrolled)
|
||||||
|
|
@ -956,6 +953,8 @@ namespace ogfx
|
||||||
{
|
{
|
||||||
evt.mouse = &(ogfx::MouseEventData&)signal.userData;
|
evt.mouse = &(ogfx::MouseEventData&)signal.userData;
|
||||||
evt.__original_signal_id = ostd::BuiltinSignals::MouseReleased;
|
evt.__original_signal_id = ostd::BuiltinSignals::MouseReleased;
|
||||||
|
if (m_cmenu.isVisible())
|
||||||
|
m_cmenu.onMouseReleased(evt);
|
||||||
m_rootWidget.__onMouseReleased(evt);
|
m_rootWidget.__onMouseReleased(evt);
|
||||||
}
|
}
|
||||||
else if (signal.ID == ostd::BuiltinSignals::KeyPressed)
|
else if (signal.ID == ostd::BuiltinSignals::KeyPressed)
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include <ogfx/gui/widgets/RootWidget.hpp>
|
#include <ogfx/gui/widgets/RootWidget.hpp>
|
||||||
#include <ogfx/render/BasicRenderer.hpp>
|
#include <ogfx/render/BasicRenderer.hpp>
|
||||||
#include <ogfx/gui/WindowOutputHandler.hpp>
|
#include <ogfx/gui/WindowOutputHandler.hpp>
|
||||||
|
#include <ogfx/gui/ContextMenu.hpp>
|
||||||
|
|
||||||
namespace ogfx
|
namespace ogfx
|
||||||
{
|
{
|
||||||
|
|
@ -269,6 +270,8 @@ namespace ogfx
|
||||||
inline Window(i32 width, i32 height, const String& title) { initialize(width, height, title); }
|
inline Window(i32 width, i32 height, const String& title) { initialize(width, height, title); }
|
||||||
void addWidget(Widget& widget, const Vec2& position = { 0, 0 });
|
void addWidget(Widget& widget, const Vec2& position = { 0, 0 });
|
||||||
void setTheme(const ostd::Stylesheet& theme) override;
|
void setTheme(const ostd::Stylesheet& theme) override;
|
||||||
|
inline void showContextMenu(const Vec2& pos) { m_cmenu.show(pos); }
|
||||||
|
inline void setContextMenu(const ContextMenu::Instance& instance) { m_cmenu.setInstance(instance); }
|
||||||
|
|
||||||
inline virtual void onInitialize(void) { }
|
inline virtual void onInitialize(void) { }
|
||||||
inline virtual void onDestroy(void) { }
|
inline virtual void onDestroy(void) { }
|
||||||
|
|
@ -293,6 +296,7 @@ namespace ogfx
|
||||||
widgets::RootWidget m_rootWidget { *this };
|
widgets::RootWidget m_rootWidget { *this };
|
||||||
ostd::StepTimer m_fixedUpdateTimer;
|
ostd::StepTimer m_fixedUpdateTimer;
|
||||||
ostd::StepTimer::TimePoint m_lastFrameTime;
|
ostd::StepTimer::TimePoint m_lastFrameTime;
|
||||||
|
ContextMenu m_cmenu { *this };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,8 @@ namespace ogfx
|
||||||
|
|
||||||
void ListView::onMouseReleased(const Event& event)
|
void ListView::onMouseReleased(const Event& event)
|
||||||
{
|
{
|
||||||
|
if (!isMouseInside())
|
||||||
|
return;
|
||||||
if (event.mouse->button != ogfx::MouseEventData::eButton::Left)
|
if (event.mouse->button != ogfx::MouseEventData::eButton::Left)
|
||||||
return;
|
return;
|
||||||
if (isMouseInsideAnyScrollbar())
|
if (isMouseInsideAnyScrollbar())
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,8 @@ namespace ogfx
|
||||||
{
|
{
|
||||||
if (!isMouseInside())
|
if (!isMouseInside())
|
||||||
return;
|
return;
|
||||||
set_value(mouse_position_to_value({ event.mouse->position_x, event.mouse->position_y }));
|
if (event.mouse->button == MouseEventData::eButton::Left)
|
||||||
|
set_value(mouse_position_to_value({ event.mouse->position_x, event.mouse->position_y }));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Slider::onMouseScrolled(const Event& event)
|
void Slider::onMouseScrolled(const Event& event)
|
||||||
|
|
|
||||||
|
|
@ -340,8 +340,8 @@ namespace ostd
|
||||||
inline virtual Vec2 addPos(Vec2 pos) { return addPos(pos.x, pos.y); }
|
inline virtual Vec2 addPos(Vec2 pos) { return addPos(pos.x, pos.y); }
|
||||||
inline virtual f32 addw(f32 ww) { setw(getw() + ww); return getw(); }
|
inline virtual f32 addw(f32 ww) { setw(getw() + ww); return getw(); }
|
||||||
inline virtual f32 addh(f32 hh) { seth(geth() + hh); return geth(); }
|
inline virtual f32 addh(f32 hh) { seth(geth() + hh); return geth(); }
|
||||||
inline virtual Vec2 addSize(f32 ww, f32 hh) { return Vec2(addw(ww), addh(hh)); }
|
inline virtual Vec2 addSize(f32 ww, f32 hh) { addw(ww), addh(hh); return getSize(); }
|
||||||
inline virtual Vec2 addSize(Vec2 size) { return addPos(size.x, size.y); }
|
inline virtual Vec2 addSize(Vec2 size) { addSize(size.x, size.y); return getSize(); }
|
||||||
|
|
||||||
inline virtual f32 subx(f32 xx) { setx(getx() - xx); return getx(); }
|
inline virtual f32 subx(f32 xx) { setx(getx() - xx); return getx(); }
|
||||||
inline virtual f32 suby(f32 yy) { sety(gety() - yy); return gety(); }
|
inline virtual f32 suby(f32 yy) { sety(gety() - yy); return gety(); }
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,8 @@ class Window : public ogfx::gui::Window
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setContextMenu(m_menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void onSignal(ostd::Signal& signal) override
|
inline void onSignal(ostd::Signal& signal) override
|
||||||
|
|
@ -192,6 +194,14 @@ class Window : public ogfx::gui::Window
|
||||||
if (evtData.keyCode == ogfx::KeyCode::Escape)
|
if (evtData.keyCode == ogfx::KeyCode::Escape)
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
else if (signal.ID == ostd::BuiltinSignals::MouseReleased)
|
||||||
|
{
|
||||||
|
auto& mmd = cast<ogfx::MouseEventData&>(signal.userData);
|
||||||
|
if (mmd.button == ogfx::MouseEventData::eButton::Right)
|
||||||
|
{
|
||||||
|
showContextMenu({ mmd.position_x, mmd.position_y });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onRedraw(ogfx::BasicRenderer2D& gfx) override
|
void onRedraw(ogfx::BasicRenderer2D& gfx) override
|
||||||
|
|
@ -224,6 +234,17 @@ class Window : public ogfx::gui::Window
|
||||||
ListView m_list { *this };
|
ListView m_list { *this };
|
||||||
Label m_drawCallsLbl { *this };
|
Label m_drawCallsLbl { *this };
|
||||||
|
|
||||||
|
ogfx::gui::ContextMenu::Instance m_menu {
|
||||||
|
{
|
||||||
|
{ "Update" },
|
||||||
|
{ "File" },
|
||||||
|
{ "Open RAW" },
|
||||||
|
{ "Mouse Settings" },
|
||||||
|
{ "Terror" },
|
||||||
|
{ "Properties" }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ostd::StepTimer m_timer;
|
ostd::StepTimer m_timer;
|
||||||
ostd::AsyncJob<bool> m_progressJob;
|
ostd::AsyncJob<bool> m_progressJob;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue