diff --git a/Makefile b/Makefile index f48ad2a..b0be399 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ COMMON = \ \ filewatcher.cc \ window.cc \ + dialogs.cc \ globals.cc \ profiling.cc \ shaders.cc \ diff --git a/camera.hh b/camera.hh index 53f345a..2979dd9 100644 --- a/camera.hh +++ b/camera.hh @@ -1,5 +1,5 @@ #pragma once -#include "imousectrl.hh" +#include "mousectrl.hh" /*= T_Camera =================================================================*/ diff --git a/dialogs.cc b/dialogs.cc new file mode 100644 index 0000000..d6a0459 --- /dev/null +++ b/dialogs.cc @@ -0,0 +1,170 @@ +#include "externals.hh" +#include "dialogs.hh" +#include + + +/*= A_ModalDialog ============================================================*/ + +A_ModalDialog::A_ModalDialog( + char const* const id ) noexcept + : id_{ id , uint32_t( strlen( id ) + 1 ) } +{} + +A_ModalDialog::A_ModalDialog( + T_String const& id ) noexcept +{ + if ( id && id[ id.length( ) - 1 ] == 0 ) { + id_ = ebcl::T_Buffer< char >{ id.data( ) , id.size( ) }; + } else { + id_ = id.toOSString( ); + } +} + +A_ModalDialog::~A_ModalDialog( ) +{ } + +uint8_t A_ModalDialog::addButton( + char const* name ) noexcept +{ + buttons_.addNew( name , strlen( name ) + 1 ); + return buttons_.size( ) - 1; +} + +uint8_t A_ModalDialog::addButton( + T_String const& name ) noexcept +{ + if ( name && name[ name.length( ) - 1 ] == 0 ) { + buttons_.addNew( name.data( ) , name.size( ) ); + } else { + buttons_.add( name.toOSString( ) ); + } + return buttons_.size( ); +} + +void A_ModalDialog::initDialog( ) noexcept +{} + +bool A_ModalDialog::draw( ) noexcept +{ + using namespace ImGui; + if ( !open_ ) { + assert( buttons_.size( ) ); + OpenPopup( &id_[ 0 ] ); + open_ = true; + } + + initDialog( ); + if ( initialSize_ ) { + SetNextWindowSize( *initialSize_ , ImGuiCond_Appearing ); + } + + BeginPopupModal( &id_[ 0 ] , nullptr , ImGuiWindowFlags_NoResize ); + const auto btMask{ drawDialog( ) }; + assert( btMask != 0 ); + + const ImVec2 ws( GetWindowContentRegionMax( ) ); + auto const& style( GetStyle( ) ); + const float innerWidth{ ws.x - 2 * style.FramePadding.x }; + const auto nButtons{ buttons_.size( ) }; + const ImVec2 buttonSize{ + std::max( 40.f , ( innerWidth - nButtons * style.FramePadding.x ) / nButtons ) , + 0.f + }; + + int32_t clicked{ -1 }; + for ( auto i = 0u ; i < buttons_.size( ) ; i ++ ) { + const auto m{ 1 << i }; + const bool d{ ( btMask & m ) == 0 }; + if ( i ) { + SameLine( 0 ); + } + if ( d ) { + PushItemFlag( ImGuiItemFlags_Disabled , true ); + PushStyleVar( ImGuiStyleVar_Alpha , + GetStyle( ).Alpha * .5f ); + } + if ( Button( &buttons_[ i ][ 0 ] , buttonSize ) ) { + assert( clicked == -1 ); + clicked = i; + } + if ( d ) { + PopItemFlag( ); + PopStyleVar( ); + } + } + const bool close( clicked != -1 && onButton( clicked ) ); + if ( close ) { + CloseCurrentPopup( ); + } + EndPopup( ); + return !close; +} + + +/*= T_MessageBox =============================================================*/ + +void T_MessageBox::setButtons( + const T_Buttons buttons ) noexcept +{ + assert( buttons ); + for ( auto i = 0u , nb = 0u ; i < 4 ; i ++ ) { + const E_Button b{ (E_Button) i }; + const bool hasButton{ buttons & T_Buttons{ b } }; + if ( !hasButton ) { + continue; + } + buttonMask_ |= ( 1 << nb ); + nb ++; + buttons_.add( b ); + switch ( b ) { + case BT_OK: addButton( "OK" ); break; + case BT_CANCEL: addButton( "Cancel" ); break; + case BT_YES: addButton( "Yes" ); break; + case BT_NO: addButton( "No" ); break; + } + } +} + +T_MessageBox::T_MessageBox( + char const* const title , + char const* const text , + F_Handler handler , + const T_Buttons buttons ) noexcept + : A_ModalDialog( title ) , text_{ text , strlen( text ) + 1 } , + handler_{ std::move( handler ) } +{ + setButtons( buttons ); + setInitialSize( 300.f , 100.f ); +} + +T_MessageBox::T_MessageBox( + T_String const& title , + T_String const& text , + F_Handler handler , + const T_Buttons buttons ) noexcept + : A_ModalDialog( title ) , handler_{ std::move( handler ) } +{ + if ( text && text[ text.length( ) - 1 ] == 0 ) { + text_ = ebcl::T_Buffer< char >{ text.data( ) , text.size( ) }; + } else { + text_ = text.toOSString( ); + } + setButtons( buttons ); + setInitialSize( 300.f , 100.f ); +} + +uint8_t T_MessageBox::drawDialog( ) noexcept +{ + ImGui::TextWrapped( "%s" , &text_[ 0 ] ); + return buttonMask_; +} + +bool T_MessageBox::onButton( + uint8_t button ) noexcept +{ + assert( button < buttons_.size( ) ); + if ( handler_ ) { + handler_( buttons_[ button ] ); + } + return true; +} diff --git a/dialogs.hh b/dialogs.hh new file mode 100644 index 0000000..c238fa3 --- /dev/null +++ b/dialogs.hh @@ -0,0 +1,91 @@ +#pragma once +#ifndef REAL_BUILD +# include "externals.hh" +#endif + + +/*= DIALOGS ==================================================================*/ + +// Base class for a modal dialog that can be pushed to the window's modal +// stack. +class A_ModalDialog +{ + private: + ebcl::T_Buffer< char > id_; + bool open_{ false }; + T_Optional< ImVec2 > initialSize_; + T_StaticArray< ebcl::T_Buffer< char > , 4 > buttons_; + + protected: + A_ModalDialog( char const* id ) noexcept; + A_ModalDialog( T_String const& id ) noexcept; + + void setInitialSize( const float width , + const float height ) noexcept + { initialSize_.setNew( width , height ); } + + // Add a button w/ the specified name, return its identifier + uint8_t addButton( char const* name ) noexcept; + uint8_t addButton( T_String const& name ) noexcept; + + // Initialisation, called right before the window is opened. + // Does nothing by default. + virtual void initDialog( ) noexcept; + + // Draw the dialog box's contents, returns a mask that determines + // which buttons should be enabled. + virtual uint8_t drawDialog( ) noexcept = 0; + + // Button click handler. Returns true if the dialog should be + // closed. + virtual bool onButton( uint8_t button ) noexcept = 0; + + public: + NO_COPY( A_ModalDialog ); + NO_MOVE( A_ModalDialog ); + virtual ~A_ModalDialog( ); + + // Draw and handle the dialog box. Returns true if it must be kept, + // or false if it must be removed from the stack. + bool draw( ) noexcept; +}; +using P_ModalDialog = T_OwnPtr< A_ModalDialog >; + +// Simple modal message box with configurable buttons +class T_MessageBox : public A_ModalDialog +{ + public: + enum E_Button { + BT_OK , + BT_CANCEL , + BT_YES , + BT_NO , + }; + using T_Buttons = T_Flags< E_Button >; + + // Result handler + using F_Handler = std::function< void( E_Button ) >; + + private: + ebcl::T_Buffer< char > text_; + F_Handler handler_; + uint8_t buttonMask_{ 0 }; + T_StaticArray< E_Button , 4 > buttons_; + + void setButtons( T_Buttons buttons ) noexcept; + + protected: + uint8_t drawDialog( ) noexcept override; + bool onButton( uint8_t button ) noexcept override; + + public: + T_MessageBox( char const* title , + char const* text , + F_Handler handler = { } , + T_Buttons buttons = BT_OK ) noexcept; + T_MessageBox( T_String const& title , + T_String const& text , + F_Handler handler = { } , + T_Buttons buttons = BT_OK ) noexcept; +}; + diff --git a/imousectrl.hh b/mousectrl.hh similarity index 51% rename from imousectrl.hh rename to mousectrl.hh index ab27629..7e5584a 100644 --- a/imousectrl.hh +++ b/mousectrl.hh @@ -1,30 +1,9 @@ #pragma once -#ifndef REAL_BUILD -# include "externals.hh" -#endif +#include "ui-input.hh" /*= MOUSE CONTROLS INTERFACE ===================================================*/ -enum class E_MouseButton { - LEFT , - MIDDLE , - RIGHT , -}; -using T_MouseButtons = T_Flags< E_MouseButton >; - -/*------------------------------------------------------------------------------*/ - -enum class E_KeyboardModifier -{ - CTRL , - SHIFT , - ALT , -}; -using T_KeyboardModifiers = T_Flags< E_KeyboardModifier >; - -/*------------------------------------------------------------------------------*/ - class A_MouseCtrl { public: diff --git a/sync.hh b/sync.hh index af3bfca..085ac3c 100644 --- a/sync.hh +++ b/sync.hh @@ -1,7 +1,7 @@ #pragma once #include "filewatcher.hh" #include "utilities.hh" -#include "imousectrl.hh" +#include "mousectrl.hh" #include #include diff --git a/ui-input.hh b/ui-input.hh new file mode 100644 index 0000000..16d32dc --- /dev/null +++ b/ui-input.hh @@ -0,0 +1,22 @@ +#pragma once +#ifndef REAL_BUILD +# include "externals.hh" +#endif + + +enum class E_MouseButton { + LEFT , + MIDDLE , + RIGHT , +}; +using T_MouseButtons = T_Flags< E_MouseButton >; + +/*------------------------------------------------------------------------------*/ + +enum class E_KeyboardModifier +{ + CTRL , + SHIFT , + ALT , +}; +using T_KeyboardModifiers = T_Flags< E_KeyboardModifier >; diff --git a/window.cc b/window.cc index 89611d8..8332e34 100644 --- a/window.cc +++ b/window.cc @@ -4,173 +4,6 @@ #include -/*= A_ModalDialog ============================================================*/ - -A_ModalDialog::A_ModalDialog( - char const* const id ) noexcept - : id_{ id , uint32_t( strlen( id ) + 1 ) } -{} - -A_ModalDialog::A_ModalDialog( - T_String const& id ) noexcept -{ - if ( id && id[ id.length( ) - 1 ] == 0 ) { - id_ = ebcl::T_Buffer< char >{ id.data( ) , id.size( ) }; - } else { - id_ = id.toOSString( ); - } -} - -A_ModalDialog::~A_ModalDialog( ) -{ } - -uint8_t A_ModalDialog::addButton( - char const* name ) noexcept -{ - buttons_.addNew( name , strlen( name ) + 1 ); - return buttons_.size( ) - 1; -} - -uint8_t A_ModalDialog::addButton( - T_String const& name ) noexcept -{ - if ( name && name[ name.length( ) - 1 ] == 0 ) { - buttons_.addNew( name.data( ) , name.size( ) ); - } else { - buttons_.add( name.toOSString( ) ); - } - return buttons_.size( ); -} - -void A_ModalDialog::initDialog( ) noexcept -{} - -bool A_ModalDialog::draw( ) noexcept -{ - using namespace ImGui; - if ( !open_ ) { - assert( buttons_.size( ) ); - OpenPopup( &id_[ 0 ] ); - open_ = true; - } - - initDialog( ); - if ( initialSize_ ) { - SetNextWindowSize( *initialSize_ , ImGuiCond_Appearing ); - } - - BeginPopupModal( &id_[ 0 ] , nullptr , ImGuiWindowFlags_NoResize ); - const auto btMask{ drawDialog( ) }; - assert( btMask != 0 ); - - const ImVec2 ws( GetWindowContentRegionMax( ) ); - auto const& style( GetStyle( ) ); - const float innerWidth{ ws.x - 2 * style.FramePadding.x }; - const auto nButtons{ buttons_.size( ) }; - const ImVec2 buttonSize{ - std::max( 40.f , ( innerWidth - nButtons * style.FramePadding.x ) / nButtons ) , - 0.f - }; - - int32_t clicked{ -1 }; - for ( auto i = 0u ; i < buttons_.size( ) ; i ++ ) { - const auto m{ 1 << i }; - const bool d{ ( btMask & m ) == 0 }; - if ( i ) { - SameLine( 0 ); - } - if ( d ) { - PushItemFlag( ImGuiItemFlags_Disabled , true ); - PushStyleVar( ImGuiStyleVar_Alpha , - GetStyle( ).Alpha * .5f ); - } - if ( Button( &buttons_[ i ][ 0 ] , buttonSize ) ) { - assert( clicked == -1 ); - clicked = i; - } - if ( d ) { - PopItemFlag( ); - PopStyleVar( ); - } - } - const bool close( clicked != -1 && onButton( clicked ) ); - if ( close ) { - CloseCurrentPopup( ); - } - EndPopup( ); - return !close; -} - - -/*= T_MessageBox =============================================================*/ - -void T_MessageBox::setButtons( - const T_Buttons buttons ) noexcept -{ - assert( buttons ); - for ( auto i = 0u , nb = 0u ; i < 4 ; i ++ ) { - const E_Button b{ (E_Button) i }; - const bool hasButton{ buttons & T_Buttons{ b } }; - if ( !hasButton ) { - continue; - } - buttonMask_ |= ( 1 << nb ); - nb ++; - buttons_.add( b ); - switch ( b ) { - case BT_OK: addButton( "OK" ); break; - case BT_CANCEL: addButton( "Cancel" ); break; - case BT_YES: addButton( "Yes" ); break; - case BT_NO: addButton( "No" ); break; - } - } -} - -T_MessageBox::T_MessageBox( - char const* const title , - char const* const text , - F_Handler handler , - const T_Buttons buttons ) noexcept - : A_ModalDialog( title ) , text_{ text , strlen( text ) + 1 } , - handler_{ std::move( handler ) } -{ - setButtons( buttons ); - setInitialSize( 300.f , 100.f ); -} - -T_MessageBox::T_MessageBox( - T_String const& title , - T_String const& text , - F_Handler handler , - const T_Buttons buttons ) noexcept - : A_ModalDialog( title ) , handler_{ std::move( handler ) } -{ - if ( text && text[ text.length( ) - 1 ] == 0 ) { - text_ = ebcl::T_Buffer< char >{ text.data( ) , text.size( ) }; - } else { - text_ = text.toOSString( ); - } - setButtons( buttons ); - setInitialSize( 300.f , 100.f ); -} - -uint8_t T_MessageBox::drawDialog( ) noexcept -{ - ImGui::TextWrapped( "%s" , &text_[ 0 ] ); - return buttonMask_; -} - -bool T_MessageBox::onButton( - uint8_t button ) noexcept -{ - assert( button < buttons_.size( ) ); - if ( handler_ ) { - handler_( buttons_[ button ] ); - } - return true; -} - - /*= T_Window =================================================================*/ namespace { diff --git a/window.hh b/window.hh index 7d8b2ce..8e30c22 100644 --- a/window.hh +++ b/window.hh @@ -1,103 +1,16 @@ #pragma once -#ifndef REAL_BUILD -# include "externals.hh" -#endif +#include "dialogs.hh" +#include "mousectrl.hh" -// Base class for a modal dialog that can be pushed to the window's modal -// stack. -class A_ModalDialog -{ - private: - ebcl::T_Buffer< char > id_; - bool open_{ false }; - T_Optional< ImVec2 > initialSize_; - T_StaticArray< ebcl::T_Buffer< char > , 4 > buttons_; - - protected: - A_ModalDialog( char const* id ) noexcept; - A_ModalDialog( T_String const& id ) noexcept; - - void setInitialSize( const float width , - const float height ) noexcept - { initialSize_.setNew( width , height ); } - - // Add a button w/ the specified name, return its identifier - uint8_t addButton( char const* name ) noexcept; - uint8_t addButton( T_String const& name ) noexcept; - - // Initialisation, called right before the window is opened. - // Does nothing by default. - virtual void initDialog( ) noexcept; - - // Draw the dialog box's contents, returns a mask that determines - // which buttons should be enabled. - virtual uint8_t drawDialog( ) noexcept = 0; - - // Button click handler. Returns true if the dialog should be - // closed. - virtual bool onButton( uint8_t button ) noexcept = 0; - - public: - NO_COPY( A_ModalDialog ); - NO_MOVE( A_ModalDialog ); - virtual ~A_ModalDialog( ); - - // Draw and handle the dialog box. Returns true if it must be kept, - // or false if it must be removed from the stack. - bool draw( ) noexcept; -}; -using P_ModalDialog = T_OwnPtr< A_ModalDialog >; - - -class T_MessageBox : public A_ModalDialog -{ - public: - enum E_Button { - BT_OK , - BT_CANCEL , - BT_YES , - BT_NO , - }; - using T_Buttons = T_Flags< E_Button >; - - // Result handler - using F_Handler = std::function< void( E_Button ) >; - - private: - ebcl::T_Buffer< char > text_; - F_Handler handler_; - uint8_t buttonMask_{ 0 }; - T_StaticArray< E_Button , 4 > buttons_; - - void setButtons( T_Buttons buttons ) noexcept; - - protected: - uint8_t drawDialog( ) noexcept override; - bool onButton( uint8_t button ) noexcept override; - - public: - T_MessageBox( char const* title , - char const* text , - F_Handler handler = { } , - T_Buttons buttons = BT_OK ) noexcept; - T_MessageBox( T_String const& title , - T_String const& text , - F_Handler handler = { } , - T_Buttons buttons = BT_OK ) noexcept; -}; - +/*= WINDOW MANAGEMENT ========================================================*/ struct T_Window { T_Window( ); ~T_Window( ); - void startFrame( const bool capture , - ImVec2 const& mouseInitial ) const; - void warpMouse( ImVec2 const& pos ) const; - - void swap( ) const; + //---------------------------------------------------------------------- void pushDialog( P_ModalDialog dialog ) noexcept { modals_.add( std::move( dialog ) ); } @@ -106,20 +19,29 @@ struct T_Window char const* text , T_MessageBox::F_Handler handler = { } , T_MessageBox::T_Buttons buttons = T_MessageBox::BT_OK ) - { modals_.add( NewOwned< T_MessageBox >( title , text , handler , buttons ) ); } + { pushDialog( NewOwned< T_MessageBox >( title , text , handler , buttons ) ); } void msgbox( T_String const& title , T_String const& text , T_MessageBox::F_Handler handler = { } , T_MessageBox::T_Buttons buttons = T_MessageBox::BT_OK ) - { modals_.add( NewOwned< T_MessageBox >( title , text , handler , buttons ) ); } + { pushDialog( NewOwned< T_MessageBox >( title , text , handler , buttons ) ); } - void handleDialogs( ) noexcept; + //---------------------------------------------------------------------- ImFont* defaultFont( ) const noexcept { return defaultFont_; } ImFont* smallFont( ) const noexcept { return smallFont_; } + //---------------------------------------------------------------------- + + void startFrame( const bool capture , + ImVec2 const& mouseInitial ) const; + void warpMouse( ImVec2 const& pos ) const; + + void swap( ) const; + void handleDialogs( ) noexcept; + private: SDL_Window * window; SDL_GLContext gl;