229 lines
9.4 KiB
Dart
229 lines
9.4 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:cwtch/themes/cwtch.dart';
|
|
import 'package:cwtch/themes/opaque.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:yaml/yaml.dart';
|
|
import 'package:path/path.dart' as path;
|
|
|
|
import '../main.dart';
|
|
|
|
Future<Map<String, Map<String, OpaqueThemeType>>> loadBuiltinThemes() async {
|
|
final manifestJson = await rootBundle.loadString('AssetManifest.json');
|
|
final themesList = json.decode(manifestJson).keys.where((String key) => key.startsWith('assets/themes'));
|
|
|
|
Map<String, Map<String, OpaqueThemeType>> themes = Map();
|
|
|
|
for (String themefile in themesList) {
|
|
if (themefile.substring(themefile.length - 4) != ".yml") {
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
var data = await loadAssetYamlTheme(themefile);
|
|
|
|
if (data != null) {
|
|
// remove "assets/themes" and "theme.yml" from name
|
|
var themename = themefile.substring(14, themefile.lastIndexOf("/"));
|
|
themes[themename] = data;
|
|
}
|
|
} catch (e) {
|
|
print("Failed to load theme: $themefile with exception: $e");
|
|
}
|
|
}
|
|
return themes;
|
|
}
|
|
|
|
Future<Map<String, Map<String, OpaqueThemeType>>> loadCustomThemes(String themesDirPath) async {
|
|
Map<String, Map<String, OpaqueThemeType>> themes = Map();
|
|
|
|
Directory themesDir = Directory(themesDirPath);
|
|
if (!themesDir.existsSync()) {
|
|
themesDir = await themesDir.create();
|
|
}
|
|
await themesDir.list(recursive: false).forEach((themeDir) async {
|
|
//final themeDir = Directory(path.join(themesDir, themedir));
|
|
File themefile = File(path.join(themeDir.path, "theme.yml"));
|
|
if (themefile.existsSync()) {
|
|
try {
|
|
var data = await loadFileYamlTheme(themefile.path, themeDir.path);
|
|
|
|
if (data != null) {
|
|
themes[path.basename(themeDir.path)] = data;
|
|
}
|
|
} catch (e) {
|
|
print("Failed to load theme: $themefile with exception: $e");
|
|
}
|
|
}
|
|
});
|
|
|
|
return themes;
|
|
}
|
|
|
|
Future<YamlMap?> readAssetYamlTheme(String themefile) async {
|
|
final contents = await rootBundle.loadString(themefile);
|
|
return loadYaml(contents);
|
|
}
|
|
|
|
Future<Map<String, OpaqueThemeType>?> loadAssetYamlTheme(String themefile) async {
|
|
final yml = await readAssetYamlTheme(themefile);
|
|
|
|
if (yml == null) {
|
|
print("Error: failed to load theme: $themefile");
|
|
return null;
|
|
}
|
|
return loadYamlTheme(yml);
|
|
}
|
|
|
|
Future<Map<String, OpaqueThemeType>?> loadFileYamlTheme(String themefile, String assetsDir) async {
|
|
final file = File(themefile);
|
|
if (!file.existsSync()) {
|
|
print("Error: theme file: $themefile does not exist");
|
|
return null;
|
|
}
|
|
final contents = file.readAsStringSync();
|
|
final yml = await loadYaml(contents);
|
|
if (yml == null) {
|
|
print("Error: failed to load theme: $themefile");
|
|
return null;
|
|
}
|
|
return loadYamlTheme(yml, assetsDir);
|
|
}
|
|
|
|
Future<Map<String, OpaqueThemeType>?> loadYamlTheme(YamlMap yml, [String? assetsDir]) async {
|
|
Map<String, OpaqueThemeType> subthemes = Map();
|
|
if ((yml["themes"] as YamlMap).containsKey(mode_dark)) {
|
|
subthemes[mode_dark] = YmlTheme(yml, yml["themes"]["name"], mode_dark, assetsDir);
|
|
}
|
|
if ((yml["themes"] as YamlMap).containsKey(mode_light)) {
|
|
subthemes[mode_light] = YmlTheme(yml, yml["themes"]["name"], mode_light, assetsDir);
|
|
}
|
|
return subthemes;
|
|
}
|
|
|
|
class YmlTheme extends OpaqueThemeType {
|
|
late YamlMap yml;
|
|
late String mode;
|
|
late String theme;
|
|
late String? assetsDir;
|
|
late OpaqueThemeType fallbackTheme;
|
|
|
|
YmlTheme(YamlMap yml, theme, mode, assetsDir) {
|
|
this.yml = yml;
|
|
this.theme = theme;
|
|
this.mode = mode;
|
|
this.assetsDir = assetsDir;
|
|
if (mode == mode_dark) {
|
|
fallbackTheme = CwtchDark();
|
|
} else {
|
|
fallbackTheme = CwtchLight();
|
|
}
|
|
}
|
|
|
|
Color? getColor(String name) {
|
|
var val = yml["themes"][mode]["theme"][name];
|
|
if (!(val is int)) {
|
|
val = yml["themes"][mode]["theme"][val] ?? val;
|
|
}
|
|
if (!(val is int)) {
|
|
val = yml["themes"][mode]?["colors"]?[val] ?? val;
|
|
}
|
|
if (!(val is int)) {
|
|
val = yml["colors"]?[val];
|
|
}
|
|
if (!(val is int)) {
|
|
return null;
|
|
}
|
|
|
|
if (val <= 0xFFFFFF) {
|
|
return Color(0xFF000000 + val);
|
|
} else {
|
|
return Color(val);
|
|
}
|
|
}
|
|
|
|
String? getImage(String name) {
|
|
var val = yml["themes"][mode]["theme"]?[name];
|
|
if (val != null) {
|
|
if (assetsDir == null) {
|
|
return path.join("assets", "themes", yml["themes"]["name"], val);
|
|
} else {
|
|
return val;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
get backgroundMainColor => getColor("backgroundMainColor") ?? fallbackTheme.backgroundMainColor;
|
|
get backgroundPaneColor => getColor("backgroundPaneColor") ?? fallbackTheme.backgroundPaneColor;
|
|
get topbarColor => getColor("topbarColor") ?? fallbackTheme.topbarColor;
|
|
get mainTextColor => getColor("mainTextColor") ?? fallbackTheme.mainTextColor;
|
|
get hilightElementColor => getColor("hilightElementColor") ?? fallbackTheme.hilightElementColor;
|
|
get backgroundHilightElementColor => getColor("backgroundHilightElementColor") ?? fallbackTheme.backgroundHilightElementColor;
|
|
get sendHintTextColor => getColor("sendHintTextColor") ?? fallbackTheme.sendHintTextColor;
|
|
get defaultButtonColor => getColor("defaultButtonColor") ?? fallbackTheme.defaultButtonColor;
|
|
get defaultButtonActiveColor => /*mode == mode_light ? darken(defaultButtonColor) :*/ lighten(getColor("defaultButtonColor") ?? fallbackTheme.defaultButtonColor);
|
|
get defaultButtonTextColor => getColor("defaultButtonTextColor") ?? fallbackTheme.defaultButtonTextColor;
|
|
get defaultButtonDisabledColor => getColor("defaultButtonDisabledColor") ?? fallbackTheme.defaultButtonDisabledColor;
|
|
get textfieldBackgroundColor => getColor("textfieldBackgroundColor") ?? fallbackTheme.textfieldBackgroundColor;
|
|
get textfieldBorderColor => getColor("textfieldBorderColor") ?? fallbackTheme.textfieldBorderColor;
|
|
get textfieldHintColor => getColor("textfieldHintColor") ?? fallbackTheme.textfieldHintColor;
|
|
get textfieldErrorColor => getColor("textfieldErrorColor") ?? fallbackTheme.textfieldErrorColor;
|
|
get textfieldSelectionColor => getColor("textfieldSelectionColor") ?? fallbackTheme.textfieldSelectionColor;
|
|
get scrollbarDefaultColor => getColor("scrollbarDefaultColor") ?? fallbackTheme.scrollbarDefaultColor;
|
|
get portraitBackgroundColor => getColor("portraitBackgroundColor") ?? fallbackTheme.portraitBackgroundColor;
|
|
get portraitOnlineBorderColor => getColor("portraitOnlineBorderColor") ?? fallbackTheme.portraitOnlineBorderColor;
|
|
get portraitOfflineBorderColor => getColor("portraitOfflineBorderColor") ?? fallbackTheme.portraitOfflineBorderColor;
|
|
get portraitBlockedBorderColor => getColor("portraitBlockedBorderColor") ?? fallbackTheme.portraitBlockedBorderColor;
|
|
get portraitBlockedTextColor => getColor("portraitBlockedTextColor") ?? fallbackTheme.portraitBlockedTextColor;
|
|
get portraitContactBadgeColor => getColor("portraitContactBadgeColor") ?? fallbackTheme.portraitContactBadgeColor;
|
|
get portraitContactBadgeTextColor => getColor("portraitContactBadgeTextColor") ?? fallbackTheme.portraitContactBadgeTextColor;
|
|
get portraitProfileBadgeColor => getColor("portraitProfileBadgeColor") ?? fallbackTheme.portraitProfileBadgeColor;
|
|
get portraitProfileBadgeTextColor => getColor("portraitProfileBadgeTextColor") ?? fallbackTheme.portraitProfileBadgeTextColor;
|
|
get portraitOnlineAwayColor => getColor("portraitOnlineAwayColor") ?? fallbackTheme.portraitOnlineAwayColor;
|
|
get portraitOnlineBusyColor => getColor("portraitOnlineBusyColor") ?? fallbackTheme.portraitOnlineBusyColor;
|
|
get dropShadowColor => getColor("dropShadowColor") ?? fallbackTheme.dropShadowColor;
|
|
get toolbarIconColor => getColor("toolbarIconColor") ?? fallbackTheme.toolbarIconColor;
|
|
get chatReactionIconColor => getColor("chatReactionIconColor") ?? fallbackTheme.chatReactionIconColor;
|
|
get messageFromMeBackgroundColor => getColor("messageFromMeBackgroundColor") ?? fallbackTheme.messageFromMeBackgroundColor;
|
|
get messageFromMeTextColor => getColor("messageFromMeTextColor") ?? fallbackTheme.messageFromMeTextColor;
|
|
get messageFromOtherBackgroundColor => getColor("messageFromOtherBackgroundColor") ?? fallbackTheme.messageFromOtherBackgroundColor;
|
|
get messageFromOtherTextColor => getColor("messageFromOtherTextColor") ?? fallbackTheme.messageFromOtherTextColor;
|
|
get messageSelectionColor => getColor("messageSelectionColor") ?? fallbackTheme.messageSelectionColor;
|
|
get menuBackgroundColor => getColor("menuBackgroundColor") ?? fallbackTheme.menuBackgroundColor;
|
|
get snackbarBackgroundColor => getColor("snackbarBackgroundColor") ?? fallbackTheme.snackbarBackgroundColor;
|
|
get snackbarTextColor => getColor("snackbarTextColor") ?? fallbackTheme.snackbarTextColor;
|
|
|
|
// Images
|
|
|
|
get chatImageColor => getColor("chatImageColor") ?? fallbackTheme.chatImageColor;
|
|
get chatImage => getImage("chatImage") ?? fallbackTheme.chatImage;
|
|
|
|
ImageProvider loadImage(String key, {BuildContext? context}) {
|
|
File f = File(key);
|
|
if (f.existsSync()) {
|
|
return FileImage(f);
|
|
}
|
|
|
|
final assetsDir = this.assetsDir;
|
|
if (assetsDir != null) {
|
|
File f = File(path.join(assetsDir, key));
|
|
if (f.existsSync()) {
|
|
return FileImage(f);
|
|
}
|
|
}
|
|
|
|
if (context != null) {
|
|
File af = File(path.join(Provider.of<FlwtchState>(context, listen: false).cwtch.getAssetsDir(), key));
|
|
if (af.existsSync()) {
|
|
return FileImage(af);
|
|
}
|
|
}
|
|
|
|
return AssetImage(key);
|
|
}
|
|
}
|