Skip to content

Commit

Permalink
feat(settings): Ventura-style Settings overhaul (#141)
Browse files Browse the repository at this point in the history
* feat (settings): inital new settings UI

Follows macOS Ventura settings style, enabled by default

* patch (settings): match window resizing behaviour to that of macOS Settings

* feat (settings): add ability to set sidebar icon from assets

* patch (settings): move to Settings instead of Window

* feat (settings): re-add footer settings button with working selector

turns out the selector still worked, just under a new name

refactor: user footer to be closer in styling to the official client’s one

* fix(settings): only patch window if new settings style active

force-unwrapping an optional to find the underlying Settings NSWindow caused a crash on macOS 12

fixes #144

* feat(settings): migrate most existing settings views to ventura style

* patch(settings): update sidebar icon/colors

* feat(settings): port all existing views to ventura style

As far as possible, the Form + Section pattern has been used for all settings views

* patch(settings): credits page touchup

patch(settings): update sidebar icon for “voice and video”

* patch(settings): move profile sidebar item into its own section with no title

Just like macOS’ system settings

* patch(settings): final cleanups

* fix some UI issues in the Accessibility page
* move sidebar toggle hide code to View extension

* feat(settings): major user profile ui improvements
  • Loading branch information
cryptoAlgorithm authored May 10, 2023
1 parent 74e03f5 commit 81cedba
Show file tree
Hide file tree
Showing 28 changed files with 1,073 additions and 571 deletions.
48 changes: 36 additions & 12 deletions Swiftcord.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Binary file modified Swiftcord/Assets.xcassets/.DS_Store
Binary file not shown.
20 changes: 20 additions & 0 deletions Swiftcord/Assets.xcassets/NitroPink.colorset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFA",
"green" : "0x73",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
12 changes: 12 additions & 0 deletions Swiftcord/Assets.xcassets/ServerBoost.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ServerBoost.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 11 additions & 5 deletions Swiftcord/ButtonStyles/FlatButtonStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@
import SwiftUI

struct FlatButtonStyle: ButtonStyle {
// Use vars so not all params have to be supplied all the time
var prominent = true
var outlined = false
var text = false
init(prominent: Bool = true, outlined: Bool = false, text: Bool = false, customBase: Color? = nil) {
self.prominent = prominent
self.outlined = outlined
self.text = text
self.customBase = customBase
}

let prominent: Bool
let outlined: Bool
let text: Bool

var customBase: Color?
let customBase: Color?

@State private var hovered = false

Expand Down
19 changes: 16 additions & 3 deletions Swiftcord/SwiftcordApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct SwiftcordApp: App {
)
.onAppear {
// Fix list assertion errors
// The window has been marked as needing another Update Constraints in Window pass, but it has already had more Update Constraints in Window passes than there are views in the window.
UserDefaults.standard.set(false, forKey: "NSWindowAssertWhenDisplayCycleLimitReached")

guard gateway.socket == nil else { return }
Expand Down Expand Up @@ -90,11 +91,11 @@ struct SwiftcordApp: App {
}
}
.commands {
#if !APP_STORE
#if !APP_STORE
CommandGroup(after: .appInfo) {
CheckForUpdatesView(updaterViewModel: updaterViewModel)
}
#endif
#endif

SidebarCommands()
NavigationCommands(state: state, gateway: gateway)
Expand All @@ -107,12 +108,24 @@ struct SwiftcordApp: App {
.environmentObject(gateway)
.environmentObject(state)
.environmentObject(acctManager)
.environmentObject(updaterViewModel)
.preferredColorScheme(
selectedTheme == "dark"
? .dark
: (selectedTheme == "light" ? .light : .none)
)
// .environment(\.locale, .init(identifier: "zh-Hans"))
}
}
}

@available(macOS 13, *)
struct SettingsCommands: View {
@Environment(\.openWindow) private var openWindow

var body: some View {
Divider()
Button("Settings") {
openWindow(id: "settings")
}.keyboardShortcut(",", modifiers: .command)
}
}
21 changes: 21 additions & 0 deletions Swiftcord/Utils/Extensions/View+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,24 @@ extension View {
}
}
}

extension View {
func removeSidebarToggle(windowModifier: @escaping (NSWindow) -> Void = { _ in }) -> some View {
modifier(RemoveSidebarToggleModifier(windowModifier: windowModifier))
}
}

