mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-05-24 08:20:42 -04:00
frontend: Add new Idian widgets
Co-Authored-By: derrod <rodney@rodney.io>
This commit is contained in:
@@ -63,6 +63,7 @@ include(cmake/feature-twitch.cmake)
|
||||
include(cmake/feature-restream.cmake)
|
||||
include(cmake/feature-youtube.cmake)
|
||||
include(cmake/feature-whatsnew.cmake)
|
||||
include(cmake/feature-idian-playground.cmake)
|
||||
|
||||
add_subdirectory(plugins)
|
||||
|
||||
|
||||
9
frontend/cmake/feature-idian-playground.cmake
Normal file
9
frontend/cmake/feature-idian-playground.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
option(ENABLE_WIDGET_PLAYGROUND "Enable building custom widget demo window" OFF)
|
||||
|
||||
if(ENABLE_WIDGET_PLAYGROUND)
|
||||
target_sources(
|
||||
obs-studio
|
||||
PRIVATE forms/OBSIdianPlayground.ui dialogs/OBSIdianPlayground.hpp dialogs/OBSIdianPlayground.cpp
|
||||
)
|
||||
target_enable_feature(obs-studio "Widget Playground" ENABLE_WIDGET_PLAYGROUND)
|
||||
endif()
|
||||
@@ -81,4 +81,22 @@ target_sources(
|
||||
components/VolumeSlider.hpp
|
||||
components/WindowCaptureToolbar.cpp
|
||||
components/WindowCaptureToolbar.hpp
|
||||
components/idian/OBSActionRow.cpp
|
||||
components/idian/OBSActionRow.hpp
|
||||
components/idian/OBSCheckBox.cpp
|
||||
components/idian/OBSCheckBox.hpp
|
||||
components/idian/OBSComboBox.cpp
|
||||
components/idian/OBSComboBox.hpp
|
||||
components/idian/OBSDoubleSpinBox.cpp
|
||||
components/idian/OBSDoubleSpinBox.hpp
|
||||
components/idian/OBSGroupBox.cpp
|
||||
components/idian/OBSGroupBox.hpp
|
||||
components/idian/OBSIdianWidget.hpp
|
||||
components/idian/OBSPropertiesList.cpp
|
||||
components/idian/OBSPropertiesList.hpp
|
||||
components/idian/OBSSpinBox.cpp
|
||||
components/idian/OBSSpinBox.hpp
|
||||
components/idian/OBSToggleSwitch.cpp
|
||||
components/idian/OBSToggleSwitch.hpp
|
||||
components/idian/obs-widgets.hpp
|
||||
)
|
||||
|
||||
@@ -37,4 +37,6 @@ target_sources(
|
||||
dialogs/OBSRemux.hpp
|
||||
dialogs/OBSWhatsNew.cpp
|
||||
dialogs/OBSWhatsNew.hpp
|
||||
dialogs/OBSIdianPlayground.cpp
|
||||
dialogs/OBSIdianPlayground.hpp
|
||||
)
|
||||
|
||||
372
frontend/components/idian/OBSActionRow.cpp
Normal file
372
frontend/components/idian/OBSActionRow.cpp
Normal file
@@ -0,0 +1,372 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QApplication>
|
||||
#include <QSizePolicy>
|
||||
|
||||
#include "OBSActionRow.hpp"
|
||||
#include <util/base.h>
|
||||
#include <QSvgRenderer>
|
||||
|
||||
OBSActionRowWidget::OBSActionRowWidget(QWidget *parent) : OBSActionRow(parent)
|
||||
{
|
||||
layout = new QGridLayout(this);
|
||||
layout->setVerticalSpacing(0);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
labelLayout = new QVBoxLayout();
|
||||
labelLayout->setSpacing(0);
|
||||
labelLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setLayout(layout);
|
||||
|
||||
layout->setColumnMinimumWidth(0, 0);
|
||||
layout->setColumnStretch(0, 0);
|
||||
layout->setColumnStretch(1, 40);
|
||||
layout->setColumnStretch(2, 55);
|
||||
|
||||
nameLabel = new QLabel();
|
||||
nameLabel->setVisible(false);
|
||||
OBSIdianUtils::addClass(nameLabel, "title");
|
||||
|
||||
descriptionLabel = new QLabel();
|
||||
descriptionLabel->setVisible(false);
|
||||
OBSIdianUtils::addClass(descriptionLabel, "description");
|
||||
|
||||
labelLayout->addWidget(nameLabel);
|
||||
labelLayout->addWidget(descriptionLabel);
|
||||
|
||||
layout->addLayout(labelLayout, 0, 1, Qt::AlignLeft);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::setPrefix(QWidget *w, bool auto_connect)
|
||||
{
|
||||
setSuffixEnabled(false);
|
||||
|
||||
_prefix = w;
|
||||
|
||||
if (auto_connect)
|
||||
this->connectBuddyWidget(w);
|
||||
|
||||
_prefix->setParent(this);
|
||||
layout->addWidget(_prefix, 0, 0, Qt::AlignLeft);
|
||||
layout->setColumnStretch(0, 3);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::setSuffix(QWidget *w, bool auto_connect)
|
||||
{
|
||||
setPrefixEnabled(false);
|
||||
|
||||
_suffix = w;
|
||||
|
||||
if (auto_connect)
|
||||
this->connectBuddyWidget(w);
|
||||
|
||||
_suffix->setParent(this);
|
||||
layout->addWidget(_suffix, 0, 2, Qt::AlignRight | Qt::AlignVCenter);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::setPrefixEnabled(bool enabled)
|
||||
{
|
||||
if (!_prefix)
|
||||
return;
|
||||
if (enabled)
|
||||
setSuffixEnabled(false);
|
||||
if (enabled == _prefix->isEnabled() && enabled == _prefix->isVisible())
|
||||
return;
|
||||
|
||||
layout->setColumnStretch(0, enabled ? 3 : 0);
|
||||
_prefix->setEnabled(enabled);
|
||||
_prefix->setVisible(enabled);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::setSuffixEnabled(bool enabled)
|
||||
{
|
||||
if (!_suffix)
|
||||
return;
|
||||
if (enabled)
|
||||
setPrefixEnabled(false);
|
||||
if (enabled == _suffix->isEnabled() && enabled == _suffix->isVisible())
|
||||
return;
|
||||
|
||||
_suffix->setEnabled(enabled);
|
||||
_suffix->setVisible(enabled);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::setTitle(QString name)
|
||||
{
|
||||
nameLabel->setText(name);
|
||||
setAccessibleName(name);
|
||||
showTitle(true);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::setDescription(QString description)
|
||||
{
|
||||
descriptionLabel->setText(description);
|
||||
setAccessibleDescription(description);
|
||||
showDescription(true);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::showTitle(bool visible)
|
||||
{
|
||||
nameLabel->setVisible(visible);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::showDescription(bool visible)
|
||||
{
|
||||
descriptionLabel->setVisible(visible);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::setBuddy(QWidget *widget)
|
||||
{
|
||||
buddyWidget = widget;
|
||||
OBSIdianUtils::addClass(widget, "row-buddy");
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::setChangeCursor(bool change)
|
||||
{
|
||||
changeCursor = change;
|
||||
OBSIdianUtils::toggleClass("cursor-pointer", change);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::enterEvent(QEnterEvent *event)
|
||||
{
|
||||
if (!isEnabled())
|
||||
return;
|
||||
|
||||
if (changeCursor) {
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
}
|
||||
|
||||
OBSIdianUtils::addClass("hover");
|
||||
|
||||
if (buddyWidget)
|
||||
OBSIdianUtils::repolish(buddyWidget);
|
||||
|
||||
if (hasPrefix() || hasSuffix()) {
|
||||
OBSIdianUtils::polishChildren();
|
||||
}
|
||||
|
||||
OBSActionRow::enterEvent(event);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::leaveEvent(QEvent *event)
|
||||
{
|
||||
OBSIdianUtils::removeClass("hover");
|
||||
|
||||
if (buddyWidget)
|
||||
OBSIdianUtils::repolish(buddyWidget);
|
||||
|
||||
if (hasPrefix() || hasSuffix()) {
|
||||
OBSIdianUtils::polishChildren();
|
||||
}
|
||||
|
||||
OBSActionRow::leaveEvent(event);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() & Qt::LeftButton) {
|
||||
emit clicked();
|
||||
}
|
||||
QFrame::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::keyReleaseEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->key() == Qt::Key_Space || event->key() == Qt::Key_Enter) {
|
||||
emit clicked();
|
||||
}
|
||||
QFrame::keyReleaseEvent(event);
|
||||
}
|
||||
|
||||
void OBSActionRowWidget::connectBuddyWidget(QWidget *widget)
|
||||
{
|
||||
setAccessibleName(nameLabel->text());
|
||||
setFocusProxy(widget);
|
||||
setBuddy(widget);
|
||||
|
||||
/* If element is an OBSToggleSwitch and checkable, forward
|
||||
* clicks to the widget */
|
||||
OBSToggleSwitch *obsToggle = dynamic_cast<OBSToggleSwitch *>(widget);
|
||||
if (obsToggle && obsToggle->isCheckable()) {
|
||||
setChangeCursor(true);
|
||||
|
||||
connect(this, &OBSActionRowWidget::clicked, obsToggle, &OBSToggleSwitch::click);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If element is any other QAbstractButton subclass,
|
||||
* and checkable, forward clicks to the widget. */
|
||||
QAbstractButton *button = dynamic_cast<QAbstractButton *>(widget);
|
||||
if (button && button->isCheckable()) {
|
||||
setChangeCursor(true);
|
||||
|
||||
connect(this, &OBSActionRowWidget::clicked, button, &QAbstractButton::click);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If element is an OBSComboBox, clicks toggle the dropdown. */
|
||||
OBSComboBox *obsCombo = dynamic_cast<OBSComboBox *>(widget);
|
||||
if (obsCombo) {
|
||||
setChangeCursor(true);
|
||||
|
||||
connect(this, &OBSActionRowWidget::clicked, obsCombo, &OBSComboBox::togglePopup);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Button for expanding a collapsible ActionRow
|
||||
*/
|
||||
OBSActionRowExpandButton::OBSActionRowExpandButton(QWidget *parent) : QAbstractButton(parent), OBSIdianUtils(this)
|
||||
{
|
||||
setCheckable(true);
|
||||
}
|
||||
|
||||
void OBSActionRowExpandButton::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
UNUSED_PARAMETER(event);
|
||||
|
||||
QStyleOptionButton opt;
|
||||
opt.initFrom(this);
|
||||
QPainter p(this);
|
||||
|
||||
bool checked = isChecked();
|
||||
|
||||
opt.state.setFlag(QStyle::State_On, checked);
|
||||
opt.state.setFlag(QStyle::State_Off, !checked);
|
||||
|
||||
opt.state.setFlag(QStyle::State_Sunken, checked);
|
||||
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||
|
||||
style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, &p, this);
|
||||
style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, &p, this);
|
||||
}
|
||||
|
||||
/*
|
||||
* ActionRow variant that can be expanded to show another properties list
|
||||
*/
|
||||
OBSCollapsibleRowWidget::OBSCollapsibleRowWidget(const QString &name, QWidget *parent) : OBSActionRow(parent)
|
||||
{
|
||||
layout = new QVBoxLayout;
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
setLayout(layout);
|
||||
|
||||
rowWidget = new OBSCollapsibleRowFrame();
|
||||
rowLayout = new QHBoxLayout();
|
||||
rowLayout->setContentsMargins(0, 0, 0, 0);
|
||||
rowLayout->setSpacing(0);
|
||||
rowWidget->setLayout(rowLayout);
|
||||
|
||||
actionRow = new OBSActionRowWidget();
|
||||
actionRow->setTitle(name);
|
||||
actionRow->setChangeCursor(false);
|
||||
|
||||
rowLayout->addWidget(actionRow);
|
||||
|
||||
propertyList = new OBSPropertiesList(this);
|
||||
propertyList->setVisible(false);
|
||||
|
||||
expandFrame = new QFrame();
|
||||
btnLayout = new QHBoxLayout();
|
||||
btnLayout->setContentsMargins(0, 0, 0, 0);
|
||||
btnLayout->setSpacing(0);
|
||||
expandFrame->setLayout(btnLayout);
|
||||
OBSIdianUtils::addClass(expandFrame, "btn-frame");
|
||||
actionRow->setBuddy(expandFrame);
|
||||
|
||||
expandButton = new OBSActionRowExpandButton(this);
|
||||
btnLayout->addWidget(expandButton);
|
||||
|
||||
rowLayout->addWidget(expandFrame);
|
||||
|
||||
layout->addWidget(rowWidget);
|
||||
layout->addWidget(propertyList);
|
||||
|
||||
actionRow->setFocusProxy(expandButton);
|
||||
|
||||
connect(expandButton, &QAbstractButton::clicked, this, &OBSCollapsibleRowWidget::toggleVisibility);
|
||||
|
||||
connect(actionRow, &OBSActionRowWidget::clicked, expandButton, &QAbstractButton::click);
|
||||
}
|
||||
|
||||
OBSCollapsibleRowWidget::OBSCollapsibleRowWidget(const QString &name, const QString &desc, QWidget *parent)
|
||||
: OBSCollapsibleRowWidget(name, parent)
|
||||
{
|
||||
actionRow->setDescription(desc);
|
||||
}
|
||||
|
||||
void OBSCollapsibleRowWidget::setCheckable(bool check)
|
||||
{
|
||||
checkable = check;
|
||||
|
||||
if (checkable && !toggleSwitch) {
|
||||
propertyList->setEnabled(false);
|
||||
OBSIdianUtils::polishChildren(propertyList);
|
||||
|
||||
toggleSwitch = new OBSToggleSwitch(false);
|
||||
|
||||
actionRow->setSuffix(toggleSwitch, false);
|
||||
connect(toggleSwitch, &OBSToggleSwitch::toggled, propertyList, &OBSPropertiesList::setEnabled);
|
||||
}
|
||||
|
||||
if (!checkable && toggleSwitch) {
|
||||
propertyList->setEnabled(true);
|
||||
OBSIdianUtils::polishChildren(propertyList);
|
||||
|
||||
actionRow->suffix()->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void OBSCollapsibleRowWidget::toggleVisibility()
|
||||
{
|
||||
bool visible = !propertyList->isVisible();
|
||||
|
||||
propertyList->setVisible(visible);
|
||||
expandButton->setChecked(visible);
|
||||
}
|
||||
|
||||
void OBSCollapsibleRowWidget::addRow(OBSActionRow *actionRow)
|
||||
{
|
||||
propertyList->addRow(actionRow);
|
||||
}
|
||||
|
||||
OBSCollapsibleRowFrame::OBSCollapsibleRowFrame(QWidget *parent) : QFrame(parent), OBSIdianUtils(this) {}
|
||||
|
||||
void OBSCollapsibleRowFrame::enterEvent(QEnterEvent *event)
|
||||
{
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
|
||||
OBSIdianUtils::addClass("hover");
|
||||
OBSIdianUtils::polishChildren();
|
||||
|
||||
QWidget::enterEvent(event);
|
||||
}
|
||||
|
||||
void OBSCollapsibleRowFrame::leaveEvent(QEvent *event)
|
||||
{
|
||||
OBSIdianUtils::removeClass("hover");
|
||||
OBSIdianUtils::polishChildren();
|
||||
|
||||
QWidget::leaveEvent(event);
|
||||
}
|
||||
215
frontend/components/idian/OBSActionRow.hpp
Normal file
215
frontend/components/idian/OBSActionRow.hpp
Normal file
@@ -0,0 +1,215 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QFrame>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QScrollArea>
|
||||
#include <QMouseEvent>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include "OBSIdianWidget.hpp"
|
||||
#include "OBSPropertiesList.hpp"
|
||||
#include "OBSToggleSwitch.hpp"
|
||||
#include "OBSComboBox.hpp"
|
||||
#include "OBSSpinBox.hpp"
|
||||
#include "OBSDoubleSpinBox.hpp"
|
||||
|
||||
/**
|
||||
* Base class mostly so adding stuff to a list is easier
|
||||
*/
|
||||
class OBSActionRow : public QFrame, public OBSIdianUtils {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OBSActionRow(QWidget *parent = nullptr) : QFrame(parent), OBSIdianUtils(this) { setAccessibleName(""); };
|
||||
};
|
||||
|
||||
/**
|
||||
* Proxy for QScrollArea for QSS styling
|
||||
*/
|
||||
class OBSScrollArea : public QScrollArea {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OBSScrollArea(QWidget *parent = nullptr) : QScrollArea(parent) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic OBS row widget containing one or more controls
|
||||
*/
|
||||
class OBSActionRowWidget : public OBSActionRow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OBSActionRowWidget(QWidget *parent = nullptr);
|
||||
|
||||
void setPrefix(QWidget *w, bool autoConnect = true);
|
||||
void setSuffix(QWidget *w, bool autoConnect = true);
|
||||
|
||||
bool hasPrefix() { return _prefix; }
|
||||
bool hasSuffix() { return _suffix; }
|
||||
|
||||
QWidget *prefix() const { return _prefix; }
|
||||
QWidget *suffix() const { return _suffix; }
|
||||
|
||||
void setPrefixEnabled(bool enabled);
|
||||
void setSuffixEnabled(bool enabled);
|
||||
|
||||
void setTitle(QString name);
|
||||
void setDescription(QString description);
|
||||
|
||||
void showTitle(bool visible);
|
||||
void showDescription(bool visible);
|
||||
|
||||
void setBuddy(QWidget *w);
|
||||
|
||||
void setChangeCursor(bool change);
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
void enterEvent(QEnterEvent *) override;
|
||||
void leaveEvent(QEvent *) override;
|
||||
void mouseReleaseEvent(QMouseEvent *) override;
|
||||
void keyReleaseEvent(QKeyEvent *) override;
|
||||
bool hasDescription() const { return descriptionLabel != nullptr; }
|
||||
|
||||
void focusInEvent(QFocusEvent *event) override
|
||||
{
|
||||
OBSIdianUtils::showKeyFocused(event);
|
||||
QFrame::focusInEvent(event);
|
||||
}
|
||||
|
||||
void focusOutEvent(QFocusEvent *event) override
|
||||
{
|
||||
OBSIdianUtils::hideKeyFocused(event);
|
||||
QFrame::focusOutEvent(event);
|
||||
}
|
||||
|
||||
private:
|
||||
QGridLayout *layout;
|
||||
|
||||
QVBoxLayout *labelLayout = nullptr;
|
||||
|
||||
QLabel *nameLabel = nullptr;
|
||||
QLabel *descriptionLabel = nullptr;
|
||||
|
||||
QWidget *_prefix = nullptr;
|
||||
QWidget *_suffix = nullptr;
|
||||
|
||||
QWidget *buddyWidget = nullptr;
|
||||
|
||||
void connectBuddyWidget(QWidget *widget);
|
||||
bool changeCursor = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Collapsible row expand button
|
||||
*/
|
||||
class OBSActionRowExpandButton : public QAbstractButton, public OBSIdianUtils {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QPixmap extendDown;
|
||||
QPixmap extendUp;
|
||||
|
||||
friend class OBSCollapsibleRowWidget;
|
||||
|
||||
protected:
|
||||
explicit OBSActionRowExpandButton(QWidget *parent = nullptr);
|
||||
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
|
||||
void focusInEvent(QFocusEvent *event) override
|
||||
{
|
||||
OBSIdianUtils::showKeyFocused(event);
|
||||
QAbstractButton::focusInEvent(event);
|
||||
}
|
||||
|
||||
void focusOutEvent(QFocusEvent *event) override
|
||||
{
|
||||
OBSIdianUtils::hideKeyFocused(event);
|
||||
QAbstractButton::focusOutEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
class OBSCollapsibleRowFrame : protected QFrame, protected OBSIdianUtils {
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
explicit OBSCollapsibleRowFrame(QWidget *parent = nullptr);
|
||||
|
||||
void enterEvent(QEnterEvent *) override;
|
||||
void leaveEvent(QEvent *) override;
|
||||
|
||||
void focusInEvent(QFocusEvent *event) override
|
||||
{
|
||||
OBSIdianUtils::showKeyFocused(event);
|
||||
QWidget::focusInEvent(event);
|
||||
}
|
||||
|
||||
void focusOutEvent(QFocusEvent *event) override
|
||||
{
|
||||
OBSIdianUtils::hideKeyFocused(event);
|
||||
QWidget::focusOutEvent(event);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class OBSCollapsibleRowWidget;
|
||||
};
|
||||
|
||||
/**
|
||||
* Collapsible Generic OBS property container
|
||||
*/
|
||||
class OBSCollapsibleRowWidget : public OBSActionRow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OBSCollapsibleRowWidget(const QString &name, QWidget *parent = nullptr);
|
||||
OBSCollapsibleRowWidget(const QString &name, const QString &desc = nullptr, QWidget *parent = nullptr);
|
||||
|
||||
void setCheckable(bool check);
|
||||
bool isCheckable() { return checkable; }
|
||||
|
||||
void addRow(OBSActionRow *actionRow);
|
||||
|
||||
private:
|
||||
void toggleVisibility();
|
||||
|
||||
QPixmap extendDown;
|
||||
QPixmap extendUp;
|
||||
|
||||
QVBoxLayout *layout;
|
||||
OBSCollapsibleRowFrame *rowWidget;
|
||||
QHBoxLayout *rowLayout;
|
||||
|
||||
OBSActionRowWidget *actionRow;
|
||||
QFrame *expandFrame;
|
||||
QHBoxLayout *btnLayout;
|
||||
OBSActionRowExpandButton *expandButton;
|
||||
OBSPropertiesList *propertyList;
|
||||
|
||||
OBSToggleSwitch *toggleSwitch = nullptr;
|
||||
bool checkable = false;
|
||||
};
|
||||
20
frontend/components/idian/OBSCheckBox.cpp
Normal file
20
frontend/components/idian/OBSCheckBox.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2024 by Taylor Giampaolo <warchamp7@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "OBSCheckBox.hpp"
|
||||
|
||||
OBSCheckBox::OBSCheckBox(QWidget *parent) : QCheckBox(parent), OBSIdianUtils(this) {}
|
||||
42
frontend/components/idian/OBSCheckBox.hpp
Normal file
42
frontend/components/idian/OBSCheckBox.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2024 by Taylor Giampaolo <warchamp7@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QCheckBox>
|
||||
|
||||
#include "OBSIdianWidget.hpp"
|
||||
|
||||
class OBSCheckBox : public QCheckBox, public OBSIdianUtils {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
OBSCheckBox(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void focusInEvent(QFocusEvent *e) override
|
||||
{
|
||||
OBSIdianUtils::showKeyFocused(e);
|
||||
QAbstractButton::focusInEvent(e);
|
||||
}
|
||||
|
||||
void focusOutEvent(QFocusEvent *e) override
|
||||
{
|
||||
OBSIdianUtils::hideKeyFocused(e);
|
||||
QAbstractButton::focusOutEvent(e);
|
||||
}
|
||||
};
|
||||
65
frontend/components/idian/OBSComboBox.cpp
Normal file
65
frontend/components/idian/OBSComboBox.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2024 by Taylor Giampaolo <warchamp7@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "OBSActionRow.hpp"
|
||||
#include "OBSComboBox.hpp"
|
||||
#include <QTimer>
|
||||
#include <util/base.h>
|
||||
|
||||
#define UNUSED_PARAMETER(param) (void)param
|
||||
|
||||
OBSComboBox::OBSComboBox(QWidget *parent) : QComboBox(parent), OBSIdianUtils(this) {}
|
||||
|
||||
void OBSComboBox::showPopup()
|
||||
{
|
||||
if (allowOpeningPopup) {
|
||||
allowOpeningPopup = false;
|
||||
QComboBox::showPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void OBSComboBox::hidePopup()
|
||||
{
|
||||
// It would be nice to find a better way to do this.
|
||||
//
|
||||
// When the dropdown is closed, block attempts to open it
|
||||
// again for a short time. This is so clicking a GenericRow
|
||||
// with the dropdown open doesn't immediately close and re-open it.
|
||||
// I have tried all sorts of things involving handling mouse events
|
||||
// and event filters on both GenericRow and ComboBox.
|
||||
//
|
||||
// All my efforts have failed so we get this instead.
|
||||
allowOpeningPopup = false;
|
||||
QTimer::singleShot(120, this, [=]() { allowOpeningPopup = true; });
|
||||
|
||||
QComboBox::hidePopup();
|
||||
}
|
||||
|
||||
void OBSComboBox::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
blog(LOG_INFO, "OBSComboBox::mousePressEvent");
|
||||
QComboBox::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void OBSComboBox::togglePopup()
|
||||
{
|
||||
if (view()->isVisible()) {
|
||||
OBSComboBox::hidePopup();
|
||||
} else {
|
||||
OBSComboBox::showPopup();
|
||||
}
|
||||
}
|
||||
54
frontend/components/idian/OBSComboBox.hpp
Normal file
54
frontend/components/idian/OBSComboBox.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2024 by Taylor Giampaolo <warchamp7@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QAbstractItemView>
|
||||
|
||||
#include "OBSIdianWidget.hpp"
|
||||
|
||||
class OBSComboBox : public QComboBox, public OBSIdianUtils {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OBSComboBox(QWidget *parent = nullptr);
|
||||
|
||||
public Q_SLOTS:
|
||||
void togglePopup();
|
||||
|
||||
private:
|
||||
bool allowOpeningPopup = true;
|
||||
|
||||
protected:
|
||||
void showPopup() override;
|
||||
void hidePopup() override;
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
|
||||
void focusInEvent(QFocusEvent *e) override
|
||||
{
|
||||
OBSIdianUtils::showKeyFocused(e);
|
||||
QComboBox::focusInEvent(e);
|
||||
}
|
||||
|
||||
void focusOutEvent(QFocusEvent *e) override
|
||||
{
|
||||
OBSIdianUtils::hideKeyFocused(e);
|
||||
QComboBox::focusOutEvent(e);
|
||||
}
|
||||
};
|
||||
45
frontend/components/idian/OBSDoubleSpinBox.cpp
Normal file
45
frontend/components/idian/OBSDoubleSpinBox.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "OBSDoubleSpinBox.hpp"
|
||||
|
||||
OBSDoubleSpinBox::OBSDoubleSpinBox(QWidget *parent) : QFrame(parent)
|
||||
{
|
||||
layout = new QHBoxLayout();
|
||||
setLayout(layout);
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
decr = new QPushButton("-");
|
||||
decr->setObjectName("obsSpinBoxButton");
|
||||
layout->addWidget(decr);
|
||||
|
||||
setFocusProxy(decr);
|
||||
|
||||
sbox = new QDoubleSpinBox();
|
||||
sbox->setObjectName("obsSpinBox");
|
||||
sbox->setButtonSymbols(QAbstractSpinBox::NoButtons);
|
||||
sbox->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(sbox);
|
||||
|
||||
incr = new QPushButton("+");
|
||||
incr->setObjectName("obsSpinBoxButton");
|
||||
layout->addWidget(incr);
|
||||
|
||||
connect(decr, &QPushButton::pressed, sbox, &QDoubleSpinBox::stepDown);
|
||||
connect(incr, &QPushButton::pressed, sbox, &QDoubleSpinBox::stepUp);
|
||||
}
|
||||
38
frontend/components/idian/OBSDoubleSpinBox.hpp
Normal file
38
frontend/components/idian/OBSDoubleSpinBox.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <QLayout>
|
||||
#include <QSpinBox>
|
||||
#include <QPushButton>
|
||||
|
||||
class OBSDoubleSpinBox : public QFrame {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
OBSDoubleSpinBox(QWidget *parent = nullptr);
|
||||
|
||||
QDoubleSpinBox *spinBox() const { return sbox; }
|
||||
|
||||
private:
|
||||
QHBoxLayout *layout;
|
||||
QPushButton *decr;
|
||||
QPushButton *incr;
|
||||
QDoubleSpinBox *sbox;
|
||||
};
|
||||
122
frontend/components/idian/OBSGroupBox.cpp
Normal file
122
frontend/components/idian/OBSGroupBox.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "OBSIdianWidget.hpp"
|
||||
#include "OBSGroupBox.hpp"
|
||||
|
||||
OBSGroupBox::OBSGroupBox(QWidget *parent) : QFrame(parent), OBSIdianUtils(this)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
layout->setSpacing(0);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
headerContainer = new QWidget();
|
||||
headerLayout = new QHBoxLayout();
|
||||
headerLayout->setSpacing(0);
|
||||
headerLayout->setContentsMargins(0, 0, 0, 0);
|
||||
headerContainer->setLayout(headerLayout);
|
||||
OBSIdianUtils::addClass(headerContainer, "header");
|
||||
|
||||
labelContainer = new QWidget();
|
||||
labelLayout = new QVBoxLayout();
|
||||
labelLayout->setSpacing(0);
|
||||
labelLayout->setContentsMargins(0, 0, 0, 0);
|
||||
labelContainer->setLayout(labelLayout);
|
||||
labelContainer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
|
||||
|
||||
controlContainer = new QWidget();
|
||||
controlLayout = new QVBoxLayout();
|
||||
controlLayout->setSpacing(0);
|
||||
controlLayout->setContentsMargins(0, 0, 0, 0);
|
||||
controlContainer->setLayout(controlLayout);
|
||||
|
||||
headerLayout->addWidget(labelContainer);
|
||||
headerLayout->addWidget(controlContainer);
|
||||
|
||||
contentsContainer = new QWidget();
|
||||
contentsLayout = new QVBoxLayout();
|
||||
contentsLayout->setSpacing(0);
|
||||
contentsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
contentsContainer->setLayout(contentsLayout);
|
||||
OBSIdianUtils::addClass(contentsContainer, "contents");
|
||||
|
||||
layout->addWidget(headerContainer);
|
||||
layout->addWidget(contentsContainer);
|
||||
|
||||
propertyList = new OBSPropertiesList(this);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
contentsLayout->addWidget(propertyList);
|
||||
/*contentsContainer->setSizePolicy(policy);*/
|
||||
|
||||
nameLabel = new QLabel();
|
||||
OBSIdianUtils::addClass(nameLabel, "title");
|
||||
nameLabel->setVisible(false);
|
||||
labelLayout->addWidget(nameLabel);
|
||||
|
||||
descriptionLabel = new QLabel();
|
||||
OBSIdianUtils::addClass(descriptionLabel, "description");
|
||||
descriptionLabel->setVisible(false);
|
||||
labelLayout->addWidget(descriptionLabel);
|
||||
}
|
||||
|
||||
void OBSGroupBox::addRow(OBSActionRow *actionRow) const
|
||||
{
|
||||
propertyList->addRow(actionRow);
|
||||
}
|
||||
|
||||
void OBSGroupBox::setTitle(QString name)
|
||||
{
|
||||
nameLabel->setText(name);
|
||||
setAccessibleName(name);
|
||||
showTitle(true);
|
||||
}
|
||||
|
||||
void OBSGroupBox::setDescription(QString desc)
|
||||
{
|
||||
descriptionLabel->setText(desc);
|
||||
setAccessibleDescription(desc);
|
||||
showDescription(true);
|
||||
}
|
||||
|
||||
void OBSGroupBox::showTitle(bool visible)
|
||||
{
|
||||
nameLabel->setVisible(visible);
|
||||
}
|
||||
|
||||
void OBSGroupBox::showDescription(bool visible)
|
||||
{
|
||||
descriptionLabel->setVisible(visible);
|
||||
}
|
||||
|
||||
void OBSGroupBox::setCheckable(bool check)
|
||||
{
|
||||
checkable = check;
|
||||
|
||||
if (checkable && !toggleSwitch) {
|
||||
toggleSwitch = new OBSToggleSwitch(true);
|
||||
controlLayout->addWidget(toggleSwitch);
|
||||
connect(toggleSwitch, &OBSToggleSwitch::toggled, this,
|
||||
[=](bool checked) { propertyList->setEnabled(checked); });
|
||||
}
|
||||
|
||||
if (!checkable && toggleSwitch) {
|
||||
controlLayout->removeWidget(toggleSwitch);
|
||||
toggleSwitch->deleteLater();
|
||||
}
|
||||
}
|
||||
68
frontend/components/idian/OBSGroupBox.hpp
Normal file
68
frontend/components/idian/OBSGroupBox.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QLayout>
|
||||
#include <QLabel>
|
||||
#include <QWidget>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include "OBSActionRow.hpp"
|
||||
#include "OBSPropertiesList.hpp"
|
||||
#include "OBSToggleSwitch.hpp"
|
||||
|
||||
class OBSGroupBox : public QFrame, public OBSIdianUtils {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OBSGroupBox(QWidget *parent = nullptr);
|
||||
|
||||
OBSPropertiesList *properties() const { return propertyList; }
|
||||
|
||||
void addRow(OBSActionRow *actionRow) const;
|
||||
|
||||
void setTitle(QString name);
|
||||
void setDescription(QString desc);
|
||||
|
||||
void showTitle(bool visible);
|
||||
void showDescription(bool visible);
|
||||
|
||||
void setCheckable(bool check);
|
||||
bool isCheckable() { return checkable; }
|
||||
|
||||
private:
|
||||
QVBoxLayout *layout = nullptr;
|
||||
|
||||
QWidget *headerContainer = nullptr;
|
||||
QHBoxLayout *headerLayout = nullptr;
|
||||
QWidget *labelContainer = nullptr;
|
||||
QVBoxLayout *labelLayout = nullptr;
|
||||
QWidget *controlContainer = nullptr;
|
||||
QVBoxLayout *controlLayout = nullptr;
|
||||
|
||||
QWidget *contentsContainer = nullptr;
|
||||
QVBoxLayout *contentsLayout = nullptr;
|
||||
|
||||
QLabel *nameLabel = nullptr;
|
||||
QLabel *descriptionLabel = nullptr;
|
||||
|
||||
OBSPropertiesList *propertyList = nullptr;
|
||||
|
||||
OBSToggleSwitch *toggleSwitch = nullptr;
|
||||
bool checkable = false;
|
||||
};
|
||||
152
frontend/components/idian/OBSIdianWidget.hpp
Normal file
152
frontend/components/idian/OBSIdianWidget.hpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2024 by Taylor Giampaolo <warchamp7@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QStyle>
|
||||
#include <QFocusEvent>
|
||||
#include <QRegularExpression>
|
||||
|
||||
/*
|
||||
* Helpers for OBS Idian widgets
|
||||
*/
|
||||
|
||||
static const QRegularExpression classRegex = QRegularExpression("^[a-zA-Z][a-zA-Z0-9_-]*$");
|
||||
|
||||
class OBSIdianUtils {
|
||||
|
||||
static bool classNameIsValid(const QString &name)
|
||||
{
|
||||
QRegularExpressionMatch match = classRegex.match(name);
|
||||
return match.hasMatch();
|
||||
}
|
||||
|
||||
public:
|
||||
QWidget *parent = nullptr;
|
||||
|
||||
OBSIdianUtils(QWidget *w) { parent = w; }
|
||||
|
||||
/*
|
||||
* Set a custom property whenever the widget has
|
||||
* keyboard focus specifically
|
||||
*/
|
||||
void showKeyFocused(QFocusEvent *e)
|
||||
{
|
||||
if (e->reason() != Qt::MouseFocusReason && e->reason() != Qt::PopupFocusReason) {
|
||||
addClass("keyFocus");
|
||||
} else {
|
||||
removeClass("keyFocus");
|
||||
}
|
||||
}
|
||||
|
||||
void hideKeyFocused(QFocusEvent *e)
|
||||
{
|
||||
if (e->reason() != Qt::PopupFocusReason) {
|
||||
removeClass("keyFocus");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Force all children widgets to repaint
|
||||
*/
|
||||
void polishChildren() { polishChildren(parent); }
|
||||
|
||||
static void polishChildren(QWidget *widget)
|
||||
{
|
||||
for (QWidget *child : widget->findChildren<QWidget *>()) {
|
||||
repolish(child);
|
||||
}
|
||||
}
|
||||
|
||||
void repolish() { repolish(parent); }
|
||||
|
||||
static void repolish(QWidget *widget)
|
||||
{
|
||||
widget->style()->unpolish(widget);
|
||||
widget->style()->polish(widget);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds a style class to the widget
|
||||
*/
|
||||
void addClass(const QString &classname) { addClass(parent, classname); }
|
||||
|
||||
static void addClass(QWidget *widget, const QString &classname)
|
||||
{
|
||||
if (!classNameIsValid(classname)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant current = widget->property("class");
|
||||
|
||||
QStringList classList = current.toString().split(" ");
|
||||
if (classList.contains(classname)) {
|
||||
return;
|
||||
}
|
||||
|
||||
classList.removeDuplicates();
|
||||
classList.append(classname);
|
||||
|
||||
widget->setProperty("class", classList.join(" "));
|
||||
|
||||
repolish(widget);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes a style class from a widget
|
||||
*/
|
||||
void removeClass(const QString &classname) { removeClass(parent, classname); }
|
||||
|
||||
static void removeClass(QWidget *widget, const QString &classname)
|
||||
{
|
||||
if (!classNameIsValid(classname)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant current = widget->property("class");
|
||||
if (current.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList classList = current.toString().split(" ");
|
||||
if (!classList.contains(classname, Qt::CaseSensitive)) {
|
||||
return;
|
||||
}
|
||||
|
||||
classList.removeDuplicates();
|
||||
classList.removeAll(classname);
|
||||
|
||||
widget->setProperty("class", classList.join(" "));
|
||||
|
||||
repolish(widget);
|
||||
}
|
||||
|
||||
/*
|
||||
* Forces the addition or removal of a style class from a widget
|
||||
*/
|
||||
void toggleClass(const QString &classname, bool toggle) { toggleClass(parent, classname, toggle); }
|
||||
|
||||
static void toggleClass(QWidget *widget, const QString &classname, bool toggle)
|
||||
{
|
||||
if (toggle) {
|
||||
addClass(widget, classname);
|
||||
} else {
|
||||
removeClass(widget, classname);
|
||||
}
|
||||
}
|
||||
};
|
||||
80
frontend/components/idian/OBSPropertiesList.cpp
Normal file
80
frontend/components/idian/OBSPropertiesList.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <QStyle>
|
||||
|
||||
#include "OBSPropertiesList.hpp"
|
||||
#include "OBSActionRow.hpp"
|
||||
|
||||
OBSPropertiesList::OBSPropertiesList(QWidget *parent) : QFrame(parent)
|
||||
{
|
||||
layout = new QVBoxLayout();
|
||||
layout->setSpacing(0);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
|
||||
|
||||
rowsList = QList<OBSActionRow *>();
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
/* Note: This function takes ownership of the added widget
|
||||
* and it may be deleted when the properties list is destroyed
|
||||
* or the clear() method is called! */
|
||||
void OBSPropertiesList::addRow(OBSActionRow *actionRow)
|
||||
{
|
||||
// Add custom spacer once more than one element exists
|
||||
if (layout->count() > 0)
|
||||
layout->addWidget(new OBSPropertiesListSpacer(this));
|
||||
|
||||
// Custom properties to work around :first and :last not existing.
|
||||
if (!first) {
|
||||
OBSIdianUtils::addClass(actionRow, "first");
|
||||
first = actionRow;
|
||||
}
|
||||
|
||||
// Remove last property from existing last item
|
||||
if (last)
|
||||
OBSIdianUtils::removeClass(last, "last");
|
||||
|
||||
// Most recently added item is also always last
|
||||
OBSIdianUtils::addClass(actionRow, "last");
|
||||
last = actionRow;
|
||||
|
||||
actionRow->setParent(this);
|
||||
rowsList.append(actionRow);
|
||||
layout->addWidget(actionRow);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void OBSPropertiesList::clear()
|
||||
{
|
||||
rowsList.clear();
|
||||
first = nullptr;
|
||||
last = nullptr;
|
||||
QLayoutItem *item = layout->takeAt(0);
|
||||
|
||||
while (item) {
|
||||
if (item->widget())
|
||||
item->widget()->deleteLater();
|
||||
delete item;
|
||||
|
||||
item = layout->takeAt(0);
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
57
frontend/components/idian/OBSPropertiesList.hpp
Normal file
57
frontend/components/idian/OBSPropertiesList.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <QWidget>
|
||||
#include <QLayout>
|
||||
|
||||
#include "OBSIdianWidget.hpp"
|
||||
|
||||
class OBSActionRow;
|
||||
|
||||
class OBSPropertiesList : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OBSPropertiesList(QWidget *parent = nullptr);
|
||||
|
||||
void addRow(OBSActionRow *actionRow);
|
||||
void clear();
|
||||
|
||||
QList<OBSActionRow *> rows() const { return rowsList; }
|
||||
|
||||
private:
|
||||
OBSActionRow *first = nullptr;
|
||||
OBSActionRow *last = nullptr;
|
||||
|
||||
QVBoxLayout *layout;
|
||||
QList<OBSActionRow *> rowsList;
|
||||
};
|
||||
|
||||
/**
|
||||
* Spacer with only cosmetic functionality
|
||||
*/
|
||||
class OBSPropertiesListSpacer : public QFrame {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OBSPropertiesListSpacer(QWidget *parent = nullptr) : QFrame(parent)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
}
|
||||
};
|
||||
42
frontend/components/idian/OBSSpinBox.cpp
Normal file
42
frontend/components/idian/OBSSpinBox.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "OBSSpinBox.hpp"
|
||||
|
||||
OBSSpinBox::OBSSpinBox(QWidget *parent) : QFrame(parent)
|
||||
{
|
||||
layout = new QHBoxLayout();
|
||||
setLayout(layout);
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
decr = new QPushButton("-");
|
||||
decr->setObjectName("obsSpinBoxButton");
|
||||
layout->addWidget(decr);
|
||||
|
||||
sbox = new QSpinBox();
|
||||
sbox->setObjectName("obsSpinBox");
|
||||
sbox->setButtonSymbols(QAbstractSpinBox::NoButtons);
|
||||
layout->addWidget(sbox);
|
||||
|
||||
incr = new QPushButton("+");
|
||||
incr->setObjectName("obsSpinBoxButton");
|
||||
layout->addWidget(incr);
|
||||
|
||||
connect(decr, &QPushButton::pressed, sbox, &QSpinBox::stepDown);
|
||||
connect(incr, &QPushButton::pressed, sbox, &QSpinBox::stepUp);
|
||||
}
|
||||
38
frontend/components/idian/OBSSpinBox.hpp
Normal file
38
frontend/components/idian/OBSSpinBox.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <QLayout>
|
||||
#include <QSpinBox>
|
||||
#include <QPushButton>
|
||||
|
||||
class OBSSpinBox : public QFrame {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
OBSSpinBox(QWidget *parent = nullptr);
|
||||
|
||||
QSpinBox *spinBox() const { return sbox; }
|
||||
|
||||
private:
|
||||
QHBoxLayout *layout;
|
||||
QPushButton *decr;
|
||||
QPushButton *incr;
|
||||
QSpinBox *sbox;
|
||||
};
|
||||
247
frontend/components/idian/OBSToggleSwitch.cpp
Normal file
247
frontend/components/idian/OBSToggleSwitch.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "OBSToggleSwitch.hpp"
|
||||
#include <util/base.h>
|
||||
|
||||
#define UNUSED_PARAMETER(param) (void)param
|
||||
|
||||
static QColor blendColors(const QColor &color1, const QColor &color2, float ratio)
|
||||
{
|
||||
int r = color1.red() * (1 - ratio) + color2.red() * ratio;
|
||||
int g = color1.green() * (1 - ratio) + color2.green() * ratio;
|
||||
int b = color1.blue() * (1 - ratio) + color2.blue() * ratio;
|
||||
|
||||
return QColor(r, g, b, 255);
|
||||
}
|
||||
|
||||
OBSToggleSwitch::OBSToggleSwitch(QWidget *parent)
|
||||
: QAbstractButton(parent),
|
||||
animHandle(new QPropertyAnimation(this, "xpos", this)),
|
||||
animBgColor(new QPropertyAnimation(this, "blend", this)),
|
||||
OBSIdianUtils(this)
|
||||
{
|
||||
offPos = rect().width() / 2 - 18;
|
||||
onPos = rect().width() / 2 + 18;
|
||||
xPos = offPos;
|
||||
margin = 3;
|
||||
|
||||
setCheckable(true);
|
||||
setAccessibleName("ToggleSwitch");
|
||||
|
||||
installEventFilter(this);
|
||||
|
||||
connect(this, &OBSToggleSwitch::clicked, this, &OBSToggleSwitch::onClicked);
|
||||
|
||||
connect(animHandle, &QVariantAnimation::valueChanged, this, &OBSToggleSwitch::updateBackgroundColor);
|
||||
connect(animBgColor, &QVariantAnimation::valueChanged, this, &OBSToggleSwitch::updateBackgroundColor);
|
||||
}
|
||||
|
||||
OBSToggleSwitch::OBSToggleSwitch(bool defaultState, QWidget *parent) : OBSToggleSwitch(parent)
|
||||
{
|
||||
setChecked(defaultState);
|
||||
if (defaultState) {
|
||||
xPos = onPos;
|
||||
}
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::animateHandlePosition()
|
||||
{
|
||||
animHandle->setStartValue(xPos);
|
||||
|
||||
int endPos = onPos;
|
||||
|
||||
if ((!isDelayed() && !isChecked()) || (isDelayed() && !pendingStatus))
|
||||
endPos = offPos;
|
||||
|
||||
animHandle->setEndValue(endPos);
|
||||
|
||||
animHandle->setDuration(120);
|
||||
animHandle->start();
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::updateBackgroundColor()
|
||||
{
|
||||
QColor offColor = underMouse() ? backgroundInactiveHover : backgroundInactive;
|
||||
QColor onColor = underMouse() ? backgroundActiveHover : backgroundActive;
|
||||
|
||||
if (!isDelayed()) {
|
||||
int offset = isChecked() ? 0 : offPos;
|
||||
blend = (float)(xPos - offset) / (float)(onPos);
|
||||
}
|
||||
|
||||
QColor bg = blendColors(offColor, onColor, blend);
|
||||
|
||||
if (!isEnabled())
|
||||
bg = backgroundInactive;
|
||||
|
||||
setStyleSheet("background: " + bg.name());
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::changeEvent(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::EnabledChange) {
|
||||
OBSIdianUtils::toggleClass("disabled", !isEnabled());
|
||||
updateBackgroundColor();
|
||||
}
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
UNUSED_PARAMETER(e);
|
||||
|
||||
QStyleOptionButton opt;
|
||||
opt.initFrom(this);
|
||||
QPainter p(this);
|
||||
|
||||
bool showChecked = isChecked();
|
||||
if (isDelayed()) {
|
||||
showChecked = pendingStatus;
|
||||
}
|
||||
|
||||
opt.state.setFlag(QStyle::State_On, showChecked);
|
||||
opt.state.setFlag(QStyle::State_Off, !showChecked);
|
||||
|
||||
opt.state.setFlag(QStyle::State_Sunken, true);
|
||||
|
||||
style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, &p, this);
|
||||
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
p.setBrush(handleColor);
|
||||
p.drawEllipse(QRectF(xPos, margin, handleSize, handleSize));
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::showEvent(QShowEvent *e)
|
||||
{
|
||||
margin = (rect().height() - handleSize) / 2;
|
||||
|
||||
offPos = margin;
|
||||
onPos = rect().width() - handleSize - margin;
|
||||
|
||||
xPos = isChecked() ? onPos : offPos;
|
||||
|
||||
updateBackgroundColor();
|
||||
style()->polish(this);
|
||||
|
||||
QAbstractButton::showEvent(e);
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::click()
|
||||
{
|
||||
if (!isDelayed())
|
||||
QAbstractButton::click();
|
||||
|
||||
if (isChecked() == pendingStatus)
|
||||
setPending(!isChecked());
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::onClicked(bool checked)
|
||||
{
|
||||
if (delayed)
|
||||
return;
|
||||
|
||||
setPending(checked);
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::setStatus(bool status)
|
||||
{
|
||||
if (status == isChecked() && status == pendingStatus)
|
||||
return;
|
||||
|
||||
pendingStatus = status;
|
||||
setChecked(status);
|
||||
|
||||
if (isChecked()) {
|
||||
animBgColor->setStartValue(0.0f);
|
||||
animBgColor->setEndValue(1.0f);
|
||||
} else {
|
||||
animBgColor->setStartValue(1.0f);
|
||||
animBgColor->setEndValue(0.0f);
|
||||
}
|
||||
|
||||
animBgColor->setEasingCurve(QEasingCurve::InOutCubic);
|
||||
animBgColor->setDuration(240);
|
||||
animBgColor->start();
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::setPending(bool pending)
|
||||
{
|
||||
pendingStatus = pending;
|
||||
animateHandlePosition();
|
||||
|
||||
if (!isDelayed())
|
||||
return;
|
||||
|
||||
if (pending) {
|
||||
emit pendingChecked();
|
||||
} else {
|
||||
emit pendingUnchecked();
|
||||
}
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::setDelayed(bool state)
|
||||
{
|
||||
delayed = state;
|
||||
pendingStatus = isChecked();
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::enterEvent(QEnterEvent *e)
|
||||
{
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
updateBackgroundColor();
|
||||
QAbstractButton::enterEvent(e);
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::leaveEvent(QEvent *e)
|
||||
{
|
||||
updateBackgroundColor();
|
||||
QAbstractButton::leaveEvent(e);
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::keyReleaseEvent(QKeyEvent *e)
|
||||
{
|
||||
if (!isDelayed()) {
|
||||
QAbstractButton::keyReleaseEvent(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->key() != Qt::Key_Space) {
|
||||
return;
|
||||
}
|
||||
|
||||
click();
|
||||
}
|
||||
|
||||
void OBSToggleSwitch::mouseReleaseEvent(QMouseEvent *e)
|
||||
{
|
||||
if (!isDelayed()) {
|
||||
QAbstractButton::mouseReleaseEvent(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->button() != Qt::LeftButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
click();
|
||||
}
|
||||
|
||||
QSize OBSToggleSwitch::sizeHint() const
|
||||
{
|
||||
return QSize(2 * handleSize, handleSize);
|
||||
}
|
||||
121
frontend/components/idian/OBSToggleSwitch.hpp
Normal file
121
frontend/components/idian/OBSToggleSwitch.hpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QEvent>
|
||||
#include <QBrush>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
#include <QAbstractButton>
|
||||
#include <QStyleOptionButton>
|
||||
#include <QAccessibleWidget>
|
||||
|
||||
#include "OBSIdianWidget.hpp"
|
||||
|
||||
class OBSToggleSwitch : public QAbstractButton, public OBSIdianUtils {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int xpos MEMBER xPos WRITE setPos)
|
||||
Q_PROPERTY(QColor background MEMBER backgroundInactive DESIGNABLE true)
|
||||
Q_PROPERTY(QColor background_hover MEMBER backgroundInactiveHover DESIGNABLE true)
|
||||
Q_PROPERTY(QColor background_checked MEMBER backgroundActive DESIGNABLE true)
|
||||
Q_PROPERTY(QColor background_checked_hover MEMBER backgroundActiveHover DESIGNABLE true)
|
||||
Q_PROPERTY(QColor handleColor MEMBER handleColor DESIGNABLE true)
|
||||
Q_PROPERTY(int handleSize MEMBER handleSize DESIGNABLE true)
|
||||
Q_PROPERTY(float blend MEMBER blend WRITE setBlend DESIGNABLE false)
|
||||
|
||||
public:
|
||||
OBSToggleSwitch(QWidget *parent = nullptr);
|
||||
OBSToggleSwitch(bool defaultState, QWidget *parent = nullptr);
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
void setPos(int x)
|
||||
{
|
||||
xPos = x;
|
||||
update();
|
||||
}
|
||||
|
||||
void setBlend(float newBlend)
|
||||
{
|
||||
blend = newBlend;
|
||||
update();
|
||||
}
|
||||
|
||||
void setDelayed(bool state);
|
||||
bool isDelayed() { return delayed; }
|
||||
|
||||
void setStatus(bool status);
|
||||
void setPending(bool pending);
|
||||
|
||||
public slots:
|
||||
void click();
|
||||
|
||||
signals:
|
||||
void pendingChecked();
|
||||
void pendingUnchecked();
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent *event) override;
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
void showEvent(QShowEvent *) override;
|
||||
void enterEvent(QEnterEvent *) override;
|
||||
void leaveEvent(QEvent *) override;
|
||||
void keyReleaseEvent(QKeyEvent *) override;
|
||||
void mouseReleaseEvent(QMouseEvent *) override;
|
||||
|
||||
void focusInEvent(QFocusEvent *e) override
|
||||
{
|
||||
OBSIdianUtils::showKeyFocused(e);
|
||||
QAbstractButton::focusInEvent(e);
|
||||
}
|
||||
|
||||
void focusOutEvent(QFocusEvent *e) override
|
||||
{
|
||||
OBSIdianUtils::hideKeyFocused(e);
|
||||
QAbstractButton::focusOutEvent(e);
|
||||
}
|
||||
|
||||
private slots:
|
||||
void onClicked(bool checked);
|
||||
|
||||
private:
|
||||
int xPos;
|
||||
int onPos;
|
||||
int offPos;
|
||||
int margin;
|
||||
|
||||
float blend = 0.0f;
|
||||
|
||||
bool delayed = false;
|
||||
bool pendingStatus = false;
|
||||
|
||||
void animateHandlePosition();
|
||||
|
||||
void updateBackgroundColor();
|
||||
QColor backgroundInactive;
|
||||
QColor backgroundInactiveHover;
|
||||
QColor backgroundActive;
|
||||
QColor backgroundActiveHover;
|
||||
QColor handleColor;
|
||||
int handleSize = 18;
|
||||
|
||||
QPropertyAnimation *animHandle = nullptr;
|
||||
QPropertyAnimation *animBgColor = nullptr;
|
||||
};
|
||||
37
frontend/components/idian/obs-widgets.hpp
Normal file
37
frontend/components/idian/obs-widgets.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Idian - A family of custom widgets for OBS implementing the "Yami" UI design.
|
||||
//
|
||||
// (OBS Idian, get it?)
|
||||
|
||||
#include "OBSActionRow.hpp"
|
||||
#include "OBSCheckBox.hpp"
|
||||
#include "OBSComboBox.hpp"
|
||||
#include "OBSDoubleSpinBox.hpp"
|
||||
#include "OBSGroupBox.hpp"
|
||||
#include "OBSPropertiesList.hpp"
|
||||
#include "OBSSpinBox.hpp"
|
||||
#include "OBSToggleSwitch.hpp"
|
||||
|
||||
/// Note: This file serves as an all-in-one include for custom OBS widgets.
|
||||
/// It is not intended to define any widgets by itself.
|
||||
|
||||
/// Note 2: These widgets are still heavily work in progress. They should not
|
||||
/// yet be used outside of the demo and scene collection dialogues.
|
||||
@@ -379,3 +379,108 @@ QCalendarWidget #qt_calendar_nextmonth {
|
||||
StatusBarWidget > QFrame {
|
||||
padding: 0px 12px 8px;
|
||||
}
|
||||
|
||||
OBSToggleSwitch {
|
||||
qproperty-handle: rgb(255, 255, 255);
|
||||
qproperty-backgroundActive: #284cb8;
|
||||
qproperty-backgroundInactive: #3c404b;
|
||||
}
|
||||
|
||||
OBSGroupBox {
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
OBSGroupBox > QLabel.title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
OBSGroupBox > QLabel.subtitle {
|
||||
color: palette(button-text);
|
||||
}
|
||||
|
||||
OBSPropertiesList {
|
||||
background: palette(base);
|
||||
border-radius: 4px;
|
||||
border-width: 0px;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
OBSActionRow {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-left: 3px solid transparent;
|
||||
min-height: 48px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
OBSActionRow:hover {
|
||||
background: palette(highlight);
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
OBSActionRow[last="true"]:hover {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
OBSActionRow[first="true"]:hover {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
OBSActionRow > QLabel {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
OBSActionRow > QLabel.subtitle {
|
||||
font-size: 9pt;
|
||||
color: palette(button-text);
|
||||
}
|
||||
|
||||
OBSActionRow QComboBox,
|
||||
OBSActionRow QPushButton {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
OBSActionRow QComboBox,
|
||||
OBSActionRow QComboBox:hover,
|
||||
OBSActionRow QComboBox:selected,
|
||||
OBSActionRow QComboBox::on {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
OBSActionRow QComboBox::drop-down {
|
||||
border: none;
|
||||
}
|
||||
|
||||
OBSActionRow QComboBox::down-arrow {
|
||||
image: url(./Light/collapse.svg);
|
||||
}
|
||||
|
||||
OBSPropertiesListSpacer {
|
||||
max-height: 1px;
|
||||
min-height: 1px;
|
||||
background-color: palette(midlight);
|
||||
}
|
||||
|
||||
OBSCollapsibleActionRow {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
OBSCollapsibleActionRow OBSPropertiesList {
|
||||
border-radius: 0;
|
||||
background: palette(mid);
|
||||
}
|
||||
|
||||
IdianPlayground QVBoxLayout {
|
||||
background: palette(midlight);
|
||||
border-radius: 4px;
|
||||
padding-top: 32px;
|
||||
padding-bottom: 8px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
@@ -97,6 +97,9 @@
|
||||
--padding_base_value: var(--obsPadding);
|
||||
--spacing_base_value: calc(2 + calc(var(--obsPadding) / 2));
|
||||
|
||||
--highlight_width: 1px;
|
||||
--highlight_color: var(--primary_lighter);
|
||||
|
||||
/* TODO: Better Accessibility focus state */
|
||||
/* TODO: Move Accessibilty Colors to Theme config system */
|
||||
--border_highlight: "transparent";
|
||||
@@ -172,6 +175,7 @@
|
||||
--list_item_bg_hover: var(--primary_light);
|
||||
|
||||
--input_border: var(--grey1);
|
||||
--input_border_width: 1px;
|
||||
--input_border_hover: var(--grey1);
|
||||
--input_border_focus: var(--primary);
|
||||
|
||||
@@ -182,6 +186,7 @@
|
||||
--button_bg_down: var(--grey7);
|
||||
--button_bg_disabled: var(--grey6);
|
||||
|
||||
--button_border_width: var(--input_border_width);
|
||||
--button_border: var(--button_bg);
|
||||
--button_border_hover: var(--grey1);
|
||||
--button_border_focus: var(--grey1);
|
||||
@@ -193,7 +198,7 @@
|
||||
|
||||
--tab_border: var(--border_color);
|
||||
--tab_border_hover: var(--button_border_hover);
|
||||
--tab_border_focus: var(--button_border_focus);
|
||||
--tab_border_focus: var(--primary_lighter);
|
||||
--tab_border_selected: var(--primary);
|
||||
|
||||
--tab_padding_base: calc(5px + var(--padding_base));
|
||||
@@ -203,8 +208,22 @@
|
||||
|
||||
--separator_hover: var(--white1);
|
||||
|
||||
--highlight: rgb(42, 130, 218);
|
||||
--highlight_inactive: rgb(25, 28, 34);
|
||||
--action_row_base: calc(var(--input_height_base) * 0.75);
|
||||
--action_row_height: calc(var(--action_row_base) + calc(var(--action_row_padding) * 2));
|
||||
--action_row_border: 3px;
|
||||
--action_row_input_width: calc(var(--action_row_base) * 4);
|
||||
--action_row_collapse: calc(var(--action_row_base) + var(--padding_large));
|
||||
--action_row_collapse_radius: calc(var(--action_row_collapse) / 2);
|
||||
--action_row_padding: calc(var(--padding_large) * 1.5);
|
||||
--action_row_padding_x: calc(var(--action_row_padding) * 2);
|
||||
--action_row_padding_nested: calc(var(--action_row_padding_x) * 1.5);
|
||||
|
||||
--toggle_border: 1;
|
||||
--toggle_margin: 3;
|
||||
--toggle_width: calc(var(--action_row_base) * 1.8);
|
||||
--toggle_height: calc(var(--action_row_base) * 0.9);
|
||||
--toggle_handle: calc(calc(calc(var(--toggle_height) * 0.9) - calc(var(--toggle_border) * 2)) - var(--toggle_margin));
|
||||
--toggle_radius: calc(var(--toggle_height) / 2);
|
||||
|
||||
/* Qt Palette variables can be set with the "palette_" prefix */
|
||||
--palette_window: var(--bg_window);
|
||||
@@ -538,6 +557,7 @@ QListView,
|
||||
QListWidget,
|
||||
QMenu {
|
||||
padding: var(--spacing_base);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
QMenu {
|
||||
@@ -588,6 +608,7 @@ QMenu::item:selected,
|
||||
QListView::item:selected,
|
||||
QListWidget::item:selected {
|
||||
background-color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
QMenu::item:hover,
|
||||
@@ -597,7 +618,7 @@ QMenu::item:selected:hover,
|
||||
QListView::item:selected:hover,
|
||||
QListWidget::item:selected:hover {
|
||||
background-color: var(--primary_light);
|
||||
color: var(--text);
|
||||
border-color: var(--highlight_color);
|
||||
}
|
||||
|
||||
QMenu::item:focus,
|
||||
@@ -622,7 +643,7 @@ QListWidget QLineEdit {
|
||||
padding: 0;
|
||||
padding-bottom: 1px;
|
||||
margin: 0;
|
||||
border: 1px solid var(--white1);
|
||||
border: var(--input_border_width) solid var(--white1);
|
||||
border-radius: var(--border_radius);
|
||||
}
|
||||
|
||||
@@ -937,6 +958,10 @@ QTabWidget::pane {
|
||||
border-top: 4px solid var(--tab_bg);
|
||||
}
|
||||
|
||||
QTabBar {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
QTabWidget::tab-bar {
|
||||
alignment: left;
|
||||
}
|
||||
@@ -1124,7 +1149,7 @@ QTextEdit:!editable:focus {
|
||||
QSpinBox,
|
||||
QDoubleSpinBox {
|
||||
background-color: var(--input_bg);
|
||||
border: 1px solid var(--input_bg);
|
||||
border: var(--input_border_width) solid var(--input_bg);
|
||||
border-radius: var(--border_radius);
|
||||
padding: var(--input_padding) var(--input_text_padding);
|
||||
height: var(--input_height);
|
||||
@@ -1446,7 +1471,7 @@ QSlider::handle:disabled {
|
||||
background-color: var(--button_bg);
|
||||
padding: var(--padding_base_border) var(--padding_base_border);
|
||||
margin: 0px;
|
||||
border: 1px solid var(--button_border);
|
||||
border: var(--highlight_width) solid var(--button_border);
|
||||
border-radius: var(--border_radius);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
@@ -1702,8 +1727,8 @@ QTableView::indicator:unchecked:disabled {
|
||||
max-height: var(--icon_base);
|
||||
padding: var(--padding_base);
|
||||
margin-right: var(--spacing_large);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
border: var(--highlight_width) solid transparent;
|
||||
border-radius: var(--border_radius);
|
||||
}
|
||||
|
||||
.checkbox-icon::indicator {
|
||||
@@ -1753,7 +1778,7 @@ QTableView::indicator:unchecked:disabled {
|
||||
background-color: var(--button_bg);
|
||||
padding: var(--padding_base_border) var(--padding_base_border);
|
||||
margin: 0px;
|
||||
border: 1px solid var(--button_border);
|
||||
border: var(--highlight_width) solid var(--button_border);
|
||||
border-radius: var(--border_radius);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
@@ -1763,7 +1788,7 @@ QTableView::indicator:unchecked:disabled {
|
||||
background-color: var(--button_bg_hover);
|
||||
padding: var(--padding_base_border) var(--padding_base_border);
|
||||
margin: 0px;
|
||||
border: 1px solid var(--button_border_hover);
|
||||
border: var(--highlight_width) solid var(--button_border_hover);
|
||||
icon-size: var(--icon_base);
|
||||
}
|
||||
|
||||
@@ -2153,3 +2178,282 @@ OBSBasicAdvAudio #scrollAreaWidgetContents {
|
||||
#previewZoomOutButton:focus {
|
||||
border: 1px solid var(--input_border_hover);
|
||||
}
|
||||
|
||||
/* Idian Widgets */
|
||||
OBSGroupBox {
|
||||
border-radius: var(--border_radius);
|
||||
font-weight: bold;
|
||||
margin: 0 0 var(--spacing_base);
|
||||
min-width: 300px;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
OBSGroupBox .header .title {
|
||||
font-weight: bold;
|
||||
padding: var(--padding_large) 0;
|
||||
}
|
||||
|
||||
OBSGroupBox .header .description {
|
||||
color: var(--text_muted);
|
||||
padding: var(--spacing_small) 0;
|
||||
}
|
||||
|
||||
OBSPropertiesList {
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
margin: var(--spacing_base) 0;
|
||||
}
|
||||
|
||||
OBSActionRowWidget {
|
||||
background: var(--grey5);
|
||||
margin: 0;
|
||||
padding: var(--action_row_padding) var(--action_row_padding_x);
|
||||
}
|
||||
|
||||
OBSActionRowWidget.keyFocus {
|
||||
background: var(--grey4);
|
||||
border: var(--highlight_width) solid var(--grey4);
|
||||
}
|
||||
|
||||
OBSActionRowWidget.cursor-pointer.hover {
|
||||
background: var(--grey4);
|
||||
border: var(--highlight_width) solid var(--grey1);
|
||||
}
|
||||
|
||||
OBSActionRowWidget.first {
|
||||
border-top-left-radius: var(--border_radius);
|
||||
border-top-right-radius: var(--border_radius);
|
||||
}
|
||||
|
||||
OBSActionRowWidget.last {
|
||||
border-bottom-left-radius: var(--border_radius);
|
||||
border-bottom-right-radius: var(--border_radius);
|
||||
}
|
||||
|
||||
OBSActionRowWidget > QWidget QLabel {
|
||||
background: red;
|
||||
margin: 4px;
|
||||
font-weight: 500;
|
||||
max-height: var(--input_height);
|
||||
}
|
||||
|
||||
OBSActionRowWidget > QLabel.description {
|
||||
font-size: var(--font_small);
|
||||
color: var(--text_muted);
|
||||
}
|
||||
|
||||
OBSToggleSwitch {
|
||||
qproperty-background: var(--grey6);
|
||||
qproperty-background_hover: var(--grey7);
|
||||
qproperty-background_checked: var(--primary);
|
||||
qproperty-background_checked_hover: var(--primary_light);
|
||||
|
||||
min-width: var(--toggle_width);
|
||||
min-height: var(--toggle_height);
|
||||
|
||||
border-radius: var(--toggle_radius);
|
||||
|
||||
qproperty-handleColor: var(--white1);
|
||||
qproperty-handleSize: var(--toggle_handle);
|
||||
|
||||
border: var(--highlight_width) solid transparent;
|
||||
}
|
||||
|
||||
OBSToggleSwitch:hover {
|
||||
border-color: var(--grey4);
|
||||
}
|
||||
|
||||
OBSToggleSwitch:checked:hover {
|
||||
border-color: var(--white1);
|
||||
}
|
||||
|
||||
OBSToggleSwitch.keyFocus {
|
||||
border-color: var(--highlight_color);
|
||||
}
|
||||
|
||||
OBSActionRowWidget OBSToggleSwitch:hover,
|
||||
OBSActionRowWidget.hover > OBSToggleSwitch.row-buddy {
|
||||
border-color: var(--grey1);
|
||||
}
|
||||
|
||||
OBSActionRowWidget OBSToggleSwitch:checked:hover,
|
||||
OBSActionRowWidget.hover OBSToggleSwitch.row-buddy:checked {
|
||||
border-color: var(--white1);
|
||||
}
|
||||
|
||||
OBSActionRowWidget QComboBox {
|
||||
background-color: transparent;
|
||||
min-height: var(--action_row_base);
|
||||
max-height: var(--action_row_base);
|
||||
min-width: var(--action_row_input_width);
|
||||
border: var(--highlight_width) solid transparent;
|
||||
padding: 0;
|
||||
padding-left: var(--padding_xlarge);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
OBSActionRowWidget QComboBox:focus {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
OBSActionRowWidget QComboBox:hover {
|
||||
border-color: var(--grey1);
|
||||
}
|
||||
|
||||
OBSActionRowWidget QComboBox.keyFocus {
|
||||
border-color: var(--highlight_color);
|
||||
}
|
||||
|
||||
OBSActionRowWidget QComboBox::drop-down {
|
||||
border: none;
|
||||
}
|
||||
|
||||
OBSActionRowWidget QComboBox::down-arrow {
|
||||
image: url(theme:Dark/collapse.svg);
|
||||
}
|
||||
|
||||
OBSActionRowWidget QComboBox QAbstractItemView {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
OBSActionRowWidget QComboBox QAbstractItemView::item {
|
||||
background-color: var(--bg_base);
|
||||
padding: var(--padding_base) var(--padding_large);
|
||||
}
|
||||
|
||||
OBSActionRowWidget QComboBox QAbstractItemView::item:hover,
|
||||
OBSActionRowWidget QComboBox QAbstractItemView::item:selected {
|
||||
background-color: var(--list_item_bg_selected);
|
||||
padding: var(--padding_base) var(--padding_large);
|
||||
}
|
||||
|
||||
OBSActionRowWidget QPushButton,
|
||||
OBSActionRowWidget QSpinBox,
|
||||
OBSActionRowWidget QDoubleSpinBox {
|
||||
margin: 0;
|
||||
padding: var(--padding_base) var(--action_row_padding_x);
|
||||
}
|
||||
|
||||
OBSPropertiesListSpacer {
|
||||
max-height: var(--spacing_small);
|
||||
min-height: var(--spacing_small);
|
||||
background-color: var(--bg_window);
|
||||
}
|
||||
|
||||
OBSActionRowWidget OBSCheckBox {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
OBSActionRowWidget OBSCheckBox::indicator,
|
||||
OBSActionRowWidget OBSCheckBox::indicator:unchecked:hover {
|
||||
border: var(--highlight_width) solid transparent;
|
||||
border-radius: var(--border_radius);
|
||||
}
|
||||
|
||||
OBSActionRowWidget.hover > OBSCheckBox.row-buddy::indicator,
|
||||
OBSActionRowWidget > OBSCheckBox::indicator:unchecked:hover,
|
||||
OBSActionRowWidget > OBSCheckBox::indicator:hover {
|
||||
border-color: var(--grey1);
|
||||
}
|
||||
|
||||
OBSActionRowWidget.hover > OBSCheckBox.row-buddy::indicator:unchecked,
|
||||
OBSCheckBox.keyFocus::indicator:unchecked {
|
||||
image: url(theme:Yami/checkbox_unchecked_focus.svg);
|
||||
}
|
||||
|
||||
OBSActionRowWidget OBSCheckBox.keyFocus::indicator,
|
||||
OBSActionRowWidget.hover > OBSCheckBox::indicator {
|
||||
image: url(theme:Yami/checkbox_checked_focus.svg);
|
||||
}
|
||||
|
||||
OBSActionRowWidget OBSCheckBox.keyFocus::indicator,
|
||||
OBSActionRowWidget OBSCheckBox.keyFocus::indicator:unchecked,
|
||||
OBSActionRowWidget OBSCheckBox.keyFocus::indicator:hover,
|
||||
OBSActionRowWidget OBSCheckBox.keyFocus::indicator:unchecked:hover {
|
||||
border-color: var(--highlight_color);
|
||||
}
|
||||
|
||||
OBSCollapsibleRowWidget {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
OBSCollapsibleRowWidget.keyFocus {
|
||||
border: var(--highlight_width) solid var(--highlight_color);
|
||||
}
|
||||
|
||||
OBSCollapsibleRowWidget OBSPropertiesList {
|
||||
border-radius: 0;
|
||||
border-left: 1px solid var(--grey5);
|
||||
border-right: 1px solid var(--grey5);
|
||||
border-bottom: 1px solid var(--grey5);
|
||||
margin: var(--spacing_small) 0px 0px;
|
||||
}
|
||||
|
||||
OBSCollapsibleRowWidget OBSPropertiesList OBSActionRowWidget {
|
||||
background-color: var(--grey6);
|
||||
padding-left: var(--action_row_padding_nested);
|
||||
}
|
||||
|
||||
OBSCollapsibleRowWidget OBSActionRowWidget.first,
|
||||
OBSCollapsibleRowWidget OBSActionRowWidget.last {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
OBSCollapsibleRowWidget OBSPropertiesList OBSToggleSwitch {
|
||||
qproperty-background: var(--grey7);
|
||||
qproperty-background_hover: var(--grey6);
|
||||
}
|
||||
|
||||
OBSActionRowExpandButton {
|
||||
background: transparent;
|
||||
min-width: var(--action_row_collapse);
|
||||
max-width: var(--action_row_collapse);
|
||||
min-height: var(--action_row_collapse);
|
||||
max-height: var(--action_row_collapse);
|
||||
border: none;
|
||||
}
|
||||
|
||||
OBSActionRowExpandButton::indicator {
|
||||
background: var(--grey5);
|
||||
border-radius: var(--action_row_collapse_radius);
|
||||
padding: var(--padding_large);
|
||||
image: url(theme:Dark/down.svg);
|
||||
border: var(--highlight_width) solid var(--grey5);
|
||||
}
|
||||
|
||||
OBSActionRowExpandButton::indicator:checked {
|
||||
image: url(theme:Dark/up.svg);
|
||||
}
|
||||
|
||||
OBSActionRowExpandButton.keyFocus,
|
||||
OBSActionRowExpandButton.keyFocus::indicator {
|
||||
border-color: var(--highlight_color);
|
||||
}
|
||||
|
||||
OBSCollapsibleRowFrame .btn-frame {
|
||||
background: var(--grey5);
|
||||
padding: var(--action_row_padding) var(--action_row_padding_x);
|
||||
}
|
||||
|
||||
OBSCollapsibleRowFrame.hover .btn-frame {
|
||||
background: var(--grey4);
|
||||
}
|
||||
|
||||
OBSCollapsibleRowFrame.hover OBSActionRowWidget,
|
||||
OBSCollapsibleRowFrame.hover OBSActionRowWidget.hover {
|
||||
background: var(--grey4);
|
||||
border: 2px solid var(--grey1);
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
OBSCollapsibleRowFrame.hover .row-buddy {
|
||||
background: var(--grey4);
|
||||
border: 2px solid var(--grey1);
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
OBSCollapsibleRowFrame.hover OBSActionRowExpandButton::indicator {
|
||||
border-color: var(--grey1);
|
||||
}
|
||||
|
||||
132
frontend/dialogs/OBSIdianPlayground.cpp
Normal file
132
frontend/dialogs/OBSIdianPlayground.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "OBSIdianPlayground.hpp"
|
||||
|
||||
#include "components/idian/obs-widgets.hpp"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
OBSIdianPlayground::OBSIdianPlayground(QWidget *parent) : QDialog(parent), ui(new Ui_OBSIdianPlayground)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
|
||||
|
||||
OBSGroupBox *test;
|
||||
OBSActionRowWidget *tmp;
|
||||
|
||||
OBSComboBox *cbox = new OBSComboBox;
|
||||
cbox->addItem("Test 1");
|
||||
cbox->addItem("Test 2");
|
||||
|
||||
// Group box 1
|
||||
test = new OBSGroupBox(this);
|
||||
|
||||
tmp = new OBSActionRowWidget();
|
||||
tmp->setTitle("Row with a dropdown");
|
||||
tmp->setSuffix(cbox);
|
||||
test->properties()->addRow(tmp);
|
||||
|
||||
cbox = new OBSComboBox;
|
||||
cbox->addItem("Test 3");
|
||||
cbox->addItem("Test 4");
|
||||
tmp = new OBSActionRowWidget();
|
||||
tmp->setTitle("Row with a dropdown");
|
||||
tmp->setDescription("And a subtitle!");
|
||||
tmp->setSuffix(cbox);
|
||||
test->properties()->addRow(tmp);
|
||||
|
||||
tmp = new OBSActionRowWidget();
|
||||
tmp->setTitle("Toggle Switch");
|
||||
tmp->setSuffix(new OBSToggleSwitch());
|
||||
test->properties()->addRow(tmp);
|
||||
ui->scrollAreaWidgetContents->layout()->addWidget(test);
|
||||
|
||||
tmp = new OBSActionRowWidget();
|
||||
tmp->setTitle("Delayed toggle switch");
|
||||
tmp->setDescription("The state can be set separately");
|
||||
auto tswitch = new OBSToggleSwitch;
|
||||
tswitch->setDelayed(true);
|
||||
connect(tswitch, &OBSToggleSwitch::pendingChecked, this, [=]() {
|
||||
// Do async enable stuff, then set toggle status when complete
|
||||
QTimer::singleShot(1000, [=]() { tswitch->setStatus(true); });
|
||||
});
|
||||
connect(tswitch, &OBSToggleSwitch::pendingUnchecked, this, [=]() {
|
||||
// Do async disable stuff, then set toggle status when complete
|
||||
QTimer::singleShot(1000, [=]() { tswitch->setStatus(false); });
|
||||
});
|
||||
tmp->setSuffix(tswitch);
|
||||
test->properties()->addRow(tmp);
|
||||
|
||||
// Group box 2
|
||||
test = new OBSGroupBox();
|
||||
test->setTitle("Just a few checkboxes");
|
||||
|
||||
tmp = new OBSActionRowWidget();
|
||||
tmp->setTitle("Box 1");
|
||||
tmp->setPrefix(new OBSCheckBox);
|
||||
test->properties()->addRow(tmp);
|
||||
|
||||
tmp = new OBSActionRowWidget();
|
||||
tmp->setTitle("Box 2");
|
||||
tmp->setPrefix(new OBSCheckBox);
|
||||
test->properties()->addRow(tmp);
|
||||
|
||||
ui->scrollAreaWidgetContents->layout()->addWidget(test);
|
||||
|
||||
// Group box 2
|
||||
test = new OBSGroupBox();
|
||||
test->setTitle("Another Group");
|
||||
test->setDescription("With a subtitle");
|
||||
|
||||
tmp = new OBSActionRowWidget();
|
||||
tmp->setTitle("Placeholder");
|
||||
tmp->setSuffix(new OBSToggleSwitch);
|
||||
test->properties()->addRow(tmp);
|
||||
|
||||
OBSCollapsibleRowWidget *tmp2 = new OBSCollapsibleRowWidget("A Collapsible row!", this);
|
||||
tmp2->setCheckable(true);
|
||||
test->addRow(tmp2);
|
||||
|
||||
tmp = new OBSActionRowWidget();
|
||||
tmp->setTitle("Spin box demo");
|
||||
tmp->setSuffix(new OBSDoubleSpinBox());
|
||||
tmp2->addRow(tmp);
|
||||
|
||||
tmp = new OBSActionRowWidget();
|
||||
tmp->setTitle("Just another placeholder");
|
||||
tmp->setSuffix(new OBSToggleSwitch(true));
|
||||
tmp2->addRow(tmp);
|
||||
|
||||
tmp = new OBSActionRowWidget();
|
||||
tmp->setTitle("Placeholder 2");
|
||||
tmp->setSuffix(new OBSToggleSwitch);
|
||||
test->properties()->addRow(tmp);
|
||||
|
||||
ui->scrollAreaWidgetContents->setContentsMargins(0, 0, 0, 0);
|
||||
ui->scrollAreaWidgetContents->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
ui->scrollAreaWidgetContents->layout()->addWidget(test);
|
||||
ui->scrollAreaWidgetContents->layout()->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
|
||||
|
||||
// Test Checkable Group
|
||||
OBSGroupBox *test2 = new OBSGroupBox();
|
||||
test2->setTitle("Checkable Group");
|
||||
test2->setDescription("Description goes here");
|
||||
test2->setCheckable(true);
|
||||
ui->scrollAreaWidgetContents->layout()->addWidget(test2);
|
||||
}
|
||||
36
frontend/dialogs/OBSIdianPlayground.hpp
Normal file
36
frontend/dialogs/OBSIdianPlayground.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "components/idian/obs-widgets.hpp"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <ui_OBSIdianPlayground.h>
|
||||
|
||||
// QDialog including a bunch of custom widgets for demoing
|
||||
class OBSIdianPlayground : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
std::unique_ptr<Ui_OBSIdianPlayground> ui;
|
||||
|
||||
public:
|
||||
OBSIdianPlayground(QWidget *parent);
|
||||
};
|
||||
@@ -937,6 +937,7 @@
|
||||
<string>Basic.MainMenu.Tools</string>
|
||||
</property>
|
||||
<addaction name="autoConfigure"/>
|
||||
<addaction name="widgetPlayground"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuDocks">
|
||||
@@ -2165,6 +2166,11 @@
|
||||
<string>PasteDuplicate</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="widgetPlayground">
|
||||
<property name="text">
|
||||
<string>Widget Playground</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="autoConfigure2">
|
||||
<property name="text">
|
||||
<string>Basic.AutoConfig</string>
|
||||
|
||||
55
frontend/forms/OBSIdianPlayground.ui
Normal file
55
frontend/forms/OBSIdianPlayground.ui
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OBSIdianPlayground</class>
|
||||
<widget class="QDialog" name="OBSIdianPlayground">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>700</width>
|
||||
<height>700</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Idian Playground</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>786</width>
|
||||
<height>919</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="scrollContentsLayout">
|
||||
<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>9</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
5
frontend/forms/images/hide.svg
Normal file
5
frontend/forms/images/hide.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<path d="M3,5L3,6C3,6.277 3.113,6.527 3.293,6.707L8,11.414L12.707,6.707C12.887,6.527 13,6.277 13,6L13,5L12,5C11.723,5 11.473,5.113 11.293,5.293L8,8.586L4.707,5.293C4.527,5.113 4.277,5 4,5L3,5Z" style="fill:rgb(162,162,162);fill-rule:nonzero;"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 699 B |
@@ -30,6 +30,7 @@
|
||||
<file>images/visible.svg</file>
|
||||
<file>images/help.svg</file>
|
||||
<file>images/help_light.svg</file>
|
||||
<file>images/hide.svg</file>
|
||||
<file>images/revert.svg</file>
|
||||
<file>images/alert.svg</file>
|
||||
<file>images/warning.svg</file>
|
||||
|
||||
@@ -422,6 +422,10 @@ OBSBasic::OBSBasic(QWidget *parent) : OBSMainWindow(parent), undo_s(ui), ui(new
|
||||
ui->actionE_xit->setShortcut(Qt::CTRL | Qt::Key_Q);
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_WIDGET_PLAYGROUND
|
||||
ui->widgetPlayground->setVisible(false);
|
||||
#endif
|
||||
|
||||
auto addNudge = [this](const QKeySequence &seq, MoveDir direction, int distance) {
|
||||
QAction *nudge = new QAction(ui->preview);
|
||||
nudge->setShortcut(seq);
|
||||
|
||||
@@ -630,6 +630,7 @@ private slots:
|
||||
|
||||
void on_autoConfigure_triggered();
|
||||
void on_stats_triggered();
|
||||
void on_widgetPlayground_triggered();
|
||||
|
||||
void on_resetUI_triggered();
|
||||
|
||||
|
||||
@@ -42,6 +42,10 @@
|
||||
#endif
|
||||
#include <wizards/AutoConfig.hpp>
|
||||
|
||||
#ifdef ENABLE_WIDGET_PLAYGROUND
|
||||
#include "dialogs/OBSIdianPlayground.hpp"
|
||||
#endif
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include <QDesktopServices>
|
||||
@@ -635,6 +639,16 @@ void OBSBasic::on_stats_triggered()
|
||||
stats = statsDlg;
|
||||
}
|
||||
|
||||
void OBSBasic::on_widgetPlayground_triggered()
|
||||
{
|
||||
#ifdef ENABLE_WIDGET_PLAYGROUND
|
||||
OBSIdianPlayground playground(this);
|
||||
playground.setModal(true);
|
||||
playground.show();
|
||||
playground.exec();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OBSBasic::on_actionShowAbout_triggered()
|
||||
{
|
||||
if (about)
|
||||
|
||||
Reference in New Issue
Block a user