cwtch-ui/lib/themes/opaque.dart

327 lines
16 KiB
Dart

import 'dart:io';
import 'dart:core';
import 'package:cwtch/themes/cwtch.dart';
import 'package:cwtch/themes/yamltheme.dart';
import 'package:flutter/material.dart';
import 'package:cwtch/settings.dart';
import 'package:flutter/services.dart';
import 'package:path/path.dart' as path;
const custom_themes_subdir = "themes";
const mode_light = "light";
const mode_dark = "dark";
final TextStyle defaultSmallTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.normal, fontSize: 10);
final TextStyle defaultMessageTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w400, fontSize: 13, fontFamilyFallback: [Platform.isWindows ? 'Segoe UI Emoji' : "Noto Color Emoji"]);
final TextStyle defaultFormLabelTextStyle = TextStyle(
fontFamily: "Inter",
fontWeight: FontWeight.bold,
fontSize: 20,
);
final TextStyle defaultTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w500, fontSize: 12);
final TextStyle defaultTextButtonStyle = defaultTextStyle.copyWith(fontWeight: FontWeight.bold);
final TextStyle defaultDropDownMenuItemTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: 16);
class ThemeLoader extends ChangeNotifier {
Map<String, Map<String, OpaqueThemeType>> themes = Map();
LoadThemes(String cwtchDir) async {
themes.clear(); // clear themes...
loadBuiltinThemes().then((builtinThemes) {
themes.addAll(builtinThemes);
notifyListeners();
loadCustomThemes(path.join(cwtchDir, custom_themes_subdir)).then((customThemes) {
themes.addAll(customThemes);
notifyListeners();
});
});
}
OpaqueThemeType getTheme(String? themeId, String? mode) {
if (themeId == null) {
themeId = cwtch_theme;
}
if (themeId == mode_light) {
mode = mode_light;
}
if (themeId == mode_dark) {
mode = mode_dark;
}
var theme = themes[themeId]?[mode] ?? themes[themeId]?[flipMode(mode ?? mode_dark)];
return theme ?? CwtchDark();
}
String flipMode(String mode) {
if (mode == mode_dark) {
return mode_light;
}
return mode_dark;
}
}
Color lighten(Color color, [double amount = 0.15]) {
final hsl = HSLColor.fromColor(color);
final hslLight = hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));
return hslLight.toColor();
}
Color darken(Color color, [double amount = 0.15]) {
final hsl = HSLColor.fromColor(color);
final hslDarken = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
return hslDarken.toColor();
}
abstract class OpaqueThemeType {
static final Color red = Color(0xFFFF0000);
get theme => "dummy";
get mode => mode_light;
// Main screen background color (message pane, item rows)
get backgroundMainColor => red;
// pane colors (settings)
get backgroundPaneColor => red;
get topbarColor => red;
get mainTextColor => red;
// pressed row, offline heart
get hilightElementColor => red;
// Selected Row
get backgroundHilightElementColor => red;
// Faded text color for suggestions in textfields
// Todo: implement way more places
get sendHintTextColor => red;
get defaultButtonColor => red;
get defaultButtonActiveColor => /*mode == mode_light ? darken(defaultButtonColor) :*/ lighten(defaultButtonColor);
get defaultButtonTextColor => red;
get defaultButtonDisabledColor => red;
get textfieldBackgroundColor => red;
get textfieldBorderColor => red;
get textfieldHintColor => red;
get textfieldErrorColor => red;
get textfieldSelectionColor => red;
get scrollbarDefaultColor => red;
get portraitBackgroundColor => red;
get portraitOnlineBorderColor => red;
get portraitOfflineBorderColor => red;
get portraitBlockedBorderColor => red;
get portraitBlockedTextColor => red;
get portraitContactBadgeColor => red;
get portraitContactBadgeTextColor => red;
get portraitProfileBadgeColor => red;
get portraitProfileBadgeTextColor => red;
get portraitOnlineAwayColor => Color(0xFFFFF59D);
get portraitOnlineBusyColor => Color(0xFFEF9A9A);
// dropshaddpow
// todo: probably should not be reply icon color in messagerow
get dropShadowColor => red;
get toolbarIconColor => red;
get chatReactionIconColor => red;
get messageFromMeBackgroundColor => red;
get messageFromMeTextColor => red;
get messageFromOtherBackgroundColor => red;
get messageFromOtherTextColor => red;
get messageSelectionColor => red;
get menuBackgroundColor => red;
get snackbarBackgroundColor => red;
get snackbarTextColor => red;
// Images
get chatImageColor => red;
get chatImage => null;
ImageProvider loadImage(String key, {BuildContext? context}) {
return AssetImage("");
}
// Sizes
double contactOnionTextSize() {
return 18;
}
}
// Borrowed from Stackoverflow
MaterialColor getMaterialColor(Color color) {
final int red = color.red;
final int green = color.green;
final int blue = color.blue;
final Map<int, Color> shades = {
50: Color.fromRGBO(red, green, blue, .1),
100: Color.fromRGBO(red, green, blue, .2),
200: Color.fromRGBO(red, green, blue, .3),
300: Color.fromRGBO(red, green, blue, .4),
400: Color.fromRGBO(red, green, blue, .5),
500: Color.fromRGBO(red, green, blue, .6),
600: Color.fromRGBO(red, green, blue, .7),
700: Color.fromRGBO(red, green, blue, .8),
800: Color.fromRGBO(red, green, blue, .9),
900: Color.fromRGBO(red, green, blue, 1),
};
return MaterialColor(color.value, shades);
}
ThemeData mkThemeData(Settings opaque) {
return ThemeData(
hoverColor: opaque.current().backgroundHilightElementColor.withOpacity(0.5),
visualDensity: VisualDensity.adaptivePlatformDensity,
primaryIconTheme: IconThemeData(
color: opaque.current().mainTextColor,
),
primaryColor: opaque.current().mainTextColor,
canvasColor: opaque.current().backgroundMainColor,
highlightColor: opaque.current().hilightElementColor,
iconTheme: IconThemeData(
color: opaque.current().toolbarIconColor,
),
cardColor: opaque.current().backgroundMainColor,
bottomSheetTheme: BottomSheetThemeData(
backgroundColor: opaque.current().backgroundPaneColor,
constraints: const BoxConstraints(
maxWidth: double.infinity,
),
),
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
// Status bar color
statusBarColor: opaque.current().topbarColor,
// Status bar brightness (optional)
statusBarIconBrightness: opaque.current().mode == mode_light ? Brightness.dark : Brightness.light, // For Android (dark icons)
statusBarBrightness: opaque.current().mode == mode_light ? Brightness.dark : Brightness.light, // For iOS (dark icons)
),
backgroundColor: opaque.current().topbarColor,
iconTheme: IconThemeData(
color: opaque.current().mainTextColor,
),
titleTextStyle: TextStyle(fontWeight: FontWeight.bold, fontFamily: "Inter", color: opaque.current().mainTextColor, fontSize: opaque.fontScaling * 18.0),
actionsIconTheme: IconThemeData(
color: opaque.current().mainTextColor,
)),
listTileTheme: ListTileThemeData(
titleTextStyle: defaultFormLabelTextStyle.copyWith(color: opaque.current().mainTextColor), subtitleTextStyle: defaultMessageTextStyle.copyWith(color: opaque.current().mainTextColor)),
iconButtonTheme: IconButtonThemeData(style: ButtonStyle(textStyle: MaterialStateProperty.all(defaultFormLabelTextStyle))),
//bottomNavigationBarTheme: BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed, backgroundColor: opaque.current().backgroundHilightElementColor), // Can't determine current use
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor),
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor),
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor),
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
),
hintColor: opaque.current().textfieldHintColor,
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith((states) => states.contains(MaterialState.disabled) ? opaque.current().defaultButtonDisabledColor : opaque.current().defaultButtonColor),
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor),
overlayColor: MaterialStateProperty.resolveWith((states) => (states.contains(MaterialState.pressed) && states.contains(MaterialState.hovered))
? opaque.current().defaultButtonActiveColor
: states.contains(MaterialState.disabled)
? opaque.current().defaultButtonDisabledColor
: null),
enableFeedback: true,
textStyle: MaterialStateProperty.all(opaque.scaleFonts(defaultTextButtonStyle)),
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
),
),
filledButtonTheme: FilledButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith((states) => states.contains(MaterialState.disabled) ? opaque.current().defaultButtonDisabledColor : opaque.current().defaultButtonColor),
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor),
overlayColor: MaterialStateProperty.resolveWith((states) => (states.contains(MaterialState.pressed) && states.contains(MaterialState.hovered))
? opaque.current().defaultButtonActiveColor
: states.contains(MaterialState.disabled)
? opaque.current().defaultButtonDisabledColor
: null),
enableFeedback: true,
textStyle: MaterialStateProperty.all(opaque.scaleFonts(defaultTextButtonStyle)),
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith((states) => states.contains(MaterialState.disabled) ? opaque.current().defaultButtonDisabledColor : opaque.current().backgroundMainColor),
foregroundColor: MaterialStateProperty.all(opaque.current().mainTextColor),
side: MaterialStateProperty.all(BorderSide(color: opaque.current().defaultButtonColor)),
overlayColor: MaterialStateProperty.resolveWith((states) => (states.contains(MaterialState.pressed) && states.contains(MaterialState.hovered))
? opaque.current().defaultButtonActiveColor
: states.contains(MaterialState.disabled)
? opaque.current().defaultButtonDisabledColor
: null),
enableFeedback: true,
textStyle: MaterialStateProperty.all(opaque.scaleFonts(defaultTextButtonStyle)),
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
),
),
scrollbarTheme: ScrollbarThemeData(thumbVisibility: MaterialStateProperty.all(false), thumbColor: MaterialStateProperty.all(opaque.current().scrollbarDefaultColor)),
tabBarTheme: TabBarTheme(
labelColor: opaque.current().mainTextColor,
unselectedLabelColor: opaque.current().mainTextColor,
indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor)),
labelStyle: opaque.scaleFonts(defaultTextButtonStyle).copyWith(color: opaque.current().mainTextColor),
unselectedLabelStyle: opaque.scaleFonts(defaultTextStyle).copyWith(color: opaque.current().mainTextColor),
tabAlignment: TabAlignment.center),
dialogTheme: DialogTheme(
backgroundColor: opaque.current().backgroundPaneColor,
titleTextStyle: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, color: opaque.current().mainTextColor),
contentTextStyle: TextStyle(
fontFamily: "Inter",
color: opaque.current().mainTextColor,
)),
textTheme: TextTheme(
// NOTE: The following font scales were arrived at after consulting the material text scale
// docs: https://m3.material.io/styles/typography/type-scale-tokens and some trial and error
displaySmall: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 14.0, color: opaque.current().mainTextColor),
displayMedium: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
displayLarge: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
titleSmall: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
titleLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
titleMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 20.0, color: opaque.current().mainTextColor),
bodySmall: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 12.0, color: opaque.current().mainTextColor),
bodyMedium: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 14.0, color: opaque.current().mainTextColor),
bodyLarge: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
headlineSmall: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 24.0, color: opaque.current().mainTextColor),
headlineMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 26.0, color: opaque.current().mainTextColor),
headlineLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 28.0, color: opaque.current().mainTextColor),
labelSmall: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w100, fontSize: opaque.fontScaling * 14.0, color: opaque.current().mainTextColor),
labelMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w300, fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
labelLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w200, fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
),
switchTheme: SwitchThemeData(
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor),
thumbColor: MaterialStateProperty.all(opaque.current().mainTextColor),
trackColor: MaterialStateProperty.all(opaque.current().dropShadowColor),
),
// the only way to change the text Selection Context Menu Color ?!
brightness: opaque.current().mode == mode_dark ? Brightness.dark : Brightness.light,
floatingActionButtonTheme: FloatingActionButtonThemeData(
foregroundColor: opaque.current().mainTextColor,
backgroundColor: opaque.current().defaultButtonColor,
hoverColor: opaque.current().defaultButtonActiveColor,
enableFeedback: true,
splashColor: opaque.current().defaultButtonActiveColor),
textSelectionTheme: TextSelectionThemeData(
cursorColor: opaque.current().textfieldSelectionColor, selectionColor: opaque.current().textfieldSelectionColor, selectionHandleColor: opaque.current().textfieldSelectionColor),
popupMenuTheme: PopupMenuThemeData(
color: opaque.current().backgroundPaneColor.withOpacity(0.9),
),
snackBarTheme: SnackBarThemeData(
backgroundColor: opaque.current().snackbarBackgroundColor,
contentTextStyle: TextStyle(color: opaque.current().snackbarTextColor),
));
}