Added FocusManager

This commit is contained in:
OmniaX-Dev 2026-05-03 06:49:44 +02:00
parent 67a1dd605d
commit 203a305633
24 changed files with 373 additions and 164 deletions

View file

@ -88,6 +88,7 @@ list(APPEND OGFX_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/MenuBar.cpp
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/ToolBar.cpp
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/Layout.cpp
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/FocusManager.cpp
# gui/widgets
${CMAKE_CURRENT_LIST_DIR}/src/ogfx/gui/widgets/Widget.cpp

View file

@ -25,6 +25,8 @@
***FIX: Limit FPS even when continuous events happen
***FIX: Float values in stylesheet needing to have decimal point to be read as float
***Make parsing of vec2 and rect in stylesheet, consistent with other value functions like gradients and animations
***FIX: ToolBar::AddButton not consistent order
***FIX: KeyPress crash on TreeView
Implement cursors in Stylesheet
FIX: Window getting exponentially bigger when Desktop scale changes
FIX: Refreshing scroll when content extent changes
@ -52,6 +54,8 @@ FIX: ContextMenu rendering partly outside the screen when showing it close to th
Update theme for visible TreeView lines
Add ToolBar::addButton overload that takes a ogfx::Icon wrapper
Redesign theme overrides to be more robust
FIX: Animated icons not working in TreeView
Add modifiers to Event.keyboard path

View file

@ -160,14 +160,14 @@ namespace ogfx
Instance m_data;
f32 m_entryHeight { 0 };
Vec2 m_mousePos { 0, 0 };
ostd::Timer m_animClock;
ostd::Counter m_animClock;
stdvec<Panel> m_panels;
i32 m_pendingOpenPanelDepth { -1 };
i32 m_pendingOpenEntryIndex { -1 };
ostd::Timer m_hoverOpenTimer;
ostd::Timer m_hoverCloseTimer;
ostd::Counter m_hoverOpenTimer;
ostd::Counter m_hoverCloseTimer;
bool m_hoverOpenTimerActive { false };
bool m_hoverCloseTimerActive { false };

View file

@ -0,0 +1,154 @@
/*
OmniaFramework - A collection of useful functionality
Copyright (C) 2026 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 "FocusManager.hpp"
#include "Window.hpp"
#include "../utils/Keycodes.hpp"
namespace ogfx
{
namespace gui
{
FocusManager::FocusManager(Window& window) : m_window(window)
{
}
bool FocusManager::requestFocus(Widget& widget)
{
if (&widget == m_focused) return true;
if (!widget.isFocusEnabled()) return false;
if (m_window.getToolBar().isVisible())
{
if (requestFocus(widget, m_window.getToolBar()))
return true;
}
if (m_window.getStatusBar().isVisible())
{
if (requestFocus(widget, m_window.getStatusBar()))
return true;
}
return requestFocus(widget, m_window.m_rootWidget);
}
bool FocusManager::requestFocus(Widget& widget, Widget& root)
{
if (&widget == m_focused) return true;
if (!widget.isFocusEnabled()) return false;
if (widget.isInvalid()) return false;
if (!widget.isVisible()) return false;
if (!root.hasChildren() || !root.isChildrenEnabled())
return false;
if (!root.getChildren().hasWidget(widget))
{
for (auto& child : root.getChildren().m_widgetList)
{
if (child && requestFocus(widget, *child))
return true;
}
return false;
}
if (m_focused)
{
m_focused->m_focused = false;
m_focused->onFocusLost(Event(m_window));
m_focused->setThemeQualifier("focused", false);
}
widget.m_focused = true;
widget.onFocusGained(Event(m_window));
widget.setThemeQualifier("focused", true);
m_focused = &widget;
return true;
}
Widget* FocusManager::focusNext(void)
{
if (m_tabIndexMap.empty())
return nullptr;
i32 startIndex = -1, smallest = i32Range::max();
if (m_focused)
startIndex = m_focused->m_tabIndex;
i32 nextIndex = i32Range::max();
Widget* nextPtr = nullptr;
Widget* smallestPtr = nullptr;
for (auto&[wptr, index] : m_tabIndexMap)
{
if (index < smallest)
{
smallest = index;
smallestPtr = wptr;
}
if (index > startIndex && index < nextIndex)
{
nextIndex = index;
nextPtr = wptr;
}
}
Widget* w = nullptr;
if (!nextPtr)
{
if (!smallestPtr)
return nullptr;
w = smallestPtr;
}
else
w = nextPtr;
requestFocus(*w);
return w;
}
bool FocusManager::registerTabIndex(Widget& widget, i32 index)
{
if (index >= 0)
{
m_tabIndexMap[&widget] = index;
return true;
}
return false;
}
void FocusManager::unregisterTabIndex(Widget& widget)
{
if (m_tabIndexMap.count(&widget) > 0)
m_tabIndexMap.erase(m_tabIndexMap.find(&widget));
}
void FocusManager::clearFocus(void)
{
if (m_focused)
{
m_focused->m_focused = false;
m_focused = nullptr;
}
}
void FocusManager::onKeyReleased(const Event& event)
{
if (isTabNavigationEnabled() && event.keyboard->keyCode == KeyCode::Tab)
focusNext();
}
}
}

View file

@ -0,0 +1,55 @@
/*
OmniaFramework - A collection of useful functionality
Copyright (C) 2026 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 Window;
class FocusManager
{
public:
FocusManager(Window& window);
bool requestFocus(Widget& widget, Widget& root);
bool requestFocus(Widget& widget);
Widget* focusNext(void);
bool registerTabIndex(Widget& widget, i32 index);
void unregisterTabIndex(Widget& widget);
void clearFocus(void);
void onKeyReleased(const Event& event);
inline Widget* getFocused(void) { return m_focused; }
OSTD_BOOL_PARAM_GETSET_E(TabNavigation, m_tabNavigationEnabled);
private:
Window& m_window;
Widget* m_focused { nullptr };
bool m_tabNavigationEnabled { true };
stdumap<Widget*, i32> m_tabIndexMap;
};
}
}

View file

@ -1,21 +1,21 @@
/*
OmniaFramework - A collection of useful functionality
Copyright (C) 2026 OmniaX-Dev
OmniaFramework - A collection of useful functionality
Copyright (C) 2026 OmniaX-Dev
This file is part of OmniaFramework.
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 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.
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/>.
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
@ -60,35 +60,35 @@ namespace ogfx
u8 extraPaddingLeft { 0 };
bool cursorBlink { true };
ostd::Counter cursorBlinkCounter { 30 };
ostd::BasicCounter cursorBlinkCounter { 30 };
};
public: struct tDefaultThemes
{
inline static const Theme DebugTheme {
{ 220, 220, 255 }, //Text Color
{ 10, 20, 120 }, //Border Color
{ 0, 0, 22 }, //Background Color
{ 220, 10, 10 }, //Cursor Color
{ 220, 220, 255 }, //Text Color
{ 10, 20, 120 }, //Border Color
{ 0, 0, 22 }, //Background Color
{ 220, 10, 10 }, //Cursor Color
2, //Cursor Width
0, //Extra Padding Top
0, //Extra Padding Left
2, //Cursor Width
0, //Extra Padding Top
0, //Extra Padding Left
true, //Cursor Blink
{ 30 } //Cursor Blink Timer
true, //Cursor Blink
{ 30 } //Cursor Blink Timer
};
inline static const Theme DefaultTheme {
{ 120, 120, 180 }, //Text Color
{ 10, 20, 120 }, //Border Color
{ 0, 2, 10 }, //Background Color
{ 20, 100, 255 }, //Cursor Color
{ 120, 120, 180 }, //Text Color
{ 10, 20, 120 }, //Border Color
{ 0, 2, 10 }, //Background Color
{ 20, 100, 255 }, //Cursor Color
2, //Cursor Width
0, //Extra Padding Top
0, //Extra Padding Left
2, //Cursor Width
0, //Extra Padding Top
0, //Extra Padding Left
true, //Cursor Blink
{ 30 } //Cursor Blink Timer
true, //Cursor Blink
{ 30 } //Cursor Blink Timer
};
};
public: enum eActionEventType { None = 0, Enter, Tab };
@ -156,7 +156,7 @@ namespace ogfx
u16 m_cursorPosition { 0 };
bool m_cursorState { true };
ostd::Counter m_keyRepeatCounter { 1 };
ostd::BasicCounter m_keyRepeatCounter { 1 };
char m_lastChar { 0 };
i32 m_lastKeyCode { 0 };

View file

@ -48,6 +48,7 @@ namespace ogfx
setPosition(0, win.getMenuBar().getHeight());
else
setPosition(0, 0);
return *this;
}

View file

@ -19,9 +19,9 @@
*/
#include "WidgetManager.hpp"
#include "Window.hpp"
#include "widgets/Widget.hpp"
#include "../render/BasicRenderer.hpp"
#include "../utils/Keycodes.hpp"
#include "../../ostd/io/Memory.hpp"
namespace ogfx
@ -39,41 +39,11 @@ namespace ogfx
return found && widget.isValid();
}
bool WidgetManager::requestFocus(Widget& widget)
{
if (!hasWidget(widget)) return false;
if (&widget == m_focused) return true;
for (auto& w : m_widgetList)
{
if (w == nullptr) continue;
if (w->isInvalid()) continue;
if (!w->isVisible()) continue;
if (w->m_focused)
{
w->m_focused = false;
w->onFocusLost(Event(m_window));
w->setThemeQualifier("focused", false);
}
else
w->m_focused = false;
}
widget.m_focused = true;
widget.onFocusGained(Event(m_window));
widget.setThemeQualifier("focused", true);
m_focused = &widget;
return true;
}
bool WidgetManager::addWidget(Widget& widget)
{
if (hasWidget(widget)) return false;
widget.m_parent = &m_owner;
m_widgetList.push_back(&widget);
if (widget.m_topMost)
return true;
std::ranges::sort(m_widgetList, {}, [](Widget* w) {
return w->m_zIndex;
});
widget.refresh();
return true;
}
@ -84,32 +54,6 @@ namespace ogfx
return true;
}
Widget* WidgetManager::focusNext(void)
{
if (m_widgetList.empty())
return nullptr;
Widget* next = nullptr;
Widget* smallest = nullptr;
i32 currentTabIndex = (m_focused != nullptr ? m_focused->m_tabIndex : std::numeric_limits<i32>::max());
for (Widget* w : m_widgetList)
{
i32 tab_i = w->m_tabIndex;
if (tab_i < 0) continue;
if (!smallest || tab_i < smallest->m_tabIndex)
smallest = w;
if (tab_i > currentTabIndex)
{
if (!next || tab_i < next->m_tabIndex)
next = w;
}
}
Widget* w = next ? next : smallest;
requestFocus(*w);
return w;
}
void WidgetManager::draw(ogfx::BasicRenderer2D& gfx)
{
for (auto& w : m_widgetList)
@ -157,6 +101,7 @@ namespace ogfx
if (!w->contains(event.mouse->position_x, event.mouse->position_y, true))
continue;
event.mouse->mousePressedOnWidget = w;
cast<Window&>(m_window).getFocusManager().requestFocus(*w);
w->__onMousePressed(event);
m_mousePressedOnWidget = w;
if (event.isHandled() || w->m_stopEvents)
@ -182,7 +127,6 @@ namespace ogfx
event.mouse->mousePressedOnWidget = m_mousePressedOnWidget;
w->__onMouseReleased(event);
processDragAndDrop(w, event);
// requestFocus(*w);
if (w->isMouseInside() && w->m_stopEvents)
break;
}
@ -275,22 +219,24 @@ namespace ogfx
void WidgetManager::onKeyPressed(const Event& event)
{
if (!m_focused || !m_focused->isVisible()) return;
m_focused->__onKeyPressed(event);
auto focused = cast<Window&>(m_window).getFocusManager().getFocused();
if (!focused || !focused->isVisible()) return;
focused->__onKeyPressed(event);
}
void WidgetManager::onKeyReleased(const Event& event)
{
if (!m_focused || !m_focused->isVisible()) return;
m_focused->__onKeyReleased(event);
if (m_tabNavigationEnabled && event.keyboard->keyCode == KeyCode::Tab)
focusNext();
auto focus = cast<Window&>(m_window).getFocusManager();
auto focused = focus.getFocused();
if (!focused || !focused->isVisible()) return;
focused->__onKeyReleased(event);
}
void WidgetManager::onTextEntered(const Event& event)
{
if (!m_focused || !m_focused->isVisible()) return;
m_focused->__onTextEntered(event);
auto focused = cast<Window&>(m_window).getFocusManager().getFocused();
if (!focused || !focused->isVisible()) return;
focused->__onTextEntered(event);
}
void WidgetManager::onWindowClosed(const Event& event)

