User avatar
sfernandez
Site Admin
Posts: 2536
Joined: 22 Dec 2011, 19:20

Re: Disable keyboard shortscuts when in gui elements

16 Jun 2020, 14:04

The current state is that we are handling half of the shortcuts only after IView::KeyDown() returned explicit "false", outside Noesis. Which is working perfectly fine for actions which are entirely independent from the UI, with no unwanted side effects.
Correct me if I'm wrong, but doing this has the same problem as using the KeyTrigger, because if you have the focus in a TextBox, and press the 'R' key, IView::KeyDown() will return false (TextBox only handles specific keys it needs like backspace or delete, arrow keys...), so if there is a shortcut associated with the 'R' key it will be executed. Then the 'R' key is translated into the corresponding char and sent to the IView::Char(), and TextInput event is generated, so TextBox writes the 'r' letter too.
And maintaining keybindings in two different systems is not desirable either.
Totally agree, that is why we are trying to offer the best approach with the tools we have available in Noesis.
Trying to move the shortcut definitions to Noesis scope has led up to using KeyTrigger (as the only available tool), but with the result of evaluation order of shortcuts and text input getting flipped
As I said before that is not the case, because KeyDown will always occur before TextInput. Which leads me to another possible solution, that could be more in line with what you are explaining. Implement a ShortcutTrigger that listens to TextInput event and fires the action on the KeyUp event, if no TextBox handled the TextInput event, something like this:
void ShortcutTrigger::OnAttached()
{
    ParentClass::OnAttached();

    Noesis::UIElement* source = GetAssociatedObject();
    if (source != 0)
    {
        source->TextInput() += MakeDelegate(this, &ShortcutTrigger::OnTextInput);
        source->KeyUp() += MakeDelegate(this, &ShortcutTrigger::OnKeyUp);
    }
}

void ShortcutTrigger::OnDetaching()
{
    Noesis::UIElement* source = GetAssociatedObject();
    if (source != 0)
    {
        source->TextInput() -= MakeDelegate(this, &ShortcutTrigger::OnTextInput);
        source->KeyUp() -= MakeDelegate(this, &ShortcutTrigger::OnKeyUp);
    }

    ParentClass::OnDetaching();
}

bool ShortcutTrigger::CheckModifiers() const
{
    Noesis::Keyboard* keyboard = GetAssociatedObject()->GetKeyboard();
    return GetModifiers() == keyboard->GetModifiers();
}

void ShortcutTrigger::OnTextInput(Noesis::BaseComponent*, const Noesis::TextCompositionEventArgs&)
{
    mTextInputIgnored = true;
}

void ShortcutTrigger::OnKeyUp(Noesis::BaseComponent*, const Noesis::KeyEventArgs& e)
{
    if (e.key == GetKey() && CheckModifiers() && mTextInputIgnored)
    {
        mTextInputIgnored = false;
        InvokeActions(0);
    }
}
Is this what you are looking for?
 
Ext3h
Posts: 4
Joined: 07 Mar 2020, 10:14

Re: Disable keyboard shortscuts when in gui elements

16 Jun 2020, 20:06

Correct me if I'm wrong, but doing this has the same problem as using the KeyTrigger, because if you have the focus in a TextBox, and press the 'R' key, IView::KeyDown() will return false
You are correct, it wasn't handled by IView::KeyDown(). But we are checking for return values both of IView::KeyDown() or IView::Char() before continuing to process the input, so it did actually do something plausible for us.

The Idea of waiting on TextCompositionEvent in "ShortcutTrigger" is effectively doing the same. Doesn't exactly need to wait for KeyUp, but may already enter half-triggered state in KeyDown and trigger on TextCompositionEvent already.

But it does fall for another trap. Any logic trying to argue over TextCompositionEvents is then incapable of handling dead keys (e.g. ^), as TextCompositionEvent isn't triggered until UTF8 composite is passed to IView::Char(). (Win32Display from NoesisApp has skipped mapping any potentially dead keys (VK_OEM_*), so that doesn't show. And none are dead on US keyboard layout.)

So at least for dead key support, it's back to explicitly ignoring key presses if the source (or in fact anything in the event route) is potentially a handler of a followup TextCompositionEvent. There is no way around probing whether the scene is currently in text input mode or not, prior to deciding how to handle the key down event.
 
