diff --git a/lib/components/window_frame.dart b/lib/components/window_frame.dart index 5fd22ab..bc57a99 100644 --- a/lib/components/window_frame.dart +++ b/lib/components/window_frame.dart @@ -6,166 +6,99 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:venera/foundation/app.dart'; import 'package:venera/foundation/comic_source/comic_source.dart'; -import 'package:venera/foundation/state_controller.dart'; import 'package:window_manager/window_manager.dart'; const _kTitleBarHeight = 36.0; -class WindowFrameController extends StateController { - bool useDarkTheme = false; - - bool isHideWindowFrame = false; - - void setDarkTheme() { - useDarkTheme = true; - update(); - } - - void resetTheme() { - useDarkTheme = false; - update(); - } - - VoidCallback openSideBar = () {}; - - void hideWindowFrame() { - isHideWindowFrame = true; - update(); - } - - void showWindowFrame() { - isHideWindowFrame = false; - update(); - } -} - -class WindowFrame extends StatelessWidget { +class WindowFrame extends StatefulWidget { const WindowFrame(this.child, {super.key}); final Widget child; @override - Widget build(BuildContext context) { - StateController.putIfNotExists( - WindowFrameController()); - if (App.isMobile) return child; - return StateBuilder(builder: (controller) { - if (controller.isHideWindowFrame) return child; - - var body = Stack( - children: [ - Positioned.fill( - child: MediaQuery( - data: MediaQuery.of(context).copyWith( - padding: const EdgeInsets.only(top: _kTitleBarHeight)), - child: child, - ), - ), - Positioned( - top: 0, - left: 0, - right: 0, - child: Material( - color: Colors.transparent, - child: Theme( - data: Theme.of(context).copyWith( - brightness: controller.useDarkTheme ? Brightness.dark : null, - ), - child: Builder(builder: (context) { - return SizedBox( - height: _kTitleBarHeight, - child: Row( - children: [ - if (App.isMacOS) - const DragToMoveArea( - child: SizedBox( - height: double.infinity, - width: 16, - ), - ).paddingRight(52) - else - const SizedBox(width: 12), - Expanded( - child: DragToMoveArea( - child: Text( - 'Venera', - style: TextStyle( - fontSize: 13, - color: (controller.useDarkTheme || - context.brightness == Brightness.dark) - ? Colors.white - : Colors.black, - ), - ).toAlign(Alignment.centerLeft).paddingLeft(4+(App.isMacOS?25:0)), - ), - ), - if (kDebugMode) - const TextButton( - onPressed: debug, - child: Text('Debug'), - ), - if (!App.isMacOS) const WindowButtons() - ], - ), - ); - }), - ), - ), - ) - ], - ); - - if (App.isLinux) { - return VirtualWindowFrame(child: body); - } else { - return body; - } - }); - } - - Widget buildMenuButton( - WindowFrameController controller, BuildContext context) { - return InkWell( - onTap: () { - controller.openSideBar(); - }, - child: SizedBox( - width: 42, - height: double.infinity, - child: Center( - child: CustomPaint( - size: const Size(18, 20), - painter: _MenuPainter( - color: (controller.useDarkTheme || - Theme.of(context).brightness == Brightness.dark) - ? Colors.white - : Colors.black), - ), - ), - )); - } + State createState() => _WindowFrameState(); } -class _MenuPainter extends CustomPainter { - final Color color; - - _MenuPainter({this.color = Colors.black}); +class _WindowFrameState extends State { + bool isHideWindowFrame = false; + bool useDarkTheme = false; @override - void paint(Canvas canvas, Size size) { - final paint = getPaint(color); - final path = Path() - ..moveTo(0, size.height / 4) - ..lineTo(size.width, size.height / 4) - ..moveTo(0, size.height / 4 * 2) - ..lineTo(size.width, size.height / 4 * 2) - ..moveTo(0, size.height / 4 * 3) - ..lineTo(size.width, size.height / 4 * 3); - canvas.drawPath(path, paint); + Widget build(BuildContext context) { + if (App.isMobile) return widget.child; + if (isHideWindowFrame) return widget.child; + + var body = Stack( + children: [ + Positioned.fill( + child: MediaQuery( + data: MediaQuery.of(context).copyWith( + padding: const EdgeInsets.only(top: _kTitleBarHeight)), + child: widget.child, + ), + ), + Positioned( + top: 0, + left: 0, + right: 0, + child: Material( + color: Colors.transparent, + child: Theme( + data: Theme.of(context).copyWith( + brightness: useDarkTheme ? Brightness.dark : null, + ), + child: Builder(builder: (context) { + return SizedBox( + height: _kTitleBarHeight, + child: Row( + children: [ + if (App.isMacOS) + const DragToMoveArea( + child: SizedBox( + height: double.infinity, + width: 16, + ), + ).paddingRight(52) + else + const SizedBox(width: 12), + Expanded( + child: DragToMoveArea( + child: Text( + 'Venera', + style: TextStyle( + fontSize: 13, + color: (useDarkTheme || + context.brightness == Brightness.dark) + ? Colors.white + : Colors.black, + ), + ) + .toAlign(Alignment.centerLeft) + .paddingLeft(4 + (App.isMacOS ? 25 : 0)), + ), + ), + if (kDebugMode) + const TextButton( + onPressed: debug, + child: Text('Debug'), + ), + if (!App.isMacOS) const WindowButtons() + ], + ), + ); + }), + ), + ), + ) + ], + ); + + if (App.isLinux) { + return VirtualWindowFrame(child: body); + } else { + return body; + } } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } class WindowButtons extends StatefulWidget { @@ -489,7 +422,7 @@ class WindowPlacement { static Future get current async { var rect = await windowManager.getBounds(); - if(validate(rect)) { + if (validate(rect)) { lastValidRect = rect; } else { rect = lastValidRect ?? defaultPlacement.rect; @@ -635,4 +568,4 @@ TransitionBuilder VirtualWindowFrameInit() { void debug() { ComicSource.reload(); -} \ No newline at end of file +} diff --git a/lib/foundation/global_state.dart b/lib/foundation/global_state.dart new file mode 100644 index 0000000..400a82f --- /dev/null +++ b/lib/foundation/global_state.dart @@ -0,0 +1,66 @@ +import 'package:flutter/widgets.dart'; + +abstract class GlobalState { + static final _state = >[]; + + static void register(State state, [Object? key]) { + _state.add(Pair(key, state)); + } + + static T find([Object? key]) { + for (var pair in _state) { + if (pair.left == key && pair.right is T) { + return pair.right as T; + } + } + throw Exception('State not found'); + } + + static T? findOrNull([Object? key]) { + for (var pair in _state) { + if (pair.left == key && pair.right is T) { + return pair.right as T; + } + } + return null; + } + + static void unregister(State state, [Object? key]) { + _state.removeWhere( + (pair) => (key == null || pair.left == key) && pair.right == state); + } +} + +class Pair { + K left; + V right; + + Pair(this.left, this.right); +} + +abstract class AutomaticGlobalState + extends State { + @override + @mustCallSuper + void initState() { + super.initState(); + GlobalState.register(this, key); + } + + @override + @mustCallSuper + void dispose() { + super.dispose(); + GlobalState.unregister(this, key); + } + + Object? get key; + + void update() { + setState(() {}); + } + + void refresh() { + update(); + } +} diff --git a/lib/foundation/state_controller.dart b/lib/foundation/state_controller.dart deleted file mode 100644 index 4e5fff3..0000000 --- a/lib/foundation/state_controller.dart +++ /dev/null @@ -1,238 +0,0 @@ -import 'package:flutter/material.dart'; - -class SimpleController extends StateController { - final void Function()? refreshFunction; - - final Map Function()? control; - - SimpleController({this.refreshFunction, this.control}); - - @override - void refresh() { - (refreshFunction ?? super.refresh)(); - } - - Map get controlMap => control?.call() ?? {}; -} - -abstract class StateController { - static final _controllers = []; - - static T put(T controller, - {Object? tag, bool autoRemove = false}) { - _controllers.add(StateControllerWrapped(controller, autoRemove, tag)); - return controller; - } - - static T putIfNotExists(T controller, - {Object? tag, bool autoRemove = false}) { - return findOrNull(tag: tag) ?? - put(controller, tag: tag, autoRemove: autoRemove); - } - - static T find({Object? tag}) { - try { - return _controllers - .lastWhere((element) => - element.controller is T && (tag == null || tag == element.tag)) - .controller as T; - } catch (e) { - throw StateError("$T with tag $tag Not Found"); - } - } - - static List findAll({Object? tag}) { - return _controllers - .where((element) => - element.controller is T && (tag == null || tag == element.tag)) - .map((e) => e.controller as T) - .toList(); - } - - static T? findOrNull({Object? tag}) { - try { - return _controllers - .lastWhere((element) => - element.controller is T && (tag == null || tag == element.tag)) - .controller as T; - } catch (e) { - return null; - } - } - - static void remove([Object? tag, bool check = false]) { - for (int i = _controllers.length - 1; i >= 0; i--) { - var element = _controllers[i]; - if (element.controller is T && (tag == null || tag == element.tag)) { - if (check && !element.autoRemove) { - continue; - } - _controllers.removeAt(i); - return; - } - } - } - - static SimpleController putSimpleController( - void Function() onUpdate, Object? tag, - {void Function()? refresh, Map Function()? control}) { - var controller = SimpleController(refreshFunction: refresh, control: control); - controller.stateUpdaters.add(Pair(null, onUpdate)); - _controllers.add(StateControllerWrapped(controller, false, tag)); - return controller; - } - - List> stateUpdaters = []; - - void update([List? ids]) { - if (ids == null) { - for (var element in stateUpdaters) { - element.right(); - } - } else { - for (var element in stateUpdaters) { - if (ids.contains(element.left)) { - element.right(); - } - } - } - } - - void dispose() { - _controllers.removeWhere((element) => element.controller == this); - } - - void refresh() { - update(); - } -} - -class StateControllerWrapped { - StateController controller; - bool autoRemove; - Object? tag; - - StateControllerWrapped(this.controller, this.autoRemove, this.tag); -} - -class StateBuilder extends StatefulWidget { - const StateBuilder({ - super.key, - this.init, - this.dispose, - this.initState, - this.tag, - required this.builder, - this.id, - }); - - final T? init; - - final void Function(T controller)? dispose; - - final void Function(T controller)? initState; - - final Object? tag; - - final Widget Function(T controller) builder; - - Widget builderWrapped(StateController controller) { - return builder(controller as T); - } - - void initStateWrapped(StateController controller) { - return initState?.call(controller as T); - } - - void disposeWrapped(StateController controller) { - return dispose?.call(controller as T); - } - - final Object? id; - - @override - State createState() => _StateBuilderState(); -} - -class _StateBuilderState - extends State { - late T controller; - - @override - void initState() { - if (widget.init != null) { - StateController.put(widget.init!, tag: widget.tag, autoRemove: true); - } - try { - controller = StateController.find(tag: widget.tag); - } catch (e) { - throw "Controller Not Found"; - } - controller.stateUpdaters.add(Pair(widget.id, () { - if (mounted) { - setState(() {}); - } - })); - widget.initStateWrapped(controller); - super.initState(); - } - - @override - void dispose() { - widget.disposeWrapped(controller); - StateController.remove(widget.tag, true); - super.dispose(); - } - - @override - Widget build(BuildContext context) => widget.builderWrapped(controller); -} - -abstract class StateWithController extends State { - late final SimpleController _controller; - - void refresh() { - _controller.update(); - } - - @override - @mustCallSuper - void initState() { - _controller = StateController.putSimpleController( - () { - if (mounted) { - setState(() {}); - } - }, - tag, - refresh: refresh, - control: () => control, - ); - super.initState(); - } - - @override - @mustCallSuper - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void update() { - _controller.update(); - } - - Object? get tag; - - Map get control => {}; -} - -class Pair{ - M left; - V right; - - Pair(this.left, this.right); - - Pair.fromMap(Map map, M key): left = key, right = map[key] - ?? (throw Exception("Pair not found")); -} diff --git a/lib/pages/explore_page.dart b/lib/pages/explore_page.dart index 21a32e9..415d7d0 100644 --- a/lib/pages/explore_page.dart +++ b/lib/pages/explore_page.dart @@ -3,8 +3,8 @@ import 'package:venera/components/components.dart'; import 'package:venera/foundation/app.dart'; import 'package:venera/foundation/appdata.dart'; import 'package:venera/foundation/comic_source/comic_source.dart'; +import 'package:venera/foundation/global_state.dart'; import 'package:venera/foundation/res.dart'; -import 'package:venera/foundation/state_controller.dart'; import 'package:venera/pages/comic_source_page.dart'; import 'package:venera/pages/search_result_page.dart'; import 'package:venera/pages/settings/settings_page.dart'; @@ -52,9 +52,7 @@ class _ExplorePageState extends State if (index == 2) { int page = controller.index; String currentPageId = pages[page]; - StateController.find(tag: currentPageId) - .control!()['toTop'] - ?.call(); + GlobalState.find<_SingleExplorePageState>(currentPageId).toTop(); } } @@ -98,7 +96,7 @@ class _ExplorePageState extends State void refresh() { int page = controller.index; String currentPageId = pages[page]; - StateController.find(tag: currentPageId).refresh(); + GlobalState.find<_SingleExplorePageState>(currentPageId).refresh(); } Widget buildFAB() => Material( @@ -244,7 +242,7 @@ class _SingleExplorePage extends StatefulWidget { State<_SingleExplorePage> createState() => _SingleExplorePageState(); } -class _SingleExplorePageState extends StateWithController<_SingleExplorePage> +class _SingleExplorePageState extends AutomaticGlobalState<_SingleExplorePage> with AutomaticKeepAliveClientMixin<_SingleExplorePage> { late final ExplorePageData data; @@ -328,7 +326,7 @@ class _SingleExplorePageState extends StateWithController<_SingleExplorePage> } @override - Object? get tag => widget.title; + Object? get key => widget.title; @override void refresh() { @@ -347,9 +345,6 @@ class _SingleExplorePageState extends StateWithController<_SingleExplorePage> ); } } - - @override - Map get control => {"toTop": toTop}; } class _MixedExplorePage extends StatefulWidget { diff --git a/lib/pages/search_page.dart b/lib/pages/search_page.dart index 81d7466..4e569a4 100644 --- a/lib/pages/search_page.dart +++ b/lib/pages/search_page.dart @@ -7,7 +7,7 @@ import 'package:venera/components/components.dart'; import 'package:venera/foundation/app.dart'; import 'package:venera/foundation/appdata.dart'; import 'package:venera/foundation/comic_source/comic_source.dart'; -import 'package:venera/foundation/state_controller.dart'; +import 'package:venera/foundation/global_state.dart'; import 'package:venera/pages/aggregated_search_page.dart'; import 'package:venera/pages/search_result_page.dart'; import 'package:venera/pages/settings/settings_page.dart'; diff --git a/lib/pages/search_result_page.dart b/lib/pages/search_result_page.dart index e7cff3d..94f700d 100644 --- a/lib/pages/search_result_page.dart +++ b/lib/pages/search_result_page.dart @@ -3,7 +3,7 @@ import 'package:venera/components/components.dart'; import 'package:venera/foundation/app.dart'; import 'package:venera/foundation/appdata.dart'; import 'package:venera/foundation/comic_source/comic_source.dart'; -import 'package:venera/foundation/state_controller.dart'; +import 'package:venera/foundation/global_state.dart'; import 'package:venera/pages/search_page.dart'; import 'package:venera/utils/ext.dart'; import 'package:venera/utils/tags_translation.dart';