mattDavo / Yippy

macOS open source clipboard manager
MIT License
364 stars 26 forks source link

Pasting into window in active display #54

Open karlhorky opened 2 years ago

karlhorky commented 2 years ago

Hey @mattDavo ! 👋 Hope that 2022 is treating you well so far!

Yippy continues to be amazing for everything, have recommended it to a bunch of students too! 🙌 They are getting a lot out of it too.

One thing that breaks my workflow often is the multi-monitor support:

When pasting from Yippy, it does not paste to the active app on the last-focused display, it always pastes to the window in the primary monitor (at least, this is the behavior on my MacBook Pro with built-in monitor, when connected to an external display.

Repro steps:

  1. Connect MacBook to external display
  2. Use an app such as Google Chrome or iTerm2 with multiple windows - one on the primary monitor, one on the non-primary monitor
  3. Copy something
  4. Focus the window in the non-primary display
  5. Paste from Yippy using the keyboard shortcut
  6. 💥 The text is pasted in the primary display
karlhorky commented 2 years ago

@mattDavo I think a workaround for this would be an option to disable the search bar. Do you think this would be possible?

karlhorky commented 1 year ago

@mattDavo Digging into this a bit today (no idea what I'm doing here with Swift 😅) I found that fixing this properly (focusing the correct window of the NSWorkspace.shared.frontmostApplication to paste into) may be possible by using this AXUIElement thing that I found in the mado-size repo:

    static func frontmost() -> AppWindow? {
        guard let frontmostApplication = NSWorkspace.shared().frontmostApplication else {
            return nil
        }

        let appElement = AXUIElementCreateApplication(frontmostApplication.processIdentifier)

        var result: AnyObject?
        guard AXUIElementCopyAttributeValue(appElement, kAXFocusedWindowAttribute as CFString, &result) == .success else {
            return nil
        }

        let windowElement = result as! AXUIElement
        return AppWindow(app: frontmostApplication, appElement: appElement, windowElement: windowElement)
    }

https://github.com/shadanan/mado-size/blob/db05433c4b0e4b3491b5c14c47996e4338a80fe5/MadoSize/AppWindow.swift#LL17-L31C6

This kAXMainWindowAttribute could then be set using this:

AXUIElementSetAttributeValue(window, kAXMainWindowAttribute as CFString, true as CFTypeRef)

https://stackoverflow.com/questions/2114283/setting-the-front-most-window-using-accessibility-api#comment100177793_33337614

Currently, in the YippyViewController.paste method, there is no focus management:

https://github.com/mattDavo/Yippy/blob/c9fb7ea754bbe9e5fd6aa22678e825e9c9a17d9d/Yippy/Sources/Windows/Yippy/YippyViewController.swift#L298-L302

However, there is some focus management in the YippyWindowController - maybe the active window AXUIElement could be saved there?

https://github.com/mattDavo/Yippy/blob/c9fb7ea754bbe9e5fd6aa22678e825e9c9a17d9d/Yippy/Sources/Windows/Yippy/YippyViewController.swift#L298-L302

What do you think?

karlhorky commented 1 year ago

Haha btw ChatGPT agrees here (of course, this could always be wrong, haven't looked in detail yet):

Screenshot 2022-12-05 at 17 46 30

Full answer:

To restore focus to the main window of the frontmost application using NSWorkspace, AXUIElementCreateApplication and AXUIElement in Swift, you can try the following steps:

Use NSWorkspace to get the frontmost application, using the frontmostApplication method:

let workspace = NSWorkspace.shared
let frontmostApp = workspace.frontmostApplication

Use AXUIElementCreateApplication to create an AXUIElement for the frontmost application, using the processIdentifier of the NSRunningApplication object from step 1:

let pid = frontmostApp?.processIdentifier
let app = AXUIElementCreateApplication(pid!)

Use AXUIElementCopyAttributeValue to get the main window of the frontmost application, using the kAXMainWindowAttribute constant and the AXUIElement object from step 2:

var mainWindow: AXUIElement? = nil
let error = AXUIElementCopyAttributeValue(app, kAXMainWindowAttribute as CFString, &mainWindow)

Use AXUIElementSetAttributeValue to restore focus to the main window of the frontmost application, using the kAXMainWindowAttribute constant and the AXUIElement object for the main window from step 3:

let error = AXUIElementSetAttributeValue(app, kAXMainWindowAttribute as CFString, mainWindow)

Release the AXUIElement objects when you are finished with them, using the CFRelease function:

mainWindow?.release()
app.release()

Note that these steps are provided for informational purposes only, and may not work as expected in all situations. You may need to modify the code to handle errors and other edge cases. For more information, you can refer to the documentation for the NSWorkspace, AXUIElementCreateApplication, AXUIElementCopyAttributeValue, and AXUIElementSetAttributeValue functions.

mattDavo commented 1 year ago

Hey @karlhorky, sorry have abandoned this project for a while. Thanks for looking at how to solve this problem 🙏 I'll try take a look at this (and some other issues) over the coming weeks.

karlhorky commented 1 year ago

No problem, can totally understand getting busy!

Thanks for taking a look soon! 🙌

karlhorky commented 1 year ago

I tried to implement this myself in XCode, but I couldn't get the project to build - even after pod install, it could not find the RxSwift module (kind of like this problem):

No such module 'RxSwift'

I'm pretty new to XCode development so I didn't quickly find a workable solution on this, but if you have tips for setting up a project on my machine (MacBook Pro M1, macOS Ventura 13.0.1 (22A400), XCode 14.1 (14B47b)), I'd be glad to give them a try!

mattDavo commented 1 year ago

Ah classic - have run into those kinds of problems before. Always problems when you update Xcode... I've got an M1 now so will see how I go; will likely have to update some configuration, then hopefully will work for the both of us 🤞

karlhorky commented 11 months ago

Hey @mattDavo, hope you're well! 🙌 In case you get any time to set up Yippy with new XCode, let me know, I'd be happy to try to move this issue forward a bit more.

The main thing is that I couldn't figure out how to get the project to build - tried debugging a bit but didn't find a solution back then

mattDavo commented 11 months ago

Hey @karlhorky, I'm doing well I hope you are as well!

Wow a year has gone by quick 😅 I did actually try to get it up and running back then but just couldn't figure it out either... I meant to keep trying but well...

I've just had another attempt with some more success than last time 🎉 I've pushed some changes in matt-revamp-dev-env which gets it working for me.

If you checkout and run pod update in the project root directory hopefully it will work for you 🤞