Copy the link of the current page to paste board

This commit is contained in:
Balazs Perlaki-Horvath 2025-05-10 22:15:10 +02:00 committed by Kelson
parent 1fa79ec5dd
commit aaea5a87d0
7 changed files with 63 additions and 15 deletions

View File

@ -37,6 +37,7 @@ struct Kiwix: App {
@State private var selectedAmount: SelectedAmount? @State private var selectedAmount: SelectedAmount?
@StateObject var formReset = FormReset() @StateObject var formReset = FormReset()
@FocusState private var isSearchFocused: Bool @FocusState private var isSearchFocused: Bool
@FocusedValue(\.browserURL) var browserURL
init() { init() {
UNUserNotificationCenter.current().delegate = notificationCenterDelegate UNUserNotificationCenter.current().delegate = notificationCenterDelegate
@ -78,6 +79,16 @@ struct Kiwix: App {
SidebarNavigationCommands() SidebarNavigationCommands()
Divider() Divider()
} }
CommandGroup(after: .pasteboard) {
Button(LocalString.library_zim_file_context_copy_url) {
if let browserURL {
CopyPasteMenu.copyToPasteBoard(url: browserURL)
}
}
.disabled(browserURL == nil)
.keyboardShortcut("c", modifiers: [.command, .shift])
}
CommandGroup(after: .textEditing) { CommandGroup(after: .textEditing) {
Button(LocalString.common_search) { Button(LocalString.common_search) {
isSearchFocused = true isSearchFocused = true

View File

@ -39,6 +39,7 @@
"common.button.no" = "no"; "common.button.no" = "no";
"common.button.print" = "Print"; "common.button.print" = "Print";
"common.button.share" = "Share"; "common.button.share" = "Share";
"common.button.share_as_pdf" = "Share as PDF";
"common.search" = "Search"; "common.search" = "Search";
"common.tab.manager.title" = "Tabs Manager"; "common.tab.manager.title" = "Tabs Manager";

View File

@ -23,6 +23,7 @@
"common.button.no" = "It is a title in the summary table on macOS: we list the attributes of a ZIM file: does it contain pictures yes/no, does it contain Videos yes/no, Details? yes/no"; "common.button.no" = "It is a title in the summary table on macOS: we list the attributes of a ZIM file: does it contain pictures yes/no, does it contain Videos yes/no, Details? yes/no";
"common.button.print" = "Accessibility label for button to print the currently loaded article"; "common.button.print" = "Accessibility label for button to print the currently loaded article";
"common.button.share" = "Accessibility label for share button, to share the currently loaded article with an external app."; "common.button.share" = "Accessibility label for share button, to share the currently loaded article with an external app.";
"common.button.share_as_pdf" = "Accesibility label for share button. to share the curretly loadded article with an external application in a PDF file format.";
"common.search" = "The default placeholder text for searchbars, when the search input field is empty"; "common.search" = "The default placeholder text for searchbars, when the search input field is empty";
"common.tab.manager.title" = "Accessibility label for button tab bar button that opens an overlay menu."; "common.tab.manager.title" = "Accessibility label for button tab bar button that opens an overlay menu.";
"common.tab.navigation.title" = "On iPad it is a title in the sidemenu grouping tabs related buttons"; "common.tab.navigation.title" = "On iPad it is a title in the sidemenu grouping tabs related buttons";

View File

@ -44,11 +44,6 @@ struct BrowserTab: View {
goForward: { [weak browser] in goForward: { [weak browser] in
browser?.webView.goForward() browser?.webView.goForward()
}) })
if let url = browser.url {
CopyPasteMenu(url: url)
.keyboardShortcut("c", modifiers: [.command, .shift])
}
} }
#elseif os(iOS) #elseif os(iOS)
ToolbarItemGroup(placement: .navigationBarLeading) { ToolbarItemGroup(placement: .navigationBarLeading) {
@ -82,12 +77,21 @@ struct BrowserTab: View {
} }
#else #else
if !Brand.hideShareButton { if !Brand.hideShareButton {
ExportButton( Menu {
relativeToView: browser.webView, if let url = browser.webView.url {
webViewURL: browser.webView.url, CopyPasteMenu(url: url)
pageDataWithExtension: { [weak browser] in await browser?.pageDataWithExtension() }, .keyboardShortcut("c", modifiers: [.command, .shift])
isButtonDisabled: browser.zimFileName.isEmpty }
) ExportButton(
relativeToView: browser.webView,
webViewURL: browser.webView.url,
pageDataWithExtension: { [weak browser] in await browser?.pageDataWithExtension() },
isButtonDisabled: browser.zimFileName.isEmpty,
buttonLabel: LocalString.common_button_share_as_pdf
)
} label: {
Label(LocalString.common_button_share, systemImage: "square.and.arrow.up")
}.disabled(browser.webView.url == nil)
} }
if !Brand.hidePrintButton { if !Brand.hidePrintButton {
PrintButton(browserURLName: { [weak browser] in PrintButton(browserURLName: { [weak browser] in
@ -117,6 +121,9 @@ struct BrowserTab: View {
} }
.environmentObject(search) .environmentObject(search)
.focusedSceneValue(\.isBrowserURLSet, browser.url != nil) .focusedSceneValue(\.isBrowserURLSet, browser.url != nil)
#if os(macOS)
.focusedSceneValue(\.browserURL, browser.url)
#endif
.focusedSceneValue(\.canGoBack, browser.canGoBack) .focusedSceneValue(\.canGoBack, browser.canGoBack)
.focusedSceneValue(\.canGoForward, browser.canGoForward) .focusedSceneValue(\.canGoForward, browser.canGoForward)
.modifier(ExternalLinkHandler(externalURL: $browser.externalURL)) .modifier(ExternalLinkHandler(externalURL: $browser.externalURL))

View File

@ -23,8 +23,7 @@ struct CopyPasteMenu: View {
var body: some View { var body: some View {
Button { Button {
#if os(macOS) #if os(macOS)
NSPasteboard.general.clearContents() Self.copyToPasteBoard(url: url)
NSPasteboard.general.setString(url.absoluteString, forType: .string)
#elseif os(iOS) #elseif os(iOS)
UIPasteboard.general.setValue(url.absoluteString, forPasteboardType: UTType.url.identifier) UIPasteboard.general.setValue(url.absoluteString, forPasteboardType: UTType.url.identifier)
#endif #endif
@ -32,4 +31,11 @@ struct CopyPasteMenu: View {
Label(LocalString.library_zim_file_context_copy_url, systemImage: "doc.on.doc") Label(LocalString.library_zim_file_context_copy_url, systemImage: "doc.on.doc")
} }
} }
#if os(macOS)
public static func copyToPasteBoard(url: URL) {
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(url.absoluteString, forType: .string)
}
#endif
} }

View File

@ -23,6 +23,8 @@ struct ExportButton: View {
let webViewURL: URL? let webViewURL: URL?
let pageDataWithExtension: () async -> (Data, String?)? let pageDataWithExtension: () async -> (Data, String?)?
let isButtonDisabled: Bool let isButtonDisabled: Bool
var buttonLabel: String = LocalString.common_button_share
/// - Returns: Returns the browser data, fileName and extension /// - Returns: Returns the browser data, fileName and extension
private func dataNameAndExtension() async -> FileExportData? { private func dataNameAndExtension() async -> FileExportData? {
@ -48,12 +50,22 @@ struct ExportButton: View {
NotificationCenter.exportFileData(exportData) NotificationCenter.exportFileData(exportData)
#else #else
guard let url = await tempFileURL() else { return } guard let url = await tempFileURL() else { return }
NSSharingServicePicker(items: [url]).show(relativeTo: .null, of: relativeToView, preferredEdge: .minY) NSSharingServicePicker(items: [url]).show(
relativeTo: NSRect(
origin: .zero,
size: CGSize(
width: 640,
height: 54
)
),
of: relativeToView,
preferredEdge: .minY
)
#endif #endif
} }
} label: { } label: {
Label { Label {
Text(LocalString.common_button_share) Text(buttonLabel)
} icon: { } icon: {
Image(systemName: "square.and.arrow.up") Image(systemName: "square.and.arrow.up")
} }

View File

@ -25,6 +25,10 @@ struct IsBrowserURLSet: FocusedValueKey {
typealias Value = Bool typealias Value = Bool
} }
struct BrowserURL: FocusedValueKey {
typealias Value = URL
}
struct CanGoBackKey: FocusedValueKey { struct CanGoBackKey: FocusedValueKey {
typealias Value = Bool typealias Value = Bool
} }
@ -38,6 +42,12 @@ struct NavigationItemKey: FocusedValueKey {
} }
extension FocusedValues { extension FocusedValues {
#if os(macOS)
var browserURL: BrowserURL.Value? {
get { self[BrowserURL.self] }
set { self[BrowserURL.self] = newValue}
}
#endif
var isBrowserURLSet: IsBrowserURLSet.Value? { var isBrowserURLSet: IsBrowserURLSet.Value? {
get { self[IsBrowserURLSet.self] } get { self[IsBrowserURLSet.self] }
set { self[IsBrowserURLSet.self] = newValue } set { self[IsBrowserURLSet.self] = newValue }