feat: button label editor updates

This commit is contained in:
2023-10-16 02:20:26 +03:00
parent 7fada0c523
commit 95de302b86
9 changed files with 235 additions and 68 deletions

View File

@@ -43,3 +43,4 @@ class DialogButtonSet {
);
}
}

View File

@@ -27,7 +27,9 @@ class MUDAction {
debugPrint('MUDAction.invoke: ${this.content}, $matches');
var content = this.content;
for (var i = 0; i < matches.length; i++) {
content = content.replaceAll('%$i', matches[i]);
// escape %% in the pattern
final pattern = RegExp('(?<!%)%$i');
content = content.replaceAll(pattern, matches[i]);
}
content = doVariableReplacements(store, content);
debugPrint('MUDAction.invoking: $content');

View File

@@ -228,6 +228,11 @@ class GameButtonData {
break;
}
}
@override
String toString() {
return 'GameButtonData(id: $id, label: $label)';
}
}
class GameButton extends StatefulWidget {
@@ -518,10 +523,14 @@ class GameButtonLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IconTheme(
data: _iconTheme(context),
child: Icon(data.icon),
);
if (data.isText) {
return Center(child: Text(data.label!, textScaleFactor: 1.2));
} else {
return IconTheme(
data: _iconTheme(context),
child: Icon(data.icon),
);
}
}
IconThemeData _iconTheme(BuildContext context) =>
@@ -531,15 +540,21 @@ class GameButtonLabel extends StatelessWidget {
class GameButtonLabelData {
String? label;
IconData? icon;
String? iconName;
IconThemeData? iconTheme;
bool get isIcon => icon != null;
bool get isText => label != null;
GameButtonLabelData({
this.label,
this.icon,
this.iconName,
this.iconTheme,
}) : assert(label != null || icon != null);
}) : assert(label != null || icon != null),
assert(icon == null || iconName != null);
factory GameButtonLabelData.empty() => GameButtonLabelData(label: '?');
factory GameButtonLabelData.empty() => GameButtonLabelData(label: '');
factory GameButtonLabelData.fromJson(Map<String, dynamic> json) {
return GameButtonLabelData(
@@ -550,6 +565,7 @@ class GameButtonLabelData {
fontFamily: json['iconFontFamily'] ?? 'MaterialIcons',
)
: null,
iconName: json['iconName'] ?? '',
iconTheme: json['iconTheme'] != null
? _iconThemeDataFromJson(json['iconTheme'])
: null,
@@ -560,6 +576,7 @@ class GameButtonLabelData {
return {
'label': label,
'icon': icon?.codePoint,
'iconName': iconName,
'iconFontFamily': icon?.fontFamily,
'iconTheme': iconTheme != null ? _iconThemeDataToJson(iconTheme!) : null,
};
@@ -581,14 +598,23 @@ class GameButtonLabelData {
GameButtonLabelData copyWith({
String? label,
IconData? icon,
String? iconName,
IconThemeData? iconTheme,
}) {
label = icon != null ? null : label ?? this.label;
icon = label != null ? null : icon ?? this.icon;
return GameButtonLabelData(
label: label ?? this.label,
icon: icon ?? this.icon,
iconName: iconName ?? this.iconName,
iconTheme: iconTheme ?? this.iconTheme,
);
}
@override
String toString() {
return 'GameButtonLabel(${isText ? 'label:' : 'icon:'} ${isText ? label! : iconName!})';
}
}
enum GameButtonInteraction {

View File

@@ -191,10 +191,12 @@ class GameButtonSetData {
Alignment? alignment,
double? spacing,
String? group,
GameButtonLabelData? label,
}) =>
GameButtonSetData(
id: id ?? this.id,
enabled: enabled ?? this.enabled,
label: label?.label ?? this.label,
name: name ?? this.name,
type: type ?? this.type,
buttons: buttons ?? [...this.buttons],
@@ -307,42 +309,69 @@ final movementPreset = GameButtonSetData(
null,
GameButtonData(
id: uuid(),
label: GameButtonLabelData(icon: Icons.keyboard_arrow_up),
label: GameButtonLabelData(
icon: Icons.keyboard_arrow_up,
iconName: 'keyboard_arrow_up',
),
pressAction: MUDAction('north'),
),
GameButtonData(
id: uuid(),
label: GameButtonLabelData(icon: Icons.keyboard_double_arrow_up),
label: GameButtonLabelData(
icon: Icons.keyboard_double_arrow_up,
iconName: 'keyboard_double_arrow_up',
),
pressAction: MUDAction('up'),
),
GameButtonData(
id: uuid(),
label: GameButtonLabelData(icon: Icons.keyboard_arrow_left),
label: GameButtonLabelData(
icon: Icons.keyboard_arrow_left,
iconName: 'keyboard_arrow_left',
),
pressAction: MUDAction('west'),
),
GameButtonData(
id: uuid(),
label: GameButtonLabelData(icon: Icons.visibility_outlined),
labelUp: GameButtonLabelData(icon: Icons.exit_to_app),
labelDown: GameButtonLabelData(icon: Icons.exit_to_app),
label: GameButtonLabelData(
icon: Icons.visibility_outlined,
iconName: 'visibility_outlined',
),
labelUp: GameButtonLabelData(
icon: Icons.exit_to_app,
iconName: 'exit_to_app',
),
labelDown: GameButtonLabelData(
icon: Icons.exit_to_app,
iconName: 'exit_to_app',
),
pressAction: MUDAction('look'),
dragUpAction: MUDAction('exits'),
dragDownAction: MUDAction('exits'),
),
GameButtonData(
id: uuid(),
label: GameButtonLabelData(icon: Icons.keyboard_arrow_right),
label: GameButtonLabelData(
icon: Icons.keyboard_arrow_right,
iconName: 'keyboard_arrow_right',
),
pressAction: MUDAction('east'),
),
null,
GameButtonData(
id: uuid(),
label: GameButtonLabelData(icon: Icons.keyboard_arrow_down),
label: GameButtonLabelData(
icon: Icons.keyboard_arrow_down,
iconName: 'keyboard_arrow_down',
),
pressAction: MUDAction('south'),
),
GameButtonData(
id: uuid(),
label: GameButtonLabelData(icon: Icons.keyboard_double_arrow_down),
label: GameButtonLabelData(
icon: Icons.keyboard_double_arrow_down,
iconName: 'keyboard_double_arrow_down',
),
pressAction: MUDAction('down'),
),
],

3
lib/core/map_utils.dart Normal file
View File

@@ -0,0 +1,3 @@
extension MapExtension<K, V> on Map<K, V> {
Map<V, K> get inverse => map((k, v) => MapEntry(v, k));
}

View File

@@ -87,7 +87,7 @@ class _ButtonEditorDialogState extends State<ButtonEditorDialog> {
GameButtonInteraction.longPress
? null
: () async {
final icon = await showDialog(
final updated = await showDialog(
context: context,
builder: (context) =>
GameButtonLabelEditorDialog(
@@ -95,11 +95,9 @@ class _ButtonEditorDialogState extends State<ButtonEditorDialog> {
label ?? GameButtonLabelData.empty(),
),
);
if (icon != null) {
final data = label?.copyWith(icon: icon) ??
GameButtonLabelData(icon: icon);
if (updated != null) {
setState(() {
this.data.setLabel(interaction, data);
data.setLabel(interaction, updated);
});
}
},

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import '../core/dialog_utils.dart';
import '../core/features/game_button.dart';
import '../widgets/icon_selector.dart';
@@ -14,12 +15,38 @@ class GameButtonLabelEditorDialog extends StatefulWidget {
}
class _GameButtonLabelEditorDialogState
extends State<GameButtonLabelEditorDialog> {
String search = '';
extends State<GameButtonLabelEditorDialog> with TickerProviderStateMixin {
IconSelectorDisplay display = IconSelectorDisplay.grid;
late final GameButtonLabelData data;
late String search;
late int tabIndex;
late TabController tabController;
@override
void initState() {
super.initState();
data = widget.data.copyWith();
debugPrint('data: ${data.iconName}');
search = data.iconName ?? '';
tabIndex = data.icon == null && data.label != null && data.label!.isNotEmpty
? 1
: 0;
tabController =
TabController(vsync: this, length: tabs.length, initialIndex: tabIndex);
}
final tabs = [
const Tab(text: 'Icon'),
const Tab(text: 'Text/Emoji'),
];
@override
Widget build(BuildContext context) {
final actions = DialogUtils.saveButtons(
context,
() => Navigator.pop(context, data),
dismissOnSave: false,
);
return Dialog(
child: SingleChildScrollView(
child: Padding(
@@ -33,40 +60,97 @@ class _GameButtonLabelEditorDialogState
'Edit Label',
style: Theme.of(context).textTheme.titleLarge,
),
Row(
children: [
Expanded(
child: TextField(
onChanged: (value) => setState(() => search = value),
decoration: const InputDecoration(
labelText: 'Search Icon',
const SizedBox(height: 16),
TabBar(
controller: tabController,
tabs: tabs,
),
AnimatedContainer(
duration: const Duration(milliseconds: 300),
// height: tabIndex == 0 ? 400 : 100,
height: 400,
child: TabBarView(
controller: tabController,
children: [
SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Expanded(
child: TextFormField(
initialValue: search,
onChanged: (value) =>
setState(() => search = value),
decoration: InputDecoration(
labelText: 'Search Icon',
suffixIcon: search.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear),
onPressed: () =>
setState(() => search = ''),
)
: null,
),
),
),
IconButton(
icon: Icon(
display == IconSelectorDisplay.grid
? Icons.view_list
: Icons.view_module,
),
onPressed: () => setState(
() => display =
display == IconSelectorDisplay.grid
? IconSelectorDisplay.list
: IconSelectorDisplay.grid,
),
),
],
),
const SizedBox(height: 16),
SizedBox(
height: 600,
child: IconSelector(
search: search,
display: display,
selectedIcon: data.icon,
onSelected: (icon, iconName) {
setState(() {
data.icon = icon;
data.iconName = iconName;
data.label = null;
});
},
),
),
],
),
),
),
IconButton(
icon: Icon(
display == IconSelectorDisplay.grid
? Icons.view_list
: Icons.view_module,
TextFormField(
initialValue: data.label,
onChanged: (value) => setState(
() {
setState(() {
data.label = value;
data.icon = null;
data.iconName = null;
});
},
),
decoration: const InputDecoration(
labelText: 'Label',
helperText:
'You can use plain text or emojies to be shown on the button',
),
),
onPressed: () => setState(() => display =
display == IconSelectorDisplay.grid
? IconSelectorDisplay.list
: IconSelectorDisplay.grid),
),
],
),
const SizedBox(height: 16),
SizedBox(
height: 600,
child: IconSelector(
search: search,
display: display,
onSelected: (icon) {
Navigator.of(context).pop(icon);
},
],
),
),
const SizedBox(height: 16),
actions.row(),
],
),
),
@@ -75,3 +159,4 @@ class _GameButtonLabelEditorDialogState
);
}
}

View File

@@ -188,8 +188,6 @@ class _ButtonSetEditorState extends State<ButtonSetEditor> {
data.getRowFromIndex(index),
);
debugPrint('rowIndices: $rowIndices');
setState(() {
for (final index in rowIndices) {
data.buttons.insert(
@@ -210,7 +208,6 @@ class _ButtonSetEditorState extends State<ButtonSetEditor> {
data.getColumnFromIndex(index),
)
.reversed;
debugPrint('btnIndices: $btnIndices');
setState(() {
for (final index in btnIndices) {
data.buttons.insert(index, null);
@@ -229,7 +226,6 @@ class _ButtonSetEditorState extends State<ButtonSetEditor> {
data.getColumnFromIndex(index),
)
.reversed;
debugPrint('btnIndices: $btnIndices');
setState(() {
for (final index in btnIndices) {
data.buttons.insert(index + 1, null);

View File

@@ -10,12 +10,14 @@ class IconSelector extends StatelessWidget {
super.key,
this.search = '',
required this.onSelected,
this.selectedIcon,
this.display = IconSelectorDisplay.grid,
});
final ValueChanged<IconData> onSelected;
final void Function(IconData data, String name) onSelected;
final String search;
final IconSelectorDisplay display;
final IconData? selectedIcon;
@override
Widget build(BuildContext context) {
@@ -56,21 +58,40 @@ class IconSelector extends StatelessWidget {
leading: Icon(e.value),
title: Text(e.key, overflow: TextOverflow.ellipsis),
subtitle: Text(e.key),
onTap: () => onSelected(e.value),
selected: selectedIcon != null && selectedIcon == e.value,
onTap: () => onSelected(e.value, e.key),
);
}
Widget _buildGridItem(BuildContext context, MapEntry<String, IconData> e) {
final radius = BorderRadius.circular(16);
return InkWell(
onTap: () => onSelected(e.value),
child: Tooltip(
message: e.key,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(e.value),
Text(e.key, overflow: TextOverflow.ellipsis),
],
onTap: () => onSelected(e.value, e.key),
borderRadius: radius,
child: Container(
decoration: BoxDecoration(
borderRadius: radius,
color: selectedIcon != null && selectedIcon == e.value
? Theme.of(context).primaryColor.withOpacity(0.1)
: null,
),
child: Tooltip(
message: e.key,
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(e.value),
Text(e.key,
overflow: TextOverflow.ellipsis,
maxLines: 3,
textAlign: TextAlign.center),
],
),
),
),
),
),
);
@@ -82,6 +103,12 @@ class IconSelector extends StatelessWidget {
// // key = key.replaceAll('_sharp', '').replaceAll('_outline', '');
// return false;
// }
return search.isEmpty || key.contains(search.toLowerCase());
return search.isEmpty ||
_cleanString(key).contains(_cleanString(search.toLowerCase()));
}
String _cleanString(String s) {
return s.replaceAll('_', '').replaceAll('-', '');
}
}