Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Editable manages the viewport display #338

Merged
merged 8 commits into from
Jul 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 81 additions & 40 deletions packages/fleather/lib/src/rendering/editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,21 +137,22 @@
with RelayoutWhenSystemFontsChangeMixin
implements RenderAbstractEditor {
RenderEditor({
ViewportOffset? offset,
super.children,
required ParchmentDocument document,
required super.padding,
required super.textDirection,
required ParchmentDocument document,
required ViewportOffset offset,
required bool hasFocus,
required TextSelection selection,
required LayerLink startHandleLayerLink,
required LayerLink endHandleLayerLink,
required super.padding,
required CursorController cursorController,
this.onSelectionChanged,
EdgeInsets floatingCursorAddedMargin =
const EdgeInsets.fromLTRB(4, 4, 4, 5),
double? maxContentWidth,
}) : _document = document,
_offset = offset,
_hasFocus = hasFocus,
_selection = selection,
_startHandleLayerLink = startHandleLayerLink,
Expand Down Expand Up @@ -186,19 +187,26 @@
markNeedsSemanticsUpdate();
}

Offset get paintOffset => Offset(0.0, -(offset?.pixels ?? 0.0));
Offset get paintOffset => Offset(0.0, -offset.pixels);

ViewportOffset? get offset => _offset;
ViewportOffset? _offset;
ViewportOffset get offset => _offset;
ViewportOffset _offset;

