Browse Source

'Fix' merge conflict

Equbuxu 3 years ago
parent
commit
46d9c174a6

+ 0 - 14
PixiEditor/Models/Commands/Attributes/Evaluators/FactoryAttribute.cs

@@ -1,14 +0,0 @@
-namespace PixiEditor.Models.Commands.Attributes;
-
-public partial class Evaluator
-{
-    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true)]
-    public class FactoryAttribute : EvaluatorAttribute
-    {
-        public object Parameter { get; set; }
-
-        public FactoryAttribute(string name)
-            : base(name)
-        { }
-    }
-}

+ 0 - 7
PixiEditor/Models/Commands/CommandController.cs

@@ -21,8 +21,6 @@ namespace PixiEditor.Models.Commands
 
         public List<CommandGroup> CommandGroups { get; }
 
-        public Dictionary<string, FactoryEvaluator> FactoryEvaluators { get; }
-
         public Dictionary<string, CanExecuteEvaluator> CanExecuteEvaluators { get; }
 
         public Dictionary<string, IconEvaluator> IconEvaluators { get; }
@@ -40,7 +38,6 @@ namespace PixiEditor.Models.Commands
 
             Commands = new();
             CommandGroups = new();
-            FactoryEvaluators = new();
             CanExecuteEvaluators = new();
             IconEvaluators = new();
         }
@@ -91,10 +88,6 @@ namespace PixiEditor.Models.Commands
                                     Evaluate = y => x.Invoke(y) && required.Invoke(this).All(z => z.EvaluateEvaluator(null, y))
                                 });
                         }
