From 9fe528e3f188118b9c5a854b1073c43478c1f867 Mon Sep 17 00:00:00 2001 From: Jason Morley Date: Thu, 19 Jan 2023 14:26:39 +0000 Subject: [PATCH] fix: Show the license alongside the currently selected symbol (#125) This would benefit from some refactoring of the existing code, but doesn't attempt to make any of those changes to minimise the amount of change required. --- Symbolic.xcodeproj/project.pbxproj | 4 + Symbolic/Views/LicenseView.swift | 73 +++++++++++++++++++ Symbolic/Views/SettingsView.swift | 3 - Symbolic/Views/SymbolPicker.swift | 113 +++++++++++++++++++---------- 4 files changed, 150 insertions(+), 43 deletions(-) create mode 100644 Symbolic/Views/LicenseView.swift diff --git a/Symbolic.xcodeproj/project.pbxproj b/Symbolic.xcodeproj/project.pbxproj index 12d167e907..3db6901374 100644 --- a/Symbolic.xcodeproj/project.pbxproj +++ b/Symbolic.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ D8CD4A9A28AC35C600D57F34 /* SymbolicUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CD4A9928AC35C600D57F34 /* SymbolicUITests.swift */; }; D8CD4A9C28AC35C600D57F34 /* SymbolicUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CD4A9B28AC35C600D57F34 /* SymbolicUITestsLaunchTests.swift */; }; D8D9731128ACF8D2006CBF15 /* ExportToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D9731028ACF8D2006CBF15 /* ExportToolbar.swift */; }; + D8E4C3C12979809D00A25DF9 /* LicenseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E4C3C02979809D00A25DF9 /* LicenseView.swift */; }; D8F6317428AC7D7A00DFEED0 /* SFSymbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F6317328AC7D7A00DFEED0 /* SFSymbols.swift */; }; D8F6317628AC85C600DFEED0 /* SymbolPickerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F6317528AC85C600DFEED0 /* SymbolPickerModel.swift */; }; D8F99EC328B13955007C3868 /* diligence-license in Resources */ = {isa = PBXBuildFile; fileRef = D8F99EC228B13955007C3868 /* diligence-license */; }; @@ -137,6 +138,7 @@ D8CD4A9928AC35C600D57F34 /* SymbolicUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolicUITests.swift; sourceTree = ""; }; D8CD4A9B28AC35C600D57F34 /* SymbolicUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolicUITestsLaunchTests.swift; sourceTree = ""; }; D8D9731028ACF8D2006CBF15 /* ExportToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportToolbar.swift; sourceTree = ""; }; + D8E4C3C02979809D00A25DF9 /* LicenseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicenseView.swift; sourceTree = ""; }; D8F6317328AC7D7A00DFEED0 /* SFSymbols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SFSymbols.swift; sourceTree = ""; }; D8F6317528AC85C600DFEED0 /* SymbolPickerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolPickerModel.swift; sourceTree = ""; }; D8F99EC028B138FE007C3868 /* diligence */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = diligence; sourceTree = ""; }; @@ -222,6 +224,7 @@ D83970E628B132C700282EE8 /* UnityScaleWindow.swift */, D826B5C3296B054000693D27 /* WatchGridView.swift */, D8CB5A472971C4B5002007C4 /* SVGImage.swift */, + D8E4C3C02979809D00A25DF9 /* LicenseView.swift */, ); path = Views; sourceTree = ""; @@ -549,6 +552,7 @@ D84868E8296ACC58009FDF32 /* Header.swift in Sources */, D8CD4A7E28AC35C300D57F34 /* SymbolicApp.swift in Sources */, D826B5C6296B09FC00693D27 /* Path.swift in Sources */, + D8E4C3C12979809D00A25DF9 /* LicenseView.swift in Sources */, D8B6130A293BB98100F29E0C /* ApplicationModel.swift in Sources */, D864A23C2965E239008A4261 /* MacIconView.swift in Sources */, D8CB5A3E2970F4D0002007C4 /* SymbolReference.swift in Sources */, diff --git a/Symbolic/Views/LicenseView.swift b/Symbolic/Views/LicenseView.swift new file mode 100644 index 0000000000..6da0a8aa49 --- /dev/null +++ b/Symbolic/Views/LicenseView.swift @@ -0,0 +1,73 @@ +// Copyright (c) 2022-2023 InSeven Limited +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import SwiftUI + +import Diligence + +struct LicenseView: View { + + private struct LayoutMetrics { + static let width = 400.0 + static let height = 500.0 + } + + var license: License + + public init(license: License) { + self.license = license + } + + public var body: some View { + ScrollView { + VStack { + HStack { + Text("Author") + Spacer() + Text(license.author) + .foregroundColor(.secondary) + } + Divider() + Text(license.text) + .multilineTextAlignment(.leading) + } + .padding() + } + .safeAreaInset(edge: .bottom) { + VStack(spacing: 0) { + Divider() + HStack { + Spacer() + Button("Copy") { + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(license.text, forType: .string) + } + } + .padding() + } + .background(Color.textBackgroundColor) + } + .background(Color.textBackgroundColor) + .navigationTitle(license.name) + .frame(width: LayoutMetrics.width, height: LayoutMetrics.height) + .foregroundColor(.primary) + } + +} diff --git a/Symbolic/Views/SettingsView.swift b/Symbolic/Views/SettingsView.swift index ff43651eda..a75554cd8b 100644 --- a/Symbolic/Views/SettingsView.swift +++ b/Symbolic/Views/SettingsView.swift @@ -35,9 +35,6 @@ struct SettingsView: View { Form { Section("Icon") { SymbolPicker("Image", selection: $document.icon.symbol.undoable(undoManager, context: undoContext)) - if let set = SymbolManager.shared.set(for: document.icon.symbol) { - LabeledContent("Author", value: set.author) - } Slider(value: $document.icon.iconScale.undoable(undoManager, context: undoContext), in: 0...1.2) { Text("Size") } diff --git a/Symbolic/Views/SymbolPicker.swift b/Symbolic/Views/SymbolPicker.swift index d3e03bea21..2cc60617c9 100644 --- a/Symbolic/Views/SymbolPicker.swift +++ b/Symbolic/Views/SymbolPicker.swift @@ -20,6 +20,7 @@ import SwiftUI +import Diligence import Interact struct SymbolPicker: View { @@ -50,55 +51,60 @@ struct SymbolPicker: View { var body: some View { LabeledContent("Symbol") { - Button { - isPresented = true - } label: { - HStack { - SymbolView(symbol: selection.wrappedValue) + VStack(alignment: .trailing) { + Button { + isPresented = true + } label: { + HStack { + SymbolView(symbol: selection.wrappedValue) + } + .frame(width: LayoutMetrics.buttonSize.width, height: LayoutMetrics.buttonSize.height) } - .frame(width: LayoutMetrics.buttonSize.width, height: LayoutMetrics.buttonSize.height) - } - .controlSize(.large) - .popover(isPresented: $isPresented) { + .controlSize(.large) + .popover(isPresented: $isPresented) { - VStack(spacing: 0) { - TextField(text: $model.filter, prompt: Text("Search")) { - EmptyView() - } - .multilineTextAlignment(.leading) - .textFieldStyle(.roundedBorder) - .padding() - .background(Color(nsColor: NSColor.controlBackgroundColor)) + VStack(spacing: 0) { + TextField(text: $model.filter, prompt: Text("Search")) { + EmptyView() + } + .multilineTextAlignment(.leading) + .textFieldStyle(.roundedBorder) + .padding() + .background(Color(nsColor: NSColor.controlBackgroundColor)) - ScrollView { - LazyVGrid(columns: columns, - spacing: LayoutMetrics.interItemSpacing, - pinnedViews: [.sectionHeaders]) { - ForEach(model.filteredSymbols) { section in - Section { - ForEach(section.symbols) { symbol in - SymbolView(symbol: symbol.reference) - .symbolPickerCell(isHighlighted: selection.wrappedValue == symbol.reference) - .onTapGesture { - isPresented = false - selection.wrappedValue = symbol.reference - } - .help(symbol.localizedDescription) + ScrollView { + LazyVGrid(columns: columns, + spacing: LayoutMetrics.interItemSpacing, + pinnedViews: [.sectionHeaders]) { + ForEach(model.filteredSymbols) { section in + Section { + ForEach(section.symbols) { symbol in + SymbolView(symbol: symbol.reference) + .symbolPickerCell(isHighlighted: selection.wrappedValue == symbol.reference) + .onTapGesture { + isPresented = false + selection.wrappedValue = symbol.reference + } + .help(symbol.localizedDescription) + } + } header: { + Text(section.name) + .textCase(.uppercase) + .horizontalSpace(.trailing) + .padding([.top, .bottom], LayoutMetrics.sectionHeaderVerticalPadding) + .background(Color(nsColor: NSColor.controlBackgroundColor)) } - } header: { - Text(section.name) - .textCase(.uppercase) - .horizontalSpace(.trailing) - .padding([.top, .bottom], LayoutMetrics.sectionHeaderVerticalPadding) - .background(Color(nsColor: NSColor.controlBackgroundColor)) } } + .padding([.leading, .trailing, .bottom]) } - .padding([.leading, .trailing, .bottom]) } + .background(Color(nsColor: NSColor.controlBackgroundColor)) + .frame(height: LayoutMetrics.height) + } + if let set = SymbolManager.shared.set(for: selection.wrappedValue) { + SetDetails(set: set) } - .background(Color(nsColor: NSColor.controlBackgroundColor)) - .frame(height: LayoutMetrics.height) } } .onAppear { @@ -111,3 +117,30 @@ struct SymbolPicker: View { } } + +struct SetDetails: View { + + let set: SymbolSet + + @State var isPresented: Bool = false + + var body: some View { + HStack(spacing: 4.0) { + Text(set.name) + Button { + isPresented.toggle() + } label: { + Image(systemName: "info.circle") + } + .buttonStyle(.plain) + .popover(isPresented: $isPresented) { + if let licenseUrl = set.licenseUrl { + LicenseView(license: License(set.name, author: set.author, url: licenseUrl)) + } else { + LicenseView(license: License(set.name, author: set.author, text: "Unknown")) + } + } + } + } + +}