Fixes race conditions with PointerEnter/Exit events conflicting with Selection and unselection. This change provides better encapsulation of the logic to enable a selected item for accelerator (hotkey) events, and allow mouse input on results where the pointer is over.

This commit is contained in:
ryanbodrug-microsoft
2020-04-22 21:35:00 -07:00
parent 46d1bc274c
commit e13967cb83
4 changed files with 88 additions and 31 deletions

View File

@@ -8,11 +8,16 @@
xmlns:ToolkitBehaviors="using:Microsoft.Toolkit.Uwp.UI.Animations.Behaviors"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters"
d:DesignHeight="300"
d:DesignWidth="720">
<UserControl.Resources>
<converters:BoolToObjectConverter x:Key="BoolToVisibilityConverter" TrueValue="Visible" FalseValue="Collapsed"/>
</UserControl.Resources>
<Grid
x:Name="PowerBar"
x:Name="PowerBar"
Background="{ThemeResource BackdropAcrylicBrush}"
Translation="0,0,16"
VerticalAlignment="Top">
<ListView
x:Name="SuggestionsList"
@@ -21,28 +26,22 @@
MinHeight="{Binding Results.MaxHeight}"
AllowFocusOnInteraction="False"
IsItemClickEnabled="True"
Margin="0"
Margin="{ThemeResource AutoSuggestListMargin}"
Padding="{ThemeResource AutoSuggestListPadding}"
ItemsSource="{Binding Results.Results, Mode=OneWay}"
SelectionMode="Single"
SelectedIndex="{Binding Results.SelectedIndex, Mode=TwoWay}"
Style="{StaticResource ListViewNoAnimations}">
Style="{StaticResource ListViewNoAnimations}"
>
<ListView.ItemTemplate>
<DataTemplate >
<Grid Height="72" Width="690" Background="Transparent" RowSpacing="0">
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding IsSelected}" ComparisonCondition="Equal" Value="true" >
<Core:CallMethodAction TargetObject="{Binding ElementName=ShowActionButtons}" MethodName="StartAnimation"/>
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding IsSelected}" ComparisonCondition="Equal" Value="false">
<Core:CallMethodAction TargetObject="{Binding ElementName=HideActionsButtons}" MethodName="StartAnimation"/>
</Core:DataTriggerBehavior>
<Core:EventTriggerBehavior EventName="PointerEntered">
<Core:CallMethodAction TargetObject="{Binding ElementName=ShowActionButtons}" MethodName="StartAnimation"/>
<Core:InvokeCommandAction Command="{Binding LoadContextMenuCommand}"/>
<Core:InvokeCommandAction Command="{Binding ActivateContextButtonsHoverCommand}"/>
</Core:EventTriggerBehavior>
<Core:EventTriggerBehavior EventName="PointerExited">
<Core:CallMethodAction TargetObject="{Binding ElementName=HideActionsButtons}" MethodName="StartAnimation"/>
<Core:InvokeCommandAction Command="{Binding DeactivateContextButtonsHoverCommand}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<Grid.ColumnDefinitions>
@@ -64,16 +63,12 @@
Grid.Column="1"
Margin="0,0,42,0"
Height="46"
Visibility="{Binding AreContextButtonsActive, Converter={StaticResource BoolToVisibilityConverter}}"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding ContextMenuItems}"
SelectionMode="Single"
SelectedIndex="{Binding ContextMenuSelectedIndex}">
<Interactivity:Interaction.Behaviors>
<ToolkitBehaviors:Fade x:Name="ShowActionButtons" Duration="250" Delay="0" AutomaticallyStart="False" Value="1" />
<ToolkitBehaviors:Fade x:Name="HideActionsButtons" Duration="250" Delay="0" AutomaticallyStart="False" Value="0" />
</Interactivity:Interaction.Behaviors>
<GridView.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Command}" VerticalAlignment="Center" CornerRadius="4" Height="42" Width="42" BorderThickness="1" Style="{ThemeResource ButtonRevealStyle}">
@@ -87,7 +82,7 @@
<KeyboardAccelerator
Key="{Binding AcceleratorKey}"
Modifiers="{Binding AcceleratorModifiers}"
IsEnabled="{Binding IsEnabled}"
IsEnabled="{Binding IsAcceleratorKeyEnabled}"
/>
</Button.KeyboardAccelerators>
</Button>

