Skip to content

Commit 0efd472

Browse files
Warchamp7Lain-B
andcommitted
frontend: Replace add source dropdown with dialog
Co-Authored-By: Lain <[email protected]>
1 parent 902f6d1 commit 0efd472

26 files changed

+2726
-525
lines changed

frontend/cmake/ui-components.cmake

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ target_sources(
3636
components/DisplayCaptureToolbar.cpp
3737
components/DisplayCaptureToolbar.hpp
3838
components/EditWidget.hpp
39+
components/FlowFrame.cpp
40+
components/FlowFrame.hpp
41+
components/FlowLayout.cpp
42+
components/FlowLayout.hpp
3943
components/FocusList.cpp
4044
components/FocusList.hpp
4145
components/GameCaptureToolbar.cpp
@@ -65,6 +69,8 @@ target_sources(
6569
components/SceneTree.hpp
6670
components/SilentUpdateCheckBox.hpp
6771
components/SilentUpdateSpinBox.hpp
72+
components/SourceSelectButton.cpp
73+
components/SourceSelectButton.hpp
6874
components/SourceToolbar.cpp
6975
components/SourceToolbar.hpp
7076
components/SourceTree.cpp

frontend/cmake/ui-utility.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ target_sources(
4949
utility/RemuxQueueModel.hpp
5050
utility/RemuxWorker.cpp
5151
utility/RemuxWorker.hpp
52+
utility/ResizeSignaler.hpp
5253
utility/SceneRenameDelegate.cpp
5354
utility/SceneRenameDelegate.hpp
5455
utility/ScreenshotObj.cpp
@@ -58,6 +59,8 @@ target_sources(
5859
utility/SimpleOutput.hpp
5960
utility/StartMultiTrackVideoStreamingGuard.hpp
6061
utility/SurfaceEventFilter.hpp
62+
utility/ThumbnailManager.cpp
63+
utility/ThumbnailManager.hpp
6164
utility/VCamConfig.hpp
6265
utility/audio-encoders.cpp
6366
utility/audio-encoders.hpp

frontend/components/FlowFrame.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/******************************************************************************
2+
Copyright (C) 2025 by Taylor Giampaolo <[email protected]>
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 2 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
******************************************************************************/
17+
18+
#include <components/FlowFrame.hpp>
19+
20+
#include <QKeyEvent>
21+
#include <QScrollArea>
22+
23+
FlowFrame::FlowFrame(QWidget *parent) : QFrame(parent)
24+
{
25+
layout = new FlowLayout(this);
26+
setLayout(layout);
27+
}
28+
29+
void FlowFrame::keyPressEvent(QKeyEvent *event)
30+
{
31+
QWidget *focused = focusWidget();
32+
if (!focused) {
33+
return;
34+
}
35+
36+
int index = -1;
37+
for (int i = 0; i < layout->count(); ++i) {
38+
if (!layout->itemAt(i)->widget()) {
39+
continue;
40+
}
41+
42+
auto focusProxy = layout->itemAt(i)->widget()->focusProxy();
43+
if (layout->itemAt(i)->widget() == focused || focusProxy == focused) {
44+
if (focusProxy == focused) {
45+
focused = layout->itemAt(i)->widget();
46+
}
47+
48+
index = i;
49+
break;
50+
}
51+
}
52+
53+
if (index == -1) {
54+
return;
55+
}
56+
57+
const QRect focusedRect = focused->geometry();
58+
QWidget *nextFocus = nullptr;
59+
60+
switch (event->key()) {
61+
case Qt::Key_Right:
62+
case Qt::Key_Down:
63+
case Qt::Key_Left:
64+
case Qt::Key_Up: {
65+
// Find next widget in the given direction
66+
int bestDistance = INT_MAX;
67+
for (int i = 0; i < layout->count(); ++i) {
68+
if (i == index) {
69+
continue;
70+
}
71+
72+
QWidget *widget = layout->itemAt(i)->widget();
73+
const QRect rect = widget->geometry();
74+
75+
bool isCandidate = false;
76+
int distance = INT_MAX;
77+
78+
switch (event->key()) {
79+
case Qt::Key_Right:
80+
if (rect.left() > focusedRect.right()) {
81+
distance = (rect.left() - focusedRect.right()) +
82+
qAbs(rect.center().y() - focusedRect.center().y());
83+
isCandidate = true;
84+
}
85+
break;
86+
case Qt::Key_Left:
87+
if (rect.right() < focusedRect.left()) {
88+
distance = (focusedRect.left() - rect.right()) +
89+
qAbs(rect.center().y() - focusedRect.center().y());
90+
isCandidate = true;
91+
}
92+
break;
93+
case Qt::Key_Down:
94+
if (rect.top() > focusedRect.bottom()) {
95+
distance = (rect.top() - focusedRect.bottom()) +
96+
qAbs(rect.center().x() - focusedRect.center().x());
97+
isCandidate = true;
98+
}
99+
break;
100+
case Qt::Key_Up:
101+
if (rect.bottom() < focusedRect.top()) {
102+
distance = (focusedRect.top() - rect.bottom()) +
103+
qAbs(rect.center().x() - focusedRect.center().x());
104+
isCandidate = true;
105+
}
106+
break;
107+
}
108+
109+
if (isCandidate && distance < bestDistance) {
110+
bestDistance = distance;
111+
nextFocus = widget;
112+
}
113+
}
114+
break;
115+
}
116+
default:
117+
QWidget::keyPressEvent(event);
118+
return;
119+
}
120+
121+
if (nextFocus) {
122+
nextFocus->setFocus();
123+
124+
QWidget *scrollParent = nextFocus->parentWidget();
125+
while (scrollParent) {
126+
QScrollArea *scrollArea = qobject_cast<QScrollArea *>(scrollParent);
127+
if (scrollArea) {
128+
scrollArea->ensureWidgetVisible(nextFocus, 20, 20);
129+
break;
130+
}
131+
scrollParent = scrollParent->parentWidget();
132+
}
133+
}
134+
}

frontend/components/FlowFrame.hpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/******************************************************************************
2+
Copyright (C) 2025 by Taylor Giampaolo <[email protected]>
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 2 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
******************************************************************************/
17+
18+
#pragma once
19+
20+
#include "FlowLayout.hpp"
21+
22+
#include <QFrame>
23+
24+
class FlowFrame : public QFrame {
25+
Q_OBJECT
26+
27+
public:
28+
explicit FlowFrame(QWidget *parent = nullptr);
29+
30+
FlowLayout *flowLayout() const { return layout; }
31+
32+
protected:
33+
void keyPressEvent(QKeyEvent *event) override;
34+
35+
private:
36+
FlowLayout *layout;
37+
};

frontend/components/FlowLayout.cpp

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/******************************************************************************
2+
Example provided by Qt
3+
<https://doc.qt.io/qt-6/qtwidgets-layouts-flowlayout-example.html>
4+
5+
Copyright (C) 2016 The Qt Company Ltd.
6+
SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
7+
******************************************************************************/
8+
9+
#include "FlowLayout.hpp"
10+
11+
#include <QWidget>
12+
13+
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
14+
: QLayout(parent),
15+
m_hSpace(hSpacing),
16+
m_vSpace(vSpacing)
17+
{
18+
setContentsMargins(margin, margin, margin, margin);
19+
}
20+
21+
FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) : m_hSpace(hSpacing), m_vSpace(vSpacing)
22+
{
23+
setContentsMargins(margin, margin, margin, margin);
24+
}
25+
26+
FlowLayout::~FlowLayout()
27+
{
28+
QLayoutItem *item;
29+
while ((item = takeAt(0))) {
30+
delete item;
31+
}
32+
}
33+
34+
void FlowLayout::addItem(QLayoutItem *item)
35+
{
36+
itemList.append(item);
37+
}
38+
39+
int FlowLayout::horizontalSpacing() const
40+
{
41+
if (m_hSpace >= 0) {
42+
return m_hSpace;
43+
} else {
44+
return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
45+
}
46+
}
47+
48+
int FlowLayout::verticalSpacing() const
49+
{
50+
if (m_vSpace >= 0) {
51+
return m_vSpace;
52+
} else {
53+
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
54+
}
55+
}
56+
57+
int FlowLayout::count() const
58+
{
59+
return itemList.size();
60+
}
61+
62+
QLayoutItem *FlowLayout::itemAt(int index) const
63+
{
64+
return itemList.value(index);
65+
}
66+
67+
QLayoutItem *FlowLayout::takeAt(int index)
68+
{
69+
if (index >= 0 && index < itemList.size()) {
70+
return itemList.takeAt(index);
71+
}
72+
return nullptr;
73+
}
74+
75+
Qt::Orientations FlowLayout::expandingDirections() const
76+
{
77+
return {};
78+
}
79+
80+
bool FlowLayout::hasHeightForWidth() const
81+
{
82+
return true;
83+
}
84+
85+
int FlowLayout::heightForWidth(int width) const
86+
{
87+
int height = doLayout(QRect(0, 0, width, 0), true);
88+
return height;
89+
}
90+
91+
void FlowLayout::setGeometry(const QRect &rect)
92+
{
93+
QLayout::setGeometry(rect);
94+
doLayout(rect, false);
95+
}
96+
97+
QSize FlowLayout::sizeHint() const
98+
{
99+
return minimumSize();
100+
}
101+
102+
QSize FlowLayout::minimumSize() const
103+
{
104+
QSize size;
105+
for (const QLayoutItem *item : std::as_const(itemList)) {
106+
size = size.expandedTo(item->minimumSize());
107+
}
108+
109+
const QMargins margins = contentsMargins();
110+
size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
111+
return size;
112+
}
113+
114+
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
115+
{
116+
int left;
117+
int top;
118+
int right;
119+
int bottom;
120+
121+
getContentsMargins(&left, &top, &right, &bottom);
122+
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
123+
int x = effectiveRect.x();
124+
int y = effectiveRect.y();
125+
int lineHeight = 0;
126+
127+
for (QLayoutItem *item : std::as_const(itemList)) {
128+
const QWidget *wid = item->widget();
129+
int spaceX = horizontalSpacing();
130+
if (spaceX == -1) {
131+
spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton,
132+
Qt::Horizontal);
133+
}
134+
135+
int spaceY = verticalSpacing();
136+
if (spaceY == -1) {
137+
spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton,
138+
Qt::Vertical);
139+
}
140+
141+
int nextX = x + item->sizeHint().width() + spaceX;
142+
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
143+
x = effectiveRect.x();
144+
y = y + lineHeight + spaceY;
145+
nextX = x + item->sizeHint().width() + spaceX;
146+
lineHeight = 0;
147+
}
148+
149+
if (!testOnly) {
150+
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
151+
}
152+
153+
x = nextX;
154+
lineHeight = qMax(lineHeight, item->sizeHint().height());
155+
}
156+
return y + lineHeight - rect.y() + bottom;
157+
}
158+
159+
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
160+
{
161+
QObject *parent = this->parent();
162+
if (!parent) {
163+
return -1;
164+
} else if (parent->isWidgetType()) {
165+
QWidget *pw = static_cast<QWidget *>(parent);
166+
return pw->style()->pixelMetric(pm, nullptr, pw);
167+
} else {
168+
return static_cast<QLayout *>(parent)->spacing();
169+
}
170+
}

0 commit comments

Comments
 (0)