LCOV - code coverage report
Current view: top level - lib/themes - opaque.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 102 183 55.7 %
Date: 2024-04-05 23:55:39 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           0 : 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 chatReactionIconColor => red;
     131           0 :   get messageFromMeBackgroundColor => red;
     132           0 :   get messageFromMeTextColor => red;
     133           0 :   get messageFromOtherBackgroundColor => red;
     134           0 :   get messageFromOtherTextColor => red;
     135           0 :   get messageSelectionColor => red;
     136             : 
     137           0 :   get menuBackgroundColor => red;
     138             : 
     139           0 :   get snackbarBackgroundColor => red;
     140           0 :   get snackbarTextColor => red;
     141             : 
     142             :   // Images
     143             : 
     144           0 :   get chatImageColor => red;
     145           0 :   get chatImage => null;
     146             : 
     147           0 :   ImageProvider loadImage(String key, {BuildContext? context}) {
     148           0 :     return AssetImage("");
     149             :   }
     150             : 
     151             :   // Sizes
     152           0 :   double contactOnionTextSize() {
     153             :     return 18;
     154             :   }
     155             : }
     156             : 
     157             : // Borrowed from Stackoverflow
     158           0 : MaterialColor getMaterialColor(Color color) {
     159           0 :   final int red = color.red;
     160           0 :   final int green = color.green;
     161           0 :   final int blue = color.blue;
     162             : 
     163           0 :   final Map<int, Color> shades = {
     164           0 :     50: Color.fromRGBO(red, green, blue, .1),
     165           0 :     100: Color.fromRGBO(red, green, blue, .2),
     166           0 :     200: Color.fromRGBO(red, green, blue, .3),
     167           0 :     300: Color.fromRGBO(red, green, blue, .4),
     168           0 :     400: Color.fromRGBO(red, green, blue, .5),
     169           0 :     500: Color.fromRGBO(red, green, blue, .6),
     170           0 :     600: Color.fromRGBO(red, green, blue, .7),
     171           0 :     700: Color.fromRGBO(red, green, blue, .8),
     172           0 :     800: Color.fromRGBO(red, green, blue, .9),
     173           0 :     900: Color.fromRGBO(red, green, blue, 1),
     174             :   };
     175             : 
     176           0 :   return MaterialColor(color.value, shades);
     177             : }
     178             : 
     179           4 : ThemeData mkThemeData(Settings opaque) {
     180           4 :   return ThemeData(
     181          12 :       hoverColor: opaque.current().backgroundHilightElementColor.withOpacity(0.5),
     182           4 :       visualDensity: VisualDensity.adaptivePlatformDensity,
     183           4 :       primaryIconTheme: IconThemeData(
     184           8 :         color: opaque.current().mainTextColor,
     185             :       ),
     186           8 :       primaryColor: opaque.current().mainTextColor,
     187           8 :       canvasColor: opaque.current().backgroundMainColor,
     188           8 :       highlightColor: opaque.current().hilightElementColor,
     189           4 :       iconTheme: IconThemeData(
     190           8 :         color: opaque.current().toolbarIconColor,
     191             :       ),
     192           8 :       cardColor: opaque.current().backgroundMainColor,
     193           4 :       bottomSheetTheme: BottomSheetThemeData(
     194           8 :         backgroundColor: opaque.current().backgroundPaneColor,
     195             :         constraints: const BoxConstraints(
     196             :           maxWidth: double.infinity,
     197             :         ),
     198             :       ),
     199           4 :       appBarTheme: AppBarTheme(
     200           4 :           systemOverlayStyle: SystemUiOverlayStyle(
     201             :             // Status bar color
     202           8 :             statusBarColor: opaque.current().topbarColor,
     203             :             // Status bar brightness (optional)
     204          12 :             statusBarIconBrightness: opaque.current().mode == mode_light ? Brightness.dark : Brightness.light, // For Android (dark icons)
     205          12 :             statusBarBrightness: opaque.current().mode == mode_light ? Brightness.dark : Brightness.light, // For iOS (dark icons)
     206             :           ),
     207           8 :           backgroundColor: opaque.current().topbarColor,
     208           4 :           iconTheme: IconThemeData(
     209           8 :             color: opaque.current().mainTextColor,
     210             :           ),
     211          20 :           titleTextStyle: TextStyle(fontWeight: FontWeight.bold, fontFamily: "Inter", color: opaque.current().mainTextColor, fontSize: opaque.fontScaling * 18.0),
     212           4 :           actionsIconTheme: IconThemeData(
     213           8 :             color: opaque.current().mainTextColor,
     214             :           )),
     215          12 :       listTileTheme: ListTileThemeData(titleTextStyle: defaultFormLabelTextStyle, subtitleTextStyle: defaultMessageTextStyle),
     216          16 :       iconButtonTheme: IconButtonThemeData(style: ButtonStyle(textStyle: MaterialStateProperty.all(defaultFormLabelTextStyle))),
     217             :       //bottomNavigationBarTheme: BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed, backgroundColor: opaque.current().backgroundHilightElementColor),  // Can't determine current use
     218           4 :       textButtonTheme: TextButtonThemeData(
     219           4 :         style: ButtonStyle(
     220          12 :             backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor),
     221          12 :             foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor),
     222          12 :             overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor),
     223           8 :             padding: MaterialStateProperty.all(EdgeInsets.all(20))),
     224             :       ),
     225           8 :       hintColor: opaque.current().textfieldHintColor,
     226           4 :       elevatedButtonTheme: ElevatedButtonThemeData(
     227           4 :         style: ButtonStyle(
     228           4 :           backgroundColor: MaterialStateProperty.resolveWith((states) => states.contains(MaterialState.disabled) ? opaque.current().defaultButtonDisabledColor : opaque.current().defaultButtonColor),
     229          12 :           foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor),
     230           4 :           overlayColor: MaterialStateProperty.resolveWith((states) => (states.contains(MaterialState.pressed) && states.contains(MaterialState.hovered))
     231           0 :               ? opaque.current().defaultButtonActiveColor
     232           0 :               : states.contains(MaterialState.disabled)
     233           0 :                   ? opaque.current().defaultButtonDisabledColor
     234             :                   : null),
     235             :           enableFeedback: true,
     236          12 :           textStyle: MaterialStateProperty.all(opaque.scaleFonts(defaultTextButtonStyle)),
     237           8 :           padding: MaterialStateProperty.all(EdgeInsets.all(20)),
     238           8 :           shape: MaterialStateProperty.all(RoundedRectangleBorder(
     239           4 :             borderRadius: BorderRadius.circular(6.0),
     240             :           )),
     241             :         ),
     242             :       ),
     243          20 :       scrollbarTheme: ScrollbarThemeData(thumbVisibility: MaterialStateProperty.all(false), thumbColor: MaterialStateProperty.all(opaque.current().scrollbarDefaultColor)),
     244           4 :       tabBarTheme: TabBarTheme(
     245           8 :           labelColor: opaque.current().mainTextColor,
     246           8 :           unselectedLabelColor: opaque.current().mainTextColor,
     247          16 :           indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor)),
     248           8 :           labelStyle: opaque.scaleFonts(defaultTextButtonStyle),
     249           8 :           unselectedLabelStyle: opaque.scaleFonts(defaultTextStyle),
     250             :           tabAlignment: TabAlignment.center),
     251           4 :       dialogTheme: DialogTheme(
     252           8 :           backgroundColor: opaque.current().backgroundPaneColor,
     253          12 :           titleTextStyle: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, color: opaque.current().mainTextColor),
     254           4 :           contentTextStyle: TextStyle(
     255             :             fontFamily: "Inter",
     256           8 :             color: opaque.current().mainTextColor,
     257             :           )),
     258           4 :       textTheme: TextTheme(
     259             :         // NOTE: The following font scales were arrived at after consulting the material text scale
     260             :         // docs: https://m3.material.io/styles/typography/type-scale-tokens and some trial and error
     261          20 :         displaySmall: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 14.0, color: opaque.current().mainTextColor),
     262          20 :         displayMedium: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
     263          20 :         displayLarge: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
     264          20 :         titleSmall: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
     265          20 :         titleLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
     266          20 :         titleMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 20.0, color: opaque.current().mainTextColor),
     267          20 :         bodySmall: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 12.0, color: opaque.current().mainTextColor),
     268          20 :         bodyMedium: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 14.0, color: opaque.current().mainTextColor),
     269          20 :         bodyLarge: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
     270          20 :         headlineSmall: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 24.0, color: opaque.current().mainTextColor),
     271          20 :         headlineMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 26.0, color: opaque.current().mainTextColor),
     272          20 :         headlineLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 28.0, color: opaque.current().mainTextColor),
     273          20 :         labelSmall: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w100, fontSize: opaque.fontScaling * 14.0, color: opaque.current().mainTextColor),
     274          20 :         labelMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w300, fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
     275          20 :         labelLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w200, fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
     276             :       ),
     277           4 :       switchTheme: SwitchThemeData(
     278          12 :         overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor),
     279          12 :         thumbColor: MaterialStateProperty.all(opaque.current().mainTextColor),
     280          12 :         trackColor: MaterialStateProperty.all(opaque.current().dropShadowColor),
     281             :       ),
     282             :       // the only way to change the text Selection Context Menu Color ?!
     283          12 :       brightness: opaque.current().mode == mode_dark ? Brightness.dark : Brightness.light,
     284           4 :       floatingActionButtonTheme: FloatingActionButtonThemeData(
     285           8 :           foregroundColor: opaque.current().mainTextColor,
     286           8 :           backgroundColor: opaque.current().defaultButtonColor,
     287           8 :           hoverColor: opaque.current().defaultButtonActiveColor,
     288             :           enableFeedback: true,
     289           8 :           splashColor: opaque.current().defaultButtonActiveColor),
     290           4 :       textSelectionTheme: TextSelectionThemeData(
     291          24 :           cursorColor: opaque.current().textfieldSelectionColor, selectionColor: opaque.current().textfieldSelectionColor, selectionHandleColor: opaque.current().textfieldSelectionColor),
     292           4 :       popupMenuTheme: PopupMenuThemeData(
     293          12 :         color: opaque.current().backgroundPaneColor.withOpacity(0.9),
     294             :       ),
     295           4 :       snackBarTheme: SnackBarThemeData(
     296           8 :         backgroundColor: opaque.current().snackbarBackgroundColor,
     297          12 :         contentTextStyle: TextStyle(color: opaque.current().snackbarTextColor),
     298             :       ));
     299             : }

Generated by: LCOV version 1.14