mirror of
https://github.com/chenasraf/mudblock.git
synced 2026-05-17 17:48:05 +00:00
feat: ui updates
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'parser/parser.dart';
|
||||
|
||||
@@ -25,6 +25,30 @@ class ColorUtils {
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
static Color darken(Color color, double amount) {
|
||||
assert(amount >= 0 && amount <= 1);
|
||||
|
||||
final hsl = HSLColor.fromColor(color);
|
||||
final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
|
||||
|
||||
return hslDark.toColor();
|
||||
}
|
||||
|
||||
static Color lighten(Color color, double amount) {
|
||||
assert(amount >= 0 && amount <= 1);
|
||||
|
||||
final hsl = HSLColor.fromColor(color);
|
||||
final hslLight =
|
||||
hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));
|
||||
|
||||
return hslLight.toColor();
|
||||
}
|
||||
|
||||
static Brightness getBrightness(Color color) => ThemeData.estimateBrightnessForColor(color);
|
||||
|
||||
static bool isDark(Color color) => getBrightness(color) == Brightness.dark;
|
||||
static bool isLight(Color color) => getBrightness(color) == Brightness.light;
|
||||
}
|
||||
|
||||
class ColoredText extends ColorToken {
|
||||
@@ -378,3 +402,4 @@ const xtermColorMap = {
|
||||
254: 0xFFE4E4E4,
|
||||
255: 0xFFEEEEEE,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/src/gestures/events.dart';
|
||||
import '../color_utils.dart';
|
||||
import '../platform_utils.dart';
|
||||
import '../store.dart';
|
||||
|
||||
@@ -248,6 +250,8 @@ class _GameButtonState extends State<GameButton> with GameStoreStateMixin {
|
||||
final parentAutomation = Automation.empty();
|
||||
Offset? _dragStart;
|
||||
Offset? _dragEnd;
|
||||
bool isHovering = false;
|
||||
bool isClicking = false;
|
||||
|
||||
//
|
||||
GameButtonData get data => widget.data;
|
||||
@@ -261,12 +265,37 @@ class _GameButtonState extends State<GameButton> with GameStoreStateMixin {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final curLabel = _currentDirectionIcon(context) ?? data.label;
|
||||
final child = Container(
|
||||
// final tooltip = Tooltip(
|
||||
// message: [
|
||||
// GameButtonInteraction.press,
|
||||
// GameButtonInteraction.longPress,
|
||||
// GameButtonInteraction.dragUp,
|
||||
// GameButtonInteraction.dragDown,
|
||||
// GameButtonInteraction.dragLeft,
|
||||
// GameButtonInteraction.dragRight,
|
||||
// ]
|
||||
// .map((dir) {
|
||||
// final content = data.getAction(_direction)?.content;
|
||||
// if (content == null || content.isEmpty) {
|
||||
// return '';
|
||||
// }
|
||||
// final label = dir.name.capitalize();
|
||||
// return '$label: $content';
|
||||
// })
|
||||
// .where((s) => s.isNotEmpty)
|
||||
// .join('\n'),
|
||||
// );
|
||||
final child = AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 100),
|
||||
width: data.size,
|
||||
height: data.size,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: _color(context),
|
||||
color: isClicking
|
||||
? _dragColor(context)
|
||||
: isHovering
|
||||
? _hoverColor(context)
|
||||
: _color(context),
|
||||
),
|
||||
child: GameButtonLabel(data: curLabel),
|
||||
);
|
||||
@@ -290,15 +319,22 @@ class _GameButtonState extends State<GameButton> with GameStoreStateMixin {
|
||||
required Widget child,
|
||||
}) {
|
||||
if (PlatformUtils.isDesktop) {
|
||||
return Listener(
|
||||
onPointerDown: _onPointerDown,
|
||||
onPointerMove: _onPointerMove,
|
||||
onPointerUp: _onPointerUp,
|
||||
child: child,
|
||||
return MouseRegion(
|
||||
onEnter: (_) => setState(() => isHovering = true),
|
||||
onExit: (_) => setState(() => isHovering = false),
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: Listener(
|
||||
onPointerDown: _onPointerDown,
|
||||
onPointerMove: _onPointerMove,
|
||||
onPointerUp: _onPointerUp,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTapDown: _onTapDown,
|
||||
onTapUp: _onTapUp,
|
||||
onTap: _onPressed,
|
||||
onLongPress: _onLongPress,
|
||||
onVerticalDragStart: _onDragStart,
|
||||
@@ -331,12 +367,43 @@ class _GameButtonState extends State<GameButton> with GameStoreStateMixin {
|
||||
Theme.of(context).buttonTheme.colorScheme?.background ??
|
||||
Colors.grey;
|
||||
|
||||
Color _hoverColor(BuildContext context) {
|
||||
final source = _color(context);
|
||||
if (ColorUtils.isDark(source)) {
|
||||
return ColorUtils.lighten(source, 0.05);
|
||||
}
|
||||
return ColorUtils.darken(source, 0.05);
|
||||
}
|
||||
|
||||
Color _dragColor(BuildContext context) {
|
||||
final source = _color(context);
|
||||
if (ColorUtils.isDark(source)) {
|
||||
return ColorUtils.lighten(source, 0.1);
|
||||
}
|
||||
return ColorUtils.darken(source, 0.1);
|
||||
}
|
||||
|
||||
void _onPressed() {
|
||||
_callCurrentDirection();
|
||||
}
|
||||
|
||||
void _onLongPress() {
|
||||
_callAction(data.longPressAction);
|
||||
setState(() {
|
||||
isClicking = false;
|
||||
});
|
||||
}
|
||||
|
||||
void _onTapDown(TapDownDetails details) {
|
||||
setState(() {
|
||||
isClicking = true;
|
||||
});
|
||||
}
|
||||
|
||||
void _onTapUp(TapUpDetails details) {
|
||||
setState(() {
|
||||
isClicking = false;
|
||||
});
|
||||
}
|
||||
|
||||
void _onDragStart(DragStartDetails details) {
|
||||
@@ -367,6 +434,7 @@ class _GameButtonState extends State<GameButton> with GameStoreStateMixin {
|
||||
void _onPointerUp(PointerUpEvent event) {
|
||||
_callCurrentDirection();
|
||||
setState(() {
|
||||
isClicking = false;
|
||||
_direction = GameButtonInteraction.press;
|
||||
});
|
||||
}
|
||||
@@ -374,6 +442,7 @@ class _GameButtonState extends State<GameButton> with GameStoreStateMixin {
|
||||
void _onPointerDown(PointerDownEvent event) {
|
||||
_dragStart = event.position;
|
||||
setState(() {
|
||||
isClicking = true;
|
||||
_direction = GameButtonInteraction.press;
|
||||
});
|
||||
}
|
||||
@@ -516,3 +585,4 @@ enum GameButtonInteraction {
|
||||
dragLeft,
|
||||
dragRight,
|
||||
}
|
||||
|
||||
|
||||
@@ -23,15 +23,10 @@ class GameButtonSet extends StatelessWidget {
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final containerSize = data.size;
|
||||
return Container(
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border.all(color: Colors.white),
|
||||
// ),
|
||||
child: SizedBox(
|
||||
width: containerSize.width,
|
||||
height: containerSize.height,
|
||||
child: _buildButtonContainer(context),
|
||||
),
|
||||
return SizedBox(
|
||||
width: containerSize.width,
|
||||
height: containerSize.height,
|
||||
child: _buildButtonContainer(context),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -96,7 +96,6 @@ class LuaBindings {
|
||||
}
|
||||
profile.variables[name]!.value = value;
|
||||
profile.saveVariable(
|
||||
profile.variables.values.toList(),
|
||||
profile.variables[name]!,
|
||||
);
|
||||
return 0;
|
||||
@@ -125,14 +124,12 @@ class LuaAliasBindings extends LuaAutomationBindings<Alias> {
|
||||
i.enabled = state;
|
||||
return store.currentProfile.saveAlias(i);
|
||||
}));
|
||||
return store.currentProfile.getAliases();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveSingle(Alias item, bool state) async {
|
||||
item.enabled = state;
|
||||
await store.currentProfile.saveAlias(item);
|
||||
return store.currentProfile.getAliases();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,14 +155,12 @@ class LuaTriggerBindings extends LuaAutomationBindings<Trigger> {
|
||||
i.enabled = state;
|
||||
return store.currentProfile.saveTrigger(i);
|
||||
}));
|
||||
return store.currentProfile.getTriggers();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveSingle(Trigger item, bool state) async {
|
||||
item.enabled = state;
|
||||
await store.currentProfile.saveTrigger(item);
|
||||
return store.currentProfile.getTriggers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,14 +186,12 @@ class LuaButtonSetBindings extends LuaAutomationBindings<GameButtonSetData> {
|
||||
i.enabled = state;
|
||||
return store.currentProfile.saveButtonSet(i);
|
||||
}));
|
||||
return store.currentProfile.getTriggers();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveSingle(GameButtonSetData item, bool state) async {
|
||||
item.enabled = state;
|
||||
await store.currentProfile.saveButtonSet(item);
|
||||
return store.currentProfile.getTriggers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class PluginBase extends ChangeNotifier {
|
||||
|
||||
List<Future<void>> additionalLoaders() => [];
|
||||
|
||||
Future<List<Trigger>> loadTriggers() async {
|
||||
Future<List<Trigger>> _loadTriggers() async {
|
||||
debugPrint('$this loadTriggers');
|
||||
final triggers = await storage.readDirectory('triggers');
|
||||
final triggerFiles = <Map<String, dynamic>>[];
|
||||
@@ -42,7 +42,7 @@ class PluginBase extends ChangeNotifier {
|
||||
return triggerFiles.map((e) => Trigger.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
Future<List<Alias>> loadAliases() async {
|
||||
Future<List<Alias>> _loadAliases() async {
|
||||
debugPrint('$this loadAliases');
|
||||
final aliases = await storage.readDirectory('aliases');
|
||||
final aliasFiles = <Map<String, dynamic>>[];
|
||||
@@ -145,41 +145,29 @@ class PluginBase extends ChangeNotifier {
|
||||
return storage.deleteFile('button_sets/${buttonSet.id}');
|
||||
}
|
||||
|
||||
Future<void> saveVariable(List<Variable> current, Variable update) async {
|
||||
Future<void> saveVariable(Variable update) async {
|
||||
debugPrint('$this saveVariable: $update');
|
||||
final existing = current.indexWhere(
|
||||
(v) => v.name == update.name,
|
||||
);
|
||||
if (existing >= 0) {
|
||||
current[existing] = update;
|
||||
} else {
|
||||
current.add(update);
|
||||
}
|
||||
variables[update.name] = update;
|
||||
notifyListeners();
|
||||
return storage.writeFile(
|
||||
'vars',
|
||||
{'vars': current.map((v) => v.toJson()).toList()},
|
||||
{'vars': variables.values.map((v) => v.toJson()).toList()},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deleteVariable(List<Variable> current, Variable update) async {
|
||||
Future<void> deleteVariable(Variable update) async {
|
||||
debugPrint('$this deleteVariable: $update');
|
||||
final existing = current.indexWhere(
|
||||
(v) => v.name == update.name,
|
||||
);
|
||||
if (existing >= 0) {
|
||||
current.removeAt(existing);
|
||||
}
|
||||
variables.remove(update.name);
|
||||
notifyListeners();
|
||||
return storage.writeFile(
|
||||
'vars',
|
||||
{'vars': current.map((v) => v.toJson()).toList()},
|
||||
{'vars': variables.values.map((v) => v.toJson()).toList()},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getTriggers() async {
|
||||
debugPrint('loadTriggers');
|
||||
final list = await loadTriggers();
|
||||
final list = await _loadTriggers();
|
||||
triggers.clear();
|
||||
triggers.addAll(list);
|
||||
notifyListeners();
|
||||
@@ -187,7 +175,7 @@ class PluginBase extends ChangeNotifier {
|
||||
}
|
||||
|
||||
Future<void> getAliases() async {
|
||||
final list = await loadAliases();
|
||||
final list = await _loadAliases();
|
||||
aliases.clear();
|
||||
aliases.addAll(list);
|
||||
notifyListeners();
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import '../core/features/alias.dart';
|
||||
import '../core/features/trigger.dart';
|
||||
import '../core/store.dart';
|
||||
import '../pages/about_page.dart';
|
||||
import '../pages/alias_list_page.dart';
|
||||
import '../pages/alias_page.dart';
|
||||
import '../pages/button_set_page.dart';
|
||||
@@ -43,6 +44,7 @@ class Paths {
|
||||
static const shortcuts = '/shortcuts';
|
||||
|
||||
static const settings = '/settings';
|
||||
static const about = '/about';
|
||||
}
|
||||
|
||||
final routes = <String, Widget Function(BuildContext)>{
|
||||
@@ -89,7 +91,12 @@ final routes = <String, Widget Function(BuildContext)>{
|
||||
|
||||
// variables
|
||||
Paths.variables: (context) => GameStore.consumer(
|
||||
builder: (context, store, child) => const VariableListPage(),
|
||||
builder: (context, store, child) => VariableListPage(
|
||||
variables: store.currentProfile.variables.values.toList(),
|
||||
onSave: (variable) async {
|
||||
store.currentProfile.saveVariable(variable);
|
||||
},
|
||||
),
|
||||
),
|
||||
Paths.variable: (context) {
|
||||
final variable = ModalRoute.of(context)!.settings.arguments as Variable?;
|
||||
@@ -143,6 +150,9 @@ final routes = <String, Widget Function(BuildContext)>{
|
||||
);
|
||||
},
|
||||
|
||||
// about
|
||||
Paths.about: (context) => const AboutPage(),
|
||||
|
||||
// home
|
||||
Paths.home: (context) => HomeScaffold(
|
||||
builder: (context, _) {
|
||||
|
||||
@@ -12,8 +12,17 @@ List<String> splitIntoWords(String string) {
|
||||
.split(RegExp(r'[\W_]+|(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])'));
|
||||
}
|
||||
|
||||
String capitalize(String string) {
|
||||
String _capitalize(String string) {
|
||||
return splitIntoWords(string)
|
||||
.map((word) => word[0].toUpperCase() + word.substring(1))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
extension StringExtension on String {
|
||||
String capitalize() {
|
||||
return _capitalize(this);
|
||||
}
|
||||
String trimMultiline() {
|
||||
return split('\n').map((e) => e.trim()).join('\n').trim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class _ButtonEditorDialogState extends State<ButtonEditorDialog> {
|
||||
initialValue:
|
||||
data.getAction(interaction)?.content ?? '',
|
||||
decoration: InputDecoration(
|
||||
label: Text(capitalize(interaction.name)),
|
||||
label: Text(interaction.name.capitalize()),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
|
||||
@@ -41,7 +41,7 @@ class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueGrey),
|
||||
useMaterial3: true,
|
||||
);
|
||||
return MaterialApp(
|
||||
|
||||
90
lib/pages/about_page.dart
Normal file
90
lib/pages/about_page.dart
Normal file
@@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class AboutPage extends StatelessWidget {
|
||||
const AboutPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final logo = SizedBox.square(
|
||||
dimension: 288, child: Image.asset('assets/images/logo/logo@4x.png'));
|
||||
final title = ListTile(
|
||||
title: Text(
|
||||
'Mudblock',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
subtitle: const Text('By Chen Asraf'),
|
||||
);
|
||||
final version = ListTile(
|
||||
title: const Text(
|
||||
'Version: ',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: FutureBuilder(
|
||||
future: PackageInfo.fromPlatform().then((pkg) => pkg.version),
|
||||
builder: (context, snapshot) => Text(
|
||||
snapshot.hasData ? snapshot.data ?? '-' : '...',
|
||||
),
|
||||
),
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('About Mudblock'),
|
||||
),
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
width: 800,
|
||||
child: ListView(
|
||||
children: [
|
||||
if (MediaQuery.of(context).size.width <= 600) ...[
|
||||
logo,
|
||||
title,
|
||||
version,
|
||||
],
|
||||
if (MediaQuery.of(context).size.width > 600) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
logo,
|
||||
const SizedBox(width: 32),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
title,
|
||||
version,
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 32),
|
||||
ListTile(
|
||||
title: const Text('GitHub'),
|
||||
subtitle: const Text('View the source code on GitHub'),
|
||||
onTap: () => launchUrl(Uri.parse('https://github.com/chenasraf/mudblock')),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Discord'),
|
||||
subtitle: const Text('Join our Discord server'),
|
||||
onTap: () => launchUrl(Uri.parse('https://discord.gg/22XRWSyK')),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Privacy Policy'),
|
||||
subtitle: const Text('https://mudblock.app/privacy'),
|
||||
onTap: () => launchUrl(Uri.parse('https://mudblock.app/privacy')),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ class _GameButtonSetPageState extends State<GameButtonSetPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final platformWindowName = PlatformUtils.isDesktop ? 'window' : 'screen';
|
||||
final interaction = PlatformUtils.isDesktop ? 'Click' : 'Tap';
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Button Set'),
|
||||
@@ -77,7 +78,7 @@ class _GameButtonSetPageState extends State<GameButtonSetPage> {
|
||||
.map(
|
||||
(e) => DropdownMenuEntry(
|
||||
value: e,
|
||||
label: capitalize(e.name),
|
||||
label: e.name.capitalize(),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
@@ -105,7 +106,7 @@ class _GameButtonSetPageState extends State<GameButtonSetPage> {
|
||||
.map(
|
||||
(e) => DropdownMenuEntry(
|
||||
value: e,
|
||||
label: capitalize(e.toString().split('.')[1]),
|
||||
label: (e.toString().split('.')[1]).capitalize(),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
@@ -116,12 +117,26 @@ class _GameButtonSetPageState extends State<GameButtonSetPage> {
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Buttons',
|
||||
style: Theme.of(context).textTheme.titleLarge!,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
'''
|
||||
$interaction a space (with +) to add buttons.
|
||||
$interaction a button to edit it, or add buttons next to it.
|
||||
'''.trimMultiline(),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ButtonSetEditor(
|
||||
key: Key(buttonSet.type.name),
|
||||
data: buttonSet,
|
||||
onUpdate: (data) {
|
||||
setState(() {
|
||||
buttonSet = buttonSet.copyWith(buttons: data.buttons);
|
||||
buttonSet =
|
||||
buttonSet.copyWith(buttons: data.buttons);
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
@@ -47,6 +47,7 @@ class HomeScaffold extends StatelessWidget with GameStoreMixin {
|
||||
return Text('v$version');
|
||||
},
|
||||
),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.about),
|
||||
),
|
||||
const Divider(),
|
||||
GameStore.consumer(
|
||||
|
||||
@@ -5,7 +5,14 @@ import '../core/features/variable.dart';
|
||||
import '../core/routes.dart';
|
||||
|
||||
class VariableListPage extends StatelessWidget with GameStoreMixin {
|
||||
const VariableListPage({super.key});
|
||||
const VariableListPage({
|
||||
super.key,
|
||||
required this.variables,
|
||||
required this.onSave,
|
||||
});
|
||||
|
||||
final List<Variable> variables;
|
||||
final Future<void> Function(Variable variable) onSave;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -13,30 +20,23 @@ class VariableListPage extends StatelessWidget with GameStoreMixin {
|
||||
appBar: AppBar(
|
||||
title: const Text('Variables'),
|
||||
),
|
||||
// TODO extract this to props
|
||||
body: GameStore.consumer(
|
||||
builder: (context, store, child) {
|
||||
debugPrint('Variable list rebuild');
|
||||
final variables = store.currentProfile.variables.values;
|
||||
return ListView.builder(
|
||||
itemCount: variables.length,
|
||||
itemBuilder: (context, item) {
|
||||
final variable = variables.elementAt(item);
|
||||
return ListTile(
|
||||
key: Key(variable.name),
|
||||
title: Text(variable.name),
|
||||
subtitle: Text(variable.value),
|
||||
onTap: () async {
|
||||
final updated = await Navigator.pushNamed(
|
||||
context,
|
||||
Paths.variable,
|
||||
arguments: variable,
|
||||
);
|
||||
if (updated != null) {
|
||||
await save(store, updated as Variable);
|
||||
}
|
||||
},
|
||||
body: ListView.builder(
|
||||
itemCount: variables.length,
|
||||
itemBuilder: (context, item) {
|
||||
final variable = variables.elementAt(item);
|
||||
return ListTile(
|
||||
key: Key(variable.name),
|
||||
title: Text(variable.name),
|
||||
subtitle: Text(variable.value),
|
||||
onTap: () async {
|
||||
final updated = await Navigator.pushNamed(
|
||||
context,
|
||||
Paths.variable,
|
||||
arguments: variable,
|
||||
);
|
||||
if (updated != null) {
|
||||
await onSave(updated as Variable);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -44,21 +44,13 @@ class VariableListPage extends StatelessWidget with GameStoreMixin {
|
||||
floatingActionButton: FloatingActionButton(
|
||||
child: const Icon(Icons.add),
|
||||
onPressed: () async {
|
||||
final store = storeOf(context);
|
||||
final variable = await Navigator.pushNamed(context, Paths.variable);
|
||||
if (variable != null) {
|
||||
save(store, variable as Variable);
|
||||
onSave(variable as Variable);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// TODO extract this to props
|
||||
Future<void> save(GameStore store, Variable updated) async {
|
||||
await store.currentProfile
|
||||
.saveVariable(store.currentProfile.variables.values.toList(), updated);
|
||||
await store.currentProfile.loadVariables();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,19 +32,30 @@ class _ButtonSetEditorState extends State<ButtonSetEditor> {
|
||||
return _buildContainer(
|
||||
context,
|
||||
(context, index) {
|
||||
final theme = Theme.of(context);
|
||||
final button = data.buttons[index];
|
||||
final size = button?.size ?? GameButtonData.defaultSize;
|
||||
final Widget child = button != null
|
||||
? FakeGameButton(label: button.label)
|
||||
: const Icon(Icons.add);
|
||||
: const Icon(Icons.add, color: Colors.white);
|
||||
return Container(
|
||||
height: size - data.spacing,
|
||||
width: size - data.spacing,
|
||||
color: Colors.grey,
|
||||
height: size,
|
||||
width: size,
|
||||
decoration: BoxDecoration(
|
||||
color: button == null
|
||||
? theme.dividerColor.withOpacity(0.2)
|
||||
: theme.dividerColor.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: theme.dividerColor,
|
||||
width: 1,
|
||||
style: BorderStyle.solid,
|
||||
),
|
||||
),
|
||||
// color: Colors.grey,
|
||||
child: GameButtonWrapper(
|
||||
size: size,
|
||||
isEmpty: button == null,
|
||||
spacing: data.spacing,
|
||||
onAdd: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -87,7 +98,10 @@ class _ButtonSetEditorState extends State<ButtonSetEditor> {
|
||||
: data.type == GameButtonSetType.row
|
||||
? _rowMenuItems(index)
|
||||
: _columnMenuItems(index),
|
||||
child: child,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(data.spacing / 2),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -168,11 +182,11 @@ class _ButtonSetEditorState extends State<ButtonSetEditor> {
|
||||
value: 'add_row_below',
|
||||
label: 'Add row below',
|
||||
onSelected: () {
|
||||
final rowIndices = data
|
||||
.getRowIndices(
|
||||
data.getRowFromIndex(index),
|
||||
)
|
||||
.reversed;
|
||||
final rowIndices = data.getRowIndices(
|
||||
data.getRowFromIndex(index),
|
||||
);
|
||||
|
||||
debugPrint('rowIndices: $rowIndices');
|
||||
|
||||
setState(() {
|
||||
for (final index in rowIndices) {
|
||||
@@ -264,20 +278,32 @@ class _ButtonSetEditorState extends State<ButtonSetEditor> {
|
||||
BuildContext context,
|
||||
Widget Function(BuildContext context, int index) builder,
|
||||
) {
|
||||
final size = data.size;
|
||||
final size = Size(
|
||||
data.size.width + data.spacing * 6,
|
||||
data.size.height + data.spacing * 10,
|
||||
);
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
child: Container(
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
child: GameButtonSet.buildContainer(
|
||||
context: context,
|
||||
type: data.type,
|
||||
size: data.size,
|
||||
count: data.buttons.length,
|
||||
crossAxisCount: data.crossAxisCount,
|
||||
spacing: data.spacing,
|
||||
alignment: data.alignment,
|
||||
builder: builder,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: GameButtonSet.buildContainer(
|
||||
context: context,
|
||||
type: data.type,
|
||||
size: data.size,
|
||||
count: data.buttons.length,
|
||||
crossAxisCount: data.crossAxisCount,
|
||||
spacing: data.spacing / 3,
|
||||
alignment: data.alignment,
|
||||
builder: builder,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -293,7 +319,6 @@ class GameButtonWrapper extends StatelessWidget {
|
||||
required this.onDelete,
|
||||
required this.onClear,
|
||||
required this.size,
|
||||
required this.spacing,
|
||||
required this.isEmpty,
|
||||
this.emptySpaceControls = const [],
|
||||
});
|
||||
@@ -304,7 +329,6 @@ class GameButtonWrapper extends StatelessWidget {
|
||||
final void Function() onClear;
|
||||
final void Function() onDelete;
|
||||
final double size;
|
||||
final double spacing;
|
||||
final List<FakeGameButtonMenuItem> emptySpaceControls;
|
||||
final bool isEmpty;
|
||||
|
||||
|
||||
@@ -7,12 +7,16 @@
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) window_manager_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
|
||||
window_manager_plugin_register_with_registrar(window_manager_registrar);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
screen_retriever
|
||||
url_launcher_linux
|
||||
window_manager
|
||||
)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import package_info_plus
|
||||
import path_provider_foundation
|
||||
import screen_retriever
|
||||
import shared_preferences_foundation
|
||||
import url_launcher_macos
|
||||
import window_manager
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
@@ -16,5 +17,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ PODS:
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- url_launcher_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- window_manager (0.2.0):
|
||||
- FlutterMacOS
|
||||
|
||||
@@ -19,6 +21,7 @@ DEPENDENCIES:
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
|
||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
@@ -32,6 +35,8 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
|
||||
shared_preferences_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
|
||||
url_launcher_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||
window_manager:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
|
||||
|
||||
@@ -41,6 +46,7 @@ SPEC CHECKSUMS:
|
||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
||||
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
|
||||
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
|
||||
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
|
||||
|
||||
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
|
||||
|
||||
66
pubspec.lock
66
pubspec.lock
@@ -524,6 +524,70 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.14"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.20"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.8"
|
||||
uuid:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -590,4 +654,4 @@ packages:
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.1.2 <4.0.0"
|
||||
flutter: ">=3.7.0"
|
||||
flutter: ">=3.13.0"
|
||||
|
||||
@@ -47,6 +47,7 @@ dependencies:
|
||||
lua_dardo: ^0.0.5
|
||||
path_provider: ^2.1.1
|
||||
package_info_plus: ^4.1.0
|
||||
url_launcher: ^6.1.14
|
||||
# permission_handler: ^11.0.0
|
||||
|
||||
dev_dependencies:
|
||||
@@ -79,6 +80,7 @@ flutter:
|
||||
# - images/a_dot_ham.jpeg
|
||||
assets:
|
||||
- assets/images/logo/logo.png
|
||||
- assets/images/logo/logo@4x.png
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||
|
||||
@@ -7,11 +7,14 @@
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
WindowManagerPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
screen_retriever
|
||||
url_launcher_windows
|
||||
window_manager
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user