mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-26 03:03:45 +01:00 
			
		
		
		
	Started work on new unified category selector/editor
This commit is contained in:
		| @@ -1,5 +1,9 @@ | ||||
| import 'dart:math'; | ||||
|  | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:obtainium/components/generated_form_modal.dart'; | ||||
| import 'package:obtainium/providers/settings_provider.dart'; | ||||
|  | ||||
| abstract class GeneratedFormItem { | ||||
|   late String key; | ||||
| @@ -82,6 +86,27 @@ class GeneratedFormSwitch extends GeneratedFormItem { | ||||
|   } | ||||
| } | ||||
|  | ||||
| class GeneratedFormTagInput extends GeneratedFormItem { | ||||
|   late MapEntry<String, String>? deleteConfirmationMessage; | ||||
|   GeneratedFormTagInput(String key, | ||||
|       {String label = 'Input', | ||||
|       List<Widget> belowWidgets = const [], | ||||
|       Map<String, MapEntry<int, bool>> defaultValue = const {}, | ||||
|       List<String? Function(Map<String, MapEntry<int, bool>> value)> | ||||
|           additionalValidators = const [], | ||||
|       this.deleteConfirmationMessage}) | ||||
|       : super(key, | ||||
|             label: label, | ||||
|             belowWidgets: belowWidgets, | ||||
|             defaultValue: defaultValue, | ||||
|             additionalValidators: additionalValidators); | ||||
|  | ||||
|   @override | ||||
|   Map<String, MapEntry<int, bool>> ensureType(val) { | ||||
|     return val is Map<String, MapEntry<int, bool>> ? val : {}; | ||||
|   } | ||||
| } | ||||
|  | ||||
| typedef OnValueChanges = void Function( | ||||
|     Map<String, dynamic> values, bool valid, bool isBuilding); | ||||
|  | ||||
| @@ -120,6 +145,21 @@ class _GeneratedFormState extends State<GeneratedForm> { | ||||
|     widget.onValueChanges(returnValues, valid, isBuilding); | ||||
|   } | ||||
|  | ||||
|   // Generates a random light color | ||||
| // Courtesy of ChatGPT 😭 (with a bugfix 🥳) | ||||
|   Color generateRandomLightColor() { | ||||
|     // Create a random number generator | ||||
|     final Random random = Random(); | ||||
|  | ||||
|     // Generate random hue, saturation, and value values | ||||
|     final double hue = random.nextDouble() * 360; | ||||
|     final double saturation = 0.5 + random.nextDouble() * 0.5; | ||||
|     final double value = 0.9 + random.nextDouble() * 0.1; | ||||
|  | ||||
|     // Create a HSV color with the random values | ||||
|     return HSVColor.fromAHSV(1.0, hue, saturation, value).toColor(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
| @@ -212,6 +252,117 @@ class _GeneratedFormState extends State<GeneratedForm> { | ||||
|                   }) | ||||
|             ], | ||||
|           ); | ||||
|         } else if (widget.items[r][e] is GeneratedFormTagInput) { | ||||
|           formInputs[r][e] = Wrap( | ||||
|             children: [ | ||||
|               ...(values[widget.items[r][e].key] | ||||
|                           as Map<String, MapEntry<int, bool>>?) | ||||
|                       ?.entries | ||||
|                       .map((e2) { | ||||
|                     return Padding( | ||||
|                         padding: const EdgeInsets.symmetric(horizontal: 4), | ||||
|                         child: ChoiceChip( | ||||
|                           label: Text(e2.key), | ||||
|                           backgroundColor: Color(e2.value.key).withAlpha(200), | ||||
|                           selectedColor: Color(e2.value.key), | ||||
|                           visualDensity: VisualDensity.compact, | ||||
|                           selected: e2.value.value, | ||||
|                           onSelected: (value) { | ||||
|                             setState(() { | ||||
|                               (values[widget.items[r][e].key] as Map<String, | ||||
|                                       MapEntry<int, bool>>)[e2.key] = | ||||
|                                   MapEntry( | ||||
|                                       (values[widget.items[r][e].key] as Map< | ||||
|                                               String, | ||||
|                                               MapEntry<int, bool>>)[e2.key]! | ||||
|                                           .key, | ||||
|                                       value); | ||||
|                               someValueChanged(); | ||||
|                             }); | ||||
|                           }, | ||||
|                         )); | ||||
|                   }) ?? | ||||
|                   [const SizedBox.shrink()], | ||||
|               (values[widget.items[r][e].key] | ||||
|                               as Map<String, MapEntry<int, bool>>?) | ||||
|                           ?.values | ||||
|                           .where((e) => e.value) | ||||
|                           .isNotEmpty == | ||||
|                       true | ||||
|                   ? Padding( | ||||
|                       padding: const EdgeInsets.symmetric(horizontal: 4), | ||||
|                       child: IconButton( | ||||
|                         onPressed: () { | ||||
|                           fn() { | ||||
|                             setState(() { | ||||
|                               var temp = values[widget.items[r][e].key] | ||||
|                                   as Map<String, MapEntry<int, bool>>; | ||||
|                               temp.removeWhere((key, value) => value.value); | ||||
|                               values[widget.items[r][e].key] = temp; | ||||
|                               someValueChanged(); | ||||
|                             }); | ||||
|                           } | ||||
|  | ||||
|                           if ((widget.items[r][e] as GeneratedFormTagInput) | ||||
|                                   .deleteConfirmationMessage != | ||||
|                               null) { | ||||
|                             showDialog<Map<String, dynamic>?>( | ||||
|                                 context: context, | ||||
|                                 builder: (BuildContext ctx) { | ||||
|                                   return GeneratedFormModal( | ||||
|                                       title: tr('deleteCategoryQuestion'), | ||||
|                                       message: tr('categoryDeleteWarning'), | ||||
|                                       items: const []); | ||||
|                                 }).then((value) { | ||||
|                               if (value != null) { | ||||
|                                 fn(); | ||||
|                               } | ||||
|                             }); | ||||
|                           } else { | ||||
|                             fn(); | ||||
|                           } | ||||
|                         }, | ||||
|                         icon: const Icon(Icons.remove), | ||||
|                         visualDensity: VisualDensity.compact, | ||||
|                         tooltip: tr('remove'), | ||||
|                       )) | ||||
|                   : const SizedBox.shrink(), | ||||
|               Padding( | ||||
|                   padding: const EdgeInsets.symmetric(horizontal: 4), | ||||
|                   child: IconButton( | ||||
|                     onPressed: () { | ||||
|                       showDialog<Map<String, dynamic>?>( | ||||
|                           context: context, | ||||
|                           builder: (BuildContext ctx) { | ||||
|                             return GeneratedFormModal( | ||||
|                                 title: widget.items[r][e].label, | ||||
|                                 items: [ | ||||
|                                   [ | ||||
|                                     GeneratedFormTextField('label', | ||||
|                                         label: tr('label')) | ||||
|                                   ] | ||||
|                                 ]); | ||||
|                           }).then((value) { | ||||
|                         String? label = value?['label']; | ||||
|                         if (label != null) { | ||||
|                           setState(() { | ||||
|                             var temp = values[widget.items[r][e].key] | ||||
|                                 as Map<String, MapEntry<int, bool>>?; | ||||
|                             temp ??= {}; | ||||
|                             temp[label] = MapEntry( | ||||
|                                 generateRandomLightColor().value, false); | ||||
|                             values[widget.items[r][e].key] = temp; | ||||
|                             someValueChanged(); | ||||
|                           }); | ||||
|                         } | ||||
|                       }); | ||||
|                     }, | ||||
|                     icon: const Icon(Icons.add), | ||||
|                     visualDensity: VisualDensity.compact, | ||||
|                     tooltip: tr('add'), | ||||
|                   )), | ||||
|             ], | ||||
|           ); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   | ||||
| @@ -174,12 +174,21 @@ class _SettingsPageState extends State<SettingsPage> { | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     var categories = settingsProvider.categories; | ||||
|     var categoryTagInput = GeneratedForm(items: [ | ||||
|       [ | ||||
|         GeneratedFormTagInput('categories', | ||||
|             defaultValue: categories | ||||
|                 .map((key, value) => MapEntry(key, MapEntry(value, false))), | ||||
|             deleteConfirmationMessage: MapEntry( | ||||
|                 tr('deleteCategoryQuestion'), tr('categoryDeleteWarning'))) | ||||
|       ] | ||||
|     ], onValueChanges: ((values, valid, isBuilding) {})); | ||||
|  | ||||
|     const height16 = SizedBox( | ||||
|       height: 16, | ||||
|     ); | ||||
|  | ||||
|     var categories = settingsProvider.categories; | ||||
|  | ||||
|     return Scaffold( | ||||
|         backgroundColor: Theme.of(context).colorScheme.surface, | ||||
|         body: CustomScrollView(slivers: <Widget>[ | ||||
| @@ -264,85 +273,7 @@ class _SettingsPageState extends State<SettingsPage> { | ||||
|                                   color: Theme.of(context).colorScheme.primary), | ||||
|                             ), | ||||
|                             height16, | ||||
|                             Wrap( | ||||
|                               children: [ | ||||
|                                 ...categories.entries.toList().map((e) { | ||||
|                                   return Padding( | ||||
|                                       padding: const EdgeInsets.symmetric( | ||||
|                                           horizontal: 4), | ||||
|                                       child: Chip( | ||||
|                                         label: Text(e.key), | ||||
|                                         backgroundColor: Color(e.value), | ||||
|                                         visualDensity: VisualDensity.compact, | ||||
|                                         onDeleted: () { | ||||
|                                           showDialog<Map<String, dynamic>?>( | ||||
|                                               context: context, | ||||
|                                               builder: (BuildContext ctx) { | ||||
|                                                 return GeneratedFormModal( | ||||
|                                                     title: tr( | ||||
|                                                         'deleteCategoryQuestion'), | ||||
|                                                     message: tr( | ||||
|                                                         'categoryDeleteWarning', | ||||
|                                                         args: [e.key]), | ||||
|                                                     items: []); | ||||
|                                               }).then((value) { | ||||
|                                             if (value != null) { | ||||
|                                               setState(() { | ||||
|                                                 categories.remove(e.key); | ||||
|                                                 settingsProvider.categories = | ||||
|                                                     categories; | ||||
|                                               }); | ||||
|                                               appsProvider.saveApps(appsProvider | ||||
|                                                   .apps.values | ||||
|                                                   .where((element) => | ||||
|                                                       element.app.category == | ||||
|                                                       e.key) | ||||
|                                                   .map((e) { | ||||
|                                                 var a = e.app; | ||||
|                                                 a.category = null; | ||||
|                                                 return a; | ||||
|                                               }).toList()); | ||||
|                                             } | ||||
|                                           }); | ||||
|                                         }, | ||||
|                                       )); | ||||
|                                 }), | ||||
|                                 Padding( | ||||
|                                     padding: const EdgeInsets.symmetric( | ||||
|                                         horizontal: 4), | ||||
|                                     child: IconButton( | ||||
|                                       onPressed: () { | ||||
|                                         showDialog<Map<String, dynamic>?>( | ||||
|                                             context: context, | ||||
|                                             builder: (BuildContext ctx) { | ||||
|                                               return GeneratedFormModal( | ||||
|                                                   title: tr('addCategory'), | ||||
|                                                   items: [ | ||||
|                                                     [ | ||||
|                                                       GeneratedFormTextField( | ||||
|                                                           'label', | ||||
|                                                           label: tr('label')) | ||||
|                                                     ] | ||||
|                                                   ]); | ||||
|                                             }).then((value) { | ||||
|                                           String? label = value?['label']; | ||||
|                                           if (label != null) { | ||||
|                                             setState(() { | ||||
|                                               categories[label] = | ||||
|                                                   generateRandomLightColor() | ||||
|                                                       .value; | ||||
|                                               settingsProvider.categories = | ||||
|                                                   categories; | ||||
|                                             }); | ||||
|                                           } | ||||
|                                         }); | ||||
|                                       }, | ||||
|                                       icon: const Icon(Icons.add), | ||||
|                                       visualDensity: VisualDensity.compact, | ||||
|                                       tooltip: tr('add'), | ||||
|                                     )) | ||||
|                               ], | ||||
|                             ) | ||||
|                             categoryTagInput | ||||
|                           ], | ||||
|                         ))), | ||||
|           SliverToBoxAdapter( | ||||
| @@ -457,3 +388,17 @@ class _LogsDialogState extends State<LogsDialog> { | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class CategoryEditorSelector extends StatefulWidget { | ||||
|   const CategoryEditorSelector({super.key}); | ||||
|  | ||||
|   @override | ||||
|   State<CategoryEditorSelector> createState() => _CategoryEditorSelectorState(); | ||||
| } | ||||
|  | ||||
| class _CategoryEditorSelectorState extends State<CategoryEditorSelector> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Container(); | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user