View file

@ -39,10 +39,8 @@ namespace ogfx
public:
WidgetManager(WindowCore& window, Widget& owner);
bool hasWidget(Widget& widget);
bool requestFocus(Widget& widget);
bool addWidget(Widget& widget);
bool removeWidget(Widget& widget);
Widget* focusNext(void);
void draw(ogfx::BasicRenderer2D& gfx);
void update(void);
@ -71,9 +69,9 @@ namespace ogfx
WindowCore& m_window;
Widget& m_owner;
stdvec<Widget*> m_widgetList;
Widget* m_focused { nullptr };
bool m_tabNavigationEnabled { true };
Widget* m_mousePressedOnWidget { nullptr };
friend class FocusManager;
};
}
}

View file

@ -1008,6 +1008,7 @@ namespace ogfx
{
evt.keyboard = &(ogfx::KeyEventData&)signal.userData;
evt.__original_signal_id = ostd::BuiltinSignals::KeyReleased;
m_focusManager.onKeyReleased(evt);
m_toolbar.__onKeyReleased(evt);
m_statusbar.__onKeyReleased(evt);
m_rootWidget.__onKeyReleased(evt);

View file

@ -32,6 +32,7 @@
#include <ogfx/gui/ContextMenu.hpp>
#include <ogfx/gui/MenuBar.hpp>
#include <ogfx/gui/ToolBar.hpp>
#include <ogfx/gui/FocusManager.hpp>
namespace ogfx
{
@ -181,7 +182,7 @@ namespace ogfx
String m_title { "" };
i32 m_blockingEventsDelay { 33 };
Vec2 m_mousePosition { 0, 0 };
ostd::Timer m_tooltipTimer;
ostd::Counter m_tooltipTimer;
String m_tooltipText { "" };
bool m_running { false };
@ -259,7 +260,7 @@ namespace ogfx
i32 m_fps { 0 };
ostd::StepTimer m_fixedUpdateTImer;
ostd::StepTimer m_fpsUpdateTimer;
ostd::Timer m_fpsUpdateClock;
ostd::Counter m_fpsUpdateClock;
u64 m_frameTimeAcc { 0 };
i32 m_frameCount { 0 };
};
@ -284,6 +285,7 @@ namespace ogfx
inline void showMenuBar(bool show = true) { m_menubar.show(show); }
inline void showToolBar(bool show = true) { m_toolbar.setVisible(show); }
inline void showStatusBar(bool show = true) { m_statusbar.setVisible(show); }
inline FocusManager& getFocusManager(void) { return m_focusManager; }
inline virtual void onInitialize(void) { }
inline virtual void onDestroy(void) { }
@ -308,6 +310,7 @@ namespace ogfx
protected:
RootWidget m_rootWidget { *this };
FocusManager m_focusManager { *this };
ostd::StepTimer m_fixedUpdateTimer;
ostd::StepTimer::TimePoint m_lastFrameTime;
ostd::StepTimer m_mainLoopTimer;
@ -317,6 +320,7 @@ namespace ogfx
ToolBar m_statusbar { *this, true };
friend class RootWidget;
friend class FocusManager;
};
}
}

