123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'dart:ui' as ui;
- import 'dart:math' as math;
- /// Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
- ///
- /// THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
- /// AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
- /// UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
- /// This notice may not be removed from this file.
- class CPDFImageSliderThumbShape extends SliderComponentShape {
- final double imageWidth;
- final double imageHeight;
- final ui.Image image;
- CPDFImageSliderThumbShape({
- this.imageWidth = 20,
- this.imageHeight = 20,
- required this.image,
- });
- @override
- Size getPreferredSize(bool isEnabled, bool isDiscrete) {
- return Size(imageWidth, imageHeight);
- }
- @override
- void paint(
- PaintingContext context,
- Offset center, {
- Animation<double>? activationAnimation,
- Animation<double>? enableAnimation,
- bool? isDiscrete,
- TextPainter? labelPainter,
- RenderBox? parentBox,
- SliderThemeData? sliderTheme,
- TextDirection? textDirection,
- double? value,
- double? textScaleFactor,
- Size? sizeWithOverflow,
- }) {
- final canvas = context.canvas;
- final imageOffset =
- Offset(center.dx - imageWidth / 2, center.dy - imageHeight / 2);
- final double targetWidth = imageWidth;
- final double targetHeight = imageHeight;
- final double scaleX = targetWidth / image.width.toDouble();
- final double scaleY = targetHeight / image.height.toDouble();
- final double scale = scaleX < scaleY ? scaleX : scaleY;
- final double destWidth = image.width.toDouble() * scale;
- final double destHeight = image.height.toDouble() * scale;
- final paint = Paint();
- final srcRect =
- Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble());
- final dstRect =
- Rect.fromLTWH(imageOffset.dx, imageOffset.dy, destWidth, destHeight);
- canvas.drawImageRect(image, srcRect, dstRect, paint);
- }
- }
- enum CPDFSliderIndicatorPosition { top, left, right, bottom }
- class CPDFPageSliderValueIndicatorShape extends SliderComponentShape {
- CPDFSliderIndicatorPosition position;
- late _RectangularSliderValueIndicatorPathPainter _pathPainter;
- CPDFPageSliderValueIndicatorShape({required this.position}) {
- _pathPainter =
- _RectangularSliderValueIndicatorPathPainter(position: position);
- }
- @override
- Size getPreferredSize(
- bool isEnabled,
- bool isDiscrete, {
- TextPainter? labelPainter,
- double? textScaleFactor,
- }) {
- assert(labelPainter != null);
- assert(textScaleFactor != null && textScaleFactor >= 0);
- return _pathPainter.getPreferredSize(labelPainter!, textScaleFactor!);
- }
- @override
- void paint(
- PaintingContext context,
- Offset center, {
- required Animation<double> activationAnimation,
- required Animation<double> enableAnimation,
- required bool isDiscrete,
- required TextPainter labelPainter,
- required RenderBox parentBox,
- required SliderThemeData sliderTheme,
- required TextDirection textDirection,
- required double value,
- required double textScaleFactor,
- required Size sizeWithOverflow,
- }) {
- final Canvas canvas = context.canvas;
- final double scale = activationAnimation.value;
- _pathPainter.paint(
- parentBox: parentBox,
- canvas: canvas,
- center: center,
- scale: scale,
- labelPainter: labelPainter,
- textScaleFactor: textScaleFactor,
- sizeWithOverflow: sizeWithOverflow,
- backgroundPaintColor: sliderTheme.valueIndicatorColor!,
- );
- }
- }
- class _RectangularSliderValueIndicatorPathPainter {
- CPDFSliderIndicatorPosition position;
- _RectangularSliderValueIndicatorPathPainter({required this.position});
- static const double _labelPadding = 8.0;
- static const double _minLabelWidth = 32.0;
- static const double _bottomTipYOffset = 24.0;
- static const double _upperRectRadius = 4;
- Size getPreferredSize(
- TextPainter labelPainter,
- double textScaleFactor,
- ) {
- return Size(
- _upperRectangleWidth(labelPainter, 1, textScaleFactor),
- labelPainter.height + _labelPadding,
- );
- }
- double getHorizontalShift({
- required RenderBox parentBox,
- required Offset center,
- required TextPainter labelPainter,
- required double textScaleFactor,
- required Size sizeWithOverflow,
- required double scale,
- }) {
- assert(!sizeWithOverflow.isEmpty);
- const double edgePadding = 16.0;
- final double rectangleWidth =
- _upperRectangleWidth(labelPainter, scale, textScaleFactor);
- /// Value indicator draws on the Overlay and by using the global Offset
- /// we are making sure we use the bounds of the Overlay instead of the Slider.
- final Offset globalCenter = parentBox.localToGlobal(center);
- // The rectangle must be shifted towards the center so that it minimizes the
- // chance of it rendering outside the bounds of the render box. If the shift
- // is negative, then the lobe is shifted from right to left, and if it is
- // positive, then the lobe is shifted from left to right.
- final double overflowLeft =
- math.max(0, rectangleWidth / 2 - globalCenter.dx + edgePadding);
- final double overflowRight = math.max(
- 0,
- rectangleWidth / 2 -
- (sizeWithOverflow.width - globalCenter.dx - edgePadding));
- if (rectangleWidth < sizeWithOverflow.width) {
- return overflowLeft - overflowRight;
- } else if (overflowLeft - overflowRight > 0) {
- return overflowLeft - (edgePadding * textScaleFactor);
- } else {
- return -overflowRight + (edgePadding * textScaleFactor);
- }
- }
- double _upperRectangleWidth(
- TextPainter labelPainter, double scale, double textScaleFactor) {
- final double unscaledWidth =
- math.max(_minLabelWidth * textScaleFactor, labelPainter.width) +
- _labelPadding * 2;
- return unscaledWidth * scale;
- }
- void paint({
- required RenderBox parentBox,
- required Canvas canvas,
- required Offset center,
- required double scale,
- required TextPainter labelPainter,
- required double textScaleFactor,
- required Size sizeWithOverflow,
- required Color backgroundPaintColor,
- Color? strokePaintColor,
- }) {
- if (scale == 0.0) {
- // Zero scale essentially means "do not draw anything", so it's safe to just return.
- return;
- }
- assert(!sizeWithOverflow.isEmpty);
- final double rectangleWidth =
- _upperRectangleWidth(labelPainter, scale, textScaleFactor);
- final double horizontalShift = getHorizontalShift(
- parentBox: parentBox,
- center: center,
- labelPainter: labelPainter,
- textScaleFactor: textScaleFactor,
- sizeWithOverflow: sizeWithOverflow,
- scale: scale,
- );
- final double rectHeight = labelPainter.height + _labelPadding;
- final Rect upperRect = Rect.fromLTWH(
- -rectangleWidth / 2 + horizontalShift,
- -rectHeight,
- rectangleWidth,
- rectHeight,
- );
- final Path trianglePath = Path()..close();
- final Paint fillPaint = Paint()..color = backgroundPaintColor;
- final RRect upperRRect = RRect.fromRectAndRadius(
- upperRect, const Radius.circular(_upperRectRadius));
- trianglePath.addRRect(upperRRect);
- canvas.save();
- // Prepare the canvas for the base of the tooltip, which is relative to the
- // center of the thumb.
- switch (position) {
- case CPDFSliderIndicatorPosition.top:
- canvas.translate(center.dx, center.dy + _bottomTipYOffset * 2);
- canvas.scale(scale, scale);
- break;
- case CPDFSliderIndicatorPosition.bottom:
- canvas.translate(center.dx, center.dy - _bottomTipYOffset);
- canvas.scale(scale, scale);
- break;
- case CPDFSliderIndicatorPosition.left:
- final double bottomPadding =
- parentBox.size.height / 2 + _bottomTipYOffset;
- canvas.translate(
- center.dx + horizontalShift / 2, center.dy - bottomPadding / 2);
- canvas.scale(scale, scale);
- canvas.rotate(-(math.pi / 2));
- break;
- case CPDFSliderIndicatorPosition.right:
- final double bottomPadding =
- parentBox.size.height / 2 + _bottomTipYOffset;
- canvas.translate(
- center.dx - horizontalShift / 2, center.dy + bottomPadding / 2);
- canvas.scale(scale, scale);
- canvas.rotate(-(math.pi / 2));
- break;
- }
- canvas.save();
- if (strokePaintColor != null) {
- final Paint strokePaint = Paint()
- ..color = strokePaintColor
- ..strokeWidth = 1.0
- ..style = PaintingStyle.stroke;
- canvas.drawPath(trianglePath, strokePaint);
- }
- canvas.drawPath(trianglePath, fillPaint);
- // The label text is centered within the value indicator.
- final double bottomTipToUpperRectTranslateY = -upperRect.height;
- canvas.translate(0, bottomTipToUpperRectTranslateY);
- final Offset boxCenter = Offset(horizontalShift, upperRect.height / 2);
- final Offset halfLabelPainterOffset =
- Offset(labelPainter.width / 2, labelPainter.height / 2);
- final Offset labelOffset = boxCenter - halfLabelPainterOffset;
- labelPainter.paint(canvas, labelOffset);
- canvas.restore();
- }
- }
|