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

Copy, Paste & Select All Hotkeys not working in Browser Source Interact & Docks (macOS) #365

Open
dustindmiller opened this issue Jun 17, 2022 · 6 comments

Comments

@dustindmiller
Copy link

Operating System Info

macOS 11

Other OS

No response

OBS Studio Version

27.2.4

OBS Studio Version (Other)

No response

OBS Studio Log URL

All

OBS Studio Crash Log URL

No response

Expected Behavior

Using CMD+C, CMD+V & CMD+A should copy, paste and select all respectively.

Current Behavior

Using mouse and contextual menu is the only way to currently do this.

Steps to Reproduce

  1. Place cursor in browser source interact or dock.
  2. Try hotkey (example CMD+A)
  3. Does not function (does not select all)
  4. Same for all other aforementioned commands.

Anything else we should know?

No response

@dustindmiller dustindmiller changed the title Copy, Paste & Select All Hotkeys not working in Browser Source Interact & Docks (masOS) Copy, Paste & Select All Hotkeys not working in Browser Source Interact & Docks (macOS) Jun 17, 2022
@WizardCM WizardCM transferred this issue from obsproject/.github Jun 17, 2022
@WizardCM
Copy link
Member

WizardCM commented Jun 17, 2022

Note: this was split from #238.

Honestly, this is pretty much expected behaviour. The way hotkeys work on macOS as an operating system is that whatever is in the menu bar takes priority.

To properly fix this, we'd have to ensure OBS knows what context the user is in, and modify the Edit menu to replace the actions of copy/paste/select all with their CEF counterparts.

Unfortunately, I don't know how viable/possible this is. I know CEF in general is very bad at notifying the parent application (in this case OBS) whether it's focused.

The good(?) news is the API to perform the actions themselves does exist as part of CefFrame(), including

  • Copy
  • Paste
  • Undo
  • Redo
  • Select All

@jhennig3
Copy link

  1. Set up OBS Studio and OBS Browser Source on macOS:

  2. Modify the OBS Browser Source plugin:

    • Navigate to the obs-browser plugin directory.
    • Open the obs-browser-source.cpp file in a text editor or IDE.
  3. Define the custom Edit menu:

    • Inside the obs-browser-source.cpp file, locate the browser_source_create function.
    • In this function, create a new instance of CefMenuModel to represent the custom Edit menu:
      CefRefPtr<CefMenuModel> edit_menu = CefMenuModel::CreateMenuModel();
    • Add custom items to the menu using the AddItem method. For example:
      edit_menu->AddItem(1, "Copy");
      edit_menu->AddItem(2, "Paste");
      edit_menu->AddItem(3, "Select All");
    • Set the new edit_menu as a submenu for the browser source's context menu:
      browser_source->context_menu()->AddSubMenu(4, "Edit", edit_menu);
  4. Handle user input for the new menu item:

    • Create a new class that inherits from CefMenuModelDelegate and override the ExecuteCommand method to handle the custom menu commands:
      class EditMenuDelegate : public CefMenuModelDelegate {
      public:
        virtual void ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
                                    int command_id,
                                    CefEventHandle event) OVERRIDE {
          // Handle the custom menu commands here.
        }
      
        IMPLEMENT_REFCOUNTING(EditMenuDelegate);
      };
    • In the browser_source_create function, create a new instance of the EditMenuDelegate class and set it as the delegate for the edit_menu:
      CefRefPtr<EditMenuDelegate> edit_menu_delegate = new EditMenuDelegate();
      edit_menu->SetDelegate(edit_menu_delegate.get());
  5. Trigger the corresponding CEF actions:

    • In the ExecuteCommand method of the EditMenuDelegate class, use the command_id parameter to determine which action to perform. For example:
      void ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
                          int command_id,
                          CefEventHandle event) OVERRIDE {
        CefRefPtr<CefBrowser> browser = ...; // Get the CEF browser instance from the OBS browser source.
      
        switch (command_id) {
          case 1: // Copy
            browser->GetFocusedFrame()->Copy();
            break;
          case 2: // Paste
            browser->GetFocusedFrame()->Paste();
            break;
          case 3: // Select All
            browser->GetFocusedFrame()->SelectAll();
            break;
          default:
            break;
        }
      }
  6. Compile and test:

    • Rebuild the OBS Browser Source plugin with your changes.
    • Test the new custom Edit menu in OBS Studio, ensuring that the custom copy, paste, and select all commands work as expected.

@jhennig3
Copy link

The OBS Browser plugin uses CEF internally, but it doesn't expose the source code for the CEF part in a .cpp file. Instead, you'll have to modify the JavaScript code that communicates with the plugin to create a custom context menu with the desired functionality.

To create a custom Edit menu for the OBS Browser Source, follow these steps:

Set up a new local HTML file that will be used as the Browser Source:

Create a new HTML file with the following basic structure:
php
Copy code

<title>Custom Edit Menu</title> <style> /* Add any required CSS here */ </style> <script> // Add JavaScript code here </script> Save the file locally, e.g., as custom_edit_menu.html. Add an event listener for the context menu to the JavaScript code in the HTML file:

javascript
Copy code
window.addEventListener('contextmenu', (event) => {
event.preventDefault();
// Code to create and display the custom context menu will go here.
});
Create the custom Edit menu using JavaScript:

