UI: Add grouping
parent
46cce067f0
commit
88b6c63964
|
@ -152,6 +152,7 @@ set(obs_SOURCES
|
|||
window-log-reply.cpp
|
||||
window-projector.cpp
|
||||
window-remux.cpp
|
||||
source-tree.cpp
|
||||
properties-view.cpp
|
||||
focus-list.cpp
|
||||
menu-button.cpp
|
||||
|
@ -198,6 +199,7 @@ set(obs_HEADERS
|
|||
window-log-reply.hpp
|
||||
window-projector.hpp
|
||||
window-remux.hpp
|
||||
source-tree.hpp
|
||||
properties-view.hpp
|
||||
properties-view.moc.hpp
|
||||
display-helpers.hpp
|
||||
|
@ -211,6 +213,7 @@ set(obs_HEADERS
|
|||
visibility-checkbox.hpp
|
||||
locked-checkbox.hpp
|
||||
horizontal-scroll-area.hpp
|
||||
expand-checkbox.hpp
|
||||
vertical-scroll-area.hpp
|
||||
visibility-item-widget.hpp
|
||||
slider-absoluteset-style.hpp
|
||||
|
|
|
@ -84,6 +84,7 @@ StudioMode.Preview="Preview"
|
|||
StudioMode.Program="Program"
|
||||
ShowInMultiview="Show in Multiview"
|
||||
VerticalLayout="Vertical Layout"
|
||||
Group="Group"
|
||||
|
||||
# warning if program already open
|
||||
AlreadyRunning.Title="OBS is already running"
|
||||
|
@ -458,6 +459,9 @@ Basic.Main.StoppingReplayBuffer="Stopping Replay Buffer..."
|
|||
Basic.Main.StopStreaming="Stop Streaming"
|
||||
Basic.Main.StoppingStreaming="Stopping Stream..."
|
||||
Basic.Main.ForceStopStreaming="Stop Streaming (discard delay)"
|
||||
Basic.Main.Group="Group %1"
|
||||
Basic.Main.GroupItems="Group Selected Items"
|
||||
Basic.Main.Ungroup="Ungroup"
|
||||
|
||||
# basic mode file menu
|
||||
Basic.MainMenu.File="&File"
|
||||
|
|
|
@ -94,7 +94,8 @@ QMenuBar::item:selected {
|
|||
}
|
||||
|
||||
/* Listbox item */
|
||||
QListWidget::item {
|
||||
QListWidget::item,
|
||||
SourceTree::item {
|
||||
padding: 4px 2px;
|
||||
margin-bottom: 2px;
|
||||
margin-top: 0px;
|
||||
|
@ -110,11 +111,6 @@ QListWidget QLineEdit {
|
|||
border-radius: none;
|
||||
}
|
||||
|
||||
SourceListWidget::item {
|
||||
margin-bottom: 1px;
|
||||
padding: -4px 2px;
|
||||
}
|
||||
|
||||
/* Dock stuff */
|
||||
QDockWidget {
|
||||
background: transparent;
|
||||
|
@ -157,6 +153,23 @@ SourceListWidget {
|
|||
border-bottom: 2px solid #2f2f2f;
|
||||
}
|
||||
|
||||
SourceTree {
|
||||
border: none;
|
||||
border-bottom: 1px solid #2f2f2f;
|
||||
}
|
||||
|
||||
SourceTree QLabel {
|
||||
padding: 2px 0px;
|
||||
margin: -2px 4px -2px;
|
||||
}
|
||||
|
||||
SourceTree QLineEdit {
|
||||
background-color: #0c101e;
|
||||
padding: 2px;
|
||||
margin: -2px 6px -2px 3px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#scenesFrame,
|
||||
#sourcesFrame {
|
||||
margin-left: -7px;
|
||||
|
@ -179,13 +192,15 @@ SourceListWidget {
|
|||
}
|
||||
|
||||
/* Listbox item selected, unfocused */
|
||||
QListWidget::item:hover {
|
||||
QListWidget::item:hover,
|
||||
SourceTree::item:hover {
|
||||
background-color: #212121;
|
||||
border: 1px solid #333336;
|
||||
}
|
||||
|
||||
/* Listbox item selected */
|
||||
QListWidget::item:selected {
|
||||
QListWidget::item:selected,
|
||||
SourceTree::item:selected {
|
||||
background-color: #131a30;
|
||||
border: 1px solid #252a45;
|
||||
}
|
||||
|
@ -727,6 +742,30 @@ OBSHotkeyLabel[hotkeyPairHover=true] {
|
|||
}
|
||||
|
||||
|
||||
/* Group Collapse Checkbox */
|
||||
|
||||
SourceTreeSubItemCheckBox {
|
||||
background: transparent;
|
||||
outline: none;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator:checked,
|
||||
SourceTreeSubItemCheckBox::indicator:checked:hover {
|
||||
image: url(./Dark/expand.png);
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator:unchecked,
|
||||
SourceTreeSubItemCheckBox::indicator:unchecked:hover {
|
||||
image: url(./Dark/collapse.png);
|
||||
}
|
||||
|
||||
|
||||
/* Label warning/error */
|
||||
|
||||
QLabel#warningLabel {
|
||||
|
@ -753,10 +792,6 @@ OBSBasicProperties,
|
|||
background: #101010;
|
||||
}
|
||||
|
||||
#OBSBasicSourceSelect #sourceList {
|
||||
border-bottom: 2px solid #333336;
|
||||
}
|
||||
|
||||
FocusList::item {
|
||||
padding: 0px 2px;
|
||||
}
|
||||
|
|
|
@ -551,6 +551,27 @@ OBSHotkeyLabel[hotkeyPairHover=true] {
|
|||
}
|
||||
|
||||
|
||||
/* Group Collapse Checkbox */
|
||||
|
||||
SourceTreeSubItemCheckBox {
|
||||
background: transparent;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator:checked {
|
||||
image: url(./Dark/expand.png);
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator:unchecked {
|
||||
image: url(./Dark/collapse.png);
|
||||
}
|
||||
|
||||
|
||||
/* Label warning/error */
|
||||
|
||||
QLabel#warningLabel {
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
|
@ -51,6 +51,24 @@ MuteCheckBox::indicator:unchecked {
|
|||
image: url(:/res/images/unmute.png);
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox {
|
||||
background: transparent;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator:checked {
|
||||
image: url(:/res/images/expand.png);
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator:unchecked {
|
||||
image: url(:/res/images/collapse.png);
|
||||
}
|
||||
|
||||
OBSHotkeyLabel[hotkeyPairHover=true] {
|
||||
color: red;
|
||||
}
|
||||
|
|
|
@ -701,6 +701,30 @@ MuteCheckBox::indicator:unchecked:disabled {
|
|||
image: url(./Dark/unmute.png);
|
||||
}
|
||||
|
||||
/****************************/
|
||||
/* --- Group Checkboxes --- */
|
||||
/****************************/
|
||||
|
||||
SourceTreeSubItemCheckBox {
|
||||
background: transparent;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator:checked,
|
||||
SourceTreeSubItemCheckBox::indicator:checked:hover {
|
||||
image: url(./Dark/expand.png);
|
||||
}
|
||||
|
||||
SourceTreeSubItemCheckBox::indicator:unchecked,
|
||||
SourceTreeSubItemCheckBox::indicator:unchecked:hover {
|
||||
image: url(./Dark/collapse.png);
|
||||
}
|
||||
|
||||
/*************************/
|
||||
/* --- Progress bars --- */
|
||||
/*************************/
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#include <QCheckBox>
|
||||
|
||||
class ExpandCheckBox : public QCheckBox {
|
||||
Q_OBJECT
|
||||
};
|
|
@ -518,7 +518,7 @@
|
|||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="SourceListWidget" name="sources">
|
||||
<widget class="SourceTree" name="sources">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
|
@ -1682,9 +1682,9 @@
|
|||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>SourceListWidget</class>
|
||||
<extends>QListWidget</extends>
|
||||
<header>source-list-widget.hpp</header>
|
||||
<class>SourceTree</class>
|
||||
<extends>QListView</extends>
|
||||
<header>source-tree.hpp</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
|
@ -17,6 +17,8 @@
|
|||
<file>images/tray_active.png</file>
|
||||
<file>images/locked_mask.png</file>
|
||||
<file>images/unlocked_mask.png</file>
|
||||
<file>images/collapse.png</file>
|
||||
<file>images/expand.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/settings">
|
||||
<file>images/settings/advanced.png</file>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,171 @@
|
|||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QVector>
|
||||
#include <QPointer>
|
||||
#include <QListView>
|
||||
#include <QCheckBox>
|
||||
#include <QAbstractListModel>
|
||||
|
||||
class QLabel;
|
||||
class QCheckBox;
|
||||
class QLineEdit;
|
||||
class SourceTree;
|
||||
class QSpacerItem;
|
||||
class QHBoxLayout;
|
||||
class LockedCheckBox;
|
||||
class VisibilityCheckBox;
|
||||
class VisibilityItemWidget;
|
||||
|
||||
class SourceTreeSubItemCheckBox : public QCheckBox {
|
||||
Q_OBJECT
|
||||
};
|
||||
|
||||
class SourceTreeItem : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
friend class SourceTree;
|
||||
friend class SourceTreeModel;
|
||||
|
||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
|
||||
virtual bool eventFilter(QObject *object, QEvent *event) override;
|
||||
|
||||
void Update(bool force);
|
||||
|
||||
enum class Type {
|
||||
Unknown,
|
||||
Item,
|
||||
Group,
|
||||
SubItem,
|
||||
};
|
||||
|
||||
void DisconnectSignals();
|
||||
void ReconnectSignals();
|
||||
|
||||
Type type = Type::Unknown;
|
||||
|
||||
public:
|
||||
explicit SourceTreeItem(SourceTree *tree, OBSSceneItem sceneitem);
|
||||
|
||||
private:
|
||||
QSpacerItem *spacer = nullptr;
|
||||
QCheckBox *expand = nullptr;
|
||||
VisibilityCheckBox *vis = nullptr;
|
||||
LockedCheckBox *lock = nullptr;
|
||||
QHBoxLayout *boxLayout = nullptr;
|
||||
QLabel *label = nullptr;
|
||||
|
||||
QLineEdit *editor = nullptr;
|
||||
|
||||
SourceTree *tree;
|
||||
OBSSceneItem sceneitem;
|
||||
OBSSignal sceneRemoveSignal;
|
||||
OBSSignal itemRemoveSignal;
|
||||
OBSSignal visibleSignal;
|
||||
OBSSignal renameSignal;
|
||||
OBSSignal removeSignal;
|
||||
|
||||
private slots:
|
||||
void EnterEditMode();
|
||||
void ExitEditMode(bool save);
|
||||
|
||||
void VisibilityChanged(bool visible);
|
||||
void Renamed(const QString &name);
|
||||
|
||||
void ExpandClicked(bool checked);
|
||||
};
|
||||
|
||||
class SourceTreeModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
friend class SourceTree;
|
||||
friend class SourceTreeItem;
|
||||
|
||||
SourceTree *st;
|
||||
QVector<OBSSceneItem> items;
|
||||
bool hasGroups = false;
|
||||
|
||||
static void OBSFrontendEvent(enum obs_frontend_event event, void *ptr);
|
||||
void Clear();
|
||||
void SceneChanged();
|
||||
void ReorderItems();
|
||||
|
||||
void Add(obs_sceneitem_t *item);
|
||||
void Remove(obs_sceneitem_t *item);
|
||||
OBSSceneItem Get(int idx);
|
||||
QString GetNewGroupName();
|
||||
void AddGroup();
|
||||
|
||||
void GroupSelectedItems(QModelIndexList &indices);
|
||||
void UngroupSelectedGroups(QModelIndexList &indices);
|
||||
|
||||
void ExpandGroup(obs_sceneitem_t *item);
|
||||
void CollapseGroup(obs_sceneitem_t *item);
|
||||
|
||||
void UpdateGroupState(bool update);
|
||||
|
||||
public:
|
||||
explicit SourceTreeModel(SourceTree *st);
|
||||
~SourceTreeModel();
|
||||
|
||||
virtual int rowCount(const QModelIndex &parent) const override;
|
||||
virtual QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
virtual Qt::DropActions supportedDropActions() const override;
|
||||
};
|
||||
|
||||
class SourceTree : public QListView {
|
||||
Q_OBJECT
|
||||
|
||||
bool ignoreReorder = false;
|
||||
|
||||
friend class SourceTreeModel;
|
||||
friend class SourceTreeItem;
|
||||
|
||||
void ResetWidgets();
|
||||
void UpdateWidget(const QModelIndex &idx, obs_sceneitem_t *item);
|
||||
void UpdateWidgets(bool force = false);
|
||||
|
||||
inline SourceTreeModel *GetStm() const
|
||||
{
|
||||
return reinterpret_cast<SourceTreeModel *>(model());
|
||||
}
|
||||
|
||||
inline SourceTreeItem *GetItemWidget(int idx)
|
||||
{
|
||||
QWidget *widget = indexWidget(GetStm()->createIndex(idx, 0));
|
||||
return reinterpret_cast<SourceTreeItem *>(widget);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit SourceTree(QWidget *parent = nullptr);
|
||||
|
||||
inline bool IgnoreReorder() const {return ignoreReorder;}
|
||||
inline void ReorderItems() {GetStm()->ReorderItems();}
|
||||
inline void Clear() {GetStm()->Clear();}
|
||||
|
||||
inline void Add(obs_sceneitem_t *item) {GetStm()->Add(item);}
|
||||
inline void Remove(obs_sceneitem_t *item) {GetStm()->Remove(item);}
|
||||
inline OBSSceneItem Get(int idx) {return GetStm()->Get(idx);}
|
||||
inline QString GetNewGroupName() {return GetStm()->GetNewGroupName();}
|
||||
|
||||
void SelectItem(obs_sceneitem_t *sceneitem, bool select);
|
||||
|
||||
bool MultipleBaseSelected() const;
|
||||
bool GroupsSelected() const;
|
||||
bool GroupedItemsSelected() const;
|
||||
|
||||
public slots:
|
||||
void GroupSelectedItems();
|
||||
void UngroupSelectedGroups();
|
||||
void AddGroup();
|
||||
void Edit(int idx);
|
||||
|
||||
protected:
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
virtual void dropEvent(QDropEvent *event) override;
|
||||
|
||||
virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
|
||||
};
|
|
@ -54,6 +54,7 @@
|
|||
#include "display-helpers.hpp"
|
||||
#include "volume-control.hpp"
|
||||
#include "remote-text.hpp"
|
||||
#include "source-tree.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "win-update/win-update.hpp"
|
||||
|
@ -148,8 +149,6 @@ OBSBasic::OBSBasic(QWidget *parent)
|
|||
|
||||
copyActionsDynamicProperties();
|
||||
|
||||
ui->sources->setItemDelegate(new VisibilityItemDelegate(ui->sources));
|
||||
|
||||
char styleSheetPath[512];
|
||||
int ret = GetProfilePath(styleSheetPath, sizeof(styleSheetPath),
|
||||
"stylesheet.qss");
|
||||
|
@ -201,13 +200,6 @@ OBSBasic::OBSBasic(QWidget *parent)
|
|||
SLOT(SceneNameEdited(QWidget*,
|
||||
QAbstractItemDelegate::EndEditHint)));
|
||||
|
||||
connect(ui->sources->itemDelegate(),
|
||||
SIGNAL(closeEditor(QWidget*,
|
||||
QAbstractItemDelegate::EndEditHint)),
|
||||
this,
|
||||
SLOT(SceneItemNameEdited(QWidget*,
|
||||
QAbstractItemDelegate::EndEditHint)));
|
||||
|
||||
cpuUsageInfo = os_cpu_usage_info_start();
|
||||
cpuUsageTimer = new QTimer(this);
|
||||
connect(cpuUsageTimer, SIGNAL(timeout()),
|
||||
|
@ -2024,7 +2016,7 @@ OBSSceneItem OBSBasic::GetSceneItem(QListWidgetItem *item)
|
|||
|
||||
OBSSceneItem OBSBasic::GetCurrentSceneItem()
|
||||
{
|
||||
return GetSceneItem(GetTopSelectedSourceItem());
|
||||
return ui->sources->Get(GetTopSelectedSourceItem());
|
||||
}
|
||||
|
||||
void OBSBasic::UpdatePreviewScalingMenu()
|
||||
|
@ -2047,32 +2039,6 @@ void OBSBasic::UpdatePreviewScalingMenu()
|
|||
scalingAmount == float(ovi.output_width) / float(ovi.base_width));
|
||||
}
|
||||
|
||||
void OBSBasic::UpdateSources(OBSScene scene)
|
||||
{
|
||||
ClearListItems(ui->sources);
|
||||
|
||||
obs_scene_enum_items(scene,
|
||||
[] (obs_scene_t *scene, obs_sceneitem_t *item, void *p)
|
||||
{
|
||||
OBSBasic *window = static_cast<OBSBasic*>(p);
|
||||
window->InsertSceneItem(item);
|
||||
|
||||
UNUSED_PARAMETER(scene);
|
||||
return true;
|
||||
}, this);
|
||||
}
|
||||
|
||||
void OBSBasic::InsertSceneItem(obs_sceneitem_t *item)
|
||||
{
|
||||
QListWidgetItem *listItem = new QListWidgetItem();
|
||||
SetOBSRef(listItem, OBSSceneItem(item));
|
||||
|
||||
ui->sources->insertItem(0, listItem);
|
||||
ui->sources->setCurrentRow(0, QItemSelectionModel::ClearAndSelect);
|
||||
|
||||
SetupVisibilityItem(ui->sources, listItem, item);
|
||||
}
|
||||
|
||||
void OBSBasic::CreateInteractionWindow(obs_source_t *source)
|
||||
{
|
||||
if (interaction)
|
||||
|
@ -2199,7 +2165,7 @@ void OBSBasic::RemoveScene(OBSSource source)
|
|||
|
||||
if (sel != nullptr) {
|
||||
if (sel == ui->scenes->currentItem())
|
||||
ClearListItems(ui->sources);
|
||||
ui->sources->Clear();
|
||||
delete sel;
|
||||
}
|
||||
|
||||
|
@ -2221,7 +2187,7 @@ void OBSBasic::AddSceneItem(OBSSceneItem item)
|
|||
obs_scene_t *scene = obs_sceneitem_get_scene(item);
|
||||
|
||||
if (GetCurrentScene() == scene)
|
||||
InsertSceneItem(item);
|
||||
ui->sources->Add(item);
|
||||
|
||||
SaveProject();
|
||||
|
||||
|
@ -2237,14 +2203,7 @@ void OBSBasic::AddSceneItem(OBSSceneItem item)
|
|||
|
||||
void OBSBasic::RemoveSceneItem(OBSSceneItem item)
|
||||
{
|
||||
for (int i = 0; i < ui->sources->count(); i++) {
|
||||
QListWidgetItem *listItem = ui->sources->item(i);
|
||||
|
||||
if (GetOBSRef<OBSSceneItem>(listItem) == item) {
|
||||
DeleteListItem(ui->sources, listItem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ui->sources->Remove(item);
|
||||
|
||||
SaveProject();
|
||||
|
||||
|
@ -2276,8 +2235,6 @@ void OBSBasic::UpdateSceneSelection(OBSSource source)
|
|||
ui->scenes->setCurrentItem(items.first());
|
||||
sceneChanging = false;
|
||||
|
||||
UpdateSources(scene);
|
||||
|
||||
OBSScene curScene =
|
||||
GetOBSRef<OBSScene>(ui->scenes->currentItem());
|
||||
if (api && scene != curScene)
|
||||
|
@ -2322,19 +2279,7 @@ void OBSBasic::SelectSceneItem(OBSScene scene, OBSSceneItem item, bool select)
|
|||
if (scene != GetCurrentScene() || ignoreSelectionUpdate)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < ui->sources->count(); i++) {
|
||||
QListWidgetItem *witem = ui->sources->item(i);
|
||||
QVariant data =
|
||||
witem->data(static_cast<int>(QtDataRole::OBSRef));
|
||||
if (!data.canConvert<OBSSceneItem>())
|
||||
continue;
|
||||
|
||||
if (item != data.value<OBSSceneItem>())
|
||||
continue;
|
||||
|
||||
witem->setSelected(select);
|
||||
break;
|
||||
}
|
||||
ui->sources->SelectItem(item, select);
|
||||
}
|
||||
|
||||
static inline bool SourceMixerHidden(obs_source_t *source)
|
||||
|
@ -2664,7 +2609,8 @@ void OBSBasic::DeactivateAudioSource(OBSSource source)
|
|||
|
||||
bool OBSBasic::QueryRemoveSource(obs_source_t *source)
|
||||
{
|
||||
if (obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE) {
|
||||
if (obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE &&
|
||||
!obs_sceneitem_group_from_source(source)) {
|
||||
int count = ui->scenes->count();
|
||||
|
||||
if (count == 1) {
|
||||
|
@ -2830,62 +2776,12 @@ void OBSBasic::RemoveSelectedSceneItem()
|
|||
}
|
||||
}
|
||||
|
||||
struct ReorderInfo {
|
||||
int idx = 0;
|
||||
OBSBasic *window;
|
||||
|
||||
inline ReorderInfo(OBSBasic *window_) : window(window_) {}
|
||||
};
|
||||
|
||||
void OBSBasic::ReorderSceneItem(obs_sceneitem_t *item, size_t idx)
|
||||
{
|
||||
int count = ui->sources->count();
|
||||
int idx_inv = count - (int)idx - 1;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
QListWidgetItem *listItem = ui->sources->item(i);
|
||||
OBSSceneItem sceneItem = GetOBSRef<OBSSceneItem>(listItem);
|
||||
|
||||
if (sceneItem == item) {
|
||||
if ((int)idx_inv != i) {
|
||||
bool sel = (ui->sources->currentRow() == i);
|
||||
|
||||
listItem = TakeListItem(ui->sources, i);
|
||||
if (listItem) {
|
||||
ui->sources->insertItem(idx_inv,
|
||||
listItem);
|
||||
SetupVisibilityItem(ui->sources,
|
||||
listItem, item);
|
||||
|
||||
if (sel)
|
||||
ui->sources->setCurrentRow(
|
||||
idx_inv);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasic::ReorderSources(OBSScene scene)
|
||||
{
|
||||
ReorderInfo info(this);
|
||||
|
||||
if (scene != GetCurrentScene() || ui->sources->IgnoreReorder())
|
||||
return;
|
||||
|
||||
obs_scene_enum_items(scene,
|
||||
[] (obs_scene_t*, obs_sceneitem_t *item, void *p)
|
||||
{
|
||||
ReorderInfo *info =
|
||||
reinterpret_cast<ReorderInfo*>(p);
|
||||
|
||||
info->window->ReorderSceneItem(item,
|
||||
info->idx++);
|
||||
return true;
|
||||
}, &info);
|
||||
|
||||
ui->sources->ReorderItems();
|
||||
SaveProject();
|
||||
}
|
||||
|
||||
|
@ -3410,7 +3306,7 @@ void OBSBasic::ClearSceneData()
|
|||
|
||||
ClearVolumeControls();
|
||||
ClearListItems(ui->scenes);
|
||||
ClearListItems(ui->sources);
|
||||
ui->sources->Clear();
|
||||
ClearQuickTransitions();
|
||||
ui->transitions->clear();
|
||||
|
||||
|
@ -3800,44 +3696,10 @@ void OBSBasic::MoveSceneToBottom()
|
|||
ui->scenes->count() - 1);
|
||||
}
|
||||
|
||||
void OBSBasic::on_sources_itemSelectionChanged()
|
||||
{
|
||||
SignalBlocker sourcesSignalBlocker(ui->sources);
|
||||
|
||||
auto updateItemSelection = [&]()
|
||||
{
|
||||
ignoreSelectionUpdate = true;
|
||||
for (int i = 0; i < ui->sources->count(); i++)
|
||||
{
|
||||
QListWidgetItem *wItem = ui->sources->item(i);
|
||||
OBSSceneItem item = GetOBSRef<OBSSceneItem>(wItem);
|
||||
|
||||
obs_sceneitem_select(item, wItem->isSelected());
|
||||
}
|
||||
ignoreSelectionUpdate = false;
|
||||
};
|
||||
using updateItemSelection_t = decltype(updateItemSelection);
|
||||
|
||||
obs_scene_atomic_update(GetCurrentScene(),
|
||||
[](void *data, obs_scene_t *)
|
||||
{
|
||||
(*static_cast<updateItemSelection_t*>(data))();
|
||||
}, static_cast<void*>(&updateItemSelection));
|
||||
}
|
||||
|
||||
void OBSBasic::EditSceneItemName()
|
||||
{
|
||||
QListWidgetItem *item = GetTopSelectedSourceItem();
|
||||
Qt::ItemFlags flags = item->flags();
|
||||
OBSSceneItem sceneItem= GetOBSRef<OBSSceneItem>(item);
|
||||
obs_source_t *source = obs_sceneitem_get_source(sceneItem);
|
||||
const char *name = obs_source_get_name(source);
|
||||
|
||||
item->setText(QT_UTF8(name));
|
||||
item->setFlags(flags | Qt::ItemIsEditable);
|
||||
ui->sources->removeItemWidget(item);
|
||||
ui->sources->editItem(item);
|
||||
item->setFlags(flags);
|
||||
int idx = GetTopSelectedSourceItem();
|
||||
ui->sources->Edit(idx);
|
||||
}
|
||||
|
||||
void OBSBasic::SetDeinterlacingMode()
|
||||
|
@ -3937,7 +3799,7 @@ QMenu *OBSBasic::AddScaleFilteringMenu(obs_sceneitem_t *item)
|
|||
return menu;
|
||||
}
|
||||
|
||||
void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
|
||||
void OBSBasic::CreateSourcePopupMenu(int idx, bool preview)
|
||||
{
|
||||
QMenu popup(this);
|
||||
QPointer<QMenu> previewProjector;
|
||||
|
@ -3978,6 +3840,17 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
|
|||
ui->actionCopyFilters->setEnabled(false);
|
||||
ui->actionCopySource->setEnabled(false);
|
||||
|
||||
if (ui->sources->MultipleBaseSelected()) {
|
||||
popup.addSeparator();
|
||||
popup.addAction(QTStr("Basic.Main.GroupItems"),
|
||||
ui->sources, SLOT(GroupSelectedItems()));
|
||||
|
||||
} else if (ui->sources->GroupsSelected()) {
|
||||
popup.addSeparator();
|
||||
popup.addAction(QTStr("Basic.Main.Ungroup"),
|
||||
ui->sources, SLOT(UngroupSelectedGroups()));
|
||||
}
|
||||
|
||||
popup.addSeparator();
|
||||
popup.addAction(ui->actionCopySource);
|
||||
popup.addAction(ui->actionPasteRef);
|
||||
|
@ -3989,11 +3862,11 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
|
|||
popup.addAction(ui->actionPasteFilters);
|
||||
popup.addSeparator();
|
||||
|
||||
if (item) {
|
||||
if (idx != -1) {
|
||||
if (addSourceMenu)
|
||||
popup.addSeparator();
|
||||
|
||||
OBSSceneItem sceneItem = GetSceneItem(item);
|
||||
OBSSceneItem sceneItem = ui->sources->Get(idx);
|
||||
obs_source_t *source = obs_sceneitem_get_source(sceneItem);
|
||||
uint32_t flags = obs_source_get_output_flags(source);
|
||||
bool isAsyncVideo = (flags & OBS_SOURCE_ASYNC_VIDEO) ==
|
||||
|
@ -4064,20 +3937,10 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
|
|||
|
||||
void OBSBasic::on_sources_customContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
if (ui->scenes->count())
|
||||
CreateSourcePopupMenu(ui->sources->itemAt(pos), false);
|
||||
}
|
||||
|
||||
void OBSBasic::on_sources_itemDoubleClicked(QListWidgetItem *witem)
|
||||
{
|
||||
if (!witem)
|
||||
return;
|
||||
|
||||
OBSSceneItem item = GetSceneItem(witem);
|
||||
OBSSource source = obs_sceneitem_get_source(item);
|
||||
|
||||
if (source)
|
||||
CreatePropertiesWindow(source);
|
||||
if (ui->scenes->count()) {
|
||||
QModelIndex idx = ui->sources->indexAt(pos);
|
||||
CreateSourcePopupMenu(idx.row(), false);
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasic::on_scenes_itemDoubleClicked(QListWidgetItem *witem)
|
||||
|
@ -4161,6 +4024,12 @@ QMenu *OBSBasic::CreateAddSourcePopupMenu()
|
|||
|
||||
addSource(popup, "scene", Str("Basic.Scene"));
|
||||
|
||||
popup->addSeparator();
|
||||
QAction *addGroup = new QAction(QTStr("Group"), this);
|
||||
connect(addGroup, SIGNAL(triggered(bool)),
|
||||
ui->sources, SLOT(AddGroup()));
|
||||
popup->addAction(addGroup);
|
||||
|
||||
if (!foundDeprecated) {
|
||||
delete deprecated;
|
||||
deprecated = nullptr;
|
||||
|
@ -4171,6 +4040,7 @@ QMenu *OBSBasic::CreateAddSourcePopupMenu()
|
|||
popup = nullptr;
|
||||
|
||||
} else if (foundDeprecated) {
|
||||
popup->addSeparator();
|
||||
popup->addMenu(deprecated);
|
||||
}
|
||||
|
||||
|
@ -4206,20 +4076,24 @@ void OBSBasic::on_actionAddSource_triggered()
|
|||
AddSourcePopupMenu(QCursor::pos());
|
||||
}
|
||||
|
||||
static bool remove_items(obs_scene_t *, obs_sceneitem_t *item, void *param)
|
||||
{
|
||||
vector<OBSSceneItem> &items =
|
||||
*reinterpret_cast<vector<OBSSceneItem>*>(param);
|
||||
|
||||
if (obs_sceneitem_selected(item)) {
|
||||
items.emplace_back(item);
|
||||
} else if (obs_sceneitem_is_group(item)) {
|
||||
obs_sceneitem_group_enum_items(item, remove_items, &items);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
void OBSBasic::on_actionRemoveSource_triggered()
|
||||
{
|
||||
vector<OBSSceneItem> items;
|
||||
|
||||
auto func = [] (obs_scene_t *, obs_sceneitem_t *item, void *param)
|
||||
{
|
||||
vector<OBSSceneItem> &items =
|
||||
*reinterpret_cast<vector<OBSSceneItem>*>(param);
|
||||
if (obs_sceneitem_selected(item))
|
||||
items.emplace_back(item);
|
||||
return true;
|
||||
};
|
||||
|
||||
obs_scene_enum_items(GetCurrentScene(), func, &items);
|
||||
obs_scene_enum_items(GetCurrentScene(), remove_items, &items);
|
||||
|
||||
if (!items.size())
|
||||
return;
|
||||
|
@ -4484,26 +4358,6 @@ void OBSBasic::SceneNameEdited(QWidget *editor,
|
|||
UNUSED_PARAMETER(endHint);
|
||||
}
|
||||
|
||||
void OBSBasic::SceneItemNameEdited(QWidget *editor,
|
||||
QAbstractItemDelegate::EndEditHint endHint)
|
||||
{
|
||||
OBSSceneItem item = GetCurrentSceneItem();
|
||||
QLineEdit *edit = qobject_cast<QLineEdit*>(editor);
|
||||
string text = QT_TO_UTF8(edit->text().trimmed());
|
||||
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
obs_source_t *source = obs_sceneitem_get_source(item);
|
||||
RenameListItem(this, ui->sources, source, text);
|
||||
|
||||
QListWidgetItem *listItem = ui->sources->currentItem();
|
||||
listItem->setText(QString());
|
||||
SetupVisibilityItem(ui->sources, listItem, item);
|
||||
|
||||
UNUSED_PARAMETER(endHint);
|
||||
}
|
||||
|
||||
void OBSBasic::OpenFilters()
|
||||
{
|
||||
OBSSceneItem item = GetCurrentSceneItem();
|
||||
|
@ -5150,13 +5004,11 @@ void OBSBasic::on_actionShowProfileFolder_triggered()
|
|||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
}
|
||||
|
||||
QListWidgetItem *OBSBasic::GetTopSelectedSourceItem()
|
||||
int OBSBasic::GetTopSelectedSourceItem()
|
||||
{
|
||||
QList<QListWidgetItem*> selectedItems = ui->sources->selectedItems();
|
||||
QListWidgetItem *topItem = nullptr;
|
||||
if (selectedItems.size() != 0)
|
||||
topItem = selectedItems[0];
|
||||
return topItem;
|
||||
QModelIndexList selectedItems =
|
||||
ui->sources->selectionModel()->selectedIndexes();
|
||||
return selectedItems.count() ? selectedItems[0].row() : -1;
|
||||
}
|
||||
|
||||
void OBSBasic::on_preview_customContextMenuRequested(const QPoint &pos)
|
||||
|
@ -5613,6 +5465,22 @@ static bool nudge_callback(obs_scene_t*, obs_sceneitem_t *item, void *param)
|
|||
struct vec2 pos;
|
||||
|
||||
if (!obs_sceneitem_selected(item)) {
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
struct vec3 offset3;
|
||||
vec3_set(&offset3, offset.x, offset.y, 0.0f);
|
||||
|
||||
struct matrix4 matrix;
|
||||
obs_sceneitem_get_draw_transform(item, &matrix);
|
||||
vec4_set(&matrix.t, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||
matrix4_inv(&matrix, &matrix);
|
||||
vec3_transform(&offset3, &offset3, &matrix);
|
||||
|
||||
struct vec2 new_offset;
|
||||
vec2_set(&new_offset, offset3.x, offset3.y);
|
||||
obs_sceneitem_group_enum_items(item, nudge_callback,
|
||||
&new_offset);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6188,12 +6056,13 @@ bool OBSBasic::sysTrayMinimizeToTray()
|
|||
|
||||
void OBSBasic::on_actionCopySource_triggered()
|
||||
{
|
||||
on_actionCopyTransform_triggered();
|
||||
|
||||
OBSSceneItem item = GetCurrentSceneItem();
|
||||
|
||||
if (!item)
|
||||
return;
|
||||
if (!!obs_sceneitem_is_group(item))
|
||||
return;
|
||||
|
||||
on_actionCopyTransform_triggered();
|
||||
|
||||
OBSSource source = obs_sceneitem_get_source(item);
|
||||
|
||||
|
|
|
@ -232,9 +232,6 @@ private:
|
|||
|
||||
void UpdatePreviewScalingMenu();
|
||||
|
||||
void UpdateSources(OBSScene scene);
|
||||
void InsertSceneItem(obs_sceneitem_t *item);
|
||||
|
||||
void LoadSceneListOrder(obs_data_array_t *array);
|
||||
obs_data_array_t *SaveSceneListOrder();
|
||||
void ChangeSceneIndex(bool relative, int idx, int invalidIdx);
|
||||
|
@ -243,10 +240,6 @@ private:
|
|||
void TempStreamOutput(const char *url, const char *key,
|
||||
int vBitrate, int aBitrate);
|
||||
|
||||
void CreateInteractionWindow(obs_source_t *source);
|
||||
void CreatePropertiesWindow(obs_source_t *source);
|
||||
void CreateFiltersWindow(obs_source_t *source);
|
||||
|
||||
void CloseDialogs();
|
||||
void ClearSceneData();
|
||||
|
||||
|
@ -275,7 +268,7 @@ private:
|
|||
|
||||
void SaveProjectNow();
|
||||
|
||||
QListWidgetItem *GetTopSelectedSourceItem();
|
||||
int GetTopSelectedSourceItem();
|
||||
|
||||
obs_hotkey_pair_id streamingHotkeys, recordingHotkeys,
|
||||
replayBufHotkeys;
|
||||
|
@ -550,11 +543,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void ReorderSceneItem(obs_sceneitem_t *item, size_t idx);
|
||||
|
||||
QMenu *AddDeinterlacingMenu(obs_source_t *source);
|
||||
QMenu *AddScaleFilteringMenu(obs_sceneitem_t *item);
|
||||
void CreateSourcePopupMenu(QListWidgetItem *item, bool preview);
|
||||
void CreateSourcePopupMenu(int idx, bool preview);
|
||||
|
||||
void UpdateTitleBar();
|
||||
void UpdateSceneSelection(OBSSource source);
|
||||
|
@ -564,6 +555,10 @@ public:
|
|||
|
||||
void OpenSavedProjectors();
|
||||
|
||||
void CreateInteractionWindow(obs_source_t *source);
|
||||
void CreatePropertiesWindow(obs_source_t *source);
|
||||
void CreateFiltersWindow(obs_source_t *source);
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent *event) override;
|
||||
virtual void changeEvent(QEvent *event) override;
|
||||
|
@ -605,9 +600,7 @@ private slots:
|
|||
void on_actionRemoveScene_triggered();
|
||||
void on_actionSceneUp_triggered();
|
||||
void on_actionSceneDown_triggered();
|
||||
void on_sources_itemSelectionChanged();
|
||||
void on_sources_customContextMenuRequested(const QPoint &pos);
|
||||
void on_sources_itemDoubleClicked(QListWidgetItem *item);
|
||||
void on_scenes_itemDoubleClicked(QListWidgetItem *item);
|
||||
void on_actionAddSource_triggered();
|
||||
void on_actionRemoveSource_triggered();
|
||||
|
@ -689,8 +682,6 @@ private slots:
|
|||
|
||||
void SceneNameEdited(QWidget *editor,
|
||||
QAbstractItemDelegate::EndEditHint endHint);
|
||||
void SceneItemNameEdited(QWidget *editor,
|
||||
QAbstractItemDelegate::EndEditHint endHint);
|
||||
|
||||
void OpenSceneFilters();
|
||||
void OpenFilters();
|
||||
|
|
|
@ -39,6 +39,8 @@ struct SceneFindData {
|
|||
OBSSceneItem item;
|
||||
bool selectBelow;
|
||||
|
||||
obs_sceneitem_t *group = nullptr;
|
||||
|
||||
SceneFindData(const SceneFindData &) = delete;
|
||||
SceneFindData(SceneFindData &&) = delete;
|
||||
SceneFindData& operator=(const SceneFindData &) = delete;
|
||||
|
@ -214,11 +216,26 @@ static bool CheckItemSelected(obs_scene_t *scene, obs_sceneitem_t *item,
|
|||
|
||||
if (!SceneItemHasVideo(item))
|
||||
return true;
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
data->group = item;
|
||||
obs_sceneitem_group_enum_items(item, CheckItemSelected, param);
|
||||
data->group = nullptr;
|
||||
|
||||
if (data->item) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
|
||||
|
||||
obs_sceneitem_get_box_transform(item, &transform);
|
||||
|
||||
if (data->group) {
|
||||
matrix4 parent_transform;
|
||||
obs_sceneitem_get_draw_transform(data->group, &parent_transform);
|
||||
matrix4_mul(&transform, &transform, &parent_transform);
|
||||
}
|
||||
|
||||
matrix4_inv(&transform, &transform);
|
||||
vec3_transform(&transformedPos, &pos3, &transform);
|
||||
|
||||
|
@ -268,10 +285,35 @@ struct HandleFindData {
|
|||
static bool FindHandleAtPos(obs_scene_t *scene, obs_sceneitem_t *item,
|
||||
void *param)
|
||||
{
|
||||
if (!obs_sceneitem_selected(item))
|
||||
return true;
|
||||
|
||||
HandleFindData *data = reinterpret_cast<HandleFindData*>(param);
|
||||
|
||||
if (!obs_sceneitem_selected(item)) {
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
matrix4 transform;
|
||||
vec3 new_pos3;
|
||||
vec3_set(&new_pos3, data->pos.x, data->pos.y, 0.0f);
|
||||
vec3_divf(&new_pos3, &new_pos3, data->scale);
|
||||
|
||||
obs_sceneitem_get_draw_transform(item, &transform);
|
||||
matrix4_inv(&transform, &transform);
|
||||
vec3_transform(&new_pos3, &new_pos3, &transform);
|
||||
|
||||
vec2 new_pos;
|
||||
vec2_set(&new_pos, new_pos3.x, new_pos3.y);
|
||||
HandleFindData findData(new_pos, 1.0f);
|
||||
findData.item = data->item;
|
||||
findData.handle = data->handle;
|
||||
|
||||
obs_sceneitem_group_enum_items(item, FindHandleAtPos,
|
||||
&findData);
|
||||
|
||||
data->item = findData.item;
|
||||
data->handle = findData.handle;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
matrix4 transform;
|
||||
vec3 pos3;
|
||||
float closestHandle = HANDLE_SEL_RADIUS;
|
||||
|
@ -377,6 +419,15 @@ void OBSBasicPreview::GetStretchHandleData(const vec2 &pos)
|
|||
startCrop.left - startCrop.right);
|
||||
cropSize.y = float(obs_source_get_height(source) -
|
||||
startCrop.top - startCrop.bottom);
|
||||
|
||||
stretchGroup = obs_sceneitem_get_group(stretchItem);
|
||||
if (stretchGroup) {
|
||||
obs_sceneitem_get_draw_transform(stretchGroup,
|
||||
&invGroupTransform);
|
||||
matrix4_inv(&invGroupTransform,
|
||||
&invGroupTransform);
|
||||
obs_sceneitem_defer_group_resize_begin(stretchGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -482,6 +533,9 @@ static bool select_one(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
|
|||
{
|
||||
obs_sceneitem_t *selectedItem =
|
||||
reinterpret_cast<obs_sceneitem_t*>(param);
|
||||
if (obs_sceneitem_is_group(item))
|
||||
obs_sceneitem_group_enum_items(item, select_one, param);
|
||||
|
||||
obs_sceneitem_select(item, (selectedItem == item));
|
||||
|
||||
UNUSED_PARAMETER(scene);
|
||||
|
@ -534,7 +588,12 @@ void OBSBasicPreview::mouseReleaseEvent(QMouseEvent *event)
|
|||
if (!mouseMoved)
|
||||
ProcessClick(pos);
|
||||
|
||||
if (stretchGroup) {
|
||||
obs_sceneitem_defer_group_resize_end(stretchGroup);
|
||||
}
|
||||
|
||||
stretchItem = nullptr;
|
||||
stretchGroup = nullptr;
|
||||
mouseDown = false;
|
||||
mouseMoved = false;
|
||||
cropping = false;
|
||||
|
@ -692,9 +751,22 @@ static bool move_items(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
|
|||
if (obs_sceneitem_locked(item))
|
||||
return true;
|
||||
|
||||
bool selected = obs_sceneitem_selected(item);
|
||||
vec2 *offset = reinterpret_cast<vec2*>(param);
|
||||
|
||||
if (obs_sceneitem_selected(item)) {
|
||||
if (obs_sceneitem_is_group(item) && !selected) {
|
||||
matrix4 transform;
|
||||
vec3 new_offset;
|
||||
vec3_set(&new_offset, offset->x, offset->y, 0.0f);
|
||||
|
||||
obs_sceneitem_get_draw_transform(item, &transform);
|
||||
vec4_set(&transform.t, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||
matrix4_inv(&transform, &transform);
|
||||
vec3_transform(&new_offset, &new_offset, &transform);
|
||||
obs_sceneitem_group_enum_items(item, move_items, &new_offset);
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
vec2 pos;
|
||||
obs_sceneitem_get_pos(item, &pos);
|
||||
vec2_add(&pos, &pos, offset);
|
||||
|
@ -1063,6 +1135,17 @@ void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event)
|
|||
pos.y = std::round(pos.y);
|
||||
|
||||
if (stretchHandle != ItemHandle::None) {
|
||||
obs_sceneitem_t *group = obs_sceneitem_get_group(
|
||||
stretchItem);
|
||||
if (group) {
|
||||
vec3 group_pos;
|
||||
vec3_set(&group_pos, pos.x, pos.y, 0.0f);
|
||||
vec3_transform(&group_pos, &group_pos,
|
||||
&invGroupTransform);
|
||||
pos.x = group_pos.x;
|
||||
pos.y = group_pos.y;
|
||||
}
|
||||
|
||||
if (cropping)
|
||||
CropItem(pos);
|
||||
else
|
||||
|
@ -1110,6 +1193,16 @@ bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene,
|
|||
if (!SceneItemHasVideo(item))
|
||||
return true;
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
matrix4 mat;
|
||||
obs_sceneitem_get_draw_transform(item, &mat);
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_mul(&mat);
|
||||
obs_sceneitem_group_enum_items(item, DrawSelectedItem, param);
|
||||
gs_matrix_pop();
|
||||
}
|
||||
|
||||
if (!obs_sceneitem_selected(item))
|
||||
return true;
|
||||
|
||||
|
|
|
@ -35,11 +35,13 @@ private:
|
|||
obs_sceneitem_crop startCrop;
|
||||
vec2 startItemPos;
|
||||
vec2 cropSize;
|
||||
OBSSceneItem stretchGroup;
|
||||
OBSSceneItem stretchItem;
|
||||
ItemHandle stretchHandle = ItemHandle::None;
|
||||
vec2 stretchItemSize;
|
||||
matrix4 screenToItem;
|
||||
matrix4 itemToScreen;
|
||||
matrix4 invGroupTransform;
|
||||
|
||||
vec2 startPos;
|
||||
vec2 lastMoveOffset;
|
||||
|
|
Loading…
Reference in New Issue