-                        else if (attribute is Evaluator.FactoryAttribute factory)
-                        {
-                            AddEvaluator<Evaluator.FactoryAttribute, FactoryEvaluator, object>(method, instanceType, factory, FactoryEvaluators);
-                        }
                         else if (attribute is Evaluator.IconAttribute icon)
                         {
                             AddEvaluator<Evaluator.IconAttribute, IconEvaluator, ImageSource>(method, instanceType, icon, IconEvaluators);

+ 0 - 6
PixiEditor/Models/Commands/Evaluators/FactoryEvaluator.cs

@@ -1,6 +0,0 @@
-namespace PixiEditor.Models.Commands.Evaluators
-{
-    public class FactoryEvaluator : Evaluator<object>
-    {
-    }
-}

+ 0 - 5
PixiEditor/Models/Services/CommandProvider.cs

@@ -22,11 +22,6 @@ namespace PixiEditor.Models.Services
         public bool CanExecute(string name, Command command, object argument) =>
             _controller.CanExecuteEvaluators[name].EvaluateEvaluator(command, argument);
 
-        public FactoryEvaluator GetFactoryEvaluator(string name) => _controller.FactoryEvaluators[name];
-
-        public object GetFromFactory(string name, Command command, object argument) =>
-            _controller.FactoryEvaluators[name].EvaluateEvaluator(command, argument);
-
         public IconEvaluator GetIconEvaluator(string name) => _controller.IconEvaluators[name];
 
         public ImageSource GetIcon(string name, Command command, object argument) =>

+ 250 - 0
PixiEditor/ViewModels/CommandSearchViewModel.cs

@@ -0,0 +1,250 @@
+using PixiEditor.Helpers;
+using PixiEditor.Models.Commands;
+using PixiEditor.Models.Commands.Search;
+using SkiaSharp;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Text.RegularExpressions;
+using PixiEditor.Models.DataHolders;
+
+namespace PixiEditor.ViewModels
+{
+    public class CommandSearchViewModel : NotifyableObject
+    {
+        private string searchTerm;
+        private SearchResult selectedCommand;
+        private bool invalidColor;
+
+        public string SearchTerm
+        {
+            get => searchTerm;
+            set
+            {
+                if (SetProperty(ref searchTerm, value))
+                {
+                    UpdateSearchResults();
+                }
+            }
+        }
+
+        public SearchResult SelectedResult
+        {
+            get => selectedCommand;
+            set
+            {
+                if (SetProperty(ref selectedCommand, value, out var oldValue))
+                {
+                    if (oldValue != null)
+                    {
+                        oldValue.IsSelected = false;
+                    }
+
+                    if (value != null)
+                    {
+                        value.IsSelected = true;
+                    }
+                }
+            }
+        }
+
+        public bool InvalidColor
+        {
+            get => invalidColor;
+            set => SetProperty(ref invalidColor, value);
+        }
+        
+        public ObservableCollection<SearchResult> Results { get; } = new();
+
+        public CommandSearchViewModel()
+        {
+            UpdateSearchResults();
+        }
+
+        private void UpdateSearchResults()
+        {
+            Results.Clear();
+
+            var recentlyOpened = HandleRecentlyOpened();
+            
+            if (string.IsNullOrWhiteSpace(SearchTerm))
+            {
+                foreach (var result in recentlyOpened)
+                {
+                    Results.Add(result);
+                }
+
+                SelectedResult = Results.FirstOrDefault(x => x.CanExecute);
+                InvalidColor = false;
+                return;
+            }
+
+            var filePath = SearchTerm.Trim(' ', '"', '\'');
+
+            if (filePath.StartsWith("~"))
+            {
+                filePath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), filePath[1..]);
+            }
+
+            HandleFile(filePath);
+            HandleColor();
+            HandleCommands();
+
+            foreach (var result in recentlyOpened)
+            {
+                Results.Add(result);
+            }
+
+            SelectedResult = Results.FirstOrDefault(x => x.CanExecute);
+        }
+
+        private void HandleColor()
+        {
+            if (SearchTerm.StartsWith('#'))
+            {
+                InvalidColor = !SKColor.TryParse(SearchTerm, out var color);
+                
+                if (!InvalidColor)
+                {
+                    Results.Add(new ColorSearchResult(color)
+                    {
+                        SearchTerm = searchTerm
+                    });
+                }
+            }
+            else if (SearchTerm.StartsWith("rgb") || searchTerm.StartsWith("rgba"))
+            {
+                Match match = Regex.Match(SearchTerm, @"rgba?\(? *(?<r>\d{1,3}) *, *(?<g>\d{1,3}) *, *(?<b>\d{1,3})(?: *, *(?<a>\d{1,3}))?");
+
+                if (match.Success)
+                {
+                    ParseRGB(match);
+                }
+                else if (SearchTerm.StartsWith("rgb(") || SearchTerm.StartsWith("rgba("))
+                {
+                    InvalidColor = true;
+                }
+                else
+                {
+                    InvalidColor = false;
+                }
+            }
+            else
+            {
+                InvalidColor = false;
+            }
+
+            void ParseRGB(Match match)
+            {
+                bool invalid = !(
+                    byte.TryParse(match.Groups["r"].ValueSpan, out var r) &
+                    byte.TryParse(match.Groups["g"].ValueSpan, out var g) &
+                    byte.TryParse(match.Groups["b"].ValueSpan, out var b)
+                );
+
+                var aText = match.Groups["a"].Value;
+                
+                if (string.IsNullOrEmpty(aText) || !byte.TryParse(aText, out var a))
+                {
+                    a = 255;
+                }
+                else
+                {
+                    invalid = true;
+                }
+
+                InvalidColor = invalid;
+                
+                if (!InvalidColor)
+                {
+                    Results.Add(new ColorSearchResult(new SKColor(r, g, b, a))
+                    {
+                        SearchTerm = searchTerm
+                    });
+                }
+            }
+        }
+
+        private void HandleCommands()
+        {
+            foreach (var command in CommandController.Current.Commands
+                         .Where(x => x.Description.Contains(SearchTerm, StringComparison.OrdinalIgnoreCase))
+                         .OrderByDescending(x =>
+                             x.Description.Contains($" {SearchTerm} ", StringComparison.OrdinalIgnoreCase))
+                         .Take(12))
+            {
+                Results.Add(
+                    new CommandSearchResult(command)
+                    {
+                        SearchTerm = searchTerm,
+                        Match = Match(command.Description)
+                    });
+            }
+        }
+        
+        private IEnumerable<FileSearchResult> HandleRecentlyOpened()
+        {
+            IEnumerable<RecentlyOpenedDocument> enumerable = ViewModelMain.Current.FileSubViewModel.RecentlyOpened;
+
+            if (!string.IsNullOrWhiteSpace(SearchTerm))
+            {
+                enumerable = enumerable.Where(x => x.FilePath.Contains(searchTerm, StringComparison.OrdinalIgnoreCase));
+            }
+
+            foreach (var file in enumerable)
+            {
+                yield return new FileSearchResult(file.FilePath)
+                {
+                    SearchTerm = searchTerm,
+                    Match = Match(file.FilePath)
+                };
+            }
+        }
+
+        private void HandleFile(string filePath)
+        {
+            if (!Path.IsPathFullyQualified(filePath))
+            {
+                return;
+            }
+
+            GetDirectory(filePath, out var directory, out var name);
+            var files = Directory.EnumerateFiles(directory)
+                .Where(x => SupportedFilesHelper.IsExtensionSupported(Path.GetExtension(x)));
+
+            if (name is not null or "")
+            {
+                files = files.Where(x => x.Contains(name, StringComparison.OrdinalIgnoreCase));
+            }
+
+            foreach (var file in files.Select(x => Path.GetFullPath(x)))
+            {
+                Results.Add(
+                    new FileSearchResult(file)
+                    {
+                        SearchTerm = name,
+                        Match = Match($".../{Path.GetFileName(file)}", name)
+                    });
+            }
+        }
+
+        private Match Match(string text) => Match(text, SearchTerm);
+
+        private Match Match(string text, string searchTerm) =>
+            Regex.Match(text, $"(.*)({Regex.Escape(searchTerm ?? string.Empty)})(.*)", RegexOptions.IgnoreCase);
+
+        private bool GetDirectory(string path, out string directory, out string file)
+        {
+            if (Directory.Exists(path))
+            {
+                directory = path;
+                file = string.Empty;
+                return true;
+            }
+
+            directory = Path.GetDirectoryName(path);
+            file = Path.GetFileName(path);
+
+            return Directory.Exists(directory);
+        }
+    }
+}

