mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-31 08:08:19 +08:00
opt: binary search fontsize (#1818)
* opt: permission * opt: opt: binary search fontsize * use transform Signed-off-by: dom <githubaccount56556@proton.me> * fix Signed-off-by: dom <githubaccount56556@proton.me> * opt: matrix * opt [skip ci] * tweaks [skip ci] Signed-off-by: dom <githubaccount56556@proton.me> --------- Co-authored-by: dom <githubaccount56556@proton.me>
This commit is contained in:
committed by
GitHub
parent
8964197b73
commit
77fff92939
@@ -15,6 +15,8 @@
|
|||||||
* along with PiliPlus. If not, see <https://www.gnu.org/licenses/>.
|
* along with PiliPlus. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart' show listEquals, kDebugMode;
|
import 'package:flutter/foundation.dart' show listEquals, kDebugMode;
|
||||||
import 'package:flutter/gestures.dart' show TapGestureRecognizer;
|
import 'package:flutter/gestures.dart' show TapGestureRecognizer;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -22,21 +24,20 @@ import 'package:flutter/rendering.dart' show BoxHitTestEntry;
|
|||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
sealed class BaseSegment {
|
sealed class BaseSegment {
|
||||||
final double start;
|
|
||||||
final double end;
|
final double end;
|
||||||
|
|
||||||
const BaseSegment({
|
const BaseSegment({
|
||||||
required this.start,
|
|
||||||
required this.end,
|
required this.end,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class Segment extends BaseSegment {
|
class Segment extends BaseSegment {
|
||||||
|
final double start;
|
||||||
final Color color;
|
final Color color;
|
||||||
|
|
||||||
const Segment({
|
const Segment({
|
||||||
required super.start,
|
required this.start,
|
||||||
required super.end,
|
required super.end,
|
||||||
required this.color,
|
required this.color,
|
||||||
});
|
});
|
||||||
@@ -64,7 +65,6 @@ class ViewPointSegment extends BaseSegment {
|
|||||||
final int? to;
|
final int? to;
|
||||||
|
|
||||||
const ViewPointSegment({
|
const ViewPointSegment({
|
||||||
required super.start,
|
|
||||||
required super.end,
|
required super.end,
|
||||||
this.title,
|
this.title,
|
||||||
this.url,
|
this.url,
|
||||||
@@ -78,8 +78,7 @@ class ViewPointSegment extends BaseSegment {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (other is ViewPointSegment) {
|
if (other is ViewPointSegment) {
|
||||||
return start == other.start &&
|
return end == other.end &&
|
||||||
end == other.end &&
|
|
||||||
title == other.title &&
|
title == other.title &&
|
||||||
url == other.url &&
|
url == other.url &&
|
||||||
from == other.from &&
|
from == other.from &&
|
||||||
@@ -89,13 +88,13 @@ class ViewPointSegment extends BaseSegment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@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<Segment> {
|
class SegmentProgressBar extends BaseSegmentProgressBar<Segment> {
|
||||||
const SegmentProgressBar({
|
const SegmentProgressBar({
|
||||||
super.key,
|
super.key,
|
||||||
super.height = 3.5,
|
super.height,
|
||||||
required super.segments,
|
required super.segments,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -145,7 +144,7 @@ class ViewPointSegmentProgressBar
|
|||||||
extends BaseSegmentProgressBar<ViewPointSegment> {
|
extends BaseSegmentProgressBar<ViewPointSegment> {
|
||||||
const ViewPointSegmentProgressBar({
|
const ViewPointSegmentProgressBar({
|
||||||
super.key,
|
super.key,
|
||||||
super.height = 3.5,
|
super.height,
|
||||||
required super.segments,
|
required super.segments,
|
||||||
this.onSeek,
|
this.onSeek,
|
||||||
});
|
});
|
||||||
@@ -192,6 +191,31 @@ class RenderViewPointProgressBar
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const double _barHeight = 15.0;
|
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
|
@override
|
||||||
void paint(PaintingContext context, Offset offset) {
|
void paint(PaintingContext context, Offset offset) {
|
||||||
@@ -206,61 +230,47 @@ class RenderViewPointProgressBar
|
|||||||
|
|
||||||
paint.color = Colors.black.withValues(alpha: 0.5);
|
paint.color = Colors.black.withValues(alpha: 0.5);
|
||||||
|
|
||||||
for (int index = 0; index < segments.length; index++) {
|
double prevEnd = 0.0;
|
||||||
final isFirst = index == 0;
|
for (final segment in segments) {
|
||||||
final item = segments[index];
|
final segmentEnd = segment.end * size.width;
|
||||||
final segmentStart = item.start * size.width;
|
canvas.drawRect(
|
||||||
final segmentEnd = item.end * size.width;
|
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 ||
|
final isOverflow = textWidth > segmentWidth;
|
||||||
(segmentEnd == segmentStart && segmentStart > 0)) {
|
final Offset offset;
|
||||||
double fontSize = 10;
|
if (isOverflow) {
|
||||||
|
final scale = segmentWidth / textWidth;
|
||||||
TextPainter getTextPainter() => TextPainter(
|
canvas
|
||||||
text: TextSpan(
|
..save()
|
||||||
text: item.title,
|
..translate(prevEnd, (_barHeight - textHeight * scale) / 2)
|
||||||
style: TextStyle(
|
..scale(scale);
|
||||||
color: Colors.white,
|
offset = Offset.zero;
|
||||||
fontSize: fontSize,
|
} else {
|
||||||
height: 1,
|
offset = Offset(
|
||||||
),
|
(segmentWidth - textWidth) / 2 + prevEnd,
|
||||||
),
|
(_barHeight - textHeight) / 2,
|
||||||
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 width = isFirst ? segmentStart : segmentStart - prevStart;
|
canvas.drawParagraph(paragraph, offset);
|
||||||
|
paragraph.dispose();
|
||||||
while (textPainter.width > width - 2 && fontSize >= 2) {
|
if (isOverflow) {
|
||||||
fontSize -= 0.5;
|
canvas.restore();
|
||||||
textPainter.dispose();
|
|
||||||
textPainter = getTextPainter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
try {
|
||||||
final seg = details.localPosition.dx / size.width;
|
final seg = details.localPosition.dx / size.width;
|
||||||
final item = _segments
|
final item = _segments
|
||||||
.where((item) => item.start >= seg)
|
.where((item) => item.end >= seg)
|
||||||
.reduce((a, b) => a.start < b.start ? a : b);
|
.reduce((a, b) => a.end < b.end ? a : b);
|
||||||
if (item.from case final from?) {
|
if (item.from case final from?) {
|
||||||
_onSeek?.call(Duration(seconds: from));
|
_onSeek?.call(Duration(seconds: from));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1585,13 +1585,9 @@ class VideoDetailController extends GetxController
|
|||||||
response.viewPoints?.firstOrNull?.type == 2) {
|
response.viewPoints?.firstOrNull?.type == 2) {
|
||||||
try {
|
try {
|
||||||
viewPointList.value = response.viewPoints!.map((item) {
|
viewPointList.value = response.viewPoints!.map((item) {
|
||||||
double start = (item.to! / (data.timeLength! / 1000)).clamp(
|
final end = (item.to! / (data.timeLength! / 1000)).clamp(0.0, 1.0);
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
);
|
|
||||||
return ViewPointSegment(
|
return ViewPointSegment(
|
||||||
start: start,
|
end: end,
|
||||||
end: start,
|
|
||||||
title: item.content,
|
title: item.content,
|
||||||
url: item.imgUrl,
|
url: item.imgUrl,
|
||||||
from: item.from,
|
from: item.from,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart';
|
import 'package:permission_handler_platform_interface/permission_handler_platform_interface.dart';
|
||||||
@@ -24,57 +25,51 @@ Future<bool> openAppSettings() => _handler.openAppSettings();
|
|||||||
/// Actions that can be executed on a permission.
|
/// Actions that can be executed on a permission.
|
||||||
extension PermissionActions on Permission {
|
extension PermissionActions on Permission {
|
||||||
/// Callback for when permission is denied.
|
/// Callback for when permission is denied.
|
||||||
static Future<void>? Function()? _onDenied;
|
static VoidCallback? _onDenied;
|
||||||
|
|
||||||
/// Callback for when permission is granted.
|
/// Callback for when permission is granted.
|
||||||
static Future<void>? Function()? _onGranted;
|
static VoidCallback? _onGranted;
|
||||||
|
|
||||||
/// Callback for when permission is permanently denied.
|
/// Callback for when permission is permanently denied.
|
||||||
static Future<void>? Function()? _onPermanentlyDenied;
|
static VoidCallback? _onPermanentlyDenied;
|
||||||
|
|
||||||
/// Callback for when permission is restricted.
|
/// Callback for when permission is restricted.
|
||||||
static Future<void>? Function()? _onRestricted;
|
static VoidCallback? _onRestricted;
|
||||||
|
|
||||||
/// Callback for when permission is limited.
|
/// Callback for when permission is limited.
|
||||||
static Future<void>? Function()? _onLimited;
|
static VoidCallback? _onLimited;
|
||||||
|
|
||||||
/// Callback for when permission is Provisional.
|
/// Callback for when permission is Provisional.
|
||||||
static Future<void>? Function()? _onProvisional;
|
static VoidCallback? _onProvisional;
|
||||||
|
|
||||||
/// Method to set a callback for when permission is denied.
|
/// Method to set a callback for when permission is denied.
|
||||||
Permission onDeniedCallback(Future<void>? Function()? callback) {
|
void onDeniedCallback(VoidCallback? callback) {
|
||||||
_onDenied = callback;
|
_onDenied = callback;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Method to set a callback for when permission is granted.
|
/// Method to set a callback for when permission is granted.
|
||||||
Permission onGrantedCallback(Future<void>? Function()? callback) {
|
void onGrantedCallback(VoidCallback? callback) {
|
||||||
_onGranted = callback;
|
_onGranted = callback;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Method to set a callback for when permission is permanently denied.
|
/// Method to set a callback for when permission is permanently denied.
|
||||||
Permission onPermanentlyDeniedCallback(Future<void>? Function()? callback) {
|
void onPermanentlyDeniedCallback(VoidCallback? callback) {
|
||||||
_onPermanentlyDenied = callback;
|
_onPermanentlyDenied = callback;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Method to set a callback for when permission is restricted.
|
/// Method to set a callback for when permission is restricted.
|
||||||
Permission onRestrictedCallback(Future<void>? Function()? callback) {
|
void onRestrictedCallback(VoidCallback? callback) {
|
||||||
_onRestricted = callback;
|
_onRestricted = callback;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Method to set a callback for when permission is limited.
|
/// Method to set a callback for when permission is limited.
|
||||||
Permission onLimitedCallback(Future<void>? Function()? callback) {
|
void onLimitedCallback(VoidCallback? callback) {
|
||||||
_onLimited = callback;
|
_onLimited = callback;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Method to set a callback for when permission is provisional.
|
/// Method to set a callback for when permission is provisional.
|
||||||
Permission onProvisionalCallback(Future<void>? Function()? callback) {
|
void onProvisionalCallback(VoidCallback? callback) {
|
||||||
_onProvisional = callback;
|
_onProvisional = callback;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the current status of the given [Permission].
|
/// 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
|
/// This is only implemented on Android, calling this on iOS always returns
|
||||||
/// [false].
|
/// [false].
|
||||||
Future<bool> get shouldShowRequestRationale async {
|
FutureOr<bool> get shouldShowRequestRationale {
|
||||||
if (defaultTargetPlatform != TargetPlatform.android) {
|
if (!Platform.isAndroid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,19 +103,14 @@ extension PermissionActions on Permission {
|
|||||||
final permissionStatus =
|
final permissionStatus =
|
||||||
(await [this].request())[this] ?? PermissionStatus.denied;
|
(await [this].request())[this] ?? PermissionStatus.denied;
|
||||||
|
|
||||||
if (permissionStatus.isDenied) {
|
(switch (permissionStatus) {
|
||||||
_onDenied?.call();
|
.denied => _onDenied,
|
||||||
} else if (permissionStatus.isGranted) {
|
.granted => _onGranted,
|
||||||
_onGranted?.call();
|
.restricted => _onRestricted,
|
||||||
} else if (permissionStatus.isPermanentlyDenied) {
|
.limited => _onLimited,
|
||||||
_onPermanentlyDenied?.call();
|
.permanentlyDenied => _onPermanentlyDenied,
|
||||||
} else if (permissionStatus.isRestricted) {
|
.provisional => _onProvisional,
|
||||||
_onRestricted?.call();
|
})?.call();
|
||||||
} else if (permissionStatus.isLimited) {
|
|
||||||
_onLimited?.call();
|
|
||||||
} else if (permissionStatus.isProvisional) {
|
|
||||||
_onProvisional?.call();
|
|
||||||
}
|
|
||||||
|
|
||||||
return permissionStatus;
|
return permissionStatus;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user