Browse Source

Merge pull request #808 from aryaman31/side_context_menu

Added right click context menu feature request.
Vincent Chan 2 năm trước cách đây
mục cha
commit
ec3a84c557

+ 70 - 8
frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/disclosure_action.dart

@@ -3,6 +3,7 @@ import 'package:dartz/dartz.dart' as dartz;
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/icon_button.dart';
+import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:provider/provider.dart';
@@ -11,10 +12,13 @@ import 'item.dart';
 
 // [[Widget: LifeCycle]]
 // https://flutterbyexample.com/lesson/stateful-widget-lifecycle
-class ViewDisclosureButton extends StatelessWidget with ActionList<ViewDisclosureActionWrapper>, FlowyOverlayDelegate {
+class ViewDisclosureButton extends StatelessWidget
+    with ActionList<ViewDisclosureActionWrapper>, FlowyOverlayDelegate {
   final Function() onTap;
   final Function(dartz.Option<ViewDisclosureAction>) onSelected;
-  final _items = ViewDisclosureAction.values.map((action) => ViewDisclosureActionWrapper(action)).toList();
+  final _items = ViewDisclosureAction.values
+      .map((action) => ViewDisclosureActionWrapper(action))
+      .toList();
 
   ViewDisclosureButton({
     Key? key,
@@ -40,12 +44,13 @@ class ViewDisclosureButton extends StatelessWidget with ActionList<ViewDisclosur
   List<ViewDisclosureActionWrapper> get items => _items;
 
   @override
-  void Function(dartz.Option<ViewDisclosureActionWrapper> p1) get selectCallback => (result) {
-        result.fold(
-          () => onSelected(dartz.none()),
-          (wrapper) => onSelected(dartz.some(wrapper.inner)),
-        );
-      };
+  void Function(dartz.Option<ViewDisclosureActionWrapper> p1)
+      get selectCallback => (result) {
+            result.fold(
+              () => onSelected(dartz.none()),
+              (wrapper) => onSelected(dartz.some(wrapper.inner)),
+            );
+          };
 
   @override
   FlowyOverlayDelegate? get delegate => this;
@@ -56,6 +61,63 @@ class ViewDisclosureButton extends StatelessWidget with ActionList<ViewDisclosur
   }
 }
 
+class ViewDisclosureRegion extends StatelessWidget
+    with ActionList<ViewDisclosureActionWrapper>, FlowyOverlayDelegate {
+  final Widget child;
+  final Function() onTap;
+  final Function(dartz.Option<ViewDisclosureAction>) onSelected;
+  final _items = ViewDisclosureAction.values
+      .map((action) => ViewDisclosureActionWrapper(action))
+      .toList();
+
+  ViewDisclosureRegion(
+      {Key? key,
+      required this.onSelected,
+      required this.onTap,
+      required this.child})
+      : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Listener(
+      onPointerDown: (event) => {_handleClick(event, context)},
+      child: child,
+    );
+  }
+
+  @override
+  FlowyOverlayDelegate? get delegate => this;
+
+  @override
+  List<ViewDisclosureActionWrapper> get items => _items;
+
+  @override
+  void Function(dartz.Option<ViewDisclosureActionWrapper> p1)
+      get selectCallback => (result) {
+            result.fold(
+              () => onSelected(dartz.none()),
+              (wrapper) => onSelected(dartz.some(wrapper.inner)),
+            );
+          };
+
+  @override
+  void didRemove() {
+    onSelected(dartz.none());
+  }
+
+  void _handleClick(PointerDownEvent event, BuildContext context) {
+    if (event.kind == PointerDeviceKind.mouse &&
+        event.buttons == kSecondaryMouseButton) {
+      RenderBox box = context.findRenderObject() as RenderBox;
+      Offset position = box.localToGlobal(Offset.zero);
+      double x = event.position.dx - position.dx - box.size.width;
+      double y = event.position.dy - position.dy - box.size.height;
+      onTap();
+      show(context, anchorOffset: Offset(x, y));
+    }
+  }
+}
+
 class ViewDisclosureActionWrapper extends ActionItem {
   final ViewDisclosureAction inner;
 

+ 38 - 17
frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/item.dart

@@ -36,37 +36,57 @@ class ViewSectionItem extends StatelessWidget {
     final theme = context.watch<AppTheme>();
     return MultiBlocProvider(
       providers: [
-        BlocProvider(create: (ctx) => getIt<ViewBloc>(param1: view)..add(const ViewEvent.initial())),
+        BlocProvider(
+            create: (ctx) =>
+                getIt<ViewBloc>(param1: view)..add(const ViewEvent.initial())),
       ],
       child: BlocBuilder<ViewBloc, ViewState>(
         builder: (context, state) {
-          return Padding(
-            padding: const EdgeInsets.symmetric(vertical: 4),
-            child: InkWell(
-              onTap: () => onSelected(context.read<ViewBloc>().state.view),
-              child: FlowyHover(
-                style: HoverStyle(hoverColor: theme.bg3),
-                builder: (_, onHover) => _render(context, onHover, state, theme.iconColor),
-                setSelected: () => state.isEditing || isSelected,
-              ),
-            ),
-          );
+          return ViewDisclosureRegion(
+              onTap: () => context
+                  .read<ViewBloc>()
+                  .add(const ViewEvent.setIsEditing(true)),
+              onSelected: (action) {
+                context
+                    .read<ViewBloc>()
+                    .add(const ViewEvent.setIsEditing(false));
+                _handleAction(context, action);
+              },
+              child: Padding(
+                padding: const EdgeInsets.symmetric(vertical: 4),
+                child: InkWell(
+                  onTap: () => onSelected(context.read<ViewBloc>().state.view),
+                  child: FlowyHover(
+                    style: HoverStyle(hoverColor: theme.bg3),
+                    builder: (_, onHover) =>
+                        _render(context, onHover, state, theme.iconColor),
+                    setSelected: () => state.isEditing || isSelected,
+                  ),
+                ),
+              ));
         },
       ),
     );
   }
 
-  Widget _render(BuildContext context, bool onHover, ViewState state, Color iconColor) {
+  Widget _render(
+      BuildContext context, bool onHover, ViewState state, Color iconColor) {
     List<Widget> children = [
-      SizedBox(width: 16, height: 16, child: state.view.renderThumbnail(iconColor: iconColor)),
+      SizedBox(
+          width: 16,
+          height: 16,
+          child: state.view.renderThumbnail(iconColor: iconColor)),
       const HSpace(2),
-      Expanded(child: FlowyText.regular(state.view.name, fontSize: 12, overflow: TextOverflow.clip)),
+      Expanded(
+          child: FlowyText.regular(state.view.name,
+              fontSize: 12, overflow: TextOverflow.clip)),
     ];
 
     if (onHover || state.isEditing) {
       children.add(
         ViewDisclosureButton(
-          onTap: () => context.read<ViewBloc>().add(const ViewEvent.setIsEditing(true)),
+          onTap: () =>
+              context.read<ViewBloc>().add(const ViewEvent.setIsEditing(true)),
           onSelected: (action) {
             context.read<ViewBloc>().add(const ViewEvent.setIsEditing(false));
             _handleAction(context, action);
@@ -84,7 +104,8 @@ class ViewSectionItem extends StatelessWidget {
     );
   }
 
-  void _handleAction(BuildContext context, dartz.Option<ViewDisclosureAction> action) {
+  void _handleAction(
+      BuildContext context, dartz.Option<ViewDisclosureAction> action) {
     action.foldRight({}, (action, previous) {
       switch (action) {
         case ViewDisclosureAction.rename: