opt: image view

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2024-12-15 11:44:44 +08:00
parent fee1ad56f7
commit 52f888167f
11 changed files with 120 additions and 57 deletions

View File

@@ -79,6 +79,8 @@ class InteractiveViewer extends StatefulWidget {
this.onPanStart,
this.onPanUpdate,
this.onPanEnd,
this.onReset,
this.isAnimating,
required Widget this.child,
}) : assert(minScale > 0),
assert(interactionEndFrictionCoefficient > 0),
@@ -127,6 +129,8 @@ class InteractiveViewer extends StatefulWidget {
this.onPanStart,
this.onPanUpdate,
this.onPanEnd,
this.onReset,
this.isAnimating,
required InteractiveViewerWidgetBuilder this.builder,
}) : assert(minScale > 0),
assert(interactionEndFrictionCoefficient > 0),
@@ -147,6 +151,8 @@ class InteractiveViewer extends StatefulWidget {
constrained = false,
child = null;
final Function? isAnimating;
final VoidCallback? onReset;
final ValueChanged<ScaleStartDetails>? onPanStart;
final ValueChanged<ScaleUpdateDetails>? onPanUpdate;
final ValueChanged<ScaleEndDetails>? onPanEnd;
@@ -755,8 +761,9 @@ class _InteractiveViewerState extends State<InteractiveViewer>
// Handle the start of a gesture. All of pan, scale, and rotate are handled
// with GestureDetector's scale gesture.
void _onScaleStart(ScaleStartDetails details) {
if (details.pointerCount < 2 &&
_transformationController?.value.row0.x == 1.0) {
if (widget.isAnimating?.call() == true ||
(details.pointerCount < 2 &&
_transformationController?.value.row0.x == 1.0)) {
widget.onPanStart?.call(details);
return;
}
@@ -788,8 +795,9 @@ class _InteractiveViewerState extends State<InteractiveViewer>
// Handle an update to an ongoing gesture. All of pan, scale, and rotate are
// handled with GestureDetector's scale gesture.
void _onScaleUpdate(ScaleUpdateDetails details) {
if (details.pointerCount < 2 &&
_transformationController?.value.row0.x == 1.0) {
if (widget.isAnimating?.call() == true ||
(details.pointerCount < 2 &&
_transformationController?.value.row0.x == 1.0)) {
widget.onPanUpdate?.call(details);
return;
}
@@ -892,8 +900,12 @@ class _InteractiveViewerState extends State<InteractiveViewer>
// Handle the end of a gesture of _GestureType. All of pan, scale, and rotate
// are handled with GestureDetector's scale gesture.
void _onScaleEnd(ScaleEndDetails details) {
if (details.pointerCount < 2 &&
_transformationController?.value.row0.x == 1.0) {
if (_transformationController?.value.row0.x == 1.0) {
widget.onReset?.call();
}
if (widget.isAnimating?.call() == true ||
(details.pointerCount < 2 &&
_transformationController?.value.row0.x == 1.0)) {
widget.onPanEnd?.call(details);
return;
}

View File

@@ -25,9 +25,11 @@ class InteractiveViewerBoundary extends StatefulWidget {
required this.maxScale,
required this.minScale,
this.onDismissed,
this.onReset,
this.dismissThreshold = 0.2,
});
final VoidCallback? onReset;
final double dismissThreshold;
final VoidCallback? onDismissed;
@@ -230,6 +232,8 @@ class InteractiveViewerBoundaryState extends State<InteractiveViewerBoundary>
onPanStart: _handleDragStart,
onPanUpdate: _handleDragUpdate,
onPanEnd: _handleDragEnd,
onReset: widget.onReset,
isAnimating: () => _animateController.value != 0,
child: content,
);
}

View File

@@ -6,7 +6,6 @@ import 'package:PiliPalaX/utils/utils.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
@@ -224,6 +223,13 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
Get.back();
widget.onDismissed?.call(_pageController!.page!.floor());
},
onReset: () {
if (!_enablePageView) {
setState(() {
_enablePageView = true;
});
}
},
child: PageView.builder(
onPageChanged: _onPageChanged,
controller: _pageController,

View File

@@ -67,7 +67,7 @@ class NetworkImgLayer extends StatelessWidget {
child: Builder(
builder: (context) => CachedNetworkImage(
imageUrl:
'${src?.startsWith('//') == true ? 'https:$src' : src?.http2https}${thumbnail ? '@${quality ?? defaultImgQuality}q.webp' : ''}',
'${src?.startsWith('//') == true ? 'https:$src' : src?.http2https}${type != 'emote' && thumbnail ? '@${quality ?? defaultImgQuality}q.webp' : ''}',
width: width,
height: ignoreHeight == null || ignoreHeight == false
? height

View File

@@ -384,7 +384,7 @@ class UserHttp {
'pn': pn,
'platform': 'web',
});
if (res.data['code'] == 0) {
if (res.data['code'] == 0 && res.data['data'] is Map) {
return {
'status': true,
'data': SubFolderModelData.fromJson(res.data['data'])

View File

@@ -11,6 +11,8 @@ class SetSwitchItem extends StatefulWidget {
final bool? needReboot;
final Widget? leading;
final GestureTapCallback? onTap;
final EdgeInsetsGeometry? contentPadding;
final TextStyle? titleStyle;
const SetSwitchItem({
this.title,
@@ -21,6 +23,8 @@ class SetSwitchItem extends StatefulWidget {
this.needReboot,
this.leading,
this.onTap,
this.contentPadding,
this.titleStyle,
super.key,
});
@@ -45,7 +49,7 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
// Utils.checkUpdate();
// }
widget.onChanged?.call(val);
if (widget.needReboot != null && widget.needReboot!) {
if (widget.needReboot == true) {
SmartDialog.showToast('重启生效');
}
setState(() {});
@@ -53,15 +57,18 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
@override
Widget build(BuildContext context) {
TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!.copyWith(
color: widget.onTap != null && !val
? Theme.of(context).colorScheme.outline
: null);
TextStyle titleStyle = widget.titleStyle ??
Theme.of(context).textTheme.titleMedium!.copyWith(
color: widget.onTap != null && !val
? Theme.of(context).colorScheme.outline
: null,
);
TextStyle subTitleStyle = Theme.of(context)
.textTheme
.labelMedium!
.copyWith(color: Theme.of(context).colorScheme.outline);
return ListTile(
contentPadding: widget.contentPadding,
enabled: widget.onTap != null ? val : true,
enableFeedback: true,
onTap: () =>

View File

@@ -48,6 +48,7 @@ class _SubPageState extends State<SubPage> {
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// TODO: refactor
Map? data = snapshot.data;
if (data != null && data['status']) {
return Obx(() => CustomScrollView(

View File

@@ -202,6 +202,15 @@ class _SubDetailPageState extends State<SubDetailPage> {
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// TODO: refactor
if (snapshot.data is! Map) {
return HttpError(
callback: () => setState(() {
_futureBuilderFuture =
_subDetailController.queryUserSubFolderDetail();
}),
);
}
Map data = snapshot.data;
if (data['status']) {
if (_subDetailController.item.mediaCount == 0) {

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:PiliPalaX/pages/setting/widgets/switch_item.dart';
import 'package:PiliPalaX/utils/id_utils.dart';
import 'package:canvas_danmaku/canvas_danmaku.dart';
import 'package:floating/floating.dart';
@@ -916,6 +917,7 @@ class _HeaderControlState extends State<HeaderControl> {
double strokeWidth = widget.controller!.strokeWidth;
// 字体粗细
int fontWeight = widget.controller!.fontWeight;
bool massiveMode = widget.controller!.massiveMode;
final DanmakuController danmakuController =
widget.controller!.danmakuController!;
@@ -1041,7 +1043,7 @@ class _HeaderControlState extends State<HeaderControl> {
),
const Text('显示区域'),
Padding(
padding: const EdgeInsets.only(top: 12, bottom: 18),
padding: const EdgeInsets.only(top: 12),
child: Row(
children: [
for (final Map<String, dynamic> i in showAreas) ...[
@@ -1066,6 +1068,23 @@ class _HeaderControlState extends State<HeaderControl> {
],
),
),
SetSwitchItem(
title: '海量弹幕',
contentPadding: EdgeInsets.all(0),
titleStyle: TextStyle(fontSize: 14),
defaultVal: massiveMode,
setKey: SettingBoxKey.danmakuMassiveMode,
onChanged: (value) {
massiveMode = value;
widget.controller!.massiveMode = value;
setState(() {});
try {
danmakuController.updateOption(
danmakuController.option.copyWith(massiveMode: value),
);
} catch (_) {}
},
),
Text('不透明度 ${opacityVal * 100}%'),
Padding(
padding: const EdgeInsets.only(
@@ -1261,6 +1280,47 @@ class _HeaderControlState extends State<HeaderControl> {
),
),
),
Text('弹幕时长 $danmakuDurationVal'),
Padding(
padding: const EdgeInsets.only(
top: 0,
bottom: 6,
left: 10,
right: 10,
),
child: SliderTheme(
data: SliderThemeData(
trackShape: MSliderTrackShape(),
thumbColor: Theme.of(context).colorScheme.primary,
activeTrackColor: Theme.of(context).colorScheme.primary,
trackHeight: 10,
thumbShape: const RoundSliderThumbShape(
enabledThumbRadius: 6.0),
),
child: Slider(
min: 1,
max: 4,
value: pow(danmakuDurationVal, 1 / 4) as double,
divisions: 60,
label: danmakuDurationVal.toString(),
onChanged: (double val) {
danmakuDurationVal =
(pow(val, 4) as double).toPrecision(2);
widget.controller!.danmakuDurationVal =
danmakuDurationVal;
widget.controller?.putDanmakuSettings();
setState(() {});
try {
danmakuController.updateOption(
danmakuController.option.copyWith(
duration: danmakuDurationVal ~/
widget.controller!.playbackSpeed),
);
} catch (_) {}
},
),
),
),
Text(
'字幕字体大小 ${(subtitleFontScale * 100).toStringAsFixed(1)}%'),
Padding(
@@ -1331,47 +1391,6 @@ class _HeaderControlState extends State<HeaderControl> {
),
),
),
Text('弹幕时长 $danmakuDurationVal'),
Padding(
padding: const EdgeInsets.only(
top: 0,
bottom: 6,
left: 10,
right: 10,
),
child: SliderTheme(
data: SliderThemeData(
trackShape: MSliderTrackShape(),
thumbColor: Theme.of(context).colorScheme.primary,
activeTrackColor: Theme.of(context).colorScheme.primary,
trackHeight: 10,
thumbShape: const RoundSliderThumbShape(
enabledThumbRadius: 6.0),
),
child: Slider(
min: 1,
max: 4,
value: pow(danmakuDurationVal, 1 / 4) as double,
divisions: 60,
label: danmakuDurationVal.toString(),
onChanged: (double val) {
danmakuDurationVal =
(pow(val, 4) as double).toPrecision(2);
widget.controller!.danmakuDurationVal =
danmakuDurationVal;
widget.controller?.putDanmakuSettings();
setState(() {});
try {
danmakuController.updateOption(
danmakuController.option.copyWith(
duration: danmakuDurationVal ~/
widget.controller!.playbackSpeed),
);
} catch (_) {}
},
),
),
),
],
),
),

View File

@@ -250,6 +250,7 @@ class PlPlayerController {
late double fontSizeFSVal;
late double strokeWidth;
late int fontWeight;
late bool massiveMode;
late double danmakuDurationVal;
late List<double> speedList;
double? defaultDuration;
@@ -352,6 +353,7 @@ class PlPlayerController {
fontSizeFSVal = GStorage.danmakuFontScaleFS;
subtitleFontScale.value = GStorage.subtitleFontScale;
subtitleFontScaleFS.value = GStorage.subtitleFontScaleFS;
massiveMode = GStorage.danmakuMassiveMode;
// 弹幕时间
danmakuDurationVal =
setting.get(SettingBoxKey.danmakuDuration, defaultValue: 7.29);

View File

@@ -1,6 +1,5 @@
import 'dart:convert';
import 'dart:io';
import 'dart:ui';
import 'package:PiliPalaX/common/widgets/pair.dart';
import 'package:PiliPalaX/http/constants.dart';
import 'package:PiliPalaX/models/common/theme_type.dart';
@@ -99,6 +98,9 @@ class GStorage {
static double get danmakuFontScaleFS =>
setting.get(SettingBoxKey.danmakuFontScaleFS, defaultValue: 1.2);
static bool get danmakuMassiveMode =>
setting.get(SettingBoxKey.danmakuMassiveMode, defaultValue: false);
static double get subtitleFontScale =>
setting.get(SettingBoxKey.subtitleFontScale, defaultValue: 1.0);
@@ -323,6 +325,7 @@ class SettingBoxKey {
danmakuFontScale = 'danmakuFontScale',
danmakuFontScaleFS = 'danmakuFontScaleFS',
danmakuDuration = 'danmakuDuration',
danmakuMassiveMode = 'danmakuMassiveMode',
strokeWidth = 'strokeWidth',
fontWeight = 'fontWeight',
memberTab = 'memberTab',