View file

@ -38,6 +38,7 @@ namespace ogfx
setPadding({ 5, 5, 5, 5 });
setTypeName("ogfx::gui::Label");
disableChildren();
disableFocus();
enableBackground(false);
setStylesheetCategoryName("label");
validate();
@ -127,6 +128,7 @@ namespace ogfx
setPadding({ 0, 0, 0, 0 });
setTypeName("ogfx::gui::ImageLabel");
disableChildren();
disableFocus();
disableBackground();
disableBorder();
setStylesheetCategoryName("image");

View file

@ -30,6 +30,7 @@ namespace ogfx
setPadding({ 0, 0, 0, 0 });
setTypeName("ogfx::gui::ProgressBar");
disableChildren();
disableFocus();
enableBackground();
enableBorder();
setStylesheetCategoryName("progressbar");

View file

@ -29,6 +29,7 @@ namespace ogfx
RootWidget::RootWidget(WindowCore& window) : Widget({ 0, 0, 0, 0 }, window)
{
setRootChild();
disableFocus();
setSize(cast<f32>(window.getWindowWidth()), cast<f32>(window.getWindowHeight()));
setStylesheetCategoryName("window");
setTypeName("ogfx::gui::RootWidget");

View file

@ -31,6 +31,7 @@ namespace ogfx
setMargin({ 0, 0, 0, 0 });
setTypeName("ogfx::gui::Label");
disableChildren();
disableFocus();
enableBackground(true);
enableBorder(false);
enableTopMost(true);
@ -154,6 +155,7 @@ namespace ogfx
setMargin({ 0, 0, 0, 0 });
setTypeName("ogfx::gui::Label");
disableChildren();
disableFocus();
enableBackground(true);
enableBorder(false);
enableTopMost(true);

View file

@ -277,11 +277,11 @@ namespace ogfx
auto& item = *m_list[i];
if (!item.isValid()) continue;
if (!item.isVisible()) continue;
if (!item.isIconEnabled()) continue;
f32 itemH = item.getDimensions().y;
if (y > visibleEnd) break;
if (item.getIcon().getFrameCount() != 1)
std::cout << item.getIcon().getCurrentFrame() << "\n";
item.getIcon().update();
if (item.isIconEnabled())
item.getIcon().update();
y += itemH;
}
}

