LCOV - code coverage report
Current view: top level - lib/views - globalsettingsappearanceview.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 283 0.0 %
Date: 2024-02-26 20:09:01 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:io';
       2             : 
       3             : import 'package:flutter/material.dart';
       4             : import 'package:provider/provider.dart';
       5             : import 'package:flutter_gen/gen_l10n/app_localizations.dart';
       6             : import 'package:path/path.dart' as path;
       7             : 
       8             : import '../config.dart';
       9             : import '../controllers/filesharing.dart';
      10             : import '../cwtch_icons_icons.dart';
      11             : import '../main.dart';
      12             : import '../models/appstate.dart';
      13             : import '../settings.dart';
      14             : import '../themes/cwtch.dart';
      15             : import '../themes/opaque.dart';
      16             : import '../themes/yamltheme.dart';
      17             : import 'globalsettingsview.dart';
      18             : 
      19             : class GlobalSettingsAppearanceView extends StatefulWidget {
      20           0 :   @override
      21           0 :   _GlobalSettingsAppearanceViewState createState() => _GlobalSettingsAppearanceViewState();
      22             : }
      23             : 
      24             : class _GlobalSettingsAppearanceViewState extends State<GlobalSettingsAppearanceView> {
      25             :   ScrollController settingsListScrollController = ScrollController();
      26             : 
      27           0 :   Widget build(BuildContext context) {
      28           0 :     return Consumer<Settings>(builder: (ccontext, settings, child) {
      29           0 :       return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) {
      30           0 :         return Scrollbar(
      31           0 :             key: Key("AppearanceSettingsView"),
      32             :             trackVisibility: true,
      33           0 :             controller: settingsListScrollController,
      34           0 :             child: SingleChildScrollView(
      35             :                 clipBehavior: Clip.antiAlias,
      36           0 :                 controller: settingsListScrollController,
      37           0 :                 padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
      38           0 :                 child: ConstrainedBox(
      39           0 :                     constraints: BoxConstraints(
      40           0 :                       minHeight: viewportConstraints.maxHeight,
      41             :                     ),
      42           0 :                     child: Column(children: [
      43           0 :                       ListTile(
      44           0 :                           title: Text(AppLocalizations.of(context)!.settingLanguage),
      45           0 :                           leading: Icon(CwtchIcons.change_language, color: settings.current().mainTextColor),
      46           0 :                           trailing: Container(
      47           0 :                               width: MediaQuery.of(context).size.width / 4,
      48           0 :                               child: DropdownButton(
      49           0 :                                   key: Key("languagelist"),
      50             :                                   isExpanded: true,
      51           0 :                                   value: Provider.of<Settings>(context).locale.toString(),
      52           0 :                                   onChanged: (String? newValue) {
      53           0 :                                     setState(() {
      54           0 :                                       EnvironmentConfig.debugLog("setting language: $newValue");
      55           0 :                                       settings.switchLocaleByCode(newValue!);
      56           0 :                                       saveSettings(context);
      57             :                                     });
      58             :                                   },
      59           0 :                                   items: AppLocalizations.supportedLocales.map<DropdownMenuItem<String>>((Locale value) {
      60           0 :                                     return DropdownMenuItem<String>(
      61           0 :                                       value: value.toString(),
      62           0 :                                       child: Text(
      63           0 :                                         key: Key("dropdownLanguage" + value.languageCode),
      64           0 :                                         getLanguageFull(context, value.languageCode, value.countryCode),
      65           0 :                                         style: settings.scaleFonts(defaultDropDownMenuItemTextStyle),
      66             :                                         overflow: TextOverflow.ellipsis,
      67             :                                       ),
      68             :                                     );
      69           0 :                                   }).toList()))),
      70           0 :                       SwitchListTile(
      71           0 :                         title: Text(AppLocalizations.of(context)!.settingTheme),
      72           0 :                         value: settings.current().mode == mode_light,
      73           0 :                         onChanged: (bool value) {
      74             :                           if (value) {
      75           0 :                             settings.setTheme(settings.theme.theme, mode_light);
      76             :                           } else {
      77           0 :                             settings.setTheme(settings.theme.theme, mode_dark);
      78             :                           }
      79             : 
      80             : // Save Settings...
      81           0 :                           saveSettings(context);
      82             :                         },
      83           0 :                         activeTrackColor: settings.theme.defaultButtonColor,
      84           0 :                         inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
      85           0 :                         secondary: Icon(CwtchIcons.change_theme, color: settings.current().mainTextColor),
      86             :                       ),
      87           0 :                       ListTile(
      88           0 :                         title: Text(AppLocalizations.of(context)!.themeColorLabel),
      89           0 :                         trailing: Container(
      90           0 :                             width: MediaQuery.of(context).size.width / 4,
      91           0 :                             child: DropdownButton<String>(
      92           0 :                                 key: Key("DropdownTheme"),
      93             :                                 isExpanded: true,
      94           0 :                                 value: Provider.of<Settings>(context).themeId,
      95           0 :                                 onChanged: (String? newValue) {
      96           0 :                                   setState(() {
      97           0 :                                     settings.setTheme(newValue!, settings.theme.mode);
      98           0 :                                     saveSettings(context);
      99             :                                   });
     100             :                                 },
     101           0 :                                 items: settings.themeloader.themes.keys.map<DropdownMenuItem<String>>((String themeId) {
     102           0 :                                   return DropdownMenuItem<String>(
     103             :                                     value: themeId,
     104           0 :                                     child: Text(getThemeName(context, settings, themeId), style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)), //"ddi_$themeId", key: Key("ddi_$themeId")),
     105             :                                   );
     106           0 :                                 }).toList())),
     107           0 :                         leading: Icon(Icons.palette, color: settings.current().mainTextColor),
     108             :                       ),
     109           0 :                       Visibility(
     110             :                           // TODO: Android support needs gomobile support for reading / writing themes, and ideally importing from a .zip or .tar.gz
     111           0 :                           visible: !Platform.isAndroid,
     112           0 :                           child: ListTile(
     113           0 :                               leading: Icon(Icons.palette, color: Provider.of<Settings>(context).theme.messageFromMeTextColor),
     114           0 :                               title: Text(AppLocalizations.of(context)!.settingsImportThemeTitle),
     115           0 :                               subtitle: Text(AppLocalizations.of(context)!.settingsImportThemeDescription),
     116             :                               //AppLocalizations.of(
     117             :                               //context)!
     118             :                               //.fileSharingSettingsDownloadFolderDescription,
     119           0 :                               trailing: Container(
     120           0 :                                   width: MediaQuery.of(context).size.width / 4,
     121           0 :                                   child: ElevatedButton.icon(
     122           0 :                                     label: Text(AppLocalizations.of(context)!.settingsImportThemeButton),
     123           0 :                                     onPressed: Provider.of<AppState>(context).disableFilePicker
     124             :                                         ? null
     125           0 :                                         : () async {
     126           0 :                                             if (Platform.isAndroid) {
     127             :                                               return;
     128             :                                             }
     129           0 :                                             var selectedDirectory = await showSelectDirectoryPicker(context);
     130             :                                             if (selectedDirectory != null) {
     131           0 :                                               selectedDirectory += path.separator;
     132           0 :                                               final customThemeDir = path.join(await Provider.of<FlwtchState>(context, listen: false).cwtch.getCwtchDir(), custom_themes_subdir);
     133           0 :                                               importThemeCheck(context, settings, customThemeDir, selectedDirectory);
     134             :                                             } else {
     135             :                                               // User canceled the picker
     136             :                                             }
     137             :                                           },
     138             :                                     //onChanged: widget.onSave,
     139           0 :                                     icon: Icon(Icons.folder),
     140             :                                     //tooltip: widget.tooltip,
     141             :                                   )))),
     142           0 :                       SwitchListTile(
     143           0 :                         title: Text(AppLocalizations.of(context)!.settingsThemeImages),
     144           0 :                         subtitle: Text(AppLocalizations.of(context)!.settingsThemeImagesDescription),
     145           0 :                         value: settings.themeImages,
     146           0 :                         onChanged: (bool value) {
     147           0 :                           settings.themeImages = value; // Save Settings...
     148           0 :                           saveSettings(context);
     149             :                         },
     150           0 :                         activeTrackColor: settings.theme.defaultButtonColor,
     151           0 :                         inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
     152           0 :                         secondary: Icon(Icons.image, color: settings.current().mainTextColor),
     153             :                       ),
     154           0 :                       ListTile(
     155           0 :                           title: Text(AppLocalizations.of(context)!.settingUIColumnPortrait),
     156           0 :                           leading: Icon(Icons.table_chart, color: settings.current().mainTextColor),
     157           0 :                           trailing: Container(
     158           0 :                               width: MediaQuery.of(context).size.width / 4,
     159           0 :                               child: DropdownButton(
     160             :                                   isExpanded: true,
     161           0 :                                   value: settings.uiColumnModePortrait.toString(),
     162           0 :                                   onChanged: (String? newValue) {
     163           0 :                                     settings.uiColumnModePortrait = Settings.uiColumnModeFromString(newValue!);
     164           0 :                                     saveSettings(context);
     165             :                                   },
     166           0 :                                   items: Settings.uiColumnModeOptions(false).map<DropdownMenuItem<String>>((DualpaneMode value) {
     167           0 :                                     return DropdownMenuItem<String>(
     168           0 :                                       value: value.toString(),
     169           0 :                                       child: Text(Settings.uiColumnModeToString(value, context), style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
     170             :                                     );
     171           0 :                                   }).toList()))),
     172           0 :                       ListTile(
     173           0 :                           title: Text(
     174           0 :                             AppLocalizations.of(context)!.settingUIColumnLandscape,
     175             :                             textWidthBasis: TextWidthBasis.longestLine,
     176             :                             softWrap: true,
     177             :                           ),
     178           0 :                           leading: Icon(Icons.stay_primary_landscape, color: settings.current().mainTextColor),
     179           0 :                           trailing: Container(
     180           0 :                               width: MediaQuery.of(context).size.width / 4,
     181           0 :                               child: Container(
     182           0 :                                   width: MediaQuery.of(context).size.width / 4,
     183           0 :                                   child: DropdownButton(
     184             :                                       isExpanded: true,
     185           0 :                                       value: settings.uiColumnModeLandscape.toString(),
     186           0 :                                       onChanged: (String? newValue) {
     187           0 :                                         settings.uiColumnModeLandscape = Settings.uiColumnModeFromString(newValue!);
     188           0 :                                         saveSettings(context);
     189             :                                       },
     190           0 :                                       items: Settings.uiColumnModeOptions(true).map<DropdownMenuItem<String>>((DualpaneMode value) {
     191           0 :                                         return DropdownMenuItem<String>(
     192           0 :                                           value: value.toString(),
     193           0 :                                           child: Text(Settings.uiColumnModeToString(value, context), overflow: TextOverflow.ellipsis, style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
     194             :                                         );
     195           0 :                                       }).toList())))),
     196           0 :                       ListTile(
     197           0 :                         title: Text(AppLocalizations.of(context)!.defaultScalingText),
     198           0 :                         subtitle: Text(AppLocalizations.of(context)!.fontScalingDescription),
     199           0 :                         trailing: Container(
     200           0 :                             width: MediaQuery.of(context).size.width / 4,
     201           0 :                             child: Slider(
     202           0 :                                 onChanged: (double value) {
     203           0 :                                   settings.fontScaling = value;
     204             : // Save Settings...
     205           0 :                                   saveSettings(context);
     206           0 :                                   EnvironmentConfig.debugLog("Font Scaling: $value");
     207             :                                 },
     208             :                                 min: 0.5,
     209             :                                 divisions: 12,
     210             :                                 max: 2.0,
     211           0 :                                 label: '${settings.fontScaling * 100}%',
     212           0 :                                 activeColor: settings.current().defaultButtonColor,
     213           0 :                                 thumbColor: settings.current().mainTextColor,
     214           0 :                                 overlayColor: MaterialStateProperty.all(settings.current().mainTextColor),
     215           0 :                                 inactiveColor: settings.theme.defaultButtonDisabledColor,
     216           0 :                                 value: settings.fontScaling)),
     217           0 :                         leading: Icon(Icons.format_size, color: settings.current().mainTextColor),
     218             :                       ),
     219           0 :                       SwitchListTile(
     220           0 :                         title: Text(AppLocalizations.of(context)!.streamerModeLabel),
     221           0 :                         subtitle: Text(AppLocalizations.of(context)!.descriptionStreamerMode),
     222           0 :                         value: settings.streamerMode,
     223           0 :                         onChanged: (bool value) {
     224           0 :                           settings.setStreamerMode(value);
     225             : // Save Settings...
     226           0 :                           saveSettings(context);
     227             :                         },
     228           0 :                         activeTrackColor: settings.theme.defaultButtonColor,
     229           0 :                         inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
     230           0 :                         secondary: Icon(CwtchIcons.streamer_bunnymask, color: settings.current().mainTextColor),
     231             :                       ),
     232           0 :                       SwitchListTile(
     233           0 :                         title: Text(AppLocalizations.of(context)!.formattingExperiment),
     234           0 :                         subtitle: Text(AppLocalizations.of(context)!.messageFormattingDescription),
     235           0 :                         value: settings.isExperimentEnabled(FormattingExperiment),
     236           0 :                         onChanged: (bool value) {
     237             :                           if (value) {
     238           0 :                             settings.enableExperiment(FormattingExperiment);
     239             :                           } else {
     240           0 :                             settings.disableExperiment(FormattingExperiment);
     241             :                           }
     242           0 :                           saveSettings(context);
     243             :                         },
     244           0 :                         activeTrackColor: settings.theme.defaultButtonActiveColor,
     245           0 :                         inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
     246           0 :                         secondary: Icon(Icons.text_fields, color: settings.current().mainTextColor),
     247             :                       ),
     248             :                     ]))));
     249             :       });
     250             :     });
     251             :   }
     252             : 
     253             :   /// A slightly verbose way to extract the full language name from
     254             :   /// an individual language code. There might be a more efficient way of doing this.
     255           0 :   String getLanguageFull(context, String languageCode, String? countryCode) {
     256           0 :     if (languageCode == "en") {
     257           0 :       return AppLocalizations.of(context)!.localeEn;
     258             :     }
     259           0 :     if (languageCode == "es") {
     260           0 :       return AppLocalizations.of(context)!.localeEs;
     261             :     }
     262           0 :     if (languageCode == "fr") {
     263           0 :       return AppLocalizations.of(context)!.localeFr;
     264             :     }
     265           0 :     if (languageCode == "pt" && countryCode == "BR") {
     266           0 :       return AppLocalizations.of(context)!.localePtBr;
     267             :     }
     268           0 :     if (languageCode == "pt") {
     269           0 :       return AppLocalizations.of(context)!.localePt;
     270             :     }
     271           0 :     if (languageCode == "de") {
     272           0 :       return AppLocalizations.of(context)!.localeDe;
     273             :     }
     274           0 :     if (languageCode == "el") {
     275           0 :       return AppLocalizations.of(context)!.localeEl;
     276             :     }
     277           0 :     if (languageCode == "it") {
     278           0 :       return AppLocalizations.of(context)!.localeIt;
     279             :     }
     280           0 :     if (languageCode == "no") {
     281           0 :       return AppLocalizations.of(context)!.localeNo;
     282             :     }
     283           0 :     if (languageCode == "pl") {
     284           0 :       return AppLocalizations.of(context)!.localePl;
     285             :     }
     286           0 :     if (languageCode == "lb") {
     287           0 :       return AppLocalizations.of(context)!.localeLb;
     288             :     }
     289           0 :     if (languageCode == "ru") {
     290           0 :       return AppLocalizations.of(context)!.localeRU;
     291             :     }
     292           0 :     if (languageCode == "ro") {
     293           0 :       return AppLocalizations.of(context)!.localeRo;
     294             :     }
     295           0 :     if (languageCode == "cy") {
     296           0 :       return AppLocalizations.of(context)!.localeCy;
     297             :     }
     298           0 :     if (languageCode == "da") {
     299           0 :       return AppLocalizations.of(context)!.localeDa;
     300             :     }
     301           0 :     if (languageCode == "tr") {
     302           0 :       return AppLocalizations.of(context)!.localeTr;
     303             :     }
     304           0 :     if (languageCode == "nl") {
     305           0 :       return AppLocalizations.of(context)!.localeNl;
     306             :     }
     307           0 :     if (languageCode == "sk") {
     308           0 :       return AppLocalizations.of(context)!.localeSk;
     309             :     }
     310           0 :     if (languageCode == "ko") {
     311           0 :       return AppLocalizations.of(context)!.localeKo;
     312             :     }
     313           0 :     if (languageCode == "ja") {
     314           0 :       return AppLocalizations.of(context)!.localeJa;
     315             :     }
     316           0 :     if (languageCode == "sv") {
     317           0 :       return AppLocalizations.of(context)!.localeSv;
     318             :     }
     319           0 :     if (languageCode == "sw") {
     320           0 :       return AppLocalizations.of(context)!.localeSw;
     321             :     }
     322           0 :     if (languageCode == "uk") {
     323           0 :       return AppLocalizations.of(context)!.localeUk;
     324             :     }
     325           0 :     if (languageCode == "uz") {
     326           0 :       return AppLocalizations.of(context)!.localeUzbek;
     327             :     }
     328             :     return languageCode;
     329             :   }
     330             : 
     331             :   /// Since we don't seem to able to dynamically pull translations, this function maps themes to their names
     332           0 :   String getThemeName(context, Settings settings, String theme) {
     333             :     switch (theme) {
     334           0 :       case cwtch_theme:
     335           0 :         return AppLocalizations.of(context)!.themeNameCwtch;
     336           0 :       case "ghost":
     337           0 :         return AppLocalizations.of(context)!.themeNameGhost;
     338           0 :       case "mermaid":
     339           0 :         return AppLocalizations.of(context)!.themeNameMermaid;
     340           0 :       case "midnight":
     341           0 :         return AppLocalizations.of(context)!.themeNameMidnight;
     342           0 :       case "neon1":
     343           0 :         return AppLocalizations.of(context)!.themeNameNeon1;
     344           0 :       case "neon2":
     345           0 :         return AppLocalizations.of(context)!.themeNameNeon2;
     346           0 :       case "pumpkin":
     347           0 :         return AppLocalizations.of(context)!.themeNamePumpkin;
     348           0 :       case "vampire":
     349           0 :         return AppLocalizations.of(context)!.themeNameVampire;
     350           0 :       case "witch":
     351           0 :         return AppLocalizations.of(context)!.themeNameWitch;
     352           0 :       case "juniper":
     353             :         return "Juniper"; // Juniper is a noun, and doesn't get subject to translation...
     354             :     }
     355           0 :     return settings.themeloader.themes[theme]?[mode_light]?.theme ?? settings.themeloader.themes[theme]?[mode_dark]?.theme ?? theme;
     356             :   }
     357             : 
     358           0 :   void importThemeCheck(BuildContext context, Settings settings, String themesDir, String newThemeDirectory) async {
     359             :     // check is theme
     360           0 :     final srcDir = Directory(newThemeDirectory);
     361           0 :     String themeName = path.basename(newThemeDirectory);
     362             : 
     363           0 :     File themeFile = File(path.join(newThemeDirectory, "theme.yml"));
     364             : 
     365           0 :     if (!themeFile.existsSync()) {
     366             :       // error, isnt valid theme, no .yml theme file found
     367           0 :       SnackBar err = SnackBar(content: Text(AppLocalizations.of(context)!.settingsThemeErrorInvalid.replaceAll("\$themeName", themeName)));
     368           0 :       ScaffoldMessenger.of(context).showSnackBar(err);
     369             :       return;
     370             :     }
     371             : 
     372           0 :     Directory targetDir = Directory(path.join(themesDir, themeName));
     373             :     // check if exists
     374           0 :     if (settings.themeloader.themes.containsKey(themeName) || targetDir.existsSync()) {
     375           0 :       _modalConfirmOverwriteCustomTheme(srcDir, targetDir, themesDir, themeName, settings);
     376             :     } else {
     377           0 :       importTheme(srcDir, targetDir, themesDir, themeName, settings);
     378             :     }
     379             :   }
     380             : 
     381           0 :   void importTheme(Directory srcDir, Directory targetDir, String themesDir, String themeName, Settings settings) async {
     382           0 :     if (!targetDir.existsSync()) {
     383           0 :       targetDir = await targetDir.create();
     384             :     }
     385             : 
     386             :     // importTheme(newVal)
     387           0 :     await for (var entity in srcDir.list(recursive: false)) {
     388           0 :       if (entity is File) {
     389           0 :         entity.copySync(path.join(targetDir.path, path.basename(entity.path)));
     390             :       }
     391             :     }
     392             : 
     393           0 :     var data = await loadFileYamlTheme(path.join(targetDir.path, "theme.yml"), targetDir.path);
     394             :     if (data != null) {
     395           0 :       settings.themeloader.themes[themeName] = data;
     396             :     }
     397             : 
     398           0 :     if (settings.current().theme == themeName) {
     399           0 :       settings.setTheme(themeName, settings.current().mode);
     400             :     }
     401             :   }
     402             : 
     403           0 :   void _modalConfirmOverwriteCustomTheme(Directory srcDir, Directory targetDir, String themesDir, String themeName, Settings settings) {
     404           0 :     showModalBottomSheet<void>(
     405           0 :         context: context,
     406             :         isScrollControlled: true,
     407           0 :         builder: (BuildContext context) {
     408           0 :           return Padding(
     409           0 :               padding: MediaQuery.of(context).viewInsets,
     410           0 :               child: RepaintBoundary(
     411           0 :                   child: Container(
     412           0 :                       height: Platform.isAndroid ? 250 : 200, // bespoke value courtesy of the [TextField] docs
     413           0 :                       child: Center(
     414           0 :                           child: Padding(
     415           0 :                               padding: EdgeInsets.all(10.0),
     416           0 :                               child: Column(mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: <Widget>[
     417           0 :                                 Text(AppLocalizations.of(context)!.settingThemeOverwriteQuestion.replaceAll("\$themeName", themeName)),
     418           0 :                                 SizedBox(
     419             :                                   height: 20,
     420             :                                 ),
     421           0 :                                 Row(
     422             :                                   mainAxisAlignment: MainAxisAlignment.spaceEvenly,
     423           0 :                                   children: [
     424           0 :                                     Spacer(),
     425           0 :                                     Expanded(
     426           0 :                                         child: ElevatedButton(
     427           0 :                                       child: Text(AppLocalizations.of(context)!.settingThemeOverwriteConfirm, semanticsLabel: AppLocalizations.of(context)!.settingThemeOverwriteConfirm),
     428           0 :                                       onPressed: () {
     429           0 :                                         importTheme(srcDir, targetDir, themesDir, themeName, settings);
     430             : 
     431           0 :                                         Navigator.pop(context);
     432             :                                       },
     433             :                                     )),
     434           0 :                                     SizedBox(
     435             :                                       width: 20,
     436             :                                     ),
     437           0 :                                     Expanded(
     438           0 :                                         child: ElevatedButton(
     439           0 :                                       child: Text(AppLocalizations.of(context)!.cancel, semanticsLabel: AppLocalizations.of(context)!.cancel),
     440           0 :                                       onPressed: () {
     441           0 :                                         Navigator.pop(context);
     442             :                                       },
     443             :                                     )),
     444           0 :                                     Spacer(),
     445             :                                   ],
     446             :                                 )
     447             :                               ]))))));
     448             :         });
     449             :   }
     450             : }

Generated by: LCOV version 1.14