set offset(ViewportOffset? value) {
set offset(ViewportOffset value) {
if (_offset == value) return;
if (attached) _offset?.removeListener(markNeedsPaint);
if (attached) _offset.removeListener(markNeedsPaint);

Check warning on line 197 in packages/fleather/lib/src/rendering/editor.dart

View check run for this annotation

Codecov / codecov/patch

packages/fleather/lib/src/rendering/editor.dart#L197

Added line #L197 was not covered by tests
_offset = value;
if (attached) _offset?.addListener(markNeedsPaint);
if (attached) _offset.addListener(markNeedsPaint);

Check warning on line 199 in packages/fleather/lib/src/rendering/editor.dart

View check run for this annotation

Codecov / codecov/patch

packages/fleather/lib/src/rendering/editor.dart#L199

Added line #L199 was not covered by tests
markNeedsLayout();
}

double _maxScrollExtent = 0;

// We need to check the paint offset here because during animation, the start of
// the text may position outside the visible region even when the text fits.
bool get _hasVisualOverflow =>
_maxScrollExtent > 0 || paintOffset != Offset.zero;

Offset? _lastSecondaryTapDownPosition;

Offset? get lastSecondaryTapDownPosition => _lastSecondaryTapDownPosition;
Expand Down Expand Up @@ -331,8 +339,7 @@
/// this editor from above it.
///
/// Returns `null` if the cursor is currently visible.
double? getOffsetToRevealCursor(
double viewportHeight, double scrollOffset, double offsetInViewport) {
double? getOffsetToRevealCursor(double viewportHeight, double scrollOffset) {
const kMargin = 8.0;
// Endpoints coordinates represents lower left or lower right corner of
// the selection. If we want to scroll up to reveal the caret we need to
Expand All @@ -346,10 +353,8 @@
offset: selection.extentOffset - child.node.documentOffset);
final caretTop = endpoints.single.point.dy -
child.preferredLineHeight(childPosition) -
kMargin +
offsetInViewport;
final caretBottom =
endpoints.single.point.dy + kMargin + offsetInViewport;
kMargin;
final caretBottom = endpoints.single.point.dy + kMargin;
final caretHeight = caretBottom - caretTop;
double? dy;

Expand Down Expand Up @@ -572,20 +577,6 @@

@override
void performLayout() {
assert(() {
if (!constraints.hasBoundedHeight) return true;
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary(
'RenderEditableContainerBox must have unlimited space along its main axis.'),
ErrorDescription(
'RenderEditableContainerBox does not clip or resize its children, so it must be '
'placed in a parent that does not constrain the main '
'axis.'),
ErrorHint(
'You probably want to put the RenderEditableContainerBox inside a '
'RenderViewport with a matching main axis.')
]);
}());
assert(() {
if (constraints.hasBoundedWidth) return true;
throw FlutterError.fromParts(<DiagnosticsNode>[
Expand All @@ -601,7 +592,7 @@
resolvePadding();
assert(resolvedPadding != null);

var mainAxisExtent = resolvedPadding!.top;
var contentSize = resolvedPadding!.top;
var child = firstChild;
final innerConstraints = BoxConstraints.tightFor(
width: math.min(
Expand All @@ -614,25 +605,50 @@
child.layout(innerConstraints, parentUsesSize: true);
final childParentData = child.parentData as EditableContainerParentData;
childParentData.offset =
Offset(resolvedPadding!.left + leftOffset, mainAxisExtent);
mainAxisExtent += child.size.height;
Offset(resolvedPadding!.left + leftOffset, contentSize);
contentSize += child.size.height;
assert(child.parentData == childParentData);
child = childParentData.nextSibling;
}
mainAxisExtent += resolvedPadding!.bottom;
size = constraints.constrain(Size(constraints.maxWidth, mainAxisExtent));
contentSize += resolvedPadding!.bottom;
size = constraints
.constrain(Size(_maxContentWidth ?? constraints.maxWidth, contentSize));
_maxScrollExtent = math.max(0.0, contentSize - size.height);
offset.applyViewportDimension(size.height);
offset.applyContentDimensions(0.0, _maxScrollExtent);

assert(size.isFinite);
}

@override
void attach(PipelineOwner owner) {
super.attach(owner);
_offset.addListener(markNeedsPaint);
}

final LayerHandle<ClipRectLayer> _clipRectLayer =
LayerHandle<ClipRectLayer>();

@override
void paint(PaintingContext context, Offset offset) {
if (hasFocus &&
_cursorController.showCursor.value &&
!_cursorController.style.paintAboveText) {
_paintFloatingCursor(context, offset);
}
defaultPaint(context, offset);
if (_hasVisualOverflow) {
_clipRectLayer.layer = context.pushClipRect(
needsCompositing,
offset,
Offset.zero & size,
(context, offset) => defaultPaint(context, offset + paintOffset),
clipBehavior: Clip.hardEdge,
oldLayer: _clipRectLayer.layer,
);
} else {
_clipRectLayer.layer = null;
defaultPaint(context, offset);
}
_updateSelectionExtentsVisibility(offset + paintOffset);
_paintHandleLayers(context, getEndpointsForSelection(selection));

Expand All @@ -650,7 +666,7 @@

void _paintHandleLayers(
PaintingContext context, List<TextSelectionPoint> endpoints) {
var startPoint = endpoints[0].point;
var startPoint = endpoints[0].point + paintOffset;
startPoint = Offset(
startPoint.dx.clamp(0.0, size.width),
startPoint.dy.clamp(0.0, size.height),
Expand All @@ -661,7 +677,7 @@
Offset.zero,
);
if (endpoints.length == 2) {
var endPoint = endpoints[1].point;
var endPoint = endpoints[1].point + paintOffset;
endPoint = Offset(
endPoint.dx.clamp(0.0, size.width),
endPoint.dy.clamp(0.0, size.height),
Expand All @@ -686,17 +702,41 @@
@override
TextPosition getPositionForOffset(Offset offset) {
final local = globalToLocal(offset);
final child = childAtOffset(local);
final child = childAtOffset(local - paintOffset);

final parentData = child.parentData as BoxParentData;
final localOffset = local - parentData.offset;
final localOffset = local - parentData.offset - paintOffset;
final localPosition = child.getPositionForOffset(localOffset);
return TextPosition(
offset: localPosition.offset + child.node.offset,
affinity: localPosition.affinity,
);
}

// Override needed to account for ViewPort-like behaviour of renderer
@override
RenderEditableBox childAtOffset(Offset offset) {
assert(firstChild != null);
resolvePadding();

if (offset.dy <= resolvedPadding!.top) return firstChild!;
if (offset.dy >= size.height + _maxScrollExtent - resolvedPadding!.bottom) {
return lastChild!;
}

var child = firstChild;
var dy = resolvedPadding!.top;
var dx = -offset.dx;
while (child != null) {
if (child.size.contains(offset.translate(dx, -dy))) {
return child;
}
dy += child.size.height;
child = childAfter(child);
}
return lastChild!;

Check warning on line 737 in packages/fleather/lib/src/rendering/editor.dart

View check run for this annotation

Codecov / codecov/patch

packages/fleather/lib/src/rendering/editor.dart#L737

Added line #L737 was not covered by tests
}

@override
Rect getLocalRectForCaret(TextPosition position) {
final targetChild = childAtPosition(position);
Expand All @@ -705,7 +745,8 @@
final childLocalRect = targetChild.getLocalRectForCaret(localPosition);

final boxParentData = targetChild.parentData as BoxParentData;
return childLocalRect.shift(Offset(0, boxParentData.offset.dy));
return childLocalRect
.shift(Offset(0, boxParentData.offset.dy + paintOffset.dy));
}

// Start floating cursor
Expand Down
41 changes: 0 additions & 41 deletions packages/fleather/lib/src/widgets/baseline_proxy.dart

This file was deleted.

Loading
Loading