Skip to content

Commit a86ad6a

Browse files
committed
frontend: Add thumbnail size options to source select
1 parent dbb9409 commit a86ad6a

9 files changed

Lines changed: 333 additions & 103 deletions

File tree

frontend/components/SourceSelectButton.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "SourceSelectButton.hpp"
2020

21+
#include <dialogs/OBSBasicSourceSelect.hpp>
2122
#include <utility/ThumbnailManager.hpp>
2223
#include <utility/ThumbnailView.hpp>
2324
#include <widgets/OBSBasic.hpp>
@@ -49,7 +50,7 @@ SourceSelectButton::SourceSelectButton(OBSWeakSource weak, QWidget *parent) : QA
4950
setLayout(layout);
5051

5152
label = new QLabel(sourceName);
52-
label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
53+
label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
5354
label->setAttribute(Qt::WA_TransparentForMouseEvents);
5455
label->setObjectName("name");
5556

@@ -126,6 +127,8 @@ void SourceSelectButton::resizeEvent(QResizeEvent *)
126127
if (QLayout *layout = this->layout()) {
127128
layout->setContentsMargins(left, top, right, bottom);
128129
}
130+
131+
updatePixmap(thumbnail->getPixmap());
129132
}
130133

131134
void SourceSelectButton::enterEvent(QEnterEvent *)
@@ -170,6 +173,12 @@ void SourceSelectButton::handleSourceRenamed(QString name)
170173
label->setText(name);
171174
}
172175

