1+ import 'package:collection/collection.dart' ;
2+ import 'package:dictionaries/addons.dart' ;
3+ import 'package:dictionaries/injections.dart' ;
4+ import 'package:flutter_environments_plus/flutter_environments_plus.dart' ;
5+ import 'package:flutter_riverpod/legacy.dart' ;
6+ import 'package:flutter/material.dart' ;
7+ import 'package:menu_bar/menu_bar.dart' ;
8+ import 'package:styled_logger/styled_logger.dart' ;
9+ import 'package:menu_bar/src/entry.dart' ;
10+
11+ final menuBarProvider = StateProvider <List <DictionariesMenuBarEntry >>((r) => []);
12+ final headerTitleProvider = StateProvider <String ?>((r) => null );
13+
14+ bool get isPlatformMenuBarSupported {
15+ return Environment .isMacos;
16+ }
17+
18+ List <BarButton > generateBarButtonsFromEntries (BuildContext context, List <DictionariesMenuBarEntry > entries) {
19+ return entries.map ((x) {
20+ return BarButton (text: Text (x.text), submenu: SubMenu (menuItems: generateMenuBarFromEntries (context, [x.text], x.children)));
21+ }).whereType <BarButton >().toList ();
22+ }
23+
24+ List <MenuEntry > generateMenuBarFromEntries (BuildContext context, List <String > keys, List <DictionariesMenuBarEntry > entries) {
25+ return entries.map ((x) {
26+ if (InjectionCollection .instance.menuBarDeletions.any ((y) => ListEquality ().equals (y.injection.keys, [...keys, x.text]))) return null ;
27+
28+ if (x.divider) {
29+ return MenuDivider ();
30+ } else if (x.onActivate != null ) {
31+ return MenuButton (text: Text (x.text), onTap: () => x.onActivate !(context), shortcut: x.shortcut);
32+ } else if (x.children.isNotEmpty) {
33+ return MenuButton (text: Text (x.text), submenu: SubMenu (menuItems: generateMenuBarFromEntries (context, [...keys, x.text], x.children)));
34+ } else {
35+ return MenuButton (text: Text (x.text));
36+ }
37+ }).whereType <MenuEntry >().toList ();
38+ }
39+
40+ extension InjectAllMenuBarEntries on List <DictionariesMenuBarEntry > {
41+ List <DictionariesMenuBarEntry > inject (List <DictionariesMenuBarInjection > injections) {
42+ injectAllMenuBarEntries (this , injections);
43+ return this ;
44+ }
45+ }
46+
47+ void injectAllMenuBarEntries (List <DictionariesMenuBarEntry > current, List <DictionariesMenuBarInjection > injections) {
48+ for (final injection in injections) {
49+ bool invalid = false ;
50+ DictionariesMenuBarEntry ? currentEntry;
51+
52+ for (int i = 0 ; i < injection.keys.length; i++ ) {
53+ final key = injection.keys[i];
54+ final l = currentEntry? .children ?? current;
55+ int index = l.indexWhere ((x) => x.text == key);
56+
57+ if (index < 0 ) {
58+ // Not found
59+ final e = DictionariesMenuBarEntry (key, children: []);
60+ l.add (e);
61+ currentEntry = e;
62+ } else {
63+ final e = l[index];
64+
65+ if (e.onActivate != null ) {
66+ // The thing we're trying to traverse into has an onActivate,
67+ // so we'll warn and ignore.
68+
69+ Logger .warn ("Menu bar injection tries to traverse into $key , but $key can't be traversed into! Full path: ${injection .keys .join ("/" )}" );
70+ invalid = true ;
71+ break ;
72+ } else {
73+ currentEntry = e;
74+ }
75+ }
76+ }
77+
78+ if (invalid) continue ;
79+ final l = currentEntry? .children ?? current;
80+ int after = injection.rightAfter != null ? (injection.rightAfter! .leading ? - 2 : l.indexWhere ((x) => x.text == injection.rightAfter! .text)) : - 1 ;
81+ l.insertAll (after >= 0 ? after + 1 : (after == - 2 ? 0 : l.length), injection.entries);
82+ }
83+ }
84+
85+ class DictionariesMenuBarEntry {
86+ final String id;
87+ final String ? text;
88+ final void Function (BuildContext context)? onActivate;
89+ final List <DictionariesMenuBarEntry > children;
90+ final MenuSerializableShortcut ? shortcut;
91+ final bool divider;
92+
93+ const DictionariesMenuBarEntry (this .id, this .text, {this .onActivate, this .shortcut, this .children = const []}) : divider = false ;
94+ const DictionariesMenuBarEntry .divider (this .id) : text = null , onActivate = null , shortcut = null , children = const [], divider = true ;
95+
96+ static const String dividerAfterExport = "DividerAfterExport" ;
97+ static const String beforeDownloadOriginalFile = "BeforeDownloadOriginalFile" ;
98+
99+ static const String debugAddonOptionsTopDivider = "DebugAddonOptionsTopDivider" ;
100+ static const String debugAddonOptionsSecondDivider = "DebugAddonOptionsSecondDivider" ;
101+ static const String debugAddonOptionsThirdDivider = "DebugAddonOptionsThirdDivider" ;
102+ }
103+
104+ class DictionariesApp extends StatefulWidget {
105+ final Widget child;
106+ final VoidCallback ifUsingPlatformMenuBar;
107+ const DictionariesApp ({super .key, required this .child, required this .ifUsingPlatformMenuBar});
108+
109+ @override
110+ State <DictionariesApp > createState () => _DictionariesAppState ();
111+ }
112+
113+ class _DictionariesAppState extends State <DictionariesApp > {
114+ String ? title;
115+ List <DictionariesMenuBarEntry > items = [];
116+
117+ @override
118+ Widget build (BuildContext context) {
119+ return SizedBox (
120+ height: 60 ,
121+ child: Column (
122+ children: [
123+ Text (["Dictionaries" , title].whereType <String >().join (" - " )),
124+ if (! isPlatformMenuBarSupported) MenuBarWidget (
125+ barButtons: generateBarButtonsFromEntries (context, items),
126+ child: widget.child,
127+ ) else widget.child,
128+ ],
129+ ),
130+ );
131+ }
132+ }
0 commit comments