View Issue Details

IDProjectCategoryView StatusLast Update
0035421LazarusWidgetsetpublic2019-12-23 21:01
ReporterZoë Peterson Assigned ToDmitry Boyarintsev  
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product Version2.0.3 (SVN) 
Summary0035421: [Patch] Add undo/redo keyboard shortcuts for Cocoa TEdit and TMemo
DescriptionThe attached patch fixes Cocoa's TEdit and TMemo so they support Cmd+Z/Cmd+Shift+Z for undo/redo.

TEdit relies on Cocoa's default NSTextField behavior, which uses the shared undoManager and clears the undo stack whenever it gains focus.

For TMemo I added an internal undoManager to each TCocoaTextView so it could maintain the undo stack across focus switches. It's a little weird, because it will group typing across focus switches, but that seemed preferable to clearing the stack entirely like TEdit does. If you'd prefer that they be match, leave out the changes to cocoatextedits.pas and instead add a "TCocoaTextView.becomeFirstResponder" that calls undoManager.removeAllActions.
Steps To ReproduceCreate an LCLCocoa application, drop a TEdit and TMemo on the form, and run it. With this patch, Cmd+Z/Cmd+Shift+Z will work to do undo/redo. Without it it will just beep.
TagsNo tags attached.
Fixed in Revision61024
LazTarget-
WidgetsetCocoa
Attached Files

Relationships

related to 0036073 assignedDmitry Boyarintsev Cocoa: Fix TEdit/TMemo Undo/Redo 

Activities

Zoë Peterson

2019-04-19 23:48

reporter  

cocoaedit_undo.patch (4,074 bytes)   
diff --git a/lcl/interfaces/cocoa/cocoatextedits.pas b/lcl/interfaces/cocoa/cocoatextedits.pas
index 11fd9d8b97..bc6c2dcd23 100644
--- a/lcl/interfaces/cocoa/cocoatextedits.pas
+++ b/lcl/interfaces/cocoa/cocoatextedits.pas
@@ -105,10 +105,13 @@ type
   public
     callback: ICommonCallback;
     FEnabled: Boolean;
+    FUndoManager: NSUndoManager;
 
     supressTextChangeEvent: Integer; // if above zero, then don't send text change event
 
+    procedure dealloc; override;
     function acceptsFirstResponder: LCLObjCBoolean; override;
+    function undoManager: NSUndoManager; override;
     function lclGetCallback: ICommonCallback; override;
     procedure lclClearCallback; override;
     procedure resetCursorRects; override;
@@ -901,11 +904,30 @@ begin
   inherited flagsChanged(event);
 end;
 
+procedure TCocoaTextView.dealloc;
+begin
+  if Assigned(FUndoManager) then
+    FUndoManager.release;
+  inherited dealloc;
+end;
+
 function TCocoaTextView.acceptsFirstResponder: LCLObjCBoolean;
 begin
   Result := True;
 end;
 
+function TCocoaTextView.undoManager: NSUndoManager;
+begin
+  if allowsUndo then
+  begin
+    if not Assigned(FUndoManager) then
+      FUndoManager := NSUndoManager.alloc.init;
+    Result := FUndoManager;
+  end
+  else
+    Result := nil;
+end;
+
 function TCocoaTextView.lclGetCallback: ICommonCallback;
 begin
   Result := callback;
diff --git a/lcl/interfaces/cocoa/cocoawindows.pas b/lcl/interfaces/cocoa/cocoawindows.pas
index 4103337ab0..462b9e55fe 100644
--- a/lcl/interfaces/cocoa/cocoawindows.pas
+++ b/lcl/interfaces/cocoa/cocoawindows.pas
@@ -385,7 +385,9 @@ begin
     callback.DidResignKeyNotification;
 end;
 
-procedure NSResponderHotKeys(asender: NSResponder; event: NSEvent; var handled: LCLObjCBoolean; atarget: id);
+procedure NSResponderHotKeys(asender: NSResponder; event: NSEvent; var handled: LCLObjCBoolean; atarget: NSResponder);
+var
+  undoManager: NSUndoManager;
 begin
   // todo: system keys could be overriden. thus need to review the current
   //       keyboard configuration first. See "Key Bindings" at
@@ -399,13 +401,28 @@ begin
     if Assigned(event.charactersIgnoringModifiers.UTF8String) then
     begin
       case event.charactersIgnoringModifiers.UTF8String^ of
-        // redo/undo are not implemented in either of TextControls?
-        //'Z': handled := NSApplication(NSApp).sendAction_to_from(objcselector('redo:'), atarget, asender);
         'a': handled := NSApplication(NSApp).sendAction_to_from(objcselector('selectAll:'), atarget, asender);
         'c': handled := NSApplication(NSApp).sendAction_to_from(objcselector('copy:'), atarget, asender);
         'v': handled := NSApplication(NSApp).sendAction_to_from(objcselector('paste:'), atarget, asender);
         'x': handled := NSApplication(NSApp).sendAction_to_from(objcselector('cut:'), atarget, asender);
-        //'z': handled := NSApplication(NSApp).sendAction_to_from(objcselector('undo:'), atarget, asender);
+        'z':
+        begin
+          undoManager := atarget.undoManager;
+          if Assigned(undoManager) and undoManager.canUndo then
+          begin
+            handled := true;
+            undoManager.undo;
+          end;
+        end;
+        'Z':
+        begin
+          undoManager := atarget.undoManager;
+          if Assigned(undoManager) and undoManager.canRedo then
+          begin
+            handled := true;
+            undoManager.redo;
+          end;
+        end;
       end;
     end;
   end;
diff --git a/lcl/interfaces/cocoa/cocoawsstdctrls.pas b/lcl/interfaces/cocoa/cocoawsstdctrls.pas
index 4fbacc131f..822d2e5571 100644
--- a/lcl/interfaces/cocoa/cocoawsstdctrls.pas
+++ b/lcl/interfaces/cocoa/cocoawsstdctrls.pas
@@ -1354,6 +1354,7 @@ begin
   nr.size.width:=AParams.Width;
 
   txt := TCocoaTextView.alloc.initwithframe(nr);
+  txt.setAllowsUndo(true);
   // setting up a default system font (to be consistent with other widgetsets)
   txt.setFont( NSFont.systemFontOfSize( NSFont.systemFontSizeForControlSize(NSRegularControlSize) ));
   txt.setRichText(false);
cocoaedit_undo.patch (4,074 bytes)   

Dmitry Boyarintsev

2019-04-20 07:22

developer   ~0115686

thank you very much! applied

Issue History

Date Modified Username Field Change
2019-04-19 23:48 Zoë Peterson New Issue
2019-04-19 23:48 Zoë Peterson File Added: cocoaedit_undo.patch
2019-04-20 07:22 Dmitry Boyarintsev Fixed in Revision => 61024
2019-04-20 07:22 Dmitry Boyarintsev LazTarget => -
2019-04-20 07:22 Dmitry Boyarintsev Note Added: 0115686
2019-04-20 07:22 Dmitry Boyarintsev Status new => resolved
2019-04-20 07:22 Dmitry Boyarintsev Resolution open => fixed
2019-04-20 07:22 Dmitry Boyarintsev Assigned To => Dmitry Boyarintsev
2019-05-17 23:39 Zoë Peterson Status resolved => closed
2019-12-23 21:01 Dmitry Boyarintsev Relationship added related to 0036073