ios themes

This commit is contained in:
Evgeny @ SimpleX Chat
2026-04-20 23:11:15 +00:00
parent 4dcac59ff0
commit 037cf95e12
3 changed files with 111 additions and 73 deletions
+1 -1
View File
@@ -80,7 +80,7 @@ class ThemeManager {
if theme == nil && perUserTheme == nil && perChatTheme == nil && themeOverridesForType == nil {
return ActiveTheme(name: themeName, base: baseTheme.base, colors: baseTheme.colors, appColors: baseTheme.appColors, wallpaper: baseTheme.wallpaper)
}
let presetWallpaperTheme: ThemeColors? = if let themeOverridesForType, case let WallpaperType.preset(filename, _) = themeOverridesForType {
let presetWallpaperTheme: ResolvedColors? = if let themeOverridesForType, case let WallpaperType.preset(filename, _) = themeOverridesForType {
PresetWallpaper.from(filename)?.colors[baseTheme.base]
} else if let wallpaper = perChatTheme?.wallpaper {
if let preset = wallpaper.preset { PresetWallpaper.from(preset)?.colors[baseTheme.base] } else { nil }
@@ -101,28 +101,28 @@ public enum PresetWallpaper: CaseIterable {
}
}
public var colors: [DefaultTheme: ThemeColors] {
public var colors: [DefaultTheme: ResolvedColors] {
switch self {
case .cats: [
DefaultTheme.LIGHT: ThemeColors.from(
DefaultTheme.LIGHT: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#fffffaed",
sentQuote: "#fffaf0d6",
receivedMessage: "#ffF8F7F4",
receivedQuote: "#ffefede9"
),
DefaultTheme.DARK: ThemeColors.from(
)),
DefaultTheme.DARK: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff2f2919",
sentQuote: "#ff473a1d",
receivedMessage: "#ff272624",
receivedQuote: "#ff373633"
),
DefaultTheme.SIMPLEX: ThemeColors.from(
)),
DefaultTheme.SIMPLEX: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff41371b",
sentQuote: "#ff654f1c",
receivedMessage: "#ff272624",
receivedQuote: "#ff373633"
),
DefaultTheme.BLACK: ThemeColors.from(
)),
DefaultTheme.BLACK: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff41371b",
sentQuote: "#ff654f1c",
receivedMessage: "#ff1f1e1b",
@@ -130,25 +130,25 @@ public enum PresetWallpaper: CaseIterable {
)
]
case .flowers: [
DefaultTheme.LIGHT: ThemeColors.from(
DefaultTheme.LIGHT: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#fff1ffe5",
sentQuote: "#ffdcf9c4",
receivedMessage: "#ffF4F8F2",
receivedQuote: "#ffe7ece7"
),
DefaultTheme.DARK: ThemeColors.from(
)),
DefaultTheme.DARK: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff163521",
sentQuote: "#ff1B5330",
receivedMessage: "#ff242523",
receivedQuote: "#ff353733"
),
DefaultTheme.SIMPLEX: ThemeColors.from(
)),
DefaultTheme.SIMPLEX: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff184739",
sentQuote: "#ff1F6F4B",
receivedMessage: "#ff242523",
receivedQuote: "#ff353733"
),
DefaultTheme.BLACK: ThemeColors.from(
)),
DefaultTheme.BLACK: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff184739",
sentQuote: "#ff1F6F4B",
receivedMessage: "#ff1c1f1a",
@@ -156,25 +156,25 @@ public enum PresetWallpaper: CaseIterable {
)
]
case .hearts: [
DefaultTheme.LIGHT: ThemeColors.from(
DefaultTheme.LIGHT: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#fffff4f4",
sentQuote: "#ffffdfdf",
receivedMessage: "#fff8f6f6",
receivedQuote: "#ffefebeb"
),
DefaultTheme.DARK: ThemeColors.from(
)),
DefaultTheme.DARK: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff301515",
sentQuote: "#ff4C1818",
receivedMessage: "#ff242121",
receivedQuote: "#ff3b3535"
),
DefaultTheme.SIMPLEX: ThemeColors.from(
)),
DefaultTheme.SIMPLEX: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff491A28",
sentQuote: "#ff761F29",
receivedMessage: "#ff242121",
receivedQuote: "#ff3b3535"
),
DefaultTheme.BLACK: ThemeColors.from(
)),
DefaultTheme.BLACK: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff491A28",
sentQuote: "#ff761F29",
receivedMessage: "#ff1f1b1b",
@@ -182,25 +182,25 @@ public enum PresetWallpaper: CaseIterable {
)
]
case .kids: [
DefaultTheme.LIGHT: ThemeColors.from(
DefaultTheme.LIGHT: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ffeafeff",
sentQuote: "#ffcbf4f7",
receivedMessage: "#fff3fafa",
receivedQuote: "#ffe4efef"
),
DefaultTheme.DARK: ThemeColors.from(
)),
DefaultTheme.DARK: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff16302F",
sentQuote: "#ff1a4a49",
receivedMessage: "#ff252626",
receivedQuote: "#ff373A39"
),
DefaultTheme.SIMPLEX: ThemeColors.from(
)),
DefaultTheme.SIMPLEX: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff1a4745",
sentQuote: "#ff1d6b69",
receivedMessage: "#ff252626",
receivedQuote: "#ff373a39"
),
DefaultTheme.BLACK: ThemeColors.from(
)),
DefaultTheme.BLACK: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff1a4745",
sentQuote: "#ff1d6b69",
receivedMessage: "#ff1e1f1f",
@@ -208,51 +208,51 @@ public enum PresetWallpaper: CaseIterable {
)
]
case .school: [
DefaultTheme.LIGHT: ThemeColors.from(
sentMessage: "oklch(0.9756479, 0.01416295, 231.2013)",
sentQuote: "oklch(0.9331527, 0.03006113, 232.4212)",
receivedMessage: "oklch(0.9697657, 0.005748723, 264.5325)",
receivedQuote: "oklch(0.9296755, 0.00918803, 258.3366)"
DefaultTheme.LIGHT: ResolvedColors(
sentMessage: oklch(0.9756479, 0.01416295, 231.2013),
sentQuote: oklch(0.9331527, 0.03006113, 232.4212),
receivedMessage: oklch(0.9697657, 0.005748723, 264.5325),
receivedQuote: oklch(0.9296755, 0.00918803, 258.3366)
),
DefaultTheme.DARK: ThemeColors.from(
sentMessage: "oklch(0.267226, 0.03061943, 237.8609)",
sentQuote: "oklch(0.3464064, 0.04943852, 232.4005)",
receivedMessage: "oklch(0.2764251, 0.007910622, 264.4375)",
receivedQuote: "oklch(0.3548081, 0.008034593, 255.5451)"
DefaultTheme.DARK: ResolvedColors(
sentMessage: oklch(0.267226, 0.03061943, 237.8609),
sentQuote: oklch(0.3464064, 0.04943852, 232.4005),
receivedMessage: oklch(0.2764251, 0.007910622, 264.4375),
receivedQuote: oklch(0.3548081, 0.008034593, 255.5451)
),
DefaultTheme.SIMPLEX: ThemeColors.from(
sentMessage: "oklch(0.3481476, 0.07023845, 249.9259)",
sentQuote: "oklch(0.4520089, 0.08394516, 241.1934)",
receivedMessage: "oklch(0.2764251, 0.007910622, 264.4375)",
receivedQuote: "oklch(0.3548081, 0.008034593, 255.5451)"
DefaultTheme.SIMPLEX: ResolvedColors(
sentMessage: oklch(0.3481476, 0.07023845, 249.9259),
sentQuote: oklch(0.4520089, 0.08394516, 241.1934),
receivedMessage: oklch(0.2764251, 0.007910622, 264.4375),
receivedQuote: oklch(0.3548081, 0.008034593, 255.5451)
),
DefaultTheme.BLACK: ThemeColors.from(
sentMessage: "oklch(0.3481476, 0.07023845, 249.9259)",
sentQuote: "oklch(0.4520089, 0.08394516, 241.1934)",
receivedMessage: "oklch(0.2356588, 0.007789041, 274.6063)",
receivedQuote: "oklch(0.2886546, 0.007823012, 264.445)"
DefaultTheme.BLACK: ResolvedColors(
sentMessage: oklch(0.3481476, 0.07023845, 249.9259),
sentQuote: oklch(0.4520089, 0.08394516, 241.1934),
receivedMessage: oklch(0.2356588, 0.007789041, 274.6063),
receivedQuote: oklch(0.2886546, 0.007823012, 264.445)
)
]
case .travel: [
DefaultTheme.LIGHT: ThemeColors.from(
DefaultTheme.LIGHT: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#fffcf6ff",
sentQuote: "#fff2e0fc",
receivedMessage: "#ffF6F4F7",
receivedQuote: "#ffede9ee"
),
DefaultTheme.DARK: ThemeColors.from(
)),
DefaultTheme.DARK: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff33263B",
sentQuote: "#ff53385E",
receivedMessage: "#ff272528",
receivedQuote: "#ff3B373E"
),
DefaultTheme.SIMPLEX: ThemeColors.from(
)),
DefaultTheme.SIMPLEX: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff3C255D",
sentQuote: "#ff623485",
receivedMessage: "#ff26273B",
receivedQuote: "#ff3A394F"
),
DefaultTheme.BLACK: ThemeColors.from(
)),
DefaultTheme.BLACK: ResolvedColors.fromThemeColors(ThemeColors.from(
sentMessage: "#ff3C255D",
sentQuote: "#ff623485",
receivedMessage: "#ff231f23",
+54 -16
View File
@@ -226,6 +226,44 @@ public enum ThemeColor {
}
}
/// Resolved preset colors Color objects, not strings. Used for presets defined in code.
public struct ResolvedColors {
public var primary: Color? = nil
public var primaryVariant: Color? = nil
public var secondary: Color? = nil
public var secondaryVariant: Color? = nil
public var background: Color? = nil
public var surface: Color? = nil
public var title: Color? = nil
public var primaryVariant2: Color? = nil
public var sentMessage: Color? = nil
public var sentQuote: Color? = nil
public var receivedMessage: Color? = nil
public var receivedQuote: Color? = nil
public init(primary: Color? = nil, primaryVariant: Color? = nil, secondary: Color? = nil, secondaryVariant: Color? = nil, background: Color? = nil, surface: Color? = nil, title: Color? = nil, primaryVariant2: Color? = nil, sentMessage: Color? = nil, sentQuote: Color? = nil, receivedMessage: Color? = nil, receivedQuote: Color? = nil) {
self.primary = primary; self.primaryVariant = primaryVariant; self.secondary = secondary; self.secondaryVariant = secondaryVariant; self.background = background; self.surface = surface; self.title = title; self.primaryVariant2 = primaryVariant2; self.sentMessage = sentMessage; self.sentQuote = sentQuote; self.receivedMessage = receivedMessage; self.receivedQuote = receivedQuote
}
/// Temporary: convert hex ThemeColors to ResolvedColors.
public static func fromThemeColors(_ tc: ThemeColors) -> ResolvedColors {
ResolvedColors(
primary: tc.primary?.colorFromReadableHex(),
primaryVariant: tc.primaryVariant?.colorFromReadableHex(),
secondary: tc.secondary?.colorFromReadableHex(),
secondaryVariant: tc.secondaryVariant?.colorFromReadableHex(),
background: tc.background?.colorFromReadableHex(),
surface: tc.surface?.colorFromReadableHex(),
title: tc.title?.colorFromReadableHex(),
primaryVariant2: tc.primaryVariant2?.colorFromReadableHex(),
sentMessage: tc.sentMessage?.colorFromReadableHex(),
sentQuote: tc.sentQuote?.colorFromReadableHex(),
receivedMessage: tc.receivedMessage?.colorFromReadableHex(),
receivedQuote: tc.receivedQuote?.colorFromReadableHex()
)
}
}
// Spec: spec/services/theme.md#ThemeColors
public struct ThemeColors: Codable, Equatable, Hashable {
public var primary: String? = nil
@@ -434,7 +472,7 @@ public struct ThemeOverrides: Codable, Equatable, Hashable {
return ThemeOverrides(themeId: themeId, base: base, colors: c, wallpaper: w)
}
public func toColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perUserTheme: ThemeColors?, _ presetWallpaperTheme: ThemeColors?) -> Colors {
public func toColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perUserTheme: ThemeColors?, _ presetWallpaperTheme: ResolvedColors?) -> Colors {
let baseColors = switch base {
case DefaultTheme.LIGHT: LightColorPalette
case DefaultTheme.DARK: DarkColorPalette
@@ -442,16 +480,16 @@ public struct ThemeOverrides: Codable, Equatable, Hashable {
case DefaultTheme.BLACK: BlackColorPalette
}
let c = baseColors.clone()
c.primary = perChatTheme?.primary?.colorFromReadableHex() ?? perUserTheme?.primary?.colorFromReadableHex() ?? colors.primary?.colorFromReadableHex() ?? presetWallpaperTheme?.primary?.colorFromReadableHex() ?? baseColors.primary
c.primaryVariant = perChatTheme?.primaryVariant?.colorFromReadableHex() ?? perUserTheme?.primaryVariant?.colorFromReadableHex() ?? colors.primaryVariant?.colorFromReadableHex() ?? presetWallpaperTheme?.primaryVariant?.colorFromReadableHex() ?? baseColors.primaryVariant
c.secondary = perChatTheme?.secondary?.colorFromReadableHex() ?? perUserTheme?.secondary?.colorFromReadableHex() ?? colors.secondary?.colorFromReadableHex() ?? presetWallpaperTheme?.secondary?.colorFromReadableHex() ?? baseColors.secondary
c.secondaryVariant = perChatTheme?.secondaryVariant?.colorFromReadableHex() ?? perUserTheme?.secondaryVariant?.colorFromReadableHex() ?? colors.secondaryVariant?.colorFromReadableHex() ?? presetWallpaperTheme?.secondaryVariant?.colorFromReadableHex() ?? baseColors.secondaryVariant
c.background = perChatTheme?.background?.colorFromReadableHex() ?? perUserTheme?.background?.colorFromReadableHex() ?? colors.background?.colorFromReadableHex() ?? presetWallpaperTheme?.background?.colorFromReadableHex() ?? baseColors.background
c.surface = perChatTheme?.surface?.colorFromReadableHex() ?? perUserTheme?.surface?.colorFromReadableHex() ?? colors.surface?.colorFromReadableHex() ?? presetWallpaperTheme?.surface?.colorFromReadableHex() ?? baseColors.surface
c.primary = perChatTheme?.primary?.colorFromReadableHex() ?? perUserTheme?.primary?.colorFromReadableHex() ?? colors.primary?.colorFromReadableHex() ?? presetWallpaperTheme?.primary ?? baseColors.primary
c.primaryVariant = perChatTheme?.primaryVariant?.colorFromReadableHex() ?? perUserTheme?.primaryVariant?.colorFromReadableHex() ?? colors.primaryVariant?.colorFromReadableHex() ?? presetWallpaperTheme?.primaryVariant ?? baseColors.primaryVariant
c.secondary = perChatTheme?.secondary?.colorFromReadableHex() ?? perUserTheme?.secondary?.colorFromReadableHex() ?? colors.secondary?.colorFromReadableHex() ?? presetWallpaperTheme?.secondary ?? baseColors.secondary
c.secondaryVariant = perChatTheme?.secondaryVariant?.colorFromReadableHex() ?? perUserTheme?.secondaryVariant?.colorFromReadableHex() ?? colors.secondaryVariant?.colorFromReadableHex() ?? presetWallpaperTheme?.secondaryVariant ?? baseColors.secondaryVariant
c.background = perChatTheme?.background?.colorFromReadableHex() ?? perUserTheme?.background?.colorFromReadableHex() ?? colors.background?.colorFromReadableHex() ?? presetWallpaperTheme?.background ?? baseColors.background
c.surface = perChatTheme?.surface?.colorFromReadableHex() ?? perUserTheme?.surface?.colorFromReadableHex() ?? colors.surface?.colorFromReadableHex() ?? presetWallpaperTheme?.surface ?? baseColors.surface
return c
}
public func toAppColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perChatWallpaperType: WallpaperType?, _ perUserTheme: ThemeColors?, _ perUserWallpaperType: WallpaperType?, _ presetWallpaperTheme: ThemeColors?) -> AppColors {
public func toAppColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perChatWallpaperType: WallpaperType?, _ perUserTheme: ThemeColors?, _ perUserWallpaperType: WallpaperType?, _ presetWallpaperTheme: ResolvedColors?) -> AppColors {
let baseColors = switch base {
case DefaultTheme.LIGHT: LightColorPaletteApp
case DefaultTheme.DARK: DarkColorPaletteApp
@@ -459,14 +497,14 @@ public struct ThemeOverrides: Codable, Equatable, Hashable {
case DefaultTheme.BLACK: BlackColorPaletteApp
}
let sentMessageFallback = colors.sentMessage?.colorFromReadableHex() ?? presetWallpaperTheme?.sentMessage?.colorFromReadableHex() ?? baseColors.sentMessage
let sentQuoteFallback = colors.sentQuote?.colorFromReadableHex() ?? presetWallpaperTheme?.sentQuote?.colorFromReadableHex() ?? baseColors.sentQuote
let receivedMessageFallback = colors.receivedMessage?.colorFromReadableHex() ?? presetWallpaperTheme?.receivedMessage?.colorFromReadableHex() ?? baseColors.receivedMessage
let receivedQuoteFallback = colors.receivedQuote?.colorFromReadableHex() ?? presetWallpaperTheme?.receivedQuote?.colorFromReadableHex() ?? baseColors.receivedQuote
let sentMessageFallback = colors.sentMessage?.colorFromReadableHex() ?? presetWallpaperTheme?.sentMessage ?? baseColors.sentMessage
let sentQuoteFallback = colors.sentQuote?.colorFromReadableHex() ?? presetWallpaperTheme?.sentQuote ?? baseColors.sentQuote
let receivedMessageFallback = colors.receivedMessage?.colorFromReadableHex() ?? presetWallpaperTheme?.receivedMessage ?? baseColors.receivedMessage
let receivedQuoteFallback = colors.receivedQuote?.colorFromReadableHex() ?? presetWallpaperTheme?.receivedQuote ?? baseColors.receivedQuote
let c = baseColors.clone()
c.title = perChatTheme?.title?.colorFromReadableHex() ?? perUserTheme?.title?.colorFromReadableHex() ?? colors.title?.colorFromReadableHex() ?? presetWallpaperTheme?.title?.colorFromReadableHex() ?? baseColors.title
c.primaryVariant2 = perChatTheme?.primaryVariant2?.colorFromReadableHex() ?? perUserTheme?.primaryVariant2?.colorFromReadableHex() ?? colors.primaryVariant2?.colorFromReadableHex() ?? presetWallpaperTheme?.primaryVariant2?.colorFromReadableHex() ?? baseColors.primaryVariant2
c.title = perChatTheme?.title?.colorFromReadableHex() ?? perUserTheme?.title?.colorFromReadableHex() ?? colors.title?.colorFromReadableHex() ?? presetWallpaperTheme?.title ?? baseColors.title
c.primaryVariant2 = perChatTheme?.primaryVariant2?.colorFromReadableHex() ?? perUserTheme?.primaryVariant2?.colorFromReadableHex() ?? colors.primaryVariant2?.colorFromReadableHex() ?? presetWallpaperTheme?.primaryVariant2 ?? baseColors.primaryVariant2
c.sentMessage = if let c = perChatTheme?.sentMessage { c.colorFromReadableHex() } else if let perUserTheme, (perChatWallpaperType == nil || perUserWallpaperType == nil || perChatWallpaperType!.sameType(perUserWallpaperType)) { perUserTheme.sentMessage?.colorFromReadableHex() ?? sentMessageFallback } else { sentMessageFallback }
c.sentQuote = if let c = perChatTheme?.sentQuote { c.colorFromReadableHex() } else if let perUserTheme, (perChatWallpaperType == nil || perUserWallpaperType == nil || perChatWallpaperType!.sameType(perUserWallpaperType)) { perUserTheme.sentQuote?.colorFromReadableHex() ?? sentQuoteFallback } else { sentQuoteFallback }
c.receivedMessage = if let c = perChatTheme?.receivedMessage { c.colorFromReadableHex() } else if let perUserTheme, (perChatWallpaperType == nil || perUserWallpaperType == nil || perChatWallpaperType!.sameType(perUserWallpaperType)) { perUserTheme.receivedMessage?.colorFromReadableHex() ?? receivedMessageFallback }
@@ -508,7 +546,7 @@ public struct ThemeOverrides: Codable, Equatable, Hashable {
return AppWallpaper(background: background, tint: tint, type: wallpaper)
}
public func withFilledColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perChatWallpaperType: WallpaperType?, _ perUserTheme: ThemeColors?, _ perUserWallpaperType: WallpaperType?, _ presetWallpaperTheme: ThemeColors?) -> ThemeColors {
public func withFilledColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perChatWallpaperType: WallpaperType?, _ perUserTheme: ThemeColors?, _ perUserWallpaperType: WallpaperType?, _ presetWallpaperTheme: ResolvedColors?) -> ThemeColors {
let c = toColors(base, perChatTheme, perUserTheme, presetWallpaperTheme)
let ac = toAppColors(base, perChatTheme, perChatWallpaperType, perUserTheme, perUserWallpaperType, presetWallpaperTheme)
return ThemeColors(