Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: Reorganise and refactor entire frontend codebase #11622

Open
wants to merge 39 commits into
base: master
Choose a base branch
from

Conversation

PatTheMav
Copy link
Member

Description

This PR reorganises the frontend codebase to enable a switch of the project to common good practices for development large GUI applications.

The actual implementations of frontend functionality are unchanged, with the exception of cases where the new organisation of source files required them to get the project in a compilable state again.

Caution

This PR must be merged via a merge commit, as extra effort has been taken to ensure the blame history of the new files will be able to access the history of the old files. A rebase will prohibit git from figuring out these connections.

Motivation and Context

The frontend of OBS Studio is written in C++ and based on Qt6, for which good practices have evolved that are distinct from development practices common in library or pure C-based development. These practices include approaches to file naming, file structuring, class design, and header design, with the goal of improving maintainability and discoverability, as well as speed improvements for incremental builds and compiler performance.

For this refactor, the following set of rules were established and applied to the entire frontend codebase:

  • Frontend directory structure follows the concept of "building blocks" that make up the entirety of the application, with each class or interface put in a corresponding subdirectory
  • C++ class names use CapitalCase, class methods use camelCase
  • C++ class source files carry the name of their classes
  • C++ class headers contain only their own class' definition
  • C++ class headers only include other classes' interfaces (via their own header files) that the compiler needs, otherwise forward declarations are used

Additional rules that should be established but haven't been applied in this PR are:

Important

The following rules should be followed moving forward but have not been applied to this PR yet to keep the amount of actual code changes to a very low level.

