From ff03a4cda5b5171d9bbfbf63c8420c8e65f0c589 Mon Sep 17 00:00:00 2001 From: nimbold <11913706+nimbold@users.noreply.github.com> Date: Wed, 3 Jun 2026 05:03:31 +0330 Subject: [PATCH] feat: add Font Size, List Row Density, and Menu Bar Icon settings --- Sources/Firelink/AppSettings.swift | 24 +++++++++++++++++++++ Sources/Firelink/DownloadTable.swift | 3 ++- Sources/Firelink/FirelinkApp.swift | 6 +++++- Sources/Firelink/SettingsView.swift | 22 +++++++++++++++++++ Sources/Firelink/Theme.swift | 32 ++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 2 deletions(-) diff --git a/Sources/Firelink/AppSettings.swift b/Sources/Firelink/AppSettings.swift index c1e0d17..3ef5588 100644 --- a/Sources/Firelink/AppSettings.swift +++ b/Sources/Firelink/AppSettings.swift @@ -68,6 +68,18 @@ final class AppSettings: ObservableObject { @Published var appTheme: AppTheme = .system { didSet { save() } } + + @Published var appFontSize: AppFontSize = .standard { + didSet { save() } + } + + @Published var listRowDensity: ListRowDensity = .standard { + didSet { save() } + } + + @Published var showMenuBarIcon: Bool = true { + didSet { save() } + } @Published var perServerConnections: Int { didSet { @@ -134,6 +146,9 @@ final class AppSettings: ObservableObject { if let data = defaults.data(forKey: storageKey), let stored = try? JSONDecoder().decode(StoredSettings.self, from: data) { appTheme = stored.appTheme ?? .system + appFontSize = stored.appFontSize ?? .standard + listRowDensity = stored.listRowDensity ?? .standard + showMenuBarIcon = stored.showMenuBarIcon ?? true perServerConnections = min(max(stored.perServerConnections, 1), 16) maxConcurrentDownloads = min(max(stored.maxConcurrentDownloads ?? 3, 1), 12) globalSpeedLimitKiBPerSecond = min(max(stored.globalSpeedLimitKiBPerSecond ?? 0, 0), 10_485_760) @@ -143,6 +158,9 @@ final class AppSettings: ObservableObject { downloadDirectories = Self.decodeDirectories(stored.downloadDirectories) } else { appTheme = .system + appFontSize = .standard + listRowDensity = .standard + showMenuBarIcon = true perServerConnections = 16 maxConcurrentDownloads = 3 globalSpeedLimitKiBPerSecond = 0 @@ -225,6 +243,9 @@ final class AppSettings: ObservableObject { private func save() { let stored = StoredSettings( appTheme: appTheme, + appFontSize: appFontSize, + listRowDensity: listRowDensity, + showMenuBarIcon: showMenuBarIcon, perServerConnections: perServerConnections, maxConcurrentDownloads: maxConcurrentDownloads, globalSpeedLimitKiBPerSecond: globalSpeedLimitKiBPerSecond, @@ -285,6 +306,9 @@ final class AppSettings: ObservableObject { private struct StoredSettings: Codable { var appTheme: AppTheme? + var appFontSize: AppFontSize? + var listRowDensity: ListRowDensity? + var showMenuBarIcon: Bool? var perServerConnections: Int var maxConcurrentDownloads: Int? var globalSpeedLimitKiBPerSecond: Int? diff --git a/Sources/Firelink/DownloadTable.swift b/Sources/Firelink/DownloadTable.swift index 36b88f9..5b5eace 100644 --- a/Sources/Firelink/DownloadTable.swift +++ b/Sources/Firelink/DownloadTable.swift @@ -526,6 +526,7 @@ struct DownloadTable: View { } private struct DownloadRow: View { + @EnvironmentObject private var settings: AppSettings let item: DownloadItem let priorityNumber: Int? let visibleColumns: [DownloadColumn] @@ -537,7 +538,7 @@ private struct DownloadRow: View { ForEach(visibleColumns) { column in cell(for: column) .padding(.horizontal, 8) - .padding(.vertical, 8) + .padding(.vertical, settings.listRowDensity.verticalPadding) .frame(width: columnWidth(column), alignment: alignment(for: column)) .clipped() } diff --git a/Sources/Firelink/FirelinkApp.swift b/Sources/Firelink/FirelinkApp.swift index f326269..3d7e985 100644 --- a/Sources/Firelink/FirelinkApp.swift +++ b/Sources/Firelink/FirelinkApp.swift @@ -21,6 +21,7 @@ struct FirelinkApp: App { .environmentObject(settings) .environmentObject(schedulerController) .modifier(AppThemeModifier(theme: settings.appTheme)) + .dynamicTypeSize(settings.appFontSize.dynamicTypeSize) .frame(minWidth: 1180, idealWidth: 1280, minHeight: 720, idealHeight: 760) } .windowStyle(.titleBar) @@ -30,6 +31,7 @@ struct FirelinkApp: App { .environmentObject(controller) .environmentObject(settings) .modifier(AppThemeModifier(theme: settings.appTheme)) + .dynamicTypeSize(settings.appFontSize.dynamicTypeSize) } .windowResizability(.contentSize) @@ -39,9 +41,11 @@ struct FirelinkApp: App { .environmentObject(controller) .environmentObject(settings) .modifier(AppThemeModifier(theme: settings.appTheme)) + .dynamicTypeSize(settings.appFontSize.dynamicTypeSize) } else { ContentUnavailableView("Download Not Found", systemImage: "questionmark.circle") .modifier(AppThemeModifier(theme: settings.appTheme)) + .dynamicTypeSize(settings.appFontSize.dynamicTypeSize) } } .windowResizability(.contentSize) @@ -55,7 +59,7 @@ struct FirelinkApp: App { } } - MenuBarExtra("Firelink", systemImage: "arrow.down.circle") { + MenuBarExtra("Firelink", systemImage: "arrow.down.circle", isInserted: $settings.showMenuBarIcon) { TrayMenuView() .environmentObject(controller) } diff --git a/Sources/Firelink/SettingsView.swift b/Sources/Firelink/SettingsView.swift index af25030..6d55641 100644 --- a/Sources/Firelink/SettingsView.swift +++ b/Sources/Firelink/SettingsView.swift @@ -137,6 +137,28 @@ private struct LookAndFeelSettingsPane: View { .font(.caption) .foregroundStyle(.secondary) } + + Section("Display") { + Picker("Font Size", selection: $settings.appFontSize) { + ForEach(AppFontSize.allCases) { size in + Text(size.rawValue).tag(size) + } + } + + Picker("List Row Density", selection: $settings.listRowDensity) { + ForEach(ListRowDensity.allCases) { density in + Text(density.rawValue).tag(density) + } + } + } + + Section("Menu Bar") { + Toggle("Show menu bar icon", isOn: $settings.showMenuBarIcon) + + Text("Provides quick access to downloads and queues from the macOS menu bar.") + .font(.caption) + .foregroundStyle(.secondary) + } } .formStyle(.grouped) } diff --git a/Sources/Firelink/Theme.swift b/Sources/Firelink/Theme.swift index d4bc53f..85738bf 100644 --- a/Sources/Firelink/Theme.swift +++ b/Sources/Firelink/Theme.swift @@ -1,6 +1,38 @@ import SwiftUI import AppKit +enum AppFontSize: String, Codable, CaseIterable, Identifiable, Sendable { + case small = "Small" + case standard = "Standard" + case large = "Large" + + var id: String { rawValue } + + var dynamicTypeSize: DynamicTypeSize { + switch self { + case .small: return .small + case .standard: return .medium + case .large: return .xLarge + } + } +} + +enum ListRowDensity: String, Codable, CaseIterable, Identifiable, Sendable { + case compact = "Compact" + case standard = "Standard" + case relaxed = "Relaxed" + + var id: String { rawValue } + + var verticalPadding: CGFloat { + switch self { + case .compact: return 4 + case .standard: return 8 + case .relaxed: return 14 + } + } +} + enum AppTheme: String, Codable, CaseIterable, Identifiable, Sendable { case system = "System Default" case light = "Light"