made the theme picker ui pretty

boxes now touch and dont have spacing between
vertical layout
context menu actually targets correct theme
the themebox is flexible and can be smaller/larger doesnt matter
This commit is contained in:
neon443
2025-06-29 13:42:05 +01:00
parent 24ca75f8c6
commit 870ebb6fcc
4 changed files with 136 additions and 118 deletions

View File

@@ -5,7 +5,7 @@
<key>id</key> <key>id</key>
<string>67A5D068-DC42-4C24-8058-775D7D24F8FE</string> <string>67A5D068-DC42-4C24-8058-775D7D24F8FE</string>
<key>name</key> <key>name</key>
<string>iTerm2SolarizedDar</string> <string>solarizedDark</string>
<key>Ansi 0 Color</key> <key>Ansi 0 Color</key>
<dict> <dict>
<key>Alpha Component</key> <key>Alpha Component</key>

View File

@@ -5,7 +5,7 @@
<key>id</key> <key>id</key>
<string>262CB71A-C253-4A78-959E-6E080A458EBB</string> <string>262CB71A-C253-4A78-959E-6E080A458EBB</string>
<key>name</key> <key>name</key>
<string>iTerm2SolarizedLight</string> <string>solarizedLight</string>
<key>Ansi 0 Color</key> <key>Ansi 0 Color</key>
<dict> <dict>
<key>Alpha Component</key> <key>Alpha Component</key>

View File

@@ -19,102 +19,76 @@ struct ThemeManagerView: View {
@State var rename: String = "" @State var rename: String = ""
let grid: GridItem = GridItem( let grid: GridItem = GridItem(
.fixed(90), .flexible(minimum: 100, maximum: 200),
spacing: 8, spacing: 8,
alignment: .center alignment: .center
) )
var body: some View { var body: some View {
NavigationStack { ScrollView {
List { if hostsManager.themes.isEmpty {
Section("Your Themes") { VStack(alignment: .leading) {
if hostsManager.themes.isEmpty { Image(systemName: "paintpalette")
VStack(alignment: .leading) { .resizable().scaledToFit()
Image(systemName: "paintpalette") .symbolRenderingMode(.multicolor)
.resizable().scaledToFit() .frame(width: 50)
.symbolRenderingMode(.multicolor) Text("No themes (yet)")
.frame(width: 50) .font(.title)
Text("No themes (yet)") .padding(.vertical, 10)
.font(.title)
.padding(.vertical, 10)
.bold()
Text("Tap the Safari icon at the top right to find themes!")
Text("Once you find one that you like, copy it's link and enter it here using the link button.")
}
} else {
ScrollView(.horizontal) {
LazyHGrid(rows: [grid, grid], alignment: .center, spacing: 8) {
ForEach(hostsManager.themes) { theme in
ThemePreview(hostsManager: hostsManager, theme: theme)
.contextMenu {
Button() {
themeToRename = theme
rename = theme.name
showRenameAlert.toggle()
} label: {
Label("Rename", systemImage: "pencil")
}
Button(role: .destructive) {
hostsManager.deleteTheme(theme)
} label: {
Label("Delete", systemImage: "trash")
}
}
}
}
.animation(.default, value: hostsManager.themes)
.alert("", isPresented: $showRenameAlert) {
TextField("", text: $rename)
Button("OK") {
hostsManager.renameTheme(themeToRename, to: rename)
rename = ""
}
}
}
.fixedSize(horizontal: false, vertical: true)
.scrollIndicators(.hidden)
}
}
Section("Builtin Themes") {
ScrollView(.horizontal) {
LazyHGrid(rows: [grid, grid], alignment: .center, spacing: 8) {
ForEach(Theme.builtinThemes) { theme in
ThemePreview(hostsManager: hostsManager, theme: theme)
}
}
.animation(.default, value: hostsManager.themes)
}
.scrollIndicators(.hidden)
.fixedSize(horizontal: false, vertical: true)
}
}
.navigationTitle("Themes")
.alert("Enter URL", isPresented: $showAlert) {
TextField("", text: $importURL, prompt: Text("URL"))
Button("Cancel") {}
Button() {
hostsManager.downloadTheme(fromUrl: URL(string: importURL))
importURL = ""
} label: {
Label("Import", systemImage: "square.and.arrow.down")
.bold() .bold()
Text("Tap the Safari icon at the top right to find themes!")
Text("Once you find one that you like, copy it's link and enter it here using the link button.")
}
} else {
LazyVGrid(columns: [grid, grid], alignment: .center, spacing: 8) {
ForEach(hostsManager.themes) { theme in
ThemePreview(hostsManager: hostsManager, theme: theme, canModify: true)
}
}
.padding(.horizontal)
.animation(.default, value: hostsManager.themes)
}
HStack {
Text("Built-in Themes")
.padding(.top)
.padding(.horizontal)
.font(.headline)
Spacer()
}
LazyVGrid(columns: [grid, grid], alignment: .center, spacing: 8) {
ForEach(Theme.builtinThemes) { theme in
ThemePreview(hostsManager: hostsManager, theme: theme, canModify: false)
} }
} }
.toolbar { .padding(.horizontal)
ToolbarItem() { .animation(.default, value: hostsManager.themes)
Button() { }
UIApplication.shared.open(URL(string: "https://iterm2colorschemes.com")!) .navigationTitle("Themes")
} label: { .alert("Enter URL", isPresented: $showAlert) {
Label("Open themes site", systemImage: "safari") TextField("", text: $importURL, prompt: Text("URL"))
} Button("Cancel") {}
Button() {
hostsManager.downloadTheme(fromUrl: URL(string: importURL))
importURL = ""
} label: {
Label("Import", systemImage: "square.and.arrow.down")
.bold()
}
}
.toolbar {
ToolbarItem() {
Button() {
UIApplication.shared.open(URL(string: "https://iterm2colorschemes.com")!)
} label: {
Label("Open themes site", systemImage: "safari")
} }
ToolbarItem() { }
Button() { ToolbarItem() {
showAlert.toggle() Button() {
} label: { showAlert.toggle()
Label("From URL", systemImage: "link") } label: {
} Label("From URL", systemImage: "link")
} }
} }
} }
@@ -122,8 +96,10 @@ struct ThemeManagerView: View {
} }
#Preview { #Preview {
ThemeManagerView( NavigationStack {
hostsManager: HostsManager(), ThemeManagerView(
importURL: "https://raw.githubusercontent.com/mbadolato/iTerm2-Color-Schemes/master/schemes/catppuccin-frappe.itermcolors" hostsManager: HostsManager(),
) importURL: "https://raw.githubusercontent.com/mbadolato/iTerm2-Color-Schemes/master/schemes/catppuccin-frappe.itermcolors"
)
}
} }