View file

@ -36,6 +36,15 @@ namespace ogfx
m_window = &window;
}
Widget::~Widget(void)
{
if (m_window && m_tabIndex >= 0)
cast<Window&>(*m_window).getFocusManager().unregisterTabIndex(*this);
auto& fm = cast<Window&>(*m_window).getFocusManager();
if (fm.getFocused() == this)
fm.clearFocus(); // you'd add this method
}
bool Widget::addWidget(Widget& child, const Vec2& position, bool __skip_callback)
{
if (!m_allowChildren)
@ -229,6 +238,16 @@ namespace ogfx
m_visible = v;
}
bool Widget::setTabIndex(i32 index)
{
if (cast<Window&>(getWindow()).getFocusManager().registerTabIndex(*this, index))
{
m_tabIndex = index;
return true;
}
return false;
}
void Widget::setLayout(std::unique_ptr<Layout> layout)
{
m_layout = std::move(layout);
@ -297,6 +316,9 @@ namespace ogfx
if (m_showBorder)
gfx.drawRoundRect({ getGlobalPosition(), getSize() }, m_borderColor, m_borderRadius, m_borderWidth);
afterDraw(gfx);
if (isFocused())
gfx.drawRoundRect({ getGlobalPosition(), getSize() }, Colors::Red, m_borderRadius, 3);
}
void Widget::__update(void)
@ -416,41 +438,26 @@ namespace ogfx
void Widget::__onKeyPressed(const Event& event)
{
if (event.isHandled() || !isKeyPressedEventEnabled()) return;
if (hasChildren())
m_widgets.onKeyPressed(event);
if (!event.isHandled())
{
if (callback_onKeyPressed)
callback_onKeyPressed(event);
onKeyPressed(event);
}
if (callback_onKeyPressed)
callback_onKeyPressed(event);
onKeyPressed(event);
}
void Widget::__onKeyReleased(const Event& event)
{
if (event.isHandled() || !isKeyReleasedEventEnabled()) return;
m_pressedButton = ogfx::MouseEventData::eButton::None;
if (hasChildren())
m_widgets.onKeyReleased(event);
if (!event.isHandled())
{
if (callback_onKeyReleased)
callback_onKeyReleased(event);
onKeyReleased(event);
}
if (callback_onKeyReleased)
callback_onKeyReleased(event);
onKeyReleased(event);
}
void Widget::__onTextEntered(const Event& event)
{
if (event.isHandled() || !isTextEnteredEventEnabled()) return;
if (hasChildren())
m_widgets.onTextEntered(event);
if (!event.isHandled())
{
if (callback_onTextEntered)
callback_onTextEntered(event);
onTextEntered(event);
}
if (callback_onTextEntered)
callback_onTextEntered(event);
onTextEntered(event);
}
void Widget::__onWindowClosed(const Event& event)

