Răsfoiți Sursa

Better undo and focus

Krzysztof Krysiński 7 luni în urmă
părinte
comite
d055d5fbc6
21 a modificat fișierele cu 159 adăugiri și 46 ștergeri
  1. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Vectors/SetShapeGeometry_UpdateableChange.cs
  2. 8 0
      src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs
  3. 2 0
      src/PixiEditor/Models/DocumentModels/Public/DocumentEventsModule.cs
  4. 6 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/Features/IQuickToolSwitchable.cs
  5. 1 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformSelectedExecutor.cs
  6. 14 8
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorTextToolExecutor.cs
  7. 3 2
      src/PixiEditor/Models/Handlers/IToolHandler.cs
  8. 1 0
      src/PixiEditor/Models/Handlers/IToolsHandler.cs
  9. 8 0
      src/PixiEditor/ViewModels/Document/TransformOverlays/TextOverlayViewModel.cs
  10. 25 15
      src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs
  11. 7 2
      src/PixiEditor/ViewModels/Tools/ToolViewModel.cs
  12. 2 2
      src/PixiEditor/ViewModels/Tools/Tools/MoveToolViewModel.cs
  13. 13 1
      src/PixiEditor/ViewModels/Tools/Tools/TextToolViewModel.cs
  14. 2 2
      src/PixiEditor/ViewModels/Tools/Tools/VectorEllipseToolViewModel.cs
  15. 2 2
      src/PixiEditor/ViewModels/Tools/Tools/VectorLineToolViewModel.cs
  16. 2 2
      src/PixiEditor/ViewModels/Tools/Tools/VectorPathToolViewModel.cs
  17. 2 2
      src/PixiEditor/ViewModels/Tools/Tools/VectorRectangleToolViewModel.cs
  18. 7 1
      src/PixiEditor/Views/Main/ViewportControls/ViewportOverlays.cs
  19. 6 0
      src/PixiEditor/Views/Overlays/Overlay.cs
  20. 40 6
      src/PixiEditor/Views/Overlays/TextOverlay/TextOverlay.cs
  21. 7 0
      src/PixiEditor/Views/Rendering/Scene.cs

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Vectors/SetShapeGeometry_UpdateableChange.cs

@@ -102,7 +102,7 @@ internal class SetShapeGeometry_UpdateableChange : InterruptableUpdateableChange
     {
         if (other is SetShapeGeometry_UpdateableChange change)
         {
-            return change.TargetId == TargetId;
+            return change.TargetId == TargetId && change.Data is not TextVectorData; // text should not be merged into one change
         }
 
         return false;

+ 8 - 0
src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs

@@ -313,4 +313,12 @@ internal class ChangeExecutionController
             textOverlayHandler.OnTextChanged(text);
         }
     }
+
+    public void QuickToolSwitchInlet()
+    {
+        if (currentSession is IQuickToolSwitchable quickToolSwitchable)
+        {
+            quickToolSwitchable.OnQuickToolSwitch();
+        }
+    }
 }

+ 2 - 0
src/PixiEditor/Models/DocumentModels/Public/DocumentEventsModule.cs

@@ -51,4 +51,6 @@ internal class DocumentEventsModule
     public void OnSymmetryDragStarted(SymmetryAxisDirection dir) => Internals.ChangeController.SymmetryDragStartedInlet(dir);
     public void OnSymmetryDragged(SymmetryAxisDragInfo info) => Internals.ChangeController.SymmetryDraggedInlet(info);
     public void OnSymmetryDragEnded(SymmetryAxisDirection dir) => Internals.ChangeController.SymmetryDragEndedInlet(dir);
+
+    public void QuickToolSwitchInlet() => Internals.ChangeController.QuickToolSwitchInlet();
 }

+ 6 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/Features/IQuickToolSwitchable.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
+
+internal interface IQuickToolSwitchable : IExecutorFeature
+{
+    public void OnQuickToolSwitch();
+}

+ 1 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformSelectedExecutor.cs

