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;
3438constexpr int kDeprecatedRole = Qt::UserRole + 2 ;
3539
3640constexpr QStringView kRecentTypeId = u" _recent" ;
37- constexpr int kRecentListLimit = 16 ;
41+ constexpr int kRecentListLimit = 24 ;
3842
3943struct 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
291326OBSBasicSourceSelect::~OBSBasicSourceSelect ()
@@ -381,7 +416,7 @@ void OBSBasicSourceSelect::refreshSources()
381416
382417void 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,68 @@ 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" , static_cast <int >(newSize));
734+
735+ emit thumbnailSizeChanged (static_cast <int >(newSize));
736+
737+ if (newLayout != nullptr ) {
738+ newLayout->setContentsMargins (0 , 0 , 0 , 0 );
739+ newLayout->setSpacing (0 );
740+
741+ QLayoutItem *item;
742+ while ((item = sourceListLayout->takeAt (0 )) != nullptr ) {
743+ newLayout->addItem (item);
744+ }
745+
746+ ui->existingListContainerLayout ->insertLayout (0 , newLayout);
747+
748+ delete sourceListLayout;
749+ sourceListLayout = newLayout;
750+ }
751+
752+ sourceListLayout->activate ();
753+ ui->existingListFrame ->updateGeometry ();
754+
755+ // Incredibly omega super cursed hack to solve an issue where the vertical layout for list mode would not
756+ // properly calculate heights for the widgets causing a squished size for exactly one tick of the event loop
757+ // but ONLY when clicking a button with the mouse. Keyboard works totally fine. This would cause very brief
758+ // visual flicker when switching to list mode.
759+ // Wait for Qt to finish processing whatever hellish stuff it's doing before re-enabling updates.
760+ QMetaObject::invokeMethod (
761+ this , [this ]() { ui->existingListFrame ->setUpdatesEnabled (true ); }, Qt::QueuedConnection);
762+ }
763+
663764void OBSBasicSourceSelect::addSelectedItem (const std::string &uuid)
664765{
665766 auto it = std::find (selectedItems.begin (), selectedItems.end (), uuid);
@@ -674,7 +775,7 @@ void OBSBasicSourceSelect::addSelectedItem(const std::string &uuid)
674775 return ;
675776 }
676777
677- lastSelectedIndex = existingFlowLayout ->indexOf (button);
778+ lastSelectedIndex = sourceListLayout ->indexOf (button);
678779}
679780
680781void OBSBasicSourceSelect::removeSelectedItem (const std::string &uuid)
@@ -690,7 +791,7 @@ void OBSBasicSourceSelect::removeSelectedItem(const std::string &uuid)
690791 return ;
691792 }
692793
693- lastSelectedIndex = existingFlowLayout ->indexOf (button);
794+ lastSelectedIndex = sourceListLayout ->indexOf (button);
694795}
695796
696797void OBSBasicSourceSelect::clearSelectedItems ()
@@ -715,8 +816,8 @@ void OBSBasicSourceSelect::clearSelectedItems()
715816
716817SourceSelectButton *OBSBasicSourceSelect::findButtonForUuid (const std::string &uuid)
717818{
718- for (int i = 0 ; i <= existingFlowLayout ->count (); ++i) {
719- QLayoutItem *layoutItem = existingFlowLayout ->itemAt (i);
819+ for (int i = 0 ; i <= sourceListLayout ->count (); ++i) {
820+ QLayoutItem *layoutItem = sourceListLayout ->itemAt (i);
720821 if (!layoutItem || !layoutItem->widget ()) {
721822 continue ;
722823 }
@@ -947,6 +1048,7 @@ void OBSBasicSourceSelect::sourceTypeSelected(QListWidgetItem *current, QListWid
9471048 ui->noExistingLabel ->setText (
9481049 QTStr (" Basic.SourceSelect.NoExisting" ).arg (getDisplayNameForSourceType (selectedTypeId)));
9491050
950- ui->existingScrollArea ->setVisible (existingFlowLayout->count () != 0 );
951- ui->noExistingLabel ->setVisible (existingFlowLayout->count () == 0 );
1051+ bool showExistingList = sourceListLayout->count () != 0 ;
1052+ ui->existingScrollArea ->setVisible (showExistingList);
1053+ ui->noExistingLabel ->setVisible (!showExistingList);
9521054}
0 commit comments