Forráskód Böngészése

UI: Add Grid Mode to Scenes Widget

Adds an option to the right click menu in the scenes widget to switch
modes. When in regular list mode, it'll let you select grid mode, and in
grid mode, it'll let you select list mode. Grid mode changes the scenes
widget to have a grid of buttons for scenes rather than a list, much
like XSplit.
VodBox 6 éve
szülő
commit
c0e2e7f12e

+ 2 - 0
UI/CMakeLists.txt

@@ -236,6 +236,7 @@ set(obs_SOURCES
 	window-remux.cpp
 	auth-base.cpp
 	source-tree.cpp
+	scene-tree.cpp
 	properties-view.cpp
 	focus-list.cpp
 	menu-button.cpp
@@ -288,6 +289,7 @@ set(obs_HEADERS
 	window-remux.hpp
 	auth-base.hpp
 	source-tree.hpp
+	scene-tree.hpp
 	properties-view.hpp
 	properties-view.moc.hpp
 	display-helpers.hpp

+ 2 - 0
UI/data/locale/en-US.ini

@@ -530,6 +530,8 @@ Basic.Main.ForceStopStreaming="Stop Streaming (discard delay)"
 Basic.Main.Group="Group %1"
 Basic.Main.GroupItems="Group Selected Items"
 Basic.Main.Ungroup="Ungroup"
+Basic.Main.GridMode="Grid Mode"
+Basic.Main.ListMode="List Mode"
 
 # basic mode file menu
 Basic.MainMenu.File="&File"

+ 22 - 0
UI/data/themes/Acri.qss

@@ -1022,3 +1022,25 @@ OBSBasic {
     qproperty-sceneIcon: url(./Dark/sources/scene.svg);
     qproperty-defaultIcon: url(./Dark/sources/default.svg);
 }
+
+/* Scene Tree */
+
+SceneTree#scenes {
+    qproperty-gridItemWidth: 180;
+    qproperty-gridItemHeight: 35;
+}
+
+*[gridMode="true"] SceneTree#scenes {
+	border-bottom: none;
+}
+
+*[gridMode="false"] SceneTree#scenes {
+	border-bottom: 2px solid #2f2f2f;
+}
+
+*[gridMode="true"] SceneTree::item {
+    padding: 4px;
+    padding-left: 10px;
+    padding-right: 10px;
+    margin: 0px;
+}

+ 35 - 0
UI/data/themes/Dark.qss

@@ -12,6 +12,7 @@
 /*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
 /*   GNU General Public License for more details.                             */
 /*                                                                            */
+/*                                                                            */
 /*   You should have received a copy of the GNU General Public License        */
 /*   along with this program.  If not, see <http://www.gnu.org/licenses/>.    */
 /******************************************************************************/
@@ -774,3 +775,37 @@ OBSBasic {
     qproperty-sceneIcon: url(./Dark/sources/scene.svg);
     qproperty-defaultIcon: url(./Dark/sources/default.svg);
 }
+
+/* Scene Tree */
+
+SceneTree {
+    qproperty-gridItemWidth: 150;
+	qproperty-gridItemHeight: 27;
+}
+
+*[gridMode="true"] SceneTree::item {
+    color: rgb(225,224,225); /* veryLight */
+    background-color: rgb(76,76,76);
+    border: none;
+    border-radius: 3px;
+    padding: 4px;
+	padding-left: 10px;
+	padding-right: 10px;
+	margin: 1px;
+}
+
+*[gridMode="true"] SceneTree::item:selected {
+    background-color: rgb(122,121,122); /* light */
+}
+
+*[gridMode="true"] SceneTree::item:hover {
+    background-color: rgb(122,121,122); /* light */
+}
+
+*[gridMode="true"] SceneTree::item:pressed {
+    background-color: rgb(31,30,31); /* veryDark */
+}
+
+*[gridMode="true"] SceneTree::item:checked {
+    background-color: rgb(122,121,122); /* light */
+}

+ 7 - 0
UI/data/themes/Rachni.qss

@@ -1365,3 +1365,10 @@ OBSBasic {
     qproperty-sceneIcon: url(./Dark/sources/scene.svg);
     qproperty-defaultIcon: url(./Dark/sources/default.svg);
 }
+
+/* Scene Tree */
+
+SceneTree#scenes {
+    qproperty-gridItemWidth: 150;
+    qproperty-gridItemHeight: 30;
+}

+ 7 - 0
UI/data/themes/System.qss

@@ -233,3 +233,10 @@ QListWidget::item,
 SourceTree::item {
 	height: 24px;
 }
