Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 98 additions & 2 deletions FinderSyncExt/FinderSyncExt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,32 @@ class FinderSyncExt: FIFinderSync, @unchecked Sendable {

// 构建常用目录菜单
if !config.commonDirs.isEmpty {
if config.commonDirsCollapsed {
if currentMenuKind == .contextualMenuForItems,
!(FIFinderSyncController.default().selectedItemURLs() ?? []).isEmpty {
if config.shouldShowCopyToCommonDirs {
addCommonDirTransferMenu(
to: menu,
title: AppLocalization.localized("Copy To"),
icon: "doc.on.doc",
commonDirs: config.commonDirs,
action: #selector(handleCopyToCommonDirClick(_:)),
operation: "copy"
)
}

if config.shouldShowMoveToCommonDirs {
addCommonDirTransferMenu(
to: menu,
title: AppLocalization.localized("Move To"),
icon: "folder",
commonDirs: config.commonDirs,
action: #selector(handleMoveToCommonDirClick(_:)),
operation: "move"
)
}
}

if config.shouldShowCommonDirs, config.commonDirsCollapsed {
// 折叠:使用子菜单
let commonDirsTitle = AppLocalization.localized("Common Dirs")
let commonDirsSubMenu = NSMenu(title: commonDirsTitle)
Expand All @@ -373,7 +398,7 @@ class FinderSyncExt: FIFinderSync, @unchecked Sendable {
commonDirsItem.submenu = commonDirsSubMenu
commonDirsItem.image = templateSymbol("folder")
menu.addItem(commonDirsItem)
} else {
} else if config.shouldShowCommonDirs {
// 不折叠:直接显示菜单项
for commonDir in config.commonDirs {
let item = NSMenuItem(title: commonDir.name, action: #selector(handleCommonDirClick(_:)), keyEquivalent: "")
Expand Down Expand Up @@ -406,6 +431,33 @@ class FinderSyncExt: FIFinderSync, @unchecked Sendable {
return "commondir_\(commonDir.id)".hash
}

private func hashForCommonDirTransfer(_ commonDir: CommonDirMenuItem, operation: String) -> Int {
return "\(operation)_commondir_\(commonDir.id)".hash
}

private func addCommonDirTransferMenu(
to menu: NSMenu,
title: String,
icon: String,
commonDirs: [CommonDirMenuItem],
action: Selector,
operation: String
) {
let subMenu = NSMenu(title: title)
for commonDir in commonDirs {
let item = NSMenuItem(title: commonDir.name, action: action, keyEquivalent: "")
item.tag = hashForCommonDirTransfer(commonDir, operation: operation)
item.target = self
item.image = loadIcon(named: commonDir.icon, accessibilityDescription: commonDir.name)
subMenu.addItem(item)
}

let item = NSMenuItem(title: title, action: nil, keyEquivalent: "")
item.submenu = subMenu
item.image = templateSymbol(icon)
menu.addItem(item)
}

// MARK: - Menu Action Handlers

@objc private func handleActionClick(_ sender: NSMenuItem) {
Expand Down Expand Up @@ -496,6 +548,36 @@ class FinderSyncExt: FIFinderSync, @unchecked Sendable {
messager.sendClickEvent(event)
}

@objc private func handleCopyToCommonDirClick(_ sender: NSMenuItem) {
handleCommonDirTransferClick(sender, itemType: .copyToCommonDir, operation: "copy")
}

@objc private func handleMoveToCommonDirClick(_ sender: NSMenuItem) {
handleCommonDirTransferClick(sender, itemType: .moveToCommonDir, operation: "move")
}

private func handleCommonDirTransferClick(_ sender: NSMenuItem, itemType: MenuItemType, operation: String) {
guard let config = cachedMenuConfig,
let commonDir = config.commonDirs.first(where: { hashForCommonDirTransfer($0, operation: operation) == sender.tag }) else {
logger.warning("CommonDir transfer target not found for tag: \(sender.tag)")
return
}

let itemPaths = selectedActionTargetPaths()
guard !itemPaths.isEmpty else {
logger.warning("No selected items for common dir transfer")
return
}

let event = ClickEventPayload(
itemId: commonDir.id,
itemType: itemType,
target: itemPaths,
trigger: getTriggerForMenuKind()
)
messager.sendClickEvent(event)
}

// MARK: - Helper Methods

/// 获取触发来源
Expand Down Expand Up @@ -531,6 +613,20 @@ class FinderSyncExt: FIFinderSync, @unchecked Sendable {

return []
}

private func selectedActionTargetPaths() -> [String] {
let selectedItems = FIFinderSyncController.default().selectedItemURLs() ?? []
if !selectedItems.isEmpty {
return selectedItems.map { $0.path }
}

if currentMenuKind == .contextualMenuForContainer,
let targetURL = FIFinderSyncController.default().targetedURL() {
return [targetURL.path]
}

return []
}
}

// MARK: - NSMenu Extension
Expand Down
232 changes: 231 additions & 1 deletion FinderSyncExt/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,236 @@
}
}
},
"Copy File Name" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Copy File Name"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "ファイル名をコピー"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "复制文件名"
}
}
}
},
"Copy Parent Path" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Copy Parent Path"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "親フォルダのパスをコピー"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "复制父目录路径"
}
}
}
},
"Copy To" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Copy To"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "コピー先"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "复制到"
}
}
}
},
"Destination folder is not available." : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Destination folder is not available."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "コピー先または移動先のフォルダを利用できません。"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "目标文件夹不可用。"
}
}
}
},
"Move To" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Move To"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "移動先"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "移动到"
}
}
}
},
"New Folder" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "New Folder"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "新規フォルダ"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "新建文件夹"
}
}
}
},
"Please select files or folders first." : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Please select files or folders first."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "先にファイルまたはフォルダを選択してください。"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "请先选择文件或文件夹。"
}
}
}
},
"Protected system folders cannot be moved: %@" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Protected system folders cannot be moved: %@"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "保護されたシステムフォルダは移動できません: %@"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "受保护的系统文件夹不能移动:%@"
}
}
}
},
"Put Into New Folder" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Put Into New Folder"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "新規フォルダに入れる"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "放入新文件夹"
}
}
}
},
"Selected items must be in the same folder." : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Selected items must be in the same folder."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "選択した項目は同じフォルダ内にある必要があります。"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "所选项目必须位于同一个文件夹。"
}
}
}
},
"Default Open App" : {
"extractionState" : "manual",
"localizations" : {
Expand Down Expand Up @@ -3263,4 +3493,4 @@
}
},
"version" : "1.0"
}
}
Loading
Loading