@@ -193,6 +193,7 @@ internal class TransformSelectedExecutor : UpdateableChangeExecutor, ITransforma
         if (document.SelectedStructureMember is ILayerHandler layerHandler && layerHandler.QuickEditTool != null)
         {
             ViewModelMain.Current.ToolsSubViewModel.SetActiveTool(layerHandler.QuickEditTool, false);
+            ViewModelMain.Current.ToolsSubViewModel.QuickToolSwitchInlet();
             return true;
         }
 

+ 14 - 8
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorTextToolExecutor.cs

@@ -15,7 +15,7 @@ using PixiEditor.Models.Tools;
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 
-internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEvents
+internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEvents, IQuickToolSwitchable
 {
     private ITextToolHandler textHandler;
     private ITextToolbar toolbar;
@@ -26,6 +26,8 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
     private Matrix3X3 lastMatrix = Matrix3X3.Identity;
     private Font? cachedFont;
 
+    public override bool BlocksOtherActions => false;
+
     public override ExecutorType Type => ExecutorType.ToolLinked;
 
     public override ExecutionState Start()
@@ -57,8 +59,6 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
             lastText = textData.Text;
             position = textData.Position;
             lastMatrix = textData.TransformationMatrix;
-
-            document.TextOverlayHandler.SetCursorPosition(internals.ChangeController.LastPrecisePosition);
         }
         else if (shape is null)
         {
@@ -75,6 +75,12 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
         return ExecutionState.Success;
     }
 
+
+    public void OnQuickToolSwitch()
+    {
+        document.TextOverlayHandler.SetCursorPosition(internals.ChangeController.LastPrecisePosition);
+    }
+
     public override void ForceStop()
     {
         internals.ActionAccumulator.AddFinishedActions(new EndSetShapeGeometry_Action());
@@ -84,9 +90,9 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
     public void OnTextChanged(string text)
     {
         var constructedText = ConstructTextData(text);
-        internals.ActionAccumulator.AddFinishedActions();
-        internals.ActionAccumulator.AddActions(
+        internals.ActionAccumulator.AddFinishedActions(
             new SetShapeGeometry_Action(selectedMember.Id, constructedText),
+            new EndSetShapeGeometry_Action(),
             new SetLowDpiRendering_Action(selectedMember.Id, toolbar.ForceLowDpiRendering));
         lastText = text;
         document.TextOverlayHandler.Font = constructedText.Font;
@@ -107,11 +113,11 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
         }
         else if (name is nameof(ITextToolbar.FontSize))
         {
-            if(cachedFont == null)
+            if (cachedFont == null)
             {
                 cachedFont = toolbar.ConstructFont();
             }
-            
+
             document.TextOverlayHandler.Font.Size = toolbar.FontSize;
             cachedFont.Size = toolbar.FontSize;
         }
@@ -171,6 +177,6 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
 
     bool IExecutorFeature.IsFeatureEnabled(IExecutorFeature feature)
     {
-        return feature is ITextOverlayEvents;
+        return feature is ITextOverlayEvents || feature is IQuickToolSwitchable;
     }
 }

+ 3 - 2
src/PixiEditor/Models/Handlers/IToolHandler.cs

@@ -63,9 +63,10 @@ internal interface IToolHandler : IHandler
     public void SetToolSetSettings(IToolSetHandler toolset, Dictionary<string, object>? settings);
     public void ApplyToolSetSettings(IToolSetHandler toolset);
     public void OnToolDeselected(bool transient);
-    public void OnPostUndo();
-    public void OnPostRedo();
+    public void OnPostUndoInlet();
+    public void OnPostRedoInlet();
     public void OnActiveFrameChanged(int newFrame);
     public void OnPreUndoInlet();
     public void OnPreRedoInlet();
+    public void QuickToolSwitchInlet();
 }

+ 1 - 0
src/PixiEditor/Models/Handlers/IToolsHandler.cs

@@ -35,4 +35,5 @@ internal interface IToolsHandler : IHandler
     public void OnPostRedoInlet();
     public void OnPreRedoInlet();
     public void OnPreUndoInlet();
+    public void QuickToolSwitchInlet();
 }

+ 8 - 0
src/PixiEditor/ViewModels/Document/TransformOverlays/TextOverlayViewModel.cs