User avatar
sfernandez
Site Admin
Posts: 2536
Joined: 22 Dec 2011, 19:20

Re: Disable keyboard shortscuts when in gui elements

19 Jun 2020, 16:26

I understand what you are saying, you're right using TextInput won't work when a shortcut references a dead key.

Taking into account your comments...
it's that the TextBox should (be able to) consume the input it can handle before shortcuts are triggered
I've been doing more tests and I verified that WPF's TextBox only sets Handled to true for KeyDown event on some specific keys, and TextInput event. The KeyUp event simply bubbles up for all keys. So without breaking that behavior, implementing a shortcut trigger that works on all the scenarios you have exposed can't rely on those events being handled by inner controls.

I think it is better to try to solve the specific problem here: don't trigger shortcuts if a TextBox (or PasswordBox) has the focus (as I exposed before).
Do you have any suggestion on how to improve that approach?
 
User avatar
Moah
Posts: 2
Joined: 19 Apr 2021, 10:06
Location: Stockholm, Sweden
Contact:

Re: Disable keyboard shortscuts when in gui elements

28 Apr 2022, 15:36

Was there ever a better resolution to this? I have a very similar problem. In our game we have a background window that defines a bunch of "default" shortcuts, but new windows being opened should be able to redefine those shortcuts.
We also have the issue that textboxes don't get the letters associated with the background window shortcuts.
This is how we define those shortcuts:
<jomini:WindowView x:Class="Game.IngameScreen"
                    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                    xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
                    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                    xmlns:jomini="clr-namespace:JominiNoesisGui;assembly=JominiNoesisGui.Extension"
                    xmlns:game="clr-namespace:Game"
                    mc:Ignorable="d"
                    d:DataContext="{d:DesignInstance Type=game:IngameScreenModel, IsDesignTimeCreatable=True}"
                    d:DesignWidth="1920" d:DesignHeight="1080"
                    Focusable="True"
                    Name="MainScreen">
    <UserControl.InputBindings>
        <KeyBinding Key="Esc" Command="{Binding OpenMenuCommand}" />
        <KeyBinding Key="Add" Command="{Binding GameTimeControlModel.IncreaseSpeedCommand}" />
        <KeyBinding Key="Subtract" Command="{Binding GameTimeControlModel.DecreaseSpeedCommand}" />
        <KeyBinding Key="Space" Command="{Binding GameTimeControlModel.PauseCommand}" />
        <KeyBinding Key="R" Command="{Binding MapIconsManager.FakeCameraShakeCommand}" />
        <KeyBinding Key="F" Command="{Binding OpenSystemSearchWindowCommand}"/>
    </UserControl.InputBindings>
</jomini:WindowView>
And here, for example, our PauseMenu which redefines "Esc" to close itself. Which doesn't work, because Esc is used by the background window to open the PauseMenu:
<jomini:WindowView x:Class="Game.PauseMenuWindow"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:jomini="clr-namespace:JominiNoesisGui;assembly=JominiNoesisGui.Extension"
             xmlns:game="clr-namespace:Game"
             xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
             d:DataContext="{d:DesignInstance Type=game:PauseMenuWindowModel, IsDesignTimeCreatable=True}"
             d:DesignHeight="320" d:DesignWidth="300"
             mc:Ignorable="d">
    <game:UIScalingDecorator>
        <Grid Background="#FFE4E4E4" Height="320" Width="300">
            <TextBlock TextWrapping="Wrap" Text="Game Paused" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Top" FontSize="24" Foreground="Blue" Margin="0,34.076,0,234"/>
            <Button Content="Resume Game" Command="{Binding CloseCommand}"  Width="219.75" Height="44.5" HorizontalAlignment="Center" VerticalAlignment="Top"  Margin="0,97,0,0" />
            <Button Content="Exit to Main Menu" Command="{Binding ExitToMainMenuCommand}"  Width="219.75" Height="44.5" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="40.125,167.5,40.125,0" />
            <Button Content="Exit to Desktop" Command="{Binding ExitToDesktopCommand}"  Width="219.75" Height="44.5" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="40.125,233.5,40.125,0" />
            <b:Interaction.Behaviors>
                <b:MouseDragElementBehavior />
            </b:Interaction.Behaviors>
        </Grid>
    </game:UIScalingDecorator>
    <UserControl.InputBindings>
        <KeyBinding Key="Esc" Command="{Binding CloseCommand}" />
    </UserControl.InputBindings>

