Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dragging strings to new tab button #3228

Merged
merged 5 commits into from
Sep 12, 2024
Merged
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
18 changes: 17 additions & 1 deletion DuckDuckGo/PinnedTabs/View/PinnedTabView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import SwiftUI
import SwiftUIExtensions

struct PinnedTabView: View {
struct PinnedTabView: View, DropDelegate {
enum Const {
static let dimension: CGFloat = 34
static let cornerRadius: CGFloat = 10
Expand Down Expand Up @@ -49,6 +49,7 @@ struct PinnedTabView: View {
.buttonStyle(TouchDownButtonStyle())
.cornerRadius(Const.cornerRadius, corners: [.topLeft, .topRight])
.contextMenu { contextMenu }
.onDrop(of: ["public.text"], delegate: self)

BorderView(isSelected: isSelected,
cornerRadius: Const.cornerRadius,
Expand All @@ -62,7 +63,22 @@ struct PinnedTabView: View {
} else {
stack
}
}

func performDrop(info: DropInfo) -> Bool {
if let item = info.itemProviders(for: ["public.utf8-plain-text"]).first {
item.loadItem(forTypeIdentifier: "public.utf8-plain-text", options: nil) { (data, _) in
if let data = data as? Data, let droppedString = NSString(data: data, encoding: NSUTF8StringEncoding) {
print(droppedString)
let url = URL.makeURL(from: droppedString as String)!

DispatchQueue.main.async {
model.setUrl(url, source: .ui)
}
}
}
}
return true
}

private var isSelected: Bool {
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/TabBar/View/TabBarCollectionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ final class TabBarCollectionView: NSCollectionView {
register(nib, forItemWithIdentifier: TabBarViewItem.identifier)

// Register for the dropped object types we can accept.
registerForDraggedTypes([.URL, .fileURL, TabBarViewItemPasteboardWriter.utiInternalType])
registerForDraggedTypes([.URL, .fileURL, TabBarViewItemPasteboardWriter.utiInternalType, .string])
// Enable dragging items within and into our CollectionView.
setDraggingSourceOperationMask([.private], forLocal: true)
}
Expand Down
45 changes: 44 additions & 1 deletion DuckDuckGo/TabBar/View/TabBarViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,8 @@ final class TabBarViewController: NSViewController {
}

private func setupAddTabButton() {
addTabButton.delegate = self
addTabButton.registerForDraggedTypes([.string])
addTabButton.target = self
addTabButton.action = #selector(addButtonAction(_:))
addTabButton.toolTip = UserText.newTabTooltip
Expand Down Expand Up @@ -625,6 +627,29 @@ final class TabBarViewController: NSViewController {

}

extension TabBarViewController: MouseOverButtonDelegate {

func mouseOverButton(_ sender: MouseOverButton, draggingEntered info: any NSDraggingInfo, isMouseOver: UnsafeMutablePointer<Bool>) -> NSDragOperation {
assert(sender === addTabButton || sender === footerAddButton)
let pasteboard = info.draggingPasteboard

if let types = pasteboard.types, types.contains(.string) {
return .copy
}
return .none
}

func mouseOverButton(_ sender: MouseOverButton, performDragOperation info: any NSDraggingInfo) -> Bool {
assert(sender === addTabButton || sender === footerAddButton)
if let string = info.draggingPasteboard.string(forType: .string), let url = URL.makeURL(from: string) {
tabCollectionViewModel.insertOrAppendNewTab(.url(url, credential: nil, source: .ui))
return true
}

return true
}
}

extension TabBarViewController: TabCollectionViewModelDelegate {

func tabCollectionViewModelDidAppend(_ tabCollectionViewModel: TabCollectionViewModel, selected: Bool) {
Expand Down Expand Up @@ -882,6 +907,8 @@ extension TabBarViewController: NSCollectionViewDataSource {
footer.addButton?.action = #selector(addButtonAction(_:))
footer.toolTip = UserText.newTabTooltip
self.footerAddButton = footer.addButton
self.footerAddButton?.delegate = self
self.footerAddButton?.registerForDraggedTypes([.string])
}

return view
Expand Down Expand Up @@ -942,6 +969,11 @@ extension TabBarViewController: NSCollectionViewDelegate {
// allow dropping URLs or files
guard draggingInfo.draggingPasteboard.url == nil else { return .copy }

// Check if the pasteboard contains string data
if draggingInfo.draggingPasteboard.availableType(from: [.string]) != nil {
return .copy
}

// dragging a tab
guard case .private = draggingInfo.draggingSourceOperationMask,
draggingInfo.draggingPasteboard.types == [TabBarViewItemPasteboardWriter.utiInternalType] else { return .none }
Expand All @@ -959,7 +991,9 @@ extension TabBarViewController: NSCollectionViewDelegate {
tabCollectionViewModel.insert(Tab(content: .url(url, source: .appOpenUrl), burnerMode: tabCollectionViewModel.burnerMode),
at: .unpinned(newIndex),
selected: true)

return true
} else if let string = draggingInfo.draggingPasteboard.string(forType: .string), let url = URL.makeURL(from: string) {
tabCollectionViewModel.insertOrAppendNewTab(.url(url, credential: nil, source: .reload))
return true
}

Expand Down Expand Up @@ -1222,6 +1256,15 @@ extension TabBarViewController: TabBarViewItemDelegate {
return tab.audioState
}

func tabBarViewItem(_ tabBarViewItem: TabBarViewItem, replaceWithStringSearch: String) {
guard let indexPath = collectionView.indexPath(for: tabBarViewItem),
let tab = tabCollectionViewModel.tabCollection.tabs[safe: indexPath.item] else { return }

if let url = URL.makeURL(from: replaceWithStringSearch) {
tab.setContent(.url(url, credential: nil, source: .reload))
}
}

func otherTabBarViewItemsState(for tabBarViewItem: TabBarViewItem) -> OtherTabBarViewItemsState {
guard let indexPath = collectionView.indexPath(for: tabBarViewItem) else {
assertionFailure("TabBarViewController: Failed to get index path of tab bar view item")
Expand Down
18 changes: 18 additions & 0 deletions DuckDuckGo/TabBar/View/TabBarViewItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ protocol TabBarViewItemDelegate: AnyObject {
func tabBarViewItemMuteUnmuteSite(_ tabBarViewItem: TabBarViewItem)
func tabBarViewItemRemoveFireproofing(_ tabBarViewItem: TabBarViewItem)
func tabBarViewItemAudioState(_ tabBarViewItem: TabBarViewItem) -> WKWebView.AudioState?
func tabBarViewItem(_ tabBarViewItem: TabBarViewItem, replaceWithStringSearch: String)

func otherTabBarViewItemsState(for tabBarViewItem: TabBarViewItem) -> OtherTabBarViewItemsState

Expand Down Expand Up @@ -393,6 +394,9 @@ final class TabBarViewItem: NSCollectionViewItem {
} else {
faviconImageView.contentTintColor = nil
}

mouseOverView.registerForDraggedTypes([.string])
mouseOverView.delegate = self
}

private var usedPermissions = Permissions() {
Expand Down Expand Up @@ -664,6 +668,20 @@ extension TabBarViewItem: MouseClickViewDelegate {
delegate?.tabBarViewItemCloseAction(self)
}

func mouseOverView(_ sender: MouseOverView, performDragOperation info: any NSDraggingInfo) -> Bool {
if let droppedString = info.draggingPasteboard.string(forType: .string) {
delegate?.tabBarViewItem(self, replaceWithStringSearch: droppedString)
return true
}
return false
}

func mouseOverView(_ sender: MouseOverView, draggingEntered info: any NSDraggingInfo, isMouseOver: UnsafeMutablePointer<Bool>) -> NSDragOperation {
if info.draggingPasteboard.availableType(from: [.string]) != nil {
return .copy
}
return []
}
}

extension TabBarViewItem {
Expand Down
4 changes: 4 additions & 0 deletions UnitTests/TabBar/View/MockTabViewItemDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ class MockTabViewItemDelegate: TabBarViewItemDelegate {
OtherTabBarViewItemsState(hasItemsToTheLeft: hasItemsToTheLeft, hasItemsToTheRight: hasItemsToTheRight)
}

func tabBarViewItem(_ tabBarViewItem: TabBarViewItem, replaceWithStringSearch: String) {

}

func clear() {
self.audioState = nil
}
Expand Down
Loading