@@ -17,6 +17,7 @@ internal class TextOverlayViewModel : ObservableObject, ITextOverlayHandler
     private Matrix3X3 matrix = Matrix3X3.Identity;
     private double? spacing;
     private int cursorPosition;
+    private int selectionEnd;
 
     public event Action<string>? TextChanged;
 
@@ -75,6 +76,12 @@ internal class TextOverlayViewModel : ObservableObject, ITextOverlayHandler
         set => SetProperty(ref cursorPosition, value);
     }
 
+    public int SelectionEnd
+    {
+        get => selectionEnd;
+        set => SetProperty(ref selectionEnd, value);
+    }
+
     public void SetCursorPosition(VecD closestToPosition)
     {
         VecD mapped = Matrix.Invert().MapPoint(closestToPosition);
@@ -85,6 +92,7 @@ internal class TextOverlayViewModel : ObservableObject, ITextOverlayHandler
             .First().index;
         
         CursorPosition = indexOfClosest;
+        SelectionEnd = indexOfClosest;
     }
 
     public TextOverlayViewModel()

+ 25 - 15
src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs

@@ -136,7 +136,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
         ActiveToolSet = toolSetHandler;
         ActiveToolSet.ApplyToolSetSettings();
         UpdateEnabledState();
-        
+
         ActiveTool?.OnToolSelected(false);
     }
 
