obs-studio/UI/scene-tree.cpp
gxalpha dc30cf0843 UI: Don't reselect SceneTree items if tree is clearing
In the scene tree, we currently prevent deselecting items by
automatically reselecting a new one to workaround a regression in Qt 6.2
that would let users unselect items even in SingleSelection QItemViews.
When clearing however, we explicitly want to unselect the currently
selected item, so we should avoid reselecting it in the SceneTree.
2022-09-30 05:41:35 -07:00

259 lines
5.9 KiB
C++

#include "obs.hpp"
#include "scene-tree.hpp"
#include "obs-app.hpp"
#include <QSizePolicy>
#include <QScrollBar>
#include <QDropEvent>
#include <QPushButton>
#include <QTimer>
SceneTree::SceneTree(QWidget *parent_) : QListWidget(parent_)
{
installEventFilter(this);
setDragDropMode(InternalMove);
setMovement(QListView::Snap);
}
void SceneTree::SetGridMode(bool grid)
{
config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode", grid);
parent()->setProperty("gridMode", grid);
gridMode = grid;
if (gridMode) {
setResizeMode(QListView::Adjust);
setViewMode(QListView::IconMode);
setUniformItemSizes(true);
setStyleSheet("*{padding: 0; margin: 0;}");
} else {
setViewMode(QListView::ListMode);
setResizeMode(QListView::Fixed);
setStyleSheet("");
}
QResizeEvent event(size(), size());
resizeEvent(&event);
}
bool SceneTree::GetGridMode()
{
return gridMode;
}
void SceneTree::SetGridItemWidth(int width)
{
maxWidth = width;
}
void SceneTree::SetGridItemHeight(int height)
{
itemHeight = height;
}
int SceneTree::GetGridItemWidth()
{
return maxWidth;
}
int SceneTree::GetGridItemHeight()
{
return itemHeight;
}
bool SceneTree::eventFilter(QObject *obj, QEvent *event)
{
return QObject::eventFilter(obj, event);
}
void SceneTree::resizeEvent(QResizeEvent *event)
{
if (gridMode) {
int scrollWid = verticalScrollBar()->sizeHint().width();
const QRect lastItem = visualItemRect(item(count() - 1));
const int h = lastItem.y() + lastItem.height();
if (h < height()) {
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollWid = 0;
} else {
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
}
int wid = contentsRect().width() - scrollWid - 1;
int items = (int)ceil((float)wid / maxWidth);
int itemWidth = wid / items;
setGridSize(QSize(itemWidth, itemHeight));
for (int i = 0; i < count(); i++) {
item(i)->setSizeHint(QSize(itemWidth, itemHeight));
}
} else {
setGridSize(QSize());
for (int i = 0; i < count(); i++) {
item(i)->setData(Qt::SizeHintRole, QVariant());
}
}
QListWidget::resizeEvent(event);
}
void SceneTree::startDrag(Qt::DropActions supportedActions)
{
QListWidget::startDrag(supportedActions);
}
void SceneTree::dropEvent(QDropEvent *event)
{
if (event->source() != this) {
QListWidget::dropEvent(event);
return;
}
if (gridMode) {
int scrollWid = verticalScrollBar()->sizeHint().width();
const QRect firstItem = visualItemRect(item(0));
const QRect lastItem = visualItemRect(item(count() - 1));
const int h = lastItem.y() + lastItem.height();
const int firstItemY = abs(firstItem.y());
if (h < height()) {
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollWid = 0;
} else {
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
}
float wid = contentsRect().width() - scrollWid - 1;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QPoint point = event->position().toPoint();
#else
QPoint point = event->pos();
#endif
int x = (float)point.x() / wid * ceil(wid / maxWidth);
int y = (point.y() + firstItemY) / itemHeight;
int r = x + y * ceil(wid / maxWidth);
QListWidgetItem *item = takeItem(selectedIndexes()[0].row());
insertItem(r, item);
setCurrentItem(item);
resize(size());
}
QListWidget::dropEvent(event);
// We must call resizeEvent to correctly place all grid items.
// We also do this in rowsInserted.
QResizeEvent resEvent(size(), size());
SceneTree::resizeEvent(&resEvent);
QTimer::singleShot(100, [this]() { emit scenesReordered(); });
}
void SceneTree::RepositionGrid(QDragMoveEvent *event)
{
int scrollWid = verticalScrollBar()->sizeHint().width();
const QRect firstItem = visualItemRect(item(0));
const QRect lastItem = visualItemRect(item(count() - 1));
const int h = lastItem.y() + lastItem.height();
const int firstItemY = abs(firstItem.y());
if (h < height()) {
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollWid = 0;
} else {
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
}
float wid = contentsRect().width() - scrollWid - 1;
if (event) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QPoint point = event->position().toPoint();
#else
QPoint point = event->pos();
#endif
int x = (float)point.x() / wid * ceil(wid / maxWidth);
int y = (point.y() + firstItemY) / itemHeight;
int r = x + y * ceil(wid / maxWidth);
int orig = selectedIndexes()[0].row();
for (int i = 0; i < count(); i++) {
auto *wItem = item(i);
if (wItem->isSelected())
continue;
QModelIndex index = indexFromItem(wItem);
int off = (i >= r ? 1 : 0) -
(i > orig && i > r ? 1 : 0) -
(i > orig && i == r ? 2 : 0);
int xPos = (i + off) % (int)ceil(wid / maxWidth);
int yPos = (i + off) / (int)ceil(wid / maxWidth);
QSize g = gridSize();
QPoint position(xPos * g.width(), yPos * g.height());
setPositionForIndex(position, index);
}
} else {
for (int i = 0; i < count(); i++) {
auto *wItem = item(i);
if (wItem->isSelected())
continue;
QModelIndex index = indexFromItem(wItem);
int xPos = i % (int)ceil(wid / maxWidth);
int yPos = i / (int)ceil(wid / maxWidth);
QSize g = gridSize();
QPoint position(xPos * g.width(), yPos * g.height());
setPositionForIndex(position, index);
}
}
}
void SceneTree::dragMoveEvent(QDragMoveEvent *event)
{
if (gridMode) {
RepositionGrid(event);
}
QListWidget::dragMoveEvent(event);
}
void SceneTree::dragLeaveEvent(QDragLeaveEvent *event)
{
if (gridMode) {
RepositionGrid();
}
QListWidget::dragLeaveEvent(event);
}
void SceneTree::rowsInserted(const QModelIndex &parent, int start, int end)
{
QResizeEvent event(size(), size());
SceneTree::resizeEvent(&event);
QListWidget::rowsInserted(parent, start, end);
}
// Workaround for QTBUG-105870. Remove once that is solved upstream.
void SceneTree::selectionChanged(const QItemSelection &selected,
const QItemSelection &deselected)
{
if (selected.count() == 0 && deselected.count() > 0 &&
!property("clearing").toBool())
setCurrentRow(deselected.indexes().front().row());
}