Panels: Right click Reload, use Qt context menu #163

Merged
WizardCM merged 2 commits from custom-context-menu into master 2021-03-22 19:28:26 -05:00
WizardCM commented 2019-05-18 06:50:12 -05:00 (Migrated from github.com)

Description

This PR introduces two core changes, split into their own two commits (Gif examples at the end).

  1. It allows for the modification of the contents of the CEF context menu. This is a native capability, and I've used it to perform two minor changes: the first is to enable a Reload option, to refresh the page. The second is to hide the Print option, as it's not needed. I considered also hiding "View page source". Feedback here would be good. This change overall is quite small, and can be used down the line to add/remove any options, whether they're native ones (like the Reload one) or completely custom functions.

  2. Next I added a function to replace the native CEF context menu with a Qt menu. This is also a native capability, and ensures that not only the context menu is consistent with others, but also matches the OBS theme.

The second change went through a few iterations, but in the end I settled on this.

Motivation and Context

The primary motivation is consistency. The context menu for browser docks doesn't currently follow the same design rules as all other context menus in OBS, making it feel out of place and "tacked on". It also behaves unexpectedly when the user presses the context menu key on the keyboard, where it opens two menus where one cannot be closed.
The second motivation is extensibility. By using native CEF functions to modify the dropdown, it opens the doors to further options in the future: like a "Dock Configuration" button, for example. In this case, it starts off simple by adding a much-requested Reload option.

Implementation details:

  • RunContextMenu requires a "return true" if you're rendering your own context menu (which, we are). The callback can be run synchronously or asynchronously, and has no time limit
  • CEF Docs state: "Do not keep references to params or model outside of this callback." therefore I had to create a new object to store the basics of the contents of the dropdown. The model gives us everything we need - the text, command id, enabled status, and menu item type.
  • Qt doesn't allow you to create widgets outside of the UI thread, so once the menu items are acquired from the model, we create a QMenu in the UI thread, and populate it with the items, creating separators as needed.
  • The use of exec() was chosen as it's used in other places in OBS, and because it allowed for the smallest bit of code: it synchronously returns whether an action was clicked or not, allowing the ability to then run the necessary CEF callback. If you don't run the Cancel() callback if an item hasn't been chosen, then you can't open the dropdown a second time. The event just never triggers.

How Has This Been Tested?

Add a custom browser dock, or enable service integration, and right click anywhere within the webpage. Depending on whether text is selected, and what element you right click on, CEF will give you different options in the context menu. Example: right click a random area where there's no text, and then right click a text input field (the latter will unlock Copy/Paste).

Before:
2019-05-18_21-29-19

After:
2019-05-18_21-34-36

Types of changes

  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Code cleanup (non-breaking change which makes code smaller or more readable)

Checklist:

  • My code has been run through clang-format.
  • I have read the contributing document.
  • My code is not on the master branch.
  • The code has been tested.
  • All commit messages are properly formatted and commits squashed where appropriate.
### Description This PR introduces two core changes, split into their own two commits **(Gif examples at the end)**. 1. It allows for the modification of the contents of the CEF context menu. This is a native capability, and I've used it to perform two minor changes: the first is to enable a Reload option, to refresh the page. The second is to hide the Print option, as it's not needed. I considered also hiding "View page source". Feedback here would be good. This change overall is quite small, and can be used down the line to add/remove any options, whether they're native ones (like the Reload one) or completely custom functions. 2. Next I added a function to replace the native CEF context menu with a Qt menu. This is also a native capability, and ensures that not only the context menu is consistent with others, but also matches the OBS theme. The second change went through a few iterations, but in the end I settled on this. ### Motivation and Context The primary motivation is consistency. The context menu for browser docks doesn't currently follow the same design rules as all other context menus in OBS, making it feel out of place and "tacked on". It also behaves unexpectedly when the user presses the context menu key on the keyboard, where it opens two menus where one cannot be closed. The second motivation is extensibility. By using native CEF functions to modify the dropdown, it opens the doors to further options in the future: like a "Dock Configuration" button, for example. In this case, it starts off simple by adding a much-requested Reload option. Implementation details: * `RunContextMenu` requires a "return true" if you're rendering your own context menu (which, we are). The callback can be run synchronously or asynchronously, and has no time limit * CEF Docs state: "Do not keep references to `params` or `model` outside of this callback." therefore I had to create a new object to store the basics of the contents of the dropdown. The `model` gives us everything we need - the `text`, `command id`, `enabled` status, and menu item `type`. * Qt doesn't allow you to create widgets outside of the UI thread, so once the menu items are acquired from the `model`, we create a `QMenu` in the UI thread, and populate it with the items, creating separators as needed. * The use of `exec()` was chosen as it's used in other places in OBS, and because it allowed for the smallest bit of code: it synchronously returns whether an action was clicked or not, allowing the ability to then run the necessary CEF callback. **If you don't run the Cancel() callback if an item hasn't been chosen, then you can't open the dropdown a second time.** The event just never triggers. ### How Has This Been Tested? Add a custom browser dock, or enable service integration, and right click anywhere within the webpage. Depending on whether text is selected, and what element you right click on, CEF will give you different options in the context menu. Example: right click a random area where there's no text, and then right click a text input field (the latter will unlock Copy/Paste). **Before:** ![2019-05-18_21-29-19](https://user-images.githubusercontent.com/941350/57968901-28608880-79b4-11e9-980d-1cbf67f48434.gif) **After:** ![2019-05-18_21-34-36](https://user-images.githubusercontent.com/941350/57968965-c3596280-79b4-11e9-8234-c0f6df0473b3.gif) ### Types of changes - New feature (non-breaking change which adds functionality) - Bug fix (non-breaking change which fixes an issue) - Code cleanup (non-breaking change which makes code smaller or more readable) ### Checklist: - [x] My code has been run through [clang-format](https://github.com/obsproject/obs-studio/blob/master/.clang-format). - [x] I have read the [**contributing** document](https://github.com/obsproject/obs-studio/blob/master/CONTRIBUTING.rst). - [x] My code is not on the master branch. - [x] The code has been tested. - [x] All commit messages are properly formatted and commits squashed where appropriate.
WizardCM commented 2019-06-25 18:13:32 -05:00 (Migrated from github.com)

Rebased with clang-format.

Rebased with clang-format.
RytoEX (Migrated from github.com) requested changes 2020-12-18 08:30:47 -06:00
RytoEX (Migrated from github.com) left a comment

Discussed on call with @WizardCM . Leaving review notes here to help keep a paper trail.

Discussed on call with @WizardCM . Leaving review notes here to help keep a paper trail.
@ -186,6 +194,72 @@ bool QCefBrowserClient::OnBeforePopup(
return true;
RytoEX (Migrated from github.com) commented 2020-12-18 08:27:44 -06:00

You can probably only check one of these conditions, or check if "Copy" does not exist, since the intent was to not add the Reload button when text is highlighted.

You can probably only check one of these conditions, or check if "Copy" does not exist, since the intent was to not add the Reload button when text is highlighted.
RytoEX (Migrated from github.com) commented 2020-12-18 08:29:19 -06:00

I don't think you need to nest this here if the intent is to always remove the Print menu item.

Also, do we actually need to check if Print is visible? Does it throw an exception/error if you try to remove print if it's not visible?

I don't think you need to nest this here if the intent is to _always_ remove the Print menu item. Also, do we actually need to check if Print is visible? Does it throw an exception/error if you try to remove print if it's _not_ visible?
RytoEX (Migrated from github.com) commented 2020-12-18 08:30:17 -06:00

Discussed with @WizardCM , but I'd split this into a separate function and call it similar to how we use QMetaObject::invokeMethod elsewhere. That should help with indenting and line length.

Discussed with @WizardCM , but I'd split this into a separate function and call it similar to how we use `QMetaObject::invokeMethod` elsewhere. That should help with indenting and line length.
WizardCM (Migrated from github.com) reviewed 2020-12-18 18:09:56 -06:00
@ -186,6 +194,72 @@ bool QCefBrowserClient::OnBeforePopup(
return true;
WizardCM (Migrated from github.com) commented 2020-12-18 18:09:56 -06:00

Done

Done
WizardCM (Migrated from github.com) reviewed 2020-12-18 18:10:15 -06:00
@ -186,6 +194,72 @@ bool QCefBrowserClient::OnBeforePopup(
return true;
WizardCM (Migrated from github.com) commented 2020-12-18 18:10:15 -06:00

Done. Even if it doesn't currently throw an exception, I'd rather check before deleting.

Done. Even if it doesn't currently throw an exception, I'd rather check before deleting.
WizardCM (Migrated from github.com) reviewed 2020-12-18 18:11:10 -06:00
WizardCM (Migrated from github.com) commented 2020-12-18 18:11:10 -06:00

I attempted this, but invokeMethod from a non-Qt thread can only invoke slots/signals from QThread.

I attempted this, but `invokeMethod` from a non-Qt thread can only invoke slots/signals from QThread.
RytoEX (Migrated from github.com) reviewed 2020-12-18 19:51:02 -06:00
RytoEX (Migrated from github.com) commented 2020-12-18 19:51:02 -06:00

Is this then creating an anonymous slot/signal?

Is this then creating an anonymous slot/signal?
WizardCM (Migrated from github.com) reviewed 2020-12-18 20:34:31 -06:00
WizardCM (Migrated from github.com) commented 2020-12-18 20:34:31 -06:00

Pretty much?

Pretty much?
jp9000 commented 2021-03-22 14:57:21 -05:00 (Migrated from github.com)

What happens if a URL overwrites the right-click menu?

What happens if a URL overwrites the right-click menu?
WizardCM commented 2021-03-22 17:25:13 -05:00 (Migrated from github.com)

@jp9000 Solid question! I have verified on this page that the page's custom context menu takes priority in the correct situations. By the looks of it the CEF code never gets hit, so V8 (Chrome's JavaScript engine) probably handles that internally.

@jp9000 Solid question! I have verified [on this page](https://codepen.io/SitePoint/full/MYLoWY) that the page's custom context menu takes priority in the correct situations. By the looks of it the CEF code never gets hit, so V8 (Chrome's JavaScript engine) probably handles that internally.
Sign in to join this conversation.
No reviewers
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-obsproject/obs-browser!163
No description provided.