Browse Source

[flutter]: config toolbar ui

appflowy 3 years ago
parent
commit
cf98c39b98

+ 5 - 9
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/check_button.dart

@@ -1,10 +1,8 @@
 import 'package:editor/flutter_quill.dart';
 import 'package:editor/models/documents/style.dart';
-import 'package:flowy_infra/image.dart';
-import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flutter/material.dart';
-import 'package:provider/provider.dart';
+
+import 'toolbar_icon_button.dart';
 
 class FlowyCheckListButton extends StatefulWidget {
   const FlowyCheckListButton({
@@ -77,13 +75,11 @@ class _FlowyCheckListButtonState extends State<FlowyCheckListButton> {
 
   @override
   Widget build(BuildContext context) {
-    final theme = context.watch<AppTheme>();
-    return FlowyIconButton(
+    return ToolbarIconButton(
       onPressed: _toggleAttribute,
       width: widget.iconSize * kIconButtonFactor,
-      icon: svg('editor/checkbox'),
-      highlightColor: _isToggled == true ? theme.shader5 : theme.shader6,
-      hoverColor: theme.shader5,
+      iconName: 'editor/checkbox',
+      isToggled: _isToggled ?? false,
     );
   }
 

+ 96 - 0
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/header_button.dart

@@ -0,0 +1,96 @@
+import 'package:editor/flutter_quill.dart';
+import 'package:flowy_infra/image.dart';
+import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/style_widget/icon_button.dart';
+import 'package:flutter/foundation.dart';
+import 'package:editor/models/documents/style.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+import 'toolbar_icon_button.dart';
+
+class FlowyHeaderStyleButton extends StatefulWidget {
+  const FlowyHeaderStyleButton({
+    required this.controller,
+    this.iconSize = kDefaultIconSize,
+    Key? key,
+  }) : super(key: key);
+
+  final QuillController controller;
+  final double iconSize;
+
+  @override
+  _FlowyHeaderStyleButtonState createState() => _FlowyHeaderStyleButtonState();
+}
+
+class _FlowyHeaderStyleButtonState extends State<FlowyHeaderStyleButton> {
+  Attribute? _value;
+
+  Style get _selectionStyle => widget.controller.getSelectionStyle();
+
+  @override
+  void initState() {
+    super.initState();
+    setState(() {
+      _value = _selectionStyle.attributes[Attribute.header.key] ?? Attribute.header;
+    });
+    widget.controller.addListener(_didChangeEditingValue);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final _valueToText = <Attribute, String>{
+      Attribute.h1: 'H1',
+      Attribute.h2: 'H2',
+      Attribute.h3: 'H3',
+    };
+
+    final _valueAttribute = <Attribute>[Attribute.h1, Attribute.h2, Attribute.h3];
+    final _valueString = <String>['H1', 'H2', 'H3'];
+    final _attributeImageName = <String>['editor/H1', 'editor/H2', 'editor/H3'];
+
+    return Row(
+      mainAxisSize: MainAxisSize.min,
+      children: List.generate(3, (index) {
+        // final child =
+        //     _valueToText[_value] == _valueString[index] ? svg('editor/H1', color: Colors.white) : svg('editor/H1');
+
+        final _isToggled = _valueToText[_value] == _valueString[index];
+        return ToolbarIconButton(
+          onPressed: () {
+            if (_isToggled) {
+              widget.controller.formatSelection(Attribute.header);
+            } else {
+              widget.controller.formatSelection(_valueAttribute[index]);
+            }
+          },
+          width: widget.iconSize * kIconButtonFactor,
+          iconName: _attributeImageName[index],
+          isToggled: _isToggled,
+        );
+      }),
+    );
+  }
+
+  void _didChangeEditingValue() {
+    setState(() {
+      _value = _selectionStyle.attributes[Attribute.header.key] ?? Attribute.header;
+    });
+  }
+
+  @override
+  void didUpdateWidget(covariant FlowyHeaderStyleButton oldWidget) {
+    super.didUpdateWidget(oldWidget);
+    if (oldWidget.controller != widget.controller) {
+      oldWidget.controller.removeListener(_didChangeEditingValue);
+      widget.controller.addListener(_didChangeEditingValue);
+      _value = _selectionStyle.attributes[Attribute.header.key] ?? Attribute.header;
+    }
+  }
+
+  @override
+  void dispose() {
+    widget.controller.removeListener(_didChangeEditingValue);
+    super.dispose();
+  }
+}

+ 5 - 7
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/image_button.dart

@@ -1,9 +1,9 @@
 import 'package:editor/flutter_quill.dart';
-import 'package:flowy_infra/image.dart';
-import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flutter/material.dart';
 import 'package:image_picker/image_picker.dart';
 
+import 'toolbar_icon_button.dart';
+
 class FlowyImageButton extends StatelessWidget {
   const FlowyImageButton({
     required this.controller,
@@ -32,13 +32,11 @@ class FlowyImageButton extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    final theme = Theme.of(context);
-
-    return FlowyIconButton(
-      icon: svg('editor/image'),
+    return ToolbarIconButton(
+      iconName: 'editor/image',
       width: iconSize * 1.77,
-      highlightColor: theme.canvasColor,
       onPressed: () => _onPressedHandler(context),
+      isToggled: false,
     );
   }
 

+ 23 - 16
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/link_button.dart

@@ -1,3 +1,4 @@
+import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
 import 'package:editor/flutter_quill.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
@@ -47,33 +48,39 @@ class _FlowyLinkStyleButtonState extends State<FlowyLinkStyleButton> {
 
   @override
   Widget build(BuildContext context) {
+    final theme = context.watch<AppTheme>();
     final isEnabled = !widget.controller.selection.isCollapsed;
     final pressedHandler = isEnabled ? () => _openLinkDialog(context) : null;
-
-    final theme = context.watch<AppTheme>();
+    final icon = isEnabled ? svg('editor/share') : svg('editor/share', color: theme.shader4);
 
     return FlowyIconButton(
       onPressed: pressedHandler,
-      icon: svg('editor/share'),
-      highlightColor: isEnabled == true ? theme.shader5 : theme.shader6,
+      iconPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4),
+      icon: icon,
+      fillColor: theme.shader6,
       hoverColor: theme.shader5,
       width: widget.iconSize * kIconButtonFactor,
     );
   }
 
   void _openLinkDialog(BuildContext context) {
-    // showDialog<String>(
-    //   context: context,
-    //   builder: (ctx) {
-    //     return const LinkDialog();
-    //   },
-    // ).then(_linkSubmitted);
-  }
-
-  void _linkSubmitted(String? value) {
-    if (value == null || value.isEmpty) {
-      return;
+    final style = widget.controller.getSelectionStyle();
+    final values = style.values.where((v) => v.key == Attribute.link.key).map((v) => v.value);
+    String value = "";
+    if (values.isNotEmpty) {
+      assert(values.length == 1);
+      value = values.first;
     }
-    widget.controller.formatSelection(LinkAttribute(value));
+
+    TextFieldDialog(
+      title: 'URL',
+      value: value,
+      confirm: (newValue) {
+        if (newValue.isEmpty) {
+          return;
+        }
+        widget.controller.formatSelection(LinkAttribute(newValue));
+      },
+    ).show(context);
   }
 }

+ 7 - 12
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/toggle_button.dart

@@ -1,20 +1,18 @@
 import 'package:editor/flutter_quill.dart';
 import 'package:editor/models/documents/style.dart';
-
-import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flutter/material.dart';
-import 'package:provider/provider.dart';
+
+import 'toolbar_icon_button.dart';
 
 class FlowyToggleStyleButton extends StatefulWidget {
   final Attribute attribute;
-  final Widget icon;
+  final String normalIcon;
   final double iconSize;
   final QuillController controller;
 
   const FlowyToggleStyleButton({
     required this.attribute,
-    required this.icon,
+    required this.normalIcon,
     required this.controller,
     this.iconSize = kDefaultIconSize,
     Key? key,
@@ -36,14 +34,11 @@ class _ToggleStyleButtonState extends State<FlowyToggleStyleButton> {
 
   @override
   Widget build(BuildContext context) {
-    final theme = context.watch<AppTheme>();
-
-    return FlowyIconButton(
+    return ToolbarIconButton(
       onPressed: _toggleAttribute,
       width: widget.iconSize * kIconButtonFactor,
-      icon: widget.icon,
-      highlightColor: _isToggled == true ? theme.shader5 : theme.shader6,
-      hoverColor: theme.shader5,
+      isToggled: _isToggled ?? false,
+      iconName: widget.normalIcon,
     );
   }
 

+ 31 - 38
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/tool_bar.dart

@@ -2,9 +2,10 @@ import 'dart:async';
 import 'dart:math';
 
 import 'package:editor/flutter_quill.dart';
-import 'package:flowy_infra/image.dart';
 import 'package:flutter/material.dart';
+import 'package:styled_widget/styled_widget.dart';
 import 'check_button.dart';
+import 'header_button.dart';
 import 'image_button.dart';
 import 'link_button.dart';
 import 'toggle_button.dart';
@@ -16,7 +17,7 @@ class EditorToolbar extends StatelessWidget implements PreferredSizeWidget {
 
   const EditorToolbar({
     required this.children,
-    this.toolBarHeight = 36,
+    this.toolBarHeight = 46,
     this.color,
     Key? key,
   }) : super(key: key);
@@ -26,7 +27,7 @@ class EditorToolbar extends StatelessWidget implements PreferredSizeWidget {
     return Container(
       color: Theme.of(context).canvasColor,
       constraints: BoxConstraints.tightFor(height: preferredSize.height),
-      child: ToolbarButtonList(buttons: children),
+      child: ToolbarButtonList(buttons: children).padding(horizontal: 4, vertical: 4),
     );
   }
 
@@ -62,25 +63,25 @@ class EditorToolbar extends StatelessWidget implements PreferredSizeWidget {
         ),
         FlowyToggleStyleButton(
           attribute: Attribute.bold,
-          icon: svg('editor/bold'),
+          normalIcon: 'editor/bold',
           iconSize: toolbarIconSize,
           controller: controller,
         ),
         FlowyToggleStyleButton(
           attribute: Attribute.italic,
-          icon: svg("editor/restore"),
+          normalIcon: 'editor/restore',
           iconSize: toolbarIconSize,
           controller: controller,
         ),
         FlowyToggleStyleButton(
           attribute: Attribute.underline,
-          icon: svg('editor/underline'),
+          normalIcon: 'editor/underline',
           iconSize: toolbarIconSize,
           controller: controller,
         ),
         FlowyToggleStyleButton(
           attribute: Attribute.strikeThrough,
-          icon: svg('editor/strikethrough'),
+          normalIcon: 'editor/strikethrough',
           iconSize: toolbarIconSize,
           controller: controller,
         ),
@@ -98,20 +99,20 @@ class EditorToolbar extends StatelessWidget implements PreferredSizeWidget {
           webImagePickImpl: webImagePickImpl,
           mediaPickSettingSelector: mediaPickSettingSelector,
         ),
-        SelectHeaderStyleButton(
+        FlowyHeaderStyleButton(
           controller: controller,
           iconSize: toolbarIconSize,
         ),
         FlowyToggleStyleButton(
           attribute: Attribute.ol,
           controller: controller,
-          icon: svg('editor/numbers'),
+          normalIcon: 'editor/numbers',
           iconSize: toolbarIconSize,
         ),
         FlowyToggleStyleButton(
           attribute: Attribute.ul,
           controller: controller,
-          icon: svg('editor/bullet_list'),
+          normalIcon: 'editor/bullet_list',
           iconSize: toolbarIconSize,
         ),
         FlowyCheckListButton(
@@ -122,31 +123,13 @@ class EditorToolbar extends StatelessWidget implements PreferredSizeWidget {
         FlowyToggleStyleButton(
           attribute: Attribute.inlineCode,
           controller: controller,
-          icon: svg('editor/inline_block'),
+          normalIcon: 'editor/inline_block',
           iconSize: toolbarIconSize,
         ),
         FlowyToggleStyleButton(
           attribute: Attribute.blockQuote,
           controller: controller,
-          icon: svg('editor/quote'),
-          iconSize: toolbarIconSize,
-        ),
-        FlowyToggleStyleButton(
-          attribute: Attribute.blockQuote,
-          controller: controller,
-          icon: svg('editor/quote'),
-          iconSize: toolbarIconSize,
-        ),
-        FlowyToggleStyleButton(
-          attribute: Attribute.blockQuote,
-          controller: controller,
-          icon: svg('editor/quote'),
-          iconSize: toolbarIconSize,
-        ),
-        FlowyToggleStyleButton(
-          attribute: Attribute.blockQuote,
-          controller: controller,
-          icon: svg('editor/quote'),
+          normalIcon: 'editor/quote',
           iconSize: toolbarIconSize,
         ),
         FlowyLinkStyleButton(
@@ -191,14 +174,25 @@ class _ToolbarButtonListState extends State<ToolbarButtonList> with WidgetsBindi
   Widget build(BuildContext context) {
     return LayoutBuilder(
       builder: (BuildContext context, BoxConstraints constraints) {
+        List<Widget> children = [];
+        double width = (widget.buttons.length + 2) * kDefaultIconSize * kIconButtonFactor;
+        final isFit = constraints.maxWidth > width;
+        if (!isFit) {
+          children.add(_buildLeftArrow());
+          width = width + 18;
+        }
+
+        children.add(_buildScrollableList(constraints));
+
+        if (!isFit) {
+          children.add(_buildRightArrow());
+          width = width + 18;
+        }
+
         return SizedBox(
-          width: min(constraints.maxWidth, (widget.buttons.length + 3) * kDefaultIconSize * kIconButtonFactor + 16),
+          width: min(constraints.maxWidth, width),
           child: Row(
-            children: <Widget>[
-              _buildLeftArrow(),
-              _buildScrollableList(constraints),
-              _buildRightColor(),
-            ],
+            children: children,
           ),
         );
       },
@@ -261,7 +255,7 @@ class _ToolbarButtonListState extends State<ToolbarButtonList> with WidgetsBindi
     );
   }
 
-  Widget _buildRightColor() {
+  Widget _buildRightArrow() {
     return SizedBox(
       width: 8,
       child: Transform.translate(
@@ -273,7 +267,6 @@ class _ToolbarButtonListState extends State<ToolbarButtonList> with WidgetsBindi
   }
 }
 
-/// ScrollBehavior without the Material glow effect.
 class _NoGlowBehavior extends ScrollBehavior {
   @override
   Widget buildViewportChrome(BuildContext _, Widget child, AxisDirection __) {

+ 29 - 0
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/toolbar_icon_button.dart

@@ -0,0 +1,29 @@
+import 'package:flowy_infra/image.dart';
+import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/style_widget/icon_button.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class ToolbarIconButton extends StatelessWidget {
+  final double width;
+  final VoidCallback? onPressed;
+  final bool isToggled;
+  final String iconName;
+
+  const ToolbarIconButton(
+      {Key? key, required this.onPressed, required this.isToggled, required this.width, required this.iconName})
+      : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = context.watch<AppTheme>();
+    return FlowyIconButton(
+      iconPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4),
+      onPressed: onPressed,
+      width: width,
+      icon: isToggled == true ? svg(iconName, color: Colors.white) : svg(iconName),
+      fillColor: isToggled == true ? theme.main1 : theme.shader6,
+      hoverColor: isToggled == true ? theme.main1 : theme.shader5,
+    );
+  }
+}

+ 12 - 11
app_flowy/lib/workspace/presentation/widgets/dialogs.dart

@@ -14,30 +14,30 @@ import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
 import 'package:textstyle_extensions/textstyle_extensions.dart';
 export 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
 
-class RenameDialog extends StatefulWidget {
-  final String name;
+class TextFieldDialog extends StatefulWidget {
+  final String value;
   final String title;
   final void Function()? cancel;
   final void Function(String) confirm;
 
-  const RenameDialog({
+  const TextFieldDialog({
     required this.title,
-    required this.name,
+    required this.value,
     required this.confirm,
     this.cancel,
     Key? key,
   }) : super(key: key);
 
   @override
-  State<RenameDialog> createState() => _CreateRenameDialog();
+  State<TextFieldDialog> createState() => _CreateTextFieldDialog();
 }
 
-class _CreateRenameDialog extends State<RenameDialog> {
-  String newViewName = "";
+class _CreateTextFieldDialog extends State<TextFieldDialog> {
+  String newValue = "";
 
   @override
   void initState() {
-    newViewName = widget.name;
+    newValue = widget.value;
     super.initState();
   }
 
@@ -53,9 +53,10 @@ class _CreateRenameDialog extends State<RenameDialog> {
             VSpace(Insets.sm * 1.5),
           ],
           FlowyFormTextInput(
-            hintText: widget.name,
+            hintText: widget.value,
+            autoFocus: true,
             onChanged: (text) {
-              newViewName = text;
+              newValue = text;
             },
           ),
           SizedBox(height: Insets.l),
@@ -63,7 +64,7 @@ class _CreateRenameDialog extends State<RenameDialog> {
             height: 40,
             child: OkCancelButton(
               onOkPressed: () {
-                widget.confirm(newViewName);
+                widget.confirm(newValue);
               },
               onCancelPressed: () {
                 if (widget.cancel != null) {

+ 2 - 2
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/header.dart

@@ -80,14 +80,14 @@ class AddButton extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return FlowyIconButton(
-      width: 16,
+      width: 22,
       onPressed: () {
         ActionList(
           anchorContext: context,
           onSelected: onSelected,
         ).show(context);
       },
-      icon: svg("home/add"),
+      icon: svg("home/add").padding(horizontal: 3, vertical: 3),
     );
   }
 }

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

@@ -63,23 +63,25 @@ class ViewSectionItem extends StatelessWidget {
   Widget _render(BuildContext context, bool onHover, ViewState state) {
     List<Widget> children = [
       SizedBox(width: 16, height: 16, child: state.view.thumbnail()),
-      const HSpace(6),
+      const HSpace(2),
       FlowyText.regular(state.view.name, fontSize: 12),
     ];
 
     if (onHover || state.isEditing) {
       children.add(const Spacer());
-      children.add(ViewDisclosureButton(
-        onTap: () => context.read<ViewBloc>().add(const ViewEvent.setIsEditing(true)),
-        onSelected: (action) {
-          context.read<ViewBloc>().add(const ViewEvent.setIsEditing(false));
-          _handleAction(context, action);
-        },
-      ));
+      children.add(
+        ViewDisclosureButton(
+          onTap: () => context.read<ViewBloc>().add(const ViewEvent.setIsEditing(true)),
+          onSelected: (action) {
+            context.read<ViewBloc>().add(const ViewEvent.setIsEditing(false));
+            _handleAction(context, action);
+          },
+        ),
+      );
     }
 
     return SizedBox(
-      height: 24,
+      height: 26,
       child: Row(children: children).padding(
         left: MenuAppSizes.expandedPadding,
         right: MenuAppSizes.expandedIconPadding,
@@ -91,11 +93,11 @@ class ViewSectionItem extends StatelessWidget {
     action.foldRight({}, (action, previous) {
       switch (action) {
         case ViewAction.rename:
-          RenameDialog(
+          TextFieldDialog(
             title: 'Rename',
-            name: context.read<ViewBloc>().state.view.name,
-            confirm: (newName) {
-              context.read<ViewBloc>().add(ViewEvent.rename(newName));
+            value: context.read<ViewBloc>().state.view.name,
+            confirm: (newValue) {
+              context.read<ViewBloc>().add(ViewEvent.rename(newValue));
             },
           ).show(context);
 
@@ -126,7 +128,8 @@ class ViewDisclosureButton extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return FlowyIconButton(
-      width: 16,
+      iconPadding: const EdgeInsets.all(5),
+      width: 26,
       onPressed: () {
         onTap();
         ViewActionList(

+ 2 - 4
app_flowy/packages/flowy_infra/lib/image.dart

@@ -1,10 +1,8 @@
 import 'package:flutter/widgets.dart';
 import 'package:flutter_svg/flutter_svg.dart';
 
-Widget svg(String name) {
-  final Widget svg = SvgPicture.asset(
-    'assets/images/$name.svg',
-  );
+Widget svg(String name, {Color? color}) {
+  final Widget svg = SvgPicture.asset('assets/images/$name.svg', color: color);
 
   return svg;
 }

+ 17 - 5
app_flowy/packages/flowy_infra_ui/lib/style_widget/icon_button.dart

@@ -6,7 +6,7 @@ class FlowyIconButton extends StatelessWidget {
   final double? height;
   final Widget icon;
   final VoidCallback? onPressed;
-  final Color? highlightColor;
+  final Color? fillColor;
   final Color? hoverColor;
   final EdgeInsets iconPadding;
 
@@ -15,14 +15,26 @@ class FlowyIconButton extends StatelessWidget {
     this.height,
     this.onPressed,
     this.width = 30,
-    this.highlightColor = Colors.transparent,
+    this.fillColor = Colors.transparent,
     this.hoverColor = Colors.transparent,
-    this.iconPadding = const EdgeInsets.symmetric(horizontal: 6, vertical: 6),
+    this.iconPadding = EdgeInsets.zero,
     required this.icon,
   }) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
+    Widget child = icon;
+
+    // if (onPressed == null) {
+    //   child = ColorFiltered(
+    //     colorFilter: ColorFilter.mode(
+    //       Colors.grey,
+    //       BlendMode.saturation,
+    //     ),
+    //     child: child,
+    //   );
+    // }
+
     return ConstrainedBox(
       constraints: BoxConstraints.tightFor(width: width, height: width),
       child: RawMaterialButton(
@@ -30,7 +42,7 @@ class FlowyIconButton extends StatelessWidget {
         hoverElevation: 0,
         highlightElevation: 0,
         shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)),
-        fillColor: highlightColor,
+        fillColor: fillColor,
         hoverColor: hoverColor,
         focusColor: Colors.transparent,
         splashColor: Colors.transparent,
@@ -39,7 +51,7 @@ class FlowyIconButton extends StatelessWidget {
         onPressed: onPressed,
         child: Padding(
           padding: iconPadding,
-          child: SizedBox.fromSize(child: icon, size: Size(width, width)),
+          child: SizedBox.fromSize(child: child, size: Size(width, width)),
         ),
       ),
     );