Skip to content

Commit 2498ade

Browse files
Merge branch 'development' into streaming_feature
2 parents 5d510e1 + 10bdf26 commit 2498ade

File tree

8 files changed

+244
-134
lines changed

8 files changed

+244
-134
lines changed

.github/workflows/pull_request_comment.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
steps:
1515
- name: Download artifacts
1616
id: download-artifacts
17-
uses: actions/github-script@v7
17+
uses: actions/github-script@v8
1818
with:
1919
script: |
2020
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
@@ -82,7 +82,7 @@ jobs:
8282
8383
- name: Fetch PR Number
8484
id: fetch-pr-number
85-
uses: actions/github-script@v7
85+
uses: actions/github-script@v8
8686
with:
8787
script: |
8888
var fs = require('fs')
@@ -115,7 +115,7 @@ jobs:
115115
116116
- name: Build success
117117
if: ${{ github.event.workflow_run.conclusion == 'success' }}
118-
uses: actions/github-script@v7
118+
uses: actions/github-script@v8
119119
with:
120120
script: |
121121
var issue_number = ${{ steps.fetch-pr-number.outputs.pr_number }};
@@ -238,7 +238,7 @@ jobs:
238238
239239
- name: Build failed
240240
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
241-
uses: actions/github-script@v7
241+
uses: actions/github-script@v8
242242
with:
243243
script: |
244244
var issue_number = ${{ steps.fetch-pr-number.outputs.pr_number }};

lib/providers/draw_badge_provider.dart

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,33 @@ class DrawBadgeProvider extends ChangeNotifier {
1010

1111
List<List<bool>> _drawViewGrid =
1212
List.generate(11, (_) => List.generate(44, (_) => false));
13-
1413
final List<List<bool>> _previewGrid =
1514
List.generate(11, (_) => List.generate(44, (_) => false));
15+
final List<List<List<bool>>> _undoStack = [];
16+
final List<List<List<bool>>> _redoStack = [];
1617

1718
bool isDrawing = true;
1819
DrawShape _selectedShape = DrawShape.freehand;
1920
BadgeAnimation currentAnimation = LeftAnimation();
2021

22+
// ========== GETTERS ==========
2123
List<List<bool>> getDrawViewGrid() {
2224
// Merge preview + permanent grid
23-
final combined = List.generate(
24-
rows,
25-
(i) => List.generate(cols, (j) {
26-
return _drawViewGrid[i][j] || _previewGrid[i][j];
27-
}));
28-
return combined;
25+
return List.generate(
26+
rows,
27+
(i) => List.generate(
28+
cols,
29+
(j) => _drawViewGrid[i][j] || _previewGrid[i][j],
30+
),
31+
);
2932
}
3033

3134
DrawShape get selectedShape => _selectedShape;
3235
bool getIsDrawing() => isDrawing;
36+
bool get canUndo => _undoStack.isNotEmpty;
37+
bool get canRedo => _redoStack.isNotEmpty;
3338

39+
// ========== STATE SETTERS ==========
3440
void toggleIsDrawing(bool drawing) {
3541
isDrawing = drawing;
3642
notifyListeners();
@@ -47,6 +53,7 @@ class DrawBadgeProvider extends ChangeNotifier {
4753
_previewGrid[row][col] = value;
4854
} else {
4955
_drawViewGrid[row][col] = value;
56+
notifyListeners();
5057
}
5158
}
5259
}
@@ -60,6 +67,7 @@ class DrawBadgeProvider extends ChangeNotifier {
6067
}
6168

