diff --git a/ui-actions.cc b/ui-actions.cc index 751d723..b6ae5da 100644 --- a/ui-actions.cc +++ b/ui-actions.cc @@ -28,6 +28,14 @@ void T_KeyboardShortcut::toString( }( ) ); } +namespace ebcl { +M_DEFINE_HASH( T_KeyboardShortcut ) +{ + return uint32_t( item.character ) << 8 + | uint32_t( item.modifiers ); +} +} // namespace ebcl + /*= T_UIAction ===============================================================*/ diff --git a/ui-actions.hh b/ui-actions.hh index 10f23c4..0939ba9 100644 --- a/ui-actions.hh +++ b/ui-actions.hh @@ -17,7 +17,14 @@ struct T_KeyboardShortcut // Writes the keyboard shortcut to a buffer void toString( char* buffer , size_t size ) const noexcept; + + // Equality operators + bool operator ==( T_KeyboardShortcut const& other ) const noexcept + { return other.character == character && other.modifiers == modifiers; } + bool operator !=( T_KeyboardShortcut const& other ) const noexcept + { return other.character != character || other.modifiers != modifiers; } }; +namespace ebcl { M_DECLARE_HASH( T_KeyboardShortcut ); } struct T_UIAction { diff --git a/ui-app.cc b/ui-app.cc index 659c6a7..fbc7130 100644 --- a/ui-app.cc +++ b/ui-app.cc @@ -19,6 +19,8 @@ static const ImWchar IconsRanges_[] = { }; } // namespace +/*----------------------------------------------------------------------------*/ + T_UIApp::T_UIApp( ) { SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER ); @@ -83,9 +85,22 @@ T_UIApp::~T_UIApp( ) SDL_Quit( ); } +/*----------------------------------------------------------------------------*/ + void T_UIApp::addAction( T_UIAction action ) noexcept { + if ( action.shortcut ) { + const auto idx{ shortcuts_.indexOf( *action.shortcut ) }; + if ( idx == T_HashIndex::INVALID_INDEX ) { + using namespace ebcl; + T_Set< T_String > nSet{ UseTag< ArrayBacked< 8 > >( ) }; + nSet.add( action.id ); + shortcuts_.add( *action.shortcut , std::move( nSet ) ); + } else { + shortcuts_[ idx ].add( action.id ); + } + } actions_.set( std::move( action ) ); } @@ -107,10 +122,13 @@ void T_UIApp::actionButton( } } +/*----------------------------------------------------------------------------*/ + void T_UIApp::handleEvents( ) noexcept { SDL_Event event; mMove_ = ImVec2( ); + kbKeys_.clear( ); while ( SDL_PollEvent( &event ) ) { ImGui_ImplSdl_ProcessEvent( &event ); if ( event.type == SDL_QUIT ) { @@ -123,13 +141,54 @@ void T_UIApp::handleEvents( ) noexcept mMove_.x += event.motion.xrel; mMove_.y += event.motion.yrel; } + + if ( event.type == SDL_KEYDOWN ) { + const auto sym{ event.key.keysym.sym }; + if ( sym >= 0 && sym <= 127 ) { + kbKeys_.add( char( sym ) ); + } + } } ImGui_ImplSdl_NewFrame( window_ , mCapture_ , mInitial_ ); - ImGui::GetIO( ).MouseDrawCursor = true; + + auto& io( ImGui::GetIO( ) ); + io.MouseDrawCursor = true; + kbMods_ = ( ([&io]() { + T_KeyboardModifiers kb; + if ( io.KeyCtrl ) { + kb |= E_KeyboardModifier::CTRL; + } + if ( io.KeyShift ) { + kb |= E_KeyboardModifier::SHIFT; + } + if ( io.KeyAlt ) { + kb |= E_KeyboardModifier::ALT; + } + return kb; + })() ); + + handleKeyboardShortcuts( ); handleMouseCapture( ); } +void T_UIApp::render( ) noexcept +{ + handleDialogs( ); + glUseProgram( 0 ); + glBindProgramPipeline( 0 ); + UI::Textures( ).reset( ); + glClearColor( 0 , 0 , 0 , 1 ); + ImGui::Render( ); +} + +void T_UIApp::swap( ) const noexcept +{ + SDL_GL_SwapWindow( window_ ); +} + +/*----------------------------------------------------------------------------*/ + void T_UIApp::handleMouseCapture( ) noexcept { using namespace ImGui; @@ -147,19 +206,6 @@ void T_UIApp::handleMouseCapture( ) noexcept } return mb; })() ); - const T_KeyboardModifiers kb( ([&io]() { - T_KeyboardModifiers kb; - if ( io.KeyCtrl ) { - kb |= E_KeyboardModifier::CTRL; - } - if ( io.KeyShift ) { - kb |= E_KeyboardModifier::SHIFT; - } - if ( io.KeyAlt ) { - kb |= E_KeyboardModifier::ALT; - } - return kb; - })() ); const bool appCanGrab( !( ImGui::IsMouseHoveringAnyWindow( ) || io.WantCaptureMouse || io.WantCaptureKeyboard ) ); @@ -173,7 +219,7 @@ void T_UIApp::handleMouseCapture( ) noexcept } else if ( mCapture_ && mDelegate_ ) { SetMouseCursor( ImGuiMouseCursor_Move ); - mDelegate_->handleDragAndDrop( mMove_ , kb , mb ); + mDelegate_->handleDragAndDrop( mMove_ , kbMods_ , mb ); } else if ( appCanGrab && mb && mDelegate_ ) { mCapture_ = true; @@ -184,23 +230,34 @@ void T_UIApp::handleMouseCapture( ) noexcept } if ( ( appCanGrab || mCapture_ ) && io.MouseWheel && mDelegate_ ) { - mDelegate_->handleWheel( io.MouseWheel , kb , mb ); + mDelegate_->handleWheel( io.MouseWheel , kbMods_ , mb ); } } -void T_UIApp::render( ) noexcept +void T_UIApp::handleKeyboardShortcuts( ) noexcept { - handleDialogs( ); - glUseProgram( 0 ); - glBindProgramPipeline( 0 ); - UI::Textures( ).reset( ); - glClearColor( 0 , 0 , 0 , 1 ); - ImGui::Render( ); -} + auto const& io{ ImGui::GetIO( ) }; + if ( io.WantCaptureKeyboard || io.WantTextInput || !modals_.empty( ) ) { + return; + } -void T_UIApp::swap( ) const noexcept -{ - SDL_GL_SwapWindow( window_ ); + for ( auto i = 0u ; i < kbKeys_.size( ) ; i ++ ) { + const T_KeyboardShortcut k{ kbKeys_[ i ] , kbMods_ }; + auto const* const ksSet{ shortcuts_.get( k ) }; + if ( !ksSet ) { + continue; + } + + const auto nsk{ ksSet->size( ) }; + for ( auto j = 0u ; j < nsk ; j ++ ) { + T_String const& s{ (*ksSet)[ j ] }; + auto const* const a{ actions_.get( s ) }; + if ( a && ( !a->enabled || a->enabled( ) ) ) { + a->handler( ); + break; + } + } + } } void T_UIApp::handleDialogs( ) noexcept diff --git a/ui-app.hh b/ui-app.hh index 1594f42..218e1c7 100644 --- a/ui-app.hh +++ b/ui-app.hh @@ -3,6 +3,8 @@ #include "ui-mousectrl.hh" #include "ui-actions.hh" +#include + /*= WINDOW MANAGEMENT ========================================================*/ @@ -33,15 +35,6 @@ struct T_UIApp void actionMenu( T_String const& id ) const noexcept; void actionButton( T_String const& id ) const noexcept; - template< typename... Args > - T_UIAction& newAction( Args&&... args ) - { - T_UIAction a{ std::forward< Args >( args ) ... }; - T_String id{ a.id }; - addAction( std::move( a ) ); - return *actions_.get( id ); - } - //---------------------------------------------------------------------- ImFont* defaultFont( ) const noexcept @@ -78,6 +71,10 @@ struct T_UIApp // Do we need to quit? bool exiting_{ false }; + // Keyboard events / state + T_KeyboardModifiers kbMods_; + T_AutoArray< char , 32 > kbKeys_; + // Mouse capture bool mCapture_{ false }; ImVec2 mInitial_{ 0 , 0 }; @@ -92,8 +89,13 @@ struct T_UIApp []( T_UIAction const& a ) -> T_String { return a.id; } }; + T_KeyValueTable< T_KeyboardShortcut , ebcl::T_Set< T_String > > shortcuts_; + // Event handling void handleMouseCapture( ) noexcept; + void handleKeyboardShortcuts( ) noexcept; + + // Modals void handleDialogs( ) noexcept; }; diff --git a/ui-sync.cc b/ui-sync.cc index bef5757..6dc98c0 100644 --- a/ui-sync.cc +++ b/ui-sync.cc @@ -12,7 +12,7 @@ T_UISync::T_UISync( ) { - UI::Main( ).newAction( "Save curves" , []() { + UI::Main( ).addAction( T_UIAction{ "Save curves" , []() { if ( Common::Sync( ).curvesFileChanged( ) ) { UI::Main( ).msgbox( "Curves file changed" , @@ -27,12 +27,12 @@ T_UISync::T_UISync( ) } else { Common::Sync( ).saveCurves( ); } - } ).setEnabledCheck( []() { + } }.setEnabledCheck( []() { return Common::Sync( ).curvesModified( ); } ).setIcon( ICON_FA_FLOPPY_O ) - .setShortcut( T_KeyboardShortcut{ 's' , E_KeyboardModifier::CTRL } ); + .setShortcut( T_KeyboardShortcut{ 's' , E_KeyboardModifier::CTRL } ) ); //----------------------------------------------------------------------------- - UI::Main( ).newAction( "Reload curves" , []() { + UI::Main( ).addAction( T_UIAction{ "Reload curves" , []() { UI::Main( ).msgbox( "Reload curves?" , "Changes you made to the curves will be lost. Do you " @@ -42,11 +42,11 @@ T_UISync::T_UISync( ) Common::Sync( ).loadCurves( ); } } , { T_MessageBox::BT_YES , T_MessageBox::BT_NO } ); - } ).setEnabledCheck( []() { + } }.setEnabledCheck( []() { return Common::Sync( ).curvesModified( ); } ).setIcon( ICON_FA_DOWNLOAD ) .setShortcut( T_KeyboardShortcut{ 'r' , - { E_KeyboardModifier::CTRL , E_KeyboardModifier::SHIFT } } ); + { E_KeyboardModifier::CTRL , E_KeyboardModifier::SHIFT } } ) ); //----------------------------------------------------------------------------- const auto addui{ [this]( char const* type , F_Override ov ) { const bool ok{ sovuis_.add( T_String{ type } , std::move( ov ) ) };