View File

@@ -10,56 +10,95 @@ import SwiftUI
struct ThemePreview: View { struct ThemePreview: View {
@ObservedObject var hostsManager: HostsManager @ObservedObject var hostsManager: HostsManager
@State var theme: Theme @State var theme: Theme
@State var canModify: Bool
@State private var showRenameAlert: Bool = false
@State private var rename: String = ""
var isSelected: Bool { var isSelected: Bool {
return hostsManager.isThemeSelected(theme) return hostsManager.isThemeSelected(theme)
} }
var body: some View { var body: some View {
let padding: CGFloat = 10
let innerPadding: CGFloat = 5
let outerR: CGFloat = 15
var paletteR: CGFloat {
outerR-padding
}
var selectionR: CGFloat {
outerR-innerPadding
}
ZStack(alignment: .center) { ZStack(alignment: .center) {
Rectangle() Rectangle()
.fill(Color.accentColor) .fill(Color.accentColor)
Rectangle() Rectangle()
.fill(theme.background.suiColor) .fill(theme.background.suiColor)
.frame(
width: isSelected ? 190 : 200,
height: isSelected ? 80 : 90
)
.clipShape( .clipShape(
RoundedRectangle( RoundedRectangle(
cornerRadius: isSelected ? 5 : 10 cornerRadius: isSelected ? selectionR : 0
) )
) )
VStack(alignment: .leading) { .padding(isSelected ? innerPadding : 0)
VStack {
Text(theme.name) Text(theme.name)
.foregroundStyle(theme.foreground.suiColor) .foregroundStyle(theme.foreground.suiColor)
.font(.headline) .font(.headline)
.lineLimit(1)
Spacer() Spacer()
HStack(spacing: 8) {
ForEach(0..<8, id: \.self) { index in VStack(spacing: 0) {
RoundedRectangle(cornerRadius: 2) HStack(spacing: 0) {
.frame(width: 16, height: 16) ForEach(0..<8, id: \.self) { index in
.foregroundStyle(theme.ansi[index].suiColor) Rectangle()
} .aspectRatio(CGSize(width: 1, height: 1), contentMode: .fit)
} .foregroundStyle(theme.ansi[index].suiColor)
HStack(spacing: 8) { }
ForEach(8..<16, id: \.self) { index in }
RoundedRectangle(cornerRadius: 2)
.frame(width: 16, height: 16) HStack(spacing: 0) {
.foregroundStyle(theme.ansi[index].suiColor) ForEach(8..<16, id: \.self) { index in
Rectangle()
.aspectRatio(CGSize(width: 1, height: 1), contentMode: .fit)
.foregroundStyle(theme.ansi[index].suiColor)
}
} }
} }
.clipShape(RoundedRectangle(cornerRadius: paletteR))
} }
.padding(8) .padding(padding)
} }
.frame(maxWidth: 200, maxHeight: 90)
.clipShape(RoundedRectangle(cornerRadius: 10))
.animation(.spring, value: isSelected)
.onTapGesture { .onTapGesture {
hostsManager.selectTheme(theme) hostsManager.selectTheme(theme)
} }
} .animation(.spring, value: isSelected)
.clipShape(RoundedRectangle(cornerRadius: outerR))
.contextMenu {
if canModify {
Button() {
rename = theme.name
showRenameAlert.toggle()
} label: {
Label("Rename", systemImage: "pencil")
}
Button(role: .destructive) {
hostsManager.deleteTheme(theme)
} label: {
Label("Delete", systemImage: "trash")
}
}
}
.alert("Rename \(theme.name)", isPresented: $showRenameAlert) {
TextField("", text: $rename)
Button("OK") {
hostsManager.renameTheme(theme, to: rename)
rename = ""
}
}
}
} }
#Preview { #Preview {
@@ -68,6 +107,9 @@ struct ThemePreview: View {
ThemePreview( ThemePreview(
hostsManager: HostsManager(), hostsManager: HostsManager(),
theme: Theme.decodeTheme(data: data)! theme: Theme.decodeTheme(data: data)!,
canModify: true
) )
.border(Color.red)
.scaleEffect(2)
} }