View file

@ -66,6 +66,11 @@ namespace ogfx
public:
// ================================== MAIN =================================
Widget(const Rectangle& bounds, WindowCore& window);
Widget(Widget&&) = default;
Widget& operator=(Widget&&) = delete;
Widget(const Widget&) = delete;
Widget& operator=(const Widget&) = delete;
virtual ~Widget(void);
bool addWidget(Widget& child, const Vec2& position = { 0, 0 }, bool __skip_callback = false);
bool removeWidget(Widget& child);
void enable(bool enable = true);
@ -76,6 +81,7 @@ namespace ogfx
bool addThemeID(const String& id);
bool removeThemeID(const String& id);
void setVisible(bool v);
bool setTabIndex(i32 index);
virtual void setCallback(eCallback type, EventCallback callback);
using Rectangle::contains; bool contains(Vec2 p, bool includeBounds = false) const override;
template<typename T>
@ -154,10 +160,10 @@ namespace ogfx
inline static void setDragAndDropData(ostd::BaseObject& data) { s_dragAndDropData = &data; s_hasDragAndDropData = true; }
inline static void clearDragAndDropData(void) { s_dragAndDropData = nullptr; }
inline static ostd::BaseObject* getDragAndDropData(void) { return s_dragAndDropData; }
inline i32 getTabIndex(void) const { return m_tabIndex; }
inline WindowCore& getWindow(void) { return *m_window; }
inline Widget* getParent(void) { return m_parent; }
inline const Widget* getParent(void) const { return m_parent; }
inline i32 getZIndex(void) const { return m_zIndex; }
inline ogfx::MouseEventData::eButton getPressedMouseButton(void) const { return m_pressedButton; }
inline const stdvec<String>& getThemeIDList(void) const { return m_themeIDList; }
inline const ostd::Stylesheet::QualifierList& getThemeQualifierList(void) const { return m_qualifierList; }
@ -172,7 +178,6 @@ namespace ogfx
OSTD_PARAM_GETSET(ostd::ColorGradient, BackgroundGradient, m_backgroundGradient);
OSTD_PARAM_GETSET(Rectangle, Padding, m_padding);
OSTD_PARAM_GETSET(Rectangle, Margin, m_margin);
OSTD_PARAM_GETSET(i32, TabIndex, m_tabIndex);
OSTD_PARAM_GETSET(Vec2, ContentOffset, m_contentOffset);
OSTD_PARAM_GETSET(String, TooltipText, m_tooltipText);
@ -236,7 +241,6 @@ namespace ogfx
bool m_rootChild { false };
bool m_focused { false };
i32 m_tabIndex { -1 };
i32 m_zIndex { -1 };
String m_tooltipText { "" };
MouseEventData::eButton m_pressedButton { MouseEventData::eButton::None };
// ====================
@ -313,6 +317,7 @@ namespace ogfx
static ostd::BaseObject* s_dragAndDropData;
static bool s_hasDragAndDropData;
friend class WidgetManager;
friend class FocusManager;
// ====================

