Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 83 additions & 8 deletions packages/flet/lib/src/controls/keyboard_listener.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

Expand All @@ -19,6 +20,8 @@ class KeyboardListenerControl extends StatefulWidget {

class _KeyboardListenerControlState extends State<KeyboardListenerControl> {
final FocusNode _focusNode = FocusNode();
bool _escapeDownSent = false;
DateTime? _lastSynthesizedEscapeUpAt;

@override
void initState() {
Expand All @@ -43,6 +46,75 @@ class _KeyboardListenerControlState extends State<KeyboardListenerControl> {
}
}

void _triggerKeyEvent(String eventName, KeyEvent keyEvent) {
if (keyEvent.logicalKey == LogicalKeyboardKey.escape) {
_escapeDownSent = eventName == "key_down";
}
widget.control
.triggerEvent(eventName, {"key": keyEvent.logicalKey.keyLabel});
}

void _triggerEscapeKeyUp() {
if (!_escapeDownSent) {
return;
}
_escapeDownSent = false;
widget.control.triggerEvent("key_up", {"key": "Escape"});
}

KeyEventResult _handleEscapeLoop(KeyEvent keyEvent) {
final isEscape = keyEvent.logicalKey == LogicalKeyboardKey.escape;
final isPhysicalEscape =
isEscape && keyEvent.physicalKey == PhysicalKeyboardKey.escape;
final isPeriodEscape =
isEscape && keyEvent.physicalKey == PhysicalKeyboardKey.period;
final isMetaUp = keyEvent is KeyUpEvent &&
(keyEvent.physicalKey == PhysicalKeyboardKey.metaLeft ||
keyEvent.physicalKey == PhysicalKeyboardKey.metaRight ||
keyEvent.logicalKey == LogicalKeyboardKey.meta ||
keyEvent.logicalKey == LogicalKeyboardKey.metaLeft ||
keyEvent.logicalKey == LogicalKeyboardKey.metaRight);

if (keyEvent is KeyDownEvent &&
isPeriodEscape &&
HardwareKeyboard.instance.isMetaPressed) {
_triggerKeyEvent("key_down", keyEvent);
return KeyEventResult.handled;
}

if (keyEvent is KeyUpEvent && isPeriodEscape) {
_triggerEscapeKeyUp();
return KeyEventResult.handled;
}

if (_escapeDownSent && isMetaUp) {
_triggerEscapeKeyUp();
}

if (keyEvent is KeyUpEvent && isPhysicalEscape) {
_lastSynthesizedEscapeUpAt = DateTime.now();
if (keyEvent.synthesized) {
_triggerEscapeKeyUp();
return KeyEventResult.handled;
}
}

if (keyEvent is KeyDownEvent && isPhysicalEscape) {
final lastUpAt = _lastSynthesizedEscapeUpAt;
if (lastUpAt != null &&
DateTime.now().difference(lastUpAt) <
const Duration(milliseconds: 300)) {
return KeyEventResult.handled;
}
}

if (!isEscape) {
_lastSynthesizedEscapeUpAt = null;
}

return KeyEventResult.ignored;
}

@override
Widget build(BuildContext context) {
debugPrint("KeyboardListener build: ${widget.control.id}");
Expand All @@ -53,23 +125,26 @@ class _KeyboardListenerControlState extends State<KeyboardListenerControl> {
return const ErrorControl("KeyboardListener control has no content.");
}

return KeyboardListener(
return Focus(
focusNode: _focusNode,
autofocus: widget.control.getBool("autofocus", false)!,
includeSemantics: widget.control.getBool("include_semantics", true)!,
onKeyEvent: (keyEvent) {
onKeyEvent: (FocusNode node, KeyEvent keyEvent) {
final result = _handleEscapeLoop(keyEvent);
if (result == KeyEventResult.handled) {
return result;
}
if (keyEvent is KeyDownEvent) {
widget.control
.triggerEvent("key_down", {"key": keyEvent.logicalKey.keyLabel});
_triggerKeyEvent("key_down", keyEvent);
} else if (keyEvent is KeyUpEvent) {
widget.control
.triggerEvent("key_up", {"key": keyEvent.logicalKey.keyLabel});
_triggerKeyEvent("key_up", keyEvent);
} else if (keyEvent is KeyRepeatEvent) {
widget.control.triggerEvent(
"key_repeat", {"key": keyEvent.logicalKey.keyLabel});
_triggerKeyEvent("key_repeat", keyEvent);
}
return KeyEventResult.ignored;
},
child: content,
);
}
}

Loading