Skip to content

Commit

Permalink
[Presentation] Added some dialogs for selecting the character role
Browse files Browse the repository at this point in the history
  • Loading branch information
Wolfteam committed Jan 19, 2022
1 parent 27340fc commit 5d3c8d8
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 29 deletions.
98 changes: 71 additions & 27 deletions lib/presentation/shared/bullet_list.dart
Expand Up @@ -10,6 +10,7 @@ class BulletList extends StatelessWidget {
final Widget Function(int)? iconResolver;
final double fontSize;
final Function(int)? onDelete;
final EdgeInsets padding;

const BulletList({
Key? key,
Expand All @@ -19,40 +20,83 @@ class BulletList extends StatelessWidget {
this.iconResolver,
this.fontSize = 11,
this.onDelete,
this.padding = Styles.edgeInsetAll5,
}) : super(key: key);

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: items.mapIndex(
(e, index) {
Widget leading = Icon(icon, size: iconSize);
if (iconResolver != null) {
leading = iconResolver!(index);
}

return ListTile(
dense: true,
contentPadding: const EdgeInsets.only(left: 10),
visualDensity: const VisualDensity(vertical: -4),
leading: leading,
title: Transform.translate(
offset: Styles.listItemWithIconOffset,
child: Tooltip(message: e, child: Text(e, style: theme.textTheme.bodyText2!.copyWith(fontSize: fontSize))),
children: items
.mapIndex(
(e, index) => _ListItem(
index: index,
title: e,
icon: icon,
fontSize: fontSize,
iconSize: iconSize,
iconResolver: iconResolver,
onDelete: onDelete,
padding: padding,
),
)
.toList(),
);
}
}

class _ListItem extends StatelessWidget {
final int index;
final String title;
final IconData icon;
final double iconSize;
final Widget Function(int)? iconResolver;
final double fontSize;
final Function(int)? onDelete;
final EdgeInsets padding;

const _ListItem({
Key? key,
required this.index,
required this.title,
required this.icon,
required this.iconSize,
this.iconResolver,
required this.fontSize,
this.onDelete,
required this.padding,
}) : super(key: key);

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);

return Padding(
padding: padding,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (iconResolver != null) iconResolver!(index) else Icon(icon, size: iconSize),
Expanded(
child: Tooltip(
message: title,
child: Container(
margin: const EdgeInsets.only(left: 5),
child: Text(
title,
style: theme.textTheme.bodyText2!.copyWith(fontSize: fontSize),
),
),
),
),
if (onDelete != null)
InkWell(
customBorder: const CircleBorder(),
child: Icon(Icons.delete, size: iconSize),
onTap: () => onDelete!(index),
),
trailing: onDelete != null
? IconButton(
icon: const Icon(Icons.delete),
onPressed: () => onDelete!(index),
iconSize: iconSize,
splashRadius: Styles.smallButtonSplashRadius,
)
: null,
);
},
).toList(),
],
),
);
}
}
@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:shiori/domain/enums/enums.dart';
import 'package:shiori/generated/l10n.dart';
import 'package:shiori/presentation/shared/dialogs/select_enum_dialog.dart';
import 'package:shiori/presentation/shared/extensions/i18n_extensions.dart';
import 'package:shiori/presentation/shared/utils/enum_utils.dart';

class SelectCharacterRoleSubTypeDialog extends StatelessWidget {
final List<CharacterRoleSubType> excluded;
final List<CharacterRoleSubType> selectedValues;
final Function(CharacterRoleSubType?)? onSave;

const SelectCharacterRoleSubTypeDialog({
Key? key,
this.excluded = const <CharacterRoleSubType>[],
this.selectedValues = const <CharacterRoleSubType>[],
this.onSave,
}) : super(key: key);

@override
Widget build(BuildContext context) {
final s = S.of(context);
final translatedValues = EnumUtils.getTranslatedAndSortedEnum<CharacterRoleSubType>(
CharacterRoleSubType.values,
(type, _) => s.translateCharacterRoleSubType(type),
);
return SelectEnumDialog<CharacterRoleSubType>(
title: s.subType,
values: translatedValues.map((e) => e.enumValue).toList(),
selectedValues: selectedValues,
excluded: excluded,
lineThroughOnSelectedValues: true,
textResolver: (type) => translatedValues.firstWhere((el) => el.enumValue == type).translation,
onSave: (type) => _onSave(type, context),
);
}

void _onSave(CharacterRoleSubType? type, BuildContext context) {
onSave?.call(type);
Navigator.pop<CharacterRoleSubType>(context, type);
}
}
@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:shiori/domain/enums/enums.dart';
import 'package:shiori/generated/l10n.dart';
import 'package:shiori/presentation/shared/dialogs/select_enum_dialog.dart';
import 'package:shiori/presentation/shared/extensions/i18n_extensions.dart';
import 'package:shiori/presentation/shared/utils/enum_utils.dart';

