From 77fff929397b514f87e5fb9e211eb8e909d1dbab Mon Sep 17 00:00:00 2001 From: My-Responsitories <107370289+My-Responsitories@users.noreply.github.com> Date: Sun, 1 Feb 2026 00:01:34 +0800 Subject: [PATCH] opt: binary search fontsize (#1818) * opt: permission * opt: opt: binary search fontsize * use transform Signed-off-by: dom * fix Signed-off-by: dom * opt: matrix * opt [skip ci] * tweaks [skip ci] Signed-off-by: dom --------- Co-authored-by: dom --- .../progress_bar/segment_progress_bar.dart | 134 ++++++++++-------- lib/pages/video/controller.dart | 8 +- lib/utils/permission_handler.dart | 56 +++----- 3 files changed, 97 insertions(+), 101 deletions(-) diff --git a/lib/common/widgets/progress_bar/segment_progress_bar.dart b/lib/common/widgets/progress_bar/segment_progress_bar.dart index 76ffa0bb8..aec95d8a5 100644 --- a/lib/common/widgets/progress_bar/segment_progress_bar.dart +++ b/lib/common/widgets/progress_bar/segment_progress_bar.dart @@ -15,6 +15,8 @@ * along with PiliPlus. If not, see . */ +import 'dart:ui' as ui; + import 'package:flutter/foundation.dart' show listEquals, kDebugMode; import 'package:flutter/gestures.dart' show TapGestureRecognizer; import 'package:flutter/material.dart'; @@ -22,21 +24,20 @@ import 'package:flutter/rendering.dart' show BoxHitTestEntry; @immutable sealed class BaseSegment { - final double start; final double end; const BaseSegment({ - required this.start, required this.end, }); } @immutable class Segment extends BaseSegment { + final double start; final Color color; const Segment({ - required super.start, + required this.start, required super.end, required this.color, }); @@ -64,7 +65,6 @@ class ViewPointSegment extends BaseSegment { final int? to; const ViewPointSegment({ - required super.start, required super.end, this.title, this.url, @@ -78,8 +78,7 @@ class ViewPointSegment extends BaseSegment { return true; } if (other is ViewPointSegment) { - return start == other.start && - end == other.end && + return end == other.end && title == other.title && url == other.url && from == other.from && @@ -89,13 +88,13 @@ class ViewPointSegment extends BaseSegment { } @override - int get hashCode => Object.hash(start, end, title, url, from, to); + int get hashCode => Object.hash(end, title, url, from, to); } class SegmentProgressBar extends BaseSegmentProgressBar { const SegmentProgressBar({ super.key, - super.height = 3.5, + super.height, required super.segments, }); @@ -145,7 +144,7 @@ class ViewPointSegmentProgressBar extends BaseSegmentProgressBar { const ViewPointSegmentProgressBar({ super.key, - super.height = 3.5, + super.height, required super.segments, this.onSeek, }); @@ -192,6 +191,31 @@ class RenderViewPointProgressBar } static const double _barHeight = 15.0; + static const double _dividerWidth = 2.0; + + static ui.Paragraph _getParagraph(String title, double size) { + final builder = + ui.ParagraphBuilder( + ui.ParagraphStyle( + textDirection: .ltr, + strutStyle: ui.StrutStyle( + leading: 0, + height: 1, + fontSize: size, + ), + ), + ) + ..pushStyle( + ui.TextStyle( + color: Colors.white, + fontSize: size, + height: 1, + ), + ) + ..addText(title); + return builder.build() + ..layout(const ui.ParagraphConstraints(width: double.infinity)); + } @override void paint(PaintingContext context, Offset offset) { @@ -206,61 +230,47 @@ class RenderViewPointProgressBar paint.color = Colors.black.withValues(alpha: 0.5); - for (int index = 0; index < segments.length; index++) { - final isFirst = index == 0; - final item = segments[index]; - final segmentStart = item.start * size.width; - final segmentEnd = item.end * size.width; + double prevEnd = 0.0; + for (final segment in segments) { + final segmentEnd = segment.end * size.width; + canvas.drawRect( + Rect.fromLTRB( + segmentEnd, + 0, + segmentEnd + _dividerWidth, + _barHeight + height, + ), + paint, + ); + final title = segment.title; + if (title != null && title.isNotEmpty) { + final segmentWidth = segmentEnd - prevEnd; + final paragraph = _getParagraph(title, 10); + final textWidth = paragraph.maxIntrinsicWidth; + final textHeight = paragraph.height; - if (segmentEnd > segmentStart || - (segmentEnd == segmentStart && segmentStart > 0)) { - double fontSize = 10; - - TextPainter getTextPainter() => TextPainter( - text: TextSpan( - text: item.title, - style: TextStyle( - color: Colors.white, - fontSize: fontSize, - height: 1, - ), - ), - strutStyle: StrutStyle(leading: 0, height: 1, fontSize: fontSize), - textDirection: TextDirection.ltr, - )..layout(); - - TextPainter textPainter = getTextPainter(); - - late double prevStart; - if (!isFirst) { - prevStart = segments[index - 1].start * size.width; + final isOverflow = textWidth > segmentWidth; + final Offset offset; + if (isOverflow) { + final scale = segmentWidth / textWidth; + canvas + ..save() + ..translate(prevEnd, (_barHeight - textHeight * scale) / 2) + ..scale(scale); + offset = Offset.zero; + } else { + offset = Offset( + (segmentWidth - textWidth) / 2 + prevEnd, + (_barHeight - textHeight) / 2, + ); } - final width = isFirst ? segmentStart : segmentStart - prevStart; - - while (textPainter.width > width - 2 && fontSize >= 2) { - fontSize -= 0.5; - textPainter.dispose(); - textPainter = getTextPainter(); + canvas.drawParagraph(paragraph, offset); + paragraph.dispose(); + if (isOverflow) { + canvas.restore(); } - - canvas.drawRect( - Rect.fromLTRB( - segmentStart, - 0, - segmentEnd == segmentStart ? segmentStart + 2 : segmentEnd, - _barHeight + height, - ), - paint, - ); - - final textX = isFirst - ? (segmentStart - textPainter.width) / 2 - : (segmentStart - prevStart - textPainter.width) / 2 + - prevStart + - 1; - final textY = (_barHeight - textPainter.height) / 2; - textPainter.paint(canvas, Offset(textX, textY)); } + prevEnd = segmentEnd + _dividerWidth; } } @@ -299,8 +309,8 @@ class RenderViewPointProgressBar try { final seg = details.localPosition.dx / size.width; final item = _segments - .where((item) => item.start >= seg) - .reduce((a, b) => a.start < b.start ? a : b); + .where((item) => item.end >= seg) + .reduce((a, b) => a.end < b.end ? a : b); if (item.from case final from?) { _onSeek?.call(Duration(seconds: from)); } diff --git a/lib/pages/video/controller.dart b/lib/pages/video/controller.dart index f180d6e25..6bdb5c8c8 100644 --- a/lib/pages/video/controller.dart +++ b/lib/pages/video/controller.dart @@ -1585,13 +1585,9 @@ class VideoDetailController extends GetxController response.viewPoints?.firstOrNull?.type == 2) { try { viewPointList.value = response.viewPoints!.map((item) { - double start = (item.to! / (data.timeLength! / 1000)).clamp( - 0.0, - 1.0, - ); + final end = (item.to! / (data.timeLength! / 1000)).clamp(0.0, 1.0); return ViewPointSegment( - start: start, - end: start, + end: end, title: item.content, url: item.imgUrl, from: item.from, diff --git a/lib/utils/permission_handler.dart b/lib/utils/permission_handler.dart index 6a219b9ec..9e373df4e 100644 --- a/lib/utils/permission_handler.dart +++ b/lib/utils/permission_handler.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart'; @@ -24,57 +25,51 @@ Future openAppSettings() => _handler.openAppSettings(); /// Actions that can be executed on a permission. extension PermissionActions on Permission { /// Callback for when permission is denied. - static Future? Function()? _onDenied; + static VoidCallback? _onDenied; /// Callback for when permission is granted. - static Future? Function()? _onGranted; + static VoidCallback? _onGranted; /// Callback for when permission is permanently denied. - static Future? Function()? _onPermanentlyDenied; + static VoidCallback? _onPermanentlyDenied; /// Callback for when permission is restricted. - static Future? Function()? _onRestricted; + static VoidCallback? _onRestricted; /// Callback for when permission is limited. - static Future? Function()? _onLimited; + static VoidCallback? _onLimited; /// Callback for when permission is Provisional. - static Future? Function()? _onProvisional; + static VoidCallback? _onProvisional; /// Method to set a callback for when permission is denied. - Permission onDeniedCallback(Future? Function()? callback) { + void onDeniedCallback(VoidCallback? callback) { _onDenied = callback; - return this; } /// Method to set a callback for when permission is granted. - Permission onGrantedCallback(Future? Function()? callback) { + void onGrantedCallback(VoidCallback? callback) { _onGranted = callback; - return this; } /// Method to set a callback for when permission is permanently denied. - Permission onPermanentlyDeniedCallback(Future? Function()? callback) { + void onPermanentlyDeniedCallback(VoidCallback? callback) { _onPermanentlyDenied = callback; - return this; } /// Method to set a callback for when permission is restricted. - Permission onRestrictedCallback(Future? Function()? callback) { + void onRestrictedCallback(VoidCallback? callback) { _onRestricted = callback; - return this; } /// Method to set a callback for when permission is limited. - Permission onLimitedCallback(Future? Function()? callback) { + void onLimitedCallback(VoidCallback? callback) { _onLimited = callback; - return this; } /// Method to set a callback for when permission is provisional. - Permission onProvisionalCallback(Future? Function()? callback) { + void onProvisionalCallback(VoidCallback? callback) { _onProvisional = callback; - return this; } /// Checks the current status of the given [Permission]. @@ -92,8 +87,8 @@ extension PermissionActions on Permission { /// /// This is only implemented on Android, calling this on iOS always returns /// [false]. - Future get shouldShowRequestRationale async { - if (defaultTargetPlatform != TargetPlatform.android) { + FutureOr get shouldShowRequestRationale { + if (!Platform.isAndroid) { return false; } @@ -108,19 +103,14 @@ extension PermissionActions on Permission { final permissionStatus = (await [this].request())[this] ?? PermissionStatus.denied; - if (permissionStatus.isDenied) { - _onDenied?.call(); - } else if (permissionStatus.isGranted) { - _onGranted?.call(); - } else if (permissionStatus.isPermanentlyDenied) { - _onPermanentlyDenied?.call(); - } else if (permissionStatus.isRestricted) { - _onRestricted?.call(); - } else if (permissionStatus.isLimited) { - _onLimited?.call(); - } else if (permissionStatus.isProvisional) { - _onProvisional?.call(); - } + (switch (permissionStatus) { + .denied => _onDenied, + .granted => _onGranted, + .restricted => _onRestricted, + .limited => _onLimited, + .permanentlyDenied => _onPermanentlyDenied, + .provisional => _onProvisional, + })?.call(); return permissionStatus; }