UI - Global keyboard shortcuts
This commit is contained in:
parent
0fcfeb1fee
commit
fa242b08a4
5 changed files with 116 additions and 42 deletions
|
@ -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 ===============================================================*/
|
/*= T_UIAction ===============================================================*/
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,14 @@ struct T_KeyboardShortcut
|
||||||
|
|
||||||
// Writes the keyboard shortcut to a buffer
|
// Writes the keyboard shortcut to a buffer
|
||||||
void toString( char* buffer , size_t size ) const noexcept;
|
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
|
struct T_UIAction
|
||||||
{
|
{
|
||||||
|
|
111
ui-app.cc
111
ui-app.cc
|
@ -19,6 +19,8 @@ static const ImWchar IconsRanges_[] = {
|
||||||
};
|
};
|
||||||
} // namespace <anon>
|
} // namespace <anon>
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
T_UIApp::T_UIApp( )
|
T_UIApp::T_UIApp( )
|
||||||
{
|
{
|
||||||
SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER );
|
SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER );
|
||||||
|
@ -83,9 +85,22 @@ T_UIApp::~T_UIApp( )
|
||||||
SDL_Quit( );
|
SDL_Quit( );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
void T_UIApp::addAction(
|
void T_UIApp::addAction(
|
||||||
T_UIAction action ) noexcept
|
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 ) );
|
actions_.set( std::move( action ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,10 +122,13 @@ void T_UIApp::actionButton(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
void T_UIApp::handleEvents( ) noexcept
|
void T_UIApp::handleEvents( ) noexcept
|
||||||
{
|
{
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
mMove_ = ImVec2( );
|
mMove_ = ImVec2( );
|
||||||
|
kbKeys_.clear( );
|
||||||
while ( SDL_PollEvent( &event ) ) {
|
while ( SDL_PollEvent( &event ) ) {
|
||||||
ImGui_ImplSdl_ProcessEvent( &event );
|
ImGui_ImplSdl_ProcessEvent( &event );
|
||||||
if ( event.type == SDL_QUIT ) {
|
if ( event.type == SDL_QUIT ) {
|
||||||
|
@ -123,13 +141,54 @@ void T_UIApp::handleEvents( ) noexcept
|
||||||
mMove_.x += event.motion.xrel;
|
mMove_.x += event.motion.xrel;
|
||||||
mMove_.y += event.motion.yrel;
|
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_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( );
|
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
|
void T_UIApp::handleMouseCapture( ) noexcept
|
||||||
{
|
{
|
||||||
using namespace ImGui;
|
using namespace ImGui;
|
||||||
|
@ -147,19 +206,6 @@ void T_UIApp::handleMouseCapture( ) noexcept
|
||||||
}
|
}
|
||||||
return mb;
|
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( )
|
const bool appCanGrab( !( ImGui::IsMouseHoveringAnyWindow( )
|
||||||
|| io.WantCaptureMouse
|
|| io.WantCaptureMouse
|
||||||
|| io.WantCaptureKeyboard ) );
|
|| io.WantCaptureKeyboard ) );
|
||||||
|
@ -173,7 +219,7 @@ void T_UIApp::handleMouseCapture( ) noexcept
|
||||||
|
|
||||||
} else if ( mCapture_ && mDelegate_ ) {
|
} else if ( mCapture_ && mDelegate_ ) {
|
||||||
SetMouseCursor( ImGuiMouseCursor_Move );
|
SetMouseCursor( ImGuiMouseCursor_Move );
|
||||||
mDelegate_->handleDragAndDrop( mMove_ , kb , mb );
|
mDelegate_->handleDragAndDrop( mMove_ , kbMods_ , mb );
|
||||||
|
|
||||||
} else if ( appCanGrab && mb && mDelegate_ ) {
|
} else if ( appCanGrab && mb && mDelegate_ ) {
|
||||||
mCapture_ = true;
|
mCapture_ = true;
|
||||||
|
@ -184,23 +230,34 @@ void T_UIApp::handleMouseCapture( ) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ( appCanGrab || mCapture_ ) && io.MouseWheel && mDelegate_ ) {
|
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( );
|
auto const& io{ ImGui::GetIO( ) };
|
||||||
glUseProgram( 0 );
|
if ( io.WantCaptureKeyboard || io.WantTextInput || !modals_.empty( ) ) {
|
||||||
glBindProgramPipeline( 0 );
|
return;
|
||||||
UI::Textures( ).reset( );
|
}
|
||||||
glClearColor( 0 , 0 , 0 , 1 );
|
|
||||||
ImGui::Render( );
|
|
||||||
}
|
|
||||||
|
|
||||||
void T_UIApp::swap( ) const noexcept
|
for ( auto i = 0u ; i < kbKeys_.size( ) ; i ++ ) {
|
||||||
{
|
const T_KeyboardShortcut k{ kbKeys_[ i ] , kbMods_ };
|
||||||
SDL_GL_SwapWindow( window_ );
|
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
|
void T_UIApp::handleDialogs( ) noexcept
|
||||||
|
|
20
ui-app.hh
20
ui-app.hh
|
@ -3,6 +3,8 @@
|
||||||
#include "ui-mousectrl.hh"
|
#include "ui-mousectrl.hh"
|
||||||
#include "ui-actions.hh"
|
#include "ui-actions.hh"
|
||||||
|
|
||||||
|
#include <ebcl/Sets.hh>
|
||||||
|
|
||||||
|
|
||||||
/*= WINDOW MANAGEMENT ========================================================*/
|
/*= WINDOW MANAGEMENT ========================================================*/
|
||||||
|
|
||||||
|
@ -33,15 +35,6 @@ struct T_UIApp
|
||||||
void actionMenu( T_String const& id ) const noexcept;
|
void actionMenu( T_String const& id ) const noexcept;
|
||||||
void actionButton( 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
|
ImFont* defaultFont( ) const noexcept
|
||||||
|
@ -78,6 +71,10 @@ struct T_UIApp
|
||||||
// Do we need to quit?
|
// Do we need to quit?
|
||||||
bool exiting_{ false };
|
bool exiting_{ false };
|
||||||
|
|
||||||
|
// Keyboard events / state
|
||||||
|
T_KeyboardModifiers kbMods_;
|
||||||
|
T_AutoArray< char , 32 > kbKeys_;
|
||||||
|
|
||||||
// Mouse capture
|
// Mouse capture
|
||||||
bool mCapture_{ false };
|
bool mCapture_{ false };
|
||||||
ImVec2 mInitial_{ 0 , 0 };
|
ImVec2 mInitial_{ 0 , 0 };
|
||||||
|
@ -92,8 +89,13 @@ struct T_UIApp
|
||||||
[]( T_UIAction const& a ) -> T_String {
|
[]( T_UIAction const& a ) -> T_String {
|
||||||
return a.id;
|
return a.id;
|
||||||
} };
|
} };
|
||||||
|
T_KeyValueTable< T_KeyboardShortcut , ebcl::T_Set< T_String > > shortcuts_;
|
||||||
|
|
||||||
|
// Event handling
|
||||||
void handleMouseCapture( ) noexcept;
|
void handleMouseCapture( ) noexcept;
|
||||||
|
void handleKeyboardShortcuts( ) noexcept;
|
||||||
|
|
||||||
|
// Modals
|
||||||
void handleDialogs( ) noexcept;
|
void handleDialogs( ) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
12
ui-sync.cc
12
ui-sync.cc
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
T_UISync::T_UISync( )
|
T_UISync::T_UISync( )
|
||||||
{
|
{
|
||||||
UI::Main( ).newAction( "Save curves" , []() {
|
UI::Main( ).addAction( T_UIAction{ "Save curves" , []() {
|
||||||
if ( Common::Sync( ).curvesFileChanged( ) ) {
|
if ( Common::Sync( ).curvesFileChanged( ) ) {
|
||||||
UI::Main( ).msgbox(
|
UI::Main( ).msgbox(
|
||||||
"Curves file changed" ,
|
"Curves file changed" ,
|
||||||
|
@ -27,12 +27,12 @@ T_UISync::T_UISync( )
|
||||||
} else {
|
} else {
|
||||||
Common::Sync( ).saveCurves( );
|
Common::Sync( ).saveCurves( );
|
||||||
}
|
}
|
||||||
} ).setEnabledCheck( []() {
|
} }.setEnabledCheck( []() {
|
||||||
return Common::Sync( ).curvesModified( );
|
return Common::Sync( ).curvesModified( );
|
||||||
} ).setIcon( ICON_FA_FLOPPY_O )
|
} ).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(
|
UI::Main( ).msgbox(
|
||||||
"Reload curves?" ,
|
"Reload curves?" ,
|
||||||
"Changes you made to the curves will be lost. Do you "
|
"Changes you made to the curves will be lost. Do you "
|
||||||
|
@ -42,11 +42,11 @@ T_UISync::T_UISync( )
|
||||||
Common::Sync( ).loadCurves( );
|
Common::Sync( ).loadCurves( );
|
||||||
}
|
}
|
||||||
} , { T_MessageBox::BT_YES , T_MessageBox::BT_NO } );
|
} , { T_MessageBox::BT_YES , T_MessageBox::BT_NO } );
|
||||||
} ).setEnabledCheck( []() {
|
} }.setEnabledCheck( []() {
|
||||||
return Common::Sync( ).curvesModified( );
|
return Common::Sync( ).curvesModified( );
|
||||||
} ).setIcon( ICON_FA_DOWNLOAD )
|
} ).setIcon( ICON_FA_DOWNLOAD )
|
||||||
.setShortcut( T_KeyboardShortcut{ 'r' ,
|
.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 auto addui{ [this]( char const* type , F_Override ov ) {
|
||||||
const bool ok{ sovuis_.add( T_String{ type } , std::move( ov ) ) };
|
const bool ok{ sovuis_.add( T_String{ type } , std::move( ov ) ) };
|
||||||
|
|
Loading…
Reference in a new issue