diff --git a/lib/models/config_model.dart b/lib/models/config_model.dart index 141b729..b32beb2 100644 --- a/lib/models/config_model.dart +++ b/lib/models/config_model.dart @@ -6,26 +6,47 @@ class ConfigData { bool alwaysOnTop; int copySize; bool showSearchBar; + bool globalSearch; List stickerPacks; - ConfigData(this.token, this.alwaysOnTop, this.copySize, this.showSearchBar, this.stickerPacks,); + ConfigData( + this.token, + this.alwaysOnTop, + this.copySize, + this.showSearchBar, + this.globalSearch, + this.stickerPacks, + ); ConfigData.fromJson(Map json, Directory wd) - : token = json['token'] ?? '', - alwaysOnTop = json['always_on_top'] is bool ? json['always_on_top'] ?? true : true, - copySize = max(1, min(512, json['copy_size'] is int ? json['copy_size'] ?? 512 : 512)), - showSearchBar = json['show_search_bar'] is bool ? json['show_search_bar'] ?? true : true, - stickerPacks = List.generate( - ((json['sticker_packs'] ?? []) as List).length, - (index) => StickerPackConfig.fromJson(((json['sticker_packs'] ?? []) as List)[index], wd)); + : token = json['token'] ?? '', + alwaysOnTop = json['always_on_top'] is bool + ? json['always_on_top'] ?? true + : true, + copySize = max( + 1, + min(512, + json['copy_size'] is int ? json['copy_size'] ?? 512 : 512)), + showSearchBar = json['show_search_bar'] is bool + ? json['show_search_bar'] ?? true + : true, + globalSearch = json['global_search'] is bool + ? json['global_search'] ?? false + : false, + stickerPacks = List.generate( + ((json['sticker_packs'] ?? []) as List).length, + (index) => StickerPackConfig.fromJson( + ((json['sticker_packs'] ?? []) as List)[index], wd)); Map toJson() => { - 'token': token, - 'always_on_top': alwaysOnTop, - 'copy_size': copySize, - 'show_search_bar': showSearchBar, - 'sticker_packs': List.generate(stickerPacks.length, (index) => stickerPacks[index].toJson()), - }; + 'token': token, + 'always_on_top': alwaysOnTop, + 'copy_size': copySize, + 'show_search_bar': showSearchBar, + 'global_search': globalSearch, + 'sticker_packs': List.generate( + stickerPacks.length, (index) => stickerPacks[index].toJson()), + }; } class StickerPackConfig { @@ -36,16 +57,18 @@ class StickerPackConfig { String coverPath; StickerPackConfig.fromJson(Map json, Directory wd) - : coverFile = File(json['cover_path']).isAbsolute ? File(json['cover_path']) : File(wd.path + json['cover_path']), - coverPath = json['cover_path'], - basePath = json['base_path'], - name = json['name'], - id = json['id']; + : coverFile = File(json['cover_path']).isAbsolute + ? File(json['cover_path']) + : File(wd.path + json['cover_path']), + coverPath = json['cover_path'], + basePath = json['base_path'], + name = json['name'], + id = json['id']; Map toJson() => { - 'name': name, - 'id': id, - 'cover_path': coverPath, - 'base_path': basePath, - }; -} \ No newline at end of file + 'name': name, + 'id': id, + 'cover_path': coverPath, + 'base_path': basePath, + }; +} diff --git a/lib/pages/home/widgets/right_panel.dart b/lib/pages/home/widgets/right_panel.dart index 3119ecd..3b9d0ed 100644 --- a/lib/pages/home/widgets/right_panel.dart +++ b/lib/pages/home/widgets/right_panel.dart @@ -109,12 +109,19 @@ class _RightPanelState extends State { child: FittedBox( alignment: Alignment.topLeft, fit: BoxFit.fitHeight, - child: Text( - stickers.selectedPack!.name.toUpperCase(), - textAlign: TextAlign.left, - style: TextStyle( - color: theme.headerColor, - fontWeight: FontWeight.w500), + child: ValueListenableBuilder( + valueListenable: settings.globalSearch, + builder: (context, globalSearch, child) { + return Text( + (stickers.filteredWidgets ?? []).isNotEmpty && globalSearch + ? (searchController.text.isNotEmpty ? "GLOBAL SEARCH RESULTS" : "ALL STICKERS") + : stickers.selectedPack!.name.toUpperCase(), + textAlign: TextAlign.left, + style: TextStyle( + color: theme.headerColor, + fontWeight: FontWeight.w500), + ); + } ), ), ), @@ -217,36 +224,76 @@ class _RightPanelState extends State { textFieldFocus.requestFocus(); } }, - child: TextField( - focusNode: textFieldFocus, - autofocus: true, - controller: searchController, - onChanged: (value) { - query = value; - stickers.filterCurrentPack(value); - if (mounted) { - setState(() => noResult = - (stickers.filteredWidgets ?? []) - .isEmpty); - } - }, - maxLines: 1, - cursorColor: theme.inputTextColor, - decoration: InputDecoration( - isDense: true, - border: InputBorder.none, - hintText: - 'Search in current pack...', - hintStyle: TextStyle( - color: theme.inputHintColor)), - style: TextStyle( - color: theme.sbTextColor, - fontSize: 16, - ), + child: ValueListenableBuilder( + valueListenable: settings.globalSearch, + builder: (context, globalSearch, child) { + return TextField( + focusNode: textFieldFocus, + autofocus: true, + controller: searchController, + onChanged: (value) async { + query = value; + if (globalSearch) { + await stickers.filterAllPacks(value); + } else { + await stickers.filterCurrentPack(value); + } + if (mounted) { + setState(() => noResult = + (stickers.filteredWidgets ?? []) + .isEmpty); + } + }, + maxLines: 1, + cursorColor: theme.inputTextColor, + decoration: InputDecoration( + isDense: true, + border: InputBorder.none, + hintText: globalSearch ? + 'Search in all packs...' : + 'Search in current pack...', + hintStyle: TextStyle( + color: theme.inputHintColor)), + style: TextStyle( + color: theme.sbTextColor, + fontSize: 16, + ), + ); + } ), ), ), const SizedBox(width: 4), + MouseRegion( + cursor: SystemMouseCursors.click, + child: ValueListenableBuilder( + valueListenable: settings.globalSearch, + builder: (context, globalSearch, child) { + return GestureDetector( + onTap: () async { + settings.setGlobalSearch(!globalSearch); + if (globalSearch) { + await stickers.filterCurrentPack(searchController.text); + } else { + await stickers.filterAllPacks(searchController.text); + } + if (mounted) { + setState(() => noResult = + (stickers.filteredWidgets ?? []) + .isEmpty); + } + searchFocus.requestFocus(); + }, + child: Icon( + globalSearch + ? FluentSystemIcons.ic_fluent_grid_regular + : FluentSystemIcons.ic_fluent_globe_regular, + size: 18, + color: theme.sbTextColor)); + } + ), + ), + const SizedBox(width: 4), MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( diff --git a/lib/providers/config_provider.dart b/lib/providers/config_provider.dart index 552e480..db7203a 100644 --- a/lib/providers/config_provider.dart +++ b/lib/providers/config_provider.dart @@ -43,6 +43,7 @@ class ConfigProvider extends ChangeNotifier { String? token, int? copySize, bool? showSearchBar, + bool? globalSearch, List? stickerPacks, }) async { File configFile = File('${await getPath()}/config.json'); @@ -55,6 +56,7 @@ class ConfigProvider extends ChangeNotifier { if (token != null) updated.token = token; if (copySize != null) updated.copySize = copySize; if (showSearchBar != null) updated.showSearchBar = showSearchBar; + if (globalSearch != null) updated.globalSearch = globalSearch; if (stickerPacks != null) updated.stickerPacks = stickerPacks; } else { updated = await updater(config); diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 6cdf151..67e7a40 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -16,15 +16,19 @@ class SettingsProvider extends ChangeNotifier { ValueNotifier alwaysOnTop = ValueNotifier(false); ValueNotifier showSearchBar = ValueNotifier(true); + ValueNotifier globalSearch = ValueNotifier(false); ValueNotifier copySize = ValueNotifier(512); SettingsProvider(this.configProvider) { init(); } Future init() async { - alwaysOnTop.value = (await configProvider.getConfig()).alwaysOnTop; - showSearchBar.value = (await configProvider.getConfig()).showSearchBar; - copySize.value = (await configProvider.getConfig()).copySize; + final config = await configProvider.getConfig(); + + alwaysOnTop.value = config.alwaysOnTop; + showSearchBar.value = config.showSearchBar; + globalSearch.value = config.globalSearch; + copySize.value = config.copySize; } Future aotSwitch(bool aot) async { @@ -57,6 +61,15 @@ class SettingsProvider extends ChangeNotifier { }); } + Future setGlobalSearch(bool global) async { + globalSearch.value = global; + + await configProvider.updateConfig(updater: (value) async { + value.globalSearch = global; + return value; + }); + } + void setTabWidget(Widget widget, String name) { tabWidget = widget; currentTab = name; diff --git a/lib/providers/sticker_provider.dart b/lib/providers/sticker_provider.dart index 4706aeb..b7cb528 100644 --- a/lib/providers/sticker_provider.dart +++ b/lib/providers/sticker_provider.dart @@ -79,24 +79,30 @@ class StickerProvider extends ChangeNotifier { if (stickerPacks.isNotEmpty) changePack(index: stickerPacks.length - 1); } - Future loadCurrentPack() async { - if (selectedPack == null) return; + Future>> loadPackWidgets({ + String? id, + int? index, + StickerPackConfig? pack + }) async { + if (id != null) pack = stickerPacks.firstWhere((element) => element.id == id); + if (index != null) pack = stickerPacks[index]; var path = await configProvider.getPath(); - var imgPath = Directory(selectedPack!.basePath).isAbsolute ? Directory(selectedPack!.basePath) : Directory('$path${selectedPack!.basePath}'); + var imgPath = Directory(pack!.basePath).isAbsolute ? Directory(pack.basePath) : Directory('$path${pack.basePath}'); + + List> entryList = []; if (!await imgPath.exists()) { - selectedPackWidgets = []; - return; + return entryList; } + var fileList = imgPath.listSync(); fileList.removeWhere((element) => element.path.contains('cover.png')); - var supportedTypes = ['.jpg', '.jpeg', '.jfif', '.png', '.webp', '.gif', '.bmp']; + final supportedTypes = ['.jpg', '.jpeg', '.jfif', '.png', '.webp', '.gif', '.bmp']; - var entryList = >[]; for (var element in fileList) { if ((await element.stat()).type != FileSystemEntityType.file) continue; - if (!supportedTypes.contains(extension(element.path).toLowerCase())) continue; //{ + if (!supportedTypes.contains(extension(element.path).toLowerCase())) continue; String keywords = basename(element.path).split('-').last.replaceAll('_', ' '); String emoji = ''; @@ -108,7 +114,13 @@ class StickerProvider extends ChangeNotifier { entryList.add(MapEntry(emoji + keywords, SmallSticker(imageFile: File(element.path), emoji: emoji))); } - selectedPackWidgets = entryList; + return entryList; + } + + Future loadCurrentPack() async { + if (selectedPack == null) return; + + selectedPackWidgets = await loadPackWidgets(pack: selectedPack); notifyListeners(); } @@ -121,6 +133,23 @@ class StickerProvider extends ChangeNotifier { notifyListeners(); } + Future filterAllPacks(String query) async { + if (query.isEmpty) filteredWidgets = selectedPackWidgets; + List> results = []; + for (var pack in stickerPacks) { + var packEntries = await loadPackWidgets(pack: pack); + results.addAll( + packEntries.where((element) => + element.key + .toLowerCase() + .contains(query.toLowerCase()) + ) + ); + } + filteredWidgets = results; + notifyListeners(); + } + void setBigSticker(File? imageFile, String? emoji) { bigStickerEmoji = emoji; bigSticker = imageFile;