Browse Source

feat: template to json

Raihan Rizal 3 months ago
parent
commit
678474eed8
5 changed files with 201 additions and 56 deletions
  1. 4 4
      lib/canvas_setup_page.dart
  2. 9 0
      lib/main.dart
  3. 86 0
      lib/providers/editor.dart
  4. 35 2
      lib/widgets/elements.dart
  5. 67 50
      lib/widgets/toolbar.dart

+ 4 - 4
lib/canvas_setup_page.dart

@@ -18,8 +18,8 @@ class _CanvasSetupPageState extends State<CanvasSetupPage> {
18 18
     super.initState();
19 19
     final editorProvider = Provider.of<Editor>(context, listen: false);
20 20
 
21
-    _widthController.text = editorProvider.canvasProperty.width.toString();
22
-    _heightController.text = editorProvider.canvasProperty.height.toString();
21
+    _widthController.text = (editorProvider.canvasProperty.width / 10).toString();
22
+    _heightController.text = (editorProvider.canvasProperty.height / 10).toString();
23 23
 
24 24
   }
25 25
 
@@ -100,8 +100,8 @@ class _CanvasSetupPageState extends State<CanvasSetupPage> {
100 100
                     // Process data
101 101
                     editorProvider.updateCanvasProperty(
102 102
                       context,
103
-                      double.parse(_widthController.text) , 
104
-                      double.parse(_heightController.text)
103
+                      double.parse(_widthController.text) * 10 , 
104
+                      double.parse(_heightController.text) * 10
105 105
                     ); 
106 106
                     Navigator.pop(context);
107 107
                   }

+ 9 - 0
lib/main.dart