View file

@ -27,13 +27,21 @@ namespace ogfx
{
m_animData = ad;
m_spriteSheet = &spriteSheet;
m_timer.create(m_animData.fps, [&](f64 dt) -> void {
update_animation();
});
m_timer.startCount(ostd::eTimeUnits::Milliseconds);
update_animation();
return *this;
}
void Animation::update(void)
{
f64 dtms = (1.0 / cast<f64>(m_animData.fps)) * 1000.0;
if (m_timer.read() >= dtms)
{
update_animation();
m_timer.restart(ostd::eTimeUnits::Milliseconds);
}
}
void Animation::resetAnimation(void)
{
m_currentFrame = 0;

View file

@ -35,9 +35,9 @@ namespace ogfx
inline Animation(const AnimationData& ad) { create(ad); };
inline Animation(const AnimationData& ad, Image& spriteSheet) { create(ad, spriteSheet); };
inline Animation& create(const AnimationData& ad) { return create(ad, InvalidImage); }
inline void update(void) { m_timer.update(); }
inline i32 getCurrentFrame(void) const { return m_currentFrame; }
Animation& create(const AnimationData& ad, Image& spriteSheet);
void update(void);
void resetAnimation(void);
inline void setFrameCount(i32 n) { m_animData.frameCount = n; }
@ -88,7 +88,7 @@ namespace ogfx
private:
inline static Image InvalidImage;
AnimationData m_animData;
ostd::StepTimer m_timer;
ostd::Counter m_timer;
Image* m_spriteSheet { nullptr };
i32 m_currentFrame { 0 };
bool m_back { false };

View file

@ -44,6 +44,19 @@ using stdvec = std::vector<T>;
template<typename Key, typename Value>
using stdumap = std::unordered_map<Key, Value>;
using i8Range = std::numeric_limits<i8>;
using i16Range = std::numeric_limits<i16>;
using i32Range = std::numeric_limits<i32>;
using i64Range = std::numeric_limits<i64>;
using u8Range = std::numeric_limits<u8>;
using u16Range = std::numeric_limits<u16>;
using u32Range = std::numeric_limits<u32>;
using u64Range = std::numeric_limits<u64>;
using f32Range = std::numeric_limits<f32>;
using f64Range = std::numeric_limits<f64>;
#define cast static_cast
namespace ostd

View file

@ -412,7 +412,7 @@ namespace ostd
u64 Timer::start(bool print, const String& name, eTimeUnits timeUnit, OutputHandlerBase* __destination)
u64 Counter::start(bool print, const String& name, eTimeUnits timeUnit, OutputHandlerBase* __destination)
{
m_timeUnit = timeUnit;
m_started = true;
@ -459,7 +459,7 @@ namespace ostd
return 0;
}
u64 Timer::startCount(eTimeUnits timeUnit)
u64 Counter::startCount(eTimeUnits timeUnit)
{
m_timeUnit = timeUnit;
m_started = true;
@ -483,7 +483,7 @@ namespace ostd
return 0;
}
u64 Timer::end(bool print)
u64 Counter::end(bool print)
{
if (!m_started) return 0;
m_started = false;
@ -537,7 +537,7 @@ namespace ostd
return diff;
}
u64 Timer::endCount(bool stop)
u64 Counter::endCount(bool stop)
{
if (!m_started) return 0;
m_started = !stop;
@ -562,7 +562,7 @@ namespace ostd
return diff;
}
u64 Timer::restart(eTimeUnits timeUnit)
u64 Counter::restart(eTimeUnits timeUnit)
{
if (!m_started)
{
@ -574,7 +574,7 @@ namespace ostd
return elapsed;
}
u64 Timer::getEpoch(eTimeUnits timeUnit)
u64 Counter::getEpoch(eTimeUnits timeUnit)
{
switch (timeUnit)
{

View file

@ -59,11 +59,11 @@ namespace ostd
template <class T>
class _Counter
class _BasicCounter
{
public:
inline _Counter(void) { m_count = 200; m_current = 0; }
inline _Counter(T count, T step = 1) { m_count = count; m_current = 0; m_step = step; }
inline _BasicCounter(void) { m_count = 200; m_current = 0; }
inline _BasicCounter(T count, T step = 1) { m_count = count; m_current = 0; m_step = step; }
inline void setCount(T count) { m_count = count; m_counting = false; m_current = 0; }
inline T getCount(void) { return m_count; }
inline T getCurrent(void) { return m_current; };
@ -87,15 +87,15 @@ namespace ostd
bool m_counting { false };
};
typedef _Counter<u64> Counter;
typedef _Counter<f32> FloatCounter;
typedef _Counter<f64> DoubleCounter;
typedef _BasicCounter<u64> BasicCounter;
typedef _BasicCounter<f32> BasicFloatCounter;
typedef _BasicCounter<f64> BasicDoubleCounter;
class OutputHandlerBase;
class Timer
class Counter
{
public:
inline Timer(void) { m_started = false; m_current = 0; m_timeUnit = eTimeUnits::Nanoseconds; m_dest = nullptr; }
inline Counter(void) { m_started = false; m_current = 0; m_timeUnit = eTimeUnits::Nanoseconds; m_dest = nullptr; }
u64 start(bool print = true, const String& name = "", eTimeUnits timeUnit = eTimeUnits::Nanoseconds, OutputHandlerBase* __destination = nullptr);
u64 startCount(eTimeUnits timeUnit = eTimeUnits::Nanoseconds);
u64 end(bool print = true);
@ -172,7 +172,7 @@ namespace ostd
u16 years;
private:
Timer m_rtClock;
Counter m_rtClock;
f32 m_timeOfDay;
f32 m_totalSeconds;
};

View file

@ -100,9 +100,11 @@ class TestWindow : public Window
m_btn1.setCallback(Widget::eCallback::ActionPerformed, [&](const Event& event) -> void {
std::cout << showOpenFileDialog(filters) << "\n";
});
m_btn1.setTabIndex(1);
m_check1.setText("Check this out!");
m_check1.setChecked(true);
m_check1.setTabIndex(2);
m_check1.setStateChangedCallback([&](CheckBox& sender, bool state) -> void {
m_panel1.setVisible(state);
});
@ -195,12 +197,13 @@ class TestWindow : public Window
// });
m_list.collapseAll();
for (i32 i = 0; i < 20; i++)
for (i32 i = 0; i < 17; i++)
{
auto icon = l_randomIcon(iconsAD);
auto& btn = getToolBar().addButton("./icons.png");
btn.enableAnimated();
btn.setAnimationData(icon);
btn.setTabIndex(i + 100);
}
m_tabs.setSize(900, 700);
@ -210,9 +213,12 @@ class TestWindow : public Window
auto& t3 = m_tabs.addTab("Long Tab Test");
t1.addWidget(m_check1, { 30, 30 });
m_radioGroup.addButton(t1, "Radio this out!", { 30, 80 });
m_radioGroup.addButton(t1, "Radio Opt. 2", { 30, 110 });
m_radioGroup.addButton(t1, "Radio 3", { 30, 140 });
auto& rb1 = m_radioGroup.addButton(t1, "Radio this out!", { 30, 80 });
rb1.setTabIndex(3);
auto& rb2 = m_radioGroup.addButton(t1, "Radio Opt. 2", { 30, 110 });
rb2.setTabIndex(4);
auto& rb3 = m_radioGroup.addButton(t1, "Radio 3", { 30, 140 });
rb3.setTabIndex(5);
m_radioGroup.setSelectionChangedCallback([&](RadioButton& previous, RadioButton& sender) -> void {
std::cout << previous.getText() << "\n";
std::cout << sender.getText() << "\n\n";
@ -492,7 +498,7 @@ i32 main(i32 argc, char** argv)
ostd::Random::autoSeed();
ostd::initialize();
TestWindow window;
window.initialize(1200, 1100, "OmniaFramework - Test Window");
window.initialize(1200, 900, "OmniaFramework - Test Window");
window.setClearColor({ 0, 0, 0 });
window.setPosition({ 50, 50 });
// window.setWindowState(ogfx::WindowCore::eWindowState::Maximized);