mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-09-24 23:31:36 +08:00
UI: Add grouping
This commit is contained in:
+75
-206
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user