Unverified Commit cd98c49c authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Some changes needed to PlatformMenuBar before the MenuBar implementation change lands. (#109556)

parent bacab7ee
...@@ -73,12 +73,12 @@ class _MyMenuBarAppState extends State<MyMenuBarApp> { ...@@ -73,12 +73,12 @@ class _MyMenuBarAppState extends State<MyMenuBarApp> {
// │ └ There's a million things I haven't done, but just you wait. // │ └ There's a million things I haven't done, but just you wait.
// └ Quit // └ Quit
return PlatformMenuBar( return PlatformMenuBar(
menus: <MenuItem>[ menus: <PlatformMenuItem>[
PlatformMenu( PlatformMenu(
label: 'Flutter API Sample', label: 'Flutter API Sample',
menus: <MenuItem>[ menus: <PlatformMenuItem>[
PlatformMenuItemGroup( PlatformMenuItemGroup(
members: <MenuItem>[ members: <PlatformMenuItem>[
PlatformMenuItem( PlatformMenuItem(
label: 'About', label: 'About',
onSelected: () { onSelected: () {
...@@ -88,7 +88,7 @@ class _MyMenuBarAppState extends State<MyMenuBarApp> { ...@@ -88,7 +88,7 @@ class _MyMenuBarAppState extends State<MyMenuBarApp> {
], ],
), ),
PlatformMenuItemGroup( PlatformMenuItemGroup(
members: <MenuItem>[ members: <PlatformMenuItem>[
PlatformMenuItem( PlatformMenuItem(
onSelected: () { onSelected: () {
_handleMenuSelection(MenuSelection.showMessage); _handleMenuSelection(MenuSelection.showMessage);
...@@ -98,7 +98,7 @@ class _MyMenuBarAppState extends State<MyMenuBarApp> { ...@@ -98,7 +98,7 @@ class _MyMenuBarAppState extends State<MyMenuBarApp> {
), ),
PlatformMenu( PlatformMenu(
label: 'Messages', label: 'Messages',
menus: <MenuItem>[ menus: <PlatformMenuItem>[
PlatformMenuItem( PlatformMenuItem(
label: 'I am not throwing away my shot.', label: 'I am not throwing away my shot.',
shortcut: const SingleActivator(LogicalKeyboardKey.digit1, meta: true), shortcut: const SingleActivator(LogicalKeyboardKey.digit1, meta: true),
......
...@@ -406,7 +406,7 @@ class SystemChannels { ...@@ -406,7 +406,7 @@ class SystemChannels {
/// encoding the list of top level menu items in window "0", which each /// encoding the list of top level menu items in window "0", which each
/// have a hierarchy of `Map<String, Object?>` containing the required /// have a hierarchy of `Map<String, Object?>` containing the required
/// data, sent via a [StandardMessageCodec]. It is typically generated from /// data, sent via a [StandardMessageCodec]. It is typically generated from
/// a list of [MenuItem]s, and ends up looking like this example: /// a list of [PlatformMenuItem]s, and ends up looking like this example:
/// ///
/// ```dart /// ```dart
/// Map<String, Object?> menu = <String, Object?>{ /// Map<String, Object?> menu = <String, Object?>{
......
...@@ -134,8 +134,9 @@ class ShortcutSerialization { ...@@ -134,8 +134,9 @@ class ShortcutSerialization {
/// equivalents) being down. /// equivalents) being down.
static const int _shortcutModifierControl = 1 << 3; static const int _shortcutModifierControl = 1 << 3;
/// Converts the internal representation to the format needed for a [MenuItem] /// Converts the internal representation to the format needed for a
/// to include it in its serialized form for sending to the platform. /// [PlatformMenuItem] to include it in its serialized form for sending to the
/// platform.
Map<String, Object?> toChannelRepresentation() => _internal; Map<String, Object?> toChannelRepresentation() => _internal;
} }
...@@ -153,112 +154,12 @@ class ShortcutSerialization { ...@@ -153,112 +154,12 @@ class ShortcutSerialization {
/// ///
/// * [SingleActivator], a [ShortcutActivator] which implements this mixin. /// * [SingleActivator], a [ShortcutActivator] which implements this mixin.
/// * [CharacterActivator], another [ShortcutActivator] which implements this mixin. /// * [CharacterActivator], another [ShortcutActivator] which implements this mixin.
mixin MenuSerializableShortcut { mixin MenuSerializableShortcut implements ShortcutActivator {
/// Implement this in a [ShortcutActivator] subclass to allow it to be /// Implement this in a [ShortcutActivator] subclass to allow it to be
/// serialized for use in a [PlatformMenuBar]. /// serialized for use in a [PlatformMenuBar].
ShortcutSerialization serializeForMenu(); ShortcutSerialization serializeForMenu();
} }
/// An abstract class for describing cascading menu hierarchies that are part of
/// a [PlatformMenuBar].
///
/// This type is also used by [PlatformMenuDelegate.setMenus] to accept the menu
/// hierarchy to be sent to the platform, and by [PlatformMenuBar] to define the
/// menu hierarchy.
///
/// This class is abstract, and so can't be used directly. Typically subclasses
/// like [PlatformMenuItem] are used.
///
/// See also:
///
/// * [PlatformMenuBar], a widget that renders menu items using platform APIs
/// instead of Flutter.
abstract class MenuItem with Diagnosticable {
/// Allows subclasses to have const constructors.
const MenuItem();
/// Converts the representation of this item into a map suitable for sending
/// over the default "flutter/menu" channel used by [DefaultPlatformMenuDelegate].
///
/// The `delegate` is the [PlatformMenuDelegate] that is requesting the
/// serialization. The `index` is the position of this menu item in the list
/// of [menus] of the [PlatformMenu] it belongs to, and `count` is the number
/// of [menus] in the [PlatformMenu] it belongs to.
///
/// The `getId` parameter is a [MenuItemSerializableIdGenerator] function that
/// generates a unique ID for each menu item, which is to be returned in the
/// "id" field of the menu item data.
Iterable<Map<String, Object?>> toChannelRepresentation(
PlatformMenuDelegate delegate, {
required MenuItemSerializableIdGenerator getId,
});
/// The optional shortcut that selects this [MenuItem].
///
/// This shortcut is only enabled when [onSelected] is set.
MenuSerializableShortcut? get shortcut => null;
/// Returns any child [MenuItem]s of this item.
///
/// Returns an empty list if this type of menu item doesn't have
/// children.
List<MenuItem> get menus => const <MenuItem>[];
/// Returns all descendant [MenuItem]s of this item.
///
/// Returns an empty list if this type of menu item doesn't have
/// descendants.
List<MenuItem> get descendants => const <MenuItem>[];
/// Returns a callback, if any, to be invoked if the platform menu receives a
/// "Menu.selectedCallback" method call from the platform for this item.
///
/// Only items that do not have submenus will have this callback invoked.
///
/// Only one of [onSelected] or [onSelectedIntent] may be specified.
///
/// If neither [onSelected] nor [onSelectedIntent] are specified, then this
/// menu item is considered to be disabled.
///
/// The default implementation returns null.
VoidCallback? get onSelected => null;
/// Returns an intent, if any, to be invoked if the platform receives a
/// "Menu.selectedCallback" method call from the platform for this item.
///
/// Only items that do not have submenus will have this intent invoked.
///
/// Only one of [onSelected] or [onSelectedIntent] may be specified.
///
/// If neither [onSelected] nor [onSelectedIntent] are specified, then this
/// menu item is considered to be disabled.
///
/// The default implementation returns null.
Intent? get onSelectedIntent => null;
/// Returns a callback, if any, to be invoked if the platform menu receives a
/// "Menu.opened" method call from the platform for this item.
///
/// Only items that have submenus will have this callback invoked.
///
/// The default implementation returns null.
VoidCallback? get onOpen => null;
/// Returns a callback, if any, to be invoked if the platform menu receives a
/// "Menu.closed" method call from the platform for this item.
///
/// Only items that have submenus will have this callback invoked.
///
/// The default implementation returns null.
VoidCallback? get onClose => null;
/// Returns the list of group members if this menu item is a "grouping" menu
/// item, such as [PlatformMenuItemGroup].
///
/// Defaults to an empty list.
List<MenuItem> get members => const <MenuItem>[];
}
/// An abstract delegate class that can be used to set /// An abstract delegate class that can be used to set
/// [WidgetsBinding.platformMenuDelegate] to provide for managing platform /// [WidgetsBinding.platformMenuDelegate] to provide for managing platform
/// menus. /// menus.
...@@ -308,7 +209,7 @@ abstract class PlatformMenuDelegate { ...@@ -308,7 +209,7 @@ abstract class PlatformMenuDelegate {
/// that appear in a cascading menu. /// that appear in a cascading menu.
/// * [PlatformMenuItem], the class that describes the leaves of a menu /// * [PlatformMenuItem], the class that describes the leaves of a menu
/// hierarchy. /// hierarchy.
void setMenus(List<MenuItem> topLevelMenus); void setMenus(List<PlatformMenuItem> topLevelMenus);
/// Clears any existing platform-rendered menus and leaves the application /// Clears any existing platform-rendered menus and leaves the application
/// with no menus. /// with no menus.
...@@ -346,8 +247,8 @@ abstract class PlatformMenuDelegate { ...@@ -346,8 +247,8 @@ abstract class PlatformMenuDelegate {
} }
/// The signature for a function that generates unique menu item IDs for /// The signature for a function that generates unique menu item IDs for
/// serialization of a [MenuItem]. /// serialization of a [PlatformMenuItem].
typedef MenuItemSerializableIdGenerator = int Function(MenuItem item); typedef MenuItemSerializableIdGenerator = int Function(PlatformMenuItem item);
/// The platform menu delegate that handles the built-in macOS platform menu /// The platform menu delegate that handles the built-in macOS platform menu
/// generation using the 'flutter/menu' channel. /// generation using the 'flutter/menu' channel.
...@@ -370,12 +271,12 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate { ...@@ -370,12 +271,12 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate {
/// with the platform. It defaults to [SystemChannels.menu] if not supplied. /// with the platform. It defaults to [SystemChannels.menu] if not supplied.
DefaultPlatformMenuDelegate({MethodChannel? channel}) DefaultPlatformMenuDelegate({MethodChannel? channel})
: channel = channel ?? SystemChannels.menu, : channel = channel ?? SystemChannels.menu,
_idMap = <int, MenuItem>{} { _idMap = <int, PlatformMenuItem>{} {
this.channel.setMethodCallHandler(_methodCallHandler); this.channel.setMethodCallHandler(_methodCallHandler);
} }
// Map of distributed IDs to menu items. // Map of distributed IDs to menu items.
final Map<int, MenuItem> _idMap; final Map<int, PlatformMenuItem> _idMap;
// An ever increasing value used to dole out IDs. // An ever increasing value used to dole out IDs.
int _serial = 0; int _serial = 0;
// The context used to "lock" this delegate to a specific instance of // The context used to "lock" this delegate to a specific instance of
...@@ -383,14 +284,14 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate { ...@@ -383,14 +284,14 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate {
BuildContext? _lockedContext; BuildContext? _lockedContext;
@override @override
void clearMenus() => setMenus(<MenuItem>[]); void clearMenus() => setMenus(<PlatformMenuItem>[]);
@override @override
void setMenus(List<MenuItem> topLevelMenus) { void setMenus(List<PlatformMenuItem> topLevelMenus) {
_idMap.clear(); _idMap.clear();
final List<Map<String, Object?>> representation = <Map<String, Object?>>[]; final List<Map<String, Object?>> representation = <Map<String, Object?>>[];
if (topLevelMenus.isNotEmpty) { if (topLevelMenus.isNotEmpty) {
for (final MenuItem childItem in topLevelMenus) { for (final PlatformMenuItem childItem in topLevelMenus) {
representation.addAll(childItem.toChannelRepresentation(this, getId: _getId)); representation.addAll(childItem.toChannelRepresentation(this, getId: _getId));
} }
} }
...@@ -412,7 +313,7 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate { ...@@ -412,7 +313,7 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate {
/// ///
/// This is called by each DefaultPlatformMenuDelegateSerializer when /// This is called by each DefaultPlatformMenuDelegateSerializer when
/// serializing a new object so that it has a unique ID. /// serializing a new object so that it has a unique ID.
int _getId(MenuItem item) { int _getId(PlatformMenuItem item) {
_serial += 1; _serial += 1;
_idMap[_serial] = item; _idMap[_serial] = item;
return _serial; return _serial;
...@@ -457,10 +358,10 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate { ...@@ -457,10 +358,10 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate {
if (!_idMap.containsKey(id)) { if (!_idMap.containsKey(id)) {
return; return;
} }
final MenuItem item = _idMap[id]!; final PlatformMenuItem item = _idMap[id]!;
if (call.method == _kMenuSelectedCallbackMethod) { if (call.method == _kMenuSelectedCallbackMethod) {
assert(item.onSelected == null || item.onSelectedIntent == null, assert(item.onSelected == null || item.onSelectedIntent == null,
'Only one of MenuItem.onSelected or MenuItem.onSelectedIntent may be specified'); 'Only one of PlatformMenuItem.onSelected or PlatformMenuItem.onSelectedIntent may be specified');
item.onSelected?.call(); item.onSelected?.call();
if (item.onSelectedIntent != null) { if (item.onSelectedIntent != null) {
Actions.maybeInvoke(FocusManager.instance.primaryFocus!.context!, item.onSelectedIntent!); Actions.maybeInvoke(FocusManager.instance.primaryFocus!.context!, item.onSelectedIntent!);
...@@ -481,32 +382,40 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate { ...@@ -481,32 +382,40 @@ class DefaultPlatformMenuDelegate extends PlatformMenuDelegate {
/// the box, but support for other platforms may be provided via plugins that /// the box, but support for other platforms may be provided via plugins that
/// set [WidgetsBinding.platformMenuDelegate] in their initialization. /// set [WidgetsBinding.platformMenuDelegate] in their initialization.
/// ///
/// The [menus] member contains [MenuItem]s. They will not be part of the /// The [menus] member contains [PlatformMenuItem]s, which configure the
/// widget tree, since they are not required to be widgets (even if they happen /// properties of the menus on the platform menu bar.
/// to be widgets that implement [MenuItem], they still won't be part of the
/// widget tree). They are provided to configure the properties of the menus on
/// the platform menu bar.
/// ///
/// As far as Flutter is concerned, this widget has no visual representation, /// As far as Flutter is concerned, this widget has no visual representation,
/// and intercepts no events: it just returns the [child] from its build /// and intercepts no events: it just returns the [child] from its build
/// function. This is because all of the rendering, shortcuts, and event /// function. This is because all of the rendering, shortcuts, and event
/// handling for the menu is handled by the plugin on the host platform. /// handling for the menu is handled by the plugin on the host platform. It is
/// only part of the widget tree to provide a convenient refresh mechanism for
/// the menu data.
/// ///
/// There can only be one [PlatformMenuBar] at a time using the same /// There can only be one [PlatformMenuBar] at a time using the same
/// [PlatformMenuDelegate]. It will assert if more than one is detected. /// [PlatformMenuDelegate]. It will assert if more than one is detected.
/// ///
/// When calling [toStringDeep] on this widget, it will give a tree of /// When calling [toStringDeep] on this widget, it will give a tree of
/// [MenuItem]s, not a tree of widgets. /// [PlatformMenuItem]s, not a tree of widgets.
/// ///
/// {@tool sample} /// {@tool sample} This example shows a [PlatformMenuBar] that contains a single
/// This example shows a [PlatformMenuBar] that contains a single top level /// top level menu, containing three items for "About", a toggleable menu item
/// menu, containing three items for "About", a toggleable menu item for showing /// for showing a message, a cascading submenu with message choices, and "Quit".
/// a message, a cascading submenu with message choices, and "Quit".
/// ///
/// **This example will only work on macOS.** /// **This example will only work on macOS.**
/// ///
/// ** See code in examples/api/lib/material/platform_menu_bar/platform_menu_bar.0.dart ** /// ** See code in examples/api/lib/material/platform_menu_bar/platform_menu_bar.0.dart **
/// {@end-tool} /// {@end-tool}
///
/// The menus could just as effectively be managed without using the widget tree
/// by using the following code, but mixing this usage with `PlatformMenuBar` is
/// not recommended, since it will overwrite the menu configuration when it is
/// rebuilt:
///
/// ```dart
/// List<PlatformMenuItem> menus = <PlatformMenuItem>[ /* Define menus... */ ];
/// WidgetsBinding.instance.platformMenuDelegate.setMenus(menus);
/// ```
class PlatformMenuBar extends StatefulWidget with DiagnosticableTreeMixin { class PlatformMenuBar extends StatefulWidget with DiagnosticableTreeMixin {
/// Creates a const [PlatformMenuBar]. /// Creates a const [PlatformMenuBar].
/// ///
...@@ -540,8 +449,8 @@ class PlatformMenuBar extends StatefulWidget with DiagnosticableTreeMixin { ...@@ -540,8 +449,8 @@ class PlatformMenuBar extends StatefulWidget with DiagnosticableTreeMixin {
/// The list of menu items that are the top level children of the /// The list of menu items that are the top level children of the
/// [PlatformMenuBar]. /// [PlatformMenuBar].
/// ///
/// The `menus` member contains [MenuItem]s. They will not be part /// The `menus` member contains [PlatformMenuItem]s. They will not be part of
/// of the widget tree, since they are not widgets. They are provided to /// the widget tree, since they are not widgets. They are provided to
/// configure the properties of the menus on the platform menu bar. /// configure the properties of the menus on the platform menu bar.
/// ///
/// Also, a Widget in Flutter is immutable, so directly modifying the /// Also, a Widget in Flutter is immutable, so directly modifying the
...@@ -549,19 +458,19 @@ class PlatformMenuBar extends StatefulWidget with DiagnosticableTreeMixin { ...@@ -549,19 +458,19 @@ class PlatformMenuBar extends StatefulWidget with DiagnosticableTreeMixin {
/// `somePlatformMenuBarWidget.menus.add(...)` will result in incorrect /// `somePlatformMenuBarWidget.menus.add(...)` will result in incorrect
/// behaviors. Whenever the menus list is modified, a new list object /// behaviors. Whenever the menus list is modified, a new list object
/// should be provided. /// should be provided.
final List<MenuItem> menus; final List<PlatformMenuItem> menus;
@override @override
State<PlatformMenuBar> createState() => _PlatformMenuBarState(); State<PlatformMenuBar> createState() => _PlatformMenuBarState();
@override @override
List<DiagnosticsNode> debugDescribeChildren() { List<DiagnosticsNode> debugDescribeChildren() {
return menus.map<DiagnosticsNode>((MenuItem child) => child.toDiagnosticsNode()).toList(); return menus.map<DiagnosticsNode>((PlatformMenuItem child) => child.toDiagnosticsNode()).toList();
} }
} }
class _PlatformMenuBarState extends State<PlatformMenuBar> { class _PlatformMenuBarState extends State<PlatformMenuBar> {
List<MenuItem> descendants = <MenuItem>[]; List<PlatformMenuItem> descendants = <PlatformMenuItem>[];
@override @override
void initState() { void initState() {
...@@ -585,8 +494,8 @@ class _PlatformMenuBarState extends State<PlatformMenuBar> { ...@@ -585,8 +494,8 @@ class _PlatformMenuBarState extends State<PlatformMenuBar> {
@override @override
void didUpdateWidget(PlatformMenuBar oldWidget) { void didUpdateWidget(PlatformMenuBar oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
final List<MenuItem> newDescendants = <MenuItem>[ final List<PlatformMenuItem> newDescendants = <PlatformMenuItem>[
for (final MenuItem item in widget.menus) ...<MenuItem>[ for (final PlatformMenuItem item in widget.menus) ...<PlatformMenuItem>[
item, item,
...item.descendants, ...item.descendants,
], ],
...@@ -617,45 +526,39 @@ class _PlatformMenuBarState extends State<PlatformMenuBar> { ...@@ -617,45 +526,39 @@ class _PlatformMenuBarState extends State<PlatformMenuBar> {
/// ///
/// * [PlatformMenuItem], a class representing a leaf menu item in a /// * [PlatformMenuItem], a class representing a leaf menu item in a
/// [PlatformMenuBar]. /// [PlatformMenuBar].
class PlatformMenu extends MenuItem with DiagnosticableTreeMixin { class PlatformMenu extends PlatformMenuItem with DiagnosticableTreeMixin {
/// Creates a const [PlatformMenu]. /// Creates a const [PlatformMenu].
/// ///
/// The [label] and [menus] fields are required. /// The [label] and [menus] fields are required.
const PlatformMenu({ const PlatformMenu({
required this.label, required super.label,
this.onOpen, this.onOpen,
this.onClose, this.onClose,
required this.menus, required this.menus,
}); });
/// The label that will appear on the menu.
final String label;
/// The callback that is called when this menu is opened.
@override @override
final VoidCallback? onOpen; final VoidCallback? onOpen;
/// The callback that is called when this menu is closed.
@override @override
final VoidCallback? onClose; final VoidCallback? onClose;
/// The menu items in the submenu opened by this menu item. /// The menu items in the submenu opened by this menu item.
/// ///
/// If this is an empty list, this [PlatformMenu] will be disabled. /// If this is an empty list, this [PlatformMenu] will be disabled.
@override final List<PlatformMenuItem> menus;
final List<MenuItem> menus;
/// Returns all descendant [MenuItem]s of this item. /// Returns all descendant [PlatformMenuItem]s of this item.
@override @override
List<MenuItem> get descendants => getDescendants(this); List<PlatformMenuItem> get descendants => getDescendants(this);
/// Returns all descendants of the given item. /// Returns all descendants of the given item.
/// ///
/// This API is supplied so that implementers of [PlatformMenu] can share /// This API is supplied so that implementers of [PlatformMenu] can share
/// this implementation. /// this implementation.
static List<MenuItem> getDescendants(PlatformMenu item) { static List<PlatformMenuItem> getDescendants(PlatformMenu item) {
return <MenuItem>[ return <PlatformMenuItem>[
for (final MenuItem child in item.menus) ...<MenuItem>[ for (final PlatformMenuItem child in item.menus) ...<PlatformMenuItem>[
child, child,
...child.descendants, ...child.descendants,
], ],
...@@ -681,7 +584,7 @@ class PlatformMenu extends MenuItem with DiagnosticableTreeMixin { ...@@ -681,7 +584,7 @@ class PlatformMenu extends MenuItem with DiagnosticableTreeMixin {
MenuItemSerializableIdGenerator getId, MenuItemSerializableIdGenerator getId,
) { ) {
final List<Map<String, Object?>> result = <Map<String, Object?>>[]; final List<Map<String, Object?>> result = <Map<String, Object?>>[];
for (final MenuItem childItem in item.menus) { for (final PlatformMenuItem childItem in item.menus) {
result.addAll(childItem.toChannelRepresentation( result.addAll(childItem.toChannelRepresentation(
delegate, delegate,
getId: getId, getId: getId,
...@@ -717,7 +620,7 @@ class PlatformMenu extends MenuItem with DiagnosticableTreeMixin { ...@@ -717,7 +620,7 @@ class PlatformMenu extends MenuItem with DiagnosticableTreeMixin {
@override @override
List<DiagnosticsNode> debugDescribeChildren() { List<DiagnosticsNode> debugDescribeChildren() {
return menus.map<DiagnosticsNode>((MenuItem child) => child.toDiagnosticsNode()).toList(); return menus.map<DiagnosticsNode>((PlatformMenuItem child) => child.toDiagnosticsNode()).toList();
} }
@override @override
...@@ -733,17 +636,17 @@ class PlatformMenu extends MenuItem with DiagnosticableTreeMixin { ...@@ -733,17 +636,17 @@ class PlatformMenu extends MenuItem with DiagnosticableTreeMixin {
/// Visual dividers will be added before and after this group if other menu /// Visual dividers will be added before and after this group if other menu
/// items appear in the [PlatformMenu], and the leading one omitted if it is /// items appear in the [PlatformMenu], and the leading one omitted if it is
/// first and the trailing one omitted if it is last in the menu. /// first and the trailing one omitted if it is last in the menu.
class PlatformMenuItemGroup extends MenuItem { class PlatformMenuItemGroup extends PlatformMenuItem {
/// Creates a const [PlatformMenuItemGroup]. /// Creates a const [PlatformMenuItemGroup].
/// ///
/// The [members] field is required. /// The [members] field is required.
const PlatformMenuItemGroup({required this.members}); const PlatformMenuItemGroup({required this.members}) : super(label: '');
/// The [MenuItem]s that are members of this menu item group. /// The [PlatformMenuItem]s that are members of this menu item group.
/// ///
/// An assertion will be thrown if there isn't at least one member of the group. /// An assertion will be thrown if there isn't at least one member of the group.
@override @override
final List<MenuItem> members; final List<PlatformMenuItem> members;
@override @override
Iterable<Map<String, Object?>> toChannelRepresentation( Iterable<Map<String, Object?>> toChannelRepresentation(
...@@ -760,7 +663,7 @@ class PlatformMenuItemGroup extends MenuItem { ...@@ -760,7 +663,7 @@ class PlatformMenuItemGroup extends MenuItem {
/// This API is supplied so that implementers of [PlatformMenuItemGroup] can share /// This API is supplied so that implementers of [PlatformMenuItemGroup] can share
/// this implementation. /// this implementation.
static Iterable<Map<String, Object?>> serialize( static Iterable<Map<String, Object?>> serialize(
MenuItem group, PlatformMenuItem group,
PlatformMenuDelegate delegate, { PlatformMenuDelegate delegate, {
required MenuItemSerializableIdGenerator getId, required MenuItemSerializableIdGenerator getId,
}) { }) {
...@@ -769,7 +672,7 @@ class PlatformMenuItemGroup extends MenuItem { ...@@ -769,7 +672,7 @@ class PlatformMenuItemGroup extends MenuItem {
_kIdKey: getId(group), _kIdKey: getId(group),
_kIsDividerKey: true, _kIsDividerKey: true,
}); });
for (final MenuItem item in group.members) { for (final PlatformMenuItem item in group.members) {
result.addAll(item.toChannelRepresentation( result.addAll(item.toChannelRepresentation(
delegate, delegate,
getId: getId, getId: getId,
...@@ -785,21 +688,21 @@ class PlatformMenuItemGroup extends MenuItem { ...@@ -785,21 +688,21 @@ class PlatformMenuItemGroup extends MenuItem {
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties); super.debugFillProperties(properties);
properties.add(IterableProperty<MenuItem>('members', members)); properties.add(IterableProperty<PlatformMenuItem>('members', members));
} }
} }
/// A class for [MenuItem]s that do not have submenus (as a [PlatformMenu] /// A class for [PlatformMenuItem]s that do not have submenus (as a [PlatformMenu]
/// would), but can be selected. /// would), but can be selected.
/// ///
/// These [MenuItem]s are the leaves of the menu item tree, and [onSelected] /// These [PlatformMenuItem]s are the leaves of the menu item tree, and [onSelected]
/// will be called when they are selected by clicking on them, or via an /// will be called when they are selected by clicking on them, or via an
/// optional keyboard [shortcut]. /// optional keyboard [shortcut].
/// ///
/// See also: /// See also:
/// ///
/// * [PlatformMenu], a menu item that opens a submenu. /// * [PlatformMenu], a menu item that opens a submenu.
class PlatformMenuItem extends MenuItem { class PlatformMenuItem with Diagnosticable {
/// Creates a const [PlatformMenuItem]. /// Creates a const [PlatformMenuItem].
/// ///
/// The [label] attribute is required. /// The [label] attribute is required.
...@@ -816,24 +719,57 @@ class PlatformMenuItem extends MenuItem { ...@@ -816,24 +719,57 @@ class PlatformMenuItem extends MenuItem {
/// The optional shortcut that selects this [PlatformMenuItem]. /// The optional shortcut that selects this [PlatformMenuItem].
/// ///
/// This shortcut is only enabled when [onSelected] is set. /// This shortcut is only enabled when [onSelected] is set.
@override
final MenuSerializableShortcut? shortcut; final MenuSerializableShortcut? shortcut;
/// An optional callback that is called when this [PlatformMenuItem] is /// An optional callback that is called when this [PlatformMenuItem] is
/// selected. /// selected.
/// ///
/// If unset, this menu item will be disabled. /// If unset, this menu item will be disabled.
@override
final VoidCallback? onSelected; final VoidCallback? onSelected;
/// Returns a callback, if any, to be invoked if the platform menu receives a
/// "Menu.opened" method call from the platform for this item.
///
/// Only items that have submenus will have this callback invoked.
///
/// The default implementation returns null.
VoidCallback? get onOpen => null;
/// Returns a callback, if any, to be invoked if the platform menu receives a
/// "Menu.closed" method call from the platform for this item.
///
/// Only items that have submenus will have this callback invoked.
///
/// The default implementation returns null.
VoidCallback? get onClose => null;
/// An optional intent that is invoked when this [PlatformMenuItem] is /// An optional intent that is invoked when this [PlatformMenuItem] is
/// selected. /// selected.
/// ///
/// If unset, this menu item will be disabled. /// If unset, this menu item will be disabled.
@override
final Intent? onSelectedIntent; final Intent? onSelectedIntent;
@override /// Returns all descendant [PlatformMenuItem]s of this item.
///
/// Returns an empty list if this type of menu item doesn't have
/// descendants.
List<PlatformMenuItem> get descendants => const <PlatformMenuItem>[];
/// Returns the list of group members if this menu item is a "grouping" menu
/// item, such as [PlatformMenuItemGroup].
///
/// Defaults to an empty list.
List<PlatformMenuItem> get members => const <PlatformMenuItem>[];
/// Converts the representation of this item into a map suitable for sending
/// over the default "flutter/menu" channel used by [DefaultPlatformMenuDelegate].
///
/// The `delegate` is the [PlatformMenuDelegate] that is requesting the
/// serialization.
///
/// The `getId` parameter is a [MenuItemSerializableIdGenerator] function that
/// generates a unique ID for each menu item, which is to be returned in the
/// "id" field of the menu item data.
Iterable<Map<String, Object?>> toChannelRepresentation( Iterable<Map<String, Object?>> toChannelRepresentation(
PlatformMenuDelegate delegate, { PlatformMenuDelegate delegate, {
required MenuItemSerializableIdGenerator getId, required MenuItemSerializableIdGenerator getId,
......
...@@ -163,9 +163,9 @@ void main() { ...@@ -163,9 +163,9 @@ void main() {
const MaterialApp( const MaterialApp(
home: Material( home: Material(
child: PlatformMenuBar( child: PlatformMenuBar(
menus: <MenuItem>[], menus: <PlatformMenuItem>[],
child: PlatformMenuBar( child: PlatformMenuBar(
menus: <MenuItem>[], menus: <PlatformMenuItem>[],
child: SizedBox(), child: SizedBox(),
), ),
), ),
...@@ -180,7 +180,7 @@ void main() { ...@@ -180,7 +180,7 @@ void main() {
shortcut: SingleActivator(LogicalKeyboardKey.keyA), shortcut: SingleActivator(LogicalKeyboardKey.keyA),
); );
const PlatformMenuBar menuBar = PlatformMenuBar( const PlatformMenuBar menuBar = PlatformMenuBar(
menus: <MenuItem>[item], menus: <PlatformMenuItem>[item],
child: SizedBox(), child: SizedBox(),
); );
...@@ -205,14 +205,14 @@ void main() { ...@@ -205,14 +205,14 @@ void main() {
); );
}); });
}); });
group('PlatformMenuBarItem', () { group('MenuBarItem', () {
testWidgets('diagnostics', (WidgetTester tester) async { testWidgets('diagnostics', (WidgetTester tester) async {
const PlatformMenuItem childItem = PlatformMenuItem( const PlatformMenuItem childItem = PlatformMenuItem(
label: 'label', label: 'label',
); );
const PlatformMenu item = PlatformMenu( const PlatformMenu item = PlatformMenu(
label: 'label', label: 'label',
menus: <MenuItem>[childItem], menus: <PlatformMenuItem>[childItem],
); );
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
...@@ -258,19 +258,19 @@ const List<String> subMenu2 = <String>[ ...@@ -258,19 +258,19 @@ const List<String> subMenu2 = <String>[
'Sub Menu 20', 'Sub Menu 20',
]; ];
List<MenuItem> createTestMenus({ List<PlatformMenuItem> createTestMenus({
void Function(String)? onActivate, void Function(String)? onActivate,
void Function(String)? onOpen, void Function(String)? onOpen,
void Function(String)? onClose, void Function(String)? onClose,
Map<String, MenuSerializableShortcut> shortcuts = const <String, MenuSerializableShortcut>{}, Map<String, MenuSerializableShortcut> shortcuts = const <String, MenuSerializableShortcut>{},
bool includeStandard = false, bool includeStandard = false,
}) { }) {
final List<MenuItem> result = <MenuItem>[ final List<PlatformMenuItem> result = <PlatformMenuItem>[
PlatformMenu( PlatformMenu(
label: mainMenu[0], label: mainMenu[0],
onOpen: onOpen != null ? () => onOpen(mainMenu[0]) : null, onOpen: onOpen != null ? () => onOpen(mainMenu[0]) : null,
onClose: onClose != null ? () => onClose(mainMenu[0]) : null, onClose: onClose != null ? () => onClose(mainMenu[0]) : null,
menus: <MenuItem>[ menus: <PlatformMenuItem>[
PlatformMenuItem( PlatformMenuItem(
label: subMenu0[0], label: subMenu0[0],
onSelected: onActivate != null ? () => onActivate(subMenu0[0]) : null, onSelected: onActivate != null ? () => onActivate(subMenu0[0]) : null,
...@@ -282,9 +282,9 @@ List<MenuItem> createTestMenus({ ...@@ -282,9 +282,9 @@ List<MenuItem> createTestMenus({
label: mainMenu[1], label: mainMenu[1],
onOpen: onOpen != null ? () => onOpen(mainMenu[1]) : null, onOpen: onOpen != null ? () => onOpen(mainMenu[1]) : null,
onClose: onClose != null ? () => onClose(mainMenu[1]) : null, onClose: onClose != null ? () => onClose(mainMenu[1]) : null,
menus: <MenuItem>[ menus: <PlatformMenuItem>[
PlatformMenuItemGroup( PlatformMenuItemGroup(
members: <MenuItem>[ members: <PlatformMenuItem>[
PlatformMenuItem( PlatformMenuItem(
label: subMenu1[0], label: subMenu1[0],
onSelected: onActivate != null ? () => onActivate(subMenu1[0]) : null, onSelected: onActivate != null ? () => onActivate(subMenu1[0]) : null,
...@@ -296,9 +296,9 @@ List<MenuItem> createTestMenus({ ...@@ -296,9 +296,9 @@ List<MenuItem> createTestMenus({
label: subMenu1[1], label: subMenu1[1],
onOpen: onOpen != null ? () => onOpen(subMenu1[1]) : null, onOpen: onOpen != null ? () => onOpen(subMenu1[1]) : null,
onClose: onClose != null ? () => onClose(subMenu1[1]) : null, onClose: onClose != null ? () => onClose(subMenu1[1]) : null,
menus: <MenuItem>[ menus: <PlatformMenuItem>[
PlatformMenuItemGroup( PlatformMenuItemGroup(
members: <MenuItem>[ members: <PlatformMenuItem>[
PlatformMenuItem( PlatformMenuItem(
label: subSubMenu10[0], label: subSubMenu10[0],
onSelected: onActivate != null ? () => onActivate(subSubMenu10[0]) : null, onSelected: onActivate != null ? () => onActivate(subSubMenu10[0]) : null,
...@@ -307,7 +307,7 @@ List<MenuItem> createTestMenus({ ...@@ -307,7 +307,7 @@ List<MenuItem> createTestMenus({
], ],
), ),
PlatformMenuItemGroup( PlatformMenuItemGroup(
members: <MenuItem>[ members: <PlatformMenuItem>[
PlatformMenuItem( PlatformMenuItem(
label: subSubMenu10[1], label: subSubMenu10[1],
onSelected: onActivate != null ? () => onActivate(subSubMenu10[1]) : null, onSelected: onActivate != null ? () => onActivate(subSubMenu10[1]) : null,
...@@ -321,7 +321,7 @@ List<MenuItem> createTestMenus({ ...@@ -321,7 +321,7 @@ List<MenuItem> createTestMenus({
shortcut: shortcuts[subSubMenu10[2]], shortcut: shortcuts[subSubMenu10[2]],
), ),
PlatformMenuItemGroup( PlatformMenuItemGroup(
members: <MenuItem>[ members: <PlatformMenuItem>[
PlatformMenuItem( PlatformMenuItem(
label: subSubMenu10[3], label: subSubMenu10[3],
onSelected: onActivate != null ? () => onActivate(subSubMenu10[3]) : null, onSelected: onActivate != null ? () => onActivate(subSubMenu10[3]) : null,
...@@ -342,7 +342,7 @@ List<MenuItem> createTestMenus({ ...@@ -342,7 +342,7 @@ List<MenuItem> createTestMenus({
label: mainMenu[2], label: mainMenu[2],
onOpen: onOpen != null ? () => onOpen(mainMenu[2]) : null, onOpen: onOpen != null ? () => onOpen(mainMenu[2]) : null,
onClose: onClose != null ? () => onClose(mainMenu[2]) : null, onClose: onClose != null ? () => onClose(mainMenu[2]) : null,
menus: <MenuItem>[ menus: <PlatformMenuItem>[
PlatformMenuItem( PlatformMenuItem(
// Always disabled. // Always disabled.
label: subMenu2[0], label: subMenu2[0],
...@@ -355,7 +355,7 @@ List<MenuItem> createTestMenus({ ...@@ -355,7 +355,7 @@ List<MenuItem> createTestMenus({
label: mainMenu[3], label: mainMenu[3],
onOpen: onOpen != null ? () => onOpen(mainMenu[2]) : null, onOpen: onOpen != null ? () => onOpen(mainMenu[2]) : null,
onClose: onClose != null ? () => onClose(mainMenu[2]) : null, onClose: onClose != null ? () => onClose(mainMenu[2]) : null,
menus: <MenuItem>[], menus: <PlatformMenuItem>[],
), ),
]; ];
return result; return result;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment