Skip to content

Commit

Permalink
Merge pull request #3814 from canonical/better-window-size
Browse files Browse the repository at this point in the history
[gui] remember window size + better default sizes
  • Loading branch information
andrei-toterman authored Dec 20, 2024
2 parents b240d73 + 7f40ac9 commit 2d23e10
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 17 deletions.
25 changes: 8 additions & 17 deletions src/client/gui/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import 'package:hotkey_manager/hotkey_manager.dart';
import 'package:local_notifier/local_notifier.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:window_manager/window_manager.dart';
import 'package:window_size/window_size.dart';

import 'before_quit_dialog.dart';
import 'catalogue/catalogue.dart';
Expand All @@ -20,6 +19,7 @@ import 'tray_menu.dart';
import 'vm_details/mapping_slider.dart';
import 'vm_details/vm_details.dart';
import 'vm_table/vm_table_screen.dart';
import 'window_size.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
Expand All @@ -31,25 +31,12 @@ void main() async {
shortcutPolicy: ShortcutPolicy.requireCreate, // Only for Windows
);

// Get the current screen size
final screenSize = await getCurrentScreen().then((screen) {
return screen?.frame.size;
});

final windowSize = (screenSize != null)
? (screenSize.width >= 1600 && screenSize.height >= 900)
? const Size(1400, 822) // For screens 1600x900 or larger
: (screenSize.width >= 1280 && screenSize.height >= 720)
? const Size(1024, 576) // For screens 1280x720 or larger
: const Size(750, 450) // Default window size
: const Size(750, 450); // Default window size if screenSize is null

final sharedPreferences = await SharedPreferences.getInstance();
await windowManager.ensureInitialized();

final windowOptions = WindowOptions(
center: true,
minimumSize: const Size(750, 450),
size: windowSize,
size: await deriveWindowSize(sharedPreferences),
title: 'Multipass',
);

Expand All @@ -59,7 +46,6 @@ void main() async {
});

await hotKeyManager.unregisterAll();
final sharedPreferences = await SharedPreferences.getInstance();

providerContainer = ProviderContainer(overrides: [
guiSettingProvider.overrideWith(() {
Expand Down Expand Up @@ -169,6 +155,11 @@ class _AppState extends ConsumerState<App> with WindowListener {
super.dispose();
}

// this event handler is called continuously during a window resizing operation
// so we want to save the data to the disk only after the resizing stops
@override
void onWindowResize() => saveWindowSizeTimer.reset();

@override
void onWindowClose() async {
if (!await windowManager.isPreventClose()) return;
Expand Down
96 changes: 96 additions & 0 deletions src/client/gui/lib/window_size.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import 'dart:ui';

import 'package:async/async.dart';
import 'package:basics/basics.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:window_manager/window_manager.dart';
import 'package:window_size/window_size.dart';

import 'logger.dart';

const windowWidthKey = 'windowWidth';
const windowHeightKey = 'windowHeight';

String resolutionString(Size? size) {
return size != null ? '${size.width}x${size.height}' : '';
}

final saveWindowSizeTimer = RestartableTimer(1.seconds, () async {
final currentSize = await windowManager.getSize();
final sharedPreferences = await SharedPreferences.getInstance();
final screenSize = await getCurrentScreenSize();
logger.d(
'Saving window size ${currentSize.s()} for screen size ${screenSize?.s()}',
);
final prefix = resolutionString(screenSize);
sharedPreferences.setDouble('$prefix$windowWidthKey', currentSize.width);
sharedPreferences.setDouble('$prefix$windowHeightKey', currentSize.height);
});

Future<Size?> getCurrentScreenSize() async {
try {
final screen = await getCurrentScreen();
if (screen == null) throw Exception('Screen instance is null');

logger.d(
'Got Screen{frame: ${screen.frame.s()}, scaleFactor: ${screen.scaleFactor}, visibleFrame: ${screen.visibleFrame.s()}}',
);

return screen.visibleFrame.size;
} catch (e) {
logger.w('Failed to get current screen information: $e');
return null;
}
}

Future<Size> deriveWindowSize(SharedPreferences sharedPreferences) async {
final screenSize = await getCurrentScreenSize();
final prefix = resolutionString(screenSize);
final lastWidth = sharedPreferences.getDouble('$prefix$windowWidthKey');
final lastHeight = sharedPreferences.getDouble('$prefix$windowHeightKey');
final size = lastWidth != null && lastHeight != null
? Size(lastWidth, lastHeight)
: null;
logger.d('Got last window size: ${size?.s()}');
return size ?? computeDefaultWindowSize(screenSize);
}

Size computeDefaultWindowSize(Size? screenSize) {
const windowSizeFactor = 0.8;
final (screenWidth, screenHeight) = (screenSize?.width, screenSize?.height);
final aspectRatioFactor = screenSize?.flipped.aspectRatio;

final defaultWidth = switch (screenWidth) {
null || <= 1024 => 750.0,
>= 1600 => 1400.0,
_ => screenWidth * windowSizeFactor,
};

final defaultHeight = switch (screenHeight) {
null || <= 576 => 450.0,
>= 900 => 822.0,
_ => aspectRatioFactor != null
? defaultWidth * aspectRatioFactor
: screenHeight * windowSizeFactor,
};

final size = Size(defaultWidth, defaultHeight);
logger.d('Computed default window size: ${size.s()}');
return size;
}

// needed because in release mode Flutter does not emit the actual code for toString for some classes
// instead the returned strings are of type "Instance of '<Type>'"
// this is done to reduce binary size, and it cannot be turned off :face-with-rolling-eyes:
// see https://api.flutter.dev/flutter/dart-ui/keepToString-constant.html for more info
extension on Size {
String s() {
return 'Size(${width.toStringAsFixed(1)}, ${height.toStringAsFixed(1)})';
}
}

extension on Rect {
String s() {
return 'Rect.fromLTRB(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)})';
}
}

0 comments on commit 2d23e10

Please sign in to comment.