+
+/* Scene Tree */
+
+SceneTree {
+    qproperty-gridItemWidth: 150;
+	qproperty-gridItemHeight: 24;
+}

+ 6 - 1
UI/forms/OBSBasic.ui

@@ -474,7 +474,7 @@
          <number>0</number>
         </property>
         <item>
-         <widget class="QListWidget" name="scenes">
+         <widget class="SceneTree" name="scenes">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
             <horstretch>0</horstretch>
@@ -1818,6 +1818,11 @@
    <extends>QListView</extends>
    <header>source-tree.hpp</header>
   </customwidget>
+  <customwidget>
+   <class>SceneTree</class>
+   <extends>QListWidget</extends>
+   <header>scene-tree.hpp</header>
+  </customwidget>
   <customwidget>
    <class>OBSDock</class>
    <extends>QDockWidget</extends>

+ 188 - 0
UI/scene-tree.cpp

@@ -0,0 +1,188 @@
+#include "obs.hpp"
+#include "scene-tree.hpp"
+#include "obs-app.hpp"
+
+#include <QSizePolicy>
+#include <QScrollBar>
+#include <QDropEvent>
+#include <QPushButton>
+
+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("");
+	}
+
+	resizeEvent(new QResizeEvent(size(), size()));
+}
+
+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)
+{
+	QListWidget::resizeEvent(event);
+
+	if (gridMode) {
+		int scrollWid = verticalScrollBar()->sizeHint().width();
+		int h = visualItemRect(item(count() - 1)).bottom();
+
+		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());
+		setSpacing(0);
+		for (int i = 0; i < count(); i++) {
+			item(i)->setData(Qt::SizeHintRole, QVariant());
+		}
+	}
+}
+
+void SceneTree::startDrag(Qt::DropActions supportedActions)
+{
+	QListWidget::startDrag(supportedActions);
+}
+
+void SceneTree::dropEvent(QDropEvent *event)
+{
+	QListWidget::dropEvent(event);
+	if (event->source() == this && gridMode) {
+		int scrollWid = verticalScrollBar()->sizeHint().width();
+		int h = visualItemRect(item(count() - 1)).bottom();
+
+		if (h < height()) {
+			setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+			scrollWid = 0;
+		} else {
+			setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+		}
+
+		float wid = contentsRect().width() - scrollWid - 1;
+
+		QPoint point = event->pos();
+
+		int x = (float)point.x() / wid * ceil(wid / maxWidth);
+		int y = point.y() / itemHeight;
+
+		int r = x + y * ceil(wid / maxWidth);
+
+		QListWidgetItem *item = takeItem(selectedIndexes()[0].row());
+		insertItem(r, item);
+		setCurrentItem(item);
+		resize(size());
+	}
+}
+
+void SceneTree::dragMoveEvent(QDragMoveEvent *event)
+{
+	if (gridMode) {
+		int scrollWid = verticalScrollBar()->sizeHint().width();
+		int h = visualItemRect(item(count() - 1)).bottom();
+
+		if (h < height()) {
+			setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+			scrollWid = 0;
+		} else {
+			setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+		}
+
+		float wid = contentsRect().width() - scrollWid - 1;
+
+		QPoint point = event->pos();
+
+		int x = (float)point.x() / wid * ceil(wid / maxWidth);
+		int y = point.y() / 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 {
+		QListWidget::dragMoveEvent(event);
+	}
+}
+
+void SceneTree::rowsInserted(const QModelIndex &parent, int start, int end)
+{
+	QListWidget::rowsInserted(parent, start, end);
+
+	QResizeEvent *event = new QResizeEvent(size(), size());
+	SceneTree::resizeEvent(event);
+}

+ 37 - 0
UI/scene-tree.hpp

@@ -0,0 +1,37 @@
+#pragma once
+
+#include <QListWidget>
+#include <QEvent>
+#include <QItemDelegate>
+
+class SceneTree : public QListWidget {
+	Q_OBJECT
+	Q_PROPERTY(int gridItemWidth READ GetGridItemWidth WRITE
+			   SetGridItemWidth DESIGNABLE true)
+	Q_PROPERTY(int gridItemHeight READ GetGridItemHeight WRITE
+			   SetGridItemHeight DESIGNABLE true)
+
+	bool gridMode = false;
+	int maxWidth = 150;
+	int itemHeight = 24;
+
+public:
+	void SetGridMode(bool grid);
+	bool GetGridMode();
+
+	void SetGridItemWidth(int width);
+	void SetGridItemHeight(int height);
+	int GetGridItemWidth();
+	int GetGridItemHeight();
+
+	explicit SceneTree(QWidget *parent = nullptr);
+
+protected:
+	virtual bool eventFilter(QObject *obj, QEvent *event) override;
+	virtual void resizeEvent(QResizeEvent *event) override;
+	virtual void startDrag(Qt::DropActions supportedActions) override;
+	virtual void dropEvent(QDropEvent *event) override;
+	virtual void dragMoveEvent(QDragMoveEvent *event) override;
+	virtual void rowsInserted(const QModelIndex &parent, int start,
+				  int end) override;
+};

+ 38 - 1
UI/window-basic-main.cpp

@@ -28,6 +28,7 @@
 #include <QScreen>
 #include <QColorDialog>
 #include <QSizePolicy>
+#include <QScrollBar>
 
 #include <util/dstr.h>
 #include <util/util.hpp>
@@ -94,7 +95,6 @@ template<typename OBSRef> struct SignalContainer {
 	OBSRef ref;
 	vector<shared_ptr<OBSSignal>> handlers;
 };
-
 }
 
 extern volatile long insideEventLoop;