176+
void SourceSelectButton::setThumbnailSize(int sizeId)
177+
{
178+
auto size = static_cast<OBS::SourceThumbnailSize>(sizeId);
179+
updateThumbnailSize(size);
180+
}
181+
173182
void SourceSelectButton::mouseMoveEvent(QMouseEvent *event)
174183
{
175184
if (!(event->buttons() & Qt::LeftButton)) {
@@ -213,6 +222,31 @@ void SourceSelectButton::updateThumbnail()
213222
thumbnail->requestUpdate();
214223
}
215224

225+
void SourceSelectButton::updateThumbnailSize(OBS::SourceThumbnailSize size)
226+
{
227+
switch (size) {
228+
case OBS::SourceThumbnailSize::None:
229+
image->setVisible(false);
230+
label->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
231+
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
232+
break;
233+
case OBS::SourceThumbnailSize::Small:
234+
image->setFixedSize(getThumbnailWidth() * 0.65, getThumbnailHeight() * 0.65);
235+
image->setVisible(true);
236+
label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
237+
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
238+
break;
239+
case OBS::SourceThumbnailSize::Large:
240+
image->setFixedSize(getThumbnailWidth(), getThumbnailHeight());
241+
image->setVisible(true);
242+
label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
243+
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
244+
break;
245+
default:
246+
break;
247+
}
248+
}
249+
216250
void SourceSelectButton::updatePixmap(QPixmap pixmap)
217251
{
218252
if (!pixmap.isNull()) {

frontend/components/SourceSelectButton.hpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@
2020

2121
#include <obs.hpp>
2222

23-
#include <QLabel>
2423
#include <QPointer>
2524
#include <QPushButton>
26-
#include <QTimer>
2725
#include <QVBoxLayout>
2826

2927
class QLabel;
3028
class Thumbnail;
3129
class ThumbnailView;
30+
namespace OBS {
31+
enum class SourceThumbnailSize : int;
32+
}
3233

3334
class SourceSelectButton : public QAbstractButton {
3435
Q_OBJECT
@@ -45,6 +46,8 @@ class SourceSelectButton : public QAbstractButton {
4546
void setThumbnailEnabled(bool enabled);
4647
void updateThumbnail();
4748

49+
void updateThumbnailSize(OBS::SourceThumbnailSize size);
50+
4851
int getThumbnailWidth() { return thumbnailWidth; };
4952
void setThumbnailWidth(int width) { thumbnailWidth = width; }
5053

@@ -82,6 +85,9 @@ private slots:
8285
void handleSourceRemoved();
8386
void handleSourceRenamed(QString name);
8487

88+
public slots:
89+
void setThumbnailSize(int sizeId);
90+
8591
signals:
8692
void sourceRemoved();
8793
};

frontend/data/locale/en-US.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,10 @@ Basic.SourceSelect.NoExisting="You have no existing %1 sources yet."
654654
Basic.SourceSelect.Accessible.SourceName="Source Name"
655655
Basic.SourceSelect.Accessible.Existing="Add an existing source"
656656
Basic.SourceSelect.Deprecated.Create="This source type is marked as deprecated and may be removed in the future."
657+
Basic.SourceSelect.Accessible.ThumbnailSize="Adjust size of preview thumbnails"
658+
Basic.SourceSelect.ThumbnailSize.None="Disabled"
659+
Basic.SourceSelect.ThumbnailSize.Small="Small"
660+
Basic.SourceSelect.ThumbnailSize.Large="Large"
657661

658662
# source box
659663
Basic.Main.Sources.Visibility="Visibility"

frontend/dialogs/OBSBasicSourceSelect.cpp

Lines changed: 118 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@
1919
#include "OBSApp.hpp"
2020
#include "OBSBasicSourceSelect.hpp"
2121

22+
#include <components/SegmentButton.hpp>
23+
#include <components/SegmentButtonFrame.hpp>
24+
#include <components/SourceSelectButton.hpp>
25+
2226
#include <utility/ResizeSignaler.hpp>
2327
#include <utility/ThumbnailManager.hpp>
2428
#include <utility/ThumbnailView.hpp>
2529

26-
#include "qt-wrappers.hpp"
30+
#include <qt-wrappers.hpp>
2731

2832
#include <QList>
2933
#include <QMessageBox>
@@ -34,7 +38,7 @@ constexpr int kUnversionedIdRole = Qt::UserRole + 1;
3438
constexpr int kDeprecatedRole = Qt::UserRole + 2;
3539

3640
constexpr QStringView kRecentTypeId = u"_recent";
37-
constexpr int kRecentListLimit = 16;
41+
constexpr int kRecentListLimit = 24;
3842

3943
struct AddSourceData {
4044
// Input data
@@ -236,9 +240,8 @@ OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, undo_stack &undo_s)
236240

237241
ui->setupUi(this);
238242

239-
existingFlowLayout = ui->existingListFrame->flowLayout();
240-
existingFlowLayout->setContentsMargins(0, 0, 0, 0);
241-
existingFlowLayout->setSpacing(0);
243+
sourceListLayout = new FlowLayout(0, 0, 0);
244+
ui->existingListContainerLayout->addLayout(sourceListLayout);
242245

243246
// The scroll viewport is not accessible via Designer, so we have to disable autoFillBackground here.
244247
// Additionally when Qt calls setWidget on a scrollArea to set the contents widget, it force sets
@@ -286,6 +289,38 @@ OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, undo_stack &undo_s)
286289
});
287290

288291
App()->DisableHotkeys();
292+
293+
// Set up thumbnail size buttons
294+
auto *segmentFrame = new SegmentButtonFrame(this);
295+
296+
auto *listButton = new SegmentButton(segmentFrame);
297+
listButton->setIcon(QIcon(":/res/images/sources/scene.svg"));
298+
listButton->setAccessibleName(QTStr("Basic.SourceSelect.ThumbnailSize.None"));
299+
listButton->setAccessibleDescription(QTStr("Basic.SourceSelect.Accessible.ThumbnailSize"));
300+
segmentFrame->addButton(listButton, static_cast<int>(OBS::SourceThumbnailSize::None));
301+
302+
auto *smallButton = new SegmentButton(segmentFrame);
303+
smallButton->setIcon(QIcon(":/res/images/thumb-small.svg"));
304+
smallButton->setAccessibleName(QTStr("Basic.SourceSelect.ThumbnailSize.Small"));
305+
smallButton->setAccessibleDescription(QTStr("Basic.SourceSelect.Accessible.ThumbnailSize"));
306+
segmentFrame->addButton(smallButton, static_cast<int>(OBS::SourceThumbnailSize::Small));
307+
308+
auto *largeButton = new SegmentButton(segmentFrame);
309+
largeButton->setIcon(QIcon(":/res/images/thumb-large.svg"));
310+
largeButton->setAccessibleName(QTStr("Basic.SourceSelect.ThumbnailSize.Large"));
311+
largeButton->setAccessibleDescription(QTStr("Basic.SourceSelect.Accessible.ThumbnailSize"));
312+
segmentFrame->addButton(largeButton, static_cast<int>(OBS::SourceThumbnailSize::Large));
313+
314+
connect(segmentFrame->group(), &QButtonGroup::idToggled, this, &OBSBasicSourceSelect::thumbnailSizeToggled);
315+
316+
const int savedThumbnailSize = config_get_int(App()->GetAppConfig(), "BasicWindow", "AddSourceThumbnailSize");
317+
if (auto *button = segmentFrame->button(savedThumbnailSize)) {
318+
button->setChecked(true);
319+
} else {
320+
largeButton->setChecked(true);
321+
}
322+
323+
ui->headerLayout->addWidget(segmentFrame);
289324
}
290325

