瀏覽代碼

Adds global keyboard events

Christoph Trappe 3 年之前
父節點
當前提交
5934dbf1e0
共有 4 個文件被更改,包括 135 次插入55 次删除
  1. 18 2
      main.py
  2. 41 46
      nicegui/elements/hotkey.js
  3. 6 7
      nicegui/elements/hotkey.py
  4. 70 0
      nicegui/utils.py

+ 18 - 2
main.py

@@ -392,7 +392,23 @@ with example(get_decorator):
     ui.link('Try yet another route!', '/another/route/1')
 
 with example(ui.hotkey):
-    ui.hotkey(keys=['f', 'g'], on_keydown=lambda: ui.notify('Keys F and G were pressed.'))
-    ui.label('Hover over this square and press and release the keys F and G.')
+    def handle_keys(e):
+        if e.key == 'f' and not e.key.repeat:
+            if e.action.keyup:
+                ui.notify('f was just released')
+            elif e.action.keydown:
+                ui.notify('f was just pressed')
+        if e.modifiers.shiftkey and e.action.keydown:
+            if e.key.left:
+                ui.notify('going left')
+            elif e.key.right:
+                ui.notify('going right')
+            elif e.key.up:
+                ui.notify('going up')
+            elif e.key.down:
+                ui.notify('going down')
+
+    hotkeys = ui.hotkey(handle_keys)
+    ui.label('Key events can be caught globally by using the hotkey element.')
 
 ui.run(port=8080)

+ 41 - 46
nicegui/elements/hotkey.js