@@ -105,6 +105,18 @@ Q_DECLARE_METATYPE(OBSSource);
 Q_DECLARE_METATYPE(obs_order_movement);
 Q_DECLARE_METATYPE(SignalContainer<OBSScene>);
 
+QDataStream &operator<<(QDataStream &out, const SignalContainer<OBSScene> &v)
+{
+	out << v.ref;
+	return out;
+}
+
+QDataStream &operator>>(QDataStream &in, SignalContainer<OBSScene> &v)
+{
+	in >> v.ref;
+	return in;
+}
+
 template<typename T> static T GetOBSRef(QListWidgetItem *item)
 {
 	return item->data(static_cast<int>(QtDataRole::OBSRef)).value<T>();
@@ -195,6 +207,9 @@ extern void RegisterRestreamAuth();
 OBSBasic::OBSBasic(QWidget *parent)
 	: OBSMainWindow(parent), ui(new Ui::OBSBasic)
 {
+	qRegisterMetaTypeStreamOperators<SignalContainer<OBSScene>>(
+		"SignalContainer<OBSScene>");
+
 	setAttribute(Qt::WA_NativeWindow);
 
 #if TWITCH_ENABLED
@@ -252,6 +267,10 @@ OBSBasic::OBSBasic(QWidget *parent)
 	ui->scenes->setAttribute(Qt::WA_MacShowFocusRect, false);
 	ui->sources->setAttribute(Qt::WA_MacShowFocusRect, false);
 
+	bool sceneGrid = config_get_bool(App()->GlobalConfig(), "BasicWindow",
+					 "gridMode");
+	ui->scenes->SetGridMode(sceneGrid);
+
 	ui->scenes->setItemDelegate(new SceneRenameDelegate(ui->scenes));
 
 	auto displayResize = [this]() {
@@ -4051,6 +4070,7 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
 
 	QMenu popup(this);
 	QMenu order(QTStr("Basic.MainMenu.Edit.Order"), this);
+
 	popup.addAction(QTStr("Add"), this,
 			SLOT(on_actionAddScene_triggered()));
 
@@ -4132,9 +4152,26 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
 			std::bind(showInMultiview, data));
 	}
 
+	popup.addSeparator();
+
+	bool grid = ui->scenes->GetGridMode();
+
+	QAction *gridAction = new QAction(grid ? QTStr("Basic.Main.ListMode")
+					       : QTStr("Basic.Main.GridMode"),
+					  this);
+	connect(gridAction, SIGNAL(triggered()), this,
+		SLOT(on_actionGridMode_triggered()));
+	popup.addAction(gridAction);
+
 	popup.exec(QCursor::pos());
 }
 
+void OBSBasic::on_actionGridMode_triggered()
+{
+	bool gridMode = !ui->scenes->GetGridMode();
+	ui->scenes->SetGridMode(gridMode);
+}
+
 void OBSBasic::on_actionAddScene_triggered()
 {
 	string name;

+ 1 - 0
UI/window-basic-main.hpp

@@ -816,6 +816,7 @@ private slots:
 	void on_scenes_currentItemChanged(QListWidgetItem *current,
 					  QListWidgetItem *prev);
 	void on_scenes_customContextMenuRequested(const QPoint &pos);
+	void on_actionGridMode_triggered();
 	void on_actionAddScene_triggered();
 	void on_actionRemoveScene_triggered();
 	void on_actionSceneUp_triggered();