From 6c15da0af1d04db8f2b4299b5996f9100076644e Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:00:57 +0100 Subject: [PATCH 1/4] fix #545 --- lib/src/buttons/popup_button.dart | 57 ++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/lib/src/buttons/popup_button.dart b/lib/src/buttons/popup_button.dart index 0bd1ee13..e78fedb6 100644 --- a/lib/src/buttons/popup_button.dart +++ b/lib/src/buttons/popup_button.dart @@ -45,23 +45,34 @@ class _MacosPopupMenuItemButton extends StatefulWidget { class _MacosPopupMenuItemButtonState extends State<_MacosPopupMenuItemButton> { - bool _isHovered = false; + final _focusNode = FocusNode(); + + bool _isFocused = false; + + bool get _isHighlighted => _isFocused; + + static DateTime _lastMouseEnterTime = DateTime.now(); + static DateTime _lastNonMouseFocusChange = DateTime.now(); void _handleFocusChange(bool focused) { if (focused) { - final _MenuLimits menuLimits = widget.route.getMenuLimits( - widget.buttonRect, - widget.constraints.maxHeight, - widget.itemIndex, - ); - widget.route.scrollController!.animateTo( - menuLimits.scrollOffset, - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 100), - ); - setState(() => _isHovered = true); + final timeSinceMouseEnter = + DateTime.now().difference(_lastMouseEnterTime); + if (timeSinceMouseEnter > const Duration(milliseconds: 50)) { + _lastNonMouseFocusChange = DateTime.now(); + + final _MenuLimits menuLimits = widget.route.getMenuLimits( + widget.buttonRect, + widget.constraints.maxHeight, + widget.itemIndex, + ); + widget.route.scrollController!.jumpTo( + menuLimits.scrollOffset, + ); + } + setState(() => _isFocused = true); } else { - setState(() => _isHovered = false); + setState(() => _isFocused = false); } } @@ -91,14 +102,20 @@ class _MacosPopupMenuItemButtonState child = MouseRegion( cursor: SystemMouseCursors.basic, onEnter: (_) { - setState(() => _isHovered = true); - }, - onExit: (_) { - setState(() => _isHovered = false); + final timeSinceLastNonMouseFocusChange = + DateTime.now().difference(_lastNonMouseFocusChange); + if (timeSinceLastNonMouseFocusChange < + const Duration(milliseconds: 200)) { + return; + } + + _lastMouseEnterTime = DateTime.now(); + FocusScope.of(context).requestFocus(_focusNode); }, child: GestureDetector( onTap: _handleOnTap, child: Focus( + focusNode: _focusNode, onKeyEvent: (FocusNode node, KeyEvent event) { if (event.logicalKey == LogicalKeyboardKey.enter) { _handleOnTap(); @@ -110,7 +127,7 @@ class _MacosPopupMenuItemButtonState autofocus: widget.itemIndex == widget.route.selectedIndex, child: Container( decoration: BoxDecoration( - color: _isHovered + color: _isHighlighted ? MacosPopupButtonTheme.of(context).highlightColor : Colors.transparent, borderRadius: _kBorderRadius, @@ -123,7 +140,7 @@ class _MacosPopupMenuItemButtonState child: MacosIcon( CupertinoIcons.checkmark_alt, size: 16.0, - color: _isHovered + color: _isHighlighted ? MacosColors.white : brightness.resolve( MacosColors.black, @@ -135,7 +152,7 @@ class _MacosPopupMenuItemButtonState DefaultTextStyle( style: TextStyle( fontSize: 13.0, - color: _isHovered + color: _isHighlighted ? MacosColors.white : brightness.resolve( MacosColors.black, From 42fee0ba3f61a2a1e7a6ef76f5484beff86d6353 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:02:27 +0100 Subject: [PATCH 2/4] document some fields that were introduced in 6c15da0af1d04db8f2b4299b5996f9100076644e --- lib/src/buttons/popup_button.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/buttons/popup_button.dart b/lib/src/buttons/popup_button.dart index e78fedb6..975bfc2a 100644 --- a/lib/src/buttons/popup_button.dart +++ b/lib/src/buttons/popup_button.dart @@ -51,7 +51,10 @@ class _MacosPopupMenuItemButtonState bool get _isHighlighted => _isFocused; + /// The last time the mouse entered this item. static DateTime _lastMouseEnterTime = DateTime.now(); + + /// The last time the focus changed that wasn’t caused by the mouse. static DateTime _lastNonMouseFocusChange = DateTime.now(); void _handleFocusChange(bool focused) { From aa45945831524728d2441eb5fcca16620916f457 Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:06:48 +0100 Subject: [PATCH 3/4] bump version to 2.1.9 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index d0f79806..edfa20cb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: macos_ui description: Flutter widgets and themes implementing the current macOS design language. -version: 2.1.8 +version: 2.1.9 homepage: "https://macosui.dev" repository: "https://github.com/GroovinChip/macos_ui" From 4a05264c8ef568f00b52bd40552d3ff06c00cfae Mon Sep 17 00:00:00 2001 From: Adrian Samoticha <86920182+Adrian-Samoticha@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:07:02 +0100 Subject: [PATCH 4/4] add changelog entry for version 2.1.9 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97a0ff60..9a6f2b7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [2.1.9] +### 🛠️ Fixed 🛠️ +* Fix incorrect highlighting and focusing behavior of `_MacosPopupMenuItemButton`. +* Remove scrolling animation from dropdown menus when manipulating the focus with the keyboard to mimic native macOS behavior. + ## [2.1.8] ### 🛠️ Fixed 🛠️ * Fixed `shownByDefault` not being respected for the left sidebar of the `MacosWindow` (thanks, [@ShayperCool](https://github.com/ShayperCool)).