291326
OBSBasicSourceSelect::~OBSBasicSourceSelect()
@@ -381,7 +416,7 @@ void OBSBasicSourceSelect::refreshSources()
381416

382417
void OBSBasicSourceSelect::updateExistingSources(int limit)
383418
{
384-
QLayout *layout = ui->existingListFrame->flowLayout();
419+
QLayout *layout = sourceListLayout;
385420

386421
// Clear existing buttons when switching types
387422
QLayoutItem *child = nullptr;
@@ -441,9 +476,13 @@ void OBSBasicSourceSelect::updateExistingSources(int limit)
441476
}
442477

443478
SourceSelectButton *newButton = new SourceSelectButton(weak, ui->existingListFrame);
479+
newButton->updateThumbnailSize(currentThumbnailSize);
480+
connect(this, &OBSBasicSourceSelect::thumbnailSizeChanged, newButton,
481+
&SourceSelectButton::setThumbnailSize);
482+
444483
std::string uuid = obs_source_get_uuid(source);
445484

446-
existingFlowLayout->addWidget(newButton);
485+
sourceListLayout->addWidget(newButton);
447486
sourceButtons->addButton(newButton);
448487

449488
bool isSelected = false;
@@ -588,7 +627,7 @@ void OBSBasicSourceSelect::sourceButtonToggled(QAbstractButton *button, bool che
588627
return;
589628
}
590629

591-
int toggledIndex = existingFlowLayout->indexOf(sourceButton);
630+
int toggledIndex = sourceListLayout->indexOf(sourceButton);
592631

593632
std::string_view toggledUuid = sourceButton->uuid();
594633

@@ -605,7 +644,7 @@ void OBSBasicSourceSelect::sourceButtonToggled(QAbstractButton *button, bool che
605644
int end = std::max(toggledIndex, lastSelectedIndex);
606645

607646
for (int i = start; i <= end; ++i) {
608-
QLayoutItem *item = existingFlowLayout->itemAt(i);
647+
QLayoutItem *item = sourceListLayout->itemAt(i);
609648
if (!item) {
610649
continue;
611650
}
@@ -660,6 +699,69 @@ void OBSBasicSourceSelect::sourceDropped(QString uuid)
660699
}
661700
}
662701

