diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index f7c423dbc..c07764e32 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'dart:ui'; import 'package:back_button_interceptor/back_button_interceptor.dart'; import 'package:desktop_multi_window/desktop_multi_window.dart'; @@ -10,6 +11,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; import 'package:flutter_hbb/models/peer_model.dart'; +import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:window_manager/window_manager.dart'; @@ -28,6 +30,7 @@ var isWeb = false; var isWebDesktop = false; var version = ""; int androidVersion = 0; +const windowPrefix = "wm_"; DesktopType? desktopType; typedef F = String Function(String); @@ -821,8 +824,6 @@ Future initGlobalFFI() async { debugPrint("_globalFFI init end"); // after `put`, can also be globally found by Get.find(); Get.put(_globalFFI, permanent: true); - // global shared preference - await Get.putAsync(() => SharedPreferences.getInstance()); } String translate(String name) { @@ -909,3 +910,100 @@ Widget getPlatformImage(String platform, {double size = 50}) { } return Image.asset('assets/$platform.png', height: size, width: size); } + +class LastWindowPosition { + double? width; + double? height; + double? offsetWidth; + double? offsetHeight; + + LastWindowPosition( + this.width, this.height, this.offsetWidth, this.offsetHeight); + + Map toJson() { + return { + "width": width, + "height": height, + "offsetWidth": offsetWidth, + "offsetHeight": offsetHeight + }; + } + + @override + String toString() { + return jsonEncode(toJson()); + } + + static LastWindowPosition? loadFromString(String content) { + if (content.isEmpty) { + return null; + } + try { + final m = jsonDecode(content); + return LastWindowPosition( + m["width"], m["height"], m["offsetWidth"], m["offsetHeight"]); + } catch (e) { + debugPrint(e.toString()); + return null; + } + } +} + +/// Save window position and size on exit +/// Note that windowId must be provided if it's subwindow +Future saveWindowPosition(WindowType type, {int? windowId}) async { + if (type != WindowType.Main && windowId == null) { + debugPrint( + "Error: windowId cannot be null when saving positions for sub window"); + } + switch (type) { + case WindowType.Main: + List resp = await Future.wait( + [windowManager.getPosition(), windowManager.getSize()]); + Offset position = resp[0]; + Size sz = resp[1]; + final pos = + LastWindowPosition(sz.width, sz.height, position.dx, position.dy); + await Get.find() + .setString(windowPrefix + type.name, pos.toString()); + break; + default: + // TODO: implement window + break; + } +} + +/// Save window position and size on exit +/// Note that windowId must be provided if it's subwindow +Future restoreWindowPosition(WindowType type, {int? windowId}) async { + if (type != WindowType.Main && windowId == null) { + debugPrint( + "Error: windowId cannot be null when saving positions for sub window"); + } + switch (type) { + case WindowType.Main: + var pos = + Get.find().getString(windowPrefix + type.name); + if (pos == null) { + debugPrint("no window position saved, ignore restore"); + return false; + } + var lpos = LastWindowPosition.loadFromString(pos); + if (lpos == null) { + debugPrint("window position saved, but cannot be parsed"); + return false; + } + await windowManager.setSize(Size(lpos.width ?? 1280, lpos.height ?? 720)); + if (lpos.offsetWidth == null || lpos.offsetHeight == null) { + await windowManager.center(); + } else { + await windowManager + .setPosition(Offset(lpos.offsetWidth!, lpos.offsetHeight!)); + } + return true; + default: + // TODO: implement subwindow + break; + } + return false; +} diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 25d1ae66d..bbdd89b15 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -46,7 +46,10 @@ class _DesktopHomePageState extends State // close all sub windows if (await windowManager.isPreventClose()) { try { - await rustDeskWinManager.closeAllSubWindows(); + await Future.wait([ + saveWindowPosition(WindowType.Main), + rustDeskWinManager.closeAllSubWindows() + ]); } catch (err) { debugPrint("$err"); } finally { diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index d5837c09c..7968bbad7 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -10,6 +10,7 @@ import 'package:flutter_hbb/desktop/screen/desktop_remote_screen.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:window_manager/window_manager.dart'; // import 'package:window_manager/window_manager.dart'; @@ -41,6 +42,7 @@ Future main(List args) async { int type = argument['type'] ?? -1; argument['windowId'] = windowId; WindowType wType = type.windowType; + restoreWindowPosition(wType, windowId: windowId); switch (wType) { case WindowType.RemoteDesktop: desktopType = DesktopType.remote; @@ -71,6 +73,8 @@ Future main(List args) async { } Future initEnv(String appType) async { + // global shared preference + await Get.putAsync(() => SharedPreferences.getInstance()); await platformFFI.init(appType); // global FFI, use this **ONLY** for global configuration // for convenience, use global FFI on mobile platform @@ -93,9 +97,9 @@ void runMainApp(bool startService) async { } runApp(App()); // set window option - WindowOptions windowOptions = - getHiddenTitleBarWindowOptions(const Size(1280, 720)); + WindowOptions windowOptions = getHiddenTitleBarWindowOptions(); windowManager.waitUntilReadyToShow(windowOptions, () async { + restoreWindowPosition(WindowType.Main); await windowManager.show(); await windowManager.focus(); }); @@ -166,7 +170,8 @@ void runPortForwardScreen(Map argument) async { void runConnectionManagerScreen() async { // initialize window - WindowOptions windowOptions = getHiddenTitleBarWindowOptions(Size(300, 400)); + WindowOptions windowOptions = + getHiddenTitleBarWindowOptions(size: const Size(300, 400)); await Future.wait([ initEnv(kAppTypeMain), windowManager.waitUntilReadyToShow(windowOptions, () async { @@ -185,7 +190,7 @@ void runConnectionManagerScreen() async { builder: _keepScaleBuilder())); } -WindowOptions getHiddenTitleBarWindowOptions(Size size) { +WindowOptions getHiddenTitleBarWindowOptions({Size? size}) { return WindowOptions( size: size, center: true,