Skip to content

Commit

Permalink
feat: add option to keep suspended windows pinned to the top of the list
Browse files Browse the repository at this point in the history
Makes it easy to keep track of windows that are suspended.

Resolves #209
  • Loading branch information
Merrit committed Apr 1, 2024
1 parent 0aa7b20 commit 1266091
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 3 deletions.
23 changes: 20 additions & 3 deletions lib/apps_list/cubit/apps_list_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class AppsListCubit extends Cubit<AppsListState> {
windows[i] = await _refreshWindowProcess(windows[i]);
}

windows.sortWindows();
windows.sortWindows(_settingsCubit.state.pinSuspendedWindows);

emit(state.copyWith(windows: windows));
}
Expand Down Expand Up @@ -155,6 +155,8 @@ class AppsListCubit extends Cubit<AppsListState> {
[window],
);

windows.sortWindows(_settingsCubit.state.pinSuspendedWindows);

emit(state.copyWith(
windows: windows,
));
Expand Down Expand Up @@ -318,7 +320,22 @@ extension on List<InteractionError> {

extension on List<Window> {
/// Sort the windows by executable name.
void sortWindows() {
sortBy((window) => window.process.executable.toLowerCase());
///
/// If the user has enabled pinning suspended windows to the top of the list,
/// those windows will be sorted to the top.
void sortWindows(bool pinSuspendedWindows) {
sort((a, b) {
final aIsSuspended = a.process.status == ProcessStatus.suspended;
final bIsSuspended = b.process.status == ProcessStatus.suspended;

if (pinSuspendedWindows) {
if (aIsSuspended && !bIsSuspended) return -1;
if (!aIsSuspended && bIsSuspended) return 1;
}

return a.process.executable.toLowerCase().compareTo(
b.process.executable.toLowerCase(),
);
});
}
}
8 changes: 8 additions & 0 deletions lib/localization/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@
"@minimizeAndRestoreWindows": {
"description": "Label for the minimize / restore windows setting"
},
"pinSuspendedWindows": "Pin suspended windows",
"@pinSuspendedWindows": {
"description": "Whether to pin suspended windows to the top of the window list"
},
"pinSuspendedWindowsTooltip": "If enabled, suspended windows will always be shown at the top of the window list.",
"@pinSuspendedWindowsTooltip": {
"description": "Tooltip for the pin suspended windows setting"
},
"showHiddenWindows": "Show hidden windows",
"@showHiddenWindows": {
"description": "Label for the show hidden windows setting"
Expand Down
9 changes: 9 additions & 0 deletions lib/settings/cubit/settings_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class SettingsCubit extends Cubit<SettingsState> {

final bool minimizeWindows =
await storage.getValue('minimizeWindows') ?? true;
final bool pinSuspendedWindows =
await storage.getValue('pinSuspendedWindows') ?? false;
final int refreshInterval = await storage.getValue('refreshInterval') ?? 5;
final bool showHiddenWindows =
await storage.getValue('showHiddenWindows') ?? false;
Expand All @@ -80,6 +82,7 @@ class SettingsCubit extends Cubit<SettingsState> {
closeToTray: closeToTray,
hotKey: hotkey,
minimizeWindows: minimizeWindows,
pinSuspendedWindows: pinSuspendedWindows,
refreshInterval: refreshInterval,
showHiddenWindows: showHiddenWindows,
startHiddenInTray: startHiddenInTray,
Expand Down Expand Up @@ -141,6 +144,12 @@ class SettingsCubit extends Cubit<SettingsState> {
await _storage.saveValue(key: 'minimizeWindows', value: value);
}

/// Update the preference for pinning suspended windows to the top of the list.
Future<void> updatePinSuspendedWindows(bool value) async {
emit(state.copyWith(pinSuspendedWindows: value));
await _storage.saveValue(key: 'pinSuspendedWindows', value: value);
}

Future<void> updateShowHiddenWindows(bool value) async {
await _storage.saveValue(key: 'showHiddenWindows', value: value);
emit(state.copyWith(showHiddenWindows: value));
Expand Down
4 changes: 4 additions & 0 deletions lib/settings/cubit/settings_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class SettingsState with _$SettingsState {
/// restored when resuming.
required bool minimizeWindows,

/// If true suspended windows will be shown at the top of the list.
required bool pinSuspendedWindows,

/// How often to automatically refresh the list of open windows, in seconds.
required int refreshInterval,
required bool showHiddenWindows,
Expand All @@ -41,6 +44,7 @@ class SettingsState with _$SettingsState {
closeToTray: false,
hotKey: defaultHotkey,
minimizeWindows: true,
pinSuspendedWindows: false,
refreshInterval: 5,
showHiddenWindows: false,
startHiddenInTray: false,
Expand Down
44 changes: 44 additions & 0 deletions lib/settings/widgets/behaviour_section.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class BehaviourSection extends StatelessWidget {
},
),
),
const _PinSuspendedWindowsTile(),
const ShowHiddenTile(),
],
);
Expand All @@ -95,6 +96,47 @@ class BehaviourSection extends StatelessWidget {
}
}

/// Toggle pinning suspended windows to the top of the window list.
class _PinSuspendedWindowsTile extends StatelessWidget {
const _PinSuspendedWindowsTile();

@override
Widget build(BuildContext context) {
return ListTile(
title: Text.rich(
TextSpan(
children: [
TextSpan(
text: '${AppLocalizations.of(context)!.pinSuspendedWindows} ',
),
WidgetSpan(
child: Tooltip(
message:
AppLocalizations.of(context)!.pinSuspendedWindowsTooltip,
child: const Icon(
Icons.help_outline,
size: 18,
),
),
),
],
),
),
leading: const Icon(Icons.push_pin_outlined),
trailing: BlocBuilder<SettingsCubit, SettingsState>(
builder: (context, state) {
return Switch(
value: state.pinSuspendedWindows,
onChanged: (value) async {
await settingsCubit.updatePinSuspendedWindows(value);
},
);
},
),
);
}
}

class ShowHiddenTile extends StatelessWidget {
const ShowHiddenTile({super.key});

Expand Down Expand Up @@ -125,7 +167,9 @@ class ShowHiddenTile extends StatelessWidget {
return Switch(
value: state.showHiddenWindows,
onChanged: (value) async {
final appsListCubit = context.read<AppsListCubit>();
await settingsCubit.updateShowHiddenWindows(value);
await appsListCubit.manualRefresh();
},
);
},
Expand Down
1 change: 1 addition & 0 deletions test/apps_list/cubit/apps_list_cubit_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ void main() {
closeToTray: false,
hotKey: HotKey(KeyCode.again),
minimizeWindows: true,
pinSuspendedWindows: false,
refreshInterval: 5,
showHiddenWindows: false,
startHiddenInTray: false,
Expand Down

0 comments on commit 1266091

Please sign in to comment.