aboutsummaryrefslogtreecommitdiffstats
path: root/tagit/widgets/keyboard.py
blob: 2cae7d6022600117e3e43b07e2e6aee54f530af6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
"""

Part of the tagit module.
A copy of the license is provided with the project.
Author: Matthias Baumgartner, 2022
"""
# kivy imports
from kivy.uix.widget import Widget
import kivy.properties as kp

# exports
__all__ = []


## code ##

class Keyboard(Widget):
    """Captures key events and turns them into simplified events.
    Keeps a record of currently pressed modifiers (CTRL, SHIFT, etc.).
    """

    # modifiers
    MODIFIERS_NONE  = 0b00000 # 0
    MODIFIERS_CTRL  = 0b00001 # 1
    MODIFIERS_SHIFT = 0b00010 # 2
    MODIFIERS_ALT   = 0b00100 # 4
    MODIFIERS_ALTGR = 0b01000 # 8
    MODIFIERS_CMD   = 0b10000 # 16

    # modifier keymaps
    keymap = {
        303: MODIFIERS_SHIFT, # right shift
        304: MODIFIERS_SHIFT, # left shift
        305: MODIFIERS_CTRL,  # left ctrl
        306: MODIFIERS_CTRL,  # right ctrl
        307: MODIFIERS_ALTGR,
        308: MODIFIERS_ALT,
        309: MODIFIERS_CMD,   # a.k.a. windows key
    }

    modemap = {
        MODIFIERS_SHIFT: (303, 304),
        MODIFIERS_CTRL:  (305, 306),
        MODIFIERS_ALTGR: (307, ),
        MODIFIERS_ALT:   (308, ),
        MODIFIERS_CMD:   (309, ),
    }

    # current mode
    mode = kp.NumericProperty(MODIFIERS_NONE)

    # state access via properties

    @property
    def none_pressed(self):
        return self.mode & self.MODIFIERS_NONE

    @property
    def ctrl_pressed(self):
        return self.mode & self.MODIFIERS_CTRL

    @property
    def shift_pressed(self):
        return self.mode & self.MODIFIERS_SHIFT

    @property
    def alt_pressed(self):
        return self.mode & self.MODIFIERS_ALT

    @property
    def altgr_pressed(self):
        return self.mode & self.MODIFIERS_ALTGR

    @property
    def cmd_pressed(self):
        return self.mode & self.MODIFIERS_CMD


    ## outbound events

    __events__ = ('on_press', 'on_release')

    def on_press(sender, evt):
        """Key press event prototype."""
        pass

    def on_release(sender, evt):
        """Key release event prototype."""
        pass


    ## event rewriting

    def __init__ (self, **kwargs):
        super(Keyboard, self).__init__(**kwargs)
        # keybindings
        from kivy.core.window import Window
        Window.bind(on_key_up=self.on_key_up)
        Window.bind(on_key_down=self.on_key_down)
        Window.bind(on_keyboard=self.on_keyboard)

    def __del__(self):
        from kivy.core.window import Window
        Window.unbind(on_key_up=self.on_key_up)
        Window.unbind(on_key_down=self.on_key_down)
        Window.unbind(on_keyboard=self.on_keyboard)

    def on_key_up(self, wx, key, scancode):
        """Record modifier release."""
        mode = self.keymap.get(key, self.MODIFIERS_NONE)
        self.mode -= self.mode & mode
        self.dispatch('on_release', key)

    def on_key_down(self, wx, key, scancode, char, modifiers):
        """Record modifiers press."""
        mode = self.keymap.get(key, self.MODIFIERS_NONE)
        self.mode |= mode

    def on_keyboard(self, wx, key, scancode, char, modifiers):
        """Forward key presses Handles keybindings. Is called when a key press is detected.

        *key*           : ASCII or ASCII-like value
        *scancode*      : Key code returned by the input provider (e.g. keyboard)
        *char*          : String representation (if A-Z, a-z)
        *modifiers*     : 'ctrl', 'shift', 'alt', or any combination thereof, if pressed

        """
        if False:
            # print key event for debugging
            print(f"""Keybindings: Event
                Key       : {key}
                Scancode  : {scancode}
                Codepoint : {char}
                Modifiers : {modifiers}
                """)

        # forward compact event to widgets
        self.dispatch('on_press', (key, char, modifiers))
        # prevent further event propagation
        return True

## EOF ##