@@ -390,8 +390,8 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
 
         if (ActiveTool is not { CanBeUsedOnActiveLayer: true })
         {
-            if(ActiveTool.LayerTypeToCreateOnEmptyUse == null) return;
-            
+            if (ActiveTool.LayerTypeToCreateOnEmptyUse == null) return;
+
             Guid? createdLayer = Owner.LayersSubViewModel.NewLayer(
                 ActiveTool.LayerTypeToCreateOnEmptyUse,
                 ActionSource.Automated,
@@ -427,22 +427,31 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
     {
         ActiveTool?.KeyChanged(args.IsCtrlDown, args.IsShiftDown, args.IsAltDown, args.Key);
     }
-    
+
     public void OnPostUndoInlet()
     {
-        ActiveTool?.OnPostUndo();
+        ActiveTool?.OnPostUndoInlet();
     }
-    
+
     public void OnPostRedoInlet()
     {
-        ActiveTool?.OnPostRedo();
+        ActiveTool?.OnPostRedoInlet();
     }
-    
+
     public void OnPreUndoInlet()
     {
         ActiveTool?.OnPreUndoInlet();
     }
-    
+
+    public void QuickToolSwitchInlet()
+    {
+        var document = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        if (document is null)
+            return;
+
+        document.EventInlet.QuickToolSwitchInlet();
+    }
+
     public void OnPreRedoInlet()
     {
         ActiveTool?.OnPreRedoInlet();
@@ -462,7 +471,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
         foreach (ToolSetConfig toolSet in toolSetConfig)
         {
             var toolSetViewModel = new ToolSetViewModel(toolSet.Name);
-            
+
             foreach (var toolFromToolset in toolSet.Tools)
             {
                 IToolHandler? tool = allTools.FirstOrDefault(tool => tool.ToolName == toolFromToolset.ToolName);
@@ -472,7 +481,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
                 {
                     toolSetViewModel.IconOverwrites[tool] = toolFromToolset.Icon;
                 }
-                
+
                 if (tool is null)
                 {
 #if DEBUG
@@ -506,7 +515,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
             UpdateEnabledState();
         }
     }
-    
+
     private void ActiveFrameChanged(int oldFrame, int newFrame)
     {
         UpdateActiveFrame(newFrame);
@@ -519,17 +528,18 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
 
     private void DocumentOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
     {
-        if (e.PropertyName is nameof(DocumentViewModel.SelectedStructureMember) or nameof(DocumentViewModel.SoftSelectedStructureMembers))
+        if (e.PropertyName is nameof(DocumentViewModel.SelectedStructureMember)
+            or nameof(DocumentViewModel.SoftSelectedStructureMembers))
         {
             UpdateEnabledState();
         }
     }
-    
+
     private void UpdateActiveFrame(int newFrame)
     {
         ActiveTool?.OnActiveFrameChanged(newFrame);
     }
-    
+
     private void UpdateEnabledState()
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;

+ 7 - 2
src/PixiEditor/ViewModels/Tools/ToolViewModel.cs

@@ -185,12 +185,17 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
     {
     }
 
-    public virtual void OnPostUndo() { }
-    public virtual void OnPostRedo() { }
+    public virtual void OnPostUndoInlet() { }
+    public virtual void OnPostRedoInlet() { }
     public virtual void OnActiveFrameChanged(int newFrame) { }
     public virtual void OnPreUndoInlet() { }
 
     public virtual void OnPreRedoInlet() { }
+    public virtual void QuickToolSwitchInlet()
+    {
+
+    }
+
     public void SetToolSetSettings(IToolSetHandler toolset, Dictionary<string, object>? settings)
     {
         if (settings == null || settings.Count == 0 || toolset == null)

+ 2 - 2
src/PixiEditor/ViewModels/Tools/Tools/MoveToolViewModel.cs

@@ -97,7 +97,7 @@ internal class MoveToolViewModel : ToolViewModel, IMoveToolHandler
         }
     }
 
-    public override void OnPostUndo()
+    public override void OnPostUndoInlet()
     {
         if (IsActive)
         {
@@ -105,7 +105,7 @@ internal class MoveToolViewModel : ToolViewModel, IMoveToolHandler
         }
     }
 
-    public override void OnPostRedo()
+    public override void OnPostRedoInlet()
     {
         if (IsActive)
         {

+ 13 - 1
src/PixiEditor/ViewModels/Tools/Tools/TextToolViewModel.cs

@@ -27,7 +27,7 @@ internal class TextToolViewModel : ToolViewModel, ITextToolHandler
     {
         get => GetValue<double>();
     }
-    
+
     public TextToolViewModel()
     {
         Toolbar = ToolbarFactory.Create<TextToolViewModel, TextToolbar>(this);
@@ -59,4 +59,16 @@ internal class TextToolViewModel : ToolViewModel, ITextToolHandler
         OnDeselecting(false);
         OnSelected(false);
     }
+
+    public override void OnPostUndoInlet()
+    {
+        OnDeselecting(false);
+        OnSelected(false);
+    }
+
+    public override void OnPostRedoInlet()
+    {
+        OnDeselecting(false);
+        OnSelected(false);
+    }
 }

+ 2 - 2
src/PixiEditor/ViewModels/Tools/Tools/VectorEllipseToolViewModel.cs

@@ -65,7 +65,7 @@ internal class VectorEllipseToolViewModel : ShapeTool, IVectorEllipseToolHandler
         ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UseVectorEllipseTool();
     }
 
-    public override void OnPostUndo()
+    public override void OnPostUndoInlet()
     {
         if (IsActive)
         {
@@ -73,7 +73,7 @@ internal class VectorEllipseToolViewModel : ShapeTool, IVectorEllipseToolHandler
         }
     }
 
-    public override void OnPostRedo()
+    public override void OnPostRedoInlet()
     {
         if (IsActive)
         {

+ 2 - 2
src/PixiEditor/ViewModels/Tools/Tools/VectorLineToolViewModel.cs

@@ -72,7 +72,7 @@ internal class VectorLineToolViewModel : ShapeTool, IVectorLineToolHandler
         document.Tools.UseVectorLineTool();
     }
 
-    public override void OnPostUndo()
+    public override void OnPostUndoInlet()
     {
         if (IsActive)
         {
@@ -80,7 +80,7 @@ internal class VectorLineToolViewModel : ShapeTool, IVectorLineToolHandler
         }
     }
 
-    public override void OnPostRedo()
+    public override void OnPostRedoInlet()
     {
         if (IsActive)
         {

+ 2 - 2
src/PixiEditor/ViewModels/Tools/Tools/VectorPathToolViewModel.cs

@@ -128,7 +128,7 @@ internal class VectorPathToolViewModel : ShapeTool, IVectorPathToolHandler
         }
     }
 
-    public override void OnPostUndo()
+    public override void OnPostUndoInlet()
     {
         if (isActivated)
         {
@@ -136,7 +136,7 @@ internal class VectorPathToolViewModel : ShapeTool, IVectorPathToolHandler
         }
     }
 
-    public override void OnPostRedo()
+    public override void OnPostRedoInlet()
     {
         if (isActivated)
         {

+ 2 - 2
src/PixiEditor/ViewModels/Tools/Tools/VectorRectangleToolViewModel.cs

@@ -62,7 +62,7 @@ internal class VectorRectangleToolViewModel : ShapeTool, IVectorRectangleToolHan
         ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UseVectorRectangleTool();
     }
 
-    public override void OnPostUndo()
+    public override void OnPostUndoInlet()
     {
         if (IsActive)
         {
@@ -70,7 +70,7 @@ internal class VectorRectangleToolViewModel : ShapeTool, IVectorRectangleToolHan
         }
     }
 
-    public override void OnPostRedo()
+    public override void OnPostRedoInlet()
     {
         if (IsActive)
         {

+ 7 - 1
src/PixiEditor/Views/Main/ViewportControls/ViewportOverlays.cs

@@ -492,7 +492,12 @@ internal class ViewportOverlays
         {
             Source = Viewport, Path = "Document.TextOverlayViewModel.CursorPosition", Mode = BindingMode.TwoWay
         };
-        
+
+        Binding selectionEndBinding = new()
+        {
+            Source = Viewport, Path = "Document.TextOverlayViewModel.SelectionEnd", Mode = BindingMode.TwoWay
+        };
+
         textOverlay.Bind(Visual.IsVisibleProperty, isVisibleBinding);
         textOverlay.Bind(TextOverlay.TextProperty, textBinding);
         textOverlay.Bind(TextOverlay.PositionProperty, positionBinding);
@@ -501,6 +506,7 @@ internal class ViewportOverlays
         textOverlay.Bind(TextOverlay.MatrixProperty, matrixBinding);
         textOverlay.Bind(TextOverlay.SpacingProperty, spacingBinding);
         textOverlay.Bind(TextOverlay.CursorPositionProperty, cursorPositionBinding);
+        textOverlay.Bind(TextOverlay.SelectionEndProperty, selectionEndBinding);
     }
 }
 

+ 6 - 0
src/PixiEditor/Views/Overlays/Overlay.cs

@@ -44,6 +44,7 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
     }
 
     public event Action? RefreshRequested;
+    public event Action? FocusRequested;
     public event Action? RefreshCursorRequested;
     public event PointerEvent? PointerEnteredOverlay;
     public event PointerEvent? PointerExitedOverlay;
@@ -80,6 +81,11 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
         InvalidateVisual(); // For elements in visual tree
     }
 
+    public void FocusOverlay()
+    {
+        FocusRequested?.Invoke();
+    }
+
     public void ForceRefreshCursor()
     {
         RefreshCursorRequested?.Invoke();

+ 40 - 6
src/PixiEditor/Views/Overlays/TextOverlay/TextOverlay.cs

@@ -55,13 +55,13 @@ internal class TextOverlay : Overlay
         set => SetValue(CursorPositionProperty, value);
     }
 
-    public static readonly StyledProperty<int> SelectionLengthProperty = AvaloniaProperty.Register<TextOverlay, int>(
-        nameof(SelectionEnd));
+    public static readonly StyledProperty<int> SelectionEndProperty = AvaloniaProperty.Register<TextOverlay, int>(
+        nameof(SelectionEnd), coerce: ClampValue);
 
     public int SelectionEnd
     {
-        get => GetValue(SelectionLengthProperty);
-        set => SetValue(SelectionLengthProperty, value);
+        get => GetValue(SelectionEndProperty);
+        set => SetValue(SelectionEndProperty, value);
     }
 
     public static readonly StyledProperty<Matrix3X3> MatrixProperty = AvaloniaProperty.Register<TextOverlay, Matrix3X3>(
@@ -125,7 +125,7 @@ internal class TextOverlay : Overlay
         FontProperty.Changed.Subscribe(FontChanged);
         SpacingProperty.Changed.Subscribe(SpaceChanged);
 
-        AffectsOverlayRender(FontProperty, TextProperty, CursorPositionProperty, SelectionLengthProperty,
+        AffectsOverlayRender(FontProperty, TextProperty, CursorPositionProperty, SelectionEndProperty,
             IsEditingProperty,
             MatrixProperty, SpacingProperty);
     }
@@ -277,6 +277,8 @@ internal class TextOverlay : Overlay
         initialPos = args.Point;
         isLmbPressed = args.PointerButton == MouseButton.Left;
         args.Pointer.Capture(this);
+
+        args.Handled = true;
     }
 
     protected override void OnOverlayPointerMoved(OverlayPointerArgs args)
@@ -368,6 +370,15 @@ internal class TextOverlay : Overlay
     protected override void OnKeyPressed(Key key, KeyModifiers keyModifiers, string? keySymbol)
     {
         if (!IsEditing) return;
+
+        ShortcutController.BlockShortcutExecution(nameof(TextOverlay));
+
+        if(IsUndoRedoShortcut(key, keyModifiers))
+        {
+            ShortcutController.UnblockShortcutExecution(nameof(TextOverlay));
+            return;
+        }
+
         if (IsShortcut(key, keyModifiers))
         {
             ExecuteShortcut(key, keyModifiers);
@@ -377,6 +388,11 @@ internal class TextOverlay : Overlay
         InsertChar(key, keySymbol);
     }
 
+    private bool IsUndoRedoShortcut(Key key, KeyModifiers keyModifiers)
+    {
+        return key == Key.Z && keyModifiers == KeyModifiers.Control || key == Key.Y && keyModifiers == KeyModifiers.Control;
+    }
+
     private void InsertChar(Key key, string symbol)
     {
         if (key == Key.Enter)
@@ -543,6 +559,8 @@ internal class TextOverlay : Overlay
     private void RequestEditTextTriggered(object? sender, string e)
     {
         IsEditing = true;
+        CursorPosition = glyphPositions.Length;
+        SelectionEnd = CursorPosition;
     }
 
     private void UpdateGlyphs()
@@ -585,6 +603,22 @@ internal class TextOverlay : Overlay
         if (args.NewValue.Value)
         {
             ShortcutController.BlockShortcutExecution(nameof(TextOverlay));
+            TextOverlay sender = args.Sender as TextOverlay;
+            sender.UpdateGlyphs();
+
+            if(sender.CursorPosition > sender.glyphPositions.Length)
+            {
+                sender.CursorPosition = sender.glyphPositions.Length;
+            }
+
+            if (sender.SelectionEnd > sender.glyphPositions.Length)
+            {
+                sender.SelectionEnd = sender.glyphPositions.Length;
+            }
+
+            sender.lastXMovementCursorIndex = sender.CursorPosition;
+
+            sender.FocusOverlay();
         }
         else
         {
@@ -616,7 +650,7 @@ internal class TextOverlay : Overlay
         if (textOverlay == null) return newPos;
         if (textOverlay.Text == null) return 0;
 
-        return Math.Clamp(newPos, 0, textOverlay.Text.Length);
+        return Math.Clamp(newPos, 0, textOverlay.glyphPositions.Length - 1);
     }
 }
 

+ 7 - 0
src/PixiEditor/Views/Rendering/Scene.cs

@@ -497,6 +497,11 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         };
     }
 
+    private void FocusOverlay()
+    {
+        Focus();
+    }
+
     private VecD ToCanvasSpace(Point scenePosition)
     {
         Matrix transform = CalculateTransformMatrix();
@@ -548,6 +553,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
             {
                 overlay.RefreshRequested -= QueueRender;
                 overlay.RefreshCursorRequested -= RefreshCursor;
+                overlay.FocusRequested -= FocusOverlay;
             }
         }
 
@@ -557,6 +563,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
             {
                 overlay.RefreshRequested += QueueRender;
                 overlay.RefreshCursorRequested += RefreshCursor;
+                overlay.FocusRequested += FocusOverlay;
             }
         }
     }