Skip to content

Commit

Permalink
fix: remember window size & position
Browse files Browse the repository at this point in the history
  • Loading branch information
Merrit committed Jan 27, 2023
1 parent 3e048a0 commit 30c26e5
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 91 deletions.
4 changes: 2 additions & 2 deletions lib/app.dart
@@ -1,4 +1,4 @@
import 'package:flutter/material.dart' hide MenuItem;
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart';
Expand Down Expand Up @@ -37,7 +37,7 @@ class _AppState extends State<App> with TrayListener, WindowListener {
/// Only working on Windows for some reason.
/// Linux will use `flutter_window_close` instead.
if (settingsCubit.state.closeToTray) {
NyrnaWindow().hide();
NyrnaWindow.instance.hide();
return;
} else {
super.onWindowClose();
Expand Down
3 changes: 2 additions & 1 deletion lib/apps_list/apps_list_page.dart
Expand Up @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import '../app/app.dart';
import '../settings/cubit/settings_cubit.dart';
import '../theme/theme.dart';
import '../window/nyrna_window.dart';
import 'apps_list.dart';

/// The main screen for Nyrna.
Expand Down Expand Up @@ -56,7 +57,7 @@ class _AppsListPageState extends State<AppsListPage>
final updatedWindowSize = WidgetsBinding.instance.window.physicalSize;
if (_appWindowSize != updatedWindowSize) {
_appWindowSize = updatedWindowSize;
settingsCubit.saveWindowSize();
NyrnaWindow.instance.saveWindowSize();
}
super.didChangeMetrics();
}
Expand Down
16 changes: 2 additions & 14 deletions lib/main.dart
Expand Up @@ -9,7 +9,6 @@ import 'package:helpers/helpers.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:window_manager/window_manager.dart';
import 'package:window_size/window_size.dart' as window;

import 'active_window/active_window.dart';
import 'app.dart';
Expand Down Expand Up @@ -59,14 +58,12 @@ Future<void> main(List<String> args) async {
exit(0);
} else {}

final nyrnaWindow = NyrnaWindow();
final nyrnaWindow = NyrnaWindow(storage);

// Created outside runApp so it can be accessed for window settings below.
final settingsCubit = await SettingsCubit.init(
desktopIntegration: await _initDesktopIntegration(),
getWindowInfo: window.getWindowInfo,
hotkeyService: HotkeyService(activeWindow),
nyrnaWindow: nyrnaWindow,
storage: storage,
);

Expand Down Expand Up @@ -109,18 +106,9 @@ Future<void> main(List<String> args) async {
final systemTray = SystemTrayManager(nyrnaWindow);
await systemTray.initialize();

final savedWindowSize = await settingsCubit.savedWindowSize();
if (savedWindowSize != null) {
window.setWindowFrame(savedWindowSize);
}

bool visible = true;
bool? startHiddenInTray = await storage.getValue('startHiddenInTray');
if (startHiddenInTray == true) {
visible = false;
}

window.setWindowVisibility(visible: visible);
if (startHiddenInTray != true) await nyrnaWindow.show();
}

/// Message to be displayed if Nyrna is called with an unknown argument.
Expand Down
32 changes: 2 additions & 30 deletions lib/settings/cubit/settings_cubit.dart
@@ -1,12 +1,9 @@
import 'dart:convert';
import 'dart:ui';

import 'package:desktop_integration/desktop_integration.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hotkey_manager/hotkey_manager.dart';
import 'package:window_size/window_size.dart' show PlatformWindow;

import '../../apps_list/apps_list.dart';
import '../../core/core.dart';
Expand All @@ -20,29 +17,23 @@ late SettingsCubit settingsCubit;

class SettingsCubit extends Cubit<SettingsState> {
final DesktopIntegration _desktopIntegration;
final Future<PlatformWindow> Function() _getWindowInfo;
final HotkeyService _hotkeyService;
final NyrnaWindow _nyrnaWindow;
final StorageRepository _storage;

SettingsCubit._(
this._desktopIntegration,
this._getWindowInfo,
this._hotkeyService,
this._nyrnaWindow,
this._storage, {
required SettingsState initialState,
}) : super(initialState) {
settingsCubit = this;
_hotkeyService.updateHotkey(state.hotKey);
_nyrnaWindow.preventClose(state.closeToTray);
NyrnaWindow.instance.preventClose(state.closeToTray);
}

static Future<SettingsCubit> init({
required DesktopIntegration desktopIntegration,
required Future<PlatformWindow> Function() getWindowInfo,
required HotkeyService hotkeyService,
required NyrnaWindow nyrnaWindow,
required StorageRepository storage,
}) async {
bool autoStart = await storage.getValue('autoStart') ?? false;
Expand All @@ -66,9 +57,7 @@ class SettingsCubit extends Cubit<SettingsState> {

return SettingsCubit._(
desktopIntegration,
getWindowInfo,
hotkeyService,
nyrnaWindow,
storage,
initialState: SettingsState(
autoStart: autoStart,
Expand Down Expand Up @@ -121,7 +110,7 @@ class SettingsCubit extends Cubit<SettingsState> {
Future<void> updateCloseToTray([bool? closeToTray]) async {
if (closeToTray == null) return;

await _nyrnaWindow.preventClose(closeToTray);
await NyrnaWindow.instance.preventClose(closeToTray);
await _storage.saveValue(key: 'closeToTray', value: closeToTray);
emit(state.copyWith(closeToTray: closeToTray));
}
Expand Down Expand Up @@ -160,21 +149,4 @@ class SettingsCubit extends Cubit<SettingsState> {
value: jsonEncode(newHotKey.toJson()),
);
}

/// Save the current window size & position to storage.
///
/// Allows the app to remember its window size for next launch.
Future<void> saveWindowSize() async {
final windowInfo = await _getWindowInfo();
final rectJson = windowInfo.frame.toJson();
await _storage.saveValue(key: 'windowSize', value: rectJson);
}

/// Returns if available the last window size and position.
Future<Rect?> savedWindowSize() async {
String? rectJson = await _storage.getValue('windowSize');
if (rectJson == null) return null;
final windowRect = RectConverter.fromJson(rectJson);
return windowRect;
}
}
45 changes: 34 additions & 11 deletions lib/window/nyrna_window.dart
Expand Up @@ -2,13 +2,20 @@ import 'dart:io';
import 'dart:ui';

import 'package:flutter_window_close/flutter_window_close.dart';
import 'package:window_size/window_size.dart';
import 'package:window_manager/window_manager.dart';

import '../core/helpers/json_converters.dart';
import '../logs/logs.dart';
import '../settings/settings.dart';
import '../storage/storage_repository.dart';

class NyrnaWindow {
NyrnaWindow() {
final StorageRepository _storage;

static late final NyrnaWindow instance;

NyrnaWindow(this._storage) {
instance = this;
_listenForWindowClose();
}

Expand All @@ -20,11 +27,12 @@ class NyrnaWindow {
/// some reason. Probably best to switch to only using `window_manager` if
/// it starts also working on Linux in the future.
FlutterWindowClose.setWindowShouldCloseHandler(() async {
if (!settingsCubit.state.closeToTray) return true;
await hide();
final shouldExitProgram = (settingsCubit.state.closeToTray) //
? false
: true;

hide();
await settingsCubit.saveWindowSize();
return false;
return shouldExitProgram;
});
}

Expand All @@ -35,14 +43,29 @@ class NyrnaWindow {
void close() => exit(0);

Future<void> hide() async {
setWindowVisibility(visible: false);
await settingsCubit.saveWindowSize();
await saveWindowSize();
await windowManager.hide();
}

Future<void> show() async {
final Rect? savedWindowSize = await settingsCubit.savedWindowSize();
if (savedWindowSize != null) setWindowFrame(savedWindowSize);
final Rect? savedWindowSize = await getSavedWindowSize();
if (savedWindowSize != null) windowManager.setBounds(savedWindowSize);
await windowManager.show();
}

Future<void> saveWindowSize() async {
final windowInfo = await windowManager.getBounds();
final rectJson = windowInfo.toJson();
log.v('Saving window info:\n$rectJson');
await _storage.saveValue(key: 'windowSize', value: rectJson);
}

setWindowVisibility(visible: true);
/// Returns if available the last window size and position.
Future<Rect?> getSavedWindowSize() async {
String? rectJson = await _storage.getValue('windowSize');
if (rectJson == null) return null;
log.v('Retrieved saved window info:\n$rectJson');
final windowRect = RectConverter.fromJson(rectJson);
return windowRect;
}
}
36 changes: 3 additions & 33 deletions test/settings/cubit/settings_cubit_test.dart
@@ -1,16 +1,12 @@
import 'dart:ui';

import 'package:desktop_integration/desktop_integration.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hotkey_manager/hotkey_manager.dart';
import 'package:mocktail/mocktail.dart';
import 'package:nyrna/apps_list/cubit/apps_list_cubit.dart';
import 'package:nyrna/core/helpers/helpers.dart';
import 'package:nyrna/hotkey/hotkey_service.dart';
import 'package:nyrna/settings/settings.dart';
import 'package:nyrna/storage/storage_repository.dart';
import 'package:nyrna/window/nyrna_window.dart';
import 'package:window_size/window_size.dart';

class FakeDesktopIntegration extends Fake implements DesktopIntegration {
@override
Expand All @@ -30,9 +26,7 @@ class MockNyrnaWindow extends Mock implements NyrnaWindow {}

class MockStorageRepository extends Mock implements StorageRepository {}

late Future<PlatformWindow> Function() getWindowInfo;
HotkeyService hotkeyService = MockHotkeyService();
NyrnaWindow nyrnaWindow = MockNyrnaWindow();
StorageRepository storage = MockStorageRepository();

/// Cubit being tested
Expand All @@ -41,28 +35,23 @@ late SettingsCubit cubit;
SettingsState get state => cubit.state;

void main() {
final fallbackPlatformWindow = PlatformWindow(
const Rect.fromLTWH(0, 0, 100, 100),
1,
null,
);

setUpAll((() {
NyrnaWindow.instance = MockNyrnaWindow();
appsListCubit = MockAppsListCubit();
when(() => appsListCubit.setAutoRefresh(
autoRefresh: any(named: 'autoRefresh'),
refreshInterval: any(named: 'refreshInterval'),
)).thenReturn(null);

getWindowInfo = () async => fallbackPlatformWindow;
registerFallbackValue(HotKey(KeyCode.abort));
}));

setUp((() async {
when(() => hotkeyService.updateHotkey(any())).thenAnswer((_) async {});
when(() => hotkeyService.removeHotkey()).thenAnswer((_) async {});

when(() => nyrnaWindow.preventClose(any())).thenAnswer((_) async {});
when(() => NyrnaWindow.instance.preventClose(any()))
.thenAnswer((_) async {});

when(() => storage.getValue('hotkey')).thenAnswer((_) async {});
when(() => storage.deleteValue(any())).thenAnswer((_) async {});
Expand Down Expand Up @@ -92,9 +81,7 @@ void main() {

cubit = await SettingsCubit.init(
desktopIntegration: FakeDesktopIntegration(),
getWindowInfo: getWindowInfo,
hotkeyService: hotkeyService,
nyrnaWindow: nyrnaWindow,
storage: storage,
);
}));
Expand Down Expand Up @@ -243,9 +230,7 @@ void main() {
);
cubit = await SettingsCubit.init(
desktopIntegration: FakeDesktopIntegration(),
getWindowInfo: getWindowInfo,
hotkeyService: hotkeyService,
nyrnaWindow: nyrnaWindow,
storage: storage,
);
expect(state.hotKey.keyCode, KeyCode.insert);
Expand All @@ -263,20 +248,5 @@ void main() {
verify(() => storage.deleteValue('hotkey')).called(1);
});
});
test('saveWindowSize works', () async {
await cubit.saveWindowSize();
verify(() => storage.saveValue(
key: 'windowSize',
value: fallbackPlatformWindow.frame.toJson(),
)).called(1);
});

test('savedWindowSize works', () async {
when(() => storage.getValue('windowSize')).thenAnswer(
(_) async => fallbackPlatformWindow.frame.toJson(),
);
final rect = await cubit.savedWindowSize();
expect(rect, fallbackPlatformWindow.frame);
});
});
}

0 comments on commit 30c26e5

Please sign in to comment.