|
| 1 | +# Summary |
| 2 | + |
| 3 | +- Switch main dock system to [ADS][1] |
| 4 | +- Make ADS dock state per profile |
| 5 | +- Allow the user to switch between pre-made layouts |
| 6 | + - Plugins could register their own |
| 7 | + - Users could save their own |
| 8 | +- Dock added by plugins with the old method are added as "*Legacy dock*" |
| 9 | +- Frontend API methods and events about ADS docks |
| 10 | + |
| 11 | +Note: The "central widget" design is kept. |
| 12 | + |
| 13 | +# Motivation |
| 14 | + |
| 15 | +Provide a better dock system to end-users. |
| 16 | + |
| 17 | +# Design |
| 18 | +**DISCLAIMER**: The "central widget" design is kept, switching to a non-"central widget" one is not part of this RFC. |
| 19 | + |
| 20 | +## Main window dock separation |
| 21 | +To make OBS Studio compatible with ADS, Controls, Transitions, Mixer, Sources, Scenes docks need to be separated from the main window. |
| 22 | + |
| 23 | +And the central widget of main window should also be separated as well to become the central dock. |
| 24 | + |
| 25 | +So the docks will be separated as widget class with their own UI class. |
| 26 | + |
| 27 | +Then those widget are inserted in OBSDock (QDockWidget) and the central widget and put as main window central widget. |
| 28 | + |
| 29 | +And then we switch to ADS, all those widget will become docks and added the ADS dock manager. |
| 30 | + |
| 31 | +The Main window heavily rely on some UI element from those docks, to resolve that when really required OBSBasic will be set as a friend class of the widget. |
| 32 | + |
| 33 | +Each widget class will be put as a friend class in OBSBasic to allow access to private slot and avoid moving them as public. |
| 34 | + |
| 35 | +### Controls widget |
| 36 | +*Future ADS Controls dock widget* |
| 37 | + |
| 38 | +Some hotkeys and pause functions rely on UI buttons, so OBS Basic will be added as friend class to allow access to those to OBSBasic. |
| 39 | + |
| 40 | +This class will require to have OBSBasic as a friend class because: |
| 41 | +- The streaming hotkey pair rely on the stream button enabled/disabled state. |
| 42 | +- The recording hotkey pair rely on the record button checked state. |
| 43 | +- The pause hotkey pair rely on the pause button pointer and checked state. |
| 44 | +- Pause and unpause recording function rely on the pause button pointer. |
| 45 | + |
| 46 | + |
| 47 | +*Note: In my [WIP implementation][2], many signals are added to OBSBasic to interract with controls dock UI through slots. Some of them happen to be used by other docks.* |
| 48 | + |
| 49 | +### Transistions widget |
| 50 | +*Future ADS Transitions dock widget* |
| 51 | + |
| 52 | +The transition duration will become an attribute of OBSBasic, and signal and slots will be setup to synchromise it with the dock spinbox. |
| 53 | + |
| 54 | +This class will require to have OBSBasic as a friend class because the transition combobox store the transitions themselves. |
| 55 | + |
| 56 | +### Mixer widget |
| 57 | +*Future ADS Mixer dock widget* |
| 58 | + |
| 59 | +Easily separable, no need to set OBSBasic as a friend class of this widget. |
| 60 | + |
| 61 | +### Sources widget |
| 62 | +*Future ADS Sources dock widget* |
| 63 | + |
| 64 | +OBSBasic heavily rely on the SourceTree from the widget, so for now OBSBasic will access it by being a friend class of the widget. |
| 65 | + |
| 66 | +### Scenes widget |
| 67 | +*Future ADS Scenes dock widget* |
| 68 | + |
| 69 | +OBSBasic heavily rely on the SceneTree from the widget, so for now OBSBasic will access it by being a friend class of the widget. |
| 70 | + |
| 71 | +### Central widget |
| 72 | +*Future ADS Central dock widget* |
| 73 | + |
| 74 | +Some context bar, preview/program and nudge related code is moved to the central widget class. |
| 75 | + |
| 76 | +This class will have OBSBasicPreview as a friend class. |
| 77 | + |
| 78 | +This class will be a friend class of OBSBasic also to have access to some gs_vertbuffer_t type attributes. |
| 79 | + |
| 80 | +## ADS |
| 81 | +The Advanced Docking System, require to add a Dock Manager in the main window and then add the central widget and then add other docks. |
| 82 | + |
| 83 | +Some work on themes CSS will be required. |
| 84 | + |
| 85 | +### About obs-deps |
| 86 | + |
| 87 | +obs-deps already build Qt for macOS, so adding a ADS script for the Qt tarball is not a problem. |
| 88 | + |
| 89 | +But in the case of Windows, there is some issues: |
| 90 | +- ADS does not use debug Qt DLLs when building OBS with `Debug` config. So it may require to copy non-debug one just for ADS. |
| 91 | +- It needs to build Qt or repackage it to include ADS. |
| 92 | + |
| 93 | +### About ADS translations |
| 94 | +OBS does not use the Qt translations but a customised one which use source text without spaces only. |
| 95 | + |
| 96 | +So in the `OBSTranslator::translate()` function, removing spaces from source text will to translate ADS texts. |
| 97 | + |
| 98 | +In `OBSTranslator::translate()` function: |
| 99 | +```c++ |
| 100 | + if (QT_UTF8(sourceText).contains(" ")) { |
| 101 | + QString text(QT_UTF8(sourceText).remove(" ")); |
| 102 | + sourceText = strdup(QT_TO_UTF8(text)); |
| 103 | + } |
| 104 | +``` |
| 105 | +
|
| 106 | +In `en-US.ini` file: |
| 107 | +```ini |
| 108 | +# ADS |
| 109 | +CloseOthers="Close Others" |
| 110 | +CloseTab="Close Tab" |
| 111 | +CloseActiveTab="Close Active Tab" |
| 112 | +ShowView="Show View" |
| 113 | +DetachGroup="Detach Group" |
| 114 | +CloseGroup="Close Group" |
| 115 | +CloseOtherGroups="Close Other Groups" |
| 116 | +ListAllTabs="List All Tabs" |
| 117 | +``` |
| 118 | + |
| 119 | +### Floating docks titlebar on X11 |
| 120 | +On X11, ADS provide two type of titlebar for floating docks: |
| 121 | + |
| 122 | +- Native |
| 123 | + |
| 124 | +[]() |
| 125 | + |
| 126 | +- QWidget based (WIP CSS) |
| 127 | + |
| 128 | +[]() |
| 129 | + |
| 130 | +The QWidget one is used by default when using KWin based Desktop Environement like Plasma. And this titlebar requires some CSS in the themes to match it. |
| 131 | + |
| 132 | +So the QWidget based titlebar usage will be enforced thanks to a flag for the Dock Manager to "force" theme makers to theme this bar, and not just ignore it because it's only "under Plasma X11". |
| 133 | + |
| 134 | +### Dock state |
| 135 | +*The per profile dock state feature is not taken into account to describe those changes.* |
| 136 | + |
| 137 | +In the global config (`global.ini`). |
| 138 | +- `"dockState"` is kept for for backward compatiblity and no longer overwitten. It will be used if the following state is not present. |
| 139 | +- `"windowState"` is the state of the main window, since it does not store only the state of legacy docks. |
| 140 | +- `"advDockState"` is the state of the dock manager which contain only the states of any ADS dock. |
| 141 | + |
| 142 | +Service integrations only save `"advDockProfile"` |
| 143 | + |
| 144 | +ADS dock state is compressed by default but behind the scene it's XML. |
| 145 | + |
| 146 | +### OBSAdvDock |
| 147 | +OBSAdvDock is a class which inherit `ads::CDockWidget` class. |
| 148 | + |
| 149 | +This class has a constructor which require a QWidget and setup some things arround the widget and set some connections. |
| 150 | + |
| 151 | +It adds a warning message when closing a dock from a close dock button. |
| 152 | + |
| 153 | +It allows to reset a dock position after a UI reset or a layout change with a possibility to reset the size if set beforehand. |
| 154 | + |
| 155 | +### Custom Browser docks and BrowserAdvDock |
| 156 | +The custom browser docks feature is modified to use BrowserAdvDock which inherit OBSAdvDock. |
| 157 | + |
| 158 | +Those docks are stored in the dock manager and their names is stored in a QStringList for browser docks to be able to get the dock from the manager to modify it like changing the URL. |
| 159 | + |
| 160 | +Each of those browser dock is named `extraBrowser_$UUID` where `$UUID` is replaced by the dock UUID to have a really unique name. |
| 161 | + |
| 162 | +### Service integration docks |
| 163 | + |
| 164 | +Like custom browser docks, those integration are modified to use BrowserAdvDock. But their names is stored in the QStringList for plugins extra docks. |
| 165 | + |
| 166 | +Those docks are named `obs-$SERVICE_$DOCK_NAME` where `$SERVICE` is the service name in lowercase and `$DOCK_NAME` the name of the dock. |
| 167 | + |
| 168 | +`"dockState"` will be imported from the integration config if `"windowState"` is not present. |
| 169 | + |
| 170 | +### Reset UI action |
| 171 | +1. Legacy dock are hidden |
| 172 | +2. The default state written in XML is applied |
| 173 | +3. Each not shown OBSAdvDock/BrowserAdvDock dock have its position and size reseted. |
| 174 | + |
| 175 | +## Legacy dock |
| 176 | +The frontend API method `obs_frontend_add_dock()` is put in deprecation. |
| 177 | + |
| 178 | +And dock added through this method are added to a sub-menu named `Legacy dock` of the Dock menu. |
| 179 | + |
| 180 | +[]() |
| 181 | + |
| 182 | +When openning a legacy dock, a message will appear explaining that those docks will not meld wery well with "new" docks. |
| 183 | + |
| 184 | +The state of those are saved through `"windowState"` global config. |
| 185 | + |
| 186 | +## Per profile dock state |
| 187 | +Move the `"advDockState"` from global config to the profile and integration no longer store their own state. `"windowState"` is kept global. |
| 188 | + |
| 189 | +If a release happen between the switch to ADS and this feature, import the one from the integration service if the profile has one set up. |
| 190 | + |
| 191 | +## Layouts management |
| 192 | +Add the feature, to switch between registered/saved dock layouts: |
| 193 | +- Though a sub-menu in the dock menu |
| 194 | +- Through a hotkey |
| 195 | +- Through the frontend API |
| 196 | + |
| 197 | +OBS Studio could provide layouts, the default count as one. |
| 198 | + |
| 199 | +Plugins could also register their own XML layouts through the frontend API. |
| 200 | + |
| 201 | +Users could be able to save their own layouts. Those layouts will have their name prefixed with`user_` to avoid name conflicts. |
| 202 | + |
| 203 | +Note: ADS perspective feature is not directly used because it relies heavily on QSettings. |
| 204 | + |
| 205 | +## Frontend API |
| 206 | +Like said earlier, the method `obs_frontend_add_dock()` is put in deprecation. |
| 207 | + |
| 208 | +All add/remove methods related to ADS requiring a name will require the plugin module to be able to prefix the given name with the module name to avoid conflicts. |
| 209 | + |
| 210 | +### Add a dock |
| 211 | +```c++ |
| 212 | +/* takes QWidget */ |
| 213 | +#define obs_frontend_add_adv_dock(title, unique_name, dock) \ |
| 214 | + obs_frontend_add_module_adv_dock(obs_current_module(), title, \ |
| 215 | + unique_name, widget) |
| 216 | +EXPORT void obs_frontend_add_module_adv_dock(obs_module_t *module, |
| 217 | + const char *title, |
| 218 | + const char *unique_name, |
| 219 | + void *widget); |
| 220 | +``` |
| 221 | +
|
| 222 | +This allow to add a OBSAdvDock with the given QWidget. Those docks are stored in the Dock Manager and their names is stored in the QStringList for plugins extra docks. |
| 223 | +
|
| 224 | +Default height and width could be set through `"defaultHeight"` `"defaultHeight"` with the [`setProperty()`][7] method. |
| 225 | +
|
| 226 | +Minimum sizes used by the dock are based on the widget ones. |
| 227 | +
|
| 228 | +### Remove a dock |
| 229 | +```c++ |
| 230 | +#define obs_frontend_remove_adv_dock(unique_name) \ |
| 231 | + obs_frontend_remove_module_adv_dock(obs_current_module(), unique_name) |
| 232 | +EXPORT void obs_frontend_remove_module_adv_dock(obs_module_t *module, |
| 233 | + const char *unique_name); |
| 234 | +``` |
| 235 | + |
| 236 | +This allow the plugin to remove a dock added earlier. |
| 237 | + |
| 238 | +### Add a browser dock |
| 239 | +```c++ |
| 240 | +EXPORT bool obs_frontend_is_browser_available(void); |
| 241 | +``` |
| 242 | +
|
| 243 | +This allow to know if the running OBS Studio has browser feature enabled. So it return false if OBS Studio was built without browser or is running under Wayland. |
| 244 | +
|
| 245 | +```c++ |
| 246 | +#define obs_frontend_add_adv_browser_dock(dock_params, browser_params) \ |
| 247 | + obs_frontend_add_module_adv_browser_dock(obs_current_module(), \ |
| 248 | + dock_params, browser_params) |
| 249 | +EXPORT bool obs_frontend_add_module_adv_browser_dock(obs_module_t *module, |
| 250 | + struct obs_frontend_browser_dock_params *dock_params, |
| 251 | + struct obs_frontend_browser_params *browser_params) |
| 252 | +``` |
| 253 | + |
| 254 | +This allow the plugin to add a dock with a QCefWidget as widget. Return false if the browser dock could not be created. |
| 255 | + |
| 256 | +The QCefWidget will get parameters from this structure: |
| 257 | + |
| 258 | +```c++ |
| 259 | +struct obs_frontend_browser_params { |
| 260 | + const char *url; |
| 261 | + bool enable_cookie; |
| 262 | + struct dstr startup_script; |
| 263 | + DARRAY(char *) force_popup_urls; |
| 264 | +}; |
| 265 | +``` |
| 266 | +
|
| 267 | +- `bool enable_cookie`: if true `panel_cookie` will be used. The plugin maker will have to remove the dock the between profile change because the cookie manager is per profile. |
| 268 | +- `struct dstr startup_script` allow to set a startup script for the QCefWidget. |
| 269 | +- `DARRAY(char *) force_popup_urls` allow to set a list of url forced to popup. |
| 270 | +
|
| 271 | +And the dock itself will get parameters from this structure: |
| 272 | +
|
| 273 | +```c++ |
| 274 | +struct obs_frontend_browser_dock_params { |
| 275 | + const char *unique_name; |
| 276 | + const char *title; |
| 277 | + int default_width; |
| 278 | + int default_height; |
| 279 | + int min_width; |
| 280 | + int min_height; |
| 281 | +}; |
| 282 | +``` |
| 283 | + |
| 284 | +If browser docks are added in the module load or post load steps. Those will be really added later. |
| 285 | + |
| 286 | +`obs_frontend_remove_adv_dock()` can be used to remove the browser dock. |
| 287 | + |
| 288 | +### Add a dock layouts |
| 289 | +```c++ |
| 290 | +#define obs_frontend_add_adv_dock_layout(title, unique_name, xml_layout) \ |
| 291 | + obs_frontend_add_module_adv_dock_layout(obs_current_module(), \ |
| 292 | + title, unique_name, xml_layout) |
| 293 | +EXPORT void obs_frontend_add_module_adv_dock_layout(obs_module_t *module, |
| 294 | + const char *title, |
| 295 | + const char *unique_name, |
| 296 | + const char *xml_layout); |
| 297 | +``` |
| 298 | +
|
| 299 | +This allow the plugin to add a dock layouts in XML to the UI. ADS allow to test a layout (state/perspective) without applying it, so the layout will be tested before registering it. |
| 300 | +
|
| 301 | +### Get a list of docks layouts |
| 302 | +```c++ |
| 303 | +EXPORT char **obs_frontend_get_adv_dock_layouts(void); |
| 304 | +``` |
| 305 | + |
| 306 | +This allow the plugin to get a list of registered dock layouts from the UI. |
| 307 | + |
| 308 | +### Set a registered dock layout |
| 309 | +```c++ |
| 310 | +EXPORT void obs_frontend_set_adv_dock_layouts(const char *layout_name); |
| 311 | +``` |
| 312 | +
|
| 313 | +This allow the plugin to set a registered dock layouts to the UI, the asked name should come directly from the get list method. |
| 314 | +
|
| 315 | +### Remove a dock layouts |
| 316 | +```c++ |
| 317 | +#define obs_frontend_remove_adv_dock_layout(unique_name) \ |
| 318 | + obs_frontend_remove_module_adv_dock_layout(obs_current_module(), \ |
| 319 | + unique_name) |
| 320 | +EXPORT void obs_frontend_remove_module_adv_dock_layout(obs_module_t *module, |
| 321 | + const char *unique_name); |
| 322 | +``` |
| 323 | + |
| 324 | +This allow the plugin to remove a dock layouts from the UI. |
| 325 | + |
| 326 | +### Add a entirely custom dock |
| 327 | +```c++ |
| 328 | +/* takes ads::CDockWidget */ |
| 329 | +#define obs_frontend_add_custom_adv_dock(unique_name, dock) \ |
| 330 | + obs_frontend_add_module_custom_adv_dock(obs_current_module(), \ |
| 331 | + unique_name, dock) |
| 332 | +EXPORT void obs_frontend_add_module_custom_adv_dock(obs_module_t *module, |
| 333 | + const char *unique_name, |
| 334 | + void *dock); |
| 335 | +``` |
| 336 | +
|
| 337 | +Some plugin like [Sources Dock][6], do not add their docks to the Dock menu. |
| 338 | +
|
| 339 | +So this method allow to do this but requires the plugin to be link against ADS library. |
| 340 | +
|
| 341 | +And the dock will not have OBSAdvDock features. |
| 342 | +
|
| 343 | +Even if the plugin has the control over the dock, the the name is changed by the frontend API to be prefixed by the module name. |
| 344 | +
|
| 345 | +`obs_frontend_remove_adv_dock()` can be used to remove the reference stored in the Dock Manager. Because their names is stored in the QStringList for plugins custom extra docks. |
| 346 | +
|
| 347 | +### Get the XML behind a registered dock layout |
| 348 | +*Method meant to allow a Dock Layout editor tool to exist* |
| 349 | +
|
| 350 | +```c++ |
| 351 | +EXPORT char *obs_frontend_get_current_profile(const char *layout_name); |
| 352 | +``` |
| 353 | + |
| 354 | +This allow the plugin to get a registered dock layouts XML, the asked name should come directly from the get list method. |
| 355 | + |
| 356 | +### Get the XML of the actual dock state |
| 357 | +*Method meant to allow a Dock Layout editor tool to exist* |
| 358 | + |
| 359 | +```c++ |
| 360 | +EXPORT char *obs_frontend_get_current_profile(const char *layout_name); |
| 361 | +``` |
| 362 | +
|
| 363 | +This allow the plugin to get the dock states XML of the UI. |
| 364 | +
|
| 365 | +### Events addition |
| 366 | +- An event before the startup restore dock state and `OBS_FRONTEND_EVENT_FINISHED_LOADING` to allow plugins to load their docks before the restore or redo a restore if the number of extra docks has changed. This event could possibly be emitted when profile is changed before restoring profile dock state and `OBS_FRONTEND_EVENT_PROFILE_CHANGED`. |
| 367 | +
|
| 368 | +- An event when the a dock layout is applied (reset or not) to allow plugins to reset the positions of their customs docks, if they want to. |
| 369 | +
|
| 370 | +## About making dock states future proof |
| 371 | +By default generated state are version 0. If one day we make a breaking change like remove the notion of central widget and so change the version. |
| 372 | +
|
| 373 | +We could take the saved `"advDockState"` and uncompress to edit the XML, to make it compatible with the change. |
| 374 | +
|
| 375 | +# Drawbacks |
| 376 | +We can't convert old `"dockState"` to the new dock system. |
| 377 | +
|
| 378 | +# Additional Information |
| 379 | +We may require to make some changes to allow ADS to be built on FreeBSD and submit the required changes to upstream. **I'm just waiting for a build guide for OBS Studio on FreeBSD to make it.** |
| 380 | +
|
| 381 | +About Wayland support, Qt and ADS docking system have very bad support because of Qt Wayland. The Wayland backend is apparently third-class on Qt's priorities. |
| 382 | +
|
| 383 | +My WIP branches: |
| 384 | +- [Main window dock separation][2] |
| 385 | +- [Switch to ADS][3] |
| 386 | +- [Frontend API to add ADS dock][4] |
| 387 | +- [Frontend API to add custom ADS dock][5] |
| 388 | +
|
| 389 | +[1]: https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System |
| 390 | +[2]: https://github.com/tytan652/obs-studio/tree/main_win_separation |
| 391 | +[3]: https://github.com/tytan652/obs-studio/tree/advanced_docking |
| 392 | +[4]: https://github.com/tytan652/obs-studio/tree/ads_frontend_api |
| 393 | +[5]: https://github.com/tytan652/obs-studio/tree/add_custom_dock |
| 394 | +[6]: https://obsproject.com/forum/resources/source-dock.1317/ |
| 395 | +[7]: https://doc.qt.io/qt-5/qobject.html#setProperty |
0 commit comments