@@ -117,6 +117,15 @@ class _HomePageState extends State<HomePage> {
117 117
         appBar: AppBar(
118 118
           title: Text('Template Editor'),
119 119
           actions: [
120
+
121
+            // ! DEBUG BUTTON
122
+            IconButton(
123
+              onPressed: () {
124
+                Provider.of<Editor>(context, listen: false).buildJSON();
125
+              }, 
126
+              icon: Icon(Icons.bug_report_outlined)
127
+            ),
128
+
120 129
             IconButton(
121 130
               onPressed: () {
122 131
                 Navigator.push(

+ 86 - 0
lib/providers/editor.dart

@@ -1,3 +1,4 @@
1
+import 'dart:convert';
1 2
 import 'dart:developer';
2 3
 
3 4
 import 'package:defer_pointer/defer_pointer.dart';
@@ -12,6 +13,8 @@ import '../main.dart';
12 13
 var uuid = Uuid();
13 14
 
14 15
 class Editor extends ChangeNotifier {
16
+  String editorVersion  = '0.1m';
17
+
15 18
   String? selectedElmId;
16 19
   
17 20
   bool isEditing = false;
@@ -620,4 +623,87 @@ class Editor extends ChangeNotifier {
620 623
       dragToClose: true
621 624
     );
622 625
   }
626
+
627
+
628
+
629
+
630
+  // ? Template to JSON
631
+  String buildJSON() {
632
+
633
+    var elementsMap = [];
634
+
635
+    var canvasResultMap = {
636
+      'title': 'Test',
637
+      'width': (canvasProperty.width / 10).round(),
638
+      'height': (canvasProperty.height / 10).round(),
639
+      'content': {
640
+        'version': editorVersion,
641
+        'elements': []
642
+      }
643
+    };
644
+
645
+    for (var element in elementProperties) {
646
+      var elementMap = {
647
+        'id': uuid.v4(),
648
+        'content': element.valueController.text,
649
+        'height': element.elementKey.currentContext?.size?.height.round() ?? 0,
650
+        'width': element.elementKey.currentContext?.size?.width.round() ?? 0,
651
+        'position': {
652
+          'top': element.position.top.round(),
653
+          'left': element.position.left.round()
654
+        },
655
+        'size': _getElementSizeResult(element),
656
+        'rotation': _getElementRotationResult(element.quarterTurns),
657
+        'type': _getElementTypeResult(element)
658
+      };
659
+
660
+      elementsMap.add(elementMap);
661
+    }
662
+
663
+    
664
+    (canvasResultMap['content'] as Map<String, dynamic>)['elements'] = elementsMap;
665
+
666
+
667
+    final templateJsonResult = jsonEncode(canvasResultMap);
668
+
669
+    print(templateJsonResult);
670
+    log(templateJsonResult);
671
+    
672
+    return templateJsonResult;
673
+  }
674
+
675
+
676
+  String _getElementTypeResult(ElementProperty element) {
677
+    switch (element.type) {
678
+      case ElementType.text:
679
+        return 'text';
680
+      case ElementType.textbox:
681
+        return 'textbox';
682
+      case ElementType.qr:
683
+        return 'qrcode';
684
+      default:
685
+        return '';
686
+    }
687
+  }
688
+
689
+  int _getElementRotationResult(int quarterTurns) {
690
+    switch (quarterTurns) {
691
+      case 1:
692
+        return 90;
693
+      case 2:
694
+        return 180;
695
+      case 3:
696
+        return -90;
697
+      default:
698
+        return 0;
699
+    }
700
+  }
701
+
702
+  int _getElementSizeResult(ElementProperty element) {
703
+    if (element.type == ElementType.qr) {
704
+      return element.qrScale ?? 1;
705
+    }
706
+
707
+    return element.fontScale;
708
+  }
623 709
 }

+ 35 - 2
lib/widgets/elements.dart

@@ -238,7 +238,7 @@ class _ElementWidgetState extends State<ElementWidget> {
238 238
     
239 239
           if (!isElmHeightExceedCanvas && bottom > canvas.height) {
240 240
             setState(() {
241
-              element.position.top = canvas.height - height;
241
+              element.position.top = (canvas.height - height).roundToDouble() - 1;
242 242
             });
243 243
       
244 244
             print('object is out of canvas');
@@ -248,7 +248,7 @@ class _ElementWidgetState extends State<ElementWidget> {
248 248
       
249 249
           if (!isElmWidthExceedCanvas && right > canvas.width) {
250 250
             setState(() {
251
-              element.position.left = canvas.width - width;
251
+              element.position.left = (canvas.width - width).roundToDouble() - 1;
252 252
             });
253 253
       
254 254
             print('object is out of canvas');
@@ -257,6 +257,39 @@ class _ElementWidgetState extends State<ElementWidget> {
257 257
       
258 258
           Provider.of<Editor>(context, listen: false).updateElmPosition(details.delta);
259 259
         },
260
+        onPanEnd: (details) {
261
+          // ? Adjust overflow position
262
+          ElementPosition adjustedElementPosition = element.position;
263
+          final elmKeyContext = element.elementKey.currentContext;
264
+
265
+          bool isElmRotatedVertically = [1,3].contains(element.quarterTurns);
266
+    
267
+          double width = isElmRotatedVertically ? elmKeyContext!.size!.height : elmKeyContext!.size!.width;
268
+          double height = isElmRotatedVertically ? elmKeyContext.size!.width : elmKeyContext.size!.height;
269
+    
270
+          double right = element.position.left + width;
271
+          double bottom = element.position.top + height;
272
+
273
+
274
+          if (element.position.top < 0) {
275
+              adjustedElementPosition.top = 0;
276
+          }
277
+
278
+          if (element.position.left < 0) {
279
+              adjustedElementPosition.left = 0;
280
+          }
281
+
282
+          if ((element.position.top + height) > canvas.height) {
283
+              adjustedElementPosition.top = (canvas.height - height).roundToDouble() - 1;
284
+          }
285
+
286
+          if ((element.position.left + width) > canvas.width) {
287
+              adjustedElementPosition.left = (canvas.width - width).roundToDouble() - 1;
288
+            }
289
+
290
+
291
+          Provider.of<Editor>(context, listen: false).updateElmPosition(Offset(adjustedElementPosition.left - element.position.left, adjustedElementPosition.top - element.position.top));
292
+        },
260 293
         child: Stack(
261 294
           clipBehavior: Clip.none,
262 295
           children: [

+ 67 - 50
lib/widgets/toolbar.dart

@@ -140,9 +140,14 @@ class _ToolbarWidgetState extends State<ToolbarWidget> {
140 140
           'This is a variable element, the value shown in editor is a placeholder. The actual value will be generated when printing the label.',
141 141
         )
142 142
       ),
143
+      _buildElementRotatorButton(),
143 144
 
144 145
       // ? Font Resizer
145
-      _buildFontResizer(element)
146
+      _buildFontResizerWidget(element),
147
+
148
+      _buildElementPositionLockerButton(),
149
+
150
+      _buildElementRemoverButton()
146 151
     ];
147 152
   }
148 153
 
@@ -167,67 +172,24 @@ class _ToolbarWidgetState extends State<ToolbarWidget> {
167 172
         },
168 173
       ),
169 174
       
170
-      if (editorProvider.selectedElm!.type != ElementType.qr) ElevatedButton(
171
-        onPressed: Provider.of<Editor>(context).rotate, 
172
-        child: Text('Rotate')
173
-      ),
175
+      if (editorProvider.selectedElm!.type != ElementType.qr) _buildElementRotatorButton(),
174 176
 
175 177
       // ? Font Resizer (Only show when selected element is [ElementType.text, ElementType.textbox])
176
-      if (Provider.of<Editor>(context).shouldShowFontResizer) _buildFontResizer(element),
178
+      if (Provider.of<Editor>(context).shouldShowFontResizer) _buildFontResizerWidget(element),
177 179
 
178 180
       // ? Qr Resizer (Only show when selected element is ElementType.qr)
179
-      if (Provider.of<Editor>(context).shouldShowQrResizer) Row(
180
-        children: [
181
-          DropdownButton<int>(
182
-            value: element?.qrScale,
183
-            items: qrSizeDropdownItems, 
184
-            onChanged: (val) {
185
-              print('dropdown value: $val');
186
-              Provider.of<Editor>(context, listen: false).changeQrSize(val);
187
-            }
188
-          ),
189
-          IconButton.filled(
190
-            onPressed: Provider.of<Editor>(context, listen: false).incrementQrSize, 
191
-            icon: Icon(Icons.add)
192
-          ),
193
-          IconButton.filled(
194
-            onPressed:  Provider.of<Editor>(context, listen: false).decrementQrSize,
195
-            icon: Icon(Icons.remove)
196
-          ),
197
-        ],
198
-      ),
181
+      if (Provider.of<Editor>(context).shouldShowQrResizer) _buildQrResizerWidget(element),
199 182
 
200 183
       // ? Lock Element
201
-      ElevatedButton(
202
-        style: ButtonStyle(
203
-          // backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer)
204
-        ),
205
-        onPressed: () async {
206
-          Provider.of<Editor>(context, listen: false).toggleLockElement();
207
-          FocusScope.of(context).unfocus();
208
-        },
209
-        child: Text(
210
-          Provider.of<Editor>(context).selectedElm!.isLocked ? 'Unlock Element' : 'Lock Element',
211
-          // style: TextStyle(color: Theme.of(context).colorScheme.error),
212
-        )
213
-      ),
184
+      _buildElementPositionLockerButton(),
214 185
 
215 186
       // ? Delete elm button (only show when type is not ElementType.qr)
216
-      if (Provider.of<Editor>(context).shouldShowDeleteElementButton) ElevatedButton(
217
-        style: ButtonStyle(
218
-          backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer)
219
-        ),
220
-        onPressed: () async => Provider.of<Editor>(context, listen: false).deleteElement(context), 
221
-        child: Text(
222
-          'Delete Element',
223
-          style: TextStyle(color: Theme.of(context).colorScheme.error),
224
-        )
225
-      ),
187
+      if (Provider.of<Editor>(context).shouldShowDeleteElementButton) _buildElementRemoverButton()
226 188
     ];
227 189
   }
228 190
 
229 191
 
230
-  Widget _buildFontResizer(ElementProperty? element) {
192
+  Widget _buildFontResizerWidget(ElementProperty? element) {
231 193
     return Row(
232 194
       children: [
233 195
         DropdownButton<int>(
@@ -249,4 +211,59 @@ class _ToolbarWidgetState extends State<ToolbarWidget> {
249 211
       ],
250 212
     );
251 213
   }
214
+
215
+  Widget _buildQrResizerWidget(ElementProperty? element) {
216
+    return  Row(
217
+      children: [
218
+        DropdownButton<int>(
219
+          value: element?.qrScale,
220
+          items: qrSizeDropdownItems, 
221
+          onChanged: (val) {
222
+            print('dropdown value: $val');
223
+            Provider.of<Editor>(context, listen: false).changeQrSize(val);
224
+          }
225
+        ),
226
+        IconButton.filled(
227
+          onPressed: Provider.of<Editor>(context, listen: false).incrementQrSize, 
228
+          icon: Icon(Icons.add)
229
+        ),
230
+        IconButton.filled(
231
+          onPressed:  Provider.of<Editor>(context, listen: false).decrementQrSize,
232
+          icon: Icon(Icons.remove)
233
+        ),
234
+      ],
235
+    );
236
+  }
237
+
238
+  Widget _buildElementRotatorButton() {
239
+    return ElevatedButton(
240
+      onPressed: Provider.of<Editor>(context).rotate, 
241
+      child: Text('Rotate')
242
+    );
243
+  }
244
+
245
+  Widget _buildElementRemoverButton() {
246
+    return ElevatedButton(
247
+      style: ButtonStyle(
248
+        backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer)
249
+      ),
250
+      onPressed: () async => Provider.of<Editor>(context, listen: false).deleteElement(context), 
251
+      child: Text(
252
+        'Delete Element',
253
+        style: TextStyle(color: Theme.of(context).colorScheme.error),
254
+      )
255
+    );
256
+  }
257
+
258
+  Widget _buildElementPositionLockerButton() {
259
+    return ElevatedButton(
260
+      onPressed: () async {
261
+        Provider.of<Editor>(context, listen: false).toggleLockElement();
262
+        FocusScope.of(context).unfocus();
263
+      },
264
+      child: Text(
265
+        Provider.of<Editor>(context).selectedElm!.isLocked ? 'Unlock Element' : 'Lock Element',
266
+      )
267
+    );
268
+  }
252 269
 }