Browse Source

Fixed heap corruption in search control

flabbet 7 months ago
parent
commit
eb8f2dbfc2

+ 2 - 2
src/PixiEditor/Helpers/Extensions/MethodExtension.cs

@@ -9,13 +9,13 @@ public static class MethodExtension
     public static async Task<T> InvokeAsync<T>(this MethodInfo @this, object obj, params object[] parameters)
     public static async Task<T> InvokeAsync<T>(this MethodInfo @this, object obj, params object[] parameters)
     {
     {
         //TODO: uhh, make sure this is ok?
         //TODO: uhh, make sure this is ok?
-        Dispatcher.UIThread.InvokeAsync(async () => await Task.Run(async () =>
+        Dispatcher.UIThread.InvokeAsync(async () =>
         {
         {
             var task = (Task)@this.Invoke(obj, parameters);
             var task = (Task)@this.Invoke(obj, parameters);
             await task.ConfigureAwait(false);
             await task.ConfigureAwait(false);
             var resultProperty = task.GetType().GetProperty("Result");
             var resultProperty = task.GetType().GetProperty("Result");
             return (T)resultProperty.GetValue(task);
             return (T)resultProperty.GetValue(task);
-        }));
+        });
 
 
         return default;
         return default;
     }
     }

+ 4 - 2
src/PixiEditor/Models/Commands/Evaluators/IconEvaluator.cs

