浏览代码

Merge pull request #2211 from VodBox/scene-grid-mode

UI: Add Grid Mode to Scenes Widget
Jim 5 年之前
父节点
当前提交
bcddf4ddbf

+ 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();