add the ability to import themes and support for loading custom themes that aren't assets #809
|
@ -61,6 +61,8 @@ themes:
|
|||
textfieldHintColor: mainTextColor
|
||||
toolbarIconColor: settings # whiteishPurple
|
||||
topbarColor: header # darkGreyPurple
|
||||
snackbarBackgroundColor: accent
|
||||
snackbarTextColor: whiteishPurple
|
||||
chatImageColor: purple
|
||||
light:
|
||||
colors:
|
||||
|
|
|
@ -14,6 +14,8 @@ abstract class Cwtch {
|
|||
// ignore: non_constant_identifier_names
|
||||
Future<void> ReconnectCwtchForeground();
|
||||
|
||||
Future<String> getCwtchDir();
|
||||
|
||||
String getAssetsDir();
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
|
|
|
@ -135,6 +135,7 @@ class CwtchFfi implements Cwtch {
|
|||
ReceivePort _receivePort = ReceivePort();
|
||||
bool _isL10nInit = false;
|
||||
String _assetsDir = path.join(Directory.current.path, "data", "flutter_assets");
|
||||
String _cwtchDir = "";
|
||||
|
||||
static String getLibraryPath() {
|
||||
if (Platform.isWindows) {
|
||||
|
@ -169,13 +170,12 @@ class CwtchFfi implements Cwtch {
|
|||
String home = "";
|
||||
String bundledTor = "";
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
String cwtchDir = "";
|
||||
if (Platform.isLinux) {
|
||||
home = envVars['HOME'] ?? "";
|
||||
if (EnvironmentConfig.TEST_MODE) {
|
||||
cwtchDir = envVars['CWTCH_HOME']!;
|
||||
_cwtchDir = envVars['CWTCH_HOME']!;
|
||||
} else {
|
||||
cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, ".cwtch");
|
||||
_cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, ".cwtch");
|
||||
}
|
||||
|
||||
if (await File("linux/Tor/tor").exists()) {
|
||||
|
@ -192,7 +192,7 @@ class CwtchFfi implements Cwtch {
|
|||
bundledTor = "tor";
|
||||
}
|
||||
} else if (Platform.isWindows) {
|
||||
cwtchDir = envVars['CWTCH_DIR'] ?? path.join(envVars['UserProfile']!, ".cwtch");
|
||||
_cwtchDir = envVars['CWTCH_DIR'] ?? path.join(envVars['UserProfile']!, ".cwtch");
|
||||
String currentTor = path.join(Directory.current.absolute.path, "Tor\\Tor\\tor.exe");
|
||||
if (await File(currentTor).exists()) {
|
||||
bundledTor = currentTor;
|
||||
|
@ -203,7 +203,7 @@ class CwtchFfi implements Cwtch {
|
|||
_assetsDir = path.join(exeDir, "data", "flutter_assets");
|
||||
}
|
||||
} else if (Platform.isMacOS) {
|
||||
cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, "Library/Application Support/Cwtch");
|
||||
_cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, "Library/Application Support/Cwtch");
|
||||
_assetsDir = "/Applications/Cwtch.app/Contents/Frameworks/App.framework/Versions/Current/Resources/flutter_assets/";
|
||||
if (await File("Cwtch.app/Contents/MacOS/Tor/tor").exists()) {
|
||||
bundledTor = "Cwtch.app/Contents/MacOS/Tor/tor";
|
||||
|
@ -232,27 +232,27 @@ class CwtchFfi implements Cwtch {
|
|||
// if macOs and release build and no profile and is dev profile
|
||||
// copy dev profile to release profile
|
||||
if (Platform.isMacOS && EnvironmentConfig.BUILD_VER != dev_version) {
|
||||
var devProfileExists = await Directory(path.join(cwtchDir, "dev", "profiles")).exists();
|
||||
var releaseProfileExists = await Directory(path.join(cwtchDir, "profiles")).exists();
|
||||
var devProfileExists = await Directory(path.join(_cwtchDir, "dev", "profiles")).exists();
|
||||
var releaseProfileExists = await Directory(path.join(_cwtchDir, "profiles")).exists();
|
||||
if (devProfileExists && !releaseProfileExists) {
|
||||
print("MacOS one time dev -> release profile migration...");
|
||||
await Process.run("cp", ["-r", "-p", path.join(cwtchDir, "dev", "profiles"), cwtchDir]);
|
||||
await Process.run("cp", ["-r", "-p", path.join(cwtchDir, "dev", "SALT"), cwtchDir]);
|
||||
await Process.run("cp", ["-r", "-p", path.join(cwtchDir, "dev", "ui.globals"), cwtchDir]);
|
||||
await Process.run("cp", ["-r", "-p", path.join(_cwtchDir, "dev", "profiles"), _cwtchDir]);
|
||||
await Process.run("cp", ["-r", "-p", path.join(_cwtchDir, "dev", "SALT"), _cwtchDir]);
|
||||
await Process.run("cp", ["-r", "-p", path.join(_cwtchDir, "dev", "ui.globals"), _cwtchDir]);
|
||||
}
|
||||
}
|
||||
|
||||
if (EnvironmentConfig.BUILD_VER == dev_version) {
|
||||
cwtchDir = path.join(cwtchDir, "dev");
|
||||
_cwtchDir = path.join(_cwtchDir, "dev");
|
||||
}
|
||||
|
||||
print("StartCwtch( cwtchdir: $cwtchDir, torPath: $bundledTor )");
|
||||
print("StartCwtch( cwtchdir: $_cwtchDir, torPath: $bundledTor )");
|
||||
|
||||
var startCwtchC = library.lookup<NativeFunction<start_cwtch_function>>("c_StartCwtch");
|
||||
// ignore: non_constant_identifier_names
|
||||
final StartCwtch = startCwtchC.asFunction<StartCwtchFn>();
|
||||
|
||||
final utf8CwtchDir = cwtchDir.toNativeUtf8();
|
||||
final utf8CwtchDir = _cwtchDir.toNativeUtf8();
|
||||
StartCwtch(utf8CwtchDir, utf8CwtchDir.length, bundledTor.toNativeUtf8(), bundledTor.length);
|
||||
malloc.free(utf8CwtchDir);
|
||||
|
||||
|
@ -268,6 +268,10 @@ class CwtchFfi implements Cwtch {
|
|||
return _assetsDir;
|
||||
}
|
||||
|
||||
Future<String> getCwtchDir() async {
|
||||
return _cwtchDir;
|
||||
}
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> ReconnectCwtchForeground() async {
|
||||
var reconnectCwtch = library.lookup<NativeFunction<Void Function()>>("c_ReconnectCwtchForeground");
|
||||
|
|
|
@ -30,6 +30,7 @@ class CwtchGomobile implements Cwtch {
|
|||
late Future<dynamic> androidLibraryDir;
|
||||
late Future<dynamic> androidHomeDirectory;
|
||||
String androidHomeDirectoryStr = "";
|
||||
String _cwtchDir = "";
|
||||
late CwtchNotifier cwtchNotifier;
|
||||
bool _isL10nInit = false;
|
||||
|
||||
|
@ -55,6 +56,7 @@ class CwtchGomobile implements Cwtch {
|
|||
cwtchNotifier = _cwtchNotifier;
|
||||
cwtchNotifier.setMessageSeenCallback((String profile, int conversation, DateTime time) => {this.SetConversationAttribute(profile, conversation, LastMessageSeenTimeKey, time.toIso8601String())});
|
||||
androidHomeDirectory = getApplicationDocumentsDirectory();
|
||||
|
||||
androidLibraryDir = appInfoPlatform.invokeMethod('getNativeLibDir');
|
||||
|
||||
// Method channel to receive libcwtch-go events via Kotlin and dispatch them to _handleAppbusEvent (sends to cwtchNotifier)
|
||||
|
@ -67,15 +69,20 @@ class CwtchGomobile implements Cwtch {
|
|||
return "";
|
||||
}
|
||||
|
||||
Future<String> getCwtchDir() async {
|
||||
androidHomeDirectoryStr = (await androidHomeDirectory).path;
|
||||
var _cwtchDir = path.join(androidHomeDirectoryStr, ".cwtch");
|
||||
if (EnvironmentConfig.BUILD_VER == dev_version) {
|
||||
_cwtchDir = path.join(_cwtchDir, "dev");
|
||||
}
|
||||
return androidHomeDirectoryStr;
|
||||
}
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> Start() async {
|
||||
print("gomobile.dart: Start()...");
|
||||
androidHomeDirectoryStr = (await androidHomeDirectory).path;
|
||||
var cwtchDir = path.join(androidHomeDirectoryStr, ".cwtch");
|
||||
if (EnvironmentConfig.BUILD_VER == dev_version) {
|
||||
cwtchDir = path.join(cwtchDir, "dev");
|
||||
}
|
||||
String torPath = path.join(await androidLibraryDir, "libtor.so");
|
||||
String cwtchDir = await getCwtchDir();
|
||||
print("gomobile.dart: Start invokeMethod Start($cwtchDir, $torPath)...");
|
||||
cwtchPlatform.invokeMethod("Start", {"appDir": cwtchDir, "torPath": torPath});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "cy",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "da",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "de",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "el",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "en",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "es",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "fr",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "it",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "ja",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,36 @@
|
|||
{
|
||||
"@@locale": "ko",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"resetTor": "초기화",
|
||||
"torStatus": "Tor 상태",
|
||||
"torVersion": "Tor 버전",
|
||||
"passwordChangeError": "암호 변경 오류: 제공된 암호가 거부되었습니다.",
|
||||
"enterProfilePassword": "프로필을 보려면 암호를 입력하세요.",
|
||||
"todoPlaceholder": "할 일...",
|
||||
"addNewItem": "목록에 새 항목 추가",
|
||||
"addListItem": "새 목록 항목 추가",
|
||||
"networkStatusConnecting": "네트워크 및 연락처에 연결 중...",
|
||||
"networkStatusAttemptingTor": "Tor 네트워크에 연결을 시도 중",
|
||||
"networkStatusDisconnected": "인터넷 연결 끊김, 연결 확인하세요",
|
||||
"viewGroupMembershipTooltip": "그룹 구성원 보기",
|
||||
"defaultScalingText": "글꼴 크기 조정",
|
||||
"experimentsEnabled": "실험 사용",
|
||||
"loadingTor": "tor 로딩 중...",
|
||||
"builddate": "빌드 대상: %2",
|
||||
"version": "버전: %1",
|
||||
"versionTor": "버전 %1 및 tor %2",
|
||||
"settingInterfaceZoom": "확대\/축소 수준",
|
||||
"versionBuilddate": "버전: %1 빌드 대상: %2",
|
||||
"error0ProfilesLoadedForPassword": "해당 비밀번호가 포함된 프로필이 0개 로드되었습니다.",
|
||||
"deleteConfirmText": "삭제",
|
||||
"unblockBtn": "연락처 차단 해제",
|
||||
"savePeerHistoryDescription": "연락처와 연결된 기록을 삭제할지 여부를 결정합니다.",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
@ -116,7 +146,6 @@
|
|||
"server": "서버",
|
||||
"peerName": "이름",
|
||||
"peerAddress": "주소",
|
||||
"builddate": "Built on: %2",
|
||||
"deleteProfileConfirmBtn": "프로필 삭제 확인",
|
||||
"deleteConfirmLabel": "DELETE를 입력하여 확인",
|
||||
"deleteProfileBtn": "프로필 삭제",
|
||||
|
@ -152,7 +181,6 @@
|
|||
"localeSw": "Swahili \/ Kiswahili",
|
||||
"localeSv": "Swedish \/ Svenska",
|
||||
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
|
||||
"defaultScalingText": "Font Scaling",
|
||||
"localeJa": "Japanese \/ 日本語",
|
||||
"retryConnectionTooltip": "Cwtch retries peers regularly, but you can tell Cwtch to try sooner by pushing this button.",
|
||||
"retryConnection": "Retry",
|
||||
|
@ -377,9 +405,6 @@
|
|||
"addServerFirst": "You need to add a server before you can create a group",
|
||||
"deleteProfileSuccess": "Successfully deleted profile",
|
||||
"sendInvite": "Send a contact or group invite",
|
||||
"resetTor": "Reset",
|
||||
"torStatus": "Tor Status",
|
||||
"torVersion": "Tor Version",
|
||||
"sendAnInvitation": "You sent an invitation for: ",
|
||||
"contactSuggestion": "This is a contact suggestion for: ",
|
||||
"chatHistoryDefault": "This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right.",
|
||||
|
@ -390,24 +415,5 @@
|
|||
"descriptionExperiments": "Cwtch experiments are optional, opt-in features that add additional functionality to Cwtch that may have different privacy considerations than traditional 1:1 metadata resistant chat e.g. group chat, bot integration etc.",
|
||||
"tooltipUnlockProfiles": "Unlock encrypted profiles by entering their password.",
|
||||
"invalidImportString": "Invalid import string",
|
||||
"todoPlaceholder": "Todo...",
|
||||
"addNewItem": "Add a new item to the list",
|
||||
"addListItem": "Add a New List Item",
|
||||
"networkStatusConnecting": "Connecting to network and contacts...",
|
||||
"networkStatusAttemptingTor": "Attempting to connect to Tor network",
|
||||
"networkStatusDisconnected": "Disconnected from the internet, check your connection",
|
||||
"viewGroupMembershipTooltip": "View Group Membership",
|
||||
"loadingTor": "Loading tor...",
|
||||
"version": "Version %1",
|
||||
"versionTor": "Version %1 with tor %2",
|
||||
"experimentsEnabled": "Enable Experiments",
|
||||
"settingInterfaceZoom": "Zoom level",
|
||||
"versionBuilddate": "Version: %1 Built on: %2",
|
||||
"error0ProfilesLoadedForPassword": "0 profiles loaded with that password",
|
||||
"enterProfilePassword": "Enter a password to view your profiles",
|
||||
"deleteConfirmText": "DELETE",
|
||||
"passwordChangeError": "Error changing password: Supplied password rejected",
|
||||
"unblockBtn": "Unblock Contact",
|
||||
"savePeerHistoryDescription": "Determines whether to delete any history associated with the contact.",
|
||||
"pasteAddressToAddContact": "Paste a cwtch address, invitation or key bundle here to add a new conversation"
|
||||
}
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "lb",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "nl",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "no",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "pl",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "pt",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "pt_BR",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "ro",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "ru",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "sk",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "sv",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "sw",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "tr",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "uk",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"@@locale": "uz",
|
||||
"@@last_modified": "2024-01-10T06:54:15+01:00",
|
||||
"@@last_modified": "2024-02-02T08:42:10+01:00",
|
||||
"settingsImportThemeButton": "Import Theme",
|
||||
"settingsImportThemeDescription": "Select theme directory to import for use in Cwtch",
|
||||
"settingsImportThemeTitle": "Import Theme",
|
||||
"settingsThemeErrorInvalid": "Error: Could not import $themeName, theme.yml missing, not a theme directory",
|
||||
"settingThemeOverwriteQuestion": "Theme $themeName already exists, confirm overwrite?",
|
||||
"settingThemeOverwriteConfirm": "Confirm",
|
||||
"settingsThemeImagesDescription": "Enable display of images from themes",
|
||||
"settingsThemeImages": "Theme Images",
|
||||
"settingsGroupAbout": "About",
|
||||
|
|
|
@ -109,10 +109,10 @@ class FlwtchState extends State<Flwtch> with WindowListener {
|
|||
}
|
||||
print("initState: invoking cwtch.Start()");
|
||||
// Cwtch.start can take time, we don't want it blocking first splash screen draw, so postpone a smidge to let splash render
|
||||
Future.delayed(const Duration(milliseconds: 50), () {
|
||||
Future.delayed(const Duration(milliseconds: 50), () async {
|
||||
print("actually invoking cwtch.cwtch()!!!");
|
||||
cwtch.Start();
|
||||
LoadAssetThemes();
|
||||
await cwtch.Start();
|
||||
LoadThemes(await cwtch.getCwtchDir());
|
||||
});
|
||||
print("initState: starting connectivityListener");
|
||||
if (EnvironmentConfig.TEST_MODE == false) {
|
||||
|
|
|
@ -81,6 +81,8 @@ class CwtchDark extends OpaqueThemeType {
|
|||
get textfieldHintColor => mainTextColor;
|
||||
get toolbarIconColor => settings; //whiteishPurple;
|
||||
get topbarColor => header; //darkGreyPurple;
|
||||
get snackbarBackgroundColor => accent;
|
||||
get snackbarTextColor => whitePurple;
|
||||
get chatImageColor => purple;
|
||||
}
|
||||
|
||||
|
@ -127,5 +129,7 @@ class CwtchLight extends OpaqueThemeType {
|
|||
get textfieldHintColor => font;
|
||||
get toolbarIconColor => settings; //darkPurple;
|
||||
get topbarColor => header; //softPurple;
|
||||
get snackbarBackgroundColor => accent;
|
||||
get snackbarTextColor => whitePurple;
|
||||
get chatImageColor => purple;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ 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";
|
||||
|
@ -20,8 +23,10 @@ final TextStyle defaultDropDownMenuItemTextStyle = TextStyle(fontFamily: "Inter"
|
|||
|
||||
Map<String, Map<String, OpaqueThemeType>> themes = Map();
|
||||
|
||||
LoadAssetThemes() async {
|
||||
themes = await loadYamlThemes();
|
||||
LoadThemes(String cwtchDir) async {
|
||||
themes = await loadBuiltinThemes();
|
||||
final customThemes = await loadCustomThemes(path.join(cwtchDir, custom_themes_subdir));
|
||||
themes.addAll(customThemes);
|
||||
}
|
||||
|
||||
OpaqueThemeType getTheme(String themeId, String mode) {
|
||||
|
@ -119,12 +124,17 @@ abstract class OpaqueThemeType {
|
|||
get messageFromOtherBackgroundColor => red;
|
||||
get messageFromOtherTextColor => red;
|
||||
|
||||
get snackbarBackgroundColor => red;
|
||||
get snackbarTextColor => red;
|
||||
|
||||
// Images
|
||||
|
||||
get chatImageColor => red;
|
||||
get chatImage => null;
|
||||
|
||||
ImageProvider loadImage(String key, {BuildContext? context}) { return AssetImage(""); }
|
||||
ImageProvider loadImage(String key, {BuildContext? context}) {
|
||||
return AssetImage("");
|
||||
}
|
||||
|
||||
// Sizes
|
||||
double contactOnionTextSize() {
|
||||
|
@ -134,112 +144,117 @@ abstract class OpaqueThemeType {
|
|||
|
||||
ThemeData mkThemeData(Settings opaque) {
|
||||
return ThemeData(
|
||||
hoverColor: opaque.current().backgroundHilightElementColor.withOpacity(0.5),
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
primarySwatch: Colors.red,
|
||||
primaryIconTheme: IconThemeData(
|
||||
color: opaque.current().mainTextColor,
|
||||
),
|
||||
primaryColor: opaque.current().mainTextColor,
|
||||
canvasColor: opaque.current().backgroundMainColor,
|
||||
backgroundColor: opaque.current().backgroundMainColor,
|
||||
highlightColor: opaque.current().hilightElementColor,
|
||||
iconTheme: IconThemeData(
|
||||
color: opaque.current().toolbarIconColor,
|
||||
),
|
||||
cardColor: opaque.current().backgroundMainColor,
|
||||
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,
|
||||
)),
|
||||
|
||||
//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)),
|
||||
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6.0),
|
||||
)),
|
||||
hoverColor: opaque.current().backgroundHilightElementColor.withOpacity(0.5),
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
primarySwatch: Colors.red,
|
||||
primaryIconTheme: IconThemeData(
|
||||
color: opaque.current().mainTextColor,
|
||||
),
|
||||
),
|
||||
primaryColor: opaque.current().mainTextColor,
|
||||
canvasColor: opaque.current().backgroundMainColor,
|
||||
backgroundColor: opaque.current().backgroundMainColor,
|
||||
highlightColor: opaque.current().hilightElementColor,
|
||||
iconTheme: IconThemeData(
|
||||
color: opaque.current().toolbarIconColor,
|
||||
),
|
||||
cardColor: opaque.current().backgroundMainColor,
|
||||
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,
|
||||
)),
|
||||
|
||||
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),
|
||||
unselectedLabelStyle: opaque.scaleFonts(defaultTextStyle),
|
||||
),
|
||||
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().defaultButtonActiveColor, selectionColor: opaque.current().defaultButtonActiveColor, selectionHandleColor: opaque.current().defaultButtonActiveColor),
|
||||
);
|
||||
//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)),
|
||||
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6.0),
|
||||
)),
|
||||
),
|
||||
),
|
||||
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),
|
||||
unselectedLabelStyle: opaque.scaleFonts(defaultTextStyle),
|
||||
),
|
||||
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().defaultButtonActiveColor, selectionColor: opaque.current().defaultButtonActiveColor, selectionHandleColor: opaque.current().defaultButtonActiveColor),
|
||||
popupMenuTheme: PopupMenuThemeData(
|
||||
color: opaque.current().backgroundPaneColor.withOpacity(0.9),
|
||||
),
|
||||
snackBarTheme: SnackBarThemeData(
|
||||
backgroundColor: opaque.current().snackbarBackgroundColor,
|
||||
contentTextStyle: TextStyle(color: opaque.current().snackbarTextColor),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ import 'package:cwtch/themes/cwtch.dart';
|
|||
import 'package:cwtch/themes/opaque.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:glob/list_local_fs.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>>> loadYamlThemes() async {
|
||||
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'));
|
||||
|
||||
|
@ -25,7 +26,7 @@ Future<Map<String, Map<String, OpaqueThemeType>>> loadYamlThemes() async {
|
|||
}
|
||||
|
||||
try {
|
||||
var data = await loadYamlTheme(themefile);
|
||||
var data = await loadAssetYamlTheme(themefile);
|
||||
|
||||
if (data != null) {
|
||||
// remove "assets/themes" and "theme.yml" from name
|
||||
|
@ -39,24 +40,69 @@ Future<Map<String, Map<String, OpaqueThemeType>>> loadYamlThemes() async {
|
|||
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>?> loadYamlTheme(String themefile) async {
|
||||
Future<Map<String, OpaqueThemeType>?> loadAssetYamlTheme(String themefile) async {
|
||||
final yml = await readAssetYamlTheme(themefile);
|
||||
|
||||
if (yml == null) {
|
||||
print("failed to load theme: $themefile");
|
||||
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);
|
||||
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);
|
||||
subthemes[mode_light] = YmlTheme(yml, yml["themes"]["name"], mode_light, assetsDir);
|
||||
}
|
||||
return subthemes;
|
||||
}
|
||||
|
@ -65,12 +111,14 @@ class YmlTheme extends OpaqueThemeType {
|
|||
late YamlMap yml;
|
||||
late String mode;
|
||||
late String theme;
|
||||
late String? assetsDir;
|
||||
late OpaqueThemeType fallbackTheme;
|
||||
|
||||
YmlTheme(YamlMap yml, theme, mode) {
|
||||
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 {
|
||||
|
@ -92,13 +140,22 @@ class YmlTheme extends OpaqueThemeType {
|
|||
if (!(val is int)) {
|
||||
return null;
|
||||
}
|
||||
return Color(0xFF000000 + val as int);
|
||||
|
||||
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) {
|
||||
return path.join("assets", "themes", yml["themes"]["name"], val);
|
||||
if (assetsDir == null) {
|
||||
return path.join("assets", "themes", yml["themes"]["name"], val);
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -137,6 +194,8 @@ class YmlTheme extends OpaqueThemeType {
|
|||
get messageFromMeTextColor => getColor("messageFromMeTextColor") ?? fallbackTheme.messageFromMeTextColor;
|
||||
get messageFromOtherBackgroundColor => getColor("messageFromOtherBackgroundColor") ?? fallbackTheme.messageFromOtherBackgroundColor;
|
||||
get messageFromOtherTextColor => getColor("messageFromOtherTextColor") ?? fallbackTheme.messageFromOtherTextColor;
|
||||
get snackbarBackgroundColor => getColor("snackbarBackgroundColor") ?? fallbackTheme.snackbarBackgroundColor;
|
||||
get snackbarTextColor => getColor("snackbarTextColor") ?? fallbackTheme.snackbarTextColor;
|
||||
|
||||
// Images
|
||||
|
||||
|
@ -149,11 +208,16 @@ class YmlTheme extends OpaqueThemeType {
|
|||
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));
|
||||
File af = File(path.join(Provider.of<FlwtchState>(context, listen: false).cwtch.getAssetsDir(), key));
|
||||
if (af.existsSync()) {
|
||||
return FileImage(af);
|
||||
}
|
||||
|
|
|
@ -19,154 +19,104 @@ class GlobalSettingsAboutView extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _GlobalSettingsAboutViewState extends State<GlobalSettingsAboutView> {
|
||||
|
||||
ScrollController settingsListScrollController = ScrollController();
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<Settings>(builder: (ccontext, settings, child) {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
var appIcon = Icon(Icons.info, color: settings
|
||||
.current()
|
||||
.mainTextColor);
|
||||
return Scrollbar(
|
||||
key: Key("AboutSettingsView"),
|
||||
trackVisibility: true,
|
||||
return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
var appIcon = Icon(Icons.info, color: settings.current().mainTextColor);
|
||||
return Scrollbar(
|
||||
key: Key("AboutSettingsView"),
|
||||
trackVisibility: true,
|
||||
controller: settingsListScrollController,
|
||||
child: SingleChildScrollView(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
controller: settingsListScrollController,
|
||||
child: SingleChildScrollView(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
controller: settingsListScrollController,
|
||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Column(children: [
|
||||
AboutListTile(
|
||||
icon: appIcon,
|
||||
applicationIcon: Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: Icon(CwtchIcons.cwtch_knott)),
|
||||
applicationName: "Cwtch UI",
|
||||
applicationLegalese:
|
||||
'\u{a9} 2021-2023 Open Privacy Research Society',
|
||||
aboutBoxChildren: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
24.0 + 10.0 + (appIcon.size ?? 24.0),
|
||||
16.0,
|
||||
0.0,
|
||||
0.0),
|
||||
// About has 24 padding (ln 389) and there appears to be another 10 of padding in the widget
|
||||
child: SelectableText(
|
||||
AppLocalizations.of(context)!
|
||||
.versionBuilddate
|
||||
.replaceAll(
|
||||
"%1", EnvironmentConfig.BUILD_VER)
|
||||
.replaceAll("%2",
|
||||
EnvironmentConfig.BUILD_DATE)),
|
||||
)
|
||||
]),
|
||||
SwitchListTile(
|
||||
// TODO: Translate, Remove, OR Hide Prior to Release
|
||||
title: Text("Show Performance Overlay"),
|
||||
subtitle:
|
||||
Text("Display an overlay graph of render time."),
|
||||
value: settings.profileMode,
|
||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Column(children: [
|
||||
AboutListTile(
|
||||
icon: appIcon,
|
||||
applicationIcon: Padding(padding: EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)),
|
||||
applicationName: "Cwtch UI",
|
||||
applicationLegalese: '\u{a9} 2021-2023 Open Privacy Research Society',
|
||||
aboutBoxChildren: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(24.0 + 10.0 + (appIcon.size ?? 24.0), 16.0, 0.0, 0.0),
|
||||
// About has 24 padding (ln 389) and there appears to be another 10 of padding in the widget
|
||||
child: SelectableText(AppLocalizations.of(context)!.versionBuilddate.replaceAll("%1", EnvironmentConfig.BUILD_VER).replaceAll("%2", EnvironmentConfig.BUILD_DATE)),
|
||||
)
|
||||
]),
|
||||
SwitchListTile(
|
||||
// TODO: Translate, Remove, OR Hide Prior to Release
|
||||
title: Text("Show Performance Overlay"),
|
||||
subtitle: Text("Display an overlay graph of render time."),
|
||||
value: settings.profileMode,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
if (value) {
|
||||
settings.profileMode = value;
|
||||
} else {
|
||||
settings.profileMode = value;
|
||||
}
|
||||
});
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.bar_chart, color: settings.current().mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: EnvironmentConfig.BUILD_VER == dev_version && !Platform.isAndroid,
|
||||
child: SwitchListTile(
|
||||
title: Text("Show Semantic Debugger"),
|
||||
subtitle: Text("Show Accessibility Debugging View"),
|
||||
value: settings.useSemanticDebugger,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
if (value) {
|
||||
settings.profileMode = value;
|
||||
} else {
|
||||
settings.profileMode = value;
|
||||
}
|
||||
});
|
||||
if (value) {
|
||||
settings.useSemanticDebugger = value;
|
||||
} else {
|
||||
settings.useSemanticDebugger = value;
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.bar_chart,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible:
|
||||
EnvironmentConfig.BUILD_VER == dev_version &&
|
||||
!Platform.isAndroid,
|
||||
child: SwitchListTile(
|
||||
title: Text("Show Semantic Debugger"),
|
||||
subtitle:
|
||||
Text("Show Accessibility Debugging View"),
|
||||
value: settings.useSemanticDebugger,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.useSemanticDebugger = value;
|
||||
} else {
|
||||
settings.useSemanticDebugger = value;
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.settings_accessibility,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
)),
|
||||
Visibility(
|
||||
visible: EnvironmentConfig.BUILD_VER == dev_version &&
|
||||
!Platform.isAndroid,
|
||||
child: FutureBuilder(
|
||||
future:
|
||||
EnvironmentConfig.BUILD_VER != dev_version ||
|
||||
Platform.isAndroid
|
||||
? null
|
||||
: Provider
|
||||
.of<FlwtchState>(context)
|
||||
.cwtch
|
||||
.GetDebugInfo(),
|
||||
activeTrackColor: settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.settings_accessibility, color: settings.current().mainTextColor),
|
||||
)),
|
||||
Visibility(
|
||||
visible: EnvironmentConfig.BUILD_VER == dev_version && !Platform.isAndroid,
|
||||
child: FutureBuilder(
|
||||
future: EnvironmentConfig.BUILD_VER != dev_version || Platform.isAndroid ? null : Provider.of<FlwtchState>(context).cwtch.GetDebugInfo(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return Column(
|
||||
children: [
|
||||
Text("libCwtch Debug Info: " + snapshot.data.toString()),
|
||||
Text("Message Cache Size (Mb): " + (Provider.of<FlwtchState>(context).profs.cacheMemUsage() / (1024 * 1024)).toString())
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: EnvironmentConfig.BUILD_VER == dev_version,
|
||||
child: FutureBuilder(
|
||||
future: Provider.of<FlwtchState>(context).cwtch.PlatformChannelInfo(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return Column(
|
||||
children: [
|
||||
Text("libCwtch Debug Info: " +
|
||||
snapshot.data.toString()),
|
||||
Text("Message Cache Size (Mb): " +
|
||||
(Provider
|
||||
.of<FlwtchState>(context)
|
||||
.profs
|
||||
.cacheMemUsage() /
|
||||
(1024 * 1024))
|
||||
.toString())
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
HashMap<String, String> data = snapshot.data as HashMap<String, String>;
|
||||
return getPlatformInfo(settings, data);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: EnvironmentConfig.BUILD_VER == dev_version,
|
||||
child: FutureBuilder(
|
||||
future: Provider
|
||||
.of<FlwtchState>(context)
|
||||
.cwtch
|
||||
.PlatformChannelInfo(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
HashMap<String, String> data = snapshot.data
|
||||
as HashMap<String, String>;
|
||||
return getPlatformInfo(settings, data);
|
||||
}
|
||||
return Container();
|
||||
}))
|
||||
]))));
|
||||
});
|
||||
return Container();
|
||||
}))
|
||||
]))));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -194,4 +144,4 @@ class _GlobalSettingsAboutViewState extends State<GlobalSettingsAboutView> {
|
|||
}
|
||||
return pinfo.version + "." + pinfo.buildNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import '../config.dart';
|
||||
import '../controllers/filesharing.dart';
|
||||
import '../cwtch_icons_icons.dart';
|
||||
import '../main.dart';
|
||||
import '../models/appstate.dart';
|
||||
import '../settings.dart';
|
||||
import '../themes/cwtch.dart';
|
||||
import '../themes/opaque.dart';
|
||||
import '../themes/yamltheme.dart';
|
||||
import '../widgets/folderpicker.dart';
|
||||
import 'globalsettingsview.dart';
|
||||
|
||||
class GlobalSettingsAppearanceView extends StatefulWidget {
|
||||
|
@ -15,319 +23,231 @@ class GlobalSettingsAppearanceView extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _GlobalSettingsAppearanceViewState extends State<GlobalSettingsAppearanceView> {
|
||||
|
||||
ScrollController settingsListScrollController = ScrollController();
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<Settings>(builder: (ccontext, settings, child) {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
return Scrollbar(
|
||||
key: Key("AppearanceSettingsView"),
|
||||
trackVisibility: true,
|
||||
return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
return Scrollbar(
|
||||
key: Key("AppearanceSettingsView"),
|
||||
trackVisibility: true,
|
||||
controller: settingsListScrollController,
|
||||
child: SingleChildScrollView(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
controller: settingsListScrollController,
|
||||
child: SingleChildScrollView(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
controller: settingsListScrollController,
|
||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Column(children: [
|
||||
ListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.settingLanguage),
|
||||
leading: Icon(CwtchIcons.change_language,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
trailing: Container(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
child: DropdownButton(
|
||||
key: Key("languagelist"),
|
||||
isExpanded: true,
|
||||
value: Provider
|
||||
.of<Settings>(context)
|
||||
.locale
|
||||
.toString(),
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
EnvironmentConfig.debugLog(
|
||||
"setting language: $newValue");
|
||||
settings.switchLocaleByCode(newValue!);
|
||||
saveSettings(context);
|
||||
});
|
||||
},
|
||||
items: AppLocalizations.supportedLocales
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(Locale value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value.toString(),
|
||||
child: Text(
|
||||
key: Key("dropdownLanguage" +
|
||||
value.languageCode),
|
||||
getLanguageFull(
|
||||
context,
|
||||
value.languageCode,
|
||||
value.countryCode),
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
}).toList()))),
|
||||
SwitchListTile(
|
||||
title:
|
||||
Text(AppLocalizations.of(context)!.settingTheme),
|
||||
value: settings
|
||||
.current()
|
||||
.mode == mode_light,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.setTheme(
|
||||
settings.theme.theme, mode_light);
|
||||
} else {
|
||||
settings.setTheme(
|
||||
settings.theme.theme, mode_dark);
|
||||
}
|
||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Column(children: [
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!.settingLanguage),
|
||||
leading: Icon(CwtchIcons.change_language, color: settings.current().mainTextColor),
|
||||
trailing: Container(
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: DropdownButton(
|
||||
key: Key("languagelist"),
|
||||
isExpanded: true,
|
||||
value: Provider.of<Settings>(context).locale.toString(),
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
EnvironmentConfig.debugLog("setting language: $newValue");
|
||||
settings.switchLocaleByCode(newValue!);
|
||||
saveSettings(context);
|
||||
});
|
||||
},
|
||||
items: AppLocalizations.supportedLocales.map<DropdownMenuItem<String>>((Locale value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value.toString(),
|
||||
child: Text(
|
||||
key: Key("dropdownLanguage" + value.languageCode),
|
||||
getLanguageFull(context, value.languageCode, value.countryCode),
|
||||
style: settings.scaleFonts(defaultDropDownMenuItemTextStyle),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
}).toList()))),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.settingTheme),
|
||||
value: settings.current().mode == mode_light,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.setTheme(settings.theme.theme, mode_light);
|
||||
} else {
|
||||
settings.setTheme(settings.theme.theme, mode_dark);
|
||||
}
|
||||
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.change_theme,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.themeColorLabel),
|
||||
trailing: Container(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
child: DropdownButton<String>(
|
||||
key: Key("DropdownTheme"),
|
||||
isExpanded: true,
|
||||
value: Provider
|
||||
.of<Settings>(context)
|
||||
.theme
|
||||
.theme,
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
settings.setTheme(
|
||||
newValue!, settings.theme.mode);
|
||||
saveSettings(context);
|
||||
});
|
||||
},
|
||||
items: themes.keys
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(String themeId) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: themeId,
|
||||
child: Text(
|
||||
getThemeName(context, themeId),
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle)), //"ddi_$themeId", key: Key("ddi_$themeId")),
|
||||
);
|
||||
}).toList())),
|
||||
leading: Icon(Icons.palette,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.settingsThemeImages),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.settingsThemeImagesDescription),
|
||||
value: settings.themeImages,
|
||||
onChanged: (bool value) {
|
||||
settings.themeImages = value;// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.image,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.settingUIColumnPortrait),
|
||||
leading: Icon(Icons.table_chart,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.change_theme, color: settings.current().mainTextColor),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!.themeColorLabel),
|
||||
trailing: Container(
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: DropdownButton<String>(
|
||||
key: Key("DropdownTheme"),
|
||||
isExpanded: true,
|
||||
value: Provider.of<Settings>(context).theme.theme,
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
settings.setTheme(newValue!, settings.theme.mode);
|
||||
saveSettings(context);
|
||||
});
|
||||
},
|
||||
items: themes.keys.map<DropdownMenuItem<String>>((String themeId) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: themeId,
|
||||
child: Text(getThemeName(context, themeId), style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)), //"ddi_$themeId", key: Key("ddi_$themeId")),
|
||||
);
|
||||
}).toList())),
|
||||
leading: Icon(Icons.palette, color: settings.current().mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
// TODO: Android support needs gomobile support for reading / writing themes, and ideally importing from a .zip or .tar.gz
|
||||
visible: !Platform.isAndroid,
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.palette, color: Provider.of<Settings>(context).theme.messageFromMeTextColor),
|
||||
title: Text(AppLocalizations.of(context)!.settingsImportThemeTitle),
|
||||
subtitle: Text(AppLocalizations.of(context)!.settingsImportThemeDescription),
|
||||
//AppLocalizations.of(
|
||||
//context)!
|
||||
//.fileSharingSettingsDownloadFolderDescription,
|
||||
trailing: Container(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: ElevatedButton.icon(
|
||||
label: Text(AppLocalizations.of(context)!.settingsImportThemeButton),
|
||||
onPressed: Provider.of<AppState>(context).disableFilePicker
|
||||
? null
|
||||
: () async {
|
||||
if (Platform.isAndroid) {
|
||||
return;
|
||||
}
|
||||
var selectedDirectory = await showSelectDirectoryPicker(context);
|
||||
if (selectedDirectory != null) {
|
||||
selectedDirectory += path.separator;
|
||||
final customThemeDir = path.join(await Provider.of<FlwtchState>(context, listen: false).cwtch.getCwtchDir(), custom_themes_subdir);
|
||||
importThemeCheck(context, settings, customThemeDir, selectedDirectory);
|
||||
} else {
|
||||
// User canceled the picker
|
||||
}
|
||||
},
|
||||
//onChanged: widget.onSave,
|
||||
icon: Icon(Icons.folder),
|
||||
//tooltip: widget.tooltip,
|
||||
)))),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.settingsThemeImages),
|
||||
subtitle: Text(AppLocalizations.of(context)!.settingsThemeImagesDescription),
|
||||
value: settings.themeImages,
|
||||
onChanged: (bool value) {
|
||||
settings.themeImages = value; // Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.image, color: settings.current().mainTextColor),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!.settingUIColumnPortrait),
|
||||
leading: Icon(Icons.table_chart, color: settings.current().mainTextColor),
|
||||
trailing: Container(
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: DropdownButton(
|
||||
isExpanded: true,
|
||||
value: settings.uiColumnModePortrait.toString(),
|
||||
onChanged: (String? newValue) {
|
||||
settings.uiColumnModePortrait = Settings.uiColumnModeFromString(newValue!);
|
||||
saveSettings(context);
|
||||
},
|
||||
items: Settings.uiColumnModeOptions(false).map<DropdownMenuItem<String>>((DualpaneMode value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value.toString(),
|
||||
child: Text(Settings.uiColumnModeToString(value, context), style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList()))),
|
||||
ListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.settingUIColumnLandscape,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
softWrap: true,
|
||||
),
|
||||
leading: Icon(Icons.stay_primary_landscape, color: settings.current().mainTextColor),
|
||||
trailing: Container(
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: DropdownButton(
|
||||
isExpanded: true,
|
||||
value: settings.uiColumnModePortrait
|
||||
.toString(),
|
||||
value: settings.uiColumnModeLandscape.toString(),
|
||||
onChanged: (String? newValue) {
|
||||
settings.uiColumnModePortrait =
|
||||
Settings.uiColumnModeFromString(
|
||||
newValue!);
|
||||
settings.uiColumnModeLandscape = Settings.uiColumnModeFromString(newValue!);
|
||||
saveSettings(context);
|
||||
},
|
||||
items: Settings.uiColumnModeOptions(false)
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(DualpaneMode value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value.toString(),
|
||||
child: Text(
|
||||
Settings.uiColumnModeToString(
|
||||
value, context),
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList()))),
|
||||
ListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!
|
||||
.settingUIColumnLandscape,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
softWrap: true,
|
||||
),
|
||||
leading: Icon(Icons.stay_primary_landscape,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
trailing: Container(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
child: Container(
|
||||
width:
|
||||
MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
child: DropdownButton(
|
||||
isExpanded: true,
|
||||
value: settings.uiColumnModeLandscape
|
||||
.toString(),
|
||||
onChanged: (String? newValue) {
|
||||
settings.uiColumnModeLandscape =
|
||||
Settings.uiColumnModeFromString(
|
||||
newValue!);
|
||||
saveSettings(context);
|
||||
},
|
||||
items:
|
||||
Settings.uiColumnModeOptions(true)
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(DualpaneMode value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value.toString(),
|
||||
child: Text(
|
||||
Settings.uiColumnModeToString(
|
||||
value, context),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList())))),
|
||||
ListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.defaultScalingText),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.fontScalingDescription),
|
||||
trailing: Container(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
child: Slider(
|
||||
onChanged: (double value) {
|
||||
settings.fontScaling = value;
|
||||
items: Settings.uiColumnModeOptions(true).map<DropdownMenuItem<String>>((DualpaneMode value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value.toString(),
|
||||
child: Text(Settings.uiColumnModeToString(value, context), overflow: TextOverflow.ellipsis, style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList())))),
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!.defaultScalingText),
|
||||
subtitle: Text(AppLocalizations.of(context)!.fontScalingDescription),
|
||||
trailing: Container(
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: Slider(
|
||||
onChanged: (double value) {
|
||||
settings.fontScaling = value;
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
EnvironmentConfig.debugLog(
|
||||
"Font Scaling: $value");
|
||||
},
|
||||
min: 0.5,
|
||||
divisions: 12,
|
||||
max: 2.0,
|
||||
label: '${settings.fontScaling * 100}%',
|
||||
activeColor:
|
||||
settings
|
||||
.current()
|
||||
.defaultButtonColor,
|
||||
thumbColor: settings
|
||||
.current()
|
||||
.mainTextColor,
|
||||
overlayColor: MaterialStateProperty.all(
|
||||
settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
inactiveColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
value: settings.fontScaling)),
|
||||
leading: Icon(Icons.format_size,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.streamerModeLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.descriptionStreamerMode),
|
||||
value: settings.streamerMode,
|
||||
onChanged: (bool value) {
|
||||
settings.setStreamerMode(value);
|
||||
saveSettings(context);
|
||||
EnvironmentConfig.debugLog("Font Scaling: $value");
|
||||
},
|
||||
min: 0.5,
|
||||
divisions: 12,
|
||||
max: 2.0,
|
||||
label: '${settings.fontScaling * 100}%',
|
||||
activeColor: settings.current().defaultButtonColor,
|
||||
thumbColor: settings.current().mainTextColor,
|
||||
overlayColor: MaterialStateProperty.all(settings.current().mainTextColor),
|
||||
inactiveColor: settings.theme.defaultButtonDisabledColor,
|
||||
value: settings.fontScaling)),
|
||||
leading: Icon(Icons.format_size, color: settings.current().mainTextColor),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.streamerModeLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!.descriptionStreamerMode),
|
||||
value: settings.streamerMode,
|
||||
onChanged: (bool value) {
|
||||
settings.setStreamerMode(value);
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.streamer_bunnymask,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.formattingExperiment),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.messageFormattingDescription),
|
||||
value: settings
|
||||
.isExperimentEnabled(FormattingExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(FormattingExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(FormattingExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.text_fields,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
]))));
|
||||
});
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.streamer_bunnymask, color: settings.current().mainTextColor),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.formattingExperiment),
|
||||
subtitle: Text(AppLocalizations.of(context)!.messageFormattingDescription),
|
||||
value: settings.isExperimentEnabled(FormattingExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(FormattingExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(FormattingExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.text_fields, color: settings.current().mainTextColor),
|
||||
),
|
||||
]))));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -435,4 +355,97 @@ class _GlobalSettingsAppearanceViewState extends State<GlobalSettingsAppearanceV
|
|||
}
|
||||
return theme;
|
||||
}
|
||||
|
||||
void importThemeCheck(BuildContext context, Settings settings, String themesDir, String newThemeDirectory) async {
|
||||
// check is theme
|
||||
final srcDir = Directory(newThemeDirectory);
|
||||
String themeName = path.basename(newThemeDirectory);
|
||||
|
||||
File themeFile = File(path.join(newThemeDirectory, "theme.yml"));
|
||||
|
||||
if (!themeFile.existsSync()) {
|
||||
// error, isnt valid theme, no .yml theme file found
|
||||
SnackBar err = SnackBar(content: Text(AppLocalizations.of(context)!.settingsThemeErrorInvalid.replaceAll("\$themeName", themeName)));
|
||||
ScaffoldMessenger.of(context).showSnackBar(err);
|
||||
return;
|
||||
}
|
||||
|
||||
Directory targetDir = Directory(path.join(themesDir, themeName));
|
||||
// check if exists
|
||||
if (themes.containsKey(themeName) || targetDir.existsSync()) {
|
||||
_modalConfirmOverwriteCustomTheme(srcDir, targetDir, themesDir, themeName, settings);
|
||||
} else {
|
||||
importTheme(srcDir, targetDir, themesDir, themeName, settings);
|
||||
}
|
||||
}
|
||||
|
||||
void importTheme(Directory srcDir, Directory targetDir, String themesDir, String themeName, Settings settings) async {
|
||||
if (!targetDir.existsSync()) {
|
||||
targetDir = await targetDir.create();
|
||||
}
|
||||
|
||||
// importTheme(newVal)
|
||||
await for (var entity in srcDir.list(recursive: false)) {
|
||||
if (entity is File) {
|
||||
entity.copySync(path.join(targetDir.path, path.basename(entity.path)));
|
||||
}
|
||||
}
|
||||
|
||||
var data = await loadFileYamlTheme(path.join(targetDir.path, "theme.yml"), targetDir.path);
|
||||
if (data != null) {
|
||||
themes[themeName] = data;
|
||||
}
|
||||
|
||||
if (settings.current().theme == themeName) {
|
||||
settings.setTheme(themeName, settings.current().mode);
|
||||
}
|
||||
}
|
||||
|
||||
void _modalConfirmOverwriteCustomTheme(Directory srcDir, Directory targetDir, String themesDir, String themeName, Settings settings) {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (BuildContext context) {
|
||||
return Padding(
|
||||
padding: MediaQuery.of(context).viewInsets,
|
||||
child: RepaintBoundary(
|
||||
child: Container(
|
||||
height: Platform.isAndroid ? 250 : 200, // bespoke value courtesy of the [TextField] docs
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Column(mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
Text(AppLocalizations.of(context)!.settingThemeOverwriteQuestion.replaceAll("\$themeName", themeName)),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Spacer(),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context)!.settingThemeOverwriteConfirm, semanticsLabel: AppLocalizations.of(context)!.settingThemeOverwriteConfirm),
|
||||
onPressed: () {
|
||||
importTheme(srcDir, targetDir, themesDir, themeName, settings);
|
||||
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)),
|
||||
SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context)!.cancel, semanticsLabel: AppLocalizations.of(context)!.cancel),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)),
|
||||
Spacer(),
|
||||
],
|
||||
)
|
||||
]))))));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
static const androidSettingsChannel = const MethodChannel('androidSettings');
|
||||
static const androidSettingsChangeChannel = const MethodChannel('androidSettingsChanged');
|
||||
|
||||
|
||||
ScrollController settingsListScrollController = ScrollController();
|
||||
bool powerExempt = false;
|
||||
|
||||
|
@ -31,8 +30,8 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
|
||||
if (Platform.isAndroid) {
|
||||
isBatteryExempt().then((value) => setState(() {
|
||||
powerExempt = value;
|
||||
}));
|
||||
powerExempt = value;
|
||||
}));
|
||||
} else {
|
||||
powerExempt = false;
|
||||
}
|
||||
|
@ -51,8 +50,7 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
//* Android Only Requests
|
||||
|
||||
Future<bool> isBatteryExempt() async {
|
||||
return await androidSettingsChannel.invokeMethod('isBatteryExempt', {}) ??
|
||||
false;
|
||||
return await androidSettingsChannel.invokeMethod('isBatteryExempt', {}) ?? false;
|
||||
}
|
||||
|
||||
Future<void> requestBatteryExemption() async {
|
||||
|
@ -62,11 +60,9 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
|
||||
//* End Android Only Requests
|
||||
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<Settings>(builder: (ccontext, settings, child) {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
return Scrollbar(
|
||||
key: Key("BehaviourSettingsView"),
|
||||
trackVisibility: true,
|
||||
|
@ -83,10 +79,8 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
Visibility(
|
||||
visible: Platform.isAndroid,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.settingAndroidPowerExemption),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.settingAndroidPowerExemptionDescription),
|
||||
title: Text(AppLocalizations.of(context)!.settingAndroidPowerExemption),
|
||||
subtitle: Text(AppLocalizations.of(context)!.settingAndroidPowerExemptionDescription),
|
||||
value: powerExempt,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
|
@ -97,17 +91,13 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
}
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.power,
|
||||
color: settings.current().mainTextColor),
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.power, color: settings.current().mainTextColor),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.notificationPolicySettingLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.notificationPolicySettingDescription),
|
||||
title: Text(AppLocalizations.of(context)!.notificationPolicySettingLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!.notificationPolicySettingDescription),
|
||||
trailing: Container(
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: DropdownButton(
|
||||
|
@ -117,27 +107,17 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
settings.notificationPolicy = newValue!;
|
||||
saveSettings(context);
|
||||
},
|
||||
items: NotificationPolicy.values.map<
|
||||
DropdownMenuItem<NotificationPolicy>>(
|
||||
(NotificationPolicy value) {
|
||||
return DropdownMenuItem<NotificationPolicy>(
|
||||
value: value,
|
||||
child: Text(
|
||||
Settings.notificationPolicyToString(
|
||||
value, context),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList())),
|
||||
leading: Icon(CwtchIcons.chat_bubble_empty_24px,
|
||||
color: settings.current().mainTextColor),
|
||||
items: NotificationPolicy.values.map<DropdownMenuItem<NotificationPolicy>>((NotificationPolicy value) {
|
||||
return DropdownMenuItem<NotificationPolicy>(
|
||||
value: value,
|
||||
child: Text(Settings.notificationPolicyToString(value, context), overflow: TextOverflow.ellipsis, style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList())),
|
||||
leading: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.notificationContentSettingLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.notificationContentSettingDescription),
|
||||
title: Text(AppLocalizations.of(context)!.notificationContentSettingLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!.notificationContentSettingDescription),
|
||||
trailing: Container(
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: DropdownButton(
|
||||
|
@ -147,29 +127,17 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
settings.notificationContent = newValue!;
|
||||
saveSettings(context);
|
||||
},
|
||||
items: NotificationContent.values.map<
|
||||
DropdownMenuItem<
|
||||
NotificationContent>>(
|
||||
(NotificationContent value) {
|
||||
return DropdownMenuItem<
|
||||
NotificationContent>(
|
||||
value: value,
|
||||
child: Text(
|
||||
Settings.notificationContentToString(
|
||||
value, context),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList())),
|
||||
leading: Icon(CwtchIcons.chat_bubble_empty_24px,
|
||||
color: settings.current().mainTextColor),
|
||||
items: NotificationContent.values.map<DropdownMenuItem<NotificationContent>>((NotificationContent value) {
|
||||
return DropdownMenuItem<NotificationContent>(
|
||||
value: value,
|
||||
child: Text(Settings.notificationContentToString(value, context), overflow: TextOverflow.ellipsis, style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList())),
|
||||
leading: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.blockUnknownLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.descriptionBlockUnknownConnections),
|
||||
title: Text(AppLocalizations.of(context)!.blockUnknownLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!.descriptionBlockUnknownConnections),
|
||||
value: settings.blockUnknownConnections,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
|
@ -182,16 +150,12 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.block_unknown,
|
||||
color: settings.current().mainTextColor),
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.block_unknown, color: settings.current().mainTextColor),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.defaultPreserveHistorySetting),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.preserveHistorySettingDescription),
|
||||
title: Text(AppLocalizations.of(context)!.defaultPreserveHistorySetting),
|
||||
subtitle: Text(AppLocalizations.of(context)!.preserveHistorySettingDescription),
|
||||
value: settings.preserveHistoryByDefault,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
|
@ -204,13 +168,11 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.peer_history,
|
||||
color: settings.current().mainTextColor),
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.peer_history, color: settings.current().mainTextColor),
|
||||
),
|
||||
]))));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -224,8 +186,7 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
|
||||
// set up the AlertDialog
|
||||
AlertDialog alert = AlertDialog(
|
||||
title:
|
||||
Text(AppLocalizations.of(context)!.settingsAndroidPowerReenablePopup),
|
||||
title: Text(AppLocalizations.of(context)!.settingsAndroidPowerReenablePopup),
|
||||
actions: [
|
||||
okButton,
|
||||
],
|
||||
|
@ -239,4 +200,4 @@ class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourVie
|
|||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,358 +19,224 @@ class GlobalSettingsExperimentsView extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _GlobalSettingsExperimentsViewState extends State<GlobalSettingsExperimentsView> {
|
||||
|
||||
ScrollController settingsListScrollController = ScrollController();
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<Settings>(builder: (ccontext, settings, child) {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
return Scrollbar(
|
||||
key: Key("ExperimentsSettingsView"),
|
||||
trackVisibility: true,
|
||||
return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
return Scrollbar(
|
||||
key: Key("ExperimentsSettingsView"),
|
||||
trackVisibility: true,
|
||||
controller: settingsListScrollController,
|
||||
child: SingleChildScrollView(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
controller: settingsListScrollController,
|
||||
child: SingleChildScrollView(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
controller: settingsListScrollController,
|
||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Column(children: [
|
||||
SwitchListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.experimentsEnabled),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.descriptionExperiments),
|
||||
value: settings.experimentsEnabled,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiments();
|
||||
} else {
|
||||
settings.disableExperiments();
|
||||
}
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.enable_experiments,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: settings.experimentsEnabled,
|
||||
child: Column(
|
||||
children: [
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.enableGroups),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.descriptionExperimentsGroups),
|
||||
value: settings.isExperimentEnabled(
|
||||
TapirGroupsExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(
|
||||
TapirGroupsExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
TapirGroupsExperiment);
|
||||
}
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.enable_groups,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible:
|
||||
!Platform.isAndroid && !Platform.isIOS,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.settingServers),
|
||||
subtitle: Provider
|
||||
.of<FlwtchState>(
|
||||
context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsServersCompiled()
|
||||
? Text(AppLocalizations.of(context)!
|
||||
.settingServersDescription)
|
||||
: Text(
|
||||
"This version of Cwtch has been compiled without support for the server hosting experiment."),
|
||||
value: Provider
|
||||
.of<FlwtchState>(context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsServersCompiled() &&
|
||||
settings.isExperimentEnabled(
|
||||
ServerManagementExperiment),
|
||||
onChanged: Provider
|
||||
.of<FlwtchState>(
|
||||
context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsServersCompiled()
|
||||
? (bool value) {
|
||||
Provider.of<ServerListState>(
|
||||
context,
|
||||
listen: false)
|
||||
.clear();
|
||||
if (value) {
|
||||
settings.enableExperiment(
|
||||
ServerManagementExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
ServerManagementExperiment);
|
||||
}
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
}
|
||||
: null,
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor: settings
|
||||
.theme.defaultButtonDisabledColor,
|
||||
inactiveThumbColor: settings
|
||||
.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.dns_24px),
|
||||
)),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.settingFileSharing),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.descriptionFileSharing),
|
||||
value: settings.isExperimentEnabled(
|
||||
FileSharingExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
if (checkDownloadDirectory(
|
||||
context, settings)) {
|
||||
settings.enableExperiment(
|
||||
FileSharingExperiment);
|
||||
} else {
|
||||
settings.enableExperiment(
|
||||
FileSharingExperiment);
|
||||
settings.disableExperiment(
|
||||
ImagePreviewsExperiment);
|
||||
}
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
FileSharingExperiment);
|
||||
settings.disableExperiment(
|
||||
ImagePreviewsExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.attached_file_3,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: settings.isExperimentEnabled(
|
||||
FileSharingExperiment),
|
||||
child: Column(children: [
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.settingImagePreviews),
|
||||
subtitle: Text(
|
||||
AppLocalizations.of(context)!
|
||||
.settingImagePreviewsDescription),
|
||||
value: settings.isExperimentEnabled(
|
||||
ImagePreviewsExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
if (checkDownloadDirectory(
|
||||
context, settings)) {
|
||||
settings.enableExperiment(
|
||||
ImagePreviewsExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
ImagePreviewsExperiment);
|
||||
}
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
ImagePreviewsExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings
|
||||
.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor: settings
|
||||
.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.photo,
|
||||
color:
|
||||
settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: settings.isExperimentEnabled(
|
||||
ImagePreviewsExperiment) &&
|
||||
!Platform.isAndroid,
|
||||
child: CwtchFolderPicker(
|
||||
testKey: Key("DownloadFolderPicker"),
|
||||
label: AppLocalizations.of(context)!
|
||||
.settingDownloadFolder,
|
||||
initialValue: settings.downloadPath,
|
||||
textStyle: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle),
|
||||
description: AppLocalizations.of(
|
||||
context)!
|
||||
.fileSharingSettingsDownloadFolderDescription,
|
||||
tooltip: AppLocalizations.of(context)!
|
||||
.fileSharingSettingsDownloadFolderTooltip,
|
||||
onSave: (newVal) {
|
||||
settings.downloadPath = newVal;
|
||||
saveSettings(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.blodeuweddExperimentEnable),
|
||||
subtitle: Provider
|
||||
.of<FlwtchState>(context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsBlodeuweddSupported()
|
||||
? Text(AppLocalizations.of(context)!
|
||||
.blodeuweddDescription)
|
||||
: Text(AppLocalizations.of(context)!
|
||||
.blodeuweddNotSupported),
|
||||
value: Provider
|
||||
.of<FlwtchState>(context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsBlodeuweddSupported() &&
|
||||
settings.isExperimentEnabled(
|
||||
BlodeuweddExperiment),
|
||||
onChanged: Provider
|
||||
.of<FlwtchState>(context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsBlodeuweddSupported()
|
||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Column(children: [
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.experimentsEnabled),
|
||||
subtitle: Text(AppLocalizations.of(context)!.descriptionExperiments),
|
||||
value: settings.experimentsEnabled,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiments();
|
||||
} else {
|
||||
settings.disableExperiments();
|
||||
}
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.enable_experiments, color: settings.current().mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: settings.experimentsEnabled,
|
||||
child: Column(
|
||||
children: [
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.enableGroups),
|
||||
subtitle: Text(AppLocalizations.of(context)!.descriptionExperimentsGroups),
|
||||
value: settings.isExperimentEnabled(TapirGroupsExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(TapirGroupsExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(TapirGroupsExperiment);
|
||||
}
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.enable_groups, color: settings.current().mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: !Platform.isAndroid && !Platform.isIOS,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.settingServers),
|
||||
subtitle: Provider.of<FlwtchState>(context, listen: false).cwtch.IsServersCompiled()
|
||||
? Text(AppLocalizations.of(context)!.settingServersDescription)
|
||||
: Text("This version of Cwtch has been compiled without support for the server hosting experiment."),
|
||||
value: Provider.of<FlwtchState>(context, listen: false).cwtch.IsServersCompiled() && settings.isExperimentEnabled(ServerManagementExperiment),
|
||||
onChanged: Provider.of<FlwtchState>(context, listen: false).cwtch.IsServersCompiled()
|
||||
? (bool value) {
|
||||
Provider.of<ServerListState>(context, listen: false).clear();
|
||||
if (value) {
|
||||
settings.enableExperiment(ServerManagementExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(ServerManagementExperiment);
|
||||
}
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
}
|
||||
: null,
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
inactiveThumbColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.dns_24px),
|
||||
)),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.settingFileSharing),
|
||||
subtitle: Text(AppLocalizations.of(context)!.descriptionFileSharing),
|
||||
value: settings.isExperimentEnabled(FileSharingExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
if (checkDownloadDirectory(context, settings)) {
|
||||
settings.enableExperiment(FileSharingExperiment);
|
||||
} else {
|
||||
settings.enableExperiment(FileSharingExperiment);
|
||||
settings.disableExperiment(ImagePreviewsExperiment);
|
||||
}
|
||||
} else {
|
||||
settings.disableExperiment(FileSharingExperiment);
|
||||
settings.disableExperiment(ImagePreviewsExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.attached_file_3, color: settings.current().mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: settings.isExperimentEnabled(FileSharingExperiment),
|
||||
child: Column(children: [
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.settingImagePreviews),
|
||||
subtitle: Text(AppLocalizations.of(context)!.settingImagePreviewsDescription),
|
||||
value: settings.isExperimentEnabled(ImagePreviewsExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(
|
||||
BlodeuweddExperiment);
|
||||
if (checkDownloadDirectory(context, settings)) {
|
||||
settings.enableExperiment(ImagePreviewsExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(ImagePreviewsExperiment);
|
||||
}
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
BlodeuweddExperiment);
|
||||
settings.disableExperiment(ImagePreviewsExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
}
|
||||
: null,
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
inactiveThumbColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.assistant,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.photo, color: settings.current().mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: Provider
|
||||
.of<FlwtchState>(context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsBlodeuweddSupported() &&
|
||||
settings.isExperimentEnabled(
|
||||
BlodeuweddExperiment),
|
||||
visible: settings.isExperimentEnabled(ImagePreviewsExperiment) && !Platform.isAndroid,
|
||||
child: CwtchFolderPicker(
|
||||
testKey: Key("DownloadFolderPicker"),
|
||||
label: AppLocalizations.of(context)!
|
||||
.settingDownloadFolder,
|
||||
initialValue: settings.blodeuweddPath,
|
||||
description: AppLocalizations.of(context)!
|
||||
.blodeuweddPath,
|
||||
tooltip: AppLocalizations.of(context)!
|
||||
.blodeuweddPath,
|
||||
label: AppLocalizations.of(context)!.settingDownloadFolder,
|
||||
initialValue: settings.downloadPath,
|
||||
textStyle: settings.scaleFonts(defaultDropDownMenuItemTextStyle),
|
||||
description: AppLocalizations.of(context)!.fileSharingSettingsDownloadFolderDescription,
|
||||
tooltip: AppLocalizations.of(context)!.fileSharingSettingsDownloadFolderTooltip,
|
||||
onSave: (newVal) {
|
||||
settings.blodeuweddPath = newVal;
|
||||
settings.downloadPath = newVal;
|
||||
saveSettings(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
Visibility(
|
||||
visible: settings.experimentsEnabled,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.enableExperimentClickableLinks),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.experimentClickableLinksDescription),
|
||||
value: settings.isExperimentEnabled(
|
||||
ClickableLinksExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(
|
||||
ClickableLinksExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
ClickableLinksExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.link,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
)),
|
||||
Visibility(
|
||||
visible: settings.experimentsEnabled,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.enableExperimentQRCode),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.experimentQRCodeDescription),
|
||||
value: settings
|
||||
.isExperimentEnabled(QRCodeExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(QRCodeExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(QRCodeExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.qr_code,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
)),
|
||||
]))));
|
||||
});
|
||||
]),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.blodeuweddExperimentEnable),
|
||||
subtitle: Provider.of<FlwtchState>(context, listen: false).cwtch.IsBlodeuweddSupported()
|
||||
? Text(AppLocalizations.of(context)!.blodeuweddDescription)
|
||||
: Text(AppLocalizations.of(context)!.blodeuweddNotSupported),
|
||||
value: Provider.of<FlwtchState>(context, listen: false).cwtch.IsBlodeuweddSupported() && settings.isExperimentEnabled(BlodeuweddExperiment),
|
||||
onChanged: Provider.of<FlwtchState>(context, listen: false).cwtch.IsBlodeuweddSupported()
|
||||
? (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(BlodeuweddExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(BlodeuweddExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
}
|
||||
: null,
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
inactiveThumbColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.assistant, color: settings.current().mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: Provider.of<FlwtchState>(context, listen: false).cwtch.IsBlodeuweddSupported() && settings.isExperimentEnabled(BlodeuweddExperiment),
|
||||
child: CwtchFolderPicker(
|
||||
testKey: Key("DownloadFolderPicker"),
|
||||
label: AppLocalizations.of(context)!.settingDownloadFolder,
|
||||
initialValue: settings.blodeuweddPath,
|
||||
description: AppLocalizations.of(context)!.blodeuweddPath,
|
||||
tooltip: AppLocalizations.of(context)!.blodeuweddPath,
|
||||
onSave: (newVal) {
|
||||
settings.blodeuweddPath = newVal;
|
||||
saveSettings(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
Visibility(
|
||||
visible: settings.experimentsEnabled,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.enableExperimentClickableLinks),
|
||||
subtitle: Text(AppLocalizations.of(context)!.experimentClickableLinksDescription),
|
||||
value: settings.isExperimentEnabled(ClickableLinksExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(ClickableLinksExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(ClickableLinksExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.link, color: settings.current().mainTextColor),
|
||||
)),
|
||||
Visibility(
|
||||
visible: settings.experimentsEnabled,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.enableExperimentQRCode),
|
||||
subtitle: Text(AppLocalizations.of(context)!.experimentQRCodeDescription),
|
||||
value: settings.isExperimentEnabled(QRCodeExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(QRCodeExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(QRCodeExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.qr_code, color: settings.current().mainTextColor),
|
||||
)),
|
||||
]))));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -379,9 +245,7 @@ class _GlobalSettingsExperimentsViewState extends State<GlobalSettingsExperiment
|
|||
if (settings.downloadPath != "") {
|
||||
} else {
|
||||
// check if the default download path exists
|
||||
var path = Provider.of<FlwtchState>(context, listen: false)
|
||||
.cwtch
|
||||
.defaultDownloadPath();
|
||||
var path = Provider.of<FlwtchState>(context, listen: false).cwtch.defaultDownloadPath();
|
||||
if (path != null) {
|
||||
settings.downloadPath = path;
|
||||
} else {
|
||||
|
@ -393,11 +257,10 @@ class _GlobalSettingsExperimentsViewState extends State<GlobalSettingsExperiment
|
|||
return true;
|
||||
} else {
|
||||
final snackBar = SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.errorDownloadDirectoryDoesNotExist),
|
||||
content: Text(AppLocalizations.of(context)!.errorDownloadDirectoryDoesNotExist),
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ class GlobalSettingsView extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
||||
|
||||
ScrollController settingsListScrollController = ScrollController();
|
||||
|
||||
@override
|
||||
|
@ -35,7 +34,6 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
|
@ -46,23 +44,10 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
|||
bottom: TabBar(
|
||||
isScrollable: true,
|
||||
tabs: [
|
||||
Tab(
|
||||
key: Key("OpenSettingsAppearance"),
|
||||
icon: Icon(Icons.palette),
|
||||
text: AppLocalizations.of(context)!
|
||||
.settingsGroupAppearance),
|
||||
Tab(
|
||||
key: Key("OpenSettingsBehaviour"),
|
||||
icon: Icon(Icons.settings),
|
||||
text:
|
||||
AppLocalizations.of(context)!.settingGroupBehaviour),
|
||||
Tab(
|
||||
key: Key("OpenSettingsExperiments"),
|
||||
icon: Icon(CwtchIcons.enable_experiments),
|
||||
text: AppLocalizations.of(context)!
|
||||
.settingsGroupExperiments),
|
||||
Tab(icon: Icon(Icons.info), text: AppLocalizations.of(context)!
|
||||
.settingsGroupAbout),
|
||||
Tab(key: Key("OpenSettingsAppearance"), icon: Icon(Icons.palette), text: AppLocalizations.of(context)!.settingsGroupAppearance),
|
||||
Tab(key: Key("OpenSettingsBehaviour"), icon: Icon(Icons.settings), text: AppLocalizations.of(context)!.settingGroupBehaviour),
|
||||
Tab(key: Key("OpenSettingsExperiments"), icon: Icon(CwtchIcons.enable_experiments), text: AppLocalizations.of(context)!.settingsGroupExperiments),
|
||||
Tab(icon: Icon(Icons.info), text: AppLocalizations.of(context)!.settingsGroupAbout),
|
||||
],
|
||||
)),
|
||||
body: _buildSettingsList(),
|
||||
|
@ -71,15 +56,11 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
|||
|
||||
Widget _buildSettingsList() {
|
||||
return Consumer<Settings>(builder: (ccontext, settings, child) {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
return TabBarView(children: [
|
||||
GlobalSettingsAppearanceView(),
|
||||
|
||||
GlobalSettingsBehaviourView(),
|
||||
|
||||
GlobalSettingsExperimentsView(),
|
||||
|
||||
GlobalSettingsAboutView(),
|
||||
]);
|
||||
});
|
||||
|
@ -90,7 +71,5 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
|||
/// Send an UpdateGlobalSettings to the Event Bus
|
||||
saveSettings(context) {
|
||||
var settings = Provider.of<Settings>(context, listen: false);
|
||||
Provider.of<FlwtchState>(context, listen: false)
|
||||
.cwtch
|
||||
.UpdateSettings(jsonEncode(settings.asJson()));
|
||||
Provider.of<FlwtchState>(context, listen: false).cwtch.UpdateSettings(jsonEncode(settings.asJson()));
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'dart:io';
|
|||
import 'package:provider/provider.dart';
|
||||
import '../settings.dart';
|
||||
import 'buttontextfield.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'cwtchlabel.dart';
|
||||
|
||||
class CwtchFolderPicker extends StatefulWidget {
|
||||
|
@ -15,7 +16,9 @@ class CwtchFolderPicker extends StatefulWidget {
|
|||
final Function(String)? onSave;
|
||||
final Key? testKey;
|
||||
final TextStyle? textStyle;
|
||||
const CwtchFolderPicker({Key? key, this.testKey, this.textStyle, this.label = "", this.tooltip = "", this.initialValue = "", this.onSave, this.description = ""}) : super(key: key);
|
||||
final IconData icon;
|
||||
const CwtchFolderPicker({Key? key, this.testKey, this.textStyle, this.label = "", this.tooltip = "", this.initialValue = "", this.onSave, this.description = "", this.icon = Icons.file_download})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_CwtchFolderPickerState createState() => _CwtchFolderPickerState();
|
||||
|
@ -33,7 +36,7 @@ class _CwtchFolderPickerState extends State<CwtchFolderPicker> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: Icon(Icons.file_download, color: Provider.of<Settings>(context).theme.messageFromMeTextColor, size: 16),
|
||||
leading: Icon(widget.icon, color: Provider.of<Settings>(context).theme.messageFromMeTextColor),
|
||||
title: Text(widget.label),
|
||||
subtitle: Text(widget.description),
|
||||
trailing: Container(
|
||||
|
@ -53,7 +56,7 @@ class _CwtchFolderPickerState extends State<CwtchFolderPicker> {
|
|||
var selectedDirectory = await showSelectDirectoryPicker(context);
|
||||
if (selectedDirectory != null) {
|
||||
//File directory = File(selectedDirectory);
|
||||
selectedDirectory += "/";
|
||||
selectedDirectory += path.separator;
|
||||
ctrlrVal.text = selectedDirectory;
|
||||
if (widget.onSave != null) {
|
||||
widget.onSave!(selectedDirectory);
|
||||
|
|
Loading…
Reference in New Issue