@@ -16,8 +16,10 @@ internal class IconEvaluator : Evaluator<IImage>
 {
 {
     public static IconEvaluator Default { get; } = new FontIconEvaluator();
     public static IconEvaluator Default { get; } = new FontIconEvaluator();
 
 
-    public override IImage? CallEvaluate(Command command, object parameter) =>
-        base.CallEvaluate(command, parameter is CommandSearchResult or Command ? parameter : command);
+    public override IImage? CallEvaluate(Command command, object parameter)
+    {
+        return base.CallEvaluate(command, parameter is CommandSearchResult or Command ? parameter : command);
+    }
 
 
     [DebuggerDisplay("IconEvaluator.Default")]
     [DebuggerDisplay("IconEvaluator.Default")]
     private class FontIconEvaluator : IconEvaluator
     private class FontIconEvaluator : IconEvaluator

+ 4 - 1
src/PixiEditor/Models/Commands/Search/CommandSearchResult.cs

@@ -17,7 +17,10 @@ internal class CommandSearchResult : SearchResult
 
 
     public override KeyCombination Shortcut => Command.Shortcut;
     public override KeyCombination Shortcut => Command.Shortcut;
 
 
-    public CommandSearchResult(Command command) => Command = command;
+    public CommandSearchResult(Command command)
+    {
+        Command = command;
+    }
 
 
     public override void Execute()
     public override void Execute()
     {
     {

+ 14 - 9
src/PixiEditor/ViewModels/SubViewModels/ClipboardViewModel.cs

@@ -179,7 +179,7 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
             return;
             return;
 
 
         await block.ExecuteQueuedActions();
         await block.ExecuteQueuedActions();
-        
+
         ConnectRelatedNodes(doc, nodeMapping);
         ConnectRelatedNodes(doc, nodeMapping);
 
 
         doc.Operations.InvokeCustomAction(() =>
         doc.Operations.InvokeCustomAction(() =>
@@ -244,7 +244,7 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
 
 
         await ClipboardController.CopyNodes(selectedNodes);
         await ClipboardController.CopyNodes(selectedNodes);
     }
     }
-    
+
     [Command.Basic("PixiEditor.Clipboard.CopyCels", "COPY_CELS",
     [Command.Basic("PixiEditor.Clipboard.CopyCels", "COPY_CELS",
         "COPY_CELS_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanCopyCels",
         "COPY_CELS_DESCRIPTIVE", CanExecute = "PixiEditor.Clipboard.CanCopyCels",
         ShortcutContexts = [typeof(TimelineDockViewModel)],
         ShortcutContexts = [typeof(TimelineDockViewModel)],
@@ -258,7 +258,7 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
         var selectedCels = doc.AnimationDataViewModel.AllCels.Where(x => x.IsSelected).Select(x => x.Id).ToArray();
         var selectedCels = doc.AnimationDataViewModel.AllCels.Where(x => x.IsSelected).Select(x => x.Id).ToArray();
         if (selectedCels.Length == 0)
         if (selectedCels.Length == 0)
             return;
             return;
-        
+
         await ClipboardController.CopyCels(selectedCels);
         await ClipboardController.CopyCels(selectedCels);
     }
     }
 
 
@@ -303,17 +303,19 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
             ? ClipboardController.IsImage(data)
             ? ClipboardController.IsImage(data)
             : ClipboardController.IsImageInClipboard().Result;
             : ClipboardController.IsImageInClipboard().Result;
     }
     }
-    
+
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanCopyCels")]
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanCopyCels")]
     public bool CanCopyCels()
     public bool CanCopyCels()
     {
     {
-        return Owner.DocumentIsNotNull(null) && Owner.DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.AllCels.Any(x => x.IsSelected);
+        return Owner.DocumentIsNotNull(null) &&
+               Owner.DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.AllCels.Any(x => x.IsSelected);
     }
     }
-    
+
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanCopyNodes")]
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanCopyNodes")]
     public bool CanCopyNodes()
     public bool CanCopyNodes()
     {
     {
-        return Owner.DocumentIsNotNull(null) && Owner.DocumentManagerSubViewModel.ActiveDocument.NodeGraph.AllNodes.Any(x => x.IsNodeSelected);
+        return Owner.DocumentIsNotNull(null) &&
+               Owner.DocumentManagerSubViewModel.ActiveDocument.NodeGraph.AllNodes.Any(x => x.IsNodeSelected);
     }
     }
 
 
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanPasteNodes")]
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanPasteNodes")]
@@ -323,8 +325,11 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
     }
     }
 
 
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanPasteColor")]
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanPasteColor")]
-    public static async Task<bool> CanPasteColor() =>
-        ColorHelper.ParseAnyFormat((await ClipboardController.Clipboard.GetTextAsync())?.Trim() ?? string.Empty, out _);
+    public static async Task<bool> CanPasteColor()
+    {
+        return ColorHelper.ParseAnyFormat(
+            (await ClipboardController.Clipboard.GetTextAsync())?.Trim() ?? string.Empty, out _);
+    }
 
 
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanCopy")]
     [Evaluator.CanExecute("PixiEditor.Clipboard.CanCopy")]
     public bool CanCopy()
     public bool CanCopy()

+ 4 - 29
src/PixiEditor/Views/Main/CommandSearch/CommandSearchControl.axaml

@@ -7,6 +7,7 @@
              xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
              xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
              xmlns:search="clr-namespace:PixiEditor.Models.Commands.Search"
              xmlns:search="clr-namespace:PixiEditor.Models.Commands.Search"
              xmlns:shortcuts="clr-namespace:PixiEditor.Views.Shortcuts"
              xmlns:shortcuts="clr-namespace:PixiEditor.Views.Shortcuts"
+             xmlns:commandSearch="clr-namespace:PixiEditor.Views.Main.CommandSearch"
              mc:Ignorable="d"
              mc:Ignorable="d"
              Foreground="White"
              Foreground="White"
              d:DesignHeight="450" d:DesignWidth="600"
              d:DesignHeight="450" d:DesignWidth="600"
@@ -49,35 +50,9 @@
                     <ItemsControl ItemsSource="{Binding Results, ElementName=uc}" x:Name="itemscontrol">
                     <ItemsControl ItemsSource="{Binding Results, ElementName=uc}" x:Name="itemscontrol">
                         <ItemsControl.ItemTemplate>
                         <ItemsControl.ItemTemplate>
                             <DataTemplate DataType="search:SearchResult">
                             <DataTemplate DataType="search:SearchResult">
-                                <Button Padding="5" Height="40" BorderThickness="0" Background="{DynamicResource ThemeBackgroundBrush}"
-                                        Command="{Binding ButtonClickedCommand, ElementName=uc}" CornerRadius="0"
-                                        CommandParameter="{Binding}"
-                                        HorizontalContentAlignment="Stretch"
-                                        IsEnabled="{Binding CanExecute}"
-                                        Classes.keyboard="{Binding IsSelected}"
-                                        PointerMoved="Button_MouseMove">
-                                    <Button.Styles>
-                                        <Style Selector="Button.keyboard">
-                                            <Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/>
-                                        </Style>
-                                    </Button.Styles>
-                                    <Grid VerticalAlignment="Center" x:Name="dp" Margin="5,0,10,0">
-                                        <Grid.ColumnDefinitions>
-                                            <ColumnDefinition />
-                                            <ColumnDefinition Width="Auto" />
-                                        </Grid.ColumnDefinitions>
-                                        <StackPanel Orientation="Horizontal">
-                                            <!--TODO: Below causes heap corruption crash randomly when searching-->
-                                            <!--<Border Width="25" Margin="0,0,5,0" Padding="1">
-                                                <Image HorizontalAlignment="Center" Source="{Binding Icon}" />
-                                            </Border>-->
-                                            <TextBlock VerticalAlignment="Center"
-                                                       behaviours:TextBlockExtensions.BindableInlines="{Binding TextBlockContent}" />
-                                        </StackPanel>
-
-                                        <shortcuts:ShortcutHint Grid.Column="1" VerticalAlignment="Center" Shortcut="{Binding Shortcut}" />
-                                    </Grid>
-                                </Button>
+                                <commandSearch:SearchResultControl 
+                                    Result="{Binding}"
+                                    PointerMoved="SearchResult_MouseMove"/>
                             </DataTemplate>
                             </DataTemplate>
                         </ItemsControl.ItemTemplate>
                         </ItemsControl.ItemTemplate>
                     </ItemsControl>
                     </ItemsControl>

+ 2 - 2
src/PixiEditor/Views/Main/CommandSearch/CommandSearchControl.axaml.cs

@@ -284,9 +284,9 @@ internal partial class CommandSearchControl : UserControl, INotifyPropertyChange
         itemscontrol.ContainerFromIndex(newIndex)?.BringIntoView();
         itemscontrol.ContainerFromIndex(newIndex)?.BringIntoView();
     }
     }
 
 