Those changes will be applied in a later PR.

  • Free-standing functions are put in an anonymous namespace (the suggested practice for C++ code instead of static functions
    • This also solves the issue of free-standing functions being sprinkled throughout the source code and puts them in a single self-contained space. This also makes it obvious which utility functions might already exist and re-use them instead of adding yet another variant as a new static function in another translation unit.
  • The inline decoration should be used according to its actual meaning in C++: That a function definition is provided "in line" (it has no direct bearing on the code inlining behaviour of compilers). As such it should never(!) be used in source files and should only ever be used in header files, and even there it should be used with all its associated caveats applied.
  • Free-standing functions should use camelCase, not snake_case (which instead should be used to clearly identify C functions vs C++ functions)
  • Avoid unnecessary abbreviations, clearly spell out words (e.g. no AdvAudio, but AdvancedAudio).
    • This applies not only to class names but also function/method names
  • Use a single variant of capitalisation (not Youtube and YouTube in the same codebase
  • Don't use ambiguous method names like Active or Nudge, make their purpose explicit by their name itself, e.g. nudgePreviewItem or isOutputActive
    • Use Get, Set, and Is prefixes to clearly communicate whether a method gets, sets, or gets a state/status.
  • Don't rely on magic global variables or extern symbols that also "magically" have to exist in the global namespace. Encapsulate data with functionality and use APIs to access data or delegate functionality to encapsulated classes entirely.

Moving forward the principle to Respect Levels Of Abstraction should be followed not only in code architecture but also method naming.

Notably there is no good reason to update all existing code to these practices, instead they
will need to be applied to any new code and to changes made to existing code.

Note

As this PR does not rename actual classes or change implementations, some idiosyncrasies of the current code base are made more apparent by this change:

Some classes carry an OBS or OBSBasic prefix, some carry no such prefix at all. Some classes have overlay generic names (e.g. DelButton, or WorkerThread). This is by design as it will hopefully spur maintainers to clean this up if only to achieve consistency in class naming.

Notably these changes also have a positive effect on incremental compile times: Header files should be seen as "class interface definitions" that are needed for code to understand the memory layout and API of another class. Such headers should only be included in other headers sparingly and instead be limited to source files as best as possible.

A single header file per class also avoids the issue of triggering recompilation of a whole set of source code just because one of many class definitions in the same header has been changed. This also improves parallelisation in build tools and improves speed of recompilation efforts by Qt's moc precompiler (as source and header files themselves become smaller).

Unfortunately the single massive OBSBasic class with its equally massive header file persists, which is also included by almost all other frontend code in one form or another. A better architecture for the frontend code could avoid this pitfall, but for the time being a single change in OBSBasic's header will trigger recompilation of a large amount of frontend code.

How Has This Been Tested?

Successfully compiled and ran OBS Studio on macOS 15, Windows 11, and Ubuntu 24.04 with browser sources enabled and disabled and service integrations enabled as well as disabled.

Types of changes

  • 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.
  • I have included updates to all appropriate documentation.

frontend/OBSStudioAPI.hpp Show resolved Hide resolved
<string>label-preview-title</string>
<string>previewProgramLabels</string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Warchamp7 might have an opinion on this, but I think CSS/QSS class names should be singular (not plural). Also, if changing this, wouldn't the class rule in the theme files need to be changed? Or was this changed by mistake?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This string is in essence a CSS class name yes. Changing this would necessitate changes to the obt/ovt files and could be a breaking change to any custom themes people have made.

It is pretty common to use kebab-case for CSS classes and I have tried to be consistent about naming schemes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's an automatic change through rebasing though, the only classes that needed to be changed are the actual C++ class names in the customwidgets tree.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow.

There's a distinction here between the C++ class the widget is tied to, and the dynamic property class on the widget, which is used for styling purposes.

The thing changed here is a dynamic property that seems like an accidental modification.

Existing line on master
The styling rule in our obt file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I'm saying, yes. The line should not have changed and the only reason it did was because git was able to apply the change when I rebased it to master.

@@ -29,6 +34,7 @@ static constexpr const char *INGESTION_STARTED = "INGESTION_STARTED";
static constexpr const char *INGESTION_STOPPED = "INGESTION_STOPPED";

YouTubeAppDock::YouTubeAppDock(const QString &title) : BrowserDock(title), dockBrowser(nullptr)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This linebreak seems accidental.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rip the most upsettingly named file in the entire program.

frontend/OBSStudioAPI.cpp Show resolved Hide resolved
@@ -0,0 +1,27 @@
include_guard(DIRECTORY)

find_package(nlohmann_json REQUIRED)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
find_package(nlohmann_json REQUIRED)
find_package(nlohmann_json 3.11 REQUIRED)

This is the line in the original file.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks like a error from rebasing, was too easy for git to just remove it again.

find_package(Python REQUIRED COMPONENTS Interpreter Development)
target_link_libraries(obs-studio PRIVATE Python::Python)
target_link_options(obs-studio PRIVATE LINKER:-no-as-needed)
endif()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is missing the additions from 7e1e60c (#11410).

target_compile_definitions(
obs-studio
PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}" $<$<BOOL:${ENABLE_PORTABLE_CONFIG}>:ENABLE_PORTABLE_CONFIG>
PRIVATE USE_XDG OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}" $<$<BOOL:${ENABLE_PORTABLE_CONFIG}>:ENABLE_PORTABLE_CONFIG>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

USE_XDG was removed in c928fac (#11409).

find_package(nlohmann_json 3.11 REQUIRED)
find_package(nlohmann_json REQUIRED)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we removing this version requirement on purpose?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

#include <ui-config.h>
#include <qt-wrappers.hpp>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <ui-config.h>
#include <qt-wrappers.hpp>
#include <qt-wrappers.hpp>
#include <ui-config.h>

If alphabetizing, use this order. Otherwise, ignore.

class QWidget;
class QString;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swap if alphabetizing. Ignore if this order is specific.

#include <vector>
#include <string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <vector>
#include <string>
#include <string>
#include <vector>

#include "obf.h"
#include <stdbool.h>

#include "obf.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the expected order here was:

  1. corresponding .h file
  2. stdlib

See frontend/utility/audio-encoders.cpp for example.

#include <utility/platform.hpp>
#include <widgets/OBSBasic.hpp>

#include "OBSProjector.hpp"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this usually be the first include?

@WizardCM WizardCM added Enhancement Improvement to existing functionality Code Cleanup Non-breaking change which makes code smaller or more readable labels Dec 14, 2024
frontend/components/ColorSourceToolbar.cpp Show resolved Hide resolved
frontend/components/ComboSelectToolbar.cpp Show resolved Hide resolved
frontend/components/GameCaptureToolbar.cpp Show resolved Hide resolved
Comment on lines 8 to 11
class OBSSourceLabel;
class QCheckBox;

class OBSSourceLabel;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OBSSourceLabel is here twice.

#include "AutoConfigStreamPage.hpp"
#include "AutoConfigStartPage.hpp"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include "AutoConfigStreamPage.hpp"
#include "AutoConfigStartPage.hpp"
#include "AutoConfigStartPage.hpp"
#include "AutoConfigStreamPage.hpp"

If alphabetical. If other order, ignore.

#include "ui_AutoConfigVideoPage.h"
#include "ui_AutoConfigStreamPage.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include "ui_AutoConfigVideoPage.h"
#include "ui_AutoConfigStreamPage.h"
#include "ui_AutoConfigStreamPage.h"
#include "ui_AutoConfigVideoPage.h"

If alphabetical. If other order, ignore.

frontend/utility/BasicOutputHandler.cpp Show resolved Hide resolved
frontend/utility/BasicOutputHandler.cpp Show resolved Hide resolved
Comment on lines 27 to 29
#include <vector>
#include <string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <vector>
#include <string>
#include <string>
#include <vector>

frontend/widgets/ColorSelect.cpp Show resolved Hide resolved
frontend/components/DelButton.hpp Show resolved Hide resolved
frontend/components/EditWidget.hpp Show resolved Hide resolved
frontend/utility/ExtraBrowsersDelegate.hpp Show resolved Hide resolved
frontend/dialogs/OBSMissingFiles.cpp Show resolved Hide resolved
@@ -0,0 +1,12 @@
#pragma once
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing license header?

@@ -0,0 +1,15 @@
#pragma once
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing license headers.

@@ -0,0 +1,18 @@
#pragma once
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing license headers.

@@ -0,0 +1,33 @@
#pragma once
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing license header.

@@ -171,6 +136,8 @@ class OBSBasicSettings : public QDialog {
void SaveEncoder(QComboBox *combo, const char *section, const char *value);

bool ResFPSValid(obs_service_resolution *res_list, size_t res_count, int max_fps);

// TODO: Remove, orphaned method
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just leaving a review note to raise awareness of this todo.

frontend/components/VolumeSlider.cpp Show resolved Hide resolved
Copy link
Member

@RytoEX RytoEX left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finished a first pass of all files.

#include <ui-config.h>
#include <qt-wrappers.hpp>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <ui-config.h>
#include <qt-wrappers.hpp>
#include <qt-wrappers.hpp>
#include <ui-config.h>

#include <ui-config.h>
#include <qt-wrappers.hpp>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <ui-config.h>
#include <qt-wrappers.hpp>
#include <qt-wrappers.hpp>
#include <ui-config.h>

vector<FFmpegFormat> GetSupportedFormats()
{
vector<FFmpegFormat> formats;
const AVOutputFormat *output_format;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surprised the avformat.h include wasn't needed for this.

@@ -0,0 +1,18 @@
#include "OBSTranslator.hpp"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OBSTranslator comes from UI\obs-app.cpp/hpp which has a license header.

@@ -0,0 +1,14 @@
#pragma once
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OBSTranslator comes from UI\obs-app.cpp/hpp which has a license header.

#include <docks/YouTubeAppDock.hpp>
#include <dialogs/OBSYoutubeActions.hpp>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <docks/YouTubeAppDock.hpp>
#include <dialogs/OBSYoutubeActions.hpp>
#include <dialogs/OBSYoutubeActions.hpp>
#include <docks/YouTubeAppDock.hpp>

Unless include order is specific.

frontend/widgets/OBSQTDisplay.cpp Show resolved Hide resolved
Comment on lines +21 to +22
#include <utility/OBSEventFilter.hpp>
#include <utility/display-helpers.hpp>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other files seem to do case-insensitive alphabetization. I don't yet have a strong preference, so I just wanted to see if there is an intended order.

Comment on lines 10 to 12
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QVBoxLayout>

If alphabetical is preferred, order like this.

Comment on lines 18 to 23
#include <utility/platform.hpp>
#include <dialogs/OBSLogViewer.hpp>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <utility/platform.hpp>
#include <dialogs/OBSLogViewer.hpp>
#include <dialogs/OBSLogViewer.hpp>
#include <utility/platform.hpp>

Alphabetize unless order is intentional.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Code Cleanup Non-breaking change which makes code smaller or more readable Enhancement Improvement to existing functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants