// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(
    const MaterialApp(
      title: 'Menu Tester',
      home: Material(
        child: Home(),
      ),
    ),
  );
}

class Home extends StatefulWidget {
  const Home({super.key});

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  final MenuController _controller = MenuController();
  VisualDensity _density = VisualDensity.standard;
  TextDirection _textDirection = TextDirection.ltr;
  double _extraPadding = 0;
  bool _addItem = false;
  bool _transparent = false;
  bool _funkyTheme = false;

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
    MenuThemeData menuTheme = MenuTheme.of(context);
    MenuBarThemeData menuBarTheme = MenuBarTheme.of(context);
    MenuButtonThemeData menuButtonTheme = MenuButtonTheme.of(context);
    if (_funkyTheme) {
      menuTheme = const MenuThemeData(
        style: MenuStyle(
          shape: MaterialStatePropertyAll<OutlinedBorder>(
            RoundedRectangleBorder(
              borderRadius: BorderRadius.all(
                Radius.circular(10),
              ),
            ),
          ),
          backgroundColor: MaterialStatePropertyAll<Color?>(Colors.blue),
          elevation: MaterialStatePropertyAll<double?>(10),
          padding: MaterialStatePropertyAll<EdgeInsetsDirectional>(
            EdgeInsetsDirectional.all(20),
          ),
        ),
      );
      menuButtonTheme = const MenuButtonThemeData(
        style: ButtonStyle(
          shape: MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder()),
          backgroundColor: MaterialStatePropertyAll<Color?>(Colors.green),
          foregroundColor: MaterialStatePropertyAll<Color?>(Colors.white),
        ),
      );
      menuBarTheme = const MenuBarThemeData(
        style: MenuStyle(
          shape: MaterialStatePropertyAll<OutlinedBorder>(RoundedRectangleBorder()),
          backgroundColor: MaterialStatePropertyAll<Color?>(Colors.blue),
          elevation: MaterialStatePropertyAll<double?>(10),
          padding: MaterialStatePropertyAll<EdgeInsetsDirectional>(
            EdgeInsetsDirectional.all(20),
          ),
        ),
      );
    }
    return SafeArea(
      child: Padding(
        padding: EdgeInsets.all(_extraPadding),
        child: Directionality(
          textDirection: _textDirection,
          child: Theme(
            data: theme.copyWith(
              visualDensity: _density,
              menuTheme: _transparent
                  ? MenuThemeData(
                      style: MenuStyle(
                        backgroundColor: MaterialStatePropertyAll<Color>(
                          Colors.blue.withOpacity(0.12),
                        ),
                        elevation: const MaterialStatePropertyAll<double>(0),
                      ),
                    )
                  : menuTheme,
              menuBarTheme: menuBarTheme,
              menuButtonTheme: menuButtonTheme,
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                _TestMenus(
                  menuController: _controller,
                  addItem: _addItem,
                ),
                Expanded(
                  child: SingleChildScrollView(
                    child: _Controls(
                      menuController: _controller,
                      density: _density,
                      addItem: _addItem,
                      transparent: _transparent,
                      funkyTheme: _funkyTheme,
                      extraPadding: _extraPadding,
                      textDirection: _textDirection,
                      onDensityChanged: (VisualDensity value) {
                        setState(() {
                          _density = value;
                        });
                      },
                      onTextDirectionChanged: (TextDirection value) {
                        setState(() {
                          _textDirection = value;
                        });
                      },
                      onExtraPaddingChanged: (double value) {
                        setState(() {
                          _extraPadding = value;
                        });
                      },
                      onAddItemChanged: (bool value) {
                        setState(() {
                          _addItem = value;
                        });
                      },
                      onTransparentChanged: (bool value) {
                        setState(() {
                          _transparent = value;
                        });
                      },
                      onFunkyThemeChanged: (bool value) {
                        setState(() {
                          _funkyTheme = value;
                        });
                      },
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class _Controls extends StatefulWidget {
  const _Controls({
    required this.density,
    required this.textDirection,
    required this.extraPadding,
    this.addItem = false,
    this.transparent = false,
    this.funkyTheme = false,
    required this.onDensityChanged,
    required this.onTextDirectionChanged,
    required this.onExtraPaddingChanged,
    required this.onAddItemChanged,
    required this.onTransparentChanged,
    required this.onFunkyThemeChanged,
    required this.menuController,
  });

  final VisualDensity density;
  final TextDirection textDirection;
  final double extraPadding;
  final bool addItem;
  final bool transparent;
  final bool funkyTheme;
  final ValueChanged<VisualDensity> onDensityChanged;
  final ValueChanged<TextDirection> onTextDirectionChanged;
  final ValueChanged<double> onExtraPaddingChanged;
  final ValueChanged<bool> onAddItemChanged;
  final ValueChanged<bool> onTransparentChanged;
  final ValueChanged<bool> onFunkyThemeChanged;
  final MenuController menuController;

  @override
  State<_Controls> createState() => _ControlsState();
}

class _ControlsState extends State<_Controls> {
  final FocusNode _focusNode = FocusNode(debugLabel: 'Floating');

  @override
  void dispose() {
    _focusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.lightBlueAccent,
      alignment: Alignment.center,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          MenuAnchor(
            childFocusNode: _focusNode,
            style: const MenuStyle(alignment: AlignmentDirectional.topEnd),
            alignmentOffset: const Offset(100, -8),
            menuChildren: <Widget>[
              MenuItemButton(
                shortcut: TestMenu.standaloneMenu1.shortcut,
                onPressed: () {
                  _itemSelected(TestMenu.standaloneMenu1);
                },
                child: Text(TestMenu.standaloneMenu1.label),
              ),
              MenuItemButton(
                leadingIcon: const Icon(Icons.send),
                trailingIcon: const Icon(Icons.mail),
                onPressed: () {
                  _itemSelected(TestMenu.standaloneMenu2);
                },
                child: Text(TestMenu.standaloneMenu2.label),
              ),
            ],
            builder: (BuildContext context, MenuController controller, Widget? child) {
              return TextButton(
                focusNode: _focusNode,
                onPressed: () {
                  if (controller.isOpen) {
                    controller.close();
                  } else {
                    controller.open();
                  }
                },
                child: child!,
              );
            },
            child: const Text('Open Menu'),
          ),
          ConstrainedBox(
            constraints: const BoxConstraints(maxWidth: 400),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                _ControlSlider(
                  label: 'Extra Padding: ${widget.extraPadding.toStringAsFixed(1)}',
                  value: widget.extraPadding,
                  max: 40,
                  divisions: 20,
                  onChanged: (double value) {
                    widget.onExtraPaddingChanged(value);
                  },
                ),
                _ControlSlider(
                  label: 'Horizontal Density: ${widget.density.horizontal.toStringAsFixed(1)}',
                  value: widget.density.horizontal,
                  max: 4,
                  min: -4,
                  divisions: 12,
                  onChanged: (double value) {
                    widget.onDensityChanged(
                      VisualDensity(
                        horizontal: value,
                        vertical: widget.density.vertical,
                      ),
                    );
                  },
                ),
                _ControlSlider(
                  label: 'Vertical Density: ${widget.density.vertical.toStringAsFixed(1)}',
                  value: widget.density.vertical,
                  max: 4,
                  min: -4,
                  divisions: 12,
                  onChanged: (double value) {
                    widget.onDensityChanged(
                      VisualDensity(
                        horizontal: widget.density.horizontal,
                        vertical: value,
                      ),
                    );
                  },
                ),
              ],
            ),
          ),
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Row(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  Checkbox(
                    value: widget.textDirection == TextDirection.rtl,
                    onChanged: (bool? value) {
                      if (value ?? false) {
                        widget.onTextDirectionChanged(TextDirection.rtl);
                      } else {
                        widget.onTextDirectionChanged(TextDirection.ltr);
                      }
                    },
                  ),
                  const Text('RTL Text')
                ],
              ),
              Row(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  Checkbox(
                    value: widget.addItem,
                    onChanged: (bool? value) {
                      if (value ?? false) {
                        widget.onAddItemChanged(true);
                      } else {
                        widget.onAddItemChanged(false);
                      }
                    },
                  ),
                  const Text('Add Item')
                ],
              ),
              Row(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  Checkbox(
                    value: widget.transparent,
                    onChanged: (bool? value) {
                      if (value ?? false) {
                        widget.onTransparentChanged(true);
                      } else {
                        widget.onTransparentChanged(false);
                      }
                    },
                  ),
                  const Text('Transparent')
                ],
              ),
              Row(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  Checkbox(
                    value: widget.funkyTheme,
                    onChanged: (bool? value) {
                      if (value ?? false) {
                        widget.onFunkyThemeChanged(true);
                      } else {
                        widget.onFunkyThemeChanged(false);
                      }
                    },
                  ),
                  const Text('Funky Theme')
                ],
              ),
            ],
          ),
        ],
      ),
    );
  }

  void _itemSelected(TestMenu item) {
    debugPrint('App: Selected item ${item.label}');
  }
}

