diff --git a/notes/flutter_web/lib/routes/notes/layout.dart b/notes/flutter_web/lib/routes/notes/layout.dart index 0a0ef390..ccb74572 100644 --- a/notes/flutter_web/lib/routes/notes/layout.dart +++ b/notes/flutter_web/lib/routes/notes/layout.dart @@ -1,3 +1,4 @@ +import 'package:flutter/widgets.dart'; import 'package:you_flutter/router.dart'; import 'package:you_note_dart/note.dart'; import 'package:you_note_dart/note_layouts.dart'; @@ -5,3 +6,7 @@ import 'package:you_note_dart/note_layouts.dart'; PageBuilder layout(NoteBuilder builder) { return (context, uri) => NoteLayoutStyle1(uri: uri, builder: builder); } + +Widget layout2(BuildContext context, ToUri uri, NoteBuilder builder) { + return NoteLayoutStyle1(uri: uri, builder: builder); +} diff --git a/notes/learn_dart/test/syntax/types/types_test.dart b/notes/learn_dart/test/syntax/types/types_test.dart index 4a3be6e5..b232dd7d 100644 --- a/notes/learn_dart/test/syntax/types/types_test.dart +++ b/notes/learn_dart/test/syntax/types/types_test.dart @@ -41,13 +41,11 @@ void main() { Symbol? a = #a; Symbol? a2 = #a; check(a).equals(a2); - }); test('Object equals', () { - check(const Object()==const Object()).equals(true); - check( Object()!=const Object()).equals(true); - check( Object()!= Object()).equals(true); - + check(const Object() == const Object()).equals(true); + check(Object() != const Object()).equals(true); + check(Object() != Object()).equals(true); }); }); group("Enum", () { @@ -77,40 +75,51 @@ void main() { }); }); group("runtimeType 关系?", () { + test('type ==', () { + // 2s多 + DoubleLinkedQueue q = DoubleLinkedQueue(); + check(q.runtimeType == DoubleLinkedQueue).isTrue(); + check(q.runtimeType != Queue).isTrue(); + }); + test('SpecificTypeFuncInject扩展Function后可在里面加点料,方便mate模型生成代码', () { // 2s多 - for(int i=0;i<1000*1000*1000;i++){ + for (int i = 0; i < 1000 * 1000 * 1000; i++) { // ignore: unused_local_variable - var x =[] is ListBase; + var x = [] is ListBase; // check([] is ListBase).equals(true); } }); - }); group("Unique", () { test(' x', () { - Uniquely x = Uniquely(name: "x") ; - check(x==x).equals(false); + Uniquely x = Uniquely(name: "x"); + check(x == x).equals(false); }); }); } class SegmentedButton$Mate { String injectInfo; + SegmentedButton$Mate({this.onSelectionChanged}) : injectInfo = "${onSelectionChanged?.name}"; void Function(Set)? onSelectionChanged; } extension SpecificTypeFuncInject on void Function(Set selected) { static final _name = Expando(); + String get name => _name[this] ?? ""; + set name(String v) => _name[this] = v; + inject({ required String func, }) { _name[this] = func; } } + class Uniquely { final String name; @@ -131,18 +140,23 @@ class Uniquely { extension FuncCodeInject on Function { static final _from = Expando(); static final _mode = Expando(); + String get from => _from[this] ?? ""; + InjectMode get mode => _mode[this] ?? InjectMode.embed; + set name(String v) => _from[this] = v; + inject( - /// the Mate field Referenced function name - String func, { - /// expected inject code mode - InjectMode mode = InjectMode.refer, - /// is copy function context code - bool copyContext = true, - }) { + /// the Mate field Referenced function name + String func, { + /// expected inject code mode + InjectMode mode = InjectMode.refer, + + /// is copy function context code + bool copyContext = true, + }) { _from[this] = func; _mode[this] = mode; } diff --git a/packages/you_flutter/lib/router.dart b/packages/you_flutter/lib/router.dart index c267e71e..e33d2e2d 100644 --- a/packages/you_flutter/lib/router.dart +++ b/packages/you_flutter/lib/router.dart @@ -1,3 +1,3 @@ library you_router; -export 'package:you_flutter/src/router.dart' show YouRouter,To,ToUri,PageBuilder,LazyPageBuilder; \ No newline at end of file +export 'package:you_flutter/src/router.dart' show YouRouter,To,ToUri,PageBuilder,LazyPageBuilder,PageLayoutBuilder; \ No newline at end of file diff --git a/packages/you_flutter/lib/src/router.dart b/packages/you_flutter/lib/src/router.dart index dc3fe587..1f5696b1 100644 --- a/packages/you_flutter/lib/src/router.dart +++ b/packages/you_flutter/lib/src/router.dart @@ -41,6 +41,7 @@ ref: */ typedef PageBuilder = Widget Function(BuildContext context, ToUri uri); +typedef PageLayoutBuilder = Widget Function(BuildContext context, ToUri uri, PageBuilder builder); typedef LazyPageBuilder = Future Function(); // typedef PageBuilderAsync = Future Function(BuildContext context, ToUri uri); @@ -132,7 +133,7 @@ enum RoutePartType { /// To == go_router.GoRoute /// 官方的go_router内部略显复杂,且没有我们想要的layout等功能,所以自定一个简化版的to_router base class To { - /// template is a uri path segment template + /// part may be a template /// /[user]/[repository] /// - /dart-lang/sdk => {"user":"dart-lang","repository":"sdk"} /// - /flutter/flutter => {"user":"flutter","repository":"flutter"} @@ -140,17 +141,18 @@ base class To { late final String _name; late final RoutePartType _type; - // TODO 可以使之非空,改为root指向自己 late To _parent = this; final List children; - late PageBuilder? _builder; + final PageBuilder? _builder; + final PageLayoutBuilder? layout; - To( - this.part, { - Widget Function(BuildContext, ToUri)? builder, + To(this.part, { + PageBuilder? builder, + this.layout, this.children = const [], - }) : _builder = builder, + }) + : _builder = builder, assert(part == "/" || !part.contains("/"), "part:'$part' should be '/' or legal directory name") { var parsed = _parse(part); _name = parsed.$1; @@ -161,25 +163,32 @@ base class To { } } - To.lazy( - this.part, { + To.lazy(String part, { LazyPageBuilder? builder, - this.children = const [], - }) { - _builder = builder == null - ? null - : (BuildContext context, ToUri uri) => FutureBuilder( - future: builder().then((b) => b(context, uri)), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.hasError) { - return Text('page load error($uri): ${snapshot.error} \n${snapshot.stackTrace}'); - } - return snapshot.data!; - } - return const CircularProgressIndicator(); - }, - ); + List children = const [], + }) : this( + part, + builder: _asyncToSync(builder), + children: children, + ); + + static PageBuilder? _asyncToSync(LazyPageBuilder? builder) { + if (builder == null) { + return null; + } + return (BuildContext context, ToUri uri) => + FutureBuilder( + future: builder().then((b) => b(context, uri)), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasError) { + return Text('page load error($uri): ${snapshot.error} \n${snapshot.stackTrace}'); + } + return snapshot.data!; + } + return const CircularProgressIndicator(); + }, + ); } bool get isRoot => _parent == this; @@ -200,6 +209,29 @@ base class To { int get level => isRoot ? 0 : _parent.level + 1; + To? findLayoutNode() { + return _findLayoutNode(this); + } + /// To类型完全相同,才认为节点兼容 + To? _findLayoutNode(To toFind) { + if (isRoot) { + // 路由To类型不兼容 + if (runtimeType != toFind.runtimeType) { + return null; + } + // 路由To类型兼容再看是不是有layout + return layout == null ? null : this; + } + + // 现在是非root, 不兼容就向上找 + if (runtimeType != toFind.runtimeType) { + return _parent._findLayoutNode(toFind); + } + + // 没有就继续向上找 + return layout != null ? this : _parent._findLayoutNode(toFind); + } + ToUri _match({ required Uri uri, required List segments, @@ -216,9 +248,14 @@ base class To { } To? matchChild({required String segment}) { - To? matched = children.where((e) => e._type == RoutePartType.static).where((e) => segment == e._name).firstOrNull; + To? matched = children + .where((e) => e._type == RoutePartType.static) + .where((e) => segment == e._name) + .firstOrNull; if (matched != null) return matched; - matched = children.where((e) => e._type == RoutePartType.dynamic || e._type == RoutePartType.dynamicRest).firstOrNull; + matched = children + .where((e) => e._type == RoutePartType.dynamic || e._type == RoutePartType.dynamicRest) + .firstOrNull; if (matched != null) return matched; return null; } @@ -320,7 +357,11 @@ ${" " * level}'''; } To? find(String templatePath) { - return _findBySegments(Uri.parse(templatePath).pathSegments.where((e) => e.isNotEmpty).toList()); + return _findBySegments(Uri + .parse(templatePath) + .pathSegments + .where((e) => e.isNotEmpty) + .toList()); } To? _findBySegments(List segments) { @@ -352,7 +393,8 @@ class ToUri implements Uri { required Uri uri, required this.to, required Map routeParameters, - }) : _uri = uri, + }) + : _uri = uri, _routeParameters = /*safe copy*/ Map.from(routeParameters); Map get routeParameters { @@ -451,7 +493,17 @@ class ToUri implements Uri { Map? queryParameters, String? fragment, }) { - return ToUri._(to: to, routeParameters: routeParameters, uri: _uri.replace(scheme: scheme, userInfo: userInfo, host: host, port: port, path: path, pathSegments: pathSegments, query: query, queryParameters: queryParameters, fragment: fragment)); + return ToUri._(to: to, + routeParameters: routeParameters, + uri: _uri.replace(scheme: scheme, + userInfo: userInfo, + host: host, + port: port, + path: path, + pathSegments: pathSegments, + query: query, + queryParameters: queryParameters, + fragment: fragment)); } @override diff --git a/packages/you_note_dart/lib/note.dart b/packages/you_note_dart/lib/note.dart index eeb0694d..f225f50b 100644 --- a/packages/you_note_dart/lib/note.dart +++ b/packages/you_note_dart/lib/note.dart @@ -1,5 +1,6 @@ library note; export 'src/note.dart' show Cell, NoteBuilder, LazyNoteBuilder, NoteSystem; +export 'src/contents/contents.dart' show contents; export 'src/contents/mockup.dart' show MockupWindow; export 'src/contents/markdown_content.dart' show MD; diff --git a/packages/you_note_dart/lib/note_layouts.dart b/packages/you_note_dart/lib/note_layouts.dart index 9f0c9eac..98bfa213 100644 --- a/packages/you_note_dart/lib/note_layouts.dart +++ b/packages/you_note_dart/lib/note_layouts.dart @@ -1,3 +1,4 @@ library note; -export 'src/layouts/note_layout_style_1.dart' show NoteLayoutStyle1; \ No newline at end of file +export 'src/layouts/note_layout_style_1.dart' show NoteLayoutStyle1; +export 'src/layouts/note_layout_default.dart' show NoteLayoutDefault; \ No newline at end of file diff --git a/packages/you_note_dart/lib/src/layouts/note_layout_default.dart b/packages/you_note_dart/lib/src/layouts/note_layout_default.dart new file mode 100644 index 00000000..5dec2e23 --- /dev/null +++ b/packages/you_note_dart/lib/src/layouts/note_layout_default.dart @@ -0,0 +1,39 @@ + +import 'package:flutter/material.dart'; +import 'package:you_flutter/router.dart'; +import 'package:you_flutter/state.dart'; +import 'package:you_note_dart/note.dart'; + +final class NoteLayoutDefault extends StatelessWidget { + final NoteBuilder builder; + final ToUri uri; + + const NoteLayoutDefault({super.key, required this.uri, required this.builder}); + + @override + Widget build(BuildContext context) { + // collect note page contents(from print(xxx)) + Cell rootCell = Cell.empty(); + builder(context, rootCell); + + return Scaffold( + body: SafeArea( + child: SelectionArea( + /// Watch是you_flutter的state管理组件, 被其包裹的状态可以被观测刷新(ref: Cell._contents = [].signal()) + child: Watch((context) { + // contents是收集到调用print(xx)的所有结果 + var pageContents = rootCell.toList().expand((cell) sync* { + for (var content in cell.contents) { + yield Align( + alignment: Alignment.centerLeft, + child: contents.contentToWidget(content), + ); + } + }).toList(); + return ListView(children: pageContents); + }), + ), + ), + ); + } +} diff --git a/packages/you_note_dart/lib/src/layouts/page_layout_default.dart b/packages/you_note_dart/lib/src/layouts/page_layout_default.dart new file mode 100644 index 00000000..e6b5ee1f --- /dev/null +++ b/packages/you_note_dart/lib/src/layouts/page_layout_default.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:you_flutter/router.dart'; + +final class PageLayoutDefault extends StatelessWidget { + final PageBuilder builder; + final ToUri uri; + + const PageLayoutDefault({super.key, required this.uri, required this.builder}); + + @override + Widget build(BuildContext context) { + var pageBody = builder(context, uri); + return Scaffold( + body: SafeArea( + child: SelectionArea( + child: pageBody, + ), + ), + ); + } +} diff --git a/packages/you_note_dart/lib/src/note.dart b/packages/you_note_dart/lib/src/note.dart index ea6161b2..81751a0f 100644 --- a/packages/you_note_dart/lib/src/note.dart +++ b/packages/you_note_dart/lib/src/note.dart @@ -9,9 +9,34 @@ import 'package:you_flutter/state.dart'; import 'package:you_note_dart/note_conf.dart'; import 'package:you_note_dart/src/conventions.dart'; import 'package:http/http.dart' as http; +import 'package:you_note_dart/src/layouts/note_layout_default.dart'; typedef NoteBuilder = void Function(BuildContext context, Cell print); typedef LazyNoteBuilder = Future Function(BuildContext context, Cell print); +typedef NoteLayoutBuilder = Widget Function(BuildContext context, ToUri uri, NoteBuilder builder); + +base class ToNote extends To { + final NoteBuilder? _builder; + final NoteLayoutBuilder? _layout; + + ToNote(super.part, NoteBuilder? builder, NoteLayoutBuilder? layout) + : _builder = builder, + _layout = layout; + + Widget? build(BuildContext context, ToUri uri) { + if (_builder == null) { + return null; + } + + To? find = findLayoutNode(); + if (find == null) { + return NoteLayoutDefault(uri: uri, builder: _builder); + } + return (find as ToNote)._layout!(context, uri, _builder); + } + +// ToNote.lazy(super.part) : super.lazy(); +} class NoteRoute { /// A file system term, that refers to the last part of a path