diff --git a/Csv2ImageApp/Csv2ImageApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Csv2ImageApp/Csv2ImageApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4285a54..a9a578f 100644 --- a/Csv2ImageApp/Csv2ImageApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Csv2ImageApp/Csv2ImageApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,34 +1,33 @@ { - "object": { - "pins": [ - { - "package": "swift-argument-parser", - "repositoryURL": "https://github.com/apple/swift-argument-parser", - "state": { - "branch": null, - "revision": "f3c9084a71ef4376f2fabbdf1d3d90a49f1fabdb", - "version": "1.1.2" - } - }, - { - "package": "SwiftDocCPlugin", - "repositoryURL": "https://github.com/apple/swift-docc-plugin", - "state": { - "branch": null, - "revision": "3303b164430d9a7055ba484c8ead67a52f7b74f6", - "version": "1.0.0" - } - }, - { - "package": "swift-syntax", - "repositoryURL": "https://github.com/apple/swift-syntax", - "state": { - "branch": null, - "revision": "6ad4ea24b01559dde0773e3d091f1b9e36175036", - "version": "509.0.2" - } + "originHash" : "b5a609958d4527ce07000982912bd4609927f45277d7cb706c744970b7051f59", + "pins" : [ + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser", + "state" : { + "revision" : "f3c9084a71ef4376f2fabbdf1d3d90a49f1fabdb", + "version" : "1.1.2" } - ] - }, - "version": 1 + }, + { + "identity" : "swift-docc-plugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-docc-plugin", + "state" : { + "revision" : "3303b164430d9a7055ba484c8ead67a52f7b74f6", + "version" : "1.0.0" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax", + "state" : { + "revision" : "2bc86522d115234d1f588efe2bcb4ce4be8f8b82", + "version" : "510.0.3" + } + } + ], + "version" : 3 } diff --git a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+iOS.swift b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+iOS.swift index a168481..76d3a0c 100644 --- a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+iOS.swift +++ b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+iOS.swift @@ -24,125 +24,78 @@ import SwiftUI ] var body: some View { - NavigationStack { - loadedContent.id(model.state.isLoading) - .toolbar { - ToolbarItem(placement: .primaryAction) { - Button("Save") { - succeedSavingOutput = model.save() - } - } - } - .toolbar { - ToolbarItem(placement: .topBarLeading) { - Button("Back") { - backToPreviousPage = true - } - } - } - .alert("Complete Saving!", isPresented: $succeedSavingOutput) { - CButton.labeled("Back") { - withAnimation { - backToPreviousPage = true - } + NavigationView { + Form { + Section(header: Text("Export Settings")) { + Picker("Export Type", selection: Binding( + get: { model.state.exportType }, + set: { model.update(keyPath: \.exportType, value: $0) } + )) { + Text("PDF").tag(Csv.ExportType.pdf) + Text("PNG").tag(Csv.ExportType.png) } - if let savedURL = model.savedURL, Application.shared.canOpenURL(savedURL) { - CButton.labeled("Open") { - Application.shared.open(savedURL) - } - } - } - } - } + .pickerStyle(SegmentedPickerStyle()) - var loadedContent: some View { - VStack { - List { - Section("Export Type") { - Picker( - selection: Binding( - get: { - model.state.exportType - }, - set: { exportType in - model.update(keyPath: \.exportType, value: exportType) - }) - ) { - CText("PDF") - .tag(Csv.ExportType.pdf) - CText("PNG") - .tag(Csv.ExportType.png) - } label: { - EmptyView() - } - .pickerStyle(.segmented) - } - Section("Encoding") { - Menu(model.state.encoding.description) { + Picker("Encoding", selection: Binding( + get: { model.state.encoding }, + set: { model.update(keyPath: \.encoding, value: $0) } + )) { ForEach(availableEncodingType, id: \.self) { encoding in - Button { - model.update(keyPath: \.encoding, value: encoding) - } label: { - Text(encoding.description) - } + Text(encoding.description).tag(encoding) } } - .fixedSize() - } - Section("PDF Size") { - Menu(model.state.size.rawValue) { - ForEach(PdfSize.allCases.indices, id: \.self) { index in - let size = PdfSize.allCases[index] - Button { - model.update(keyPath: \.size, value: size) - } label: { - Text(size.rawValue) - } + + Picker("PDF Size", selection: Binding( + get: { model.state.size }, + set: { model.update(keyPath: \.size, value: $0) } + )) { + ForEach(PdfSize.allCases, id: \.self) { size in + Text(size.rawValue).tag(size) } } - .fixedSize() - } - Section("PDF Orientation") { - Menu(model.state.orientation.rawValue) { - ForEach(PdfSize.Orientation.allCases.indices, id: \.self) { index in - let orientation = PdfSize.Orientation.allCases[index] - Button { - model.update(keyPath: \.orientation, value: orientation) - } label: { - Text(orientation.rawValue) - } + + Picker("PDF Orientation", selection: Binding( + get: { model.state.orientation }, + set: { model.update(keyPath: \.orientation, value: $0) } + )) { + ForEach(PdfSize.Orientation.allCases, id: \.self) { orientation in + Text(orientation.rawValue).tag(orientation) } } - .fixedSize() } - } - .background(Asset.lightAccentColor.swiftUIColor) - .frame(maxHeight: 200) - GeometryReader { proxy in - VStack(alignment: .center) { + Section(header: Text("Preview")) { GeneratePreviewView( model: model, - size: .constant( - CGSize( - width: proxy.size.width, - height: proxy.size.height - ) - ) + size: .constant(CGSize(width: UIScreen.main.bounds.width - 32, height: 300)) ) + .frame(height: 300) + .background(Asset.lightAccentColor.swiftUIColor) + .cornerRadius(8) } - } - .background(Asset.lightAccentColor.swiftUIColor) + .navigationBarTitle("Generate Output", displayMode: .inline) + .navigationBarItems( + leading: Button("Back") { backToPreviousPage = true }, + trailing: Button("Save") { succeedSavingOutput = model.save() } + ) } - } - - var loadingContent: some View { - ProgressView { - CText("Loading...", font: .largeTitle) + .alert(isPresented: $succeedSavingOutput) { + Alert( + title: Text("Complete Saving!"), + message: nil, + primaryButton: .default(Text("Back")) { + withAnimation { + backToPreviousPage = true + } + }, + secondaryButton: .default(Text("Open")) { + if let savedURL = model.savedURL, Application.shared.canOpenURL(savedURL) { + Application.shared.open(savedURL) + } + } + ) } - .padding() - .progressViewStyle(.linear) } } #endif diff --git a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+macOS.swift b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+macOS.swift index f2a9c78..548138f 100644 --- a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+macOS.swift +++ b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+macOS.swift @@ -26,25 +26,56 @@ import SwiftUI @Environment(\.dismiss) var dismiss var body: some View { - VStack { - GeneratePreviewView( - model: model, - size: .constant( - .init( - width: 320, - height: 240 + NavigationView { + HSplitView { + List { + Section("Settings") { + Picker("Encoding", selection: $model.encoding) { + ForEach(availableEncodingType, id: \.self) { encoding in + Text(encoding.description).tag(encoding) + } + } + // Add more settings here as needed + } + } + .listStyle(SidebarListStyle()) + .frame(minWidth: 200, idealWidth: 250, maxWidth: 300) + + VStack { + GeneratePreviewView( + model: model, + size: .constant( + .init( + width: 480, + height: 360 + ) + ) ) - ) - ) + .frame(maxWidth: .infinity, maxHeight: .infinity) + + HStack { + Button("Generate") { + // Add generation logic here + } + .keyboardShortcut(.defaultAction) + + Button("Save As...") { + // Add save logic here + } + } + .padding() + } + } } + .navigationTitle("Generate Output") .toolbar { - CButton.labeled( - "cancel", - onPressed: { + ToolbarItem(placement: .cancellationAction) { + Button("Cancel") { dismiss() } - ) + } } + .frame(minWidth: 800, minHeight: 600) } } diff --git a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GeneratePreviewView.swift b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GeneratePreviewView.swift index f15829d..6d988ea 100644 --- a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GeneratePreviewView.swift +++ b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GeneratePreviewView.swift @@ -20,43 +20,41 @@ struct GeneratePreviewView: View { #if os(iOS) var body: some View { - Group { - if let cgImage = model.state.cgImage, model.state.exportType == .png { - let image = UIImage(cgImage: cgImage) - ScrollView { - ScrollView( - .horizontal, - content: { - Image(uiImage: image) - .resizable() - .aspectRatio(contentMode: .fit) - }) + GeometryReader { geometry in + Group { + if let cgImage = model.state.cgImage, model.state.exportType == .png { + let image = UIImage(cgImage: cgImage) + ScrollView([.vertical, .horizontal], showsIndicators: true) { + Image(uiImage: image) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: geometry.size.width, height: geometry.size.height) + } + } else if let document = model.state.pdfDocument, model.state.exportType == .pdf { + PdfDocumentView(document: document, size: $size) } - .frame(width: size.width, height: size.height) - } else if let document = model.state.pdfDocument, model.state.exportType == .pdf { - PdfDocumentView(document: document, size: _size) } } + .edgesIgnoringSafeArea(.all) } #elseif os(macOS) var body: some View { - Group { - if let cgImage = model.state.cgImage, model.state.exportType == .png { - let image = NSImage( - cgImage: cgImage, - size: CGSize(width: cgImage.width, height: cgImage.height) - ) - ScrollView(content: { - ScrollView( - .horizontal, - content: { - Image(nsImage: image) - .resizable() - .aspectRatio(contentMode: .fit) - }) - }) - } else if let document = model.state.pdfDocument, model.state.exportType == .pdf { - PdfDocumentView(document: document, size: _size) + GeometryReader { geometry in + Group { + if let cgImage = model.state.cgImage, model.state.exportType == .png { + let image = NSImage( + cgImage: cgImage, + size: CGSize(width: cgImage.width, height: cgImage.height) + ) + ScrollView([.vertical, .horizontal], showsIndicators: true) { + Image(nsImage: image) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: geometry.size.width, height: geometry.size.height) + } + } else if let document = model.state.pdfDocument, model.state.exportType == .pdf { + PdfDocumentView(document: document, size: $size) + } } } } diff --git a/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+iOS.swift b/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+iOS.swift index 3ecc318..95ece99 100644 --- a/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+iOS.swift +++ b/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+iOS.swift @@ -13,41 +13,46 @@ import SwiftUI @StateObject var model: SelectCsvModel var body: some View { - BrandingFrameView { - VStack { - Spacer() - CButton.labeled("Select Csv File") { - Task { - await model.selectFileOnDisk() + NavigationView { + Form { + Section(header: Text("Select CSV File")) { + Button(action: { + Task { + await model.selectFileOnDisk() + } + }) { + Label("Choose File", systemImage: "doc") } } - Divider().padding() - CText("Alternately, please input csv file url on the Internet.") - HStack { - TextField("example: https://bit.ly/3c2leMC", text: $model.networkUrlText) - .textFieldStyle(.roundedBorder) - Spacer() - if !model.networkUrlText.isEmpty { - CButton.icon(systemName: "xmark") { - model.networkUrlText = "" + + Section(header: Text("Or Enter URL")) { + HStack { + TextField("https://example.com/file.csv", text: $model.networkUrlText) + if !model.networkUrlText.isEmpty { + Button(action: { + model.networkUrlText = "" + }) { + Image(systemName: "xmark.circle.fill") + .foregroundColor(.secondary) + } } } - CButton.labeled("OK") { + Button("Load from URL") { Task { await model.selectFileOnTheInternet() } } - Spacer().frame(width: 16) } - Spacer() - Divider() - CText("Saved data is stored in Folder App.", isBold: true) - CButton.labeled("Open Folder App") { - model.openFolderApp() + + Section(footer: Text("Saved data is stored in Folder App.").font(.footnote)) { + Button(action: { + model.openFolderApp() + }) { + Label("Open Folder App", systemImage: "folder") + } } - Spacer().frame(height: 40) } - .padding() + .navigationTitle("Select CSV") } .alert( "Error", isPresented: $model.error.isNotNil(), diff --git a/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+macOS.swift b/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+macOS.swift index b4b6edf..5666015 100644 --- a/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+macOS.swift +++ b/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+macOS.swift @@ -9,57 +9,73 @@ import SwiftUI import UniformTypeIdentifiers #if os(macOS) - struct SelectCsvView_macOS: View { +struct SelectCsvView_macOS: View { + @State private var isTargeted: Bool = false + @StateObject var model: SelectCsvModel - @State private var isTargeted: Bool = false - @StateObject var model: SelectCsvModel - - var body: some View { - BrandingFrameView { - VStack { - CText("Drop csv file here", font: .largeTitle) - - Spacer().frame(height: 32) - CButton.labeled("Alternatively, Choose from Finder") { - Task { - await model.selectFileOnDisk() - } + var body: some View { + BrandingFrameView { + VStack(spacing: 20) { + Image(systemName: "doc.text") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 60, height: 60) + .foregroundColor(.secondary) + + Text("Drop CSV File Here") + .font(.system(size: 24, weight: .medium)) + + Text("or") + .font(.system(size: 16, weight: .regular)) + .foregroundColor(.secondary) + + Button("Choose from Finder") { + Task { + await model.selectFileOnDisk() } } + .buttonStyle(.bordered) + } + .padding(40) + .background( + RoundedRectangle(cornerRadius: 12) + .stroke(Color.secondary.opacity(0.2), lineWidth: 2) + .background(Color.secondary.opacity(0.05)) + ) + } + .frame(minWidth: 400, minHeight: 300) + .onDrop(of: [.fileURL], isTargeted: $isTargeted) { providers in + guard let provider = providers.first else { + return false } - .onDrop(of: [.fileURL], isTargeted: $isTargeted) { providers in - guard let provider = providers.first else { - return false + provider.loadItem(forTypeIdentifier: UTType.fileURL.identifier, options: nil) { data, error in + if let error = error { + print(error) + return } - provider.loadItem(forTypeIdentifier: UTType.fileURL.identifier, options: nil) { - data, error in - if let error = error { - print(error) - return - } - guard let data = data as? Data, - let url = URL(dataRepresentation: data, relativeTo: nil, isAbsolute: true) - else { - return - } - if url.lastPathComponent.contains(".csv") { - DispatchQueue.main.async { - withAnimation { - model.selectedCsv = SelectedCsvState(fileType: .local, url: url) - } + guard let data = data as? Data, + let url = URL(dataRepresentation: data, relativeTo: nil, isAbsolute: true) + else { + return + } + if url.pathExtension.lowercased() == "csv" { + DispatchQueue.main.async { + withAnimation { + model.selectedCsv = SelectedCsvState(fileType: .local, url: url) } } } - return true } + return true } } +} - struct SelectCsvView_macOS_Previews: PreviewProvider { - static var previews: some View { - SelectCsvView_macOS( - model: SelectCsvModel() - ) - } +struct SelectCsvView_macOS_Previews: PreviewProvider { + static var previews: some View { + SelectCsvView_macOS( + model: SelectCsvModel() + ) } +} #endif