Add the ability to set a custom font (#519)
* Add the ability to set a custom font * Small fixes * Indent * Add missing localization --------- Co-authored-by: Thomas Ricouard <ricouard77@gmail.com>
This commit is contained in:
parent
c6c066564d
commit
392b1bd01a
|
@ -5,11 +5,13 @@ import Status
|
|||
import SwiftUI
|
||||
|
||||
struct DisplaySettingsView: View {
|
||||
typealias FontState = Theme.FontState
|
||||
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var userPreferences: UserPreferences
|
||||
|
||||
@State private var isThemeSelectorPresented = false
|
||||
|
||||
@State private var isFontSelectorPresented = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
|
@ -33,6 +35,21 @@ struct DisplaySettingsView: View {
|
|||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
||||
Section("settings.display.section.display") {
|
||||
Picker("settings.display.font", selection: .init(get: {
|
||||
userPreferences.chosenFontData != nil ? FontState.custom : FontState.system
|
||||
}, set: { newValue in
|
||||
switch newValue {
|
||||
case .system:
|
||||
userPreferences.chosenFont = nil
|
||||
case .custom:
|
||||
isFontSelectorPresented = true
|
||||
}
|
||||
})) {
|
||||
ForEach(FontState.allCases, id: \.rawValue) { fontState in
|
||||
Text(fontState.title).tag(fontState)
|
||||
}
|
||||
}
|
||||
.navigationDestination(isPresented: $isFontSelectorPresented, destination: { FontPicker() })
|
||||
Picker("settings.display.avatar.position", selection: $theme.avatarPosition) {
|
||||
ForEach(Theme.AvatarPosition.allCases, id: \.rawValue) { position in
|
||||
Text(position.description).tag(position)
|
||||
|
|
|
@ -128,6 +128,9 @@
|
|||
"settings.push.duplicate.footer" = "Rebeu les notificacions duplicades? Proveu aquest botó màgic per a solucionar-ho";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Soluciona-ho";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
|
||||
// MARK: Tabs
|
||||
"tab.explore" = "Explora";
|
||||
|
|
|
@ -126,6 +126,9 @@
|
|||
"settings.push.duplicate.footer" = "Bekommst du doppelte Benachrichtigungen? Probier diesen magischen Knopf aus";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Beheben";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
|
||||
"enum.expand-media.show" = "Alle zeigen";
|
||||
"enum.expand-media.hide" = "Alle verstecken";
|
||||
|
|
|
@ -57,6 +57,9 @@
|
|||
"settings.app.icon.navigation-title" = "Icons";
|
||||
"settings.app.source" = "Source (GitHub link)";
|
||||
"settings.app.support" = "Support the app";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
"settings.display.avatar.position" = "Avatar position";
|
||||
"settings.display.avatar.shape" = "Avatar shape";
|
||||
"settings.display.navigation-title" = "Display Settings";
|
||||
|
|
|
@ -126,6 +126,9 @@
|
|||
"settings.push.duplicate.footer" = "¿Recibes notificaciones por duplicado? Usa este botón mágico para arreglarlo";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Arréglalo";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
|
||||
"enum.expand-media.show" = "Siempre";
|
||||
"enum.expand-media.hide" = "Nunca";
|
||||
|
|
|
@ -129,6 +129,9 @@
|
|||
"settings.push.duplicate.footer" = "Recevez-vous des notifications en double ? Essayez ce bouton magique pour résoudre le problème";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Résoudre";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
|
||||
// MARK: Tabs
|
||||
"tab.explore" = "Explorer";
|
||||
|
|
|
@ -128,6 +128,10 @@
|
|||
"settings.push.duplicate.title" = "Sistema le notifiche duplicate";
|
||||
"settings.push.duplicate.footer" = "Ricevi notifiche duplicate? Prova questo bottone magico per aggiustarle";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Aggiusta";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
"settings.other.autoplay-video" = "Auto Play dei video";
|
||||
|
||||
// MARK: Tabs
|
||||
|
|
|
@ -111,6 +111,10 @@
|
|||
"settings.push.duplicate.title" = "重複通知修正ツール";
|
||||
"settings.push.duplicate.footer" = "重複して通知を受け取っていませんか?それを修正するためにこの魔法のボタンを試してみて";
|
||||
"settings.push.duplicate.button.fix" = "🪄 修正する";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
"settings.other.autoplay-video" = "動画自動再生";
|
||||
|
||||
// MARK: Tabs
|
||||
|
|
|
@ -128,6 +128,10 @@
|
|||
"settings.push.duplicate.title" = "중복 알림 해결사";
|
||||
"settings.push.duplicate.footer" = "같은 알림이 여러 번 오나요? 여기 있는 버튼을 누르면 마법처럼 해결될 거에요.";
|
||||
"settings.push.duplicate.button.fix" = "🪄 고치기";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
"settings.other.autoplay-video" = "동영상 자동 재생";
|
||||
|
||||
// MARK: Tabs
|
||||
|
|
|
@ -129,6 +129,9 @@
|
|||
"settings.push.duplicate.footer" = "Får du dupliserte varsler? Prøv denne magiske knappen for å fikse det.";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Fiks det";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
|
||||
// MARK: Tabs
|
||||
"tab.explore" = "Utforsk";
|
||||
|
|
|
@ -126,6 +126,9 @@
|
|||
"settings.push.duplicate.footer" = "Ontvang je dubbele meldingen? Gebruik deze magische knop om dit probleem te verhelpen";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Los op";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
|
||||
// MARK: Tabs
|
||||
"tab.explore" = "Ontdekken";
|
||||
|
|
|
@ -129,6 +129,9 @@
|
|||
"settings.push.duplicate.footer" = "Otrzymujesz zduplikowane powiadomienia? Spróbuj tego magicznego przycisku, aby to naprawić";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Napraw to";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
|
||||
// MARK: Tabs
|
||||
"tab.explore" = "Odkrywaj";
|
||||
|
|
|
@ -129,6 +129,9 @@
|
|||
"settings.push.duplicate.footer" = "Recebendo notificações duplicadas? Tente este botão mágico para tentar corrigir";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Corrigir";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
|
||||
// MARK: Tabs
|
||||
"tab.explore" = "Explorar";
|
||||
|
|
|
@ -115,6 +115,9 @@
|
|||
"settings.push.duplicate.footer" = "Receiving duplicate notifications? Try this magic button in order to fix it";
|
||||
"settings.push.duplicate.button.fix" = "🪄 Fix it";
|
||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
|
||||
// MARK: Tabs
|
||||
"tab.explore" = "Keşfet";
|
||||
|
|
|
@ -129,6 +129,10 @@
|
|||
"settings.push.duplicate.button.fix" = "🪄 修复";
|
||||
"settings.other.autoplay-video" = "自动播放视频";
|
||||
|
||||
"settings.display.font" = "Timeline Font";
|
||||
"settings.display.font.system" = "System";
|
||||
"settings.display.font.custom" = "Custom";
|
||||
|
||||
// MARK: Tabs
|
||||
"tab.explore" = "探索";
|
||||
"tab.federated" = "跨站";
|
||||
|
|
|
@ -3,71 +3,70 @@ import SwiftUI
|
|||
|
||||
@MainActor
|
||||
public extension Font {
|
||||
static func userScaledFontSize(baseSize: CGFloat) -> CGFloat {
|
||||
UIFontMetrics.default.scaledValue(for: baseSize * UserPreferences.shared.fontSizeScale)
|
||||
// See https://gist.github.com/zacwest/916d31da5d03405809c4 for iOS values
|
||||
// Custom values for Mac
|
||||
private static let title = 28.0
|
||||
private static let headline = onMac ? 20.0 : 17.0
|
||||
private static let body = onMac ? 19.0 : 17.0
|
||||
private static let callout = onMac ? 17.0 : 16.0
|
||||
private static let subheadline = onMac ? 16.0 : 15.0
|
||||
private static let footnote = onMac ? 15.0 : 13.0
|
||||
private static let caption = onMac ? 14.0 : 12.0
|
||||
private static let onMac = ProcessInfo.processInfo.isiOSAppOnMac
|
||||
|
||||
private static func customFont(size: CGFloat, relativeTo textStyle: TextStyle) -> Font {
|
||||
if let chosenFont = UserPreferences.shared.chosenFont {
|
||||
return .custom(chosenFont.fontName, size: size, relativeTo: textStyle)
|
||||
}
|
||||
|
||||
return onMac ? .system(size: size) : .system(textStyle)
|
||||
}
|
||||
|
||||
|
||||
private static func customUIFont(size: CGFloat) -> UIFont {
|
||||
if let chosenFont = UserPreferences.shared.chosenFont {
|
||||
return chosenFont.withSize(size)
|
||||
}
|
||||
|
||||
return .systemFont(ofSize: size)
|
||||
}
|
||||
|
||||
private static func userScaledFontSize(baseSize: CGFloat) -> CGFloat {
|
||||
if onMac {
|
||||
return UIFontMetrics.default.scaledValue(for: baseSize * UserPreferences.shared.fontSizeScale)
|
||||
}
|
||||
|
||||
return baseSize
|
||||
}
|
||||
|
||||
static var scaledTitle: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 28))
|
||||
} else {
|
||||
return .title
|
||||
}
|
||||
customFont(size: userScaledFontSize(baseSize: title), relativeTo: .title)
|
||||
}
|
||||
|
||||
|
||||
static var scaledHeadline: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 20), weight: .semibold)
|
||||
} else {
|
||||
return .headline
|
||||
}
|
||||
customFont(size: userScaledFontSize(baseSize: headline), relativeTo: .headline).weight(.semibold)
|
||||
}
|
||||
|
||||
|
||||
static var scaledBody: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 19))
|
||||
} else {
|
||||
return .body
|
||||
}
|
||||
customFont(size: userScaledFontSize(baseSize: body), relativeTo: .body)
|
||||
}
|
||||
|
||||
|
||||
static var scaledBodyUIFont: UIFont {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return UIFont.systemFont(ofSize: userScaledFontSize(baseSize: 19))
|
||||
} else {
|
||||
return UIFont.systemFont(ofSize: 17)
|
||||
}
|
||||
customUIFont(size: userScaledFontSize(baseSize: body))
|
||||
}
|
||||
|
||||
|
||||
static var scaledCallout: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 17))
|
||||
} else {
|
||||
return .callout
|
||||
}
|
||||
customFont(size: userScaledFontSize(baseSize: callout), relativeTo: .callout)
|
||||
}
|
||||
|
||||
|
||||
static var scaledSubheadline: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 16))
|
||||
} else {
|
||||
return .subheadline
|
||||
}
|
||||
customFont(size: userScaledFontSize(baseSize: subheadline), relativeTo: .subheadline)
|
||||
}
|
||||
|
||||
|
||||
static var scaledFootnote: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 15))
|
||||
} else {
|
||||
return .footnote
|
||||
}
|
||||
customFont(size: userScaledFontSize(baseSize: footnote), relativeTo: .footnote)
|
||||
}
|
||||
|
||||
|
||||
static var scaledCaption: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 14))
|
||||
} else {
|
||||
return .caption
|
||||
}
|
||||
customFont(size: userScaledFontSize(baseSize: caption), relativeTo: .caption)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import Env
|
||||
import SwiftUI
|
||||
|
||||
public struct FontPicker: UIViewControllerRepresentable {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
public class Coordinator: NSObject, UIFontPickerViewControllerDelegate {
|
||||
private let dismiss: DismissAction
|
||||
|
||||
public init(dismiss: DismissAction) {
|
||||
self.dismiss = dismiss
|
||||
}
|
||||
|
||||
public func fontPickerViewControllerDidCancel(_ viewController: UIFontPickerViewController) {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
public func fontPickerViewControllerDidPickFont(_ viewController: UIFontPickerViewController) {
|
||||
UserPreferences.shared.chosenFont = UIFont(descriptor: viewController.selectedFontDescriptor!, size: 0)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
||||
public func makeCoordinator() -> Coordinator {
|
||||
Coordinator(dismiss: dismiss)
|
||||
}
|
||||
|
||||
public func makeUIViewController(context: Context) -> UIFontPickerViewController {
|
||||
let controller = UIFontPickerViewController()
|
||||
controller.delegate = context.coordinator
|
||||
return controller
|
||||
}
|
||||
|
||||
public func updateUIViewController(_ viewController: UIFontPickerViewController, context: Context) {}
|
||||
}
|
|
@ -8,6 +8,21 @@ public class Theme: ObservableObject {
|
|||
case selectedSet, selectedScheme
|
||||
case followSystemColorSchme
|
||||
}
|
||||
|
||||
public enum FontState: Int, CaseIterable {
|
||||
case system
|
||||
case custom
|
||||
|
||||
@MainActor
|
||||
public var title: LocalizedStringKey {
|
||||
switch self {
|
||||
case .system:
|
||||
return "settings.display.font.system"
|
||||
case .custom:
|
||||
return "settings.display.font.custom"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum AvatarPosition: String, CaseIterable {
|
||||
case leading, top
|
||||
|
|
|
@ -26,6 +26,7 @@ public class UserPreferences: ObservableObject {
|
|||
@AppStorage("app_default_post_visibility") public var appDefaultPostVisibility: Models.Visibility = .pub
|
||||
@AppStorage("app_default_posts_sensitive") public var appDefaultPostsSensitive = false
|
||||
@AppStorage("autoplay_video") public var autoPlayVideo = true
|
||||
@AppStorage("chosen_font") public private(set) var chosenFontData: Data?
|
||||
|
||||
public var postVisibility: Models.Visibility {
|
||||
if useInstanceContentSettings {
|
||||
|
@ -67,6 +68,23 @@ public class UserPreferences: ObservableObject {
|
|||
Self.sharedDefault?.set(newValue, forKey: "push_notifications_count")
|
||||
}
|
||||
}
|
||||
|
||||
public var chosenFont: UIFont? {
|
||||
get {
|
||||
guard let chosenFontData,
|
||||
let font = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIFont.self, from: chosenFontData) else { return nil }
|
||||
|
||||
return font
|
||||
}
|
||||
set {
|
||||
if let font = newValue,
|
||||
let data = try? NSKeyedArchiver.archivedData(withRootObject: font, requiringSecureCoding: false) {
|
||||
chosenFontData = data
|
||||
} else {
|
||||
chosenFontData = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published public var serverPreferences: ServerPreferences?
|
||||
|
||||
|
|
Loading…
Reference in New Issue