@@ -1,47 +1,42 @@
-Vue.component("hotkey", {
-  template: `<span v-bind:id="jp_props.id" :class="jp_props.classes" :style="jp_props.style"></span>`,
-  methods: {
-    add_event_listener_to_parent() {
-      const id = this.$props.jp_props.id.toString();
-      const element = document.getElementById(id);
-      const parent = element.parentNode;
-      const keys = this.$props.jp_props.options.keys;
-      const props = this.$props;
-      const map = keys.reduce((ac, a) => ({ ...ac, [a]: false }), {});
-      const send_event = () =>
-        send_to_server(
-          {
-            event_type: "onKeydown",
-            id: props.jp_props.id,
-            page_id: page_id,
-            websocket_id: websocket_id,
-          },
-          "event"
-        );
-      parent.tabIndex = 0;
-      parent.addEventListener("mouseover", function (e) {
-        parent.focus();
-      });
-      $(parent)
-        .keydown(function (e) {
-          if (parent === document.activeElement && e.key in map) {
-            map[e.key] = true;
-            if (Object.values(map).every(Boolean)) {
-              send_event();
-            }
-          }
-        })
-        .keyup(function (e) {
-          if (e.key in map) {
-            map[e.key] = false;
-          }
-        });
-    },
-  },
-  mounted() {
-    this.add_event_listener_to_parent();
-  },
-  props: {
-    jp_props: Object,
-  },
+Vue.component('hotkey', {
+	template: `<span v-bind:id="jp_props.id" :class="jp_props.classes" :style="jp_props.style"></span>`,
+	methods: {
+		add_event_listeners() {
+			const props = this.$props;
+			const activeJSEvents = this.$props.jp_props.options.activeJSEvents;
+			for (let i in activeJSEvents) {
+				const event = activeJSEvents[i];
+				document.addEventListener(event, function (evt) {
+					const e = {
+						event_type: 'keyboardEvent',
+						id: props.jp_props.id,
+						page_id: page_id,
+						websocket_id: websocket_id
+					};
+					if (evt instanceof KeyboardEvent) {
+						// https://developer.mozilla.org/en-US/docs/Web/Events/keydown   keyup, keypress
+						e['key_data'] = {
+							action: event,
+							altKey: evt.altKey,
+							ctrlKey: evt.ctrlKey,
+							shiftKey: evt.shiftKey,
+							metaKey: evt.metaKey,
+							code: evt.code,
+							key: evt.key,
+							location: evt.location,
+							repeat: evt.repeat,
+							locale: evt.locale
+						};
+					}
+					send_to_server(e, 'event', false);
+				});
+			}
+		}
+	},
+	mounted() {
+		this.add_event_listeners();
+	},
+	props: {
+		jp_props: Object
+	}
 });

+ 6 - 7
nicegui/elements/hotkey.py

@@ -7,17 +7,16 @@ from ..utils import handle_exceptions, provide_arguments
 
 class HotkeyView(CustomView):
 
-    def __init__(self, keys: list[str], on_keydown: Callable):
-        super().__init__('hotkey', __file__, keys=keys)
-        self.on_keydown = on_keydown
-        self.allowed_events = ['onKeydown']
+    def __init__(self, handle_keys: Callable):
+        super().__init__('hotkey', __file__, activeJSEvents=['keydown', 'keyup', 'keypress'])
+        self.allowed_events = ['keyboardEvent']
         self.style = 'display: none'
-        self.initialize(temp=False, onKeydown=handle_exceptions(provide_arguments(on_keydown)))
+        self.initialize(temp=False, on_keyboardEvent=handle_exceptions(provide_arguments(handle_keys, 'key_data', 'event_type')))
 
 
 class Hotkey(Element):
 
-    def __init__(self, keys: list[str], on_keydown: Callable = None):
+    def __init__(self, handle_keys: Callable = None):
         """Hotkeys
 
         Adds a hotkey action to an element.
@@ -25,4 +24,4 @@ class Hotkey(Element):
         :param keys: list of characters that the action should be associated with, e.g. ['f', 'g']
         :param on_keydown: callback to be executed when the specified keys are pressed while the parent is hovered
         """
-        super().__init__(HotkeyView(keys=keys, on_keydown=on_keydown))
+        super().__init__(HotkeyView(handle_keys=handle_keys))

+ 70 - 0
nicegui/utils.py

@@ -1,6 +1,7 @@
 import asyncio
 import traceback
 
+
 class EventArguments:
 
     def __init__(self, sender, **kwargs):
@@ -8,6 +9,75 @@ class EventArguments:
         for key, value in kwargs.items():
             setattr(self, key, value)
 
+        if hasattr(self, 'key_data'):
+            self.action = KeyboardAction(self.key_data)
+            self.key = KeyboardKey(self.key_data)
+            self.modifiers = KeyboardModifiers(self.key_data)
+
+
+class KeyboardAction:
+
+    def __init__(self, key_data):
+        self.key_data = key_data
+
+    @property
+    def keydown(self):
+        return hasattr(self.key_data, 'action') and self.key_data.action == 'keydown'
+
+    @property
+    def keyup(self):
+        return hasattr(self.key_data, 'action') and self.key_data.action == 'keyup'
+
+    @property
+    def keypress(self):
+        return hasattr(self.key_data, 'action') and self.key_data.action == 'keypress'
+
+
+class KeyboardKey:
+    def __init__(self, key_data):
+        self.key = getattr(key_data, 'key', False)
+        self.repeat = getattr(key_data, 'repeat', False)
+
+    def __eq__(self, other):
+        return self.key == other
+
+    @property
+    def left(self):
+        return self.key and self.key == 'ArrowLeft'
+
+    @property
+    def right(self):
+        return self.key and self.key == 'ArrowRight'
+
+    @property
+    def up(self):
+        return self.key and self.key == 'ArrowUp'
+
+    @property
+    def down(self):
+        return self.key and self.key == 'ArrowDown'
+
+
+class KeyboardModifiers:
+    def __init__(self, key_data):
+        self.key_data = key_data
+
+    @property
+    def altkey(self):
+        return getattr(self.key_data, 'shiftKey', False)
+
+    @property
+    def ctrlkey(self):
+        return getattr(self.key_data, 'ctrlKey', False)
+
+    @property
+    def shiftkey(self):
+        return getattr(self.key_data, 'shiftKey', False)
+
+    @property
+    def metakey(self):
+        return getattr(self.key_data, 'metaKey', False)
+
 
 def provide_arguments(func, *keys):
     def inner_function(sender, event):