mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-07-29 11:59:00 +00:00

* fix: only show collapse page button when the children of the page is not emtpy * chore: set minimum sidebar width to 268 * chore: replace space lock icon and pin & unpin icon * chore: change divider color * chore: update divider color * chore: improve create space color * feat: highlight delete button when hovering * chore: update translations * fix: icon align issue * feat: highlight sidebar resizer when hovering * feat: add border to popover * feat: optimize scroll bar * feat: improve scrollbar hover color * feat: support creating a new page via cmd+n * chore: improve scrollbar color * feat: improve tooltip style * chore: fix unit test * chore: bump version 0.6.6
240 lines
6.8 KiB
Dart
240 lines
6.8 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:appflowy/startup/startup.dart';
|
|
import 'package:appflowy/startup/tasks/app_window_size_manager.dart';
|
|
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';
|
|
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
|
import 'package:appflowy/workspace/application/sidebar/rename_view/rename_view_bloc.dart';
|
|
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_setting.dart';
|
|
import 'package:appflowy_backend/log.dart';
|
|
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:scaled_app/scaled_app.dart';
|
|
|
|
typedef KeyDownHandler = void Function(HotKey hotKey);
|
|
|
|
ValueNotifier<int> switchToTheNextSpace = ValueNotifier(0);
|
|
ValueNotifier<int> createNewPageNotifier = ValueNotifier(0);
|
|
|
|
/// Helper class that utilizes the global [HotKeyManager] to easily
|
|
/// add a [HotKey] with different handlers.
|
|
///
|
|
/// Makes registration of a [HotKey] simple and easy to read, and makes
|
|
/// sure the [KeyDownHandler], and other handlers, are grouped with the
|
|
/// relevant [HotKey].
|
|
///
|
|
class HotKeyItem {
|
|
HotKeyItem({
|
|
required this.hotKey,
|
|
this.keyDownHandler,
|
|
});
|
|
|
|
final HotKey hotKey;
|
|
final KeyDownHandler? keyDownHandler;
|
|
|
|
void register() =>
|
|
hotKeyManager.register(hotKey, keyDownHandler: keyDownHandler);
|
|
}
|
|
|
|
class HomeHotKeys extends StatefulWidget {
|
|
const HomeHotKeys({
|
|
super.key,
|
|
required this.userProfile,
|
|
required this.child,
|
|
});
|
|
|
|
final UserProfilePB userProfile;
|
|
final Widget child;
|
|
|
|
@override
|
|
State<HomeHotKeys> createState() => _HomeHotKeysState();
|
|
}
|
|
|
|
class _HomeHotKeysState extends State<HomeHotKeys> {
|
|
final windowSizeManager = WindowSizeManager();
|
|
|
|
late final items = [
|
|
// Collapse sidebar menu (using slash)
|
|
HotKeyItem(
|
|
hotKey: HotKey(
|
|
KeyCode.backslash,
|
|
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) => context
|
|
.read<HomeSettingBloc>()
|
|
.add(const HomeSettingEvent.collapseMenu()),
|
|
),
|
|
|
|
// Collapse sidebar menu (using .)
|
|
HotKeyItem(
|
|
hotKey: HotKey(
|
|
KeyCode.period,
|
|
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) => context
|
|
.read<HomeSettingBloc>()
|
|
.add(const HomeSettingEvent.collapseMenu()),
|
|
),
|
|
|
|
// Toggle theme mode light/dark
|
|
HotKeyItem(
|
|
hotKey: HotKey(
|
|
KeyCode.keyL,
|
|
modifiers: [
|
|
Platform.isMacOS ? KeyModifier.meta : KeyModifier.control,
|
|
KeyModifier.shift,
|
|
],
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) =>
|
|
context.read<AppearanceSettingsCubit>().toggleThemeMode(),
|
|
),
|
|
|
|
// Close current tab
|
|
HotKeyItem(
|
|
hotKey: HotKey(
|
|
KeyCode.keyW,
|
|
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) =>
|
|
context.read<TabsBloc>().add(const TabsEvent.closeCurrentTab()),
|
|
),
|
|
|
|
// Go to previous tab
|
|
HotKeyItem(
|
|
hotKey: HotKey(
|
|
KeyCode.pageUp,
|
|
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) => _selectTab(context, -1),
|
|
),
|
|
|
|
// Go to next tab
|
|
HotKeyItem(
|
|
hotKey: HotKey(
|
|
KeyCode.pageDown,
|
|
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) => _selectTab(context, 1),
|
|
),
|
|
|
|
// Rename current view
|
|
HotKeyItem(
|
|
hotKey: HotKey(
|
|
KeyCode.f2,
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) =>
|
|
getIt<RenameViewBloc>().add(const RenameViewEvent.open()),
|
|
),
|
|
|
|
// Scale up/down the app
|
|
// In some keyboards, the system returns equal as + keycode, while others may return add as + keycode, so add them both as zoom in key.
|
|
...[KeyCode.equal, KeyCode.add].map(
|
|
(keycode) => HotKeyItem(
|
|
hotKey: HotKey(
|
|
keycode,
|
|
modifiers: [
|
|
Platform.isMacOS ? KeyModifier.meta : KeyModifier.control,
|
|
],
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) => _scaleWithStep(0.1),
|
|
),
|
|
),
|
|
|
|
HotKeyItem(
|
|
hotKey: HotKey(
|
|
KeyCode.minus,
|
|
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) => _scaleWithStep(-0.1),
|
|
),
|
|
|
|
// Reset app scaling
|
|
HotKeyItem(
|
|
hotKey: HotKey(
|
|
KeyCode.digit0,
|
|
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) => _scaleToSize(1),
|
|
),
|
|
|
|
// Switch to the next space
|
|
HotKeyItem(
|
|
hotKey: HotKey(
|
|
KeyCode.keyO,
|
|
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) => switchToTheNextSpace.value++,
|
|
),
|
|
|
|
// Create a new page
|
|
HotKeyItem(
|
|
hotKey: HotKey(
|
|
KeyCode.keyN,
|
|
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
|
|
scope: HotKeyScope.inapp,
|
|
),
|
|
keyDownHandler: (_) => createNewPageNotifier.value++,
|
|
),
|
|
|
|
// Open settings dialog
|
|
openSettingsHotKey(context, widget.userProfile),
|
|
];
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_registerHotKeys(context);
|
|
}
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
_registerHotKeys(context);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) => widget.child;
|
|
|
|
void _registerHotKeys(BuildContext context) {
|
|
for (final element in items) {
|
|
element.register();
|
|
}
|
|
}
|
|
|
|
void _selectTab(BuildContext context, int change) {
|
|
final bloc = context.read<TabsBloc>();
|
|
bloc.add(TabsEvent.selectTab(bloc.state.currentIndex + change));
|
|
}
|
|
|
|
Future<void> _scaleWithStep(double step) async {
|
|
final currentScaleFactor = await windowSizeManager.getScaleFactor();
|
|
final textScale = (currentScaleFactor + step).clamp(
|
|
WindowSizeManager.minScaleFactor,
|
|
WindowSizeManager.maxScaleFactor,
|
|
);
|
|
|
|
Log.info('scale the app from $currentScaleFactor to $textScale');
|
|
|
|
await _scaleToSize(textScale);
|
|
}
|
|
|
|
Future<void> _scaleToSize(double size) async {
|
|
ScaledWidgetsFlutterBinding.instance.scaleFactor = (_) => size;
|
|
await windowSizeManager.setScaleFactor(size);
|
|
}
|
|
}
|