Skip to content

Commit 8cf55f3

Browse files
committed
Add RFC: Switch to Advanced Docking System
1 parent e5c925b commit 8cf55f3

File tree

4 files changed

+386
-0
lines changed

4 files changed

+386
-0
lines changed
Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
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 functions rely on the pause button pointer.
45+
46+
### Transistions widget
47+
*Future ADS Transitions dock widget*
48+
49+
Transitions will be stored in OBSBasic and selected through the combo box of the widget.
50+
51+
The transition duration will become an attribute of OBSBasic, and signal and slots will be setup to synchromise it with the dock spinbox.
52+
53+
Buttons for adding, removing and open settings will be connected through signals and slots to OBSBasic.
54+
55+
### Mixer widget
56+
*Future ADS Mixer dock widget*
57+
58+
Easily separable, no need to set OBSBasic as a friend class of this widget.
59+
60+
### Sources widget
61+
*Future ADS Sources dock widget*
62+
63+
OBSBasic heavily rely on the SourceTree from the widget, so for now OBSBasic will access it by being a friend class of the widget.
64+
65+
### Scenes widget
66+
*Future ADS Scenes dock widget*
67+
68+
OBSBasic heavily rely on the SceneTree from the widget, so for now OBSBasic will access it by being a friend class of the widget.
69+
70+
### Central widget
71+
*Future ADS Central dock widget*
72+
73+
Some context bar, preview/program and nudge related code is moved to the central widget class.
74+
75+
This class will have OBSBasicPreview as a friend class.
76+
77+
This class will be a friend class of OBSBasic also to have access to some gs_vertbuffer_t type attributes.
78+
79+
## ADS
80+
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.
81+
82+
Some work on themes CSS will be required.
83+
84+
### About obs-deps
85+
86+
obs-deps already build Qt for macOS, so adding a ADS script for the Qt tarball is not a problem.
87+
88+
But in the case of Windows, there is some issues:
89+
- 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.
90+
- It needs to build Qt or repackage it to include ADS.
91+
92+
### Floating docks titlebar on X11
93+
On X11, ADS provide two type of titlebar for floating docks:
94+
95+
- Native
96+
97+
[![Native titlebar under GNOME](./0047-switch-to-advanced-docking-system/linux_native_floating_dock_titlebar.png)]()
98+
99+
- QWidget based (WIP CSS)
100+
101+
[![QWidget titlebar](./0047-switch-to-advanced-docking-system/linux_qwidget_floating_dock_titlebar.png)]()
102+
103+
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.
104+
105+
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".
106+
107+
### Dock state
108+
*The per profile dock state feature is not taken into account to describe those changes.*
109+
110+
In the global config (`global.ini`).
111+
- `"dockState"` is kept for for backward compatiblity and no longer overwitten. It will be used if the following state is not present.
112+
- `"windowState"` is the state of the main window, since it does not store only the state of legacy docks.
113+
- `"advDockState"` is the state of the dock manager which contain only the states of any ADS dock.
114+
115+
Service integrations only save `"advDockProfile"`
116+
117+
ADS dock state is compressed by default but behind the scene it's XML.
118+
119+
### OBSAdvDock
120+
OBSAdvDock is a class which inherit `ads::CDockWidget` class.
121+
122+
This class has a constructor which require a QWidget and setup some things arround the widget and set some connections.
123+
124+
It adds a warning message when closing a dock from a close dock button.
125+
126+
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.
127+
128+
### Custom Browser docks and BrowserAdvDock
129+
The custom browser docks feature is modified to use BrowserAdvDock which inherit OBSAdvDock.
130+
131+
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.
132+
133+
Each of those browser dock is named `extraBrowser_$UUID` where `$UUID` is replaced by the dock UUID to have a really unique name.
134+
135+
### Service integration docks
136+
137+
Like custom browser docks, those integration are modified to use BrowserAdvDock. But their names is stored in the QStringList for plugins extra docks.
138+
139+
Those docks are named `obs-$SERVICE_$DOCK_NAME` where `$SERVICE` is the service name in lowercase and `$DOCK_NAME` the name of the dock.
140+
141+
`"dockState"` will be imported from the integration config if `"windowState"` is not present.
142+
143+
### Reset UI action
144+
1. Legacy dock are hidden
145+
2. The default state written in XML is applied
146+
3. Each not shown OBSAdvDock/BrowserAdvDock dock have its position and size reseted.
147+
148+
## Legacy dock
149+
150+
If switching to ADS is made at the same as switching to Qt 6. This feature will only concern builds that still use Qt 5 (e.g. Ubuntu 20.04).
151+
152+
The frontend API method `obs_frontend_add_dock()` is put in deprecation. And if dual switch only Qt5 and on Qt 6 the method is removed or does nothing, this is possible because plugin will need to be rebuilt agaisnt Qt6.
153+
154+
And dock added through this method are added to a sub-menu named `Legacy dock` of the Dock menu.
155+
156+
[![Legacy dock menu](./0047-switch-to-advanced-docking-system/legacy_dock.png)]()
157+
158+
When openning a legacy dock, a message will appear explaining that those docks will not meld wery well with "new" docks.
159+
160+
The state of those are saved through `"windowState"` global config.
161+
162+
## Per profile dock state
163+
Move the `"advDockState"` from global config to the profile and integration no longer store their own state. `"windowState"` is kept global.
164+
165+
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.
166+
167+
## Layouts management
168+
Add the feature, to switch between registered/saved dock layouts:
169+
- Though a sub-menu in the dock menu
170+
- Through a hotkey
171+
- Through the frontend API
172+
173+
OBS Studio could provide layouts, the default count as one.
174+
175+
Plugins could also register their own XML layouts through the frontend API.
176+
177+
Users could be able to save their own layouts. Those layouts will have their name prefixed with`user_` to avoid name conflicts.
178+
179+
Note: ADS perspective feature is not directly used because it relies heavily on QSettings.
180+
## Frontend API
181+
Like said earlier, the method `obs_frontend_add_dock()` is put in deprecation.
182+
183+
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 (`mod_name`) to avoid conflicts.
184+
185+
So the following method will be added to libobs to access modules `mod_name`:
186+
```c++
187+
EXPORT const char *obs_get_module_mod_name(obs_module_t *module);
188+
```
189+
190+
### Add a dock
191+
```c++
192+
/* takes QWidget */
193+
#define obs_frontend_add_dock_2(title, unique_name, widget) \
194+
obs_frontend_add_module_dock(obs_current_module(), title, \
195+
unique_name, widget)
196+
EXPORT void obs_frontend_add_module_dock(obs_module_t *module,
197+
const char *title,
198+
const char *unique_name,
199+
void *widget);
200+
```
201+
202+
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.
203+
204+
Default height and width would based on the widget actual size except if `"defaultWidth"` and `"defaultHeight"` properties are set with the [`setProperty()`][7] method on the widget.
205+
206+
Minimum sizes used by the dock are based on the widget ones.
207+
208+
### Remove a dock
209+
```c++
210+
#define obs_frontend_remove_dock(unique_name) \
211+
obs_frontend_remove_module_dock(obs_current_module(), unique_name)
212+
EXPORT void obs_frontend_remove_module_dock(obs_module_t *module,
213+
const char *unique_name);
214+
```
215+
216+
This allow the plugin to remove a dock added earlier.
217+
218+
### Add a browser dock
219+
```c++
220+
EXPORT bool obs_frontend_is_browser_available(void);
221+
```
222+
223+
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.
224+
225+
```c++
226+
#define obs_frontend_add_browser_dock(dock_params, browser_params) \
227+
obs_frontend_add_module_browser_dock(obs_current_module(), \
228+
dock_params, browser_params)
229+
EXPORT void obs_frontend_add_module_browser_dock(obs_module_t *module,
230+
struct obs_frontend_browser_dock_params *dock_params,
231+
struct obs_frontend_browser_params *browser_params);
232+
```
233+
234+
This allow the plugin to add a dock with a QCefWidget as widget.
235+
236+
If browser docks are added in the module load or post load steps, their parameter will be stored and those will be really added later.
237+
238+
The QCefWidget will get parameters from this structure:
239+
240+
```c++
241+
struct obs_frontend_browser_params {
242+
const char *url;
243+
bool enable_cookie;
244+
struct dstr startup_script;
245+
DARRAY(char *) force_popup_urls;
246+
};
247+
```
248+
249+
- `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.
250+
- `struct dstr startup_script` allow to set a startup script for the QCefWidget.
251+
- `DARRAY(char *) force_popup_urls` allow to set a list of url forced to popup.
252+
253+
And the dock itself will get parameters from this structure:
254+
255+
```c++
256+
struct obs_frontend_browser_dock_params {
257+
const char *unique_name;
258+
const char *title;
259+
int default_width;
260+
int default_height;
261+
int min_width;
262+
int min_height;
263+
};
264+
```
265+
266+
`obs_frontend_remove_dock()` can be used to remove the browser dock.
267+
268+
### Remove browser cookie
269+
Since enabling cookie is posible, allowing to delete those is required.
270+
271+
```c++
272+
EXPORT void obs_frontend_delete_browser_cookie(const char *url);
273+
```
274+
275+
This will remove cookie related to the given URL.
276+
277+
### Add a dock layouts
278+
```c++
279+
#define obs_frontend_add_dock_layout(title, unique_name, xml_layout) \
280+
obs_frontend_add_module_dock_layout(obs_current_module(), \
281+
title, unique_name, xml_layout)
282+
EXPORT void obs_frontend_add_module_dock_layout(obs_module_t *module,
283+
const char *title,
284+
const char *unique_name,
285+
const char *xml_layout);
286+
```
287+
288+
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.
289+
290+
### Get a list of docks layouts
291+
```c++
292+
EXPORT char **obs_frontend_get_dock_layouts(void);
293+
```
294+
295+
This allow the plugin to get a list of registered dock layouts from the UI.
296+
297+
### Set a registered dock layout
298+
```c++
299+
EXPORT void obs_frontend_set_dock_layouts(const char *layout_name);
300+
```
301+
302+
This allow the plugin to set a registered dock layouts to the UI, the asked name should come directly from the get list method.
303+
304+
### Remove a dock layouts
305+
```c++
306+
#define obs_frontend_remove_dock_layout(unique_name) \
307+
obs_frontend_remove_module_dock_layout(obs_current_module(), \
308+
unique_name)
309+
EXPORT void obs_frontend_remove_module_dock_layout(obs_module_t *module,
310+
const char *unique_name);
311+
```
312+
313+
This allow the plugin to remove a dock layouts from the UI.
314+
315+
### Add a entirely custom dock
316+
```c++
317+
/* takes ads::CDockWidget */
318+
#define obs_frontend_add_custom_dock(unique_name, dock) \
319+
obs_frontend_add_module_custom_dock(obs_current_module(), \
320+
unique_name, dock)
321+
EXPORT void obs_frontend_add_module_custom_dock(obs_module_t *module,
322+
const char *unique_name,
323+
void *dock);
324+
```
325+
326+
Some plugin like [Sources Dock][6], do not add their docks to the Dock menu.
327+
328+
So this method allow to do this but requires the plugin to be link against ADS library.
329+
330+
And the dock will not have OBSAdvDock features.
331+
332+
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.
333+
334+
`obs_frontend_remove_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.
335+
336+
### Get the XML behind a registered dock layout
337+
*Method meant to allow a Dock Layout editor tool to exist*
338+
339+
```c++
340+
EXPORT char *obs_frontend_get_current_profile(const char *layout_name);
341+
```
342+
343+
This allow the plugin to get a registered dock layouts XML, the asked name should come directly from the get list method.
344+
345+
### Get the XML of the actual dock state
346+
*Method meant to allow a Dock Layout editor tool to exist*
347+
348+
```c++
349+
EXPORT char *obs_frontend_get_current_profile(const char *layout_name);
350+
```
351+
352+
This allow the plugin to get the dock states XML of the UI.
353+
354+
### Events addition
355+
- 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`.
356+
357+
- 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.
358+
359+
## About making dock states future proof
360+
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.
361+
362+
We could take the saved `"advDockState"` and uncompress to edit the XML, to make it compatible with the change.
363+
364+
# Drawbacks
365+
We can't convert old `"dockState"` to the new dock system.
366+
367+
# Additional Information
368+
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.**
369+
370+
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.
371+
372+
My WIP branches:
373+
- [Dock separation][2]
374+
- [Central widget separation][8]
375+
- [Switch to ADS][3]
376+
- [Frontend API to add ADS dock][4]
377+
- [Frontend API to add custom ADS dock][5]
378+
379+
[1]: https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System
380+
[2]: https://github.com/tytan652/obs-studio/tree/dock_separation
381+
[3]: https://github.com/tytan652/obs-studio/tree/advanced_docking
382+
[4]: https://github.com/tytan652/obs-studio/tree/ads_frontend_api
383+
[5]: https://github.com/tytan652/obs-studio/tree/add_custom_dock
384+
[6]: https://obsproject.com/forum/resources/source-dock.1317/
385+
[7]: https://doc.qt.io/qt-5/qobject.html#setProperty
386+
[8]: https://github.com/tytan652/obs-studio/tree/central_widget_separation
9.15 KB
Loading
9.24 KB
Loading
9.02 KB
Loading

0 commit comments

Comments
 (0)