mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-07-30 23:40:13 +08:00
@@ -54,33 +54,34 @@ void _showEmoteDialog(ModuleDynamicModel? moduleDynamic) {
|
||||
builder: (context) => Dialog(
|
||||
child: Padding(
|
||||
padding: const .symmetric(horizontal: 20, vertical: 16),
|
||||
child: SingleChildScrollView(
|
||||
child: SelectionText.rich(
|
||||
TextSpan(
|
||||
children: emotes!.entries.mapIndexed(
|
||||
(i, e) {
|
||||
final emoji = e.value;
|
||||
final size = emoji.size * 25.0;
|
||||
return TextSpan(
|
||||
children: [
|
||||
if (i != 0) const TextSpan(text: '\n\n'),
|
||||
EmoteSpan(
|
||||
rawText: Style.placeHolder,
|
||||
child: NetworkImgLayer(
|
||||
src: emoji.url,
|
||||
type: .emote,
|
||||
width: size,
|
||||
height: size,
|
||||
child: SelectionArea(
|
||||
contextMenuBuilder: openUrlMenuBuilder,
|
||||
child: SingleChildScrollView(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: emotes!.entries.mapIndexed(
|
||||
(i, e) {
|
||||
final emoji = e.value;
|
||||
final size = emoji.size * 25.0;
|
||||
return TextSpan(
|
||||
children: [
|
||||
if (i != 0) const TextSpan(text: '\n\n'),
|
||||
WidgetSpan(
|
||||
child: NetworkImgLayer(
|
||||
src: emoji.url,
|
||||
type: .emote,
|
||||
width: size,
|
||||
height: size,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextSpan(text: '\n${e.key}\n${emoji.url}'),
|
||||
],
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
TextSpan(text: '\n${e.key}\n${emoji.url}'),
|
||||
],
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
style: const TextStyle(fontSize: 15, height: 1.7),
|
||||
),
|
||||
contextMenuBuilder: openUrlMenuBuilder,
|
||||
style: const TextStyle(fontSize: 15, height: 1.7),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -94,11 +95,13 @@ void _showTextDialog(String text) {
|
||||
builder: (context) => Dialog(
|
||||
child: Padding(
|
||||
padding: const .symmetric(horizontal: 20, vertical: 16),
|
||||
child: SingleChildScrollView(
|
||||
child: SelectionText(
|
||||
text,
|
||||
contextMenuBuilder: openUrlMenuBuilder,
|
||||
style: const TextStyle(fontSize: 15, height: 1.7),
|
||||
child: SelectionArea(
|
||||
contextMenuBuilder: openUrlMenuBuilder,
|
||||
child: SingleChildScrollView(
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(fontSize: 15, height: 1.7),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -12,98 +12,99 @@ void showReplyCopyDialog(
|
||||
constraints: const BoxConstraints.tightFor(width: 380),
|
||||
child: Padding(
|
||||
padding: const .symmetric(horizontal: 20, vertical: 16),
|
||||
child: SingleChildScrollView(
|
||||
child: SelectionText.rich(
|
||||
showEmote
|
||||
? TextSpan(
|
||||
children: emotes.entries.mapIndexed(
|
||||
(i, e) {
|
||||
final emote = e.value;
|
||||
final size = emote.size.toInt() * 25.0;
|
||||
return TextSpan(
|
||||
child: SelectionArea(
|
||||
contextMenuBuilder: (_, state) {
|
||||
final buttonItems = state.contextMenuButtonItems;
|
||||
if (emotes.isNotEmpty) {
|
||||
buttonItems.insertOrAdd(
|
||||
3,
|
||||
ContextMenuButtonItem(
|
||||
label: showEmote ? '文本' : '表情',
|
||||
onPressed: () {
|
||||
state.hideAndClear();
|
||||
showEmote = !showEmote;
|
||||
(context as Element).markNeedsBuild();
|
||||
},
|
||||
),
|
||||
);
|
||||
state.addLaunchMenuIfNeeded(buttonItems, index: 4);
|
||||
}
|
||||
if (state.isUncollapsed) {
|
||||
buttonItems.add(
|
||||
ContextMenuButtonItem(
|
||||
onPressed: () {
|
||||
String text = RegExp.escape(state.selectedText!);
|
||||
if (ReplyGrpc.enableFilter) text = '|$text';
|
||||
|
||||
showConfirmDialog(
|
||||
context: context,
|
||||
title: const Text('是否确认评论过滤的变更:'),
|
||||
content: Text.rich(
|
||||
TextSpan(
|
||||
text: ReplyGrpc.replyRegExp.pattern,
|
||||
children: [
|
||||
if (i != 0) const TextSpan(text: '\n\n'),
|
||||
EmoteSpan(
|
||||
rawText: Style.placeHolder,
|
||||
child: NetworkImgLayer(
|
||||
src: emote.url,
|
||||
type: .emote,
|
||||
width: size,
|
||||
height: size,
|
||||
TextSpan(
|
||||
text: text,
|
||||
style: const TextStyle(
|
||||
color: Colors.green,
|
||||
fontWeight: .bold,
|
||||
),
|
||||
),
|
||||
TextSpan(text: '\n${e.key}\n${emote.url}'),
|
||||
],
|
||||
),
|
||||
),
|
||||
onConfirm: () {
|
||||
final filter = ReplyGrpc.replyRegExp.pattern + text;
|
||||
ReplyGrpc.replyRegExp = RegExp(
|
||||
filter,
|
||||
caseSensitive: true,
|
||||
);
|
||||
ReplyGrpc.enableFilter = true;
|
||||
GStorage.setting.put(
|
||||
SettingBoxKey.banWordForReply,
|
||||
filter,
|
||||
);
|
||||
SmartDialog.showToast('已保存');
|
||||
},
|
||||
).toList(),
|
||||
)
|
||||
: TextSpan(text: message),
|
||||
contextMenuBuilder: (_, state) {
|
||||
final buttonItems = state.contextMenuButtonItems;
|
||||
if (emotes.isNotEmpty) {
|
||||
buttonItems.insertOrAdd(
|
||||
3,
|
||||
ContextMenuButtonItem(
|
||||
label: showEmote ? '文本' : '表情',
|
||||
onPressed: () {
|
||||
state.hideAndClear();
|
||||
showEmote = !showEmote;
|
||||
(context as Element).markNeedsBuild();
|
||||
},
|
||||
),
|
||||
);
|
||||
state.addLaunchMenuIfNeeded(buttonItems, index: 4);
|
||||
}
|
||||
if (state.isUncollapsed) {
|
||||
buttonItems.add(
|
||||
ContextMenuButtonItem(
|
||||
onPressed: () {
|
||||
String text = RegExp.escape(state.selectedText!);
|
||||
if (ReplyGrpc.enableFilter) text = '|$text';
|
||||
|
||||
showConfirmDialog(
|
||||
context: context,
|
||||
title: const Text('是否确认评论过滤的变更:'),
|
||||
content: Text.rich(
|
||||
TextSpan(
|
||||
text: ReplyGrpc.replyRegExp.pattern,
|
||||
);
|
||||
},
|
||||
label: '加入过滤',
|
||||
),
|
||||
);
|
||||
}
|
||||
return AdaptiveTextSelectionToolbar.buttonItems(
|
||||
buttonItems: buttonItems,
|
||||
anchors: state.contextMenuAnchors,
|
||||
);
|
||||
},
|
||||
child: SingleChildScrollView(
|
||||
child: Text.rich(
|
||||
showEmote
|
||||
? TextSpan(
|
||||
children: emotes.entries.mapIndexed(
|
||||
(i, e) {
|
||||
final emote = e.value;
|
||||
final size = emote.size.toInt() * 25.0;
|
||||
return TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: text,
|
||||
style: const TextStyle(
|
||||
color: Colors.green,
|
||||
fontWeight: .bold,
|
||||
if (i != 0) const TextSpan(text: '\n\n'),
|
||||
WidgetSpan(
|
||||
child: NetworkImgLayer(
|
||||
src: emote.url,
|
||||
type: .emote,
|
||||
width: size,
|
||||
height: size,
|
||||
),
|
||||
),
|
||||
TextSpan(text: '\n${e.key}\n${emote.url}'),
|
||||
],
|
||||
),
|
||||
),
|
||||
onConfirm: () {
|
||||
final filter = ReplyGrpc.replyRegExp.pattern + text;
|
||||
ReplyGrpc.replyRegExp = RegExp(
|
||||
filter,
|
||||
caseSensitive: true,
|
||||
);
|
||||
ReplyGrpc.enableFilter = true;
|
||||
GStorage.setting.put(
|
||||
SettingBoxKey.banWordForReply,
|
||||
filter,
|
||||
);
|
||||
SmartDialog.showToast('已保存');
|
||||
},
|
||||
);
|
||||
},
|
||||
label: '加入过滤',
|
||||
),
|
||||
);
|
||||
}
|
||||
return AdaptiveTextSelectionToolbar.buttonItems(
|
||||
buttonItems: buttonItems,
|
||||
anchors: state.contextMenuAnchors,
|
||||
);
|
||||
},
|
||||
style: const TextStyle(fontSize: 15, height: 1.7),
|
||||
).toList(),
|
||||
)
|
||||
: TextSpan(text: message),
|
||||
style: const TextStyle(fontSize: 15, height: 1.7),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
46
lib/common/widgets/tap_region_surface.dart
Normal file
46
lib/common/widgets/tap_region_surface.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter/rendering.dart' show BoxHitTestResult, HitTestEntry;
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class SelectionTapRegionSurface extends TapRegionSurface {
|
||||
const SelectionTapRegionSurface({
|
||||
super.key,
|
||||
required super.child,
|
||||
required this.isScrolling,
|
||||
});
|
||||
|
||||
final ValueGetter<bool> isScrolling;
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
return RenderSelectionTapRegionSurface(isScrolling);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(
|
||||
BuildContext context,
|
||||
RenderSelectionTapRegionSurface renderObject,
|
||||
) {
|
||||
renderObject.isScrolling = isScrolling;
|
||||
}
|
||||
}
|
||||
|
||||
class RenderSelectionTapRegionSurface extends RenderTapRegionSurface {
|
||||
RenderSelectionTapRegionSurface(this.isScrolling);
|
||||
|
||||
ValueGetter<bool> isScrolling;
|
||||
bool _isScrolling = false;
|
||||
|
||||
@override
|
||||
bool hitTest(BoxHitTestResult result, {required Offset position}) {
|
||||
_isScrolling = isScrolling();
|
||||
return super.hitTest(result, position: position);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleEvent(PointerEvent event, HitTestEntry entry) {
|
||||
if (_isScrolling) {
|
||||
return;
|
||||
}
|
||||
super.handleEvent(event, entry);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user