1e97716636
plugins/window replaces the window_manager fork on every desktop. Changelog: Tray hotkeys, node delays and a delay test
122 lines
3.1 KiB
Dart
Executable File
122 lines
3.1 KiB
Dart
Executable File
import 'dart:async';
|
|
|
|
import 'package:collection/collection.dart';
|
|
import 'package:fl_clash/common/common.dart';
|
|
import 'package:fl_clash/common/tray.dart';
|
|
import 'package:fl_clash/common/window.dart';
|
|
import 'package:fl_clash/enum/enum.dart';
|
|
import 'package:fl_clash/providers/action.dart';
|
|
import 'package:fl_clash/providers/app.dart';
|
|
import 'package:fl_clash/providers/state.dart';
|
|
import 'package:material_ui/material_ui.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:tray/tray.dart';
|
|
|
|
class TrayManager extends ConsumerStatefulWidget {
|
|
final Widget child;
|
|
|
|
const TrayManager({super.key, required this.child});
|
|
|
|
@override
|
|
ConsumerState<TrayManager> createState() => _TrayManagerState();
|
|
}
|
|
|
|
class _TrayManagerState extends ConsumerState<TrayManager> {
|
|
StreamSubscription<TrayEvent>? _subscription;
|
|
bool _isUpdating = false;
|
|
bool _hasPendingUpdate = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_subscription = Tray.instance.events.listen(_handleTrayEvent);
|
|
ref.listenManual(trayStateProvider, (prev, next) {
|
|
if (prev != next) {
|
|
_requestUpdate();
|
|
}
|
|
});
|
|
ref.listenManual(loadedLocaleProvider, (prev, next) {
|
|
if (prev != null && prev != next) {
|
|
_requestUpdate();
|
|
}
|
|
});
|
|
if (system.isMacOS) {
|
|
ref.listenManual(trayDelaysProvider, (prev, next) {
|
|
if (!const DeepCollectionEquality().equals(prev, next)) {
|
|
_requestUpdate();
|
|
}
|
|
});
|
|
ref.listenManual(trayTitleStateProvider, (prev, next) {
|
|
if (prev != next) {
|
|
_reportFailure(
|
|
appTray?.updateTitle(
|
|
showTrayTitle: next.showTrayTitle,
|
|
traffic: next.traffic,
|
|
),
|
|
);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/// A delay test changes the menu per proxy, so updates in flight coalesce.
|
|
void _requestUpdate() {
|
|
if (_isUpdating) {
|
|
_hasPendingUpdate = true;
|
|
return;
|
|
}
|
|
_isUpdating = true;
|
|
_reportFailure(_drainUpdates());
|
|
}
|
|
|
|
Future<void> _drainUpdates() async {
|
|
try {
|
|
do {
|
|
_hasPendingUpdate = false;
|
|
await ref.read(systemActionProvider.notifier).updateTray();
|
|
} while (_hasPendingUpdate && mounted);
|
|
} finally {
|
|
_isUpdating = false;
|
|
if (_hasPendingUpdate && mounted) {
|
|
_requestUpdate();
|
|
}
|
|
}
|
|
}
|
|
|
|
void _reportFailure(Future<void>? operation) {
|
|
if (operation == null) {
|
|
return;
|
|
}
|
|
unawaited(
|
|
operation.onError<Object>((error, stackTrace) {
|
|
commonPrint.log(
|
|
'Tray operation failed: ${compactError(error)}',
|
|
logLevel: LogLevel.error,
|
|
);
|
|
}),
|
|
);
|
|
}
|
|
|
|
void _handleTrayEvent(TrayEvent event) {
|
|
switch (event) {
|
|
case TrayIconActivated():
|
|
window?.show();
|
|
case TrayMenuRequested():
|
|
_reportFailure(Tray.instance.openMenu());
|
|
case TrayMenuItemSelected():
|
|
break;
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return widget.child;
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_subscription?.cancel();
|
|
super.dispose();
|
|
}
|
|
}
|