702+
void OBSBasicSourceSelect::thumbnailSizeToggled(int sizeId, bool checked)
703+
{
704+
if (!checked) {
705+
return;
706+
}
707+
708+
if (static_cast<int>(currentThumbnailSize) == sizeId) {
709+
return;
710+
}
711+
712+
auto newSize = static_cast<OBS::SourceThumbnailSize>(sizeId);
713+
updateThumbnailSize(newSize);
714+
}
715+
716+
void OBSBasicSourceSelect::updateThumbnailSize(OBS::SourceThumbnailSize newSize)
717+
{
718+
ui->existingListFrame->setUpdatesEnabled(false);
719+
720+
QLayout *newLayout = nullptr;
721+
if (newSize == OBS::SourceThumbnailSize::None) {
722+
newLayout = new QVBoxLayout();
723+
} else if (newSize == OBS::SourceThumbnailSize::Small || newSize == OBS::SourceThumbnailSize::Large) {
724+
if (currentThumbnailSize == OBS::SourceThumbnailSize::None) {
725+
newLayout = new FlowLayout(nullptr, 0, 0, 0);
726+
}
727+
} else {
728+
return;
729+
}
730+
731+
currentThumbnailSize = newSize;
732+
733+
config_set_int(App()->GetAppConfig(), "BasicWindow", "AddSourceThumbnailSize",
734+
static_cast<int>(newSize));
735+
736+
emit thumbnailSizeChanged(static_cast<int>(newSize));
737+
738+
if (newLayout != nullptr) {
739+
newLayout->setContentsMargins(0, 0, 0, 0);
740+
newLayout->setSpacing(0);
741+
742+
QLayoutItem *item;
743+
while ((item = sourceListLayout->takeAt(0)) != nullptr) {
744+
newLayout->addItem(item);
745+
}
746+
747+
ui->existingListContainerLayout->insertLayout(0, newLayout);
748+
749+
delete sourceListLayout;
750+
sourceListLayout = newLayout;
751+
}
752+
753+
sourceListLayout->activate();
754+
ui->existingListFrame->updateGeometry();
755+
756+
// Incredibly omega super cursed hack to solve an issue where the vertical layout for list mode would not
757+
// properly calculate heights for the widgets causing a squished size for exactly one tick of the event loop
758+
// but ONLY when clicking a button with the mouse. Keyboard works totally fine. This would cause very brief
759+
// visual flicker when switching to list mode.
760+
// Wait for Qt to finish processing whatever hellish stuff it's doing before re-enabling updates.
761+
QMetaObject::invokeMethod(
762+
this, [this]() { ui->existingListFrame->setUpdatesEnabled(true); }, Qt::QueuedConnection);
763+
}
764+
663765
void OBSBasicSourceSelect::addSelectedItem(const std::string &uuid)
664766
{
665767
auto it = std::find(selectedItems.begin(), selectedItems.end(), uuid);
@@ -674,7 +776,7 @@ void OBSBasicSourceSelect::addSelectedItem(const std::string &uuid)
674776
return;
675777
}
676778

677-
lastSelectedIndex = existingFlowLayout->indexOf(button);
779+
lastSelectedIndex = sourceListLayout->indexOf(button);
678780
}
679781

680782
void OBSBasicSourceSelect::removeSelectedItem(const std::string &uuid)
@@ -690,7 +792,7 @@ void OBSBasicSourceSelect::removeSelectedItem(const std::string &uuid)
690792
return;
691793
}
692794

693-
lastSelectedIndex = existingFlowLayout->indexOf(button);
795+
lastSelectedIndex = sourceListLayout->indexOf(button);
694796
}
695797

696798
void OBSBasicSourceSelect::clearSelectedItems()
@@ -715,8 +817,8 @@ void OBSBasicSourceSelect::clearSelectedItems()
715817

716818
SourceSelectButton *OBSBasicSourceSelect::findButtonForUuid(const std::string &uuid)
717819
{
718-
for (int i = 0; i <= existingFlowLayout->count(); ++i) {
719-
QLayoutItem *layoutItem = existingFlowLayout->itemAt(i);
820+
for (int i = 0; i <= sourceListLayout->count(); ++i) {
821+
QLayoutItem *layoutItem = sourceListLayout->itemAt(i);
720822
if (!layoutItem || !layoutItem->widget()) {
721823
continue;
722824
}
@@ -947,6 +1049,7 @@ void OBSBasicSourceSelect::sourceTypeSelected(QListWidgetItem *current, QListWid
9471049
ui->noExistingLabel->setText(
9481050
QTStr("Basic.SourceSelect.NoExisting").arg(getDisplayNameForSourceType(selectedTypeId)));
9491051

950-
ui->existingScrollArea->setVisible(existingFlowLayout->count() != 0);
951-
ui->noExistingLabel->setVisible(existingFlowLayout->count() == 0);
1052+
bool showExistingList = sourceListLayout->count() != 0;
1053+
ui->existingScrollArea->setVisible(showExistingList);
1054+
ui->noExistingLabel->setVisible(!showExistingList);
9521055
}

0 commit comments

Comments
 (0)