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,
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,
@@ -3154,17 +3154,44 @@ 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(

View File

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

View File

@@ -106,7 +106,7 @@ class _DynamicDetailPageState
);
}
dynamic _scrollable;
ScrollableState? _scrollable;
@override
void dispose() {
@@ -136,6 +136,7 @@ class _DynamicDetailPageState
),
);
return SelectionTapRegionSurface(
/// apply `lib/scripts/scrollable.patch`
isScrolling: () => _scrollable?.shouldIgnorePointer ?? false,
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
index b1ae878f2cc..8a7540350ef 100644
index b1ae878f2cc..7b179c7e3b3 100644
--- a/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>
void userUpdateTextEditingValue(TextEditingValue value, SelectionChangedCause? cause) {
// Compare the current TextEditingValue with the pre-format new

View File

@@ -6,6 +6,7 @@ param(
# https://github.com/flutter/flutter/issues/182281
$NewOverScrollIndicator = "362b1de29974ffc1ed6faa826e1df870d7bec75f";
# set `gestureSettings`
$BottomSheetAndroidPatch = "lib/scripts/bottom_sheet_android.patch"
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1906
@@ -17,9 +18,11 @@ $BottomSheetIOSPiliPlusPatch = "lib/scripts/bottom_sheet_ios_piliplus.patch"
$TextSelectionMenuFix = "beb2ad17004a1b118ff2bd09f55cee23198f6652";
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1662
# handle bottom scroll event
$ScrollViewPatch = "lib/scripts/scroll_view.patch"
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2106
# use `TouchGestureRecognizer` on all platforms
$TextSelectionPatch = "lib/scripts/text_selection.patch"
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1947
@@ -28,28 +31,46 @@ $NavigatorPatch = "lib/scripts/navigator.patch"
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2107
$ImageAnimPatch = "lib/scripts/image_anim.patch"
# remove `_scheduleRebuild`
$LayoutBuilderPatch = "lib/scripts/layout_builder.patch"
# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/2308
$NavigationDrawerPatch = "lib/scripts/navigation_drawer.patch"
# apply text color to icon color
$PopupMenuPatch = "lib/scripts/popup_menu.patch"
# remove `Hero` effect
$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"
# set `selectAllOnFocus` to `false` by default
$TextFieldPatch = "lib/scripts/text_field.patch"
# notify `userScrollDirection` only if position is actually changing
$ScrollPositionPatch = "lib/scripts/scroll_position.patch"
# expose `_shouldIgnorePointer`
$ScrollablePatch = "lib/scripts/scrollable.patch"
# TODO: remove
# https://github.com/flutter/flutter/issues/124078
# 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
# https://github.com/flutter/flutter/issues/90223
@@ -78,9 +99,9 @@ $picks = @($TextSelectionMenuFix)
$reverts = @()
$patches = @($ModalBarrierPatch, $TextSelectionPatch, $MouseCursorPatch,
$ImageAnimPatch, $LayoutBuilderPatch, $NavigationDrawerPatch,
$PopupMenuPatch, $FABPatch, $SelectableRegionPatch, $SelectableRegionSelectionPatch,
$EditableTextPatch, $TextFieldPatch, $ScrollPositionPatch,
$ScrollablePatch)
$PopupMenuPatch, $FABPatch, $NullSafetySelectableRegionPatch,
$SelectableRegionPatch, $EditableTextPatch, $TextFieldPatch,
$ScrollPositionPatch, $ScrollablePatch)
switch ($platform.ToLower()) {
"android" {

View File

@@ -119,7 +119,7 @@ index 09fba7fc4fb..57ee469358e 100644
void computeSemanticsInformation(
List<InlineSpanSemanticsInformation> collector, {
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
+++ b/packages/flutter/lib/src/rendering/paragraph.dart
@@ -7,6 +7,7 @@
@@ -341,18 +341,34 @@ index 97a41dafb8d..15e7ce055e9 100644
_setSelectionPosition(position, isEnd: isEnd);
if (position.offset == range.end) {
return SelectionResult.next;
@@ -3568,7 +3626,10 @@ class _SelectableFragment
@@ -3568,8 +3626,25 @@ class _SelectableFragment
final selectionPaint = Paint()
..style = PaintingStyle.fill
..color = paragraph.selectionColor!;
- 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,
+ boxHeightStyle: ui.BoxHeightStyle.max,
+ )) {
context.canvas.drawRect(textBox.toRect().shift(offset), selectionPaint);
+ boxWidthStyle: ui.BoxWidthStyle.tight,
+ );
+ 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
index a813e141dc4..d045236d031 100644
--- 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/page_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show Selectable;
extension SelectableRegionStateExt on SelectableRegionState {
void addLaunchMenuIfNeeded(
@@ -24,15 +23,11 @@ extension SelectableRegionStateExt on SelectableRegionState {
}
}
String? get selectedText => ((this as dynamic).selectable as Selectable?)
?.getSelectedContent()
?.plainText;
/// apply `lib/scripts/selectable_region.patch`
String? get selectedText => selectable?.getSelectedContent()?.plainText;
bool get isUncollapsed =>
((this as dynamic).selectionDelegate as StaticSelectionContainerDelegate)
.value
.status ==
.uncollapsed;
/// apply `lib/scripts/selectable_region.patch`
bool get isUncollapsed => selectionDelegate.value.status == .uncollapsed;
void onMenuPressed(
ValueChanged<String> callback, {