Started work on Context Menu

This commit is contained in:
OmniaX-Dev 2026-04-24 06:43:26 +02:00
parent 4bbf6a9b98
commit 99886e8c62
9 changed files with 251 additions and 14 deletions

View file

@ -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/WindowOutputHandler.cpp
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/WidgetManager.cpp
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/ContextMenu.cpp
# gui/widgets
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/Widget.cpp

View 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 });
}
}
}

View 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;
};
}
}

View file

@ -495,11 +495,11 @@ namespace ogfx
break;
}
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_MASK(2): button = MouseEventData::eButton::Middle; break;
case SDL_BUTTON_MASK(3): button = MouseEventData::eButton::Right; break;
case SDL_BUTTON_LEFT: button = MouseEventData::eButton::Left; break;
case SDL_BUTTON_MIDDLE: button = MouseEventData::eButton::Middle; break;
case SDL_BUTTON_RIGHT: button = MouseEventData::eButton::Right; break;
default: button = MouseEventData::eButton::None; break;
}
MouseEventData mmd(*this, mx, my, button);
@ -629,13 +629,6 @@ namespace ogfx
else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP)
{
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);
}
else if (event.type == SDL_EVENT_KEY_DOWN)
@ -877,6 +870,8 @@ namespace ogfx
m_rootWidget.__update();
m_rootWidget.__draw(m_gfx);
onRedraw(m_gfx);
if (m_cmenu.isVisible())
m_cmenu.draw(m_gfx);
if (isTooltipShown())
{
@ -938,6 +933,8 @@ namespace ogfx
{
evt.mouse = &(ogfx::MouseEventData&)signal.userData;
evt.__original_signal_id = ostd::BuiltinSignals::MouseMoved;
if (m_cmenu.isVisible())
m_cmenu.onMouseMoved(evt);
m_rootWidget.__onMouseMoved(evt);
}
else if (signal.ID == ostd::BuiltinSignals::MouseScrolled)
@ -956,6 +953,8 @@ namespace ogfx
{
evt.mouse = &(ogfx::MouseEventData&)signal.userData;
evt.__original_signal_id = ostd::BuiltinSignals::MouseReleased;
if (m_cmenu.isVisible())
m_cmenu.onMouseReleased(evt);
m_rootWidget.__onMouseReleased(evt);
}
else if (signal.ID == ostd::BuiltinSignals::KeyPressed)

View file

@ -29,6 +29,7 @@
#include <ogfx/gui/widgets/RootWidget.hpp>
#include <ogfx/render/BasicRenderer.hpp>
#include <ogfx/gui/WindowOutputHandler.hpp>
#include <ogfx/gui/ContextMenu.hpp>
namespace ogfx
{
@ -269,6 +270,8 @@ namespace ogfx
inline Window(i32 width, i32 height, const String& title) { initialize(width, height, title); }
void addWidget(Widget& widget, const Vec2& position = { 0, 0 });
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 onDestroy(void) { }
@ -293,6 +296,7 @@ namespace ogfx
widgets::RootWidget m_rootWidget { *this };
ostd::StepTimer m_fixedUpdateTimer;
ostd::StepTimer::TimePoint m_lastFrameTime;
ContextMenu m_cmenu { *this };
};
}
}

View file

@ -158,6 +158,8 @@ namespace ogfx
void ListView::onMouseReleased(const Event& event)
{
if (!isMouseInside())
return;
if (event.mouse->button != ogfx::MouseEventData::eButton::Left)
return;
if (isMouseInsideAnyScrollbar())

View file

@ -71,6 +71,7 @@ namespace ogfx
{
if (!isMouseInside())
return;
if (event.mouse->button == MouseEventData::eButton::Left)
set_value(mouse_position_to_value({ event.mouse->position_x, event.mouse->position_y }));
}

View file

@ -340,8 +340,8 @@ namespace ostd
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 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(Vec2 size) { return addPos(size.x, size.y); }
inline virtual Vec2 addSize(f32 ww, f32 hh) { addw(ww), addh(hh); return getSize(); }
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 suby(f32 yy) { sety(gety() - yy); return gety(); }

View file

@ -182,6 +182,8 @@ class Window : public ogfx::gui::Window
}
return true;
});
setContextMenu(m_menu);
}
inline void onSignal(ostd::Signal& signal) override
@ -192,6 +194,14 @@ class Window : public ogfx::gui::Window
if (evtData.keyCode == ogfx::KeyCode::Escape)
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
@ -224,6 +234,17 @@ class Window : public ogfx::gui::Window
ListView m_list { *this };
Label m_drawCallsLbl { *this };
ogfx::gui::ContextMenu::Instance m_menu {
{
{ "Update" },
{ "File" },
{ "Open RAW" },
{ "Mouse Settings" },
{ "Terror" },
{ "Properties" }
}
};
ostd::StepTimer m_timer;
ostd::AsyncJob<bool> m_progressJob;