Refine Font size UI
This commit is contained in:
1 parent
37c0d6bdb7
commit
799128b59b
5 files changed
+173
-191
No files matched your search
@@ -1,70 +1,6 @@
|
||||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"Invalid authentication challenge." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "认证质询无效。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "驗證詢問無效。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Invalid authentication response." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "认证响应无效。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "驗證回應無效。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This server requires interactive authentication." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此服务器需要交互式认证。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此伺服器需要互動式驗證。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Interactive authentication" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "交互式认证"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "互動式驗證"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
@@ -7063,7 +6999,7 @@
|
||||
}
|
||||
},
|
||||
"Custom" : {
|
||||
"extractionState" : "stale",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
@@ -7191,22 +7127,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Dark Configuration" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "深色配置"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "深色設定"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Dark Custom Configuration" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
@@ -10660,6 +10580,7 @@
|
||||
}
|
||||
},
|
||||
"Follow Theme" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
@@ -10692,22 +10613,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Font Configuration" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "字体配置"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "字型設定"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Font Family" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -10741,23 +10646,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Font Size Follows Theme" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "字号跟随主题"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "字級跟隨主題"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"For secrets, use the bearer token field above." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
@@ -12707,6 +12595,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Interactive authentication" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "交互式认证"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "互動式驗證"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Interactive partition editor." : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
@@ -12741,6 +12646,40 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Invalid authentication challenge." : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "认证质询无效。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "驗證詢問無效。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Invalid authentication response." : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "认证响应无效。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "驗證回應無效。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Invalid SFTP copy source path." : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -13499,22 +13438,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Light Configuration" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "浅色配置"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "淺色設定"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Light Custom Configuration" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
@@ -20785,6 +20708,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Saved workspaces could not be restored. The saved data has been kept." : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "无法恢复已保存的工作区。已保留原有保存数据。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "無法回復已儲存的工作區。已保留原有儲存資料。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scratch files" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
@@ -20852,23 +20792,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Saved workspaces could not be restored. The saved data has been kept." : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "无法恢复已保存的工作区。已保留原有保存数据。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "無法回復已儲存的工作區。已保留原有儲存資料。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Search" : {
|
||||
"comment" : "Placeholder text for a search field.",
|
||||
"isCommentAutoGenerated" : true,
|
||||
@@ -26027,23 +25950,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"This font is unavailable on this device. Following the theme instead." : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此设备上无法使用该字体,已改为跟随主题。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此裝置上無法使用此字型,已改為跟隨主題。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This address already has a different trusted fingerprint. Confirm adding this key." : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
@@ -26231,6 +26137,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"This font is unavailable on this device. Following the theme instead." : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此设备上无法使用该字体,已改为跟随主题。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此裝置上無法使用此字型,已改為跟隨主題。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This forwarding will start after the host is saved." : {
|
||||
"comment" : "A description of the state of a forwarding that is waiting for the host to be saved.",
|
||||
"isCommentAutoGenerated" : true,
|
||||
@@ -26488,6 +26411,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"This server requires interactive authentication." : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此服务器需要交互式认证。"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此伺服器需要互動式驗證。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This session is not recording history. Enable Shell Integration and use bash, zsh, or fish." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
|
||||
@@ -12,15 +12,25 @@ struct MobileTerminalSettingsView: View {
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section("Font") {
|
||||
TerminalFontPicker(family: $font.family, families: TerminalFontCatalog.shared.families)
|
||||
Toggle("Font Size Follows Theme", isOn: $font.sizeFollowsTheme)
|
||||
Stepper(value: $font.size, in: 8...36, step: 1) {
|
||||
LabeledContent("Font Size", value: font.size.formatted())
|
||||
}.disabled(font.sizeFollowsTheme)
|
||||
}
|
||||
themeSection(dark: false)
|
||||
themeSection(dark: true)
|
||||
Section("Font") {
|
||||
TerminalFontPicker(family: $font.family, families: TerminalFontCatalog.shared.families)
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Picker("Font Size", selection: $font.sizeFollowsTheme) {
|
||||
Text("Follow Theme").tag(true)
|
||||
Text("Custom").tag(false)
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
if !font.sizeFollowsTheme {
|
||||
Stepper(value: $font.size, in: 8...36, step: 1) {
|
||||
Text(font.size, format: .number.precision(.fractionLength(0))).monospacedDigit()
|
||||
}
|
||||
.accessibilityLabel("Font Size")
|
||||
.accessibilityValue(font.size.formatted())
|
||||
}
|
||||
}
|
||||
}
|
||||
Section("Integration") { Toggle("Shell Integration", isOn: binding(\.shellIntegration)) }
|
||||
if let error = model.storageError { Text(error).foregroundStyle(.red) }
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@ struct MobileTerminalFontTests {
|
||||
.environment(\.locale, Locale(identifier: language))
|
||||
.frame(width: 280, height: 320)
|
||||
.background(.background)
|
||||
try await capture(view, name: "mobile-terminal-font-options-\(language)")
|
||||
try await withWindow(view) { window in
|
||||
try await capture(window, name: "mobile-terminal-font-options-\(language)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test(arguments: ["en", "zh-Hans", "zh-Hant"], ["", "Menlo"])
|
||||
@@ -38,16 +40,24 @@ struct MobileTerminalFontTests {
|
||||
let defaults = try #require(UserDefaults(suiteName: domain))
|
||||
defer { defaults.removePersistentDomain(forName: domain) }
|
||||
defaults.set(family, forKey: SettingsKey.terminalFontFamily)
|
||||
defaults.set(family.isEmpty, forKey: SettingsKey.terminalFontFollowTheme)
|
||||
defaults.set(18.0, forKey: SettingsKey.terminalFontSize)
|
||||
let view = NavigationStack {
|
||||
MobileTerminalSettingsView(model: TerminalSettingsModel())
|
||||
}
|
||||
.defaultAppStorage(defaults)
|
||||
.environment(\.locale, Locale(identifier: language))
|
||||
try await capture(view, name: "mobile-terminal-settings-\(language)-\(family.isEmpty ? "theme" : family)")
|
||||
let name = "mobile-terminal-settings-\(language)-\(family.isEmpty ? "theme" : family)"
|
||||
try await withWindow(view) { window in
|
||||
for (index, followsTheme) in [false, true, false].enumerated() {
|
||||
defaults.set(followsTheme, forKey: SettingsKey.terminalFontFollowTheme)
|
||||
try await capture(window, name: "\(name)-\(followsTheme ? "theme-size" : "custom-size")-\(index)")
|
||||
#expect(defaults.double(forKey: SettingsKey.terminalFontSize) == 18)
|
||||
#expect(defaults.string(forKey: SettingsKey.terminalFontFamily) == family)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func capture(_ view: some View, name: String) async throws {
|
||||
private func withWindow(_ view: some View, perform: (UIWindow) async throws -> Void) async throws {
|
||||
let scene = try #require(UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.first)
|
||||
let previousWindow = scene.keyWindow
|
||||
let window = UIWindow(windowScene: scene)
|
||||
@@ -60,7 +70,11 @@ struct MobileTerminalFontTests {
|
||||
window.rootViewController = nil
|
||||
previousWindow?.makeKey()
|
||||
}
|
||||
controller.view.layoutIfNeeded()
|
||||
try await perform(window)
|
||||
}
|
||||
|
||||
private func capture(_ window: UIWindow, name: String) async throws {
|
||||
window.layoutIfNeeded()
|
||||
try await Task.sleep(for: .milliseconds(500))
|
||||
let image = UIGraphicsImageRenderer(bounds: window.bounds).image { _ in
|
||||
window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
|
||||
|
||||
@@ -710,7 +710,7 @@ private struct TerminalSettings: View {
|
||||
if let error = app.terminalSettings.storageError {
|
||||
Text(error).foregroundStyle(.orange)
|
||||
}
|
||||
Section("Light Configuration") {
|
||||
Section("Light") {
|
||||
Picker("Theme", selection: binding(\.lightTheme)) {
|
||||
ForEach(lightThemes) { Text($0.name).tag($0.name) }
|
||||
}
|
||||
@@ -728,19 +728,24 @@ private struct TerminalSettings: View {
|
||||
editConfiguration(.dark)
|
||||
}
|
||||
} header: {
|
||||
Text("Dark Configuration")
|
||||
Text("Dark")
|
||||
} footer: {
|
||||
Text(
|
||||
"Termind uses [Ghostty](https://ghostty.org) as its terminal engine. Custom configuration uses [Ghostty's configuration format](https://ghostty.org/docs/config/reference)."
|
||||
)
|
||||
}
|
||||
Section("Font Configuration") {
|
||||
Section("Font") {
|
||||
TerminalFontPicker(family: $font.family, families: TerminalFontCatalog.shared.families)
|
||||
Toggle("Font Size Follows Theme", isOn: $font.sizeFollowsTheme)
|
||||
if !font.sizeFollowsTheme {
|
||||
LabeledContent("Font Size") {
|
||||
VStack(alignment: .leading, spacing: Metrics.Space.m) {
|
||||
Picker("Font Size", selection: $font.sizeFollowsTheme) {
|
||||
Text("Follow Theme").tag(true)
|
||||
Text("Custom").tag(false)
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
if !font.sizeFollowsTheme {
|
||||
HStack {
|
||||
Slider(value: $font.size, in: 9...24, step: 1)
|
||||
.labelsHidden().accessibilityLabel("Font Size")
|
||||
Text(font.size, format: .number.precision(.fractionLength(0)))
|
||||
.monospacedDigit().frame(width: 24, alignment: .trailing)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ struct TerminalSettingsRenderingTests {
|
||||
let defaults = try #require(UserDefaults(suiteName: domain))
|
||||
defer { defaults.removePersistentDomain(forName: domain) }
|
||||
defaults.set(family, forKey: SettingsKey.terminalFontFamily)
|
||||
defaults.set(family.isEmpty, forKey: SettingsKey.terminalFontFollowTheme)
|
||||
defaults.set(18.0, forKey: SettingsKey.terminalFontSize)
|
||||
let app = AppModel(hosts: HostLibraryModel())
|
||||
let model = WindowModel(workspaces: [])
|
||||
model.openSettings(route: .category(.terminal))
|
||||
@@ -23,9 +23,16 @@ struct TerminalSettingsRenderingTests {
|
||||
.defaultAppStorage(defaults)
|
||||
.environment(\.locale, Locale(identifier: language))
|
||||
.frame(width: 640, height: 1100)
|
||||
try await capture(
|
||||
view, name: "terminal-settings-\(language)-\(family.isEmpty ? "theme" : family)",
|
||||
size: NSSize(width: 640, height: 1100))
|
||||
let name = "terminal-settings-\(language)-\(family.isEmpty ? "theme" : family)"
|
||||
try await withWindow(view, size: NSSize(width: 640, height: 1100)) { hostedView in
|
||||
for (index, followsTheme) in [false, true, false].enumerated() {
|
||||
defaults.set(followsTheme, forKey: SettingsKey.terminalFontFollowTheme)
|
||||
try await capture(
|
||||
hostedView, name: "\(name)-\(followsTheme ? "theme-size" : "custom-size")-\(index)")
|
||||
#expect(defaults.double(forKey: SettingsKey.terminalFontSize) == 18)
|
||||
#expect(defaults.string(forKey: SettingsKey.terminalFontFamily) == family)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(arguments: ["en", "zh-Hans", "zh-Hant"])
|
||||
@@ -36,10 +43,12 @@ struct TerminalSettingsRenderingTests {
|
||||
.environment(\.locale, Locale(identifier: language))
|
||||
.frame(width: 280, height: 320)
|
||||
.background(.background)
|
||||
try await capture(view, name: "terminal-font-options-\(language)", size: NSSize(width: 280, height: 320))
|
||||
try await withWindow(view, size: NSSize(width: 280, height: 320)) { hostedView in
|
||||
try await capture(hostedView, name: "terminal-font-options-\(language)")
|
||||
}
|
||||
}
|
||||
|
||||
private func capture(_ view: some View, name: String, size: NSSize) async throws {
|
||||
private func withWindow(_ view: some View, size: NSSize, perform: (NSView) async throws -> Void) async rethrows {
|
||||
let controller = NSHostingController(rootView: view)
|
||||
let window = NSWindow(
|
||||
contentRect: NSRect(origin: .zero, size: size),
|
||||
@@ -52,10 +61,14 @@ struct TerminalSettingsRenderingTests {
|
||||
window.contentViewController = nil
|
||||
window.close()
|
||||
}
|
||||
try await perform(controller.view)
|
||||
}
|
||||
|
||||
private func capture(_ view: NSView, name: String) async throws {
|
||||
try await Task.sleep(for: .milliseconds(500))
|
||||
controller.view.layoutSubtreeIfNeeded()
|
||||
let bitmap = try #require(controller.view.bitmapImageRepForCachingDisplay(in: controller.view.bounds))
|
||||
controller.view.cacheDisplay(in: controller.view.bounds, to: bitmap)
|
||||
view.layoutSubtreeIfNeeded()
|
||||
let bitmap = try #require(view.bitmapImageRepForCachingDisplay(in: view.bounds))
|
||||
view.cacheDisplay(in: view.bounds, to: bitmap)
|
||||
let data = try #require(bitmap.representation(using: .png, properties: [:]))
|
||||
Attachment.record(data, named: "\(name).png")
|
||||
#expect(data.count > 5_000)
|
||||
|
||||
Reference in new issue
Block a user