diff --git a/lib/common/widgets/custom_height_widget.dart b/lib/common/widgets/custom_height_widget.dart index e4b961028..f94655209 100644 --- a/lib/common/widgets/custom_height_widget.dart +++ b/lib/common/widgets/custom_height_widget.dart @@ -65,7 +65,4 @@ class RenderCustomHeightWidget extends RenderProxyBox { void paint(PaintingContext context, Offset offset) { context.paintChild(child!, offset + _offset); } - - @override - bool get isRepaintBoundary => true; } diff --git a/lib/common/widgets/custom_sliver_persistent_header_delegate.dart b/lib/common/widgets/custom_sliver_persistent_header_delegate.dart index 777aea679..bfc354861 100644 --- a/lib/common/widgets/custom_sliver_persistent_header_delegate.dart +++ b/lib/common/widgets/custom_sliver_persistent_header_delegate.dart @@ -95,7 +95,4 @@ class _RenderDecoratedBox extends RenderProxyBox { @override bool hitTestSelf(Offset position) => true; - - @override - bool get isRepaintBoundary => true; } diff --git a/lib/common/widgets/custom_tooltip.dart b/lib/common/widgets/custom_tooltip.dart index 1cb5b04fd..c62bb3291 100644 --- a/lib/common/widgets/custom_tooltip.dart +++ b/lib/common/widgets/custom_tooltip.dart @@ -259,9 +259,6 @@ class _RenderToolTip extends RenderBox child = childParentData.nextSibling; } } - - @override - bool get isRepaintBoundary => true; } class Triangle extends LeafRenderObjectWidget { @@ -328,14 +325,11 @@ class RenderTriangle extends RenderBox { ..style = PaintingStyle.fill; final path = Path() - ..moveTo(0, 0) - ..lineTo(size.width, 0) - ..lineTo(size.width / 2, size.height) + ..moveTo(offset.dx, offset.dy) + ..lineTo(offset.dx + size.width, offset.dy) + ..lineTo(offset.dx + size.width / 2, size.height + offset.dy) ..close(); context.canvas.drawPath(path, paint); } - - @override - bool get isRepaintBoundary => true; } diff --git a/lib/common/widgets/disabled_icon.dart b/lib/common/widgets/disabled_icon.dart index fa1554756..fc67b1ca4 100644 --- a/lib/common/widgets/disabled_icon.dart +++ b/lib/common/widgets/disabled_icon.dart @@ -121,6 +121,7 @@ class RenderMaskedIcon extends RenderProxyBox { final canvas = context.canvas; + var rectOffset = offset; Size size = this.size; final exceedWidth = size.width > _iconSize; final exceedHeight = size.height > _iconSize; @@ -128,14 +129,14 @@ class RenderMaskedIcon extends RenderProxyBox { final dx = exceedWidth ? (size.width - _iconSize) / 2.0 : 0.0; final dy = exceedHeight ? (size.height - _iconSize) / 2.0 : 0.0; size = Size.square(_iconSize); - offset = Offset(dx, dy); + rectOffset += Offset(dx, dy); } else if (size.width < _iconSize && size.height < _iconSize) { size = Size.square(_iconSize); } final strokeWidth = size.width / 12; - var rect = offset & size; + var rect = rectOffset & size; final sqrt2Width = strokeWidth * sqrt2; // rotate pi / 4 @@ -155,7 +156,7 @@ class RenderMaskedIcon extends RenderProxyBox { canvas ..save() ..clipPath(path, doAntiAlias: false); - super.paint(context, .zero); + super.paint(context, offset); canvas.restore(); @@ -174,7 +175,4 @@ class RenderMaskedIcon extends RenderProxyBox { linePaint, ); } - - @override - bool get isRepaintBoundary => true; } diff --git a/lib/common/widgets/image/custom_grid_view.dart b/lib/common/widgets/image/custom_grid_view.dart index e76115911..3099bd194 100644 --- a/lib/common/widgets/image/custom_grid_view.dart +++ b/lib/common/widgets/image/custom_grid_view.dart @@ -440,5 +440,5 @@ class RenderImageGrid extends RenderBox } @override - bool get isRepaintBoundary => true; + bool get isRepaintBoundary => true; // gif repaint } diff --git a/lib/common/widgets/marquee.dart b/lib/common/widgets/marquee.dart index de49577df..31d9b7002 100644 --- a/lib/common/widgets/marquee.dart +++ b/lib/common/widgets/marquee.dart @@ -226,13 +226,14 @@ abstract class MarqueeRender extends RenderBox if (_distance > 0) { updateSize(); _ticker.initIfNeeded(_onTick); + markNeedsCompositingBitsUpdate(); } else { _ticker.cancel(); } } @override - bool get isRepaintBoundary => true; + bool get isRepaintBoundary => _ticker._ticker != null; void paintCenter(PaintingContext context, Offset offset) { if (_direction == Axis.horizontal) { diff --git a/lib/common/widgets/progress_bar/segment_progress_bar.dart b/lib/common/widgets/progress_bar/segment_progress_bar.dart index 875822dc4..123040714 100644 --- a/lib/common/widgets/progress_bar/segment_progress_bar.dart +++ b/lib/common/widgets/progress_bar/segment_progress_bar.dart @@ -123,17 +123,17 @@ class RenderProgressBar extends BaseRenderProgressBar { for (final segment in segments) { paint.color = segment.color; - final segmentStart = segment.start * size.width; - final segmentEnd = segment.end * size.width; + final segmentStart = offset.dx + segment.start * size.width; + final segmentEnd = offset.dx + segment.end * size.width; if (segmentEnd > segmentStart || (segmentEnd == segmentStart && segmentStart > 0)) { canvas.drawRect( Rect.fromLTRB( segmentStart, - 0, + offset.dy, segmentEnd == segmentStart ? segmentStart + 2 : segmentEnd, - size.height, + size.height + offset.dy, ), paint, ); @@ -225,6 +225,12 @@ class RenderViewPointProgressBar final canvas = context.canvas; final paint = Paint()..style = PaintingStyle.fill; + if (offset != .zero) { + canvas + ..save() + ..translate(offset.dx, offset.dy); + } + assert(segments.isSortedBy((i) => i.end)); canvas.drawRect( @@ -276,6 +282,7 @@ class RenderViewPointProgressBar } prevEnd = segmentEnd + _dividerWidth; } + if (offset != .zero) canvas.restore(); } ValueSetter? _onSeek; @@ -371,7 +378,4 @@ class BaseRenderProgressBar extends RenderBox { void performLayout() { size = constraints.constrainDimensions(constraints.maxWidth, height); } - - @override - bool get isRepaintBoundary => true; } diff --git a/lib/common/widgets/progress_bar/video_progress_indicator.dart b/lib/common/widgets/progress_bar/video_progress_indicator.dart index 7fe68cb4c..6bd3d4995 100644 --- a/lib/common/widgets/progress_bar/video_progress_indicator.dart +++ b/lib/common/widgets/progress_bar/video_progress_indicator.dart @@ -119,7 +119,9 @@ class RenderProgressBar extends RenderBox { @override void paint(PaintingContext context, Offset offset) { final size = this.size; - final canvas = context.canvas; + final canvas = context.canvas + ..save() + ..translate(offset.dx, offset.dy); final paint = Paint()..style = .fill; canvas.clipRect( @@ -147,8 +149,6 @@ class RenderProgressBar extends RenderBox { ..drawRect(left, paint..color = _color) ..drawRect(right, paint..color = _backgroundColor); } + canvas.restore(); } - - @override - bool get isRepaintBoundary => true; } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index fab6efbbb..8ae12d984 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -997,12 +997,15 @@ class _RenderBorderIndicator extends RenderBox { final size = this.size; final canvas = context.canvas; final width = size.width / 2; - if (!_isLeft) { - canvas.translate(width, 0); - } + BoxBorder.paintNonUniformBorder( canvas, - Rect.fromLTRB(0, 0, width, size.height), + Rect.fromLTWH( + offset.dx + (_isLeft ? 0 : width), + offset.dy, + width, + size.height, + ), borderRadius: BorderRadius.only( topLeft: _isLeft ? _radius : .zero, topRight: _isLeft ? .zero : _radius, @@ -1012,9 +1015,6 @@ class _RenderBorderIndicator extends RenderBox { color: Colors.white38, ); } - - @override - bool get isRepaintBoundary => true; } class LiveDanmaku extends StatefulWidget { diff --git a/lib/pages/video/introduction/ugc/widgets/action_item.dart b/lib/pages/video/introduction/ugc/widgets/action_item.dart index ddafe4955..d22481272 100644 --- a/lib/pages/video/introduction/ugc/widgets/action_item.dart +++ b/lib/pages/video/introduction/ugc/widgets/action_item.dart @@ -66,22 +66,25 @@ class ActionItem extends StatelessWidget { child = SizedBox.square(dimension: 28, child: child); } - child = InkWell( - borderRadius: const BorderRadius.all(Radius.circular(6)), - onTap: _isThumbsUp ? null : onTap, - onLongPress: _isThumbsUp ? null : onLongPress, - onSecondaryTap: PlatformUtils.isMobile || _isThumbsUp - ? null - : onLongPress, - onTapDown: _isThumbsUp ? (_) => onStartTriple!() : null, - onTapUp: _isThumbsUp ? (_) => onCancelTriple!(true) : null, - onTapCancel: _isThumbsUp ? onCancelTriple : null, - child: expand - ? Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [child, _buildText(theme)], - ) - : child, + child = Material( + type: .transparency, + child: InkWell( + borderRadius: const BorderRadius.all(Radius.circular(6)), + onTap: _isThumbsUp ? null : onTap, + onLongPress: _isThumbsUp ? null : onLongPress, + onSecondaryTap: PlatformUtils.isMobile || _isThumbsUp + ? null + : onLongPress, + onTapDown: _isThumbsUp ? (_) => onStartTriple!() : null, + onTapUp: _isThumbsUp ? (_) => onCancelTriple!(true) : null, + onTapCancel: _isThumbsUp ? onCancelTriple : null, + child: expand + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [child, _buildText(theme)], + ) + : child, + ), ); return expand ? Expanded(child: child) : child; } diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 62e83a91e..fe60bbbe4 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -66,7 +66,8 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart' show RenderProxyBox; +import 'package:flutter/rendering.dart' + show RenderProxyBox, SemanticsConfiguration; import 'package:flutter/services.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -379,38 +380,15 @@ class _PLVideoPlayerState extends State ), /// 时间进度 - BottomControlType.time => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - // 播放时间 - Obx( - () => Text( - DurationUtils.formatDuration( - plPlayerController.positionSeconds.value, - ), - style: const TextStyle( - color: Colors.white, - fontSize: 10, - height: 1.4, - fontFeatures: [FontFeature.tabularFigures()], - ), - ), + BottomControlType.time => Obx( + () => _VideoTime( + position: DurationUtils.formatDuration( + plPlayerController.positionSeconds.value, ), - Obx( - () => Text( - DurationUtils.formatDuration( - plPlayerController.duration.value.inSeconds, - ), - style: const TextStyle( - color: Color(0xFFD0D0D0), - fontSize: 10, - height: 1.4, - fontFeatures: [FontFeature.tabularFigures()], - ), - ), + duration: DurationUtils.formatDuration( + plPlayerController.duration.value.inSeconds, ), - ], + ), ), /// 高能进度条 @@ -1617,39 +1595,41 @@ class _PLVideoPlayerState extends State top: -1, bottom: -1, child: ClipRect( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - AppBarAni( - isTop: true, - controller: animationController, - isFullScreen: isFullScreen, - child: plPlayerController.isDesktopPip - ? GestureDetector( - behavior: HitTestBehavior.translucent, - onPanStart: (_) => windowManager.startDragging(), - child: widget.headerControl, - ) - : widget.headerControl, - ), - AppBarAni( - isTop: false, - controller: animationController, - isFullScreen: isFullScreen, - child: - widget.bottomControl ?? - BottomControl( - maxWidth: maxWidth, - isFullScreen: isFullScreen, - controller: plPlayerController, - videoDetailController: videoDetailController, - buildBottomControl: () => buildBottomControl( - videoDetailController, - maxWidth > maxHeight, + child: RepaintBoundary( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AppBarAni( + isTop: true, + controller: animationController, + isFullScreen: isFullScreen, + child: plPlayerController.isDesktopPip + ? GestureDetector( + behavior: HitTestBehavior.translucent, + onPanStart: (_) => windowManager.startDragging(), + child: widget.headerControl, + ) + : widget.headerControl, + ), + AppBarAni( + isTop: false, + controller: animationController, + isFullScreen: isFullScreen, + child: + widget.bottomControl ?? + BottomControl( + maxWidth: maxWidth, + isFullScreen: isFullScreen, + controller: plPlayerController, + videoDetailController: videoDetailController, + buildBottomControl: () => buildBottomControl( + videoDetailController, + maxWidth > maxHeight, + ), ), - ), - ), - ], + ), + ], + ), ), ), ), @@ -2767,12 +2747,7 @@ class _RenderDanmakuTip extends RenderProxyBox { void paint(PaintingContext context, Offset offset) { final paint = Paint() ..color = const Color(0xB3000000) - ..style = PaintingStyle.fill; - - final strokePaint = Paint() - ..color = const Color(0x7EFFFFFF) - ..style = PaintingStyle.stroke - ..strokeWidth = 1.25; + ..style = .fill; final radius = size.height / 2; const triangleBase = _triangleHeight * 2 / 3; @@ -2803,11 +2778,140 @@ class _RenderDanmakuTip extends RenderProxyBox { ..close(); context.canvas + ..save() + ..translate(offset.dx, offset.dy) ..drawPath(path, paint) - ..drawPath(path, strokePaint); + ..drawPath( + path, + paint + ..color = const Color(0x7EFFFFFF) + ..style = PaintingStyle.stroke + ..strokeWidth = 1.25, + ) + ..restore(); super.paint(context, offset); } +} + +class _VideoTime extends LeafRenderObjectWidget { + const _VideoTime({ + required this.position, + required this.duration, + }); + + final String position; + final String duration; + + @override + _RenderVideoTime createRenderObject(BuildContext context) => _RenderVideoTime( + position: position, + duration: duration, + ); + + @override + void updateRenderObject( + BuildContext context, + covariant _RenderVideoTime renderObject, + ) { + renderObject + ..position = position + ..duration = duration; + } +} + +class _RenderVideoTime extends RenderBox { + _RenderVideoTime({ + required String position, + required String duration, + }) : _position = position, + _duration = duration; + + String _duration; + set duration(String value) { + _duration = value; + final paragraph = _buildParagraph(const Color(0xFFD0D0D0), _duration); + if (paragraph.maxIntrinsicWidth != _cache?.maxIntrinsicWidth) { + markNeedsLayout(); + } + _cache?.dispose(); + _cache = paragraph; + markNeedsSemanticsUpdate(); + } + + String _position; + set position(String value) { + _position = value; + markNeedsPaint(); + markNeedsSemanticsUpdate(); + } + + ui.Paragraph? _cache; + + ui.Paragraph _buildParagraph(Color color, String time) { + final builder = + ui.ParagraphBuilder( + ui.ParagraphStyle( + fontSize: 10, + height: 1.4, + fontFamily: 'Monospace', + ), + ) + ..pushStyle( + ui.TextStyle( + color: color, + fontSize: 10, + height: 1.4, + fontFamily: 'Monospace', + fontFeatures: const [FontFeature.tabularFigures()], + ), + ) + ..addText(time); + return builder.build() + ..layout(ui.ParagraphConstraints(width: constraints.maxWidth)); + } + + @override + ui.Size computeDryLayout(covariant BoxConstraints constraints) { + final paragraph = _cache ??= _buildParagraph( + const Color(0xFFD0D0D0), + _duration, + ); + return Size(paragraph.maxIntrinsicWidth, paragraph.height * 2); + } + + @override + void describeSemanticsConfiguration(SemanticsConfiguration config) { + super.describeSemanticsConfiguration(config); + config.label = 'position:$_position\nduration:$_duration'; + } + + @override + void performLayout() { + size = computeDryLayout(constraints); + } + + @override + void paint(PaintingContext context, ui.Offset offset) { + final para = _buildParagraph(Colors.white, _position); + context.canvas + ..drawParagraph( + para, + Offset( + offset.dx + _cache!.maxIntrinsicWidth - para.maxIntrinsicWidth, + offset.dy, + ), + ) + ..drawParagraph(_cache!, Offset(offset.dx, offset.dy + para.height)); + para.dispose(); + } + + @override + void dispose() { + _cache?.dispose(); + _cache = null; + super.dispose(); + } @override bool get isRepaintBoundary => true; diff --git a/lib/plugin/pl_player/widgets/mpv_convert_webp.dart b/lib/plugin/pl_player/widgets/mpv_convert_webp.dart index 003751b15..2210b3352 100644 --- a/lib/plugin/pl_player/widgets/mpv_convert_webp.dart +++ b/lib/plugin/pl_player/widgets/mpv_convert_webp.dart @@ -105,7 +105,7 @@ class MpvConvertWebp { final text = log.text.toDartString().trim(); debugPrint('WebpConvert: $level $prefix : $text'); if (kDebugMode) { - _success = level != 'error' && level != 'fatal'; + if (level == 'error' || level == 'fatal') _success = false; } else { _success = false; }