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:
Paul 2023-01-30 06:25:55 +00:00 committed by GitHub
parent c6c066564d
commit 392b1bd01a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 184 additions and 52 deletions

View File

@ -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)

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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" = "跨站";

View File

@ -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)
}
}

View File

@ -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) {}
}

View File

@ -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

View File

@ -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?