diff --git a/other/TODO.txt b/other/TODO.txt new file mode 100644 index 0000000..37be370 --- /dev/null +++ b/other/TODO.txt @@ -0,0 +1 @@ +Add Color constants to ostd::Color \ No newline at end of file diff --git a/other/build.nr b/other/build.nr index a862943..c05d464 100644 --- a/other/build.nr +++ b/other/build.nr @@ -1 +1 @@ -2040 +2041 diff --git a/other/changelog.txt b/other/changelog.txt deleted file mode 100644 index ee4f637..0000000 --- a/other/changelog.txt +++ /dev/null @@ -1,19 +0,0 @@ -[0.2.1958] -Added ostd::OutputHandlerBase::tab() virtual function -Added ogfx::WindowBaseOutputHandler class - -[0.2.1936] -Added JsonFile class to handle Json files -Added MidiParser class to parse Midi files - -[0.2.1923] -Added MacOS support - -[0.1.1823] -Added more Builtin signals - -[0.1.1818] -Added ostd::String::fixedLength(...) method, with corresponding 'new_fixedLength' version. -Added "KeyboardController" class to (implemented in ) to handle - keypresses in the console in a cross-platform way -Added helper functions in ostd::Utils for dealing with raw arrays diff --git a/src/ogfx/gui/Window.cpp b/src/ogfx/gui/Window.cpp index a8a920d..933fa1e 100644 --- a/src/ogfx/gui/Window.cpp +++ b/src/ogfx/gui/Window.cpp @@ -20,15 +20,11 @@ #include "Window.hpp" #include "../../ostd/utils/Time.hpp" -#include "io/IOHandlers.hpp" -#include "string/String.hpp" -#include -#include -#include -#include namespace ogfx { + const Uint32 REDRAW_EVENT = SDL_RegisterEvents(1); + WindowCore::~WindowCore(void) { __on_window_destroy(); @@ -36,21 +32,18 @@ namespace ogfx SDL_DestroyCursor(m_cursor_Arrow); SDL_DestroyRenderer(m_renderer); SDL_DestroyWindow(m_window); - SDL_Quit(); - TTF_Quit(); + SDLSysten::release(); } void WindowCore::initialize(int32_t width, int32_t height, const ostd::String& title) { if (m_initialized) return; + SDLSysten::acquire(); m_windowWidth = width; m_windowHeight = height; m_title = title; - if (!SDL_Init(SDL_INIT_VIDEO)) - { - printf( "SDL could not initialize! Error: %s\n", SDL_GetError() ); - exit(1); - } + setBlockingEventsRefreshFPS(DefaultBlockingEventsFPS); + m_window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOW_RESIZABLE); m_renderer = SDL_CreateRenderer(m_window, nullptr); SDL_SetWindowMinimumSize(m_window, m_windowWidth, m_windowHeight); @@ -60,6 +53,13 @@ namespace ogfx m_cursor_Arrow = SDL_CreateSystemCursor(SDL_SystemCursor::SDL_SYSTEM_CURSOR_DEFAULT); m_cursor_IBeam = SDL_CreateSystemCursor(SDL_SystemCursor::SDL_SYSTEM_CURSOR_TEXT); + m_wout.attachWindow(*this); + m_wout.setFontSize(22); + m_wout.setConsoleMaxCharacters({ 1000, 1000 }); + m_wout.setConsolePosition({ 5, 5 }); + m_wout.setWrapMode(ogfx::GraphicsWindowOutputHandler::eWrapMode::NewLine); + m_wout.setDefaultForegroundColor({ 255, 255, 255, 255 }); + m_initialized = true; m_running = true; @@ -81,6 +81,12 @@ namespace ogfx __on_window_init(width, height, title); } + void WindowCore::mainLoop(void) + { + if (isInitialized()) + __main_loop(); + } + void WindowCore::close(void) { __on_window_close(); @@ -136,6 +142,24 @@ namespace ogfx SDL_DestroySurface(appIcon); } + void WindowCore::setBlockingEventsRefreshFPS(uint32_t fps) + { + if (fps == 0 || fps > MaxBlockingEventsFPS) + { + setBlockingEventsRefreshFPS(DefaultBlockingEventsFPS); + return; + } + m_blockingEventsDelay = static_cast(std::floor((1.0 / (double)fps) * 1000)); + } + + void WindowCore::requestRedraw(void) + { + SDL_Event e{}; + SDL_zero(e); + e.type = REDRAW_EVENT; + SDL_PushEvent(&e); + } + MouseEventData WindowCore::get_mouse_state(void) { float mx = 0, my = 0; @@ -159,7 +183,7 @@ namespace ogfx if (isBlockingEventsEnabled()) { - if (SDL_WaitEventTimeout(&event, 16)) + if (SDL_WaitEventTimeout(&event, m_blockingEventsDelay)) __handle_event(event); } else @@ -169,9 +193,26 @@ namespace ogfx } } + void WindowCore::before_render(void) + { + SDL_SetRenderDrawColor(m_renderer, getClearColor().r, getClearColor().g, getClearColor().b, getClearColor().a); + if (m_refreshScreen) + SDL_RenderClear(m_renderer); + wout().beginFrame(); + } + + void WindowCore::after_render(void) + { + SDL_RenderPresent(m_renderer); + } + void WindowCore::__handle_event(SDL_Event& event) { - if (event.type == SDL_EVENT_QUIT) + if (event.type == REDRAW_EVENT) + { + //Doesn't need to do anything, the event exists just to make SDL_WaitEventTimeout() return early + } + else if (event.type == SDL_EVENT_QUIT) { close(); } @@ -254,13 +295,11 @@ namespace ogfx void GraphicsWindow::__main_loop(void) { handle_events(); - SDL_SetRenderDrawColor(m_renderer, getClearColor().r, getClearColor().g, getClearColor().b, getClearColor().a); - if (m_refreshScreen) - SDL_RenderClear(m_renderer); + before_render(); m_fixedUpdateTImer.update(); onUpdate(); onRender(); - SDL_RenderPresent(m_renderer); + after_render(); m_frameTimeAcc += m_fpsUpdateClock.restart(ostd::eTimeUnits::Seconds); m_frameCount++; m_fpsUpdateTimer.update(); @@ -299,10 +338,10 @@ namespace ogfx while (isRunning()) { handle_events(); - SDL_SetRenderDrawColor(m_renderer, getClearColor().r, getClearColor().g, getClearColor().b, getClearColor().a); - SDL_RenderClear(m_renderer); + before_render(); + onRedraw(m_gfx); m_gfx.drawString("Hello World", { 100, 100 }, { 255, 0, 0 }); - SDL_RenderPresent(m_renderer); + after_render(); } } diff --git a/src/ogfx/gui/Window.hpp b/src/ogfx/gui/Window.hpp index 1655661..5a00df2 100644 --- a/src/ogfx/gui/Window.hpp +++ b/src/ogfx/gui/Window.hpp @@ -20,13 +20,13 @@ #pragma once -#include #include #include #include #include #include #include +#include namespace ogfx { @@ -38,13 +38,15 @@ namespace ogfx virtual ~WindowCore(void); inline WindowCore(int32_t width, int32_t height, const ostd::String& title) { initialize(width, height, title); } void initialize(int32_t width, int32_t height, const ostd::String& title); - inline void mainLoop(void) { if (isInitialized()) __main_loop(); } + void mainLoop(void); void close(void); void setSize(int32_t width, int32_t height); void setTitle(const ostd::String& title); void setCursor(eCursor cursor); void enableResizable(bool enable = true); void setIcon(const ostd::String& iconFilePath); + void setBlockingEventsRefreshFPS(uint32_t fps); + void requestRedraw(void); inline bool isInitialized(void) const { return m_initialized; } inline bool isRunning(void) const { return m_running; } @@ -60,10 +62,14 @@ namespace ogfx inline SDL_Renderer* getSDLRenderer(void) { return m_renderer; } inline void enableBlockingEvents(bool enable = true) { m_blockingEvents = enable; } inline bool isBlockingEventsEnabled(void) const { return m_blockingEvents; } + inline ostd::ConsoleOutputHandler& out(void) { return m_out; } + inline GraphicsWindowOutputHandler& wout(void) { return m_wout; } protected: MouseEventData get_mouse_state(void); virtual void handle_events(void); + virtual void before_render(void); + virtual void after_render(void); inline virtual void __on_event(SDL_Event& event) { } inline virtual void __on_window_init(int32_t width, int32_t height, const ostd::String& title) { } inline virtual void __on_window_destroy(void) { } @@ -76,6 +82,8 @@ namespace ogfx protected: SDL_Window* m_window { nullptr }; SDL_Renderer* m_renderer { nullptr }; + ostd::ConsoleOutputHandler m_out; + GraphicsWindowOutputHandler m_wout; private: ostd::Color m_clearColor { 10, 10, 10, 255 }; @@ -83,15 +91,21 @@ namespace ogfx int32_t m_windowWidth { 0 }; int32_t m_windowHeight { 0 }; ostd::String m_title { "" }; + int32_t m_blockingEventsDelay { 33 }; bool m_running { false }; bool m_initialized { false }; bool m_visible { true }; bool m_blockingEvents { false }; bool m_resizeable { true }; + bool m_refreshScreen { true }; SDL_Cursor* m_cursor_IBeam { nullptr }; SDL_Cursor* m_cursor_Arrow { nullptr }; + + public: + inline static constexpr int32_t MaxBlockingEventsFPS { 240 }; + inline static constexpr int32_t DefaultBlockingEventsFPS { 30 }; }; class GraphicsWindow : public WindowCore { @@ -124,7 +138,6 @@ namespace ogfx ostd::Timer m_fpsUpdateClock; uint64_t m_frameTimeAcc { 0 }; int32_t m_frameCount { 0 }; - bool m_refreshScreen { true }; }; namespace gui { @@ -139,6 +152,7 @@ namespace ogfx inline virtual void onDestroy(void) { } inline virtual void onClose(void) { } inline virtual void onSDLEvent(SDL_Event& event) { } + inline virtual void onRedraw(BasicRenderer2D& gfx) { } protected: void __on_window_init(int32_t width, int32_t height, const ostd::String& title) override; @@ -148,8 +162,7 @@ namespace ogfx void __main_loop(void) override; private: - ogfx::BasicRenderer2D m_gfx; - ostd::ConsoleOutputHandler m_out; + BasicRenderer2D m_gfx; }; } } diff --git a/src/ogfx/gui/WindowOutputHandler.cpp b/src/ogfx/gui/WindowOutputHandler.cpp index 707f92f..66b9e40 100644 --- a/src/ogfx/gui/WindowOutputHandler.cpp +++ b/src/ogfx/gui/WindowOutputHandler.cpp @@ -26,6 +26,7 @@ namespace ogfx { using namespace ostd; + GraphicsWindowOutputHandler::GraphicsWindowOutputHandler(void) { } diff --git a/src/ogfx/render/FontUtils.cpp b/src/ogfx/render/FontUtils.cpp index cadb5b9..3cf3ea2 100644 --- a/src/ogfx/render/FontUtils.cpp +++ b/src/ogfx/render/FontUtils.cpp @@ -23,6 +23,24 @@ namespace ogfx { + TTFRenderer::SignalHandler::SignalHandler(TTFRenderer& parent) : m_parent(parent) + { + enableSignals(); + connectSignal(ostd::tBuiltinSignals::BeforeSDLShutdown); + setTypeName("ostd::GraphicsWindowOutputHandler::SignalHandler"); + validate(); + } + + void TTFRenderer::SignalHandler::handleSignal(ostd::tSignal& signal) + { + if (signal.ID == ostd::tBuiltinSignals::BeforeSDLShutdown) + { + m_parent.closeFont(); + } + } + + + TTFRenderer::~TTFRenderer(void) { closeFont(); @@ -179,12 +197,6 @@ namespace ogfx { if (TTFRenderer::m_initialized) return set_error_state(tErrors::NoError); if (renderer == nullptr) return set_error_state(tErrors::NullRenderer); - if (!TTF_Init()) - { - print_ttf_error("TTF_Init"); - SDL_Quit(); - return set_error_state(tErrors::FailedToLoad); - } m_renderer = renderer; TTFRenderer::m_initialized = true; return set_error_state(tErrors::NoError); diff --git a/src/ogfx/render/FontUtils.hpp b/src/ogfx/render/FontUtils.hpp index a673007..0207fdb 100644 --- a/src/ogfx/render/FontUtils.hpp +++ b/src/ogfx/render/FontUtils.hpp @@ -28,6 +28,15 @@ namespace ogfx { class TTFRenderer { + private: class SignalHandler : public ostd::BaseObject + { + public: + SignalHandler(TTFRenderer& parent); + void handleSignal(ostd::tSignal& signal) override; + + private: + TTFRenderer& m_parent; + }; public: struct tErrors { inline static constexpr int32_t NoError = 0; @@ -68,6 +77,7 @@ namespace ogfx private: ostd::ConsoleOutputHandler m_out; + SignalHandler m_sigHndl { *this }; bool m_initialized { false }; bool m_fontOpen { false }; TTF_Font* m_font { nullptr }; diff --git a/src/ogfx/utils/SDLInclude.hpp b/src/ogfx/utils/SDLInclude.hpp index cfac199..082e157 100644 --- a/src/ogfx/utils/SDLInclude.hpp +++ b/src/ogfx/utils/SDLInclude.hpp @@ -21,6 +21,8 @@ #pragma once #include +#include +#include #include #include @@ -29,3 +31,55 @@ #include #include #include + +namespace ogfx +{ + class SDLSysten + { + public: + static inline void acquire(void) + { + if (s_refCount == 0) + init_sdl(); + s_refCount++; + } + + static inline void release(void) + { + s_refCount--; + if (s_refCount == 0) + shutdown_sdl(); + } + + private: + static inline void init_sdl(void) + { + if (!SDL_Init(SDL_INIT_VIDEO)) + { + OX_FATAL("SDL could not initialize! Error: %s\n", SDL_GetError()); + exit(ErrorSDLInitFailed); + } + if (!TTF_Init()) + { + OX_FATAL("SDL_ttf could not initialize! Error: %s\n", SDL_GetError()); + SDL_Quit(); + exit(ErrorSDLTTFInitFailed); + } + } + + static inline void shutdown_sdl(void) + { + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::BeforeSDLShutdown, ostd:: tSignalPriority::RealTime); + TTF_Quit(); + SDL_Quit(); + } + + private: + inline static int s_refCount { 0 }; + + public: + inline static constexpr int32_t ErrorSDLInitFailed { 1 }; + inline static constexpr int32_t ErrorSDLTTFInitFailed { 2 }; + + }; +} diff --git a/src/ostd/ostd.hpp b/src/ostd/ostd.hpp index 345fe5c..7a2bad2 100644 --- a/src/ostd/ostd.hpp +++ b/src/ostd/ostd.hpp @@ -45,3 +45,11 @@ #include #include + +namespace ostd +{ + inline void initialize(void) + { + ostd::SignalHandler::init(true); + } +} diff --git a/src/ostd/utils/Signals.cpp b/src/ostd/utils/Signals.cpp index c2ef328..888a2ae 100755 --- a/src/ostd/utils/Signals.cpp +++ b/src/ostd/utils/Signals.cpp @@ -31,6 +31,8 @@ namespace ostd SignalHandler::m_delegatedSignals.reserve(SignalHandler::__DELEGATED_SIGNALS_BUFFER_START_SIZE); SignalHandler::m_onGuiEventRecievers.clear(); SignalHandler::m_onGuiEventRecievers.reserve(SignalHandler::__SIGNAL_BUFFER_START_SIZE); + SignalHandler::m_beforeSDLShutdownRecievers.clear(); + SignalHandler::m_beforeSDLShutdownRecievers.reserve(SignalHandler::__SIGNAL_BUFFER_START_SIZE); m_initialized = true; } @@ -71,6 +73,8 @@ namespace ostd sig_list = &m_windowClosedRecievers; else if (signal_id == tBuiltinSignals::OnGuiEvent) sig_list = &m_onGuiEventRecievers; + else if (signal_id == tBuiltinSignals::BeforeSDLShutdown) + sig_list = &m_beforeSDLShutdownRecievers; else if (signal_id > tBuiltinSignals::CustomSignalBase) sig_list = &m_customRecievers; if (sig_list == nullptr) @@ -110,6 +114,8 @@ namespace ostd m_windowClosedRecievers.push_back({ &object, signal_id }); else if (signal_id == tBuiltinSignals::OnGuiEvent) m_onGuiEventRecievers.push_back({ &object, signal_id }); + else if (signal_id == tBuiltinSignals::BeforeSDLShutdown) + m_beforeSDLShutdownRecievers.push_back({ &object, signal_id }); else if (signal_id > tBuiltinSignals::CustomSignalBase) m_customRecievers.push_back({ &object, signal_id }); else diff --git a/src/ostd/utils/Signals.hpp b/src/ostd/utils/Signals.hpp index 4293668..8bc74aa 100755 --- a/src/ostd/utils/Signals.hpp +++ b/src/ostd/utils/Signals.hpp @@ -45,6 +45,8 @@ namespace ostd inline static constexpr uint32_t OnGuiEvent = 0x2001; + inline static constexpr uint32_t BeforeSDLShutdown = 0x3001; + inline static constexpr uint32_t WindowResized = 0x1001; inline static constexpr uint32_t WindowClosed = 0x1002; /*********************/ @@ -106,6 +108,7 @@ namespace ostd inline static std::vector m_windowResizedRecievers; inline static std::vector m_windowClosedRecievers; inline static std::vector m_onGuiEventRecievers; + inline static std::vector m_beforeSDLShutdownRecievers; /************************************/ inline static constexpr uint16_t __SIGNAL_BUFFER_START_SIZE { 128 }; diff --git a/src/test/GuiTest.cpp b/src/test/GuiTest.cpp index baff5e7..4087ab4 100644 --- a/src/test/GuiTest.cpp +++ b/src/test/GuiTest.cpp @@ -34,6 +34,8 @@ * ListBox * ComboBox * TreeView + * Save/Open File Dialogs (and folder dialogs) + * Tab Panel */ #include @@ -58,11 +60,17 @@ class Window : public ogfx::gui::Window } } + void onRedraw(ogfx::BasicRenderer2D& gfx) override + { + wout().fg(ostd::Color { 255, 0, 0 }).p("Hello ").fg(ostd::Color { 0, 0, 255 }).p("World"); + } + private: }; int main(int argc, char** argv) { + ostd::initialize(); Window window; window.initialize(800, 600, "OmniaFramework - Test Window"); window.setClearColor({ 0, 0, 0 });