mirror of
https://github.com/localsend/localsend.git
synced 2026-06-23 04:10:07 +00:00
feat: minimize to tray and autostart
This commit is contained in:
@@ -75,7 +75,10 @@
|
||||
"language": "Language",
|
||||
"languageOptions": {
|
||||
"system": "System"
|
||||
}
|
||||
},
|
||||
"minimizeToTray": "Minimize to tray",
|
||||
"launchAtStartup": "Autostart after login",
|
||||
"launchMinimized": "Start hidden"
|
||||
},
|
||||
"receive": {
|
||||
"title": "Receive",
|
||||
|
||||
@@ -75,7 +75,10 @@
|
||||
"language": "Sprache",
|
||||
"languageOptions": {
|
||||
"system": "System"
|
||||
}
|
||||
},
|
||||
"minimizeToTray": "In Symbolleiste minimieren",
|
||||
"launchAtStartup": "Autostart nach Login",
|
||||
"launchMinimized": "Versteckt starten"
|
||||
},
|
||||
"receive": {
|
||||
"title": "Empfangen",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 872 B |
+16
-3
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:localsend_app/gen/strings.g.dart';
|
||||
@@ -12,16 +11,19 @@ import 'package:localsend_app/provider/settings_provider.dart';
|
||||
import 'package:localsend_app/theme.dart';
|
||||
import 'package:localsend_app/util/platform_check.dart';
|
||||
import 'package:localsend_app/util/snackbar.dart';
|
||||
import 'package:localsend_app/util/tray_helper.dart';
|
||||
import 'package:routerino/routerino.dart';
|
||||
import 'package:share_handler/share_handler.dart';
|
||||
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
const launchAtStartupArg = 'autostart';
|
||||
|
||||
/// Will be called before the MaterialApp started
|
||||
Future<PersistenceService> preInit() async {
|
||||
Future<PersistenceService> preInit(List<String> args) async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
if (!kIsWeb && checkPlatformIsDesktop()) {
|
||||
if (checkPlatformIsDesktop()) {
|
||||
await windowManager.ensureInitialized();
|
||||
WindowManager.instance.setMinimumSize(const Size(400, 500));
|
||||
|
||||
@@ -46,6 +48,17 @@ Future<PersistenceService> preInit() async {
|
||||
LocaleSettings.setLocale(locale);
|
||||
}
|
||||
|
||||
if (checkPlatformIsDesktop()) {
|
||||
// initialize tray AFTER i18n has been initialized
|
||||
await initTray();
|
||||
|
||||
if (!args.contains(launchAtStartupArg) || !persistenceService.isLaunchMinimized()) {
|
||||
// We show this app, when (1) app started manually, (2) app should not start minimized
|
||||
// In other words: only start minimized when launched on startup and "launchMinimized" is configured
|
||||
await windowManager.show();
|
||||
}
|
||||
}
|
||||
|
||||
return persistenceService;
|
||||
}
|
||||
|
||||
|
||||
+44
-20
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
@@ -10,11 +12,15 @@ import 'package:localsend_app/provider/persistence_provider.dart';
|
||||
import 'package:localsend_app/provider/settings_provider.dart';
|
||||
import 'package:localsend_app/theme.dart';
|
||||
import 'package:localsend_app/util/device_info_helper.dart';
|
||||
import 'package:localsend_app/widget/life_cycle_watcher.dart';
|
||||
import 'package:localsend_app/util/platform_check.dart';
|
||||
import 'package:localsend_app/widget/watcher/life_cycle_watcher.dart';
|
||||
import 'package:localsend_app/widget/watcher/tray_watcher.dart';
|
||||
import 'package:localsend_app/widget/watcher/window_watcher.dart';
|
||||
import 'package:routerino/routerino.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
final persistenceService = await preInit();
|
||||
Future<void> main(List<String> args) async {
|
||||
final persistenceService = await preInit(args);
|
||||
runApp(TranslationProvider(
|
||||
child: ProviderScope(
|
||||
overrides: [
|
||||
@@ -32,23 +38,41 @@ class LocalSendApp extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final themeMode = ref.watch(settingsProvider.select((settings) => settings.theme));
|
||||
return LifeCycleWatcher(
|
||||
onChangedState: (AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
ref.read(networkInfoProvider.notifier).init();
|
||||
}
|
||||
},
|
||||
child: MaterialApp(
|
||||
title: t.appName,
|
||||
locale: TranslationProvider.of(context).flutterLocale,
|
||||
supportedLocales: AppLocaleUtils.supportedLocales,
|
||||
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: getTheme(Brightness.light),
|
||||
darkTheme: getTheme(Brightness.dark),
|
||||
themeMode: themeMode,
|
||||
navigatorKey: Routerino.navigatorKey,
|
||||
home: const HomePage(),
|
||||
return TrayWatcher(
|
||||
child: WindowWatcher(
|
||||
onClose: () async {
|
||||
if (!checkPlatformIsDesktop()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (ref.read(settingsProvider).minimizeToTray) {
|
||||
await windowManager.hide();
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
},
|
||||
child: LifeCycleWatcher(
|
||||
onChangedState: (AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
ref.read(networkInfoProvider.notifier).init();
|
||||
}
|
||||
},
|
||||
child: MaterialApp(
|
||||
title: t.appName,
|
||||
locale: TranslationProvider.of(context).flutterLocale,
|
||||
supportedLocales: AppLocaleUtils.supportedLocales,
|
||||
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: getTheme(Brightness.light),
|
||||
darkTheme: getTheme(Brightness.dark),
|
||||
themeMode: themeMode,
|
||||
navigatorKey: Routerino.navigatorKey,
|
||||
home: const HomePage(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,5 +14,8 @@ class Settings with _$Settings {
|
||||
required String? destination, // null = default
|
||||
required bool saveToGallery, // only Android, iOS
|
||||
required bool quickSave, // automatically accept file requests
|
||||
required bool minimizeToTray, // minimize to tray instead of exiting the app
|
||||
required bool launchAtStartup, // launch on start / login
|
||||
required bool launchMinimized, // start hidden in tray (only available when launchAtStartup is true)
|
||||
}) = _Settings;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:launch_at_startup/launch_at_startup.dart';
|
||||
import 'package:localsend_app/gen/strings.g.dart';
|
||||
import 'package:localsend_app/init.dart';
|
||||
import 'package:localsend_app/pages/about_page.dart';
|
||||
import 'package:localsend_app/pages/changelog_page.dart';
|
||||
import 'package:localsend_app/provider/network/server_provider.dart';
|
||||
@@ -15,6 +19,7 @@ import 'package:localsend_app/widget/custom_dropdown_button.dart';
|
||||
import 'package:localsend_app/widget/dialogs/quick_save_notice.dart';
|
||||
import 'package:localsend_app/widget/local_send_logo.dart';
|
||||
import 'package:localsend_app/widget/responsive_list_view.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:routerino/routerino.dart';
|
||||
|
||||
class SettingsTab extends ConsumerStatefulWidget {
|
||||
@@ -101,32 +106,61 @@ class _SettingsTabState extends ConsumerState<SettingsTab> {
|
||||
},
|
||||
),
|
||||
),
|
||||
if (checkPlatformIsDesktop()) ...[
|
||||
_BooleanEntry(
|
||||
label: t.settingsTab.general.minimizeToTray,
|
||||
value: settings.minimizeToTray,
|
||||
onChanged: (b) async {
|
||||
await ref.read(settingsProvider.notifier).setMinimizeToTray(b);
|
||||
},
|
||||
),
|
||||
_BooleanEntry(
|
||||
label: t.settingsTab.general.launchAtStartup,
|
||||
value: settings.launchAtStartup,
|
||||
onChanged: (b) async {
|
||||
try {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
|
||||
launchAtStartup.setup(
|
||||
appName: packageInfo.appName,
|
||||
appPath: Platform.resolvedExecutable,
|
||||
args: [launchAtStartupArg],
|
||||
);
|
||||
if (b) {
|
||||
await launchAtStartup.enable();
|
||||
} else {
|
||||
await launchAtStartup.disable();
|
||||
}
|
||||
await ref.read(settingsProvider.notifier).setLaunchAtStartup(b);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
},
|
||||
),
|
||||
if (settings.launchAtStartup)
|
||||
_BooleanEntry(
|
||||
label: t.settingsTab.general.launchMinimized,
|
||||
value: settings.launchMinimized,
|
||||
onChanged: (b) async {
|
||||
await ref.read(settingsProvider.notifier).setLaunchMinimized(b);
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
_SettingsSection(
|
||||
title: t.settingsTab.receive.title,
|
||||
children: [
|
||||
_SettingsEntry(
|
||||
_BooleanEntry(
|
||||
label: t.settingsTab.receive.quickSave,
|
||||
child: CustomDropdownButton<bool>(
|
||||
value: settings.quickSave,
|
||||
items: [false, true].map((b) {
|
||||
return DropdownMenuItem(
|
||||
value: b,
|
||||
alignment: Alignment.center,
|
||||
child: Text(b ? t.general.on : t.general.off),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (b) async {
|
||||
if (b != null) {
|
||||
final old = settings.quickSave;
|
||||
await ref.read(settingsProvider.notifier).setQuickSave(b);
|
||||
if (!old && b && mounted) {
|
||||
QuickSaveNotice.open(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
value: settings.quickSave,
|
||||
onChanged: (b) async {
|
||||
final old = settings.quickSave;
|
||||
await ref.read(settingsProvider.notifier).setQuickSave(b);
|
||||
if (!old && b && mounted) {
|
||||
QuickSaveNotice.open(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
if (checkPlatform([TargetPlatform.windows, TargetPlatform.macOS, TargetPlatform.linux]))
|
||||
_SettingsEntry(
|
||||
@@ -155,23 +189,12 @@ class _SettingsTabState extends ConsumerState<SettingsTab> {
|
||||
),
|
||||
),
|
||||
if (checkPlatformWithGallery())
|
||||
_SettingsEntry(
|
||||
_BooleanEntry(
|
||||
label: t.settingsTab.receive.saveToGallery,
|
||||
child: CustomDropdownButton<bool>(
|
||||
value: settings.saveToGallery,
|
||||
items: [false, true].map((b) {
|
||||
return DropdownMenuItem(
|
||||
value: b,
|
||||
alignment: Alignment.center,
|
||||
child: Text(b ? t.general.on : t.general.off),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (b) async {
|
||||
if (b != null) {
|
||||
await ref.read(settingsProvider.notifier).setSaveToGallery(b);
|
||||
}
|
||||
},
|
||||
),
|
||||
value: settings.saveToGallery,
|
||||
onChanged: (b) async {
|
||||
await ref.read(settingsProvider.notifier).setSaveToGallery(b);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -359,6 +382,40 @@ class _SettingsEntry extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// A specialized version of [_SettingsEntry].
|
||||
class _BooleanEntry extends StatelessWidget {
|
||||
final String label;
|
||||
final bool value;
|
||||
final ValueChanged<bool> onChanged;
|
||||
const _BooleanEntry({
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _SettingsEntry(
|
||||
label: label,
|
||||
child: CustomDropdownButton<bool>(
|
||||
value: value,
|
||||
items: [false, true].map((b) {
|
||||
return DropdownMenuItem(
|
||||
value: b,
|
||||
alignment: Alignment.center,
|
||||
child: Text(b ? t.general.on : t.general.off),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (b) {
|
||||
if (b != null) {
|
||||
onChanged(b);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SettingsSection extends StatelessWidget {
|
||||
final String title;
|
||||
final List<Widget> children;
|
||||
|
||||
@@ -31,6 +31,7 @@ import 'package:shelf/shelf.dart';
|
||||
import 'package:shelf/shelf_io.dart';
|
||||
import 'package:shelf_router/shelf_router.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
/// This provider manages receiving file requests.
|
||||
final serverProvider = StateNotifierProvider<ServerNotifier, ServerState?>((ref) {
|
||||
@@ -158,6 +159,10 @@ class ServerNotifier extends StateNotifier<ServerState?> {
|
||||
for (final f in dto.files.values) f.id: f.fileName,
|
||||
};
|
||||
} else {
|
||||
if (checkPlatformIsDesktop() && (await windowManager.isMinimized() || !(await windowManager.isVisible()))) {
|
||||
await windowManager.show();
|
||||
}
|
||||
|
||||
// ignore: use_build_context_synchronously
|
||||
Routerino.context.push(() => const ReceivePage());
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ const _portKey = 'ls_port';
|
||||
const _destinationKey = 'ls_destination';
|
||||
const _saveToGallery = 'ls_save_to_gallery';
|
||||
const _quickSave = 'ls_quick_save';
|
||||
const _minimizeToTray = 'ls_minimize_to_tray';
|
||||
const _launchAtStartup = 'ls_launch_at_startup';
|
||||
const _launchMinimized = 'ls_start_minimized';
|
||||
|
||||
const defaultPort = 53317;
|
||||
|
||||
@@ -110,4 +113,28 @@ class PersistenceService {
|
||||
Future<void> setQuickSave(bool quickSave) async {
|
||||
await _prefs.setBool(_quickSave, quickSave);
|
||||
}
|
||||
|
||||
bool isMinimizeToTray() {
|
||||
return _prefs.getBool(_minimizeToTray) ?? false;
|
||||
}
|
||||
|
||||
Future<void> setMinimizeToTray(bool minimizeToTray) async {
|
||||
await _prefs.setBool(_minimizeToTray, minimizeToTray);
|
||||
}
|
||||
|
||||
bool isLaunchAtStartup() {
|
||||
return _prefs.getBool(_launchAtStartup) ?? false;
|
||||
}
|
||||
|
||||
Future<void> setLaunchAtStartup(bool launchAtStartup) async {
|
||||
await _prefs.setBool(_launchAtStartup, launchAtStartup);
|
||||
}
|
||||
|
||||
bool isLaunchMinimized() {
|
||||
return _prefs.getBool(_launchMinimized) ?? true;
|
||||
}
|
||||
|
||||
Future<void> setLaunchMinimized(bool launchMinimized) async {
|
||||
await _prefs.setBool(_launchMinimized, launchMinimized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ class SettingsNotifier extends StateNotifier<Settings> {
|
||||
destination: service.getDestination(),
|
||||
saveToGallery: service.isSaveToGallery(),
|
||||
quickSave: service.isQuickSave(),
|
||||
minimizeToTray: service.isMinimizeToTray(),
|
||||
launchAtStartup: service.isLaunchAtStartup(),
|
||||
launchMinimized: service.isLaunchMinimized(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,4 +77,25 @@ class SettingsNotifier extends StateNotifier<Settings> {
|
||||
quickSave: quickSave,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setMinimizeToTray(bool minimizeToTray) async {
|
||||
await _service.setMinimizeToTray(minimizeToTray);
|
||||
state = state.copyWith(
|
||||
minimizeToTray: minimizeToTray,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setLaunchAtStartup(bool launchAtStartup) async {
|
||||
await _service.setLaunchAtStartup(launchAtStartup);
|
||||
state = state.copyWith(
|
||||
launchAtStartup: launchAtStartup,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setLaunchMinimized(bool launchMinimized) async {
|
||||
await _service.setLaunchMinimized(launchMinimized);
|
||||
state = state.copyWith(
|
||||
launchMinimized: launchMinimized,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:localsend_app/gen/assets.gen.dart';
|
||||
import 'package:localsend_app/gen/strings.g.dart';
|
||||
import 'package:localsend_app/util/platform_check.dart';
|
||||
import 'package:tray_manager/tray_manager.dart';
|
||||
|
||||
Future<void> initTray() async {
|
||||
if (!checkPlatformIsDesktop()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await trayManager.setIcon(
|
||||
checkPlatform([TargetPlatform.windows]) ? Assets.img.logo32Ico : Assets.img.logo32Png.path,
|
||||
);
|
||||
final items = [
|
||||
MenuItem(
|
||||
key: 'exit_app',
|
||||
label: t.general.close,
|
||||
),
|
||||
];
|
||||
await trayManager.setContextMenu(Menu(items: items));
|
||||
await trayManager.setToolTip(t.appName);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart' hide MenuItem;
|
||||
import 'package:tray_manager/tray_manager.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
class TrayWatcher extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const TrayWatcher({required this.child, super.key});
|
||||
|
||||
@override
|
||||
State<TrayWatcher> createState() => _TrayWatcherState();
|
||||
}
|
||||
|
||||
class _TrayWatcherState extends State<TrayWatcher> with TrayListener {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
trayManager.addListener(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
trayManager.removeListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void onTrayIconMouseDown() async {
|
||||
try {
|
||||
if (await windowManager.isVisible() && !(await windowManager.isMinimized())) {
|
||||
await trayManager.popUpContextMenu();
|
||||
} else {
|
||||
await windowManager.show();
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onTrayIconRightMouseDown() {
|
||||
trayManager.popUpContextMenu();
|
||||
}
|
||||
|
||||
@override
|
||||
void onTrayMenuItemClick(MenuItem menuItem) {
|
||||
// There is only an exit button
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'package:flutter/material.dart' hide MenuItem;
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
class WindowWatcher extends StatefulWidget {
|
||||
final Widget child;
|
||||
final VoidCallback onClose;
|
||||
|
||||
const WindowWatcher({
|
||||
required this.child,
|
||||
required this.onClose,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<WindowWatcher> createState() => _WindowWatcherState();
|
||||
}
|
||||
|
||||
class _WindowWatcherState extends State<WindowWatcher> with WindowListener {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
windowManager.addListener(this);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
try {
|
||||
// always handle close actions manually
|
||||
await windowManager.setPreventClose(true);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
windowManager.removeListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void onWindowClose() {
|
||||
widget.onClose();
|
||||
}
|
||||
|
||||
@override
|
||||
void onWindowFocus() {
|
||||
// call set state according to window_manager README
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <desktop_drop/desktop_drop_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <tray_manager/tray_manager_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
@@ -18,6 +19,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||
g_autoptr(FlPluginRegistrar) tray_manager_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin");
|
||||
tray_manager_plugin_register_with_registrar(tray_manager_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
desktop_drop
|
||||
screen_retriever
|
||||
tray_manager
|
||||
url_launcher_linux
|
||||
window_manager
|
||||
)
|
||||
|
||||
@@ -48,7 +48,7 @@ static void my_application_activate(GApplication* application) {
|
||||
}
|
||||
|
||||
gtk_window_set_default_size(window, 400, 500);
|
||||
gtk_widget_show(GTK_WIDGET(window));
|
||||
gtk_widget_realize(GTK_WIDGET(window));
|
||||
|
||||
g_autoptr(FlDartProject) project = fl_dart_project_new();
|
||||
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
|
||||
|
||||
@@ -14,6 +14,7 @@ import path_provider_macos
|
||||
import photo_manager
|
||||
import screen_retriever
|
||||
import shared_preferences_macos
|
||||
import tray_manager
|
||||
import url_launcher_macos
|
||||
import wakelock_macos
|
||||
import window_manager
|
||||
@@ -28,6 +29,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
PhotoManagerPlugin.register(with: registry.registrar(forPlugin: "PhotoManagerPlugin"))
|
||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin"))
|
||||
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||
|
||||
@@ -4,6 +4,7 @@ import FlutterMacOS
|
||||
@NSApplicationMain
|
||||
class AppDelegate: FlutterAppDelegate {
|
||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
return true
|
||||
// LocalSend handles the close event manually
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import window_manager
|
||||
|
||||
class MainFlutterWindow: NSWindow {
|
||||
override func awakeFromNib() {
|
||||
@@ -12,4 +13,10 @@ class MainFlutterWindow: NSWindow {
|
||||
|
||||
super.awakeFromNib()
|
||||
}
|
||||
|
||||
// window_manager: start hidden
|
||||
override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) {
|
||||
super.order(place, relativeTo: otherWin)
|
||||
hiddenWindowAtLaunch()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,6 +541,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.5.4"
|
||||
launch_at_startup:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: launch_at_startup
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -576,6 +583,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.5"
|
||||
menu_base:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: menu_base
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -968,6 +982,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
shortid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shortid
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -1106,6 +1127,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
tray_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: tray_manager
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1295,6 +1323,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32_registry
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
window_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -26,6 +26,7 @@ dependencies:
|
||||
image_gallery_saver: 1.7.1
|
||||
image_picker: 0.8.6
|
||||
json_annotation: 4.7.0
|
||||
launch_at_startup: 0.2.1
|
||||
network_info_plus: 3.0.1
|
||||
open_filex: 4.3.2
|
||||
package_info_plus: 3.0.2
|
||||
@@ -39,6 +40,7 @@ dependencies:
|
||||
shelf_router: 1.1.3
|
||||
slang: 3.9.0
|
||||
slang_flutter: 3.9.0
|
||||
tray_manager: 0.2.0
|
||||
url_launcher: 6.1.7
|
||||
uuid: 3.0.7
|
||||
wakelock: 0.6.2
|
||||
@@ -69,3 +71,4 @@ msix_config:
|
||||
identity_name: 11157TienDoNam.LocalSend
|
||||
logo_path: assets\img\logo-512.png
|
||||
languages: en-us, de-de
|
||||
enable_at_startup: true
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <network_info_plus/network_info_plus_windows_plugin.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <tray_manager/tray_manager_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
@@ -25,6 +26,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||
TrayManagerPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("TrayManagerPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
WindowManagerPluginRegisterWithRegistrar(
|
||||
|
||||
@@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
network_info_plus
|
||||
permission_handler_windows
|
||||
screen_retriever
|
||||
tray_manager
|
||||
url_launcher_windows
|
||||
window_manager
|
||||
)
|
||||
|
||||
@@ -117,7 +117,7 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
|
||||
double scale_factor = dpi / 96.0;
|
||||
|
||||
HWND window = CreateWindow(
|
||||
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||
window_class, title.c_str(), WS_OVERLAPPEDWINDOW, // do not add WS_VISIBLE since the window will be shown later
|
||||
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
|
||||
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
|
||||
nullptr, nullptr, GetModuleHandle(nullptr), this);
|
||||
|
||||
Reference in New Issue
Block a user