tkinter rejects Cyrillic chars in bind(). Use keycode-based <Control-Key> dispatch instead (same approach as terminal_widget). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
61 lines
1.7 KiB
Python
61 lines
1.7 KiB
Python
"""
|
|
Ctrl+Z / Ctrl+Y undo/redo for CTkEntry widgets.
|
|
tk.Entry has no built-in undo, so we track history manually.
|
|
"""
|
|
|
|
|
|
def enable_undo(ctk_entry):
|
|
"""Add Ctrl+Z undo and Ctrl+Y redo to a CTkEntry widget."""
|
|
entry = ctk_entry._entry
|
|
history = [entry.get()]
|
|
redo_stack = []
|
|
_recording = [True]
|
|
|
|
def _snapshot(*_args):
|
|
if not _recording[0]:
|
|
return
|
|
val = entry.get()
|
|
if not history or history[-1] != val:
|
|
history.append(val)
|
|
redo_stack.clear()
|
|
# Cap history size
|
|
if len(history) > 200:
|
|
del history[:100]
|
|
|
|
def _undo(event):
|
|
if len(history) > 1:
|
|
redo_stack.append(history.pop())
|
|
_recording[0] = False
|
|
entry.delete(0, "end")
|
|
entry.insert(0, history[-1])
|
|
_recording[0] = True
|
|
elif history:
|
|
redo_stack.append(history.pop())
|
|
_recording[0] = False
|
|
entry.delete(0, "end")
|
|
_recording[0] = True
|
|
return "break"
|
|
|
|
def _redo(event):
|
|
if redo_stack:
|
|
val = redo_stack.pop()
|
|
history.append(val)
|
|
_recording[0] = False
|
|
entry.delete(0, "end")
|
|
entry.insert(0, val)
|
|
_recording[0] = True
|
|
return "break"
|
|
|
|
def _on_ctrl_key(event):
|
|
"""Route Ctrl+key by physical keycode (layout-independent)."""
|
|
if event.keycode == 90: # Z
|
|
return _undo(event)
|
|
if event.keycode == 89: # Y
|
|
return _redo(event)
|
|
return None
|
|
|
|
entry.bind("<KeyRelease>", _snapshot, add="+")
|
|
entry.bind("<Control-z>", _undo)
|
|
entry.bind("<Control-y>", _redo)
|
|
entry.bind("<Control-Key>", _on_ctrl_key)
|