You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
4.0 KiB
131 lines
4.0 KiB
2 months ago
|
import 'package:flutter/material.dart';
|
||
|
import 'package:provider/provider.dart';
|
||
|
import 'package:flutter/services.dart'; // 用于复制到剪贴板
|
||
|
import 'package:win_text_editor/app/providers/logger.dart';
|
||
|
|
||
|
class ConsolePanel extends StatefulWidget {
|
||
|
const ConsolePanel({super.key});
|
||
|
|
||
|
@override
|
||
|
State<ConsolePanel> createState() => _ConsolePanelState();
|
||
|
}
|
||
|
|
||
|
class _ConsolePanelState extends State<ConsolePanel> {
|
||
|
double _height = 100;
|
||
|
final double _minHeight = 50;
|
||
|
final double _maxHeight = 300;
|
||
|
final ScrollController _scrollController = ScrollController();
|
||
|
String? _selectedLog; // 当前选中的日志内容
|
||
|
|
||
|
@override
|
||
|
void dispose() {
|
||
|
_scrollController.dispose();
|
||
|
super.dispose();
|
||
|
}
|
||
|
|
||
|
Color _getLogColor(LogLevel level) {
|
||
|
switch (level) {
|
||
|
case LogLevel.error:
|
||
|
return Colors.red[400]!;
|
||
|
case LogLevel.warning:
|
||
|
return Colors.orange[400]!;
|
||
|
case LogLevel.info:
|
||
|
return Colors.blue[400]!;
|
||
|
case LogLevel.debug:
|
||
|
return Colors.grey;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 显示右键菜单
|
||
|
void _showContextMenu(BuildContext context, Offset position, String logContent) async {
|
||
|
final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
|
||
|
|
||
|
await showMenu(
|
||
|
context: context,
|
||
|
position: RelativeRect.fromRect(
|
||
|
Rect.fromPoints(position, position),
|
||
|
Offset.zero & overlay.size,
|
||
|
),
|
||
|
items: [
|
||
|
PopupMenuItem(
|
||
|
child: const Text('复制'),
|
||
|
onTap: () async {
|
||
|
await Clipboard.setData(ClipboardData(text: logContent));
|
||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||
|
const SnackBar(content: Text('已复制到剪贴板'), duration: Duration(seconds: 1)),
|
||
|
);
|
||
|
},
|
||
|
),
|
||
|
PopupMenuItem(
|
||
|
child: const Text('清除日志'),
|
||
|
onTap: () {
|
||
|
Provider.of<Logger>(context, listen: false).clear();
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
);
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
final logger = Provider.of<Logger>(context);
|
||
|
final consoleHeight = _height.clamp(_minHeight, _maxHeight);
|
||
|
|
||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
|
if (_scrollController.hasClients) {
|
||
|
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return Column(
|
||
|
children: [
|
||
|
// 拖拽手柄
|
||
|
GestureDetector(
|
||
|
behavior: HitTestBehavior.translucent,
|
||
|
onPanUpdate: (details) {
|
||
|
setState(() {
|
||
|
_height = (_height - details.delta.dy).clamp(_minHeight, _maxHeight);
|
||
|
});
|
||
|
},
|
||
|
child: MouseRegion(
|
||
|
cursor: SystemMouseCursors.resizeUpDown,
|
||
|
child: Container(height: 4, color: Colors.grey[300]),
|
||
|
),
|
||
|
),
|
||
|
// 控制台内容区域
|
||
|
SizedBox(
|
||
|
height: consoleHeight,
|
||
|
child: Container(
|
||
|
color: Colors.grey[100],
|
||
|
padding: const EdgeInsets.all(8),
|
||
|
child: ListView.builder(
|
||
|
controller: _scrollController,
|
||
|
itemCount: logger.logs.length,
|
||
|
itemBuilder: (context, index) {
|
||
|
final log = logger.logs[index];
|
||
|
final logContent = log.toString();
|
||
|
return Padding(
|
||
|
padding: const EdgeInsets.symmetric(vertical: 2),
|
||
|
child: GestureDetector(
|
||
|
onSecondaryTapDown: (details) {
|
||
|
_showContextMenu(context, details.globalPosition, logContent);
|
||
|
},
|
||
|
child: MouseRegion(
|
||
|
cursor: SystemMouseCursors.click,
|
||
|
child: SelectableText(
|
||
|
// 改为可选择的文本
|
||
|
logContent,
|
||
|
style: TextStyle(fontFamily: 'monospace', color: _getLogColor(log.level)),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
);
|
||
|
},
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
);
|
||
|
}
|
||
|
}
|