mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-24 18:18:38 +00:00
tweaks (#1444)
* opt: proxy * opt: calcWindowPosition * fix: height depend on svg * bump * fix * ci: cache linux * string systemProxyPort --------- Co-authored-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
committed by
GitHub
parent
a928e48159
commit
a5715868b3
226
lib/common/widgets/cached_network_svg_image.dart
Normal file
226
lib/common/widgets/cached_network_svg_image.dart
Normal file
@@ -0,0 +1,226 @@
|
||||
// code from cached_network_svg_image;
|
||||
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class CachedNetworkSVGImage extends StatefulWidget {
|
||||
CachedNetworkSVGImage(
|
||||
String url, {
|
||||
Key? key,
|
||||
String? cacheKey,
|
||||
Widget? placeholder,
|
||||
Widget? errorWidget,
|
||||
double? width,
|
||||
double? height,
|
||||
Map<String, String>? headers,
|
||||
BoxFit fit = BoxFit.contain,
|
||||
AlignmentGeometry alignment = Alignment.center,
|
||||
bool matchTextDirection = false,
|
||||
bool allowDrawingOutsideViewBox = false,
|
||||
String? semanticsLabel,
|
||||
bool excludeFromSemantics = false,
|
||||
SvgTheme theme = const SvgTheme(),
|
||||
ColorFilter? colorFilter,
|
||||
WidgetBuilder? placeholderBuilder,
|
||||
BaseCacheManager? cacheManager,
|
||||
}) : _url = url,
|
||||
_cacheKey = cacheKey,
|
||||
_placeholder = placeholder,
|
||||
_errorWidget = errorWidget,
|
||||
_width = width,
|
||||
_height = height,
|
||||
_headers = headers,
|
||||
_fit = fit,
|
||||
_alignment = alignment,
|
||||
_matchTextDirection = matchTextDirection,
|
||||
_allowDrawingOutsideViewBox = allowDrawingOutsideViewBox,
|
||||
_semanticsLabel = semanticsLabel,
|
||||
_excludeFromSemantics = excludeFromSemantics,
|
||||
_theme = theme,
|
||||
_colorFilter = colorFilter,
|
||||
_placeholderBuilder = placeholderBuilder,
|
||||
_cacheManager = cacheManager ?? DefaultCacheManager(),
|
||||
super(key: key ?? ValueKey(cacheKey ?? url));
|
||||
|
||||
final String _url;
|
||||
final String? _cacheKey;
|
||||
final Widget? _placeholder;
|
||||
final Widget? _errorWidget;
|
||||
final double? _width;
|
||||
final double? _height;
|
||||
final Map<String, String>? _headers;
|
||||
final BoxFit _fit;
|
||||
final AlignmentGeometry _alignment;
|
||||
final bool _matchTextDirection;
|
||||
final bool _allowDrawingOutsideViewBox;
|
||||
final String? _semanticsLabel;
|
||||
final bool _excludeFromSemantics;
|
||||
final SvgTheme _theme;
|
||||
final ColorFilter? _colorFilter;
|
||||
final WidgetBuilder? _placeholderBuilder;
|
||||
final BaseCacheManager _cacheManager;
|
||||
|
||||
@override
|
||||
State<CachedNetworkSVGImage> createState() => _CachedNetworkSVGImageState();
|
||||
|
||||
static Future<void> preCache(
|
||||
String imageUrl, {
|
||||
String? cacheKey,
|
||||
BaseCacheManager? cacheManager,
|
||||
}) {
|
||||
final key = cacheKey ?? _generateKeyFromUrl(imageUrl);
|
||||
cacheManager ??= DefaultCacheManager();
|
||||
return cacheManager.downloadFile(key);
|
||||
}
|
||||
|
||||
static Future<void> clearCacheForUrl(
|
||||
String imageUrl, {
|
||||
String? cacheKey,
|
||||
BaseCacheManager? cacheManager,
|
||||
}) {
|
||||
final key = cacheKey ?? _generateKeyFromUrl(imageUrl);
|
||||
cacheManager ??= DefaultCacheManager();
|
||||
return cacheManager.removeFile(key);
|
||||
}
|
||||
|
||||
static Future<void> clearCache({BaseCacheManager? cacheManager}) {
|
||||
cacheManager ??= DefaultCacheManager();
|
||||
return cacheManager.emptyCache();
|
||||
}
|
||||
|
||||
static String _generateKeyFromUrl(String url) => url.split('?').first;
|
||||
}
|
||||
|
||||
class _CachedNetworkSVGImageState extends State<CachedNetworkSVGImage> {
|
||||
bool _isLoading = false;
|
||||
bool _isError = false;
|
||||
String? _svgString;
|
||||
late final String _cacheKey;
|
||||
double? height;
|
||||
late TextScaler textScaler;
|
||||
|
||||
static final _sizeRegExp = RegExp(
|
||||
r'height="([\d\.]+)([c-x]{2})?"',
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_cacheKey =
|
||||
widget._cacheKey ??
|
||||
CachedNetworkSVGImage._generateKeyFromUrl(widget._url);
|
||||
_loadImage();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
textScaler = MediaQuery.textScalerOf(context);
|
||||
}
|
||||
|
||||
Future<void> _loadImage() async {
|
||||
try {
|
||||
var file = (await widget._cacheManager.getFileFromMemory(
|
||||
_cacheKey,
|
||||
))?.file;
|
||||
|
||||
file ??= await widget._cacheManager.getSingleFile(
|
||||
widget._url,
|
||||
key: _cacheKey,
|
||||
headers: widget._headers ?? {},
|
||||
);
|
||||
final svg = await file.readAsString();
|
||||
_svgString = svg;
|
||||
if (widget._width == null && widget._height == null) {
|
||||
final match = _sizeRegExp.firstMatch(svg);
|
||||
if (match != null) {
|
||||
double h = double.parse(match.group(1)!);
|
||||
final suffix = match.group(2);
|
||||
if (suffix != null) {
|
||||
h *= switch (suffix) {
|
||||
'em' => textScaler.scale(widget._theme.fontSize),
|
||||
'ex' => textScaler.scale(widget._theme.xHeight),
|
||||
'pt' => 1.25,
|
||||
'pc' => 15.0,
|
||||
'mm' => 3.543307,
|
||||
'cm' => 35.43307,
|
||||
'in' => 90.0,
|
||||
_ => 1.0,
|
||||
};
|
||||
}
|
||||
height = h;
|
||||
}
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
|
||||
_setState();
|
||||
} catch (e) {
|
||||
log('CachedNetworkSVGImage: $e');
|
||||
|
||||
_isError = true;
|
||||
_isLoading = false;
|
||||
|
||||
_setState();
|
||||
}
|
||||
}
|
||||
|
||||
void _setState() {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
} else {
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) => setState(() {}));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: widget._width,
|
||||
height: widget._height,
|
||||
child: _buildImage(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget? _buildImage() {
|
||||
if (_isLoading) return _buildPlaceholderWidget();
|
||||
|
||||
if (_isError) return _buildErrorWidget();
|
||||
|
||||
return _buildSVGImage();
|
||||
}
|
||||
|
||||
Widget _buildPlaceholderWidget() => Center(child: widget._placeholder);
|
||||
|
||||
Widget _buildErrorWidget() => Center(child: widget._errorWidget);
|
||||
|
||||
Widget? _buildSVGImage() {
|
||||
if (_svgString == null) {
|
||||
return Center(child: widget._placeholderBuilder?.call(context));
|
||||
}
|
||||
|
||||
return SvgPicture.string(
|
||||
_svgString!,
|
||||
fit: widget._fit,
|
||||
width: widget._width,
|
||||
height: widget._height ?? height,
|
||||
alignment: widget._alignment,
|
||||
matchTextDirection: widget._matchTextDirection,
|
||||
allowDrawingOutsideViewBox: widget._allowDrawingOutsideViewBox,
|
||||
semanticsLabel: widget._semanticsLabel,
|
||||
excludeFromSemantics: widget._excludeFromSemantics,
|
||||
colorFilter: widget._colorFilter,
|
||||
placeholderBuilder: widget._placeholderBuilder,
|
||||
theme: widget._theme,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -106,32 +106,31 @@ class Request {
|
||||
persistentConnection: true,
|
||||
);
|
||||
|
||||
bool enableSystemProxy = Pref.enableSystemProxy;
|
||||
final bool enableSystemProxy;
|
||||
late final String systemProxyHost;
|
||||
late final int? systemProxyPort;
|
||||
if (enableSystemProxy) {
|
||||
if (Pref.enableSystemProxy) {
|
||||
systemProxyHost = Pref.systemProxyHost;
|
||||
systemProxyPort = int.tryParse(Pref.systemProxyPort);
|
||||
enableSystemProxy = systemProxyPort != null && systemProxyHost.isNotEmpty;
|
||||
} else {
|
||||
enableSystemProxy = false;
|
||||
}
|
||||
|
||||
final http11Adapter = IOHttpClientAdapter(
|
||||
createHttpClient: () {
|
||||
final client = HttpClient()
|
||||
..idleTimeout = const Duration(seconds: 15)
|
||||
..autoUncompress = false; // Http2Adapter没有自动解压, 统一行为
|
||||
// 设置代理
|
||||
if (enableSystemProxy) {
|
||||
client
|
||||
..findProxy = ((_) => 'PROXY $systemProxyHost:$systemProxyPort')
|
||||
..badCertificateCallback =
|
||||
(X509Certificate cert, String host, int port) => true;
|
||||
}
|
||||
return client;
|
||||
},
|
||||
createHttpClient: enableSystemProxy
|
||||
? () => HttpClient()
|
||||
..idleTimeout = const Duration(seconds: 15)
|
||||
..autoUncompress = false
|
||||
..findProxy = ((_) => 'PROXY $systemProxyHost:$systemProxyPort')
|
||||
..badCertificateCallback =
|
||||
(X509Certificate cert, String host, int port) => true
|
||||
: () => HttpClient()
|
||||
..idleTimeout = const Duration(seconds: 15)
|
||||
..autoUncompress = false, // Http2Adapter没有自动解压, 统一行为
|
||||
);
|
||||
|
||||
late Uri proxy;
|
||||
late final Uri proxy;
|
||||
if (enableSystemProxy) {
|
||||
proxy = Uri(
|
||||
scheme: 'http',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/common/widgets/cached_network_svg_image.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/custom_grid_view.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/http/constants.dart';
|
||||
@@ -15,7 +16,6 @@ import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/image_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:cached_network_svg_image/cached_network_svg_image.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -88,7 +88,6 @@ class OpusContent extends StatelessWidget {
|
||||
child: CachedNetworkSVGImage(
|
||||
cacheKey: latex,
|
||||
semanticsLabel: latex,
|
||||
height: 65,
|
||||
'${HttpString.apiBaseUrl}/x/web-frontend/mathjax/tex?formula=${Uri.encodeComponent(latex)}',
|
||||
colorFilter: ColorFilter.mode(
|
||||
colorScheme.onSurfaceVariant,
|
||||
@@ -96,6 +95,7 @@ class OpusContent extends StatelessWidget {
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
placeholderBuilder: (_) => Text(latex),
|
||||
errorWidget: Text(latex),
|
||||
),
|
||||
);
|
||||
default:
|
||||
|
||||
@@ -984,7 +984,7 @@ List<SettingsModel> get extraSettings => [
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
labelText: systemProxyHost != ''
|
||||
labelText: systemProxyHost.isNotEmpty
|
||||
? systemProxyHost
|
||||
: '请输入Host,使用 . 分割',
|
||||
border: const OutlineInputBorder(
|
||||
@@ -992,16 +992,14 @@ List<SettingsModel> get extraSettings => [
|
||||
),
|
||||
hintText: systemProxyHost,
|
||||
),
|
||||
onChanged: (e) {
|
||||
systemProxyHost = e;
|
||||
},
|
||||
onChanged: (e) => systemProxyHost = e,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
TextField(
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
labelText: systemProxyPort != ''
|
||||
labelText: systemProxyPort.isNotEmpty
|
||||
? systemProxyPort
|
||||
: '请输入Port',
|
||||
border: const OutlineInputBorder(
|
||||
@@ -1009,9 +1007,8 @@ List<SettingsModel> get extraSettings => [
|
||||
),
|
||||
hintText: systemProxyPort,
|
||||
),
|
||||
onChanged: (e) {
|
||||
systemProxyPort = e;
|
||||
},
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
onChanged: (e) => systemProxyPort = e,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,34 +1,37 @@
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:screen_retriever/screen_retriever.dart';
|
||||
|
||||
Future<Offset> calcWindowPosition(Size windowSize) async {
|
||||
Display primaryDisplay = await screenRetriever.getPrimaryDisplay();
|
||||
List<Display> allDisplays = await screenRetriever.getAllDisplays();
|
||||
Offset cursorScreenPoint = await screenRetriever.getCursorScreenPoint();
|
||||
final allDisplays = screenRetriever.getAllDisplays();
|
||||
final cursorScreenPoint = await screenRetriever.getCursorScreenPoint();
|
||||
|
||||
Display currentDisplay = allDisplays.firstWhere(
|
||||
(display) => Rect.fromLTWH(
|
||||
display.visiblePosition!.dx,
|
||||
display.visiblePosition!.dy,
|
||||
display.size.width,
|
||||
display.size.height,
|
||||
).contains(cursorScreenPoint),
|
||||
orElse: () => primaryDisplay,
|
||||
);
|
||||
final currentDisplay =
|
||||
(await allDisplays).firstWhereOrNull(
|
||||
(display) => (display.visiblePosition! & display.size).contains(
|
||||
cursorScreenPoint,
|
||||
),
|
||||
) ??
|
||||
await screenRetriever.getPrimaryDisplay();
|
||||
|
||||
num visibleWidth = currentDisplay.size.width;
|
||||
num visibleHeight = currentDisplay.size.height;
|
||||
num visibleStartX = 0;
|
||||
num visibleStartY = 0;
|
||||
|
||||
if (currentDisplay.visibleSize != null) {
|
||||
visibleWidth = currentDisplay.visibleSize!.width;
|
||||
visibleHeight = currentDisplay.visibleSize!.height;
|
||||
final double visibleWidth;
|
||||
final double visibleHeight;
|
||||
if (currentDisplay.visibleSize case final size?) {
|
||||
visibleWidth = size.width;
|
||||
visibleHeight = size.height;
|
||||
} else {
|
||||
visibleWidth = currentDisplay.size.width;
|
||||
visibleHeight = currentDisplay.size.height;
|
||||
}
|
||||
if (currentDisplay.visiblePosition != null) {
|
||||
visibleStartX = currentDisplay.visiblePosition!.dx;
|
||||
visibleStartY = currentDisplay.visiblePosition!.dy;
|
||||
|
||||
final double visibleStartX;
|
||||
final double visibleStartY;
|
||||
if (currentDisplay.visiblePosition case final offset?) {
|
||||
visibleStartX = offset.dx;
|
||||
visibleStartY = offset.dy;
|
||||
} else {
|
||||
visibleStartX = visibleStartY = 0;
|
||||
}
|
||||
|
||||
final windowPosition = Pref.windowPosition;
|
||||
|
||||
Reference in New Issue
Block a user