class _ControlSlider extends StatelessWidget {
  const _ControlSlider({
    required this.label,
    required this.value,
    required this.onChanged,
    this.min = 0,
    this.max = 1,
    this.divisions,
  });

  final String label;
  final double value;
  final ValueChanged<double> onChanged;
  final double min;
  final double max;
  final int? divisions;

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.end,
      children: <Widget>[
        Container(
          alignment: AlignmentDirectional.centerEnd,
          constraints: const BoxConstraints(minWidth: 150),
          child: Text(label),
        ),
        Expanded(
          child: Slider(
            value: value,
            min: min,
            max: max,
            divisions: divisions,
            onChanged: onChanged,
          ),
        ),
      ],
    );
  }
}

class _TestMenus extends StatefulWidget {
  const _TestMenus({
    required this.menuController,
    this.addItem = false,
  });

  final MenuController menuController;
  final bool addItem;

  @override
  State<_TestMenus> createState() => _TestMenusState();
}

class _TestMenusState extends State<_TestMenus> {
  final TextEditingController textController = TextEditingController();
  bool? checkboxState = false;
  TestMenu? radioValue;
  ShortcutRegistryEntry? _shortcutsEntry;

  void _itemSelected(TestMenu item) {
    debugPrint('App: Selected item ${item.label}');
  }

  void _openItem(TestMenu item) {
    debugPrint('App: Opened item ${item.label}');
  }

  void _closeItem(TestMenu item) {
    debugPrint('App: Closed item ${item.label}');
  }

  void _setRadio(TestMenu item) {
    debugPrint('App: Set Radio item ${item.label}');
    setState(() {
      radioValue = item;
    });
  }

  void _setCheck(TestMenu item) {
    debugPrint('App: Set Checkbox item ${item.label}');
    setState(() {
        switch (checkboxState) {
          case false:
            checkboxState = true;
            break;
          case true:
            checkboxState = null;
            break;
          case null:
            checkboxState = false;
            break;
        }
    });
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _shortcutsEntry?.dispose();
    final Map<ShortcutActivator, Intent> shortcuts = <ShortcutActivator, Intent>{};
    for (final TestMenu item in TestMenu.values) {
       if (item.shortcut == null) {
        continue;
       }
      switch (item) {
        case TestMenu.radioMenu1:
        case TestMenu.radioMenu2:
        case TestMenu.radioMenu3:
          shortcuts[item.shortcut!] = VoidCallbackIntent(() => _setRadio(item));
          break;
        case TestMenu.subMenu1:
          shortcuts[item.shortcut!] = VoidCallbackIntent(() => _setCheck(item));
          break;
        case TestMenu.mainMenu1:
        case TestMenu.mainMenu2:
        case TestMenu.mainMenu3:
        case TestMenu.mainMenu4:
        case TestMenu.subMenu2:
        case TestMenu.subMenu3:
        case TestMenu.subMenu4:
        case TestMenu.subMenu5:
        case TestMenu.subMenu6:
        case TestMenu.subMenu7:
        case TestMenu.subMenu8:
        case TestMenu.subSubMenu1:
        case TestMenu.subSubMenu2:
        case TestMenu.subSubMenu3:
        case TestMenu.subSubSubMenu1:
        case TestMenu.testButton:
        case TestMenu.standaloneMenu1:
        case TestMenu.standaloneMenu2:
          shortcuts[item.shortcut!] = VoidCallbackIntent(() => _itemSelected(item));
          break;
      }
    }
    _shortcutsEntry = ShortcutRegistry.of(context).addAll(shortcuts);
  }

