opt paint text selection rect

add patch desc

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-07-29 19:39:23 +08:00
parent 557585334a
commit 133f45945c
7 changed files with 155 additions and 32 deletions

View File

@@ -295,7 +295,7 @@ class RenderEditable extends RenderBox
Offset cursorOffset = Offset.zero, Offset cursorOffset = Offset.zero,
double devicePixelRatio = 1.0, double devicePixelRatio = 1.0,
ui.BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.max, ui.BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.max,
ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.max, ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.tight,
bool? enableInteractiveSelection, bool? enableInteractiveSelection,
this.floatingCursorAddedMargin = const EdgeInsets.fromLTRB(4, 4, 4, 5), this.floatingCursorAddedMargin = const EdgeInsets.fromLTRB(4, 4, 4, 5),
TextRange? promptRectRange, TextRange? promptRectRange,
@@ -3154,17 +3154,44 @@ class _TextHighlightPainter extends RenderEditablePainter {
highlightPaint.color = color; highlightPaint.color = color;
final TextPainter textPainter = renderEditable._textPainter; final TextPainter textPainter = renderEditable._textPainter;
final Set<TextBox> boxes = textPainter
.getBoxesForSelection(
TextSelection(baseOffset: range.start, extentOffset: range.end),
boxHeightStyle: selectionHeightStyle,
boxWidthStyle: selectionWidthStyle,
)
.toSet();
for (final box in boxes) { final List<ui.TextBox> boxes = textPainter.getBoxesForSelection(
TextSelection(baseOffset: range.start, extentOffset: range.end),
boxHeightStyle: ui.BoxHeightStyle.max,
boxWidthStyle: ui.BoxWidthStyle.tight,
);
ui.TextBox? mergedBox;
for (final textBox in boxes) {
if (mergedBox == null) {
mergedBox = textBox;
} else if (mergedBox.top != textBox.top) {
canvas.drawRect(
Rect.fromLTRB(
mergedBox.left,
mergedBox.top,
size.width,
mergedBox.bottom,
)
.shift(renderEditable._paintOffset)
.intersect(
Rect.fromLTRB(0, 0, textPainter.width, textPainter.height),
),
highlightPaint,
);
mergedBox = textBox;
} else {
mergedBox = TextBox.fromLTRBD(
mergedBox.left,
mergedBox.top,
textBox.right,
mergedBox.bottom,
mergedBox.direction,
);
}
}
if (mergedBox != null) {
canvas.drawRect( canvas.drawRect(
box mergedBox
.toRect() .toRect()
.shift(renderEditable._paintOffset) .shift(renderEditable._paintOffset)
.intersect( .intersect(

View File

@@ -1717,7 +1717,7 @@ class EditableText extends StatefulWidget {
// } // }
// return ui.BoxWidthStyle.tight; // return ui.BoxWidthStyle.tight;
// } // }
return ui.BoxWidthStyle.max; return ui.BoxWidthStyle.tight;
} }
/// The default value for [stylusHandwritingEnabled]. /// The default value for [stylusHandwritingEnabled].

View File

@@ -106,7 +106,7 @@ class _DynamicDetailPageState
); );
} }
dynamic _scrollable; ScrollableState? _scrollable;
@override @override
void dispose() { void dispose() {
@@ -136,6 +136,7 @@ class _DynamicDetailPageState
), ),
); );
return SelectionTapRegionSurface( return SelectionTapRegionSurface(
/// apply `lib/scripts/scrollable.patch`
isScrolling: () => _scrollable?.shouldIgnorePointer ?? false, isScrolling: () => _scrollable?.shouldIgnorePointer ?? false,
child: child, child: child,
); );

View File

@@ -1,7 +1,70 @@
diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart
index 5d0121e3f37..a42c758b2fb 100644
--- a/packages/flutter/lib/src/rendering/editable.dart
+++ b/packages/flutter/lib/src/rendering/editable.dart
@@ -321,7 +321,7 @@ class RenderEditable extends RenderBox
Offset cursorOffset = Offset.zero,
double devicePixelRatio = 1.0,
ui.BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.max,
- ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.max,
+ ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.tight,
bool? enableInteractiveSelection,
this.floatingCursorAddedMargin = const EdgeInsets.fromLTRB(4, 4, 4, 5),
TextRange? promptRectRange,
@@ -2912,17 +2912,31 @@ class _TextHighlightPainter extends RenderEditablePainter {
highlightPaint.color = color;
final TextPainter textPainter = renderEditable._textPainter;
- final Set<TextBox> boxes = textPainter
- .getBoxesForSelection(
- TextSelection(baseOffset: range.start, extentOffset: range.end),
- boxHeightStyle: selectionHeightStyle,
- boxWidthStyle: selectionWidthStyle,
- )
- .toSet();
- for (final box in boxes) {
+ final List<ui.TextBox> boxes = textPainter.getBoxesForSelection(
+ TextSelection(baseOffset: range.start, extentOffset: range.end),
+ boxHeightStyle: ui.BoxHeightStyle.max,
+ boxWidthStyle: ui.BoxWidthStyle.tight,
+ );
+ ui.TextBox? mergedBox;
+ for (final textBox in boxes) {
+ if (mergedBox == null) {
+ mergedBox = textBox;
+ } else if (mergedBox.top != textBox.top) {
+ canvas.drawRect(
+ Rect.fromLTRB(mergedBox.left, mergedBox.top, size.width, mergedBox.bottom)
+ .shift(renderEditable._paintOffset)
+ .intersect(Rect.fromLTRB(0, 0, textPainter.width, textPainter.height)),
+ highlightPaint,
+ );
+ mergedBox = textBox;
+ } else {
+ mergedBox = TextBox.fromLTRBD(mergedBox.left, mergedBox.top, textBox.right, mergedBox.bottom, mergedBox.direction);
+ }
+ }
+ if (mergedBox != null) {
canvas.drawRect(
- box
+ mergedBox
.toRect()
.shift(renderEditable._paintOffset)
.intersect(Rect.fromLTWH(0, 0, textPainter.width, textPainter.height)),
diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart
index b1ae878f2cc..8a7540350ef 100644 index b1ae878f2cc..7b179c7e3b3 100644
--- a/packages/flutter/lib/src/widgets/editable_text.dart --- a/packages/flutter/lib/src/widgets/editable_text.dart
+++ b/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart
@@ -2099,7 +2099,7 @@ class EditableText extends StatefulWidget {
}
return ui.BoxWidthStyle.tight;
}
- return ui.BoxWidthStyle.max;
+ return ui.BoxWidthStyle.tight;
}
/// The default value for [stylusHandwritingEnabled].
@@ -5046,7 +5046,9 @@ class EditableTextState extends State<EditableText> @@ -5046,7 +5046,9 @@ class EditableTextState extends State<EditableText>
void userUpdateTextEditingValue(TextEditingValue value, SelectionChangedCause? cause) { void userUpdateTextEditingValue(TextEditingValue value, SelectionChangedCause? cause) {
// Compare the current TextEditingValue with the pre-format new // Compare the current TextEditingValue with the pre-format new

View File

@@ -6,6 +6,7 @@ param(
# https://github.com/flutter/flutter/issues/182281 # https://github.com/flutter/flutter/issues/182281
$NewOverScrollIndicator = "362b1de29974ffc1ed6faa826e1df870d7bec75f"; $NewOverScrollIndicator = "362b1de29974ffc1ed6faa826e1df870d7bec75f";
# set `gestureSettings`
$BottomSheetAndroidPatch = "lib/scripts/bottom_sheet_android.patch" $BottomSheetAndroidPatch = "lib/scripts/bottom_sheet_android.patch"
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1906 # https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1906
@@ -17,9 +18,11 @@ $BottomSheetIOSPiliPlusPatch = "lib/scripts/bottom_sheet_ios_piliplus.patch"
$TextSelectionMenuFix = "beb2ad17004a1b118ff2bd09f55cee23198f6652"; $TextSelectionMenuFix = "beb2ad17004a1b118ff2bd09f55cee23198f6652";
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1662 # https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1662
# handle bottom scroll event
$ScrollViewPatch = "lib/scripts/scroll_view.patch" $ScrollViewPatch = "lib/scripts/scroll_view.patch"
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2106 # https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2106
# use `TouchGestureRecognizer` on all platforms
$TextSelectionPatch = "lib/scripts/text_selection.patch" $TextSelectionPatch = "lib/scripts/text_selection.patch"
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1947 # https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1947
@@ -28,28 +31,46 @@ $NavigatorPatch = "lib/scripts/navigator.patch"
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2107 # https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2107
$ImageAnimPatch = "lib/scripts/image_anim.patch" $ImageAnimPatch = "lib/scripts/image_anim.patch"
# remove `_scheduleRebuild`
$LayoutBuilderPatch = "lib/scripts/layout_builder.patch" $LayoutBuilderPatch = "lib/scripts/layout_builder.patch"
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2308 # https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2308
$NavigationDrawerPatch = "lib/scripts/navigation_drawer.patch" $NavigationDrawerPatch = "lib/scripts/navigation_drawer.patch"
# apply text color to icon color
$PopupMenuPatch = "lib/scripts/popup_menu.patch" $PopupMenuPatch = "lib/scripts/popup_menu.patch"
# remove `Hero` effect
$FABPatch = "lib/scripts/fab.patch" $FABPatch = "lib/scripts/fab.patch"
$SelectableRegionSelectionPatch = "lib/scripts/selectable_region.patch" # https://github.com/flutter/flutter/issues/139890
# https://github.com/flutter/flutter/issues/174689
# separator support
# clamp handle offset
# widgetspan selection support
# clear selection when tapping outside
# free selection if there is only one text
# clamp dragging selection behavior on Android
# show selection menu if secondary tap position is in text region on desktop
$SelectableRegionPatch = "lib/scripts/selectable_region.patch"
# https://github.com/flutter/flutter/issues/132047
# https://github.com/flutter/flutter/issues/174689
$EditableTextPatch = "lib/scripts/editable_text.patch" $EditableTextPatch = "lib/scripts/editable_text.patch"
# set `selectAllOnFocus` to `false` by default
$TextFieldPatch = "lib/scripts/text_field.patch" $TextFieldPatch = "lib/scripts/text_field.patch"
# notify `userScrollDirection` only if position is actually changing
$ScrollPositionPatch = "lib/scripts/scroll_position.patch" $ScrollPositionPatch = "lib/scripts/scroll_position.patch"
# expose `_shouldIgnorePointer`
$ScrollablePatch = "lib/scripts/scrollable.patch" $ScrollablePatch = "lib/scripts/scrollable.patch"
# TODO: remove # TODO: remove
# https://github.com/flutter/flutter/issues/124078
# https://github.com/flutter/flutter/pull/183261 # https://github.com/flutter/flutter/pull/183261
$SelectableRegionPatch = "lib/scripts/null_safety_for_selectable_region.patch" $NullSafetySelectableRegionPatch = "lib/scripts/null_safety_for_selectable_region.patch"
# TODO: remove # TODO: remove
# https://github.com/flutter/flutter/issues/90223 # https://github.com/flutter/flutter/issues/90223
@@ -78,9 +99,9 @@ $picks = @($TextSelectionMenuFix)
$reverts = @() $reverts = @()
$patches = @($ModalBarrierPatch, $TextSelectionPatch, $MouseCursorPatch, $patches = @($ModalBarrierPatch, $TextSelectionPatch, $MouseCursorPatch,
$ImageAnimPatch, $LayoutBuilderPatch, $NavigationDrawerPatch, $ImageAnimPatch, $LayoutBuilderPatch, $NavigationDrawerPatch,
$PopupMenuPatch, $FABPatch, $SelectableRegionPatch, $SelectableRegionSelectionPatch, $PopupMenuPatch, $FABPatch, $NullSafetySelectableRegionPatch,
$EditableTextPatch, $TextFieldPatch, $ScrollPositionPatch, $SelectableRegionPatch, $EditableTextPatch, $TextFieldPatch,
$ScrollablePatch) $ScrollPositionPatch, $ScrollablePatch)
switch ($platform.ToLower()) { switch ($platform.ToLower()) {
"android" { "android" {

View File

@@ -119,7 +119,7 @@ index 09fba7fc4fb..57ee469358e 100644
void computeSemanticsInformation( void computeSemanticsInformation(
List<InlineSpanSemanticsInformation> collector, { List<InlineSpanSemanticsInformation> collector, {
diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart
index 97a41dafb8d..15e7ce055e9 100644 index 97a41dafb8d..1dc2f36538d 100644
--- a/packages/flutter/lib/src/rendering/paragraph.dart --- a/packages/flutter/lib/src/rendering/paragraph.dart
+++ b/packages/flutter/lib/src/rendering/paragraph.dart +++ b/packages/flutter/lib/src/rendering/paragraph.dart
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
@@ -341,18 +341,34 @@ index 97a41dafb8d..15e7ce055e9 100644
_setSelectionPosition(position, isEnd: isEnd); _setSelectionPosition(position, isEnd: isEnd);
if (position.offset == range.end) { if (position.offset == range.end) {
return SelectionResult.next; return SelectionResult.next;
@@ -3568,7 +3626,10 @@ class _SelectableFragment @@ -3568,8 +3626,25 @@ class _SelectableFragment
final selectionPaint = Paint() final selectionPaint = Paint()
..style = PaintingStyle.fill ..style = PaintingStyle.fill
..color = paragraph.selectionColor!; ..color = paragraph.selectionColor!;
- for (final TextBox textBox in paragraph.getBoxesForSelection(selection)) { - for (final TextBox textBox in paragraph.getBoxesForSelection(selection)) {
+ for (final TextBox textBox in paragraph.getBoxesForSelection( - context.canvas.drawRect(textBox.toRect().shift(offset), selectionPaint);
+
+ final List<ui.TextBox> boxes = paragraph.getBoxesForSelection(
+ selection, + selection,
+ boxHeightStyle: ui.BoxHeightStyle.max, + boxHeightStyle: ui.BoxHeightStyle.max,
+ )) { + boxWidthStyle: ui.BoxWidthStyle.tight,
context.canvas.drawRect(textBox.toRect().shift(offset), selectionPaint); + );
+ ui.TextBox? mergedBox;
+ for (final textBox in boxes) {
+ if (mergedBox == null) {
+ mergedBox = textBox;
+ } else if (mergedBox.top != textBox.top) {
+ context.canvas.drawRect(Rect.fromLTRB(mergedBox.left, mergedBox.top, paragraph.size.width, mergedBox.bottom).shift(offset), selectionPaint);
+ mergedBox = textBox;
+ } else {
+ mergedBox = TextBox.fromLTRBD(mergedBox.left, mergedBox.top, textBox.right, mergedBox.bottom, mergedBox.direction);
+ }
+ }
+ if (mergedBox != null) {
+ context.canvas.drawRect(mergedBox.toRect().shift(offset), selectionPaint);
} }
} }
if (_startHandleLayerLink != null && value.startSelectionPoint != null) {
diff --git a/packages/flutter/lib/src/rendering/selection.dart b/packages/flutter/lib/src/rendering/selection.dart diff --git a/packages/flutter/lib/src/rendering/selection.dart b/packages/flutter/lib/src/rendering/selection.dart
index a813e141dc4..d045236d031 100644 index a813e141dc4..d045236d031 100644
--- a/packages/flutter/lib/src/rendering/selection.dart --- a/packages/flutter/lib/src/rendering/selection.dart

View File

@@ -1,7 +1,6 @@
import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:PiliPlus/utils/extension/iterable_ext.dart';
import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show Selectable;
extension SelectableRegionStateExt on SelectableRegionState { extension SelectableRegionStateExt on SelectableRegionState {
void addLaunchMenuIfNeeded( void addLaunchMenuIfNeeded(
@@ -24,15 +23,11 @@ extension SelectableRegionStateExt on SelectableRegionState {
} }
} }
String? get selectedText => ((this as dynamic).selectable as Selectable?) /// apply `lib/scripts/selectable_region.patch`
?.getSelectedContent() String? get selectedText => selectable?.getSelectedContent()?.plainText;
?.plainText;
bool get isUncollapsed => /// apply `lib/scripts/selectable_region.patch`
((this as dynamic).selectionDelegate as StaticSelectionContainerDelegate) bool get isUncollapsed => selectionDelegate.value.status == .uncollapsed;
.value
.status ==
.uncollapsed;
void onMenuPressed( void onMenuPressed(
ValueChanged<String> callback, { ValueChanged<String> callback, {