2023-09-18 19:45:59 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'package:intl/intl.dart';
|
|
|
|
import 'package:ju_rc_app/lib/boxes.dart';
|
|
|
|
import 'package:ju_rc_app/lib/serial.dart';
|
|
|
|
|
|
|
|
class SerialBox extends JuBox {
|
|
|
|
const SerialBox({super.key});
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<StatefulWidget> createState() => _SerialBoxState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _SerialBoxState extends State<SerialBox> {
|
|
|
|
USerial serial = getSerial();
|
|
|
|
final ScrollController _controller = ScrollController();
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
serial.listen(serialListen);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
serial.removeListen(serialListen);
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
void serialListen(String line, SerialCommand? _) {
|
|
|
|
setState(() {
|
|
|
|
//change serial lines
|
|
|
|
_controller.jumpTo(_controller.position.maxScrollExtent);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) => GestureDetector(
|
|
|
|
onTap: () {
|
|
|
|
Navigator.push(
|
|
|
|
context,
|
|
|
|
MaterialPageRoute(builder: (context) => const SerialDetailPage()),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
child: ListView(
|
|
|
|
controller: _controller,
|
|
|
|
shrinkWrap: false,
|
|
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
|
|
children: serial.lines
|
|
|
|
.last(20)
|
|
|
|
.map(
|
|
|
|
(e) => Row(
|
|
|
|
children: [
|
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.all(0.0),
|
|
|
|
child: Icon(e.$1 != SerialMessageType.received
|
|
|
|
? Icons.arrow_left
|
|
|
|
: Icons.arrow_right),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: Text(
|
|
|
|
e.$3,
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
maxLines: 1,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.toList(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
class SerialDetailPage extends StatefulWidget {
|
|
|
|
const SerialDetailPage({super.key});
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<StatefulWidget> createState() => _SerialDetailPageState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _SerialDetailPageState extends State<SerialDetailPage> {
|
|
|
|
final ScrollController _controller = ScrollController();
|
|
|
|
USerial serial = getSerial();
|
|
|
|
bool jump = true;
|
|
|
|
final List<String> _messages = [];
|
|
|
|
int messageIndex = 0;
|
|
|
|
final TextEditingController _textController = TextEditingController();
|
|
|
|
FocusNode fc = FocusNode();
|
|
|
|
FocusNode _textFocus = FocusNode();
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
serial.listen(serialListen);
|
|
|
|
fc.requestFocus();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
serial.removeListen(serialListen);
|
|
|
|
_textController.dispose();
|
|
|
|
_controller.dispose();
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
void serialListen(String line, SerialCommand? _) {
|
|
|
|
if (!jump) return;
|
|
|
|
setState(() {
|
|
|
|
//change serial lines
|
|
|
|
_controller.jumpTo(_controller.position.maxScrollExtent);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void submit() {
|
|
|
|
if (_textController.text.isNotEmpty) {
|
|
|
|
setState(() {
|
|
|
|
serial.sprintln(_textController.text);
|
|
|
|
_messages.add(_textController.text);
|
|
|
|
messageIndex = _messages.length;
|
2023-09-18 20:25:23 +02:00
|
|
|
_textController.text = "";
|
2023-09-18 19:45:59 +02:00
|
|
|
});
|
|
|
|
_textFocus.requestFocus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _handleKeyEvent(RawKeyEvent event) {
|
|
|
|
if (event.runtimeType.toString() == 'RawKeyDownEvent') {
|
|
|
|
if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
|
|
|
|
if (messageIndex > 0) {
|
2023-09-18 20:25:23 +02:00
|
|
|
messageIndex--;
|
|
|
|
Future.delayed(const Duration(microseconds: 10), () {
|
2023-09-18 19:45:59 +02:00
|
|
|
_textController.text = _messages[messageIndex];
|
2023-09-18 20:25:23 +02:00
|
|
|
_textController.selection =
|
|
|
|
TextSelection.collapsed(offset: _textController.text.length);
|
2023-09-18 19:45:59 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
} else if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
|
|
|
|
if (messageIndex >= _messages.length - 1) {
|
|
|
|
setState(() {
|
|
|
|
messageIndex = _messages.length;
|
2023-09-18 20:25:23 +02:00
|
|
|
Future.delayed(const Duration(microseconds: 10), () {
|
|
|
|
_textController.text = "";
|
|
|
|
_textController.selection = const TextSelection.collapsed(offset: 0);
|
|
|
|
});
|
2023-09-18 19:45:59 +02:00
|
|
|
});
|
|
|
|
} else {
|
2023-09-18 20:25:23 +02:00
|
|
|
messageIndex++;
|
|
|
|
Future.delayed(const Duration(microseconds: 10), () {
|
2023-09-18 19:45:59 +02:00
|
|
|
_textController.text = _messages[messageIndex];
|
2023-09-18 20:25:23 +02:00
|
|
|
_textController.selection =
|
|
|
|
TextSelection.collapsed(offset: _textController.text.length);
|
2023-09-18 19:45:59 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Scaffold(
|
|
|
|
appBar: AppBar(
|
|
|
|
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
|
|
|
title: const Text(
|
|
|
|
"Serial Console",
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 16,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
toolbarHeight: 40,
|
|
|
|
),
|
|
|
|
body: Column(
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: ListView(
|
|
|
|
controller: _controller,
|
|
|
|
children: serial.lines.items
|
|
|
|
.map(
|
|
|
|
(e) => Row(
|
|
|
|
children: [
|
|
|
|
Text(DateFormat('HH:mm:ss.S').format(e.$2)),
|
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.all(0.0),
|
|
|
|
child: Icon(e.$1 != SerialMessageType.received
|
|
|
|
? Icons.arrow_left
|
|
|
|
: Icons.arrow_right),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: Text(
|
|
|
|
e.$3,
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
maxLines: 1,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.toList(),
|
|
|
|
)),
|
|
|
|
RawKeyboardListener(
|
|
|
|
focusNode: fc,
|
|
|
|
autofocus: true,
|
|
|
|
onKey: _handleKeyEvent,
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 8.0),
|
|
|
|
child: Row(
|
|
|
|
children: <Widget>[
|
|
|
|
Expanded(
|
|
|
|
child: TextField(
|
|
|
|
focusNode: _textFocus,
|
|
|
|
controller: _textController,
|
|
|
|
autofocus: true,
|
|
|
|
decoration: const InputDecoration(
|
|
|
|
hintText: 'Enter a Command',
|
|
|
|
),
|
|
|
|
onSubmitted: (_) {
|
|
|
|
submit();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
IconButton(
|
|
|
|
icon: const Icon(Icons.send),
|
|
|
|
onPressed: submit,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
))
|
|
|
|
],
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|