+ 7 - 0
PixiEditor/ViewModels/SubViewModels/Main/StylusViewModel.cs

@@ -3,6 +3,7 @@ using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.UserPreferences;
 using System.Windows.Input;
+using PixiEditor.Models.Commands.Attributes;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
@@ -59,6 +60,12 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             UpdateUseTouchGesture();
         }
 
+        [Command.Basic("PixiEditor.Stylus.TogglePenMode", "Toggle Pen Mode", "Toggle Pen Mode")]
+        public void TogglePenMode()
+        {
+            IsPenModeEnabled = !IsPenModeEnabled;
+        }
+
         private void UpdateUseTouchGesture()
         {
             if (Owner.ToolsSubViewModel.ActiveTool is not (MoveViewportTool or ZoomTool))

+ 134 - 0
PixiEditor/Views/UserControls/CommandSearchControl.xaml

@@ -0,0 +1,134 @@
+<UserControl x:Class="PixiEditor.Views.UserControls.CommandSearchControl"
+             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:local="clr-namespace:PixiEditor.Views.UserControls"
+             xmlns:vm="clr-namespace:PixiEditor.ViewModels"
+             xmlns:behaves="clr-namespace:PixiEditor.Helpers.Behaviours"
+             xmlns:cmdssearch="clr-namespace:PixiEditor.Models.Commands.Search"
+             xmlns:conv="clr-namespace:PixiEditor.Helpers.Converters"
+             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
+             mc:Ignorable="d"
+             Foreground="White"
+             d:DesignHeight="450" d:DesignWidth="600"
+             x:Name="uc">
+    <Grid DataContext="{DynamicResource viewModel}" x:Name="mainGrid">
+        <Grid.Resources>
+            <ResourceDictionary>
+                <vm:CommandSearchViewModel x:Key="viewModel" />
+            </ResourceDictionary>
+        </Grid.Resources>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto" />
+            <RowDefinition Height="*" />
+            <RowDefinition Height="Auto" />
+        </Grid.RowDefinitions>
+
+        <TextBox Text="{Binding SearchTerm, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18"
+                 Padding="5"
+                 x:Name="textBox">
+            <i:Interaction.Behaviors>
+                <behaves:TextBoxFocusBehavior SelectOnMouseClick="True" />
+                <behaves:GlobalShortcutFocusBehavior />
+            </i:Interaction.Behaviors>
+            <TextBox.Style>
+                <Style TargetType="TextBox" BasedOn="{StaticResource DarkTextBoxStyle}">
+                    <Style.Resources>
+                        <Style TargetType="Border">
+                            <Setter Property="CornerRadius" Value="5,5,0,0" />
+                        </Style>
+                    </Style.Resources>
+                </Style>
+            </TextBox.Style>
+        </TextBox>
+        <Border Grid.Row="1" BorderThickness="1,0,1,0" BorderBrush="{StaticResource BrighterAccentColor}"
+                Background="{StaticResource AccentColor}">
+            <Grid>
+                <TextBlock Text="Invalid color" TextAlignment="Center" Foreground="Gray" Margin="0,5,0,0"
+                           Visibility="{Binding InvalidColor, Converter={BoolToVisibilityConverter}}"/>
+                <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
+                    <ItemsControl ItemsSource="{Binding Results}" MinWidth="400">
+                        <ItemsControl.ItemTemplate>
+                            <DataTemplate DataType="cmdssearch:SearchResult">
+                                <Button Padding="5" Height="40" BorderThickness="0" Background="Transparent"
+                                        Command="{Binding ExecuteCommand}"
+                                        CommandParameter="{Binding}"
+                                        MouseMove="Button_MouseMove">
+                                    <Button.Style>
+                                        <Style TargetType="Button">
+                                            <Setter Property="Template">
+                                                <Setter.Value>
+                                                    <ControlTemplate TargetType="Button">
+                                                        <Border>
+                                                            <Border.Style>
+                                                                <Style TargetType="Border">
+                                                                    <Style.Triggers>
+                                                                        <DataTrigger
+                                                                            Binding="{Binding IsSelected, Mode=TwoWay}"
+                                                                            Value="False">
+                                                                            <Setter Property="Background"
+                                                                                Value="Transparent" />
+                                                                        </DataTrigger>
+                                                                        <DataTrigger
+                                                                            Binding="{Binding IsSelected, Mode=TwoWay}"
+                                                                            Value="True">
+                                                                            <Setter Property="Background"
+                                                                                Value="{StaticResource BrighterAccentColor}" />
+                                                                        </DataTrigger>
+                                                                        <DataTrigger Binding="{Binding CanExecute}"
+                                                                            Value="False">
+                                                                            <Setter Property="Background"
+                                                                                Value="Transparent" />
+                                                                        </DataTrigger>
+                                                                    </Style.Triggers>
+                                                                </Style>
+                                                            </Border.Style>
+                                                            <ContentPresenter />
+                                                        </Border>
+                                                    </ControlTemplate>
+                                                </Setter.Value>
+                                            </Setter>
+                                        </Style>
+                                    </Button.Style>
+                                    <Button.Resources>
+                                        <Style TargetType="TextBlock">
+                                            <Setter Property="FontSize" Value="16" />
+                                            <Style.Triggers>
+                                                <DataTrigger Binding="{Binding CanExecute}" Value="True">
+                                                    <Setter Property="Foreground" Value="White" />
+                                                </DataTrigger>
+                                                <DataTrigger Binding="{Binding CanExecute}" Value="False">
+                                                    <Setter Property="Foreground" Value="Gray" />
+                                                </DataTrigger>
+                                            </Style.Triggers>
+                                        </Style>
+                                    </Button.Resources>
+                                    <Grid VerticalAlignment="Center" x:Name="dp" Margin="5,0,10,0">
+                                        <Grid.ColumnDefinitions>
+                                            <ColumnDefinition />
+                                            <ColumnDefinition Width="Auto" />
+                                        </Grid.ColumnDefinitions>
+                                        <StackPanel Orientation="Horizontal">
+                                            <Border Width="25" Margin="0,0,5,0" Padding="1">
+                                                <Image HorizontalAlignment="Center" Source="{Binding Icon}" />
+                                            </Border>
+                                            <TextBlock VerticalAlignment="Center"
+                                                       behaves:TextBlockExtensions.BindableInlines="{Binding TextBlockContent}" />
+                                        </StackPanel>
+                                        <TextBlock Grid.Column="1" VerticalAlignment="Center"
+                                                   HorizontalAlignment="Right" Text="{Binding Shortcut}" />
+                                    </Grid>
+                                </Button>
+                            </DataTemplate>
+                        </ItemsControl.ItemTemplate>
+                    </ItemsControl>
+                </ScrollViewer>
+            </Grid>
+        </Border>
+        <Border Grid.Row="2" BorderThickness="1" BorderBrush="{StaticResource BrighterAccentColor}"
+                CornerRadius="0,0,5,5" Background="{StaticResource AccentColor}">
+            <TextBlock Margin="5" FontSize="16" Text="{Binding SelectedResult.Description}" />
+        </Border>
+    </Grid>
+</UserControl>