From 8a86ae5226fe56fbac32d9f4926df4cf3dcb3da1 Mon Sep 17 00:00:00 2001 From: WorldFS Date: Thu, 24 Nov 2022 12:44:35 +0800 Subject: [PATCH] UI: Resize projector window by resolution or scale --- UI/CMakeLists.txt | 2 + UI/data/locale/en-US.ini | 4 + UI/window-projector-custom-size-dialog.cpp | 94 +++++++++++++++++++ UI/window-projector-custom-size-dialog.hpp | 30 ++++++ UI/window-projector.cpp | 102 +++++++++++++++++++++ UI/window-projector.hpp | 5 + 6 files changed, 237 insertions(+) create mode 100644 UI/window-projector-custom-size-dialog.cpp create mode 100644 UI/window-projector-custom-size-dialog.hpp diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 1d49ab0ba74cac..073e9a750885e5 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -276,6 +276,8 @@ target_sources( window-log-reply.cpp window-projector.cpp window-projector.hpp + window-projector-custom-size-dialog.cpp + window-projector-custom-size-dialog.hpp window-remux.cpp window-remux.hpp) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 8a201838068d93..413974a365966c 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -43,6 +43,10 @@ SourceWindow="Windowed Projector (Source)" MultiviewProjector="Multiview (Fullscreen)" MultiviewWindowed="Multiview (Windowed)" ResizeProjectorWindowToContent="Fit window to content" +ResizeProjectorWindow="Resize window" +ResizeProjectorWindowCustom="Custom..." +ResizeProjectorWindowCustomResolution="Resolution" +ResizeProjectorWindowCustomScale="Scale" Clear="Clear" Revert="Revert" Show="Show" diff --git a/UI/window-projector-custom-size-dialog.cpp b/UI/window-projector-custom-size-dialog.cpp new file mode 100644 index 00000000000000..8149c20ca25a1f --- /dev/null +++ b/UI/window-projector-custom-size-dialog.cpp @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include +#include "obs-app.hpp" +#include "window-projector-custom-size-dialog.hpp" + +OBSProjectorCustomSizeDialog::OBSProjectorCustomSizeDialog(QWidget *parent) + : QDialog(parent) +{ + QSize parentSize = parent->size(); + this->setWindowTitle(QTStr("ResizeProjectorWindow")); + this->setAttribute(Qt::WA_DeleteOnClose); + + QVBoxLayout *layout = new QVBoxLayout(this); + + // Input method dropdown + customMethod = new QComboBox(); + customMethod->addItem(QTStr("ResizeProjectorWindowCustomResolution")); + customMethod->addItem(QTStr("ResizeProjectorWindowCustomScale")); + layout->addWidget(customMethod); + connect(customMethod, + QOverload::of(&QComboBox::currentIndexChanged), this, + &OBSProjectorCustomSizeDialog::ChangeCustomMethod); + + // layout to contain both resolution and scale input + inputLayout = new QStackedLayout(); + + // resolution input + QFrame *resolutionInput = new QFrame(); + QGridLayout *resolutionInputLayout = new QGridLayout(); + resolutionInput->setLayout(resolutionInputLayout); + + width = CreateSpinBox(parentSize.width()); + resolutionInputLayout->addWidget(width, 0, 0); + resolutionInputLayout->setColumnStretch(0, 1); + QLabel *resolutionLabel = new QLabel(QString("x")); + resolutionInputLayout->addWidget(resolutionLabel, 0, 1); + height = CreateSpinBox(parentSize.height()); + resolutionInputLayout->addWidget(height, 0, 2); + resolutionInputLayout->setColumnStretch(2, 1); + inputLayout->addWidget(resolutionInput); + + // scale input + QFrame *scaleInput = new QFrame(); + QGridLayout *scaleInputLayout = new QGridLayout(); + scaleInput->setLayout(scaleInputLayout); + scale = CreateSpinBox(100); + scaleInputLayout->addWidget(scale, 0, 0); + scaleInputLayout->setColumnStretch(0, 1); + QLabel *scaleLabel = new QLabel(QString("%")); + scaleInputLayout->addWidget(scaleLabel, 0, 1); + inputLayout->addWidget(scaleInput); + + // only show resolution input on open + inputLayout->setCurrentIndex(0); + + layout->addLayout(inputLayout); + + // OK button + QPushButton *okButton = new QPushButton(QTStr("OK")); + layout->addWidget(okButton); + connect(okButton, &QPushButton::clicked, this, + &OBSProjectorCustomSizeDialog::ConfirmAndClose); + + this->setLayout(layout); +} + +QSpinBox *OBSProjectorCustomSizeDialog::CreateSpinBox(int initValue) +{ + QSpinBox *spinbox = new QSpinBox(); + spinbox->setMinimum(1); + spinbox->setMaximum(INT_MAX); + spinbox->setValue(initValue); + return spinbox; +} + +void OBSProjectorCustomSizeDialog::ChangeCustomMethod(int index) +{ + inputLayout->setCurrentIndex(index); +} + +void OBSProjectorCustomSizeDialog::ConfirmAndClose() +{ + if (customMethod->currentIndex() == 0) { + emit ApplyResolution(width->value(), height->value()); + } else { + emit ApplyScale(scale->value()); + } + emit accept(); +} diff --git a/UI/window-projector-custom-size-dialog.hpp b/UI/window-projector-custom-size-dialog.hpp new file mode 100644 index 00000000000000..90489e17b79371 --- /dev/null +++ b/UI/window-projector-custom-size-dialog.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include +#include + +class OBSProjectorCustomSizeDialog : public QDialog { + Q_OBJECT + +public: + OBSProjectorCustomSizeDialog(QWidget *parent); + +private: + QComboBox *customMethod; + QSpinBox *width; + QSpinBox *height; + QSpinBox *scale; + QStackedLayout *inputLayout; + QSpinBox *CreateSpinBox(int initValue); + +signals: + void ApplyResolution(int width, int height); + void ApplyScale(int scale); + +private slots: + void ChangeCustomMethod(int index); + void ConfirmAndClose(); +}; diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index df2e94cbf34bc9..cdc22456348b21 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -9,6 +9,7 @@ #include "qt-wrappers.hpp" #include "platform.hpp" #include "multiview.hpp" +#include "window-projector-custom-size-dialog.hpp" static QList multiviewProjectors; @@ -290,6 +291,70 @@ void OBSProjector::mousePressEvent(QMouseEvent *event) } else if (!this->isMaximized()) { popup.addAction(QTStr("ResizeProjectorWindowToContent"), this, SLOT(ResizeToContent())); + + // Resize Window menu + QMenu *resizeWindowMenu = + new QMenu(QTStr("ResizeProjectorWindow")); + + // Resize Window menu: preset resolution items + auto resizeToResolutionAction = [this](QAction *action) { + int width = action->property("width").toInt(); + int height = action->property("height").toInt(); + resize(width, height); + }; + + int resolutionPresets[][2] = {{1280, 720}, + {1920, 1080}, + {2560, 1440}, + {3840, 2160}}; + for (size_t i = 0; + i < sizeof(resolutionPresets) / (sizeof(int) * 2); + i++) { + QAction *resolution = new QAction( + QString("%1 x %2") + .arg(resolutionPresets[i][0]) + .arg(resolutionPresets[i][1]), + this); + resolution->setProperty( + "width", resolutionPresets[i][0]); + resolution->setProperty( + "height", resolutionPresets[i][1]); + connect(resolution, &QAction::triggered, + std::bind(resizeToResolutionAction, + resolution)); + resizeWindowMenu->addAction(resolution); + } + resizeWindowMenu->addSeparator(); + + // Resize Window menu: preset scale items + auto resizeToScaleAction = [this](QAction *action) { + double scale = + action->property("scale").toInt(); + ResizeToScale(scale); + }; + + int scalePresets[] = {50, 75, 100, 125, 150, 200}; + + for (size_t i = 0; + i < sizeof(scalePresets) / sizeof(int); i++) { + QAction *scale = new QAction( + QString("%1%").arg(scalePresets[i]), + this); + scale->setProperty("scale", scalePresets[i]); + connect(scale, &QAction::triggered, + std::bind(resizeToScaleAction, scale)); + resizeWindowMenu->addAction(scale); + } + + resizeWindowMenu->addSeparator(); + + QAction *custom = new QAction( + QTStr("ResizeProjectorWindowCustom"), this); + connect(custom, &QAction::triggered, this, + &OBSProjector::OpenCustomWindowSizeDialog); + resizeWindowMenu->addAction(custom); + + popup.addMenu(resizeWindowMenu); } QAction *hideFrameAction = @@ -517,6 +582,43 @@ void OBSProjector::ResizeToContent() resize(newX, newY); } +QSize OBSProjector::GetTargetSize() +{ + OBSSource source = GetSource(); + if (source) { + return QSize(std::max(obs_source_get_width(source), 1u), + std::max(obs_source_get_height(source), 1u)); + } else { + struct obs_video_info ovi; + obs_get_video_info(&ovi); + return QSize(ovi.base_width, ovi.base_height); + } +} + +void OBSProjector::ResizeToScale(int scale) +{ + QSize targetSize = GetTargetSize(); + double scaleFactor = scale / 100.0; + resize(targetSize.width() * scaleFactor, + targetSize.height() * scaleFactor); +} + +void OBSProjector::ResizeToResolution(int width, int height) +{ + resize(width, height); +} + +void OBSProjector::OpenCustomWindowSizeDialog() +{ + OBSProjectorCustomSizeDialog *dialog = + new OBSProjectorCustomSizeDialog(this); + connect(dialog, &OBSProjectorCustomSizeDialog::ApplyResolution, this, + &OBSProjector::ResizeToResolution); + connect(dialog, &OBSProjectorCustomSizeDialog::ApplyScale, this, + &OBSProjector::ResizeToScale); + dialog->open(); +} + void OBSProjector::AlwaysOnTopToggled(bool isAlwaysOnTop) { SetIsAlwaysOnTop(isAlwaysOnTop, true); diff --git a/UI/window-projector.hpp b/UI/window-projector.hpp index ea7012b1b51c1a..4807b2fe8d67cd 100644 --- a/UI/window-projector.hpp +++ b/UI/window-projector.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include "qt-display.hpp" #include "multiview.hpp" @@ -49,11 +50,15 @@ class OBSProjector : public OBSQTDisplay { void SetMonitor(int monitor); QScreen *screen = nullptr; + QSize GetTargetSize(); private slots: void EscapeTriggered(); void OpenFullScreenProjector(); void ResizeToContent(); + void ResizeToScale(int scale); + void ResizeToResolution(int width, int height); + void OpenCustomWindowSizeDialog(); void OpenWindowedProjector(); void AlwaysOnTopToggled(bool alwaysOnTop); void ScreenRemoved(QScreen *screen_);