diff --git a/lib/common/widgets/flutter/text_field/editable.dart b/lib/common/widgets/flutter/text_field/editable.dart index 8cce7e999..4d616e705 100644 --- a/lib/common/widgets/flutter/text_field/editable.dart +++ b/lib/common/widgets/flutter/text_field/editable.dart @@ -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 boxes = textPainter - .getBoxesForSelection( - TextSelection(baseOffset: range.start, extentOffset: range.end), - boxHeightStyle: selectionHeightStyle, - boxWidthStyle: selectionWidthStyle, - ) - .toSet(); - for (final box in boxes) { + final List 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( diff --git a/lib/common/widgets/flutter/text_field/editable_text.dart b/lib/common/widgets/flutter/text_field/editable_text.dart index 53052499f..0350eab49 100644 --- a/lib/common/widgets/flutter/text_field/editable_text.dart +++ b/lib/common/widgets/flutter/text_field/editable_text.dart @@ -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]. diff --git a/lib/pages/dynamics_detail/view.dart b/lib/pages/dynamics_detail/view.dart index e00420cfe..2e9a4ef54 100644 --- a/lib/pages/dynamics_detail/view.dart +++ b/lib/pages/dynamics_detail/view.dart @@ -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, ); diff --git a/lib/scripts/editable_text.patch b/lib/scripts/editable_text.patch index b76817f0d..df852056b 100644 --- a/lib/scripts/editable_text.patch +++ b/lib/scripts/editable_text.patch @@ -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 boxes = textPainter +- .getBoxesForSelection( +- TextSelection(baseOffset: range.start, extentOffset: range.end), +- boxHeightStyle: selectionHeightStyle, +- boxWidthStyle: selectionWidthStyle, +- ) +- .toSet(); + +- for (final box in boxes) { ++ final List 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 void userUpdateTextEditingValue(TextEditingValue value, SelectionChangedCause? cause) { // Compare the current TextEditingValue with the pre-format new diff --git a/lib/scripts/patch.ps1 b/lib/scripts/patch.ps1 index 24f087906..c8faeeab5 100644 --- a/lib/scripts/patch.ps1 +++ b/lib/scripts/patch.ps1 @@ -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" { diff --git a/lib/scripts/selectable_region.patch b/lib/scripts/selectable_region.patch index 14bb40f0e..6950e31ae 100644 --- a/lib/scripts/selectable_region.patch +++ b/lib/scripts/selectable_region.patch @@ -119,7 +119,7 @@ index 09fba7fc4fb..57ee469358e 100644 void computeSemanticsInformation( List 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 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 diff --git a/lib/utils/extension/selectable_region_ext.dart b/lib/utils/extension/selectable_region_ext.dart index e41b24495..67b0b438b 100644 --- a/lib/utils/extension/selectable_region_ext.dart +++ b/lib/utils/extension/selectable_region_ext.dart @@ -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 callback, {