mirror of
https://github.com/X1a0He/Adobe-Downloader.git
synced 2025-11-25 03:14:57 +08:00
Optimize: The display of the About page, Language selection page, Version selection page, and Settings page.
This commit is contained in:
50
.gitignore
vendored
50
.gitignore
vendored
@@ -1,6 +1,48 @@
|
||||
.idea/
|
||||
Adobe-Downloader.xcodeproj
|
||||
Adobe-Downloader.xcodeproj/*.workspace
|
||||
# Xcode
|
||||
.DS_Store
|
||||
*.DS_Store
|
||||
project.xcworkspace
|
||||
.idea/
|
||||
|
||||
## User settings
|
||||
xcuserdata/
|
||||
*.xcuserstate
|
||||
|
||||
## Xcode Workspace
|
||||
*.xcworkspace
|
||||
*.xcodeproj/project.xcworkspace/
|
||||
*.xcodeproj/xcuserdata/
|
||||
|
||||
## Build generated
|
||||
build/
|
||||
DerivedData/
|
||||
|
||||
## Various settings
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata/
|
||||
|
||||
## Other
|
||||
*.moved-aside
|
||||
*.xccheckout
|
||||
*.xcscmblueprint
|
||||
*.xcuserstate
|
||||
.xcuserstate
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
## Playgrounds
|
||||
timeline.xctimeline
|
||||
playground.xcworkspace
|
||||
|
||||
# Swift Package Manager
|
||||
.build/
|
||||
Binary file not shown.
@@ -29,36 +29,34 @@ struct ContentView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 20) {
|
||||
HStack {
|
||||
Text("Adobe Downloader")
|
||||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
.frame(minWidth: 200)
|
||||
HStack {
|
||||
SettingsView(
|
||||
useDefaultLanguage: $useDefaultLanguage,
|
||||
useDefaultDirectory: $useDefaultDirectory,
|
||||
onSelectLanguage: selectLanguage,
|
||||
onSelectDirectory: selectDirectory
|
||||
)
|
||||
}
|
||||
HStack(spacing: 16) {
|
||||
Text("Adobe Downloader")
|
||||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
.frame(width: 180)
|
||||
|
||||
SettingsView(
|
||||
useDefaultLanguage: $useDefaultLanguage,
|
||||
useDefaultDirectory: $useDefaultDirectory,
|
||||
onSelectLanguage: selectLanguage,
|
||||
onSelectDirectory: selectDirectory
|
||||
)
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
HStack(spacing: 8) {
|
||||
SearchField(text: $searchText)
|
||||
.frame(width: 160)
|
||||
.frame(width: 140)
|
||||
|
||||
Button(action: refreshData) {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
.imageScale(.large)
|
||||
.imageScale(.medium)
|
||||
}
|
||||
.disabled(isRefreshing)
|
||||
.buttonStyle(.borderless)
|
||||
|
||||
Button(action: { showDownloadManager.toggle() }) {
|
||||
Image(systemName: "arrow.down.circle")
|
||||
.imageScale(.large)
|
||||
.imageScale(.medium)
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.overlay(
|
||||
@@ -66,18 +64,18 @@ struct ContentView: View {
|
||||
if !networkManager.downloadTasks.isEmpty {
|
||||
Text("\(networkManager.downloadTasks.count)")
|
||||
.font(.caption2)
|
||||
.padding(4)
|
||||
.padding(3)
|
||||
.background(Color.blue)
|
||||
.clipShape(Circle())
|
||||
.foregroundColor(.white)
|
||||
.offset(x: 10, y: -10)
|
||||
.offset(x: 8, y: -8)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
.frame(width: 220)
|
||||
.frame(width: 200)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 8)
|
||||
.background(Color(NSColor.windowBackgroundColor))
|
||||
|
||||
|
||||
@@ -13,14 +13,12 @@ struct AboutView: View {
|
||||
.tabItem {
|
||||
Label("General", systemImage: "gear")
|
||||
}
|
||||
|
||||
|
||||
AboutAppView()
|
||||
.tabItem {
|
||||
Label("About", systemImage: "info.circle")
|
||||
}
|
||||
}
|
||||
.frame(width: 375, height: 150)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,25 +31,25 @@ struct GeneralSettingsView: View {
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
GroupBox(label: Text("下载设置")) {
|
||||
GroupBox(label: Text("下载设置").padding(.bottom, 8)) {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
// 语言设置
|
||||
HStack {
|
||||
Toggle("使用默认语言", isOn: $useDefaultLanguage)
|
||||
.padding(.leading, 5)
|
||||
Spacer()
|
||||
Text(getLanguageName(code: defaultLanguage))
|
||||
.foregroundColor(.secondary)
|
||||
Button("选择") {
|
||||
showLanguagePicker = true
|
||||
}
|
||||
.padding(.trailing, 5)
|
||||
.buttonStyle(.borderless)
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
// 目录设置
|
||||
HStack {
|
||||
Toggle("使用默认目录", isOn: $useDefaultDirectory)
|
||||
.padding(.leading, 5)
|
||||
Spacer()
|
||||
Text(formatPath(defaultDirectory))
|
||||
.foregroundColor(.secondary)
|
||||
@@ -60,6 +58,7 @@ struct GeneralSettingsView: View {
|
||||
Button("选择") {
|
||||
selectDirectory()
|
||||
}
|
||||
.padding(.trailing, 5)
|
||||
.buttonStyle(.borderless)
|
||||
}
|
||||
}
|
||||
@@ -67,6 +66,7 @@ struct GeneralSettingsView: View {
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.frame(maxHeight: .infinity, alignment: .top)
|
||||
.sheet(isPresented: $showLanguagePicker) {
|
||||
LanguagePickerView(languages: AppStatics.supportedLanguages) { language in
|
||||
defaultLanguage = language
|
||||
@@ -112,13 +112,23 @@ struct AboutAppView: View {
|
||||
Text("By X1a0He. ❤️ Love from China. ❤️")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Link("Github: Adobe Downloader",
|
||||
destination: URL(string: "https://github.com/X1a0He/Adobe-Downloader")!)
|
||||
.font(.caption)
|
||||
.foregroundColor(.blue)
|
||||
|
||||
Link("Thanks Drovosek01: adobe-packager",
|
||||
destination: URL(string: "https://github.com/Drovosek01/adobe-packager")!)
|
||||
.font(.caption)
|
||||
.foregroundColor(.blue)
|
||||
|
||||
Text("Released under GPLv3.")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.padding()
|
||||
.fixedSize(horizontal: true, vertical: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,19 @@ struct LanguagePickerView: View {
|
||||
let onLanguageSelected: (String) -> Void
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@AppStorage("defaultLanguage") private var defaultLanguage: String = "zh_CN"
|
||||
@State private var searchText: String = ""
|
||||
|
||||
private var filteredLanguages: [(code: String, name: String)] {
|
||||
guard !searchText.isEmpty else {
|
||||
return languages
|
||||
}
|
||||
|
||||
let searchTerms = searchText.lowercased()
|
||||
return languages.filter { language in
|
||||
language.name.lowercased().contains(searchTerms) ||
|
||||
language.code.lowercased().contains(searchTerms)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
@@ -25,41 +38,118 @@ struct LanguagePickerView: View {
|
||||
.padding()
|
||||
.background(Color(NSColor.controlBackgroundColor))
|
||||
|
||||
HStack {
|
||||
Image(systemName: "magnifyingglass")
|
||||
.foregroundColor(.secondary)
|
||||
TextField("搜索语言", text: $searchText)
|
||||
.textFieldStyle(.plain)
|
||||
if !searchText.isEmpty {
|
||||
Button(action: { searchText = "" }) {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
.padding(8)
|
||||
.background(Color(NSColor.controlBackgroundColor))
|
||||
.cornerRadius(6)
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 8)
|
||||
|
||||
Divider()
|
||||
|
||||
ScrollView {
|
||||
LazyVGrid(
|
||||
columns: [GridItem(.flexible())],
|
||||
spacing: 8
|
||||
) {
|
||||
ForEach(languages, id: \.code) { language in
|
||||
Button(action: {
|
||||
defaultLanguage = language.code
|
||||
onLanguageSelected(language.code)
|
||||
dismiss()
|
||||
}) {
|
||||
HStack {
|
||||
Text(language.name)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
if defaultLanguage == language.code {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(Array(filteredLanguages.enumerated()), id: \.element.code) { index, language in
|
||||
LanguageRow(
|
||||
language: language,
|
||||
isSelected: language.code == defaultLanguage,
|
||||
onSelect: {
|
||||
defaultLanguage = language.code
|
||||
onLanguageSelected(language.code)
|
||||
dismiss()
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 8)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.fill(defaultLanguage == language.code ? Color.blue.opacity(0.1) : Color.clear)
|
||||
)
|
||||
|
||||
if index < filteredLanguages.count - 1 {
|
||||
Divider()
|
||||
.padding(.leading, 44)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
if filteredLanguages.isEmpty {
|
||||
ContentUnavailableView(
|
||||
"未找到语言",
|
||||
systemImage: "magnifyingglass",
|
||||
description: Text("尝试其他搜索关键词")
|
||||
)
|
||||
}
|
||||
}
|
||||
.frame(width: 300, height: 400)
|
||||
.frame(width: 320, height: 400)
|
||||
}
|
||||
}
|
||||
|
||||
struct LanguageRow: View {
|
||||
let language: (code: String, name: String)
|
||||
let isSelected: Bool
|
||||
let onSelect: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Button(action: onSelect) {
|
||||
HStack(spacing: 12) {
|
||||
Image(systemName: getLanguageIcon(language.code))
|
||||
.foregroundColor(.blue)
|
||||
.frame(width: 24)
|
||||
|
||||
Text(language.name)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Text(language.code)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
if isSelected {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.blue)
|
||||
.frame(width: 20)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 10)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.background(isSelected ? Color.blue.opacity(0.1) : Color.clear)
|
||||
}
|
||||
|
||||
private func getLanguageIcon(_ code: String) -> String {
|
||||
switch code {
|
||||
case "zh_CN", "zh_TW":
|
||||
return "character.textbox"
|
||||
case "en_US", "en_GB":
|
||||
return "a.square"
|
||||
case "ja_JP":
|
||||
return "j.square"
|
||||
case "ko_KR":
|
||||
return "k.square"
|
||||
case "fr_FR":
|
||||
return "f.square"
|
||||
case "de_DE":
|
||||
return "d.square"
|
||||
case "es_ES":
|
||||
return "e.square"
|
||||
case "it_IT":
|
||||
return "i.square"
|
||||
case "ru_RU":
|
||||
return "r.square"
|
||||
case "ALL":
|
||||
return "globe"
|
||||
default:
|
||||
return "character.square"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ struct SettingsView: View {
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 12) {
|
||||
// 语言设置
|
||||
HStack(spacing: 4) {
|
||||
Toggle(isOn: $useDefaultLanguage) {
|
||||
Text("语言:")
|
||||
@@ -40,7 +39,6 @@ struct SettingsView: View {
|
||||
Divider()
|
||||
.frame(height: 16)
|
||||
|
||||
// 目录设置
|
||||
HStack(spacing: 4) {
|
||||
Toggle(isOn: $useDefaultDirectory) {
|
||||
Text("目录:")
|
||||
|
||||
@@ -9,10 +9,27 @@ struct VersionPickerView: View {
|
||||
let product: Product
|
||||
let onVersionSelected: (String) -> Void
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@AppStorage("defaultDirectory") private var defaultDirectory: String = ""
|
||||
@AppStorage("useDefaultDirectory") private var useDefaultDirectory: Bool = true
|
||||
@AppStorage("defaultLanguage") private var defaultLanguage: String = "zh_CN"
|
||||
|
||||
private var sortedVersions: [(version: String, platform: String)] {
|
||||
private var sortedVersions: [(version: String, platform: String, exists: Bool)] {
|
||||
product.versions
|
||||
.map { (version: $0.key, platform: $0.value.apPlatform) }
|
||||
.map { version -> (version: String, platform: String, exists: Bool) in
|
||||
let installerPath: String
|
||||
let appName = "Install \(product.sapCode)_\(version.key)-\(defaultLanguage)-\(version.value.apPlatform).app"
|
||||
if useDefaultDirectory && !defaultDirectory.isEmpty {
|
||||
installerPath = (defaultDirectory as NSString).appendingPathComponent(appName)
|
||||
} else {
|
||||
let downloadsPath = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first?.path ?? ""
|
||||
installerPath = (downloadsPath as NSString).appendingPathComponent(appName)
|
||||
}
|
||||
return (
|
||||
version: version.key,
|
||||
platform: version.value.apPlatform,
|
||||
exists: FileManager.default.fileExists(atPath: installerPath)
|
||||
)
|
||||
}
|
||||
.sorted { $0.version.compare($1.version, options: .numeric) == .orderedDescending }
|
||||
}
|
||||
|
||||
@@ -34,36 +51,65 @@ struct VersionPickerView: View {
|
||||
}
|
||||
.padding()
|
||||
.background(Color(NSColor.controlBackgroundColor))
|
||||
|
||||
Divider()
|
||||
|
||||
List {
|
||||
ForEach(sortedVersions, id: \.version) { version in
|
||||
Button(action: {
|
||||
onVersionSelected(version.version)
|
||||
dismiss()
|
||||
}) {
|
||||
HStack(spacing: 12) {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(version.version)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
Text(getPlatformDisplayName(version.platform))
|
||||
ScrollView(showsIndicators: false) {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(sortedVersions, id: \.version) { version in
|
||||
Button(action: {
|
||||
onVersionSelected(version.version)
|
||||
dismiss()
|
||||
}) {
|
||||
HStack(spacing: 16) {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(version.version)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
.fontWeight(.medium)
|
||||
.lineLimit(1)
|
||||
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: getPlatformIcon(version.platform))
|
||||
.foregroundColor(.secondary)
|
||||
Text(getPlatformDisplayName(version.platform))
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
if version.exists {
|
||||
Text("已下载")
|
||||
.font(.caption)
|
||||
.foregroundColor(.green)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color.green.opacity(0.1))
|
||||
.cornerRadius(4)
|
||||
}
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 12)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.buttonStyle(.plain)
|
||||
.background(Color(NSColor.controlBackgroundColor).opacity(0.01))
|
||||
.cornerRadius(8)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 2)
|
||||
Divider()
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
.frame(width: 300, height: 400)
|
||||
.frame(width: 360, height: 400)
|
||||
.background(Color(NSColor.windowBackgroundColor))
|
||||
}
|
||||
|
||||
private func getPlatformDisplayName(_ platform: String) -> String {
|
||||
@@ -78,6 +124,19 @@ struct VersionPickerView: View {
|
||||
return platform
|
||||
}
|
||||
}
|
||||
|
||||
private func getPlatformIcon(_ platform: String) -> String {
|
||||
switch platform {
|
||||
case "macuniversal":
|
||||
return "cpu"
|
||||
case "macarm64":
|
||||
return "memorychip"
|
||||
case "osx10-64", "osx10":
|
||||
return "desktopcomputer"
|
||||
default:
|
||||
return "questionmark.circle"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
@@ -95,6 +154,22 @@ struct VersionPickerView: View {
|
||||
apPlatform: "macuniversal",
|
||||
dependencies: [],
|
||||
buildGuid: ""
|
||||
),
|
||||
"24.6.0": Product.ProductVersion(
|
||||
sapCode: "PHSP",
|
||||
baseVersion: "24.6.0",
|
||||
productVersion: "24.6.0",
|
||||
apPlatform: "macuniversal",
|
||||
dependencies: [],
|
||||
buildGuid: ""
|
||||
),
|
||||
"24.5.0": Product.ProductVersion(
|
||||
sapCode: "PHSP",
|
||||
baseVersion: "24.5.0",
|
||||
productVersion: "24.5.0",
|
||||
apPlatform: "macuniversal",
|
||||
dependencies: [],
|
||||
buildGuid: ""
|
||||
)
|
||||
],
|
||||
icons: []
|
||||
|
||||
@@ -65,9 +65,15 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Github: Adobe Downloader" : {
|
||||
|
||||
},
|
||||
"Released under GPLv3." : {
|
||||
|
||||
},
|
||||
"Thanks Drovosek01: adobe-packager" : {
|
||||
|
||||
},
|
||||
"Welcome to Adobe Downloader" : {
|
||||
|
||||
@@ -125,6 +131,12 @@
|
||||
},
|
||||
"尝试使用不同的搜索关键词" : {
|
||||
|
||||
},
|
||||
"尝试其他搜索关键词" : {
|
||||
|
||||
},
|
||||
"已下载" : {
|
||||
|
||||
},
|
||||
"已完成" : {
|
||||
|
||||
@@ -134,6 +146,9 @@
|
||||
},
|
||||
"搜索应用" : {
|
||||
|
||||
},
|
||||
"搜索语言" : {
|
||||
|
||||
},
|
||||
"是否要安装 %@?" : {
|
||||
|
||||
@@ -143,6 +158,9 @@
|
||||
},
|
||||
"服务器响应无效" : {
|
||||
"comment" : "Invalid response"
|
||||
},
|
||||
"未找到语言" : {
|
||||
|
||||
},
|
||||
"正在下载 %@ (%d/%d)" : {
|
||||
"comment" : "Download status downloading",
|
||||
|
||||
Reference in New Issue
Block a user