generated from dellevin/template
151 lines
4.2 KiB
Dart
151 lines
4.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
/// 文本选中后弹出的工具条
|
|
class EpubSelectionToolbar extends StatelessWidget {
|
|
/// 选中文字的屏幕坐标区域
|
|
final Rect selectionRect;
|
|
|
|
/// 点击复制按钮的回调
|
|
final VoidCallback onCopy;
|
|
|
|
/// 点击高亮按钮的回调
|
|
final VoidCallback onHighlight;
|
|
|
|
/// 点击摘抄到笔记的回调
|
|
final VoidCallback onExcerpt;
|
|
|
|
/// 点击外部区域关闭
|
|
final VoidCallback onDismiss;
|
|
|
|
const EpubSelectionToolbar({
|
|
super.key,
|
|
required this.selectionRect,
|
|
required this.onCopy,
|
|
required this.onHighlight,
|
|
required this.onExcerpt,
|
|
required this.onDismiss,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
final colors = theme.colorScheme;
|
|
|
|
// 计算工具条位置:优先在选区上方,空间不足时放下方
|
|
final mediaQuery = MediaQuery.of(context);
|
|
final selectionTop = selectionRect.top;
|
|
final selectionBottom = selectionRect.bottom;
|
|
const toolbarHeight = 44.0;
|
|
const margin = 8.0;
|
|
final showAbove = selectionTop > toolbarHeight + margin + 80;
|
|
final top = showAbove
|
|
? selectionTop - toolbarHeight - margin
|
|
: selectionBottom + margin;
|
|
|
|
// 水平居中于选区,但不超出屏幕
|
|
const toolbarWidth = 220.0;
|
|
double left = selectionRect.center.dx - toolbarWidth / 2;
|
|
left = left.clamp(12.0, mediaQuery.size.width - toolbarWidth - 12.0);
|
|
|
|
return Stack(
|
|
children: [
|
|
// 全屏遮罩,点击关闭
|
|
Positioned.fill(
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: onDismiss,
|
|
child: const SizedBox.expand(),
|
|
),
|
|
),
|
|
Positioned(
|
|
left: left,
|
|
top: top < 0 ? margin : top,
|
|
child: Material(
|
|
elevation: 6,
|
|
borderRadius: BorderRadius.circular(10),
|
|
color: colors.surface,
|
|
surfaceTintColor: colors.surfaceTint,
|
|
child: SizedBox(
|
|
height: toolbarHeight,
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
_buildButton(
|
|
context,
|
|
icon: Icons.highlight_outlined,
|
|
label: '高亮',
|
|
onTap: onHighlight,
|
|
color: colors.primary,
|
|
),
|
|
_divider(colors),
|
|
_buildButton(
|
|
context,
|
|
icon: Icons.copy_outlined,
|
|
label: '复制',
|
|
onTap: onCopy,
|
|
color: colors.primary,
|
|
),
|
|
_divider(colors),
|
|
_buildButton(
|
|
context,
|
|
icon: Icons.edit_note_outlined,
|
|
label: '摘抄',
|
|
onTap: onExcerpt,
|
|
color: colors.primary,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _divider(ColorScheme colors) {
|
|
return Container(
|
|
width: 0.5,
|
|
height: 22,
|
|
color: colors.outlineVariant,
|
|
);
|
|
}
|
|
|
|
Widget _buildButton(
|
|
BuildContext context, {
|
|
required IconData icon,
|
|
required String label,
|
|
required VoidCallback onTap,
|
|
required Color color,
|
|
}) {
|
|
final theme = Theme.of(context);
|
|
final colors = theme.colorScheme;
|
|
return InkWell(
|
|
onTap: onTap,
|
|
borderRadius: BorderRadius.circular(8),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 14),
|
|
child: Row(
|
|
children: [
|
|
Icon(icon, size: 18, color: color),
|
|
const SizedBox(width: 6),
|
|
Text(
|
|
label,
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: colors.onSurface,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 复制文字到剪贴板的辅助函数
|
|
Future<void> copyTextToClipboard(String text) async {
|
|
await Clipboard.setData(ClipboardData(text: text));
|
|
}
|