mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: image view
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'])
|
||||
|
||||
@@ -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: () =>
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 (_) {}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user