View File

@@ -12,6 +12,6 @@ namespace Wox.ViewModel
public ICommand Command { get; set; }
public string AcceleratorKey { get; set; }
public string AcceleratorModifiers { get; set; }
public bool IsEnabled { get; set; }
public bool IsAcceleratorKeyEnabled { get; set; }
}
}

View File

@@ -17,12 +17,24 @@ namespace Wox.ViewModel
{
public class ResultViewModel : BaseModel
{
public enum ActivationType
{
Selection,
Hover
};
public List<ContextMenuItemViewModel> ContextMenuItems { get; set; }
public ICommand LoadContextMenuCommand { get; set; }
public ICommand ActivateContextButtonsHoverCommand { get; set; }
public ICommand ActivateContextButtonsSelectionCommand { get; set; }
public ICommand DeactivateContextButtonsHoverCommand { get; set; }
public ICommand DeactivateContextButtonsSelectionCommand { get; set; }
public bool IsSelected { get; set; }
public bool AreContextButtonsActive { get; set; }
public int ContextMenuSelectedIndex { get; set; }
const int NoSelectionIndex = -1;
@@ -34,9 +46,62 @@ namespace Wox.ViewModel
Result = result;
}
ContextMenuSelectedIndex = NoSelectionIndex;
LoadContextMenuCommand = new RelayCommand(LoadContextMenu);
ActivateContextButtonsHoverCommand = new RelayCommand(ActivateContextButtonsHoverAction);
ActivateContextButtonsSelectionCommand = new RelayCommand(ActivateContextButtonsSelectionAction);
DeactivateContextButtonsHoverCommand = new RelayCommand(DeactivateContextButtonsHoverAction);
DeactivateContextButtonsSelectionCommand = new RelayCommand(DeactivateContextButtonsSelectionAction);
}
public void LoadContextMenu(object sender=null)
private void ActivateContextButtonsHoverAction(object sender)
{
ActivateContextButtons(ActivationType.Hover);
}
private void ActivateContextButtonsSelectionAction(object sender)
{
ActivateContextButtons(ActivationType.Selection);
}
public void ActivateContextButtons(ActivationType activationType)
{
if (ContextMenuItems == null)
{
LoadContextMenu();
}
AreContextButtonsActive = true;
if (activationType == ActivationType.Selection)
{
IsSelected = true;
EnableContextMenuAcceleratorKeys();
}
}
private void DeactivateContextButtonsHoverAction(object sender)
{
DeactivateContextButtons(ActivationType.Hover);
}
private void DeactivateContextButtonsSelectionAction(object sender)
{
DeactivateContextButtons(ActivationType.Selection);
}
public void DeactivateContextButtons(ActivationType activationType)
{
AreContextButtonsActive = false;
if (activationType == ActivationType.Selection)
{
IsSelected = false;
DisableContextMenuAcceleratorkeys();
}
}
public void LoadContextMenu()
{
var results = PluginManager.GetContextMenusForPlugin(Result);
var newItems = new List<ContextMenuItemViewModel>();
@@ -68,19 +133,19 @@ namespace Wox.ViewModel
ContextMenuItems = newItems;
}
internal void EnableContextMenu()
private void EnableContextMenuAcceleratorKeys()
{
foreach(var i in ContextMenuItems)
{
i.IsEnabled = true;
i.IsAcceleratorKeyEnabled = true;
}
}
internal void DisableContextMenu()
private void DisableContextMenuAcceleratorkeys()
{
foreach (var i in ContextMenuItems)
{
i.IsEnabled = false;
i.IsAcceleratorKeyEnabled = false;
}
}

View File

@@ -59,14 +59,11 @@ namespace Wox.ViewModel
{
if (_selectedItem != null)
{
_selectedItem.IsSelected = false;
_selectedItem.DisableContextMenu();
_selectedItem.DeactivateContextButtons(ResultViewModel.ActivationType.Selection);
}
_selectedItem = value;
_selectedItem.LoadContextMenu();
_selectedItem.EnableContextMenu();
_selectedItem.IsSelected = true;
_selectedItem.ActivateContextButtons(ResultViewModel.ActivationType.Selection);
}
}
}