Added FocusManager
This commit is contained in:
parent
67a1dd605d
commit
203a305633
24 changed files with 373 additions and 164 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
||||
|
|
|
|||
154
src/ogfx/gui/FocusManager.cpp
Normal file
154
src/ogfx/gui/FocusManager.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
55
src/ogfx/gui/FocusManager.hpp
Normal file
55
src/ogfx/gui/FocusManager.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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 };
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ namespace ogfx
|
|||
setPosition(0, win.getMenuBar().getHeight());
|
||||
else
|
||||
setPosition(0, 0);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ namespace ogfx
|
|||
setPadding({ 0, 0, 0, 0 });
|
||||
setTypeName("ogfx::gui::ProgressBar");
|
||||
disableChildren();
|
||||
disableFocus();
|
||||
enableBackground();
|
||||
enableBorder();
|
||||
setStylesheetCategoryName("progressbar");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
// ====================
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue