diff --git a/CHANGELOG.md b/CHANGELOG.md index c76330f58..664276239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## next + +- `TabView` macos shortcuts ([#728](https://github.com/bdlukaa/fluent_ui/issues/728)) +- `TabView` focus on children now works properly ([#648](https://github.com/bdlukaa/fluent_ui/issues/648)) +- `TabView` colors now follow the Win UI 3 theme resources ([#730](https://github.com/bdlukaa/fluent_ui/pull/730)) +- Add myanmar localization ([#682](https://github.com/bdlukaa/fluent_ui/pull/682)) + ## 4.3.0 - Correctly calculate the padding around the flyout on automatic mode @@ -31,7 +38,6 @@ ``` - **BREAKING** Removed deprecated memebers: `DropDownButtonItem` and `DropDownButton.buttonStyle` ([#724](https://github.com/bdlukaa/fluent_ui/pull/724)) - `ThemeData` is depreacted. Use `FluentThemeData` instead ([#722](https://github.com/bdlukaa/fluent_ui/issues/722)) -- Add myanmar localization ## 4.2.0 diff --git a/example/lib/screens/navigation/tab_view.dart b/example/lib/screens/navigation/tab_view.dart index da8349eac..5b2ad2c92 100644 --- a/example/lib/screens/navigation/tab_view.dart +++ b/example/lib/screens/navigation/tab_view.dart @@ -155,8 +155,61 @@ class _TabViewPageState extends State with PageMixin { }, ), ), - // TODO: TabView snippets - codeSnippet: '''''', + codeSnippet: '''int currentIndex = 0; +List tabs = []; + +/// Creates a tab for the given index +Tab generateTab(int index) { + late Tab tab; + tab = Tab( + text: Text('Document \$index'), + semanticLabel: 'Document #\$index', + icon: const FlutterLogo(), + body: Container( + color: Colors.accentColors[Random().nextInt(Colors.accentColors.length)], + ), + onClosed: () { + setState(() { + tabs!.remove(tab); + + if (currentIndex > 0) currentIndex--; + }); + }, + ); + return tab; +} + +TabView( + tabs: tabs!, + currentIndex: currentIndex, + onChanged: (index) => setState(() => currentIndex = index), + tabWidthBehavior: $tabWidthBehavior, + closeButtonVisibility: $closeButtonVisibilityMode, + showScrollButtons: $showScrollButtons, + wheelScroll: $wheelScroll, + onNewPressed: () { + setState(() { + final index = tabs!.length + 1; + final tab = generateTab(index); + tabs!.add(tab); + }); + }, + onReorder: (oldIndex, newIndex) { + setState(() { + if (oldIndex < newIndex) { + newIndex -= 1; + } + final item = tabs!.removeAt(oldIndex); + tabs!.insert(newIndex, item); + + if (currentIndex == newIndex) { + currentIndex = oldIndex; + } else if (currentIndex == oldIndex) { + currentIndex = newIndex; + } + }); + }, +)''', ), ], ); diff --git a/example/pubspec.lock b/example/pubspec.lock index e4fed7dd4..c88c6fde2 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -563,5 +563,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.18.5 <3.0.0" + dart: ">=2.18.5 <4.0.0" flutter: ">=3.7.0" diff --git a/lib/src/controls/navigation/tab_view.dart b/lib/src/controls/navigation/tab_view.dart index f5bb0ed0b..a158cac45 100644 --- a/lib/src/controls/navigation/tab_view.dart +++ b/lib/src/controls/navigation/tab_view.dart @@ -584,9 +584,12 @@ class _TabViewState extends State { ), if (widget.tabs.isNotEmpty) Expanded( - child: IndexedStack( - index: widget.currentIndex, - children: widget.tabs.map((tab) => tab.body).toList(), + child: Focus( + autofocus: true, + child: IndexedStack( + index: widget.currentIndex, + children: widget.tabs.map((tab) => tab.body).toList(), + ), ), ), ]); @@ -595,17 +598,35 @@ class _TabViewState extends State { close(widget.currentIndex); } + // For more info, refer to [SingleActivator] docs + var ctrl = true; + var meta = false; + if (!kIsWeb && + [TargetPlatform.iOS, TargetPlatform.macOS] + .contains(defaultTargetPlatform)) { + ctrl = false; + meta = true; + } + return FocusScope( autofocus: true, child: CallbackShortcuts( bindings: { - const SingleActivator(LogicalKeyboardKey.f4, control: true): - onClosePressed, - const SingleActivator(LogicalKeyboardKey.keyW, control: true): - onClosePressed, - const SingleActivator(LogicalKeyboardKey.keyT, control: true): () { - widget.onNewPressed?.call(); - }, + SingleActivator( + LogicalKeyboardKey.f4, + control: ctrl, + meta: meta, + ): onClosePressed, + SingleActivator( + LogicalKeyboardKey.keyW, + control: ctrl, + meta: meta, + ): onClosePressed, + SingleActivator( + LogicalKeyboardKey.keyT, + control: ctrl, + meta: meta, + ): () => widget.onNewPressed?.call(), ...Map.fromIterable( List.generate(9, (index) => index), key: (i) { @@ -620,7 +641,7 @@ class _TabViewState extends State { LogicalKeyboardKey.digit8, LogicalKeyboardKey.digit9, ]; - return SingleActivator(digits[i], control: true); + return SingleActivator(digits[i], control: ctrl, meta: meta); }, value: (index) { return () { @@ -758,24 +779,46 @@ class __TabState extends State<_Tab> return (widget.tab.text as RichText).text.toPlainText(); } }(); + return HoverButton( key: widget.tab.key, semanticLabel: widget.tab.semanticLabel ?? text, onPressed: widget.onPressed, builder: (context, states) { - final foregroundColor = ButtonState.resolveWith((states) { - if (widget.selected) { - return res.textFillColorPrimary; - } else if (states.isPressing) { - return res.textFillColorTertiary; - } else if (states.isHovering) { + /// https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/TreeView/TreeView_themeresources.xaml#L19-L26 + final foregroundColor = ButtonState.resolveWith((states) { + if (states.isPressing) { return res.textFillColorSecondary; + } else if (states.isHovering) { + return res.textFillColorPrimary; } else if (states.isDisabled) { return res.textFillColorDisabled; } else { - return res.textFillColorSecondary; + return widget.selected + ? res.textFillColorPrimary + : res.textFillColorSecondary; + } + }).resolve(states); + + /// https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/TreeView/TreeView_themeresources.xaml#L10-L17 + final backgroundColor = ButtonState.resolveWith((states) { + if (states.isPressing) { + return widget.selected + ? res.subtleFillColorSecondary + : res.subtleFillColorTertiary; + } else if (states.isHovering) { + return widget.selected + ? res.subtleFillColorTertiary + : res.subtleFillColorSecondary; + } else if (states.isDisabled) { + return res.subtleFillColorDisabled; + } else { + return widget.selected + ? res.subtleFillColorSecondary + : res.subtleFillColorTransparent; } }).resolve(states); + const borderRadius = BorderRadius.vertical(top: Radius.circular(6)); Widget child = FocusBorder( focused: states.isFocused, @@ -786,8 +829,11 @@ class __TabState extends State<_Tab> height: _kTileHeight, constraints: widget.tabWidthBehavior == TabWidthBehavior.sizeToContent - ? null - : const BoxConstraints(maxWidth: _kMaxTileWidth), + ? const BoxConstraints(minHeight: 28.0) + : const BoxConstraints( + maxWidth: _kMaxTileWidth, + minHeight: 28.0, + ), padding: const EdgeInsetsDirectional.only( start: 8, top: 3, @@ -796,13 +842,9 @@ class __TabState extends State<_Tab> ), decoration: BoxDecoration( borderRadius: borderRadius, - color: widget.selected - ? null - : ButtonThemeData.uncheckedInputColor( - theme, - states, - transparentWhenNone: true, - ), + + // if selected, the background is painted by _TabPainter + color: widget.selected ? null : backgroundColor, ), child: () { final result = ClipRect( @@ -892,7 +934,7 @@ class __TabState extends State<_Tab> } if (widget.selected) { child = CustomPaint( - painter: _TabPainter(res.solidBackgroundFillColorTertiary), + painter: _TabPainter(backgroundColor), child: child, ); } diff --git a/pubspec.lock b/pubspec.lock index 64f4afbeb..686264266 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -358,5 +358,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.18.0 <3.0.0" + dart: ">=2.18.0 <4.0.0" flutter: ">=3.7.0"