LCOV - code coverage report
Current view: top level - lib/themes - opaque.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 124 211 58.8 %
Date: 2024-09-24 21:50:54 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:io';
       2             : import 'dart:core';
       3             : 
       4             : import 'package:cwtch/themes/cwtch.dart';
       5             : import 'package:cwtch/themes/yamltheme.dart';
       6             : import 'package:flutter/material.dart';
       7             : import 'package:cwtch/settings.dart';
       8             : import 'package:flutter/services.dart';
       9             : import 'package:path/path.dart' as path;
      10             : 
      11             : const custom_themes_subdir = "themes";
      12             : 
      13             : const mode_light = "light";
      14             : const mode_dark = "dark";
      15             : 
      16           0 : final TextStyle defaultSmallTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.normal, fontSize: 10);
      17          20 : final TextStyle defaultMessageTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w400, fontSize: 13, fontFamilyFallback: [Platform.isWindows ? 'Segoe UI Emoji' : "Noto Color Emoji"]);
      18          12 : final TextStyle defaultFormLabelTextStyle = TextStyle(
      19             :   fontFamily: "Inter",
      20             :   fontWeight: FontWeight.bold,
      21             :   fontSize: 20,
      22             : );
      23          12 : final TextStyle defaultTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w500, fontSize: 12);
      24          16 : final TextStyle defaultTextButtonStyle = defaultTextStyle.copyWith(fontWeight: FontWeight.bold);
      25          12 : final TextStyle defaultDropDownMenuItemTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: 16);
      26             : 
      27             : class ThemeLoader extends ChangeNotifier {
      28             :   Map<String, Map<String, OpaqueThemeType>> themes = Map();
      29             : 
      30           0 :   LoadThemes(String cwtchDir) async {
      31           0 :     themes.clear(); // clear themes...
      32           0 :     loadBuiltinThemes().then((builtinThemes) {
      33           0 :       themes.addAll(builtinThemes);
      34           0 :       notifyListeners();
      35           0 :       loadCustomThemes(path.join(cwtchDir, custom_themes_subdir)).then((customThemes) {
      36           0 :         themes.addAll(customThemes);
      37           0 :         notifyListeners();
      38             :       });
      39             :     });
      40             :   }
      41             : 
      42           4 :   OpaqueThemeType getTheme(String? themeId, String? mode) {
      43             :     if (themeId == null) {
      44             :       themeId = cwtch_theme;
      45             :     }
      46           4 :     if (themeId == mode_light) {
      47             :       mode = mode_light;
      48             :     }
      49           4 :     if (themeId == mode_dark) {
      50             :       mode = mode_dark;
      51             :     }
      52          16 :     var theme = themes[themeId]?[mode] ?? themes[themeId]?[flipMode(mode ?? mode_dark)];
      53           4 :     return theme ?? CwtchDark();
      54             :   }
      55             : 
      56           0 :   String flipMode(String mode) {
      57           0 :     if (mode == mode_dark) {
      58             :       return mode_light;
      59             :     }
      60             :     return mode_dark;
      61             :   }
      62             : }
      63             : 
      64           4 : Color lighten(Color color, [double amount = 0.15]) {
      65           4 :   final hsl = HSLColor.fromColor(color);
      66          16 :   final hslLight = hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));
      67             : 
      68           4 :   return hslLight.toColor();
      69             : }
      70             : 
      71           0 : Color darken(Color color, [double amount = 0.15]) {
      72           0 :   final hsl = HSLColor.fromColor(color);
      73           0 :   final hslDarken = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
      74             : 
      75           0 :   return hslDarken.toColor();
      76             : }
      77             : 
      78             : abstract class OpaqueThemeType {
      79           0 :   static final Color red = Color(0xFFFF0000);
      80             : 
      81           0 :   get theme => "dummy";
      82           0 :   get mode => mode_light;
      83             : 
      84             :   // Main screen background color (message pane, item rows)
      85           0 :   get backgroundMainColor => red;
      86             : 
      87             :   // pane colors (settings)
      88           0 :   get backgroundPaneColor => red;
      89             : 
      90           0 :   get topbarColor => red;
      91             : 
      92           0 :   get mainTextColor => red;
      93             : 
      94             :   // pressed row, offline heart
      95           0 :   get hilightElementColor => red;
      96             :   // Selected Row
      97           0 :   get backgroundHilightElementColor => red;
      98             :   // Faded text color for suggestions in textfields
      99             :   // Todo: implement way more places
     100           0 :   get sendHintTextColor => red;
     101             : 
     102           0 :   get defaultButtonColor => red;
     103          12 :   get defaultButtonActiveColor => /*mode == mode_light ? darken(defaultButtonColor) :*/ lighten(defaultButtonColor);
     104           0 :   get defaultButtonTextColor => red;
     105           0 :   get defaultButtonDisabledColor => red;
     106           0 :   get textfieldBackgroundColor => red;
     107           0 :   get textfieldBorderColor => red;
     108           0 :   get textfieldHintColor => red;
     109           0 :   get textfieldErrorColor => red;
     110           0 :   get textfieldSelectionColor => red;
     111           0 :   get scrollbarDefaultColor => red;
     112           0 :   get portraitBackgroundColor => red;
     113           0 :   get portraitOnlineBorderColor => red;
     114           0 :   get portraitOfflineBorderColor => red;
     115           0 :   get portraitBlockedBorderColor => red;
     116           0 :   get portraitBlockedTextColor => red;
     117           0 :   get portraitContactBadgeColor => red;
     118           0 :   get portraitContactBadgeTextColor => red;
     119           0 :   get portraitProfileBadgeColor => red;
     120           0 :   get portraitProfileBadgeTextColor => red;
     121             : 
     122           0 :   get portraitOnlineAwayColor => Color(0xFFFFF59D);
     123           0 :   get portraitOnlineBusyColor => Color(0xFFEF9A9A);
     124             : 
     125             :   // dropshaddpow
     126             :   // todo: probably should not be reply icon color in messagerow
     127           0 :   get dropShadowColor => red;
     128             : 
     129           0 :   get toolbarIconColor => red;
     130           0 :   get toolbarBackgroundColor => red;
     131           0 :   get chatReactionIconColor => red;
     132           0 :   get messageFromMeBackgroundColor => red;
     133           0 :   get messageFromMeTextColor => red;
     134           0 :   get messageFromOtherBackgroundColor => red;
     135           0 :   get messageFromOtherTextColor => red;
     136           0 :   get messageSelectionColor => red;
     137             : 
     138           0 :   get menuBackgroundColor => red;
     139             : 
     140           0 :   get snackbarBackgroundColor => red;
     141           0 :   get snackbarTextColor => red;
     142             : 
     143             :   // Images
     144             : 
     145           0 :   get chatImageColor => red;
     146           0 :   get chatImage => null;
     147             : 
     148           0 :   ImageProvider loadImage(String key, {BuildContext? context}) {
     149           0 :     return AssetImage("");
     150             :   }
     151             : 
     152             :   // Sizes
     153           0 :   double contactOnionTextSize() {
     154             :     return 18;
     155             :   }
     156             : }
     157             : 
     158             : // Borrowed from Stackoverflow
     159           0 : MaterialColor getMaterialColor(Color color) {
     160           0 :   final int red = color.red;
     161           0 :   final int green = color.green;
     162           0 :   final int blue = color.blue;
     163             : 
     164           0 :   final Map<int, Color> shades = {
     165           0 :     50: Color.fromRGBO(red, green, blue, .1),
     166           0 :     100: Color.fromRGBO(red, green, blue, .2),
     167           0 :     200: Color.fromRGBO(red, green, blue, .3),
     168           0 :     300: Color.fromRGBO(red, green, blue, .4),
     169           0 :     400: Color.fromRGBO(red, green, blue, .5),
     170           0 :     500: Color.fromRGBO(red, green, blue, .6),
     171           0 :     600: Color.fromRGBO(red, green, blue, .7),
     172           0 :     700: Color.fromRGBO(red, green, blue, .8),
     173           0 :     800: Color.fromRGBO(red, green, blue, .9),
     174           0 :     900: Color.fromRGBO(red, green, blue, 1),
     175             :   };
     176             : 
     177           0 :   return MaterialColor(color.value, shades);
     178             : }
     179             : 
     180           4 : ThemeData mkThemeData(Settings opaque) {
     181           4 :   return ThemeData(
     182          12 :       hoverColor: opaque.current().backgroundHilightElementColor.withOpacity(0.5),
     183           4 :       visualDensity: VisualDensity.adaptivePlatformDensity,
     184           4 :       primaryIconTheme: IconThemeData(
     185           8 :         color: opaque.current().mainTextColor,
     186             :       ),
     187           8 :       primaryColor: opaque.current().mainTextColor,
     188           8 :       canvasColor: opaque.current().backgroundMainColor,
     189          24 :       highlightColor: opaque.current().mode == mode_light ? darken(opaque.current().backgroundHilightElementColor) : lighten(opaque.current().backgroundHilightElementColor),
     190           4 :       iconTheme: IconThemeData(
     191           8 :         color: opaque.current().toolbarIconColor,
     192             :       ),
     193           8 :       cardColor: opaque.current().backgroundMainColor,
     194           4 :       bottomSheetTheme: BottomSheetThemeData(
     195           8 :         backgroundColor: opaque.current().backgroundPaneColor,
     196             :         constraints: const BoxConstraints(
     197             :           maxWidth: double.infinity,
     198             :         ),
     199             :       ),
     200           4 :       appBarTheme: AppBarTheme(
     201           4 :           systemOverlayStyle: SystemUiOverlayStyle(
     202             :             // Status bar color
     203           8 :             statusBarColor: opaque.current().topbarColor,
     204             :             // Status bar brightness (optional)
     205          12 :             statusBarIconBrightness: opaque.current().mode == mode_light ? Brightness.dark : Brightness.light, // For Android (dark icons)
     206          12 :             statusBarBrightness: opaque.current().mode == mode_light ? Brightness.dark : Brightness.light, // For iOS (dark icons)
     207             :           ),
     208           8 :           backgroundColor: opaque.current().topbarColor,
     209           4 :           iconTheme: IconThemeData(
     210           8 :             color: opaque.current().mainTextColor,
     211             :           ),
     212          20 :           titleTextStyle: TextStyle(fontWeight: FontWeight.bold, fontFamily: "Inter", color: opaque.current().mainTextColor, fontSize: opaque.fontScaling * 18.0),
     213           4 :           actionsIconTheme: IconThemeData(
     214           8 :             color: opaque.current().mainTextColor,
     215             :           )),
     216           4 :       listTileTheme: ListTileThemeData(
     217          32 :           titleTextStyle: defaultFormLabelTextStyle.copyWith(color: opaque.current().mainTextColor), subtitleTextStyle: defaultMessageTextStyle.copyWith(color: opaque.current().mainTextColor)),
     218             :       //bottomNavigationBarTheme: BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed, backgroundColor: opaque.current().backgroundHilightElementColor),  // Can't determine current use
     219           4 :       textButtonTheme: TextButtonThemeData(
     220           4 :         style: ButtonStyle(
     221          12 :             backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor),
     222          12 :             foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor),
     223          12 :             overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor),
     224           8 :             padding: MaterialStateProperty.all(EdgeInsets.all(20))),
     225             :       ),
     226           8 :       hintColor: opaque.current().textfieldHintColor,
     227           4 :       elevatedButtonTheme: ElevatedButtonThemeData(
     228           4 :         style: ButtonStyle(
     229           4 :           backgroundColor: MaterialStateProperty.resolveWith((states) => states.contains(MaterialState.disabled) ? opaque.current().defaultButtonDisabledColor : opaque.current().defaultButtonColor),
     230          12 :           foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor),
     231           4 :           overlayColor: MaterialStateProperty.resolveWith((states) => (states.contains(MaterialState.pressed) && states.contains(MaterialState.hovered))
     232           0 :               ? opaque.current().defaultButtonActiveColor
     233           0 :               : states.contains(MaterialState.disabled)
     234           0 :                   ? opaque.current().defaultButtonDisabledColor
     235             :                   : null),
     236             :           enableFeedback: true,
     237          12 :           textStyle: MaterialStateProperty.all(opaque.scaleFonts(defaultTextButtonStyle)),
     238           8 :           padding: MaterialStateProperty.all(EdgeInsets.all(20)),
     239             :         ),
     240             :       ),
     241           4 :       filledButtonTheme: FilledButtonThemeData(
     242           4 :         style: ButtonStyle(
     243           4 :           backgroundColor: MaterialStateProperty.resolveWith((states) => states.contains(MaterialState.disabled) ? opaque.current().defaultButtonDisabledColor : opaque.current().defaultButtonColor),
     244          12 :           foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor),
     245           4 :           overlayColor: MaterialStateProperty.resolveWith((states) => (states.contains(MaterialState.pressed) && states.contains(MaterialState.hovered))
     246           0 :               ? opaque.current().defaultButtonActiveColor
     247           0 :               : states.contains(MaterialState.disabled)
     248           0 :                   ? opaque.current().defaultButtonDisabledColor
     249             :                   : null),
     250             :           enableFeedback: true,
     251          12 :           textStyle: MaterialStateProperty.all(opaque.scaleFonts(defaultTextButtonStyle)),
     252           8 :           padding: MaterialStateProperty.all(EdgeInsets.all(20)),
     253             :         ),
     254             :       ),
     255           4 :       outlinedButtonTheme: OutlinedButtonThemeData(
     256           4 :         style: ButtonStyle(
     257           4 :           backgroundColor: MaterialStateProperty.resolveWith((states) => states.contains(MaterialState.disabled) ? opaque.current().defaultButtonDisabledColor : opaque.current().backgroundMainColor),
     258          12 :           foregroundColor: MaterialStateProperty.all(opaque.current().mainTextColor),
     259          16 :           side: MaterialStateProperty.all(BorderSide(color: opaque.current().defaultButtonColor)),
     260           4 :           overlayColor: MaterialStateProperty.resolveWith((states) => (states.contains(MaterialState.pressed) && states.contains(MaterialState.hovered))
     261           0 :               ? opaque.current().defaultButtonActiveColor
     262           0 :               : states.contains(MaterialState.disabled)
     263           0 :                   ? opaque.current().defaultButtonDisabledColor
     264             :                   : null),
     265             :           enableFeedback: true,
     266          12 :           textStyle: MaterialStateProperty.all(opaque.scaleFonts(defaultTextButtonStyle)),
     267           8 :           padding: MaterialStateProperty.all(EdgeInsets.all(20)),
     268             :         ),
     269             :       ),
     270          20 :       scrollbarTheme: ScrollbarThemeData(thumbVisibility: MaterialStateProperty.all(false), thumbColor: MaterialStateProperty.all(opaque.current().scrollbarDefaultColor)),
     271           4 :       sliderTheme: SliderThemeData(
     272           8 :         activeTrackColor: opaque.current().defaultButtonColor, // color of slider bar for left active region
     273           8 :         inactiveTrackColor: opaque.theme.defaultButtonDisabledColor, // color of slider bar for right inactive region
     274           8 :         thumbColor: opaque.current().mainTextColor, // color of slider widget
     275           8 :         overlayColor: opaque.current().mainTextColor, // color around active widget
     276           8 :         valueIndicatorColor: opaque.current().backgroundHilightElementColor,
     277          20 :         valueIndicatorTextStyle: opaque.scaleFonts(defaultDropDownMenuItemTextStyle).copyWith(color: opaque.current().hilightElementColor),
     278           4 :         valueIndicatorShape: RectangularSliderValueIndicatorShape(), //RoundSliderThumbShape(),
     279             :       ),
     280           4 :       tabBarTheme: TabBarTheme(
     281           8 :           labelColor: opaque.current().mainTextColor,
     282           8 :           unselectedLabelColor: opaque.current().mainTextColor,
     283          16 :           indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor)),
     284          20 :           labelStyle: opaque.scaleFonts(defaultTextButtonStyle).copyWith(color: opaque.current().mainTextColor),
     285          20 :           unselectedLabelStyle: opaque.scaleFonts(defaultTextStyle).copyWith(color: opaque.current().mainTextColor),
     286             :           tabAlignment: TabAlignment.center),
     287           4 :       dialogTheme: DialogTheme(
     288           8 :           backgroundColor: opaque.current().backgroundPaneColor,
     289          12 :           titleTextStyle: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, color: opaque.current().mainTextColor),
     290           4 :           contentTextStyle: TextStyle(
     291             :             fontFamily: "Inter",
     292           8 :             color: opaque.current().mainTextColor,
     293             :           )),
     294           4 :       textTheme: TextTheme(
     295             :         // NOTE: The following font scales were arrived at after consulting the material text scale
     296             :         // docs: https://m3.material.io/styles/typography/type-scale-tokens and some trial and error
     297          20 :         displaySmall: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 14.0, color: opaque.current().mainTextColor),
     298          20 :         displayMedium: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
     299          20 :         displayLarge: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
     300          20 :         titleSmall: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
     301          20 :         titleLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
     302          20 :         titleMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 20.0, color: opaque.current().mainTextColor),
     303          20 :         bodySmall: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 12.0, color: opaque.current().mainTextColor),
     304          20 :         bodyMedium: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 14.0, color: opaque.current().mainTextColor),
     305          20 :         bodyLarge: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
     306          20 :         headlineSmall: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 24.0, color: opaque.current().mainTextColor),
     307          20 :         headlineMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 26.0, color: opaque.current().mainTextColor),
     308          20 :         headlineLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 28.0, color: opaque.current().mainTextColor),
     309          20 :         labelSmall: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w100, fontSize: opaque.fontScaling * 14.0, color: opaque.current().mainTextColor),
     310          20 :         labelMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w300, fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
     311          20 :         labelLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w200, fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
     312             :       ),
     313           4 :       switchTheme: SwitchThemeData(
     314          12 :         overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor),
     315          12 :         thumbColor: MaterialStateProperty.all(opaque.current().mainTextColor),
     316          12 :         trackColor: MaterialStateProperty.all(opaque.current().dropShadowColor),
     317             :       ),
     318             :       // the only way to change the text Selection Context Menu Color ?!
     319          12 :       brightness: opaque.current().mode == mode_dark ? Brightness.dark : Brightness.light,
     320           4 :       floatingActionButtonTheme: FloatingActionButtonThemeData(
     321           8 :           foregroundColor: opaque.current().mainTextColor,
     322           8 :           backgroundColor: opaque.current().defaultButtonColor,
     323           8 :           hoverColor: opaque.current().defaultButtonActiveColor,
     324             :           enableFeedback: true,
     325           8 :           splashColor: opaque.current().defaultButtonActiveColor),
     326           4 :       textSelectionTheme: TextSelectionThemeData(
     327          24 :           cursorColor: opaque.current().textfieldSelectionColor, selectionColor: opaque.current().textfieldSelectionColor, selectionHandleColor: opaque.current().textfieldSelectionColor),
     328           4 :       popupMenuTheme: PopupMenuThemeData(
     329          12 :         color: opaque.current().backgroundPaneColor.withOpacity(0.9),
     330             :       ),
     331           4 :       snackBarTheme: SnackBarThemeData(
     332           8 :         backgroundColor: opaque.current().snackbarBackgroundColor,
     333          12 :         contentTextStyle: TextStyle(color: opaque.current().snackbarTextColor),
     334             :       ));
     335             : }

Generated by: LCOV version 1.14