From b1dbf934ee38dd9a636d66ab8cd5b249ead7106f Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 13 Feb 2022 17:40:03 +0200 Subject: [PATCH 01/36] Add empty classic flutter app. --- bin/main.dart | 4 ++- .../memento/memento_editor/application.dart | 13 ++++++++ patterns/memento/memento_editor/main.dart | 31 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 patterns/memento/memento_editor/application.dart create mode 100644 patterns/memento/memento_editor/main.dart diff --git a/bin/main.dart b/bin/main.dart index 2a23b38..935d592 100644 --- a/bin/main.dart +++ b/bin/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../patterns/observer/subscriber_flutter_widget/main.dart'; import '../patterns/adapter/flutter_adapter/main.dart'; +import '../patterns/memento/memento_editor/main.dart'; void main() { runApp(MyApp()); @@ -12,10 +13,11 @@ class MyApp extends StatelessWidget { return MaterialApp( title: 'Refactoring Guru: Flutter launcher', theme: ThemeData(primarySwatch: Colors.pink), - initialRoute: '/adapter/flutter_adapter', + initialRoute: '/memento/flutter_memento_editor', routes: { '/observer/subscriber_flutter_widget': (_) => SubscriberFlutterApp(), '/adapter/flutter_adapter': (_) => FlutterAdapterApp(), + '/memento/flutter_memento_editor': (_) => FlutterMementoEditorApp(), }, ); } diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart new file mode 100644 index 0000000..c448933 --- /dev/null +++ b/patterns/memento/memento_editor/application.dart @@ -0,0 +1,13 @@ +import 'dart:ui'; + +import '../../adapter/flutter_adapter/classic_app/classic_app.dart'; + +class MementoEditorApplication extends ClassicApp { + @override + void onPaint(Canvas canvas, Size canvasSize) { + canvas.drawRect( + Offset.zero & canvasSize, + Paint()..color = Color(0xff2ac932), + ); + } +} diff --git a/patterns/memento/memento_editor/main.dart b/patterns/memento/memento_editor/main.dart new file mode 100644 index 0000000..d09c661 --- /dev/null +++ b/patterns/memento/memento_editor/main.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +import '../../adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart' as adapter; +import 'application.dart'; +import 'widgets/right_panel_widget.dart'; + +class FlutterMementoEditorApp extends StatefulWidget { + @override + State createState() => _FlutterMementoEditorAppState(); +} + +class _FlutterMementoEditorAppState extends State { + final app = MementoEditorApplication(); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Row( + children: [ + Expanded( + child: adapter.ClassicAppAdapterWidget( + classicApp: app, + ), + ), + RightPanelWidget(), + ], + ), + ); + } +} + From 087e7c9a168fa7d635cb8ebbfef52501ff322b34 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 13 Feb 2022 19:35:58 +0200 Subject: [PATCH 02/36] Add flutter right panel properties. --- .../widgets/right_panel_widget.dart | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 patterns/memento/memento_editor/widgets/right_panel_widget.dart diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart new file mode 100644 index 0000000..42c5486 --- /dev/null +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -0,0 +1,171 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class RightPanelWidget extends StatelessWidget { + RightPanelWidget({Key? key}) : super(key: key); + + final colors = [ + Color(0xFFFFFFFF), + Color(0xFFD81B60), + Color(0xFF5E35B1), + Color(0xFF1E88E5), + Color(0xFF43A047), + ]; + + static const rowHeight = 60.0; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 300, + child: Column( + children: [ + Padding( + padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'SHAPE PROPERTIES', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 20), + Row( + children: [ + _buildNumberField('x:'), + SizedBox(width: 20), + _buildNumberField('y:'), + ], + ), + SizedBox(height: 20), + _buildNumberField('size:'), + SizedBox(height: 20), + Padding( + padding: EdgeInsets.symmetric(vertical: 14), + child: Row( + children: [ + Text('color:'), + SizedBox(width: 10), + ...colors + .map( + (color) => Container( + color: color, + width: 20, + height: 20, + ), + ) + .toList(), + ], + ), + ), + ], + ), + ), + Container( + height: 2, + color: Colors.grey.withOpacity(0.5), + ), + Expanded( + child: Padding( + padding: + EdgeInsets.only(left: 20, right: 20, top: 15, bottom: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'MEMENTO', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + Text( + '1. Select the shape.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + '2. Change color, size or position.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + '3. Click the "save state" button.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + 'Now you can restore states by selecting them from the list.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + OutlinedButton( + child: Text('Save state'), + onPressed: () {}, + ), + ], + ), + SizedBox(height: 5), + Expanded( + child: ColoredBox( + color: Colors.white, + child: Material( + type: MaterialType.transparency, + child: ListView( + padding: EdgeInsets.all(5), + children: [ + ColoredBox( + color: Colors.black.withOpacity(0.02), + child: ListTile( + leading: Container( + color: Colors.green, + width: 50, + height: double.infinity, + child: Icon(Icons.animation), + ), + title: Text('Snapshot 1'), + subtitle: Text('fefefefsfsdfas'), + onTap: () {}, + ), + ) + ], + ), + ), + ), + ), + ], + ), + ), + ) + ], + ), + ); + } + + Widget _buildNumberField(String name) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(name), + SizedBox(width: 10), + SizedBox( + width: 50, + child: TextField( + controller: TextEditingController(text: '0'), + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + ), + ), + ), + ], + ); + } +} From 026ea37584c5d103a052ca42d397fd0f52221472 Mon Sep 17 00:00:00 2001 From: ilopX Date: Sun, 13 Feb 2022 19:38:12 +0200 Subject: [PATCH 03/36] Add fake shape painter. --- .../memento/memento_editor/application.dart | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index c448933..fe8eecb 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,5 +1,8 @@ +import 'dart:math'; import 'dart:ui'; +import 'package:flutter/cupertino.dart'; + import '../../adapter/flutter_adapter/classic_app/classic_app.dart'; class MementoEditorApplication extends ClassicApp { @@ -7,7 +10,26 @@ class MementoEditorApplication extends ClassicApp { void onPaint(Canvas canvas, Size canvasSize) { canvas.drawRect( Offset.zero & canvasSize, - Paint()..color = Color(0xff2ac932), + Paint()..color = Color(0xff404040), ); + + final paintStroke = Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xFFD81B60); + + final paintFill = Paint() + ..style = PaintingStyle.fill + ..color = Color(0xffffffff); + + const positionRadius = 300.0; + for (var i = 0; i < 5; i++) { + final x = + positionRadius / 2 + positionRadius + cos(i * 8) * positionRadius; + final y = + positionRadius / 2 + positionRadius + sin(i * 8) * positionRadius; + + canvas.drawCircle(Offset(x, y), 60, paintFill); + canvas.drawCircle(Offset(x, y), 60, paintStroke); + } } } From 4b9695b8bbe2c5ba4e5d43911eb9764b7fffe083 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 10:41:56 +0200 Subject: [PATCH 04/36] Add mouseUp & mouseMove events. --- .../adapter/classic_app_render_object.dart | 13 ++++++++----- .../flutter_adapter/classic_app/classic_app.dart | 6 +++++- .../adapter/flutter_adapter/client_app/app.dart | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart index 4470082..0811cda 100644 --- a/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart +++ b/patterns/adapter/flutter_adapter/adapter/classic_app_render_object.dart @@ -63,13 +63,16 @@ class ClassicAppRenderObject extends RenderBox { @override void handleEvent(PointerEvent event, covariant BoxHitTestEntry entry) { if (event is PointerHoverEvent || event is PointerMoveEvent) { - } else if (event is PointerDownEvent) { - if (event.buttons == kPrimaryMouseButton) { - _classicApp.onMouseDown(); - } else if (event.buttons == kSecondaryMouseButton) {} - else if (event.buttons == kMiddleMouseButton) {} + _classicApp.onMouseMove(event.position.dx, event.position.dy); } else if (event is PointerScrollEvent) { _classicApp.onPointerWheel(event.scrollDelta.dx, event.scrollDelta.dy); + } else if (event is PointerDownEvent) { + if (event.buttons == kPrimaryMouseButton) { + _classicApp.onMouseDown(event.position.dx, event.position.dy); + } else if (event.buttons == kSecondaryMouseButton) { + } else if (event.buttons == kMiddleMouseButton) {} + } else if (event is PointerUpEvent) { + _classicApp.onMouseUp(); } } diff --git a/patterns/adapter/flutter_adapter/classic_app/classic_app.dart b/patterns/adapter/flutter_adapter/classic_app/classic_app.dart index f19a5c2..7e56111 100644 --- a/patterns/adapter/flutter_adapter/classic_app/classic_app.dart +++ b/patterns/adapter/flutter_adapter/classic_app/classic_app.dart @@ -7,7 +7,11 @@ import 'repaint_compatible.dart'; abstract class ClassicApp implements RepaintCompatible { final events = AppObserver(); - void onMouseDown() {} + void onMouseDown(double x, double y) {} + + void onMouseUp() {} + + void onMouseMove(double x, double y) {} void onPointerWheel(double deltaX, double deltaY) {} diff --git a/patterns/adapter/flutter_adapter/client_app/app.dart b/patterns/adapter/flutter_adapter/client_app/app.dart index b648c75..7a4f5a7 100644 --- a/patterns/adapter/flutter_adapter/client_app/app.dart +++ b/patterns/adapter/flutter_adapter/client_app/app.dart @@ -19,7 +19,7 @@ class App extends ClassicApp { } @override - void onMouseDown() { + void onMouseDown(_, __) { textColoring.color = colorRules.nextColor(textColoring.color); } From a515748d43e87d4909966d2d185261d38f469cef Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 10:43:10 +0200 Subject: [PATCH 05/36] Create Editor class. --- .../memento/memento_editor/application.dart | 36 +----- .../memento/memento_editor/editor/editor.dart | 116 ++++++++++++++++++ patterns/memento/memento_editor/main.dart | 2 +- 3 files changed, 120 insertions(+), 34 deletions(-) create mode 100644 patterns/memento/memento_editor/editor/editor.dart diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index fe8eecb..e6d4ac2 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,35 +1,5 @@ -import 'dart:math'; -import 'dart:ui'; +import 'editor/editor.dart'; -import 'package:flutter/cupertino.dart'; - -import '../../adapter/flutter_adapter/classic_app/classic_app.dart'; - -class MementoEditorApplication extends ClassicApp { - @override - void onPaint(Canvas canvas, Size canvasSize) { - canvas.drawRect( - Offset.zero & canvasSize, - Paint()..color = Color(0xff404040), - ); - - final paintStroke = Paint() - ..style = PaintingStyle.stroke - ..color = Color(0xFFD81B60); - - final paintFill = Paint() - ..style = PaintingStyle.fill - ..color = Color(0xffffffff); - - const positionRadius = 300.0; - for (var i = 0; i < 5; i++) { - final x = - positionRadius / 2 + positionRadius + cos(i * 8) * positionRadius; - final y = - positionRadius / 2 + positionRadius + sin(i * 8) * positionRadius; - - canvas.drawCircle(Offset(x, y), 60, paintFill); - canvas.drawCircle(Offset(x, y), 60, paintStroke); - } - } +class MementoEditorApplication { + final editor = Editor(); } diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart new file mode 100644 index 0000000..9ea7f6c --- /dev/null +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -0,0 +1,116 @@ +import 'dart:math'; +import 'dart:ui'; + +import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; + +class Circle { + double x; + double y; + var color = Color(0xFFFFFFFF); + var size = 60.0; + + Circle(this.x, this.y); + + static final paintStroke = Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xFFD81B60) + ..strokeWidth = 2; + + void paint(Canvas canvas) { + final paintFill = Paint() + ..style = PaintingStyle.fill + ..color = color; + + canvas.drawCircle(Offset(x, y), size, paintFill); + canvas.drawCircle(Offset(x, y), size, paintStroke); + } + + bool isBounded(double x, double y) { + return ((x - this.x) * (x - this.x) + (y - this.y) * (y - this.y) <= + size * size); + } +} + +class Editor extends ClassicApp { + final shapes = []; + + Editor() { + const positionRadius = 300.0; + for (var i = 0; i < 7; i++) { + final x = 60 + positionRadius + cos(i / 1.15) * positionRadius; + final y = 60 + positionRadius + sin(i / 1.15) * positionRadius; + shapes.add(Circle(x, y)); + } + } + + var isMouseDown = false; + + var d = 60.0; + + Circle? selectedCircle; + + @override + void onMouseDown(double x, double y) { + isMouseDown = true; + for (final circle in shapes) { + if (circle.isBounded(x, y)) { + selectedCircle = circle; + xStart = circle.x - x; + yStart = circle.y - y; + repaint(); + return; + } + } + selectedCircle = null; + repaint(); + } + + var xStart = 0.0; + var yStart = 0.0; + + @override + void onMouseMove(double x, double y) { + if (isMouseDown && selectedCircle != null) { + selectedCircle!.x = x + xStart; + selectedCircle!.y = y + yStart; + repaint(); + } + } + + @override + void onPointerWheel(double deltaX, double deltaY) { + if (selectedCircle != null) { + selectedCircle!.size -= deltaY / 5; + repaint(); + } + } + + @override + void onMouseUp() { + isMouseDown = false; + } + + @override + void onPaint(Canvas canvas, Size canvasSize) { + canvas.drawRect( + Offset.zero & canvasSize, + Paint()..color = Color(0xff404040), + ); + + for (final circle in shapes.reversed) { + circle.paint(canvas); + } + + if (selectedCircle != null) { + final circleSize = selectedCircle!.size; + final x = (selectedCircle!.x - circleSize).roundToDouble() - 1.5; + final y = (selectedCircle!.y - circleSize).roundToDouble() - 1.5; + canvas.drawRect( + Rect.fromLTWH(x, y, circleSize * 2 + 3, circleSize * 2 + 3), + Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xff26e6ff), + ); + } + } +} diff --git a/patterns/memento/memento_editor/main.dart b/patterns/memento/memento_editor/main.dart index d09c661..3e20002 100644 --- a/patterns/memento/memento_editor/main.dart +++ b/patterns/memento/memento_editor/main.dart @@ -19,7 +19,7 @@ class _FlutterMementoEditorAppState extends State { children: [ Expanded( child: adapter.ClassicAppAdapterWidget( - classicApp: app, + classicApp: app.editor, ), ), RightPanelWidget(), From 6b8924532b00188da86c3ba43a01e230ef0eaccf Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 10:43:44 +0200 Subject: [PATCH 06/36] Add black color to palette. --- .../memento/memento_editor/widgets/right_panel_widget.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 42c5486..4ec69e4 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -1,11 +1,11 @@ -import 'package:flutter/cupertino.dart'; + import 'package:flutter/material.dart'; class RightPanelWidget extends StatelessWidget { RightPanelWidget({Key? key}) : super(key: key); final colors = [ - Color(0xFFFFFFFF), + Color(0xFF000000), Color(0xFFD81B60), Color(0xFF5E35B1), Color(0xFF1E88E5), From 301906e1fd4934e16841611b1a19b1d87cb8a542 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:53:23 +0200 Subject: [PATCH 07/36] Add shape class. --- .../memento/memento_editor/editor/shape.dart | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 patterns/memento/memento_editor/editor/shape.dart diff --git a/patterns/memento/memento_editor/editor/shape.dart b/patterns/memento/memento_editor/editor/shape.dart new file mode 100644 index 0000000..a7c4a3f --- /dev/null +++ b/patterns/memento/memento_editor/editor/shape.dart @@ -0,0 +1,40 @@ +import 'dart:ui'; + +class Shape { + double x; + double y; + var color = Color(0xFFFFFFFF); + var _size = 60.0; + + double get size => _size; + + set size(double newSize) { + if (newSize < 10) { + newSize = 10; + } else if (newSize > 200) { + newSize = 200; + } + _size = newSize; + } + + Shape(this.x, this.y); + + static final paintStroke = Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xFFD81B60) + ..strokeWidth = 2; + + void paint(Canvas canvas) { + final paintFill = Paint() + ..style = PaintingStyle.fill + ..color = color; + + canvas.drawCircle(Offset(x, y), _size, paintFill); + canvas.drawCircle(Offset(x, y), _size, paintStroke); + } + + bool isBounded(double x, double y) { + return ((x - this.x) * (x - this.x) + (y - this.y) * (y - this.y) <= + _size * _size); + } +} From 3a5bb4a54775dd6d23feaa2bb6c75005d4cf9945 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:53:45 +0200 Subject: [PATCH 08/36] Add Shapes. --- .../memento/memento_editor/editor/shapes.dart | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 patterns/memento/memento_editor/editor/shapes.dart diff --git a/patterns/memento/memento_editor/editor/shapes.dart b/patterns/memento/memento_editor/editor/shapes.dart new file mode 100644 index 0000000..51b0645 --- /dev/null +++ b/patterns/memento/memento_editor/editor/shapes.dart @@ -0,0 +1,57 @@ +import 'dart:ui'; + +import 'shape.dart'; + +mixin Shapes { + final shapes = []; + + void paintShapes(Canvas canvas) { + for (final shape in shapes.reversed) { + shape.paint(canvas); + } + } + + Shape? _selectedShape; + + Shape? get selectedShape => _selectedShape; + + void select(double x, double y) { + _selectedShape = findCircle(x, y); + + if (_selectedShape != null) { + _xStart = _selectedShape!.x - x; + _yStart = _selectedShape!.y - y; + } + } + + void unSelect() { + _selectedShape = null; + } + + void changeSize(double delta) { + final currentSize = _selectedShape?.size; + + if (currentSize != null) { + final newSize = currentSize - delta; + if (newSize != _selectedShape!.size) { + _selectedShape!.size = newSize; + } + } + } + + void drag(double x, double y) { + _selectedShape!.x = x + _xStart; + _selectedShape!.y = y + _yStart; + } + + var _xStart = 0.0; + var _yStart = 0.0; + + Shape? findCircle(double x, double y) { + for (final shape in shapes) { + if (shape.isBounded(x, y)) { + return shape; + } + } + } +} From 95ff8c68b30e58b5af7b33eda64f5c67ebb570a3 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:54:01 +0200 Subject: [PATCH 09/36] Add Manipulator. --- .../memento_editor/editor/manipulator.dart | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 patterns/memento/memento_editor/editor/manipulator.dart diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart new file mode 100644 index 0000000..0598a1c --- /dev/null +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -0,0 +1,61 @@ +import 'dart:ui'; + +import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; +import 'shapes.dart'; + +mixin Manipulator implements ClassicApp, Shapes { + void paintSelectFrame(Canvas canvas) { + if (selectedShape != null) { + final shapeSize = selectedShape!.size; + final x = (selectedShape!.x - shapeSize).roundToDouble() - 1.5; + final y = (selectedShape!.y - shapeSize).roundToDouble() - 1.5; + canvas.drawRect( + Rect.fromLTWH(x, y, shapeSize * 2 + 3, shapeSize * 2 + 3), + Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xff26e6ff), + ); + } + } + + var _isMouseDown = false; + + @override + void onMouseDown(double x, double y) { + _isMouseDown = true; + final currSelection = selectedShape; + + select(x, y); + + if (currSelection == selectedShape) { + return; + } + + if (selectedShape == null) { + unSelect(); + } + + repaint(); + } + + @override + void onMouseMove(double x, double y) { + if (_isMouseDown && selectedShape != null) { + drag(x, y); + repaint(); + } + } + + @override + void onPointerWheel(double deltaX, double deltaY) { + if (selectedShape != null) { + changeSize(deltaY / 5); + repaint(); + } + } + + @override + void onMouseUp() { + _isMouseDown = false; + } +} From 4676aa475bbe0c8898c7c4f9c71bfc23635db34a Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:54:22 +0200 Subject: [PATCH 10/36] Add ColorsWidget. --- .../memento_editor/widgets/colors_widget.dart | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 patterns/memento/memento_editor/widgets/colors_widget.dart diff --git a/patterns/memento/memento_editor/widgets/colors_widget.dart b/patterns/memento/memento_editor/widgets/colors_widget.dart new file mode 100644 index 0000000..9e62582 --- /dev/null +++ b/patterns/memento/memento_editor/widgets/colors_widget.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; + +import '../../../bridge/devices_remote_control/devices/radio.dart'; + +class ColorsWidget extends StatelessWidget { + final Color? currentColor; + final List colors; + final void Function(Color color) onColorSelect; + + const ColorsWidget({ + Key? key, + required this.currentColor, + required this.colors, + required this.onColorSelect, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Opacity( + opacity: currentColor == null ? 0.2 : 1.0, + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black87), + ), + child: Row( + children: colors.map(_buildColorButton).toList(), + ), + ), + ); + } + + Widget _buildColorButton(Color color) { + final isColorSelect = (color == currentColor); + return GestureDetector( + onTap: () { + onColorSelect(color); + }, + child: Container( + width: 20, + height: 20, + color: color, + child: isColorSelect ? _buildSelectColorIcon() : null, + ), + ); + } + + Widget _buildSelectColorIcon() { + return Center( + child: Container( + width: 5, + height: 5, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.8), + borderRadius: BorderRadius.all(Radius.circular(2)), + border: Border.all(color: Colors.black.withOpacity(0.2))), + ), + ); + } +} From 879e467f3659a22e1b3d1b18fa36896ac6022f72 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:55:11 +0200 Subject: [PATCH 11/36] Add SubscriberWidget to property panel. --- .../widgets/right_panel_widget.dart | 119 +++++++++++------- 1 file changed, 72 insertions(+), 47 deletions(-) diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 4ec69e4..83dc512 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -1,8 +1,14 @@ - import 'package:flutter/material.dart'; +import '../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; +import '../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; +import '../editor/editor.dart'; +import 'colors_widget.dart'; + class RightPanelWidget extends StatelessWidget { - RightPanelWidget({Key? key}) : super(key: key); + final Editor editor; + + RightPanelWidget({Key? key, required this.editor}) : super(key: key); final colors = [ Color(0xFF000000), @@ -10,6 +16,7 @@ class RightPanelWidget extends StatelessWidget { Color(0xFF5E35B1), Color(0xFF1E88E5), Color(0xFF43A047), + Color(0xFFFFFFFF), ]; static const rowHeight = 60.0; @@ -22,47 +29,57 @@ class RightPanelWidget extends StatelessWidget { children: [ Padding( padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'SHAPE PROPERTIES', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 20), - Row( - children: [ - _buildNumberField('x:'), - SizedBox(width: 20), - _buildNumberField('y:'), - ], - ), - SizedBox(height: 20), - _buildNumberField('size:'), - SizedBox(height: 20), - Padding( - padding: EdgeInsets.symmetric(vertical: 14), - child: Row( + child: SubscriberWidget( + observer: editor.events, + builder: (buildContext, event) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('color:'), - SizedBox(width: 10), - ...colors - .map( - (color) => Container( - color: color, - width: 20, - height: 20, + Text( + 'SHAPE PROPERTIES', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 20), + Row( + children: [ + _buildNumberField('x:', editor.selectedShape?.x), + SizedBox(width: 20), + _buildNumberField('y:', editor.selectedShape?.y), + ], + ), + SizedBox(height: 20), + _buildNumberField('size:', editor.selectedShape?.size), + SizedBox(height: 10), + Padding( + padding: EdgeInsets.symmetric(vertical: 14), + child: Row( + children: [ + Text( + 'color:', + style: TextStyle( + color: Colors.black.withOpacity( + editor.selectedShape == null ? 0.5 : 1.0, + ), + ), ), - ) - .toList(), + SizedBox(width: 10), + ColorsWidget( + currentColor: editor.selectedShape?.color, + colors: colors, + onColorSelect: (newColor) { + editor.selectedShape?.color = newColor; + editor.repaint(); + }, + ), + ], + ), + ), ], - ), - ), - ], - ), + ); + }), ), Container( height: 2, @@ -104,7 +121,7 @@ class RightPanelWidget extends StatelessWidget { ), SizedBox(height: 20), Row( - mainAxisAlignment: MainAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.start, children: [ OutlinedButton( child: Text('Save state'), @@ -143,24 +160,32 @@ class RightPanelWidget extends StatelessWidget { ], ), ), - ) + ), ], ), ); } - Widget _buildNumberField(String name) { + Widget _buildNumberField(String name, double? value) { return Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text(name), + Text( + name, + style: TextStyle( + color: Colors.black.withOpacity(value == null ? 0.5 : 1.0), + ), + ), SizedBox(width: 10), SizedBox( - width: 50, + width: 60, child: TextField( - controller: TextEditingController(text: '0'), + enabled: value != null, + controller: TextEditingController( + text: value == null ? '' : value.toStringAsFixed(0), + ), decoration: InputDecoration( - filled: true, + filled: value != null, fillColor: Colors.white, ), ), From a60f7414e2ca0ef0a5fc692d10bdce91f0f77303 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:55:58 +0200 Subject: [PATCH 12/36] Format main.dart. --- patterns/memento/memento_editor/main.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/patterns/memento/memento_editor/main.dart b/patterns/memento/memento_editor/main.dart index 3e20002..bee7de3 100644 --- a/patterns/memento/memento_editor/main.dart +++ b/patterns/memento/memento_editor/main.dart @@ -1,12 +1,14 @@ import 'package:flutter/material.dart'; -import '../../adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart' as adapter; +import '../../adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart' + as adapter; import 'application.dart'; import 'widgets/right_panel_widget.dart'; class FlutterMementoEditorApp extends StatefulWidget { @override - State createState() => _FlutterMementoEditorAppState(); + State createState() => + _FlutterMementoEditorAppState(); } class _FlutterMementoEditorAppState extends State { @@ -22,10 +24,9 @@ class _FlutterMementoEditorAppState extends State { classicApp: app.editor, ), ), - RightPanelWidget(), + RightPanelWidget(editor: app.editor), ], ), ); } } - From 4e837870e8144b17c39c4b161bbb1985c94bd8c2 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 20:57:34 +0200 Subject: [PATCH 13/36] Move Manipulator and Shape out of Editor. --- .../memento/memento_editor/editor/editor.dart | 104 ++---------------- .../memento_editor/widgets/colors_widget.dart | 2 - 2 files changed, 10 insertions(+), 96 deletions(-) diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index 9ea7f6c..1b66e4f 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -2,115 +2,31 @@ import 'dart:math'; import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; +import 'shape.dart'; +import 'manipulator.dart'; +import 'shapes.dart'; -class Circle { - double x; - double y; - var color = Color(0xFFFFFFFF); - var size = 60.0; - - Circle(this.x, this.y); - - static final paintStroke = Paint() - ..style = PaintingStyle.stroke - ..color = Color(0xFFD81B60) - ..strokeWidth = 2; - - void paint(Canvas canvas) { - final paintFill = Paint() - ..style = PaintingStyle.fill - ..color = color; - - canvas.drawCircle(Offset(x, y), size, paintFill); - canvas.drawCircle(Offset(x, y), size, paintStroke); - } - - bool isBounded(double x, double y) { - return ((x - this.x) * (x - this.x) + (y - this.y) * (y - this.y) <= - size * size); - } -} - -class Editor extends ClassicApp { - final shapes = []; - +class Editor extends ClassicApp with Manipulator, Shapes { Editor() { const positionRadius = 300.0; for (var i = 0; i < 7; i++) { final x = 60 + positionRadius + cos(i / 1.15) * positionRadius; final y = 60 + positionRadius + sin(i / 1.15) * positionRadius; - shapes.add(Circle(x, y)); + shapes.add(Shape(x, y)); } } - var isMouseDown = false; - - var d = 60.0; - - Circle? selectedCircle; - @override - void onMouseDown(double x, double y) { - isMouseDown = true; - for (final circle in shapes) { - if (circle.isBounded(x, y)) { - selectedCircle = circle; - xStart = circle.x - x; - yStart = circle.y - y; - repaint(); - return; - } - } - selectedCircle = null; - repaint(); - } - - var xStart = 0.0; - var yStart = 0.0; - - @override - void onMouseMove(double x, double y) { - if (isMouseDown && selectedCircle != null) { - selectedCircle!.x = x + xStart; - selectedCircle!.y = y + yStart; - repaint(); - } - } - - @override - void onPointerWheel(double deltaX, double deltaY) { - if (selectedCircle != null) { - selectedCircle!.size -= deltaY / 5; - repaint(); - } - } - - @override - void onMouseUp() { - isMouseDown = false; + void onPaint(Canvas canvas, Size canvasSize) { + _paintBackground(canvas, canvasSize); + paintShapes(canvas); + paintSelectFrame(canvas); } - @override - void onPaint(Canvas canvas, Size canvasSize) { + void _paintBackground(Canvas canvas, Size canvasSize) { canvas.drawRect( Offset.zero & canvasSize, Paint()..color = Color(0xff404040), ); - - for (final circle in shapes.reversed) { - circle.paint(canvas); - } - - if (selectedCircle != null) { - final circleSize = selectedCircle!.size; - final x = (selectedCircle!.x - circleSize).roundToDouble() - 1.5; - final y = (selectedCircle!.y - circleSize).roundToDouble() - 1.5; - canvas.drawRect( - Rect.fromLTWH(x, y, circleSize * 2 + 3, circleSize * 2 + 3), - Paint() - ..style = PaintingStyle.stroke - ..color = Color(0xff26e6ff), - ); - } } } diff --git a/patterns/memento/memento_editor/widgets/colors_widget.dart b/patterns/memento/memento_editor/widgets/colors_widget.dart index 9e62582..8aae037 100644 --- a/patterns/memento/memento_editor/widgets/colors_widget.dart +++ b/patterns/memento/memento_editor/widgets/colors_widget.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; -import '../../../bridge/devices_remote_control/devices/radio.dart'; - class ColorsWidget extends StatelessWidget { final Color? currentColor; final List colors; From b5246bfee784666d1dc6739dbff280ec3f0891a3 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 23:20:00 +0200 Subject: [PATCH 14/36] Add visible args to Shape. --- patterns/memento/memento_editor/editor/shape.dart | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/patterns/memento/memento_editor/editor/shape.dart b/patterns/memento/memento_editor/editor/shape.dart index a7c4a3f..28369c5 100644 --- a/patterns/memento/memento_editor/editor/shape.dart +++ b/patterns/memento/memento_editor/editor/shape.dart @@ -3,8 +3,8 @@ import 'dart:ui'; class Shape { double x; double y; - var color = Color(0xFFFFFFFF); - var _size = 60.0; + Color color; + double _size; double get size => _size; @@ -17,7 +17,12 @@ class Shape { _size = newSize; } - Shape(this.x, this.y); + Shape( + this.x, + this.y, [ + this.color = const Color(0xFFFFFFFF), + this._size = 60.0, + ]); static final paintStroke = Paint() ..style = PaintingStyle.stroke From 04dd1d68c6aa9c401a4e79db3a26b43de30a2e65 Mon Sep 17 00:00:00 2001 From: ilopX Date: Mon, 14 Feb 2022 23:22:35 +0200 Subject: [PATCH 15/36] Add backup & replace editor to app. --- .../memento/memento_editor/application.dart | 1 + .../memento/memento_editor/editor/editor.dart | 46 ++++++++++++ patterns/memento/memento_editor/main.dart | 3 +- .../widgets/right_panel_widget.dart | 75 ++++++++++++------- 4 files changed, 95 insertions(+), 30 deletions(-) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index e6d4ac2..4860989 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -2,4 +2,5 @@ import 'editor/editor.dart'; class MementoEditorApplication { final editor = Editor(); + final snapshots = []; } diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index 1b66e4f..b4efa16 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -1,4 +1,6 @@ +import 'dart:convert'; import 'dart:math'; +import 'dart:typed_data'; import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; @@ -6,7 +8,51 @@ import 'shape.dart'; import 'manipulator.dart'; import 'shapes.dart'; +typedef Snapshot = String; + + class Editor extends ClassicApp with Manipulator, Shapes { + Snapshot backup() { + final byteSize = shapes.length * 16; + final data = ByteData(byteSize); + var byteOffset = 0; + + for (final shape in shapes) { + data + ..setFloat32(byteOffset, shape.x) + ..setFloat32(byteOffset + 4, shape.y) + ..setInt32(byteOffset + 8, shape.color.value) + ..setFloat32(byteOffset + 12, shape.size); + byteOffset += 16; + } + + return data.buffer + .asUint8List().map((e) => e.toRadixString(16).padLeft(3, '0')) + .join(); + } + + void restore(Snapshot snapshot) { + final unBase = Base64Decoder().convert(snapshot); + final byteData = ByteData.sublistView(unBase); + final shapeCount = byteData.lengthInBytes ~/ 16; + + shapes.clear(); + var byteOffset = 0; + + for (var i = 0; i < shapeCount; i++) { + final shape = Shape( + byteData.getFloat32(byteOffset), + byteData.getFloat32(byteOffset + 4), + Color(byteData.getInt32(byteOffset + 8)), + byteData.getFloat32(byteOffset + 12), + ); + shapes.add(shape); + byteOffset += 16; + } + + repaint(); + } + Editor() { const positionRadius = 300.0; for (var i = 0; i < 7; i++) { diff --git a/patterns/memento/memento_editor/main.dart b/patterns/memento/memento_editor/main.dart index bee7de3..dc13777 100644 --- a/patterns/memento/memento_editor/main.dart +++ b/patterns/memento/memento_editor/main.dart @@ -5,6 +5,7 @@ import '../../adapter/flutter_adapter/adapter/classic_app_adapter_widget.dart' import 'application.dart'; import 'widgets/right_panel_widget.dart'; + class FlutterMementoEditorApp extends StatefulWidget { @override State createState() => @@ -24,7 +25,7 @@ class _FlutterMementoEditorAppState extends State { classicApp: app.editor, ), ), - RightPanelWidget(editor: app.editor), + RightPanelWidget(app: app), ], ), ); diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 83dc512..83dd8c3 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; import '../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; import '../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; -import '../editor/editor.dart'; +import '../application.dart'; import 'colors_widget.dart'; class RightPanelWidget extends StatelessWidget { - final Editor editor; + final MementoEditorApplication app; - RightPanelWidget({Key? key, required this.editor}) : super(key: key); + RightPanelWidget({Key? key, required this.app}) : super(key: key); final colors = [ Color(0xFF000000), @@ -30,7 +30,7 @@ class RightPanelWidget extends StatelessWidget { Padding( padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: 10), child: SubscriberWidget( - observer: editor.events, + observer: app.editor.events, builder: (buildContext, event) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -45,13 +45,16 @@ class RightPanelWidget extends StatelessWidget { SizedBox(height: 20), Row( children: [ - _buildNumberField('x:', editor.selectedShape?.x), + _buildNumberField('x:', app.editor.selectedShape?.x), SizedBox(width: 20), - _buildNumberField('y:', editor.selectedShape?.y), + _buildNumberField('y:', app.editor.selectedShape?.y), ], ), SizedBox(height: 20), - _buildNumberField('size:', editor.selectedShape?.size), + _buildNumberField( + 'size:', + app.editor.selectedShape?.size, + ), SizedBox(height: 10), Padding( padding: EdgeInsets.symmetric(vertical: 14), @@ -61,17 +64,17 @@ class RightPanelWidget extends StatelessWidget { 'color:', style: TextStyle( color: Colors.black.withOpacity( - editor.selectedShape == null ? 0.5 : 1.0, + app.editor.selectedShape == null ? 0.5 : 1.0, ), ), ), SizedBox(width: 10), ColorsWidget( - currentColor: editor.selectedShape?.color, + currentColor: app.editor.selectedShape?.color, colors: colors, onColorSelect: (newColor) { - editor.selectedShape?.color = newColor; - editor.repaint(); + app.editor.selectedShape?.color = newColor; + app.editor.repaint(); }, ), ], @@ -125,7 +128,10 @@ class RightPanelWidget extends StatelessWidget { children: [ OutlinedButton( child: Text('Save state'), - onPressed: () {}, + onPressed: () { + app.snapshots.add(app.editor.backup()); + app.editor.repaint(); + }, ), ], ), @@ -135,24 +141,35 @@ class RightPanelWidget extends StatelessWidget { color: Colors.white, child: Material( type: MaterialType.transparency, - child: ListView( - padding: EdgeInsets.all(5), - children: [ - ColoredBox( - color: Colors.black.withOpacity(0.02), - child: ListTile( - leading: Container( - color: Colors.green, - width: 50, - height: double.infinity, - child: Icon(Icons.animation), - ), - title: Text('Snapshot 1'), - subtitle: Text('fefefefsfsdfas'), - onTap: () {}, + child: SubscriberWidget( + observer: app.editor.events, + builder: (_, __) => ListView( + padding: EdgeInsets.all(5), + children: [ + ...app.snapshots.map( + (e) { + return Container( + margin: EdgeInsets.only(bottom: 4), + color: Colors.black.withOpacity(0.02), + child: ListTile( + leading: Container( + color: Colors.green, + width: 50, + height: double.infinity, + child: Icon(Icons.animation), + ), + title: Text('Snapshot'), + subtitle: SingleChildScrollView( + child: Text(e), + scrollDirection: Axis.horizontal, + ), + onTap: () {}, + ), + ); + }, ), - ) - ], + ], + ), ), ), ), From 6edfaf293b15aa28857a3316fabbbed534369f84 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 15 Feb 2022 14:37:13 +0200 Subject: [PATCH 16/36] Fix deploy_flutter: remove "&" from commit text. --- bin/deploy_flutter_demos.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/deploy_flutter_demos.dart b/bin/deploy_flutter_demos.dart index b6e0509..387cd87 100644 --- a/bin/deploy_flutter_demos.dart +++ b/bin/deploy_flutter_demos.dart @@ -119,7 +119,7 @@ Future repositoryOriginUrl(Directory workingDir) async { Future lastProjectCommit() async { final rawCommit = await cmd('git log -1 --pretty=%B', workingDirectory: projectDir); - final formatCommit = rawCommit.replaceAll(' ', '_'); + final formatCommit = rawCommit.replaceAll(' ', '_').replaceAll('&', ''); return 'auto_commit:_$formatCommit'; } From 7d03cfd38d8ef047e798f430d9a2672cabed45b7 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 15 Feb 2022 22:31:48 +0200 Subject: [PATCH 17/36] Add separate project directories. --- .../memento/memento_editor/application.dart | 1 + .../memento/memento_editor/editor/editor.dart | 53 +---- .../memento_editor/editor/manipulator.dart | 2 +- .../memento_pattern/originator.dart | 53 +++++ .../{editor => shapes}/shape.dart | 0 .../{editor => shapes}/shapes.dart | 5 +- .../{ => composite}/colors_widget.dart | 9 +- .../widgets/composite/named_panel.dart | 30 +++ .../panels/shape_properties_widget.dart | 91 +++++++++ .../widgets/panels/snapshot_widget.dart | 92 +++++++++ .../widgets/right_panel_widget.dart | 192 +----------------- 11 files changed, 286 insertions(+), 242 deletions(-) create mode 100644 patterns/memento/memento_editor/memento_pattern/originator.dart rename patterns/memento/memento_editor/{editor => shapes}/shape.dart (100%) rename patterns/memento/memento_editor/{editor => shapes}/shapes.dart (92%) rename patterns/memento/memento_editor/widgets/{ => composite}/colors_widget.dart (85%) create mode 100644 patterns/memento/memento_editor/widgets/composite/named_panel.dart create mode 100644 patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart create mode 100644 patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index 4860989..5420f65 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,4 +1,5 @@ import 'editor/editor.dart'; +import 'memento_pattern/originator.dart'; class MementoEditorApplication { final editor = Editor(); diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index b4efa16..af306c6 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -1,58 +1,13 @@ -import 'dart:convert'; import 'dart:math'; -import 'dart:typed_data'; import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; -import 'shape.dart'; +import '../memento_pattern/originator.dart'; +import '../shapes/shape.dart'; import 'manipulator.dart'; -import 'shapes.dart'; - -typedef Snapshot = String; - - -class Editor extends ClassicApp with Manipulator, Shapes { - Snapshot backup() { - final byteSize = shapes.length * 16; - final data = ByteData(byteSize); - var byteOffset = 0; - - for (final shape in shapes) { - data - ..setFloat32(byteOffset, shape.x) - ..setFloat32(byteOffset + 4, shape.y) - ..setInt32(byteOffset + 8, shape.color.value) - ..setFloat32(byteOffset + 12, shape.size); - byteOffset += 16; - } - - return data.buffer - .asUint8List().map((e) => e.toRadixString(16).padLeft(3, '0')) - .join(); - } - - void restore(Snapshot snapshot) { - final unBase = Base64Decoder().convert(snapshot); - final byteData = ByteData.sublistView(unBase); - final shapeCount = byteData.lengthInBytes ~/ 16; - - shapes.clear(); - var byteOffset = 0; - - for (var i = 0; i < shapeCount; i++) { - final shape = Shape( - byteData.getFloat32(byteOffset), - byteData.getFloat32(byteOffset + 4), - Color(byteData.getInt32(byteOffset + 8)), - byteData.getFloat32(byteOffset + 12), - ); - shapes.add(shape); - byteOffset += 16; - } - - repaint(); - } +import '../shapes/shapes.dart'; +class Editor extends ClassicApp with Manipulator, Shapes, Originator { Editor() { const positionRadius = 300.0; for (var i = 0; i < 7; i++) { diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index 0598a1c..ce8d7ee 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -1,7 +1,7 @@ import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; -import 'shapes.dart'; +import '../shapes/shapes.dart'; mixin Manipulator implements ClassicApp, Shapes { void paintSelectFrame(Canvas canvas) { diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart new file mode 100644 index 0000000..9ac5bbf --- /dev/null +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -0,0 +1,53 @@ +import 'dart:convert'; +import 'dart:typed_data'; +import 'dart:ui'; + +import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; +import '../shapes/shape.dart'; +import '../shapes/shapes.dart'; + +typedef Snapshot = String; + +mixin Originator implements Shapes, ClassicApp { + Snapshot backup() { + final byteSize = shapes.length * 16; + final data = ByteData(byteSize); + var byteOffset = 0; + + for (final shape in shapes) { + data + ..setFloat32(byteOffset, shape.x) + ..setFloat32(byteOffset + 4, shape.y) + ..setInt32(byteOffset + 8, shape.color.value) + ..setFloat32(byteOffset + 12, shape.size); + byteOffset += 16; + } + + return data.buffer + .asUint8List() + .map((e) => e.toRadixString(16).padLeft(3, '0')) + .join(); + } + + void restore(Snapshot snapshot) { + final unBase = Base64Decoder().convert(snapshot); + final byteData = ByteData.sublistView(unBase); + final shapeCount = byteData.lengthInBytes ~/ 16; + + shapes.clear(); + var byteOffset = 0; + + for (var i = 0; i < shapeCount; i++) { + final shape = Shape( + byteData.getFloat32(byteOffset), + byteData.getFloat32(byteOffset + 4), + Color(byteData.getInt32(byteOffset + 8)), + byteData.getFloat32(byteOffset + 12), + ); + shapes.add(shape); + byteOffset += 16; + } + + repaint(); + } +} diff --git a/patterns/memento/memento_editor/editor/shape.dart b/patterns/memento/memento_editor/shapes/shape.dart similarity index 100% rename from patterns/memento/memento_editor/editor/shape.dart rename to patterns/memento/memento_editor/shapes/shape.dart diff --git a/patterns/memento/memento_editor/editor/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart similarity index 92% rename from patterns/memento/memento_editor/editor/shapes.dart rename to patterns/memento/memento_editor/shapes/shapes.dart index 51b0645..669f8ec 100644 --- a/patterns/memento/memento_editor/editor/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -1,5 +1,4 @@ import 'dart:ui'; - import 'shape.dart'; mixin Shapes { @@ -16,7 +15,7 @@ mixin Shapes { Shape? get selectedShape => _selectedShape; void select(double x, double y) { - _selectedShape = findCircle(x, y); + _selectedShape = findShape(x, y); if (_selectedShape != null) { _xStart = _selectedShape!.x - x; @@ -47,7 +46,7 @@ mixin Shapes { var _xStart = 0.0; var _yStart = 0.0; - Shape? findCircle(double x, double y) { + Shape? findShape(double x, double y) { for (final shape in shapes) { if (shape.isBounded(x, y)) { return shape; diff --git a/patterns/memento/memento_editor/widgets/colors_widget.dart b/patterns/memento/memento_editor/widgets/composite/colors_widget.dart similarity index 85% rename from patterns/memento/memento_editor/widgets/colors_widget.dart rename to patterns/memento/memento_editor/widgets/composite/colors_widget.dart index 8aae037..829fff6 100644 --- a/patterns/memento/memento_editor/widgets/colors_widget.dart +++ b/patterns/memento/memento_editor/widgets/composite/colors_widget.dart @@ -48,9 +48,12 @@ class ColorsWidget extends StatelessWidget { width: 5, height: 5, decoration: BoxDecoration( - color: Colors.white.withOpacity(0.8), - borderRadius: BorderRadius.all(Radius.circular(2)), - border: Border.all(color: Colors.black.withOpacity(0.2))), + color: Colors.white.withOpacity(0.8), + borderRadius: BorderRadius.all(Radius.circular(2)), + border: Border.all( + color: Colors.black.withOpacity(0.2), + ), + ), ), ); } diff --git a/patterns/memento/memento_editor/widgets/composite/named_panel.dart b/patterns/memento/memento_editor/widgets/composite/named_panel.dart new file mode 100644 index 0000000..4f7c810 --- /dev/null +++ b/patterns/memento/memento_editor/widgets/composite/named_panel.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +class NamedPanel extends StatelessWidget { + final String name; + final List children; + + NamedPanel({ + Key? key, + required this.name, + required this.children, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + name, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 20), + ...children, + ], + ); + } +} diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart new file mode 100644 index 0000000..cb92d76 --- /dev/null +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; + +import '../../application.dart'; +import '../composite/colors_widget.dart'; +import '../composite/named_panel.dart'; + + +class ShapePropertiesWidget extends StatelessWidget { + final MementoEditorApplication app; + final List colors; + + const ShapePropertiesWidget({ + Key? key, + required this.app, + required this.colors, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return NamedPanel( + name: 'SHAPE PROPERTIES', + children: [ + Row( + children: [ + _buildNumberField('x:', app.editor.selectedShape?.x), + SizedBox(width: 20), + _buildNumberField('y:', app.editor.selectedShape?.y), + ], + ), + SizedBox(height: 20), + _buildNumberField( + 'size:', + app.editor.selectedShape?.size, + ), + SizedBox(height: 10), + Padding( + padding: EdgeInsets.symmetric(vertical: 14), + child: Row( + children: [ + Text( + 'color:', + style: TextStyle( + color: Colors.black.withOpacity( + app.editor.selectedShape == null ? 0.5 : 1.0, + ), + ), + ), + SizedBox(width: 10), + ColorsWidget( + currentColor: app.editor.selectedShape?.color, + colors: colors, + onColorSelect: (newColor) { + app.editor.selectedShape?.color = newColor; + app.editor.repaint(); + }, + ), + ], + ), + ), + ], + ); + } + + Widget _buildNumberField(String name, double? value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + name, + style: TextStyle( + color: Colors.black.withOpacity(value == null ? 0.5 : 1.0), + ), + ), + SizedBox(width: 10), + SizedBox( + width: 60, + child: TextField( + enabled: value != null, + controller: TextEditingController( + text: value == null ? '' : value.toStringAsFixed(0), + ), + decoration: InputDecoration( + filled: value != null, + fillColor: Colors.white, + ), + ), + ), + ], + ); + } +} diff --git a/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart b/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart new file mode 100644 index 0000000..8066ab5 --- /dev/null +++ b/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; + +import '../../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; +import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; +import '../../application.dart'; +import '../composite/named_panel.dart'; + +class SnapshotWidget extends StatelessWidget { + final MementoEditorApplication app; + + const SnapshotWidget({Key? key, required this.app}) : super(key: key); + + @override + Widget build(BuildContext context) { + return NamedPanel( + name: 'MEMENTO', + children: [ + Text( + '1. Select the shape.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + '2. Change color, size or position.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + '3. Click the "save state" button.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 5), + Text( + 'Now you can restore states by selecting them from the list.', + style: Theme.of(context).textTheme.bodyMedium, + ), + SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + OutlinedButton( + child: Text('Save state'), + onPressed: () { + app.snapshots.add(app.editor.backup()); + app.editor.repaint(); + }, + ), + ], + ), + SizedBox(height: 5), + Expanded( + child: ColoredBox( + color: Colors.white, + child: Material( + type: MaterialType.transparency, + child: SubscriberWidget( + observer: app.editor.events, + builder: (_, __) => ListView( + padding: EdgeInsets.all(5), + children: [ + ...app.snapshots.map( + (e) { + return Container( + margin: EdgeInsets.only(bottom: 4), + color: Colors.black.withOpacity(0.02), + child: ListTile( + leading: Container( + color: Colors.green, + width: 50, + height: double.infinity, + child: Icon(Icons.animation), + ), + title: Text('Snapshot'), + subtitle: SingleChildScrollView( + child: Text(e), + scrollDirection: Axis.horizontal, + ), + onTap: () {}, + ), + ); + }, + ), + ], + ), + ), + ), + ), + ), + ], + ); + } +} diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 83dd8c3..197e8ad 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; -import '../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; -import '../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; import '../application.dart'; -import 'colors_widget.dart'; +import 'panels/shape_properties_widget.dart'; +import 'panels/snapshot_widget.dart'; class RightPanelWidget extends StatelessWidget { final MementoEditorApplication app; @@ -19,195 +18,16 @@ class RightPanelWidget extends StatelessWidget { Color(0xFFFFFFFF), ]; - static const rowHeight = 60.0; - @override Widget build(BuildContext context) { - return SizedBox( - width: 300, + return Padding( + padding: EdgeInsets.all(20), child: Column( children: [ - Padding( - padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: 10), - child: SubscriberWidget( - observer: app.editor.events, - builder: (buildContext, event) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'SHAPE PROPERTIES', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 20), - Row( - children: [ - _buildNumberField('x:', app.editor.selectedShape?.x), - SizedBox(width: 20), - _buildNumberField('y:', app.editor.selectedShape?.y), - ], - ), - SizedBox(height: 20), - _buildNumberField( - 'size:', - app.editor.selectedShape?.size, - ), - SizedBox(height: 10), - Padding( - padding: EdgeInsets.symmetric(vertical: 14), - child: Row( - children: [ - Text( - 'color:', - style: TextStyle( - color: Colors.black.withOpacity( - app.editor.selectedShape == null ? 0.5 : 1.0, - ), - ), - ), - SizedBox(width: 10), - ColorsWidget( - currentColor: app.editor.selectedShape?.color, - colors: colors, - onColorSelect: (newColor) { - app.editor.selectedShape?.color = newColor; - app.editor.repaint(); - }, - ), - ], - ), - ), - ], - ); - }), - ), - Container( - height: 2, - color: Colors.grey.withOpacity(0.5), - ), - Expanded( - child: Padding( - padding: - EdgeInsets.only(left: 20, right: 20, top: 15, bottom: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'MEMENTO', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 10), - Text( - '1. Select the shape.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - '2. Change color, size or position.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - '3. Click the "save state" button.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - 'Now you can restore states by selecting them from the list.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - OutlinedButton( - child: Text('Save state'), - onPressed: () { - app.snapshots.add(app.editor.backup()); - app.editor.repaint(); - }, - ), - ], - ), - SizedBox(height: 5), - Expanded( - child: ColoredBox( - color: Colors.white, - child: Material( - type: MaterialType.transparency, - child: SubscriberWidget( - observer: app.editor.events, - builder: (_, __) => ListView( - padding: EdgeInsets.all(5), - children: [ - ...app.snapshots.map( - (e) { - return Container( - margin: EdgeInsets.only(bottom: 4), - color: Colors.black.withOpacity(0.02), - child: ListTile( - leading: Container( - color: Colors.green, - width: 50, - height: double.infinity, - child: Icon(Icons.animation), - ), - title: Text('Snapshot'), - subtitle: SingleChildScrollView( - child: Text(e), - scrollDirection: Axis.horizontal, - ), - onTap: () {}, - ), - ); - }, - ), - ], - ), - ), - ), - ), - ), - ], - ), - ), - ), + ShapePropertiesWidget(app: app, colors: colors), + SnapshotWidget(app: app), ], ), ); } - - Widget _buildNumberField(String name, double? value) { - return Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - name, - style: TextStyle( - color: Colors.black.withOpacity(value == null ? 0.5 : 1.0), - ), - ), - SizedBox(width: 10), - SizedBox( - width: 60, - child: TextField( - enabled: value != null, - controller: TextEditingController( - text: value == null ? '' : value.toStringAsFixed(0), - ), - decoration: InputDecoration( - filled: value != null, - fillColor: Colors.white, - ), - ), - ), - ], - ); - } } From 843bb1686da50b7fd3edda7bfc60247cf1d315fd Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 15 Feb 2022 22:42:40 +0200 Subject: [PATCH 18/36] Add SelectedShape class. --- .../memento_editor/editor/manipulator.dart | 22 ++++----- .../memento_editor/shapes/selected_shape.dart | 24 ++++++++++ .../memento/memento_editor/shapes/shapes.dart | 47 ++++++------------- .../panels/shape_properties_widget.dart | 12 ++--- 4 files changed, 56 insertions(+), 49 deletions(-) create mode 100644 patterns/memento/memento_editor/shapes/selected_shape.dart diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index ce8d7ee..cf8ba47 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -5,10 +5,10 @@ import '../shapes/shapes.dart'; mixin Manipulator implements ClassicApp, Shapes { void paintSelectFrame(Canvas canvas) { - if (selectedShape != null) { - final shapeSize = selectedShape!.size; - final x = (selectedShape!.x - shapeSize).roundToDouble() - 1.5; - final y = (selectedShape!.y - shapeSize).roundToDouble() - 1.5; + if (selected != null) { + final shapeSize = selected!.shape.size; + final x = (selected!.shape.x - shapeSize).roundToDouble() - 1.5; + final y = (selected!.shape.y - shapeSize).roundToDouble() - 1.5; canvas.drawRect( Rect.fromLTWH(x, y, shapeSize * 2 + 3, shapeSize * 2 + 3), Paint() @@ -23,15 +23,15 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseDown(double x, double y) { _isMouseDown = true; - final currSelection = selectedShape; + final currSelection = selected; select(x, y); - if (currSelection == selectedShape) { + if (currSelection == selected) { return; } - if (selectedShape == null) { + if (selected == null) { unSelect(); } @@ -40,16 +40,16 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseMove(double x, double y) { - if (_isMouseDown && selectedShape != null) { - drag(x, y); + if (_isMouseDown) { + selected?.dragTo(x, y); repaint(); } } @override void onPointerWheel(double deltaX, double deltaY) { - if (selectedShape != null) { - changeSize(deltaY / 5); + if (selected != null) { + selected!.changeSize(deltaY / 5); repaint(); } } diff --git a/patterns/memento/memento_editor/shapes/selected_shape.dart b/patterns/memento/memento_editor/shapes/selected_shape.dart new file mode 100644 index 0000000..52ba568 --- /dev/null +++ b/patterns/memento/memento_editor/shapes/selected_shape.dart @@ -0,0 +1,24 @@ +import 'shape.dart'; + +class SelectedShape { + final Shape shape; + + SelectedShape(this.shape, this._xStart, this._yStart); + + void changeSize(double delta) { + final currentSize = shape.size; + final newSize = currentSize - delta; + + if (newSize != shape.size) { + shape.size = newSize; + } + } + + final double _xStart; + final double _yStart; + + void dragTo(double x, double y) { + shape.x = x + _xStart; + shape.y = y + _yStart; + } +} diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index 669f8ec..fd93213 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -1,51 +1,28 @@ import 'dart:ui'; +import 'selected_shape.dart'; import 'shape.dart'; mixin Shapes { final shapes = []; - void paintShapes(Canvas canvas) { - for (final shape in shapes.reversed) { - shape.paint(canvas); - } - } + SelectedShape? _selected; - Shape? _selectedShape; - - Shape? get selectedShape => _selectedShape; + SelectedShape? get selected => _selected; void select(double x, double y) { - _selectedShape = findShape(x, y); + final shape = findShape(x, y); - if (_selectedShape != null) { - _xStart = _selectedShape!.x - x; - _yStart = _selectedShape!.y - y; + if (shape != null) { + _selected = SelectedShape(shape, shape.x - x, shape.y - y); + } else { + _selected = null; } } void unSelect() { - _selectedShape = null; - } - - void changeSize(double delta) { - final currentSize = _selectedShape?.size; - - if (currentSize != null) { - final newSize = currentSize - delta; - if (newSize != _selectedShape!.size) { - _selectedShape!.size = newSize; - } - } - } - - void drag(double x, double y) { - _selectedShape!.x = x + _xStart; - _selectedShape!.y = y + _yStart; + _selected = null; } - var _xStart = 0.0; - var _yStart = 0.0; - Shape? findShape(double x, double y) { for (final shape in shapes) { if (shape.isBounded(x, y)) { @@ -53,4 +30,10 @@ mixin Shapes { } } } + + void paintShapes(Canvas canvas) { + for (final shape in shapes.reversed) { + shape.paint(canvas); + } + } } diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart index cb92d76..813dbba 100644 --- a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -22,15 +22,15 @@ class ShapePropertiesWidget extends StatelessWidget { children: [ Row( children: [ - _buildNumberField('x:', app.editor.selectedShape?.x), + _buildNumberField('x:', app.editor.selected?.shape.x), SizedBox(width: 20), - _buildNumberField('y:', app.editor.selectedShape?.y), + _buildNumberField('y:', app.editor.selected?.shape.y), ], ), SizedBox(height: 20), _buildNumberField( 'size:', - app.editor.selectedShape?.size, + app.editor.selected?.shape.size, ), SizedBox(height: 10), Padding( @@ -41,16 +41,16 @@ class ShapePropertiesWidget extends StatelessWidget { 'color:', style: TextStyle( color: Colors.black.withOpacity( - app.editor.selectedShape == null ? 0.5 : 1.0, + app.editor.selected == null ? 0.5 : 1.0, ), ), ), SizedBox(width: 10), ColorsWidget( - currentColor: app.editor.selectedShape?.color, + currentColor: app.editor.selected?.shape.color, colors: colors, onColorSelect: (newColor) { - app.editor.selectedShape?.color = newColor; + app.editor.selected?.shape.color = newColor; app.editor.repaint(); }, ), From 04a4d26b325f312704cdf9e8f2dce1ca5b7a1c77 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 15 Feb 2022 22:48:25 +0200 Subject: [PATCH 19/36] Move "paintSelectFrame" from manipulator to "SelectedShape" class. --- .../memento/memento_editor/editor/editor.dart | 2 +- .../memento_editor/editor/manipulator.dart | 16 ---------------- .../memento_editor/shapes/selected_shape.dart | 13 +++++++++++++ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index af306c6..ce9add8 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -21,7 +21,7 @@ class Editor extends ClassicApp with Manipulator, Shapes, Originator { void onPaint(Canvas canvas, Size canvasSize) { _paintBackground(canvas, canvasSize); paintShapes(canvas); - paintSelectFrame(canvas); + selected?.paintSelectFrame(canvas); } void _paintBackground(Canvas canvas, Size canvasSize) { diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index cf8ba47..76fd0c0 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -1,23 +1,7 @@ -import 'dart:ui'; - import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import '../shapes/shapes.dart'; mixin Manipulator implements ClassicApp, Shapes { - void paintSelectFrame(Canvas canvas) { - if (selected != null) { - final shapeSize = selected!.shape.size; - final x = (selected!.shape.x - shapeSize).roundToDouble() - 1.5; - final y = (selected!.shape.y - shapeSize).roundToDouble() - 1.5; - canvas.drawRect( - Rect.fromLTWH(x, y, shapeSize * 2 + 3, shapeSize * 2 + 3), - Paint() - ..style = PaintingStyle.stroke - ..color = Color(0xff26e6ff), - ); - } - } - var _isMouseDown = false; @override diff --git a/patterns/memento/memento_editor/shapes/selected_shape.dart b/patterns/memento/memento_editor/shapes/selected_shape.dart index 52ba568..371c6b7 100644 --- a/patterns/memento/memento_editor/shapes/selected_shape.dart +++ b/patterns/memento/memento_editor/shapes/selected_shape.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'shape.dart'; class SelectedShape { @@ -21,4 +23,15 @@ class SelectedShape { shape.x = x + _xStart; shape.y = y + _yStart; } + + void paintSelectFrame(Canvas canvas) { + final x = (shape.x - shape.size).roundToDouble() - 1.5; + final y = (shape.y - shape.size).roundToDouble() - 1.5; + canvas.drawRect( + Rect.fromLTWH(x, y, shape.size * 2 + 3, shape.size * 2 + 3), + Paint() + ..style = PaintingStyle.stroke + ..color = Color(0xff26e6ff), + ); + } } From bf9d0e9fd4a341774165918255ce7496e23e658a Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:34:07 +0200 Subject: [PATCH 20/36] Add SnapshotWidget. --- .../composite/snapshot_list_widget.dart | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart diff --git a/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart b/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart new file mode 100644 index 0000000..dfaf3ee --- /dev/null +++ b/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class SnapshotListWidget extends StatelessWidget { + final List snapshots; + + const SnapshotListWidget({Key? key, required this.snapshots}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return ColoredBox( + color: Colors.white, + child: Material( + type: MaterialType.transparency, + child: ListView( + padding: EdgeInsets.all(5), + children: snapshots.map((e) => _buildItem('Snapshot', e)).toList(), + ), + ), + ); + } + + Widget _buildItem(String name, String hash) { + return Container( + margin: EdgeInsets.only(bottom: 4), + color: Colors.black.withOpacity(0.02), + child: ListTile( + leading: Container( + color: Colors.green, + width: 50, + height: double.infinity, + child: Icon(Icons.animation), + ), + title: Text(name), + subtitle: SingleChildScrollView( + child: Text(hash), + scrollDirection: Axis.horizontal, + ), + onTap: () {}, + ), + ); + } +} From 95f88e79fd1a5e07974bcf8790f9c6a56edb8dfd Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:34:36 +0200 Subject: [PATCH 21/36] Fix vertical space. --- .../memento/memento_editor/widgets/composite/named_panel.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/memento/memento_editor/widgets/composite/named_panel.dart b/patterns/memento/memento_editor/widgets/composite/named_panel.dart index 4f7c810..a6637d4 100644 --- a/patterns/memento/memento_editor/widgets/composite/named_panel.dart +++ b/patterns/memento/memento_editor/widgets/composite/named_panel.dart @@ -22,7 +22,7 @@ class NamedPanel extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - SizedBox(height: 20), + SizedBox(height: 15), ...children, ], ); From cb0c152763724f71175d33f5a15873b9d251fb91 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:35:30 +0200 Subject: [PATCH 22/36] Fix right panel width & add space line. --- .../memento_editor/widgets/right_panel_widget.dart | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 197e8ad..689335d 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -20,12 +20,20 @@ class RightPanelWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( + return Container( padding: EdgeInsets.all(20), + width: 300, child: Column( children: [ ShapePropertiesWidget(app: app, colors: colors), - SnapshotWidget(app: app), + Container( + margin: EdgeInsets.symmetric(vertical: 20), + height: 2, + color: Colors.black.withOpacity(.2), + ), + Expanded( + child: SnapshotWidget(app: app), + ), ], ), ); From 086add1b09361d5d0d91f6e26c83b20fcf75e5ac Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:36:32 +0200 Subject: [PATCH 23/36] Remove SubscriberWidget from ShapeProperties. --- .../panels/shape_properties_widget.dart | 81 ++++++++++--------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart index 813dbba..bfbc90a 100644 --- a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; +import '../../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; +import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; import '../../application.dart'; import '../composite/colors_widget.dart'; import '../composite/named_panel.dart'; - class ShapePropertiesWidget extends StatelessWidget { final MementoEditorApplication app; final List colors; @@ -17,47 +18,49 @@ class ShapePropertiesWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return NamedPanel( - name: 'SHAPE PROPERTIES', - children: [ - Row( + return SubscriberWidget( + observer: app.editor.events, + builder: (buildContext, event) { + return NamedPanel( + name: 'SHAPE PROPERTIES', children: [ - _buildNumberField('x:', app.editor.selected?.shape.x), - SizedBox(width: 20), - _buildNumberField('y:', app.editor.selected?.shape.y), - ], - ), - SizedBox(height: 20), - _buildNumberField( - 'size:', - app.editor.selected?.shape.size, - ), - SizedBox(height: 10), - Padding( - padding: EdgeInsets.symmetric(vertical: 14), - child: Row( - children: [ - Text( - 'color:', - style: TextStyle( - color: Colors.black.withOpacity( - app.editor.selected == null ? 0.5 : 1.0, + Row( + children: [ + _buildNumberField('x:', app.editor.selected?.shape.x), + SizedBox(width: 20), + _buildNumberField('y:', app.editor.selected?.shape.y), + ], + ), + SizedBox(height: 20), + _buildNumberField( + 'size:', + app.editor.selected?.shape.size, + ), + SizedBox(height: 20), + Row( + children: [ + Text( + 'color:', + style: TextStyle( + color: Colors.black.withOpacity( + app.editor.selected == null ? 0.5 : 1.0, + ), ), ), - ), - SizedBox(width: 10), - ColorsWidget( - currentColor: app.editor.selected?.shape.color, - colors: colors, - onColorSelect: (newColor) { - app.editor.selected?.shape.color = newColor; - app.editor.repaint(); - }, - ), - ], - ), - ), - ], + SizedBox(width: 10), + ColorsWidget( + currentColor: app.editor.selected?.shape.color, + colors: colors, + onColorSelect: (newColor) { + app.editor.selected?.shape.color = newColor; + app.editor.repaint(); + }, + ), + ], + ), + ], + ); + }, ); } From dcb55595bfea6146f75368d13f9e0f0c920f164f Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:37:34 +0200 Subject: [PATCH 24/36] Add separate method: "_buildSaveStateButton", "_buildDescription" to "SnapshotWidget". --- .../widgets/panels/snapshot_widget.dart | 112 +++++++----------- 1 file changed, 44 insertions(+), 68 deletions(-) diff --git a/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart b/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart index 8066ab5..c0b23a1 100644 --- a/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; -import '../../../../adapter/flutter_adapter/classic_app/repaint_event.dart'; -import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; import '../../application.dart'; import '../composite/named_panel.dart'; +import '../composite/snapshot_list_widget.dart'; class SnapshotWidget extends StatelessWidget { final MementoEditorApplication app; @@ -15,78 +14,55 @@ class SnapshotWidget extends StatelessWidget { return NamedPanel( name: 'MEMENTO', children: [ - Text( - '1. Select the shape.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - '2. Change color, size or position.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - '3. Click the "save state" button.', - style: Theme.of(context).textTheme.bodyMedium, - ), - SizedBox(height: 5), - Text( - 'Now you can restore states by selecting them from the list.', - style: Theme.of(context).textTheme.bodyMedium, - ), + ..._buildDescription(Theme.of(context).textTheme.bodyMedium!), SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - OutlinedButton( - child: Text('Save state'), - onPressed: () { - app.snapshots.add(app.editor.backup()); - app.editor.repaint(); - }, - ), - ], - ), + _buildSaveStateButton(), SizedBox(height: 5), Expanded( - child: ColoredBox( - color: Colors.white, - child: Material( - type: MaterialType.transparency, - child: SubscriberWidget( - observer: app.editor.events, - builder: (_, __) => ListView( - padding: EdgeInsets.all(5), - children: [ - ...app.snapshots.map( - (e) { - return Container( - margin: EdgeInsets.only(bottom: 4), - color: Colors.black.withOpacity(0.02), - child: ListTile( - leading: Container( - color: Colors.green, - width: 50, - height: double.infinity, - child: Icon(Icons.animation), - ), - title: Text('Snapshot'), - subtitle: SingleChildScrollView( - child: Text(e), - scrollDirection: Axis.horizontal, - ), - onTap: () {}, - ), - ); - }, - ), - ], - ), - ), - ), + child: SnapshotListWidget( + snapshots: app.snapshots, ), ), ], ); } + + List _buildDescription(TextStyle style) { + return [ + Text( + '1. Select the shape.', + style: style, + ), + SizedBox(height: 5), + Text( + '2. Change color, size or position.', + style: style, + ), + SizedBox(height: 5), + Text( + '3. Click the "save state" button.', + style: style, + ), + SizedBox(height: 5), + Text( + 'Now you can restore states by selecting them from the list.', + style: style, + ), + ]; + } + + Widget _buildSaveStateButton() { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + OutlinedButton( + child: Text('Save state'), + onPressed: () { + app.snapshots.add(app.editor.backup()); + app.editor.repaint(); + }, + ), + ], + ); + } } From f6f022343555732711327d3154837eafe3bec5bf Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 11:40:42 +0200 Subject: [PATCH 25/36] Add separate class Snapshot. --- patterns/memento/memento_editor/application.dart | 2 +- patterns/memento/memento_editor/memento_pattern/caretaker.dart | 3 +++ patterns/memento/memento_editor/memento_pattern/memento.dart | 3 +++ .../memento/memento_editor/memento_pattern/originator.dart | 3 +-- patterns/memento/memento_editor/memento_pattern/snapshot.dart | 1 + 5 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 patterns/memento/memento_editor/memento_pattern/caretaker.dart create mode 100644 patterns/memento/memento_editor/memento_pattern/memento.dart create mode 100644 patterns/memento/memento_editor/memento_pattern/snapshot.dart diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index 5420f65..6adf10d 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,5 +1,5 @@ import 'editor/editor.dart'; -import 'memento_pattern/originator.dart'; +import 'memento_pattern/snapshot.dart'; class MementoEditorApplication { final editor = Editor(); diff --git a/patterns/memento/memento_editor/memento_pattern/caretaker.dart b/patterns/memento/memento_editor/memento_pattern/caretaker.dart new file mode 100644 index 0000000..d85f19a --- /dev/null +++ b/patterns/memento/memento_editor/memento_pattern/caretaker.dart @@ -0,0 +1,3 @@ +class Caretaker { + //final snapshot +} diff --git a/patterns/memento/memento_editor/memento_pattern/memento.dart b/patterns/memento/memento_editor/memento_pattern/memento.dart new file mode 100644 index 0000000..9c9c41d --- /dev/null +++ b/patterns/memento/memento_editor/memento_pattern/memento.dart @@ -0,0 +1,3 @@ +class Memento { + +} diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 9ac5bbf..77b0403 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -5,8 +5,7 @@ import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import '../shapes/shape.dart'; import '../shapes/shapes.dart'; - -typedef Snapshot = String; +import 'snapshot.dart'; mixin Originator implements Shapes, ClassicApp { Snapshot backup() { diff --git a/patterns/memento/memento_editor/memento_pattern/snapshot.dart b/patterns/memento/memento_editor/memento_pattern/snapshot.dart new file mode 100644 index 0000000..e71d8a3 --- /dev/null +++ b/patterns/memento/memento_editor/memento_pattern/snapshot.dart @@ -0,0 +1 @@ +typedef Snapshot = String; From 46283bbb4bc5aaf923ca3ac68836d6c5a0985aae Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 13:22:23 +0200 Subject: [PATCH 26/36] Remove initialization to MementoEditorApplication. --- patterns/memento/memento_editor/application.dart | 12 ++++++++++++ patterns/memento/memento_editor/editor/editor.dart | 13 +------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index 6adf10d..2cc8a17 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,7 +1,19 @@ +import 'dart:math'; + import 'editor/editor.dart'; import 'memento_pattern/snapshot.dart'; +import 'shapes/shape.dart'; class MementoEditorApplication { final editor = Editor(); final snapshots = []; + + MementoEditorApplication() { + const positionRadius = 300.0; + for (var i = 0; i < 7; i++) { + final x = 60 + positionRadius + cos(i / 1.15) * positionRadius; + final y = 60 + positionRadius + sin(i / 1.15) * positionRadius; + editor.shapes.add(Shape(x, y)); + } + } } diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index ce9add8..5ddc2a6 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -1,22 +1,11 @@ -import 'dart:math'; import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import '../memento_pattern/originator.dart'; -import '../shapes/shape.dart'; -import 'manipulator.dart'; import '../shapes/shapes.dart'; +import 'manipulator.dart'; class Editor extends ClassicApp with Manipulator, Shapes, Originator { - Editor() { - const positionRadius = 300.0; - for (var i = 0; i < 7; i++) { - final x = 60 + positionRadius + cos(i / 1.15) * positionRadius; - final y = 60 + positionRadius + sin(i / 1.15) * positionRadius; - shapes.add(Shape(x, y)); - } - } - @override void onPaint(Canvas canvas, Size canvasSize) { _paintBackground(canvas, canvasSize); From c26c817f113882b59b6616de00c96d34ea84b1d4 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 16 Feb 2022 13:30:26 +0200 Subject: [PATCH 27/36] Rename SnapshotWidget to MementoWidget. --- .../panels/{snapshot_widget.dart => memento_widget.dart} | 4 ++-- .../memento/memento_editor/widgets/right_panel_widget.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename patterns/memento/memento_editor/widgets/panels/{snapshot_widget.dart => memento_widget.dart} (92%) diff --git a/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart similarity index 92% rename from patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart rename to patterns/memento/memento_editor/widgets/panels/memento_widget.dart index c0b23a1..7789e59 100644 --- a/patterns/memento/memento_editor/widgets/panels/snapshot_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart @@ -4,10 +4,10 @@ import '../../application.dart'; import '../composite/named_panel.dart'; import '../composite/snapshot_list_widget.dart'; -class SnapshotWidget extends StatelessWidget { +class MementoWidget extends StatelessWidget { final MementoEditorApplication app; - const SnapshotWidget({Key? key, required this.app}) : super(key: key); + const MementoWidget({Key? key, required this.app}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/patterns/memento/memento_editor/widgets/right_panel_widget.dart b/patterns/memento/memento_editor/widgets/right_panel_widget.dart index 689335d..6c29bb9 100644 --- a/patterns/memento/memento_editor/widgets/right_panel_widget.dart +++ b/patterns/memento/memento_editor/widgets/right_panel_widget.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import '../application.dart'; import 'panels/shape_properties_widget.dart'; -import 'panels/snapshot_widget.dart'; +import 'panels/memento_widget.dart'; class RightPanelWidget extends StatelessWidget { final MementoEditorApplication app; @@ -32,7 +32,7 @@ class RightPanelWidget extends StatelessWidget { color: Colors.black.withOpacity(.2), ), Expanded( - child: SnapshotWidget(app: app), + child: MementoWidget(app: app), ), ], ), From 57802be642c170485bc59be41be2bbea262030a7 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 18:37:42 +0300 Subject: [PATCH 28/36] Add "Caretaker". --- .../memento/memento_editor/application.dart | 35 ++++++++++++++++--- .../editor/create_memento_event.dart | 3 ++ .../memento/memento_editor/editor/editor.dart | 2 +- .../memento_pattern/caretaker.dart | 17 ++++++++- .../memento_pattern/memento.dart | 6 ++++ .../memento_pattern/originator.dart | 7 +--- .../memento_editor/shapes/selected_shape.dart | 2 +- .../memento/memento_editor/shapes/shapes.dart | 2 ++ .../composite/snapshot_list_widget.dart | 28 +++++++++------ .../widgets/panels/memento_widget.dart | 17 +++++---- pubspec.yaml | 6 ++-- 11 files changed, 93 insertions(+), 32 deletions(-) create mode 100644 patterns/memento/memento_editor/editor/create_memento_event.dart diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index 2cc8a17..af31cdb 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,19 +1,44 @@ import 'dart:math'; +import 'editor/create_memento_event.dart'; import 'editor/editor.dart'; -import 'memento_pattern/snapshot.dart'; +import 'memento_pattern/caretaker.dart'; +import 'memento_pattern/memento.dart'; import 'shapes/shape.dart'; class MementoEditorApplication { final editor = Editor(); - final snapshots = []; + final caretaker = Caretaker(); MementoEditorApplication() { - const positionRadius = 300.0; + createDefaultShapes(); + } + + void createDefaultShapes() { + const radius = 300.0; for (var i = 0; i < 7; i++) { - final x = 60 + positionRadius + cos(i / 1.15) * positionRadius; - final y = 60 + positionRadius + sin(i / 1.15) * positionRadius; + final x = 60 + radius + cos(i / 1.15) * radius; + final y = 60 + radius + sin(i / 1.15) * radius; editor.shapes.add(Shape(x, y)); } } + + void saveState() { + final snapshot = editor.backup(); + + if (caretaker.isSnapshotExists(snapshot)) { + return; + } + + final memento = Memento(DateTime.now(), snapshot); + caretaker.addMemento(memento); + editor.events.notify(CreateMementoEvent()); + } + + void restoreState(Memento memento) { + editor + ..unSelect() + ..restore(memento.snapshot) + ..repaint(); + } } diff --git a/patterns/memento/memento_editor/editor/create_memento_event.dart b/patterns/memento/memento_editor/editor/create_memento_event.dart new file mode 100644 index 0000000..51acd4c --- /dev/null +++ b/patterns/memento/memento_editor/editor/create_memento_event.dart @@ -0,0 +1,3 @@ +import '../../../observer/app_observer/observer/event.dart'; + +class CreateMementoEvent extends Event {} diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index 5ddc2a6..24480a9 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -10,7 +10,7 @@ class Editor extends ClassicApp with Manipulator, Shapes, Originator { void onPaint(Canvas canvas, Size canvasSize) { _paintBackground(canvas, canvasSize); paintShapes(canvas); - selected?.paintSelectFrame(canvas); + selected?.paintSelectionBox(canvas); } void _paintBackground(Canvas canvas, Size canvasSize) { diff --git a/patterns/memento/memento_editor/memento_pattern/caretaker.dart b/patterns/memento/memento_editor/memento_pattern/caretaker.dart index d85f19a..971e3f5 100644 --- a/patterns/memento/memento_editor/memento_pattern/caretaker.dart +++ b/patterns/memento/memento_editor/memento_pattern/caretaker.dart @@ -1,3 +1,18 @@ +import 'memento.dart'; +import 'snapshot.dart'; + class Caretaker { - //final snapshot + final _mementoList = []; + + List get list => List.unmodifiable(_mementoList); + + void addMemento(Memento memento) { + _mementoList.add(memento); + } + + bool isSnapshotExists(Snapshot snapshot) { + return list.any( + (e) => e.snapshot == snapshot, + ); + } } diff --git a/patterns/memento/memento_editor/memento_pattern/memento.dart b/patterns/memento/memento_editor/memento_pattern/memento.dart index 9c9c41d..2bfb0b7 100644 --- a/patterns/memento/memento_editor/memento_pattern/memento.dart +++ b/patterns/memento/memento_editor/memento_pattern/memento.dart @@ -1,3 +1,9 @@ + +import 'snapshot.dart'; + class Memento { + final DateTime time; + final Snapshot snapshot; + Memento(this.time, this.snapshot); } diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 77b0403..47fab15 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -22,10 +22,7 @@ mixin Originator implements Shapes, ClassicApp { byteOffset += 16; } - return data.buffer - .asUint8List() - .map((e) => e.toRadixString(16).padLeft(3, '0')) - .join(); + return Base64Encoder().convert(data.buffer.asUint8List()); } void restore(Snapshot snapshot) { @@ -46,7 +43,5 @@ mixin Originator implements Shapes, ClassicApp { shapes.add(shape); byteOffset += 16; } - - repaint(); } } diff --git a/patterns/memento/memento_editor/shapes/selected_shape.dart b/patterns/memento/memento_editor/shapes/selected_shape.dart index 371c6b7..6797977 100644 --- a/patterns/memento/memento_editor/shapes/selected_shape.dart +++ b/patterns/memento/memento_editor/shapes/selected_shape.dart @@ -24,7 +24,7 @@ class SelectedShape { shape.y = y + _yStart; } - void paintSelectFrame(Canvas canvas) { + void paintSelectionBox(Canvas canvas) { final x = (shape.x - shape.size).roundToDouble() - 1.5; final y = (shape.y - shape.size).roundToDouble() - 1.5; canvas.drawRect( diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index fd93213..730190e 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -29,6 +29,8 @@ mixin Shapes { return shape; } } + + return null; } void paintShapes(Canvas canvas) { diff --git a/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart b/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart index dfaf3ee..33968f2 100644 --- a/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart +++ b/patterns/memento/memento_editor/widgets/composite/snapshot_list_widget.dart @@ -1,10 +1,16 @@ import 'package:flutter/material.dart'; +import '../../memento_pattern/memento.dart'; + class SnapshotListWidget extends StatelessWidget { - final List snapshots; + final List mementoList; + final void Function(Memento) onMementoRestore; - const SnapshotListWidget({Key? key, required this.snapshots}) - : super(key: key); + const SnapshotListWidget({ + Key? key, + required this.mementoList, + required this.onMementoRestore, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -14,29 +20,31 @@ class SnapshotListWidget extends StatelessWidget { type: MaterialType.transparency, child: ListView( padding: EdgeInsets.all(5), - children: snapshots.map((e) => _buildItem('Snapshot', e)).toList(), + children: mementoList.map((e) => _buildItem('Snapshot', e)).toList(), ), ), ); } - Widget _buildItem(String name, String hash) { + Widget _buildItem(String name, Memento memento) { return Container( margin: EdgeInsets.only(bottom: 4), color: Colors.black.withOpacity(0.02), child: ListTile( leading: Container( - color: Colors.green, + color: Colors.grey.shade200, width: 50, height: double.infinity, - child: Icon(Icons.animation), + child: Icon(Icons.backup), ), - title: Text(name), + title: Text(name ), subtitle: SingleChildScrollView( - child: Text(hash), + child: Text(memento.time.toIso8601String()), scrollDirection: Axis.horizontal, ), - onTap: () {}, + onTap: () { + onMementoRestore(memento); + }, ), ); } diff --git a/patterns/memento/memento_editor/widgets/panels/memento_widget.dart b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart index 7789e59..13c5c53 100644 --- a/patterns/memento/memento_editor/widgets/panels/memento_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; import '../../application.dart'; +import '../../editor/create_memento_event.dart'; import '../composite/named_panel.dart'; import '../composite/snapshot_list_widget.dart'; @@ -19,8 +21,14 @@ class MementoWidget extends StatelessWidget { _buildSaveStateButton(), SizedBox(height: 5), Expanded( - child: SnapshotListWidget( - snapshots: app.snapshots, + child: SubscriberWidget( + observer: app.editor.events, + builder: (buildContext, event) { + return SnapshotListWidget( + mementoList: app.caretaker.list, + onMementoRestore: app.restoreState, + ); + }, ), ), ], @@ -57,10 +65,7 @@ class MementoWidget extends StatelessWidget { children: [ OutlinedButton( child: Text('Save state'), - onPressed: () { - app.snapshots.add(app.editor.backup()); - app.editor.repaint(); - }, + onPressed: app.saveState, ), ], ); diff --git a/pubspec.yaml b/pubspec.yaml index 9499ccb..b2c58fc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,8 +12,10 @@ dependencies: collection: ^1.15.0 flutter: sdk: flutter - + cupertino_icons: ^1.0.2 + dev_dependencies: lints: ^1.0.0 - +flutter: + uses-material-design: true From a56469f30069696c71ee13650548cb52fa26eb13 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 19:48:20 +0300 Subject: [PATCH 29/36] add: save/restore selected shape. --- patterns/memento/memento_editor/application.dart | 1 - .../memento_pattern/originator.dart | 15 +++++++++++++-- .../memento/memento_editor/shapes/shapes.dart | 10 ++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index af31cdb..8c8dd8f 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -37,7 +37,6 @@ class MementoEditorApplication { void restoreState(Memento memento) { editor - ..unSelect() ..restore(memento.snapshot) ..repaint(); } diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 47fab15..2c3092f 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -9,7 +9,7 @@ import 'snapshot.dart'; mixin Originator implements Shapes, ClassicApp { Snapshot backup() { - final byteSize = shapes.length * 16; + final byteSize = shapes.length * 16 + 4; final data = ByteData(byteSize); var byteOffset = 0; @@ -22,13 +22,17 @@ mixin Originator implements Shapes, ClassicApp { byteOffset += 16; } + // save selected shape + final index = (selected != null) ? shapes.indexOf(selected!.shape) : -1; + data.setInt32(byteOffset, index); + return Base64Encoder().convert(data.buffer.asUint8List()); } void restore(Snapshot snapshot) { final unBase = Base64Decoder().convert(snapshot); final byteData = ByteData.sublistView(unBase); - final shapeCount = byteData.lengthInBytes ~/ 16; + final shapeCount = (byteData.lengthInBytes - 4) ~/ 16; shapes.clear(); var byteOffset = 0; @@ -43,5 +47,12 @@ mixin Originator implements Shapes, ClassicApp { shapes.add(shape); byteOffset += 16; } + + // load selection shape index + final selectedIndex = byteData.getInt32(byteOffset); + + if (selectedIndex != -1) { + selectByIndex(selectedIndex); + } } } diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index 730190e..72029eb 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -19,6 +19,16 @@ mixin Shapes { } } + void selectByIndex(int index) { + if (index == -1) { + return; + } + + if (index <= shapes.length - 1) { + _selected = SelectedShape(shapes[index], 0, 0); + } + } + void unSelect() { _selected = null; } From 22359dba7152ee11c1e30d9ea67d878c2c4bdbb7 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 21:56:24 +0300 Subject: [PATCH 30/36] Refactoring originator. --- .../memento/memento_editor/application.dart | 1 + .../memento_pattern/originator.dart | 77 +++++++++++++++---- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index 8c8dd8f..af31cdb 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -37,6 +37,7 @@ class MementoEditorApplication { void restoreState(Memento memento) { editor + ..unSelect() ..restore(memento.snapshot) ..repaint(); } diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 2c3092f..2b92c75 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -9,8 +9,49 @@ import 'snapshot.dart'; mixin Originator implements Shapes, ClassicApp { Snapshot backup() { - final byteSize = shapes.length * 16 + 4; - final data = ByteData(byteSize); + final data = _allocateBuffer(); + _writeShapes(data); + _writeSelectedIndex(data); + return _toSnapshot(data); + } + + void restore(Snapshot snapshot) { + final byteData = _fromSnapshotToByteData(snapshot); + final newShapes = _readShapes(byteData); + final selectedIndex = _readSelectedIndex(byteData); + shapes.clear(); + shapes.addAll(newShapes); + selectByIndex(selectedIndex); + } + + static const _shapeByteSize = 16; + static const _selectedIndexByteSize = 4; + + ByteData _allocateBuffer() { + final byteSize = shapes.length * _shapeByteSize + _selectedIndexByteSize; + return ByteData(byteSize); + } + + ByteData _fromSnapshotToByteData(Snapshot snapshot) { + final unBase = Base64Decoder().convert(snapshot); + final byteData = ByteData.sublistView(unBase); + return byteData; + } + + void _writeSelectedIndex(ByteData data) { + late final int selectedIndex; + + if (selected == null) { + selectedIndex = -1; + } else { + selectedIndex = shapes.indexOf(selected!.shape); + } + + final byteOffset = data.lengthInBytes - _selectedIndexByteSize; + data.setInt32(byteOffset, selectedIndex); + } + + int _writeShapes(ByteData data) { var byteOffset = 0; for (final shape in shapes) { @@ -22,20 +63,17 @@ mixin Originator implements Shapes, ClassicApp { byteOffset += 16; } - // save selected shape - final index = (selected != null) ? shapes.indexOf(selected!.shape) : -1; - data.setInt32(byteOffset, index); - - return Base64Encoder().convert(data.buffer.asUint8List()); + return byteOffset; } - void restore(Snapshot snapshot) { - final unBase = Base64Decoder().convert(snapshot); - final byteData = ByteData.sublistView(unBase); - final shapeCount = (byteData.lengthInBytes - 4) ~/ 16; + int _getNumberOfShapes(ByteData byteData) { + return (byteData.lengthInBytes - _selectedIndexByteSize) ~/ _shapeByteSize; + } - shapes.clear(); + List _readShapes(ByteData byteData) { + final shapeCount = _getNumberOfShapes(byteData); var byteOffset = 0; + final shapes = []; for (var i = 0; i < shapeCount; i++) { final shape = Shape( @@ -48,11 +86,16 @@ mixin Originator implements Shapes, ClassicApp { byteOffset += 16; } - // load selection shape index - final selectedIndex = byteData.getInt32(byteOffset); + return shapes; + } - if (selectedIndex != -1) { - selectByIndex(selectedIndex); - } + int _readSelectedIndex(ByteData byteData) { + return byteData.getInt32(byteData.lengthInBytes - _selectedIndexByteSize); + } + + Snapshot _toSnapshot(ByteData data) { + return Base64Encoder().convert( + data.buffer.asUint8List(), + ); } } From 1552c21b6df97d4ed64e49052c3c29eeb719d0d2 Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 21:58:18 +0300 Subject: [PATCH 31/36] Rename variable selected to selectedShape. --- patterns/memento/memento_editor/editor/editor.dart | 2 +- .../memento/memento_editor/editor/manipulator.dart | 12 ++++++------ .../memento_editor/memento_pattern/originator.dart | 4 ++-- patterns/memento/memento_editor/shapes/shapes.dart | 12 ++++++------ .../widgets/panels/shape_properties_widget.dart | 12 ++++++------ 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index 24480a9..e4ea879 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -10,7 +10,7 @@ class Editor extends ClassicApp with Manipulator, Shapes, Originator { void onPaint(Canvas canvas, Size canvasSize) { _paintBackground(canvas, canvasSize); paintShapes(canvas); - selected?.paintSelectionBox(canvas); + selectedShape?.paintSelectionBox(canvas); } void _paintBackground(Canvas canvas, Size canvasSize) { diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index 76fd0c0..56d4c5e 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -7,15 +7,15 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseDown(double x, double y) { _isMouseDown = true; - final currSelection = selected; + final currSelection = selectedShape; select(x, y); - if (currSelection == selected) { + if (currSelection == selectedShape) { return; } - if (selected == null) { + if (selectedShape == null) { unSelect(); } @@ -25,15 +25,15 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseMove(double x, double y) { if (_isMouseDown) { - selected?.dragTo(x, y); + selectedShape?.dragTo(x, y); repaint(); } } @override void onPointerWheel(double deltaX, double deltaY) { - if (selected != null) { - selected!.changeSize(deltaY / 5); + if (selectedShape != null) { + selectedShape!.changeSize(deltaY / 5); repaint(); } } diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 2b92c75..47d4a58 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -41,10 +41,10 @@ mixin Originator implements Shapes, ClassicApp { void _writeSelectedIndex(ByteData data) { late final int selectedIndex; - if (selected == null) { + if (selectedShape == null) { selectedIndex = -1; } else { - selectedIndex = shapes.indexOf(selected!.shape); + selectedIndex = shapes.indexOf(selectedShape!.shape); } final byteOffset = data.lengthInBytes - _selectedIndexByteSize; diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index 72029eb..c360503 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -5,17 +5,17 @@ import 'shape.dart'; mixin Shapes { final shapes = []; - SelectedShape? _selected; + SelectedShape? _selectedShape; - SelectedShape? get selected => _selected; + SelectedShape? get selectedShape => _selectedShape; void select(double x, double y) { final shape = findShape(x, y); if (shape != null) { - _selected = SelectedShape(shape, shape.x - x, shape.y - y); + _selectedShape = SelectedShape(shape, shape.x - x, shape.y - y); } else { - _selected = null; + _selectedShape = null; } } @@ -25,12 +25,12 @@ mixin Shapes { } if (index <= shapes.length - 1) { - _selected = SelectedShape(shapes[index], 0, 0); + _selectedShape = SelectedShape(shapes[index], 0, 0); } } void unSelect() { - _selected = null; + _selectedShape = null; } Shape? findShape(double x, double y) { diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart index bfbc90a..70d5cfd 100644 --- a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -26,15 +26,15 @@ class ShapePropertiesWidget extends StatelessWidget { children: [ Row( children: [ - _buildNumberField('x:', app.editor.selected?.shape.x), + _buildNumberField('x:', app.editor.selectedShape?.shape.x), SizedBox(width: 20), - _buildNumberField('y:', app.editor.selected?.shape.y), + _buildNumberField('y:', app.editor.selectedShape?.shape.y), ], ), SizedBox(height: 20), _buildNumberField( 'size:', - app.editor.selected?.shape.size, + app.editor.selectedShape?.shape.size, ), SizedBox(height: 20), Row( @@ -43,16 +43,16 @@ class ShapePropertiesWidget extends StatelessWidget { 'color:', style: TextStyle( color: Colors.black.withOpacity( - app.editor.selected == null ? 0.5 : 1.0, + app.editor.selectedShape == null ? 0.5 : 1.0, ), ), ), SizedBox(width: 10), ColorsWidget( - currentColor: app.editor.selected?.shape.color, + currentColor: app.editor.selectedShape?.shape.color, colors: colors, onColorSelect: (newColor) { - app.editor.selected?.shape.color = newColor; + app.editor.selectedShape?.shape.color = newColor; app.editor.repaint(); }, ), From 89bee4ede0b0972b2d5b39d5dc1bebf6f00dfa4e Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 22:43:04 +0300 Subject: [PATCH 32/36] Make Shape setter fields is private. --- .../memento_editor/editor/manipulator.dart | 5 +-- .../memento_pattern/originator.dart | 2 +- .../memento_editor/shapes/selected_shape.dart | 37 +++++++++++++---- .../memento/memento_editor/shapes/shape.dart | 41 +++++++++++-------- .../memento/memento_editor/shapes/shapes.dart | 9 ++-- .../panels/shape_properties_widget.dart | 4 +- 6 files changed, 60 insertions(+), 38 deletions(-) diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index 56d4c5e..90e9b14 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -32,10 +32,7 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onPointerWheel(double deltaX, double deltaY) { - if (selectedShape != null) { - selectedShape!.changeSize(deltaY / 5); - repaint(); - } + selectedShape?.changeSize(deltaY / 5); } @override diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 47d4a58..078d2df 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -3,8 +3,8 @@ import 'dart:typed_data'; import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; -import '../shapes/shape.dart'; import '../shapes/shapes.dart'; +import '../shapes/shape.dart'; import 'snapshot.dart'; mixin Originator implements Shapes, ClassicApp { diff --git a/patterns/memento/memento_editor/shapes/selected_shape.dart b/patterns/memento/memento_editor/shapes/selected_shape.dart index 6797977..941b3da 100644 --- a/patterns/memento/memento_editor/shapes/selected_shape.dart +++ b/patterns/memento/memento_editor/shapes/selected_shape.dart @@ -1,27 +1,46 @@ -import 'dart:ui'; - -import 'shape.dart'; +part of 'shape.dart'; class SelectedShape { final Shape shape; + final void Function() repaint; - SelectedShape(this.shape, this._xStart, this._yStart); + SelectedShape(this.shape, this._xStart, this._yStart, this.repaint); void changeSize(double delta) { final currentSize = shape.size; - final newSize = currentSize - delta; + var newSize = currentSize - delta; + + if (newSize == shape.size) { + return; + } - if (newSize != shape.size) { - shape.size = newSize; + if (newSize < 10) { + newSize = 10; + } else if (newSize > 200) { + newSize = 200; } + + shape._size = newSize; + repaint(); } final double _xStart; final double _yStart; void dragTo(double x, double y) { - shape.x = x + _xStart; - shape.y = y + _yStart; + shape._x = x + _xStart; + shape._y = y + _yStart; + + repaint(); + } + + void changeColor(Color newColor) { + if (shape.color == newColor) { + return; + } + + shape._color = newColor; + repaint(); } void paintSelectionBox(Canvas canvas) { diff --git a/patterns/memento/memento_editor/shapes/shape.dart b/patterns/memento/memento_editor/shapes/shape.dart index 28369c5..09233c3 100644 --- a/patterns/memento/memento_editor/shapes/shape.dart +++ b/patterns/memento/memento_editor/shapes/shape.dart @@ -1,30 +1,34 @@ +// ignore_for_file: prefer_final_fields + import 'dart:ui'; +part 'selected_shape.dart'; + class Shape { - double x; - double y; - Color color; + double _x; + + double get x => _x; + + double _y; + + double get y => _y; + + Color _color; + + Color get color => _color; + double _size; double get size => _size; - set size(double newSize) { - if (newSize < 10) { - newSize = 10; - } else if (newSize > 200) { - newSize = 200; - } - _size = newSize; - } - Shape( - this.x, - this.y, [ - this.color = const Color(0xFFFFFFFF), + this._x, + this._y, [ + this._color = const Color(0xFFFFFFFF), this._size = 60.0, ]); - static final paintStroke = Paint() + static final _paintStroke = Paint() ..style = PaintingStyle.stroke ..color = Color(0xFFD81B60) ..strokeWidth = 2; @@ -34,8 +38,9 @@ class Shape { ..style = PaintingStyle.fill ..color = color; - canvas.drawCircle(Offset(x, y), _size, paintFill); - canvas.drawCircle(Offset(x, y), _size, paintStroke); + final offset = Offset(x, y); + canvas.drawCircle(offset, _size, paintFill); + canvas.drawCircle(offset, _size, _paintStroke); } bool isBounded(double x, double y) { diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index c360503..09e4e58 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -1,8 +1,9 @@ import 'dart:ui'; -import 'selected_shape.dart'; +import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import 'shape.dart'; -mixin Shapes { + +mixin Shapes implements ClassicApp{ final shapes = []; SelectedShape? _selectedShape; @@ -13,7 +14,7 @@ mixin Shapes { final shape = findShape(x, y); if (shape != null) { - _selectedShape = SelectedShape(shape, shape.x - x, shape.y - y); + _selectedShape = SelectedShape(shape, shape.x - x, shape.y - y, repaint); } else { _selectedShape = null; } @@ -25,7 +26,7 @@ mixin Shapes { } if (index <= shapes.length - 1) { - _selectedShape = SelectedShape(shapes[index], 0, 0); + _selectedShape = SelectedShape(shapes[index], 0, 0, repaint); } } diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart index 70d5cfd..a9c3259 100644 --- a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -52,8 +52,8 @@ class ShapePropertiesWidget extends StatelessWidget { currentColor: app.editor.selectedShape?.shape.color, colors: colors, onColorSelect: (newColor) { - app.editor.selectedShape?.shape.color = newColor; - app.editor.repaint(); + app.editor.selectedShape?.changeColor(newColor); + }, ), ], From aa2e582fe7de7ad5ac781e262ea5cbb9bea4d9fc Mon Sep 17 00:00:00 2001 From: ilopX Date: Tue, 26 Apr 2022 22:53:26 +0300 Subject: [PATCH 33/36] Rename SelectedShape to ActiveShape. --- .../memento/memento_editor/editor/editor.dart | 2 +- .../memento_editor/editor/manipulator.dart | 10 +++++----- .../memento_pattern/caretaker.dart | 2 +- .../memento_pattern/originator.dart | 4 ++-- ...{selected_shape.dart => active_shape.dart} | 4 ++-- .../memento/memento_editor/shapes/shape.dart | 2 +- .../memento/memento_editor/shapes/shapes.dart | 19 +++++++++---------- .../panels/shape_properties_widget.dart | 12 ++++++------ 8 files changed, 27 insertions(+), 28 deletions(-) rename patterns/memento/memento_editor/shapes/{selected_shape.dart => active_shape.dart} (91%) diff --git a/patterns/memento/memento_editor/editor/editor.dart b/patterns/memento/memento_editor/editor/editor.dart index e4ea879..8ab97ac 100644 --- a/patterns/memento/memento_editor/editor/editor.dart +++ b/patterns/memento/memento_editor/editor/editor.dart @@ -10,7 +10,7 @@ class Editor extends ClassicApp with Manipulator, Shapes, Originator { void onPaint(Canvas canvas, Size canvasSize) { _paintBackground(canvas, canvasSize); paintShapes(canvas); - selectedShape?.paintSelectionBox(canvas); + activeShape?.paintSelectionBox(canvas); } void _paintBackground(Canvas canvas, Size canvasSize) { diff --git a/patterns/memento/memento_editor/editor/manipulator.dart b/patterns/memento/memento_editor/editor/manipulator.dart index 90e9b14..c883e97 100644 --- a/patterns/memento/memento_editor/editor/manipulator.dart +++ b/patterns/memento/memento_editor/editor/manipulator.dart @@ -7,15 +7,15 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseDown(double x, double y) { _isMouseDown = true; - final currSelection = selectedShape; + final currSelection = activeShape; select(x, y); - if (currSelection == selectedShape) { + if (currSelection == activeShape) { return; } - if (selectedShape == null) { + if (activeShape == null) { unSelect(); } @@ -25,14 +25,14 @@ mixin Manipulator implements ClassicApp, Shapes { @override void onMouseMove(double x, double y) { if (_isMouseDown) { - selectedShape?.dragTo(x, y); + activeShape?.dragTo(x, y); repaint(); } } @override void onPointerWheel(double deltaX, double deltaY) { - selectedShape?.changeSize(deltaY / 5); + activeShape?.changeSize(deltaY / 5); } @override diff --git a/patterns/memento/memento_editor/memento_pattern/caretaker.dart b/patterns/memento/memento_editor/memento_pattern/caretaker.dart index 971e3f5..e736f00 100644 --- a/patterns/memento/memento_editor/memento_pattern/caretaker.dart +++ b/patterns/memento/memento_editor/memento_pattern/caretaker.dart @@ -7,7 +7,7 @@ class Caretaker { List get list => List.unmodifiable(_mementoList); void addMemento(Memento memento) { - _mementoList.add(memento); + _mementoList.add(memento); } bool isSnapshotExists(Snapshot snapshot) { diff --git a/patterns/memento/memento_editor/memento_pattern/originator.dart b/patterns/memento/memento_editor/memento_pattern/originator.dart index 078d2df..8db1b44 100644 --- a/patterns/memento/memento_editor/memento_pattern/originator.dart +++ b/patterns/memento/memento_editor/memento_pattern/originator.dart @@ -41,10 +41,10 @@ mixin Originator implements Shapes, ClassicApp { void _writeSelectedIndex(ByteData data) { late final int selectedIndex; - if (selectedShape == null) { + if (activeShape == null) { selectedIndex = -1; } else { - selectedIndex = shapes.indexOf(selectedShape!.shape); + selectedIndex = shapes.indexOf(activeShape!.shape); } final byteOffset = data.lengthInBytes - _selectedIndexByteSize; diff --git a/patterns/memento/memento_editor/shapes/selected_shape.dart b/patterns/memento/memento_editor/shapes/active_shape.dart similarity index 91% rename from patterns/memento/memento_editor/shapes/selected_shape.dart rename to patterns/memento/memento_editor/shapes/active_shape.dart index 941b3da..b13a21d 100644 --- a/patterns/memento/memento_editor/shapes/selected_shape.dart +++ b/patterns/memento/memento_editor/shapes/active_shape.dart @@ -1,10 +1,10 @@ part of 'shape.dart'; -class SelectedShape { +class ActiveShape { final Shape shape; final void Function() repaint; - SelectedShape(this.shape, this._xStart, this._yStart, this.repaint); + ActiveShape(this.shape, this._xStart, this._yStart, this.repaint); void changeSize(double delta) { final currentSize = shape.size; diff --git a/patterns/memento/memento_editor/shapes/shape.dart b/patterns/memento/memento_editor/shapes/shape.dart index 09233c3..fa27539 100644 --- a/patterns/memento/memento_editor/shapes/shape.dart +++ b/patterns/memento/memento_editor/shapes/shape.dart @@ -2,7 +2,7 @@ import 'dart:ui'; -part 'selected_shape.dart'; +part 'active_shape.dart'; class Shape { double _x; diff --git a/patterns/memento/memento_editor/shapes/shapes.dart b/patterns/memento/memento_editor/shapes/shapes.dart index 09e4e58..83370b7 100644 --- a/patterns/memento/memento_editor/shapes/shapes.dart +++ b/patterns/memento/memento_editor/shapes/shapes.dart @@ -2,21 +2,20 @@ import 'dart:ui'; import '../../../adapter/flutter_adapter/classic_app/classic_app.dart'; import 'shape.dart'; - -mixin Shapes implements ClassicApp{ +mixin Shapes implements ClassicApp { final shapes = []; - SelectedShape? _selectedShape; + ActiveShape? _activeShape; - SelectedShape? get selectedShape => _selectedShape; + ActiveShape? get activeShape => _activeShape; void select(double x, double y) { final shape = findShape(x, y); if (shape != null) { - _selectedShape = SelectedShape(shape, shape.x - x, shape.y - y, repaint); + _activeShape = ActiveShape(shape, shape.x - x, shape.y - y, repaint); } else { - _selectedShape = null; + _activeShape = null; } } @@ -26,16 +25,16 @@ mixin Shapes implements ClassicApp{ } if (index <= shapes.length - 1) { - _selectedShape = SelectedShape(shapes[index], 0, 0, repaint); + _activeShape = ActiveShape(shapes[index], 0, 0, repaint); } } void unSelect() { - _selectedShape = null; + _activeShape = null; } Shape? findShape(double x, double y) { - for (final shape in shapes) { + for (final shape in shapes.reversed) { if (shape.isBounded(x, y)) { return shape; } @@ -45,7 +44,7 @@ mixin Shapes implements ClassicApp{ } void paintShapes(Canvas canvas) { - for (final shape in shapes.reversed) { + for (final shape in shapes) { shape.paint(canvas); } } diff --git a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart index a9c3259..f923727 100644 --- a/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/shape_properties_widget.dart @@ -26,15 +26,15 @@ class ShapePropertiesWidget extends StatelessWidget { children: [ Row( children: [ - _buildNumberField('x:', app.editor.selectedShape?.shape.x), + _buildNumberField('x:', app.editor.activeShape?.shape.x), SizedBox(width: 20), - _buildNumberField('y:', app.editor.selectedShape?.shape.y), + _buildNumberField('y:', app.editor.activeShape?.shape.y), ], ), SizedBox(height: 20), _buildNumberField( 'size:', - app.editor.selectedShape?.shape.size, + app.editor.activeShape?.shape.size, ), SizedBox(height: 20), Row( @@ -43,16 +43,16 @@ class ShapePropertiesWidget extends StatelessWidget { 'color:', style: TextStyle( color: Colors.black.withOpacity( - app.editor.selectedShape == null ? 0.5 : 1.0, + app.editor.activeShape == null ? 0.5 : 1.0, ), ), ), SizedBox(width: 10), ColorsWidget( - currentColor: app.editor.selectedShape?.shape.color, + currentColor: app.editor.activeShape?.shape.color, colors: colors, onColorSelect: (newColor) { - app.editor.selectedShape?.changeColor(newColor); + app.editor.activeShape?.changeColor(newColor); }, ), From 2b74748d1241efd54f747138638ce7b4acb3822b Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 27 Apr 2022 00:21:36 +0300 Subject: [PATCH 34/36] Rename memento event. --- patterns/memento/memento_editor/application.dart | 4 ++-- .../{create_memento_event.dart => memento_create_event.dart} | 2 +- .../memento/memento_editor/widgets/panels/memento_widget.dart | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename patterns/memento/memento_editor/editor/{create_memento_event.dart => memento_create_event.dart} (59%) diff --git a/patterns/memento/memento_editor/application.dart b/patterns/memento/memento_editor/application.dart index af31cdb..a98d3cc 100644 --- a/patterns/memento/memento_editor/application.dart +++ b/patterns/memento/memento_editor/application.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'editor/create_memento_event.dart'; +import 'editor/memento_create_event.dart'; import 'editor/editor.dart'; import 'memento_pattern/caretaker.dart'; import 'memento_pattern/memento.dart'; @@ -32,7 +32,7 @@ class MementoEditorApplication { final memento = Memento(DateTime.now(), snapshot); caretaker.addMemento(memento); - editor.events.notify(CreateMementoEvent()); + editor.events.notify(MementoCreateEvent()); } void restoreState(Memento memento) { diff --git a/patterns/memento/memento_editor/editor/create_memento_event.dart b/patterns/memento/memento_editor/editor/memento_create_event.dart similarity index 59% rename from patterns/memento/memento_editor/editor/create_memento_event.dart rename to patterns/memento/memento_editor/editor/memento_create_event.dart index 51acd4c..67e459a 100644 --- a/patterns/memento/memento_editor/editor/create_memento_event.dart +++ b/patterns/memento/memento_editor/editor/memento_create_event.dart @@ -1,3 +1,3 @@ import '../../../observer/app_observer/observer/event.dart'; -class CreateMementoEvent extends Event {} +class MementoCreateEvent extends Event {} diff --git a/patterns/memento/memento_editor/widgets/panels/memento_widget.dart b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart index 13c5c53..c61dc67 100644 --- a/patterns/memento/memento_editor/widgets/panels/memento_widget.dart +++ b/patterns/memento/memento_editor/widgets/panels/memento_widget.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import '../../../../observer/subscriber_flutter_widget/subscriber/subscriber_widget.dart'; import '../../application.dart'; -import '../../editor/create_memento_event.dart'; +import '../../editor/memento_create_event.dart'; import '../composite/named_panel.dart'; import '../composite/snapshot_list_widget.dart'; @@ -21,7 +21,7 @@ class MementoWidget extends StatelessWidget { _buildSaveStateButton(), SizedBox(height: 5), Expanded( - child: SubscriberWidget( + child: SubscriberWidget( observer: app.editor.events, builder: (buildContext, event) { return SnapshotListWidget( From 3ae4283dcc18f2fbb1e8890cc068a00c3106c1fc Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 27 Apr 2022 01:15:59 +0300 Subject: [PATCH 35/36] Add README.md. --- patterns/memento/memento_editor/README.md | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 patterns/memento/memento_editor/README.md diff --git a/patterns/memento/memento_editor/README.md b/patterns/memento/memento_editor/README.md new file mode 100644 index 0000000..622c72f --- /dev/null +++ b/patterns/memento/memento_editor/README.md @@ -0,0 +1,48 @@ +# Memento pattern +Memento is a behavioral design pattern that lets you save and restore the previous state of an +object without revealing the details of its implementation. + +Tutorial: [here](https://refactoring.guru/design-patterns/memento). + +### Online demo: +Click on the picture to see a [demo](https://RefactoringGuru.github.io/design-patterns-dart/#/memento/flutter_memento_editor). + +[![image](https://user-images.githubusercontent.com/8049534/165401175-88bc4593-4624-45b4-8c03-6f1390ed771a.png)](https://refactoringguru.github.io/design-patterns-dart/#/memento/flutter_memento_editor) + + +### Dependency Patterns +This complex example includes these implementations: +- [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] +- [[SubscriberWidget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] + +### Diagram: +![image](https://user-images.githubusercontent.com/8049534/165399085-06835617-8ef1-4e2f-930f-03d730433afb.png) + +### Client code: +```dart +class MementoEditorApplication { + final editor = Editor(); + final caretaker = Caretaker(); + + void createDefaultShapes() {/*...*/} + + void saveState() { + final snapshot = editor.backup(); + + if (caretaker.isSnapshotExists(snapshot)) { + return; + } + + final memento = Memento(DateTime.now(), snapshot); + caretaker.addMemento(memento); + editor.events.notify(MementoCreateEvent()); + } + + void restoreState(Memento memento) { + editor + ..unSelect() + ..restore(memento.snapshot) + ..repaint(); + } +} +``` From 849a2c0da7818319afc6b69e243ba265a9a105e8 Mon Sep 17 00:00:00 2001 From: ilopX Date: Wed, 27 Apr 2022 01:20:22 +0300 Subject: [PATCH 36/36] Bump version 0.18.0. --- CHANGELOG.md | 3 +++ README.md | 4 ++-- pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b55e030..21d69b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.18.0 +- Add Memento Editor. + ## 0.17.16 - refactoring - Simplifying the ternary construction. - Remove multiline comment from main README. diff --git a/README.md b/README.md index 87b9ef5..84db0c3 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. - [ ] Interpreter - [ ] **Iterator** - [ ] **Mediator** - - [x] **Memento** - [[Conceptual](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/memento/conceptual)] + - [x] **Memento** - [[Conceptual](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/memento/conceptual)] [[Memento Editor](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/memento/memento_editor)] - [x] **Observer** - [[Open-Close Editor Events](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/open_close_editor_events)] [[AppObserver](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/app_observer)] [[Subscriber Flutter Widget](https://github.com/RefactoringGuru/design-patterns-dart/tree/master/patterns/observer/subscriber_flutter_widget)] - [ ] **State** - [ ] **Template Method** @@ -33,7 +33,7 @@ It contains **Dart** examples for all classic **GoF** design patterns. ## Requirements The examples were written in **Dart 2.15**. -Some complex examples require **Flutter 2.15.0**. +Some complex examples require **Flutter 2.12**. ## Contributor's Guide diff --git a/pubspec.yaml b/pubspec.yaml index b2c58fc..43d8765 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: design_patterns_dart description: Dart examples for all classic GoF design patterns. -version: 0.17.16 +version: 0.18.0 homepage: https://refactoring.guru/design-patterns repository: https://github.com/RefactoringGuru/design-patterns-dart issue_tracker: https://github.com/RefactoringGuru/design-patterns-dart/issue