private struct RemoveSidebarToggleModifier: ViewModifier {
let windowModifier: (NSWindow) -> Void

func body(content: Content) -> some View {
content.task {
guard let window = NSApp.windows.first(where: { $0.identifier?.rawValue == "com_apple_SwiftUI_Settings_window" }) else { return }
windowModifier(window)
let sidebaritem = "com.apple.SwiftUI.navigationSplitView.toggleSidebar"
if let index = window.toolbar?.items.firstIndex(where: { $0.itemIdentifier.rawValue == sidebaritem }) {
window.toolbar?.removeItem(at: index)
}
}
}
}
48 changes: 17 additions & 31 deletions Swiftcord/Views/Settings/App/AppSettingsAccessibilityView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,7 @@ struct AppSettingsAccessibilityView: View {
@AppStorage("ttsRate") private var ttsRate = 0.5

var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text("settings.app.accessibility").font(.title)

Text("settings.app.accessibility.chatInput")
.font(.headline)
.textCase(.uppercase)
.opacity(0.75)
Section("settings.app.accessibility.chatInput") {
VStack(alignment: .leading) {
Toggle(isOn: $alwaysAnimStickers) {
Text("Always animate stickers").frame(maxWidth: .infinity, alignment: .leading)
Expand All @@ -31,22 +25,29 @@ struct AppSettingsAccessibilityView: View {
Text("settings.animInteraction").font(.caption)
}
}
}

Divider()

Text("settings.app.accessibility.chatInput")
.font(.headline)
.textCase(.uppercase)
.opacity(0.75)
Section("settings.app.accessibility.chatInput") {
Toggle(isOn: $showSendButton) {
Text("settings.showSendBtn").frame(maxWidth: .infinity, alignment: .leading)
}
.toggleStyle(.switch)
.tint(.green)
}

Divider()

Text("settings.tts.rate").font(.headline).textCase(.uppercase).opacity(0.75)
Section {
VStack(alignment: .leading, spacing: 0) {
Slider(value: $ttsRate, in: 0...1, step: 0.1) {
Text("Narration speed")
} minimumValueLabel: {
Text("Slower").font(.subheadline).opacity(0.75)
} maximumValueLabel: {
Text("Faster").font(.subheadline).opacity(0.75)
}
}
} header: {
Text("settings.tts.rate")
} footer: {
Button {
let text = "This is what text-to-speech sounds like at the current speed"

Expand All @@ -59,21 +60,6 @@ struct AppSettingsAccessibilityView: View {
} label: {
Label("Preview", systemImage: "play.fill")
}
.buttonStyle(FlatButtonStyle())
.controlSize(.small)
VStack(alignment: .leading, spacing: 0) {
HStack {
Text("Slower").font(.subheadline).opacity(0.75)
Spacer()
Text("settings.tts.defaultSpeed").font(.subheadline).foregroundColor(.green)
Spacer()
Text("Faster").font(.subheadline).opacity(0.75)
}
Slider(value: $ttsRate, in: 0...1, step: 0.1)
Text(String(format: "%.1f", ttsRate))
.font(.subheadline)
.frame(maxWidth: .infinity, alignment: .trailing)
}
}
}
}
13 changes: 4 additions & 9 deletions Swiftcord/Views/Settings/App/AppSettingsAdvancedView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,7 @@ struct AppSettingsAdvancedView: View {
@State private var hasToggledAnalytics = false

var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text("settings.app.advanced").font(.title)

Text("settings.app.advanced.analytics")
.font(.headline)
.textCase(.uppercase)
.opacity(0.75)
Section("settings.app.advanced.analytics") {
Toggle(isOn: $analyticsEnabled) {
Text("settings.app.advanced.analytics.option")
.frame(maxWidth: .infinity, alignment: .leading)
Expand All @@ -32,10 +26,11 @@ struct AppSettingsAdvancedView: View {
}
Analytics.enabled = enabled
}.disabled(hasToggledAnalytics)
Text("settings.app.advanced.analytics.caption").font(.caption)

Divider()
Text("settings.app.advanced.analytics.caption").font(.callout).foregroundColor(.secondary)
}

Section {
Text("settings.app.advanced.crashes")
}
}
Expand Down
7 changes: 3 additions & 4 deletions Swiftcord/Views/Settings/App/AppSettingsAppearanceView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@ struct AppSettingsAppearanceView: View {
@AppStorage("theme") private var selectedTheme = "system"

var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text("settings.app.appearance").font(.title)

Section {
Picker("settings.app.appearance.theme", selection: $selectedTheme) {
ForEach(themes, id: \.self) {
Text($0.capitalized)
}
}.pickerStyle(.menu)
Text("A known bug causes rendering glitches when the theme is switched from a theme that isn't the current system theme, to the system theme. It seems to be due to SwiftUI itself, but I'm looking for workarounds.")
.font(.caption)
.font(.callout)
.foregroundColor(.secondary)
}
}
}
57 changes: 34 additions & 23 deletions Swiftcord/Views/Settings/Misc/AboutSwiftcordView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,43 @@
import SwiftUI

struct AboutSwiftcordView: View {
var body: some View {
VStack(spacing: 16) {
VStack(spacing: 8) {
Image(nsImage: NSApp.applicationIconImage).resizable().frame(width: 128, height: 128)
Text(appName ?? "").font(.largeTitle)
Text("settings.others.about.desc")
@EnvironmentObject var updaterViewModel: UpdaterViewModel

// IMO its better to just crash if these are missing in the info dict.
// If they are nil there are bigger problems than the app crashing.
var body: some View {
Section {
// IMO its better to just crash if these are missing in the info dict.
// If they are nil there are bigger problems than the app crashing.
HStack {
Text("Version")
Spacer()
// swiftlint:disable force_cast
Text("\(appName ?? "") \(Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String) \(Bundle.main.infoDictionary!["CFBundleVersion"] as! String) settings.others.about.ver").font(.caption)
Text(Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String)
.foregroundColor(.secondary)
}

Divider()

Group {
Text("settings.others.about.caption").multilineTextAlignment(.center)
Text("settings.others.about.supportPkg")
HStack {
Text("Build")
Spacer()
// swiftlint:disable force_cast
Text(Bundle.main.infoDictionary!["CFBundleVersion"] as! String)
.foregroundColor(.secondary)
}
Spacer()
}.padding(40)
}
}
} header: {
VStack(spacing: 4) {
Image(nsImage: NSApp.applicationIconImage).resizable().frame(width: 128, height: 128)
Text(appName ?? "").font(.largeTitle).foregroundColor(.primary)
Text("settings.others.about.desc").font(.title3)
}
.frame(maxWidth: .infinity)
.padding(.top, 8)
.padding(.bottom, 16)
} footer: {
Button("Check for Updates…", action: updaterViewModel.checkForUpdates)
.disabled(!updaterViewModel.canCheckForUpdates)
}

struct AboutSwiftcordView_Previews: PreviewProvider {
static var previews: some View {
AboutSwiftcordView()
}
Section {
Text("settings.others.about.caption")
Text("settings.others.about.supportPkg")
}
}
}
Loading

0 comments on commit 81cedba

Please sign in to comment.