6269
void commitGridUpdate() {
70+
_pushToUndoStack();
6371
for (int i = 0; i < rows; i++) {
6472
for (int j = 0; j < cols; j++) {
6573
if (_previewGrid[i][j]) {
@@ -72,12 +80,14 @@ class DrawBadgeProvider extends ChangeNotifier {
7280
}
7381

7482
void resetDrawViewGrid() {
83+
_pushToUndoStack();
7584
_drawViewGrid =
7685
List.generate(rows, (_) => List.generate(cols, (_) => false));
7786
notifyListeners();
7887
}
7988

8089
void updateDrawViewGrid(List<List<bool>> badgeData) {
90+
_pushToUndoStack();
8191
for (int i = 0; i < rows; i++) {
8292
for (int j = 0; j < cols; j++) {
8393
_drawViewGrid[i][j] =
@@ -92,10 +102,41 @@ class DrawBadgeProvider extends ChangeNotifier {
92102
final col = (position.dx / cellSize).floor();
93103
return GridPosition(row, col);
94104
}
105+
106+
// ========== UNDO / REDO ==========
107+
void _pushToUndoStack() {
108+
_undoStack.add(_copyGrid(_drawViewGrid));
109+
_redoStack.clear(); // Invalidate redo stack on new action
110+
}
111+
112+
void pushToUndoStack() {
113+
_pushToUndoStack(); // Existing private method
114+
}
115+
116+
void undo() {
117+
if (_undoStack.isNotEmpty) {
118+
_redoStack.add(_copyGrid(_drawViewGrid));
119+
_drawViewGrid = _undoStack.removeLast();
120+
notifyListeners();
121+
}
122+
}
123+
124+
void redo() {
125+
if (_redoStack.isNotEmpty) {
126+
_undoStack.add(_copyGrid(_drawViewGrid));
127+
_drawViewGrid = _redoStack.removeLast();
128+
notifyListeners();
129+
}
130+
}
131+
132+
List<List<bool>> _copyGrid(List<List<bool>> grid) {
133+
return grid.map((row) => List<bool>.from(row)).toList();
134+
}
95135
}
96136

97137
class GridPosition {
98138
final int x;
99139
final int y;
140+
100141
GridPosition(this.x, this.y);
101142
}

lib/view/draw_badge_screen.dart

Lines changed: 85 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -97,25 +97,32 @@ class _DrawBadgeState extends State<DrawBadge> {
9797
child: Column(
9898
mainAxisAlignment: MainAxisAlignment.center,
9999
children: [
100-
// Main action buttons - centered and closer together
101100
Row(
102101
mainAxisAlignment: MainAxisAlignment.center,
103102
children: [
104-
_buildCompactButton(true, Icons.edit, 'Draw'),
105-
const SizedBox(width: 8),
106-
_buildCompactButton(false, Icons.delete, 'Erase'),
107-
const SizedBox(width: 8),
108-
_buildResetButton(),
109-
const SizedBox(width: 8),
110-
_buildSaveButton(fileHelper),
111-
const SizedBox(width: 8),
112-
_buildShapesToggleButton(),
103+
Flexible(
104+
child: _buildCompactButton(
105+
true, Icons.edit, 'Draw')),
106+
const SizedBox(width: 2),
107+
Flexible(
108+
child: _buildCompactButton(
109+
false, Icons.delete, 'Erase')),
110+
const SizedBox(width: 2),
111+
Flexible(child: _buildResetButton()),
112+
const SizedBox(width: 2),
113+
Flexible(child: _buildSaveButton(fileHelper)),
114+
const SizedBox(width: 2),
115+
Flexible(child: _buildShapesToggleButton()),
116+
const SizedBox(width: 2),
117+
Flexible(child: _buildUndoButton()),
118+
const SizedBox(width: 2),
119+
Flexible(child: _buildRedoButton()),
113120
],
114121
),
122+
const SizedBox(height: 8),
115123
],
116124
),
117125
),
118-
119126
// Shape options - only show when toggled, fixed height
120127
if (_showShapeOptions)
121128
Container(
@@ -126,16 +133,16 @@ class _DrawBadgeState extends State<DrawBadge> {
126133
children: [
127134
_buildCompactShapeCard(
128135
context, DrawShape.freehand, Icons.gesture, 'Free'),
129-
const SizedBox(width: 6),
136+
const SizedBox(width: 2),
130137
_buildCompactShapeCard(context, DrawShape.square,
131138
Icons.crop_square, 'Square'),
132-
const SizedBox(width: 6),
139+
const SizedBox(width: 2),
133140
_buildCompactShapeCard(context, DrawShape.rectangle,
134141
Icons.rectangle_outlined, 'Rect'),
135-
const SizedBox(width: 6),
142+
const SizedBox(width: 2),
136143
_buildCompactShapeCard(context, DrawShape.circle,
137144
Icons.circle_outlined, 'Circle'),
138-
const SizedBox(width: 6),
145+
const SizedBox(width: 2),
139146
_buildCompactShapeCard(context, DrawShape.triangle,
140147
Icons.change_history, 'Triangle'),
141148
],
@@ -202,7 +209,7 @@ class _DrawBadgeState extends State<DrawBadge> {
202209

203210
Widget _buildSaveButton(FileHelper fileHelper) {
204211
return TextButton(
205-
onPressed: () {
212+
onPressed: () async {
206213
List<List<int>> badgeGrid = drawToggle
207214
.getDrawViewGrid()
208215
.map((e) => e.map((e) => e ? 1 : 0).toList())
@@ -211,19 +218,19 @@ class _DrawBadgeState extends State<DrawBadge> {
211218
Converters.convertBitmapToLEDHex(badgeGrid, false);
212219

213220
if (widget.isSavedCard!) {
214-
fileHelper.updateBadgeText(widget.filename!, hexString);
221+
await fileHelper.updateBadgeText(widget.filename!, hexString);
215222
} else if (widget.isSavedClipart!) {
216-
fileHelper.updateClipart(widget.filename!, badgeGrid);
223+
await fileHelper.updateClipart(widget.filename!, badgeGrid);
217224
} else {
218-
fileHelper.saveImage(drawToggle.getDrawViewGrid());
225+
await fileHelper.saveImage(drawToggle.getDrawViewGrid());
219226
}
220227

221-
fileHelper.generateClipartCache();
228+
await fileHelper.generateClipartCache();
222229
ToastUtils().showToast("Clipart Saved Successfully");
223230

224-
Future.delayed(const Duration(milliseconds: 800), () {
231+
if (mounted) {
225232
Navigator.of(context).popUntil((route) => route.isFirst);
226-
});
233+
}
227234
},
228235
style: TextButton.styleFrom(
229236
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
@@ -271,6 +278,62 @@ class _DrawBadgeState extends State<DrawBadge> {
271278
);
272279
}
273280

281+
Widget _buildUndoButton() {
282+
return AnimatedBuilder(
283+
animation: drawToggle,
284+
builder: (context, _) {
285+
final bool canUndo = drawToggle.canUndo;
286+
final Color buttonColor = canUndo ? Colors.black : Colors.grey;
287+
288+
return TextButton(
289+
onPressed: canUndo
290+
? () {
291+
drawToggle.undo();
292+
}
293+
: null,
294+
style: TextButton.styleFrom(
295+
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
296+
minimumSize: const Size(60, 40),
297+
),
298+
child: Column(
299+
mainAxisSize: MainAxisSize.min,
300+
children: [
301+
Icon(Icons.undo, color: buttonColor, size: 20),
302+
const SizedBox(height: 2),
303+
Text('Undo', style: TextStyle(color: buttonColor, fontSize: 10)),
304+
],
305+
),
306+
);
307+
},
308+
);
309+
}
310+
311+
Widget _buildRedoButton() {
312+
return AnimatedBuilder(
313+
animation: drawToggle,
314+
builder: (context, _) {
315+
final bool canRedo = drawToggle.canRedo;
316+
final Color buttonColor = canRedo ? Colors.black : Colors.grey;
317+
318+
return TextButton(
319+
onPressed: canRedo ? drawToggle.redo : null,
320+
style: TextButton.styleFrom(
321+
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
322+
minimumSize: const Size(60, 40),
323+
),
324+
child: Column(
325+
mainAxisSize: MainAxisSize.min,
326+
children: [
327+
Icon(Icons.redo, color: buttonColor, size: 20),
328+
const SizedBox(height: 2),
329+
Text('Redo', style: TextStyle(color: buttonColor, fontSize: 10)),
330+
],
331+
),
332+
);
333+
},
334+
);
335+
}
336+
274337
Widget _buildCompactShapeCard(
275338
BuildContext context, DrawShape shape, IconData icon, String label) {
276339
final isSelected = drawToggle.selectedShape == shape;

lib/view/save_badge_screen.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,9 @@ class _SaveBadgeScreenState extends State<SaveBadgeScreen> {
9494
),
9595
Consumer<BadgeSlotProvider>(
9696
builder: (context, selectionProvider, _) {
97-
if (selectionProvider.selectedBadges.isEmpty)
97+
if (selectionProvider.selectedBadges.isEmpty) {
9898
return SizedBox.shrink();
99+
}
99100
return IconButton(
100101
icon: const Icon(Icons.delete, color: Colors.red),
101102
tooltip: 'Delete Selected',

0 commit comments

Comments
 (0)