Open dustindmiller opened 2 years ago
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
Set up OBS Studio and OBS Browser Source on macOS:
Modify the OBS Browser Source plugin:
obs-browser-source.cpp
file in a text editor or IDE.Define the custom Edit menu:
obs-browser-source.cpp
file, locate the browser_source_create
function.CefMenuModel
to represent the custom Edit menu:
CefRefPtr<CefMenuModel> edit_menu = CefMenuModel::CreateMenuModel();
AddItem
method. For example:
edit_menu->AddItem(1, "Copy");
edit_menu->AddItem(2, "Paste");
edit_menu->AddItem(3, "Select All");
edit_menu
as a submenu for the browser source's context menu:
browser_source->context_menu()->AddSubMenu(4, "Edit", edit_menu);
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);
};
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());
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;
}
}
Compile and test:
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 <!DOCTYPE html>
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.
Set up a new local HTML file that will be used as the Browser Source:
<!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>
custom_edit_menu.html
.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. });
Create the custom Edit menu using JavaScript:
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;
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);
Display the custom context menu and handle hiding it:
document.body.appendChild(contextMenu);
window.addEventListener('click', () => {
if (document.body.contains(contextMenu)) {
document.body.removeChild(contextMenu);
}
});
Set up OBS Studio and the Browser Source:
custom_edit_menu.html
.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.
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.
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
Anything else we should know?
No response