mirror of
https://github.com/chenasraf/mudblock.git
synced 2026-05-18 01:48:57 +00:00
feat: button editor wip
This commit is contained in:
15
.github/FUNDING.yml
vendored
Normal file
15
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: chenasraf
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: casraf
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom:
|
||||
[
|
||||
'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=TSH3C3ABGQM22¤cy_code=ILS&source=url',
|
||||
]
|
||||
@@ -24,9 +24,9 @@ class GameButtonSet extends StatelessWidget {
|
||||
builder: (context) {
|
||||
final containerSize = data.size;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.white),
|
||||
),
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border.all(color: Colors.white),
|
||||
// ),
|
||||
child: SizedBox(
|
||||
width: containerSize.width,
|
||||
height: containerSize.height,
|
||||
@@ -193,27 +193,13 @@ class GameButtonSetData {
|
||||
double calculateWidth() {
|
||||
switch (type) {
|
||||
case GameButtonSetType.row:
|
||||
return buttons.fold<double>(
|
||||
0,
|
||||
(sum, button) =>
|
||||
sum + (button?.size ?? GameButtonData.defaultSize)) +
|
||||
(buttons.length - 1) * spacing;
|
||||
return sumSize(buttons) + (buttons.length - 1) * spacing;
|
||||
case GameButtonSetType.column:
|
||||
return buttons.fold<double>(
|
||||
0,
|
||||
(sum, button) =>
|
||||
max(sum, button?.size ?? GameButtonData.defaultSize)) +
|
||||
(buttons.length - 1) * spacing;
|
||||
return maxSize(buttons) + spacing;
|
||||
case GameButtonSetType.grid:
|
||||
final rowCount = buttons.length ~/ (crossAxisCount ?? 3);
|
||||
final colCount = buttons.length ~/ rowCount;
|
||||
final colWidths = List.generate(
|
||||
colCount,
|
||||
(index) => getButtonsInColumn(index).fold<double>(
|
||||
0,
|
||||
(sum, button) =>
|
||||
max(sum, button?.size ?? GameButtonData.defaultSize),
|
||||
),
|
||||
(index) => maxSize(getColumnButtons(index)),
|
||||
);
|
||||
|
||||
return colWidths.fold<double>(0, (sum, width) => sum + width) +
|
||||
@@ -224,46 +210,71 @@ class GameButtonSetData {
|
||||
double calculateHeight() {
|
||||
switch (type) {
|
||||
case GameButtonSetType.row:
|
||||
return buttons.fold<double>(
|
||||
0,
|
||||
(sum, button) =>
|
||||
max(sum, button?.size ?? GameButtonData.defaultSize)) +
|
||||
(buttons.length - 1) * spacing;
|
||||
return maxSize(buttons) + spacing;
|
||||
case GameButtonSetType.column:
|
||||
return buttons.fold<double>(
|
||||
0,
|
||||
(sum, button) =>
|
||||
sum + (button?.size ?? GameButtonData.defaultSize)) +
|
||||
(buttons.length - 1) * spacing;
|
||||
return sumSize(buttons) + (buttons.length - 1) * spacing;
|
||||
case GameButtonSetType.grid:
|
||||
final rowCount = buttons.length ~/ (crossAxisCount ?? 3);
|
||||
final rowHeights = List.generate(
|
||||
rowCount,
|
||||
(index) => getButtonsInRow(index).fold<double>(
|
||||
0,
|
||||
(sum, button) =>
|
||||
max(sum, button?.size ?? GameButtonData.defaultSize),
|
||||
),
|
||||
(index) => maxSize(getRowButtons(index)),
|
||||
);
|
||||
return rowHeights.fold<double>(0, (sum, height) => sum + height) +
|
||||
(rowCount - 1) * spacing;
|
||||
}
|
||||
}
|
||||
|
||||
List<GameButtonData?> getButtonsInRow(int row) {
|
||||
final rowCount = buttons.length ~/ (crossAxisCount ?? 3);
|
||||
final colCount = buttons.length ~/ rowCount;
|
||||
return buttons.sublist(row * colCount, (row + 1) * colCount).toList();
|
||||
}
|
||||
|
||||
List<GameButtonData?> getButtonsInColumn(int col) {
|
||||
final rowCount = buttons.length ~/ (crossAxisCount ?? 3);
|
||||
final colCount = buttons.length ~/ rowCount;
|
||||
return List<GameButtonData?>.generate(
|
||||
rowCount,
|
||||
(index) => buttons[index * colCount + col],
|
||||
double maxSize(List<GameButtonData?> buttons) {
|
||||
return buttons.fold<double>(
|
||||
0,
|
||||
(sum, button) => max(sum, button?.size ?? GameButtonData.defaultSize),
|
||||
);
|
||||
}
|
||||
|
||||
double sumSize(List<GameButtonData?> buttons) {
|
||||
return buttons.fold<double>(
|
||||
0,
|
||||
(sum, button) => sum + (button?.size ?? GameButtonData.defaultSize),
|
||||
);
|
||||
}
|
||||
|
||||
List<GameButtonData?> getRowButtons(int row) =>
|
||||
getRowIndices(row).map((index) => buttons[index]).toList(growable: false);
|
||||
|
||||
List<GameButtonData?> getColumnButtons(int col) => getColumnIndices(col)
|
||||
.map((index) => buttons[index])
|
||||
.toList(growable: false);
|
||||
|
||||
int get rowCount {
|
||||
switch (type) {
|
||||
case GameButtonSetType.row:
|
||||
return 1;
|
||||
case GameButtonSetType.column:
|
||||
return buttons.length;
|
||||
case GameButtonSetType.grid:
|
||||
return buttons.length ~/ crossAxisCount!;
|
||||
}
|
||||
}
|
||||
|
||||
int get colCount {
|
||||
switch (type) {
|
||||
case GameButtonSetType.row:
|
||||
return buttons.length;
|
||||
case GameButtonSetType.column:
|
||||
return 1;
|
||||
case GameButtonSetType.grid:
|
||||
return crossAxisCount!;
|
||||
}
|
||||
}
|
||||
|
||||
List<int> getRowIndices(int row) =>
|
||||
List<int>.generate(colCount, (index) => row * colCount + index);
|
||||
|
||||
List<int> getColumnIndices(int col) =>
|
||||
List<int>.generate(rowCount, (index) => index * colCount + col);
|
||||
|
||||
int getColumnFromIndex(int index) => index % colCount;
|
||||
|
||||
int getRowFromIndex(int index) => index ~/ colCount;
|
||||
}
|
||||
|
||||
enum GameButtonSetType {
|
||||
|
||||
@@ -26,6 +26,7 @@ void main() async {
|
||||
backgroundColor: Colors.transparent,
|
||||
skipTaskbar: false,
|
||||
titleBarStyle: TitleBarStyle.hidden,
|
||||
center: true,
|
||||
);
|
||||
windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||
await windowManager.show();
|
||||
@@ -66,3 +67,4 @@ class MyApp extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,14 +29,19 @@ class ButtonSetListPage extends StatelessWidget with GameStoreMixin {
|
||||
),
|
||||
];
|
||||
},
|
||||
onSelected: (value) {
|
||||
onSelected: (value) async {
|
||||
final store = storeOf(context);
|
||||
switch (value) {
|
||||
case 'navigation_preset':
|
||||
Navigator.pushNamed(
|
||||
final data = await Navigator.pushNamed(
|
||||
context,
|
||||
Paths.buttonSet,
|
||||
arguments: movementPreset,
|
||||
);
|
||||
if (data != null) {
|
||||
store.currentProfile.saveButtonSet(data as GameButtonSetData);
|
||||
store.loadButtonSets();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
@@ -93,3 +98,4 @@ class ButtonSetListPage extends StatelessWidget with GameStoreMixin {
|
||||
await store.loadButtonSets();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,76 +33,231 @@ class _ButtonSetEditorState extends State<ButtonSetEditor> {
|
||||
context,
|
||||
(context, index) {
|
||||
final button = data.buttons[index];
|
||||
final size = button?.size ?? GameButtonData.defaultSize;
|
||||
final Widget child = button != null
|
||||
? FakeGameButton(label: button.label)
|
||||
: const Icon(Icons.add);
|
||||
return Container(
|
||||
height: (button?.size ?? GameButtonData.defaultSize) - data.spacing,
|
||||
width: (button?.size ?? GameButtonData.defaultSize) - data.spacing,
|
||||
height: size - data.spacing,
|
||||
width: size - data.spacing,
|
||||
color: Colors.grey,
|
||||
child: button != null
|
||||
? FakeGameButton(
|
||||
label: button.label,
|
||||
size: button.size ?? GameButtonData.defaultSize,
|
||||
spacing: data.spacing,
|
||||
onEdit: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => ButtonEditorDialog(
|
||||
data: button,
|
||||
onSave: (data) {
|
||||
setState(() {
|
||||
this.data.buttons[index] = data;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
onDelete: () {
|
||||
child: GameButtonWrapper(
|
||||
size: size,
|
||||
isEmpty: button == null,
|
||||
spacing: data.spacing,
|
||||
onAdd: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => ButtonEditorDialog(
|
||||
onSave: (data) {
|
||||
setState(() {
|
||||
data.buttons[index] = null;
|
||||
this.data.buttons[index] = data;
|
||||
});
|
||||
},
|
||||
emptySpaceControls: [
|
||||
FakeGameButtonMenuItem(
|
||||
value: 'add_row_above',
|
||||
label: 'Add row above',
|
||||
onSelected: () {
|
||||
final colCount = data.crossAxisCount ?? 3;
|
||||
final startOfRowIndex = index - (index % colCount);
|
||||
|
||||
setState(() {
|
||||
data.buttons.insertAll(
|
||||
startOfRowIndex,
|
||||
List.generate(
|
||||
colCount,
|
||||
(index) => null,
|
||||
),
|
||||
);
|
||||
widget.onUpdate(data);
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => ButtonEditorDialog(
|
||||
onSave: (data) {
|
||||
setState(() {
|
||||
this.data.buttons[index] = data;
|
||||
widget.onUpdate(this.data);
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
},
|
||||
onEdit: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => ButtonEditorDialog(
|
||||
data: button,
|
||||
onSave: (data) {
|
||||
setState(() {
|
||||
this.data.buttons[index] = data;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
onDelete: () {
|
||||
setState(() {
|
||||
data.buttons.removeAt(index);
|
||||
});
|
||||
},
|
||||
onClear: () {
|
||||
setState(() {
|
||||
data.buttons[index] = null;
|
||||
});
|
||||
},
|
||||
emptySpaceControls: data.type == GameButtonSetType.grid
|
||||
? _gridMenuItems(index)
|
||||
: data.type == GameButtonSetType.row
|
||||
? _rowMenuItems(index)
|
||||
: _columnMenuItems(index),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
List<FakeGameButtonMenuItem> _columnMenuItems(int index) {
|
||||
return [
|
||||
FakeGameButtonMenuItem(
|
||||
value: 'add_above',
|
||||
label: 'Add above',
|
||||
onSelected: () {
|
||||
setState(() {
|
||||
data.buttons.insert(index, null);
|
||||
widget.onUpdate(data);
|
||||
});
|
||||
},
|
||||
),
|
||||
FakeGameButtonMenuItem(
|
||||
value: 'add_below',
|
||||
label: 'Add below',
|
||||
onSelected: () {
|
||||
setState(() {
|
||||
data.buttons.insert(index + 1, null);
|
||||
widget.onUpdate(data);
|
||||
});
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<FakeGameButtonMenuItem> _rowMenuItems(int index) {
|
||||
return [
|
||||
FakeGameButtonMenuItem(
|
||||
value: 'add_left',
|
||||
label: 'Add left',
|
||||
onSelected: () {
|
||||
setState(() {
|
||||
data.buttons.insert(index, null);
|
||||
widget.onUpdate(data);
|
||||
});
|
||||
},
|
||||
),
|
||||
FakeGameButtonMenuItem(
|
||||
value: 'add_right',
|
||||
label: 'Add right',
|
||||
onSelected: () {
|
||||
setState(() {
|
||||
data.buttons.insert(index + 1, null);
|
||||
widget.onUpdate(data);
|
||||
});
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<FakeGameButtonMenuItem> _gridMenuItems(int index) {
|
||||
return [
|
||||
FakeGameButtonMenuItem(
|
||||
value: 'add_row_above',
|
||||
label: 'Add row above',
|
||||
onSelected: () {
|
||||
final startOfRowIndex = index - (index % data.colCount);
|
||||
|
||||
setState(() {
|
||||
data.buttons.insertAll(
|
||||
startOfRowIndex,
|
||||
List.generate(
|
||||
data.colCount,
|
||||
(index) => null,
|
||||
),
|
||||
);
|
||||
widget.onUpdate(data);
|
||||
});
|
||||
},
|
||||
),
|
||||
FakeGameButtonMenuItem(
|
||||
value: 'add_row_below',
|
||||
label: 'Add row below',
|
||||
onSelected: () {
|
||||
final rowIndices = data
|
||||
.getRowIndices(
|
||||
data.getRowFromIndex(index),
|
||||
)
|
||||
.reversed;
|
||||
|
||||
setState(() {
|
||||
for (final index in rowIndices) {
|
||||
data.buttons.insert(
|
||||
index + data.colCount,
|
||||
null,
|
||||
);
|
||||
}
|
||||
widget.onUpdate(data);
|
||||
});
|
||||
},
|
||||
),
|
||||
FakeGameButtonMenuItem(
|
||||
value: 'add_column_left',
|
||||
label: 'Add column left',
|
||||
onSelected: () {
|
||||
final btnIndices = data
|
||||
.getColumnIndices(
|
||||
data.getColumnFromIndex(index),
|
||||
)
|
||||
.reversed;
|
||||
debugPrint('btnIndices: $btnIndices');
|
||||
setState(() {
|
||||
for (final index in btnIndices) {
|
||||
data.buttons.insert(index, null);
|
||||
}
|
||||
data.crossAxisCount = data.colCount + 1;
|
||||
widget.onUpdate(data);
|
||||
});
|
||||
},
|
||||
),
|
||||
FakeGameButtonMenuItem(
|
||||
value: 'add_column_right',
|
||||
label: 'Add column right',
|
||||
onSelected: () {
|
||||
final btnIndices = data
|
||||
.getColumnIndices(
|
||||
data.getColumnFromIndex(index),
|
||||
)
|
||||
.reversed;
|
||||
debugPrint('btnIndices: $btnIndices');
|
||||
setState(() {
|
||||
for (final index in btnIndices) {
|
||||
data.buttons.insert(index + 1, null);
|
||||
}
|
||||
data.crossAxisCount = data.colCount + 1;
|
||||
widget.onUpdate(data);
|
||||
});
|
||||
},
|
||||
),
|
||||
FakeGameButtonMenuItem(
|
||||
value: 'remove_row',
|
||||
label: 'Remove row',
|
||||
onSelected: () {
|
||||
final rowIndices = data
|
||||
.getRowIndices(
|
||||
data.getRowFromIndex(index),
|
||||
)
|
||||
.reversed;
|
||||
setState(() {
|
||||
for (final index in rowIndices) {
|
||||
data.buttons.removeAt(index);
|
||||
}
|
||||
widget.onUpdate(data);
|
||||
});
|
||||
},
|
||||
),
|
||||
FakeGameButtonMenuItem(
|
||||
value: 'remove_column',
|
||||
label: 'Remove column',
|
||||
onSelected: () {
|
||||
setState(() {
|
||||
final colIndices = data
|
||||
.getColumnIndices(
|
||||
data.getColumnFromIndex(index),
|
||||
)
|
||||
.reversed;
|
||||
for (final index in colIndices) {
|
||||
data.buttons.removeAt(index);
|
||||
}
|
||||
widget.onUpdate(data);
|
||||
});
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Widget _buildContainer(
|
||||
BuildContext context,
|
||||
Widget Function(BuildContext context, int index) builder,
|
||||
@@ -127,23 +282,29 @@ class _ButtonSetEditorState extends State<ButtonSetEditor> {
|
||||
}
|
||||
}
|
||||
|
||||
class FakeGameButton extends StatelessWidget {
|
||||
const FakeGameButton({
|
||||
class GameButtonWrapper extends StatelessWidget {
|
||||
const GameButtonWrapper({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.child,
|
||||
required this.onAdd,
|
||||
required this.onEdit,
|
||||
required this.onDelete,
|
||||
required this.onClear,
|
||||
required this.size,
|
||||
required this.spacing,
|
||||
required this.isEmpty,
|
||||
this.emptySpaceControls = const [],
|
||||
});
|
||||
|
||||
final GameButtonLabelData label;
|
||||
final Widget child;
|
||||
final void Function() onAdd;
|
||||
final void Function() onEdit;
|
||||
final void Function() onClear;
|
||||
final void Function() onDelete;
|
||||
final double size;
|
||||
final double spacing;
|
||||
final List<FakeGameButtonMenuItem> emptySpaceControls;
|
||||
final bool isEmpty;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -151,16 +312,27 @@ class FakeGameButton extends StatelessWidget {
|
||||
offset: Offset(0, size),
|
||||
tooltip: '',
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(
|
||||
value: 'edit',
|
||||
child: Text('Edit'),
|
||||
),
|
||||
if (!isEmpty)
|
||||
const PopupMenuItem(
|
||||
value: 'edit',
|
||||
child: Text('Edit'),
|
||||
),
|
||||
if (isEmpty)
|
||||
const PopupMenuItem(
|
||||
value: 'add',
|
||||
child: Text('Add'),
|
||||
),
|
||||
...emptySpaceControls.map(
|
||||
(control) => PopupMenuItem(
|
||||
value: control.value,
|
||||
child: Text(control.label),
|
||||
),
|
||||
),
|
||||
if (!isEmpty)
|
||||
const PopupMenuItem(
|
||||
value: 'clear',
|
||||
child: Text('Clear'),
|
||||
),
|
||||
const PopupMenuItem(
|
||||
value: 'delete',
|
||||
child: Text('Delete'),
|
||||
@@ -168,9 +340,14 @@ class FakeGameButton extends StatelessWidget {
|
||||
],
|
||||
onSelected: (value) {
|
||||
switch (value) {
|
||||
case 'add':
|
||||
onAdd();
|
||||
case 'edit':
|
||||
onEdit();
|
||||
break;
|
||||
case 'clear':
|
||||
onClear();
|
||||
break;
|
||||
case 'delete':
|
||||
onDelete();
|
||||
break;
|
||||
@@ -190,10 +367,24 @@ class FakeGameButton extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
},
|
||||
child: GameButton(
|
||||
enabled: false,
|
||||
data: GameButtonData.empty().copyWith(label: label),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FakeGameButton extends StatelessWidget {
|
||||
const FakeGameButton({
|
||||
super.key,
|
||||
required this.label,
|
||||
});
|
||||
|
||||
final GameButtonLabelData label;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GameButton(
|
||||
enabled: false,
|
||||
data: GameButtonData.empty().copyWith(label: label),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user