frontend: Update Plugin Manager v1 UI

This commit is contained in:
Warchamp7
2025-09-12 14:18:27 -04:00
committed by Ryan Foster
parent 139fd801ee
commit 9a3fe95211
7 changed files with 517 additions and 64 deletions
+7 -4
View File
@@ -118,6 +118,7 @@ SceneFilters="Open Scene Filters"
List="List"
Grid="Grid"
Automatic="Automatic"
ComingSoon="Coming Soon"
# warning for plugin load failures
PluginsFailedToLoad.Title="Plugin Load Error"
@@ -1622,10 +1623,12 @@ MultitrackVideo.IncompatibleSettings.AudioChannels="%1 is not currently compatib
MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Audio → General → Channels] needs to be set to '%1'"
MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 requires multiple different settings for [Audio → General → Channels]"
#Plugin ManagerW
# Plugin Manager window
Basic.OpenPluginManager="Plugin Manager"
PluginManager="Plugin Manager"
PluginManager.HelpText="Plugin Manager"
PluginManager.Restart="Restart OBS?"
PluginManager.NeedsRestart="To apply these changes, OBS needs to restart. Do you want to restart now?"
PluginManager.Restart="OBS Studio must be restarted to apply plugin changes"
PluginManager.MissingPlugin="[PLUGIN NOT FOUND]"
PluginManager.Section.Discover="Browse"
PluginManager.Section.Manage="Installed"
PluginManager.Section.Updates="Updates"
PluginManager.Section.Manage.Title="Manage Enabled Plugins"
+84 -21
View File
@@ -98,8 +98,8 @@
--spacing_base_value: calc(2 + calc(var(--obsPadding) / 2));
--highlight_width: 1px;
--highlight_color: var(--primary_lighter);
--highlight_color: var(--primary_lighter);
/* TODO: Better Accessibility focus state */
/* TODO: Move Accessibilty Colors to Theme config system */
--border_highlight: "transparent";
@@ -144,7 +144,7 @@
--scrollbar_size: 12px;
--settings_scrollbar_size: calc(var(--scrollbar_size) + 9px);
--scrollbar_handle: var(--grey4);
--scrollbar_bg: var(--grey6);
--scrollbar_hover: var(--grey3);
--scrollbar_down: var(--grey2);
@@ -275,6 +275,11 @@
background-color: var(--bg_base);
}
.text-title {
font-size: var(--font_large);
font-weight: bold;
}
.text-heading {
font-size: var(--font_heading);
font-weight: bold;
@@ -305,7 +310,8 @@
}
.frame-notice {
background: var(--bg_preview);
background: var(--grey8);
border: 1px solid var(--grey6);
border-radius: var(--border_radius);
padding: var(--padding_xlarge) var(--padding_large);
}
@@ -314,6 +320,67 @@
padding: var(--padding_large) 0px;
}
.dialog-container {
padding: var(--padding_large) var(--padding_xlarge);
}
.dialog-frame {
background-color: var(--grey6);
border-radius: var(--border_radius);
border: 1px solid var(--border_color);
margin: var(--spacing_base);
}
.dialog-frame > QWidget {
margin: var(--spacing_base) 0;
}
.button-primary {
background-color: var(--primary_dark);
border-color: var(--primary);
outline: none;
}
.button-primary:hover,
.button-primary:focus {
background-color: var(--primary);
border-color: var(--white3);
}
.button-primary:hover {
border-color: var(--primary_light);
}
.margin {
margin: var(--spacing_base);
}
.margin-x {
margin-left: var(--spacing_base);
margin-right: var(--spacing_base);
}
.margin-left {
margin-left: var(--spacing_base);
}
.margin-right {
margin-right: var(--spacing_base);
}
.margin-y {
margin-top: var(--spacing_base);
margin-bottom: var(--spacing_base);
}
.margin-top {
margin-top: var(--spacing_base);
}
.margin-bottom {
margin-bottom: var(--spacing_base);
}
/* Icon Overrides */
.icon-plus {
@@ -608,17 +675,14 @@ QMenu::item:selected,
QListView::item:selected,
QListWidget::item:selected {
background-color: var(--primary);
border-color: var(--primary);
border-color: var(--primary_light);
}
QMenu::item:hover,
QListView::item:hover,
QListWidget::item:hover,
QMenu::item:selected:hover,
QListView::item:selected:hover,
QListWidget::item:selected:hover {
background-color: var(--primary_light);
border-color: var(--highlight_color);
QListWidget::item:hover {
background: var(--grey4);
border: 1px solid var(--grey3);
}
QMenu::item:focus,
@@ -627,7 +691,15 @@ QListWidget::item:focus,
QMenu::item:selected:focus,
QListView::item:selected:focus,
QListWidget::item:selected:focus {
border: 1px solid var(--border_highlight);
border: 1px solid var(--white3);
}
QMenu::item:selected:hover,
QListView::item:selected:hover,
QListWidget::item:selected:hover {
background: var(--primary_light);
border: 1px solid var(--primary_lighter);
color: var(--text);
}
QListView::item:disabled,
@@ -655,7 +727,6 @@ QListWidget QLineEdit:focus {
/* Settings QList */
OBSBasicSettings QScrollBar:vertical {
width: var(--scrollbar_size);
width: var(--settings_scrollbar_size);
margin-left: 9px;
}
@@ -882,14 +953,6 @@ QScrollBar::handle:disabled {
border-color: transparent;
}
OBSDock QListWidget {
padding: 0;
}
OBSDock QListWidget::item {
margin: 0 var(--spacing_base);
}
OBSDock QScrollBar {
border-radius: 0;
}
-5
View File
@@ -110,11 +110,6 @@ QDockWidget > QWidget {
padding: var(--padding_xlarge);
}
SceneTree::item,
SourceTreeItem {
border-width: 0px;
}
QMenu::item {
padding: var(--padding_menu_y) var(--padding_menu);
}
+337 -22
View File
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>850</width>
<width>861</width>
<height>400</height>
</rect>
</property>
@@ -16,31 +16,346 @@
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>6</number>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="dialogInner">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>PluginManager.HelpText</string>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="class" stdset="0">
<string>dialog-container</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="sectionFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="class" stdset="0">
<string>dialog-frame</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="sectionList">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="modulesFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="class" stdset="0">
<string>dialog-container dialog-frame</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="text">
<string>PluginManager.Section.Manage.Title</string>
</property>
<property name="class" stdset="0">
<string>text-title</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="modulesListContainer">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="widgetResizable">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="class" stdset="0">
<string/>
</property>
<widget class="QWidget" name="modulesListContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>689</width>
<height>50</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="modulesList">
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QLabel" name="manageRestartLabel">
<property name="text">
<string>PluginManager.Restart</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="class" stdset="0">
<string>frame-notice</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="footer">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="class" stdset="0">
<string>dialog-container</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QListWidget" name="modulesList"/>
</item>
</layout>
</widget>
<resources/>
+2 -2
View File
@@ -297,8 +297,8 @@ void PluginManager::open()
}
if (changed) {
QMessageBox::StandardButton button = OBSMessageBox::question(
main, QTStr("PluginManager.Restart"), QTStr("PluginManager.NeedsRestart"));
QMessageBox::StandardButton button =
OBSMessageBox::question(main, QTStr("Restart"), QTStr("NeedsRestart"));
if (button == QMessageBox::Yes) {
restart = true;
+81 -10
View File
@@ -40,12 +40,40 @@ PluginManagerWindow::PluginManagerWindow(std::vector<ModuleInfo> const &modules,
ui->setupUi(this);
ui->modulesListContainer->viewport()->setAutoFillBackground(false);
ui->modulesListContents->setAutoFillBackground(false);
// Set up sidebar entries
ui->sectionList->clear();
ui->sectionList->setSelectionMode(QAbstractItemView::SingleSelection);
connect(ui->sectionList, &QListWidget::itemSelectionChanged, this,
&PluginManagerWindow::sectionSelectionChanged);
QListWidgetItem *browse = new QListWidgetItem(QTStr("PluginManager.Section.Discover"));
browse->setFlags(browse->flags() & ~Qt::ItemIsEnabled);
browse->setFlags(browse->flags() & ~Qt::ItemIsSelectable);
browse->setToolTip(QTStr("ComingSoon"));
ui->sectionList->addItem(browse);
QListWidgetItem *installed = new QListWidgetItem(QTStr("PluginManager.Section.Manage"));
ui->sectionList->addItem(installed);
QListWidgetItem *updates = new QListWidgetItem(QTStr("PluginManager.Section.Updates"));
updates->setFlags(updates->flags() & ~Qt::ItemIsEnabled);
updates->setFlags(updates->flags() & ~Qt::ItemIsSelectable);
updates->setToolTip(QTStr("ComingSoon"));
ui->sectionList->addItem(updates);
setSection(ui->sectionList->indexFromItem(installed));
std::sort(modules_.begin(), modules_.end(), [](const ModuleInfo &a, const ModuleInfo &b) {
std::string aName = !a.display_name.empty() ? a.display_name : a.module_name;
std::string bName = !b.display_name.empty() ? b.display_name : b.module_name;
return aName < bName;
});
int row = 0;
for (auto &metadata : modules_) {
std::string id = metadata.module_name;
// Check if the module is missing:
@@ -56,24 +84,67 @@ PluginManagerWindow::PluginManagerWindow(std::vector<ModuleInfo> const &modules,
if (missing) {
name += " " + QTStr("PluginManager.MissingPlugin");
}
auto item = new QListWidgetItem(name);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(metadata.enabled ? Qt::Checked : Qt::Unchecked);
auto item = new QCheckBox(name);
item->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
item->setChecked(metadata.enabled);
if (!metadata.enabledAtLaunch) {
item->setProperty("class", "text-muted");
}
if (missing) {
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
item->setEnabled(false);
}
ui->modulesList->addItem(item);
ui->modulesList->layout()->addWidget(item);
connect(item, &QCheckBox::toggled, this, [this, row](bool checked) {
modules_[row].enabled = checked;
ui->manageRestartLabel->setVisible(isEnabledPluginsChanged());
});
row++;
}
connect(ui->modulesList, &QListWidget::itemChanged, this, [this](QListWidgetItem *item) {
auto row = ui->modulesList->row(item);
bool checked = item->checkState() == Qt::Checked;
modules_[row].enabled = checked;
});
ui->modulesList->adjustSize();
ui->modulesListContents->adjustSize();
ui->manageRestartLabel->setVisible(isEnabledPluginsChanged());
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
void PluginManagerWindow::sectionSelectionChanged()
{
auto selected = ui->sectionList->selectedItems();
if (selected.count() != 1) {
setSection(activeSectionIndex);
} else {
auto selectionIndex = ui->sectionList->indexFromItem(selected.first());
setSection(selectionIndex);
}
}
void PluginManagerWindow::setSection(QPersistentModelIndex index)
{
if (ui->sectionList->itemFromIndex(index)) {
activeSectionIndex = index;
ui->sectionList->setCurrentIndex(index);
}
}
bool PluginManagerWindow::isEnabledPluginsChanged()
{
bool result = false;
for (auto &metadata : modules_) {
if (metadata.enabledAtLaunch != metadata.enabled) {
result = true;
break;
}
}
return result;
}
}; // namespace OBS
@@ -35,6 +35,12 @@ public:
private:
std::vector<ModuleInfo> modules_;
void sectionSelectionChanged();
QPersistentModelIndex activeSectionIndex;
void setSection(QPersistentModelIndex index);
bool isEnabledPluginsChanged();
};
}; // namespace OBS