Skip to content

Commit 917e59b

Browse files
committed
Add RFC: Switch to Advanced Docking System
1 parent e5c925b commit 917e59b

File tree

4 files changed

+342
-0
lines changed

4 files changed

+342
-0
lines changed
Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
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+
10+
Note: The "central widget" design is kept.
11+
12+
# Motivation
13+
14+
Provide a better dock system to end-users.
15+
16+
# Design
17+
**DISCLAIMER**: I kept the "central widget" design, switching to a non-"central widget" one is not part of this RFC.
18+
19+
## Main window dock separation
20+
To make OBS Studio compatible with ADS, Controls, Transitions, Mixer, Sources, Scenes docks need to be separated from the main window.
21+
22+
And the central widget of main window should also be separated as well to become the central dock.
23+
24+
So the central widget and docks will be separated as widget and then inserted in ADS dock, and the docks inside the dock manager.
25+
26+
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.
27+
28+
Each widget class will be put as a friend class in OBSBasic to allow access to private slot and avoid moving them as public.
29+
30+
### Controls widget
31+
*Future ADS Controls dock widget*
32+
33+
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.
34+
35+
This class will require to have OBSBasic as a friend class because:
36+
- The streaming hotkey pair rely on the stream button enabled/disabled state.
37+
- The recording hotkey pair rely on the record button checked state.
38+
- The pause hotkey pair rely on the pause button pointer and checked state.
39+
- Pause and unpause recording function rely on the pause button pointer.
40+
41+
42+
*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.*
43+
44+
### Transistions widget
45+
*Future ADS Transitions dock widget*
46+
47+
The transition duration will become an attribute of OBSBasic, and signal and slots will be setup to synchromise it with the dock spinbox.
48+
49+
In a first verion this class will require to have OBSBasic as a friend class because the transition combobox store the transitions themselves.
50+
51+
### Mixer widget
52+
*Future ADS Mixer dock widget*
53+
54+
Easily separable, no need to set OBSBasic as a friend class of this widget.
55+
56+
### Sources widget
57+
*Future ADS Sources dock widget*
58+
59+
OBSBasic heavily rely on the SourceTree from the widget, so for now OBSBasic will access it by being a friend class of the widget.
60+
61+
### Scenes widget
62+
*Future ADS Scenes dock widget*
63+
64+
OBSBasic heavily rely on the SceneTree from the widget, so for now OBSBasic will access it by being a friend class of the widget.
65+
66+
### Central widget
67+
*Future ADS Central dock widget*
68+
69+
This class will have OBSBasicPreview as a friend class.
70+
71+
This class will be a friend class of OBSBasic also to have access to some gs_vertbuffer_t type attributes.
72+
73+
## ADS
74+
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.
75+
76+
Some work on themes CSS will be required.
77+
78+
### Floating docks titlebar on X11
79+
On X11, ADS provide two type of titlebar for floating docks:
80+
81+
- Native
82+
83+
[![Native titlebar under GNOME](./0047-switch-to-advanced-docking-system/linux_native_floating_dock_titlebar.png)]()
84+
85+
- QWidget based (WIP CSS)
86+
87+
[![QWidget titlebar](./0047-switch-to-advanced-docking-system/linux_qwidget_floating_dock_titlebar.png)]()
88+
89+
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.
90+
91+
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".
92+
93+
### Dock state
94+
*The per profile dock state feature is not taken into account.*
95+
96+
In the global config (`global.ini`).
97+
- `"dockState"` is kept for for backward compatiblity and no longer overwitten. It will be used if the following state is not present.
98+
- `"windowState"` is the state of the main window, since it does not store only the state of legacy docks.
99+
- `"advDockState"` is the state of the dock manager which contain only the states of any ADS dock.
100+
101+
Service integrations only save `"advDockProfile"`
102+
103+
ADS dock state is compressed by default but behind the scene it's XML.
104+
105+
### OBSAdvDock
106+
OBSAdvDock is a class which inherit `ads::CDockWidget` class.
107+
108+
This class has a constructor which require a QWidget and setup some things arround the widget and set some connections.
109+
110+
It adds a warning message when closing a dock from a close dock button.
111+
112+
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.
113+
114+
### Custom Browser docks and BrowserAdvDock
115+
The custom browser docks feature is modified to use BrowserAdvDock which inherit OBSAdvDock.
116+
117+
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.
118+
119+
Each of those browser dock is named `extraBrowser_$UUID` where `$UUID` is replaced by the dock UUID to have a really unique name.
120+
121+
### Service integration docks
122+
123+
Like custom browser docks, those integration are modified to use BrowserAdvDock. But their names is stored in the QStringList for plugins extra docks.
124+
125+
Those docks are named `obs-$SERVICE_$DOCK_NAME` where `$SERVICE` is the service name in lowercase and `$DOCK_NAME` the name of the dock.
126+
127+
`"dockState"` will be imported from the integration config if `"windowState"` is not present.
128+
129+
### Reset UI action
130+
1. Legacy dock are hidden
131+
2. The default state written in XML is applied
132+
3. Each not shown OBSAdvDock/BrowserAdvDock dock have its position and size reseted.
133+
134+
## Legacy dock
135+
The frontend API method `obs_frontend_add_dock()` is put in deprecation.
136+
137+
And dock added through this method are added to a sub-menu named `Legacy dock` of the Dock menu.
138+
139+
[![Legacy dock menu](./0047-switch-to-advanced-docking-system/legacy_dock.png)]()
140+
141+
When openning a legacy dock, a message will appear explaining that those docks will not meld wery well with "new" docks.
142+
143+
The state of those are saved through `"windowState"` global config.
144+
145+
## Per profile dock state
146+
Move the `"advDockState"` from global config to the profile. `"windowState"` is kept global.
147+
148+
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.
149+
150+
## Layouts management
151+
Add the feature, to switch between registered/saved dock layouts:
152+
- Though a sub-menu in the dock menu
153+
- Through a hotkey
154+
- Through the frontend API
155+
156+
OBS Studio could provide layouts, the default count as one.
157+
158+
Plugins could also register their own XML layouts through the frontend API.
159+
160+
Users could be able to save their own layouts. Those layouts will have their name prefixed with`user_` to avoid name conflicts.
161+
162+
Note: ADS perspective feature is not directly used because it relies heavily on QSettings.
163+
164+
## Frontend API
165+
Like said earlier, the method `obs_frontend_add_dock()` is put in deprecation.
166+
167+
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.
168+
169+
### Add a dock
170+
```c++
171+
/* takes QWidget */
172+
#define obs_frontend_add_adv_dock(title, unique_name, dock) \
173+
obs_frontend_add_module_adv_dock(obs_current_module(), title, \
174+
unique_name, widget)
175+
EXPORT void obs_frontend_add_module_adv_dock(obs_module_t *module,
176+
const char *title,
177+
const char *unique_name,
178+
void *widget);
179+
```
180+
181+
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.
182+
183+
Default height and width could be set through `"defaultHeight"` `"defaultHeight"` with the [`setProperty()`][7] method.
184+
185+
Minimum sizes used by the dock are based on the widget ones.
186+
187+
### Remove a dock
188+
```c++
189+
#define obs_frontend_remove_adv_dock(unique_name) \
190+
obs_frontend_remove_module_adv_dock(obs_current_module(), unique_name)
191+
EXPORT void obs_frontend_remove_module_adv_dock(obs_module_t *module,
192+
const char *unique_name);
193+
```
194+
195+
This allow the plugin to remove a dock added earlier.
196+
197+
### Add a browser dock
198+
```c++
199+
#define obs_frontend_add_adv_browser_dock(dock_params, browser_params) \
200+
obs_frontend_add_module_adv_browser_dock(obs_current_module(), \
201+
dock_params, browser_params)
202+
EXPORT bool obs_frontend_add_module_adv_browser_dock(obs_module_t *module,
203+
struct obs_frontend_browser_dock_params *dock_params,
204+
struct obs_frontend_browser_params *browser_params)
205+
```
206+
207+
This allow the plugin to add a dock with a QCefWidget as widget. Return false if the browser dock could not be created.
208+
209+
The QCefWidget will get parameters from this structure:
210+
211+
```c++
212+
struct obs_frontend_browser_params {
213+
const char *url;
214+
bool enable_cookie;
215+
struct dstr startup_script;
216+
DARRAY(char *) force_popup_urls;
217+
};
218+
```
219+
220+
- `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.
221+
- `struct dstr startup_script` allow to set a startup script for the QCefWidget.
222+
- `DARRAY(char *) force_popup_urls` allow to set a list of url forced to popup.
223+
224+
And the dock itself will get parameters from this structure:
225+
226+
```c++
227+
struct obs_frontend_browser_dock_params {
228+
const char *unique_name;
229+
const char *title;
230+
int default_width;
231+
int default_height;
232+
int min_width;
233+
int min_height;
234+
};
235+
```
236+
237+
`obs_frontend_remove_adv_dock()` can be used to remove the browser dock.
238+
239+
### Add a dock layouts
240+
```c++
241+
#define obs_frontend_add_adv_dock_layout(title, unique_name, xml_layout) \
242+
obs_frontend_add_module_adv_dock_layout(obs_current_module(), \
243+
title, unique_name, xml_layout)
244+
EXPORT void obs_frontend_add_module_adv_dock_layout(obs_module_t *module,
245+
const char *title,
246+
const char *unique_name,
247+
const char *xml_layout);
248+
```
249+
250+
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.
251+
252+
### Get a list of docks layouts
253+
```c++
254+
EXPORT char **obs_frontend_get_adv_dock_layouts(void);
255+
```
256+
257+
This allow the plugin to get a list of registered dock layouts from the UI.
258+
259+
### Set a registered dock layout
260+
```c++
261+
EXPORT void obs_frontend_set_adv_dock_layouts(const char *layout_name);
262+
```
263+
264+
This allow the plugin to set a registered dock layouts to the UI, the asked name should come directly from the get list method.
265+
266+
### Remove a dock layouts
267+
```c++
268+
#define obs_frontend_remove_adv_dock_layout(unique_name) \
269+
obs_frontend_remove_module_adv_dock_layout(obs_current_module(), \
270+
unique_name)
271+
EXPORT void obs_frontend_remove_module_adv_dock_layout(obs_module_t *module,
272+
const char *unique_name);
273+
```
274+
275+
This allow the plugin to remove a dock layouts from the UI.
276+
277+
### Add a entirely custom dock
278+
```c++
279+
/* takes ads::CDockWidget */
280+
#define obs_frontend_add_custom_adv_dock(unique_name, dock) \
281+
obs_frontend_add_module_custom_adv_dock(obs_current_module(), \
282+
unique_name, dock)
283+
EXPORT void obs_frontend_add_module_custom_adv_dock(obs_module_t *module,
284+
const char *unique_name,
285+
void *dock);
286+
```
287+
288+
Some plugin like [Sources Dock][6], do not add their docks to the Dock menu.
289+
290+
So this method allow to do this but requires the plugin to be link against ADS library.
291+
292+
And the dock will not have OBSAdvDock features.
293+
294+
`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.
295+
296+
### Get the XML behind a registered dock layout
297+
*Method meant to allow a Dock Layout editor tool to exist*
298+
299+
```c++
300+
EXPORT char *obs_frontend_get_current_profile(const char *layout_name);
301+
```
302+
303+
This allow the plugin to get a registered dock layouts XML, the asked name should come directly from the get list method.
304+
305+
### Get the XML of the actual dock state
306+
*Method meant to allow a Dock Layout editor tool to exist*
307+
308+
```c++
309+
EXPORT char *obs_frontend_get_current_profile(const char *layout_name);
310+
```
311+
312+
This allow the plugin to get the dock states XML of the UI.
313+
314+
### Events addition
315+
- 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`.
316+
317+
- 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.
318+
319+
## About making dock states future proof
320+
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.
321+
322+
We could take the saved `"advDockState"` and uncompress to edit the XML, to make it compatible with the change.
323+
324+
# Drawbacks
325+
We can't convert old `"dockState"` to the new dock system.
326+
327+
# Additional Information
328+
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.**
329+
330+
My WIP branches:
331+
- [Main window dock separation][2]
332+
- [Switch to ADS][3]
333+
- [Frontend API to add ADS dock][4]
334+
- [Frontend API to add custom ADS dock][5]
335+
336+
[1]: https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System
337+
[2]: https://github.com/tytan652/obs-studio/tree/main_win_separation
338+
[3]: https://github.com/tytan652/obs-studio/tree/advanced_docking
339+
[4]: https://github.com/tytan652/obs-studio/tree/ads_frontend_api
340+
[5]: https://github.com/tytan652/obs-studio/tree/add_custom_dock
341+
[6]: https://obsproject.com/forum/resources/source-dock.1317/
342+
[7]: https://doc.qt.io/qt-5/qobject.html#setProperty
Loading
Loading
Loading

0 commit comments

Comments
 (0)