</jomini:WindowView>
So is there a way to either deactivate the background shortcuts when another window has the focus, or a way to set a priority to windows for shortcut, with the topmost window being highest priority, etc?
ImageGwenael "Moah" Tranvouez
Tech Lead
[email protected]
 
User avatar
sfernandez
Site Admin
Posts: 2536
Joined: 22 Dec 2011, 19:20

Re: Disable keyboard shortscuts when in gui elements

29 Apr 2022, 19:26

Hi,

Defining nested KeyBindings like you did is permited and works the same as in WPF. But you have to take into account that the key bindings are based on the routing of KeyDown event. So if you have a hierarchy with 2 nested WindowView, each one with its own key bindings, the inner view can only execute its bindings if the focus is in any of its inner controls. I just tried a similar scenario and it works as expected in WPF and Noesis: a first Window is opened containing focus and when ESC key is pressed its command is executed to show the PauseMenu which sets the focus to one of its controls. Now when ESC is pressed, the CloseCommand of the pause menu is executed (and nothing else because it sets the event Handled to true).

Anyway, given the problems that KeyBinding has when dealing with TextBoxes (see https://stackoverflow.com/questions/331 ... extbox-use), and also the problems described in this post when trying to use the KeyTrigger alternative, we decided to create a new trigger (ShortcutTrigger) to try to solve this in a cleaner way: #2340

Do you think that approach could fit in your scenario?
 
User avatar
Moah
Posts: 2
Joined: 19 Apr 2021, 10:06
Location: Stockholm, Sweden
Contact:

Re: Disable keyboard shortscuts when in gui elements

19 May 2022, 13:02

Hello,

sorry for the delay, I had to disappear from work for a while, but I'm now back and hopefully we can resume this discussion.

Unfortunately I'm not very familiar with WPF and am not quite sure what "it works the same as in WPF." entail. I'm also not quite sure how the routing of KeyDown events functions. I thought just sending the key events from our engine to Noesis would have noesis route it appropriately? To the focused widget, then "bubbling up" through its parents until some widget handles or there are no more parents to handle it?
Is that something we have to implement ourself?
Now, I know that the text widget has the focus, since i give it the focus immediately, and yet it doesn't receive the letters associated with a shortcut in the "main view" (ie the parent's parent).
CSystemSearchWindow::CSystemSearchWindow()
{
	Loaded() += Noesis::MakeDelegate( this, &CSystemSearchWindow::OnLoadedOrReloaded );
	Reloaded() += Noesis::MakeDelegate( this, &CSystemSearchWindow::OnLoadedOrReloaded );
}

CSystemSearchWindow::~CSystemSearchWindow()
{
	Loaded() -= Noesis::MakeDelegate( this, &CSystemSearchWindow::OnLoadedOrReloaded );
	Reloaded() -= Noesis::MakeDelegate( this, &CSystemSearchWindow::OnLoadedOrReloaded );
}

void CSystemSearchWindow::OnLoadedOrReloaded( Noesis::BaseComponent* pComponent, const Noesis::RoutedEventArgs& Args )
{
	Noesis::TextBox* pTextBox = GetTemplateChild<Noesis::TextBox>( "Input" );
	if ( pTextBox )
	{
		pTextBox->Focus( true );
	}
}
This new method will definitely help with text boxes, btw.
ImageGwenael "Moah" Tranvouez
Tech Lead
[email protected]
 
User avatar
sfernandez
Site Admin
Posts: 2536
Joined: 22 Dec 2011, 19:20

Re: Disable keyboard shortscuts when in gui elements

20 May 2022, 11:23

You got it right, the routing of events is automatically handled by Noesis, and for key events it starts from the focused element and then bubble up to the root until some widget handles it.

Then I think implementing a ShortcutTrigger as I proposed will solve your problems.

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot] and 1 guest