From 4303defd5a775bac7d7f389befd901934788470b Mon Sep 17 00:00:00 2001 From: cg2121 Date: Wed, 20 Apr 2022 06:08:12 -0500 Subject: [PATCH] UI: Add dock widget title bars This moves the dock toolbars to the title bar of the dock, saving space. --- UI/data/locale/en-US.ini | 2 + UI/data/themes/Acri.qss | 22 +++----- UI/data/themes/Dark.qss | 32 +++++++----- UI/data/themes/Dark/dots.svg | 3 ++ UI/data/themes/Rachni.qss | 39 +++++++++------ UI/data/themes/System.qss | 11 ++++ UI/forms/OBSBasic.ui | 38 -------------- UI/forms/images/dots.svg | 3 ++ UI/forms/obs.qrc | 1 + UI/window-basic-main.cpp | 70 ++++++++++++++------------ UI/window-basic-main.hpp | 7 +-- UI/window-dock.cpp | 97 ++++++++++++++++++++++++++++++++++++ UI/window-dock.hpp | 22 +++++++- UI/window-extra-browsers.cpp | 2 +- 14 files changed, 231 insertions(+), 118 deletions(-) create mode 100644 UI/data/themes/Dark/dots.svg create mode 100644 UI/forms/images/dots.svg diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index b2892163f963c2..5dc11081b856b9 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -141,6 +141,8 @@ TwitchAuth.Feed="Twitch Activity Feed" TwitchAuth.TwoFactorFail.Title="Could not query stream key" TwitchAuth.TwoFactorFail.Text="OBS was unable to connect to your Twitch account. Please make sure two-factor authentication is set up in your Twitch security settings as this is required to stream." RestreamAuth.Channels="Restream Channels" +SetFloating="Set Floating" +SetDocked="Set Docked" # copy filters Copy.Filters="Copy Filters" diff --git a/UI/data/themes/Acri.qss b/UI/data/themes/Acri.qss index 54e91b2cc5416d..d4a8eef8eee5e9 100644 --- a/UI/data/themes/Acri.qss +++ b/UI/data/themes/Acri.qss @@ -154,10 +154,6 @@ QListView QLineEdit { } /* Dock stuff */ -QDockWidget { - titlebar-close-icon: url('./Dark/close.svg'); - titlebar-normal-icon: url('./Dark/popout.svg'); -} QDockWidget { background: palette(window); @@ -167,7 +163,7 @@ QDockWidget { border-bottom: 2px solid rgb(47,47,47); } -QDockWidget::title { +QFrame#dockTitleBar { border-bottom: 2px solid rgb(47,47,47); margin-left: 5px; margin-right: 5px; @@ -181,20 +177,16 @@ QDockWidget::title { background-repeat: none; } -QDockWidget::close-button, -QDockWidget::float-button { - icon-size: 20px; - subcontrol-position: top right; - subcontrol-origin: padding; - right: 0px; - margin: 0px; +QFrame#dockTitleBar > QLabel { + font-weight: bold; + font-size: 12px; + background: transparent; } -QDockWidget::float-button { - right: 20px; +* [themeID="dotsIcon"] { + qproperty-icon: url(./Dark/dots.svg); } - QListView#scenes, SourceListWidget { border: none; diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss index fa1e939392abff..679b00ad100f87 100644 --- a/UI/data/themes/Dark.qss +++ b/UI/data/themes/Dark.qss @@ -119,28 +119,34 @@ SourceTree QLineEdit { /* Dock Widget */ -QDockWidget { - titlebar-close-icon: url('./Dark/close.svg'); - titlebar-normal-icon: url('./Dark/popout.svg'); +QFrame#dockTitleBar { + background-color: rgb(70,69,70); +} + +QFrame#dockTitleBar > QLabel { + font-weight: bold; + font-size: 12px; + background: transparent; } -QDockWidget::title { - text-align: center; +QPushButton#dockTitleBarButton::flat { background-color: rgb(70,69,70); } -QDockWidget::close-button, QDockWidget::float-button { - border: 1px solid transparent; - background: transparent; - padding: 0px; +QPushButton#dockTitleBarButton::flat:hover { + background-color: rgb(122,121,122); /* light */ } -QDockWidget::close-button:hover, QDockWidget::float-button:hover { - background: transparent; +QPushButton#dockTitleBarButton::flat:pressed { + background-color: palette(base); +} + +QPushButton#dockTitleBarButton::flat:disabled { + background-color: rgb(46,45,46); } -QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { - padding: 1px -1px -1px 1px; +* [themeID="dotsIcon"] { + qproperty-icon: url(./Dark/dots.svg); } /* Group Box */ diff --git a/UI/data/themes/Dark/dots.svg b/UI/data/themes/Dark/dots.svg new file mode 100644 index 00000000000000..137d22b7dc1ba9 --- /dev/null +++ b/UI/data/themes/Dark/dots.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss index b0f367ae7be816..f57ef0b6bce238 100644 --- a/UI/data/themes/Rachni.qss +++ b/UI/data/themes/Rachni.qss @@ -188,38 +188,45 @@ QListView::item:hover:!active { /* --- Dock widget --- */ /***********************/ -QDockWidget { - titlebar-close-icon: url('./Dark/close.svg'); - titlebar-normal-icon: url('./Dark/popout.svg'); -} - QDockWidget { background: palette(window); border: 1px solid rgb(58, 64, 69); /* Light Blue-gray */ } -QDockWidget::title { - text-align: left; +QFrame#dockTitleBar { background: palette(shadow); padding-left: 5px; } - -QDockWidget::close-button, QDockWidget::float-button { - border: 1px solid transparent; - border-radius: 2px; +QFrame#dockTitleBar > QLabel { + font-weight: bold; + font-size: 12px; background: transparent; } -QDockWidget::close-button:hover, QDockWidget::float-button:hover { - background: rgba(255, 148, 194, 0.25); /* Light Pink (Secondary Light) */ +QPushButton#dockTitleBarButton::flat { + background-color: palette(shadow); + border: none; + outline: none; +} + +QPushButton#dockTitleBarButton::flat:hover { + background-color: rgba(240, 98, 146, 0.5); /* Pink (Secondary) */ + margin: 0; + padding: 0; + border-radius: 2px; + border: none; + outline: none; } -QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { - padding: 1px -1px -1px 1px; - background: rgba(255, 148, 194, 0.25); /* Light Pink (Secondary Light) */ +QPushButton#dockTitleBarButton::flat:pressed { + background-color: rgb(240, 98, 146); /* Pink (Secondary) */ + border: 1px solid rgb(240, 98, 146); /* Pink (Secondary) */ } +* [themeID="dotsIcon"] { + qproperty-icon: url(./Dark/dots.svg); +} /***********************/ /* --- Group Boxes --- */ diff --git a/UI/data/themes/System.qss b/UI/data/themes/System.qss index 82dfbc42de9a44..d7a01fbcd0df75 100644 --- a/UI/data/themes/System.qss +++ b/UI/data/themes/System.qss @@ -357,4 +357,15 @@ QCalendarWidget #qt_calendar_nextmonth { padding: 2px; qproperty-icon: url(./Dark/expand.svg); icon-size: 16px, 16px; +} + +/* Dock Title Bar */ +#dockTitleBar > QLabel { + font-weight: bold; + font-size: 12px; + background: transparent; +} + +* [themeID="dotsIcon"] { + qproperty-icon: url(:/res/images/dots.svg); } \ No newline at end of file diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index be96c96a8d3a0d..4d94a10cd4ab76 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -667,7 +667,6 @@ - @@ -800,24 +799,6 @@ - - - - - 16 - 16 - - - - false - - - - - - - - @@ -935,25 +916,6 @@ - - - - - 16 - 16 - - - - false - - - - - - - - - diff --git a/UI/forms/images/dots.svg b/UI/forms/images/dots.svg new file mode 100644 index 00000000000000..cd0c79abb12a35 --- /dev/null +++ b/UI/forms/images/dots.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/UI/forms/obs.qrc b/UI/forms/obs.qrc index e6769cc2611253..8f67b249bfcc48 100644 --- a/UI/forms/obs.qrc +++ b/UI/forms/obs.qrc @@ -56,6 +56,7 @@ images/media/media_restart.svg images/media/media_stop.svg images/interact.svg + images/dots.svg images/settings/output.svg diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 7228e54ae8709f..243158c3f0c4c8 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -267,14 +267,12 @@ OBSBasic::OBSBasic(QWidget *parent) statsDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); - statsDock->setWindowTitle(QTStr("Basic.Stats")); + statsDock->SetTitle(QTStr("Basic.Stats")); addDockWidget(Qt::BottomDockWidgetArea, statsDock); statsDock->setVisible(false); statsDock->setFloating(true); statsDock->resize(700, 200); - copyActionsDynamicProperties(); - char styleSheetPath[512]; int ret = GetProfilePath(styleSheetPath, sizeof(styleSheetPath), "stylesheet.qss"); @@ -573,26 +571,6 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, return saveData; } -void OBSBasic::copyActionsDynamicProperties() -{ - // Themes need the QAction dynamic properties - for (QAction *x : ui->scenesToolbar->actions()) { - QWidget *temp = ui->scenesToolbar->widgetForAction(x); - - for (QByteArray &y : x->dynamicPropertyNames()) { - temp->setProperty(y, x->property(y)); - } - } - - for (QAction *x : ui->sourcesToolbar->actions()) { - QWidget *temp = ui->sourcesToolbar->widgetForAction(x); - - for (QByteArray &y : x->dynamicPropertyNames()) { - temp->setProperty(y, x->property(y)); - } - } -} - void OBSBasic::UpdateVolumeControlsDecayRate() { double meterDecayRate = @@ -1923,6 +1901,8 @@ void OBSBasic::OBSInit() sysTrayEnabled && (opt_minimize_tray || sysTrayWhenStarted); + SetupDockTitleBars(); + #ifdef _WIN32 SetWin32DropStyle(this); @@ -8892,15 +8872,9 @@ void OBSBasic::on_lockUI_toggled(bool lock) extraDocks[i]->setFeatures(features); } } -} -void OBSBasic::on_toggleListboxToolbars_toggled(bool visible) -{ - ui->sourcesToolbar->setVisible(visible); - ui->scenesToolbar->setVisible(visible); - - config_set_bool(App()->GlobalConfig(), "BasicWindow", - "ShowListboxToolbars", visible); + QList list = findChildren(); + foreach(OBSDock * dock, list) { dock->EnableControlMenu(lock); } } void OBSBasic::ShowContextBar() @@ -10140,3 +10114,37 @@ void OBSBasic::SetDisplayAffinity(QWindow *window) UNUSED_PARAMETER(hideFromCapture); #endif } + +void OBSBasic::SetupDockTitleBars() +{ + ui->scenesDock->SetTitle(QTStr("Basic.Main.Scenes")); + ui->scenesDock->AddButton("addIconSmall", QTStr("Add"), this, + SLOT(on_actionAddScene_triggered())); + ui->scenesDock->AddButton("removeIconSmall", QTStr("Remove"), this, + SLOT(on_actionRemoveScene_triggered())); + ui->scenesDock->AddSeparator(); + ui->scenesDock->AddButton("upArrowIconSmall", QTStr("MoveUp"), this, + SLOT(on_actionSceneUp_triggered())); + ui->scenesDock->AddButton("downArrowIconSmall", QTStr("MoveDown"), this, + SLOT(on_actionSceneDown_triggered())); + + ui->sourcesDock->SetTitle(QTStr("Basic.Main.Sources")); + ui->sourcesDock->AddButton("addIconSmall", QTStr("Add"), this, + SLOT(on_actionAddSource_triggered())); + ui->sourcesDock->AddButton("removeIconSmall", QTStr("Remove"), this, + SLOT(on_actionRemoveSource_triggered())); + ui->sourcesDock->AddButton("configIconSmall", QTStr("Properties"), this, + SLOT(on_actionSourceProperties_triggered())); + ui->sourcesDock->AddSeparator(); + ui->sourcesDock->AddButton("upArrowIconSmall", QTStr("MoveUp"), this, + SLOT(on_actionSourceUp_triggered())); + ui->sourcesDock->AddButton("downArrowIconSmall", QTStr("MoveDown"), + this, SLOT(on_actionSourceDown_triggered())); + + ui->mixerDock->SetTitle(QTStr("Mixer")); + ui->transitionsDock->SetTitle(QTStr("Basic.SceneTransitions")); + ui->controlsDock->SetTitle(QTStr("Basic.Main.Controls")); + + QList list = findChildren(); + foreach(OBSDock * dock, list) { dock->AddControlMenu(); } +} diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 06a335007687df..bfa73a7791f4d2 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -185,6 +185,7 @@ class OBSBasic : public OBSMainWindow { friend class OBSYoutubeActions; friend struct BasicOutputHandler; friend struct OBSStudioAPI; + friend class OBSDock; enum class MoveDir { Up, Down, Left, Right }; @@ -240,7 +241,7 @@ class OBSBasic : public OBSMainWindow { QPointer transformWindow; QPointer advAudioWindow; QPointer filters; - QPointer statsDock; + QPointer statsDock; QPointer about; QPointer missDialog; QPointer logView; @@ -538,6 +539,8 @@ class OBSBasic : public OBSMainWindow { void ReceivedIntroJson(const QString &text); void ShowWhatsNew(const QString &url); + void SetupDockTitleBars(); + #ifdef BROWSER_AVAILABLE QList> extraBrowserDocks; QList> extraBrowserDockActions; @@ -804,7 +807,6 @@ private slots: void AddSource(const char *id); QMenu *CreateAddSourcePopupMenu(); void AddSourcePopupMenu(const QPoint &pos); - void copyActionsDynamicProperties(); static void HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed); @@ -1062,7 +1064,6 @@ private slots: void on_actionAlwaysOnTop_triggered(); - void on_toggleListboxToolbars_toggled(bool visible); void on_toggleContextBar_toggled(bool visible); void on_toggleStatusBar_toggled(bool visible); void on_toggleSourceIcons_toggled(bool visible); diff --git a/UI/window-dock.cpp b/UI/window-dock.cpp index ed8e928609c0eb..2acde52da731a8 100644 --- a/UI/window-dock.cpp +++ b/UI/window-dock.cpp @@ -3,6 +3,31 @@ #include #include +#include +#include +#include +#include + +OBSDock::OBSDock(QWidget *parent) : QDockWidget(parent) +{ + QFrame *titleBar = new QFrame(this); + + titleBar->setObjectName("dockTitleBar"); + + title = new QLabel(titleBar); + title->setAlignment(Qt::AlignVCenter); + + layout = new QHBoxLayout(titleBar); + layout->setContentsMargins(4, 2, 4, 2); + layout->setSpacing(4); + + layout->addWidget(title); + layout->addStretch(); + + titleBar->setLayout(layout); + + setTitleBarWidget(titleBar); +} void OBSDock::closeEvent(QCloseEvent *event) { @@ -34,3 +59,75 @@ void OBSDock::closeEvent(QCloseEvent *event) QDockWidget::closeEvent(event); } + +void OBSDock::ToggleFloating() +{ + if (isFloating()) + setFloating(false); + else + setFloating(true); +} + +void OBSDock::ShowControlMenu() +{ + QMenu popup; + + QDockWidget::DockWidgetFeatures dockFeatures = features(); + + if ((QDockWidget::DockWidgetClosable & dockFeatures) != 0) + popup.addAction(QTStr("Hide"), this, SLOT(close())); + + popup.addAction(isFloating() ? QTStr("SetDocked") + : QTStr("SetFloating"), + this, SLOT(ToggleFloating())); + + popup.exec(QCursor::pos()); +} + +void OBSDock::EnableControlMenu(bool lock) +{ + menuButton->setVisible(!lock); +} + +void OBSDock::AddControlMenu() +{ + menuButton = new QPushButton(this); + menuButton->setFlat(true); + menuButton->setObjectName("dockTitleBarButton"); + menuButton->setFixedSize(16, 16); + menuButton->setIconSize(QSize(12, 12)); + menuButton->setProperty("themeID", "dotsIcon"); + + connect(menuButton, SIGNAL(clicked()), this, SLOT(ShowControlMenu())); + + layout->addWidget(menuButton); +} + +void OBSDock::SetTitle(const QString &newTitle) +{ + title->setText(newTitle); +} + +void OBSDock::AddButton(const QString &themeID, const QString &toolTip, + const QObject *receiver, const char *slot) +{ + QPushButton *button = new QPushButton(this); + button->setFlat(true); + button->setObjectName("dockTitleBarButton"); + button->setFixedSize(16, 16); + button->setIconSize(QSize(12, 12)); + button->setProperty("themeID", themeID); + button->setToolTip(toolTip); + + connect(button, SIGNAL(clicked()), receiver, slot); + + layout->addWidget(button); +} + +void OBSDock::AddSeparator() +{ + QFrame *line = new QFrame(this); + line->setFrameShape(QFrame::VLine); + line->setFixedWidth(1); + layout->addWidget(line); +} diff --git a/UI/window-dock.hpp b/UI/window-dock.hpp index ccb1cf0ae5199c..541f27ac3ec839 100644 --- a/UI/window-dock.hpp +++ b/UI/window-dock.hpp @@ -2,11 +2,31 @@ #include +class QHBoxLayout; +class QLabel; +class QPushButton; + class OBSDock : public QDockWidget { Q_OBJECT +private: + QLabel *title = nullptr; + QHBoxLayout *layout = nullptr; + QPushButton *menuButton = nullptr; + +private slots: + void ShowControlMenu(); + void ToggleFloating(); + public: - inline OBSDock(QWidget *parent = nullptr) : QDockWidget(parent) {} + void EnableControlMenu(bool lock); + void AddControlMenu(); + + void SetTitle(const QString &newTitle); + void AddButton(const QString &themeID, const QString &toolTip, + const QObject *receiver, const char *slot); + void AddSeparator(); + OBSDock(QWidget *parent = nullptr); virtual void closeEvent(QCloseEvent *event); }; diff --git a/UI/window-extra-browsers.cpp b/UI/window-extra-browsers.cpp index 9387d0b88c7c02..539523b01ca521 100644 --- a/UI/window-extra-browsers.cpp +++ b/UI/window-extra-browsers.cpp @@ -534,7 +534,7 @@ void OBSBasic::AddExtraBrowserDock(const QString &title, const QString &url, dock->setObjectName(title + OBJ_NAME_SUFFIX); dock->resize(460, 600); dock->setMinimumSize(80, 80); - dock->setWindowTitle(title); + dock->SetTitle(title); dock->setAllowedAreas(Qt::AllDockWidgetAreas); QCefWidget *browser =