LCOV - code coverage report
Current view: top level - lib/themes - yamltheme.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 120 0.0 %
Date: 2026-07-15 19:28:30 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:convert';
       2             : import 'dart:io';
       3             : 
       4             : import 'package:cwtch/themes/cwtch.dart';
       5             : import 'package:cwtch/themes/opaque.dart';
       6             : import 'package:flutter/material.dart';
       7             : import 'package:flutter/services.dart';
       8             : import 'package:provider/provider.dart';
       9             : import 'package:yaml/yaml.dart';
      10             : import 'package:path/path.dart' as path;
      11             : 
      12             : import '../main.dart';
      13             : 
      14           0 : Future<Map<String, Map<String, OpaqueThemeType>>> loadBuiltinThemes() async {
      15             :   //final manifestJson = await rootBundle.loadString('AssetManifest.json');
      16             : 
      17           0 :   final assetManifest = await AssetManifest.loadFromAssetBundle(rootBundle);
      18           0 :   final themesList = assetManifest.listAssets().where((String key) => key.startsWith('assets/themes'));
      19             : 
      20             :   //final themesList = json.decode(manifestJson).keys.where((String key) => key.startsWith('assets/themes'));
      21           0 :   Map<String, Map<String, OpaqueThemeType>> themes = Map();
      22             : 
      23           0 :   for (String themefile in themesList) {
      24           0 :     if (themefile.substring(themefile.length - 4) != ".yml") {
      25             :       continue;
      26             :     }
      27             : 
      28             :     try {
      29           0 :       var data = await loadAssetYamlTheme(themefile);
      30             : 
      31             :       if (data != null) {
      32             :         // remove "assets/themes" and "theme.yml" from name
      33           0 :         var themename = themefile.substring(14, themefile.lastIndexOf("/"));
      34           0 :         themes[themename] = data;
      35             :       }
      36             :     } catch (e) {
      37           0 :       print("Failed to load theme: $themefile with exception: $e");
      38             :     }
      39             :   }
      40             :   return themes;
      41             : }
      42             : 
      43           0 : Future<Map<String, Map<String, OpaqueThemeType>>> loadCustomThemes(String themesDirPath) async {
      44           0 :   Map<String, Map<String, OpaqueThemeType>> themes = Map();
      45             : 
      46           0 :   Directory themesDir = Directory(themesDirPath);
      47           0 :   if (!themesDir.existsSync()) {
      48           0 :     themesDir.createSync(recursive: true);
      49             :   }
      50           0 :   await themesDir.list(recursive: false).forEach((themeDir) async {
      51             :     //final themeDir = Directory(path.join(themesDir, themedir));
      52           0 :     File themefile = File(path.join(themeDir.path, "theme.yml"));
      53           0 :     if (themefile.existsSync()) {
      54             :       try {
      55           0 :         var data = await loadFileYamlTheme(themefile.path, themeDir.path);
      56             : 
      57             :         if (data != null) {
      58           0 :           themes[path.basename(themeDir.path)] = data;
      59             :         }
      60             :       } catch (e) {
      61           0 :         print("Failed to load theme: $themefile with exception: $e");
      62             :       }
      63             :     }
      64             :   });
      65             : 
      66             :   return themes;
      67             : }
      68             : 
      69           0 : Future<YamlMap?> readAssetYamlTheme(String themefile) async {
      70           0 :   final contents = await rootBundle.loadString(themefile);
      71           0 :   return loadYaml(contents);
      72             : }
      73             : 
      74           0 : Future<Map<String, OpaqueThemeType>?> loadAssetYamlTheme(String themefile) async {
      75           0 :   final yml = await readAssetYamlTheme(themefile);
      76             : 
      77             :   if (yml == null) {
      78           0 :     print("Error: failed to load theme: $themefile");
      79             :     return null;
      80             :   }
      81           0 :   return loadYamlTheme(yml);
      82             : }
      83             : 
      84           0 : Future<Map<String, OpaqueThemeType>?> loadFileYamlTheme(String themefile, String assetsDir) async {
      85           0 :   final file = File(themefile);
      86           0 :   if (!file.existsSync()) {
      87           0 :     print("Error: theme file: $themefile does not exist");
      88             :     return null;
      89             :   }
      90           0 :   final contents = file.readAsStringSync();
      91           0 :   final yml = await loadYaml(contents);
      92             :   if (yml == null) {
      93           0 :     print("Error: failed to load theme: $themefile");
      94             :     return null;
      95             :   }
      96           0 :   return loadYamlTheme(yml, assetsDir);
      97             : }
      98             : 
      99           0 : Future<Map<String, OpaqueThemeType>?> loadYamlTheme(YamlMap yml, [String? assetsDir]) async {
     100           0 :   Map<String, OpaqueThemeType> subthemes = Map();
     101           0 :   if ((yml["themes"] as YamlMap).containsKey(mode_dark)) {
     102           0 :     subthemes[mode_dark] = YmlTheme(yml, yml["themes"]["name"], mode_dark, assetsDir);
     103             :   }
     104           0 :   if ((yml["themes"] as YamlMap).containsKey(mode_light)) {
     105           0 :     subthemes[mode_light] = YmlTheme(yml, yml["themes"]["name"], mode_light, assetsDir);
     106             :   }
     107             :   return subthemes;
     108             : }
     109             : 
     110             : class YmlTheme extends OpaqueThemeType {
     111             :   late YamlMap yml;
     112             :   late String mode;
     113             :   late String theme;
     114             :   late String? assetsDir;
     115             :   late OpaqueThemeType fallbackTheme;
     116             : 
     117           0 :   YmlTheme(YamlMap yml, theme, mode, assetsDir) {
     118           0 :     this.yml = yml;
     119           0 :     this.theme = theme;
     120           0 :     this.mode = mode;
     121           0 :     this.assetsDir = assetsDir;
     122           0 :     if (mode == mode_dark) {
     123           0 :       fallbackTheme = CwtchDark();
     124             :     } else {
     125           0 :       fallbackTheme = CwtchLight();
     126             :     }
     127             :   }
     128             : 
     129           0 :   Color? getColor(String name) {
     130           0 :     var val = yml["themes"][mode]["theme"][name];
     131           0 :     if (!(val is int)) {
     132           0 :       val = yml["themes"][mode]["theme"][val] ?? val;
     133             :     }
     134           0 :     if (!(val is int)) {
     135           0 :       val = yml["themes"][mode]?["colors"]?[val] ?? val;
     136             :     }
     137           0 :     if (!(val is int)) {
     138           0 :       val = yml["colors"]?[val];
     139             :     }
     140           0 :     if (!(val is int)) {
     141             :       return null;
     142             :     }
     143             : 
     144           0 :     if (val <= 0xFFFFFF) {
     145           0 :       return Color(0xFF000000 + val);
     146             :     } else {
     147           0 :       return Color(val);
     148             :     }
     149             :   }
     150             : 
     151           0 :   String? getImage(String name) {
     152           0 :     var val = yml["themes"][mode]["theme"]?[name];
     153             :     if (val != null) {
     154           0 :       if (assetsDir == null) {
     155           0 :         return path.join("assets", "themes", yml["themes"]["name"], val);
     156             :       } else {
     157             :         return val;
     158             :       }
     159             :     }
     160             :     return null;
     161             :   }
     162             : 
     163           0 :   get backgroundMainColor => getColor("backgroundMainColor") ?? fallbackTheme.backgroundMainColor;
     164           0 :   get backgroundPaneColor => getColor("backgroundPaneColor") ?? fallbackTheme.backgroundPaneColor;
     165           0 :   get topbarColor => getColor("topbarColor") ?? fallbackTheme.topbarColor;
     166           0 :   get mainTextColor => getColor("mainTextColor") ?? fallbackTheme.mainTextColor;
     167           0 :   get hilightElementColor => getColor("hilightElementColor") ?? fallbackTheme.hilightElementColor;
     168           0 :   get backgroundHilightElementColor => getColor("backgroundHilightElementColor") ?? fallbackTheme.backgroundHilightElementColor;
     169           0 :   get sendHintTextColor => getColor("sendHintTextColor") ?? fallbackTheme.sendHintTextColor;
     170           0 :   get defaultButtonColor => getColor("defaultButtonColor") ?? fallbackTheme.defaultButtonColor;
     171           0 :   get defaultButtonActiveColor => /*mode == mode_light ? darken(defaultButtonColor) :*/ lighten(getColor("defaultButtonColor") ?? fallbackTheme.defaultButtonColor);
     172           0 :   get defaultButtonTextColor => getColor("defaultButtonTextColor") ?? fallbackTheme.defaultButtonTextColor;
     173           0 :   get defaultButtonDisabledColor => getColor("defaultButtonDisabledColor") ?? fallbackTheme.defaultButtonDisabledColor;
     174           0 :   get textfieldBackgroundColor => getColor("textfieldBackgroundColor") ?? fallbackTheme.textfieldBackgroundColor;
     175           0 :   get textfieldBorderColor => getColor("textfieldBorderColor") ?? fallbackTheme.textfieldBorderColor;
     176           0 :   get textfieldHintColor => getColor("textfieldHintColor") ?? fallbackTheme.textfieldHintColor;
     177           0 :   get textfieldErrorColor => getColor("textfieldErrorColor") ?? fallbackTheme.textfieldErrorColor;
     178           0 :   get textfieldSelectionColor => getColor("textfieldSelectionColor") ?? fallbackTheme.textfieldSelectionColor;
     179           0 :   get scrollbarDefaultColor => getColor("scrollbarDefaultColor") ?? fallbackTheme.scrollbarDefaultColor;
     180           0 :   get portraitBackgroundColor => getColor("portraitBackgroundColor") ?? fallbackTheme.portraitBackgroundColor;
     181           0 :   get portraitOnlineBorderColor => getColor("portraitOnlineBorderColor") ?? fallbackTheme.portraitOnlineBorderColor;
     182           0 :   get portraitOfflineBorderColor => getColor("portraitOfflineBorderColor") ?? fallbackTheme.portraitOfflineBorderColor;
     183           0 :   get portraitBlockedBorderColor => getColor("portraitBlockedBorderColor") ?? fallbackTheme.portraitBlockedBorderColor;
     184           0 :   get portraitBlockedTextColor => getColor("portraitBlockedTextColor") ?? fallbackTheme.portraitBlockedTextColor;
     185           0 :   get portraitContactBadgeColor => getColor("portraitContactBadgeColor") ?? fallbackTheme.portraitContactBadgeColor;
     186           0 :   get portraitContactBadgeTextColor => getColor("portraitContactBadgeTextColor") ?? fallbackTheme.portraitContactBadgeTextColor;
     187           0 :   get portraitProfileBadgeColor => getColor("portraitProfileBadgeColor") ?? fallbackTheme.portraitProfileBadgeColor;
     188           0 :   get portraitProfileBadgeTextColor => getColor("portraitProfileBadgeTextColor") ?? fallbackTheme.portraitProfileBadgeTextColor;
     189           0 :   get portraitOnlineAwayColor => getColor("portraitOnlineAwayColor") ?? fallbackTheme.portraitOnlineAwayColor;
     190           0 :   get portraitOnlineBusyColor => getColor("portraitOnlineBusyColor") ?? fallbackTheme.portraitOnlineBusyColor;
     191           0 :   get dropShadowColor => getColor("dropShadowColor") ?? fallbackTheme.dropShadowColor;
     192           0 :   get toolbarIconColor => getColor("toolbarIconColor") ?? fallbackTheme.toolbarIconColor;
     193           0 :   get toolbarBackgroundColor => getColor("toolbarBackgroundColor") ?? fallbackTheme.toolbarBackgroundColor;
     194           0 :   get chatReactionIconColor => getColor("chatReactionIconColor") ?? fallbackTheme.chatReactionIconColor;
     195           0 :   get messageFromMeBackgroundColor => getColor("messageFromMeBackgroundColor") ?? fallbackTheme.messageFromMeBackgroundColor;
     196           0 :   get messageFromMeTextColor => getColor("messageFromMeTextColor") ?? fallbackTheme.messageFromMeTextColor;
     197           0 :   get messageFromOtherBackgroundColor => getColor("messageFromOtherBackgroundColor") ?? fallbackTheme.messageFromOtherBackgroundColor;
     198           0 :   get messageFromOtherTextColor => getColor("messageFromOtherTextColor") ?? fallbackTheme.messageFromOtherTextColor;
     199           0 :   get messageSelectionColor => getColor("messageSelectionColor") ?? fallbackTheme.messageSelectionColor;
     200           0 :   get menuBackgroundColor => getColor("menuBackgroundColor") ?? fallbackTheme.menuBackgroundColor;
     201           0 :   get snackbarBackgroundColor => getColor("snackbarBackgroundColor") ?? fallbackTheme.snackbarBackgroundColor;
     202           0 :   get snackbarTextColor => getColor("snackbarTextColor") ?? fallbackTheme.snackbarTextColor;
     203             : 
     204             :   // Images
     205             : 
     206           0 :   get chatImageColor => getColor("chatImageColor") ?? fallbackTheme.chatImageColor;
     207           0 :   get chatImage => getImage("chatImage") ?? fallbackTheme.chatImage;
     208             : 
     209           0 :   ImageProvider loadImage(String key, {BuildContext? context}) {
     210           0 :     File f = File(key);
     211           0 :     if (f.existsSync()) {
     212           0 :       return FileImage(f);
     213             :     }
     214             : 
     215           0 :     final assetsDir = this.assetsDir;
     216             :     if (assetsDir != null) {
     217           0 :       File f = File(path.join(assetsDir, key));
     218           0 :       if (f.existsSync()) {
     219           0 :         return FileImage(f);
     220             :       }
     221             :     }
     222             : 
     223             :     if (context != null) {
     224           0 :       File af = File(path.join(Provider.of<FlwtchState>(context, listen: false).cwtch.getAssetsDir(), key));
     225           0 :       if (af.existsSync()) {
     226           0 :         return FileImage(af);
     227             :       }
     228             :     }
     229             : 
     230           0 :     return AssetImage(key);
     231             :   }
     232             : }

Generated by: LCOV version 1.14