Sfoglia il codice sorgente

[flutter]: config popup window

appflowy 3 anni fa
parent
commit
7a214ba3f4

+ 1 - 2
app_flowy/lib/startup/tasks/application_task.dart

@@ -36,11 +36,10 @@ class ApplicationWidget extends StatelessWidget {
     // setWindowFrame(const Rect.fromLTWH(0, 0, launchWidth, launchWidth / ratio));
 
     final theme = AppTheme.fromType(ThemeType.light);
-    FlowyOverlayConfig config = FlowyOverlayConfig(barrierColor: Colors.transparent);
     return Provider.value(
       value: theme,
       child: MaterialApp(
-        builder: overlayManagerBuilder(config: config),
+        builder: overlayManagerBuilder(),
         debugShowCheckedModeBanner: false,
         theme: theme.themeData,
         navigatorKey: AppGlobals.rootNavKey,

+ 77 - 0
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/action.dart

@@ -0,0 +1,77 @@
+import 'package:dartz/dartz.dart' as dartz;
+import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flowy_infra_ui/style_widget/hover.dart';
+import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:provider/provider.dart';
+import 'package:styled_widget/styled_widget.dart';
+import 'package:app_flowy/workspace/domain/view_edit.dart';
+
+class ViewActionList implements FlowyOverlayDelegate {
+  final Function(dartz.Option<ViewAction>) onSelected;
+  final BuildContext anchorContext;
+  final String _identifier = 'ViewActionList';
+
+  const ViewActionList({required this.anchorContext, required this.onSelected});
+
+  void show(BuildContext buildContext) {
+    final items = ViewAction.values
+        .map((action) => ActionItem(
+            action: action,
+            onSelected: (action) {
+              FlowyOverlay.of(buildContext).remove(_identifier);
+              onSelected(dartz.some(action));
+            }))
+        .toList();
+
+    ListOverlay.showWithAnchor(
+      buildContext,
+      identifier: _identifier,
+      itemCount: items.length,
+      itemBuilder: (context, index) => items[index],
+      anchorContext: anchorContext,
+      anchorDirection: AnchorDirection.bottomRight,
+      maxWidth: 120,
+      maxHeight: 80,
+      delegate: this,
+    );
+  }
+
+  @override
+  void didRemove() {
+    onSelected(dartz.none());
+  }
+}
+
+class ActionItem extends StatelessWidget {
+  final ViewAction action;
+  final Function(ViewAction) onSelected;
+  const ActionItem({
+    Key? key,
+    required this.action,
+    required this.onSelected,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = context.watch<AppTheme>();
+
+    return FlowyHover(
+      config: HoverDisplayConfig(hoverColor: theme.hover),
+      builder: (context, onHover) {
+        return GestureDetector(
+          onTap: () => onSelected(action),
+          child: FlowyText.medium(
+            action.name,
+            fontSize: 12,
+          ).padding(
+            horizontal: 10,
+            vertical: 6,
+          ),
+        );
+      },
+    );
+  }
+}

+ 13 - 64
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/item.dart

@@ -2,10 +2,10 @@ import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/view/view_bloc.dart';
 import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
 import 'package:app_flowy/workspace/domain/view_ext.dart';
+import 'package:app_flowy/workspace/presentation/widgets/pop_up_window.dart';
 import 'package:dartz/dartz.dart' as dartz;
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/hover.dart';
 import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
@@ -20,6 +20,8 @@ import 'package:app_flowy/workspace/domain/image.dart';
 import 'package:app_flowy/workspace/domain/view_edit.dart';
 import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/menu_app.dart';
 
+import 'action.dart';
+
 // ignore: must_be_immutable
 class ViewSectionItem extends StatelessWidget {
   final bool isSelected;
@@ -89,8 +91,12 @@ class ViewSectionItem extends StatelessWidget {
     action.foldRight({}, (action, previous) {
       switch (action) {
         case ViewAction.rename:
-
-          // TODO: Handle this case.
+          FlowyPoppuWindow.show(
+            context,
+            child: ViewRenamePannel(renameCallback: (name) {
+              context.read<ViewBloc>().add(ViewEvent.rename(name));
+            }),
+          );
           break;
         case ViewAction.delete:
           context.read<ViewBloc>().add(const ViewEvent.delete());
@@ -128,69 +134,12 @@ class ViewDisclosureButton extends StatelessWidget {
   }
 }
 
-class ViewActionList implements FlowyOverlayDelegate {
-  final Function(dartz.Option<ViewAction>) onSelected;
-  final BuildContext anchorContext;
-  final String _identifier = 'ViewActionList';
-
-  const ViewActionList({required this.anchorContext, required this.onSelected});
-
-  void show(BuildContext buildContext) {
-    final items = ViewAction.values
-        .map((action) => ActionItem(
-            action: action,
-            onSelected: (action) {
-              FlowyOverlay.of(buildContext).remove(_identifier);
-              onSelected(dartz.some(action));
-            }))
-        .toList();
-
-    ListOverlay.showWithAnchor(
-      buildContext,
-      identifier: _identifier,
-      itemCount: items.length,
-      itemBuilder: (context, index) => items[index],
-      anchorContext: anchorContext,
-      anchorDirection: AnchorDirection.bottomRight,
-      maxWidth: 120,
-      maxHeight: 80,
-      delegate: this,
-    );
-  }
-
-  @override
-  void didRemove() {
-    onSelected(dartz.none());
-  }
-}
-
-class ActionItem extends StatelessWidget {
-  final ViewAction action;
-  final Function(ViewAction) onSelected;
-  const ActionItem({
-    Key? key,
-    required this.action,
-    required this.onSelected,
-  }) : super(key: key);
+class ViewRenamePannel extends StatelessWidget {
+  final void Function(String) renameCallback;
+  const ViewRenamePannel({Key? key, required this.renameCallback}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
-    final theme = context.watch<AppTheme>();
-
-    return FlowyHover(
-      config: HoverDisplayConfig(hoverColor: theme.hover),
-      builder: (context, onHover) {
-        return GestureDetector(
-          onTap: () => onSelected(action),
-          child: FlowyText.medium(
-            action.name,
-            fontSize: 12,
-          ).padding(
-            horizontal: 10,
-            vertical: 6,
-          ),
-        );
-      },
-    );
+    return SizedBox(width: 100, height: 200, child: Container(color: Colors.black));
   }
 }

+ 7 - 14
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/section.dart

@@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:provider/provider.dart';
+import 'package:styled_widget/styled_widget.dart';
 import 'item.dart';
 
 class ViewListNotifier extends ChangeNotifier {
@@ -47,10 +48,7 @@ class ViewSection extends StatelessWidget {
     // The ViewListNotifier will be updated after ViewListData changed passed by parent widget
     return ChangeNotifierProxyProvider<ViewListNotifier, ViewSectionNotifier>(
       create: (_) {
-        final views = Provider.of<ViewListNotifier>(
-          context,
-          listen: false,
-        ).items;
+        final views = Provider.of<ViewListNotifier>(context, listen: false).items;
         return ViewSectionNotifier(views);
       },
       update: (_, notifier, controller) => controller!..update(notifier),
@@ -61,20 +59,15 @@ class ViewSection extends StatelessWidget {
   }
 
   Widget _renderSectionItems(BuildContext context, List<View> views) {
-    var viewWidgets = views.map((view) {
-      final item = ViewSectionItem(
+    var viewWidgets = views.map(
+      (view) => ViewSectionItem(
         view: view,
         isSelected: _isViewSelected(context, view.id),
         onSelected: (view) => context.read<ViewSectionNotifier>().setSelectedView(view),
-      );
-
-      return Padding(
-        padding: const EdgeInsets.symmetric(vertical: 4),
-        child: item,
-      );
-    }).toList(growable: false);
+      ).padding(vertical: 4),
+    );
 
-    return Column(children: viewWidgets);
+    return Column(children: viewWidgets.toList(growable: false));
   }
 
   bool _isViewSelected(BuildContext context, String viewId) {

+ 28 - 0
app_flowy/lib/workspace/presentation/widgets/pop_up_window.dart

@@ -0,0 +1,28 @@
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flutter/material.dart';
+import 'package:window_size/window_size.dart';
+
+class FlowyPoppuWindow extends StatelessWidget {
+  final Widget child;
+  const FlowyPoppuWindow({Key? key, required this.child}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return child;
+  }
+
+  static Future<void> show(
+    BuildContext context, {
+    required Widget child,
+  }) async {
+    final window = await getWindowInfo();
+    FlowyOverlay.of(context).insertWithRect(
+      widget: FlowyPoppuWindow(child: child),
+      identifier: 'FlowyPoppuWindow',
+      anchorPosition: Offset.zero,
+      anchorSize: window.frame.size,
+      anchorDirection: AnchorDirection.center,
+      style: FlowyOverlayStyle(blur: true),
+    );
+  }
+}

+ 40 - 19
app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_overlay.dart

@@ -3,6 +3,7 @@
 import 'package:dartz/dartz.dart' show Tuple3;
 import 'package:flowy_infra_ui/src/flowy_overlay/layout.dart';
 import 'package:flutter/material.dart';
+import 'dart:ui';
 
 /// Specifies how overlay are anchored to the SourceWidget
 enum AnchorDirection {
@@ -11,6 +12,7 @@ enum AnchorDirection {
   topRight,
   bottomLeft,
   bottomRight,
+  center,
 
   // Edge aligned with a edge of the SourceWidget
   topWithLeftAligned,
@@ -55,21 +57,20 @@ enum OnBackBehavior {
   dismiss,
 }
 
-class FlowyOverlayConfig {
+class FlowyOverlayStyle {
   final Color barrierColor;
+  bool blur;
 
-  FlowyOverlayConfig({required this.barrierColor});
-
-  const FlowyOverlayConfig.defualt() : barrierColor = Colors.transparent;
+  FlowyOverlayStyle({this.barrierColor = Colors.transparent, this.blur = false});
 }
 
 final GlobalKey<FlowyOverlayState> _key = GlobalKey<FlowyOverlayState>();
 
 /// Invoke this method in app generation process
-TransitionBuilder overlayManagerBuilder({FlowyOverlayConfig config = const FlowyOverlayConfig.defualt()}) {
+TransitionBuilder overlayManagerBuilder() {
   return (context, child) {
     assert(child != null, 'Child can\'t be null.');
-    return FlowyOverlay(key: _key, child: child!, config: config);
+    return FlowyOverlay(key: _key, child: child!);
   };
 }
 
@@ -78,12 +79,10 @@ abstract class FlowyOverlayDelegate {
 }
 
 class FlowyOverlay extends StatefulWidget {
-  const FlowyOverlay({Key? key, required this.child, required this.config}) : super(key: key);
+  const FlowyOverlay({Key? key, required this.child}) : super(key: key);
 
   final Widget child;
 
-  final FlowyOverlayConfig config;
-
   static FlowyOverlayState of(BuildContext context, {bool rootOverlay = false}) {
     FlowyOverlayState? state = maybeOf(context, rootOverlay: rootOverlay);
     assert(() {
@@ -113,6 +112,7 @@ class FlowyOverlay extends StatefulWidget {
 
 class FlowyOverlayState extends State<FlowyOverlay> {
   List<Tuple3<Widget, String, FlowyOverlayDelegate?>> _overlayList = [];
+  FlowyOverlayStyle style = FlowyOverlayStyle();
 
   /// Insert a overlay widget which frame is set by the widget, not the component.
   /// Be sure to specify the offset and size using a anchorable widget (like `Postition`, `CompositedTransformFollower`)
@@ -137,7 +137,12 @@ class FlowyOverlayState extends State<FlowyOverlay> {
     AnchorDirection? anchorDirection,
     FlowyOverlayDelegate? delegate,
     OverlapBehaviour? overlapBehaviour,
+    FlowyOverlayStyle? style,
   }) {
+    if (style != null) {
+      this.style = style;
+    }
+
     _showOverlay(
       widget: widget,
       identifier: identifier,
@@ -157,7 +162,10 @@ class FlowyOverlayState extends State<FlowyOverlay> {
     AnchorDirection? anchorDirection,
     FlowyOverlayDelegate? delegate,
     OverlapBehaviour? overlapBehaviour,
+    FlowyOverlayStyle? style,
   }) {
+    this.style = style ?? FlowyOverlayStyle();
+
     _showOverlay(
       widget: widget,
       identifier: identifier,
@@ -245,17 +253,30 @@ class FlowyOverlayState extends State<FlowyOverlay> {
   @override
   Widget build(BuildContext context) {
     final overlays = _overlayList.map((ele) => ele.value1);
-    final children = <Widget>[
-      widget.child,
-      if (overlays.isNotEmpty)
-        Container(
-          color: widget.config.barrierColor,
-          child: GestureDetector(
-            behavior: HitTestBehavior.opaque,
-            onTap: _handleTapOnBackground,
-          ),
+    List<Widget> children = <Widget>[widget.child];
+
+    Widget? child;
+    if (overlays.isNotEmpty) {
+      child = Container(
+        color: style.barrierColor,
+        child: GestureDetector(
+          behavior: HitTestBehavior.opaque,
+          onTap: _handleTapOnBackground,
         ),
-    ];
+      );
+
+      if (style.blur) {
+        child = BackdropFilter(
+          child: child,
+          filter: ImageFilter.blur(sigmaX: 4, sigmaY: 4),
+        );
+      }
+    }
+
+    if (child != null) {
+      children.add(child);
+    }
+
     return Stack(
       children: children..addAll(overlays),
     );

+ 9 - 0
app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/layout.dart

@@ -55,6 +55,12 @@ class OverlayLayoutDelegate extends SingleChildLayoutDelegate {
               constraints.maxHeight - anchorRect.bottom,
             ));
             break;
+          case AnchorDirection.center:
+            childConstraints = BoxConstraints.loose(Size(
+              constraints.maxWidth,
+              constraints.maxHeight,
+            ));
+            break;
           case AnchorDirection.topWithLeftAligned:
             childConstraints = BoxConstraints.loose(Size(
               constraints.maxWidth - anchorRect.left,
@@ -165,6 +171,9 @@ class OverlayLayoutDelegate extends SingleChildLayoutDelegate {
           anchorRect.bottom,
         );
         break;
+      case AnchorDirection.center:
+        position = anchorRect.center;
+        break;
       case AnchorDirection.topWithLeftAligned:
         position = Offset(
           anchorRect.left,

+ 4 - 0
app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/list_overlay.dart

@@ -55,6 +55,7 @@ class ListOverlay extends StatelessWidget {
     AnchorDirection? anchorDirection,
     FlowyOverlayDelegate? delegate,
     OverlapBehaviour? overlapBehaviour,
+    FlowyOverlayStyle? style,
   }) {
     FlowyOverlay.of(context).insertWithAnchor(
       widget: ListOverlay(
@@ -69,6 +70,7 @@ class ListOverlay extends StatelessWidget {
       anchorDirection: anchorDirection,
       delegate: delegate,
       overlapBehaviour: overlapBehaviour,
+      style: style,
     );
   }
 
@@ -86,6 +88,7 @@ class ListOverlay extends StatelessWidget {
     AnchorDirection? anchorDirection,
     FlowyOverlayDelegate? delegate,
     OverlapBehaviour? overlapBehaviour,
+    FlowyOverlayStyle? style,
   }) {
     FlowyOverlay.of(context).insertWithRect(
       widget: ListOverlay(
@@ -101,6 +104,7 @@ class ListOverlay extends StatelessWidget {
       anchorDirection: anchorDirection,
       delegate: delegate,
       overlapBehaviour: overlapBehaviour,
+      style: style,
     );
   }
 }