-    private void Button_MouseMove(object sender, PointerEventArgs e)
+    private void SearchResult_MouseMove(object sender, PointerEventArgs e)
     {
     {
-        var searchResult = ((Button)sender).DataContext as SearchResult;
+        var searchResult = ((SearchResultControl)sender).DataContext as SearchResult;
         MouseSelectedResult = searchResult;
         MouseSelectedResult = searchResult;
     }
     }
 
 

+ 40 - 0
src/PixiEditor/Views/Main/CommandSearch/SearchResultControl.axaml

@@ -0,0 +1,40 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
+             xmlns:shortcuts="clr-namespace:PixiEditor.Views.Shortcuts"
+             x:ClassModifier="internal"
+             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             Name="resultControl"
+             x:Class="PixiEditor.Views.Main.CommandSearch.SearchResultControl">
+    <Button DataContext="{Binding ElementName=resultControl}" Padding="5" Height="40" BorderThickness="0"
+            Background="{DynamicResource ThemeBackgroundBrush}"
+            Command="{Binding ButtonClickedCommand}" CornerRadius="0"
+            CommandParameter="{Binding}"
+            HorizontalContentAlignment="Stretch"
+            IsEnabled="{Binding CanExecute}"
+            Classes.keyboard="{Binding Result.IsSelected}">
+        <Button.Styles>
+            <Style Selector="Button.keyboard">
+                <Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}" />
+            </Style>
+        </Button.Styles>
+        <Grid VerticalAlignment="Center" x:Name="dp" Margin="5,0,10,0">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition />
+                <ColumnDefinition Width="Auto" />
+            </Grid.ColumnDefinitions>
+            <StackPanel Orientation="Horizontal">
+                <!--TODO: Below causes heap corruption crash randomly when searching-->
+                <Border Width="25" Margin="0,0,5,0" Padding="1">
+                    <Image HorizontalAlignment="Center" Source="{Binding EvaluatedIcon}" />
+                </Border>
+                <TextBlock VerticalAlignment="Center"
+                           behaviours:TextBlockExtensions.BindableInlines="{Binding Result.TextBlockContent}" />
+            </StackPanel>
+
+            <shortcuts:ShortcutHint Grid.Column="1" VerticalAlignment="Center" Shortcut="{Binding Result.Shortcut}" />
+        </Grid>
+    </Button>
+</UserControl>

+ 65 - 0
src/PixiEditor/Views/Main/CommandSearch/SearchResultControl.axaml.cs

@@ -0,0 +1,65 @@
+using System.ComponentModel;
+using System.Windows.Input;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+using Avalonia.Threading;
+using PixiEditor.Models.Commands.Search;
+
+namespace PixiEditor.Views.Main.CommandSearch;
+
+internal partial class SearchResultControl : UserControl, INotifyPropertyChanged
+{
+    public static readonly StyledProperty<SearchResult> ResultProperty =
+        AvaloniaProperty.Register<SearchResultControl, SearchResult>(
+            nameof(Result));
+
+    public static readonly StyledProperty<ICommand> ButtonClickedCommandProperty =
+        AvaloniaProperty.Register<SearchResultControl, ICommand>(
+            nameof(ButtonClickedCommand));
+
+    public ICommand ButtonClickedCommand
+    {
+        get => GetValue(ButtonClickedCommandProperty);
+        set => SetValue(ButtonClickedCommandProperty, value);
+    }
+
+    public SearchResult Result
+    {
+        get => GetValue(ResultProperty);
+        set => SetValue(ResultProperty, value);
+    }
+
+    public IImage? EvaluatedIcon { get; private set; }
+    public bool CanExecute { get; private set; } = true;
+
+    public new event PropertyChangedEventHandler? PropertyChanged;
+
+    public SearchResultControl()
+    {
+        InitializeComponent();
+    }
+
+    protected override void OnLoaded(RoutedEventArgs e)
+    {
+        base.OnLoaded(e);
+
+        EvaluateCanExecute();
+        EvaluateIcon();
+    }
+
+    private void EvaluateCanExecute()
+    {
+        CanExecute = Result.CanExecute;
+        PropertyChanged?.Invoke(this, new(nameof(CanExecute)));
+    }
+
+    private void EvaluateIcon()
+    {
+        IImage icon = Result.Icon;
+        EvaluatedIcon = icon;
+        PropertyChanged?.Invoke(this, new(nameof(EvaluatedIcon)));
+    }
+}