class SelectCharacterRoleTypeDialog extends StatelessWidget {
final List<CharacterRoleType> excluded;
final List<CharacterRoleType> selectedValues;
final Function(CharacterRoleType?)? onSave;

const SelectCharacterRoleTypeDialog({
Key? key,
this.excluded = const <CharacterRoleType>[],
this.selectedValues = const <CharacterRoleType>[],
this.onSave,
}) : super(key: key);

@override
Widget build(BuildContext context) {
final s = S.of(context);
final translatedValues = EnumUtils.getTranslatedAndSortedEnum<CharacterRoleType>(
CharacterRoleType.values,
(type, _) => s.translateCharacterRoleType(type),
);
return SelectEnumDialog<CharacterRoleType>(
title: s.role,
values: translatedValues.map((e) => e.enumValue).toList(),
selectedValues: selectedValues,
excluded: excluded,
lineThroughOnSelectedValues: true,
textResolver: (type) => translatedValues.firstWhere((el) => el.enumValue == type).translation,
onSave: (type) => _onSave(type, context),
);
}

void _onSave(CharacterRoleType? type, BuildContext context) {
onSave?.call(type);
Navigator.pop<CharacterRoleType>(context, type);
}
}
103 changes: 103 additions & 0 deletions lib/presentation/shared/dialogs/sort_items_dialog.dart
@@ -0,0 +1,103 @@
import 'package:flutter/material.dart';
import 'package:shiori/domain/models/models.dart';
import 'package:shiori/generated/l10n.dart';
import 'package:shiori/presentation/shared/extensions/media_query_extensions.dart';
import 'package:shiori/presentation/shared/utils/toast_utils.dart';

class SortItemsDialog extends StatefulWidget {
final List<SortableItem> items;
final void Function(SortResult result) onSave;

const SortItemsDialog({
Key? key,
required this.items,
required this.onSave,
}) : super(key: key);

@override
State<SortItemsDialog> createState() => _SortItemsDialogState();
}

class _SortItemsDialogState extends State<SortItemsDialog> {
List<SortableItem> _items = [];

@override
void initState() {
_items = [...widget.items];
super.initState();
}

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final s = S.of(context);
final fToast = ToastUtils.of(context);
final mq = MediaQuery.of(context);
return AlertDialog(
title: Text(s.sort),
content: SizedBox(
height: mq.getHeightForDialogs(_items.length),
width: mq.getWidthForDialogs(),
child: ReorderableListView.builder(
shrinkWrap: true,
itemCount: _items.length,
itemBuilder: (ctx, index) {
final item = _items[index];
final position = index + 1;
return ListTile(
key: Key('$index'),
title: Text('#$position - ${item.text}', overflow: TextOverflow.ellipsis),
onTap: () => ToastUtils.showInfoToast(fToast, s.holdToReorder),
);
},
onReorder: (oldIndex, newIndex) => _onReorder(oldIndex, newIndex, context),
),
),
actions: [
OutlinedButton(
onPressed: () => _discardChanges(context),
child: Text(s.cancel, style: TextStyle(color: theme.primaryColor)),
),
ElevatedButton(
onPressed: () => _applyChanges(context),
child: Text(s.save),
)
],
);
}

void _onReorder(int oldIndex, int newIndex, BuildContext context) {
final item = _items.elementAt(oldIndex);

int updatedNewIndex = newIndex;
if (updatedNewIndex >= _items.length) {
updatedNewIndex--;
}
if (updatedNewIndex < 0) {
updatedNewIndex = 0;
}

setState(() {
_items.removeAt(oldIndex);
_items.insert(updatedNewIndex, item);
});
}

void _discardChanges(BuildContext context) {
Navigator.of(context).pop();
}

void _applyChanges(BuildContext context) {
bool somethingChanged = false;
for (var i = 0; i < widget.items.length; i++) {
final current = widget.items[i];
if (current.key != _items[i].key) {
somethingChanged = true;
break;
}
}

widget.onSave(SortResult(somethingChanged, _items));
Navigator.of(context).pop();
}
}
2 changes: 2 additions & 0 deletions lib/presentation/shared/dialogs/text_dialog.dart
Expand Up @@ -58,7 +58,9 @@ class _TextDialogState extends State<TextDialog> {
final theme = Theme.of(context);
final s = S.of(context);
final mq = MediaQuery.of(context);

return AlertDialog(
scrollable: true,
title: Text(widget.isInEditMode ? s.edit : s.add),
content: SizedBox(
width: mq.getWidthForDialogs(),
Expand Down
1 change: 0 additions & 1 deletion lib/presentation/shared/number_picker.dart
@@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:infinite_listview/infinite_listview.dart';
Expand Down
4 changes: 3 additions & 1 deletion lib/presentation/shared/weapons_button_bar.dart
Expand Up @@ -9,12 +9,14 @@ class WeaponsButtonBar extends StatelessWidget {
final List<WeaponType> selectedValues;
final Function(WeaponType) onClick;
final double iconSize;
final bool enabled;

const WeaponsButtonBar({
Key? key,
required this.onClick,
this.selectedValues = const [],
this.iconSize = 24,
this.enabled = true,
}) : super(key: key);

@override
Expand All @@ -37,7 +39,7 @@ class WeaponsButtonBar extends StatelessWidget {
opacity: !isSelected ? 1 : 0.2,
child: Image.asset(value.getWeaponAssetPath()),
),
onPressed: () => onClick(value),
onPressed: !enabled ? null : () => onClick(value),
tooltip: tooltip,
);
}
Expand Down

0 comments on commit 5d3c8d8

Please sign in to comment.