editor: dynamic padding

This commit is contained in:
Thibault Deckers 2025-02-18 23:50:08 +01:00
parent 8fe267d345
commit 5c297c1daf
2 changed files with 48 additions and 8 deletions

View file

@ -90,7 +90,7 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
void _registerWidget(Cropper widget) {
_subscriptions.add(widget.magnifierController.stateStream.listen(_onViewStateChanged));
_subscriptions.add(widget.magnifierController.scaleBoundariesStream.map((v) => v.viewportSize).listen(_onViewportSizeChanged));
_subscriptions.add(widget.magnifierController.scaleBoundariesStream.map((v) => v.viewportSize).distinct().listen(_onViewportSizeChanged));
_subscriptions.add(widget.transformController.activityStream.listen(_onTransformActivity));
_subscriptions.add(widget.transformController.transformationStream.map((v) => v.orientation).distinct().listen(_onOrientationChanged));
_subscriptions.add(widget.transformController.transformationStream.map((v) => v.straightenDegrees).distinct().listen(_onStraightenDegreesChanged));
@ -454,13 +454,41 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
if (boundaries != null) {
magnifierController.setScaleBoundaries(
boundaries.copyWith(
padding: const EdgeInsets.all(double.infinity),
padding: _getBoundariesPadding,
),
);
}
_showRegion();
}
EdgeInsets _getBoundariesPadding(double scale) {
// TODO TLAD handle orientation
if (transformation.orientation != TransformOrientation.normal) {
return const EdgeInsets.all(double.infinity);
}
// TODO TLAD handle straightening
if (transformation.straightenDegrees != 0) {
return const EdgeInsets.all(double.infinity);
}
final viewState = _getViewState();
if (viewState != null) {
final viewportSize = viewState.viewportSize;
final contentSize = viewState.contentSize;
if (viewportSize != null && contentSize != null) {
final fullRegion = CropRegion.fromRect(Offset.zero & contentSize);
final fullOutline = _containingOutlineFromRegion(viewState, fullRegion);
final cropOutline = _outlineNotifier.value;
final paddingWidth = max(0.0, (min(fullOutline.width, viewportSize.width) - cropOutline.width) / 2);
final paddingHeight = max(0.0, (min(fullOutline.height, viewportSize.height) - cropOutline.height) / 2);
return EdgeInsets.symmetric(vertical: paddingHeight, horizontal: paddingWidth);
}
}
return EdgeInsets.zero;
}
ViewState? _getViewState() {
final scaleBoundaries = magnifierController.scaleBoundaries;
if (scaleBoundaries == null) return null;
@ -560,6 +588,16 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
return clampedRegion;
}
Rect _containingOutlineFromRegion(ViewState viewState, CropRegion region) {
final regionToOutlineMatrix = _getRegionToOutlineMatrix(viewState);
final points = region.corners.map(regionToOutlineMatrix.transformOffset).toList();
final dxSet = points.map((v) => v.dx).toSet();
final dySet = points.map((v) => v.dy).toSet();
final topLeft = Offset(dxSet.reduce(min), dySet.reduce(min));
final bottomRight = Offset(dxSet.reduce(max), dySet.reduce(max));
return Rect.fromPoints(topLeft, bottomRight);
}
Rect _containedOutlineFromRegion(ViewState viewState, CropRegion region) {
final regionToOutlineMatrix = _getRegionToOutlineMatrix(viewState);
final points = region.corners.map(regionToOutlineMatrix.transformOffset).toList();

View file

@ -16,7 +16,7 @@ class ScaleBoundaries extends Equatable {
final ScaleLevel _initialScale;
final Size viewportSize;
final Size contentSize;
final EdgeInsets padding;
final EdgeInsets Function(double scale)? padding;
final Matrix4? externalTransform;
static const Alignment basePosition = Alignment.center;
@ -31,7 +31,7 @@ class ScaleBoundaries extends Equatable {
required ScaleLevel initialScale,
required this.viewportSize,
required this.contentSize,
this.padding = EdgeInsets.zero,
this.padding,
this.externalTransform,
}) : _allowOriginalScaleBeyondRange = allowOriginalScaleBeyondRange,
_minScale = minScale,
@ -45,7 +45,7 @@ class ScaleBoundaries extends Equatable {
initialScale: ScaleLevel(ref: ScaleReference.contained),
viewportSize: Size.zero,
contentSize: Size.zero,
padding: EdgeInsets.zero,
padding: null,
);
ScaleBoundaries copyWith({
@ -55,7 +55,7 @@ class ScaleBoundaries extends Equatable {
ScaleLevel? initialScale,
Size? viewportSize,
Size? contentSize,
EdgeInsets? padding,
EdgeInsets Function(double scale)? padding,
Matrix4? externalTransform,
}) {
return ScaleBoundaries(
@ -115,7 +115,8 @@ class ScaleBoundaries extends Equatable {
final minX = ((positionX - 1).abs() / 2) * widthDiff * -1;
final maxX = ((positionX + 1).abs() / 2) * widthDiff;
return EdgeRange(minX - padding.left, maxX + padding.right);
final _padding = padding?.call(scale) ?? EdgeInsets.zero;
return EdgeRange(minX - _padding.left, maxX + _padding.right);
}
EdgeRange getYEdges({required double scale}) {
@ -127,7 +128,8 @@ class ScaleBoundaries extends Equatable {
final minY = ((positionY - 1).abs() / 2) * heightDiff * -1;
final maxY = ((positionY + 1).abs() / 2) * heightDiff;
return EdgeRange(minY - padding.top, maxY + padding.bottom);
final _padding = padding?.call(scale) ?? EdgeInsets.zero;
return EdgeRange(minY - _padding.top, maxY + _padding.bottom);
}
double clampScale(double scale) {