  @override
  void dispose() {
    _shortcutsEntry?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        Expanded(
          child: MenuBar(
            controller: widget.menuController,
            children: <Widget>[
              SubmenuButton(
                onOpen: () {
                  _openItem(TestMenu.mainMenu1);
                },
                onClose: () {
                  _closeItem(TestMenu.mainMenu1);
                },
                menuChildren: <Widget>[
                  CheckboxMenuButton(
                    value: checkboxState,
                    tristate: true,
                    shortcut: TestMenu.subMenu1.shortcut,
                    trailingIcon: const Icon(Icons.assessment),
                    onChanged: (bool? value) {
                      setState(() {
                        checkboxState = value;
                      });
                      _itemSelected(TestMenu.subMenu1);
                    },
                    child: Text(TestMenu.subMenu1.label),
                  ),
                  RadioMenuButton<TestMenu>(
                    value: TestMenu.radioMenu1,
                    groupValue: radioValue,
                    toggleable: true,
                    shortcut: TestMenu.radioMenu1.shortcut,
                    trailingIcon: const Icon(Icons.assessment),
                    onChanged: (TestMenu? value) {
                      setState(() {
                        radioValue = value;
                      });
                      _itemSelected(TestMenu.radioMenu1);
                    },
                    child: Text(TestMenu.radioMenu1.label),
                  ),
                  RadioMenuButton<TestMenu>(
                    value: TestMenu.radioMenu2,
                    groupValue: radioValue,
                    toggleable: true,
                    shortcut: TestMenu.radioMenu2.shortcut,
                    trailingIcon: const Icon(Icons.assessment),
                    onChanged: (TestMenu? value) {
                      setState(() {
                        radioValue = value;
                      });
                      _itemSelected(TestMenu.radioMenu2);
                    },
                    child: Text(TestMenu.radioMenu2.label),
                  ),
                  RadioMenuButton<TestMenu>(
                    value: TestMenu.radioMenu3,
                    groupValue: radioValue,
                    toggleable: true,
                    shortcut: TestMenu.radioMenu3.shortcut,
                    trailingIcon: const Icon(Icons.assessment),
                    onChanged: (TestMenu? value) {
                      setState(() {
                        radioValue = value;
                      });
                      _itemSelected(TestMenu.radioMenu3);
                    },
                    child: Text(TestMenu.radioMenu3.label),
                  ),
                  MenuItemButton(
                    leadingIcon: const Icon(Icons.send),
                    trailingIcon: const Icon(Icons.mail),
                    onPressed: () {
                      _itemSelected(TestMenu.subMenu2);
                    },
                    child: Text(TestMenu.subMenu2.label),
                  ),
                ],
                child: Text(TestMenu.mainMenu1.label),
              ),
              SubmenuButton(
                onOpen: () {
                  _openItem(TestMenu.mainMenu2);
                },
                onClose: () {
                  _closeItem(TestMenu.mainMenu2);
                },
                menuChildren: <Widget>[
                  TextButton(
                    child: const Text('TEST'),
                    onPressed: () {
                      _itemSelected(TestMenu.testButton);
                      widget.menuController.close();
                    },
                  ),
                  MenuItemButton(
                    shortcut: TestMenu.subMenu3.shortcut,
                    onPressed: () {
                      _itemSelected(TestMenu.subMenu3);
                    },
                    child: Text(TestMenu.subMenu3.label),
                  ),
                ],
                child: Text(TestMenu.mainMenu2.label),
              ),
              SubmenuButton(
                onOpen: () {
                  _openItem(TestMenu.mainMenu3);
                },
                onClose: () {
                  _closeItem(TestMenu.mainMenu3);
                },
                menuChildren: <Widget>[
                  MenuItemButton(
                    child: Text(TestMenu.subMenu8.label),
                    onPressed: () {
                      _itemSelected(TestMenu.subMenu8);
                    },
                  ),
                ],
                child: Text(TestMenu.mainMenu3.label),
              ),
              SubmenuButton(
                onOpen: () {
                  _openItem(TestMenu.mainMenu4);
                },
                onClose: () {
                  _closeItem(TestMenu.mainMenu4);
                },
                menuChildren: <Widget>[
                  Actions(
                    actions: <Type, Action<Intent>>{
                      ActivateIntent: CallbackAction<ActivateIntent>(
                        onInvoke: (ActivateIntent? intent) {
                          debugPrint('Activated!');
                          return;
                        },
                      )
                    },
                    child: MenuItemButton(
                      onPressed: () {
                        debugPrint('Activated text input item with ${textController.text} as a value.');
                      },
                      child: SizedBox(
                        width: 200,
                        child: TextField(
                          controller: textController,
                          onSubmitted: (String value) {
                            debugPrint('String $value submitted.');
                          },
                        ),
                      ),
                    ),
                  ),
                  SubmenuButton(
                    onOpen: () {
                      _openItem(TestMenu.subMenu5);
                    },
                    onClose: () {
                      _closeItem(TestMenu.subMenu5);
                    },
                    menuChildren: <Widget>[
                      MenuItemButton(
                        shortcut: TestMenu.subSubMenu1.shortcut,
                        onPressed: () {
                          _itemSelected(TestMenu.subSubMenu1);
                        },
                        child: Text(TestMenu.subSubMenu1.label),
                      ),
                      MenuItemButton(
                        child: Text(TestMenu.subSubMenu2.label),
                        onPressed: () {
                          _itemSelected(TestMenu.subSubMenu2);
                        },
                      ),
                      if (widget.addItem)
                        SubmenuButton(
                          menuChildren: <Widget>[
                            MenuItemButton(
                              shortcut: TestMenu.subSubSubMenu1.shortcut,
                              onPressed: () {
                                _itemSelected(TestMenu.subSubSubMenu1);
                              },
                              child: Text(TestMenu.subSubSubMenu1.label),
                            ),
                          ],
                          child: Text(TestMenu.subSubMenu3.label),
                        ),
                    ],
                    child: Text(TestMenu.subMenu5.label),
                  ),
                  MenuItemButton(
                    // Disabled button
                    shortcut: TestMenu.subMenu6.shortcut,
                    child: Text(TestMenu.subMenu6.label),
                  ),
                  MenuItemButton(
                    child: Text(TestMenu.subMenu7.label),
                    onPressed: () {
                      _itemSelected(TestMenu.subMenu7);
                    },
                  ),
                  MenuItemButton(
                    child: Text(TestMenu.subMenu7.label),
                    onPressed: () {
                      _itemSelected(TestMenu.subMenu7);
                    },
                  ),
                  MenuItemButton(
                    child: Text(TestMenu.subMenu8.label),
                    onPressed: () {
                      _itemSelected(TestMenu.subMenu8);
                    },
                  ),
                ],
                child: Text(TestMenu.mainMenu4.label),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

enum TestMenu {
  mainMenu1('Menu 1'),
  mainMenu2('Menu 2'),
  mainMenu3('Menu 3'),
  mainMenu4('Menu 4'),
  radioMenu1('Radio Menu One', SingleActivator(LogicalKeyboardKey.digit1, control: true)),
  radioMenu2('Radio Menu Two', SingleActivator(LogicalKeyboardKey.digit2, control: true)),
  radioMenu3('Radio Menu Three', SingleActivator(LogicalKeyboardKey.digit3, control: true)),
  subMenu1('Sub Menu 1', SingleActivator(LogicalKeyboardKey.keyB, control: true)),
  subMenu2('Sub Menu 2'),
  subMenu3('Sub Menu 3', SingleActivator(LogicalKeyboardKey.enter, control: true)),
  subMenu4('Sub Menu 4'),
  subMenu5('Sub Menu 5'),
  subMenu6('Sub Menu 6', SingleActivator(LogicalKeyboardKey.tab, control: true)),
  subMenu7('Sub Menu 7'),
  subMenu8('Sub Menu 8'),
  subSubMenu1('Sub Sub Menu 1', SingleActivator(LogicalKeyboardKey.f10, control: true)),
  subSubMenu2('Sub Sub Menu 2'),
  subSubMenu3('Sub Sub Menu 3'),
  subSubSubMenu1('Sub Sub Sub Menu 1', SingleActivator(LogicalKeyboardKey.f11, control: true)),
  testButton('TEST button'),
  standaloneMenu1('Standalone Menu 1', SingleActivator(LogicalKeyboardKey.keyC, control: true)),
  standaloneMenu2('Standalone Menu 2');

  const TestMenu(this.label, [this.shortcut]);
  final String label;
  final MenuSerializableShortcut? shortcut;
}