Browse Source

Font family, font size and other stuff

flabbet 6 months ago
parent
commit
3f8d79f8c9

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit c0bcbd1558af511bdd361232b3c5ae6ffadebdb6
+Subproject commit 648ef9146c16bd272eb116729eff4d07b48b4f88

+ 1 - 0
src/PixiEditor.UI.Common/Fonts/PixiPerfectIcons.axaml

@@ -110,6 +110,7 @@
             <system:String x:Key="icon-zoom-out">&#xE962;</system:String>
             <system:String x:Key="icon-pen">&#xE971;</system:String>
             <system:String x:Key="icon-link">&#xE96B;</system:String>
+            <system:String x:Key="icon-upload">&#xE96D;</system:String>
             <system:String x:Key="icon-search">&#xE976;</system:String>
             <system:String x:Key="icon-hard-drive">&#xE96C;</system:String>
             <system:String x:Key="icon-copy-add">&#xe921;</system:String>

+ 2 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -836,5 +836,6 @@
   "GREATER_THAN_OR_EQUAL": "Greater than or equal",
   "COLOR_NODE": "Color",
   "CONVERT_TO_CURVE": "Convert to curve",
-  "CONVERT_TO_CURVE_DESCRIPTIVE": "Convert selected vector layer to a curve/path"
+  "CONVERT_TO_CURVE_DESCRIPTIVE": "Convert selected vector layer to a curve/path",
+  "FONT_FILES": "Font Files"
 }

+ 18 - 0
src/PixiEditor/Helpers/Converters/FontFamilyNameToAvaloniaFontFamily.cs

@@ -0,0 +1,18 @@
+using System.Globalization;
+using Avalonia.Media;
+using Drawie.Backend.Core.Text;
+
+namespace PixiEditor.Helpers.Converters;
+
+internal class FontFamilyNameToAvaloniaFontFamily : SingleInstanceConverter<FontFamilyNameToAvaloniaFontFamily>
+{
+    public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        if (value is FontFamilyName familyName)
+        {
+            return new FontFamily(familyName.Name);
+        }
+
+        return value;
+    }
+}

+ 5 - 0
src/PixiEditor/Models/Controllers/ClipboardController.cs

@@ -161,6 +161,11 @@ internal static class ClipboardController
 
         await Clipboard.SetDataObjectAsync(data);
     }
+    
+    public static async Task<string> GetTextFromClipboard()
+    {
+        return await Clipboard.GetTextAsync();
+    }
 
     private static async Task AddImageToClipboard(Surface actuallySurface, DataObject data)
     {

+ 19 - 5
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorTextToolExecutor.cs

@@ -1,6 +1,7 @@
 using Avalonia.Input;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Text;
 using Drawie.Numerics;
 using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
@@ -16,7 +17,7 @@ namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEvents
 {
     private ITextToolHandler textHandler;
-    private IFillableShapeToolbar toolbar;
+    private ITextToolbar toolbar;
     private IStructureMemberHandler selectedMember;
 
     private string lastText = "";
@@ -31,7 +32,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
             return ExecutionState.Error;
         }
 
-        toolbar = textHandler.Toolbar as IFillableShapeToolbar;
+        toolbar = textHandler.Toolbar as ITextToolbar;
         if (toolbar == null)
         {
             return ExecutionState.Error;
@@ -54,7 +55,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
         }
         else if (shape is null)
         {
-            document.TextOverlayHandler.Show("", controller.LastPrecisePosition, 12);
+            document.TextOverlayHandler.Show("", controller.LastPrecisePosition, toolbar.FontSize);
             lastText = "";
             position = controller.LastPrecisePosition;
         }
@@ -88,7 +89,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
     {
         if (!primary || !toolbar.SyncWithPrimaryColor)
         {
-             return;
+            return;
         }
 
         toolbar.StrokeColor = color.ToColor();
@@ -97,6 +98,18 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
 
     private TextVectorData ConstructTextData(string text)
     {
+        Font font = null;
+        if (toolbar.FontFamily != null)
+        {
+            font = Font.FromFontFamily(toolbar.FontFamily);
+        }
+
+        if (font is null)
+        {
+            font = Font.CreateDefault();
+        }
+
+        font.Size = (float)toolbar.FontSize;
         return new TextVectorData()
         {
             Text = text,
@@ -105,7 +118,8 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
             FillColor = toolbar.FillColor.ToColor(),
             StrokeWidth = (float)toolbar.ToolSize,
             StrokeColor = toolbar.StrokeColor.ToColor(),
-            TransformationMatrix = lastMatrix
+            TransformationMatrix = lastMatrix,
+            Font = font
         };
     }
 

+ 9 - 0
src/PixiEditor/Models/Handlers/Toolbars/ITextToolbar.cs

@@ -0,0 +1,9 @@
+using Drawie.Backend.Core.Text;
+
+namespace PixiEditor.Models.Handlers.Toolbars;
+
+internal interface ITextToolbar : IFillableShapeToolbar
+{
+    public double FontSize { get; set; }
+    public FontFamilyName? FontFamily { get; set; }
+}

+ 4 - 0
src/PixiEditor/PixiEditor.csproj

@@ -139,6 +139,10 @@
       <DependentUpon>PercentSettingView.axaml</DependentUpon>
       <SubType>Code</SubType>
     </Compile>
+    <Compile Update="Views\Tools\ToolSettings\Settings\FontFamilySettingView.axaml.cs">
+      <DependentUpon>FontFamilySettingView.axaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
   </ItemGroup>
 
   <ItemGroup>

+ 79 - 0
src/PixiEditor/ViewModels/Tools/ToolSettings/Settings/FontFamilySettingViewModel.cs

@@ -0,0 +1,79 @@
+using System.Collections.ObjectModel;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Media;
+using Avalonia.Media.Fonts;
+using Avalonia.Platform.Storage;
+using CommunityToolkit.Mvvm.Input;
+using Drawie.Backend.Core.Text;
+using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Models.IO;
+using PixiEditor.ViewModels.UserPreferences;
+
+namespace PixiEditor.ViewModels.Tools.ToolSettings.Settings;
+
+internal class FontFamilySettingViewModel : Setting<FontFamilyName>
+{
+    private int selectedIndex;
+
+
+    private ObservableCollection<FontFamilyName> _fonts;
+
+    public ObservableCollection<FontFamilyName> Fonts
+    {
+        get
+        {
+            return _fonts;
+        }
+        set
+        {
+            SetProperty(ref _fonts, value);
+        }
+    }
+
+
+    public int FontIndex
+    {
+        get
+        {
+            return selectedIndex;
+        }
+        set
+        {
+            SetProperty(ref selectedIndex, value);
+            Value = Fonts[value];
+        }
+    }
+
+    public AsyncRelayCommand UploadFontCommand { get; }
+
+    public FontFamilySettingViewModel(string name, string displayName) : base(name)
+    {
+        Label = displayName;
+        Fonts = new ObservableCollection<FontFamilyName>(FontManager.Current.SystemFonts.Select(x => new FontFamilyName(x.Name)));
+        UploadFontCommand = new AsyncRelayCommand(UploadFont);
+    }
+
+    private async Task UploadFont()
+    {
+        FilePickerFileType[] filter =
+        [
+            new FilePickerFileType(new LocalizedString("FONT_FILES")) { Patterns = new List<string> { "*.ttf", "*.otf" } },
+            new FilePickerFileType("TrueType Font") { Patterns = new List<string> { "*.ttf" } },
+            new FilePickerFileType("OpenType Font") { Patterns = new List<string> { "*.otf" } },
+        ];
+        
+        if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+        {
+            var dialog = await desktop.MainWindow.StorageProvider.OpenFilePickerAsync(
+                new FilePickerOpenOptions { FileTypeFilter = filter });
+
+            if (dialog.Count == 0)
+                return;
+
+            var fontPath = dialog[0];
+            Fonts.Add(new FontFamilyName(fontPath.Path, Path.GetFileNameWithoutExtension(fontPath.Name)));
+        }
+    }
+}

+ 22 - 6
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/TextToolbar.cs

@@ -1,23 +1,39 @@
 using Drawie.Backend.Core.Text;
+using PixiEditor.Models.Handlers.Toolbars;
+using PixiEditor.ViewModels.Tools.ToolSettings.Settings;
 
 namespace PixiEditor.ViewModels.Tools.ToolSettings.Toolbars;
 
-internal class TextToolbar : FillableShapeToolbar
+internal class TextToolbar : FillableShapeToolbar, ITextToolbar
 {
-    /*public Font Font
+    public FontFamilyName FontFamily
     {
         get
         {
-            return GetSetting<FontSettingViewModel>(nameof(Font)).Value;
+            return GetSetting<FontFamilySettingViewModel>(nameof(FontFamily)).Value;
         }
         set
         {
-            GetSetting<FontSettingViewModel>(nameof(Font)).Value = value;
+            GetSetting<FontFamilySettingViewModel>(nameof(FontFamily)).Value = value;
+        }
+    }
+    
+    public double FontSize
+    {
+        get
+        {
+            return GetSetting<SizeSettingViewModel>(nameof(FontSize)).Value;
+        }
+        set
+        {
+            GetSetting<SizeSettingViewModel>(nameof(FontSize)).Value = value;
         }
     }
     
     public TextToolbar()
     {
-        AddSetting(new FontSettingViewModel(nameof(Font), "FONT_LABEL"));
-    }*/
+        AddSetting(new FontFamilySettingViewModel(nameof(FontFamily), "FONT_LABEL"));
+        var sizeSetting = new SizeSettingViewModel(nameof(FontSize), "FONT_SIZE_LABEL") { Value = 12 };
+        AddSetting(sizeSetting);
+    }
 }

+ 6 - 0
src/PixiEditor/ViewModels/Tools/Tools/TextToolViewModel.cs

@@ -20,6 +20,12 @@ internal class TextToolViewModel : ToolViewModel, ITextToolHandler
     public override bool IsErasable => false;
     public override bool StopsLinkedToolOnUse => false;
 
+    [Settings.Inherited]
+    public double FontSize
+    {
+        get => GetValue<double>();
+    }
+    
     public TextToolViewModel()
     {
         Toolbar = ToolbarFactory.Create<TextToolViewModel, TextToolbar>(this);

+ 43 - 3
src/PixiEditor/Views/Overlays/TextOverlay/TextOverlay.cs

@@ -1,7 +1,9 @@
 using Avalonia;
 using Avalonia.Input;
+using Avalonia.Threading;
 using Drawie.Numerics;
 using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Input;
 using PixiEditor.OperatingSystem;
 using Canvas = Drawie.Backend.Core.Surfaces.Canvas;
 
@@ -9,6 +11,16 @@ namespace PixiEditor.Views.Overlays.TextOverlay;
 
 public class TextOverlay : Overlay
 {
+    private Dictionary<KeyCombination, Action> shortcuts;
+
+    public TextOverlay()
+    {
+        shortcuts = new Dictionary<KeyCombination, Action>
+        {
+            { new KeyCombination(Key.V, KeyModifiers.Control), PasteText },
+        };
+    }
+
     public static readonly StyledProperty<string> TextProperty = AvaloniaProperty.Register<TextOverlay, string>(
         nameof(Text));
 
@@ -47,6 +59,12 @@ public class TextOverlay : Overlay
 
     protected override void OnKeyPressed(Key key, KeyModifiers keyModifiers)
     {
+        if (IsShortcut(key, keyModifiers))
+        {
+            ExecuteShortcut(key, keyModifiers);
+            return;
+        }
+
         if (key == Key.Back)
         {
             if (Text.Length > 0)
@@ -65,18 +83,40 @@ public class TextOverlay : Overlay
         else
         {
             string converted = IOperatingSystem.Current.InputKeys.GetKeyboardKey(key);
-            if(converted == null || converted.Length > 1) return;
-            
+            if (converted == null || converted.Length > 1) return;
+
             string toAdd = keyModifiers.HasFlag(KeyModifiers.Shift) ? converted.ToUpper() : converted.ToLower();
             char? keyChar = toAdd.FirstOrDefault();
             if (keyChar != null)
             {
-                if(char.IsControl(keyChar.Value)) return;
+                if (char.IsControl(keyChar.Value)) return;
                 Text += keyChar;
             }
         }
     }
 
+    private bool IsShortcut(Key key, KeyModifiers keyModifiers)
+    {
+        return shortcuts.ContainsKey(new KeyCombination(key, keyModifiers));
+    }
+
+    private void ExecuteShortcut(Key key, KeyModifiers keyModifiers)
+    {
+        KeyCombination shortcut = new(key, keyModifiers);
+        if (shortcuts.ContainsKey(shortcut))
+        {
+            shortcuts[shortcut].Invoke();
+        }
+    }
+
+    private void PasteText()
+    {
+        ClipboardController.GetTextFromClipboard().ContinueWith(t =>
+        {
+            Dispatcher.UIThread.Invoke(() => Text += t.Result);
+        }, TaskContinuationOptions.OnlyOnRanToCompletion);
+    }
+
     private static void IsVisibleChanged(AvaloniaPropertyChangedEventArgs<bool> args)
     {
         if (args.NewValue.Value)

+ 29 - 0
src/PixiEditor/Views/Tools/ToolSettings/Settings/FontFamilySettingView.axaml

@@ -0,0 +1,29 @@
+<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:settings="clr-namespace:PixiEditor.ViewModels.Tools.ToolSettings.Settings"
+             xmlns:enums="clr-namespace:PixiEditor.ChangeableDocument.Enums;assembly=PixiEditor.ChangeableDocument"
+             xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
+             xmlns:helpers="clr-namespace:PixiEditor.Helpers"
+             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
+             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             x:Class="PixiEditor.Views.Tools.ToolSettings.Settings.FontFamilySettingView">
+    <Design.DataContext>
+        <settings:FontFamilySettingViewModel />
+    </Design.DataContext>
+
+    <StackPanel Orientation="Horizontal">
+        <Button Classes="pixi-icon" FontSize="16" Command="{Binding UploadFontCommand}" Content="{DynamicResource icon-upload}"/>
+        <ComboBox VerticalAlignment="Center"
+                  MinWidth="85"
+                  ItemsSource="{Binding Fonts}"
+                  SelectedIndex="{Binding FontIndex, Mode=TwoWay}">
+            <ComboBox.ItemTemplate>
+                <DataTemplate>
+                    <TextBlock Text="{Binding Name}" FontFamily="{Binding Converter={converters:FontFamilyNameToAvaloniaFontFamily}}" />
+                </DataTemplate>
+            </ComboBox.ItemTemplate>
+        </ComboBox>
+    </StackPanel>
+</UserControl>

+ 15 - 0
src/PixiEditor/Views/Tools/ToolSettings/Settings/FontFamilySettingView.axaml.cs

@@ -0,0 +1,15 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+
+namespace PixiEditor.Views.Tools.ToolSettings.Settings;
+
+public partial class FontFamilySettingView : UserControl
+{
+    public FontFamilySettingView()
+    {
+        InitializeComponent();
+    }
+}
+