generated from dellevin/template
新增epub书摘,高亮文字,适配平板
This commit is contained in:
150
lib/pages/epub_reader/epub_selection_toolbar.dart
Normal file
150
lib/pages/epub_reader/epub_selection_toolbar.dart
Normal file
@@ -0,0 +1,150 @@
|
||||
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));
|
||||
}
|
||||
Reference in New Issue
Block a user