From dc87a8126178c499377837e4344eaf5bcb4baddf Mon Sep 17 00:00:00 2001 From: dev-hann Date: Thu, 24 Mar 2022 17:12:12 -0300 Subject: [PATCH 1/3] Show clear button even without focus --- lib/src/controls/form/auto_suggest_box.dart | 6 ++---- lib/src/controls/utils/hover_button.dart | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/src/controls/form/auto_suggest_box.dart b/lib/src/controls/form/auto_suggest_box.dart index b0ea8335b..d1b908d42 100644 --- a/lib/src/controls/form/auto_suggest_box.dart +++ b/lib/src/controls/form/auto_suggest_box.dart @@ -293,11 +293,9 @@ class _AutoSuggestBoxState extends State { clipBehavior: _entry != null ? Clip.none : Clip.antiAliasWithSaveLayer, prefix: widget.leadingIcon, - suffix: Row(children: [ + suffix: Row(mainAxisSize: MainAxisSize.min, children: [ if (widget.trailingIcon != null) widget.trailingIcon!, - if (widget.clearButtonEnabled && - controller.text.isNotEmpty && - focusNode.hasFocus) + if (widget.clearButtonEnabled && controller.text.isNotEmpty) Padding( padding: const EdgeInsetsDirectional.only(start: 2.0), child: IconButton( diff --git a/lib/src/controls/utils/hover_button.dart b/lib/src/controls/utils/hover_button.dart index ac24efd2f..0359b19d2 100644 --- a/lib/src/controls/utils/hover_button.dart +++ b/lib/src/controls/utils/hover_button.dart @@ -222,7 +222,7 @@ class _HoverButtonState extends State { label: widget.semanticLabel, button: true, enabled: enabled, - focusable: enabled, + focusable: enabled && node.canRequestFocus, focused: node.hasFocus, child: w, ), From 084038b90331bf3ff7ec2ce6035142b7e507b9c1 Mon Sep 17 00:00:00 2001 From: dev-hann Date: Thu, 24 Mar 2022 17:30:03 -0300 Subject: [PATCH 2/3] Update TextBox colors --- example/lib/screens/forms.dart | 15 ++- lib/src/controls/form/text_box.dart | 132 +++++++++++++---------- lib/src/controls/utils/hover_button.dart | 5 +- 3 files changed, 88 insertions(+), 64 deletions(-) diff --git a/example/lib/screens/forms.dart b/example/lib/screens/forms.dart index 214102422..b8a827bbb 100644 --- a/example/lib/screens/forms.dart +++ b/example/lib/screens/forms.dart @@ -193,15 +193,12 @@ class _FormsState extends State { const SizedBox(height: 20), InfoLabel( label: 'Selectable Text', - child: Mica( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: SelectableText( - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', - selectionControls: fluentTextSelectionControls, - showCursor: true, - cursorWidth: 1.5, - ), + child: Card( + child: SelectableText( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', + selectionControls: fluentTextSelectionControls, + showCursor: true, + cursorWidth: 1.5, ), ), ), diff --git a/lib/src/controls/form/text_box.dart b/lib/src/controls/form/text_box.dart index f20df945f..ca21f7990 100644 --- a/lib/src/controls/form/text_box.dart +++ b/lib/src/controls/form/text_box.dart @@ -824,12 +824,6 @@ class _TextBoxState extends State widget.keyboardAppearance ?? theme.brightness; final Color cursorColor = widget.cursorColor ?? theme.inactiveColor; final Color disabledColor = theme.disabledColor; - final Color backgroundColor = _effectiveFocusNode.hasFocus - ? theme.scaffoldBackgroundColor - : AccentColor('normal', const { - 'normal': Colors.white, - 'dark': Color(0xFF2d2d2d), - }).resolve(context); final TextStyle placeholderStyle = textStyle .copyWith( @@ -950,55 +944,59 @@ class _TextBoxState extends State }, child: IgnorePointer( ignoring: !enabled, - child: AnimatedContainer( - duration: theme.fasterAnimationDuration, - curve: theme.animationCurve, - decoration: BoxDecoration( - borderRadius: radius, - border: Border.all( - style: _effectiveFocusNode.hasFocus - ? BorderStyle.solid - : BorderStyle.none, - width: 1, - color: theme.brightness.isLight - ? const Color.fromRGBO(0, 0, 0, 0.08) - : const Color.fromRGBO(255, 255, 255, 0.07), - ), - color: enabled - ? backgroundColor - : theme.brightness.isLight - ? const Color.fromRGBO(249, 249, 249, 0.3) - : const Color.fromRGBO(255, 255, 255, 0.04), - ).copyWith( - backgroundBlendMode: widget.decoration?.backgroundBlendMode, - border: widget.decoration?.border, - - /// This border radius can't be applied, otherwise the error "A borderRadius - /// can only be given for a uniform Border." will be thrown. Instead, - /// [radius] is already set to get the value from [widget.decoration?.borderRadius], - /// if any. - // borderRadius: widget.decoration?.borderRadius, - boxShadow: widget.decoration?.boxShadow, - color: widget.decoration?.color, - gradient: widget.decoration?.gradient, - image: widget.decoration?.image, - shape: widget.decoration?.shape, - ), - foregroundDecoration: foregroundDecoration, - constraints: BoxConstraints(minHeight: widget.minHeight ?? 0), - child: _selectionGestureDetectorBuilder.buildGestureDetector( - behavior: HitTestBehavior.translucent, - child: Align( - alignment: Alignment(-1.0, _textAlignVertical.y), - widthFactor: 1.0, - heightFactor: 1.0, - child: _addTextDependentAttachments( - paddedEditable, - textStyle, - placeholderStyle, + child: HoverButton( + actionsEnabled: false, + onPressed: enabled ? () {} : null, + builder: (context, states) { + return Container( + decoration: BoxDecoration( + borderRadius: radius, + border: Border.all( + style: _effectiveFocusNode.hasFocus + ? BorderStyle.solid + : BorderStyle.none, + width: 1, + color: theme.brightness.isLight + ? const Color.fromRGBO(0, 0, 0, 0.08) + : const Color.fromRGBO(255, 255, 255, 0.07), + ), + color: _backgroundColor(states), + ).copyWith( + backgroundBlendMode: widget.decoration?.backgroundBlendMode, + border: widget.decoration?.border, + + /// This border radius can't be applied, otherwise the error "A borderRadius + /// can only be given for a uniform Border." will be thrown. Instead, + /// [radius] is already set to get the value from [widget.decoration?.borderRadius], + /// if any. + // borderRadius: widget.decoration?.borderRadius, + boxShadow: widget.decoration?.boxShadow, + color: widget.decoration?.color, + gradient: widget.decoration?.gradient, + image: widget.decoration?.image, + shape: widget.decoration?.shape, ), - ), - ), + constraints: BoxConstraints(minHeight: widget.minHeight ?? 0), + child: AnimatedContainer( + duration: theme.fasterAnimationDuration, + curve: theme.animationCurve, + foregroundDecoration: foregroundDecoration, + child: _selectionGestureDetectorBuilder.buildGestureDetector( + behavior: HitTestBehavior.translucent, + child: Align( + alignment: Alignment(-1.0, _textAlignVertical.y), + widthFactor: 1.0, + heightFactor: 1.0, + child: _addTextDependentAttachments( + paddedEditable, + textStyle, + placeholderStyle, + ), + ), + ), + ), + ); + }, ), ), ); @@ -1050,4 +1048,30 @@ class _TextBoxState extends State ), ); } + + Color _backgroundColor(Set states) { + final brightness = FluentTheme.of(context).brightness; + + if (brightness.isDark) { + if (!enabled) { + return const Color.fromRGBO(255, 255, 255, 0.04); + } else if (states.isPressing || states.isFocused) { + return const Color(0xFF1f1f1f); + } else if (states.isHovering) { + return const Color(0xFF323232); + } else { + return const Color(0xFF2d2d2d); + } + } else { + if (!enabled) { + return const Color.fromRGBO(249, 249, 249, 0.3); + } else if (states.isPressing || states.isFocused) { + return const Color(0xFFffffff); + } else if (states.isHovering) { + return const Color(0xFFfbfbfb); + } else { + return const Color(0xFFf6f6f6); + } + } + } } diff --git a/lib/src/controls/utils/hover_button.dart b/lib/src/controls/utils/hover_button.dart index 0359b19d2..738ca5666 100644 --- a/lib/src/controls/utils/hover_button.dart +++ b/lib/src/controls/utils/hover_button.dart @@ -44,6 +44,7 @@ class HoverButton extends StatefulWidget { this.onHorizontalDragEnd, this.onFocusChange, this.autofocus = false, + this.actionsEnabled = true, }) : super(key: key); /// {@template fluent_ui.controls.inputs.HoverButton.mouseCursor} @@ -94,6 +95,8 @@ class HoverButton extends StatefulWidget { final ValueChanged? onFocusChange; + final bool actionsEnabled; + @override _HoverButtonState createState() => _HoverButtonState(); } @@ -207,7 +210,7 @@ class _HoverButtonState extends State { focusNode: node, autofocus: widget.autofocus, enabled: enabled, - actions: _actionMap, + actions: widget.actionsEnabled ? _actionMap : {}, onFocusChange: widget.onFocusChange, onShowFocusHighlight: (v) { if (mounted) setState(() => _shouldShowFocus = v); From 6b99caa1dc5c3d2ef7590f80475c42f0531bfdbd Mon Sep 17 00:00:00 2001 From: dev-hann Date: Thu, 24 Mar 2022 17:39:36 -0300 Subject: [PATCH 3/3] Make use of clearGlobalKey --- lib/src/controls/form/auto_suggest_box.dart | 5 ++++- lib/src/controls/form/text_box.dart | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/src/controls/form/auto_suggest_box.dart b/lib/src/controls/form/auto_suggest_box.dart index d1b908d42..1d286642e 100644 --- a/lib/src/controls/form/auto_suggest_box.dart +++ b/lib/src/controls/form/auto_suggest_box.dart @@ -182,9 +182,10 @@ class _AutoSuggestBoxState extends State { final GlobalKey _textBoxKey = GlobalKey(); late TextEditingController controller; - final FocusScopeNode overlayNode = FocusScopeNode(); + final clearGlobalKey = GlobalKey(); + @override void initState() { super.initState(); @@ -293,12 +294,14 @@ class _AutoSuggestBoxState extends State { clipBehavior: _entry != null ? Clip.none : Clip.antiAliasWithSaveLayer, prefix: widget.leadingIcon, + clearGlobalKey: clearGlobalKey, suffix: Row(mainAxisSize: MainAxisSize.min, children: [ if (widget.trailingIcon != null) widget.trailingIcon!, if (widget.clearButtonEnabled && controller.text.isNotEmpty) Padding( padding: const EdgeInsetsDirectional.only(start: 2.0), child: IconButton( + key: clearGlobalKey, icon: const Icon(FluentIcons.chrome_close), onPressed: () { controller.clear(); diff --git a/lib/src/controls/form/text_box.dart b/lib/src/controls/form/text_box.dart index ca21f7990..a76df6fbe 100644 --- a/lib/src/controls/form/text_box.dart +++ b/lib/src/controls/form/text_box.dart @@ -27,9 +27,14 @@ class _TextBoxSelectionGestureDetectorBuilder @override void onSingleTapUp(TapUpDetails details) { + editableText.hideToolbar(); + // Because TextSelectionGestureDetector listens to taps that happen on + // widgets in front of it, tapping the clear button will also trigger + // this handler. If the clear button widget recognizes the up event, + // then do not handle it. if (_state._clearGlobalKey.currentContext != null) { final RenderBox renderBox = _state._clearGlobalKey.currentContext! - .findRenderObject() as RenderBox; + .findRenderObject()! as RenderBox; final Offset localOffset = renderBox.globalToLocal(details.globalPosition); if (renderBox.hitTest(BoxHitTestResult(), position: localOffset)) { @@ -38,7 +43,7 @@ class _TextBoxSelectionGestureDetectorBuilder } super.onSingleTapUp(details); _state._requestKeyboard(); - if (_state.widget.onTap != null) _state.widget.onTap!(); + _state.widget.onTap?.call(); } @override @@ -125,6 +130,7 @@ class TextBox extends StatefulWidget { this.decoration, this.foregroundDecoration, this.highlightColor, + this.clearGlobalKey, }) : assert(obscuringCharacter.length == 1), smartDashesType = smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled), @@ -483,6 +489,8 @@ class TextBox extends StatefulWidget { final ButtonThemeData? iconButtonThemeData; + final GlobalKey? clearGlobalKey; + @override _TextBoxState createState() => _TextBoxState(); @@ -560,7 +568,9 @@ class TextBox extends StatefulWidget { class _TextBoxState extends State with RestorationMixin, AutomaticKeepAliveClientMixin implements TextSelectionGestureDetectorBuilderDelegate { - final GlobalKey _clearGlobalKey = GlobalKey(); + final _localClearGlobalKey = GlobalKey(); + GlobalKey get _clearGlobalKey => + widget.clearGlobalKey ?? _localClearGlobalKey; RestorableTextEditingController? _controller; TextEditingController get _effectiveController =>