Add a follow system colors option close #128

This commit is contained in:
Thomas Ricouard 2023-01-20 21:58:57 +01:00
parent cd05a75ab9
commit 5321b2b8d8
12 changed files with 82 additions and 47 deletions

View file

@ -622,7 +622,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.3; MARKETING_VERSION = 1.0.4;
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp.IceCubesNotifications; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp.IceCubesNotifications;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -652,7 +652,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.3; MARKETING_VERSION = 1.0.4;
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp.IceCubesNotifications; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp.IceCubesNotifications;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -683,7 +683,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.3; MARKETING_VERSION = 1.0.4;
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp.IceCubesShareExtension; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp.IceCubesShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -713,7 +713,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.3; MARKETING_VERSION = 1.0.4;
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp.IceCubesShareExtension; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp.IceCubesShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -877,7 +877,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0.3; MARKETING_VERSION = 1.0.4;
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto; SDKROOT = auto;
@ -929,7 +929,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0.3; MARKETING_VERSION = 1.0.4;
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto; SDKROOT = auto;

View file

@ -14,6 +14,7 @@ struct IceCubesApp: App {
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
@Environment(\.scenePhase) private var scenePhase @Environment(\.scenePhase) private var scenePhase
@StateObject private var appAccountsManager = AppAccountsManager.shared @StateObject private var appAccountsManager = AppAccountsManager.shared
@StateObject private var currentInstance = CurrentInstance.shared @StateObject private var currentInstance = CurrentInstance.shared
@StateObject private var currentAccount = CurrentAccount.shared @StateObject private var currentAccount = CurrentAccount.shared
@ -197,6 +198,8 @@ struct IceCubesApp: App {
} }
class AppDelegate: NSObject, UIApplicationDelegate { class AppDelegate: NSObject, UIApplicationDelegate {
let themeObserver = ThemeObserverViewController(nibName: nil, bundle: nil)
func application(_: UIApplication, func application(_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool
{ {
@ -219,3 +222,11 @@ class AppDelegate: NSObject, UIApplicationDelegate {
func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError _: Error) {} func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError _: Error) {}
} }
class ThemeObserverViewController: UIViewController {
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
print(traitCollection.userInterfaceStyle.rawValue)
}
}

View file

@ -13,10 +13,20 @@ struct DisplaySettingsView: View {
var body: some View { var body: some View {
Form { Form {
Section("settings.display.section.theme") { Section("settings.display.section.theme") {
Toggle("settings.display.theme.systemColor", isOn: $theme.followSystemColorScheme)
themeSelectorButton themeSelectorButton
ColorPicker("settings.display.theme.tint", selection: $theme.tintColor) ColorPicker("settings.display.theme.tint", selection: $theme.tintColor)
.onChange(of: theme.tintColor) { newValue in
theme.followSystemColorScheme = false
}
ColorPicker("settings.display.theme.background", selection: $theme.primaryBackgroundColor) ColorPicker("settings.display.theme.background", selection: $theme.primaryBackgroundColor)
.onChange(of: theme.primaryBackgroundColor) { newValue in
theme.followSystemColorScheme = false
}
ColorPicker("settings.display.theme.secondary-background", selection: $theme.secondaryBackgroundColor) ColorPicker("settings.display.theme.secondary-background", selection: $theme.secondaryBackgroundColor)
.onChange(of: theme.primaryBackgroundColor) { newValue in
theme.followSystemColorScheme = false
}
} }
.listRowBackground(theme.primaryBackgroundColor) .listRowBackground(theme.primaryBackgroundColor)

View file

@ -7,7 +7,7 @@ import Network
import SwiftUI import SwiftUI
import Timeline import Timeline
struct TimelineTab: View { struct TimelineTab: View {
@EnvironmentObject private var theme: Theme @EnvironmentObject private var theme: Theme
@EnvironmentObject private var currentAccount: CurrentAccount @EnvironmentObject private var currentAccount: CurrentAccount
@EnvironmentObject private var preferences: UserPreferences @EnvironmentObject private var preferences: UserPreferences

View file

@ -61,6 +61,7 @@
"settings.display.theme.background" = "Hintergrundfarbe"; "settings.display.theme.background" = "Hintergrundfarbe";
"settings.display.theme.secondary-background" = "Sekundäre Hintergrundfarbe"; "settings.display.theme.secondary-background" = "Sekundäre Hintergrundfarbe";
"settings.display.theme.tint" = "Akzentfarbe"; "settings.display.theme.tint" = "Akzentfarbe";
"settings.display.theme.systemColor" = "Match-System";
"settings.general.browser" = "Browser"; "settings.general.browser" = "Browser";
"settings.general.browser.in-app" = "In-App Browser"; "settings.general.browser.in-app" = "In-App Browser";
"settings.general.browser.system" = "System Browser"; "settings.general.browser.system" = "System Browser";

View file

@ -61,6 +61,7 @@
"settings.display.theme.background" = "Background color"; "settings.display.theme.background" = "Background color";
"settings.display.theme.secondary-background" = "Secondary Background color"; "settings.display.theme.secondary-background" = "Secondary Background color";
"settings.display.theme.tint" = "Tint color"; "settings.display.theme.tint" = "Tint color";
"settings.display.theme.systemColor" = "Match System";
"settings.general.browser" = "Browser"; "settings.general.browser" = "Browser";
"settings.general.browser.in-app" = "In-App Browser"; "settings.general.browser.in-app" = "In-App Browser";
"settings.general.browser.system" = "System Browser"; "settings.general.browser.system" = "System Browser";

View file

@ -61,6 +61,7 @@
"settings.display.theme.background" = "Color de fondo"; "settings.display.theme.background" = "Color de fondo";
"settings.display.theme.secondary-background" = "Color de fondo secundario"; "settings.display.theme.secondary-background" = "Color de fondo secundario";
"settings.display.theme.tint" = "Color"; "settings.display.theme.tint" = "Color";
"settings.display.theme.systemColor" = "Sistema de coincidencia";
"settings.general.browser" = "Navegador"; "settings.general.browser" = "Navegador";
"settings.general.browser.in-app" = "Interno"; "settings.general.browser.in-app" = "Interno";
"settings.general.browser.system" = "Sistema"; "settings.general.browser.system" = "Sistema";

View file

@ -61,6 +61,7 @@
"settings.display.theme.background" = "Achtergrondkleur"; "settings.display.theme.background" = "Achtergrondkleur";
"settings.display.theme.secondary-background" = "Secundaire Achtergrondkleur"; "settings.display.theme.secondary-background" = "Secundaire Achtergrondkleur";
"settings.display.theme.tint" = "Kleurtint"; "settings.display.theme.tint" = "Kleurtint";
"settings.display.theme.systemColor" = "Match systeem";
"settings.general.browser" = "Browser"; "settings.general.browser" = "Browser";
"settings.general.browser.in-app" = "In-App Browser"; "settings.general.browser.in-app" = "In-App Browser";
"settings.general.browser.system" = "Systeem Browser"; "settings.general.browser.system" = "Systeem Browser";

View file

@ -1,4 +1,11 @@
import SwiftUI import SwiftUI
public let availableColorsSets: [ColorSetCouple] =
[.init(light: IceCubeLight(), dark: IceCubeDark()),
.init(light: DesertLight(), dark: DesertDark()),
.init(light: NemesisLight(), dark: NemesisDark()),
.init(light: MediumLight(), dark: MediumDark())]
public protocol ColorSet { public protocol ColorSet {
var name: ColorSetName { get } var name: ColorSetName { get }
var scheme: ColorScheme { get } var scheme: ColorScheme { get }
@ -23,6 +30,15 @@ public enum ColorSetName: String {
case mediumDark = "Medium - Dark" case mediumDark = "Medium - Dark"
} }
public struct ColorSetCouple: Identifiable {
public var id: String {
dark.name.rawValue + light.name.rawValue
}
public let light: ColorSet
public let dark: ColorSet
}
public struct IceCubeDark: ColorSet { public struct IceCubeDark: ColorSet {
public var name: ColorSetName = .iceCubeDark public var name: ColorSetName = .iceCubeDark
public var scheme: ColorScheme = .dark public var scheme: ColorScheme = .dark

View file

@ -6,6 +6,7 @@ public class Theme: ObservableObject {
case colorScheme, tint, label, primaryBackground, secondaryBackground case colorScheme, tint, label, primaryBackground, secondaryBackground
case avatarPosition, avatarShape, statusActionsDisplay, statusDisplayStyle case avatarPosition, avatarShape, statusActionsDisplay, statusDisplayStyle
case selectedSet, selectedScheme case selectedSet, selectedScheme
case followSystemColorSchme
} }
public enum AvatarPosition: String, CaseIterable { public enum AvatarPosition: String, CaseIterable {
@ -62,7 +63,7 @@ public class Theme: ObservableObject {
} }
} }
@AppStorage("is_previously_set") private var isSet: Bool = false @AppStorage("is_previously_set") public var isThemePreviouslySet: Bool = false
@AppStorage(ThemeKey.selectedScheme.rawValue) public var selectedScheme: ColorScheme = .dark @AppStorage(ThemeKey.selectedScheme.rawValue) public var selectedScheme: ColorScheme = .dark
@AppStorage(ThemeKey.tint.rawValue) public var tintColor: Color = .black @AppStorage(ThemeKey.tint.rawValue) public var tintColor: Color = .black
@AppStorage(ThemeKey.primaryBackground.rawValue) public var primaryBackgroundColor: Color = .white @AppStorage(ThemeKey.primaryBackground.rawValue) public var primaryBackgroundColor: Color = .white
@ -73,6 +74,7 @@ public class Theme: ObservableObject {
@AppStorage(ThemeKey.selectedSet.rawValue) var storedSet: ColorSetName = .iceCubeDark @AppStorage(ThemeKey.selectedSet.rawValue) var storedSet: ColorSetName = .iceCubeDark
@AppStorage(ThemeKey.statusActionsDisplay.rawValue) public var statusActionsDisplay: StatusActionsDisplay = .full @AppStorage(ThemeKey.statusActionsDisplay.rawValue) public var statusActionsDisplay: StatusActionsDisplay = .full
@AppStorage(ThemeKey.statusDisplayStyle.rawValue) public var statusDisplayStyle: StatusDisplayStyle = .large @AppStorage(ThemeKey.statusDisplayStyle.rawValue) public var statusDisplayStyle: StatusDisplayStyle = .large
@AppStorage(ThemeKey.followSystemColorSchme.rawValue) public var followSystemColorScheme: Bool = true
@Published public var avatarPosition: AvatarPosition = .top @Published public var avatarPosition: AvatarPosition = .top
@Published public var avatarShape: AvatarShape = .rounded @Published public var avatarShape: AvatarShape = .rounded
@ -85,13 +87,6 @@ public class Theme: ObservableObject {
private init() { private init() {
selectedSet = storedSet selectedSet = storedSet
// If theme is never set before set the default store. This should only execute once after install.
if !isSet {
setColor(withName: .iceCubeDark)
isSet = true
}
avatarPosition = AvatarPosition(rawValue: rawAvatarPosition) ?? .top avatarPosition = AvatarPosition(rawValue: rawAvatarPosition) ?? .top
avatarShape = AvatarShape(rawValue: rawAvatarShape) ?? .rounded avatarShape = AvatarShape(rawValue: rawAvatarShape) ?? .rounded

View file

@ -10,43 +10,48 @@ public extension View {
} }
struct ThemeApplier: ViewModifier { struct ThemeApplier: ViewModifier {
@Environment(\EnvironmentValues.colorScheme) var colorScheme
@ObservedObject var theme: Theme @ObservedObject var theme: Theme
var actualColorScheme: SwiftUI.ColorScheme? {
if theme.followSystemColorScheme {
return nil
}
return theme.selectedScheme == ColorScheme.dark ? .dark : .light
}
func body(content: Content) -> some View { func body(content: Content) -> some View {
content content
.tint(theme.tintColor) .tint(theme.tintColor)
.preferredColorScheme(theme.selectedScheme == ColorScheme.dark ? .dark : .light) .preferredColorScheme(actualColorScheme)
#if canImport(UIKit) #if canImport(UIKit)
.onAppear { .onAppear {
// If theme is never set before set the default store. This should only execute once after install.
if !theme.isThemePreviouslySet {
theme.selectedSet = colorScheme == .dark ? .iceCubeDark : .iceCubeLight
theme.isThemePreviouslySet = true
}
setWindowTint(theme.tintColor) setWindowTint(theme.tintColor)
setWindowUserInterfaceStyle(theme.selectedScheme)
setBarsColor(theme.primaryBackgroundColor) setBarsColor(theme.primaryBackgroundColor)
} }
.onChange(of: theme.tintColor) { newValue in .onChange(of: theme.tintColor) { newValue in
setWindowTint(newValue) setWindowTint(newValue)
} }
.onChange(of: theme.selectedScheme) { newValue in
setWindowUserInterfaceStyle(newValue)
}
.onChange(of: theme.primaryBackgroundColor) { newValue in .onChange(of: theme.primaryBackgroundColor) { newValue in
setBarsColor(newValue) setBarsColor(newValue)
} }
.onChange(of: colorScheme) { newColorScheme in
if theme.followSystemColorScheme,
let sets = availableColorsSets
.first(where: { $0.light.name == theme.selectedSet || $0.dark.name == theme.selectedSet }) {
theme.selectedSet = newColorScheme == .dark ? sets.dark.name : sets.light.name
}
}
#endif #endif
} }
#if canImport(UIKit) #if canImport(UIKit)
private func setWindowUserInterfaceStyle(_ colorScheme: ColorScheme) {
allWindows()
.forEach {
switch colorScheme {
case .dark:
$0.overrideUserInterfaceStyle = .dark
case .light:
$0.overrideUserInterfaceStyle = .light
}
}
}
private func setWindowTint(_ color: Color) { private func setWindowTint(_ color: Color) {
allWindows() allWindows()
.forEach { .forEach {

View file

@ -10,21 +10,11 @@ public struct ThemePreviewView: View {
public var body: some View { public var body: some View {
ScrollView { ScrollView {
HStack(spacing: gutterSpace) { ForEach(availableColorsSets) { couple in
ThemeBoxView(color: IceCubeDark()) HStack(spacing: gutterSpace) {
ThemeBoxView(color: IceCubeLight()) ThemeBoxView(color: couple.light)
} ThemeBoxView(color: couple.dark)
HStack(spacing: gutterSpace) { }
ThemeBoxView(color: DesertDark())
ThemeBoxView(color: DesertLight())
}
HStack(spacing: gutterSpace) {
ThemeBoxView(color: NemesisDark())
ThemeBoxView(color: NemesisLight())
}
HStack(spacing: gutterSpace) {
ThemeBoxView(color: MediumDark())
ThemeBoxView(color: MediumLight())
} }
} }
.padding(4) .padding(4)
@ -94,6 +84,10 @@ struct ThemeBoxView: View {
isSelected = newValue.rawValue == color.name.rawValue isSelected = newValue.rawValue == color.name.rawValue
} }
.onTapGesture { .onTapGesture {
let currentScheme = theme.selectedScheme
if color.scheme != currentScheme {
theme.followSystemColorScheme = false
}
theme.selectedSet = color.name theme.selectedSet = color.name
} }
} }