Inside the event listener, create an HTML element for the custom context menu:
javascript
Copy code
const contextMenu = document.createElement('div');
contextMenu.style.position = 'fixed';
contextMenu.style.left = ${event.clientX}px;
contextMenu.style.top = ${event.clientY}px;
contextMenu.style.backgroundColor = '#f8f8f8';
contextMenu.style.border = '1px solid #ccc';
contextMenu.style.padding = '8px';
contextMenu.style.zIndex = 9999;
Create menu items for copy, paste, and select all, and add them to the context menu:
javascript
Copy code
const copyItem = document.createElement('button');
copyItem.textContent = 'Copy';
copyItem.onclick = () => {
document.execCommand('copy');
document.body.removeChild(contextMenu);
};
contextMenu.appendChild(copyItem);

const pasteItem = document.createElement('button');
pasteItem.textContent = 'Paste';
pasteItem.onclick = () => {
document.execCommand('paste');
document.body.removeChild(contextMenu);
};
contextMenu.appendChild(pasteItem);

const selectAllItem = document.createElement('button');
selectAllItem.textContent = 'Select All';
selectAllItem.onclick = () => {
document.execCommand('selectAll');
document.body.removeChild(contextMenu);
};
contextMenu.appendChild(selectAllItem);
Display the custom context menu and handle hiding it:

Add the context menu to the document body:
javascript
Copy code
document.body.appendChild(contextMenu);
Add an event listener to hide the context menu when clicking outside of it:
javascript
Copy code
window.addEventListener('click', () => {
if (document.body.contains(contextMenu)) {
document.body.removeChild(contextMenu);
}
});
Set up OBS Studio and the Browser Source:

Open OBS Studio.
Create a new Browser Source.
In the Browser Source properties, set the local file as the source, e.g., custom_edit_menu.html.
With these modifications, you should have a custom context menu with copy, paste, and select all commands in the OBS Browser Source. Note that this approach uses JavaScript's execCommand function, which is now deprecated. You may need to use the Clipboard API.

@jhennig3
Copy link

  1. Set up a new local HTML file that will be used as the Browser Source:

    • Create a new HTML file with the following basic structure:
      <!DOCTYPE html>
      <html>
      <head>
        <title>Custom Edit Menu</title>
        <style>
          /* Add any required CSS here */
        </style>
      </head>
      <body>
        <!-- Add your page content here -->
        <script>
          // Add JavaScript code here
        </script>
      </body>
      </html>
      
    • Save the file locally, e.g., as custom_edit_menu.html.
  2. Add an event listener for the context menu to the JavaScript code in the HTML file:
    window.addEventListener('contextmenu', (event) => {
    event.preventDefault();
    // Code to create and display the custom context menu will go here.
    });

  3. Create the custom Edit menu using JavaScript:

  • Inside the event listener, create an HTML element for the custom context menu:
    const contextMenu = document.createElement('div');
    contextMenu.style.position = 'fixed';
    contextMenu.style.left = `${event.clientX}px`;
    contextMenu.style.top = `${event.clientY}px`;
    contextMenu.style.backgroundColor = '#f8f8f8';
    contextMenu.style.border = '1px solid #ccc';
    contextMenu.style.padding = '8px';
    contextMenu.style.zIndex = 9999;
    
  • Create menu items for copy, paste, and select all, and add them to the context menu using the Clipboard API:
    const copyItem = document.createElement('button');
    copyItem.textContent = 'Copy';
    copyItem.onclick = async () => {
      try {
        const selectedText = window.getSelection().toString();
        await navigator.clipboard.writeText(selectedText);
      } catch (err) {
        console.error('Failed to copy text:', err);
      }
      document.body.removeChild(contextMenu);
    };
    contextMenu.appendChild(copyItem);
    
    const pasteItem = document.createElement('button');
    pasteItem.textContent = 'Paste';
    pasteItem.onclick = async () => {
      try {
        const clipboardText = await navigator.clipboard.readText();
        // Handle pasting the text as needed.
      } catch (err) {
        console.error('Failed to paste text:', err);
      }
      document.body.removeChild(contextMenu);
    };
    contextMenu.appendChild(pasteItem);
    
    const selectAllItem = document.createElement('button');
    selectAllItem.textContent = 'Select All';
    selectAllItem.onclick = () => {
      document.execCommand('selectAll');
      document.body.removeChild(contextMenu);
    };
    contextMenu.appendChild(selectAllItem);
    
  1. Display the custom context menu and handle hiding it:
  • Add the context menu to the document body:
    document.body.appendChild(contextMenu);
    
  • Add an event listener to hide the context menu when clicking outside of it:
    window.addEventListener('click', () => {
      if (document.body.contains(contextMenu)) {
        document.body.removeChild(contextMenu);
      }
    });
    
  1. Set up OBS Studio and the Browser Source:
  • Open OBS Studio.
  • Create a new Browser Source.
  • In the Browser Source properties, set the local file as the source, e.g., custom_edit_menu.html.

@Blackn0va
Copy link

is there a possibility that the copy paste is also installed with the hotkey in a next update? The function has otherwise no use for me.

@hyperiris
Copy link

This is a CEF related bug.

please try https://glitch.com/edit/#!/async-clipboard-text in web browser and cef demo such as cefclient, you can find that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants