Skip to content

Commit 3692180

Browse files
committed
Even more addons stuff
1 parent 5b0f4b0 commit 3692180

File tree

8 files changed

+113
-47
lines changed

8 files changed

+113
-47
lines changed

addons.yaml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# This file is used for defining addons.
2+
# For an example addon, see banana below.
3+
#
4+
# To generate the addon files, run this command:
5+
# dart run dictionaries:addons
6+
17
banana:
28
# The name of the package containing the code for your plugin.
39
# This *must* match what's in your pubspec.yaml.
@@ -32,7 +38,9 @@ banana:
3238

3339
# This is if you used a remote Git repository in one of your pubspec.yamls.
3440
# It can point to whatever Dart lets it point to.
35-
# This is optional, and will only be used if your source is git.
41+
#
42+
# This is required if your source is git.
43+
# It's required for consistency, as Dart locks remote Git repos anyways.
3644
#ref: "v11.3"
3745

3846
# The ID of your addon.
@@ -42,39 +50,33 @@ banana:
4250
id: "com.calebh101.banana"
4351

4452
# The name of your addon. This is pretty self-explanatory.
45-
# This should match what your package defines.
4653
# This is required.
4754
name: "Banana"
4855

49-
# This should match what your package defines.
5056
# This is required.
5157
version: "1.0.0A"
5258

5359
# A short description of your addon.
54-
# This should match what your package defines.
5560
# This is not required.
5661
description: "BANANA! This addon is a showcase of what you can do with addons."
5762

58-
# This should match what your package defines.
5963
# This can either be not present (no authors), a string (1 author), or a list of strings.
6064
# This is not required and defaults to an empty list.
6165
authors: "Calebh101"
6266

6367
# The main web page for your addon. This is pretty self explanatory.
64-
# This should match what your package defines.
6568
# This is not required, but if present, it needs to be able to be parsed as a URI.
6669
mainpage: "https://github.com/Calebh101/Dictionaries/blob/main/addons/banana/README.md"
6770

6871
# The repository for your addon, or where your code is hosted.
69-
# This should match what your package defines.
7072
# This is not required, but if present, it needs to be able to be parsed as a URI.
7173
#
7274
# NOTE: This is required if your source is git.
7375
#repository: "https://github.com/Calebh101/Dictionaries"
7476

7577
# This is the file that will be imported in the generated addon loader.
7678
# It must expose a `DictionariesAddon Function() load` function.
77-
# This is not required, and it defaults to /init.dart.
79+
# This is not required, and it defaults to /init.dart. This must start with a slash.
7880
#initfile: "/dictionaries_addon.dart"
7981

8082
oc_snapshot:

bin/addons.dart

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import 'dart:convert';
12
import 'dart:io';
23

34
import 'package:dictionaries/lib/plist_parser.dart';
45
import 'package:path/path.dart' as p;
56
import 'package:yaml/yaml.dart';
67
import 'package:yaml_edit/yaml_edit.dart';
78

8-
Future<void> main(List<String> arguments) async {
9+
void main(List<String> arguments) {
910
Directory root = Directory.current;
1011
Directory lib = Directory(p.join(root.path, "lib"));
1112

@@ -66,6 +67,7 @@ Future<void> main(List<String> arguments) async {
6667

6768
case AddonSource.git:
6869
if (data["repository"] is! String) throw Exception("repository must be a string.");
70+
if (data["ref"] is! String) throw Exception("ref must be a string.");
6971

7072
pubspecEntry = {"git": {
7173
"url": data["repository"] as String,
@@ -76,21 +78,25 @@ Future<void> main(List<String> arguments) async {
7678
break;
7779
}
7880

79-
Addon addon = Addon(debug: data["debug"] == true, packageName: data["package"], id: data["id"] ?? "com.unknown", name: data["name"] ?? "Unknown", version: data["version"] ?? "Unknown", description: data["description"], authors: authors, mainpage: Uri.tryParse(data["mainpage"] ?? ""), repository: Uri.tryParse(data["repository"] ?? ""), source: source, pubspecEntry: pubspecEntry, initFile: data["initfile"] is String ? data["initfile"] : Addon.defaultInitFile);
81+
Addon addon = Addon(debug: data["debug"] == true, packageName: data["package"], id: data["id"] ?? "com.unknown", name: data["name"] ?? "Unknown", version: data["version"] ?? "Unknown", description: data["description"], authors: authors, mainpage: data["mainpage"] is String ? Uri.tryParse(data["mainpage"]) : null, repository: data["repository"] is String ? Uri.tryParse(data["repository"]) : null, source: source, pubspecEntry: pubspecEntry, initFile: data["initfile"] is String ? data["initfile"] : Addon.defaultInitFile);
8082
addons.add(addon);
8183
}
8284

83-
List<String> imports = ["import 'package:dictionaries/core/addonloader.dart';", ...addons.map((x) => "import 'package:${x.packageName}${x.initFile}.dart' as ${x.packageName};")];
84-
List<String> loadStatements = [...addons.map((x) => "(load: ${x.packageName}.load, debug: ${x.debug})")];
85+
List<String> imports = ["import 'package:dictionaries/core/addonloader.dart';", ...addons.map((x) => "import 'package:${x.packageName}${x.initFile}' as ${x.packageName};")];
86+
List<String> loadStatements = [...addons.map((x) => "(id: '${x.id}', load: ${x.packageName}.load, debug: ${x.debug})")];
87+
List<String> infoStatements = [...addons.map((x) => "'${x.id}': ${jsonEncode(x.toJson())}")];
8588

8689
String result = """
8790
// This is an auto-generated file. Do not edit this.
8891
${imports.join("\n")}
89-
List<({DictionariesAddon Function() load, bool debug})> loadAddons() => [${loadStatements.join(",")}];
92+
List<({DictionariesAddon Function() load, bool debug, String id})> loadAddons() => [${loadStatements.join(",")}];
93+
Map<String, Map<String, dynamic>> loadAddonInfo() => {${infoStatements.join(",")}};
9094
""".trim();
9195

9296
print("Ready to write out to ${loader.path} and ${pubspec.path}. Press any key to continue.");
93-
int byte = (await stdin.first)[0];
97+
stdin..lineMode = false..echoMode = false;
98+
stdin.readByteSync();
99+
stdin..lineMode = true..echoMode = true;
94100
loader.writeAsStringSync(result);
95101

96102
final editor = YamlEditor(pubspec.readAsStringSync());
@@ -126,4 +132,21 @@ class Addon {
126132
const Addon({required this.packageName, required this.debug, required this.id, required this.name, required this.version, required this.description, required this.authors, required this.mainpage, required this.repository, required this.source, required this.pubspecEntry, required this.initFile});
127133

128134
static const String defaultInitFile = "/init.dart";
135+
136+
Map<String, dynamic> toJson() {
137+
return {
138+
"packageName": packageName,
139+
"initFile": initFile,
140+
"debug": debug,
141+
"id": id,
142+
"name": name,
143+
"version": version,
144+
"description": description,
145+
"authors": authors,
146+
"mainPage": mainpage?.toString(),
147+
"repository": repository?.toString(),
148+
"source": source.name,
149+
"pubspecEntry": pubspecEntry,
150+
};
151+
}
129152
}

lib/addons.dart

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ library;
22

33
import 'dart:async';
44

5+
import 'package:dictionaries/core/addonloader.g.dart';
56
import 'package:dictionaries/injections.dart';
67
import 'package:dictionaries/ui/header.dart';
78
import 'package:dictionaries/addons.dart';
@@ -19,25 +20,12 @@ abstract class DictionariesAddon {
1920

2021
/// The ID of your addon. This should follow the Java naming procedure, and the final identifier should be lower snake case. Example:
2122
///
22-
/// `com.calebh101.oc_snapshot`
23-
final String id;
24-
25-
/// The version of your addon. This can be in any format you like.
26-
final String version;
27-
28-
/// The optional short description of your addon.
29-
final String? description;
30-
31-
/// A list of names of who made this, This can be in any format you like.
32-
final List<String> authors;
33-
34-
/// A link to your addon's optional "main page". Take this how you'd like, but [repository] is a separate property for an addon's repository.
35-
final Uri? mainpage;
36-
37-
/// An addon's open source repository.
23+
/// ```
24+
/// com.calebh101.oc_snapshot
25+
/// ```
3826
///
39-
/// Reminder: Your addon must be open-source and publicly available to be able to be used in Dictionaries!
40-
final Uri? repository;
27+
/// This should match what's defined in `addons.yaml`.
28+
final String id;
4129

4230
/// If this addon should be completely ignored by the loader.
4331
final bool doNotShow;
@@ -47,19 +35,22 @@ abstract class DictionariesAddon {
4735
DictionariesAddon({
4836
required this.name,
4937
required this.id,
50-
required this.version,
51-
this.authors = const [],
52-
this.description,
53-
this.mainpage,
54-
this.repository,
5538
this.doNotShow = false,
5639
});
5740

5841
/// This is called to register your addon.
5942
/// **Do not call this yourself**.
60-
FutureOr<void> register(bool debug) async {
61-
InjectionCollection.instance.addons.add(this);
43+
FutureOr<bool> register(bool debug) async {
44+
Map<String, dynamic>? data = loadAddonInfo()[id];
45+
46+
if (data == null) {
47+
Logger.warn("Addon $id not found in loadAddonInfo! Did you forget to add your addon in addons.yaml?");
48+
return false;
49+
}
50+
51+
InjectionCollection.instance.addons.add(DictionariesAddonData(this, data));
6252
await onRegister(debug);
53+
return true;
6354
}
6455

6556
/// This is called when your addon is registered. You don't call this, but you *override* this in your class.
@@ -89,7 +80,7 @@ final class DictionariesMaterialAppInjection {
8980
}
9081
}
9182

92-
class DictionariesMenuBarInjection {
83+
final class DictionariesMenuBarInjection {
9384
final List<DictionariesMenuBarInjectionKey> keys;
9485
final List<DictionariesMenuBarEntry> entries;
9586
final DictionariesMenuBarPosition? rightAfter;
@@ -101,7 +92,7 @@ class DictionariesMenuBarInjection {
10192
}
10293
}
10394

104-
class DictionariesMenuBarInjectionKey {
95+
final class DictionariesMenuBarInjectionKey {
10596
final String id;
10697
final String? defaultName;
10798

@@ -111,9 +102,14 @@ class DictionariesMenuBarInjectionKey {
111102
String toString() {
112103
return "DictionariesMenuBarInjectionKey(id: $id, defaultName; $defaultName)";
113104
}
105+
106+
@override
107+
bool operator ==(Object other) {
108+
return other is DictionariesMenuBarInjectionKey && id == other.id;
109+
}
114110
}
115111

116-
class DictionariesMenuBarDeletion {
112+
final class DictionariesMenuBarDeletion {
117113
final List<DictionariesMenuBarInjectionKey> keys;
118114
const DictionariesMenuBarDeletion(this.keys);
119115

@@ -122,15 +118,15 @@ class DictionariesMenuBarDeletion {
122118
}
123119
}
124120

125-
class DictionariesMenuBarPosition {
121+
final class DictionariesMenuBarPosition {
126122
final bool leading;
127123
final String? text;
128124

129125
const DictionariesMenuBarPosition.fromKey(String key) : text = key, leading = false;
130126
const DictionariesMenuBarPosition.leading() : text = null, leading = true;
131127
}
132128

133-
class AddonLogger {
129+
final class AddonLogger {
134130
static void print(AddonContext context, Object? input, {List<Object?>? attachments}) {
135131
Logger.print("${context.id}: $input", attachments: attachments);
136132
}

lib/core/addonloader.g.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
import 'package:dictionaries/core/addonloader.dart';
33
import 'package:banana/init.dart' as banana;
44
import 'package:dictionaries_oc_snapshot/init.dart' as dictionaries_oc_snapshot;
5-
List<({DictionariesAddon Function() load, bool debug})> loadAddons() => [(load: banana.load, debug: true),(load: dictionaries_oc_snapshot.load, debug: true)];
5+
List<({DictionariesAddon Function() load, bool debug, String id})> loadAddons() => [(id: 'com.calebh101.banana', load: banana.load, debug: true),(id: 'com.calebh101.oc_snapshot', load: dictionaries_oc_snapshot.load, debug: true)];
6+
Map<String, Map<String, dynamic>> loadAddonInfo() => {'com.calebh101.banana': {"packageName":"banana","initFile":"/init.dart","debug":true,"id":"com.calebh101.banana","name":"Banana","version":"1.0.0A","description":"BANANA! This addon is a showcase of what you can do with addons.","authors":["Calebh101"],"mainPage":"https://github.com/Calebh101/Dictionaries/blob/main/addons/banana/README.md","repository":null,"source":"relativePath","pubspecEntry":{"path":"addons/banana"}},'com.calebh101.oc_snapshot': {"packageName":"dictionaries_oc_snapshot","initFile":"/init.dart","debug":true,"id":"com.calebh101.oc_snapshot","name":"OC Snapshot","version":"1.0.0A","description":null,"authors":["Calebh101"],"mainPage":null,"repository":null,"source":"relativePath","pubspecEntry":{"path":"addons/dictionaries_oc_snapshot"}}};

lib/injections.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,27 @@ class InjectionCollection {
44
InjectionCollection();
55
static late InjectionCollection instance;
66

7-
List<DictionariesAddon> addons = [];
7+
List<DictionariesAddonData> addons = [];
88

99
List<Injection<DictionariesMenuBarInjection>> menuBarInjections = [];
1010
List<Injection<DictionariesMenuBarDeletion>> menuBarDeletions = [];
1111
List<Injection<DictionariesMaterialAppInjection>> materialAppInjections = [];
12+
13+
void setInstance() {
14+
instance = this;
15+
}
1216
}
1317

1418
class Injection<T> {
1519
final AddonContext context;
1620
final T injection;
1721

1822
const Injection(this.context, this.injection);
23+
}
24+
25+
class DictionariesAddonData {
26+
final DictionariesAddon addon;
27+
final Map<String, dynamic> data;
28+
29+
const DictionariesAddonData(this.addon, this.data);
1930
}

lib/main.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import 'package:dictionaries/core/addonloader.g.dart';
2+
import 'package:dictionaries/injections.dart';
3+
import 'package:styled_logger/styled_logger.dart';
4+
5+
void main(List<String> arguments) {
6+
InjectionCollection injectionCollection = InjectionCollection()..setInstance();
7+
8+
for (final x in loadAddons()) {
9+
Logger.print("Loading addon ${x.id}...");
10+
x.load.call();
11+
}
12+
}

lib/ui/header.dart

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ import 'package:menu_bar/menu_bar.dart';
88
import 'package:styled_logger/styled_logger.dart';
99
import 'package:menu_bar/src/entry.dart';
1010

11+
typedef OnActivate = void Function(BuildContext context);
12+
1113
final menuBarProvider = StateProvider<List<DictionariesMenuBarEntry>>((r) => []);
1214
final headerTitleProvider = StateProvider<String?>((r) => null);
1315

16+
Map<DictionariesMenuBarInjectionKey, OnActivate> onActivates = {};
17+
1418
bool get isPlatformMenuBarSupported {
1519
return Environment.isMacos;
1620
}
@@ -86,14 +90,30 @@ void injectAllMenuBarEntries(List<DictionariesMenuBarEntry> current, List<Dictio
8690
class DictionariesMenuBarEntry {
8791
final String id;
8892
final String? text;
89-
final void Function(BuildContext context)? onActivate;
93+
final OnActivate? onActivate;
9094
final List<DictionariesMenuBarEntry> children;
9195
final MenuSerializableShortcut? shortcut;
9296
final bool divider;
9397

9498
const DictionariesMenuBarEntry(this.id, this.text, {this.onActivate, this.shortcut, this.children = const []}) : divider = false;
9599
const DictionariesMenuBarEntry.divider(this.id) : text = null, onActivate = null, shortcut = null, children = const [], divider = true;
96100

101+
void activate(BuildContext context) {
102+
if (!isActive) return;
103+
if (onActivate != null) return onActivate!.call(context);
104+
if (onActivates.keys.any((x) => x.id == id)) return onActivates.entries.firstWhere((x) => x.key.id == id).value.call(context);
105+
}
106+
107+
bool get isActive {
108+
return onActivate != null || onActivates.entries.any((x) => x.key.id == id);
109+
}
110+
111+
static bool injectHandler(DictionariesMenuBarInjectionKey key, OnActivate onActivate) {
112+
if (onActivates.containsKey(key)) return false;
113+
onActivates[key] = onActivate;
114+
return true;
115+
}
116+
97117
static const String dividerAfterExport = "DividerAfterExport";
98118
static const String beforeDownloadOriginalFile = "BeforeDownloadOriginalFile";
99119

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ dependencies:
4646
path: ^1.9.1
4747
quick_listener: ^0.0.1
4848
flutter_riverpod: ^3.2.0
49+
4950
banana:
5051
path: addons/banana
5152
dictionaries_oc_snapshot:

0 commit comments

Comments
 (0)