mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feature: Shortcut for collapse the left sidebar #692
This commit is contained in:
parent
4a00f3c2ca
commit
88423c1e86
@ -1,6 +1,7 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/user/presentation/splash_screen.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FlowyApp implements EntryPoint {
|
||||
@ -14,5 +15,8 @@ void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await EasyLocalization.ensureInitialized();
|
||||
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await hotKeyManager.unregisterAll();
|
||||
|
||||
await FlowyRunner.run(FlowyApp());
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:app_flowy/plugin/plugin.dart';
|
||||
import 'package:app_flowy/workspace/application/home/home_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/hotkeys.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/edit_pannel/pannel_animation.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/float_bubble/question_bubble.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
@ -21,7 +22,8 @@ import 'menu/menu.dart';
|
||||
class HomeScreen extends StatefulWidget {
|
||||
final UserProfilePB user;
|
||||
final CurrentWorkspaceSettingPB workspaceSetting;
|
||||
const HomeScreen(this.user, this.workspaceSetting, {Key? key}) : super(key: key);
|
||||
const HomeScreen(this.user, this.workspaceSetting, {Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<HomeScreen> createState() => _HomeScreenState();
|
||||
@ -47,11 +49,13 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
providers: [
|
||||
BlocProvider<HomeBloc>(
|
||||
create: (context) {
|
||||
return HomeBloc(widget.user, widget.workspaceSetting)..add(const HomeEvent.initial());
|
||||
return HomeBloc(widget.user, widget.workspaceSetting)
|
||||
..add(const HomeEvent.initial());
|
||||
},
|
||||
),
|
||||
],
|
||||
child: Scaffold(
|
||||
child: HomeHotKeys(
|
||||
child: Scaffold(
|
||||
body: BlocListener<HomeBloc, HomeState>(
|
||||
listenWhen: (p, c) => p.unauthorized != c.unauthorized,
|
||||
listener: (context, state) {
|
||||
@ -62,9 +66,12 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
child: BlocBuilder<HomeBloc, HomeState>(
|
||||
buildWhen: (previous, current) => previous != current,
|
||||
builder: (context, state) {
|
||||
final collapasedNotifier = getIt<HomeStackManager>().collapsedNotifier;
|
||||
final collapasedNotifier =
|
||||
getIt<HomeStackManager>().collapsedNotifier;
|
||||
collapasedNotifier.addPublishListener((isCollapsed) {
|
||||
context.read<HomeBloc>().add(HomeEvent.forceCollapse(isCollapsed));
|
||||
context
|
||||
.read<HomeBloc>()
|
||||
.add(HomeEvent.forceCollapse(isCollapsed));
|
||||
});
|
||||
return FlowyContainer(
|
||||
Theme.of(context).colorScheme.surface,
|
||||
@ -74,7 +81,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -107,7 +114,10 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHomeMenu({required HomeLayout layout, required BuildContext context, required HomeState state}) {
|
||||
Widget _buildHomeMenu(
|
||||
{required HomeLayout layout,
|
||||
required BuildContext context,
|
||||
required HomeState state}) {
|
||||
final workspaceSetting = state.workspaceSetting;
|
||||
if (initialView == null && workspaceSetting.hasLatestView()) {
|
||||
initialView = workspaceSetting.latestView;
|
||||
@ -124,7 +134,8 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
collapsedNotifier: getIt<HomeStackManager>().collapsedNotifier,
|
||||
);
|
||||
|
||||
final latestView = workspaceSetting.hasLatestView() ? workspaceSetting.latestView : null;
|
||||
final latestView =
|
||||
workspaceSetting.hasLatestView() ? workspaceSetting.latestView : null;
|
||||
if (getIt<MenuSharedState>().latestOpenView == null) {
|
||||
/// AppFlowy will open the view that the last time the user opened it. The _buildHomeMenu will get called when AppFlowy's screen resizes. So we only set the latestOpenView when it's null.
|
||||
getIt<MenuSharedState>().latestOpenView = latestView;
|
||||
@ -133,10 +144,14 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu));
|
||||
}
|
||||
|
||||
Widget _buildEditPannel({required HomeState homeState, required BuildContext context, required HomeLayout layout}) {
|
||||
Widget _buildEditPannel(
|
||||
{required HomeState homeState,
|
||||
required BuildContext context,
|
||||
required HomeLayout layout}) {
|
||||
final homeBloc = context.read<HomeBloc>();
|
||||
return BlocBuilder<HomeBloc, HomeState>(
|
||||
buildWhen: (previous, current) => previous.pannelContext != current.pannelContext,
|
||||
buildWhen: (previous, current) =>
|
||||
previous.pannelContext != current.pannelContext,
|
||||
builder: (context, state) {
|
||||
return state.pannelContext.fold(
|
||||
() => const SizedBox(),
|
||||
@ -144,7 +159,8 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
child: RepaintBoundary(
|
||||
child: EditPannel(
|
||||
pannelContext: pannelContext,
|
||||
onEndEdit: () => homeBloc.add(const HomeEvent.dismissEditPannel()),
|
||||
onEndEdit: () =>
|
||||
homeBloc.add(const HomeEvent.dismissEditPannel()),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -161,7 +177,9 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
child: GestureDetector(
|
||||
dragStartBehavior: DragStartBehavior.down,
|
||||
onPanUpdate: ((details) {
|
||||
context.read<HomeBloc>().add(HomeEvent.editPannelResized(details.delta.dx));
|
||||
context
|
||||
.read<HomeBloc>()
|
||||
.add(HomeEvent.editPannelResized(details.delta.dx));
|
||||
}),
|
||||
behavior: HitTestBehavior.translucent,
|
||||
child: SizedBox(
|
||||
@ -186,11 +204,21 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
closeX: -layout.menuWidth,
|
||||
isClosed: !layout.showMenu,
|
||||
)
|
||||
.positioned(left: 0, top: 0, width: layout.menuWidth, bottom: 0, animate: true)
|
||||
.positioned(
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: layout.menuWidth,
|
||||
bottom: 0,
|
||||
animate: true)
|
||||
.animate(layout.animDuration, Curves.easeOut),
|
||||
homeStack
|
||||
.constrained(minWidth: 500)
|
||||
.positioned(left: layout.homePageLOffset, right: layout.homePageROffset, bottom: 0, top: 0, animate: true)
|
||||
.positioned(
|
||||
left: layout.homePageLOffset,
|
||||
right: layout.homePageROffset,
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
animate: true)
|
||||
.animate(layout.animDuration, Curves.easeOut),
|
||||
homeMenuResizer.positioned(left: layout.homePageLOffset - 5),
|
||||
bubble
|
||||
@ -206,7 +234,8 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
closeX: layout.editPannelWidth,
|
||||
isClosed: !layout.showEditPannel,
|
||||
)
|
||||
.positioned(right: 0, top: 0, bottom: 0, width: layout.editPannelWidth),
|
||||
.positioned(
|
||||
right: 0, top: 0, bottom: 0, width: layout.editPannelWidth),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/home/home_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class HomeHotKeys extends StatelessWidget {
|
||||
final Widget child;
|
||||
const HomeHotKeys({required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
HotKey _hotKey = HotKey(
|
||||
KeyCode.backslash,
|
||||
modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control],
|
||||
// Set hotkey scope (default is HotKeyScope.system)
|
||||
scope: HotKeyScope.inapp, // Set as inapp-wide hotkey.
|
||||
);
|
||||
hotKeyManager.register(
|
||||
_hotKey,
|
||||
keyDownHandler: (hotKey) {
|
||||
context.read<HomeBloc>().add(const HomeEvent.collapseMenu());
|
||||
getIt<HomeStackManager>().collapsedNotifier.value =
|
||||
!getIt<HomeStackManager>().collapsedNotifier.currentValue!;
|
||||
},
|
||||
);
|
||||
return child;
|
||||
}
|
||||
}
|
@ -49,7 +49,8 @@ class HomeMenu extends StatelessWidget {
|
||||
providers: [
|
||||
BlocProvider<MenuBloc>(
|
||||
create: (context) {
|
||||
final menuBloc = getIt<MenuBloc>(param1: user, param2: workspaceSetting.workspace.id);
|
||||
final menuBloc = getIt<MenuBloc>(
|
||||
param1: user, param2: workspaceSetting.workspace.id);
|
||||
menuBloc.add(const MenuEvent.initial());
|
||||
return menuBloc;
|
||||
},
|
||||
@ -106,18 +107,22 @@ class HomeMenu extends StatelessWidget {
|
||||
|
||||
Widget _renderApps(BuildContext context) {
|
||||
return ExpandableTheme(
|
||||
data: ExpandableThemeData(useInkWell: true, animationDuration: Durations.medium),
|
||||
data: ExpandableThemeData(
|
||||
useInkWell: true, animationDuration: Durations.medium),
|
||||
child: Expanded(
|
||||
child: ScrollConfiguration(
|
||||
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
||||
child: BlocSelector<MenuBloc, MenuState, List<Widget>>(
|
||||
selector: (state) => state.apps.map((app) => MenuApp(app, key: ValueKey(app.id))).toList(),
|
||||
selector: (state) => state.apps
|
||||
.map((app) => MenuApp(app, key: ValueKey(app.id)))
|
||||
.toList(),
|
||||
builder: (context, menuItems) {
|
||||
return ReorderableListView.builder(
|
||||
itemCount: menuItems.length,
|
||||
buildDefaultDragHandles: false,
|
||||
header: Padding(
|
||||
padding: EdgeInsets.only(bottom: 20.0 - MenuAppSizes.appVPadding),
|
||||
padding:
|
||||
EdgeInsets.only(bottom: 20.0 - MenuAppSizes.appVPadding),
|
||||
child: MenuUser(user),
|
||||
),
|
||||
onReorder: (oldIndex, newIndex) {
|
||||
@ -126,7 +131,9 @@ class HomeMenu extends StatelessWidget {
|
||||
// receive: oldIndex: 0, newIndex: 2
|
||||
// Workaround: if newIndex > oldIndex, we just minus one
|
||||
int index = newIndex > oldIndex ? newIndex - 1 : newIndex;
|
||||
context.read<MenuBloc>().add(MenuEvent.moveApp(oldIndex, index));
|
||||
context
|
||||
.read<MenuBloc>()
|
||||
.add(MenuEvent.moveApp(oldIndex, index));
|
||||
},
|
||||
physics: StyledScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
@ -134,7 +141,8 @@ class HomeMenu extends StatelessWidget {
|
||||
key: ValueKey(menuItems[index].key),
|
||||
index: index,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: MenuAppSizes.appVPadding / 2),
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: MenuAppSizes.appVPadding / 2),
|
||||
child: menuItems[index],
|
||||
),
|
||||
);
|
||||
@ -149,7 +157,8 @@ class HomeMenu extends StatelessWidget {
|
||||
|
||||
Widget _renderNewAppButton(BuildContext context) {
|
||||
return NewAppButton(
|
||||
press: (appName) => context.read<MenuBloc>().add(MenuEvent.createApp(appName, desc: "")),
|
||||
press: (appName) =>
|
||||
context.read<MenuBloc>().add(MenuEvent.createApp(appName, desc: "")),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -208,12 +217,22 @@ class MenuTopBar extends StatelessWidget {
|
||||
children: [
|
||||
renderIcon(context),
|
||||
const Spacer(),
|
||||
FlowyIconButton(
|
||||
width: 28,
|
||||
onPressed: () => context.read<HomeBloc>().add(const HomeEvent.collapseMenu()),
|
||||
iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
|
||||
icon: svgWidget("home/hide_menu", color: theme.iconColor),
|
||||
)
|
||||
Tooltip(
|
||||
richMessage: TextSpan(children: [
|
||||
const TextSpan(text: "Close sidebar\n"),
|
||||
TextSpan(
|
||||
text: Platform.isMacOS ? "⌘+\\" : "Ctrl+\\",
|
||||
style: const TextStyle(color: Colors.white60),
|
||||
),
|
||||
]),
|
||||
child: FlowyIconButton(
|
||||
width: 28,
|
||||
onPressed: () => context
|
||||
.read<HomeBloc>()
|
||||
.add(const HomeEvent.collapseMenu()),
|
||||
iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
|
||||
icon: svgWidget("home/hide_menu", color: theme.iconColor),
|
||||
))
|
||||
],
|
||||
)),
|
||||
);
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:app_flowy/workspace/application/home/home_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
@ -14,7 +16,8 @@ typedef NaviAction = void Function();
|
||||
class NavigationNotifier with ChangeNotifier {
|
||||
List<NavigationItem> navigationItems;
|
||||
PublishNotifier<bool> collapasedNotifier;
|
||||
NavigationNotifier({required this.navigationItems, required this.collapasedNotifier});
|
||||
NavigationNotifier(
|
||||
{required this.navigationItems, required this.collapasedNotifier});
|
||||
|
||||
void update(HomeStackNotifier notifier) {
|
||||
bool shouldNotify = false;
|
||||
@ -69,7 +72,8 @@ class FlowyNavigation extends StatelessWidget {
|
||||
child: Row(children: [
|
||||
Selector<NavigationNotifier, PublishNotifier<bool>>(
|
||||
selector: (context, notifier) => notifier.collapasedNotifier,
|
||||
builder: (ctx, collapsedNotifier, child) => _renderCollapse(ctx, collapsedNotifier, theme)),
|
||||
builder: (ctx, collapsedNotifier, child) =>
|
||||
_renderCollapse(ctx, collapsedNotifier, theme)),
|
||||
Selector<NavigationNotifier, List<NavigationItem>>(
|
||||
selector: (context, notifier) => notifier.navigationItems,
|
||||
builder: (ctx, items, child) => Expanded(
|
||||
@ -84,7 +88,8 @@ class FlowyNavigation extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderCollapse(BuildContext context, PublishNotifier<bool> collapsedNotifier, AppTheme theme) {
|
||||
Widget _renderCollapse(BuildContext context,
|
||||
PublishNotifier<bool> collapsedNotifier, AppTheme theme) {
|
||||
return ChangeNotifierProvider.value(
|
||||
value: collapsedNotifier,
|
||||
child: Consumer(
|
||||
@ -92,15 +97,23 @@ class FlowyNavigation extends StatelessWidget {
|
||||
if (notifier.currentValue ?? false) {
|
||||
return RotationTransition(
|
||||
turns: const AlwaysStoppedAnimation(180 / 360),
|
||||
child: FlowyIconButton(
|
||||
width: 24,
|
||||
onPressed: () {
|
||||
notifier.value = false;
|
||||
ctx.read<HomeBloc>().add(const HomeEvent.collapseMenu());
|
||||
},
|
||||
iconPadding: const EdgeInsets.fromLTRB(2, 2, 2, 2),
|
||||
icon: svgWidget("home/hide_menu", color: theme.iconColor),
|
||||
),
|
||||
child: Tooltip(
|
||||
richMessage: TextSpan(children: [
|
||||
const TextSpan(text: "Open sidebar\n"),
|
||||
TextSpan(
|
||||
text: Platform.isMacOS ? "⌘+\\" : "Ctrl+\\",
|
||||
style: const TextStyle(color: Colors.white60),
|
||||
),
|
||||
]),
|
||||
child: FlowyIconButton(
|
||||
width: 24,
|
||||
onPressed: () {
|
||||
notifier.value = false;
|
||||
ctx.read<HomeBloc>().add(const HomeEvent.collapseMenu());
|
||||
},
|
||||
iconPadding: const EdgeInsets.fromLTRB(2, 2, 2, 2),
|
||||
icon: svgWidget("home/hide_menu", color: theme.iconColor),
|
||||
)),
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
@ -154,7 +167,8 @@ class NaviItemWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(child: item.leftBarItem.padding(horizontal: 2, vertical: 2));
|
||||
return Expanded(
|
||||
child: item.leftBarItem.padding(horizontal: 2, vertical: 2));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -555,6 +555,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
hotkey_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: hotkey_manager
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.7"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -76,6 +76,7 @@ dependencies:
|
||||
table_calendar: ^3.0.5
|
||||
reorderables: ^0.5.0
|
||||
linked_scroll_controller: ^0.2.0
|
||||
hotkey_manager: ^0.1.7
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^1.0.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user