diff --git a/Snip.xcodeproj/project.pbxproj b/Snip.xcodeproj/project.pbxproj index dbffc6a..0752a5c 100644 --- a/Snip.xcodeproj/project.pbxproj +++ b/Snip.xcodeproj/project.pbxproj @@ -38,6 +38,7 @@ 796B093524D1B95B006904C4 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796B093424D1B95B006904C4 /* Color.swift */; }; 796B093724D1BA1F006904C4 /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796B093624D1BA1F006904C4 /* Date.swift */; }; 797B92EC24FCDE2900071669 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 797B92EB24FCDE2900071669 /* Colors.xcassets */; }; + 798AA66725500FE300CBDC9C /* Tooltip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 798AA66625500FE300CBDC9C /* Tooltip.swift */; }; 79956A4224E5463300B823E1 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79956A4124E5463300B823E1 /* API.swift */; }; 79956A4424E5772200B823E1 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79956A4324E5772200B823E1 /* Endpoint.swift */; }; 79956A4724E57AC700B823E1 /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79956A4624E57AC700B823E1 /* Dictionary.swift */; }; @@ -117,6 +118,7 @@ 796B093424D1B95B006904C4 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; 796B093624D1BA1F006904C4 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; 797B92EB24FCDE2900071669 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; + 798AA66625500FE300CBDC9C /* Tooltip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tooltip.swift; sourceTree = ""; }; 79956A4124E5463300B823E1 /* API.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = API.swift; sourceTree = ""; }; 79956A4324E5772200B823E1 /* Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = ""; }; 79956A4624E57AC700B823E1 /* Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = ""; }; @@ -415,6 +417,7 @@ 79F07A2824D3050500469324 /* ImageButton.swift */, 791AC9C424DAE45D00CAB87C /* DeferView.swift */, 79284D6C2507DE7F00210E61 /* PagerView.swift */, + 798AA66625500FE300CBDC9C /* Tooltip.swift */, ); path = Misc; sourceTree = ""; @@ -585,6 +588,7 @@ 79BC1D3324D17124008FD16E /* Sidebar.swift in Sources */, 79A6F64C24D20B8C0037C5FF /* CodeMirrorViewController.swift in Sources */, 79BC1D2024D16E67008FD16E /* AppDelegate.swift in Sources */, + 798AA66725500FE300CBDC9C /* Tooltip.swift in Sources */, 79A6F64824D206EE0037C5FF /* CodeViewConstants.swift in Sources */, 79A6F65324D2B9D40037C5FF /* String.swift in Sources */, 791AC9C524DAE45D00CAB87C /* DeferView.swift in Sources */, @@ -784,7 +788,7 @@ CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 8; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_ASSET_PATHS = "\"Snip/Preview Content\""; DEVELOPMENT_TEAM = 7XBGRFP286; ENABLE_HARDENED_RUNTIME = YES; @@ -795,7 +799,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.3; + MARKETING_VERSION = 1.4; PRODUCT_BUNDLE_IDENTIFIER = com.pictarine.Snip; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "Snip DEBUG"; @@ -811,7 +815,7 @@ CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 8; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_ASSET_PATHS = "\"Snip/Preview Content\""; DEVELOPMENT_TEAM = 7XBGRFP286; ENABLE_HARDENED_RUNTIME = YES; @@ -822,7 +826,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.3; + MARKETING_VERSION = 1.4; PRODUCT_BUNDLE_IDENTIFIER = com.pictarine.Snip; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "Snip APPSTORE"; diff --git a/Snip/Components/CodeViewer/CodeActionsTopBar.swift b/Snip/Components/CodeViewer/CodeActionsTopBar.swift index db0ee86..9687d4a 100644 --- a/Snip/Components/CodeViewer/CodeActionsTopBar.swift +++ b/Snip/Components/CodeViewer/CodeActionsTopBar.swift @@ -33,10 +33,10 @@ struct CodeActionsTopBar: View { self.viewModel.onRename($0) }) ) - .font(Font.custom("HelveticaNeue", size: 20)) - .foregroundColor(themeTextColor) - .frame(maxHeight: .infinity) - .textFieldStyle(PlainTextFieldStyle()) + .font(Font.custom("HelveticaNeue", size: 20)) + .foregroundColor(themeTextColor) + .frame(maxHeight: .infinity) + .textFieldStyle(PlainTextFieldStyle()) if syncManager.isAuthenticated { ZStack { @@ -51,7 +51,7 @@ struct CodeActionsTopBar: View { .animation(Animation.easeOut(duration: 1).repeatForever(autoreverses: false)) .onAppear() { self.moveRightLeft.toggle() - } + } } } else { @@ -63,7 +63,8 @@ struct CodeActionsTopBar: View { .fill(viewModel.syncState == .local ? Color.RED_500 : Color.green) .frame(width: 8, height: 8) .offset(x: 7, y: 6) - ) + ) + .tooltip("Add to Gist") } } @@ -73,6 +74,7 @@ struct CodeActionsTopBar: View { ImageButton(imageName: "ic_open", action: viewModel.openRemoteURL, content: { EmptyView() }) + .tooltip("Open snippet post") } if viewModel.onPreviewToggle != nil { @@ -83,26 +85,31 @@ struct CodeActionsTopBar: View { DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { self.isPreviewEnabled.toggle() } - }, + }, content: { EmptyView() }) + .tooltip("Show/Hide preview") } ImageButton(imageName: viewModel.isSnipFavorite ? "ic_fav_selected" : "ic_fav", action: viewModel.onToggleFavorite, content: { EmptyView() }) + .tooltip("Add to favorites") ImageButton(imageName: "ic_delete", action: viewModel.onDelete, content: { EmptyView() }) + .tooltip("Delete snippet") ImageButton(imageName: "ic_share", action: { self.showSharingActions = true - }, + }, content: { SharingsPicker(isPresented: self.$showSharingActions, sharingItems: ["\(self.viewModel.snipCode) \n\n - Shared via Snip https://cutt.ly/snip"]) - }) + }) + .tooltip("Share snippet") ImageButton(imageName: "ic_info", action: { self.showInfos.toggle() }, content: { EmptyView() }) + .tooltip("Snippet info") .popover( isPresented: self.$showInfos, arrowEdge: .bottom @@ -115,7 +122,7 @@ struct CodeActionsTopBar: View { Text("Last updated \(self.viewModel.snipLastUpdate.dateAndTimetoString())") .padding(.top) }.padding(16) - } + } } .background(themeSecondaryColor.opacity(0.4)) @@ -170,7 +177,7 @@ final class CodeActionsViewModel: ObservableObject { func openRemoteURL() { guard let sourceURL = remoteURL, - let url = URL(string: sourceURL) else { return } + let url = URL(string: sourceURL) else { return } NSWorkspace.shared.open(url) } } diff --git a/Snip/Components/Misc/Tooltip.swift b/Snip/Components/Misc/Tooltip.swift new file mode 100644 index 0000000..5216f46 --- /dev/null +++ b/Snip/Components/Misc/Tooltip.swift @@ -0,0 +1,60 @@ +// +// Tooltip.swift +// Snip +// +// Created by Anthony Fernandez on 11/2/20. +// Copyright © 2020 pictarine. All rights reserved. +// + +import Foundation +import SwiftUI + + +extension View { + func tooltip(_ tip: String) -> some View { + background(GeometryReader { childGeometry in + TooltipView(tip, geometry: childGeometry) { + self + } + }) + } +} + +private struct TooltipView: View where Content: View { + let content: () -> Content + let tip: String + let geometry: GeometryProxy + + init(_ tip: String, geometry: GeometryProxy, @ViewBuilder content: @escaping () -> Content) { + self.content = content + self.tip = tip + self.geometry = geometry + } + + var body: some View { + Tooltip(tip, content: content) + .frame(width: geometry.size.width, height: geometry.size.height) + } +} + +private struct Tooltip: NSViewRepresentable { + typealias NSViewType = NSHostingView + + init(_ text: String?, @ViewBuilder content: () -> Content) { + self.text = text + self.content = content() + } + + let text: String? + let content: Content + + func makeNSView(context _: Context) -> NSHostingView { + NSViewType(rootView: content) + } + + func updateNSView(_ nsView: NSHostingView, context _: Context) { + nsView.rootView = content + nsView.toolTip = text + } +} + diff --git a/Snip/Components/Sidebar/Sidebar.swift b/Snip/Components/Sidebar/Sidebar.swift index 4d8fa1d..aa9bf40 100644 --- a/Snip/Components/Sidebar/Sidebar.swift +++ b/Snip/Components/Sidebar/Sidebar.swift @@ -41,6 +41,7 @@ struct Sidebar: View { settings.isSettingsOpened.toggle() } }, content: { EmptyView() }) + .tooltip("Settings") Spacer() Text(syncManager.connectedUser?.login ?? "") @@ -68,6 +69,7 @@ struct Sidebar: View { ) } + .tooltip("Connect to Gist from GitHub") .alert(isPresented: $showingLogoutAlert) { Alert(title: Text("Logout from Github"), message: Text("Are you sure about that?"), diff --git a/Snip/Model/Mode.swift b/Snip/Model/Mode.swift index 8e3cf1a..0532b3d 100644 --- a/Snip/Model/Mode.swift +++ b/Snip/Model/Mode.swift @@ -133,6 +133,7 @@ public enum CodeMode: String { case lua case markdown case maths + case ntriples case pascal case perl case php @@ -148,10 +149,12 @@ public enum CodeMode: String { case shell case sql case sqllite + case sparql case mysql case latex case swift case text + case turtle case vb case vue case xml @@ -191,6 +194,7 @@ public enum CodeMode: String { CodeMode.lua.mode(), CodeMode.markdown.mode(), CodeMode.maths.mode(), + CodeMode.ntriples.mode(), CodeMode.pascal.mode(), CodeMode.perl.mode(), CodeMode.php.mode(), @@ -206,10 +210,12 @@ public enum CodeMode: String { CodeMode.shell.mode(), CodeMode.sql.mode(), CodeMode.sqllite.mode(), + CodeMode.sparql.mode(), CodeMode.mysql.mode(), CodeMode.latex.mode(), CodeMode.swift.mode(), CodeMode.text.mode(), + CodeMode.turtle.mode(), CodeMode.vb.mode(), CodeMode.vue.mode(), CodeMode.xml.mode(), @@ -289,6 +295,8 @@ public enum CodeMode: String { return Mode(name: "markdown", mimeType: "text/markdown") case .maths: return Mode(name: "maths", mimeType: "text/x-mathematica") + case .ntriples: + return Mode(name: "ntriples", mimeType: "application/n-triples") case .pascal: return Mode(name: "pascal", mimeType: "text/x-pascal") case .perl: @@ -319,6 +327,8 @@ public enum CodeMode: String { return Mode(name: "sql", mimeType: "text/x-sql") case .sqllite: return Mode(name: "sqllite", mimeType: "text/x-sqlite") + case .sparql: + return Mode(name: "sparql", mimeType: "application/sparql-query") case .mysql: return Mode(name: "mysql", mimeType: "text/x-mysql") case .latex: @@ -327,6 +337,8 @@ public enum CodeMode: String { return Mode(name: "swift", mimeType: "text/x-swift") case .text: return Mode(name: "text", mimeType: "text/plain-text") + case .turtle: + return Mode(name: "turtle", mimeType: "text/turtle") case .vb: return Mode(name: "vb", mimeType: "text/x-vb") case .vue: