# -*- encoding: utf-8 -*- ''' Emacs Behavior ============== The :class:`~kivy.uix.behaviors.emacs.EmacsBehavior` `mixin `_ allows you to add `Emacs `_ keyboard shortcuts for basic movement and editing to the :class:`~kivy.uix.textinput.TextInput` widget. The shortcuts currently available are listed below: Emacs shortcuts --------------- =============== ======================================================== Shortcut Description --------------- -------------------------------------------------------- Control + a Move cursor to the beginning of the line Control + e Move cursor to the end of the line Control + f Move cursor one character to the right Control + b Move cursor one character to the left Alt + f Move cursor to the end of the word to the right Alt + b Move cursor to the start of the word to the left Alt + Backspace Delete text left of the cursor to the beginning of word Alt + d Delete text right of the cursor to the end of the word Alt + w Copy selection Control + w Cut selection Control + y Paste selection =============== ======================================================== .. warning:: If you have the :mod:`~kivy.modules.inspector` module enabled, the shortcut for opening the inspector (Control + e) conflicts with the Emacs shortcut to move to the end of the line (it will still move the cursor to the end of the line, but the inspector will open as well). ''' from kivy.properties import StringProperty __all__ = ('EmacsBehavior', ) class EmacsBehavior(object): ''' A `mixin `_ that enables Emacs-style keyboard shortcuts for the :class:`~kivy.uix.textinput.TextInput` widget. Please see the :mod:`Emacs behaviors module ` documentation for more information. .. versionadded:: 1.9.1 ''' key_bindings = StringProperty('emacs') '''String name which determines the type of key bindings to use with the :class:`~kivy.uix.textinput.TextInput`. This allows Emacs key bindings to be enabled/disabled programmatically for widgets that inherit from :class:`EmacsBehavior`. If the value is not ``'emacs'``, Emacs bindings will be disabled. Use ``'default'`` for switching to the default key bindings of TextInput. :attr:`key_bindings` is a :class:`~kivy.properties.StringProperty` and defaults to ``'emacs'``. .. versionadded:: 1.10.0 ''' def __init__(self, **kwargs): super(EmacsBehavior, self).__init__(**kwargs) self.bindings = { 'ctrl': { 'a': lambda: self.do_cursor_movement('cursor_home'), 'e': lambda: self.do_cursor_movement('cursor_end'), 'f': lambda: self.do_cursor_movement('cursor_right'), 'b': lambda: self.do_cursor_movement('cursor_left'), 'w': lambda: self._cut(self.selection_text), 'y': self.paste, }, 'alt': { 'w': self.copy, 'f': lambda: self.do_cursor_movement('cursor_right', control=True), 'b': lambda: self.do_cursor_movement('cursor_left', control=True), 'd': self.delete_word_right, '\x08': self.delete_word_left, # alt + backspace }, } def keyboard_on_key_down(self, window, keycode, text, modifiers): key, key_str = keycode # join the modifiers e.g. ['alt', 'ctrl'] mod = '+'.join(modifiers) if modifiers else None is_emacs_shortcut = False if key in range(256) and self.key_bindings == 'emacs': if mod == 'ctrl' and chr(key) in self.bindings['ctrl'].keys(): is_emacs_shortcut = True elif mod == 'alt' and chr(key) in self.bindings['alt'].keys(): is_emacs_shortcut = True else: # e.g. ctrl+alt or alt+ctrl (alt-gr key) is_emacs_shortcut = False if is_emacs_shortcut: # Look up mod and key emacs_shortcut = self.bindings[mod][chr(key)] emacs_shortcut() else: super(EmacsBehavior, self).keyboard_on_key_down(window, keycode, text, modifiers) def delete_word_right(self): '''Delete text right of the cursor to the end of the word''' if self._selection: return start_index = self.cursor_index() start_cursor = self.cursor self.do_cursor_movement('cursor_right', control=True) end_index = self.cursor_index() if start_index != end_index: s = self.text[start_index:end_index] self._set_unredo_delsel(start_index, end_index, s, from_undo=False) self.text = self.text[:start_index] + self.text[end_index:] self._set_cursor(pos=start_cursor) def delete_word_left(self): '''Delete text left of the cursor to the beginning of word''' if self._selection: return start_index = self.cursor_index() self.do_cursor_movement('cursor_left', control=True) end_cursor = self.cursor end_index = self.cursor_index() if start_index != end_index: s = self.text[end_index:start_index] self._set_unredo_delsel(end_index, start_index, s, from_undo=False) self.text = self.text[:end_index] + self.text[start_index:] self._set_cursor(pos=end_cursor)