diff --git a/assets/img/placeholdersquare.jpg b/assets/img/placeholdersquare.jpg deleted file mode 100644 index ab6d0e3c..00000000 Binary files a/assets/img/placeholdersquare.jpg and /dev/null differ diff --git a/assets/img/placeholdersquare.png b/assets/img/placeholdersquare.png new file mode 100644 index 00000000..c229e435 Binary files /dev/null and b/assets/img/placeholdersquare.png differ diff --git a/lib/file_functions.dart b/lib/file_functions.dart index 09d22470..2709a3de 100644 --- a/lib/file_functions.dart +++ b/lib/file_functions.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'dart:io'; import 'package:cross_file/cross_file.dart'; +import 'package:flutter/foundation.dart'; +import 'package:intl/intl.dart'; import 'package:pso2_mod_manager/mod_classes.dart'; import 'package:pso2_mod_manager/home_page.dart'; import 'package:pso2_mod_manager/mods_loader.dart'; @@ -154,8 +156,11 @@ Future modsToDataAdder(List modList) async { //Applied mods to app list for (var mod in actualAppliedMods) { + DateTime now = DateTime.now(); + String formattedDate = DateFormat('MM-dd-yyyy').format(now); if (appliedModsList.isEmpty) { - appliedModsList.add([mod]); + mod.appliedDate = formattedDate; + appliedModsList.insert(0, [mod]); } else { final tempMods = appliedModsList.firstWhere( (modList) => modList.indexWhere((applied) => applied.iceParent == mod.iceParent && applied.modName == mod.modName) != -1, @@ -166,9 +171,11 @@ Future modsToDataAdder(List modList) async { if (tempMods.isNotEmpty) { tempMods.add(mod); } else { - appliedModsList.add([mod]); + mod.appliedDate = formattedDate; + appliedModsList.insert(0, [mod]); } } + //appliedModsList.sort(((a, b) => a.first.appliedDate.compareTo(b.first.appliedDate))); } allModFiles.map((mod) => mod.toJson()).toList(); @@ -351,7 +358,7 @@ Future dragDropSingleFilesAdd(context, List newItemDragDropList, St List imgList = filesList.where((e) => (p.extension(e.path) == '.jpg' || p.extension(e.path) == '.png') && e.parent.path == file.parent.path).toList(); - ModFile newModFile = ModFile(0, newItemPath, modName, file.path, iceName, iceParents, '', '', getImagesList(imgList), false, true, true); + ModFile newModFile = ModFile('', newItemPath, modName, file.path, iceName, iceParents, '', '', getImagesList(imgList), false, true, true, false, []); newModFile.categoryName = selectedCategoryName.toString(); newModFile.categoryPath = catePath; newModList.add(newModFile); @@ -367,7 +374,7 @@ Future dragDropSingleFilesAdd(context, List newItemDragDropList, St final newModRoot = Directory(newItemPath).listSync(recursive: false).whereType(); final thumbnails = newModRoot.where((e) => p.extension(e.path) == '.jpg' || p.extension(e.path) == '.png').toList(); if (thumbnails.isEmpty) { - thumbnails.add(File('assets/img/placeholdersquare.jpg')); + thumbnails.add(File('assets/img/placeholdersquare.png')); } final selectedCategory = cateList.firstWhere((element) => element.categoryName == categoryName); if (selectedCategory.itemNames.indexWhere((element) => element == modName) == -1) { @@ -542,7 +549,9 @@ Future dragDropFilesAdd(context, List newItemDragDropList, String? List imgList = filesList.where((e) => (p.extension(e.path) == '.jpg' || p.extension(e.path) == '.png') && e.parent.path == file.parent.path).toList(); - ModFile newModFile = ModFile(0, newItemPath, modName, file.path, iceName, iceParents, '', '', getImagesList(imgList), false, true, true); + List vidList = filesList.where((e) => (p.extension(e.path) == '.mp4' || p.extension(e.path) == '.webm') && e.parent.path == file.parent.path).toList(); + + ModFile newModFile = ModFile('', newItemPath, modName, file.path, iceName, iceParents, '', '', getImagesList(imgList), false, true, true, false, vidList); newModFile.categoryName = selectedCategoryName.toString(); newModFile.categoryPath = catePath; newModList.add(newModFile); @@ -558,12 +567,12 @@ Future dragDropFilesAdd(context, List newItemDragDropList, String? final newModRoot = Directory(newItemPath).listSync(recursive: false).whereType(); final thumbnails = newModRoot.where((e) => p.extension(e.path) == '.jpg' || p.extension(e.path) == '.png').toList(); if (thumbnails.isEmpty) { - thumbnails.add(File('assets/img/placeholdersquare.jpg')); + thumbnails.add(File('assets/img/placeholdersquare.png')); } final selectedCategory = cateList.firstWhere((element) => element.categoryName == categoryName); if (selectedCategory.itemNames.indexWhere((element) => element == modName) == -1) { dubItemFound = false; - selectedCategory.itemNames.add(modName); + selectedCategory.itemNames.insert(0, modName); } else { dubItemFound = true; } @@ -573,12 +582,12 @@ Future dragDropFilesAdd(context, List newItemDragDropList, String? if (cate.itemNames.indexWhere((e) => e == modName) != -1) { int index = 0; if (cate.itemNames.length > 1) { - index = cate.itemNames.indexOf(newItemName.toString()); + index = cate.itemNames.indexOf(modName); } cate.allModFiles.addAll(newModList); //cate.allModFiles = []; - cate.imageIcons.add(thumbnails); - cate.numOfMods.add(0); + cate.imageIcons.insert(0, thumbnails); + cate.numOfMods.insert(0, 0); cate.numOfMods[index] = numOfMods; cate.numOfItems++; cate.numOfApplied.add(0); @@ -648,8 +657,9 @@ Future dragDropModsAdd(context, List newModDragDropList, String cur final iceName = file.path.split('\\').last; final iceParents = file.path.split(curItemName).last.split('\\$iceName').first.replaceAll('\\', ' > ').trim(); List imgList = filesList.where((e) => (p.extension(e.path) == '.jpg' || p.extension(e.path) == '.png') && e.parent.path == file.parent.path).toList(); + List vidList = filesList.where((e) => (p.extension(e.path) == '.mp4' || p.extension(e.path) == '.webm') && e.parent.path == file.parent.path).toList(); - ModFile newModFile = ModFile(0, newModPath, curItemName, file.path, iceName, iceParents, '', '', getImagesList(imgList), false, true, true); + ModFile newModFile = ModFile('', newModPath, curItemName, file.path, iceName, iceParents, '', '', getImagesList(imgList), false, true, true, false, vidList); newModFile.categoryName = matchedCategory.categoryName; newModFile.categoryPath = matchedCategory.categoryPath; newMods.add(newModFile); @@ -677,3 +687,120 @@ Future dragDropModsAdd(context, List newModDragDropList, String cur matchedCategory.allModFiles.addAll(newMods); matchedCategory.numOfMods[index] += parents.length; } + +// New Mod Adders Folder Only +Future dragDropModsAddFoldersOnly(context, List newModDragDropList, String curItemName, String itemPath, int index, String? newItemName) async { + for (var xFile in newModDragDropList) { + await Future( + () { + final files = Directory(xFile.path).listSync(recursive: true).whereType(); + if (files.isNotEmpty) { + for (var file in files) { + final fileTailPath = file.path.split('${xFile.name}\\').last.split('\\'); + String newPath = itemPath; + if (fileTailPath.indexWhere((e) => e == 'win32' || e == 'win32_na' || e == 'win32reboot' || e == 'win32reboot_na') != -1) { + fileTailPath.removeRange(fileTailPath.indexWhere((e) => e == 'win32' || e == 'win32_na' || e == 'win32reboot' || e == 'win32reboot_na'), fileTailPath.indexOf(fileTailPath.last)); + String finalTailPath = fileTailPath.join('\\'); + if (newItemName == null) { + newPath += '\\${xFile.name}\\$finalTailPath'; + } + } else { + String finalTailPath = fileTailPath.join('\\'); + if (newItemName == null) { + newPath += '\\${xFile.name}\\$finalTailPath'; + } + } + + File(newPath).createSync(recursive: true); + File(file.path).copySync(newPath); + } + } + + String newModPath = '$itemPath\\${xFile.name}'; + + //Add to list + List newMods = []; + final matchedCategory = cateList.firstWhere((element) => element.itemNames.indexWhere((e) => e == curItemName) != -1); + final filesList = Directory(newModPath).listSync(recursive: true).whereType(); + List parentsList = []; + for (var file in filesList) { + if (p.extension(file.path) == '') { + final iceName = file.path.split('\\').last; + final iceParents = file.path.split(curItemName).last.split('\\$iceName').first.replaceAll('\\', ' > ').trim(); + List imgList = filesList.where((e) => (p.extension(e.path) == '.jpg' || p.extension(e.path) == '.png') && e.parent.path == file.parent.path).toList(); + List vidList = filesList.where((e) => (p.extension(e.path) == '.mp4' || p.extension(e.path) == '.webm') && e.parent.path == file.parent.path).toList(); + + ModFile newModFile = ModFile('', newModPath, curItemName, file.path, iceName, iceParents, '', '', getImagesList(imgList), false, true, true, false, vidList); + newModFile.categoryName = matchedCategory.categoryName; + newModFile.categoryPath = matchedCategory.categoryPath; + newMods.add(newModFile); + parentsList.add(newModFile.iceParent); + + //Json Write + allModFiles.add(newModFile); + allModFiles.map((mod) => mod.toJson()).toList(); + File(modSettingsPath).writeAsStringSync(json.encode(allModFiles)); + } + } + + final parents = parentsList.toSet().toList(); + for (var parent in parents) { + final sameParentMods = newMods.where((element) => element.iceParent == parent); + modFilesList.add(sameParentMods.toList()); + } + + int index = 0; + if (matchedCategory.itemNames.length > 1) { + index = matchedCategory.itemNames.indexOf(curItemName); + } + + isLoading.clear(); + matchedCategory.allModFiles.addAll(newMods); + matchedCategory.numOfMods[index] += parents.length; + }, + ); + Provider.of(context, listen: false).modsDropAddRemoveFirst(); + } +} + +ModCategory addOrRemoveFav(List categoryList, List paramModFileList, ModCategory paramFavCate, bool isAdding) { + ModCategory tempFavCate = paramFavCate; + var curCate = categoryList.singleWhere((element) => element.categoryName == paramModFileList.first.categoryName); + if (isAdding) { + for (var element in paramModFileList) { + element.isFav = true; + tempFavCate.allModFiles.add(element); + } + if (tempFavCate.itemNames.indexWhere((element) => element == paramModFileList.first.modName) == -1) { + tempFavCate.itemNames.insert(0, paramModFileList.first.modName); + tempFavCate.imageIcons.insert(0, curCate.imageIcons[curCate.itemNames.indexOf(paramModFileList.first.modName)]); + tempFavCate.numOfMods.insert(0, 1); + tempFavCate.numOfApplied.insert(0, curCate.numOfApplied[curCate.itemNames.indexOf(paramModFileList.first.modName)]); + tempFavCate.numOfItems++; + } else { + tempFavCate.numOfMods[tempFavCate.itemNames.indexOf(paramModFileList.first.modName)] += 1; + tempFavCate.numOfApplied[tempFavCate.itemNames.indexOf(paramModFileList.first.modName)] = curCate.numOfApplied[curCate.itemNames.indexOf(paramModFileList.first.modName)]; + } + } else { + for (var element in paramModFileList) { + element.isFav = false; + tempFavCate.allModFiles.remove(element); + } + if (isViewingFav) { + modFilesList.remove(paramModFileList); + } + if (tempFavCate.allModFiles.indexWhere((element) => element.modName == paramModFileList.first.modName) == -1) { + tempFavCate.imageIcons.removeAt(tempFavCate.itemNames.indexOf(paramModFileList.first.modName)); + tempFavCate.numOfMods.removeAt(tempFavCate.itemNames.indexOf(paramModFileList.first.modName)); + tempFavCate.numOfApplied.removeAt(tempFavCate.itemNames.indexOf(paramModFileList.first.modName)); + tempFavCate.itemNames.remove(paramModFileList.first.modName); + tempFavCate.numOfItems--; + } + } + + tempFavCate.itemNames.sort(); + allModFiles.map((mod) => mod.toJson()).toList(); + File(modSettingsPath).writeAsStringSync(json.encode(allModFiles)); + + return tempFavCate; +} diff --git a/lib/home_page.dart b/lib/home_page.dart index caf2d3d9..2eba52d3 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -5,9 +5,11 @@ import 'dart:io'; import 'package:carousel_slider/carousel_slider.dart'; import 'package:cross_file/cross_file.dart'; +import 'package:dart_vlc/dart_vlc.dart'; import 'package:desktop_drop/desktop_drop.dart'; import 'package:dropdown_button2/custom_dropdown_button2.dart'; import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:multi_split_view/multi_split_view.dart'; import 'package:provider/provider.dart'; import 'package:pso2_mod_manager/data_loading_page.dart'; @@ -21,6 +23,7 @@ import 'package:pso2_mod_manager/state_provider.dart'; import 'package:url_launcher/url_launcher.dart'; List cateList = []; +List cateListSearchResult = []; Future? modFilesListGet; Future? futureImagesGet; Future? appliedModsListGet; @@ -31,6 +34,10 @@ List modAppliedDup = []; List originalFilesMissingList = []; List backupFilesMissingList = []; List isLoading = []; +bool isModAddFolderOnly = true; +bool isViewingFav = false; +bool isSearching = false; +TextEditingController searchBoxTextController = TextEditingController(); //New Cate bool addCategoryVisible = false; @@ -63,6 +70,12 @@ final List _newModToItemDragDropList = []; int _newModToItemIndex = 0; bool isModAddBtnClicked = false; +//Media Player controls +Player previewPlayer = Player(id: 69, commandlineArguments: ['--no-video-title-show']); +MediaType mediaType = MediaType.file; +CurrentState current = CurrentState(); +List medias = []; + class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @@ -71,18 +84,23 @@ class HomePage extends StatefulWidget { } class _HomePageState extends State with TickerProviderStateMixin { - final MultiSplitViewController _viewsController = MultiSplitViewController(areas: [Area(weight: 0.3), Area(weight: 0.3)]); + final MultiSplitViewController _viewsController = MultiSplitViewController(areas: [Area(weight: 0.285), Area(weight: 0.335)]); + final MultiSplitViewController _viewsControllerNoPreview = MultiSplitViewController(areas: [Area(weight: 0.30), Area(weight: 0.5)]); final MultiSplitViewController _verticalViewsController = MultiSplitViewController(areas: [Area(weight: 0.5)]); String modsViewAppBarName = ''; List selectedIndex = List.generate(cateList.length, (index) => -1); + List searchListSelectedIndex = List.generate(cateListSearchResult.length, (index) => -1); CarouselController imgSliderController = CarouselController(); + List previewImageSliders = []; int modNameCatSelected = -1; bool isModSelected = false; int currentImg = 0; bool isPreviewImgsOn = false; + bool isPreviewVidOn = false; bool modViewExpandAll = false; bool isErrorInSingleItemName = false; + double searchBoxLeftPadding = 80; late AnimationController cateAdderAniController; late Animation cateAdderAniOffset; @@ -111,6 +129,10 @@ class _HomePageState extends State with TickerProviderStateMixin { itemAdderAniController.dispose(); modAdderAniController.dispose(); _itemAdderTabcontroller.dispose(); + previewPlayer.dispose(); + _viewsController.dispose(); + _viewsControllerNoPreview.dispose(); + _verticalViewsController.dispose(); super.dispose(); } @@ -124,7 +146,7 @@ class _HomePageState extends State with TickerProviderStateMixin { MultiSplitView( axis: Axis.vertical, controller: _verticalViewsController, - children: [modPreviewView(), filesView()], + children: context.watch().previewWindowVisible ? [modPreviewView(), filesView()] : [filesView()], ) ], ); @@ -153,10 +175,74 @@ class _HomePageState extends State with TickerProviderStateMixin { return Column( children: [ AppBar( - title: Container(padding: const EdgeInsets.only(bottom: 10), child: const Text('Items')), + title: searchBoxLeftPadding == 15 ? null : Container(padding: const EdgeInsets.only(bottom: 10), child: const Text('Items')), backgroundColor: Theme.of(context).canvasColor, foregroundColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColorDark : Theme.of(context).iconTheme.color, toolbarHeight: 30, + flexibleSpace: Container( + height: 30, + width: double.maxFinite, + padding: EdgeInsets.only(left: searchBoxLeftPadding, right: 100, bottom: 3), + child: Focus( + onFocusChange: (hasFocus) { + setState(() { + if (hasFocus) { + searchBoxLeftPadding = 15; + } else { + if (searchBoxTextController.text.isEmpty) { + searchBoxLeftPadding = 80; + } else { + searchBoxLeftPadding = 15; + } + } + }); + }, + child: TextFormField( + controller: searchBoxTextController, + enableSuggestions: true, + maxLines: 1, + onChanged: (value) { + if (value != '') { + setState(() { + modFilesList.clear(); + modsViewAppBarName = 'Available Mods'; + isSearching = true; + cateListSearchResult = searchFilterResults(cateList, value); + searchListSelectedIndex = List.generate(cateListSearchResult.length, (index) => -1); + }); + } else { + setState(() { + isSearching = false; + modFilesList.clear(); + modsViewAppBarName = 'Available Mods'; + cateListSearchResult = []; + }); + } + }, + decoration: InputDecoration( + contentPadding: const EdgeInsets.only(left: 10, top: 10), + border: const OutlineInputBorder(), + hintText: 'Search', + suffixIcon: searchBoxTextController.text == '' + ? null + : SizedBox( + width: 25, + child: MaterialButton( + onPressed: searchBoxTextController.text == '' + ? null + : (() { + setState(() { + searchBoxTextController.clear(); + modFilesList.clear(); + modsViewAppBarName = 'Available Mods'; + isSearching = false; + searchBoxLeftPadding = 80; + }); + }), + child: const Icon(Icons.clear)), + )), + ), + )), actions: [ Padding( padding: const EdgeInsets.only(right: 2.5), @@ -248,228 +334,766 @@ class _HomePageState extends State with TickerProviderStateMixin { ), //Category List - Expanded( - child: SingleChildScrollView( - controller: AdjustableScrollController(80), - child: ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: cateList.length, - itemBuilder: (context, index) { - return ExpansionTile( - initiallyExpanded: false, - textColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - iconColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - collapsedTextColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - onExpansionChanged: (newState) { - setState(() { - if (!newState) { - selectedIndex = List.filled(cateList.length, -1); - } else { - selectedIndex = List.filled(cateList.length, -1); - } - }); - }, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( + if (!isSearching) + Expanded( + child: SingleChildScrollView( + controller: AdjustableScrollController(80), + child: ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: cateList.length, + itemBuilder: (context, index) { + return AbsorbPointer( + absorbing: isSearching, + child: ExpansionTile( + initiallyExpanded: false, + textColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + iconColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + collapsedTextColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + collapsedIconColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + onExpansionChanged: (newState) { + setState(() { + if (!newState) { + selectedIndex = List.filled(cateList.length, -1); + } else { + selectedIndex = List.filled(cateList.length, -1); + } + }); + }, + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text(cateList[index].categoryName), - Padding( - padding: const EdgeInsets.only(left: 10, top: 3), - child: Container( - padding: const EdgeInsets.only(left: 2, right: 2, bottom: 1), - decoration: BoxDecoration( - border: Border.all(color: Theme.of(context).highlightColor), - borderRadius: const BorderRadius.all(Radius.circular(5.0)), + Row( + children: [ + if (cateList[index].categoryName == 'Favorites') + Text( + cateList[index].categoryName, + style: const TextStyle(fontWeight: FontWeight.w500), ), - child: Text('${cateList[index].numOfItems} Items', - style: const TextStyle( - fontSize: 13, - ))), + if (cateList[index].categoryName != 'Favorites') Text(cateList[index].categoryName), + Padding( + padding: const EdgeInsets.only(left: 10, top: 3), + child: Container( + padding: const EdgeInsets.only(left: 2, right: 2, bottom: 1), + decoration: BoxDecoration( + border: Border.all(color: Theme.of(context).highlightColor), + borderRadius: const BorderRadius.all(Radius.circular(5.0)), + ), + child: Text('${cateList[index].numOfItems} Items', + style: const TextStyle( + fontSize: 13, + ))), + ), + ], ), - ], - ), - Row( - children: [ - Tooltip( - message: 'Remove ${cateList[index].categoryName}', - height: 25, - textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), - waitDuration: const Duration(seconds: 2), - child: SizedBox( - width: 40, - height: 40, - child: MaterialButton( - onPressed: (() { - setState(() { - if (cateList[index].allModFiles.indexWhere((element) => element.isApplied == true) == -1) { - categoryDeleteDialog( - context, - 100, - 'Remove Category', - 'Remove "${cateList[index].categoryName}" and move it to Deleted Items folder?\nThis will also remove all items in this category', - true, - cateList[index].categoryPath, - cateList[index].allModFiles) - .then((_) { + Row( + children: [ + if (cateList[index].categoryName != 'Favorites') + Tooltip( + message: 'Remove ${cateList[index].categoryName}', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: SizedBox( + width: 40, + height: 40, + child: MaterialButton( + onPressed: (() { setState(() { - //setstate to refresh list + if (cateList[index].allModFiles.indexWhere((element) => element.isApplied == true) == -1) { + categoryDeleteDialog( + context, + 100, + 'Remove Category', + 'Remove "${cateList[index].categoryName}" and move it to Deleted Items folder?\nThis will also remove all items in this category', + true, + cateList[index].categoryPath, + cateList[index].allModFiles) + .then((_) { + setState(() { + //setstate to refresh list + }); + }); + } else { + List tempList = cateList[index].allModFiles.where((element) => element.isApplied == true).toList(); + List stillAppliedList = []; + double popupHeight = 40; + for (var element in tempList) { + stillAppliedList.add('${element.modName}${element.iceParent} > ${element.iceName}'); + popupHeight += 24; + } + String stillApplied = stillAppliedList.join('\n'); + categoryDeleteDialog(context, popupHeight, 'Remove Category', + 'Cannot remove "${cateList[index].categoryName}". Unaplly these mods first:\n\n$stillApplied', false, cateList[index].categoryPath, []); + } }); - }); - } else { - List tempList = cateList[index].allModFiles.where((element) => element.isApplied == true).toList(); - List stillAppliedList = []; - double popupHeight = 40; - for (var element in tempList) { - stillAppliedList.add('${element.modName}${element.iceParent} > ${element.iceName}'); - popupHeight += 24; - } - String stillApplied = stillAppliedList.join('\n'); - categoryDeleteDialog(context, popupHeight, 'Remove Category', 'Cannot remove "${cateList[index].categoryName}". Unaplly these mods first:\n\n$stillApplied', - false, cateList[index].categoryPath, []); - } - }); - }), - child: Row( - children: [ - Icon( - Icons.delete_sweep_rounded, - color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - ) - ], + }), + child: Row( + children: [ + Icon( + Icons.delete_sweep_rounded, + color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + ) + ], + )), )), - )), + ], + ) ], - ) - ], - ), - children: [ - for (int i = 0; i < cateList[index].itemNames.length; i++) - Ink( - color: selectedIndex[index] == i ? Theme.of(context).highlightColor : Colors.transparent, - child: ListTile( - leading: cateList[index].imageIcons[i].first.path.split('/').last != 'placeholdersquare.jpg' - ? SizedBox( - width: 50, - height: 50, - child: Image.file( - cateList[index].imageIcons[i].first, - fit: BoxFit.fitWidth, - )) - : SizedBox( - width: 50, - height: 50, - child: Image.asset( - cateList[index].imageIcons[i].first.path, - fit: BoxFit.fitWidth, - )), - title: Text(cateList[index].itemNames[i]), - subtitle: Text('Mods: ${cateList[index].numOfMods[i]} | Applied: ${cateList[index].numOfApplied[i]}'), - trailing: Wrap( - children: [ - if (cateList[index].allModFiles.indexWhere((element) => element.modName == cateList[index].itemNames[i] && element.isNew == true) != -1) - const SizedBox(height: 50, child: Icon(Icons.new_releases, color: Colors.amber)), + ), + children: [ + //Fav list + if (cateList[index].categoryName == 'Favorites') + for (int i = 0; i < cateList[index].itemNames.length; i++) + Ink( + color: selectedIndex[index] == i ? Theme.of(context).highlightColor : Colors.transparent, + child: ListTile( + leading: cateList[index].imageIcons[i].first.path.split('/').last != 'placeholdersquare.png' + ? SizedBox( + width: 50, + height: 50, + child: Image.file( + cateList[index].imageIcons[i].first, + fit: BoxFit.fitWidth, + )) + : SizedBox( + width: 50, + height: 50, + child: Image.asset( + cateList[index].imageIcons[i].first.path, + filterQuality: FilterQuality.none, + fit: BoxFit.fitWidth, + )), + title: Text(cateList[index].itemNames[i]), + subtitle: Text('Mods: ${cateList[index].numOfMods[i]} | Applied: ${cateList[index].numOfApplied[i]}'), + trailing: Wrap( + children: [ + if (cateList[index].allModFiles.indexWhere((element) => element.modName == cateList[index].itemNames[i] && element.isNew == true) != -1) + const SizedBox(height: 50, child: Icon(Icons.new_releases, color: Colors.amber)), - //Buttons - Tooltip( - message: 'Open ${cateList[index].itemNames[i]} in File Explorer', - height: 25, - textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), - waitDuration: const Duration(seconds: 2), - child: SizedBox( - width: 34, - height: 50, - child: MaterialButton( - onPressed: (() async { - await launchUrl(Uri.parse('file:${cateList[index].categoryPath}\\${cateList[index].itemNames[i]}')); - }), - child: Row( - children: const [ - Icon( - Icons.folder_open_rounded, - size: 18, - ) - ], + //Buttons + Tooltip( + message: 'Open ${cateList[index].itemNames[i]} in File Explorer', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: SizedBox( + width: 34, + height: 50, + child: MaterialButton( + onPressed: (() async { + await launchUrl(Uri.parse('file:${cateList[index].categoryPath}\\${cateList[index].itemNames[i]}')); + }), + child: Row( + children: const [ + Icon( + Icons.folder_open_rounded, + size: 18, + ) + ], + )), )), - )), - Tooltip( - message: 'Remove ${cateList[index].itemNames[i]}', - height: 25, - textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), - waitDuration: const Duration(seconds: 2), - child: SizedBox( - width: 36, - height: 50, - child: MaterialButton( - onPressed: (() { - setState(() { - if (cateList[index].allModFiles.indexWhere((element) => element.modName == cateList[index].itemNames[i] && element.isApplied == true) == -1) { - itemDeleteDialog( - context, - 100, - 'Remove Item', - 'Remove "${cateList[index].itemNames[i]}" and move it to Deleted Items folder?\nThis will also remove all mods in this item', - true, - cateList[index], - cateList[index].itemNames[i], - cateList[index].allModFiles) - .then((_) { + if (cateList[index].categoryName == 'Favorites') + SizedBox( + width: 34, + height: 50, + child: Tooltip( + message: 'Remove "${cateList[index].itemNames[i]}" from favorites', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 1), + child: MaterialButton( + onPressed: (() async { + List> modListToRemoveFav = await getModFilesByCategory(cateList[index].allModFiles, cateList[index].itemNames[i]); + for (var element in modListToRemoveFav) { + cateList[index] = addOrRemoveFav(cateList, element, cateList[index], false); + } + setState(() {}); + }), + child: const FaIcon( + FontAwesomeIcons.heartCircleXmark, + size: 17, + //color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).hintColor : Theme.of(context).hintColor, + )), + ), + ), + + // if (cateList[index].categoryName != 'Favorites') + // Tooltip( + // message: 'Remove ${cateList[index].itemNames[i]}', + // height: 25, + // textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + // waitDuration: const Duration(seconds: 2), + // child: SizedBox( + // width: 36, + // height: 50, + // child: MaterialButton( + // onPressed: (() { + // setState(() { + // if (cateList[index].allModFiles.indexWhere((element) => element.modName == cateList[index].itemNames[i] && element.isApplied == true) == -1) { + // itemDeleteDialog( + // context, + // 100, + // 'Remove Item', + // 'Remove "${cateList[index].itemNames[i]}" and move it to Deleted Items folder?\nThis will also remove all mods in this item', + // true, + // cateList[index], + // cateList[index].itemNames[i], + // cateList[index].allModFiles) + // .then((_) { + // setState(() { + // modsViewAppBarName = 'Available Mods'; + // isModSelected = false; + // //setstate + // }); + // }); + // } else { + // List tempList = + // cateList[index].allModFiles.where((element) => element.modName == cateList[index].itemNames[i] && element.isApplied == true).toList(); + // List stillAppliedList = []; + // double popupHeight = 40; + // for (var element in tempList) { + // stillAppliedList.add('${element.modName}${element.iceParent} > ${element.iceName}'); + // popupHeight += 24; + // } + // String stillApplied = stillAppliedList.join('\n'); + // itemDeleteDialog(context, popupHeight, 'Remove Item', 'Cannot remove "${cateList[index].itemNames[i]}". Unaplly these mods first:\n\n$stillApplied', + // false, cateList[index], cateList[index].itemNames[i], []); + // } + // }); + // }), + // child: Row( + // children: const [ + // Icon( + // Icons.delete_forever_outlined, + // size: 20, + // ) + // ], + // )), + // )), + ], + ), + onTap: () { + setState(() { + isViewingFav = true; + isPreviewImgsOn = false; + modFilesListGet = getModFilesByCategory(cateList[index].allModFiles, cateList[index].itemNames[i]); + selectedIndex = List.filled(cateList.length, -1); + selectedIndex[index] = i; + modNameCatSelected = -1; + modsViewAppBarName = cateList[index].itemNames[i]; + _newModToItemIndex = index; + isModSelected = true; + isLoading.clear(); + }); + }, + ), + ), + + //Non fav + if (cateList[index].categoryName != 'Favorites') + for (int i = 0; i < cateList[index].itemNames.length; i++) + Ink( + color: selectedIndex[index] == i ? Theme.of(context).highlightColor : Colors.transparent, + child: ListTile( + leading: cateList[index].imageIcons[i].first.path.split('/').last != 'placeholdersquare.png' + ? SizedBox( + width: 50, + height: 50, + child: Image.file( + cateList[index].imageIcons[i].first, + fit: BoxFit.fitWidth, + )) + : SizedBox( + width: 50, + height: 50, + child: Image.asset( + cateList[index].imageIcons[i].first.path, + filterQuality: FilterQuality.none, + fit: BoxFit.fitWidth, + )), + title: Text(cateList[index].itemNames[i]), + subtitle: Text('Mods: ${cateList[index].numOfMods[i]} | Files applied: ${cateList[index].numOfApplied[i]}'), + trailing: Wrap( + children: [ + if (cateList[index].allModFiles.indexWhere((element) => element.modName == cateList[index].itemNames[i] && element.isNew == true) != -1) + const SizedBox(height: 50, child: Icon(Icons.new_releases, color: Colors.amber)), + + //Buttons + Tooltip( + message: 'Open ${cateList[index].itemNames[i]} in File Explorer', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: SizedBox( + width: 34, + height: 50, + child: MaterialButton( + onPressed: (() async { + await launchUrl(Uri.parse('file:${cateList[index].categoryPath}\\${cateList[index].itemNames[i]}')); + }), + child: Row( + children: const [ + Icon( + Icons.folder_open_rounded, + size: 18, + ) + ], + )), + )), + Tooltip( + message: 'Delete ${cateList[index].itemNames[i]}', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: SizedBox( + width: 36, + height: 50, + child: MaterialButton( + onPressed: (() { setState(() { - modsViewAppBarName = 'Available Mods'; - isModSelected = false; - //setstate + if (cateList[index].allModFiles.indexWhere((element) => element.modName == cateList[index].itemNames[i] && element.isApplied == true) == -1) { + itemDeleteDialog( + context, + 100, + 'Delete Item', + 'Delete "${cateList[index].itemNames[i]}" and move it to \'Deleted\' Items folder?\nThis will also delete all mods in this item', + true, + cateList[index], + cateList[index].itemNames[i], + cateList[index].allModFiles) + .then((_) { + setState(() { + modsViewAppBarName = 'Available Mods'; + isModSelected = false; + //setstate + }); + }); + } else if (cateList[index].allModFiles.indexWhere((element) => element.isFav && element.modName == cateList[index].itemNames[i]) != -1) { + double popupHeight = 40; + itemDeleteDialog(context, popupHeight, 'Delete Item', 'Cannot delete "${cateList[index].itemNames[i]}". Remove from Favorites first', false, + cateList[index], cateList[index].itemNames[i], []); + } else { + List tempList = + cateList[index].allModFiles.where((element) => element.modName == cateList[index].itemNames[i] && element.isApplied == true).toList(); + List stillAppliedList = []; + double popupHeight = 40; + for (var element in tempList) { + stillAppliedList.add('${element.modName}${element.iceParent} > ${element.iceName}'); + popupHeight += 24; + } + String stillApplied = stillAppliedList.join('\n'); + itemDeleteDialog(context, popupHeight, 'Delete Item', 'Cannot delete "${cateList[index].itemNames[i]}". Unaplly these mods first:\n\n$stillApplied', + false, cateList[index], cateList[index].itemNames[i], []); + } }); - }); - } else { - List tempList = - cateList[index].allModFiles.where((element) => element.modName == cateList[index].itemNames[i] && element.isApplied == true).toList(); - List stillAppliedList = []; - double popupHeight = 40; - for (var element in tempList) { - stillAppliedList.add('${element.modName}${element.iceParent} > ${element.iceName}'); - popupHeight += 24; - } - String stillApplied = stillAppliedList.join('\n'); - itemDeleteDialog(context, popupHeight, 'Remove Item', 'Cannot remove "${cateList[index].itemNames[i]}". Unaplly these mods first:\n\n$stillApplied', - false, cateList[index], cateList[index].itemNames[i], []); - } - }); - }), - child: Row( - children: const [ - Icon( - Icons.delete_forever_outlined, - size: 20, - ) - ], + }), + child: Row( + children: const [ + Icon( + Icons.delete_forever_outlined, + size: 20, + ) + ], + )), )), - )), + ], + ), + onTap: () { + setState(() { + isViewingFav = false; + isPreviewImgsOn = false; + modFilesListGet = getModFilesByCategory(cateList[index].allModFiles, cateList[index].itemNames[i]); + selectedIndex = List.filled(cateList.length, -1); + selectedIndex[index] = i; + modNameCatSelected = -1; + modsViewAppBarName = cateList[index].itemNames[i]; + _newModToItemIndex = index; + isModSelected = true; + isLoading.clear(); + }); + }, + ), + ) + ], + ), + ); + }, + ), + ), + ), + + //Search Result Category List + if (isSearching && cateListSearchResult.isEmpty) + const Expanded( + child: Padding( + padding: EdgeInsets.only(top: 5.0), + child: Text('No Results Found'), + )), + if (isSearching && cateListSearchResult.isNotEmpty) + Expanded( + child: SingleChildScrollView( + controller: AdjustableScrollController(80), + child: AbsorbPointer( + absorbing: !isSearching, + child: ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: cateListSearchResult.length, + itemBuilder: (context, index) { + return ExpansionTile( + initiallyExpanded: false, + textColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + iconColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + collapsedTextColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + collapsedIconColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + onExpansionChanged: (newState) { + setState(() { + if (!newState) { + searchListSelectedIndex = List.filled(cateListSearchResult.length, -1); + } else { + searchListSelectedIndex = List.filled(cateListSearchResult.length, -1); + } + }); + }, + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + children: [ + if (cateListSearchResult[index].categoryName == 'Favorites') + Text( + cateListSearchResult[index].categoryName, + style: const TextStyle(fontWeight: FontWeight.w500), + ), + if (cateListSearchResult[index].categoryName != 'Favorites') Text(cateListSearchResult[index].categoryName), + Padding( + padding: const EdgeInsets.only(left: 10, top: 3), + child: Container( + padding: const EdgeInsets.only(left: 2, right: 2, bottom: 1), + decoration: BoxDecoration( + border: Border.all(color: Theme.of(context).highlightColor), + borderRadius: const BorderRadius.all(Radius.circular(5.0)), + ), + child: Text('${cateListSearchResult[index].numOfItems} Items', + style: const TextStyle( + fontSize: 13, + ))), + ), ], ), - onTap: () { - setState(() { - isPreviewImgsOn = false; - modFilesListGet = getModFilesByCategory(cateList[index].allModFiles, cateList[index].itemNames[i]); - selectedIndex = List.filled(cateList.length, -1); - selectedIndex[index] = i; - modNameCatSelected = -1; - modsViewAppBarName = cateList[index].itemNames[i]; - _newModToItemIndex = index; - isModSelected = true; - isLoading.clear(); - }); - }, - ), - ) - ], - ); - }, + // Row( + // children: [ + // if (cateListSearchResult[index].categoryName != 'Favorites') + // Tooltip( + // message: 'Remove ${cateListSearchResult[index].categoryName}', + // height: 25, + // textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + // waitDuration: const Duration(seconds: 2), + // child: SizedBox( + // width: 40, + // height: 40, + // child: MaterialButton( + // onPressed: (() { + // setState(() { + // if (cateListSearchResult[index].allModFiles.indexWhere((element) => element.isApplied == true) == -1) { + // categoryDeleteDialog( + // context, + // 100, + // 'Remove Category', + // 'Remove "${cateListSearchResult[index].categoryName}" and move it to Deleted Items folder?\nThis will also remove all items in this category', + // true, + // cateListSearchResult[index].categoryPath, + // cateListSearchResult[index].allModFiles) + // .then((_) { + // setState(() { + // //setstate to refresh list + // }); + // }); + // } else { + // List tempList = cateListSearchResult[index].allModFiles.where((element) => element.isApplied == true).toList(); + // List stillAppliedList = []; + // double popupHeight = 40; + // for (var element in tempList) { + // stillAppliedList.add('${element.modName}${element.iceParent} > ${element.iceName}'); + // popupHeight += 24; + // } + // String stillApplied = stillAppliedList.join('\n'); + // categoryDeleteDialog( + // context, + // popupHeight, + // 'Remove Category', + // 'Cannot remove "${cateListSearchResult[index].categoryName}". Unaplly these mods first:\n\n$stillApplied', + // false, + // cateListSearchResult[index].categoryPath, []); + // } + // }); + // }), + // child: Row( + // children: [ + // Icon( + // Icons.delete_sweep_rounded, + // color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + // ) + // ], + // )), + // )), + // ], + // ) + ], + ), + children: [ + //Fav list + if (cateListSearchResult[index].categoryName == 'Favorites') + for (int i = 0; i < cateListSearchResult[index].itemNames.length; i++) + Ink( + color: searchListSelectedIndex[index] == i ? Theme.of(context).highlightColor : Colors.transparent, + child: ListTile( + leading: cateListSearchResult[index].imageIcons[i].first.path.split('/').last != 'placeholdersquare.png' + ? SizedBox( + width: 50, + height: 50, + child: Image.file( + cateListSearchResult[index].imageIcons[i].first, + fit: BoxFit.fitWidth, + )) + : SizedBox( + width: 50, + height: 50, + child: Image.asset( + cateListSearchResult[index].imageIcons[i].first.path, + filterQuality: FilterQuality.none, + fit: BoxFit.fitWidth, + )), + title: Text(cateListSearchResult[index].itemNames[i]), + subtitle: Text('Mods: ${cateListSearchResult[index].numOfMods[i]} | Applied: ${cateListSearchResult[index].numOfApplied[i]}'), + trailing: Wrap( + children: [ + if (cateListSearchResult[index].allModFiles.indexWhere((element) => element.modName == cateListSearchResult[index].itemNames[i] && element.isNew == true) != -1) + const SizedBox(height: 50, child: Icon(Icons.new_releases, color: Colors.amber)), + + //Buttons + Tooltip( + message: 'Open ${cateListSearchResult[index].itemNames[i]} in File Explorer', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: SizedBox( + width: 34, + height: 50, + child: MaterialButton( + onPressed: (() async { + await launchUrl(Uri.parse('file:${cateListSearchResult[index].categoryPath}\\${cateListSearchResult[index].itemNames[i]}')); + }), + child: Row( + children: const [ + Icon( + Icons.folder_open_rounded, + size: 18, + ) + ], + )), + )), + if (cateListSearchResult[index].categoryName == 'Favorites') + SizedBox( + width: 34, + height: 50, + child: Tooltip( + message: 'Remove "${cateListSearchResult[index].itemNames[i]}" from favorites', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 1), + child: MaterialButton( + onPressed: (() async { + List> modListToRemoveFav = await getModFilesByCategory(cateListSearchResult[index].allModFiles, cateListSearchResult[index].itemNames[i]); + for (var element in modListToRemoveFav) { + cateListSearchResult[index] = addOrRemoveFav(cateListSearchResult, element, cateListSearchResult[index], false); + } + setState(() {}); + }), + child: const FaIcon( + FontAwesomeIcons.heartCircleXmark, + size: 17, + //color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).hintColor : Theme.of(context).hintColor, + )), + ), + ), + ], + ), + onTap: () { + setState(() { + isViewingFav = true; + isPreviewImgsOn = false; + modFilesListGet = getModFilesByCategory(cateListSearchResult[index].allModFiles, cateListSearchResult[index].itemNames[i]); + searchListSelectedIndex = List.filled(cateListSearchResult.length, -1); + searchListSelectedIndex[index] = i; + modNameCatSelected = -1; + modsViewAppBarName = cateListSearchResult[index].itemNames[i]; + _newModToItemIndex = index; + isModSelected = true; + isLoading.clear(); + }); + }, + ), + ), + + //Non fav + if (cateListSearchResult[index].categoryName != 'Favorites') + for (int i = 0; i < cateListSearchResult[index].itemNames.length; i++) + Ink( + color: searchListSelectedIndex[index] == i ? Theme.of(context).highlightColor : Colors.transparent, + child: ListTile( + leading: cateListSearchResult[index].imageIcons[i].first.path.split('/').last != 'placeholdersquare.png' + ? SizedBox( + width: 50, + height: 50, + child: Image.file( + cateListSearchResult[index].imageIcons[i].first, + fit: BoxFit.fitWidth, + )) + : SizedBox( + width: 50, + height: 50, + child: Image.asset( + cateListSearchResult[index].imageIcons[i].first.path, + filterQuality: FilterQuality.none, + fit: BoxFit.fitWidth, + )), + title: Text(cateListSearchResult[index].itemNames[i]), + subtitle: Text('Mods: ${cateListSearchResult[index].numOfMods[i]} | Files applied: ${cateListSearchResult[index].numOfApplied[i]}'), + trailing: Wrap( + children: [ + if (cateListSearchResult[index].allModFiles.indexWhere((element) => element.modName == cateListSearchResult[index].itemNames[i] && element.isNew == true) != -1) + const SizedBox(height: 50, child: Icon(Icons.new_releases, color: Colors.amber)), + + //Buttons + Tooltip( + message: 'Open ${cateListSearchResult[index].itemNames[i]} in File Explorer', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: SizedBox( + width: 34, + height: 50, + child: MaterialButton( + onPressed: (() async { + await launchUrl(Uri.parse('file:${cateListSearchResult[index].categoryPath}\\${cateListSearchResult[index].itemNames[i]}')); + }), + child: Row( + children: const [ + Icon( + Icons.folder_open_rounded, + size: 18, + ) + ], + )), + )), + Tooltip( + message: 'Delete ${cateListSearchResult[index].itemNames[i]}', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: SizedBox( + width: 36, + height: 50, + child: MaterialButton( + onPressed: (() { + setState(() { + if (cateListSearchResult[index] + .allModFiles + .indexWhere((element) => element.modName == cateListSearchResult[index].itemNames[i] && element.isApplied == true) == + -1) { + ModCategory curCate = cateList.firstWhere((element) => element.categoryName == cateListSearchResult[index].categoryName); + String curItem = cateListSearchResult[index].itemNames[i]; + itemDeleteDialog( + context, + 100, + 'Delete Item', + 'Delete "${cateListSearchResult[index].itemNames[i]}" and move it to \'Deleted\' Items folder?\nThis will also delete all mods in this item', + true, + cateListSearchResult[index], + cateListSearchResult[index].itemNames[i], + cateListSearchResult[index].allModFiles) + .then((_) { + //Remove from normal Item List + + curCate.imageIcons.removeAt(curCate.itemNames.indexOf(curItem)); + curCate.numOfMods.removeAt(curCate.itemNames.indexWhere((element) => element == curItem)); + curCate.itemNames.removeWhere((element) => element == curItem); + curCate.allModFiles.removeWhere((element) => element.modName == curItem); + curCate.numOfItems--; + setState(() { + modsViewAppBarName = 'Available Mods'; + isModSelected = false; + //setstate + }); + }); + } else if (cateListSearchResult[index] + .allModFiles + .indexWhere((element) => element.isFav && element.modName == cateListSearchResult[index].itemNames[i]) != + -1) { + double popupHeight = 40; + itemDeleteDialog(context, popupHeight, 'Delete Item', 'Cannot delete "${cateListSearchResult[index].itemNames[i]}". Remove from Favorites first', + false, cateListSearchResult[index], cateListSearchResult[index].itemNames[i], []); + } else { + List tempList = cateListSearchResult[index] + .allModFiles + .where((element) => element.modName == cateListSearchResult[index].itemNames[i] && element.isApplied == true) + .toList(); + List stillAppliedList = []; + double popupHeight = 40; + for (var element in tempList) { + stillAppliedList.add('${element.modName}${element.iceParent} > ${element.iceName}'); + popupHeight += 24; + } + String stillApplied = stillAppliedList.join('\n'); + itemDeleteDialog( + context, + popupHeight, + 'Delete Item', + 'Cannot delete "${cateListSearchResult[index].itemNames[i]}". Unaplly these mods first:\n\n$stillApplied', + false, + cateListSearchResult[index], + cateListSearchResult[index].itemNames[i], []); + } + }); + }), + child: Row( + children: const [ + Icon( + Icons.delete_forever_outlined, + size: 20, + ) + ], + )), + )), + ], + ), + onTap: () { + setState(() { + isViewingFav = false; + isPreviewImgsOn = false; + modFilesListGet = getModFilesByCategory(cateListSearchResult[index].allModFiles, cateListSearchResult[index].itemNames[i]); + searchListSelectedIndex = List.filled(cateListSearchResult.length, -1); + searchListSelectedIndex[index] = i; + modNameCatSelected = -1; + modsViewAppBarName = cateListSearchResult[index].itemNames[i]; + _newModToItemIndex = index; + isModSelected = true; + isLoading.clear(); + }); + }, + ), + ) + ], + ); + }, + ), + ), ), ), - ), //Add Category Panel if (addCategoryVisible) @@ -671,7 +1295,14 @@ class _HomePageState extends State with TickerProviderStateMixin { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (_newSingleItemDragDropList.isEmpty) const Center(child: Text("Drop Modded .ice Files And Folder(s) Here To Add")), + if (_newSingleItemDragDropList.isEmpty) + Center( + child: Column( + children: const [ + Text("Drop Modded .ice Files And Folder(s)"), + Text('Here To Add'), + ], + )), if (_newSingleItemDragDropList.isNotEmpty) Expanded( child: SingleChildScrollView( @@ -746,9 +1377,23 @@ class _HomePageState extends State with TickerProviderStateMixin { isErrorInSingleItemName = true; return 'Name can\'t be empty'; } - if (cateList.indexWhere((e) => e.categoryName == selectedCategoryForSingleItem && e.itemNames.indexWhere((element) => element == value) != -1) != -1) { - isErrorInSingleItemName = true; - return 'The name already exist'; + if (selectedCategoryForSingleItem == 'Basewears' || + selectedCategoryForSingleItem == 'Setwears' || + selectedCategoryForSingleItem == 'Outerewears' || + selectedCategoryForSingleItem == 'Innerwears') { + if (cateList.indexWhere((e) => + e.categoryName == selectedCategoryForSingleItem && e.itemNames.indexWhere((element) => element.toLowerCase().contains(value.toLowerCase())) != -1) != + -1) { + isErrorInSingleItemName = true; + return 'The name already exist'; + } + } else { + if (cateList.indexWhere( + (e) => e.categoryName == selectedCategoryForSingleItem && e.itemNames.indexWhere((element) => element.toLowerCase() == value.toLowerCase()) != -1) != + -1) { + isErrorInSingleItemName = true; + return 'The name already exist'; + } } return null; }, @@ -840,7 +1485,10 @@ class _HomePageState extends State with TickerProviderStateMixin { width: double.infinity, child: Padding( padding: const EdgeInsets.symmetric(vertical: 5), - child: Text(context.watch().newItemDropDisplay), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 5), + child: Text(context.watch().newItemDropDisplay), + ), )), ), ), @@ -1085,7 +1733,7 @@ class _HomePageState extends State with TickerProviderStateMixin { AsyncSnapshot snapshot, ) { if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); + return const CircularProgressIndicator(); } else { if (snapshot.hasError) { return const Text('Error'); @@ -1108,80 +1756,345 @@ class _HomePageState extends State with TickerProviderStateMixin { onHover: (value) { if (value) { setState(() { - isPreviewImgsOn = true; - futureImagesGet = modFilesList[index].first.images; + if (modFilesList[index].first.images != null) { + isPreviewImgsOn = true; + futureImagesGet = modFilesList[index].first.images; + } + //print(modFilesList[index].first.previewVids!.length); + if (modFilesList[index].first.previewVids!.isNotEmpty) { + isPreviewVidOn = true; + isPreviewImgsOn = false; + previewPlayer.setVolume(0.0); + bool itemFound = false; + for (var vid in modFilesList[index].first.previewVids!) { + if (medias.contains(Media.file(vid))) { + itemFound = true; + } else { + medias.clear(); + } + } + + if (medias.isEmpty || !itemFound) { + for (var vid in modFilesList[index].first.previewVids!) { + medias.add(Media.file(vid)); + } + previewPlayer.open(Playlist(medias: medias, playlistMode: PlaylistMode.single), autoStart: true); + } else { + previewPlayer.bufferingProgressController.done; + previewPlayer.play(); + } + } + }); + } else { + setState(() { + isPreviewImgsOn = false; + isPreviewVidOn = false; + previewPlayer.pause(); }); } - // else { - // setState(() { - // isPreviewImgsOn = false; - // }); - // } }, - child: Card( - shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all(Radius.circular(5.0)), - side: BorderSide(width: 1, color: modFilesList[index].indexWhere((e) => e.isNew == true) != -1 ? Colors.amber : Theme.of(context).primaryColorLight)), - child: ExpansionTile( - initiallyExpanded: modViewExpandAll, - textColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - iconColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - collapsedTextColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Text(modFilesList[index].first.iceParent), - ), - //if (modFilesList[index].length > 1) - Row( - children: [ - if (isLoading[index]) - const SizedBox( - width: 40, - height: 40, - child: CircularProgressIndicator(), - ), - //Buttons - if (modFilesList[index].length > 1 && modFilesList[index].indexWhere((element) => element.isApplied == true) != -1 && !isLoading[index]) + child: GestureDetector( + onSecondaryTap: () => previewImageSliders.isNotEmpty ? pictureDialog(context, previewImageSliders) : null, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(5.0)), + side: BorderSide(width: 1, color: modFilesList[index].indexWhere((e) => e.isNew == true) != -1 ? Colors.amber : Theme.of(context).primaryColor)), + child: ExpansionTile( + initiallyExpanded: modViewExpandAll, + textColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + iconColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + collapsedTextColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text(modFilesList[index].first.iceParent), + ), + //if (modFilesList[index].length > 1) + Row( + children: [ + //Buttons SizedBox( width: 40, height: 40, child: Tooltip( - message: 'Remove all mods under "$modsViewAppBarName ${modFilesList[index].first.iceParent}" from the game', + message: modFilesList[index].first.isFav + ? 'Remove "$modsViewAppBarName ${modFilesList[index].first.iceParent}" to favorites' + : 'Add "$modsViewAppBarName ${modFilesList[index].first.iceParent}" to favorites', height: 25, textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), waitDuration: const Duration(seconds: 1), child: MaterialButton( onPressed: (() { setState(() { - modsRemover(modFilesList[index].toList()); + var favCate = cateList.singleWhere((element) => element.categoryName == 'Favorites'); + if (modFilesList[index].first.isFav) { + favCate = addOrRemoveFav(cateList, modFilesList[index], favCate, false); + } else { + favCate = addOrRemoveFav(cateList, modFilesList[index], favCate, true); + } }); }), - child: Icon( - Icons.playlist_remove, - color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - ), + child: modFilesList[index].first.isFav + ? FaIcon( + FontAwesomeIcons.heartCircleMinus, + size: 19, + color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).hintColor : Theme.of(context).hintColor, + ) + : FaIcon( + FontAwesomeIcons.heartCirclePlus, + size: 19, + color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + ), ), ), ), - if (modFilesList[index].length > 1 && modFilesList[index].indexWhere((element) => element.isApplied == false) != -1 && !isLoading[index]) - SizedBox( - width: 40, - height: 40, - child: Tooltip( - message: 'Apply mods under ${modFilesList[index].first.iceParent} to the game', - height: 25, - textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), - waitDuration: const Duration(seconds: 1), - child: MaterialButton( - onPressed: (() { - setState(() { - isLoading[index] = true; - modsToDataAdder(modFilesList[index]).then((_) { + //loading && add + if (isLoading[index]) + const SizedBox( + width: 40, + height: 40, + child: CircularProgressIndicator(), + ), + + //if (modFilesList[index].length > 1 && modFilesList[index].indexWhere((element) => element.isApplied == true) != -1 && !isLoading[index]) + if (modFilesList[index].indexWhere((element) => element.isApplied == true) != -1 && !isLoading[index]) + SizedBox( + width: 40, + height: 40, + child: Tooltip( + message: 'Remove all mods under "$modsViewAppBarName ${modFilesList[index].first.iceParent}" from the game', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 1), + child: MaterialButton( + onPressed: (() { + setState(() { + modsRemover(modFilesList[index].toList()); + }); + }), + child: Icon( + Icons.playlist_remove, + color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + ), + ), + ), + ), + //if (modFilesList[index].length > 1 && modFilesList[index].indexWhere((element) => element.isApplied == false) != -1 && !isLoading[index]) + if (modFilesList[index].indexWhere((element) => element.isApplied == false) != -1 && !isLoading[index]) + SizedBox( + width: 40, + height: 40, + child: Tooltip( + message: 'Apply mods under ${modFilesList[index].first.iceParent} to the game', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 1), + child: MaterialButton( + onPressed: (() { + setState(() { + isLoading[index] = true; + modsToDataAdder(modFilesList[index]).then((_) { + setState(() { + isLoading[index] = false; + //Messages + if (originalFilesMissingList.isNotEmpty) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + duration: const Duration(seconds: 2), + //backgroundColor: Theme.of(context).focusColor, + content: SizedBox( + height: originalFilesMissingList.length * 20, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (int i = 0; i < originalFilesMissingList.length; i++) + Text( + 'Original file of "${originalFilesMissingList[i].modName} ${originalFilesMissingList[i].iceParent} > ${originalFilesMissingList[i].iceName}" is not found'), + ], + ), + ))); + } + + if (modAppliedDup.isNotEmpty) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + duration: Duration(seconds: modAppliedDup.length), + //backgroundColor: Theme.of(context).focusColor, + content: SizedBox( + height: modAppliedDup.length * 20, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (int i = 0; i < modAppliedDup.length; i++) + Text( + 'Replaced: ${modAppliedDup[i].categoryName} > ${modAppliedDup[i].modName} ${modAppliedDup[i].iceParent} > ${modAppliedDup[i].iceName}'), + ], + ), + ))); + modAppliedDup.clear(); + } + }); + }); + }); + }), + child: Icon( + Icons.playlist_add, + color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + ), + ), + ), + ), + if (!isViewingFav) + Tooltip( + message: 'Delete $modsViewAppBarName ${modFilesList[index].first.iceParent}', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: SizedBox( + width: 36, + height: 40, + child: MaterialButton( + onPressed: (() { + setState(() { + if (modFilesList[index].indexWhere((element) => element.isApplied == true) == -1) { + modDeleteDialog( + context, + 100, + 'Delete Mods', + 'Delete "$modsViewAppBarName ${modFilesList[index].first.iceParent}" and move it to \'Deleted Items\' folder?\nThis will also delete all files in this mod', + true, + modFilesList[index].first.modPath, + modFilesList[index].first.iceParent, + modFilesList[index].first.modName, + modFilesList[index]) + .then((_) { + setState(() { + //setstate to refresh list + }); + }); + } else if (modFilesList[index].first.isFav) { + double popupHeight = 40; + modDeleteDialog( + context, + popupHeight, + 'Delete Mod', + 'Cannot delete "$modsViewAppBarName ${modFilesList[index].first.iceParent}". Remove from Favorites first', + false, + modFilesList[index].first.modPath, + modFilesList[index].first.iceParent, + modFilesList[index].first.modName, []); + } else { + List tempList = cateList[index] + .allModFiles + .where((element) => element.modName == modFilesList[index].first.modName && element.isApplied == true) + .toList(); + List stillAppliedList = []; + double popupHeight = 40; + for (var element in tempList) { + stillAppliedList.add('${element.modName}${element.iceParent} > ${element.iceName}'); + popupHeight += 24; + } + String stillApplied = stillAppliedList.join('\n'); + modDeleteDialog( + context, + popupHeight, + 'Delete Mod', + 'Cannot delete "$modsViewAppBarName ${modFilesList[index].first.iceParent}". Unaplly these files first:\n\n$stillApplied', + false, + modFilesList[index].first.modPath, + modFilesList[index].first.iceParent, + modFilesList[index].first.modName, []); + } + }); + }), + child: Row( + children: [ + Icon( + Icons.delete_rounded, + size: 20, + color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + ) + ], + )), + )), + ], + ) + ], + ), + children: [ + for (int i = 0; i < modFilesList[index].length; i++) + InkWell( + // onHover: (value) { + // if (value && + // modPreviewImgList.indexWhere((e) => + // e.path.contains( + // modFilesList[ + // index] + // .first + // .iceParent)) == + // -1) { + // setState(() { + // isPreviewImgsOn = true; + // futureImagesGet = + // modFilesList[index] + // [i] + // .images; + // }); + // } + // }, + child: ListTile( + leading: modFilesList[index][i].isNew == true + ? const Icon( + Icons.new_releases, + color: Colors.amber, + ) + : null, + title: Text(modFilesList[index][i].iceName), + //subtitle: Text(modFilesList[index][i].icePath), + minLeadingWidth: 10, + trailing: SizedBox( + width: 40, + height: 40, + child: modFilesList[index][i].isApplied + ? Tooltip( + message: 'Remove this mod from the game', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: MaterialButton( + onPressed: (() { setState(() { - isLoading[index] = false; - //Messages + modsRemover([modFilesList[index][i]]); + //appliedModsList.remove(modFilesList[index]); + if (backupFilesMissingList.isNotEmpty) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + duration: const Duration(seconds: 2), + //backgroundColor: Theme.of(context).focusColor, + content: SizedBox( + height: backupFilesMissingList.length * 20, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (int i = 0; i < backupFilesMissingList.length; i++) + Text( + 'Backup file of "${backupFilesMissingList[i].modName} ${backupFilesMissingList[i].iceParent} > ${backupFilesMissingList[i].iceName}" is not found'), + ], + ), + ))); + } + }); + }), + child: const Icon(Icons.replay), + )) + : Tooltip( + message: 'Apply this mod to the game', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: MaterialButton( + onPressed: (() { + setState(() { + modsToDataAdder([modFilesList[index][i]]); + //appliedModsList.add(modFilesList[index]); if (originalFilesMissingList.isNotEmpty) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( duration: const Duration(seconds: 2), @@ -1214,203 +2127,19 @@ class _HomePageState extends State with TickerProviderStateMixin { ], ), ))); - modAppliedDup.clear(); } + + modAppliedDup.clear(); }); - }); - }); - }), - child: Icon( - Icons.playlist_add, - color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + }), + child: const Icon(Icons.add_to_drive), + ), ), - ), - ), - ), - Tooltip( - message: 'Remove $modsViewAppBarName ${modFilesList[index].first.iceParent}', - height: 25, - textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), - waitDuration: const Duration(seconds: 2), - child: SizedBox( - width: 36, - height: 40, - child: MaterialButton( - onPressed: (() { - setState(() { - if (modFilesList[index].indexWhere((element) => element.isApplied == true) == -1) { - modDeleteDialog( - context, - 100, - 'Remove Mods', - 'Remove "$modsViewAppBarName ${modFilesList[index].first.iceParent}" and move it to Deleted Items folder?\nThis will also remove all filess in this mod', - true, - modFilesList[index].first.modPath, - modFilesList[index].first.iceParent, - modFilesList[index].first.modName, - modFilesList[index]) - .then((_) { - setState(() { - //setstate to refresh list - }); - }); - } else { - List tempList = cateList[index] - .allModFiles - .where((element) => element.modName == modFilesList[index].first.modName && element.isApplied == true) - .toList(); - List stillAppliedList = []; - double popupHeight = 40; - for (var element in tempList) { - stillAppliedList.add('${element.modName}${element.iceParent} > ${element.iceName}'); - popupHeight += 24; - } - String stillApplied = stillAppliedList.join('\n'); - modDeleteDialog( - context, - popupHeight, - 'Remove Mod', - 'Cannot remove "$modsViewAppBarName ${modFilesList[index].first.iceParent}". Unaplly these files first:\n\n$stillApplied', - false, - modFilesList[index].first.modPath, - modFilesList[index].first.iceParent, - modFilesList[index].first.modName, []); - } - }); - }), - child: Row( - children: [ - Icon( - Icons.delete_rounded, - size: 20, - color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - ) - ], - )), - )), - ], - ) + ), + )) ], - ), - children: [ - for (int i = 0; i < modFilesList[index].length; i++) - InkWell( - // onHover: (value) { - // if (value && - // modPreviewImgList.indexWhere((e) => - // e.path.contains( - // modFilesList[ - // index] - // .first - // .iceParent)) == - // -1) { - // setState(() { - // isPreviewImgsOn = true; - // futureImagesGet = - // modFilesList[index] - // [i] - // .images; - // }); - // } - // }, - child: ListTile( - leading: modFilesList[index][i].isNew == true - ? const Icon( - Icons.new_releases, - color: Colors.amber, - ) - : null, - title: Text(modFilesList[index][i].iceName), - //subtitle: Text(modFilesList[index][i].icePath), - minLeadingWidth: 10, - trailing: SizedBox( - width: 40, - height: 40, - child: modFilesList[index][i].isApplied - ? Tooltip( - message: 'Remove this mod from the game', - height: 25, - textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), - waitDuration: const Duration(seconds: 2), - child: MaterialButton( - onPressed: (() { - setState(() { - modsRemover([modFilesList[index][i]]); - //appliedModsList.remove(modFilesList[index]); - if (backupFilesMissingList.isNotEmpty) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - duration: const Duration(seconds: 2), - //backgroundColor: Theme.of(context).focusColor, - content: SizedBox( - height: backupFilesMissingList.length * 20, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - for (int i = 0; i < backupFilesMissingList.length; i++) - Text( - 'Backup file of "${backupFilesMissingList[i].modName} ${backupFilesMissingList[i].iceParent} > ${backupFilesMissingList[i].iceName}" is not found'), - ], - ), - ))); - } - }); - }), - child: const Icon(Icons.replay), - )) - : Tooltip( - message: 'Apply this mod to the game', - height: 25, - textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), - waitDuration: const Duration(seconds: 2), - child: MaterialButton( - onPressed: (() { - setState(() { - modsToDataAdder([modFilesList[index][i]]); - //appliedModsList.add(modFilesList[index]); - if (originalFilesMissingList.isNotEmpty) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - duration: const Duration(seconds: 2), - //backgroundColor: Theme.of(context).focusColor, - content: SizedBox( - height: originalFilesMissingList.length * 20, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - for (int i = 0; i < originalFilesMissingList.length; i++) - Text( - 'Original file of "${originalFilesMissingList[i].modName} ${originalFilesMissingList[i].iceParent} > ${originalFilesMissingList[i].iceName}" is not found'), - ], - ), - ))); - } - - if (modAppliedDup.isNotEmpty) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - duration: Duration(seconds: modAppliedDup.length), - //backgroundColor: Theme.of(context).focusColor, - content: SizedBox( - height: modAppliedDup.length * 20, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - for (int i = 0; i < modAppliedDup.length; i++) - Text( - 'Replaced: ${modAppliedDup[i].categoryName} > ${modAppliedDup[i].modName} ${modAppliedDup[i].iceParent} > ${modAppliedDup[i].iceName}'), - ], - ), - ))); - } - - modAppliedDup.clear(); - }); - }), - child: const Icon(Icons.add_to_drive), - ), - ), - ), - )) - ], - ))); + )), + )); })); } } @@ -1444,6 +2173,12 @@ class _HomePageState extends State with TickerProviderStateMixin { detail.files.sort(((a, b) => a.name.compareTo(b.name))); _newModToItemDragDropList.addAll(detail.files); context.read().modsDropAdd(detail.files); + for (var element in detail.files) { + if (!Directory(element.path).existsSync()) { + isModAddFolderOnly = false; + break; + } + } }); }, onDragEntered: (detail) { @@ -1473,7 +2208,12 @@ class _HomePageState extends State with TickerProviderStateMixin { child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.only(right: 10), - child: SizedBox(width: double.infinity, child: Text(' ${context.watch().newModDropDisplay}')), + child: SizedBox( + width: double.infinity, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 5), + child: Text(context.watch().newModDropDisplay), + )), ), ), ) @@ -1490,6 +2230,7 @@ class _HomePageState extends State with TickerProviderStateMixin { child: Padding( padding: const EdgeInsets.only(top: 10, left: 10, right: 10), child: TextFormField( + enabled: !isModAddFolderOnly, controller: newModToItemAddController, //maxLengthEnforcement: MaxLengthEnforcement.enforced, //maxLength: 100, @@ -1500,7 +2241,7 @@ class _HomePageState extends State with TickerProviderStateMixin { isDense: true, ), validator: (value) { - if (value == null || value.isEmpty) { + if (!isModAddFolderOnly && (value == null || value.isEmpty)) { return 'Mod name can\'t be empty'; } if (modFilesList.indexWhere((e) => e.indexWhere((element) => element.iceParent.split(' > ').last == value) != -1) != -1) { @@ -1538,6 +2279,7 @@ class _HomePageState extends State with TickerProviderStateMixin { setState(() { _newModToItemDragDropList.clear(); newModToItemAddController.clear(); + isModAddFolderOnly = true; context.read().modsDropAddClear(); //addModToItemVisible = false; switch (modAdderAniController.status) { @@ -1564,17 +2306,30 @@ class _HomePageState extends State with TickerProviderStateMixin { if (newModToItemFormKey.currentState!.validate()) { if (modFilesList.isNotEmpty) { isModAddBtnClicked = true; - dragDropModsAdd(context, _newModToItemDragDropList, modsViewAppBarName, modFilesList.first.first.modPath, _newModToItemIndex, - newModToItemAddController.text.isEmpty ? null : newModToItemAddController.text) - .then((_) { - setState(() { - //setstate to refresh list - _newModToItemDragDropList.clear(); - newModToItemAddController.clear(); - isModAddBtnClicked = false; - isPreviewImgsOn = false; + if (isModAddFolderOnly) { + dragDropModsAddFoldersOnly(context, _newModToItemDragDropList, modsViewAppBarName, modFilesList.first.first.modPath, _newModToItemIndex, null).then((_) { + setState(() { + //setstate to refresh list + _newModToItemDragDropList.clear(); + newModToItemAddController.clear(); + isModAddBtnClicked = false; + isPreviewImgsOn = false; + }); }); - }); + } else { + isModAddFolderOnly = true; + dragDropModsAdd(context, _newModToItemDragDropList, modsViewAppBarName, modFilesList.first.first.modPath, _newModToItemIndex, + newModToItemAddController.text.isEmpty ? null : newModToItemAddController.text) + .then((_) { + setState(() { + //setstate to refresh list + _newModToItemDragDropList.clear(); + newModToItemAddController.clear(); + isModAddBtnClicked = false; + isPreviewImgsOn = false; + }); + }); + } } //addItemVisible = false; @@ -1598,13 +2353,14 @@ class _HomePageState extends State with TickerProviderStateMixin { Widget modPreviewView() { return Column( children: [ + //if (context.watch().previewWindowVisible) AppBar( title: Container(padding: const EdgeInsets.only(bottom: 10), child: const Text('Preview')), backgroundColor: Theme.of(context).canvasColor, foregroundColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColorDark : Theme.of(context).iconTheme.color, toolbarHeight: 30, ), - if (isPreviewImgsOn) + if (isPreviewImgsOn && context.watch().previewWindowVisible) Expanded( child: FutureBuilder( future: futureImagesGet, @@ -1620,7 +2376,7 @@ class _HomePageState extends State with TickerProviderStateMixin { } else { modPreviewImgList = snapshot.data; //print(modPreviewImgList.toString()); - List previewImageSliders = modPreviewImgList + previewImageSliders = modPreviewImgList .map((item) => Container( margin: const EdgeInsets.all(2.0), child: ClipRRect( @@ -1633,44 +2389,56 @@ class _HomePageState extends State with TickerProviderStateMixin { )), )) .toList(); + List previewImageSlidersBox = []; + for (var element in previewImageSliders) { + previewImageSlidersBox.add(FittedBox( + fit: BoxFit.fitHeight, + child: element, + )); + } + previewImageSliders = previewImageSlidersBox; return Column( children: [ Expanded( - child: CarouselSlider( - items: previewImageSliders, - carouselController: imgSliderController, - options: CarouselOptions( - autoPlay: previewImageSliders.length > 1, - reverse: true, - viewportFraction: 1, - enlargeCenterPage: true, - //aspectRatio: 1.0, - onPageChanged: (index, reason) { - setState(() { - currentImg = index; - }); - }), + child: GestureDetector( + onSecondaryTap: (() => previewImageSliders.isNotEmpty ? pictureDialog(context, previewImageSliders) : null), + child: CarouselSlider( + items: previewImageSliders, + carouselController: imgSliderController, + options: CarouselOptions( + autoPlayAnimationDuration: const Duration(milliseconds: 500), + autoPlay: previewImageSliders.length > 1, + reverse: true, + viewportFraction: 1, + enlargeCenterPage: true, + //aspectRatio: 1.0, + onPageChanged: (index, reason) { + setState(() { + currentImg = index; + }); + }), + ), ), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (previewImageSliders.isNotEmpty) - SizedBox( - width: 40, - child: MaterialButton( - onPressed: (() => imgSliderController.previousPage()), - child: const Icon(Icons.arrow_left), - ), - ), + // if (previewImageSliders.isNotEmpty) + // SizedBox( + // width: 40, + // child: MaterialButton( + // onPressed: (() => imgSliderController.previousPage()), + // child: const Icon(Icons.arrow_left), + // ), + // ), Row( mainAxisAlignment: MainAxisAlignment.center, children: modPreviewImgList.asMap().entries.map((entry) { return GestureDetector( onTap: () => imgSliderController.animateToPage(entry.key), child: Container( - width: 10.0, - height: 10.0, + width: 5.0, + height: 5.0, margin: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 4.0), decoration: BoxDecoration( shape: BoxShape.circle, @@ -1679,21 +2447,30 @@ class _HomePageState extends State with TickerProviderStateMixin { ); }).toList(), ), - if (previewImageSliders.isNotEmpty) - SizedBox( - width: 40, - child: MaterialButton( - onPressed: (() => imgSliderController.nextPage()), - child: const Icon(Icons.arrow_right), - ), - ), + // if (previewImageSliders.isNotEmpty) + // SizedBox( + // width: 40, + // child: MaterialButton( + // onPressed: (() => imgSliderController.nextPage()), + // child: const Icon(Icons.arrow_right), + // ), + // ), ], ), ], ); } } - })) + })), + if (isPreviewVidOn && context.watch().previewWindowVisible) + Expanded( + child: Scaffold( + body: Video( + player: previewPlayer, + fit: BoxFit.fill, + ), + ), + ) ], ); } @@ -1727,6 +2504,8 @@ class _HomePageState extends State with TickerProviderStateMixin { } } modsRemover(tempDelete); + isPreviewImgsOn = false; + isPreviewVidOn = false; }); }), onPressed: appliedModsList.isEmpty ? null : () {}, @@ -1771,168 +2550,202 @@ class _HomePageState extends State with TickerProviderStateMixin { onHover: (value) { if (value) { setState(() { - isPreviewImgsOn = true; - futureImagesGet = appliedModsList[index].first.images; + if (appliedModsList[index].first.images != null) { + isPreviewImgsOn = true; + futureImagesGet = appliedModsList[index].first.images; + } + //print(modFilesList[index].first.previewVids!.length); + if (appliedModsList[index].first.previewVids!.isNotEmpty) { + isPreviewVidOn = true; + isPreviewImgsOn = false; + previewPlayer.setVolume(0.0); + bool itemFound = false; + for (var vid in appliedModsList[index].first.previewVids!) { + if (medias.contains(Media.file(vid))) { + itemFound = true; + } else { + medias.clear(); + } + } + + if (medias.isEmpty || !itemFound) { + for (var vid in appliedModsList[index].first.previewVids!) { + medias.add(Media.file(vid)); + } + previewPlayer.open(Playlist(medias: medias, playlistMode: PlaylistMode.single), autoStart: true); + } else { + previewPlayer.bufferingProgressController.done; + previewPlayer.play(); + } + } + }); + } else { + setState(() { + isPreviewImgsOn = false; + isPreviewVidOn = false; + previewPlayer.pause(); }); } - // else { - // setState(() { - // isPreviewImgsOn = false; - // }); - // } }, - child: Card( - shape: RoundedRectangleBorder(borderRadius: const BorderRadius.all(Radius.circular(5.0)), side: BorderSide(width: 1, color: Theme.of(context).primaryColor)), - child: ExpansionTile( - initiallyExpanded: false, - textColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - iconColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - collapsedTextColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('${appliedModsList[index].first.categoryName} > ${appliedModsList[index].first.modName}', - style: const TextStyle( - fontWeight: FontWeight.w600, - )), - Text(appliedModsList[index].first.iceParent.trimLeft()), - ], - )), - //if (appliedModsList[index].length > 1) - Row( - children: [ - if (appliedModsList.indexWhere((element) => element.indexWhere((e) => e.isApplied == true) != -1) != -1) - SizedBox( - width: 40, - height: 40, - child: Tooltip( - message: 'Remove mods under "$modsViewAppBarName ${appliedModsList[index].first.iceParent}" from the game', - height: 25, - textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), - waitDuration: const Duration(seconds: 2), - child: MaterialButton( - onPressed: (() { - setState(() { - modsRemover(appliedModsList[index].toList()); - }); - }), - child: Icon( - Icons.playlist_remove, - color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + child: GestureDetector( + onSecondaryTap: () => previewImageSliders.isNotEmpty ? pictureDialog(context, previewImageSliders) : null, + child: Card( + shape: RoundedRectangleBorder(borderRadius: const BorderRadius.all(Radius.circular(5.0)), side: BorderSide(width: 1, color: Theme.of(context).primaryColor)), + child: ExpansionTile( + initiallyExpanded: false, + textColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + iconColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + collapsedTextColor: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('${appliedModsList[index].first.categoryName} > ${appliedModsList[index].first.modName}', + style: const TextStyle( + fontWeight: FontWeight.w600, + )), + Text(appliedModsList[index].first.iceParent.trimLeft()), + ], + )), + //if (appliedModsList[index].length > 1) + Row( + children: [ + if (appliedModsList.indexWhere((element) => element.indexWhere((e) => e.isApplied == true) != -1) != -1) + SizedBox( + width: 40, + height: 40, + child: Tooltip( + message: 'Remove mods under "$modsViewAppBarName ${appliedModsList[index].first.iceParent}" from the game', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: MaterialButton( + onPressed: (() { + setState(() { + isPreviewImgsOn = false; + isPreviewVidOn = false; + modsRemover(appliedModsList[index].toList()); + }); + }), + child: Icon( + Icons.playlist_remove, + color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColor : Theme.of(context).iconTheme.color, + ), ), ), ), - ), - ], - ) - ], - ), - children: [ - for (int i = 0; i < appliedModsList[index].length; i++) - InkWell( - // onHover: (value) { - // if (value && - // modPreviewImgList.indexWhere((e) => - // e.path.contains( - // modFilesList[ - // index] - // .first - // .iceParent)) == - // -1) { - // setState(() { - // isPreviewImgsOn = true; - // futureImagesGet = - // modFilesList[index] - // [i] - // .images; - // }); - // } - // }, - child: ListTile( - // leading: appliedModsList[index][i].isNew == true - // ? Icon( - // Icons.new_releases, - // color: Theme.of(context).indicatorColor, - // ) - // : null, - title: Text(appliedModsList[index][i].iceName), - //subtitle: Text(modFilesList[index][i].icePath), - minLeadingWidth: 10, - trailing: SizedBox( - width: 40, - height: 40, - child: appliedModsList[index][i].isApplied - ? Tooltip( - message: 'Remove this mod from the game', - height: 25, - textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), - waitDuration: const Duration(seconds: 2), - child: MaterialButton( - onPressed: (() { - setState(() { - modsRemover([appliedModsList[index][i]]); - //appliedModsList.remove(modFilesList[index]); - if (backupFilesMissingList.isNotEmpty) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - duration: const Duration(seconds: 2), - //backgroundColor: Theme.of(context).focusColor, - content: SizedBox( - height: backupFilesMissingList.length * 20, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - for (int i = 0; i < backupFilesMissingList.length; i++) - Text( - 'Backup file of "${backupFilesMissingList[i].modName} ${backupFilesMissingList[i].iceParent} > ${backupFilesMissingList[i].iceName}" is not found'), - ], - ), - ))); - } - }); - }), - child: const Icon(Icons.replay), - )) - : Tooltip( - message: 'Apply this mod to the game', - height: 25, - textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), - waitDuration: const Duration(seconds: 1), - child: MaterialButton( - onPressed: (() { - setState(() { - modsToDataAdder([appliedModsList[index][i]]); - //appliedModsList.add(modFilesList[index]); - if (originalFilesMissingList.isNotEmpty) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - duration: const Duration(seconds: 2), - //backgroundColor: Theme.of(context).focusColor, - content: SizedBox( - height: originalFilesMissingList.length * 20, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - for (int i = 0; i < originalFilesMissingList.length; i++) - Text( - 'Original file of "${originalFilesMissingList[i].modName} ${originalFilesMissingList[i].iceParent} > ${originalFilesMissingList[i].iceName}" is not found'), - ], - ), - ))); - } - }); - }), - child: const Icon(Icons.add_to_drive), + ], + ) + ], + ), + children: [ + for (int i = 0; i < appliedModsList[index].length; i++) + InkWell( + // onHover: (value) { + // if (value && + // modPreviewImgList.indexWhere((e) => + // e.path.contains( + // modFilesList[ + // index] + // .first + // .iceParent)) == + // -1) { + // setState(() { + // isPreviewImgsOn = true; + // futureImagesGet = + // modFilesList[index] + // [i] + // .images; + // }); + // } + // }, + child: ListTile( + // leading: appliedModsList[index][i].isNew == true + // ? Icon( + // Icons.new_releases, + // color: Theme.of(context).indicatorColor, + // ) + // : null, + title: Text(appliedModsList[index][i].iceName), + //subtitle: Text(modFilesList[index][i].icePath), + minLeadingWidth: 10, + trailing: SizedBox( + width: 40, + height: 40, + child: appliedModsList[index][i].isApplied + ? Tooltip( + message: 'Remove this mod from the game', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: MaterialButton( + onPressed: (() { + setState(() { + isPreviewImgsOn = false; + isPreviewVidOn = false; + modsRemover([appliedModsList[index][i]]); + //appliedModsList.remove(modFilesList[index]); + if (backupFilesMissingList.isNotEmpty) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + duration: const Duration(seconds: 2), + //backgroundColor: Theme.of(context).focusColor, + content: SizedBox( + height: backupFilesMissingList.length * 20, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (int i = 0; i < backupFilesMissingList.length; i++) + Text( + 'Backup file of "${backupFilesMissingList[i].modName} ${backupFilesMissingList[i].iceParent} > ${backupFilesMissingList[i].iceName}" is not found'), + ], + ), + ))); + } + }); + }), + child: const Icon(Icons.replay), + )) + : Tooltip( + message: 'Apply this mod to the game', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 1), + child: MaterialButton( + onPressed: (() { + setState(() { + modsToDataAdder([appliedModsList[index][i]]); + //appliedModsList.add(modFilesList[index]); + if (originalFilesMissingList.isNotEmpty) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + duration: const Duration(seconds: 2), + //backgroundColor: Theme.of(context).focusColor, + content: SizedBox( + height: originalFilesMissingList.length * 20, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (int i = 0; i < originalFilesMissingList.length; i++) + Text( + 'Original file of "${originalFilesMissingList[i].modName} ${originalFilesMissingList[i].iceParent} > ${originalFilesMissingList[i].iceName}" is not found'), + ], + ), + ))); + } + }); + }), + child: const Icon(Icons.add_to_drive), + ), ), - ), + ), ), - ), - ) - ], - ))); + ) + ], + )), + )); })); } } diff --git a/lib/main.dart b/lib/main.dart index d6d110e6..463caca9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,10 +4,13 @@ import 'dart:async'; import 'dart:io'; import 'package:bitsdojo_window/bitsdojo_window.dart'; +import 'package:dart_vlc/dart_vlc.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; import 'package:pso2_mod_manager/data_loading_page.dart'; +import 'package:pso2_mod_manager/home_page.dart'; import 'package:pso2_mod_manager/mod_classes.dart'; import 'package:pso2_mod_manager/custom_window_button.dart'; import 'package:pso2_mod_manager/state_provider.dart'; @@ -26,18 +29,21 @@ String modSettingsPath = ''; String deletedItemsPath = ''; String? checkSumFilePath; FilePickerResult? checksumLocation; +bool _previewWindowVisible = false; double windowsWidth = 1280.0; double windowsHeight = 720.0; Future? filesData; List allModFiles = []; var dataStreamController = StreamController(); + Future main() async { + DartVLC.initialize(); WidgetsFlutterBinding.ensureInitialized(); await windowManager.ensureInitialized(); final prefs = await SharedPreferences.getInstance(); windowsWidth = (prefs.getDouble('windowsWidth') ?? 1280.0); windowsHeight = (prefs.getDouble('windowsHeight') ?? 720.0); - + // WindowOptions windowOptions = const WindowOptions( // size: Size(1280, 720), // center: true, @@ -51,7 +57,7 @@ Future main() async { ], child: const RestartWidget(child: MyApp()))); doWhenWindowReady(() { Size initialSize = Size(windowsWidth, windowsHeight); - appWindow.minSize = const Size(1030, 500); + appWindow.minSize = const Size(1160, 500); appWindow.size = initialSize; appWindow.alignment = Alignment.center; appWindow.title = 'PSO2NGS Mod Manager'; @@ -101,15 +107,22 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State with WindowListener { final imgStream = StreamController(); bool isDarkModeOn = false; + String appVersion = ''; @override void initState() { windowManager.addListener(this); miscCheck(); dirPathCheck(); + getAppVer(); super.initState(); } + Future getAppVer() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + appVersion = packageInfo.version; + } + @override void onWindowFocus() { // Make sure to call once. @@ -133,6 +146,13 @@ class _MyHomePageState extends State with WindowListener { if (isDarkModeOn) { MyApp.themeNotifier.value = ThemeMode.dark; } + //previewWindows Check + _previewWindowVisible = (prefs.getBool('previewWindowVisible') ?? false); + if (_previewWindowVisible) { + Provider.of(context, listen: false).previewWindowVisibleSetTrue(); + } else { + Provider.of(context, listen: false).previewWindowVisibleSetFalse(); + } }); } @@ -214,14 +234,20 @@ class _MyHomePageState extends State with WindowListener { child: Row( children: [ Expanded( - child: MoveWindow( - child: Container( - padding: const EdgeInsets.only(left: 10), - child: const Text( - 'PSO2NGS Mod Manager', - style: TextStyle(fontWeight: FontWeight.w500), - )), - )), + child: MoveWindow( + child: Container( + padding: const EdgeInsets.only(left: 10), + child: Tooltip( + message: 'Version: $appVersion | Build by キス★', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 2), + child: const Text( + 'PSO2NGS Mod Manager', + style: TextStyle(fontWeight: FontWeight.w500), + )), + )), + ), //Buttons Padding( padding: const EdgeInsets.only(bottom: 9), @@ -346,7 +372,7 @@ class _MyHomePageState extends State with WindowListener { color: Colors.red, ), SizedBox(width: 5), - Text('Checksum file missing. Click here!', style: TextStyle(fontWeight: FontWeight.w400, color: Colors.red)) + Text('Checksum missing. Click!', style: TextStyle(fontWeight: FontWeight.w400, color: Colors.red)) ], ), ), @@ -399,43 +425,91 @@ class _MyHomePageState extends State with WindowListener { ), ), - //Dark theme - if (MyApp.themeNotifier.value == ThemeMode.dark) - MaterialButton( + //Preview + Tooltip( + message: 'Show/Hide Preview Window', + height: 25, + textStyle: TextStyle(fontSize: 15, color: Theme.of(context).canvasColor), + waitDuration: const Duration(seconds: 1), + child: MaterialButton( + //visualDensity: VisualDensity.compact, onPressed: (() async { final prefs = await SharedPreferences.getInstance(); - MyApp.themeNotifier.value = ThemeMode.light; - prefs.setBool('isDarkModeOn', false); - //setState(() {}); + if (Provider.of(context, listen: false).previewWindowVisible) { + Provider.of(context, listen: false).previewWindowVisibleSetFalse(); + prefs.setBool('previewWindowVisible', false); + previewPlayer.stop(); + } else { + Provider.of(context, listen: false).previewWindowVisibleSetTrue(); + prefs.setBool('previewWindowVisible', true); + } }), child: Row( - children: const [ - Icon( - Icons.light_mode_outlined, + children: [ + const Icon( + Icons.preview_outlined, size: 18, ), - SizedBox(width: 5), - Text('Light', style: TextStyle(fontWeight: FontWeight.w400)) + const SizedBox(width: 5), + const Text('Preview: ', style: TextStyle(fontWeight: FontWeight.w400)), + if (context.watch().previewWindowVisible) + SizedBox( + width: 23, + child: Text('ON', + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 13, + color: MyApp.themeNotifier.value == ThemeMode.light ? Theme.of(context).primaryColorDark : Theme.of(context).iconTheme.color))), + if (context.watch().previewWindowVisible == false) + const SizedBox(width: 23, child: Text('OFF', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 13))) ], ), ), + ), + + //Dark theme + if (MyApp.themeNotifier.value == ThemeMode.dark) + SizedBox( + width: 70, + child: MaterialButton( + onPressed: (() async { + final prefs = await SharedPreferences.getInstance(); + MyApp.themeNotifier.value = ThemeMode.light; + prefs.setBool('isDarkModeOn', false); + //setState(() {}); + }), + child: Row( + children: const [ + Icon( + Icons.light_mode_outlined, + size: 18, + ), + SizedBox(width: 5), + Text('Light', style: TextStyle(fontWeight: FontWeight.w400)) + ], + ), + ), + ), if (MyApp.themeNotifier.value == ThemeMode.light) - MaterialButton( - onPressed: (() async { - final prefs = await SharedPreferences.getInstance(); - MyApp.themeNotifier.value = ThemeMode.dark; - prefs.setBool('isDarkModeOn', true); - //setState(() {}); - }), - child: Row( - children: const [ - Icon( - Icons.dark_mode_outlined, - size: 18, - ), - SizedBox(width: 5), - Text('Dark', style: TextStyle(fontWeight: FontWeight.w400)) - ], + SizedBox( + width: 70, + child: MaterialButton( + onPressed: (() async { + final prefs = await SharedPreferences.getInstance(); + MyApp.themeNotifier.value = ThemeMode.dark; + prefs.setBool('isDarkModeOn', true); + //setState(() {}); + }), + child: Row( + children: const [ + Icon( + Icons.dark_mode_outlined, + size: 18, + ), + SizedBox(width: 5), + Text('Dark', style: TextStyle(fontWeight: FontWeight.w400)) + ], + ), ), ), ], @@ -451,7 +525,7 @@ class _MyHomePageState extends State with WindowListener { : Column( children: const [ Text( - 'Waiting for user action', + 'Waiting for user\'s action', style: TextStyle(fontSize: 20), ), SizedBox( diff --git a/lib/mod_classes.dart b/lib/mod_classes.dart index b3745f1b..0825b097 100644 --- a/lib/mod_classes.dart +++ b/lib/mod_classes.dart @@ -33,21 +33,23 @@ class ModCategory { class ModFile extends ModCategory { ModFile( - this.numOfSubItems, - this.modPath, //mod folder path - this.modName, //mod folder name, - this.icePath, - this.iceName, - this.iceParent, - this.originalIcePath, - this.backupIcePath, - this.images, - this.isApplied, - this.isSFW, - this.isNew - ) : super('', '', [], [], 0, [], [], []); + this.appliedDate, + this.modPath, //mod folder path + this.modName, //mod folder name, + this.icePath, + this.iceName, + this.iceParent, + this.originalIcePath, + this.backupIcePath, + this.images, + this.isApplied, + this.isSFW, + this.isNew, + this.isFav, + this.previewVids) + : super('', '', [], [], 0, [], [], []); - int numOfSubItems; + String appliedDate; String modPath; String modName; String icePath; @@ -59,10 +61,13 @@ class ModFile extends ModCategory { bool isApplied; bool isSFW; bool isNew; + bool isFav; + List? previewVids; fromJson(Map json) { categoryName = json['categoryName']; categoryPath = json['categoryPath']; + appliedDate = json['appliedDate']; modPath = json['modPath']; modName = json['modName']; icePath = json['icePath']; @@ -73,12 +78,14 @@ class ModFile extends ModCategory { isApplied = json['isApplied']; isSFW = json['isSFW']; isNew = json['isNew']; + isFav = json['isFav']; } Map toJson() { final Map data = {}; data['categoryPath'] = categoryPath; data['categoryName'] = categoryName; + data['appliedDate'] = appliedDate; data['modPath'] = modPath; data['modName'] = modName; data['iceName'] = iceName; @@ -89,6 +96,7 @@ class ModFile extends ModCategory { data['isApplied'] = isApplied; data['isSFW'] = isSFW; data['isNew'] = isNew; + data['isFav'] = isFav; return data; } diff --git a/lib/mods_loader.dart b/lib/mods_loader.dart index b0e04420..72ca8999 100644 --- a/lib/mods_loader.dart +++ b/lib/mods_loader.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/cupertino.dart'; +import 'package:pso2_mod_manager/file_functions.dart'; import 'package:pso2_mod_manager/home_page.dart'; import 'package:pso2_mod_manager/main.dart'; import 'package:path/path.dart' as p; @@ -26,7 +27,8 @@ Future> modsLoader() async { //JSON Loader void convertData(var jsonResponse) { for (var b in jsonResponse) { - ModFile mod = ModFile(0, b['modPath'], b['modName'], b['icePath'], b['iceName'], b['iceParent'], b['originalIcePath'], b['backupIcePath'], null, b['isApplied'], b['isSFW'], b['isNew']); + ModFile mod = ModFile(b['appliedDate'], b['modPath'], b['modName'], b['icePath'], b['iceName'], b['iceParent'], b['originalIcePath'], b['backupIcePath'], null, b['isApplied'], b['isSFW'], + b['isNew'], b['isFav'], null); mod.categoryPath = b['categoryPath']; mod.categoryName = b['categoryName']; modFilesFromJson.add(mod); @@ -44,6 +46,7 @@ Future> modsLoader() async { String modName = '', modPath = ''; String iceParents = ''; List imgFiles = []; + List vidFiles = []; //Helpers for (var element in iceFilePathSplit) { @@ -69,19 +72,28 @@ Future> modsLoader() async { } var imgList = getImagesList(imgFiles); + //Vids helper + for (var vidFile in Directory(iceFile.parent.path).listSync(recursive: false).whereType()) { + if (p.extension(vidFile.path) == '.mp4' || p.extension(vidFile.path) == '.webm') { + vidFiles.add(vidFile); + } + } + //New ModFile - ModFile newModFile = ModFile(0, modPath, modName, iceFile.path, iceFilePathSplit.last, iceParents, '', '', imgList, false, true, false); + ModFile newModFile = ModFile('', modPath, modName, iceFile.path, iceFilePathSplit.last, iceParents, '', '', imgList, false, true, false, false, vidFiles); newModFile.categoryName = categoryName; newModFile.categoryPath = categoryPath; var jsonModFile = modFilesFromJson.firstWhere((e) => e.icePath == newModFile.icePath, orElse: () { - return ModFile(0, '', '', '', '', '', '', '', null, false, true, false); + return ModFile('', '', '', '', '', '', '', '', null, false, true, false, false, []); }); if (jsonModFile.icePath.isNotEmpty) { + newModFile.appliedDate = jsonModFile.appliedDate; newModFile.backupIcePath = jsonModFile.backupIcePath; newModFile.originalIcePath = jsonModFile.originalIcePath; newModFile.isApplied = jsonModFile.isApplied; newModFile.isSFW = jsonModFile.isSFW; newModFile.isNew = jsonModFile.isNew; + newModFile.isFav = jsonModFile.isFav; } allModFiles.add(newModFile); } @@ -95,7 +107,7 @@ Future> modsLoader() async { //Category List List categories(List allModFiles) { - File defaultCategoryItemIcon = File('assets/img/placeholdersquare.jpg'); + File defaultCategoryItemIcon = File('assets/img/placeholdersquare.png'); List categories = []; //Get categories @@ -112,6 +124,7 @@ List categories(List allModFiles) { imgFilesGet.add(file); } } + if (imgFilesGet.isNotEmpty) { imgFiles.addAll(imgFilesGet); } else { @@ -208,9 +221,137 @@ List categories(List allModFiles) { } } + //Fav + List favModListGet = []; + for (var cate in categories) { + for (var mod in cate.allModFiles) { + if (mod.isFav) { + favModListGet.add(mod); + } + } + } + + List modNames = []; + for (var mod in favModListGet) { + if (modNames.isEmpty || modNames.indexWhere((element) => element == mod.modName) == -1) { + modNames.add(mod.modName); + } + } + + List> favModsList = []; + for (var name in modNames) { + List sameMods = []; + for (var modFile in favModListGet) { + if (modFile.modName == name) { + sameMods.add(modFile); + } + } + + List parents = []; + for (var modFile in sameMods) { + if (parents.indexWhere((element) => element == modFile.iceParent) == -1) { + parents.add(modFile.iceParent); + } + } + + for (var parent in parents) { + List sameParent = sameMods.where((element) => element.iceParent == parent).toList(); + favModsList.add(sameParent); + } + } + + ModCategory tempFavCate = ModCategory('Favorites', '', [], [], 0, [], [], []); + for (var list in favModsList) { + tempFavCate = addOrRemoveFav(categories, list, tempFavCate, true); + } + tempFavCate.itemNames.sort(); + categories.insert(0, tempFavCate); + return categories; } +//Search result +List searchFilterResults(List paramCateList, String searchText) { + List resultList = []; + + for (var cate in paramCateList) { + if (cate.categoryName != 'Favorites') { + List itemNameResults = []; + if (cate.categoryName == 'Basewears' || cate.categoryName == 'Outerwears' || cate.categoryName == 'Innerwears' || cate.categoryName == 'Setwears') { + itemNameResults = cate.itemNames.where((element) => element.replaceRange(element.length - 4, null, '').toLowerCase().contains(searchText.toLowerCase())).toList(); + } else { + itemNameResults = cate.itemNames.where((element) => element.toLowerCase().contains(searchText.toLowerCase())).toList(); + } + + if (itemNameResults.isNotEmpty) { + ModCategory newCate = ModCategory(cate.categoryName, cate.categoryPath, [], [], itemNameResults.length, [], [], []); + for (var itemName in itemNameResults) { + int itemIndex = cate.itemNames.indexOf(itemName); + List imgFiles = cate.imageIcons[itemIndex]; + int numofMod = cate.numOfMods[itemIndex]; + int numofApplied = cate.numOfApplied[itemIndex]; + List modsInItems = cate.allModFiles.where((element) => element.modName == itemName).toList(); + + //Populate newcate + newCate.itemNames.add(itemName); + newCate.imageIcons.add(imgFiles); + newCate.numOfMods.add(numofMod); + newCate.numOfApplied.add(numofApplied); + newCate.allModFiles.addAll(modsInItems); + } + resultList.add(newCate); + } else { + List modFileResults = + cate.allModFiles.where((element) => element.iceName.toLowerCase().contains(searchText.toLowerCase()) || element.iceParent.toLowerCase().contains(searchText.toLowerCase())).toList(); + + if (modFileResults.isNotEmpty) { + // List parentsFromMods = []; + // for (var element in modFileResults) { + // if (parentsFromMods.indexWhere((e) => e == element.iceParent) == -1) { + // parentsFromMods.add(element.iceParent); + // } + // } + List itemNamesfromMods = []; + for (var element in modFileResults) { + if (itemNamesfromMods.indexWhere((e) => e == element.modName) == -1) { + itemNamesfromMods.add(element.modName); + } + } + + ModCategory newCate = ModCategory(cate.categoryName, cate.categoryPath, [], [], itemNamesfromMods.length, [], [], []); + for (var itemName in itemNamesfromMods) { + int itemIndex = cate.itemNames.indexOf(itemName); + List imgFiles = cate.imageIcons[itemIndex]; + List parentsInItems = []; + for (var element in modFileResults.where((element) => element.modName == itemName)) { + if (parentsInItems.indexWhere((e) => e == element.iceParent) == -1) { + parentsInItems.add(element.iceParent); + } + } + int numofMod = parentsInItems.length; + int numofApplied = modFileResults.where((element) => element.isApplied == true).length; // need to change + List modsInItems = modFileResults.where((element) => element.modName == itemName).toList(); + //List modsInItems = []; + // for (var parent in parentsFromMods) { + // modsInItems = modFileResults.where((element) => element.modName == itemName).toList(); + // } + + //Populate newcate + newCate.itemNames.add(itemName); + newCate.imageIcons.add(imgFiles); + newCate.numOfMods.add(numofMod); + newCate.numOfApplied.add(numofApplied); + newCate.allModFiles.addAll(modsInItems); + } + resultList.add(newCate); + } + } + } + } + + return resultList; +} + //Mod List Future>> getModFilesByCategory(List allModFiles, String modName) async { List> modFilesList = []; @@ -257,10 +398,11 @@ Future>> getAppliedModsList() async { if (tempMods.isNotEmpty) { tempMods.add(mod); } else { - appliedList.add([mod]); + appliedList.insert(0, [mod]); } } } + appliedList.sort(((a, b) => b.first.appliedDate.compareTo(b.first.appliedDate))); return appliedList; } diff --git a/lib/popup_handlers.dart b/lib/popup_handlers.dart index d15ca0ff..d7683e02 100644 --- a/lib/popup_handlers.dart +++ b/lib/popup_handlers.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:carousel_slider/carousel_slider.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -334,13 +335,14 @@ Future itemDeleteDialog(context, double height, String popupTitle, String popupM final subFolderList = Directory(curCate.categoryPath).listSync().whereType(); for (var folder in subFolderList) { - if (Directory(folder.path).listSync(recursive: true).whereType().isEmpty && Directory(folder.path).listSync(recursive: true).whereType().isEmpty) { + if (Directory(folder.path).listSync(recursive: true).whereType().isEmpty) { Directory(folder.path).deleteSync(recursive: true); } } } curCate.imageIcons.removeAt(curCate.itemNames.indexOf(curItem)); + curCate.numOfMods.removeAt(curCate.itemNames.indexWhere((element) => element == curItem)); curCate.itemNames.removeWhere((element) => element == curItem); curCate.allModFiles.removeWhere((element) => element.modName == curItem); curCate.numOfItems--; @@ -425,21 +427,27 @@ Future modDeleteDialog(context, double height, String popupTitle, String popupMe } } - final subFolderList = Directory(curModPath).listSync().whereType(); - for (var folder in subFolderList) { - if (Directory(folder.path).listSync(recursive: true).whereType().isEmpty && Directory(folder.path).listSync(recursive: true).whereType().isEmpty) { - Directory(folder.path).deleteSync(recursive: true); + if (Directory(curModPath).existsSync()) { + final subFolderList = Directory(curModPath).listSync().whereType(); + for (var folder in subFolderList) { + if (Directory(folder.path).listSync(recursive: true).whereType().isEmpty && Directory(folder.path).listSync(recursive: true).whereType().isEmpty) { + Directory(folder.path).deleteSync(recursive: true); + } + } + + if (Directory(curModPath).listSync(recursive: true).whereType().isEmpty && Directory(curModPath).listSync(recursive: true).whereType().isEmpty) { + Directory(curModPath).deleteSync(recursive: true); } - } - if (Directory(curModPath).listSync(recursive: true).whereType().isEmpty && Directory(curModPath).listSync(recursive: true).whereType().isEmpty) { - Directory(curModPath).deleteSync(recursive: true); } ModCategory curCate = cateList.firstWhere((cate) => cate.allModFiles.indexWhere((file) => file.modPath == curModPath) != -1); final curModIndex = curCate.itemNames.indexOf(curModName); curCate.numOfMods[curModIndex]--; - modFilesList.removeWhere((element) => element == modsList); - allModFiles.removeWhere((element) => element.categoryPath == curCate.categoryPath && element.modName == curModName); + modFilesList.removeAt(modFilesList.indexOf(modsList)); + for (var element in modsList) { + curCate.allModFiles.remove(element); + allModFiles.remove(element); + } } }), child: const Text('Sure')) @@ -449,14 +457,105 @@ Future modDeleteDialog(context, double height, String popupTitle, String popupMe }); } -class _SystemPadding extends StatelessWidget { - final Widget child; - - const _SystemPadding({Key? key, required this.child}) : super(key: key); - - @override - Widget build(BuildContext context) { - var mediaQuery = MediaQuery.of(context); - return AnimatedContainer(padding: mediaQuery.viewInsets, duration: const Duration(milliseconds: 300), child: child); - } +//Remove Mod Dialog +Future pictureDialog(context, List previewImageSliders) async { + CarouselController imgSliderController = CarouselController(); + int currentImg = 0; + await showDialog( + context: context, + builder: (BuildContext context) { + return StatefulBuilder(builder: (context, setState) { + return AlertDialog( + contentPadding: const EdgeInsets.only(top: 5, left: 5, right: 5), + content: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + InteractiveViewer( + scaleEnabled: true, + panEnabled: true, + child: AspectRatio( + aspectRatio: 16/9, + child: CarouselSlider( + items: previewImageSliders, + carouselController: imgSliderController, + options: CarouselOptions( + //height: double.maxFinite, + autoPlay: false, + reverse: true, + viewportFraction: 1.0, + enlargeCenterPage: true, + aspectRatio: 1, + onPageChanged: (index, reason) { + setState(() { + currentImg = index; + }); + }), + ), + ), + ), + ], + ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox(width: 250, child: Text('Scroll wheel: Zoom | Right mouse: Pan',)), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (previewImageSliders.isNotEmpty) + SizedBox( + width: 40, + child: MaterialButton( + onPressed: (() => imgSliderController.previousPage()), + child: const Icon(Icons.arrow_left), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: modPreviewImgList.asMap().entries.map((entry) { + return GestureDetector( + onTap: () => imgSliderController.animateToPage(entry.key), + child: Container( + width: 10.0, + height: 10.0, + margin: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 4.0), + decoration: BoxDecoration( + shape: BoxShape.circle, color: (Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black).withOpacity(currentImg == entry.key ? 0.9 : 0.4)), + ), + ); + }).toList(), + ), + if (previewImageSliders.isNotEmpty) + SizedBox( + width: 40, + child: MaterialButton( + onPressed: (() => imgSliderController.nextPage()), + child: const Icon(Icons.arrow_right), + ), + ), + ], + ), + ), + SizedBox( + width: 250, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ElevatedButton( + child: const Text('Close'), + onPressed: () { + Navigator.of(context).pop(); + }), + ], + ), + ), + ], + ), + ], + ); + }); + }); } diff --git a/lib/state_provider.dart b/lib/state_provider.dart index 62a45dab..9927f3ed 100644 --- a/lib/state_provider.dart +++ b/lib/state_provider.dart @@ -3,11 +3,13 @@ import 'package:flutter/cupertino.dart'; class stateProvider with ChangeNotifier { bool _isMainBinFound = false; + bool _previewWindowVisible = false; String _newItemDropDisplay = ''; String _newSingleItemDropDisplay = ''; String _newModDropDisplay = ''; bool get isMainBinFound => _isMainBinFound; + bool get previewWindowVisible => _previewWindowVisible; String get newItemDropDisplay => _newItemDropDisplay; String get newSingleItemDropDisplay => _newSingleItemDropDisplay; String get newModDropDisplay => _newModDropDisplay; @@ -22,6 +24,16 @@ class stateProvider with ChangeNotifier { notifyListeners(); } + void previewWindowVisibleSetTrue() { + _previewWindowVisible = true; + notifyListeners(); + } + + void previewWindowVisibleSetFalse() { + _previewWindowVisible = false; + notifyListeners(); + } + //itemList Display void singleItemsDropAdd(List paramList) { for (var file in paramList) { diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 0a0a8976..61b49a5b 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -16,6 +17,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin"); bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar); + g_autoptr(FlPluginRegistrar) dart_vlc_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DartVlcPlugin"); + dart_vlc_plugin_register_with_registrar(dart_vlc_registrar); g_autoptr(FlPluginRegistrar) desktop_drop_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin"); desktop_drop_plugin_register_with_registrar(desktop_drop_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index db194301..41254e3d 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST bitsdojo_window_linux + dart_vlc desktop_drop screen_retriever url_launcher_linux diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index f8cd1c18..fd3aaf54 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,7 +6,10 @@ import FlutterMacOS import Foundation import bitsdojo_window_macos +import dart_vlc import desktop_drop +import package_info_plus_macos +import path_provider_macos import screen_retriever import shared_preferences_macos import url_launcher_macos @@ -14,7 +17,10 @@ import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin")) + DartVlcPlugin.register(with: registry.registrar(forPlugin: "DartVlcPlugin")) DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin")) + FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) + 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")) diff --git a/pubspec.lock b/pubspec.lock index c36a4368..badaf757 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -8,6 +8,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.8.2" + audio_video_progress_bar: + dependency: transitive + description: + name: audio_video_progress_bar + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.0" bitsdojo_window: dependency: "direct main" description: @@ -98,7 +105,21 @@ packages: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "1.0.5" + dart_vlc: + dependency: "direct main" + description: + name: dart_vlc + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1" + dart_vlc_ffi: + dependency: transitive + description: + name: dart_vlc_ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.8" desktop_drop: dependency: "direct main" description: @@ -112,7 +133,7 @@ packages: name: dropdown_button2 url: "https://pub.dartlang.org" source: hosted - version: "1.5.2" + version: "1.6.2" fake_async: dependency: transitive description: @@ -153,6 +174,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + flutter_native_view: + dependency: transitive + description: + name: flutter_native_view + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -170,6 +198,27 @@ packages: description: flutter source: sdk version: "0.0.0" + font_awesome_flutter: + dependency: "direct main" + description: + name: font_awesome_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "10.1.0" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.4" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" intl: dependency: "direct main" description: @@ -218,7 +267,7 @@ packages: name: multi_split_view url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.1.0" nested: dependency: transitive description: @@ -226,6 +275,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.2" + package_info_plus_linux: + dependency: transitive + description: + name: package_info_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + package_info_plus_macos: + dependency: transitive + description: + name: package_info_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + package_info_plus_web: + dependency: transitive + description: + name: package_info_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + package_info_plus_windows: + dependency: transitive + description: + name: package_info_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" path: dependency: transitive description: @@ -233,6 +324,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.1" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.15" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.10" path_provider_linux: dependency: transitive description: @@ -240,6 +352,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" path_provider_platform_interface: dependency: transitive description: @@ -392,13 +511,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.9" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" url_launcher: dependency: "direct main" description: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.1.2" + version: "6.1.4" url_launcher_android: dependency: transitive description: @@ -433,14 +559,14 @@ packages: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.1.0" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.12" url_launcher_windows: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 612e6022..aeab311b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 +version: 1.1.0+1 environment: sdk: ">=2.17.0 <3.0.0" @@ -41,6 +41,9 @@ dependencies: url_launcher: ^6.1.2 intl: ^0.17.0 window_manager: ^0.2.5 + dart_vlc: ^0.2.1 + font_awesome_flutter: ^10.1.0 + package_info_plus: ^1.4.2 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. @@ -68,7 +71,7 @@ flutter: # the material Icons class. uses-material-design: true assets: - - assets/img/placeholdersquare.jpg + - assets/img/placeholdersquare.png # To add assets to your application, add an assets section, like this: # assets: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index db0f73ab..681ac6e4 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,7 +7,9 @@ #include "generated_plugin_registrant.h" #include +#include #include +#include #include #include #include @@ -15,8 +17,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { BitsdojoWindowPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("BitsdojoWindowPlugin")); + DartVlcPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DartVlcPlugin")); DesktopDropPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("DesktopDropPlugin")); + FlutterNativeViewPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterNativeViewPlugin")); ScreenRetrieverPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 79139113..485fe01c 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,7 +4,9 @@ list(APPEND FLUTTER_PLUGIN_LIST bitsdojo_window_windows + dart_vlc desktop_drop + flutter_native_view screen_retriever url_launcher_windows window_manager diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc index daa3bca0..e87ab56a 100644 --- a/windows/runner/Runner.rc +++ b/windows/runner/Runner.rc @@ -69,7 +69,7 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" #ifdef FLUTTER_BUILD_NAME #define VERSION_AS_STRING #FLUTTER_BUILD_NAME #else -#define VERSION_AS_STRING "1.0.0" +#define VERSION_AS_STRING "1.1.0" #endif VS_VERSION_INFO VERSIONINFO diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp index 7c81bf55..c832ac55 100644 --- a/windows/runner/main.cpp +++ b/windows/runner/main.cpp @@ -4,6 +4,7 @@ auto bdw = bitsdojo_window_configure(BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP); #include #include + #include "flutter_window.h" #include "utils.h" @@ -34,6 +35,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, } window.SetQuitOnClose(true); + ::MSG msg; while (::GetMessage(&msg, nullptr, 0, 0)) { ::TranslateMessage(&msg); diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index 9fa03845..41c9dc06 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