Преглед изворни кода

frontend: Split Qt UI component into single file per C++ class

PatTheMav пре 10 месеци
родитељ
комит
f4fe30a5b3
40 измењених фајлова са 109 додато и 15511 уклоњено
  1. 2 687
      frontend/components/ApplicationAudioCaptureToolbar.cpp
  2. 1 168
      frontend/components/ApplicationAudioCaptureToolbar.hpp
  3. 2 676
      frontend/components/AudioCaptureToolbar.cpp
  4. 1 168
      frontend/components/AudioCaptureToolbar.hpp
  5. 2 686
      frontend/components/BrowserToolbar.cpp
  6. 1 161
      frontend/components/BrowserToolbar.hpp
  7. 5 629
      frontend/components/ColorSourceToolbar.cpp
  8. 1 158
      frontend/components/ColorSourceToolbar.hpp
  9. 5 606
      frontend/components/ComboSelectToolbar.cpp
  10. 1 157
      frontend/components/ComboSelectToolbar.hpp
  11. 2 651
      frontend/components/DeviceCaptureToolbar.cpp
  12. 1 154
      frontend/components/DeviceCaptureToolbar.hpp
  13. 2 669
      frontend/components/DisplayCaptureToolbar.cpp
  14. 1 168
      frontend/components/DisplayCaptureToolbar.hpp
  15. 4 620
      frontend/components/GameCaptureToolbar.cpp
  16. 1 158
      frontend/components/GameCaptureToolbar.hpp
  17. 3 657
      frontend/components/ImageSourceToolbar.cpp
  18. 1 161
      frontend/components/ImageSourceToolbar.hpp
  19. 3 14
      frontend/components/OBSPreviewScalingComboBox.cpp
  20. 0 15
      frontend/components/OBSPreviewScalingComboBox.hpp
  21. 2 104
      frontend/components/OBSPreviewScalingLabel.cpp
  22. 0 45
      frontend/components/OBSPreviewScalingLabel.hpp
  23. 3 650
      frontend/components/SourceToolbar.cpp
  24. 1 150
      frontend/components/SourceToolbar.hpp
  25. 7 982
      frontend/components/SourceTree.cpp
  26. 3 130
      frontend/components/SourceTree.hpp
  27. 3 1600
      frontend/components/SourceTreeDelegate.cpp
  28. 4 191
      frontend/components/SourceTreeDelegate.hpp
  29. 7 1076
      frontend/components/SourceTreeItem.cpp
  30. 6 128
      frontend/components/SourceTreeItem.hpp
  31. 4 1183
      frontend/components/SourceTreeModel.cpp
  32. 3 158
      frontend/components/SourceTreeModel.hpp
  33. 8 564
      frontend/components/TextSourceToolbar.cpp
  34. 1 157
      frontend/components/TextSourceToolbar.hpp
  35. 4 71
      frontend/components/VisibilityItemDelegate.cpp
  36. 1 34
      frontend/components/VisibilityItemDelegate.hpp
  37. 6 70
      frontend/components/VisibilityItemWidget.cpp
  38. 4 19
      frontend/components/VisibilityItemWidget.hpp
  39. 2 668
      frontend/components/WindowCaptureToolbar.cpp
  40. 1 168
      frontend/components/WindowCaptureToolbar.hpp

+ 2 - 687
frontend/components/ApplicationAudioCaptureToolbar.cpp

@@ -1,255 +1,6 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
-
-#include "ui_browser-source-toolbar.h"
+#include "ApplicationAudioCaptureToolbar.hpp"
 #include "ui_device-select-toolbar.h"
-#include "ui_game-capture-toolbar.h"
-#include "ui_image-source-toolbar.h"
-#include "ui_color-source-toolbar.h"
-#include "ui_text-source-toolbar.h"
-
-#ifdef _WIN32
-#define get_os_module(win, mac, linux) obs_get_module(win)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)
-#elif __APPLE__
-#define get_os_module(win, mac, linux) obs_get_module(mac)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)
-#else
-#define get_os_module(win, mac, linux) obs_get_module(linux)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
-#endif
-
-/* ========================================================================= */
-
-SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  props(obs_source_properties(source), obs_properties_destroy)
-{
-}
-
-void SourceToolbar::SaveOldProperties(obs_source_t *source)
-{
-	oldData = obs_data_create();
-
-	OBSDataAutoRelease oldSettings = obs_source_get_settings(source);
-	obs_data_apply(oldData, oldSettings);
-	obs_data_set_string(oldData, "undo_suuid", obs_source_get_uuid(source));
-}
-
-void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
-{
-	if (!oldData) {
-		blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__);
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-
-	OBSSource currentSceneSource = main->GetCurrentSceneSource();
-	if (!currentSceneSource)
-		return;
-	std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
-	auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
-		OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
-		obs_source_reset_settings(source, settings);
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-
-		main->UpdateContextBar();
-	};
-
-	OBSDataAutoRelease new_settings = obs_data_create();
-	OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
-	obs_data_apply(new_settings, curr_settings);
-	obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
-
-	std::string undo_data(obs_data_get_json(oldData));
-	std::string redo_data(obs_data_get_json(new_settings));
-
-	if (undo_data.compare(redo_data) != 0)
-		main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
-					undo_data, redo_data, repeatable);
-
-	oldData = nullptr;
-}
-
-/* ========================================================================= */
-
-BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_BrowserSourceToolbar)
-{
-	ui->setupUi(this);
-}
-
-BrowserToolbar::~BrowserToolbar() {}
-
-void BrowserToolbar::on_refresh_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
-	obs_property_button_clicked(p, source.Get());
-}
-
-/* ========================================================================= */
-
-ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-}
-
-ComboSelectToolbar::~ComboSelectToolbar() {}
-
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
-{
-	size_t count = obs_property_list_item_count(p);
-	int cur_idx = -1;
-
-	for (size_t i = 0; i < count; i++) {
-		const char *name = obs_property_list_item_name(p, i);
-		std::string id;
-
-		if (is_int) {
-			id = std::to_string(obs_property_list_item_int(p, i));
-		} else {
-			const char *val = obs_property_list_item_string(p, i);
-			id = val ? val : "";
-		}
-
-		if (cur_id == id)
-			cur_idx = (int)i;
-
-		c->addItem(name, id.c_str());
-	}
-
-	return cur_idx;
-}
-
-void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
-					const char *prop_name, bool is_int)
-{
-	std::string cur_id;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (is_int) {
-		cur_id = std::to_string(obs_data_get_int(settings, prop_name));
-	} else {
-		cur_id = obs_data_get_string(settings, prop_name);
-	}
-
-	combo->blockSignals(true);
-
-	obs_property_t *p = obs_properties_get(props, prop_name);
-	int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);
-
-	if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
-		if (cur_idx == -1) {
-			combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
-			cur_idx = 0;
-		}
-
-		SetComboItemEnabled(combo, cur_idx, false);
-	}
-
-	combo->setCurrentIndex(cur_idx);
-	combo->blockSignals(false);
-}
-
-void ComboSelectToolbar::Init()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
-}
-
-void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
-{
-	QString id = combo->itemData(idx).toString();
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (is_int) {
-		obs_data_set_int(settings, prop_name, id.toInt());
-	} else {
-		obs_data_set_string(settings, prop_name, QT_TO_UTF8(id));
-	}
-	obs_source_update(source, settings);
-}
-
-void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	SaveOldProperties(source);
-	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
-	SetUndoProperties(source);
-}
-
-AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void AudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "device_id";
-
-	ComboSelectToolbar::Init();
-}
-
-WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void WindowCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
-	ui->deviceLabel->setText(device_str);
-
-#if !defined(_WIN32) && !defined(__APPLE__) //linux
-	prop_name = "capture_window";
-#else
-	prop_name = "window";
-#endif
-
-#ifdef __APPLE__
-	is_int = true;
-#endif
-
-	ComboSelectToolbar::Init();
-}
+#include "moc_ApplicationAudioCaptureToolbar.cpp"
 
 ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
 	: ComboSelectToolbar(parent, source)
@@ -269,439 +20,3 @@ void ApplicationAudioCaptureToolbar::Init()
 
 	ComboSelectToolbar::Init();
 }
-
-DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void DisplayCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "monitor_id";
-#elif __APPLE__
-	prop_name = "display_uuid";
-#else
-	is_int = true;
-	prop_name = "screen";
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-/* ========================================================================= */
-
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-
-	delete ui->deviceLabel;
-	delete ui->device;
-	ui->deviceLabel = nullptr;
-	ui->device = nullptr;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	active = obs_data_get_bool(settings, "active");
-
-	obs_module_t *mod = obs_get_module("win-dshow");
-	if (!mod)
-		return;
-
-	activateText = obs_module_get_locale_text(mod, "Activate");
-	deactivateText = obs_module_get_locale_text(mod, "Deactivate");
-
-	ui->activateButton->setText(active ? deactivateText : activateText);
-}
-
-DeviceCaptureToolbar::~DeviceCaptureToolbar() {}
-
-void DeviceCaptureToolbar::on_activateButton_clicked()
-{
-	OBSSource source = OBSGetStrongRef(weakSource);
-	if (!source) {
-		return;
-	}
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	bool now_active = obs_data_get_bool(settings, "active");
-
-	bool desyncedSetting = now_active != active;
-
-	active = !active;
-
-	const char *text = active ? deactivateText : activateText;
-	ui->activateButton->setText(text);
-
-	if (desyncedSetting) {
-		return;
-	}
-
-	calldata_t cd = {};
-	calldata_set_bool(&cd, "active", active);
-	proc_handler_t *ph = obs_source_get_proc_handler(source);
-	proc_handler_call(ph, "activate", &cd);
-	calldata_free(&cd);
-}
-
-/* ========================================================================= */
-
-GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_GameCaptureToolbar)
-{
-	obs_property_t *p;
-	int cur_idx;
-
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("win-capture");
-	if (!mod)
-		return;
-
-	ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
-	ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string cur_mode = obs_data_get_string(settings, "capture_mode");
-	std::string cur_window = obs_data_get_string(settings, "window");
-
-	ui->mode->blockSignals(true);
-	p = obs_properties_get(props.get(), "capture_mode");
-	cur_idx = FillPropertyCombo(ui->mode, p, cur_mode);
-	ui->mode->setCurrentIndex(cur_idx);
-	ui->mode->blockSignals(false);
-
-	ui->window->blockSignals(true);
-	p = obs_properties_get(props.get(), "window");
-	cur_idx = FillPropertyCombo(ui->window, p, cur_window);
-	ui->window->setCurrentIndex(cur_idx);
-	ui->window->blockSignals(false);
-
-	if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {
-		SetComboItemEnabled(ui->window, cur_idx, false);
-	}
-
-	UpdateWindowVisibility();
-}
-
-GameCaptureToolbar::~GameCaptureToolbar() {}
-
-void GameCaptureToolbar::UpdateWindowVisibility()
-{
-	QString mode = ui->mode->currentData().toString();
-	bool is_window = (mode == "window");
-	ui->windowLabel->setVisible(is_window);
-	ui->window->setVisible(is_window);
-}
-
-void GameCaptureToolbar::on_mode_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->mode->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-
-	UpdateWindowVisibility();
-}
-
-void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->window->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "window", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ImageSourceToolbar)
-{
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("image-source");
-	ui->pathLabel->setText(obs_module_get_locale_text(mod, "File"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string file = obs_data_get_string(settings, "file");
-
-	ui->path->setText(file.c_str());
-}
-
-ImageSourceToolbar::~ImageSourceToolbar() {}
-
-void ImageSourceToolbar::on_browse_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "file");
-	const char *desc = obs_property_description(p);
-	const char *filter = obs_property_path_filter(p);
-	const char *default_path = obs_property_path_default_path(p);
-
-	QString startDir = ui->path->text();
-	if (startDir.isEmpty())
-		startDir = default_path;
-
-	QString path = OpenFile(this, desc, startDir, filter);
-	if (path.isEmpty()) {
-		return;
-	}
-
-	ui->path->setText(path);
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "file", QT_TO_UTF8(path));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-static inline QColor color_from_int(long long val)
-{
-	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
-}
-
-static inline long long color_to_int(QColor color)
-{
-	auto shift = [&](unsigned val, int shift) {
-		return ((val & 0xff) << shift);
-	};
-
-	return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
-}
-
-ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ColorSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	unsigned int val = (unsigned int)obs_data_get_int(settings, "color");
-
-	color = color_from_int(val);
-	UpdateColor();
-}
-
-ColorSourceToolbar::~ColorSourceToolbar() {}
-
-void ColorSourceToolbar::UpdateColor()
-{
-	QPalette palette = QPalette(color);
-	ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
-	ui->color->setText(color.name(QColor::HexRgb));
-	ui->color->setPalette(palette);
-	ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
-					 .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
-					 .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
-	ui->color->setAutoFillBackground(true);
-	ui->color->setAlignment(Qt::AlignCenter);
-}
-
-void ColorSourceToolbar::on_choose_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "color");
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-	UpdateColor();
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_int(settings, "color", color_to_int(color));
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
-
-TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_TextSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-
-	const char *id = obs_source_get_unversioned_id(source);
-	bool ft2 = strcmp(id, "text_ft2_source") == 0;
-	bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
-
-	OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
-	MakeQFont(font_obj, font);
-
-	// Use "color1" if it's a freetype source and "color" elsewise
-	unsigned int val = (unsigned int)obs_data_get_int(
-		settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
-
-	color = color_from_int(val);
-
-	const char *text = obs_data_get_string(settings, "text");
-
-	bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
-	ui->emptySpace->setVisible(!single_line);
-	ui->text->setVisible(single_line);
-	if (single_line)
-		ui->text->setText(text);
-}
-
-TextSourceToolbar::~TextSourceToolbar() {}
-
-void TextSourceToolbar::on_selectFont_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	QFontDialog::FontDialogOptions options;
-	uint32_t flags;
-	bool success;
-
-#ifndef _WIN32
-	options = QFontDialog::DontUseNativeDialog;
-#endif
-
-	font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
-				    options);
-	if (!success) {
-		return;
-	}
-
-	OBSDataAutoRelease font_obj = obs_data_create();
-
-	obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family()));
-	obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName()));
-	obs_data_set_int(font_obj, "size", font.pointSize());
-	flags = font.bold() ? OBS_FONT_BOLD : 0;
-	flags |= font.italic() ? OBS_FONT_ITALIC : 0;
-	flags |= font.underline() ? OBS_FONT_UNDERLINE : 0;
-	flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;
-	obs_data_set_int(font_obj, "flags", flags);
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-
-	obs_data_set_obj(settings, "font", font_obj);
-
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_selectColor_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
-
-	obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
-
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (freetype) {
-		obs_data_set_int(settings, "color1", color_to_int(color));
-		obs_data_set_int(settings, "color2", color_to_int(color));
-	} else {
-		obs_data_set_int(settings, "color", color_to_int(color));
-	}
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_text_textChanged()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-	std::string newText = QT_TO_UTF8(ui->text->text());
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (newText == obs_data_get_string(settings, "text")) {
-		return;
-	}
-	SaveOldProperties(source);
-
-	obs_data_set_string(settings, "text", newText.c_str());
-	obs_source_update(source, nullptr);
-
-	SetUndoProperties(source, true);
-}

+ 1 - 168
frontend/components/ApplicationAudioCaptureToolbar.hpp

@@ -1,85 +1,6 @@
 #pragma once
 
-#include <memory>
-#include <obs.hpp>
-#include <QWidget>
-
-class Ui_BrowserSourceToolbar;
-class Ui_DeviceSelectToolbar;
-class Ui_GameCaptureToolbar;
-class Ui_ImageSourceToolbar;
-class Ui_ColorSourceToolbar;
-class Ui_TextSourceToolbar;
-
-class SourceToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-protected:
-	using properties_delete_t = decltype(&obs_properties_destroy);
-	using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
-
-	properties_t props;
-	OBSDataAutoRelease oldData;
-
-	void SaveOldProperties(obs_source_t *source);
-	void SetUndoProperties(obs_source_t *source, bool repeatable = false);
-
-public:
-	SourceToolbar(QWidget *parent, OBSSource source);
-
-	OBSSource GetSource() { return OBSGetStrongRef(weakSource); }
-
-public slots:
-	virtual void Update() {}
-};
-
-class BrowserToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_BrowserSourceToolbar> ui;
-
-public:
-	BrowserToolbar(QWidget *parent, OBSSource source);
-	~BrowserToolbar();
-
-public slots:
-	void on_refresh_clicked();
-};
-
-class ComboSelectToolbar : public SourceToolbar {
-	Q_OBJECT
-
-protected:
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *prop_name;
-	bool is_int = false;
-
-public:
-	ComboSelectToolbar(QWidget *parent, OBSSource source);
-	~ComboSelectToolbar();
-	virtual void Init();
-
-public slots:
-	void on_device_currentIndexChanged(int idx);
-};
-
-class AudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	AudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class WindowCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	WindowCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
+#include "ComboSelectToolbar.hpp"
 
 class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
 	Q_OBJECT
@@ -88,91 +9,3 @@ public:
 	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
 	void Init() override;
 };
-
-class DisplayCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DeviceCaptureToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *activateText;
-	const char *deactivateText;
-	bool active;
-
-public:
-	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	~DeviceCaptureToolbar();
-
-public slots:
-	void on_activateButton_clicked();
-};
-
-class GameCaptureToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_GameCaptureToolbar> ui;
-
-	void UpdateWindowVisibility();
-
-public:
-	GameCaptureToolbar(QWidget *parent, OBSSource source);
-	~GameCaptureToolbar();
-
-public slots:
-	void on_mode_currentIndexChanged(int idx);
-	void on_window_currentIndexChanged(int idx);
-};
-
-class ImageSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ImageSourceToolbar> ui;
-
-public:
-	ImageSourceToolbar(QWidget *parent, OBSSource source);
-	~ImageSourceToolbar();
-
-public slots:
-	void on_browse_clicked();
-};
-
-class ColorSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ColorSourceToolbar> ui;
-	QColor color;
-
-	void UpdateColor();
-
-public:
-	ColorSourceToolbar(QWidget *parent, OBSSource source);
-	~ColorSourceToolbar();
-
-public slots:
-	void on_choose_clicked();
-};
-
-class TextSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_TextSourceToolbar> ui;
-	QFont font;
-	QColor color;
-
-public:
-	TextSourceToolbar(QWidget *parent, OBSSource source);
-	~TextSourceToolbar();
-
-public slots:
-	void on_selectFont_clicked();
-	void on_selectColor_clicked();
-	void on_text_textChanged();
-};

+ 2 - 676
frontend/components/AudioCaptureToolbar.cpp

@@ -1,18 +1,6 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
-
-#include "ui_browser-source-toolbar.h"
+#include "AudioCaptureToolbar.hpp"
 #include "ui_device-select-toolbar.h"
-#include "ui_game-capture-toolbar.h"
-#include "ui_image-source-toolbar.h"
-#include "ui_color-source-toolbar.h"
-#include "ui_text-source-toolbar.h"
+#include "moc_AudioCaptureToolbar.cpp"
 
 #ifdef _WIN32
 #define get_os_module(win, mac, linux) obs_get_module(win)
@@ -25,186 +13,6 @@
 #define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
 #endif
 
-/* ========================================================================= */
-
-SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  props(obs_source_properties(source), obs_properties_destroy)
-{
-}
-
-void SourceToolbar::SaveOldProperties(obs_source_t *source)
-{
-	oldData = obs_data_create();
-
-	OBSDataAutoRelease oldSettings = obs_source_get_settings(source);
-	obs_data_apply(oldData, oldSettings);
-	obs_data_set_string(oldData, "undo_suuid", obs_source_get_uuid(source));
-}
-
-void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
-{
-	if (!oldData) {
-		blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__);
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-
-	OBSSource currentSceneSource = main->GetCurrentSceneSource();
-	if (!currentSceneSource)
-		return;
-	std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
-	auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
-		OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
-		obs_source_reset_settings(source, settings);
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-
-		main->UpdateContextBar();
-	};
-
-	OBSDataAutoRelease new_settings = obs_data_create();
-	OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
-	obs_data_apply(new_settings, curr_settings);
-	obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
-
-	std::string undo_data(obs_data_get_json(oldData));
-	std::string redo_data(obs_data_get_json(new_settings));
-
-	if (undo_data.compare(redo_data) != 0)
-		main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
-					undo_data, redo_data, repeatable);
-
-	oldData = nullptr;
-}
-
-/* ========================================================================= */
-
-BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_BrowserSourceToolbar)
-{
-	ui->setupUi(this);
-}
-
-BrowserToolbar::~BrowserToolbar() {}
-
-void BrowserToolbar::on_refresh_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
-	obs_property_button_clicked(p, source.Get());
-}
-
-/* ========================================================================= */
-
-ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-}
-
-ComboSelectToolbar::~ComboSelectToolbar() {}
-
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
-{
-	size_t count = obs_property_list_item_count(p);
-	int cur_idx = -1;
-
-	for (size_t i = 0; i < count; i++) {
-		const char *name = obs_property_list_item_name(p, i);
-		std::string id;
-
-		if (is_int) {
-			id = std::to_string(obs_property_list_item_int(p, i));
-		} else {
-			const char *val = obs_property_list_item_string(p, i);
-			id = val ? val : "";
-		}
-
-		if (cur_id == id)
-			cur_idx = (int)i;
-
-		c->addItem(name, id.c_str());
-	}
-
-	return cur_idx;
-}
-
-void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
-					const char *prop_name, bool is_int)
-{
-	std::string cur_id;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (is_int) {
-		cur_id = std::to_string(obs_data_get_int(settings, prop_name));
-	} else {
-		cur_id = obs_data_get_string(settings, prop_name);
-	}
-
-	combo->blockSignals(true);
-
-	obs_property_t *p = obs_properties_get(props, prop_name);
-	int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);
-
-	if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
-		if (cur_idx == -1) {
-			combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
-			cur_idx = 0;
-		}
-
-		SetComboItemEnabled(combo, cur_idx, false);
-	}
-
-	combo->setCurrentIndex(cur_idx);
-	combo->blockSignals(false);
-}
-
-void ComboSelectToolbar::Init()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
-}
-
-void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
-{
-	QString id = combo->itemData(idx).toString();
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (is_int) {
-		obs_data_set_int(settings, prop_name, id.toInt());
-	} else {
-		obs_data_set_string(settings, prop_name, QT_TO_UTF8(id));
-	}
-	obs_source_update(source, settings);
-}
-
-void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	SaveOldProperties(source);
-	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
-	SetUndoProperties(source);
-}
-
 AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
 
 void AudioCaptureToolbar::Init()
@@ -223,485 +31,3 @@ void AudioCaptureToolbar::Init()
 
 	ComboSelectToolbar::Init();
 }
-
-WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void WindowCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
-	ui->deviceLabel->setText(device_str);
-
-#if !defined(_WIN32) && !defined(__APPLE__) //linux
-	prop_name = "capture_window";
-#else
-	prop_name = "window";
-#endif
-
-#ifdef __APPLE__
-	is_int = true;
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void ApplicationAudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = obs_get_module("win-wasapi");
-	const char *device_str = obs_module_get_locale_text(mod, "Window");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "window";
-
-	ComboSelectToolbar::Init();
-}
-
-DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void DisplayCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "monitor_id";
-#elif __APPLE__
-	prop_name = "display_uuid";
-#else
-	is_int = true;
-	prop_name = "screen";
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-/* ========================================================================= */
-
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-
-	delete ui->deviceLabel;
-	delete ui->device;
-	ui->deviceLabel = nullptr;
-	ui->device = nullptr;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	active = obs_data_get_bool(settings, "active");
-
-	obs_module_t *mod = obs_get_module("win-dshow");
-	if (!mod)
-		return;
-
-	activateText = obs_module_get_locale_text(mod, "Activate");
-	deactivateText = obs_module_get_locale_text(mod, "Deactivate");
-
-	ui->activateButton->setText(active ? deactivateText : activateText);
-}
-
-DeviceCaptureToolbar::~DeviceCaptureToolbar() {}
-
-void DeviceCaptureToolbar::on_activateButton_clicked()
-{
-	OBSSource source = OBSGetStrongRef(weakSource);
-	if (!source) {
-		return;
-	}
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	bool now_active = obs_data_get_bool(settings, "active");
-
-	bool desyncedSetting = now_active != active;
-
-	active = !active;
-
-	const char *text = active ? deactivateText : activateText;
-	ui->activateButton->setText(text);
-
-	if (desyncedSetting) {
-		return;
-	}
-
-	calldata_t cd = {};
-	calldata_set_bool(&cd, "active", active);
-	proc_handler_t *ph = obs_source_get_proc_handler(source);
-	proc_handler_call(ph, "activate", &cd);
-	calldata_free(&cd);
-}
-
-/* ========================================================================= */
-
-GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_GameCaptureToolbar)
-{
-	obs_property_t *p;
-	int cur_idx;
-
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("win-capture");
-	if (!mod)
-		return;
-
-	ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
-	ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string cur_mode = obs_data_get_string(settings, "capture_mode");
-	std::string cur_window = obs_data_get_string(settings, "window");
-
-	ui->mode->blockSignals(true);
-	p = obs_properties_get(props.get(), "capture_mode");
-	cur_idx = FillPropertyCombo(ui->mode, p, cur_mode);
-	ui->mode->setCurrentIndex(cur_idx);
-	ui->mode->blockSignals(false);
-
-	ui->window->blockSignals(true);
-	p = obs_properties_get(props.get(), "window");
-	cur_idx = FillPropertyCombo(ui->window, p, cur_window);
-	ui->window->setCurrentIndex(cur_idx);
-	ui->window->blockSignals(false);
-
-	if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {
-		SetComboItemEnabled(ui->window, cur_idx, false);
-	}
-
-	UpdateWindowVisibility();
-}
-
-GameCaptureToolbar::~GameCaptureToolbar() {}
-
-void GameCaptureToolbar::UpdateWindowVisibility()
-{
-	QString mode = ui->mode->currentData().toString();
-	bool is_window = (mode == "window");
-	ui->windowLabel->setVisible(is_window);
-	ui->window->setVisible(is_window);
-}
-
-void GameCaptureToolbar::on_mode_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->mode->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-
-	UpdateWindowVisibility();
-}
-
-void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->window->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "window", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ImageSourceToolbar)
-{
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("image-source");
-	ui->pathLabel->setText(obs_module_get_locale_text(mod, "File"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string file = obs_data_get_string(settings, "file");
-
-	ui->path->setText(file.c_str());
-}
-
-ImageSourceToolbar::~ImageSourceToolbar() {}
-
-void ImageSourceToolbar::on_browse_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "file");
-	const char *desc = obs_property_description(p);
-	const char *filter = obs_property_path_filter(p);
-	const char *default_path = obs_property_path_default_path(p);
-
-	QString startDir = ui->path->text();
-	if (startDir.isEmpty())
-		startDir = default_path;
-
-	QString path = OpenFile(this, desc, startDir, filter);
-	if (path.isEmpty()) {
-		return;
-	}
-
-	ui->path->setText(path);
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "file", QT_TO_UTF8(path));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-static inline QColor color_from_int(long long val)
-{
-	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
-}
-
-static inline long long color_to_int(QColor color)
-{
-	auto shift = [&](unsigned val, int shift) {
-		return ((val & 0xff) << shift);
-	};
-
-	return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
-}
-
-ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ColorSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	unsigned int val = (unsigned int)obs_data_get_int(settings, "color");
-
-	color = color_from_int(val);
-	UpdateColor();
-}
-
-ColorSourceToolbar::~ColorSourceToolbar() {}
-
-void ColorSourceToolbar::UpdateColor()
-{
-	QPalette palette = QPalette(color);
-	ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
-	ui->color->setText(color.name(QColor::HexRgb));
-	ui->color->setPalette(palette);
-	ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
-					 .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
-					 .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
-	ui->color->setAutoFillBackground(true);
-	ui->color->setAlignment(Qt::AlignCenter);
-}
-
-void ColorSourceToolbar::on_choose_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "color");
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-	UpdateColor();
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_int(settings, "color", color_to_int(color));
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
-
-TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_TextSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-
-	const char *id = obs_source_get_unversioned_id(source);
-	bool ft2 = strcmp(id, "text_ft2_source") == 0;
-	bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
-
-	OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
-	MakeQFont(font_obj, font);
-
-	// Use "color1" if it's a freetype source and "color" elsewise
-	unsigned int val = (unsigned int)obs_data_get_int(
-		settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
-
-	color = color_from_int(val);
-
-	const char *text = obs_data_get_string(settings, "text");
-
-	bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
-	ui->emptySpace->setVisible(!single_line);
-	ui->text->setVisible(single_line);
-	if (single_line)
-		ui->text->setText(text);
-}
-
-TextSourceToolbar::~TextSourceToolbar() {}
-
-void TextSourceToolbar::on_selectFont_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	QFontDialog::FontDialogOptions options;
-	uint32_t flags;
-	bool success;
-
-#ifndef _WIN32
-	options = QFontDialog::DontUseNativeDialog;
-#endif
-
-	font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
-				    options);
-	if (!success) {
-		return;
-	}
-
-	OBSDataAutoRelease font_obj = obs_data_create();
-
-	obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family()));
-	obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName()));
-	obs_data_set_int(font_obj, "size", font.pointSize());
-	flags = font.bold() ? OBS_FONT_BOLD : 0;
-	flags |= font.italic() ? OBS_FONT_ITALIC : 0;
-	flags |= font.underline() ? OBS_FONT_UNDERLINE : 0;
-	flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;
-	obs_data_set_int(font_obj, "flags", flags);
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-
-	obs_data_set_obj(settings, "font", font_obj);
-
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_selectColor_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
-
-	obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
-
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (freetype) {
-		obs_data_set_int(settings, "color1", color_to_int(color));
-		obs_data_set_int(settings, "color2", color_to_int(color));
-	} else {
-		obs_data_set_int(settings, "color", color_to_int(color));
-	}
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_text_textChanged()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-	std::string newText = QT_TO_UTF8(ui->text->text());
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (newText == obs_data_get_string(settings, "text")) {
-		return;
-	}
-	SaveOldProperties(source);
-
-	obs_data_set_string(settings, "text", newText.c_str());
-	obs_source_update(source, nullptr);
-
-	SetUndoProperties(source, true);
-}

+ 1 - 168
frontend/components/AudioCaptureToolbar.hpp

@@ -1,69 +1,6 @@
 #pragma once
 
-#include <memory>
-#include <obs.hpp>
-#include <QWidget>
-
-class Ui_BrowserSourceToolbar;
-class Ui_DeviceSelectToolbar;
-class Ui_GameCaptureToolbar;
-class Ui_ImageSourceToolbar;
-class Ui_ColorSourceToolbar;
-class Ui_TextSourceToolbar;
-
-class SourceToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-protected:
-	using properties_delete_t = decltype(&obs_properties_destroy);
-	using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
-
-	properties_t props;
-	OBSDataAutoRelease oldData;
-
-	void SaveOldProperties(obs_source_t *source);
-	void SetUndoProperties(obs_source_t *source, bool repeatable = false);
-
-public:
-	SourceToolbar(QWidget *parent, OBSSource source);
-
-	OBSSource GetSource() { return OBSGetStrongRef(weakSource); }
-
-public slots:
-	virtual void Update() {}
-};
-
-class BrowserToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_BrowserSourceToolbar> ui;
-
-public:
-	BrowserToolbar(QWidget *parent, OBSSource source);
-	~BrowserToolbar();
-
-public slots:
-	void on_refresh_clicked();
-};
-
-class ComboSelectToolbar : public SourceToolbar {
-	Q_OBJECT
-
-protected:
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *prop_name;
-	bool is_int = false;
-
-public:
-	ComboSelectToolbar(QWidget *parent, OBSSource source);
-	~ComboSelectToolbar();
-	virtual void Init();
-
-public slots:
-	void on_device_currentIndexChanged(int idx);
-};
+#include "ComboSelectToolbar.hpp"
 
 class AudioCaptureToolbar : public ComboSelectToolbar {
 	Q_OBJECT
@@ -72,107 +9,3 @@ public:
 	AudioCaptureToolbar(QWidget *parent, OBSSource source);
 	void Init() override;
 };
-
-class WindowCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	WindowCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DisplayCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DeviceCaptureToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *activateText;
-	const char *deactivateText;
-	bool active;
-
-public:
-	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	~DeviceCaptureToolbar();
-
-public slots:
-	void on_activateButton_clicked();
-};
-
-class GameCaptureToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_GameCaptureToolbar> ui;
-
-	void UpdateWindowVisibility();
-
-public:
-	GameCaptureToolbar(QWidget *parent, OBSSource source);
-	~GameCaptureToolbar();
-
-public slots:
-	void on_mode_currentIndexChanged(int idx);
-	void on_window_currentIndexChanged(int idx);
-};
-
-class ImageSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ImageSourceToolbar> ui;
-
-public:
-	ImageSourceToolbar(QWidget *parent, OBSSource source);
-	~ImageSourceToolbar();
-
-public slots:
-	void on_browse_clicked();
-};
-
-class ColorSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ColorSourceToolbar> ui;
-	QColor color;
-
-	void UpdateColor();
-
-public:
-	ColorSourceToolbar(QWidget *parent, OBSSource source);
-	~ColorSourceToolbar();
-
-public slots:
-	void on_choose_clicked();
-};
-
-class TextSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_TextSourceToolbar> ui;
-	QFont font;
-	QColor color;
-
-public:
-	TextSourceToolbar(QWidget *parent, OBSSource source);
-	~TextSourceToolbar();
-
-public slots:
-	void on_selectFont_clicked();
-	void on_selectColor_clicked();
-	void on_text_textChanged();
-};

+ 2 - 686
frontend/components/BrowserToolbar.cpp

@@ -1,88 +1,6 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
-
+#include "BrowserToolbar.hpp"
 #include "ui_browser-source-toolbar.h"
-#include "ui_device-select-toolbar.h"
-#include "ui_game-capture-toolbar.h"
-#include "ui_image-source-toolbar.h"
-#include "ui_color-source-toolbar.h"
-#include "ui_text-source-toolbar.h"
-
-#ifdef _WIN32
-#define get_os_module(win, mac, linux) obs_get_module(win)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)
-#elif __APPLE__
-#define get_os_module(win, mac, linux) obs_get_module(mac)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)
-#else
-#define get_os_module(win, mac, linux) obs_get_module(linux)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
-#endif
-
-/* ========================================================================= */
-
-SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  props(obs_source_properties(source), obs_properties_destroy)
-{
-}
-
-void SourceToolbar::SaveOldProperties(obs_source_t *source)
-{
-	oldData = obs_data_create();
-
-	OBSDataAutoRelease oldSettings = obs_source_get_settings(source);
-	obs_data_apply(oldData, oldSettings);
-	obs_data_set_string(oldData, "undo_suuid", obs_source_get_uuid(source));
-}
-
-void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
-{
-	if (!oldData) {
-		blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__);
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-
-	OBSSource currentSceneSource = main->GetCurrentSceneSource();
-	if (!currentSceneSource)
-		return;
-	std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
-	auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
-		OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
-		obs_source_reset_settings(source, settings);
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-
-		main->UpdateContextBar();
-	};
-
-	OBSDataAutoRelease new_settings = obs_data_create();
-	OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
-	obs_data_apply(new_settings, curr_settings);
-	obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
-
-	std::string undo_data(obs_data_get_json(oldData));
-	std::string redo_data(obs_data_get_json(new_settings));
-
-	if (undo_data.compare(redo_data) != 0)
-		main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
-					undo_data, redo_data, repeatable);
-
-	oldData = nullptr;
-}
-
-/* ========================================================================= */
+#include "moc_BrowserToolbar.cpp"
 
 BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
 	: SourceToolbar(parent, source),
@@ -103,605 +21,3 @@ void BrowserToolbar::on_refresh_clicked()
 	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
 	obs_property_button_clicked(p, source.Get());
 }
-
-/* ========================================================================= */
-
-ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-}
-
-ComboSelectToolbar::~ComboSelectToolbar() {}
-
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
-{
-	size_t count = obs_property_list_item_count(p);
-	int cur_idx = -1;
-
-	for (size_t i = 0; i < count; i++) {
-		const char *name = obs_property_list_item_name(p, i);
-		std::string id;
-
-		if (is_int) {
-			id = std::to_string(obs_property_list_item_int(p, i));
-		} else {
-			const char *val = obs_property_list_item_string(p, i);
-			id = val ? val : "";
-		}
-
-		if (cur_id == id)
-			cur_idx = (int)i;
-
-		c->addItem(name, id.c_str());
-	}
-
-	return cur_idx;
-}
-
-void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
-					const char *prop_name, bool is_int)
-{
-	std::string cur_id;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (is_int) {
-		cur_id = std::to_string(obs_data_get_int(settings, prop_name));
-	} else {
-		cur_id = obs_data_get_string(settings, prop_name);
-	}
-
-	combo->blockSignals(true);
-
-	obs_property_t *p = obs_properties_get(props, prop_name);
-	int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);
-
-	if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
-		if (cur_idx == -1) {
-			combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
-			cur_idx = 0;
-		}
-
-		SetComboItemEnabled(combo, cur_idx, false);
-	}
-
-	combo->setCurrentIndex(cur_idx);
-	combo->blockSignals(false);
-}
-
-void ComboSelectToolbar::Init()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
-}
-
-void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
-{
-	QString id = combo->itemData(idx).toString();
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (is_int) {
-		obs_data_set_int(settings, prop_name, id.toInt());
-	} else {
-		obs_data_set_string(settings, prop_name, QT_TO_UTF8(id));
-	}
-	obs_source_update(source, settings);
-}
-
-void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	SaveOldProperties(source);
-	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
-	SetUndoProperties(source);
-}
-
-AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void AudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "device_id";
-
-	ComboSelectToolbar::Init();
-}
-
-WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void WindowCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
-	ui->deviceLabel->setText(device_str);
-
-#if !defined(_WIN32) && !defined(__APPLE__) //linux
-	prop_name = "capture_window";
-#else
-	prop_name = "window";
-#endif
-
-#ifdef __APPLE__
-	is_int = true;
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void ApplicationAudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = obs_get_module("win-wasapi");
-	const char *device_str = obs_module_get_locale_text(mod, "Window");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "window";
-
-	ComboSelectToolbar::Init();
-}
-
-DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void DisplayCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "monitor_id";
-#elif __APPLE__
-	prop_name = "display_uuid";
-#else
-	is_int = true;
-	prop_name = "screen";
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-/* ========================================================================= */
-
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-
-	delete ui->deviceLabel;
-	delete ui->device;
-	ui->deviceLabel = nullptr;
-	ui->device = nullptr;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	active = obs_data_get_bool(settings, "active");
-
-	obs_module_t *mod = obs_get_module("win-dshow");
-	if (!mod)
-		return;
-
-	activateText = obs_module_get_locale_text(mod, "Activate");
-	deactivateText = obs_module_get_locale_text(mod, "Deactivate");
-
-	ui->activateButton->setText(active ? deactivateText : activateText);
-}
-
-DeviceCaptureToolbar::~DeviceCaptureToolbar() {}
-
-void DeviceCaptureToolbar::on_activateButton_clicked()
-{
-	OBSSource source = OBSGetStrongRef(weakSource);
-	if (!source) {
-		return;
-	}
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	bool now_active = obs_data_get_bool(settings, "active");
-
-	bool desyncedSetting = now_active != active;
-
-	active = !active;
-
-	const char *text = active ? deactivateText : activateText;
-	ui->activateButton->setText(text);
-
-	if (desyncedSetting) {
-		return;
-	}
-
-	calldata_t cd = {};
-	calldata_set_bool(&cd, "active", active);
-	proc_handler_t *ph = obs_source_get_proc_handler(source);
-	proc_handler_call(ph, "activate", &cd);
-	calldata_free(&cd);
-}
-
-/* ========================================================================= */
-
-GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_GameCaptureToolbar)
-{
-	obs_property_t *p;
-	int cur_idx;
-
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("win-capture");
-	if (!mod)
-		return;
-
-	ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
-	ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string cur_mode = obs_data_get_string(settings, "capture_mode");
-	std::string cur_window = obs_data_get_string(settings, "window");
-
-	ui->mode->blockSignals(true);
-	p = obs_properties_get(props.get(), "capture_mode");
-	cur_idx = FillPropertyCombo(ui->mode, p, cur_mode);
-	ui->mode->setCurrentIndex(cur_idx);
-	ui->mode->blockSignals(false);
-
-	ui->window->blockSignals(true);
-	p = obs_properties_get(props.get(), "window");
-	cur_idx = FillPropertyCombo(ui->window, p, cur_window);
-	ui->window->setCurrentIndex(cur_idx);
-	ui->window->blockSignals(false);
-
-	if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {
-		SetComboItemEnabled(ui->window, cur_idx, false);
-	}
-
-	UpdateWindowVisibility();
-}
-
-GameCaptureToolbar::~GameCaptureToolbar() {}
-
-void GameCaptureToolbar::UpdateWindowVisibility()
-{
-	QString mode = ui->mode->currentData().toString();
-	bool is_window = (mode == "window");
-	ui->windowLabel->setVisible(is_window);
-	ui->window->setVisible(is_window);
-}
-
-void GameCaptureToolbar::on_mode_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->mode->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-
-	UpdateWindowVisibility();
-}
-
-void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->window->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "window", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ImageSourceToolbar)
-{
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("image-source");
-	ui->pathLabel->setText(obs_module_get_locale_text(mod, "File"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string file = obs_data_get_string(settings, "file");
-
-	ui->path->setText(file.c_str());
-}
-
-ImageSourceToolbar::~ImageSourceToolbar() {}
-
-void ImageSourceToolbar::on_browse_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "file");
-	const char *desc = obs_property_description(p);
-	const char *filter = obs_property_path_filter(p);
-	const char *default_path = obs_property_path_default_path(p);
-
-	QString startDir = ui->path->text();
-	if (startDir.isEmpty())
-		startDir = default_path;
-
-	QString path = OpenFile(this, desc, startDir, filter);
-	if (path.isEmpty()) {
-		return;
-	}
-
-	ui->path->setText(path);
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "file", QT_TO_UTF8(path));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-static inline QColor color_from_int(long long val)
-{
-	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
-}
-
-static inline long long color_to_int(QColor color)
-{
-	auto shift = [&](unsigned val, int shift) {
-		return ((val & 0xff) << shift);
-	};
-
-	return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
-}
-
-ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ColorSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	unsigned int val = (unsigned int)obs_data_get_int(settings, "color");
-
-	color = color_from_int(val);
-	UpdateColor();
-}
-
-ColorSourceToolbar::~ColorSourceToolbar() {}
-
-void ColorSourceToolbar::UpdateColor()
-{
-	QPalette palette = QPalette(color);
-	ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
-	ui->color->setText(color.name(QColor::HexRgb));
-	ui->color->setPalette(palette);
-	ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
-					 .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
-					 .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
-	ui->color->setAutoFillBackground(true);
-	ui->color->setAlignment(Qt::AlignCenter);
-}
-
-void ColorSourceToolbar::on_choose_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "color");
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-	UpdateColor();
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_int(settings, "color", color_to_int(color));
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
-
-TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_TextSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-
-	const char *id = obs_source_get_unversioned_id(source);
-	bool ft2 = strcmp(id, "text_ft2_source") == 0;
-	bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
-
-	OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
-	MakeQFont(font_obj, font);
-
-	// Use "color1" if it's a freetype source and "color" elsewise
-	unsigned int val = (unsigned int)obs_data_get_int(
-		settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
-
-	color = color_from_int(val);
-
-	const char *text = obs_data_get_string(settings, "text");
-
-	bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
-	ui->emptySpace->setVisible(!single_line);
-	ui->text->setVisible(single_line);
-	if (single_line)
-		ui->text->setText(text);
-}
-
-TextSourceToolbar::~TextSourceToolbar() {}
-
-void TextSourceToolbar::on_selectFont_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	QFontDialog::FontDialogOptions options;
-	uint32_t flags;
-	bool success;
-
-#ifndef _WIN32
-	options = QFontDialog::DontUseNativeDialog;
-#endif
-
-	font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
-				    options);
-	if (!success) {
-		return;
-	}
-
-	OBSDataAutoRelease font_obj = obs_data_create();
-
-	obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family()));
-	obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName()));
-	obs_data_set_int(font_obj, "size", font.pointSize());
-	flags = font.bold() ? OBS_FONT_BOLD : 0;
-	flags |= font.italic() ? OBS_FONT_ITALIC : 0;
-	flags |= font.underline() ? OBS_FONT_UNDERLINE : 0;
-	flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;
-	obs_data_set_int(font_obj, "flags", flags);
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-
-	obs_data_set_obj(settings, "font", font_obj);
-
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_selectColor_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
-
-	obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
-
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (freetype) {
-		obs_data_set_int(settings, "color1", color_to_int(color));
-		obs_data_set_int(settings, "color2", color_to_int(color));
-	} else {
-		obs_data_set_int(settings, "color", color_to_int(color));
-	}
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_text_textChanged()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-	std::string newText = QT_TO_UTF8(ui->text->text());
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (newText == obs_data_get_string(settings, "text")) {
-		return;
-	}
-	SaveOldProperties(source);
-
-	obs_data_set_string(settings, "text", newText.c_str());
-	obs_source_update(source, nullptr);
-
-	SetUndoProperties(source, true);
-}

+ 1 - 161
frontend/components/BrowserToolbar.hpp

@@ -1,39 +1,8 @@
 #pragma once
 
-#include <memory>
-#include <obs.hpp>
-#include <QWidget>
+#include "SourceToolbar.hpp"
 
 class Ui_BrowserSourceToolbar;
-class Ui_DeviceSelectToolbar;
-class Ui_GameCaptureToolbar;
-class Ui_ImageSourceToolbar;
-class Ui_ColorSourceToolbar;
-class Ui_TextSourceToolbar;
-
-class SourceToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-protected:
-	using properties_delete_t = decltype(&obs_properties_destroy);
-	using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
-
-	properties_t props;
-	OBSDataAutoRelease oldData;
-
-	void SaveOldProperties(obs_source_t *source);
-	void SetUndoProperties(obs_source_t *source, bool repeatable = false);
-
-public:
-	SourceToolbar(QWidget *parent, OBSSource source);
-
-	OBSSource GetSource() { return OBSGetStrongRef(weakSource); }
-
-public slots:
-	virtual void Update() {}
-};
 
 class BrowserToolbar : public SourceToolbar {
 	Q_OBJECT
@@ -47,132 +16,3 @@ public:
 public slots:
 	void on_refresh_clicked();
 };
-
-class ComboSelectToolbar : public SourceToolbar {
-	Q_OBJECT
-
-protected:
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *prop_name;
-	bool is_int = false;
-
-public:
-	ComboSelectToolbar(QWidget *parent, OBSSource source);
-	~ComboSelectToolbar();
-	virtual void Init();
-
-public slots:
-	void on_device_currentIndexChanged(int idx);
-};
-
-class AudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	AudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class WindowCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	WindowCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DisplayCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DeviceCaptureToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *activateText;
-	const char *deactivateText;
-	bool active;
-
-public:
-	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	~DeviceCaptureToolbar();
-
-public slots:
-	void on_activateButton_clicked();
-};
-
-class GameCaptureToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_GameCaptureToolbar> ui;
-
-	void UpdateWindowVisibility();
-
-public:
-	GameCaptureToolbar(QWidget *parent, OBSSource source);
-	~GameCaptureToolbar();
-
-public slots:
-	void on_mode_currentIndexChanged(int idx);
-	void on_window_currentIndexChanged(int idx);
-};
-
-class ImageSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ImageSourceToolbar> ui;
-
-public:
-	ImageSourceToolbar(QWidget *parent, OBSSource source);
-	~ImageSourceToolbar();
-
-public slots:
-	void on_browse_clicked();
-};
-
-class ColorSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ColorSourceToolbar> ui;
-	QColor color;
-
-	void UpdateColor();
-
-public:
-	ColorSourceToolbar(QWidget *parent, OBSSource source);
-	~ColorSourceToolbar();
-
-public slots:
-	void on_choose_clicked();
-};
-
-class TextSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_TextSourceToolbar> ui;
-	QFont font;
-	QColor color;
-
-public:
-	TextSourceToolbar(QWidget *parent, OBSSource source);
-	~TextSourceToolbar();
-
-public slots:
-	void on_selectFont_clicked();
-	void on_selectColor_clicked();
-	void on_text_textChanged();
-};

+ 5 - 629
frontend/components/ColorSourceToolbar.cpp

@@ -1,500 +1,16 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
-
-#include "ui_browser-source-toolbar.h"
-#include "ui_device-select-toolbar.h"
-#include "ui_game-capture-toolbar.h"
-#include "ui_image-source-toolbar.h"
+#include "ColorSourceToolbar.hpp"
 #include "ui_color-source-toolbar.h"
-#include "ui_text-source-toolbar.h"
-
-#ifdef _WIN32
-#define get_os_module(win, mac, linux) obs_get_module(win)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)
-#elif __APPLE__
-#define get_os_module(win, mac, linux) obs_get_module(mac)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)
-#else
-#define get_os_module(win, mac, linux) obs_get_module(linux)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
-#endif
-
-/* ========================================================================= */
-
-SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  props(obs_source_properties(source), obs_properties_destroy)
-{
-}
-
-void SourceToolbar::SaveOldProperties(obs_source_t *source)
-{
-	oldData = obs_data_create();
-
-	OBSDataAutoRelease oldSettings = obs_source_get_settings(source);
-	obs_data_apply(oldData, oldSettings);
-	obs_data_set_string(oldData, "undo_suuid", obs_source_get_uuid(source));
-}
-
-void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
-{
-	if (!oldData) {
-		blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__);
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-
-	OBSSource currentSceneSource = main->GetCurrentSceneSource();
-	if (!currentSceneSource)
-		return;
-	std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
-	auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
-		OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
-		obs_source_reset_settings(source, settings);
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-
-		main->UpdateContextBar();
-	};
-
-	OBSDataAutoRelease new_settings = obs_data_create();
-	OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
-	obs_data_apply(new_settings, curr_settings);
-	obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
-
-	std::string undo_data(obs_data_get_json(oldData));
-	std::string redo_data(obs_data_get_json(new_settings));
-
-	if (undo_data.compare(redo_data) != 0)
-		main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
-					undo_data, redo_data, repeatable);
-
-	oldData = nullptr;
-}
-
-/* ========================================================================= */
-
-BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_BrowserSourceToolbar)
-{
-	ui->setupUi(this);
-}
-
-BrowserToolbar::~BrowserToolbar() {}
-
-void BrowserToolbar::on_refresh_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
-	obs_property_button_clicked(p, source.Get());
-}
-
-/* ========================================================================= */
-
-ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-}
-
-ComboSelectToolbar::~ComboSelectToolbar() {}
-
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
-{
-	size_t count = obs_property_list_item_count(p);
-	int cur_idx = -1;
-
-	for (size_t i = 0; i < count; i++) {
-		const char *name = obs_property_list_item_name(p, i);
-		std::string id;
-
-		if (is_int) {
-			id = std::to_string(obs_property_list_item_int(p, i));
-		} else {
-			const char *val = obs_property_list_item_string(p, i);
-			id = val ? val : "";
-		}
-
-		if (cur_id == id)
-			cur_idx = (int)i;
-
-		c->addItem(name, id.c_str());
-	}
-
-	return cur_idx;
-}
-
-void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
-					const char *prop_name, bool is_int)
-{
-	std::string cur_id;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (is_int) {
-		cur_id = std::to_string(obs_data_get_int(settings, prop_name));
-	} else {
-		cur_id = obs_data_get_string(settings, prop_name);
-	}
-
-	combo->blockSignals(true);
-
-	obs_property_t *p = obs_properties_get(props, prop_name);
-	int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);
-
-	if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
-		if (cur_idx == -1) {
-			combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
-			cur_idx = 0;
-		}
-
-		SetComboItemEnabled(combo, cur_idx, false);
-	}
-
-	combo->setCurrentIndex(cur_idx);
-	combo->blockSignals(false);
-}
-
-void ComboSelectToolbar::Init()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
-}
-
-void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
-{
-	QString id = combo->itemData(idx).toString();
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (is_int) {
-		obs_data_set_int(settings, prop_name, id.toInt());
-	} else {
-		obs_data_set_string(settings, prop_name, QT_TO_UTF8(id));
-	}
-	obs_source_update(source, settings);
-}
-
-void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	SaveOldProperties(source);
-	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
-	SetUndoProperties(source);
-}
-
-AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void AudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "device_id";
-
-	ComboSelectToolbar::Init();
-}
-
-WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void WindowCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
-	ui->deviceLabel->setText(device_str);
-
-#if !defined(_WIN32) && !defined(__APPLE__) //linux
-	prop_name = "capture_window";
-#else
-	prop_name = "window";
-#endif
-
-#ifdef __APPLE__
-	is_int = true;
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void ApplicationAudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = obs_get_module("win-wasapi");
-	const char *device_str = obs_module_get_locale_text(mod, "Window");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "window";
-
-	ComboSelectToolbar::Init();
-}
-
-DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void DisplayCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "monitor_id";
-#elif __APPLE__
-	prop_name = "display_uuid";
-#else
-	is_int = true;
-	prop_name = "screen";
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-/* ========================================================================= */
-
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-
-	delete ui->deviceLabel;
-	delete ui->device;
-	ui->deviceLabel = nullptr;
-	ui->device = nullptr;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	active = obs_data_get_bool(settings, "active");
-
-	obs_module_t *mod = obs_get_module("win-dshow");
-	if (!mod)
-		return;
-
-	activateText = obs_module_get_locale_text(mod, "Activate");
-	deactivateText = obs_module_get_locale_text(mod, "Deactivate");
-
-	ui->activateButton->setText(active ? deactivateText : activateText);
-}
-
-DeviceCaptureToolbar::~DeviceCaptureToolbar() {}
-
-void DeviceCaptureToolbar::on_activateButton_clicked()
-{
-	OBSSource source = OBSGetStrongRef(weakSource);
-	if (!source) {
-		return;
-	}
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	bool now_active = obs_data_get_bool(settings, "active");
-
-	bool desyncedSetting = now_active != active;
-
-	active = !active;
-
-	const char *text = active ? deactivateText : activateText;
-	ui->activateButton->setText(text);
-
-	if (desyncedSetting) {
-		return;
-	}
-
-	calldata_t cd = {};
-	calldata_set_bool(&cd, "active", active);
-	proc_handler_t *ph = obs_source_get_proc_handler(source);
-	proc_handler_call(ph, "activate", &cd);
-	calldata_free(&cd);
-}
-
-/* ========================================================================= */
 
-GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_GameCaptureToolbar)
-{
-	obs_property_t *p;
-	int cur_idx;
-
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("win-capture");
-	if (!mod)
-		return;
-
-	ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
-	ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string cur_mode = obs_data_get_string(settings, "capture_mode");
-	std::string cur_window = obs_data_get_string(settings, "window");
-
-	ui->mode->blockSignals(true);
-	p = obs_properties_get(props.get(), "capture_mode");
-	cur_idx = FillPropertyCombo(ui->mode, p, cur_mode);
-	ui->mode->setCurrentIndex(cur_idx);
-	ui->mode->blockSignals(false);
-
-	ui->window->blockSignals(true);
-	p = obs_properties_get(props.get(), "window");
-	cur_idx = FillPropertyCombo(ui->window, p, cur_window);
-	ui->window->setCurrentIndex(cur_idx);
-	ui->window->blockSignals(false);
-
-	if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {
-		SetComboItemEnabled(ui->window, cur_idx, false);
-	}
-
-	UpdateWindowVisibility();
-}
-
-GameCaptureToolbar::~GameCaptureToolbar() {}
-
-void GameCaptureToolbar::UpdateWindowVisibility()
-{
-	QString mode = ui->mode->currentData().toString();
-	bool is_window = (mode == "window");
-	ui->windowLabel->setVisible(is_window);
-	ui->window->setVisible(is_window);
-}
-
-void GameCaptureToolbar::on_mode_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->mode->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-
-	UpdateWindowVisibility();
-}
-
-void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->window->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "window", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ImageSourceToolbar)
-{
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("image-source");
-	ui->pathLabel->setText(obs_module_get_locale_text(mod, "File"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string file = obs_data_get_string(settings, "file");
-
-	ui->path->setText(file.c_str());
-}
-
-ImageSourceToolbar::~ImageSourceToolbar() {}
-
-void ImageSourceToolbar::on_browse_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "file");
-	const char *desc = obs_property_description(p);
-	const char *filter = obs_property_path_filter(p);
-	const char *default_path = obs_property_path_default_path(p);
-
-	QString startDir = ui->path->text();
-	if (startDir.isEmpty())
-		startDir = default_path;
-
-	QString path = OpenFile(this, desc, startDir, filter);
-	if (path.isEmpty()) {
-		return;
-	}
-
-	ui->path->setText(path);
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "file", QT_TO_UTF8(path));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
+#include <QColorDialog>
 
-/* ========================================================================= */
+#include "moc_ColorSourceToolbar.cpp"
 
-static inline QColor color_from_int(long long val)
+QColor color_from_int(long long val)
 {
 	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
 }
 
-static inline long long color_to_int(QColor color)
+long long color_to_int(QColor color)
 {
 	auto shift = [&](unsigned val, int shift) {
 		return ((val & 0xff) << shift);
@@ -565,143 +81,3 @@ void ColorSourceToolbar::on_choose_clicked()
 
 	SetUndoProperties(source);
 }
-
-/* ========================================================================= */
-
-extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
-
-TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_TextSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-
-	const char *id = obs_source_get_unversioned_id(source);
-	bool ft2 = strcmp(id, "text_ft2_source") == 0;
-	bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
-
-	OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
-	MakeQFont(font_obj, font);
-
-	// Use "color1" if it's a freetype source and "color" elsewise
-	unsigned int val = (unsigned int)obs_data_get_int(
-		settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
-
-	color = color_from_int(val);
-
-	const char *text = obs_data_get_string(settings, "text");
-
-	bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
-	ui->emptySpace->setVisible(!single_line);
-	ui->text->setVisible(single_line);
-	if (single_line)
-		ui->text->setText(text);
-}
-
-TextSourceToolbar::~TextSourceToolbar() {}
-
-void TextSourceToolbar::on_selectFont_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	QFontDialog::FontDialogOptions options;
-	uint32_t flags;
-	bool success;
-
-#ifndef _WIN32
-	options = QFontDialog::DontUseNativeDialog;
-#endif
-
-	font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
-				    options);
-	if (!success) {
-		return;
-	}
-
-	OBSDataAutoRelease font_obj = obs_data_create();
-
-	obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family()));
-	obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName()));
-	obs_data_set_int(font_obj, "size", font.pointSize());
-	flags = font.bold() ? OBS_FONT_BOLD : 0;
-	flags |= font.italic() ? OBS_FONT_ITALIC : 0;
-	flags |= font.underline() ? OBS_FONT_UNDERLINE : 0;
-	flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;
-	obs_data_set_int(font_obj, "flags", flags);
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-
-	obs_data_set_obj(settings, "font", font_obj);
-
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_selectColor_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
-
-	obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
-
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (freetype) {
-		obs_data_set_int(settings, "color1", color_to_int(color));
-		obs_data_set_int(settings, "color2", color_to_int(color));
-	} else {
-		obs_data_set_int(settings, "color", color_to_int(color));
-	}
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_text_textChanged()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-	std::string newText = QT_TO_UTF8(ui->text->text());
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (newText == obs_data_get_string(settings, "text")) {
-		return;
-	}
-	SaveOldProperties(source);
-
-	obs_data_set_string(settings, "text", newText.c_str());
-	obs_source_update(source, nullptr);
-
-	SetUndoProperties(source, true);
-}

+ 1 - 158
frontend/components/ColorSourceToolbar.hpp

@@ -1,148 +1,8 @@
 #pragma once
 
-#include <memory>
-#include <obs.hpp>
-#include <QWidget>
+#include "SourceToolbar.hpp"
 
-class Ui_BrowserSourceToolbar;
-class Ui_DeviceSelectToolbar;
-class Ui_GameCaptureToolbar;
-class Ui_ImageSourceToolbar;
 class Ui_ColorSourceToolbar;
-class Ui_TextSourceToolbar;
-
-class SourceToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-protected:
-	using properties_delete_t = decltype(&obs_properties_destroy);
-	using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
-
-	properties_t props;
-	OBSDataAutoRelease oldData;
-
-	void SaveOldProperties(obs_source_t *source);
-	void SetUndoProperties(obs_source_t *source, bool repeatable = false);
-
-public:
-	SourceToolbar(QWidget *parent, OBSSource source);
-
-	OBSSource GetSource() { return OBSGetStrongRef(weakSource); }
-
-public slots:
-	virtual void Update() {}
-};
-
-class BrowserToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_BrowserSourceToolbar> ui;
-
-public:
-	BrowserToolbar(QWidget *parent, OBSSource source);
-	~BrowserToolbar();
-
-public slots:
-	void on_refresh_clicked();
-};
-
-class ComboSelectToolbar : public SourceToolbar {
-	Q_OBJECT
-
-protected:
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *prop_name;
-	bool is_int = false;
-
-public:
-	ComboSelectToolbar(QWidget *parent, OBSSource source);
-	~ComboSelectToolbar();
-	virtual void Init();
-
-public slots:
-	void on_device_currentIndexChanged(int idx);
-};
-
-class AudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	AudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class WindowCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	WindowCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DisplayCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DeviceCaptureToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *activateText;
-	const char *deactivateText;
-	bool active;
-
-public:
-	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	~DeviceCaptureToolbar();
-
-public slots:
-	void on_activateButton_clicked();
-};
-
-class GameCaptureToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_GameCaptureToolbar> ui;
-
-	void UpdateWindowVisibility();
-
-public:
-	GameCaptureToolbar(QWidget *parent, OBSSource source);
-	~GameCaptureToolbar();
-
-public slots:
-	void on_mode_currentIndexChanged(int idx);
-	void on_window_currentIndexChanged(int idx);
-};
-
-class ImageSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ImageSourceToolbar> ui;
-
-public:
-	ImageSourceToolbar(QWidget *parent, OBSSource source);
-	~ImageSourceToolbar();
-
-public slots:
-	void on_browse_clicked();
-};
 
 class ColorSourceToolbar : public SourceToolbar {
 	Q_OBJECT
@@ -159,20 +19,3 @@ public:
 public slots:
 	void on_choose_clicked();
 };
-
-class TextSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_TextSourceToolbar> ui;
-	QFont font;
-	QColor color;
-
-public:
-	TextSourceToolbar(QWidget *parent, OBSSource source);
-	~TextSourceToolbar();
-
-public slots:
-	void on_selectFont_clicked();
-	void on_selectColor_clicked();
-	void on_text_textChanged();
-};

+ 5 - 606
frontend/components/ComboSelectToolbar.cpp

@@ -1,110 +1,10 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
-
-#include "ui_browser-source-toolbar.h"
+#include "ComboSelectToolbar.hpp"
 #include "ui_device-select-toolbar.h"
-#include "ui_game-capture-toolbar.h"
-#include "ui_image-source-toolbar.h"
-#include "ui_color-source-toolbar.h"
-#include "ui_text-source-toolbar.h"
-
-#ifdef _WIN32
-#define get_os_module(win, mac, linux) obs_get_module(win)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)
-#elif __APPLE__
-#define get_os_module(win, mac, linux) obs_get_module(mac)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)
-#else
-#define get_os_module(win, mac, linux) obs_get_module(linux)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
-#endif
-
-/* ========================================================================= */
-
-SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  props(obs_source_properties(source), obs_properties_destroy)
-{
-}
-
-void SourceToolbar::SaveOldProperties(obs_source_t *source)
-{
-	oldData = obs_data_create();
-
-	OBSDataAutoRelease oldSettings = obs_source_get_settings(source);
-	obs_data_apply(oldData, oldSettings);
-	obs_data_set_string(oldData, "undo_suuid", obs_source_get_uuid(source));
-}
-
-void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
-{
-	if (!oldData) {
-		blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__);
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-
-	OBSSource currentSceneSource = main->GetCurrentSceneSource();
-	if (!currentSceneSource)
-		return;
-	std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
-	auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
-		OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
-		obs_source_reset_settings(source, settings);
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-
-		main->UpdateContextBar();
-	};
-
-	OBSDataAutoRelease new_settings = obs_data_create();
-	OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
-	obs_data_apply(new_settings, curr_settings);
-	obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
-
-	std::string undo_data(obs_data_get_json(oldData));
-	std::string redo_data(obs_data_get_json(new_settings));
-
-	if (undo_data.compare(redo_data) != 0)
-		main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
-					undo_data, redo_data, repeatable);
-
-	oldData = nullptr;
-}
-
-/* ========================================================================= */
-
-BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_BrowserSourceToolbar)
-{
-	ui->setupUi(this);
-}
-
-BrowserToolbar::~BrowserToolbar() {}
-
-void BrowserToolbar::on_refresh_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
 
-	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
-	obs_property_button_clicked(p, source.Get());
-}
+#include <OBSApp.hpp>
+#include <qt-wrappers.hpp>
 
-/* ========================================================================= */
+#include "moc_ComboSelectToolbar.cpp"
 
 ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
 	: SourceToolbar(parent, source),
@@ -115,7 +15,7 @@ ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
 
 ComboSelectToolbar::~ComboSelectToolbar() {}
 
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
+int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
 {
 	size_t count = obs_property_list_item_count(p);
 	int cur_idx = -1;
@@ -204,504 +104,3 @@ void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
 	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
 	SetUndoProperties(source);
 }
-
-AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void AudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "device_id";
-
-	ComboSelectToolbar::Init();
-}
-
-WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void WindowCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
-	ui->deviceLabel->setText(device_str);
-
-#if !defined(_WIN32) && !defined(__APPLE__) //linux
-	prop_name = "capture_window";
-#else
-	prop_name = "window";
-#endif
-
-#ifdef __APPLE__
-	is_int = true;
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void ApplicationAudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = obs_get_module("win-wasapi");
-	const char *device_str = obs_module_get_locale_text(mod, "Window");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "window";
-
-	ComboSelectToolbar::Init();
-}
-
-DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void DisplayCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "monitor_id";
-#elif __APPLE__
-	prop_name = "display_uuid";
-#else
-	is_int = true;
-	prop_name = "screen";
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-/* ========================================================================= */
-
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-
-	delete ui->deviceLabel;
-	delete ui->device;
-	ui->deviceLabel = nullptr;
-	ui->device = nullptr;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	active = obs_data_get_bool(settings, "active");
-
-	obs_module_t *mod = obs_get_module("win-dshow");
-	if (!mod)
-		return;
-
-	activateText = obs_module_get_locale_text(mod, "Activate");
-	deactivateText = obs_module_get_locale_text(mod, "Deactivate");
-
-	ui->activateButton->setText(active ? deactivateText : activateText);
-}
-
-DeviceCaptureToolbar::~DeviceCaptureToolbar() {}
-
-void DeviceCaptureToolbar::on_activateButton_clicked()
-{
-	OBSSource source = OBSGetStrongRef(weakSource);
-	if (!source) {
-		return;
-	}
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	bool now_active = obs_data_get_bool(settings, "active");
-
-	bool desyncedSetting = now_active != active;
-
-	active = !active;
-
-	const char *text = active ? deactivateText : activateText;
-	ui->activateButton->setText(text);
-
-	if (desyncedSetting) {
-		return;
-	}
-
-	calldata_t cd = {};
-	calldata_set_bool(&cd, "active", active);
-	proc_handler_t *ph = obs_source_get_proc_handler(source);
-	proc_handler_call(ph, "activate", &cd);
-	calldata_free(&cd);
-}
-
-/* ========================================================================= */
-
-GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_GameCaptureToolbar)
-{
-	obs_property_t *p;
-	int cur_idx;
-
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("win-capture");
-	if (!mod)
-		return;
-
-	ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
-	ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string cur_mode = obs_data_get_string(settings, "capture_mode");
-	std::string cur_window = obs_data_get_string(settings, "window");
-
-	ui->mode->blockSignals(true);
-	p = obs_properties_get(props.get(), "capture_mode");
-	cur_idx = FillPropertyCombo(ui->mode, p, cur_mode);
-	ui->mode->setCurrentIndex(cur_idx);
-	ui->mode->blockSignals(false);
-
-	ui->window->blockSignals(true);
-	p = obs_properties_get(props.get(), "window");
-	cur_idx = FillPropertyCombo(ui->window, p, cur_window);
-	ui->window->setCurrentIndex(cur_idx);
-	ui->window->blockSignals(false);
-
-	if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {
-		SetComboItemEnabled(ui->window, cur_idx, false);
-	}
-
-	UpdateWindowVisibility();
-}
-
-GameCaptureToolbar::~GameCaptureToolbar() {}
-
-void GameCaptureToolbar::UpdateWindowVisibility()
-{
-	QString mode = ui->mode->currentData().toString();
-	bool is_window = (mode == "window");
-	ui->windowLabel->setVisible(is_window);
-	ui->window->setVisible(is_window);
-}
-
-void GameCaptureToolbar::on_mode_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->mode->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-
-	UpdateWindowVisibility();
-}
-
-void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->window->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "window", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ImageSourceToolbar)
-{
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("image-source");
-	ui->pathLabel->setText(obs_module_get_locale_text(mod, "File"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string file = obs_data_get_string(settings, "file");
-
-	ui->path->setText(file.c_str());
-}
-
-ImageSourceToolbar::~ImageSourceToolbar() {}
-
-void ImageSourceToolbar::on_browse_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "file");
-	const char *desc = obs_property_description(p);
-	const char *filter = obs_property_path_filter(p);
-	const char *default_path = obs_property_path_default_path(p);
-
-	QString startDir = ui->path->text();
-	if (startDir.isEmpty())
-		startDir = default_path;
-
-	QString path = OpenFile(this, desc, startDir, filter);
-	if (path.isEmpty()) {
-		return;
-	}
-
-	ui->path->setText(path);
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "file", QT_TO_UTF8(path));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-static inline QColor color_from_int(long long val)
-{
-	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
-}
-
-static inline long long color_to_int(QColor color)
-{
-	auto shift = [&](unsigned val, int shift) {
-		return ((val & 0xff) << shift);
-	};
-
-	return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
-}
-
-ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ColorSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	unsigned int val = (unsigned int)obs_data_get_int(settings, "color");
-
-	color = color_from_int(val);
-	UpdateColor();
-}
-
-ColorSourceToolbar::~ColorSourceToolbar() {}
-
-void ColorSourceToolbar::UpdateColor()
-{
-	QPalette palette = QPalette(color);
-	ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
-	ui->color->setText(color.name(QColor::HexRgb));
-	ui->color->setPalette(palette);
-	ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
-					 .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
-					 .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
-	ui->color->setAutoFillBackground(true);
-	ui->color->setAlignment(Qt::AlignCenter);
-}
-
-void ColorSourceToolbar::on_choose_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "color");
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-	UpdateColor();
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_int(settings, "color", color_to_int(color));
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
-
-TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_TextSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-
-	const char *id = obs_source_get_unversioned_id(source);
-	bool ft2 = strcmp(id, "text_ft2_source") == 0;
-	bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
-
-	OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
-	MakeQFont(font_obj, font);
-
-	// Use "color1" if it's a freetype source and "color" elsewise
-	unsigned int val = (unsigned int)obs_data_get_int(
-		settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
-
-	color = color_from_int(val);
-
-	const char *text = obs_data_get_string(settings, "text");
-
-	bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
-	ui->emptySpace->setVisible(!single_line);
-	ui->text->setVisible(single_line);
-	if (single_line)
-		ui->text->setText(text);
-}
-
-TextSourceToolbar::~TextSourceToolbar() {}
-
-void TextSourceToolbar::on_selectFont_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	QFontDialog::FontDialogOptions options;
-	uint32_t flags;
-	bool success;
-
-#ifndef _WIN32
-	options = QFontDialog::DontUseNativeDialog;
-#endif
-
-	font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
-				    options);
-	if (!success) {
-		return;
-	}
-
-	OBSDataAutoRelease font_obj = obs_data_create();
-
-	obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family()));
-	obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName()));
-	obs_data_set_int(font_obj, "size", font.pointSize());
-	flags = font.bold() ? OBS_FONT_BOLD : 0;
-	flags |= font.italic() ? OBS_FONT_ITALIC : 0;
-	flags |= font.underline() ? OBS_FONT_UNDERLINE : 0;
-	flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;
-	obs_data_set_int(font_obj, "flags", flags);
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-
-	obs_data_set_obj(settings, "font", font_obj);
-
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_selectColor_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
-
-	obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
-
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (freetype) {
-		obs_data_set_int(settings, "color1", color_to_int(color));
-		obs_data_set_int(settings, "color2", color_to_int(color));
-	} else {
-		obs_data_set_int(settings, "color", color_to_int(color));
-	}
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_text_textChanged()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-	std::string newText = QT_TO_UTF8(ui->text->text());
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (newText == obs_data_get_string(settings, "text")) {
-		return;
-	}
-	SaveOldProperties(source);
-
-	obs_data_set_string(settings, "text", newText.c_str());
-	obs_source_update(source, nullptr);
-
-	SetUndoProperties(source, true);
-}

+ 1 - 157
frontend/components/ComboSelectToolbar.hpp

@@ -1,52 +1,8 @@
 #pragma once
 
-#include <memory>
-#include <obs.hpp>
-#include <QWidget>
+#include "SourceToolbar.hpp"
 
-class Ui_BrowserSourceToolbar;
 class Ui_DeviceSelectToolbar;
-class Ui_GameCaptureToolbar;
-class Ui_ImageSourceToolbar;
-class Ui_ColorSourceToolbar;
-class Ui_TextSourceToolbar;
-
-class SourceToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-protected:
-	using properties_delete_t = decltype(&obs_properties_destroy);
-	using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
-
-	properties_t props;
-	OBSDataAutoRelease oldData;
-
-	void SaveOldProperties(obs_source_t *source);
-	void SetUndoProperties(obs_source_t *source, bool repeatable = false);
-
-public:
-	SourceToolbar(QWidget *parent, OBSSource source);
-
-	OBSSource GetSource() { return OBSGetStrongRef(weakSource); }
-
-public slots:
-	virtual void Update() {}
-};
-
-class BrowserToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_BrowserSourceToolbar> ui;
-
-public:
-	BrowserToolbar(QWidget *parent, OBSSource source);
-	~BrowserToolbar();
-
-public slots:
-	void on_refresh_clicked();
-};
 
 class ComboSelectToolbar : public SourceToolbar {
 	Q_OBJECT
@@ -64,115 +20,3 @@ public:
 public slots:
 	void on_device_currentIndexChanged(int idx);
 };
-
-class AudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	AudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class WindowCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	WindowCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DisplayCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DeviceCaptureToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *activateText;
-	const char *deactivateText;
-	bool active;
-
-public:
-	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	~DeviceCaptureToolbar();
-
-public slots:
-	void on_activateButton_clicked();
-};
-
-class GameCaptureToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_GameCaptureToolbar> ui;
-
-	void UpdateWindowVisibility();
-
-public:
-	GameCaptureToolbar(QWidget *parent, OBSSource source);
-	~GameCaptureToolbar();
-
-public slots:
-	void on_mode_currentIndexChanged(int idx);
-	void on_window_currentIndexChanged(int idx);
-};
-
-class ImageSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ImageSourceToolbar> ui;
-
-public:
-	ImageSourceToolbar(QWidget *parent, OBSSource source);
-	~ImageSourceToolbar();
-
-public slots:
-	void on_browse_clicked();
-};
-
-class ColorSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ColorSourceToolbar> ui;
-	QColor color;
-
-	void UpdateColor();
-
-public:
-	ColorSourceToolbar(QWidget *parent, OBSSource source);
-	~ColorSourceToolbar();
-
-public slots:
-	void on_choose_clicked();
-};
-
-class TextSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_TextSourceToolbar> ui;
-	QFont font;
-	QColor color;
-
-public:
-	TextSourceToolbar(QWidget *parent, OBSSource source);
-	~TextSourceToolbar();
-
-public slots:
-	void on_selectFont_clicked();
-	void on_selectColor_clicked();
-	void on_text_textChanged();
-};

+ 2 - 651
frontend/components/DeviceCaptureToolbar.cpp

@@ -1,302 +1,6 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
-
-#include "ui_browser-source-toolbar.h"
+#include "DeviceCaptureToolbar.hpp"
 #include "ui_device-select-toolbar.h"
-#include "ui_game-capture-toolbar.h"
-#include "ui_image-source-toolbar.h"
-#include "ui_color-source-toolbar.h"
-#include "ui_text-source-toolbar.h"
-
-#ifdef _WIN32
-#define get_os_module(win, mac, linux) obs_get_module(win)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)
-#elif __APPLE__
-#define get_os_module(win, mac, linux) obs_get_module(mac)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)
-#else
-#define get_os_module(win, mac, linux) obs_get_module(linux)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
-#endif
-
-/* ========================================================================= */
-
-SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  props(obs_source_properties(source), obs_properties_destroy)
-{
-}
-
-void SourceToolbar::SaveOldProperties(obs_source_t *source)
-{
-	oldData = obs_data_create();
-
-	OBSDataAutoRelease oldSettings = obs_source_get_settings(source);
-	obs_data_apply(oldData, oldSettings);
-	obs_data_set_string(oldData, "undo_suuid", obs_source_get_uuid(source));
-}
-
-void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
-{
-	if (!oldData) {
-		blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__);
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-
-	OBSSource currentSceneSource = main->GetCurrentSceneSource();
-	if (!currentSceneSource)
-		return;
-	std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
-	auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
-		OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
-		obs_source_reset_settings(source, settings);
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-
-		main->UpdateContextBar();
-	};
-
-	OBSDataAutoRelease new_settings = obs_data_create();
-	OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
-	obs_data_apply(new_settings, curr_settings);
-	obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
-
-	std::string undo_data(obs_data_get_json(oldData));
-	std::string redo_data(obs_data_get_json(new_settings));
-
-	if (undo_data.compare(redo_data) != 0)
-		main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
-					undo_data, redo_data, repeatable);
-
-	oldData = nullptr;
-}
-
-/* ========================================================================= */
-
-BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_BrowserSourceToolbar)
-{
-	ui->setupUi(this);
-}
-
-BrowserToolbar::~BrowserToolbar() {}
-
-void BrowserToolbar::on_refresh_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
-	obs_property_button_clicked(p, source.Get());
-}
-
-/* ========================================================================= */
-
-ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-}
-
-ComboSelectToolbar::~ComboSelectToolbar() {}
-
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
-{
-	size_t count = obs_property_list_item_count(p);
-	int cur_idx = -1;
-
-	for (size_t i = 0; i < count; i++) {
-		const char *name = obs_property_list_item_name(p, i);
-		std::string id;
-
-		if (is_int) {
-			id = std::to_string(obs_property_list_item_int(p, i));
-		} else {
-			const char *val = obs_property_list_item_string(p, i);
-			id = val ? val : "";
-		}
-
-		if (cur_id == id)
-			cur_idx = (int)i;
-
-		c->addItem(name, id.c_str());
-	}
-
-	return cur_idx;
-}
-
-void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
-					const char *prop_name, bool is_int)
-{
-	std::string cur_id;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (is_int) {
-		cur_id = std::to_string(obs_data_get_int(settings, prop_name));
-	} else {
-		cur_id = obs_data_get_string(settings, prop_name);
-	}
-
-	combo->blockSignals(true);
-
-	obs_property_t *p = obs_properties_get(props, prop_name);
-	int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);
-
-	if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
-		if (cur_idx == -1) {
-			combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
-			cur_idx = 0;
-		}
-
-		SetComboItemEnabled(combo, cur_idx, false);
-	}
-
-	combo->setCurrentIndex(cur_idx);
-	combo->blockSignals(false);
-}
-
-void ComboSelectToolbar::Init()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
-}
-
-void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
-{
-	QString id = combo->itemData(idx).toString();
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (is_int) {
-		obs_data_set_int(settings, prop_name, id.toInt());
-	} else {
-		obs_data_set_string(settings, prop_name, QT_TO_UTF8(id));
-	}
-	obs_source_update(source, settings);
-}
-
-void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	SaveOldProperties(source);
-	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
-	SetUndoProperties(source);
-}
-
-AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void AudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "device_id";
-
-	ComboSelectToolbar::Init();
-}
-
-WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void WindowCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
-	ui->deviceLabel->setText(device_str);
-
-#if !defined(_WIN32) && !defined(__APPLE__) //linux
-	prop_name = "capture_window";
-#else
-	prop_name = "window";
-#endif
-
-#ifdef __APPLE__
-	is_int = true;
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void ApplicationAudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = obs_get_module("win-wasapi");
-	const char *device_str = obs_module_get_locale_text(mod, "Window");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "window";
-
-	ComboSelectToolbar::Init();
-}
-
-DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void DisplayCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "monitor_id";
-#elif __APPLE__
-	prop_name = "display_uuid";
-#else
-	is_int = true;
-	prop_name = "screen";
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-/* ========================================================================= */
+#include "moc_DeviceCaptureToolbar.cpp"
 
 DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
 	: QWidget(parent),
@@ -352,356 +56,3 @@ void DeviceCaptureToolbar::on_activateButton_clicked()
 	proc_handler_call(ph, "activate", &cd);
 	calldata_free(&cd);
 }
-
-/* ========================================================================= */
-
-GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_GameCaptureToolbar)
-{
-	obs_property_t *p;
-	int cur_idx;
-
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("win-capture");
-	if (!mod)
-		return;
-
-	ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
-	ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string cur_mode = obs_data_get_string(settings, "capture_mode");
-	std::string cur_window = obs_data_get_string(settings, "window");
-
-	ui->mode->blockSignals(true);
-	p = obs_properties_get(props.get(), "capture_mode");
-	cur_idx = FillPropertyCombo(ui->mode, p, cur_mode);
-	ui->mode->setCurrentIndex(cur_idx);
-	ui->mode->blockSignals(false);
-
-	ui->window->blockSignals(true);
-	p = obs_properties_get(props.get(), "window");
-	cur_idx = FillPropertyCombo(ui->window, p, cur_window);
-	ui->window->setCurrentIndex(cur_idx);
-	ui->window->blockSignals(false);
-
-	if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {
-		SetComboItemEnabled(ui->window, cur_idx, false);
-	}
-
-	UpdateWindowVisibility();
-}
-
-GameCaptureToolbar::~GameCaptureToolbar() {}
-
-void GameCaptureToolbar::UpdateWindowVisibility()
-{
-	QString mode = ui->mode->currentData().toString();
-	bool is_window = (mode == "window");
-	ui->windowLabel->setVisible(is_window);
-	ui->window->setVisible(is_window);
-}
-
-void GameCaptureToolbar::on_mode_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->mode->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-
-	UpdateWindowVisibility();
-}
-
-void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->window->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "window", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ImageSourceToolbar)
-{
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("image-source");
-	ui->pathLabel->setText(obs_module_get_locale_text(mod, "File"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string file = obs_data_get_string(settings, "file");
-
-	ui->path->setText(file.c_str());
-}
-
-ImageSourceToolbar::~ImageSourceToolbar() {}
-
-void ImageSourceToolbar::on_browse_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "file");
-	const char *desc = obs_property_description(p);
-	const char *filter = obs_property_path_filter(p);
-	const char *default_path = obs_property_path_default_path(p);
-
-	QString startDir = ui->path->text();
-	if (startDir.isEmpty())
-		startDir = default_path;
-
-	QString path = OpenFile(this, desc, startDir, filter);
-	if (path.isEmpty()) {
-		return;
-	}
-
-	ui->path->setText(path);
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "file", QT_TO_UTF8(path));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-static inline QColor color_from_int(long long val)
-{
-	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
-}
-
-static inline long long color_to_int(QColor color)
-{
-	auto shift = [&](unsigned val, int shift) {
-		return ((val & 0xff) << shift);
-	};
-
-	return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
-}
-
-ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ColorSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	unsigned int val = (unsigned int)obs_data_get_int(settings, "color");
-
-	color = color_from_int(val);
-	UpdateColor();
-}
-
-ColorSourceToolbar::~ColorSourceToolbar() {}
-
-void ColorSourceToolbar::UpdateColor()
-{
-	QPalette palette = QPalette(color);
-	ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
-	ui->color->setText(color.name(QColor::HexRgb));
-	ui->color->setPalette(palette);
-	ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
-					 .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
-					 .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
-	ui->color->setAutoFillBackground(true);
-	ui->color->setAlignment(Qt::AlignCenter);
-}
-
-void ColorSourceToolbar::on_choose_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "color");
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-	UpdateColor();
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_int(settings, "color", color_to_int(color));
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
-
-TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_TextSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-
-	const char *id = obs_source_get_unversioned_id(source);
-	bool ft2 = strcmp(id, "text_ft2_source") == 0;
-	bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
-
-	OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
-	MakeQFont(font_obj, font);
-
-	// Use "color1" if it's a freetype source and "color" elsewise
-	unsigned int val = (unsigned int)obs_data_get_int(
-		settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
-
-	color = color_from_int(val);
-
-	const char *text = obs_data_get_string(settings, "text");
-
-	bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
-	ui->emptySpace->setVisible(!single_line);
-	ui->text->setVisible(single_line);
-	if (single_line)
-		ui->text->setText(text);
-}
-
-TextSourceToolbar::~TextSourceToolbar() {}
-
-void TextSourceToolbar::on_selectFont_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	QFontDialog::FontDialogOptions options;
-	uint32_t flags;
-	bool success;
-
-#ifndef _WIN32
-	options = QFontDialog::DontUseNativeDialog;
-#endif
-
-	font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
-				    options);
-	if (!success) {
-		return;
-	}
-
-	OBSDataAutoRelease font_obj = obs_data_create();
-
-	obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family()));
-	obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName()));
-	obs_data_set_int(font_obj, "size", font.pointSize());
-	flags = font.bold() ? OBS_FONT_BOLD : 0;
-	flags |= font.italic() ? OBS_FONT_ITALIC : 0;
-	flags |= font.underline() ? OBS_FONT_UNDERLINE : 0;
-	flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;
-	obs_data_set_int(font_obj, "flags", flags);
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-
-	obs_data_set_obj(settings, "font", font_obj);
-
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_selectColor_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
-
-	obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
-
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (freetype) {
-		obs_data_set_int(settings, "color1", color_to_int(color));
-		obs_data_set_int(settings, "color2", color_to_int(color));
-	} else {
-		obs_data_set_int(settings, "color", color_to_int(color));
-	}
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_text_textChanged()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-	std::string newText = QT_TO_UTF8(ui->text->text());
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (newText == obs_data_get_string(settings, "text")) {
-		return;
-	}
-	SaveOldProperties(source);
-
-	obs_data_set_string(settings, "text", newText.c_str());
-	obs_source_update(source, nullptr);
-
-	SetUndoProperties(source, true);
-}

+ 1 - 154
frontend/components/DeviceCaptureToolbar.hpp

@@ -1,101 +1,10 @@
 #pragma once
 
-#include <memory>
 #include <obs.hpp>
+
 #include <QWidget>
 
-class Ui_BrowserSourceToolbar;
 class Ui_DeviceSelectToolbar;
-class Ui_GameCaptureToolbar;
-class Ui_ImageSourceToolbar;
-class Ui_ColorSourceToolbar;
-class Ui_TextSourceToolbar;
-
-class SourceToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-protected:
-	using properties_delete_t = decltype(&obs_properties_destroy);
-	using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
-
-	properties_t props;
-	OBSDataAutoRelease oldData;
-
-	void SaveOldProperties(obs_source_t *source);
-	void SetUndoProperties(obs_source_t *source, bool repeatable = false);
-
-public:
-	SourceToolbar(QWidget *parent, OBSSource source);
-
-	OBSSource GetSource() { return OBSGetStrongRef(weakSource); }
-
-public slots:
-	virtual void Update() {}
-};
-
-class BrowserToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_BrowserSourceToolbar> ui;
-
-public:
-	BrowserToolbar(QWidget *parent, OBSSource source);
-	~BrowserToolbar();
-
-public slots:
-	void on_refresh_clicked();
-};
-
-class ComboSelectToolbar : public SourceToolbar {
-	Q_OBJECT
-
-protected:
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *prop_name;
-	bool is_int = false;
-
-public:
-	ComboSelectToolbar(QWidget *parent, OBSSource source);
-	~ComboSelectToolbar();
-	virtual void Init();
-
-public slots:
-	void on_device_currentIndexChanged(int idx);
-};
-
-class AudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	AudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class WindowCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	WindowCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DisplayCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
 
 class DeviceCaptureToolbar : public QWidget {
 	Q_OBJECT
@@ -114,65 +23,3 @@ public:
 public slots:
 	void on_activateButton_clicked();
 };
-
-class GameCaptureToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_GameCaptureToolbar> ui;
-
-	void UpdateWindowVisibility();
-
-public:
-	GameCaptureToolbar(QWidget *parent, OBSSource source);
-	~GameCaptureToolbar();
-
-public slots:
-	void on_mode_currentIndexChanged(int idx);
-	void on_window_currentIndexChanged(int idx);
-};
-
-class ImageSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ImageSourceToolbar> ui;
-
-public:
-	ImageSourceToolbar(QWidget *parent, OBSSource source);
-	~ImageSourceToolbar();
-
-public slots:
-	void on_browse_clicked();
-};
-
-class ColorSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ColorSourceToolbar> ui;
-	QColor color;
-
-	void UpdateColor();
-
-public:
-	ColorSourceToolbar(QWidget *parent, OBSSource source);
-	~ColorSourceToolbar();
-
-public slots:
-	void on_choose_clicked();
-};
-
-class TextSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_TextSourceToolbar> ui;
-	QFont font;
-	QColor color;
-
-public:
-	TextSourceToolbar(QWidget *parent, OBSSource source);
-	~TextSourceToolbar();
-
-public slots:
-	void on_selectFont_clicked();
-	void on_selectColor_clicked();
-	void on_text_textChanged();
-};

+ 2 - 669
frontend/components/DisplayCaptureToolbar.cpp

@@ -1,18 +1,6 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
-
-#include "ui_browser-source-toolbar.h"
+#include "DisplayCaptureToolbar.hpp"
 #include "ui_device-select-toolbar.h"
-#include "ui_game-capture-toolbar.h"
-#include "ui_image-source-toolbar.h"
-#include "ui_color-source-toolbar.h"
-#include "ui_text-source-toolbar.h"
+#include "moc_DisplayCaptureToolbar.cpp"
 
 #ifdef _WIN32
 #define get_os_module(win, mac, linux) obs_get_module(win)
@@ -25,251 +13,6 @@
 #define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
 #endif
 
-/* ========================================================================= */
-
-SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  props(obs_source_properties(source), obs_properties_destroy)
-{
-}
-
-void SourceToolbar::SaveOldProperties(obs_source_t *source)
-{
-	oldData = obs_data_create();
-
-	OBSDataAutoRelease oldSettings = obs_source_get_settings(source);
-	obs_data_apply(oldData, oldSettings);
-	obs_data_set_string(oldData, "undo_suuid", obs_source_get_uuid(source));
-}
-
-void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
-{
-	if (!oldData) {
-		blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__);
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-
-	OBSSource currentSceneSource = main->GetCurrentSceneSource();
-	if (!currentSceneSource)
-		return;
-	std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
-	auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
-		OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
-		obs_source_reset_settings(source, settings);
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-
-		main->UpdateContextBar();
-	};
-
-	OBSDataAutoRelease new_settings = obs_data_create();
-	OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
-	obs_data_apply(new_settings, curr_settings);
-	obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
-
-	std::string undo_data(obs_data_get_json(oldData));
-	std::string redo_data(obs_data_get_json(new_settings));
-
-	if (undo_data.compare(redo_data) != 0)
-		main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
-					undo_data, redo_data, repeatable);
-
-	oldData = nullptr;
-}
-
-/* ========================================================================= */
-
-BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_BrowserSourceToolbar)
-{
-	ui->setupUi(this);
-}
-
-BrowserToolbar::~BrowserToolbar() {}
-
-void BrowserToolbar::on_refresh_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
-	obs_property_button_clicked(p, source.Get());
-}
-
-/* ========================================================================= */
-
-ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-}
-
-ComboSelectToolbar::~ComboSelectToolbar() {}
-
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
-{
-	size_t count = obs_property_list_item_count(p);
-	int cur_idx = -1;
-
-	for (size_t i = 0; i < count; i++) {
-		const char *name = obs_property_list_item_name(p, i);
-		std::string id;
-
-		if (is_int) {
-			id = std::to_string(obs_property_list_item_int(p, i));
-		} else {
-			const char *val = obs_property_list_item_string(p, i);
-			id = val ? val : "";
-		}
-
-		if (cur_id == id)
-			cur_idx = (int)i;
-
-		c->addItem(name, id.c_str());
-	}
-
-	return cur_idx;
-}
-
-void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
-					const char *prop_name, bool is_int)
-{
-	std::string cur_id;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (is_int) {
-		cur_id = std::to_string(obs_data_get_int(settings, prop_name));
-	} else {
-		cur_id = obs_data_get_string(settings, prop_name);
-	}
-
-	combo->blockSignals(true);
-
-	obs_property_t *p = obs_properties_get(props, prop_name);
-	int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);
-
-	if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
-		if (cur_idx == -1) {
-			combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
-			cur_idx = 0;
-		}
-
-		SetComboItemEnabled(combo, cur_idx, false);
-	}
-
-	combo->setCurrentIndex(cur_idx);
-	combo->blockSignals(false);
-}
-
-void ComboSelectToolbar::Init()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
-}
-
-void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
-{
-	QString id = combo->itemData(idx).toString();
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (is_int) {
-		obs_data_set_int(settings, prop_name, id.toInt());
-	} else {
-		obs_data_set_string(settings, prop_name, QT_TO_UTF8(id));
-	}
-	obs_source_update(source, settings);
-}
-
-void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	SaveOldProperties(source);
-	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
-	SetUndoProperties(source);
-}
-
-AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void AudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "device_id";
-
-	ComboSelectToolbar::Init();
-}
-
-WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void WindowCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
-	ui->deviceLabel->setText(device_str);
-
-#if !defined(_WIN32) && !defined(__APPLE__) //linux
-	prop_name = "capture_window";
-#else
-	prop_name = "window";
-#endif
-
-#ifdef __APPLE__
-	is_int = true;
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void ApplicationAudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = obs_get_module("win-wasapi");
-	const char *device_str = obs_module_get_locale_text(mod, "Window");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "window";
-
-	ComboSelectToolbar::Init();
-}
-
 DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
 
 void DisplayCaptureToolbar::Init()
@@ -295,413 +38,3 @@ void DisplayCaptureToolbar::Init()
 
 	ComboSelectToolbar::Init();
 }
-
-/* ========================================================================= */
-
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-
-	delete ui->deviceLabel;
-	delete ui->device;
-	ui->deviceLabel = nullptr;
-	ui->device = nullptr;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	active = obs_data_get_bool(settings, "active");
-
-	obs_module_t *mod = obs_get_module("win-dshow");
-	if (!mod)
-		return;
-
-	activateText = obs_module_get_locale_text(mod, "Activate");
-	deactivateText = obs_module_get_locale_text(mod, "Deactivate");
-
-	ui->activateButton->setText(active ? deactivateText : activateText);
-}
-
-DeviceCaptureToolbar::~DeviceCaptureToolbar() {}
-
-void DeviceCaptureToolbar::on_activateButton_clicked()
-{
-	OBSSource source = OBSGetStrongRef(weakSource);
-	if (!source) {
-		return;
-	}
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	bool now_active = obs_data_get_bool(settings, "active");
-
-	bool desyncedSetting = now_active != active;
-
-	active = !active;
-
-	const char *text = active ? deactivateText : activateText;
-	ui->activateButton->setText(text);
-
-	if (desyncedSetting) {
-		return;
-	}
-
-	calldata_t cd = {};
-	calldata_set_bool(&cd, "active", active);
-	proc_handler_t *ph = obs_source_get_proc_handler(source);
-	proc_handler_call(ph, "activate", &cd);
-	calldata_free(&cd);
-}
-
-/* ========================================================================= */
-
-GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_GameCaptureToolbar)
-{
-	obs_property_t *p;
-	int cur_idx;
-
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("win-capture");
-	if (!mod)
-		return;
-
-	ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
-	ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string cur_mode = obs_data_get_string(settings, "capture_mode");
-	std::string cur_window = obs_data_get_string(settings, "window");
-
-	ui->mode->blockSignals(true);
-	p = obs_properties_get(props.get(), "capture_mode");
-	cur_idx = FillPropertyCombo(ui->mode, p, cur_mode);
-	ui->mode->setCurrentIndex(cur_idx);
-	ui->mode->blockSignals(false);
-
-	ui->window->blockSignals(true);
-	p = obs_properties_get(props.get(), "window");
-	cur_idx = FillPropertyCombo(ui->window, p, cur_window);
-	ui->window->setCurrentIndex(cur_idx);
-	ui->window->blockSignals(false);
-
-	if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {
-		SetComboItemEnabled(ui->window, cur_idx, false);
-	}
-
-	UpdateWindowVisibility();
-}
-
-GameCaptureToolbar::~GameCaptureToolbar() {}
-
-void GameCaptureToolbar::UpdateWindowVisibility()
-{
-	QString mode = ui->mode->currentData().toString();
-	bool is_window = (mode == "window");
-	ui->windowLabel->setVisible(is_window);
-	ui->window->setVisible(is_window);
-}
-
-void GameCaptureToolbar::on_mode_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->mode->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-
-	UpdateWindowVisibility();
-}
-
-void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->window->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "window", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ImageSourceToolbar)
-{
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("image-source");
-	ui->pathLabel->setText(obs_module_get_locale_text(mod, "File"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string file = obs_data_get_string(settings, "file");
-
-	ui->path->setText(file.c_str());
-}
-
-ImageSourceToolbar::~ImageSourceToolbar() {}
-
-void ImageSourceToolbar::on_browse_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "file");
-	const char *desc = obs_property_description(p);
-	const char *filter = obs_property_path_filter(p);
-	const char *default_path = obs_property_path_default_path(p);
-
-	QString startDir = ui->path->text();
-	if (startDir.isEmpty())
-		startDir = default_path;
-
-	QString path = OpenFile(this, desc, startDir, filter);
-	if (path.isEmpty()) {
-		return;
-	}
-
-	ui->path->setText(path);
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "file", QT_TO_UTF8(path));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-static inline QColor color_from_int(long long val)
-{
-	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
-}
-
-static inline long long color_to_int(QColor color)
-{
-	auto shift = [&](unsigned val, int shift) {
-		return ((val & 0xff) << shift);
-	};
-
-	return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
-}
-
-ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ColorSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	unsigned int val = (unsigned int)obs_data_get_int(settings, "color");
-
-	color = color_from_int(val);
-	UpdateColor();
-}
-
-ColorSourceToolbar::~ColorSourceToolbar() {}
-
-void ColorSourceToolbar::UpdateColor()
-{
-	QPalette palette = QPalette(color);
-	ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
-	ui->color->setText(color.name(QColor::HexRgb));
-	ui->color->setPalette(palette);
-	ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
-					 .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
-					 .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
-	ui->color->setAutoFillBackground(true);
-	ui->color->setAlignment(Qt::AlignCenter);
-}
-
-void ColorSourceToolbar::on_choose_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "color");
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-	UpdateColor();
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_int(settings, "color", color_to_int(color));
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
-
-TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_TextSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-
-	const char *id = obs_source_get_unversioned_id(source);
-	bool ft2 = strcmp(id, "text_ft2_source") == 0;
-	bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
-
-	OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
-	MakeQFont(font_obj, font);
-
-	// Use "color1" if it's a freetype source and "color" elsewise
-	unsigned int val = (unsigned int)obs_data_get_int(
-		settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
-
-	color = color_from_int(val);
-
-	const char *text = obs_data_get_string(settings, "text");
-
-	bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
-	ui->emptySpace->setVisible(!single_line);
-	ui->text->setVisible(single_line);
-	if (single_line)
-		ui->text->setText(text);
-}
-
-TextSourceToolbar::~TextSourceToolbar() {}
-
-void TextSourceToolbar::on_selectFont_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	QFontDialog::FontDialogOptions options;
-	uint32_t flags;
-	bool success;
-
-#ifndef _WIN32
-	options = QFontDialog::DontUseNativeDialog;
-#endif
-
-	font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
-				    options);
-	if (!success) {
-		return;
-	}
-
-	OBSDataAutoRelease font_obj = obs_data_create();
-
-	obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family()));
-	obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName()));
-	obs_data_set_int(font_obj, "size", font.pointSize());
-	flags = font.bold() ? OBS_FONT_BOLD : 0;
-	flags |= font.italic() ? OBS_FONT_ITALIC : 0;
-	flags |= font.underline() ? OBS_FONT_UNDERLINE : 0;
-	flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;
-	obs_data_set_int(font_obj, "flags", flags);
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-
-	obs_data_set_obj(settings, "font", font_obj);
-
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_selectColor_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
-
-	obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
-
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (freetype) {
-		obs_data_set_int(settings, "color1", color_to_int(color));
-		obs_data_set_int(settings, "color2", color_to_int(color));
-	} else {
-		obs_data_set_int(settings, "color", color_to_int(color));
-	}
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_text_textChanged()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-	std::string newText = QT_TO_UTF8(ui->text->text());
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (newText == obs_data_get_string(settings, "text")) {
-		return;
-	}
-	SaveOldProperties(source);
-
-	obs_data_set_string(settings, "text", newText.c_str());
-	obs_source_update(source, nullptr);
-
-	SetUndoProperties(source, true);
-}

+ 1 - 168
frontend/components/DisplayCaptureToolbar.hpp

@@ -1,93 +1,6 @@
 #pragma once
 
-#include <memory>
-#include <obs.hpp>
-#include <QWidget>
-
-class Ui_BrowserSourceToolbar;
-class Ui_DeviceSelectToolbar;
-class Ui_GameCaptureToolbar;
-class Ui_ImageSourceToolbar;
-class Ui_ColorSourceToolbar;
-class Ui_TextSourceToolbar;
-
-class SourceToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-protected:
-	using properties_delete_t = decltype(&obs_properties_destroy);
-	using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
-
-	properties_t props;
-	OBSDataAutoRelease oldData;
-
-	void SaveOldProperties(obs_source_t *source);
-	void SetUndoProperties(obs_source_t *source, bool repeatable = false);
-
-public:
-	SourceToolbar(QWidget *parent, OBSSource source);
-
-	OBSSource GetSource() { return OBSGetStrongRef(weakSource); }
-
-public slots:
-	virtual void Update() {}
-};
-
-class BrowserToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_BrowserSourceToolbar> ui;
-
-public:
-	BrowserToolbar(QWidget *parent, OBSSource source);
-	~BrowserToolbar();
-
-public slots:
-	void on_refresh_clicked();
-};
-
-class ComboSelectToolbar : public SourceToolbar {
-	Q_OBJECT
-
-protected:
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *prop_name;
-	bool is_int = false;
-
-public:
-	ComboSelectToolbar(QWidget *parent, OBSSource source);
-	~ComboSelectToolbar();
-	virtual void Init();
-
-public slots:
-	void on_device_currentIndexChanged(int idx);
-};
-
-class AudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	AudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class WindowCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	WindowCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
+#include "ComboSelectToolbar.hpp"
 
 class DisplayCaptureToolbar : public ComboSelectToolbar {
 	Q_OBJECT
@@ -96,83 +9,3 @@ public:
 	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
 	void Init() override;
 };
-
-class DeviceCaptureToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *activateText;
-	const char *deactivateText;
-	bool active;
-
-public:
-	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	~DeviceCaptureToolbar();
-
-public slots:
-	void on_activateButton_clicked();
-};
-
-class GameCaptureToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_GameCaptureToolbar> ui;
-
-	void UpdateWindowVisibility();
-
-public:
-	GameCaptureToolbar(QWidget *parent, OBSSource source);
-	~GameCaptureToolbar();
-
-public slots:
-	void on_mode_currentIndexChanged(int idx);
-	void on_window_currentIndexChanged(int idx);
-};
-
-class ImageSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ImageSourceToolbar> ui;
-
-public:
-	ImageSourceToolbar(QWidget *parent, OBSSource source);
-	~ImageSourceToolbar();
-
-public slots:
-	void on_browse_clicked();
-};
-
-class ColorSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ColorSourceToolbar> ui;
-	QColor color;
-
-	void UpdateColor();
-
-public:
-	ColorSourceToolbar(QWidget *parent, OBSSource source);
-	~ColorSourceToolbar();
-
-public slots:
-	void on_choose_clicked();
-};
-
-class TextSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_TextSourceToolbar> ui;
-	QFont font;
-	QColor color;
-
-public:
-	TextSourceToolbar(QWidget *parent, OBSSource source);
-	~TextSourceToolbar();
-
-public slots:
-	void on_selectFont_clicked();
-	void on_selectColor_clicked();
-	void on_text_textChanged();
-};

+ 4 - 620
frontend/components/GameCaptureToolbar.cpp

@@ -1,359 +1,11 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
-
-#include "ui_browser-source-toolbar.h"
-#include "ui_device-select-toolbar.h"
+#include "GameCaptureToolbar.hpp"
 #include "ui_game-capture-toolbar.h"
-#include "ui_image-source-toolbar.h"
-#include "ui_color-source-toolbar.h"
-#include "ui_text-source-toolbar.h"
-
-#ifdef _WIN32
-#define get_os_module(win, mac, linux) obs_get_module(win)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)
-#elif __APPLE__
-#define get_os_module(win, mac, linux) obs_get_module(mac)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)
-#else
-#define get_os_module(win, mac, linux) obs_get_module(linux)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
-#endif
-
-/* ========================================================================= */
-
-SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  props(obs_source_properties(source), obs_properties_destroy)
-{
-}
-
-void SourceToolbar::SaveOldProperties(obs_source_t *source)
-{
-	oldData = obs_data_create();
-
-	OBSDataAutoRelease oldSettings = obs_source_get_settings(source);
-	obs_data_apply(oldData, oldSettings);
-	obs_data_set_string(oldData, "undo_suuid", obs_source_get_uuid(source));
-}
-
-void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
-{
-	if (!oldData) {
-		blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__);
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-
-	OBSSource currentSceneSource = main->GetCurrentSceneSource();
-	if (!currentSceneSource)
-		return;
-	std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
-	auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
-		OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
-		obs_source_reset_settings(source, settings);
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-
-		main->UpdateContextBar();
-	};
-
-	OBSDataAutoRelease new_settings = obs_data_create();
-	OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
-	obs_data_apply(new_settings, curr_settings);
-	obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
-
-	std::string undo_data(obs_data_get_json(oldData));
-	std::string redo_data(obs_data_get_json(new_settings));
-
-	if (undo_data.compare(redo_data) != 0)
-		main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
-					undo_data, redo_data, repeatable);
-
-	oldData = nullptr;
-}
-
-/* ========================================================================= */
-
-BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_BrowserSourceToolbar)
-{
-	ui->setupUi(this);
-}
-
-BrowserToolbar::~BrowserToolbar() {}
-
-void BrowserToolbar::on_refresh_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
-	obs_property_button_clicked(p, source.Get());
-}
-
-/* ========================================================================= */
-
-ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-}
-
-ComboSelectToolbar::~ComboSelectToolbar() {}
-
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
-{
-	size_t count = obs_property_list_item_count(p);
-	int cur_idx = -1;
-
-	for (size_t i = 0; i < count; i++) {
-		const char *name = obs_property_list_item_name(p, i);
-		std::string id;
-
-		if (is_int) {
-			id = std::to_string(obs_property_list_item_int(p, i));
-		} else {
-			const char *val = obs_property_list_item_string(p, i);
-			id = val ? val : "";
-		}
-
-		if (cur_id == id)
-			cur_idx = (int)i;
-
-		c->addItem(name, id.c_str());
-	}
-
-	return cur_idx;
-}
-
-void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
-					const char *prop_name, bool is_int)
-{
-	std::string cur_id;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (is_int) {
-		cur_id = std::to_string(obs_data_get_int(settings, prop_name));
-	} else {
-		cur_id = obs_data_get_string(settings, prop_name);
-	}
-
-	combo->blockSignals(true);
-
-	obs_property_t *p = obs_properties_get(props, prop_name);
-	int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);
-
-	if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
-		if (cur_idx == -1) {
-			combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
-			cur_idx = 0;
-		}
-
-		SetComboItemEnabled(combo, cur_idx, false);
-	}
-
-	combo->setCurrentIndex(cur_idx);
-	combo->blockSignals(false);
-}
-
-void ComboSelectToolbar::Init()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
-}
-
-void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
-{
-	QString id = combo->itemData(idx).toString();
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (is_int) {
-		obs_data_set_int(settings, prop_name, id.toInt());
-	} else {
-		obs_data_set_string(settings, prop_name, QT_TO_UTF8(id));
-	}
-	obs_source_update(source, settings);
-}
-
-void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	SaveOldProperties(source);
-	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
-	SetUndoProperties(source);
-}
-
-AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void AudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "device_id";
-
-	ComboSelectToolbar::Init();
-}
-
-WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void WindowCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
-	ui->deviceLabel->setText(device_str);
-
-#if !defined(_WIN32) && !defined(__APPLE__) //linux
-	prop_name = "capture_window";
-#else
-	prop_name = "window";
-#endif
-
-#ifdef __APPLE__
-	is_int = true;
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void ApplicationAudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = obs_get_module("win-wasapi");
-	const char *device_str = obs_module_get_locale_text(mod, "Window");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "window";
-
-	ComboSelectToolbar::Init();
-}
-
-DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void DisplayCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "monitor_id";
-#elif __APPLE__
-	prop_name = "display_uuid";
-#else
-	is_int = true;
-	prop_name = "screen";
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-/* ========================================================================= */
-
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-
-	delete ui->deviceLabel;
-	delete ui->device;
-	ui->deviceLabel = nullptr;
-	ui->device = nullptr;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	active = obs_data_get_bool(settings, "active");
-
-	obs_module_t *mod = obs_get_module("win-dshow");
-	if (!mod)
-		return;
-
-	activateText = obs_module_get_locale_text(mod, "Activate");
-	deactivateText = obs_module_get_locale_text(mod, "Deactivate");
-
-	ui->activateButton->setText(active ? deactivateText : activateText);
-}
-
-DeviceCaptureToolbar::~DeviceCaptureToolbar() {}
-
-void DeviceCaptureToolbar::on_activateButton_clicked()
-{
-	OBSSource source = OBSGetStrongRef(weakSource);
-	if (!source) {
-		return;
-	}
 
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	bool now_active = obs_data_get_bool(settings, "active");
-
-	bool desyncedSetting = now_active != active;
-
-	active = !active;
-
-	const char *text = active ? deactivateText : activateText;
-	ui->activateButton->setText(text);
-
-	if (desyncedSetting) {
-		return;
-	}
+#include <qt-wrappers.hpp>
 
-	calldata_t cd = {};
-	calldata_set_bool(&cd, "active", active);
-	proc_handler_t *ph = obs_source_get_proc_handler(source);
-	proc_handler_call(ph, "activate", &cd);
-	calldata_free(&cd);
-}
+#include "moc_GameCaptureToolbar.cpp"
 
-/* ========================================================================= */
+extern int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false);
 
 GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
 	: SourceToolbar(parent, source),
@@ -437,271 +89,3 @@ void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
 	obs_source_update(source, settings);
 	SetUndoProperties(source);
 }
-
-/* ========================================================================= */
-
-ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ImageSourceToolbar)
-{
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("image-source");
-	ui->pathLabel->setText(obs_module_get_locale_text(mod, "File"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string file = obs_data_get_string(settings, "file");
-
-	ui->path->setText(file.c_str());
-}
-
-ImageSourceToolbar::~ImageSourceToolbar() {}
-
-void ImageSourceToolbar::on_browse_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "file");
-	const char *desc = obs_property_description(p);
-	const char *filter = obs_property_path_filter(p);
-	const char *default_path = obs_property_path_default_path(p);
-
-	QString startDir = ui->path->text();
-	if (startDir.isEmpty())
-		startDir = default_path;
-
-	QString path = OpenFile(this, desc, startDir, filter);
-	if (path.isEmpty()) {
-		return;
-	}
-
-	ui->path->setText(path);
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "file", QT_TO_UTF8(path));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-static inline QColor color_from_int(long long val)
-{
-	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
-}
-
-static inline long long color_to_int(QColor color)
-{
-	auto shift = [&](unsigned val, int shift) {
-		return ((val & 0xff) << shift);
-	};
-
-	return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
-}
-
-ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ColorSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	unsigned int val = (unsigned int)obs_data_get_int(settings, "color");
-
-	color = color_from_int(val);
-	UpdateColor();
-}
-
-ColorSourceToolbar::~ColorSourceToolbar() {}
-
-void ColorSourceToolbar::UpdateColor()
-{
-	QPalette palette = QPalette(color);
-	ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
-	ui->color->setText(color.name(QColor::HexRgb));
-	ui->color->setPalette(palette);
-	ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
-					 .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
-					 .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
-	ui->color->setAutoFillBackground(true);
-	ui->color->setAlignment(Qt::AlignCenter);
-}
-
-void ColorSourceToolbar::on_choose_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "color");
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-	UpdateColor();
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_int(settings, "color", color_to_int(color));
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
-
-TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_TextSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-
-	const char *id = obs_source_get_unversioned_id(source);
-	bool ft2 = strcmp(id, "text_ft2_source") == 0;
-	bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
-
-	OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
-	MakeQFont(font_obj, font);
-
-	// Use "color1" if it's a freetype source and "color" elsewise
-	unsigned int val = (unsigned int)obs_data_get_int(
-		settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
-
-	color = color_from_int(val);
-
-	const char *text = obs_data_get_string(settings, "text");
-
-	bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
-	ui->emptySpace->setVisible(!single_line);
-	ui->text->setVisible(single_line);
-	if (single_line)
-		ui->text->setText(text);
-}
-
-TextSourceToolbar::~TextSourceToolbar() {}
-
-void TextSourceToolbar::on_selectFont_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	QFontDialog::FontDialogOptions options;
-	uint32_t flags;
-	bool success;
-
-#ifndef _WIN32
-	options = QFontDialog::DontUseNativeDialog;
-#endif
-
-	font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
-				    options);
-	if (!success) {
-		return;
-	}
-
-	OBSDataAutoRelease font_obj = obs_data_create();
-
-	obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family()));
-	obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName()));
-	obs_data_set_int(font_obj, "size", font.pointSize());
-	flags = font.bold() ? OBS_FONT_BOLD : 0;
-	flags |= font.italic() ? OBS_FONT_ITALIC : 0;
-	flags |= font.underline() ? OBS_FONT_UNDERLINE : 0;
-	flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;
-	obs_data_set_int(font_obj, "flags", flags);
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-
-	obs_data_set_obj(settings, "font", font_obj);
-
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_selectColor_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
-
-	obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
-
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (freetype) {
-		obs_data_set_int(settings, "color1", color_to_int(color));
-		obs_data_set_int(settings, "color2", color_to_int(color));
-	} else {
-		obs_data_set_int(settings, "color", color_to_int(color));
-	}
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_text_textChanged()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-	std::string newText = QT_TO_UTF8(ui->text->text());
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (newText == obs_data_get_string(settings, "text")) {
-		return;
-	}
-	SaveOldProperties(source);
-
-	obs_data_set_string(settings, "text", newText.c_str());
-	obs_source_update(source, nullptr);
-
-	SetUndoProperties(source, true);
-}

+ 1 - 158
frontend/components/GameCaptureToolbar.hpp

@@ -1,119 +1,8 @@
 #pragma once
 
-#include <memory>
-#include <obs.hpp>
-#include <QWidget>
+#include "SourceToolbar.hpp"
 
-class Ui_BrowserSourceToolbar;
-class Ui_DeviceSelectToolbar;
 class Ui_GameCaptureToolbar;
-class Ui_ImageSourceToolbar;
-class Ui_ColorSourceToolbar;
-class Ui_TextSourceToolbar;
-
-class SourceToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-protected:
-	using properties_delete_t = decltype(&obs_properties_destroy);
-	using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
-
-	properties_t props;
-	OBSDataAutoRelease oldData;
-
-	void SaveOldProperties(obs_source_t *source);
-	void SetUndoProperties(obs_source_t *source, bool repeatable = false);
-
-public:
-	SourceToolbar(QWidget *parent, OBSSource source);
-
-	OBSSource GetSource() { return OBSGetStrongRef(weakSource); }
-
-public slots:
-	virtual void Update() {}
-};
-
-class BrowserToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_BrowserSourceToolbar> ui;
-
-public:
-	BrowserToolbar(QWidget *parent, OBSSource source);
-	~BrowserToolbar();
-
-public slots:
-	void on_refresh_clicked();
-};
-
-class ComboSelectToolbar : public SourceToolbar {
-	Q_OBJECT
-
-protected:
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *prop_name;
-	bool is_int = false;
-
-public:
-	ComboSelectToolbar(QWidget *parent, OBSSource source);
-	~ComboSelectToolbar();
-	virtual void Init();
-
-public slots:
-	void on_device_currentIndexChanged(int idx);
-};
-
-class AudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	AudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class WindowCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	WindowCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DisplayCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DeviceCaptureToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *activateText;
-	const char *deactivateText;
-	bool active;
-
-public:
-	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	~DeviceCaptureToolbar();
-
-public slots:
-	void on_activateButton_clicked();
-};
 
 class GameCaptureToolbar : public SourceToolbar {
 	Q_OBJECT
@@ -130,49 +19,3 @@ public slots:
 	void on_mode_currentIndexChanged(int idx);
 	void on_window_currentIndexChanged(int idx);
 };
-
-class ImageSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ImageSourceToolbar> ui;
-
-public:
-	ImageSourceToolbar(QWidget *parent, OBSSource source);
-	~ImageSourceToolbar();
-
-public slots:
-	void on_browse_clicked();
-};
-
-class ColorSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ColorSourceToolbar> ui;
-	QColor color;
-
-	void UpdateColor();
-
-public:
-	ColorSourceToolbar(QWidget *parent, OBSSource source);
-	~ColorSourceToolbar();
-
-public slots:
-	void on_choose_clicked();
-};
-
-class TextSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_TextSourceToolbar> ui;
-	QFont font;
-	QColor color;
-
-public:
-	TextSourceToolbar(QWidget *parent, OBSSource source);
-	~TextSourceToolbar();
-
-public slots:
-	void on_selectFont_clicked();
-	void on_selectColor_clicked();
-	void on_text_textChanged();
-};

+ 3 - 657
frontend/components/ImageSourceToolbar.cpp

@@ -1,444 +1,9 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
-
-#include "ui_browser-source-toolbar.h"
-#include "ui_device-select-toolbar.h"
-#include "ui_game-capture-toolbar.h"
+#include "ImageSourceToolbar.hpp"
 #include "ui_image-source-toolbar.h"
-#include "ui_color-source-toolbar.h"
-#include "ui_text-source-toolbar.h"
-
-#ifdef _WIN32
-#define get_os_module(win, mac, linux) obs_get_module(win)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)
-#elif __APPLE__
-#define get_os_module(win, mac, linux) obs_get_module(mac)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)
-#else
-#define get_os_module(win, mac, linux) obs_get_module(linux)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
-#endif
-
-/* ========================================================================= */
-
-SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  props(obs_source_properties(source), obs_properties_destroy)
-{
-}
-
-void SourceToolbar::SaveOldProperties(obs_source_t *source)
-{
-	oldData = obs_data_create();
-
-	OBSDataAutoRelease oldSettings = obs_source_get_settings(source);
-	obs_data_apply(oldData, oldSettings);
-	obs_data_set_string(oldData, "undo_suuid", obs_source_get_uuid(source));
-}
-
-void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
-{
-	if (!oldData) {
-		blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__);
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-
-	OBSSource currentSceneSource = main->GetCurrentSceneSource();
-	if (!currentSceneSource)
-		return;
-	std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
-	auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
-		OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
-		obs_source_reset_settings(source, settings);
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-
-		main->UpdateContextBar();
-	};
-
-	OBSDataAutoRelease new_settings = obs_data_create();
-	OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
-	obs_data_apply(new_settings, curr_settings);
-	obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
-
-	std::string undo_data(obs_data_get_json(oldData));
-	std::string redo_data(obs_data_get_json(new_settings));
-
-	if (undo_data.compare(redo_data) != 0)
-		main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
-					undo_data, redo_data, repeatable);
-
-	oldData = nullptr;
-}
-
-/* ========================================================================= */
-
-BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_BrowserSourceToolbar)
-{
-	ui->setupUi(this);
-}
-
-BrowserToolbar::~BrowserToolbar() {}
-
-void BrowserToolbar::on_refresh_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
-	obs_property_button_clicked(p, source.Get());
-}
-
-/* ========================================================================= */
-
-ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-}
-
-ComboSelectToolbar::~ComboSelectToolbar() {}
-
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
-{
-	size_t count = obs_property_list_item_count(p);
-	int cur_idx = -1;
-
-	for (size_t i = 0; i < count; i++) {
-		const char *name = obs_property_list_item_name(p, i);
-		std::string id;
-
-		if (is_int) {
-			id = std::to_string(obs_property_list_item_int(p, i));
-		} else {
-			const char *val = obs_property_list_item_string(p, i);
-			id = val ? val : "";
-		}
-
-		if (cur_id == id)
-			cur_idx = (int)i;
-
-		c->addItem(name, id.c_str());
-	}
-
-	return cur_idx;
-}
-
-void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
-					const char *prop_name, bool is_int)
-{
-	std::string cur_id;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (is_int) {
-		cur_id = std::to_string(obs_data_get_int(settings, prop_name));
-	} else {
-		cur_id = obs_data_get_string(settings, prop_name);
-	}
-
-	combo->blockSignals(true);
-
-	obs_property_t *p = obs_properties_get(props, prop_name);
-	int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);
-
-	if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
-		if (cur_idx == -1) {
-			combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
-			cur_idx = 0;
-		}
-
-		SetComboItemEnabled(combo, cur_idx, false);
-	}
-
-	combo->setCurrentIndex(cur_idx);
-	combo->blockSignals(false);
-}
-
-void ComboSelectToolbar::Init()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
-}
-
-void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
-{
-	QString id = combo->itemData(idx).toString();
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (is_int) {
-		obs_data_set_int(settings, prop_name, id.toInt());
-	} else {
-		obs_data_set_string(settings, prop_name, QT_TO_UTF8(id));
-	}
-	obs_source_update(source, settings);
-}
-
-void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	SaveOldProperties(source);
-	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
-	SetUndoProperties(source);
-}
-
-AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void AudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "device_id";
-
-	ComboSelectToolbar::Init();
-}
-
-WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void WindowCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
-	ui->deviceLabel->setText(device_str);
-
-#if !defined(_WIN32) && !defined(__APPLE__) //linux
-	prop_name = "capture_window";
-#else
-	prop_name = "window";
-#endif
-
-#ifdef __APPLE__
-	is_int = true;
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void ApplicationAudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = obs_get_module("win-wasapi");
-	const char *device_str = obs_module_get_locale_text(mod, "Window");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "window";
-
-	ComboSelectToolbar::Init();
-}
-
-DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void DisplayCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "monitor_id";
-#elif __APPLE__
-	prop_name = "display_uuid";
-#else
-	is_int = true;
-	prop_name = "screen";
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-/* ========================================================================= */
-
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-
-	delete ui->deviceLabel;
-	delete ui->device;
-	ui->deviceLabel = nullptr;
-	ui->device = nullptr;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	active = obs_data_get_bool(settings, "active");
-
-	obs_module_t *mod = obs_get_module("win-dshow");
-	if (!mod)
-		return;
-
-	activateText = obs_module_get_locale_text(mod, "Activate");
-	deactivateText = obs_module_get_locale_text(mod, "Deactivate");
-
-	ui->activateButton->setText(active ? deactivateText : activateText);
-}
-
-DeviceCaptureToolbar::~DeviceCaptureToolbar() {}
-
-void DeviceCaptureToolbar::on_activateButton_clicked()
-{
-	OBSSource source = OBSGetStrongRef(weakSource);
-	if (!source) {
-		return;
-	}
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	bool now_active = obs_data_get_bool(settings, "active");
-
-	bool desyncedSetting = now_active != active;
-
-	active = !active;
-
-	const char *text = active ? deactivateText : activateText;
-	ui->activateButton->setText(text);
-
-	if (desyncedSetting) {
-		return;
-	}
-
-	calldata_t cd = {};
-	calldata_set_bool(&cd, "active", active);
-	proc_handler_t *ph = obs_source_get_proc_handler(source);
-	proc_handler_call(ph, "activate", &cd);
-	calldata_free(&cd);
-}
-
-/* ========================================================================= */
-
-GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_GameCaptureToolbar)
-{
-	obs_property_t *p;
-	int cur_idx;
 
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("win-capture");
-	if (!mod)
-		return;
-
-	ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
-	ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string cur_mode = obs_data_get_string(settings, "capture_mode");
-	std::string cur_window = obs_data_get_string(settings, "window");
-
-	ui->mode->blockSignals(true);
-	p = obs_properties_get(props.get(), "capture_mode");
-	cur_idx = FillPropertyCombo(ui->mode, p, cur_mode);
-	ui->mode->setCurrentIndex(cur_idx);
-	ui->mode->blockSignals(false);
-
-	ui->window->blockSignals(true);
-	p = obs_properties_get(props.get(), "window");
-	cur_idx = FillPropertyCombo(ui->window, p, cur_window);
-	ui->window->setCurrentIndex(cur_idx);
-	ui->window->blockSignals(false);
-
-	if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {
-		SetComboItemEnabled(ui->window, cur_idx, false);
-	}
-
-	UpdateWindowVisibility();
-}
-
-GameCaptureToolbar::~GameCaptureToolbar() {}
-
-void GameCaptureToolbar::UpdateWindowVisibility()
-{
-	QString mode = ui->mode->currentData().toString();
-	bool is_window = (mode == "window");
-	ui->windowLabel->setVisible(is_window);
-	ui->window->setVisible(is_window);
-}
-
-void GameCaptureToolbar::on_mode_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->mode->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-
-	UpdateWindowVisibility();
-}
-
-void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->window->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "window", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
+#include <qt-wrappers.hpp>
 
-/* ========================================================================= */
+#include "moc_ImageSourceToolbar.cpp"
 
 ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
 	: SourceToolbar(parent, source),
@@ -486,222 +51,3 @@ void ImageSourceToolbar::on_browse_clicked()
 	obs_source_update(source, settings);
 	SetUndoProperties(source);
 }
-
-/* ========================================================================= */
-
-static inline QColor color_from_int(long long val)
-{
-	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
-}
-
-static inline long long color_to_int(QColor color)
-{
-	auto shift = [&](unsigned val, int shift) {
-		return ((val & 0xff) << shift);
-	};
-
-	return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
-}
-
-ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ColorSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	unsigned int val = (unsigned int)obs_data_get_int(settings, "color");
-
-	color = color_from_int(val);
-	UpdateColor();
-}
-
-ColorSourceToolbar::~ColorSourceToolbar() {}
-
-void ColorSourceToolbar::UpdateColor()
-{
-	QPalette palette = QPalette(color);
-	ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
-	ui->color->setText(color.name(QColor::HexRgb));
-	ui->color->setPalette(palette);
-	ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
-					 .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
-					 .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
-	ui->color->setAutoFillBackground(true);
-	ui->color->setAlignment(Qt::AlignCenter);
-}
-
-void ColorSourceToolbar::on_choose_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "color");
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-	UpdateColor();
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_int(settings, "color", color_to_int(color));
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
-
-TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_TextSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-
-	const char *id = obs_source_get_unversioned_id(source);
-	bool ft2 = strcmp(id, "text_ft2_source") == 0;
-	bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
-
-	OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
-	MakeQFont(font_obj, font);
-
-	// Use "color1" if it's a freetype source and "color" elsewise
-	unsigned int val = (unsigned int)obs_data_get_int(
-		settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
-
-	color = color_from_int(val);
-
-	const char *text = obs_data_get_string(settings, "text");
-
-	bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
-	ui->emptySpace->setVisible(!single_line);
-	ui->text->setVisible(single_line);
-	if (single_line)
-		ui->text->setText(text);
-}
-
-TextSourceToolbar::~TextSourceToolbar() {}
-
-void TextSourceToolbar::on_selectFont_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	QFontDialog::FontDialogOptions options;
-	uint32_t flags;
-	bool success;
-
-#ifndef _WIN32
-	options = QFontDialog::DontUseNativeDialog;
-#endif
-
-	font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
-				    options);
-	if (!success) {
-		return;
-	}
-
-	OBSDataAutoRelease font_obj = obs_data_create();
-
-	obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family()));
-	obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName()));
-	obs_data_set_int(font_obj, "size", font.pointSize());
-	flags = font.bold() ? OBS_FONT_BOLD : 0;
-	flags |= font.italic() ? OBS_FONT_ITALIC : 0;
-	flags |= font.underline() ? OBS_FONT_UNDERLINE : 0;
-	flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;
-	obs_data_set_int(font_obj, "flags", flags);
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-
-	obs_data_set_obj(settings, "font", font_obj);
-
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_selectColor_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
-
-	obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
-
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (freetype) {
-		obs_data_set_int(settings, "color1", color_to_int(color));
-		obs_data_set_int(settings, "color2", color_to_int(color));
-	} else {
-		obs_data_set_int(settings, "color", color_to_int(color));
-	}
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_text_textChanged()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-	std::string newText = QT_TO_UTF8(ui->text->text());
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (newText == obs_data_get_string(settings, "text")) {
-		return;
-	}
-	SaveOldProperties(source);
-
-	obs_data_set_string(settings, "text", newText.c_str());
-	obs_source_update(source, nullptr);
-
-	SetUndoProperties(source, true);
-}

+ 1 - 161
frontend/components/ImageSourceToolbar.hpp

@@ -1,135 +1,8 @@
 #pragma once
 
-#include <memory>
-#include <obs.hpp>
-#include <QWidget>
+#include "SourceToolbar.hpp"
 
-class Ui_BrowserSourceToolbar;
-class Ui_DeviceSelectToolbar;
-class Ui_GameCaptureToolbar;
 class Ui_ImageSourceToolbar;
-class Ui_ColorSourceToolbar;
-class Ui_TextSourceToolbar;
-
-class SourceToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-protected:
-	using properties_delete_t = decltype(&obs_properties_destroy);
-	using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
-
-	properties_t props;
-	OBSDataAutoRelease oldData;
-
-	void SaveOldProperties(obs_source_t *source);
-	void SetUndoProperties(obs_source_t *source, bool repeatable = false);
-
-public:
-	SourceToolbar(QWidget *parent, OBSSource source);
-
-	OBSSource GetSource() { return OBSGetStrongRef(weakSource); }
-
-public slots:
-	virtual void Update() {}
-};
-
-class BrowserToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_BrowserSourceToolbar> ui;
-
-public:
-	BrowserToolbar(QWidget *parent, OBSSource source);
-	~BrowserToolbar();
-
-public slots:
-	void on_refresh_clicked();
-};
-
-class ComboSelectToolbar : public SourceToolbar {
-	Q_OBJECT
-
-protected:
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *prop_name;
-	bool is_int = false;
-
-public:
-	ComboSelectToolbar(QWidget *parent, OBSSource source);
-	~ComboSelectToolbar();
-	virtual void Init();
-
-public slots:
-	void on_device_currentIndexChanged(int idx);
-};
-
-class AudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	AudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class WindowCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	WindowCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DisplayCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DeviceCaptureToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *activateText;
-	const char *deactivateText;
-	bool active;
-
-public:
-	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	~DeviceCaptureToolbar();
-
-public slots:
-	void on_activateButton_clicked();
-};
-
-class GameCaptureToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_GameCaptureToolbar> ui;
-
-	void UpdateWindowVisibility();
-
-public:
-	GameCaptureToolbar(QWidget *parent, OBSSource source);
-	~GameCaptureToolbar();
-
-public slots:
-	void on_mode_currentIndexChanged(int idx);
-	void on_window_currentIndexChanged(int idx);
-};
 
 class ImageSourceToolbar : public SourceToolbar {
 	Q_OBJECT
@@ -143,36 +16,3 @@ public:
 public slots:
 	void on_browse_clicked();
 };
-
-class ColorSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ColorSourceToolbar> ui;
-	QColor color;
-
-	void UpdateColor();
-
-public:
-	ColorSourceToolbar(QWidget *parent, OBSSource source);
-	~ColorSourceToolbar();
-
-public slots:
-	void on_choose_clicked();
-};
-
-class TextSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_TextSourceToolbar> ui;
-	QFont font;
-	QColor color;
-
-public:
-	TextSourceToolbar(QWidget *parent, OBSSource source);
-	~TextSourceToolbar();
-
-public slots:
-	void on_selectFont_clicked();
-	void on_selectColor_clicked();
-	void on_text_textChanged();
-};

+ 3 - 14
frontend/components/OBSPreviewScalingComboBox.cpp

@@ -15,23 +15,12 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
 
-#include "preview-controls.hpp"
-#include <obs-app.hpp>
+#include "OBSPreviewScalingComboBox.hpp"
 
-/* Preview Scale Label */
-void OBSPreviewScalingLabel::PreviewScaleChanged(float scale)
-{
-	previewScale = scale;
-	UpdateScaleLabel();
-}
+#include <OBSApp.hpp>
 
-void OBSPreviewScalingLabel::UpdateScaleLabel()
-{
-	float previewScalePercent = floor(100.0f * previewScale);
-	setText(QString::number(previewScalePercent) + "%");
-}
+#include "moc_OBSPreviewScalingComboBox.cpp"
 
-/* Preview Scaling ComboBox */
 void OBSPreviewScalingComboBox::PreviewFixedScalingChanged(bool fixed)
 {
 	if (fixedScaling == fixed)

+ 0 - 15
frontend/components/OBSPreviewScalingComboBox.hpp

@@ -17,23 +17,8 @@
 
 #pragma once
 
-#include <QLabel>
 #include <QComboBox>
 
-class OBSPreviewScalingLabel : public QLabel {
-	Q_OBJECT
-
-public:
-	OBSPreviewScalingLabel(QWidget *parent = nullptr) : QLabel(parent) {}
-
-public slots:
-	void PreviewScaleChanged(float scale);
-
-private:
-	float previewScale = 0.0f;
-	void UpdateScaleLabel();
-};
-
 class OBSPreviewScalingComboBox : public QComboBox {
 	Q_OBJECT
 

+ 2 - 104
frontend/components/OBSPreviewScalingLabel.cpp

@@ -15,10 +15,9 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
 
-#include "preview-controls.hpp"
-#include <obs-app.hpp>
+#include "OBSPreviewScalingLabel.hpp"
+#include "moc_OBSPreviewScalingLabel.cpp"
 
-/* Preview Scale Label */
 void OBSPreviewScalingLabel::PreviewScaleChanged(float scale)
 {
 	previewScale = scale;
@@ -30,104 +29,3 @@ void OBSPreviewScalingLabel::UpdateScaleLabel()
 	float previewScalePercent = floor(100.0f * previewScale);
 	setText(QString::number(previewScalePercent) + "%");
 }
-
-/* Preview Scaling ComboBox */
-void OBSPreviewScalingComboBox::PreviewFixedScalingChanged(bool fixed)
-{
-	if (fixedScaling == fixed)
-		return;
-
-	fixedScaling = fixed;
-	UpdateSelection();
-}
-
-void OBSPreviewScalingComboBox::CanvasResized(uint32_t width, uint32_t height)
-{
-	SetCanvasSize(width, height);
-	UpdateCanvasText();
-}
-
-void OBSPreviewScalingComboBox::OutputResized(uint32_t width, uint32_t height)
-{
-	SetOutputSize(width, height);
-
-	bool canvasMatchesOutput = output_width == canvas_width && output_height == canvas_height;
-
-	SetScaleOutputEnabled(!canvasMatchesOutput);
-	UpdateOutputText();
-}
-
-void OBSPreviewScalingComboBox::PreviewScaleChanged(float scale)
-{
-	previewScale = scale;
-
-	if (fixedScaling) {
-		UpdateSelection();
-		UpdateAllText();
-	} else {
-		UpdateScaledText();
-	}
-}
-
-void OBSPreviewScalingComboBox::SetScaleOutputEnabled(bool show)
-{
-	if (scaleOutputEnabled == show)
-		return;
-
-	scaleOutputEnabled = show;
-
-	if (scaleOutputEnabled) {
-		addItem(QTStr("Basic.MainMenu.Edit.Scale.Output"));
-	} else {
-		removeItem(2);
-	}
-}
-
-void OBSPreviewScalingComboBox::UpdateAllText()
-{
-	UpdateCanvasText();
-	UpdateOutputText();
-	UpdateScaledText();
-}
-
-void OBSPreviewScalingComboBox::UpdateCanvasText()
-{
-	QString text = QTStr("Basic.MainMenu.Edit.Scale.Canvas");
-	text = text.arg(QString::number(canvas_width), QString::number(canvas_height));
-	setItemText(1, text);
-}
-
-void OBSPreviewScalingComboBox::UpdateOutputText()
-{
-	if (scaleOutputEnabled) {
-		QString text = QTStr("Basic.MainMenu.Edit.Scale.Output");
-		text = text.arg(QString::number(output_width), QString::number(output_height));
-		setItemText(2, text);
-	}
-}
-
-void OBSPreviewScalingComboBox::UpdateScaledText()
-{
-	QString text = QTStr("Basic.MainMenu.Edit.Scale.Manual");
-	text = text.arg(QString::number(floor(canvas_width * previewScale)),
-			QString::number(floor(canvas_height * previewScale)));
-	setPlaceholderText(text);
-}
-
-void OBSPreviewScalingComboBox::UpdateSelection()
-{
-	QSignalBlocker sb(this);
-	float outputScale = float(output_width) / float(canvas_width);
-
-	if (!fixedScaling) {
-		setCurrentIndex(0);
-	} else {
-		if (previewScale == 1.0f) {
-			setCurrentIndex(1);
-		} else if (scaleOutputEnabled && (previewScale == outputScale)) {
-			setCurrentIndex(2);
-		} else {
-			setCurrentIndex(-1);
-		}
-	}
-}

+ 0 - 45
frontend/components/OBSPreviewScalingLabel.hpp

@@ -18,7 +18,6 @@
 #pragma once
 
 #include <QLabel>
-#include <QComboBox>
 
 class OBSPreviewScalingLabel : public QLabel {
 	Q_OBJECT
@@ -33,47 +32,3 @@ private:
 	float previewScale = 0.0f;
 	void UpdateScaleLabel();
 };
-
-class OBSPreviewScalingComboBox : public QComboBox {
-	Q_OBJECT
-
-public:
-	OBSPreviewScalingComboBox(QWidget *parent = nullptr) : QComboBox(parent) {}
-
-	inline void SetCanvasSize(uint32_t width, uint32_t height)
-	{
-		canvas_width = width;
-		canvas_height = height;
-	};
-	inline void SetOutputSize(uint32_t width, uint32_t height)
-	{
-		output_width = width;
-		output_height = height;
-	};
-	void UpdateAllText();
-
-public slots:
-	void PreviewScaleChanged(float scale);
-	void PreviewFixedScalingChanged(bool fixed);
-	void CanvasResized(uint32_t width, uint32_t height);
-	void OutputResized(uint32_t width, uint32_t height);
-
-private:
-	uint32_t canvas_width = 0;
-	uint32_t canvas_height = 0;
-
-	uint32_t output_width = 0;
-	uint32_t output_height = 0;
-
-	float previewScale = 0.0f;
-
-	bool fixedScaling = false;
-
-	bool scaleOutputEnabled = false;
-	void SetScaleOutputEnabled(bool show);
-
-	void UpdateCanvasText();
-	void UpdateOutputText();
-	void UpdateScaledText();
-	void UpdateSelection();
-};

+ 3 - 650
frontend/components/SourceToolbar.cpp

@@ -1,31 +1,8 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
+#include "SourceToolbar.hpp"
 
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
+#include <widgets/OBSBasic.hpp>
 
-#include "ui_browser-source-toolbar.h"
-#include "ui_device-select-toolbar.h"
-#include "ui_game-capture-toolbar.h"
-#include "ui_image-source-toolbar.h"
-#include "ui_color-source-toolbar.h"
-#include "ui_text-source-toolbar.h"
-
-#ifdef _WIN32
-#define get_os_module(win, mac, linux) obs_get_module(win)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)
-#elif __APPLE__
-#define get_os_module(win, mac, linux) obs_get_module(mac)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)
-#else
-#define get_os_module(win, mac, linux) obs_get_module(linux)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
-#endif
-
-/* ========================================================================= */
+#include "moc_SourceToolbar.cpp"
 
 SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
 	: QWidget(parent),
@@ -81,627 +58,3 @@ void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
 
 	oldData = nullptr;
 }
-
-/* ========================================================================= */
-
-BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_BrowserSourceToolbar)
-{
-	ui->setupUi(this);
-}
-
-BrowserToolbar::~BrowserToolbar() {}
-
-void BrowserToolbar::on_refresh_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
-	obs_property_button_clicked(p, source.Get());
-}
-
-/* ========================================================================= */
-
-ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-}
-
-ComboSelectToolbar::~ComboSelectToolbar() {}
-
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
-{
-	size_t count = obs_property_list_item_count(p);
-	int cur_idx = -1;
-
-	for (size_t i = 0; i < count; i++) {
-		const char *name = obs_property_list_item_name(p, i);
-		std::string id;
-
-		if (is_int) {
-			id = std::to_string(obs_property_list_item_int(p, i));
-		} else {
-			const char *val = obs_property_list_item_string(p, i);
-			id = val ? val : "";
-		}
-
-		if (cur_id == id)
-			cur_idx = (int)i;
-
-		c->addItem(name, id.c_str());
-	}
-
-	return cur_idx;
-}
-
-void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
-					const char *prop_name, bool is_int)
-{
-	std::string cur_id;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (is_int) {
-		cur_id = std::to_string(obs_data_get_int(settings, prop_name));
-	} else {
-		cur_id = obs_data_get_string(settings, prop_name);
-	}
-
-	combo->blockSignals(true);
-
-	obs_property_t *p = obs_properties_get(props, prop_name);
-	int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);
-
-	if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
-		if (cur_idx == -1) {
-			combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
-			cur_idx = 0;
-		}
-
-		SetComboItemEnabled(combo, cur_idx, false);
-	}
-
-	combo->setCurrentIndex(cur_idx);
-	combo->blockSignals(false);
-}
-
-void ComboSelectToolbar::Init()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
-}
-
-void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
-{
-	QString id = combo->itemData(idx).toString();
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (is_int) {
-		obs_data_set_int(settings, prop_name, id.toInt());
-	} else {
-		obs_data_set_string(settings, prop_name, QT_TO_UTF8(id));
-	}
-	obs_source_update(source, settings);
-}
-
-void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	SaveOldProperties(source);
-	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
-	SetUndoProperties(source);
-}
-
-AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void AudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "device_id";
-
-	ComboSelectToolbar::Init();
-}
-
-WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void WindowCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
-	ui->deviceLabel->setText(device_str);
-
-#if !defined(_WIN32) && !defined(__APPLE__) //linux
-	prop_name = "capture_window";
-#else
-	prop_name = "window";
-#endif
-
-#ifdef __APPLE__
-	is_int = true;
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void ApplicationAudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = obs_get_module("win-wasapi");
-	const char *device_str = obs_module_get_locale_text(mod, "Window");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "window";
-
-	ComboSelectToolbar::Init();
-}
-
-DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void DisplayCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "monitor_id";
-#elif __APPLE__
-	prop_name = "display_uuid";
-#else
-	is_int = true;
-	prop_name = "screen";
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-/* ========================================================================= */
-
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-
-	delete ui->deviceLabel;
-	delete ui->device;
-	ui->deviceLabel = nullptr;
-	ui->device = nullptr;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	active = obs_data_get_bool(settings, "active");
-
-	obs_module_t *mod = obs_get_module("win-dshow");
-	if (!mod)
-		return;
-
-	activateText = obs_module_get_locale_text(mod, "Activate");
-	deactivateText = obs_module_get_locale_text(mod, "Deactivate");
-
-	ui->activateButton->setText(active ? deactivateText : activateText);
-}
-
-DeviceCaptureToolbar::~DeviceCaptureToolbar() {}
-
-void DeviceCaptureToolbar::on_activateButton_clicked()
-{
-	OBSSource source = OBSGetStrongRef(weakSource);
-	if (!source) {
-		return;
-	}
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	bool now_active = obs_data_get_bool(settings, "active");
-
-	bool desyncedSetting = now_active != active;
-
-	active = !active;
-
-	const char *text = active ? deactivateText : activateText;
-	ui->activateButton->setText(text);
-
-	if (desyncedSetting) {
-		return;
-	}
-
-	calldata_t cd = {};
-	calldata_set_bool(&cd, "active", active);
-	proc_handler_t *ph = obs_source_get_proc_handler(source);
-	proc_handler_call(ph, "activate", &cd);
-	calldata_free(&cd);
-}
-
-/* ========================================================================= */
-
-GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_GameCaptureToolbar)
-{
-	obs_property_t *p;
-	int cur_idx;
-
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("win-capture");
-	if (!mod)
-		return;
-
-	ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
-	ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string cur_mode = obs_data_get_string(settings, "capture_mode");
-	std::string cur_window = obs_data_get_string(settings, "window");
-
-	ui->mode->blockSignals(true);
-	p = obs_properties_get(props.get(), "capture_mode");
-	cur_idx = FillPropertyCombo(ui->mode, p, cur_mode);
-	ui->mode->setCurrentIndex(cur_idx);
-	ui->mode->blockSignals(false);
-
-	ui->window->blockSignals(true);
-	p = obs_properties_get(props.get(), "window");
-	cur_idx = FillPropertyCombo(ui->window, p, cur_window);
-	ui->window->setCurrentIndex(cur_idx);
-	ui->window->blockSignals(false);
-
-	if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {
-		SetComboItemEnabled(ui->window, cur_idx, false);
-	}
-
-	UpdateWindowVisibility();
-}
-
-GameCaptureToolbar::~GameCaptureToolbar() {}
-
-void GameCaptureToolbar::UpdateWindowVisibility()
-{
-	QString mode = ui->mode->currentData().toString();
-	bool is_window = (mode == "window");
-	ui->windowLabel->setVisible(is_window);
-	ui->window->setVisible(is_window);
-}
-
-void GameCaptureToolbar::on_mode_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->mode->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-
-	UpdateWindowVisibility();
-}
-
-void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->window->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "window", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ImageSourceToolbar)
-{
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("image-source");
-	ui->pathLabel->setText(obs_module_get_locale_text(mod, "File"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string file = obs_data_get_string(settings, "file");
-
-	ui->path->setText(file.c_str());
-}
-
-ImageSourceToolbar::~ImageSourceToolbar() {}
-
-void ImageSourceToolbar::on_browse_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "file");
-	const char *desc = obs_property_description(p);
-	const char *filter = obs_property_path_filter(p);
-	const char *default_path = obs_property_path_default_path(p);
-
-	QString startDir = ui->path->text();
-	if (startDir.isEmpty())
-		startDir = default_path;
-
-	QString path = OpenFile(this, desc, startDir, filter);
-	if (path.isEmpty()) {
-		return;
-	}
-
-	ui->path->setText(path);
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "file", QT_TO_UTF8(path));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-static inline QColor color_from_int(long long val)
-{
-	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
-}
-
-static inline long long color_to_int(QColor color)
-{
-	auto shift = [&](unsigned val, int shift) {
-		return ((val & 0xff) << shift);
-	};
-
-	return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
-}
-
-ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ColorSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	unsigned int val = (unsigned int)obs_data_get_int(settings, "color");
-
-	color = color_from_int(val);
-	UpdateColor();
-}
-
-ColorSourceToolbar::~ColorSourceToolbar() {}
-
-void ColorSourceToolbar::UpdateColor()
-{
-	QPalette palette = QPalette(color);
-	ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
-	ui->color->setText(color.name(QColor::HexRgb));
-	ui->color->setPalette(palette);
-	ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
-					 .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
-					 .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
-	ui->color->setAutoFillBackground(true);
-	ui->color->setAlignment(Qt::AlignCenter);
-}
-
-void ColorSourceToolbar::on_choose_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "color");
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-	UpdateColor();
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_int(settings, "color", color_to_int(color));
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
-
-TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_TextSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-
-	const char *id = obs_source_get_unversioned_id(source);
-	bool ft2 = strcmp(id, "text_ft2_source") == 0;
-	bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
-
-	OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
-	MakeQFont(font_obj, font);
-
-	// Use "color1" if it's a freetype source and "color" elsewise
-	unsigned int val = (unsigned int)obs_data_get_int(
-		settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
-
-	color = color_from_int(val);
-
-	const char *text = obs_data_get_string(settings, "text");
-
-	bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
-	ui->emptySpace->setVisible(!single_line);
-	ui->text->setVisible(single_line);
-	if (single_line)
-		ui->text->setText(text);
-}
-
-TextSourceToolbar::~TextSourceToolbar() {}
-
-void TextSourceToolbar::on_selectFont_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	QFontDialog::FontDialogOptions options;
-	uint32_t flags;
-	bool success;
-
-#ifndef _WIN32
-	options = QFontDialog::DontUseNativeDialog;
-#endif
-
-	font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
-				    options);
-	if (!success) {
-		return;
-	}
-
-	OBSDataAutoRelease font_obj = obs_data_create();
-
-	obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family()));
-	obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName()));
-	obs_data_set_int(font_obj, "size", font.pointSize());
-	flags = font.bold() ? OBS_FONT_BOLD : 0;
-	flags |= font.italic() ? OBS_FONT_ITALIC : 0;
-	flags |= font.underline() ? OBS_FONT_UNDERLINE : 0;
-	flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;
-	obs_data_set_int(font_obj, "flags", flags);
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-
-	obs_data_set_obj(settings, "font", font_obj);
-
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_selectColor_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
-
-	obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
-
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (freetype) {
-		obs_data_set_int(settings, "color1", color_to_int(color));
-		obs_data_set_int(settings, "color2", color_to_int(color));
-	} else {
-		obs_data_set_int(settings, "color", color_to_int(color));
-	}
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_text_textChanged()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-	std::string newText = QT_TO_UTF8(ui->text->text());
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (newText == obs_data_get_string(settings, "text")) {
-		return;
-	}
-	SaveOldProperties(source);
-
-	obs_data_set_string(settings, "text", newText.c_str());
-	obs_source_update(source, nullptr);
-
-	SetUndoProperties(source, true);
-}

+ 1 - 150
frontend/components/SourceToolbar.hpp

@@ -1,15 +1,8 @@
 #pragma once
 
-#include <memory>
 #include <obs.hpp>
-#include <QWidget>
 
-class Ui_BrowserSourceToolbar;
-class Ui_DeviceSelectToolbar;
-class Ui_GameCaptureToolbar;
-class Ui_ImageSourceToolbar;
-class Ui_ColorSourceToolbar;
-class Ui_TextSourceToolbar;
+#include <QWidget>
 
 class SourceToolbar : public QWidget {
 	Q_OBJECT
@@ -34,145 +27,3 @@ public:
 public slots:
 	virtual void Update() {}
 };
-
-class BrowserToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_BrowserSourceToolbar> ui;
-
-public:
-	BrowserToolbar(QWidget *parent, OBSSource source);
-	~BrowserToolbar();
-
-public slots:
-	void on_refresh_clicked();
-};
-
-class ComboSelectToolbar : public SourceToolbar {
-	Q_OBJECT
-
-protected:
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *prop_name;
-	bool is_int = false;
-
-public:
-	ComboSelectToolbar(QWidget *parent, OBSSource source);
-	~ComboSelectToolbar();
-	virtual void Init();
-
-public slots:
-	void on_device_currentIndexChanged(int idx);
-};
-
-class AudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	AudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class WindowCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	WindowCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DisplayCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DeviceCaptureToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *activateText;
-	const char *deactivateText;
-	bool active;
-
-public:
-	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	~DeviceCaptureToolbar();
-
-public slots:
-	void on_activateButton_clicked();
-};
-
-class GameCaptureToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_GameCaptureToolbar> ui;
-
-	void UpdateWindowVisibility();
-
-public:
-	GameCaptureToolbar(QWidget *parent, OBSSource source);
-	~GameCaptureToolbar();
-
-public slots:
-	void on_mode_currentIndexChanged(int idx);
-	void on_window_currentIndexChanged(int idx);
-};
-
-class ImageSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ImageSourceToolbar> ui;
-
-public:
-	ImageSourceToolbar(QWidget *parent, OBSSource source);
-	~ImageSourceToolbar();
-
-public slots:
-	void on_browse_clicked();
-};
-
-class ColorSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ColorSourceToolbar> ui;
-	QColor color;
-
-	void UpdateColor();
-
-public:
-	ColorSourceToolbar(QWidget *parent, OBSSource source);
-	~ColorSourceToolbar();
-
-public slots:
-	void on_choose_clicked();
-};
-
-class TextSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_TextSourceToolbar> ui;
-	QFont font;
-	QColor color;
-
-public:
-	TextSourceToolbar(QWidget *parent, OBSSource source);
-	~TextSourceToolbar();
-
-public slots:
-	void on_selectFont_clicked();
-	void on_selectColor_clicked();
-	void on_text_textChanged();
-};

+ 7 - 982
frontend/components/SourceTree.cpp

@@ -1,635 +1,16 @@
-#include "window-basic-main.hpp"
-#include "obs-app.hpp"
-#include "source-tree.hpp"
-#include "platform.hpp"
-#include "source-label.hpp"
-
-#include <qt-wrappers.hpp>
-#include <obs-frontend-api.h>
-#include <obs.h>
-
-#include <string>
-
-#include <QLabel>
-#include <QLineEdit>
-#include <QSpacerItem>
-#include <QPushButton>
-#include <QVBoxLayout>
-#include <QHBoxLayout>
-#include <QMouseEvent>
-#include <QAccessible>
-
-#include <QStylePainter>
-#include <QStyleOptionFocusRect>
+#include "SourceTree.hpp"
+#include "SourceTreeDelegate.hpp"
 
-static inline OBSScene GetCurrentScene()
-{
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-	return main->GetCurrentScene();
-}
-
-/* ========================================================================= */
-
-SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) : tree(tree_), sceneitem(sceneitem_)
-{
-	setAttribute(Qt::WA_TranslucentBackground);
-	setMouseTracking(true);
-
-	obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-	const char *name = obs_source_get_name(source);
-
-	OBSDataAutoRelease privData = obs_sceneitem_get_private_settings(sceneitem);
-	int preset = obs_data_get_int(privData, "color-preset");
-
-	if (preset == 1) {
-		const char *color = obs_data_get_string(privData, "color");
-		std::string col = "background: ";
-		col += color;
-		setStyleSheet(col.c_str());
-	} else if (preset > 1) {
-		setStyleSheet("");
-		setProperty("bgColor", preset - 1);
-	} else {
-		setStyleSheet("background: none");
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-	const char *id = obs_source_get_id(source);
-
-	bool sourceVisible = obs_sceneitem_visible(sceneitem);
-
-	if (tree->iconsVisible) {
-		QIcon icon;
-
-		if (strcmp(id, "scene") == 0)
-			icon = main->GetSceneIcon();
-		else if (strcmp(id, "group") == 0)
-			icon = main->GetGroupIcon();
-		else
-			icon = main->GetSourceIcon(id);
-
-		QPixmap pixmap = icon.pixmap(QSize(16, 16));
-
-		iconLabel = new QLabel();
-		iconLabel->setPixmap(pixmap);
-		iconLabel->setEnabled(sourceVisible);
-		iconLabel->setStyleSheet("background: none");
-		iconLabel->setProperty("class", "source-icon");
-	}
-
-	vis = new QCheckBox();
-	vis->setProperty("class", "checkbox-icon indicator-visibility");
-	vis->setChecked(sourceVisible);
-	vis->setAccessibleName(QTStr("Basic.Main.Sources.Visibility"));
-	vis->setAccessibleDescription(QTStr("Basic.Main.Sources.VisibilityDescription").arg(name));
-
-	lock = new QCheckBox();
-	lock->setProperty("class", "checkbox-icon indicator-lock");
-	lock->setChecked(obs_sceneitem_locked(sceneitem));
-	lock->setAccessibleName(QTStr("Basic.Main.Sources.Lock"));
-	lock->setAccessibleDescription(QTStr("Basic.Main.Sources.LockDescription").arg(name));
-
-	label = new OBSSourceLabel(source);
-	label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
-	label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
-	label->setAttribute(Qt::WA_TranslucentBackground);
-	label->setEnabled(sourceVisible);
-
-#ifdef __APPLE__
-	vis->setAttribute(Qt::WA_LayoutUsesWidgetRect);
-	lock->setAttribute(Qt::WA_LayoutUsesWidgetRect);
-#endif
-
-	boxLayout = new QHBoxLayout();
-
-	boxLayout->setContentsMargins(0, 0, 0, 0);
-	boxLayout->setSpacing(0);
-	if (iconLabel) {
-		boxLayout->addWidget(iconLabel);
-		boxLayout->addSpacing(2);
-	}
-	boxLayout->addWidget(label);
-	boxLayout->addWidget(vis);
-	boxLayout->addWidget(lock);
-#ifdef __APPLE__
-	/* Hack: Fixes a bug where scrollbars would be above the lock icon */
-	boxLayout->addSpacing(16);
-#endif
-
-	Update(false);
-
-	setLayout(boxLayout);
-
-	/* --------------------------------------------------------- */
-
-	auto setItemVisible = [this](bool val) {
-		obs_scene_t *scene = obs_sceneitem_get_scene(sceneitem);
-		obs_source_t *scenesource = obs_scene_get_source(scene);
-		int64_t id = obs_sceneitem_get_id(sceneitem);
-		const char *name = obs_source_get_name(scenesource);
-		const char *uuid = obs_source_get_uuid(scenesource);
-		obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-
-		auto undo_redo = [](const std::string &uuid, int64_t id, bool val) {
-			OBSSourceAutoRelease s = obs_get_source_by_uuid(uuid.c_str());
-			obs_scene_t *sc = obs_group_or_scene_from_source(s);
-			obs_sceneitem_t *si = obs_scene_find_sceneitem_by_id(sc, id);
-			if (si)
-				obs_sceneitem_set_visible(si, val);
-		};
-
-		QString str = QTStr(val ? "Undo.ShowSceneItem" : "Undo.HideSceneItem");
-
-		OBSBasic *main = OBSBasic::Get();
-		main->undo_s.add_action(str.arg(obs_source_get_name(source), name),
-					std::bind(undo_redo, std::placeholders::_1, id, !val),
-					std::bind(undo_redo, std::placeholders::_1, id, val), uuid, uuid);
-
-		QSignalBlocker sourcesSignalBlocker(this);
-		obs_sceneitem_set_visible(sceneitem, val);
-	};
-
-	auto setItemLocked = [this](bool checked) {
-		QSignalBlocker sourcesSignalBlocker(this);
-		obs_sceneitem_set_locked(sceneitem, checked);
-	};
-
-	connect(vis, &QAbstractButton::clicked, setItemVisible);
-	connect(lock, &QAbstractButton::clicked, setItemLocked);
-}
-
-void SourceTreeItem::paintEvent(QPaintEvent *event)
-{
-	QStyleOption opt;
-	opt.initFrom(this);
-	QPainter p(this);
-	style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
-
-	QWidget::paintEvent(event);
-}
-
-void SourceTreeItem::DisconnectSignals()
-{
-	sigs.clear();
-}
-
-void SourceTreeItem::Clear()
-{
-	DisconnectSignals();
-	sceneitem = nullptr;
-}
-
-void SourceTreeItem::ReconnectSignals()
-{
-	if (!sceneitem)
-		return;
-
-	DisconnectSignals();
-
-	/* --------------------------------------------------------- */
-
-	auto removeItem = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-		obs_scene_t *curScene = (obs_scene_t *)calldata_ptr(cd, "scene");
-
-		if (curItem == this_->sceneitem) {
-			QMetaObject::invokeMethod(this_->tree, "Remove", Q_ARG(OBSSceneItem, curItem),
-						  Q_ARG(OBSScene, curScene));
-			curItem = nullptr;
-		}
-		if (!curItem)
-			QMetaObject::invokeMethod(this_, "Clear");
-	};
-
-	auto itemVisible = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-		bool visible = calldata_bool(cd, "visible");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "VisibilityChanged", Q_ARG(bool, visible));
-	};
-
-	auto itemLocked = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-		bool locked = calldata_bool(cd, "locked");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "LockedChanged", Q_ARG(bool, locked));
-	};
-
-	auto itemSelect = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "Select");
-	};
-
-	auto itemDeselect = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "Deselect");
-	};
-
-	auto reorderGroup = [](void *data, calldata_t *) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		QMetaObject::invokeMethod(this_->tree, "ReorderItems");
-	};
-
-	obs_scene_t *scene = obs_sceneitem_get_scene(sceneitem);
-	obs_source_t *sceneSource = obs_scene_get_source(scene);
-	signal_handler_t *signal = obs_source_get_signal_handler(sceneSource);
-
-	sigs.emplace_back(signal, "remove", removeItem, this);
-	sigs.emplace_back(signal, "item_remove", removeItem, this);
-	sigs.emplace_back(signal, "item_visible", itemVisible, this);
-	sigs.emplace_back(signal, "item_locked", itemLocked, this);
-	sigs.emplace_back(signal, "item_select", itemSelect, this);
-	sigs.emplace_back(signal, "item_deselect", itemDeselect, this);
-
-	if (obs_sceneitem_is_group(sceneitem)) {
-		obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-		signal = obs_source_get_signal_handler(source);
-
-		sigs.emplace_back(signal, "reorder", reorderGroup, this);
-	}
-
-	/* --------------------------------------------------------- */
-
-	auto removeSource = [](void *data, calldata_t *) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		this_->DisconnectSignals();
-		this_->sceneitem = nullptr;
-		QMetaObject::invokeMethod(this_->tree, "RefreshItems");
-	};
-
-	obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-	signal = obs_source_get_signal_handler(source);
-	sigs.emplace_back(signal, "remove", removeSource, this);
-}
-
-void SourceTreeItem::mouseDoubleClickEvent(QMouseEvent *event)
-{
-	QWidget::mouseDoubleClickEvent(event);
-
-	if (expand) {
-		expand->setChecked(!expand->isChecked());
-	} else {
-		obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-		OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-		if (obs_source_configurable(source)) {
-			main->CreatePropertiesWindow(source);
-		}
-	}
-}
-
-void SourceTreeItem::enterEvent(QEnterEvent *event)
-{
-	QWidget::enterEvent(event);
-
-	OBSBasicPreview *preview = OBSBasicPreview::Get();
-
-	std::lock_guard<std::mutex> lock(preview->selectMutex);
-	preview->hoveredPreviewItems.clear();
-	preview->hoveredPreviewItems.push_back(sceneitem);
-}
-
-void SourceTreeItem::leaveEvent(QEvent *event)
-{
-	QWidget::leaveEvent(event);
+#include <widgets/OBSBasic.hpp>
 
-	OBSBasicPreview *preview = OBSBasicPreview::Get();
+#include <QPainter>
 
-	std::lock_guard<std::mutex> lock(preview->selectMutex);
-	preview->hoveredPreviewItems.clear();
-}
-
-bool SourceTreeItem::IsEditing()
-{
-	return editor != nullptr;
-}
-
-void SourceTreeItem::EnterEditMode()
-{
-	setFocusPolicy(Qt::StrongFocus);
-	int index = boxLayout->indexOf(label);
-	boxLayout->removeWidget(label);
-	editor = new QLineEdit(label->text());
-	editor->setStyleSheet("background: none");
-	editor->selectAll();
-	editor->installEventFilter(this);
-	boxLayout->insertWidget(index, editor);
-	setFocusProxy(editor);
-}
-
-void SourceTreeItem::ExitEditMode(bool save)
-{
-	ExitEditModeInternal(save);
-
-	if (tree->undoSceneData) {
-		OBSBasic *main = OBSBasic::Get();
-		main->undo_s.pop_disabled();
-
-		OBSData redoSceneData = main->BackupScene(GetCurrentScene());
-
-		QString text = QTStr("Undo.GroupItems").arg(newName.c_str());
-		main->CreateSceneUndoRedoAction(text, tree->undoSceneData, redoSceneData);
+#include "moc_SourceTree.cpp"
 
-		tree->undoSceneData = nullptr;
-	}
-}
-
-void SourceTreeItem::ExitEditModeInternal(bool save)
+static inline OBSScene GetCurrentScene()
 {
-	if (!editor) {
-		return;
-	}
-
 	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-	OBSScene scene = main->GetCurrentScene();
-
-	newName = QT_TO_UTF8(editor->text());
-
-	setFocusProxy(nullptr);
-	int index = boxLayout->indexOf(editor);
-	boxLayout->removeWidget(editor);
-	delete editor;
-	editor = nullptr;
-	setFocusPolicy(Qt::NoFocus);
-	boxLayout->insertWidget(index, label);
-	setFocus();
-
-	/* ----------------------------------------- */
-	/* check for empty string                    */
-
-	if (!save)
-		return;
-
-	if (newName.empty()) {
-		OBSMessageBox::information(main, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text"));
-		return;
-	}
-
-	/* ----------------------------------------- */
-	/* Check for same name                       */
-
-	obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-	if (newName == obs_source_get_name(source))
-		return;
-
-	/* ----------------------------------------- */
-	/* check for existing source                 */
-
-	OBSSourceAutoRelease existingSource = obs_get_source_by_name(newName.c_str());
-	bool exists = !!existingSource;
-
-	if (exists) {
-		OBSMessageBox::information(main, QTStr("NameExists.Title"), QTStr("NameExists.Text"));
-		return;
-	}
-
-	/* ----------------------------------------- */
-	/* rename                                    */
-
-	QSignalBlocker sourcesSignalBlocker(this);
-	std::string prevName(obs_source_get_name(source));
-	std::string scene_uuid = obs_source_get_uuid(main->GetCurrentSceneSource());
-	auto undo = [scene_uuid, prevName, main](const std::string &data) {
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
-		obs_source_set_name(source, prevName.c_str());
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-	};
-
-	std::string editedName = newName;
-
-	auto redo = [scene_uuid, main, editedName](const std::string &data) {
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
-		obs_source_set_name(source, editedName.c_str());
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-	};
-
-	const char *uuid = obs_source_get_uuid(source);
-	main->undo_s.add_action(QTStr("Undo.Rename").arg(newName.c_str()), undo, redo, uuid, uuid);
-
-	obs_source_set_name(source, newName.c_str());
-}
-
-bool SourceTreeItem::eventFilter(QObject *object, QEvent *event)
-{
-	if (editor != object)
-		return false;
-
-	if (LineEditCanceled(event)) {
-		QMetaObject::invokeMethod(this, "ExitEditMode", Qt::QueuedConnection, Q_ARG(bool, false));
-		return true;
-	}
-	if (LineEditChanged(event)) {
-		QMetaObject::invokeMethod(this, "ExitEditMode", Qt::QueuedConnection, Q_ARG(bool, true));
-		return true;
-	}
-
-	return false;
-}
-
-void SourceTreeItem::VisibilityChanged(bool visible)
-{
-	if (iconLabel) {
-		iconLabel->setEnabled(visible);
-	}
-	label->setEnabled(visible);
-	vis->setChecked(visible);
-}
-
-void SourceTreeItem::LockedChanged(bool locked)
-{
-	lock->setChecked(locked);
-	OBSBasic::Get()->UpdateEditMenu();
-}
-
-void SourceTreeItem::Update(bool force)
-{
-	OBSScene scene = GetCurrentScene();
-	obs_scene_t *itemScene = obs_sceneitem_get_scene(sceneitem);
-
-	Type newType;
-
-	/* ------------------------------------------------- */
-	/* if it's a group item, insert group checkbox       */
-
-	if (obs_sceneitem_is_group(sceneitem)) {
-		newType = Type::Group;
-
-		/* ------------------------------------------------- */
-		/* if it's a group sub-item                          */
-
-	} else if (itemScene != scene) {
-		newType = Type::SubItem;
-
-		/* ------------------------------------------------- */
-		/* if it's a regular item                            */
-
-	} else {
-		newType = Type::Item;
-	}
-
-	/* ------------------------------------------------- */
-
-	if (!force && newType == type) {
-		return;
-	}
-
-	/* ------------------------------------------------- */
-
-	ReconnectSignals();
-
-	if (spacer) {
-		boxLayout->removeItem(spacer);
-		delete spacer;
-		spacer = nullptr;
-	}
-
-	if (type == Type::Group) {
-		boxLayout->removeWidget(expand);
-		expand->deleteLater();
-		expand = nullptr;
-	}
-
-	type = newType;
-
-	if (type == Type::SubItem) {
-		spacer = new QSpacerItem(16, 1);
-		boxLayout->insertItem(0, spacer);
-
-	} else if (type == Type::Group) {
-		expand = new QCheckBox();
-		expand->setProperty("class", "checkbox-icon indicator-expand");
-#ifdef __APPLE__
-		expand->setAttribute(Qt::WA_LayoutUsesWidgetRect);
-#endif
-		boxLayout->insertWidget(0, expand);
-
-		OBSDataAutoRelease data = obs_sceneitem_get_private_settings(sceneitem);
-		expand->blockSignals(true);
-		expand->setChecked(obs_data_get_bool(data, "collapsed"));
-		expand->blockSignals(false);
-
-		connect(expand, &QPushButton::toggled, this, &SourceTreeItem::ExpandClicked);
-
-	} else {
-		spacer = new QSpacerItem(3, 1);
-		boxLayout->insertItem(0, spacer);
-	}
-}
-
-void SourceTreeItem::ExpandClicked(bool checked)
-{
-	OBSDataAutoRelease data = obs_sceneitem_get_private_settings(sceneitem);
-
-	obs_data_set_bool(data, "collapsed", checked);
-
-	if (!checked)
-		tree->GetStm()->ExpandGroup(sceneitem);
-	else
-		tree->GetStm()->CollapseGroup(sceneitem);
-}
-
-void SourceTreeItem::Select()
-{
-	tree->SelectItem(sceneitem, true);
-	OBSBasic::Get()->UpdateContextBarDeferred();
-	OBSBasic::Get()->UpdateEditMenu();
-}
-
-void SourceTreeItem::Deselect()
-{
-	tree->SelectItem(sceneitem, false);
-	OBSBasic::Get()->UpdateContextBarDeferred();
-	OBSBasic::Get()->UpdateEditMenu();
-}
-
-/* ========================================================================= */
-
-void SourceTreeModel::OBSFrontendEvent(enum obs_frontend_event event, void *ptr)
-{
-	SourceTreeModel *stm = reinterpret_cast<SourceTreeModel *>(ptr);
-
-	switch (event) {
-	case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
-		stm->SceneChanged();
-		break;
-	case OBS_FRONTEND_EVENT_EXIT:
-		stm->Clear();
-		obs_frontend_remove_event_callback(OBSFrontendEvent, stm);
-		break;
-	case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP:
-		stm->Clear();
-		break;
-	default:
-		break;
-	}
-}
-
-void SourceTreeModel::Clear()
-{
-	beginResetModel();
-	items.clear();
-	endResetModel();
-
-	hasGroups = false;
-}
-
-static bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
-{
-	QVector<OBSSceneItem> &items = *reinterpret_cast<QVector<OBSSceneItem> *>(ptr);
-
-	obs_source_t *src = obs_sceneitem_get_source(item);
-	if (obs_source_removed(src)) {
-		return true;
-	}
-
-	if (obs_sceneitem_is_group(item)) {
-		OBSDataAutoRelease data = obs_sceneitem_get_private_settings(item);
-
-		bool collapse = obs_data_get_bool(data, "collapsed");
-		if (!collapse) {
-			obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-			obs_scene_enum_items(scene, enumItem, &items);
-		}
-	}
-
-	items.insert(0, item);
-	return true;
-}
-
-void SourceTreeModel::SceneChanged()
-{
-	OBSScene scene = GetCurrentScene();
-
-	beginResetModel();
-	items.clear();
-	obs_scene_enum_items(scene, enumItem, &items);
-	endResetModel();
-
-	UpdateGroupState(false);
-	st->ResetWidgets();
-
-	for (int i = 0; i < items.count(); i++) {
-		bool select = obs_sceneitem_selected(items[i]);
-		QModelIndex index = createIndex(i, 0);
-
-		st->selectionModel()->select(index,
-					     select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
-	}
+	return main->GetCurrentScene();
 }
 
 /* moves a scene item index (blame linux distros for using older Qt builds) */
@@ -640,347 +21,6 @@ static inline void MoveItem(QVector<OBSSceneItem> &items, int oldIdx, int newIdx
 	items.insert(newIdx, item);
 }
 
-/* reorders list optimally with model reorder funcs */
-void SourceTreeModel::ReorderItems()
-{
-	OBSScene scene = GetCurrentScene();
-
-	QVector<OBSSceneItem> newitems;
-	obs_scene_enum_items(scene, enumItem, &newitems);
-
-	/* if item list has changed size, do full reset */
-	if (newitems.count() != items.count()) {
-		SceneChanged();
-		return;
-	}
-
-	for (;;) {
-		int idx1Old = 0;
-		int idx1New = 0;
-		int count;
-		int i;
-
-		/* find first starting changed item index */
-		for (i = 0; i < newitems.count(); i++) {
-			obs_sceneitem_t *oldItem = items[i];
-			obs_sceneitem_t *newItem = newitems[i];
-			if (oldItem != newItem) {
-				idx1Old = i;
-				break;
-			}
-		}
-
-		/* if everything is the same, break */
-		if (i == newitems.count()) {
-			break;
-		}
-
-		/* find new starting index */
-		for (i = idx1Old + 1; i < newitems.count(); i++) {
-			obs_sceneitem_t *oldItem = items[idx1Old];
-			obs_sceneitem_t *newItem = newitems[i];
-
-			if (oldItem == newItem) {
-				idx1New = i;
-				break;
-			}
-		}
-
-		/* if item could not be found, do full reset */
-		if (i == newitems.count()) {
-			SceneChanged();
-			return;
-		}
-
-		/* get move count */
-		for (count = 1; (idx1New + count) < newitems.count(); count++) {
-			int oldIdx = idx1Old + count;
-			int newIdx = idx1New + count;
-
-			obs_sceneitem_t *oldItem = items[oldIdx];
-			obs_sceneitem_t *newItem = newitems[newIdx];
-
-			if (oldItem != newItem) {
-				break;
-			}
-		}
-
-		/* move items */
-		beginMoveRows(QModelIndex(), idx1Old, idx1Old + count - 1, QModelIndex(), idx1New + count);
-		for (i = 0; i < count; i++) {
-			int to = idx1New + count;
-			if (to > idx1Old)
-				to--;
-			MoveItem(items, idx1Old, to);
-		}
-		endMoveRows();
-	}
-}
-
-void SourceTreeModel::Add(obs_sceneitem_t *item)
-{
-	if (obs_sceneitem_is_group(item)) {
-		SceneChanged();
-	} else {
-		beginInsertRows(QModelIndex(), 0, 0);
-		items.insert(0, item);
-		endInsertRows();
-
-		st->UpdateWidget(createIndex(0, 0, nullptr), item);
-	}
-}
-
-void SourceTreeModel::Remove(obs_sceneitem_t *item)
-{
-	int idx = -1;
-	for (int i = 0; i < items.count(); i++) {
-		if (items[i] == item) {
-			idx = i;
-			break;
-		}
-	}
-
-	if (idx == -1)
-		return;
-
-	int startIdx = idx;
-	int endIdx = idx;
-
-	bool is_group = obs_sceneitem_is_group(item);
-	if (is_group) {
-		obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-		for (int i = endIdx + 1; i < items.count(); i++) {
-			obs_sceneitem_t *subitem = items[i];
-			obs_scene_t *subscene = obs_sceneitem_get_scene(subitem);
-
-			if (subscene == scene)
-				endIdx = i;
-			else
-				break;
-		}
-	}
-
-	beginRemoveRows(QModelIndex(), startIdx, endIdx);
-	items.remove(idx, endIdx - startIdx + 1);
-	endRemoveRows();
-
-	if (is_group)
-		UpdateGroupState(true);
-
-	OBSBasic::Get()->UpdateContextBarDeferred();
-}
-
-OBSSceneItem SourceTreeModel::Get(int idx)
-{
-	if (idx == -1 || idx >= items.count())
-		return OBSSceneItem();
-	return items[idx];
-}
-
-SourceTreeModel::SourceTreeModel(SourceTree *st_) : QAbstractListModel(st_), st(st_)
-{
-	obs_frontend_add_event_callback(OBSFrontendEvent, this);
-}
-
-int SourceTreeModel::rowCount(const QModelIndex &parent) const
-{
-	return parent.isValid() ? 0 : items.count();
-}
-
-QVariant SourceTreeModel::data(const QModelIndex &index, int role) const
-{
-	if (role == Qt::AccessibleTextRole) {
-		OBSSceneItem item = items[index.row()];
-		obs_source_t *source = obs_sceneitem_get_source(item);
-		return QVariant(QT_UTF8(obs_source_get_name(source)));
-	}
-
-	return QVariant();
-}
-
-Qt::ItemFlags SourceTreeModel::flags(const QModelIndex &index) const
-{
-	if (!index.isValid())
-		return QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled;
-
-	obs_sceneitem_t *item = items[index.row()];
-	bool is_group = obs_sceneitem_is_group(item);
-
-	return QAbstractListModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled |
-	       (is_group ? Qt::ItemIsDropEnabled : Qt::NoItemFlags);
-}
-
-Qt::DropActions SourceTreeModel::supportedDropActions() const
-{
-	return QAbstractItemModel::supportedDropActions() | Qt::MoveAction;
-}
-
-QString SourceTreeModel::GetNewGroupName()
-{
-	OBSScene scene = GetCurrentScene();
-	QString name = QTStr("Group");
-
-	int i = 2;
-	for (;;) {
-		OBSSourceAutoRelease group = obs_get_source_by_name(QT_TO_UTF8(name));
-		if (!group)
-			break;
-		name = QTStr("Basic.Main.Group").arg(QString::number(i++));
-	}
-
-	return name;
-}
-
-void SourceTreeModel::AddGroup()
-{
-	QString name = GetNewGroupName();
-	obs_sceneitem_t *group = obs_scene_add_group(GetCurrentScene(), QT_TO_UTF8(name));
-	if (!group)
-		return;
-
-	beginInsertRows(QModelIndex(), 0, 0);
-	items.insert(0, group);
-	endInsertRows();
-
-	st->UpdateWidget(createIndex(0, 0, nullptr), group);
-	UpdateGroupState(true);
-
-	QMetaObject::invokeMethod(st, "Edit", Qt::QueuedConnection, Q_ARG(int, 0));
-}
-
-void SourceTreeModel::GroupSelectedItems(QModelIndexList &indices)
-{
-	if (indices.count() == 0)
-		return;
-
-	OBSBasic *main = OBSBasic::Get();
-	OBSScene scene = GetCurrentScene();
-	QString name = GetNewGroupName();
-
-	QVector<obs_sceneitem_t *> item_order;
-
-	for (int i = indices.count() - 1; i >= 0; i--) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		item_order << item;
-	}
-
-	st->undoSceneData = main->BackupScene(scene);
-
-	obs_sceneitem_t *item = obs_scene_insert_group(scene, QT_TO_UTF8(name), item_order.data(), item_order.size());
-	if (!item) {
-		st->undoSceneData = nullptr;
-		return;
-	}
-
-	main->undo_s.push_disabled();
-
-	for (obs_sceneitem_t *item : item_order)
-		obs_sceneitem_select(item, false);
-
-	hasGroups = true;
-	st->UpdateWidgets(true);
-
-	obs_sceneitem_select(item, true);
-
-	/* ----------------------------------------------------------------- */
-	/* obs_scene_insert_group triggers a full refresh of scene items via */
-	/* the item_add signal. No need to insert a row, just edit the one   */
-	/* that's created automatically.                                     */
-
-	int newIdx = indices[0].row();
-	QMetaObject::invokeMethod(st, "NewGroupEdit", Qt::QueuedConnection, Q_ARG(int, newIdx));
-}
-
-void SourceTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
-{
-	OBSBasic *main = OBSBasic::Get();
-	if (indices.count() == 0)
-		return;
-
-	OBSScene scene = main->GetCurrentScene();
-	OBSData undoData = main->BackupScene(scene);
-
-	for (int i = indices.count() - 1; i >= 0; i--) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		obs_sceneitem_group_ungroup(item);
-	}
-
-	SceneChanged();
-
-	OBSData redoData = main->BackupScene(scene);
-	main->CreateSceneUndoRedoAction(QTStr("Basic.Main.Ungroup"), undoData, redoData);
-}
-
-void SourceTreeModel::ExpandGroup(obs_sceneitem_t *item)
-{
-	int itemIdx = items.indexOf(item);
-	if (itemIdx == -1)
-		return;
-
-	itemIdx++;
-
-	obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-	QVector<OBSSceneItem> subItems;
-	obs_scene_enum_items(scene, enumItem, &subItems);
-
-	if (!subItems.size())
-		return;
-
-	beginInsertRows(QModelIndex(), itemIdx, itemIdx + subItems.size() - 1);
-	for (int i = 0; i < subItems.size(); i++)
-		items.insert(i + itemIdx, subItems[i]);
-	endInsertRows();
-
-	st->UpdateWidgets();
-}
-
-void SourceTreeModel::CollapseGroup(obs_sceneitem_t *item)
-{
-	int startIdx = -1;
-	int endIdx = -1;
-
-	obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-	for (int i = 0; i < items.size(); i++) {
-		obs_scene_t *itemScene = obs_sceneitem_get_scene(items[i]);
-
-		if (itemScene == scene) {
-			if (startIdx == -1)
-				startIdx = i;
-			endIdx = i;
-		}
-	}
-
-	if (startIdx == -1)
-		return;
-
-	beginRemoveRows(QModelIndex(), startIdx, endIdx);
-	items.remove(startIdx, endIdx - startIdx + 1);
-	endRemoveRows();
-}
-
-void SourceTreeModel::UpdateGroupState(bool update)
-{
-	bool nowHasGroups = false;
-	for (auto &item : items) {
-		if (obs_sceneitem_is_group(item)) {
-			nowHasGroups = true;
-			break;
-		}
-	}
-
-	if (nowHasGroups != hasGroups) {
-		hasGroups = nowHasGroups;
-		if (update) {
-			st->UpdateWidgets(true);
-		}
-	}
-}
-
-/* ========================================================================= */
-
 SourceTree::SourceTree(QWidget *parent_) : QListView(parent_)
 {
 	SourceTreeModel *stm_ = new SourceTreeModel(this);
@@ -1067,8 +107,6 @@ void SourceTree::SelectItem(obs_sceneitem_t *sceneitem, bool select)
 		selectionModel()->select(index, select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
 }
 
-Q_DECLARE_METATYPE(OBSSceneItem);
-
 void SourceTree::mouseDoubleClickEvent(QMouseEvent *event)
 {
 	if (event->button() == Qt::LeftButton)
@@ -1598,16 +636,3 @@ void SourceTree::paintEvent(QPaintEvent *event)
 		QListView::paintEvent(event);
 	}
 }
-
-SourceTreeDelegate::SourceTreeDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
-
-QSize SourceTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
-{
-	SourceTree *tree = qobject_cast<SourceTree *>(parent());
-	QWidget *item = tree->indexWidget(index);
-
-	if (!item)
-		return (QSize(0, 0));
-
-	return (QSize(option.widget->minimumWidth(), item->height()));
-}

+ 3 - 130
frontend/components/SourceTree.hpp

@@ -1,130 +1,11 @@
 #pragma once
 
-#include <QList>
-#include <QVector>
-#include <QPointer>
+#include "SourceTreeItem.hpp"
+#include "SourceTreeModel.hpp"
+
 #include <QListView>
-#include <QCheckBox>
 #include <QStaticText>
 #include <QSvgRenderer>
-#include <QAbstractListModel>
-#include <QStyledItemDelegate>
-#include <obs.hpp>
-#include <obs-frontend-api.h>
-
-class QLabel;
-class OBSSourceLabel;
-class QCheckBox;
-class QLineEdit;
-class SourceTree;
-class QSpacerItem;
-class QHBoxLayout;
-class VisibilityItemWidget;
-
-class SourceTreeItem : public QFrame {
-	Q_OBJECT
-
-	friend class SourceTree;
-	friend class SourceTreeModel;
-
-	void mouseDoubleClickEvent(QMouseEvent *event) override;
-	void enterEvent(QEnterEvent *event) override;
-	void leaveEvent(QEvent *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);
-	bool IsEditing();
-
-private:
-	QSpacerItem *spacer = nullptr;
-	QCheckBox *expand = nullptr;
-	QLabel *iconLabel = nullptr;
-	QCheckBox *vis = nullptr;
-	QCheckBox *lock = nullptr;
-	QHBoxLayout *boxLayout = nullptr;
-	OBSSourceLabel *label = nullptr;
-
-	QLineEdit *editor = nullptr;
-
-	std::string newName;
-
-	SourceTree *tree;
-	OBSSceneItem sceneitem;
-	std::vector<OBSSignal> sigs;
-
-	virtual void paintEvent(QPaintEvent *event) override;
-
-	void ExitEditModeInternal(bool save);
-
-private slots:
-	void Clear();
-
-	void EnterEditMode();
-	void ExitEditMode(bool save);
-
-	void VisibilityChanged(bool visible);
-	void LockedChanged(bool locked);
-
-	void ExpandClicked(bool checked);
-
-	void Select();
-	void Deselect();
-};
-
-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);
-
-	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
@@ -192,11 +73,3 @@ protected:
 
 	virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
 };
-
-class SourceTreeDelegate : public QStyledItemDelegate {
-	Q_OBJECT
-
-public:
-	SourceTreeDelegate(QObject *parent);
-	virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
-};

+ 3 - 1600
frontend/components/SourceTreeDelegate.cpp

@@ -1,1603 +1,6 @@
-#include "window-basic-main.hpp"
-#include "obs-app.hpp"
-#include "source-tree.hpp"
-#include "platform.hpp"
-#include "source-label.hpp"
-
-#include <qt-wrappers.hpp>
-#include <obs-frontend-api.h>
-#include <obs.h>
-
-#include <string>
-
-#include <QLabel>
-#include <QLineEdit>
-#include <QSpacerItem>
-#include <QPushButton>
-#include <QVBoxLayout>
-#include <QHBoxLayout>
-#include <QMouseEvent>
-#include <QAccessible>
-
-#include <QStylePainter>
-#include <QStyleOptionFocusRect>
-
-static inline OBSScene GetCurrentScene()
-{
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-	return main->GetCurrentScene();
-}
-
-/* ========================================================================= */
-
-SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) : tree(tree_), sceneitem(sceneitem_)
-{
-	setAttribute(Qt::WA_TranslucentBackground);
-	setMouseTracking(true);
-
-	obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-	const char *name = obs_source_get_name(source);
-
-	OBSDataAutoRelease privData = obs_sceneitem_get_private_settings(sceneitem);
-	int preset = obs_data_get_int(privData, "color-preset");
-
-	if (preset == 1) {
-		const char *color = obs_data_get_string(privData, "color");
-		std::string col = "background: ";
-		col += color;
-		setStyleSheet(col.c_str());
-	} else if (preset > 1) {
-		setStyleSheet("");
-		setProperty("bgColor", preset - 1);
-	} else {
-		setStyleSheet("background: none");
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-	const char *id = obs_source_get_id(source);
-
-	bool sourceVisible = obs_sceneitem_visible(sceneitem);
-
-	if (tree->iconsVisible) {
-		QIcon icon;
-
-		if (strcmp(id, "scene") == 0)
-			icon = main->GetSceneIcon();
-		else if (strcmp(id, "group") == 0)
-			icon = main->GetGroupIcon();
-		else
-			icon = main->GetSourceIcon(id);
-
-		QPixmap pixmap = icon.pixmap(QSize(16, 16));
-
-		iconLabel = new QLabel();
-		iconLabel->setPixmap(pixmap);
-		iconLabel->setEnabled(sourceVisible);
-		iconLabel->setStyleSheet("background: none");
-		iconLabel->setProperty("class", "source-icon");
-	}
-
-	vis = new QCheckBox();
-	vis->setProperty("class", "checkbox-icon indicator-visibility");
-	vis->setChecked(sourceVisible);
-	vis->setAccessibleName(QTStr("Basic.Main.Sources.Visibility"));
-	vis->setAccessibleDescription(QTStr("Basic.Main.Sources.VisibilityDescription").arg(name));
-
-	lock = new QCheckBox();
-	lock->setProperty("class", "checkbox-icon indicator-lock");
-	lock->setChecked(obs_sceneitem_locked(sceneitem));
-	lock->setAccessibleName(QTStr("Basic.Main.Sources.Lock"));
-	lock->setAccessibleDescription(QTStr("Basic.Main.Sources.LockDescription").arg(name));
-
-	label = new OBSSourceLabel(source);
-	label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
-	label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
-	label->setAttribute(Qt::WA_TranslucentBackground);
-	label->setEnabled(sourceVisible);
-
-#ifdef __APPLE__
-	vis->setAttribute(Qt::WA_LayoutUsesWidgetRect);
-	lock->setAttribute(Qt::WA_LayoutUsesWidgetRect);
-#endif
-
-	boxLayout = new QHBoxLayout();
-
-	boxLayout->setContentsMargins(0, 0, 0, 0);
-	boxLayout->setSpacing(0);
-	if (iconLabel) {
-		boxLayout->addWidget(iconLabel);
-		boxLayout->addSpacing(2);
-	}
-	boxLayout->addWidget(label);
-	boxLayout->addWidget(vis);
-	boxLayout->addWidget(lock);
-#ifdef __APPLE__
-	/* Hack: Fixes a bug where scrollbars would be above the lock icon */
-	boxLayout->addSpacing(16);
-#endif
-
-	Update(false);
-
-	setLayout(boxLayout);
-
-	/* --------------------------------------------------------- */
-
-	auto setItemVisible = [this](bool val) {
-		obs_scene_t *scene = obs_sceneitem_get_scene(sceneitem);
-		obs_source_t *scenesource = obs_scene_get_source(scene);
-		int64_t id = obs_sceneitem_get_id(sceneitem);
-		const char *name = obs_source_get_name(scenesource);
-		const char *uuid = obs_source_get_uuid(scenesource);
-		obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-
-		auto undo_redo = [](const std::string &uuid, int64_t id, bool val) {
-			OBSSourceAutoRelease s = obs_get_source_by_uuid(uuid.c_str());
-			obs_scene_t *sc = obs_group_or_scene_from_source(s);
-			obs_sceneitem_t *si = obs_scene_find_sceneitem_by_id(sc, id);
-			if (si)
-				obs_sceneitem_set_visible(si, val);
-		};
-
-		QString str = QTStr(val ? "Undo.ShowSceneItem" : "Undo.HideSceneItem");
-
-		OBSBasic *main = OBSBasic::Get();
-		main->undo_s.add_action(str.arg(obs_source_get_name(source), name),
-					std::bind(undo_redo, std::placeholders::_1, id, !val),
-					std::bind(undo_redo, std::placeholders::_1, id, val), uuid, uuid);
-
-		QSignalBlocker sourcesSignalBlocker(this);
-		obs_sceneitem_set_visible(sceneitem, val);
-	};
-
-	auto setItemLocked = [this](bool checked) {
-		QSignalBlocker sourcesSignalBlocker(this);
-		obs_sceneitem_set_locked(sceneitem, checked);
-	};
-
-	connect(vis, &QAbstractButton::clicked, setItemVisible);
-	connect(lock, &QAbstractButton::clicked, setItemLocked);
-}
-
-void SourceTreeItem::paintEvent(QPaintEvent *event)
-{
-	QStyleOption opt;
-	opt.initFrom(this);
-	QPainter p(this);
-	style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
-
-	QWidget::paintEvent(event);
-}
-
-void SourceTreeItem::DisconnectSignals()
-{
-	sigs.clear();
-}
-
-void SourceTreeItem::Clear()
-{
-	DisconnectSignals();
-	sceneitem = nullptr;
-}
-
-void SourceTreeItem::ReconnectSignals()
-{
-	if (!sceneitem)
-		return;
-
-	DisconnectSignals();
-
-	/* --------------------------------------------------------- */
-
-	auto removeItem = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-		obs_scene_t *curScene = (obs_scene_t *)calldata_ptr(cd, "scene");
-
-		if (curItem == this_->sceneitem) {
-			QMetaObject::invokeMethod(this_->tree, "Remove", Q_ARG(OBSSceneItem, curItem),
-						  Q_ARG(OBSScene, curScene));
-			curItem = nullptr;
-		}
-		if (!curItem)
-			QMetaObject::invokeMethod(this_, "Clear");
-	};
-
-	auto itemVisible = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-		bool visible = calldata_bool(cd, "visible");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "VisibilityChanged", Q_ARG(bool, visible));
-	};
-
-	auto itemLocked = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-		bool locked = calldata_bool(cd, "locked");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "LockedChanged", Q_ARG(bool, locked));
-	};
-
-	auto itemSelect = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "Select");
-	};
-
-	auto itemDeselect = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "Deselect");
-	};
-
-	auto reorderGroup = [](void *data, calldata_t *) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		QMetaObject::invokeMethod(this_->tree, "ReorderItems");
-	};
-
-	obs_scene_t *scene = obs_sceneitem_get_scene(sceneitem);
-	obs_source_t *sceneSource = obs_scene_get_source(scene);
-	signal_handler_t *signal = obs_source_get_signal_handler(sceneSource);
-
-	sigs.emplace_back(signal, "remove", removeItem, this);
-	sigs.emplace_back(signal, "item_remove", removeItem, this);
-	sigs.emplace_back(signal, "item_visible", itemVisible, this);
-	sigs.emplace_back(signal, "item_locked", itemLocked, this);
-	sigs.emplace_back(signal, "item_select", itemSelect, this);
-	sigs.emplace_back(signal, "item_deselect", itemDeselect, this);
-
-	if (obs_sceneitem_is_group(sceneitem)) {
-		obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-		signal = obs_source_get_signal_handler(source);
-
-		sigs.emplace_back(signal, "reorder", reorderGroup, this);
-	}
-
-	/* --------------------------------------------------------- */
-
-	auto removeSource = [](void *data, calldata_t *) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		this_->DisconnectSignals();
-		this_->sceneitem = nullptr;
-		QMetaObject::invokeMethod(this_->tree, "RefreshItems");
-	};
-
-	obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-	signal = obs_source_get_signal_handler(source);
-	sigs.emplace_back(signal, "remove", removeSource, this);
-}
-
-void SourceTreeItem::mouseDoubleClickEvent(QMouseEvent *event)
-{
-	QWidget::mouseDoubleClickEvent(event);
-
-	if (expand) {
-		expand->setChecked(!expand->isChecked());
-	} else {
-		obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-		OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-		if (obs_source_configurable(source)) {
-			main->CreatePropertiesWindow(source);
-		}
-	}
-}
-
-void SourceTreeItem::enterEvent(QEnterEvent *event)
-{
-	QWidget::enterEvent(event);
-
-	OBSBasicPreview *preview = OBSBasicPreview::Get();
-
-	std::lock_guard<std::mutex> lock(preview->selectMutex);
-	preview->hoveredPreviewItems.clear();
-	preview->hoveredPreviewItems.push_back(sceneitem);
-}
-
-void SourceTreeItem::leaveEvent(QEvent *event)
-{
-	QWidget::leaveEvent(event);
-
-	OBSBasicPreview *preview = OBSBasicPreview::Get();
-
-	std::lock_guard<std::mutex> lock(preview->selectMutex);
-	preview->hoveredPreviewItems.clear();
-}
-
-bool SourceTreeItem::IsEditing()
-{
-	return editor != nullptr;
-}
-
-void SourceTreeItem::EnterEditMode()
-{
-	setFocusPolicy(Qt::StrongFocus);
-	int index = boxLayout->indexOf(label);
-	boxLayout->removeWidget(label);
-	editor = new QLineEdit(label->text());
-	editor->setStyleSheet("background: none");
-	editor->selectAll();
-	editor->installEventFilter(this);
-	boxLayout->insertWidget(index, editor);
-	setFocusProxy(editor);
-}
-
-void SourceTreeItem::ExitEditMode(bool save)
-{
-	ExitEditModeInternal(save);
-
-	if (tree->undoSceneData) {
-		OBSBasic *main = OBSBasic::Get();
-		main->undo_s.pop_disabled();
-
-		OBSData redoSceneData = main->BackupScene(GetCurrentScene());
-
-		QString text = QTStr("Undo.GroupItems").arg(newName.c_str());
-		main->CreateSceneUndoRedoAction(text, tree->undoSceneData, redoSceneData);
-
-		tree->undoSceneData = nullptr;
-	}
-}
-
-void SourceTreeItem::ExitEditModeInternal(bool save)
-{
-	if (!editor) {
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-	OBSScene scene = main->GetCurrentScene();
-
-	newName = QT_TO_UTF8(editor->text());
-
-	setFocusProxy(nullptr);
-	int index = boxLayout->indexOf(editor);
-	boxLayout->removeWidget(editor);
-	delete editor;
-	editor = nullptr;
-	setFocusPolicy(Qt::NoFocus);
-	boxLayout->insertWidget(index, label);
-	setFocus();
-
-	/* ----------------------------------------- */
-	/* check for empty string                    */
-
-	if (!save)
-		return;
-
-	if (newName.empty()) {
-		OBSMessageBox::information(main, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text"));
-		return;
-	}
-
-	/* ----------------------------------------- */
-	/* Check for same name                       */
-
-	obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-	if (newName == obs_source_get_name(source))
-		return;
-
-	/* ----------------------------------------- */
-	/* check for existing source                 */
-
-	OBSSourceAutoRelease existingSource = obs_get_source_by_name(newName.c_str());
-	bool exists = !!existingSource;
-
-	if (exists) {
-		OBSMessageBox::information(main, QTStr("NameExists.Title"), QTStr("NameExists.Text"));
-		return;
-	}
-
-	/* ----------------------------------------- */
-	/* rename                                    */
-
-	QSignalBlocker sourcesSignalBlocker(this);
-	std::string prevName(obs_source_get_name(source));
-	std::string scene_uuid = obs_source_get_uuid(main->GetCurrentSceneSource());
-	auto undo = [scene_uuid, prevName, main](const std::string &data) {
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
-		obs_source_set_name(source, prevName.c_str());
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-	};
-
-	std::string editedName = newName;
-
-	auto redo = [scene_uuid, main, editedName](const std::string &data) {
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
-		obs_source_set_name(source, editedName.c_str());
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-	};
-
-	const char *uuid = obs_source_get_uuid(source);
-	main->undo_s.add_action(QTStr("Undo.Rename").arg(newName.c_str()), undo, redo, uuid, uuid);
-
-	obs_source_set_name(source, newName.c_str());
-}
-
-bool SourceTreeItem::eventFilter(QObject *object, QEvent *event)
-{
-	if (editor != object)
-		return false;
-
-	if (LineEditCanceled(event)) {
-		QMetaObject::invokeMethod(this, "ExitEditMode", Qt::QueuedConnection, Q_ARG(bool, false));
-		return true;
-	}
-	if (LineEditChanged(event)) {
-		QMetaObject::invokeMethod(this, "ExitEditMode", Qt::QueuedConnection, Q_ARG(bool, true));
-		return true;
-	}
-
-	return false;
-}
-
-void SourceTreeItem::VisibilityChanged(bool visible)
-{
-	if (iconLabel) {
-		iconLabel->setEnabled(visible);
-	}
-	label->setEnabled(visible);
-	vis->setChecked(visible);
-}
-
-void SourceTreeItem::LockedChanged(bool locked)
-{
-	lock->setChecked(locked);
-	OBSBasic::Get()->UpdateEditMenu();
-}
-
-void SourceTreeItem::Update(bool force)
-{
-	OBSScene scene = GetCurrentScene();
-	obs_scene_t *itemScene = obs_sceneitem_get_scene(sceneitem);
-
-	Type newType;
-
-	/* ------------------------------------------------- */
-	/* if it's a group item, insert group checkbox       */
-
-	if (obs_sceneitem_is_group(sceneitem)) {
-		newType = Type::Group;
-
-		/* ------------------------------------------------- */
-		/* if it's a group sub-item                          */
-
-	} else if (itemScene != scene) {
-		newType = Type::SubItem;
-
-		/* ------------------------------------------------- */
-		/* if it's a regular item                            */
-
-	} else {
-		newType = Type::Item;
-	}
-
-	/* ------------------------------------------------- */
-
-	if (!force && newType == type) {
-		return;
-	}
-
-	/* ------------------------------------------------- */
-
-	ReconnectSignals();
-
-	if (spacer) {
-		boxLayout->removeItem(spacer);
-		delete spacer;
-		spacer = nullptr;
-	}
-
-	if (type == Type::Group) {
-		boxLayout->removeWidget(expand);
-		expand->deleteLater();
-		expand = nullptr;
-	}
-
-	type = newType;
-
-	if (type == Type::SubItem) {
-		spacer = new QSpacerItem(16, 1);
-		boxLayout->insertItem(0, spacer);
-
-	} else if (type == Type::Group) {
-		expand = new QCheckBox();
-		expand->setProperty("class", "checkbox-icon indicator-expand");
-#ifdef __APPLE__
-		expand->setAttribute(Qt::WA_LayoutUsesWidgetRect);
-#endif
-		boxLayout->insertWidget(0, expand);
-
-		OBSDataAutoRelease data = obs_sceneitem_get_private_settings(sceneitem);
-		expand->blockSignals(true);
-		expand->setChecked(obs_data_get_bool(data, "collapsed"));
-		expand->blockSignals(false);
-
-		connect(expand, &QPushButton::toggled, this, &SourceTreeItem::ExpandClicked);
-
-	} else {
-		spacer = new QSpacerItem(3, 1);
-		boxLayout->insertItem(0, spacer);
-	}
-}
-
-void SourceTreeItem::ExpandClicked(bool checked)
-{
-	OBSDataAutoRelease data = obs_sceneitem_get_private_settings(sceneitem);
-
-	obs_data_set_bool(data, "collapsed", checked);
-
-	if (!checked)
-		tree->GetStm()->ExpandGroup(sceneitem);
-	else
-		tree->GetStm()->CollapseGroup(sceneitem);
-}
-
-void SourceTreeItem::Select()
-{
-	tree->SelectItem(sceneitem, true);
-	OBSBasic::Get()->UpdateContextBarDeferred();
-	OBSBasic::Get()->UpdateEditMenu();
-}
-
-void SourceTreeItem::Deselect()
-{
-	tree->SelectItem(sceneitem, false);
-	OBSBasic::Get()->UpdateContextBarDeferred();
-	OBSBasic::Get()->UpdateEditMenu();
-}
-
-/* ========================================================================= */
-
-void SourceTreeModel::OBSFrontendEvent(enum obs_frontend_event event, void *ptr)
-{
-	SourceTreeModel *stm = reinterpret_cast<SourceTreeModel *>(ptr);
-
-	switch (event) {
-	case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
-		stm->SceneChanged();
-		break;
-	case OBS_FRONTEND_EVENT_EXIT:
-		stm->Clear();
-		obs_frontend_remove_event_callback(OBSFrontendEvent, stm);
-		break;
-	case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP:
-		stm->Clear();
-		break;
-	default:
-		break;
-	}
-}
-
-void SourceTreeModel::Clear()
-{
-	beginResetModel();
-	items.clear();
-	endResetModel();
-
-	hasGroups = false;
-}
-
-static bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
-{
-	QVector<OBSSceneItem> &items = *reinterpret_cast<QVector<OBSSceneItem> *>(ptr);
-
-	obs_source_t *src = obs_sceneitem_get_source(item);
-	if (obs_source_removed(src)) {
-		return true;
-	}
-
-	if (obs_sceneitem_is_group(item)) {
-		OBSDataAutoRelease data = obs_sceneitem_get_private_settings(item);
-
-		bool collapse = obs_data_get_bool(data, "collapsed");
-		if (!collapse) {
-			obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-			obs_scene_enum_items(scene, enumItem, &items);
-		}
-	}
-
-	items.insert(0, item);
-	return true;
-}
-
-void SourceTreeModel::SceneChanged()
-{
-	OBSScene scene = GetCurrentScene();
-
-	beginResetModel();
-	items.clear();
-	obs_scene_enum_items(scene, enumItem, &items);
-	endResetModel();
-
-	UpdateGroupState(false);
-	st->ResetWidgets();
-
-	for (int i = 0; i < items.count(); i++) {
-		bool select = obs_sceneitem_selected(items[i]);
-		QModelIndex index = createIndex(i, 0);
-
-		st->selectionModel()->select(index,
-					     select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
-	}
-}
-
-/* moves a scene item index (blame linux distros for using older Qt builds) */
-static inline void MoveItem(QVector<OBSSceneItem> &items, int oldIdx, int newIdx)
-{
-	OBSSceneItem item = items[oldIdx];
-	items.remove(oldIdx);
-	items.insert(newIdx, item);
-}
-
-/* reorders list optimally with model reorder funcs */
-void SourceTreeModel::ReorderItems()
-{
-	OBSScene scene = GetCurrentScene();
-
-	QVector<OBSSceneItem> newitems;
-	obs_scene_enum_items(scene, enumItem, &newitems);
-
-	/* if item list has changed size, do full reset */
-	if (newitems.count() != items.count()) {
-		SceneChanged();
-		return;
-	}
-
-	for (;;) {
-		int idx1Old = 0;
-		int idx1New = 0;
-		int count;
-		int i;
-
-		/* find first starting changed item index */
-		for (i = 0; i < newitems.count(); i++) {
-			obs_sceneitem_t *oldItem = items[i];
-			obs_sceneitem_t *newItem = newitems[i];
-			if (oldItem != newItem) {
-				idx1Old = i;
-				break;
-			}
-		}
-
-		/* if everything is the same, break */
-		if (i == newitems.count()) {
-			break;
-		}
-
-		/* find new starting index */
-		for (i = idx1Old + 1; i < newitems.count(); i++) {
-			obs_sceneitem_t *oldItem = items[idx1Old];
-			obs_sceneitem_t *newItem = newitems[i];
-
-			if (oldItem == newItem) {
-				idx1New = i;
-				break;
-			}
-		}
-
-		/* if item could not be found, do full reset */
-		if (i == newitems.count()) {
-			SceneChanged();
-			return;
-		}
-
-		/* get move count */
-		for (count = 1; (idx1New + count) < newitems.count(); count++) {
-			int oldIdx = idx1Old + count;
-			int newIdx = idx1New + count;
-
-			obs_sceneitem_t *oldItem = items[oldIdx];
-			obs_sceneitem_t *newItem = newitems[newIdx];
-
-			if (oldItem != newItem) {
-				break;
-			}
-		}
-
-		/* move items */
-		beginMoveRows(QModelIndex(), idx1Old, idx1Old + count - 1, QModelIndex(), idx1New + count);
-		for (i = 0; i < count; i++) {
-			int to = idx1New + count;
-			if (to > idx1Old)
-				to--;
-			MoveItem(items, idx1Old, to);
-		}
-		endMoveRows();
-	}
-}
-
-void SourceTreeModel::Add(obs_sceneitem_t *item)
-{
-	if (obs_sceneitem_is_group(item)) {
-		SceneChanged();
-	} else {
-		beginInsertRows(QModelIndex(), 0, 0);
-		items.insert(0, item);
-		endInsertRows();
-
-		st->UpdateWidget(createIndex(0, 0, nullptr), item);
-	}
-}
-
-void SourceTreeModel::Remove(obs_sceneitem_t *item)
-{
-	int idx = -1;
-	for (int i = 0; i < items.count(); i++) {
-		if (items[i] == item) {
-			idx = i;
-			break;
-		}
-	}
-
-	if (idx == -1)
-		return;
-
-	int startIdx = idx;
-	int endIdx = idx;
-
-	bool is_group = obs_sceneitem_is_group(item);
-	if (is_group) {
-		obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-		for (int i = endIdx + 1; i < items.count(); i++) {
-			obs_sceneitem_t *subitem = items[i];
-			obs_scene_t *subscene = obs_sceneitem_get_scene(subitem);
-
-			if (subscene == scene)
-				endIdx = i;
-			else
-				break;
-		}
-	}
-
-	beginRemoveRows(QModelIndex(), startIdx, endIdx);
-	items.remove(idx, endIdx - startIdx + 1);
-	endRemoveRows();
-
-	if (is_group)
-		UpdateGroupState(true);
-
-	OBSBasic::Get()->UpdateContextBarDeferred();
-}
-
-OBSSceneItem SourceTreeModel::Get(int idx)
-{
-	if (idx == -1 || idx >= items.count())
-		return OBSSceneItem();
-	return items[idx];
-}
-
-SourceTreeModel::SourceTreeModel(SourceTree *st_) : QAbstractListModel(st_), st(st_)
-{
-	obs_frontend_add_event_callback(OBSFrontendEvent, this);
-}
-
-int SourceTreeModel::rowCount(const QModelIndex &parent) const
-{
-	return parent.isValid() ? 0 : items.count();
-}
-
-QVariant SourceTreeModel::data(const QModelIndex &index, int role) const
-{
-	if (role == Qt::AccessibleTextRole) {
-		OBSSceneItem item = items[index.row()];
-		obs_source_t *source = obs_sceneitem_get_source(item);
-		return QVariant(QT_UTF8(obs_source_get_name(source)));
-	}
-
-	return QVariant();
-}
-
-Qt::ItemFlags SourceTreeModel::flags(const QModelIndex &index) const
-{
-	if (!index.isValid())
-		return QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled;
-
-	obs_sceneitem_t *item = items[index.row()];
-	bool is_group = obs_sceneitem_is_group(item);
-
-	return QAbstractListModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled |
-	       (is_group ? Qt::ItemIsDropEnabled : Qt::NoItemFlags);
-}
-
-Qt::DropActions SourceTreeModel::supportedDropActions() const
-{
-	return QAbstractItemModel::supportedDropActions() | Qt::MoveAction;
-}
-
-QString SourceTreeModel::GetNewGroupName()
-{
-	OBSScene scene = GetCurrentScene();
-	QString name = QTStr("Group");
-
-	int i = 2;
-	for (;;) {
-		OBSSourceAutoRelease group = obs_get_source_by_name(QT_TO_UTF8(name));
-		if (!group)
-			break;
-		name = QTStr("Basic.Main.Group").arg(QString::number(i++));
-	}
-
-	return name;
-}
-
-void SourceTreeModel::AddGroup()
-{
-	QString name = GetNewGroupName();
-	obs_sceneitem_t *group = obs_scene_add_group(GetCurrentScene(), QT_TO_UTF8(name));
-	if (!group)
-		return;
-
-	beginInsertRows(QModelIndex(), 0, 0);
-	items.insert(0, group);
-	endInsertRows();
-
-	st->UpdateWidget(createIndex(0, 0, nullptr), group);
-	UpdateGroupState(true);
-
-	QMetaObject::invokeMethod(st, "Edit", Qt::QueuedConnection, Q_ARG(int, 0));
-}
-
-void SourceTreeModel::GroupSelectedItems(QModelIndexList &indices)
-{
-	if (indices.count() == 0)
-		return;
-
-	OBSBasic *main = OBSBasic::Get();
-	OBSScene scene = GetCurrentScene();
-	QString name = GetNewGroupName();
-
-	QVector<obs_sceneitem_t *> item_order;
-
-	for (int i = indices.count() - 1; i >= 0; i--) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		item_order << item;
-	}
-
-	st->undoSceneData = main->BackupScene(scene);
-
-	obs_sceneitem_t *item = obs_scene_insert_group(scene, QT_TO_UTF8(name), item_order.data(), item_order.size());
-	if (!item) {
-		st->undoSceneData = nullptr;
-		return;
-	}
-
-	main->undo_s.push_disabled();
-
-	for (obs_sceneitem_t *item : item_order)
-		obs_sceneitem_select(item, false);
-
-	hasGroups = true;
-	st->UpdateWidgets(true);
-
-	obs_sceneitem_select(item, true);
-
-	/* ----------------------------------------------------------------- */
-	/* obs_scene_insert_group triggers a full refresh of scene items via */
-	/* the item_add signal. No need to insert a row, just edit the one   */
-	/* that's created automatically.                                     */
-
-	int newIdx = indices[0].row();
-	QMetaObject::invokeMethod(st, "NewGroupEdit", Qt::QueuedConnection, Q_ARG(int, newIdx));
-}
-
-void SourceTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
-{
-	OBSBasic *main = OBSBasic::Get();
-	if (indices.count() == 0)
-		return;
-
-	OBSScene scene = main->GetCurrentScene();
-	OBSData undoData = main->BackupScene(scene);
-
-	for (int i = indices.count() - 1; i >= 0; i--) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		obs_sceneitem_group_ungroup(item);
-	}
-
-	SceneChanged();
-
-	OBSData redoData = main->BackupScene(scene);
-	main->CreateSceneUndoRedoAction(QTStr("Basic.Main.Ungroup"), undoData, redoData);
-}
-
-void SourceTreeModel::ExpandGroup(obs_sceneitem_t *item)
-{
-	int itemIdx = items.indexOf(item);
-	if (itemIdx == -1)
-		return;
-
-	itemIdx++;
-
-	obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-	QVector<OBSSceneItem> subItems;
-	obs_scene_enum_items(scene, enumItem, &subItems);
-
-	if (!subItems.size())
-		return;
-
-	beginInsertRows(QModelIndex(), itemIdx, itemIdx + subItems.size() - 1);
-	for (int i = 0; i < subItems.size(); i++)
-		items.insert(i + itemIdx, subItems[i]);
-	endInsertRows();
-
-	st->UpdateWidgets();
-}
-
-void SourceTreeModel::CollapseGroup(obs_sceneitem_t *item)
-{
-	int startIdx = -1;
-	int endIdx = -1;
-
-	obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-	for (int i = 0; i < items.size(); i++) {
-		obs_scene_t *itemScene = obs_sceneitem_get_scene(items[i]);
-
-		if (itemScene == scene) {
-			if (startIdx == -1)
-				startIdx = i;
-			endIdx = i;
-		}
-	}
-
-	if (startIdx == -1)
-		return;
-
-	beginRemoveRows(QModelIndex(), startIdx, endIdx);
-	items.remove(startIdx, endIdx - startIdx + 1);
-	endRemoveRows();
-}
-
-void SourceTreeModel::UpdateGroupState(bool update)
-{
-	bool nowHasGroups = false;
-	for (auto &item : items) {
-		if (obs_sceneitem_is_group(item)) {
-			nowHasGroups = true;
-			break;
-		}
-	}
-
-	if (nowHasGroups != hasGroups) {
-		hasGroups = nowHasGroups;
-		if (update) {
-			st->UpdateWidgets(true);
-		}
-	}
-}
-
-/* ========================================================================= */
-
-SourceTree::SourceTree(QWidget *parent_) : QListView(parent_)
-{
-	SourceTreeModel *stm_ = new SourceTreeModel(this);
-	setModel(stm_);
-	setStyleSheet(QString("*[bgColor=\"1\"]{background-color:rgba(255,68,68,33%);}"
-			      "*[bgColor=\"2\"]{background-color:rgba(255,255,68,33%);}"
-			      "*[bgColor=\"3\"]{background-color:rgba(68,255,68,33%);}"
-			      "*[bgColor=\"4\"]{background-color:rgba(68,255,255,33%);}"
-			      "*[bgColor=\"5\"]{background-color:rgba(68,68,255,33%);}"
-			      "*[bgColor=\"6\"]{background-color:rgba(255,68,255,33%);}"
-			      "*[bgColor=\"7\"]{background-color:rgba(68,68,68,33%);}"
-			      "*[bgColor=\"8\"]{background-color:rgba(255,255,255,33%);}"));
-
-	UpdateNoSourcesMessage();
-	connect(App(), &OBSApp::StyleChanged, this, &SourceTree::UpdateNoSourcesMessage);
-	connect(App(), &OBSApp::StyleChanged, this, &SourceTree::UpdateIcons);
-
-	setItemDelegate(new SourceTreeDelegate(this));
-}
-
-void SourceTree::UpdateIcons()
-{
-	SourceTreeModel *stm = GetStm();
-	stm->SceneChanged();
-}
-
-void SourceTree::SetIconsVisible(bool visible)
-{
-	SourceTreeModel *stm = GetStm();
-
-	iconsVisible = visible;
-	stm->SceneChanged();
-}
-
-void SourceTree::ResetWidgets()
-{
-	OBSScene scene = GetCurrentScene();
-
-	SourceTreeModel *stm = GetStm();
-	stm->UpdateGroupState(false);
-
-	for (int i = 0; i < stm->items.count(); i++) {
-		QModelIndex index = stm->createIndex(i, 0, nullptr);
-		setIndexWidget(index, new SourceTreeItem(this, stm->items[i]));
-	}
-}
-
-void SourceTree::UpdateWidget(const QModelIndex &idx, obs_sceneitem_t *item)
-{
-	setIndexWidget(idx, new SourceTreeItem(this, item));
-}
-
-void SourceTree::UpdateWidgets(bool force)
-{
-	SourceTreeModel *stm = GetStm();
-
-	for (int i = 0; i < stm->items.size(); i++) {
-		obs_sceneitem_t *item = stm->items[i];
-		SourceTreeItem *widget = GetItemWidget(i);
-
-		if (!widget) {
-			UpdateWidget(stm->createIndex(i, 0), item);
-		} else {
-			widget->Update(force);
-		}
-	}
-}
-
-void SourceTree::SelectItem(obs_sceneitem_t *sceneitem, bool select)
-{
-	SourceTreeModel *stm = GetStm();
-	int i = 0;
-
-	for (; i < stm->items.count(); i++) {
-		if (stm->items[i] == sceneitem)
-			break;
-	}
-
-	if (i == stm->items.count())
-		return;
-
-	QModelIndex index = stm->createIndex(i, 0);
-	if (index.isValid() && select != selectionModel()->isSelected(index))
-		selectionModel()->select(index, select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
-}
-
-Q_DECLARE_METATYPE(OBSSceneItem);
-
-void SourceTree::mouseDoubleClickEvent(QMouseEvent *event)
-{
-	if (event->button() == Qt::LeftButton)
-		QListView::mouseDoubleClickEvent(event);
-}
-
-void SourceTree::dropEvent(QDropEvent *event)
-{
-	if (event->source() != this) {
-		QListView::dropEvent(event);
-		return;
-	}
-
-	OBSBasic *main = OBSBasic::Get();
-
-	OBSScene scene = GetCurrentScene();
-	obs_source_t *scenesource = obs_scene_get_source(scene);
-	SourceTreeModel *stm = GetStm();
-	auto &items = stm->items;
-	QModelIndexList indices = selectedIndexes();
-
-	DropIndicatorPosition indicator = dropIndicatorPosition();
-	int row = indexAt(event->position().toPoint()).row();
-	bool emptyDrop = row == -1;
-
-	if (emptyDrop) {
-		if (!items.size()) {
-			QListView::dropEvent(event);
-			return;
-		}
-
-		row = items.size() - 1;
-		indicator = QAbstractItemView::BelowItem;
-	}
-
-	/* --------------------------------------- */
-	/* store destination group if moving to a  */
-	/* group                                   */
-
-	obs_sceneitem_t *dropItem = items[row]; /* item being dropped on */
-	bool itemIsGroup = obs_sceneitem_is_group(dropItem);
-
-	obs_sceneitem_t *dropGroup = itemIsGroup ? dropItem : obs_sceneitem_get_group(scene, dropItem);
-
-	/* not a group if moving above the group */
-	if (indicator == QAbstractItemView::AboveItem && itemIsGroup)
-		dropGroup = nullptr;
-	if (emptyDrop)
-		dropGroup = nullptr;
-
-	/* --------------------------------------- */
-	/* remember to remove list items if        */
-	/* dropping on collapsed group             */
-
-	bool dropOnCollapsed = false;
-	if (dropGroup) {
-		obs_data_t *data = obs_sceneitem_get_private_settings(dropGroup);
-		dropOnCollapsed = obs_data_get_bool(data, "collapsed");
-		obs_data_release(data);
-	}
-
-	if (indicator == QAbstractItemView::BelowItem || indicator == QAbstractItemView::OnItem ||
-	    indicator == QAbstractItemView::OnViewport)
-		row++;
-
-	if (row < 0 || row > stm->items.count()) {
-		QListView::dropEvent(event);
-		return;
-	}
-
-	/* --------------------------------------- */
-	/* determine if any base group is selected */
-
-	bool hasGroups = false;
-	for (int i = 0; i < indices.size(); i++) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		if (obs_sceneitem_is_group(item)) {
-			hasGroups = true;
-			break;
-		}
-	}
-
-	/* --------------------------------------- */
-	/* if dropping a group, detect if it's     */
-	/* below another group                     */
-
-	obs_sceneitem_t *itemBelow;
-	if (row == stm->items.count())
-		itemBelow = nullptr;
-	else
-		itemBelow = stm->items[row];
-
-	if (hasGroups) {
-		if (!itemBelow || obs_sceneitem_get_group(scene, itemBelow) != dropGroup) {
-			dropGroup = nullptr;
-			dropOnCollapsed = false;
-		}
-	}
-
-	/* --------------------------------------- */
-	/* if dropping groups on other groups,     */
-	/* disregard as invalid drag/drop          */
-
-	if (dropGroup && hasGroups) {
-		QListView::dropEvent(event);
-		return;
-	}
-
-	/* --------------------------------------- */
-	/* save undo data                          */
-	std::vector<obs_source_t *> sources;
-	for (int i = 0; i < indices.size(); i++) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		if (obs_sceneitem_get_scene(item) != scene)
-			sources.push_back(obs_scene_get_source(obs_sceneitem_get_scene(item)));
-	}
-	if (dropGroup)
-		sources.push_back(obs_sceneitem_get_source(dropGroup));
-	OBSData undo_data = main->BackupScene(scene, &sources);
-
-	/* --------------------------------------- */
-	/* if selection includes base group items, */
-	/* include all group sub-items and treat   */
-	/* them all as one                         */
-
-	if (hasGroups) {
-		/* remove sub-items if selected */
-		for (int i = indices.size() - 1; i >= 0; i--) {
-			obs_sceneitem_t *item = items[indices[i].row()];
-			obs_scene_t *itemScene = obs_sceneitem_get_scene(item);
-
-			if (itemScene != scene) {
-				indices.removeAt(i);
-			}
-		}
-
-		/* add all sub-items of selected groups */
-		for (int i = indices.size() - 1; i >= 0; i--) {
-			obs_sceneitem_t *item = items[indices[i].row()];
-
-			if (obs_sceneitem_is_group(item)) {
-				for (int j = items.size() - 1; j >= 0; j--) {
-					obs_sceneitem_t *subitem = items[j];
-					obs_sceneitem_t *subitemGroup = obs_sceneitem_get_group(scene, subitem);
-
-					if (subitemGroup == item) {
-						QModelIndex idx = stm->createIndex(j, 0);
-						indices.insert(i + 1, idx);
-					}
-				}
-			}
-		}
-	}
-
-	/* --------------------------------------- */
-	/* build persistent indices                */
-
-	QList<QPersistentModelIndex> persistentIndices;
-	persistentIndices.reserve(indices.count());
-	for (QModelIndex &index : indices)
-		persistentIndices.append(index);
-	std::sort(persistentIndices.begin(), persistentIndices.end());
-
-	/* --------------------------------------- */
-	/* move all items to destination index     */
-
-	int r = row;
-	for (auto &persistentIdx : persistentIndices) {
-		int from = persistentIdx.row();
-		int to = r;
-		int itemTo = to;
-
-		if (itemTo > from)
-			itemTo--;
-
-		if (itemTo != from) {
-			stm->beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
-			MoveItem(items, from, itemTo);
-			stm->endMoveRows();
-		}
-
-		r = persistentIdx.row() + 1;
-	}
-
-	std::sort(persistentIndices.begin(), persistentIndices.end());
-	int firstIdx = persistentIndices.front().row();
-	int lastIdx = persistentIndices.back().row();
-
-	/* --------------------------------------- */
-	/* reorder scene items in back-end         */
-
-	QVector<struct obs_sceneitem_order_info> orderList;
-	obs_sceneitem_t *lastGroup = nullptr;
-	int insertCollapsedIdx = 0;
-
-	auto insertCollapsed = [&](obs_sceneitem_t *item) {
-		struct obs_sceneitem_order_info info;
-		info.group = lastGroup;
-		info.item = item;
-
-		orderList.insert(insertCollapsedIdx++, info);
-	};
-
-	using insertCollapsed_t = decltype(insertCollapsed);
-
-	auto preInsertCollapsed = [](obs_scene_t *, obs_sceneitem_t *item, void *param) {
-		(*reinterpret_cast<insertCollapsed_t *>(param))(item);
-		return true;
-	};
-
-	auto insertLastGroup = [&]() {
-		OBSDataAutoRelease data = obs_sceneitem_get_private_settings(lastGroup);
-		bool collapsed = obs_data_get_bool(data, "collapsed");
-
-		if (collapsed) {
-			insertCollapsedIdx = 0;
-			obs_sceneitem_group_enum_items(lastGroup, preInsertCollapsed, &insertCollapsed);
-		}
-
-		struct obs_sceneitem_order_info info;
-		info.group = nullptr;
-		info.item = lastGroup;
-		orderList.insert(0, info);
-	};
-
-	auto updateScene = [&]() {
-		struct obs_sceneitem_order_info info;
-
-		for (int i = 0; i < items.size(); i++) {
-			obs_sceneitem_t *item = items[i];
-			obs_sceneitem_t *group;
-
-			if (obs_sceneitem_is_group(item)) {
-				if (lastGroup) {
-					insertLastGroup();
-				}
-				lastGroup = item;
-				continue;
-			}
-
-			if (!hasGroups && i >= firstIdx && i <= lastIdx)
-				group = dropGroup;
-			else
-				group = obs_sceneitem_get_group(scene, item);
-
-			if (lastGroup && lastGroup != group) {
-				insertLastGroup();
-			}
-
-			lastGroup = group;
-
-			info.group = group;
-			info.item = item;
-			orderList.insert(0, info);
-		}
-
-		if (lastGroup) {
-			insertLastGroup();
-		}
-
-		obs_scene_reorder_items2(scene, orderList.data(), orderList.size());
-	};
-
-	using updateScene_t = decltype(updateScene);
-
-	auto preUpdateScene = [](void *data, obs_scene_t *) {
-		(*reinterpret_cast<updateScene_t *>(data))();
-	};
-
-	ignoreReorder = true;
-	obs_scene_atomic_update(scene, preUpdateScene, &updateScene);
-	ignoreReorder = false;
-
-	/* --------------------------------------- */
-	/* save redo data                          */
-
-	OBSData redo_data = main->BackupScene(scene, &sources);
-
-	/* --------------------------------------- */
-	/* add undo/redo action                    */
-
-	const char *scene_name = obs_source_get_name(scenesource);
-	QString action_name = QTStr("Undo.ReorderSources").arg(scene_name);
-	main->CreateSceneUndoRedoAction(action_name, undo_data, redo_data);
-
-	/* --------------------------------------- */
-	/* remove items if dropped in to collapsed */
-	/* group                                   */
-
-	if (dropOnCollapsed) {
-		stm->beginRemoveRows(QModelIndex(), firstIdx, lastIdx);
-		items.remove(firstIdx, lastIdx - firstIdx + 1);
-		stm->endRemoveRows();
-	}
-
-	/* --------------------------------------- */
-	/* update widgets and accept event         */
-
-	UpdateWidgets(true);
-
-	event->accept();
-	event->setDropAction(Qt::CopyAction);
-
-	QListView::dropEvent(event);
-}
-
-void SourceTree::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
-{
-	{
-		QSignalBlocker sourcesSignalBlocker(this);
-		SourceTreeModel *stm = GetStm();
-
-		QModelIndexList selectedIdxs = selected.indexes();
-		QModelIndexList deselectedIdxs = deselected.indexes();
-
-		for (int i = 0; i < selectedIdxs.count(); i++) {
-			int idx = selectedIdxs[i].row();
-			obs_sceneitem_select(stm->items[idx], true);
-		}
-
-		for (int i = 0; i < deselectedIdxs.count(); i++) {
-			int idx = deselectedIdxs[i].row();
-			obs_sceneitem_select(stm->items[idx], false);
-		}
-	}
-	QListView::selectionChanged(selected, deselected);
-}
-
-void SourceTree::NewGroupEdit(int row)
-{
-	if (!Edit(row)) {
-		OBSBasic *main = OBSBasic::Get();
-		main->undo_s.pop_disabled();
-
-		blog(LOG_WARNING, "Uh, somehow the edit didn't process, this "
-				  "code should never be reached.\nAnd by "
-				  "\"never be reached\", I mean that "
-				  "theoretically, it should be\nimpossible "
-				  "for this code to be reached. But if this "
-				  "code is reached,\nfeel free to laugh at "
-				  "Lain, because apparently it is, in fact, "
-				  "actually\npossible for this code to be "
-				  "reached. But I mean, again, theoretically\n"
-				  "it should be impossible. So if you see "
-				  "this in your log, just know that\nit's "
-				  "really dumb, and depressing. But at least "
-				  "the undo/redo action is\nstill covered, so "
-				  "in theory things *should* be fine. But "
-				  "it's entirely\npossible that they might "
-				  "not be exactly. But again, yea. This "
-				  "really\nshould not be possible.");
-
-		OBSData redoSceneData = main->BackupScene(GetCurrentScene());
-
-		QString text = QTStr("Undo.GroupItems").arg("Unknown");
-		main->CreateSceneUndoRedoAction(text, undoSceneData, redoSceneData);
-
-		undoSceneData = nullptr;
-	}
-}
-
-bool SourceTree::Edit(int row)
-{
-	SourceTreeModel *stm = GetStm();
-	if (row < 0 || row >= stm->items.count())
-		return false;
-
-	QModelIndex index = stm->createIndex(row, 0);
-	QWidget *widget = indexWidget(index);
-	SourceTreeItem *itemWidget = reinterpret_cast<SourceTreeItem *>(widget);
-	if (itemWidget->IsEditing()) {
-#ifdef __APPLE__
-		itemWidget->ExitEditMode(true);
-#endif
-		return false;
-	}
-
-	itemWidget->EnterEditMode();
-	edit(index);
-	return true;
-}
-
-bool SourceTree::MultipleBaseSelected() const
-{
-	SourceTreeModel *stm = GetStm();
-	QModelIndexList selectedIndices = selectedIndexes();
-
-	OBSScene scene = GetCurrentScene();
-
-	if (selectedIndices.size() < 1) {
-		return false;
-	}
-
-	for (auto &idx : selectedIndices) {
-		obs_sceneitem_t *item = stm->items[idx.row()];
-		if (obs_sceneitem_is_group(item)) {
-			return false;
-		}
-
-		obs_scene *itemScene = obs_sceneitem_get_scene(item);
-		if (itemScene != scene) {
-			return false;
-		}
-	}
-
-	return true;
-}
-
-bool SourceTree::GroupsSelected() const
-{
-	SourceTreeModel *stm = GetStm();
-	QModelIndexList selectedIndices = selectedIndexes();
-
-	OBSScene scene = GetCurrentScene();
-
-	if (selectedIndices.size() < 1) {
-		return false;
-	}
-
-	for (auto &idx : selectedIndices) {
-		obs_sceneitem_t *item = stm->items[idx.row()];
-		if (!obs_sceneitem_is_group(item)) {
-			return false;
-		}
-	}
-
-	return true;
-}
-
-bool SourceTree::GroupedItemsSelected() const
-{
-	SourceTreeModel *stm = GetStm();
-	QModelIndexList selectedIndices = selectedIndexes();
-	OBSScene scene = GetCurrentScene();
-
-	if (!selectedIndices.size()) {
-		return false;
-	}
-
-	for (auto &idx : selectedIndices) {
-		obs_sceneitem_t *item = stm->items[idx.row()];
-		obs_scene *itemScene = obs_sceneitem_get_scene(item);
-
-		if (itemScene != scene) {
-			return true;
-		}
-	}
-
-	return false;
-}
-
-void SourceTree::Remove(OBSSceneItem item, OBSScene scene)
-{
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-	GetStm()->Remove(item);
-	main->SaveProject();
-
-	if (!main->SavingDisabled()) {
-		obs_source_t *sceneSource = obs_scene_get_source(scene);
-		obs_source_t *itemSource = obs_sceneitem_get_source(item);
-		blog(LOG_INFO, "User Removed source '%s' (%s) from scene '%s'", obs_source_get_name(itemSource),
-		     obs_source_get_id(itemSource), obs_source_get_name(sceneSource));
-	}
-}
-
-void SourceTree::GroupSelectedItems()
-{
-	QModelIndexList indices = selectedIndexes();
-	std::sort(indices.begin(), indices.end());
-	GetStm()->GroupSelectedItems(indices);
-}
-
-void SourceTree::UngroupSelectedGroups()
-{
-	QModelIndexList indices = selectedIndexes();
-	GetStm()->UngroupSelectedGroups(indices);
-}
-
-void SourceTree::AddGroup()
-{
-	GetStm()->AddGroup();
-}
-
-void SourceTree::UpdateNoSourcesMessage()
-{
-	QString file = !App()->IsThemeDark() ? ":res/images/no_sources.svg" : "theme:Dark/no_sources.svg";
-	iconNoSources.load(file);
-
-	QTextOption opt(Qt::AlignHCenter);
-	opt.setWrapMode(QTextOption::WordWrap);
-	textNoSources.setTextOption(opt);
-	textNoSources.setText(QTStr("NoSources.Label").replace("\n", "<br/>"));
-
-	textPrepared = false;
-}
-
-void SourceTree::paintEvent(QPaintEvent *event)
-{
-	SourceTreeModel *stm = GetStm();
-	if (stm && !stm->items.count()) {
-		QPainter p(viewport());
-
-		if (!textPrepared) {
-			textNoSources.prepare(QTransform(), p.font());
-			textPrepared = true;
-		}
-
-		QRectF iconRect = iconNoSources.viewBoxF();
-		iconRect.setSize(QSizeF(32.0, 32.0));
-
-		QSizeF iconSize = iconRect.size();
-		QSizeF textSize = textNoSources.size();
-		QSizeF thisSize = size();
-		const qreal spacing = 16.0;
-
-		qreal totalHeight = iconSize.height() + spacing + textSize.height();
-
-		qreal x = thisSize.width() / 2.0 - iconSize.width() / 2.0;
-		qreal y = thisSize.height() / 2.0 - totalHeight / 2.0;
-		iconRect.moveTo(std::round(x), std::round(y));
-		iconNoSources.render(&p, iconRect);
-
-		x = thisSize.width() / 2.0 - textSize.width() / 2.0;
-		y += spacing + iconSize.height();
-		p.drawStaticText(x, y, textNoSources);
-	} else {
-		QListView::paintEvent(event);
-	}
-}
+#include "SourceTree.hpp"
+#include "SourceTreeDelegate.hpp"
+#include "moc_SourceTreeDelegate.cpp"
 
 SourceTreeDelegate::SourceTreeDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
 

+ 4 - 191
frontend/components/SourceTreeDelegate.hpp

@@ -1,197 +1,10 @@
 #pragma once
 
-#include <QList>
-#include <QVector>
-#include <QPointer>
-#include <QListView>
-#include <QCheckBox>
-#include <QStaticText>
-#include <QSvgRenderer>
-#include <QAbstractListModel>
+#include <QModelIndex>
+#include <QObject>
+#include <QSize>
 #include <QStyledItemDelegate>
-#include <obs.hpp>
-#include <obs-frontend-api.h>
-
-class QLabel;
-class OBSSourceLabel;
-class QCheckBox;
-class QLineEdit;
-class SourceTree;
-class QSpacerItem;
-class QHBoxLayout;
-class VisibilityItemWidget;
-
-class SourceTreeItem : public QFrame {
-	Q_OBJECT
-
-	friend class SourceTree;
-	friend class SourceTreeModel;
-
-	void mouseDoubleClickEvent(QMouseEvent *event) override;
-	void enterEvent(QEnterEvent *event) override;
-	void leaveEvent(QEvent *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);
-	bool IsEditing();
-
-private:
-	QSpacerItem *spacer = nullptr;
-	QCheckBox *expand = nullptr;
-	QLabel *iconLabel = nullptr;
-	QCheckBox *vis = nullptr;
-	QCheckBox *lock = nullptr;
-	QHBoxLayout *boxLayout = nullptr;
-	OBSSourceLabel *label = nullptr;
-
-	QLineEdit *editor = nullptr;
-
-	std::string newName;
-
-	SourceTree *tree;
-	OBSSceneItem sceneitem;
-	std::vector<OBSSignal> sigs;
-
-	virtual void paintEvent(QPaintEvent *event) override;
-
-	void ExitEditModeInternal(bool save);
-
-private slots:
-	void Clear();
-
-	void EnterEditMode();
-	void ExitEditMode(bool save);
-
-	void VisibilityChanged(bool visible);
-	void LockedChanged(bool locked);
-
-	void ExpandClicked(bool checked);
-
-	void Select();
-	void Deselect();
-};
-
-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);
-
-	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;
-
-	bool textPrepared = false;
-	QStaticText textNoSources;
-	QSvgRenderer iconNoSources;
-
-	OBSData undoSceneData;
-
-	bool iconsVisible = true;
-
-	void UpdateNoSourcesMessage();
-
-	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()); }
-
-public:
-	inline SourceTreeItem *GetItemWidget(int idx)
-	{
-		QWidget *widget = indexWidget(GetStm()->createIndex(idx, 0));
-		return reinterpret_cast<SourceTreeItem *>(widget);
-	}
-
-	explicit SourceTree(QWidget *parent = nullptr);
-
-	inline bool IgnoreReorder() const { return ignoreReorder; }
-	inline void Clear() { GetStm()->Clear(); }
-
-	inline void Add(obs_sceneitem_t *item) { GetStm()->Add(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;
-
-	void UpdateIcons();
-	void SetIconsVisible(bool visible);
-
-public slots:
-	inline void ReorderItems() { GetStm()->ReorderItems(); }
-	inline void RefreshItems() { GetStm()->SceneChanged(); }
-	void Remove(OBSSceneItem item, OBSScene scene);
-	void GroupSelectedItems();
-	void UngroupSelectedGroups();
-	void AddGroup();
-	bool Edit(int idx);
-	void NewGroupEdit(int idx);
-
-protected:
-	virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
-	virtual void dropEvent(QDropEvent *event) override;
-	virtual void paintEvent(QPaintEvent *event) override;
-
-	virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
-};
+#include <QStyleOptionViewItem>
 
 class SourceTreeDelegate : public QStyledItemDelegate {
 	Q_OBJECT

+ 7 - 1076
frontend/components/SourceTreeItem.cpp

@@ -1,26 +1,15 @@
-#include "window-basic-main.hpp"
-#include "obs-app.hpp"
-#include "source-tree.hpp"
-#include "platform.hpp"
-#include "source-label.hpp"
+#include "SourceTreeItem.hpp"
 
-#include <qt-wrappers.hpp>
-#include <obs-frontend-api.h>
-#include <obs.h>
+#include <components/OBSSourceLabel.hpp>
+#include <widgets/OBSBasic.hpp>
 
-#include <string>
+#include <qt-wrappers.hpp>
 
-#include <QLabel>
+#include <QCheckBox>
 #include <QLineEdit>
-#include <QSpacerItem>
-#include <QPushButton>
-#include <QVBoxLayout>
-#include <QHBoxLayout>
-#include <QMouseEvent>
-#include <QAccessible>
+#include <QPainter>
 
-#include <QStylePainter>
-#include <QStyleOptionFocusRect>
+#include "moc_SourceTreeItem.cpp"
 
 static inline OBSScene GetCurrentScene()
 {
@@ -28,8 +17,6 @@ static inline OBSScene GetCurrentScene()
 	return main->GetCurrentScene();
 }
 
-/* ========================================================================= */
-
 SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) : tree(tree_), sceneitem(sceneitem_)
 {
 	setAttribute(Qt::WA_TranslucentBackground);
@@ -555,1059 +542,3 @@ void SourceTreeItem::Deselect()
 	OBSBasic::Get()->UpdateContextBarDeferred();
 	OBSBasic::Get()->UpdateEditMenu();
 }
-
-/* ========================================================================= */
-
-void SourceTreeModel::OBSFrontendEvent(enum obs_frontend_event event, void *ptr)
-{
-	SourceTreeModel *stm = reinterpret_cast<SourceTreeModel *>(ptr);
-
-	switch (event) {
-	case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
-		stm->SceneChanged();
-		break;
-	case OBS_FRONTEND_EVENT_EXIT:
-		stm->Clear();
-		obs_frontend_remove_event_callback(OBSFrontendEvent, stm);
-		break;
-	case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP:
-		stm->Clear();
-		break;
-	default:
-		break;
-	}
-}
-
-void SourceTreeModel::Clear()
-{
-	beginResetModel();
-	items.clear();
-	endResetModel();
-
-	hasGroups = false;
-}
-
-static bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
-{
-	QVector<OBSSceneItem> &items = *reinterpret_cast<QVector<OBSSceneItem> *>(ptr);
-
-	obs_source_t *src = obs_sceneitem_get_source(item);
-	if (obs_source_removed(src)) {
-		return true;
-	}
-
-	if (obs_sceneitem_is_group(item)) {
-		OBSDataAutoRelease data = obs_sceneitem_get_private_settings(item);
-
-		bool collapse = obs_data_get_bool(data, "collapsed");
-		if (!collapse) {
-			obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-			obs_scene_enum_items(scene, enumItem, &items);
-		}
-	}
-
-	items.insert(0, item);
-	return true;
-}
-
-void SourceTreeModel::SceneChanged()
-{
-	OBSScene scene = GetCurrentScene();
-
-	beginResetModel();
-	items.clear();
-	obs_scene_enum_items(scene, enumItem, &items);
-	endResetModel();
-
-	UpdateGroupState(false);
-	st->ResetWidgets();
-
-	for (int i = 0; i < items.count(); i++) {
-		bool select = obs_sceneitem_selected(items[i]);
-		QModelIndex index = createIndex(i, 0);
-
-		st->selectionModel()->select(index,
-					     select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
-	}
-}
-
-/* moves a scene item index (blame linux distros for using older Qt builds) */
-static inline void MoveItem(QVector<OBSSceneItem> &items, int oldIdx, int newIdx)
-{
-	OBSSceneItem item = items[oldIdx];
-	items.remove(oldIdx);
-	items.insert(newIdx, item);
-}
-
-/* reorders list optimally with model reorder funcs */
-void SourceTreeModel::ReorderItems()
-{
-	OBSScene scene = GetCurrentScene();
-
-	QVector<OBSSceneItem> newitems;
-	obs_scene_enum_items(scene, enumItem, &newitems);
-
-	/* if item list has changed size, do full reset */
-	if (newitems.count() != items.count()) {
-		SceneChanged();
-		return;
-	}
-
-	for (;;) {
-		int idx1Old = 0;
-		int idx1New = 0;
-		int count;
-		int i;
-
-		/* find first starting changed item index */
-		for (i = 0; i < newitems.count(); i++) {
-			obs_sceneitem_t *oldItem = items[i];
-			obs_sceneitem_t *newItem = newitems[i];
-			if (oldItem != newItem) {
-				idx1Old = i;
-				break;
-			}
-		}
-
-		/* if everything is the same, break */
-		if (i == newitems.count()) {
-			break;
-		}
-
-		/* find new starting index */
-		for (i = idx1Old + 1; i < newitems.count(); i++) {
-			obs_sceneitem_t *oldItem = items[idx1Old];
-			obs_sceneitem_t *newItem = newitems[i];
-
-			if (oldItem == newItem) {
-				idx1New = i;
-				break;
-			}
-		}
-
-		/* if item could not be found, do full reset */
-		if (i == newitems.count()) {
-			SceneChanged();
-			return;
-		}
-
-		/* get move count */
-		for (count = 1; (idx1New + count) < newitems.count(); count++) {
-			int oldIdx = idx1Old + count;
-			int newIdx = idx1New + count;
-
-			obs_sceneitem_t *oldItem = items[oldIdx];
-			obs_sceneitem_t *newItem = newitems[newIdx];
-
-			if (oldItem != newItem) {
-				break;
-			}
-		}
-
-		/* move items */
-		beginMoveRows(QModelIndex(), idx1Old, idx1Old + count - 1, QModelIndex(), idx1New + count);
-		for (i = 0; i < count; i++) {
-			int to = idx1New + count;
-			if (to > idx1Old)
-				to--;
-			MoveItem(items, idx1Old, to);
-		}
-		endMoveRows();
-	}
-}
-
-void SourceTreeModel::Add(obs_sceneitem_t *item)
-{
-	if (obs_sceneitem_is_group(item)) {
-		SceneChanged();
-	} else {
-		beginInsertRows(QModelIndex(), 0, 0);
-		items.insert(0, item);
-		endInsertRows();
-
-		st->UpdateWidget(createIndex(0, 0, nullptr), item);
-	}
-}
-
-void SourceTreeModel::Remove(obs_sceneitem_t *item)
-{
-	int idx = -1;
-	for (int i = 0; i < items.count(); i++) {
-		if (items[i] == item) {
-			idx = i;
-			break;
-		}
-	}
-
-	if (idx == -1)
-		return;
-
-	int startIdx = idx;
-	int endIdx = idx;
-
-	bool is_group = obs_sceneitem_is_group(item);
-	if (is_group) {
-		obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-		for (int i = endIdx + 1; i < items.count(); i++) {
-			obs_sceneitem_t *subitem = items[i];
-			obs_scene_t *subscene = obs_sceneitem_get_scene(subitem);
-
-			if (subscene == scene)
-				endIdx = i;
-			else
-				break;
-		}
-	}
-
-	beginRemoveRows(QModelIndex(), startIdx, endIdx);
-	items.remove(idx, endIdx - startIdx + 1);
-	endRemoveRows();
-
-	if (is_group)
-		UpdateGroupState(true);
-
-	OBSBasic::Get()->UpdateContextBarDeferred();
-}
-
-OBSSceneItem SourceTreeModel::Get(int idx)
-{
-	if (idx == -1 || idx >= items.count())
-		return OBSSceneItem();
-	return items[idx];
-}
-
-SourceTreeModel::SourceTreeModel(SourceTree *st_) : QAbstractListModel(st_), st(st_)
-{
-	obs_frontend_add_event_callback(OBSFrontendEvent, this);
-}
-
-int SourceTreeModel::rowCount(const QModelIndex &parent) const
-{
-	return parent.isValid() ? 0 : items.count();
-}
-
-QVariant SourceTreeModel::data(const QModelIndex &index, int role) const
-{
-	if (role == Qt::AccessibleTextRole) {
-		OBSSceneItem item = items[index.row()];
-		obs_source_t *source = obs_sceneitem_get_source(item);
-		return QVariant(QT_UTF8(obs_source_get_name(source)));
-	}
-
-	return QVariant();
-}
-
-Qt::ItemFlags SourceTreeModel::flags(const QModelIndex &index) const
-{
-	if (!index.isValid())
-		return QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled;
-
-	obs_sceneitem_t *item = items[index.row()];
-	bool is_group = obs_sceneitem_is_group(item);
-
-	return QAbstractListModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled |
-	       (is_group ? Qt::ItemIsDropEnabled : Qt::NoItemFlags);
-}
-
-Qt::DropActions SourceTreeModel::supportedDropActions() const
-{
-	return QAbstractItemModel::supportedDropActions() | Qt::MoveAction;
-}
-
-QString SourceTreeModel::GetNewGroupName()
-{
-	OBSScene scene = GetCurrentScene();
-	QString name = QTStr("Group");
-
-	int i = 2;
-	for (;;) {
-		OBSSourceAutoRelease group = obs_get_source_by_name(QT_TO_UTF8(name));
-		if (!group)
-			break;
-		name = QTStr("Basic.Main.Group").arg(QString::number(i++));
-	}
-
-	return name;
-}
-
-void SourceTreeModel::AddGroup()
-{
-	QString name = GetNewGroupName();
-	obs_sceneitem_t *group = obs_scene_add_group(GetCurrentScene(), QT_TO_UTF8(name));
-	if (!group)
-		return;
-
-	beginInsertRows(QModelIndex(), 0, 0);
-	items.insert(0, group);
-	endInsertRows();
-
-	st->UpdateWidget(createIndex(0, 0, nullptr), group);
-	UpdateGroupState(true);
-
-	QMetaObject::invokeMethod(st, "Edit", Qt::QueuedConnection, Q_ARG(int, 0));
-}
-
-void SourceTreeModel::GroupSelectedItems(QModelIndexList &indices)
-{
-	if (indices.count() == 0)
-		return;
-
-	OBSBasic *main = OBSBasic::Get();
-	OBSScene scene = GetCurrentScene();
-	QString name = GetNewGroupName();
-
-	QVector<obs_sceneitem_t *> item_order;
-
-	for (int i = indices.count() - 1; i >= 0; i--) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		item_order << item;
-	}
-
-	st->undoSceneData = main->BackupScene(scene);
-
-	obs_sceneitem_t *item = obs_scene_insert_group(scene, QT_TO_UTF8(name), item_order.data(), item_order.size());
-	if (!item) {
-		st->undoSceneData = nullptr;
-		return;
-	}
-
-	main->undo_s.push_disabled();
-
-	for (obs_sceneitem_t *item : item_order)
-		obs_sceneitem_select(item, false);
-
-	hasGroups = true;
-	st->UpdateWidgets(true);
-
-	obs_sceneitem_select(item, true);
-
-	/* ----------------------------------------------------------------- */
-	/* obs_scene_insert_group triggers a full refresh of scene items via */
-	/* the item_add signal. No need to insert a row, just edit the one   */
-	/* that's created automatically.                                     */
-
-	int newIdx = indices[0].row();
-	QMetaObject::invokeMethod(st, "NewGroupEdit", Qt::QueuedConnection, Q_ARG(int, newIdx));
-}
-
-void SourceTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
-{
-	OBSBasic *main = OBSBasic::Get();
-	if (indices.count() == 0)
-		return;
-
-	OBSScene scene = main->GetCurrentScene();
-	OBSData undoData = main->BackupScene(scene);
-
-	for (int i = indices.count() - 1; i >= 0; i--) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		obs_sceneitem_group_ungroup(item);
-	}
-
-	SceneChanged();
-
-	OBSData redoData = main->BackupScene(scene);
-	main->CreateSceneUndoRedoAction(QTStr("Basic.Main.Ungroup"), undoData, redoData);
-}
-
-void SourceTreeModel::ExpandGroup(obs_sceneitem_t *item)
-{
-	int itemIdx = items.indexOf(item);
-	if (itemIdx == -1)
-		return;
-
-	itemIdx++;
-
-	obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-	QVector<OBSSceneItem> subItems;
-	obs_scene_enum_items(scene, enumItem, &subItems);
-
-	if (!subItems.size())
-		return;
-
-	beginInsertRows(QModelIndex(), itemIdx, itemIdx + subItems.size() - 1);
-	for (int i = 0; i < subItems.size(); i++)
-		items.insert(i + itemIdx, subItems[i]);
-	endInsertRows();
-
-	st->UpdateWidgets();
-}
-
-void SourceTreeModel::CollapseGroup(obs_sceneitem_t *item)
-{
-	int startIdx = -1;
-	int endIdx = -1;
-
-	obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
-
-	for (int i = 0; i < items.size(); i++) {
-		obs_scene_t *itemScene = obs_sceneitem_get_scene(items[i]);
-
-		if (itemScene == scene) {
-			if (startIdx == -1)
-				startIdx = i;
-			endIdx = i;
-		}
-	}
-
-	if (startIdx == -1)
-		return;
-
-	beginRemoveRows(QModelIndex(), startIdx, endIdx);
-	items.remove(startIdx, endIdx - startIdx + 1);
-	endRemoveRows();
-}
-
-void SourceTreeModel::UpdateGroupState(bool update)
-{
-	bool nowHasGroups = false;
-	for (auto &item : items) {
-		if (obs_sceneitem_is_group(item)) {
-			nowHasGroups = true;
-			break;
-		}
-	}
-
-	if (nowHasGroups != hasGroups) {
-		hasGroups = nowHasGroups;
-		if (update) {
-			st->UpdateWidgets(true);
-		}
-	}
-}
-
-/* ========================================================================= */
-
-SourceTree::SourceTree(QWidget *parent_) : QListView(parent_)
-{
-	SourceTreeModel *stm_ = new SourceTreeModel(this);
-	setModel(stm_);
-	setStyleSheet(QString("*[bgColor=\"1\"]{background-color:rgba(255,68,68,33%);}"
-			      "*[bgColor=\"2\"]{background-color:rgba(255,255,68,33%);}"
-			      "*[bgColor=\"3\"]{background-color:rgba(68,255,68,33%);}"
-			      "*[bgColor=\"4\"]{background-color:rgba(68,255,255,33%);}"
-			      "*[bgColor=\"5\"]{background-color:rgba(68,68,255,33%);}"
-			      "*[bgColor=\"6\"]{background-color:rgba(255,68,255,33%);}"
-			      "*[bgColor=\"7\"]{background-color:rgba(68,68,68,33%);}"
-			      "*[bgColor=\"8\"]{background-color:rgba(255,255,255,33%);}"));
-
-	UpdateNoSourcesMessage();
-	connect(App(), &OBSApp::StyleChanged, this, &SourceTree::UpdateNoSourcesMessage);
-	connect(App(), &OBSApp::StyleChanged, this, &SourceTree::UpdateIcons);
-
-	setItemDelegate(new SourceTreeDelegate(this));
-}
-
-void SourceTree::UpdateIcons()
-{
-	SourceTreeModel *stm = GetStm();
-	stm->SceneChanged();
-}
-
-void SourceTree::SetIconsVisible(bool visible)
-{
-	SourceTreeModel *stm = GetStm();
-
-	iconsVisible = visible;
-	stm->SceneChanged();
-}
-
-void SourceTree::ResetWidgets()
-{
-	OBSScene scene = GetCurrentScene();
-
-	SourceTreeModel *stm = GetStm();
-	stm->UpdateGroupState(false);
-
-	for (int i = 0; i < stm->items.count(); i++) {
-		QModelIndex index = stm->createIndex(i, 0, nullptr);
-		setIndexWidget(index, new SourceTreeItem(this, stm->items[i]));
-	}
-}
-
-void SourceTree::UpdateWidget(const QModelIndex &idx, obs_sceneitem_t *item)
-{
-	setIndexWidget(idx, new SourceTreeItem(this, item));
-}
-
-void SourceTree::UpdateWidgets(bool force)
-{
-	SourceTreeModel *stm = GetStm();
-
-	for (int i = 0; i < stm->items.size(); i++) {
-		obs_sceneitem_t *item = stm->items[i];
-		SourceTreeItem *widget = GetItemWidget(i);
-
-		if (!widget) {
-			UpdateWidget(stm->createIndex(i, 0), item);
-		} else {
-			widget->Update(force);
-		}
-	}
-}
-
-void SourceTree::SelectItem(obs_sceneitem_t *sceneitem, bool select)
-{
-	SourceTreeModel *stm = GetStm();
-	int i = 0;
-
-	for (; i < stm->items.count(); i++) {
-		if (stm->items[i] == sceneitem)
-			break;
-	}
-
-	if (i == stm->items.count())
-		return;
-
-	QModelIndex index = stm->createIndex(i, 0);
-	if (index.isValid() && select != selectionModel()->isSelected(index))
-		selectionModel()->select(index, select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
-}
-
-Q_DECLARE_METATYPE(OBSSceneItem);
-
-void SourceTree::mouseDoubleClickEvent(QMouseEvent *event)
-{
-	if (event->button() == Qt::LeftButton)
-		QListView::mouseDoubleClickEvent(event);
-}
-
-void SourceTree::dropEvent(QDropEvent *event)
-{
-	if (event->source() != this) {
-		QListView::dropEvent(event);
-		return;
-	}
-
-	OBSBasic *main = OBSBasic::Get();
-
-	OBSScene scene = GetCurrentScene();
-	obs_source_t *scenesource = obs_scene_get_source(scene);
-	SourceTreeModel *stm = GetStm();
-	auto &items = stm->items;
-	QModelIndexList indices = selectedIndexes();
-
-	DropIndicatorPosition indicator = dropIndicatorPosition();
-	int row = indexAt(event->position().toPoint()).row();
-	bool emptyDrop = row == -1;
-
-	if (emptyDrop) {
-		if (!items.size()) {
-			QListView::dropEvent(event);
-			return;
-		}
-
-		row = items.size() - 1;
-		indicator = QAbstractItemView::BelowItem;
-	}
-
-	/* --------------------------------------- */
-	/* store destination group if moving to a  */
-	/* group                                   */
-
-	obs_sceneitem_t *dropItem = items[row]; /* item being dropped on */
-	bool itemIsGroup = obs_sceneitem_is_group(dropItem);
-
-	obs_sceneitem_t *dropGroup = itemIsGroup ? dropItem : obs_sceneitem_get_group(scene, dropItem);
-
-	/* not a group if moving above the group */
-	if (indicator == QAbstractItemView::AboveItem && itemIsGroup)
-		dropGroup = nullptr;
-	if (emptyDrop)
-		dropGroup = nullptr;
-
-	/* --------------------------------------- */
-	/* remember to remove list items if        */
-	/* dropping on collapsed group             */
-
-	bool dropOnCollapsed = false;
-	if (dropGroup) {
-		obs_data_t *data = obs_sceneitem_get_private_settings(dropGroup);
-		dropOnCollapsed = obs_data_get_bool(data, "collapsed");
-		obs_data_release(data);
-	}
-
-	if (indicator == QAbstractItemView::BelowItem || indicator == QAbstractItemView::OnItem ||
-	    indicator == QAbstractItemView::OnViewport)
-		row++;
-
-	if (row < 0 || row > stm->items.count()) {
-		QListView::dropEvent(event);
-		return;
-	}
-
-	/* --------------------------------------- */
-	/* determine if any base group is selected */
-
-	bool hasGroups = false;
-	for (int i = 0; i < indices.size(); i++) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		if (obs_sceneitem_is_group(item)) {
-			hasGroups = true;
-			break;
-		}
-	}
-
-	/* --------------------------------------- */
-	/* if dropping a group, detect if it's     */
-	/* below another group                     */
-
-	obs_sceneitem_t *itemBelow;
-	if (row == stm->items.count())
-		itemBelow = nullptr;
-	else
-		itemBelow = stm->items[row];
-
-	if (hasGroups) {
-		if (!itemBelow || obs_sceneitem_get_group(scene, itemBelow) != dropGroup) {
-			dropGroup = nullptr;
-			dropOnCollapsed = false;
-		}
-	}
-
-	/* --------------------------------------- */
-	/* if dropping groups on other groups,     */
-	/* disregard as invalid drag/drop          */
-
-	if (dropGroup && hasGroups) {
-		QListView::dropEvent(event);
-		return;
-	}
-
-	/* --------------------------------------- */
-	/* save undo data                          */
-	std::vector<obs_source_t *> sources;
-	for (int i = 0; i < indices.size(); i++) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		if (obs_sceneitem_get_scene(item) != scene)
-			sources.push_back(obs_scene_get_source(obs_sceneitem_get_scene(item)));
-	}
-	if (dropGroup)
-		sources.push_back(obs_sceneitem_get_source(dropGroup));
-	OBSData undo_data = main->BackupScene(scene, &sources);
-
-	/* --------------------------------------- */
-	/* if selection includes base group items, */
-	/* include all group sub-items and treat   */
-	/* them all as one                         */
-
-	if (hasGroups) {
-		/* remove sub-items if selected */
-		for (int i = indices.size() - 1; i >= 0; i--) {
-			obs_sceneitem_t *item = items[indices[i].row()];
-			obs_scene_t *itemScene = obs_sceneitem_get_scene(item);
-
-			if (itemScene != scene) {
-				indices.removeAt(i);
-			}
-		}
-
-		/* add all sub-items of selected groups */
-		for (int i = indices.size() - 1; i >= 0; i--) {
-			obs_sceneitem_t *item = items[indices[i].row()];
-
-			if (obs_sceneitem_is_group(item)) {
-				for (int j = items.size() - 1; j >= 0; j--) {
-					obs_sceneitem_t *subitem = items[j];
-					obs_sceneitem_t *subitemGroup = obs_sceneitem_get_group(scene, subitem);
-
-					if (subitemGroup == item) {
-						QModelIndex idx = stm->createIndex(j, 0);
-						indices.insert(i + 1, idx);
-					}
-				}
-			}
-		}
-	}
-
-	/* --------------------------------------- */
-	/* build persistent indices                */
-
-	QList<QPersistentModelIndex> persistentIndices;
-	persistentIndices.reserve(indices.count());
-	for (QModelIndex &index : indices)
-		persistentIndices.append(index);
-	std::sort(persistentIndices.begin(), persistentIndices.end());
-
-	/* --------------------------------------- */
-	/* move all items to destination index     */
-
-	int r = row;
-	for (auto &persistentIdx : persistentIndices) {
-		int from = persistentIdx.row();
-		int to = r;
-		int itemTo = to;
-
-		if (itemTo > from)
-			itemTo--;
-
-		if (itemTo != from) {
-			stm->beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
-			MoveItem(items, from, itemTo);
-			stm->endMoveRows();
-		}
-
-		r = persistentIdx.row() + 1;
-	}
-
-	std::sort(persistentIndices.begin(), persistentIndices.end());
-	int firstIdx = persistentIndices.front().row();
-	int lastIdx = persistentIndices.back().row();
-
-	/* --------------------------------------- */
-	/* reorder scene items in back-end         */
-
-	QVector<struct obs_sceneitem_order_info> orderList;
-	obs_sceneitem_t *lastGroup = nullptr;
-	int insertCollapsedIdx = 0;
-
-	auto insertCollapsed = [&](obs_sceneitem_t *item) {
-		struct obs_sceneitem_order_info info;
-		info.group = lastGroup;
-		info.item = item;
-
-		orderList.insert(insertCollapsedIdx++, info);
-	};
-
-	using insertCollapsed_t = decltype(insertCollapsed);
-
-	auto preInsertCollapsed = [](obs_scene_t *, obs_sceneitem_t *item, void *param) {
-		(*reinterpret_cast<insertCollapsed_t *>(param))(item);
-		return true;
-	};
-
-	auto insertLastGroup = [&]() {
-		OBSDataAutoRelease data = obs_sceneitem_get_private_settings(lastGroup);
-		bool collapsed = obs_data_get_bool(data, "collapsed");
-
-		if (collapsed) {
-			insertCollapsedIdx = 0;
-			obs_sceneitem_group_enum_items(lastGroup, preInsertCollapsed, &insertCollapsed);
-		}
-
-		struct obs_sceneitem_order_info info;
-		info.group = nullptr;
-		info.item = lastGroup;
-		orderList.insert(0, info);
-	};
-
-	auto updateScene = [&]() {
-		struct obs_sceneitem_order_info info;
-
-		for (int i = 0; i < items.size(); i++) {
-			obs_sceneitem_t *item = items[i];
-			obs_sceneitem_t *group;
-
-			if (obs_sceneitem_is_group(item)) {
-				if (lastGroup) {
-					insertLastGroup();
-				}
-				lastGroup = item;
-				continue;
-			}
-
-			if (!hasGroups && i >= firstIdx && i <= lastIdx)
-				group = dropGroup;
-			else
-				group = obs_sceneitem_get_group(scene, item);
-
-			if (lastGroup && lastGroup != group) {
-				insertLastGroup();
-			}
-
-			lastGroup = group;
-
-			info.group = group;
-			info.item = item;
-			orderList.insert(0, info);
-		}
-
-		if (lastGroup) {
-			insertLastGroup();
-		}
-
-		obs_scene_reorder_items2(scene, orderList.data(), orderList.size());
-	};
-
-	using updateScene_t = decltype(updateScene);
-
-	auto preUpdateScene = [](void *data, obs_scene_t *) {
-		(*reinterpret_cast<updateScene_t *>(data))();
-	};
-
-	ignoreReorder = true;
-	obs_scene_atomic_update(scene, preUpdateScene, &updateScene);
-	ignoreReorder = false;
-
-	/* --------------------------------------- */
-	/* save redo data                          */
-
-	OBSData redo_data = main->BackupScene(scene, &sources);
-
-	/* --------------------------------------- */
-	/* add undo/redo action                    */
-
-	const char *scene_name = obs_source_get_name(scenesource);
-	QString action_name = QTStr("Undo.ReorderSources").arg(scene_name);
-	main->CreateSceneUndoRedoAction(action_name, undo_data, redo_data);
-
-	/* --------------------------------------- */
-	/* remove items if dropped in to collapsed */
-	/* group                                   */
-
-	if (dropOnCollapsed) {
-		stm->beginRemoveRows(QModelIndex(), firstIdx, lastIdx);
-		items.remove(firstIdx, lastIdx - firstIdx + 1);
-		stm->endRemoveRows();
-	}
-
-	/* --------------------------------------- */
-	/* update widgets and accept event         */
-
-	UpdateWidgets(true);
-
-	event->accept();
-	event->setDropAction(Qt::CopyAction);
-
-	QListView::dropEvent(event);
-}
-
-void SourceTree::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
-{
-	{
-		QSignalBlocker sourcesSignalBlocker(this);
-		SourceTreeModel *stm = GetStm();
-
-		QModelIndexList selectedIdxs = selected.indexes();
-		QModelIndexList deselectedIdxs = deselected.indexes();
-
-		for (int i = 0; i < selectedIdxs.count(); i++) {
-			int idx = selectedIdxs[i].row();
-			obs_sceneitem_select(stm->items[idx], true);
-		}
-
-		for (int i = 0; i < deselectedIdxs.count(); i++) {
-			int idx = deselectedIdxs[i].row();
-			obs_sceneitem_select(stm->items[idx], false);
-		}
-	}
-	QListView::selectionChanged(selected, deselected);
-}
-
-void SourceTree::NewGroupEdit(int row)
-{
-	if (!Edit(row)) {
-		OBSBasic *main = OBSBasic::Get();
-		main->undo_s.pop_disabled();
-
-		blog(LOG_WARNING, "Uh, somehow the edit didn't process, this "
-				  "code should never be reached.\nAnd by "
-				  "\"never be reached\", I mean that "
-				  "theoretically, it should be\nimpossible "
-				  "for this code to be reached. But if this "
-				  "code is reached,\nfeel free to laugh at "
-				  "Lain, because apparently it is, in fact, "
-				  "actually\npossible for this code to be "
-				  "reached. But I mean, again, theoretically\n"
-				  "it should be impossible. So if you see "
-				  "this in your log, just know that\nit's "
-				  "really dumb, and depressing. But at least "
-				  "the undo/redo action is\nstill covered, so "
-				  "in theory things *should* be fine. But "
-				  "it's entirely\npossible that they might "
-				  "not be exactly. But again, yea. This "
-				  "really\nshould not be possible.");
-
-		OBSData redoSceneData = main->BackupScene(GetCurrentScene());
-
-		QString text = QTStr("Undo.GroupItems").arg("Unknown");
-		main->CreateSceneUndoRedoAction(text, undoSceneData, redoSceneData);
-
-		undoSceneData = nullptr;
-	}
-}
-
-bool SourceTree::Edit(int row)
-{
-	SourceTreeModel *stm = GetStm();
-	if (row < 0 || row >= stm->items.count())
-		return false;
-
-	QModelIndex index = stm->createIndex(row, 0);
-	QWidget *widget = indexWidget(index);
-	SourceTreeItem *itemWidget = reinterpret_cast<SourceTreeItem *>(widget);
-	if (itemWidget->IsEditing()) {
-#ifdef __APPLE__
-		itemWidget->ExitEditMode(true);
-#endif
-		return false;
-	}
-
-	itemWidget->EnterEditMode();
-	edit(index);
-	return true;
-}
-
-bool SourceTree::MultipleBaseSelected() const
-{
-	SourceTreeModel *stm = GetStm();
-	QModelIndexList selectedIndices = selectedIndexes();
-
-	OBSScene scene = GetCurrentScene();
-
-	if (selectedIndices.size() < 1) {
-		return false;
-	}
-
-	for (auto &idx : selectedIndices) {
-		obs_sceneitem_t *item = stm->items[idx.row()];
-		if (obs_sceneitem_is_group(item)) {
-			return false;
-		}
-
-		obs_scene *itemScene = obs_sceneitem_get_scene(item);
-		if (itemScene != scene) {
-			return false;
-		}
-	}
-
-	return true;
-}
-
-bool SourceTree::GroupsSelected() const
-{
-	SourceTreeModel *stm = GetStm();
-	QModelIndexList selectedIndices = selectedIndexes();
-
-	OBSScene scene = GetCurrentScene();
-
-	if (selectedIndices.size() < 1) {
-		return false;
-	}
-
-	for (auto &idx : selectedIndices) {
-		obs_sceneitem_t *item = stm->items[idx.row()];
-		if (!obs_sceneitem_is_group(item)) {
-			return false;
-		}
-	}
-
-	return true;
-}
-
-bool SourceTree::GroupedItemsSelected() const
-{
-	SourceTreeModel *stm = GetStm();
-	QModelIndexList selectedIndices = selectedIndexes();
-	OBSScene scene = GetCurrentScene();
-
-	if (!selectedIndices.size()) {
-		return false;
-	}
-
-	for (auto &idx : selectedIndices) {
-		obs_sceneitem_t *item = stm->items[idx.row()];
-		obs_scene *itemScene = obs_sceneitem_get_scene(item);
-
-		if (itemScene != scene) {
-			return true;
-		}
-	}
-
-	return false;
-}
-
-void SourceTree::Remove(OBSSceneItem item, OBSScene scene)
-{
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-	GetStm()->Remove(item);
-	main->SaveProject();
-
-	if (!main->SavingDisabled()) {
-		obs_source_t *sceneSource = obs_scene_get_source(scene);
-		obs_source_t *itemSource = obs_sceneitem_get_source(item);
-		blog(LOG_INFO, "User Removed source '%s' (%s) from scene '%s'", obs_source_get_name(itemSource),
-		     obs_source_get_id(itemSource), obs_source_get_name(sceneSource));
-	}
-}
-
-void SourceTree::GroupSelectedItems()
-{
-	QModelIndexList indices = selectedIndexes();
-	std::sort(indices.begin(), indices.end());
-	GetStm()->GroupSelectedItems(indices);
-}
-
-void SourceTree::UngroupSelectedGroups()
-{
-	QModelIndexList indices = selectedIndexes();
-	GetStm()->UngroupSelectedGroups(indices);
-}
-
-void SourceTree::AddGroup()
-{
-	GetStm()->AddGroup();
-}
-
-void SourceTree::UpdateNoSourcesMessage()
-{
-	QString file = !App()->IsThemeDark() ? ":res/images/no_sources.svg" : "theme:Dark/no_sources.svg";
-	iconNoSources.load(file);
-
-	QTextOption opt(Qt::AlignHCenter);
-	opt.setWrapMode(QTextOption::WordWrap);
-	textNoSources.setTextOption(opt);
-	textNoSources.setText(QTStr("NoSources.Label").replace("\n", "<br/>"));
-
-	textPrepared = false;
-}
-
-void SourceTree::paintEvent(QPaintEvent *event)
-{
-	SourceTreeModel *stm = GetStm();
-	if (stm && !stm->items.count()) {
-		QPainter p(viewport());
-
-		if (!textPrepared) {
-			textNoSources.prepare(QTransform(), p.font());
-			textPrepared = true;
-		}
-
-		QRectF iconRect = iconNoSources.viewBoxF();
-		iconRect.setSize(QSizeF(32.0, 32.0));
-
-		QSizeF iconSize = iconRect.size();
-		QSizeF textSize = textNoSources.size();
-		QSizeF thisSize = size();
-		const qreal spacing = 16.0;
-
-		qreal totalHeight = iconSize.height() + spacing + textSize.height();
-
-		qreal x = thisSize.width() / 2.0 - iconSize.width() / 2.0;
-		qreal y = thisSize.height() / 2.0 - totalHeight / 2.0;
-		iconRect.moveTo(std::round(x), std::round(y));
-		iconNoSources.render(&p, iconRect);
-
-		x = thisSize.width() / 2.0 - textSize.width() / 2.0;
-		y += spacing + iconSize.height();
-		p.drawStaticText(x, y, textNoSources);
-	} else {
-		QListView::paintEvent(event);
-	}
-}
-
-SourceTreeDelegate::SourceTreeDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
-
-QSize SourceTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
-{
-	SourceTree *tree = qobject_cast<SourceTree *>(parent());
-	QWidget *item = tree->indexWidget(index);
-
-	if (!item)
-		return (QSize(0, 0));
-
-	return (QSize(option.widget->minimumWidth(), item->height()));
-}

+ 6 - 128
frontend/components/SourceTreeItem.hpp

@@ -1,25 +1,17 @@
 #pragma once
 
-#include <QList>
-#include <QVector>
-#include <QPointer>
-#include <QListView>
-#include <QCheckBox>
-#include <QStaticText>
-#include <QSvgRenderer>
-#include <QAbstractListModel>
-#include <QStyledItemDelegate>
 #include <obs.hpp>
-#include <obs-frontend-api.h>
 
+#include <QFrame>
+
+class QSpacerItem;
+class QCheckBox;
 class QLabel;
+class QHBoxLayout;
 class OBSSourceLabel;
-class QCheckBox;
 class QLineEdit;
+
 class SourceTree;
-class QSpacerItem;
-class QHBoxLayout;
-class VisibilityItemWidget;
 
 class SourceTreeItem : public QFrame {
 	Q_OBJECT
@@ -86,117 +78,3 @@ private slots:
 	void Select();
 	void Deselect();
 };
-
-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);
-
-	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;
-
-	bool textPrepared = false;
-	QStaticText textNoSources;
-	QSvgRenderer iconNoSources;
-
-	OBSData undoSceneData;
-
-	bool iconsVisible = true;
-
-	void UpdateNoSourcesMessage();
-
-	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()); }
-
-public:
-	inline SourceTreeItem *GetItemWidget(int idx)
-	{
-		QWidget *widget = indexWidget(GetStm()->createIndex(idx, 0));
-		return reinterpret_cast<SourceTreeItem *>(widget);
-	}
-
-	explicit SourceTree(QWidget *parent = nullptr);
-
-	inline bool IgnoreReorder() const { return ignoreReorder; }
-	inline void Clear() { GetStm()->Clear(); }
-
-	inline void Add(obs_sceneitem_t *item) { GetStm()->Add(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;
-
-	void UpdateIcons();
-	void SetIconsVisible(bool visible);
-
-public slots:
-	inline void ReorderItems() { GetStm()->ReorderItems(); }
-	inline void RefreshItems() { GetStm()->SceneChanged(); }
-	void Remove(OBSSceneItem item, OBSScene scene);
-	void GroupSelectedItems();
-	void UngroupSelectedGroups();
-	void AddGroup();
-	bool Edit(int idx);
-	void NewGroupEdit(int idx);
-
-protected:
-	virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
-	virtual void dropEvent(QDropEvent *event) override;
-	virtual void paintEvent(QPaintEvent *event) override;
-
-	virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
-};
-
-class SourceTreeDelegate : public QStyledItemDelegate {
-	Q_OBJECT
-
-public:
-	SourceTreeDelegate(QObject *parent);
-	virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
-};

+ 4 - 1183
frontend/components/SourceTreeModel.cpp

@@ -1,26 +1,10 @@
-#include "window-basic-main.hpp"
-#include "obs-app.hpp"
-#include "source-tree.hpp"
-#include "platform.hpp"
-#include "source-label.hpp"
+#include "SourceTreeModel.hpp"
 
-#include <qt-wrappers.hpp>
-#include <obs-frontend-api.h>
-#include <obs.h>
-
-#include <string>
+#include <widgets/OBSBasic.hpp>
 
-#include <QLabel>
-#include <QLineEdit>
-#include <QSpacerItem>
-#include <QPushButton>
-#include <QVBoxLayout>
-#include <QHBoxLayout>
-#include <QMouseEvent>
-#include <QAccessible>
+#include <qt-wrappers.hpp>
 
-#include <QStylePainter>
-#include <QStyleOptionFocusRect>
+#include "moc_SourceTreeModel.cpp"
 
 static inline OBSScene GetCurrentScene()
 {
@@ -28,536 +12,6 @@ static inline OBSScene GetCurrentScene()
 	return main->GetCurrentScene();
 }
 
-/* ========================================================================= */
-
-SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) : tree(tree_), sceneitem(sceneitem_)
-{
-	setAttribute(Qt::WA_TranslucentBackground);
-	setMouseTracking(true);
-
-	obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-	const char *name = obs_source_get_name(source);
-
-	OBSDataAutoRelease privData = obs_sceneitem_get_private_settings(sceneitem);
-	int preset = obs_data_get_int(privData, "color-preset");
-
-	if (preset == 1) {
-		const char *color = obs_data_get_string(privData, "color");
-		std::string col = "background: ";
-		col += color;
-		setStyleSheet(col.c_str());
-	} else if (preset > 1) {
-		setStyleSheet("");
-		setProperty("bgColor", preset - 1);
-	} else {
-		setStyleSheet("background: none");
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-	const char *id = obs_source_get_id(source);
-
-	bool sourceVisible = obs_sceneitem_visible(sceneitem);
-
-	if (tree->iconsVisible) {
-		QIcon icon;
-
-		if (strcmp(id, "scene") == 0)
-			icon = main->GetSceneIcon();
-		else if (strcmp(id, "group") == 0)
-			icon = main->GetGroupIcon();
-		else
-			icon = main->GetSourceIcon(id);
-
-		QPixmap pixmap = icon.pixmap(QSize(16, 16));
-
-		iconLabel = new QLabel();
-		iconLabel->setPixmap(pixmap);
-		iconLabel->setEnabled(sourceVisible);
-		iconLabel->setStyleSheet("background: none");
-		iconLabel->setProperty("class", "source-icon");
-	}
-
-	vis = new QCheckBox();
-	vis->setProperty("class", "checkbox-icon indicator-visibility");
-	vis->setChecked(sourceVisible);
-	vis->setAccessibleName(QTStr("Basic.Main.Sources.Visibility"));
-	vis->setAccessibleDescription(QTStr("Basic.Main.Sources.VisibilityDescription").arg(name));
-
-	lock = new QCheckBox();
-	lock->setProperty("class", "checkbox-icon indicator-lock");
-	lock->setChecked(obs_sceneitem_locked(sceneitem));
-	lock->setAccessibleName(QTStr("Basic.Main.Sources.Lock"));
-	lock->setAccessibleDescription(QTStr("Basic.Main.Sources.LockDescription").arg(name));
-
-	label = new OBSSourceLabel(source);
-	label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
-	label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
-	label->setAttribute(Qt::WA_TranslucentBackground);
-	label->setEnabled(sourceVisible);
-
-#ifdef __APPLE__
-	vis->setAttribute(Qt::WA_LayoutUsesWidgetRect);
-	lock->setAttribute(Qt::WA_LayoutUsesWidgetRect);
-#endif
-
-	boxLayout = new QHBoxLayout();
-
-	boxLayout->setContentsMargins(0, 0, 0, 0);
-	boxLayout->setSpacing(0);
-	if (iconLabel) {
-		boxLayout->addWidget(iconLabel);
-		boxLayout->addSpacing(2);
-	}
-	boxLayout->addWidget(label);
-	boxLayout->addWidget(vis);
-	boxLayout->addWidget(lock);
-#ifdef __APPLE__
-	/* Hack: Fixes a bug where scrollbars would be above the lock icon */
-	boxLayout->addSpacing(16);
-#endif
-
-	Update(false);
-
-	setLayout(boxLayout);
-
-	/* --------------------------------------------------------- */
-
-	auto setItemVisible = [this](bool val) {
-		obs_scene_t *scene = obs_sceneitem_get_scene(sceneitem);
-		obs_source_t *scenesource = obs_scene_get_source(scene);
-		int64_t id = obs_sceneitem_get_id(sceneitem);
-		const char *name = obs_source_get_name(scenesource);
-		const char *uuid = obs_source_get_uuid(scenesource);
-		obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-
-		auto undo_redo = [](const std::string &uuid, int64_t id, bool val) {
-			OBSSourceAutoRelease s = obs_get_source_by_uuid(uuid.c_str());
-			obs_scene_t *sc = obs_group_or_scene_from_source(s);
-			obs_sceneitem_t *si = obs_scene_find_sceneitem_by_id(sc, id);
-			if (si)
-				obs_sceneitem_set_visible(si, val);
-		};
-
-		QString str = QTStr(val ? "Undo.ShowSceneItem" : "Undo.HideSceneItem");
-
-		OBSBasic *main = OBSBasic::Get();
-		main->undo_s.add_action(str.arg(obs_source_get_name(source), name),
-					std::bind(undo_redo, std::placeholders::_1, id, !val),
-					std::bind(undo_redo, std::placeholders::_1, id, val), uuid, uuid);
-
-		QSignalBlocker sourcesSignalBlocker(this);
-		obs_sceneitem_set_visible(sceneitem, val);
-	};
-
-	auto setItemLocked = [this](bool checked) {
-		QSignalBlocker sourcesSignalBlocker(this);
-		obs_sceneitem_set_locked(sceneitem, checked);
-	};
-
-	connect(vis, &QAbstractButton::clicked, setItemVisible);
-	connect(lock, &QAbstractButton::clicked, setItemLocked);
-}
-
-void SourceTreeItem::paintEvent(QPaintEvent *event)
-{
-	QStyleOption opt;
-	opt.initFrom(this);
-	QPainter p(this);
-	style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
-
-	QWidget::paintEvent(event);
-}
-
-void SourceTreeItem::DisconnectSignals()
-{
-	sigs.clear();
-}
-
-void SourceTreeItem::Clear()
-{
-	DisconnectSignals();
-	sceneitem = nullptr;
-}
-
-void SourceTreeItem::ReconnectSignals()
-{
-	if (!sceneitem)
-		return;
-
-	DisconnectSignals();
-
-	/* --------------------------------------------------------- */
-
-	auto removeItem = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-		obs_scene_t *curScene = (obs_scene_t *)calldata_ptr(cd, "scene");
-
-		if (curItem == this_->sceneitem) {
-			QMetaObject::invokeMethod(this_->tree, "Remove", Q_ARG(OBSSceneItem, curItem),
-						  Q_ARG(OBSScene, curScene));
-			curItem = nullptr;
-		}
-		if (!curItem)
-			QMetaObject::invokeMethod(this_, "Clear");
-	};
-
-	auto itemVisible = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-		bool visible = calldata_bool(cd, "visible");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "VisibilityChanged", Q_ARG(bool, visible));
-	};
-
-	auto itemLocked = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-		bool locked = calldata_bool(cd, "locked");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "LockedChanged", Q_ARG(bool, locked));
-	};
-
-	auto itemSelect = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "Select");
-	};
-
-	auto itemDeselect = [](void *data, calldata_t *cd) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		obs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, "item");
-
-		if (curItem == this_->sceneitem)
-			QMetaObject::invokeMethod(this_, "Deselect");
-	};
-
-	auto reorderGroup = [](void *data, calldata_t *) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		QMetaObject::invokeMethod(this_->tree, "ReorderItems");
-	};
-
-	obs_scene_t *scene = obs_sceneitem_get_scene(sceneitem);
-	obs_source_t *sceneSource = obs_scene_get_source(scene);
-	signal_handler_t *signal = obs_source_get_signal_handler(sceneSource);
-
-	sigs.emplace_back(signal, "remove", removeItem, this);
-	sigs.emplace_back(signal, "item_remove", removeItem, this);
-	sigs.emplace_back(signal, "item_visible", itemVisible, this);
-	sigs.emplace_back(signal, "item_locked", itemLocked, this);
-	sigs.emplace_back(signal, "item_select", itemSelect, this);
-	sigs.emplace_back(signal, "item_deselect", itemDeselect, this);
-
-	if (obs_sceneitem_is_group(sceneitem)) {
-		obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-		signal = obs_source_get_signal_handler(source);
-
-		sigs.emplace_back(signal, "reorder", reorderGroup, this);
-	}
-
-	/* --------------------------------------------------------- */
-
-	auto removeSource = [](void *data, calldata_t *) {
-		SourceTreeItem *this_ = reinterpret_cast<SourceTreeItem *>(data);
-		this_->DisconnectSignals();
-		this_->sceneitem = nullptr;
-		QMetaObject::invokeMethod(this_->tree, "RefreshItems");
-	};
-
-	obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-	signal = obs_source_get_signal_handler(source);
-	sigs.emplace_back(signal, "remove", removeSource, this);
-}
-
-void SourceTreeItem::mouseDoubleClickEvent(QMouseEvent *event)
-{
-	QWidget::mouseDoubleClickEvent(event);
-
-	if (expand) {
-		expand->setChecked(!expand->isChecked());
-	} else {
-		obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-		OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-		if (obs_source_configurable(source)) {
-			main->CreatePropertiesWindow(source);
-		}
-	}
-}
-
-void SourceTreeItem::enterEvent(QEnterEvent *event)
-{
-	QWidget::enterEvent(event);
-
-	OBSBasicPreview *preview = OBSBasicPreview::Get();
-
-	std::lock_guard<std::mutex> lock(preview->selectMutex);
-	preview->hoveredPreviewItems.clear();
-	preview->hoveredPreviewItems.push_back(sceneitem);
-}
-
-void SourceTreeItem::leaveEvent(QEvent *event)
-{
-	QWidget::leaveEvent(event);
-
-	OBSBasicPreview *preview = OBSBasicPreview::Get();
-
-	std::lock_guard<std::mutex> lock(preview->selectMutex);
-	preview->hoveredPreviewItems.clear();
-}
-
-bool SourceTreeItem::IsEditing()
-{
-	return editor != nullptr;
-}
-
-void SourceTreeItem::EnterEditMode()
-{
-	setFocusPolicy(Qt::StrongFocus);
-	int index = boxLayout->indexOf(label);
-	boxLayout->removeWidget(label);
-	editor = new QLineEdit(label->text());
-	editor->setStyleSheet("background: none");
-	editor->selectAll();
-	editor->installEventFilter(this);
-	boxLayout->insertWidget(index, editor);
-	setFocusProxy(editor);
-}
-
-void SourceTreeItem::ExitEditMode(bool save)
-{
-	ExitEditModeInternal(save);
-
-	if (tree->undoSceneData) {
-		OBSBasic *main = OBSBasic::Get();
-		main->undo_s.pop_disabled();
-
-		OBSData redoSceneData = main->BackupScene(GetCurrentScene());
-
-		QString text = QTStr("Undo.GroupItems").arg(newName.c_str());
-		main->CreateSceneUndoRedoAction(text, tree->undoSceneData, redoSceneData);
-
-		tree->undoSceneData = nullptr;
-	}
-}
-
-void SourceTreeItem::ExitEditModeInternal(bool save)
-{
-	if (!editor) {
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-	OBSScene scene = main->GetCurrentScene();
-
-	newName = QT_TO_UTF8(editor->text());
-
-	setFocusProxy(nullptr);
-	int index = boxLayout->indexOf(editor);
-	boxLayout->removeWidget(editor);
-	delete editor;
-	editor = nullptr;
-	setFocusPolicy(Qt::NoFocus);
-	boxLayout->insertWidget(index, label);
-	setFocus();
-
-	/* ----------------------------------------- */
-	/* check for empty string                    */
-
-	if (!save)
-		return;
-
-	if (newName.empty()) {
-		OBSMessageBox::information(main, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text"));
-		return;
-	}
-
-	/* ----------------------------------------- */
-	/* Check for same name                       */
-
-	obs_source_t *source = obs_sceneitem_get_source(sceneitem);
-	if (newName == obs_source_get_name(source))
-		return;
-
-	/* ----------------------------------------- */
-	/* check for existing source                 */
-
-	OBSSourceAutoRelease existingSource = obs_get_source_by_name(newName.c_str());
-	bool exists = !!existingSource;
-
-	if (exists) {
-		OBSMessageBox::information(main, QTStr("NameExists.Title"), QTStr("NameExists.Text"));
-		return;
-	}
-
-	/* ----------------------------------------- */
-	/* rename                                    */
-
-	QSignalBlocker sourcesSignalBlocker(this);
-	std::string prevName(obs_source_get_name(source));
-	std::string scene_uuid = obs_source_get_uuid(main->GetCurrentSceneSource());
-	auto undo = [scene_uuid, prevName, main](const std::string &data) {
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
-		obs_source_set_name(source, prevName.c_str());
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-	};
-
-	std::string editedName = newName;
-
-	auto redo = [scene_uuid, main, editedName](const std::string &data) {
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
-		obs_source_set_name(source, editedName.c_str());
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-	};
-
-	const char *uuid = obs_source_get_uuid(source);
-	main->undo_s.add_action(QTStr("Undo.Rename").arg(newName.c_str()), undo, redo, uuid, uuid);
-
-	obs_source_set_name(source, newName.c_str());
-}
-
-bool SourceTreeItem::eventFilter(QObject *object, QEvent *event)
-{
-	if (editor != object)
-		return false;
-
-	if (LineEditCanceled(event)) {
-		QMetaObject::invokeMethod(this, "ExitEditMode", Qt::QueuedConnection, Q_ARG(bool, false));
-		return true;
-	}
-	if (LineEditChanged(event)) {
-		QMetaObject::invokeMethod(this, "ExitEditMode", Qt::QueuedConnection, Q_ARG(bool, true));
-		return true;
-	}
-
-	return false;
-}
-
-void SourceTreeItem::VisibilityChanged(bool visible)
-{
-	if (iconLabel) {
-		iconLabel->setEnabled(visible);
-	}
-	label->setEnabled(visible);
-	vis->setChecked(visible);
-}
-
-void SourceTreeItem::LockedChanged(bool locked)
-{
-	lock->setChecked(locked);
-	OBSBasic::Get()->UpdateEditMenu();
-}
-
-void SourceTreeItem::Update(bool force)
-{
-	OBSScene scene = GetCurrentScene();
-	obs_scene_t *itemScene = obs_sceneitem_get_scene(sceneitem);
-
-	Type newType;
-
-	/* ------------------------------------------------- */
-	/* if it's a group item, insert group checkbox       */
-
-	if (obs_sceneitem_is_group(sceneitem)) {
-		newType = Type::Group;
-
-		/* ------------------------------------------------- */
-		/* if it's a group sub-item                          */
-
-	} else if (itemScene != scene) {
-		newType = Type::SubItem;
-
-		/* ------------------------------------------------- */
-		/* if it's a regular item                            */
-
-	} else {
-		newType = Type::Item;
-	}
-
-	/* ------------------------------------------------- */
-
-	if (!force && newType == type) {
-		return;
-	}
-
-	/* ------------------------------------------------- */
-
-	ReconnectSignals();
-
-	if (spacer) {
-		boxLayout->removeItem(spacer);
-		delete spacer;
-		spacer = nullptr;
-	}
-
-	if (type == Type::Group) {
-		boxLayout->removeWidget(expand);
-		expand->deleteLater();
-		expand = nullptr;
-	}
-
-	type = newType;
-
-	if (type == Type::SubItem) {
-		spacer = new QSpacerItem(16, 1);
-		boxLayout->insertItem(0, spacer);
-
-	} else if (type == Type::Group) {
-		expand = new QCheckBox();
-		expand->setProperty("class", "checkbox-icon indicator-expand");
-#ifdef __APPLE__
-		expand->setAttribute(Qt::WA_LayoutUsesWidgetRect);
-#endif
-		boxLayout->insertWidget(0, expand);
-
-		OBSDataAutoRelease data = obs_sceneitem_get_private_settings(sceneitem);
-		expand->blockSignals(true);
-		expand->setChecked(obs_data_get_bool(data, "collapsed"));
-		expand->blockSignals(false);
-
-		connect(expand, &QPushButton::toggled, this, &SourceTreeItem::ExpandClicked);
-
-	} else {
-		spacer = new QSpacerItem(3, 1);
-		boxLayout->insertItem(0, spacer);
-	}
-}
-
-void SourceTreeItem::ExpandClicked(bool checked)
-{
-	OBSDataAutoRelease data = obs_sceneitem_get_private_settings(sceneitem);
-
-	obs_data_set_bool(data, "collapsed", checked);
-
-	if (!checked)
-		tree->GetStm()->ExpandGroup(sceneitem);
-	else
-		tree->GetStm()->CollapseGroup(sceneitem);
-}
-
-void SourceTreeItem::Select()
-{
-	tree->SelectItem(sceneitem, true);
-	OBSBasic::Get()->UpdateContextBarDeferred();
-	OBSBasic::Get()->UpdateEditMenu();
-}
-
-void SourceTreeItem::Deselect()
-{
-	tree->SelectItem(sceneitem, false);
-	OBSBasic::Get()->UpdateContextBarDeferred();
-	OBSBasic::Get()->UpdateEditMenu();
-}
-
-/* ========================================================================= */
-
 void SourceTreeModel::OBSFrontendEvent(enum obs_frontend_event event, void *ptr)
 {
 	SourceTreeModel *stm = reinterpret_cast<SourceTreeModel *>(ptr);
@@ -978,636 +432,3 @@ void SourceTreeModel::UpdateGroupState(bool update)
 		}
 	}
 }
-
-/* ========================================================================= */
-
-SourceTree::SourceTree(QWidget *parent_) : QListView(parent_)
-{
-	SourceTreeModel *stm_ = new SourceTreeModel(this);
-	setModel(stm_);
-	setStyleSheet(QString("*[bgColor=\"1\"]{background-color:rgba(255,68,68,33%);}"
-			      "*[bgColor=\"2\"]{background-color:rgba(255,255,68,33%);}"
-			      "*[bgColor=\"3\"]{background-color:rgba(68,255,68,33%);}"
-			      "*[bgColor=\"4\"]{background-color:rgba(68,255,255,33%);}"
-			      "*[bgColor=\"5\"]{background-color:rgba(68,68,255,33%);}"
-			      "*[bgColor=\"6\"]{background-color:rgba(255,68,255,33%);}"
-			      "*[bgColor=\"7\"]{background-color:rgba(68,68,68,33%);}"
-			      "*[bgColor=\"8\"]{background-color:rgba(255,255,255,33%);}"));
-
-	UpdateNoSourcesMessage();
-	connect(App(), &OBSApp::StyleChanged, this, &SourceTree::UpdateNoSourcesMessage);
-	connect(App(), &OBSApp::StyleChanged, this, &SourceTree::UpdateIcons);
-
-	setItemDelegate(new SourceTreeDelegate(this));
-}
-
-void SourceTree::UpdateIcons()
-{
-	SourceTreeModel *stm = GetStm();
-	stm->SceneChanged();
-}
-
-void SourceTree::SetIconsVisible(bool visible)
-{
-	SourceTreeModel *stm = GetStm();
-
-	iconsVisible = visible;
-	stm->SceneChanged();
-}
-
-void SourceTree::ResetWidgets()
-{
-	OBSScene scene = GetCurrentScene();
-
-	SourceTreeModel *stm = GetStm();
-	stm->UpdateGroupState(false);
-
-	for (int i = 0; i < stm->items.count(); i++) {
-		QModelIndex index = stm->createIndex(i, 0, nullptr);
-		setIndexWidget(index, new SourceTreeItem(this, stm->items[i]));
-	}
-}
-
-void SourceTree::UpdateWidget(const QModelIndex &idx, obs_sceneitem_t *item)
-{
-	setIndexWidget(idx, new SourceTreeItem(this, item));
-}
-
-void SourceTree::UpdateWidgets(bool force)
-{
-	SourceTreeModel *stm = GetStm();
-
-	for (int i = 0; i < stm->items.size(); i++) {
-		obs_sceneitem_t *item = stm->items[i];
-		SourceTreeItem *widget = GetItemWidget(i);
-
-		if (!widget) {
-			UpdateWidget(stm->createIndex(i, 0), item);
-		} else {
-			widget->Update(force);
-		}
-	}
-}
-
-void SourceTree::SelectItem(obs_sceneitem_t *sceneitem, bool select)
-{
-	SourceTreeModel *stm = GetStm();
-	int i = 0;
-
-	for (; i < stm->items.count(); i++) {
-		if (stm->items[i] == sceneitem)
-			break;
-	}
-
-	if (i == stm->items.count())
-		return;
-
-	QModelIndex index = stm->createIndex(i, 0);
-	if (index.isValid() && select != selectionModel()->isSelected(index))
-		selectionModel()->select(index, select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
-}
-
-Q_DECLARE_METATYPE(OBSSceneItem);
-
-void SourceTree::mouseDoubleClickEvent(QMouseEvent *event)
-{
-	if (event->button() == Qt::LeftButton)
-		QListView::mouseDoubleClickEvent(event);
-}
-
-void SourceTree::dropEvent(QDropEvent *event)
-{
-	if (event->source() != this) {
-		QListView::dropEvent(event);
-		return;
-	}
-
-	OBSBasic *main = OBSBasic::Get();
-
-	OBSScene scene = GetCurrentScene();
-	obs_source_t *scenesource = obs_scene_get_source(scene);
-	SourceTreeModel *stm = GetStm();
-	auto &items = stm->items;
-	QModelIndexList indices = selectedIndexes();
-
-	DropIndicatorPosition indicator = dropIndicatorPosition();
-	int row = indexAt(event->position().toPoint()).row();
-	bool emptyDrop = row == -1;
-
-	if (emptyDrop) {
-		if (!items.size()) {
-			QListView::dropEvent(event);
-			return;
-		}
-
-		row = items.size() - 1;
-		indicator = QAbstractItemView::BelowItem;
-	}
-
-	/* --------------------------------------- */
-	/* store destination group if moving to a  */
-	/* group                                   */
-
-	obs_sceneitem_t *dropItem = items[row]; /* item being dropped on */
-	bool itemIsGroup = obs_sceneitem_is_group(dropItem);
-
-	obs_sceneitem_t *dropGroup = itemIsGroup ? dropItem : obs_sceneitem_get_group(scene, dropItem);
-
-	/* not a group if moving above the group */
-	if (indicator == QAbstractItemView::AboveItem && itemIsGroup)
-		dropGroup = nullptr;
-	if (emptyDrop)
-		dropGroup = nullptr;
-
-	/* --------------------------------------- */
-	/* remember to remove list items if        */
-	/* dropping on collapsed group             */
-
-	bool dropOnCollapsed = false;
-	if (dropGroup) {
-		obs_data_t *data = obs_sceneitem_get_private_settings(dropGroup);
-		dropOnCollapsed = obs_data_get_bool(data, "collapsed");
-		obs_data_release(data);
-	}
-
-	if (indicator == QAbstractItemView::BelowItem || indicator == QAbstractItemView::OnItem ||
-	    indicator == QAbstractItemView::OnViewport)
-		row++;
-
-	if (row < 0 || row > stm->items.count()) {
-		QListView::dropEvent(event);
-		return;
-	}
-
-	/* --------------------------------------- */
-	/* determine if any base group is selected */
-
-	bool hasGroups = false;
-	for (int i = 0; i < indices.size(); i++) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		if (obs_sceneitem_is_group(item)) {
-			hasGroups = true;
-			break;
-		}
-	}
-
-	/* --------------------------------------- */
-	/* if dropping a group, detect if it's     */
-	/* below another group                     */
-
-	obs_sceneitem_t *itemBelow;
-	if (row == stm->items.count())
-		itemBelow = nullptr;
-	else
-		itemBelow = stm->items[row];
-
-	if (hasGroups) {
-		if (!itemBelow || obs_sceneitem_get_group(scene, itemBelow) != dropGroup) {
-			dropGroup = nullptr;
-			dropOnCollapsed = false;
-		}
-	}
-
-	/* --------------------------------------- */
-	/* if dropping groups on other groups,     */
-	/* disregard as invalid drag/drop          */
-
-	if (dropGroup && hasGroups) {
-		QListView::dropEvent(event);
-		return;
-	}
-
-	/* --------------------------------------- */
-	/* save undo data                          */
-	std::vector<obs_source_t *> sources;
-	for (int i = 0; i < indices.size(); i++) {
-		obs_sceneitem_t *item = items[indices[i].row()];
-		if (obs_sceneitem_get_scene(item) != scene)
-			sources.push_back(obs_scene_get_source(obs_sceneitem_get_scene(item)));
-	}
-	if (dropGroup)
-		sources.push_back(obs_sceneitem_get_source(dropGroup));
-	OBSData undo_data = main->BackupScene(scene, &sources);
-
-	/* --------------------------------------- */
-	/* if selection includes base group items, */
-	/* include all group sub-items and treat   */
-	/* them all as one                         */
-
-	if (hasGroups) {
-		/* remove sub-items if selected */
-		for (int i = indices.size() - 1; i >= 0; i--) {
-			obs_sceneitem_t *item = items[indices[i].row()];
-			obs_scene_t *itemScene = obs_sceneitem_get_scene(item);
-
-			if (itemScene != scene) {
-				indices.removeAt(i);
-			}
-		}
-
-		/* add all sub-items of selected groups */
-		for (int i = indices.size() - 1; i >= 0; i--) {
-			obs_sceneitem_t *item = items[indices[i].row()];
-
-			if (obs_sceneitem_is_group(item)) {
-				for (int j = items.size() - 1; j >= 0; j--) {
-					obs_sceneitem_t *subitem = items[j];
-					obs_sceneitem_t *subitemGroup = obs_sceneitem_get_group(scene, subitem);
-
-					if (subitemGroup == item) {
-						QModelIndex idx = stm->createIndex(j, 0);
-						indices.insert(i + 1, idx);
-					}
-				}
-			}
-		}
-	}
-
-	/* --------------------------------------- */
-	/* build persistent indices                */
-
-	QList<QPersistentModelIndex> persistentIndices;
-	persistentIndices.reserve(indices.count());
-	for (QModelIndex &index : indices)
-		persistentIndices.append(index);
-	std::sort(persistentIndices.begin(), persistentIndices.end());
-
-	/* --------------------------------------- */
-	/* move all items to destination index     */
-
-	int r = row;
-	for (auto &persistentIdx : persistentIndices) {
-		int from = persistentIdx.row();
-		int to = r;
-		int itemTo = to;
-
-		if (itemTo > from)
-			itemTo--;
-
-		if (itemTo != from) {
-			stm->beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
-			MoveItem(items, from, itemTo);
-			stm->endMoveRows();
-		}
-
-		r = persistentIdx.row() + 1;
-	}
-
-	std::sort(persistentIndices.begin(), persistentIndices.end());
-	int firstIdx = persistentIndices.front().row();
-	int lastIdx = persistentIndices.back().row();
-
-	/* --------------------------------------- */
-	/* reorder scene items in back-end         */
-
-	QVector<struct obs_sceneitem_order_info> orderList;
-	obs_sceneitem_t *lastGroup = nullptr;
-	int insertCollapsedIdx = 0;
-
-	auto insertCollapsed = [&](obs_sceneitem_t *item) {
-		struct obs_sceneitem_order_info info;
-		info.group = lastGroup;
-		info.item = item;
-
-		orderList.insert(insertCollapsedIdx++, info);
-	};
-
-	using insertCollapsed_t = decltype(insertCollapsed);
-
-	auto preInsertCollapsed = [](obs_scene_t *, obs_sceneitem_t *item, void *param) {
-		(*reinterpret_cast<insertCollapsed_t *>(param))(item);
-		return true;
-	};
-
-	auto insertLastGroup = [&]() {
-		OBSDataAutoRelease data = obs_sceneitem_get_private_settings(lastGroup);
-		bool collapsed = obs_data_get_bool(data, "collapsed");
-
-		if (collapsed) {
-			insertCollapsedIdx = 0;
-			obs_sceneitem_group_enum_items(lastGroup, preInsertCollapsed, &insertCollapsed);
-		}
-
-		struct obs_sceneitem_order_info info;
-		info.group = nullptr;
-		info.item = lastGroup;
-		orderList.insert(0, info);
-	};
-
-	auto updateScene = [&]() {
-		struct obs_sceneitem_order_info info;
-
-		for (int i = 0; i < items.size(); i++) {
-			obs_sceneitem_t *item = items[i];
-			obs_sceneitem_t *group;
-
-			if (obs_sceneitem_is_group(item)) {
-				if (lastGroup) {
-					insertLastGroup();
-				}
-				lastGroup = item;
-				continue;
-			}
-
-			if (!hasGroups && i >= firstIdx && i <= lastIdx)
-				group = dropGroup;
-			else
-				group = obs_sceneitem_get_group(scene, item);
-
-			if (lastGroup && lastGroup != group) {
-				insertLastGroup();
-			}
-
-			lastGroup = group;
-
-			info.group = group;
-			info.item = item;
-			orderList.insert(0, info);
-		}
-
-		if (lastGroup) {
-			insertLastGroup();
-		}
-
-		obs_scene_reorder_items2(scene, orderList.data(), orderList.size());
-	};
-
-	using updateScene_t = decltype(updateScene);
-
-	auto preUpdateScene = [](void *data, obs_scene_t *) {
-		(*reinterpret_cast<updateScene_t *>(data))();
-	};
-
-	ignoreReorder = true;
-	obs_scene_atomic_update(scene, preUpdateScene, &updateScene);
-	ignoreReorder = false;
-
-	/* --------------------------------------- */
-	/* save redo data                          */
-
-	OBSData redo_data = main->BackupScene(scene, &sources);
-
-	/* --------------------------------------- */
-	/* add undo/redo action                    */
-
-	const char *scene_name = obs_source_get_name(scenesource);
-	QString action_name = QTStr("Undo.ReorderSources").arg(scene_name);
-	main->CreateSceneUndoRedoAction(action_name, undo_data, redo_data);
-
-	/* --------------------------------------- */
-	/* remove items if dropped in to collapsed */
-	/* group                                   */
-
-	if (dropOnCollapsed) {
-		stm->beginRemoveRows(QModelIndex(), firstIdx, lastIdx);
-		items.remove(firstIdx, lastIdx - firstIdx + 1);
-		stm->endRemoveRows();
-	}
-
-	/* --------------------------------------- */
-	/* update widgets and accept event         */
-
-	UpdateWidgets(true);
-
-	event->accept();
-	event->setDropAction(Qt::CopyAction);
-
-	QListView::dropEvent(event);
-}
-
-void SourceTree::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
-{
-	{
-		QSignalBlocker sourcesSignalBlocker(this);
-		SourceTreeModel *stm = GetStm();
-
-		QModelIndexList selectedIdxs = selected.indexes();
-		QModelIndexList deselectedIdxs = deselected.indexes();
-
-		for (int i = 0; i < selectedIdxs.count(); i++) {
-			int idx = selectedIdxs[i].row();
-			obs_sceneitem_select(stm->items[idx], true);
-		}
-
-		for (int i = 0; i < deselectedIdxs.count(); i++) {
-			int idx = deselectedIdxs[i].row();
-			obs_sceneitem_select(stm->items[idx], false);
-		}
-	}
-	QListView::selectionChanged(selected, deselected);
-}
-
-void SourceTree::NewGroupEdit(int row)
-{
-	if (!Edit(row)) {
-		OBSBasic *main = OBSBasic::Get();
-		main->undo_s.pop_disabled();
-
-		blog(LOG_WARNING, "Uh, somehow the edit didn't process, this "
-				  "code should never be reached.\nAnd by "
-				  "\"never be reached\", I mean that "
-				  "theoretically, it should be\nimpossible "
-				  "for this code to be reached. But if this "
-				  "code is reached,\nfeel free to laugh at "
-				  "Lain, because apparently it is, in fact, "
-				  "actually\npossible for this code to be "
-				  "reached. But I mean, again, theoretically\n"
-				  "it should be impossible. So if you see "
-				  "this in your log, just know that\nit's "
-				  "really dumb, and depressing. But at least "
-				  "the undo/redo action is\nstill covered, so "
-				  "in theory things *should* be fine. But "
-				  "it's entirely\npossible that they might "
-				  "not be exactly. But again, yea. This "
-				  "really\nshould not be possible.");
-
-		OBSData redoSceneData = main->BackupScene(GetCurrentScene());
-
-		QString text = QTStr("Undo.GroupItems").arg("Unknown");
-		main->CreateSceneUndoRedoAction(text, undoSceneData, redoSceneData);
-
-		undoSceneData = nullptr;
-	}
-}
-
-bool SourceTree::Edit(int row)
-{
-	SourceTreeModel *stm = GetStm();
-	if (row < 0 || row >= stm->items.count())
-		return false;
-
-	QModelIndex index = stm->createIndex(row, 0);
-	QWidget *widget = indexWidget(index);
-	SourceTreeItem *itemWidget = reinterpret_cast<SourceTreeItem *>(widget);
-	if (itemWidget->IsEditing()) {
-#ifdef __APPLE__
-		itemWidget->ExitEditMode(true);
-#endif
-		return false;
-	}
-
-	itemWidget->EnterEditMode();
-	edit(index);
-	return true;
-}
-
-bool SourceTree::MultipleBaseSelected() const
-{
-	SourceTreeModel *stm = GetStm();
-	QModelIndexList selectedIndices = selectedIndexes();
-
-	OBSScene scene = GetCurrentScene();
-
-	if (selectedIndices.size() < 1) {
-		return false;
-	}
-
-	for (auto &idx : selectedIndices) {
-		obs_sceneitem_t *item = stm->items[idx.row()];
-		if (obs_sceneitem_is_group(item)) {
-			return false;
-		}
-
-		obs_scene *itemScene = obs_sceneitem_get_scene(item);
-		if (itemScene != scene) {
-			return false;
-		}
-	}
-
-	return true;
-}
-
-bool SourceTree::GroupsSelected() const
-{
-	SourceTreeModel *stm = GetStm();
-	QModelIndexList selectedIndices = selectedIndexes();
-
-	OBSScene scene = GetCurrentScene();
-
-	if (selectedIndices.size() < 1) {
-		return false;
-	}
-
-	for (auto &idx : selectedIndices) {
-		obs_sceneitem_t *item = stm->items[idx.row()];
-		if (!obs_sceneitem_is_group(item)) {
-			return false;
-		}
-	}
-
-	return true;
-}
-
-bool SourceTree::GroupedItemsSelected() const
-{
-	SourceTreeModel *stm = GetStm();
-	QModelIndexList selectedIndices = selectedIndexes();
-	OBSScene scene = GetCurrentScene();
-
-	if (!selectedIndices.size()) {
-		return false;
-	}
-
-	for (auto &idx : selectedIndices) {
-		obs_sceneitem_t *item = stm->items[idx.row()];
-		obs_scene *itemScene = obs_sceneitem_get_scene(item);
-
-		if (itemScene != scene) {
-			return true;
-		}
-	}
-
-	return false;
-}
-
-void SourceTree::Remove(OBSSceneItem item, OBSScene scene)
-{
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-	GetStm()->Remove(item);
-	main->SaveProject();
-
-	if (!main->SavingDisabled()) {
-		obs_source_t *sceneSource = obs_scene_get_source(scene);
-		obs_source_t *itemSource = obs_sceneitem_get_source(item);
-		blog(LOG_INFO, "User Removed source '%s' (%s) from scene '%s'", obs_source_get_name(itemSource),
-		     obs_source_get_id(itemSource), obs_source_get_name(sceneSource));
-	}
-}
-
-void SourceTree::GroupSelectedItems()
-{
-	QModelIndexList indices = selectedIndexes();
-	std::sort(indices.begin(), indices.end());
-	GetStm()->GroupSelectedItems(indices);
-}
-
-void SourceTree::UngroupSelectedGroups()
-{
-	QModelIndexList indices = selectedIndexes();
-	GetStm()->UngroupSelectedGroups(indices);
-}
-
-void SourceTree::AddGroup()
-{
-	GetStm()->AddGroup();
-}
-
-void SourceTree::UpdateNoSourcesMessage()
-{
-	QString file = !App()->IsThemeDark() ? ":res/images/no_sources.svg" : "theme:Dark/no_sources.svg";
-	iconNoSources.load(file);
-
-	QTextOption opt(Qt::AlignHCenter);
-	opt.setWrapMode(QTextOption::WordWrap);
-	textNoSources.setTextOption(opt);
-	textNoSources.setText(QTStr("NoSources.Label").replace("\n", "<br/>"));
-
-	textPrepared = false;
-}
-
-void SourceTree::paintEvent(QPaintEvent *event)
-{
-	SourceTreeModel *stm = GetStm();
-	if (stm && !stm->items.count()) {
-		QPainter p(viewport());
-
-		if (!textPrepared) {
-			textNoSources.prepare(QTransform(), p.font());
-			textPrepared = true;
-		}
-
-		QRectF iconRect = iconNoSources.viewBoxF();
-		iconRect.setSize(QSizeF(32.0, 32.0));
-
-		QSizeF iconSize = iconRect.size();
-		QSizeF textSize = textNoSources.size();
-		QSizeF thisSize = size();
-		const qreal spacing = 16.0;
-
-		qreal totalHeight = iconSize.height() + spacing + textSize.height();
-
-		qreal x = thisSize.width() / 2.0 - iconSize.width() / 2.0;
-		qreal y = thisSize.height() / 2.0 - totalHeight / 2.0;
-		iconRect.moveTo(std::round(x), std::round(y));
-		iconNoSources.render(&p, iconRect);
-
-		x = thisSize.width() / 2.0 - textSize.width() / 2.0;
-		y += spacing + iconSize.height();
-		p.drawStaticText(x, y, textNoSources);
-	} else {
-		QListView::paintEvent(event);
-	}
-}
-
-SourceTreeDelegate::SourceTreeDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
-
-QSize SourceTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
-{
-	SourceTree *tree = qobject_cast<SourceTree *>(parent());
-	QWidget *item = tree->indexWidget(index);
-
-	if (!item)
-		return (QSize(0, 0));
-
-	return (QSize(option.widget->minimumWidth(), item->height()));
-}

+ 3 - 158
frontend/components/SourceTreeModel.hpp

@@ -1,91 +1,11 @@
 #pragma once
 
-#include <QList>
-#include <QVector>
-#include <QPointer>
-#include <QListView>
-#include <QCheckBox>
-#include <QStaticText>
-#include <QSvgRenderer>
-#include <QAbstractListModel>
-#include <QStyledItemDelegate>
-#include <obs.hpp>
 #include <obs-frontend-api.h>
+#include <obs.hpp>
 
-class QLabel;
-class OBSSourceLabel;
-class QCheckBox;
-class QLineEdit;
-class SourceTree;
-class QSpacerItem;
-class QHBoxLayout;
-class VisibilityItemWidget;
-
-class SourceTreeItem : public QFrame {
-	Q_OBJECT
-
-	friend class SourceTree;
-	friend class SourceTreeModel;
-
-	void mouseDoubleClickEvent(QMouseEvent *event) override;
-	void enterEvent(QEnterEvent *event) override;
-	void leaveEvent(QEvent *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);
-	bool IsEditing();
-
-private:
-	QSpacerItem *spacer = nullptr;
-	QCheckBox *expand = nullptr;
-	QLabel *iconLabel = nullptr;
-	QCheckBox *vis = nullptr;
-	QCheckBox *lock = nullptr;
-	QHBoxLayout *boxLayout = nullptr;
-	OBSSourceLabel *label = nullptr;
-
-	QLineEdit *editor = nullptr;
-
-	std::string newName;
-
-	SourceTree *tree;
-	OBSSceneItem sceneitem;
-	std::vector<OBSSignal> sigs;
-
-	virtual void paintEvent(QPaintEvent *event) override;
-
-	void ExitEditModeInternal(bool save);
-
-private slots:
-	void Clear();
-
-	void EnterEditMode();
-	void ExitEditMode(bool save);
-
-	void VisibilityChanged(bool visible);
-	void LockedChanged(bool locked);
-
-	void ExpandClicked(bool checked);
+#include <QAbstractListModel>
 
-	void Select();
-	void Deselect();
-};
+class SourceTree;
 
 class SourceTreeModel : public QAbstractListModel {
 	Q_OBJECT
@@ -125,78 +45,3 @@ public:
 	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;
-
-	bool textPrepared = false;
-	QStaticText textNoSources;
-	QSvgRenderer iconNoSources;
-
-	OBSData undoSceneData;
-
-	bool iconsVisible = true;
-
-	void UpdateNoSourcesMessage();
-
-	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()); }
-
-public:
-	inline SourceTreeItem *GetItemWidget(int idx)
-	{
-		QWidget *widget = indexWidget(GetStm()->createIndex(idx, 0));
-		return reinterpret_cast<SourceTreeItem *>(widget);
-	}
-
-	explicit SourceTree(QWidget *parent = nullptr);
-
-	inline bool IgnoreReorder() const { return ignoreReorder; }
-	inline void Clear() { GetStm()->Clear(); }
-
-	inline void Add(obs_sceneitem_t *item) { GetStm()->Add(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;
-
-	void UpdateIcons();
-	void SetIconsVisible(bool visible);
-
-public slots:
-	inline void ReorderItems() { GetStm()->ReorderItems(); }
-	inline void RefreshItems() { GetStm()->SceneChanged(); }
-	void Remove(OBSSceneItem item, OBSScene scene);
-	void GroupSelectedItems();
-	void UngroupSelectedGroups();
-	void AddGroup();
-	bool Edit(int idx);
-	void NewGroupEdit(int idx);
-
-protected:
-	virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
-	virtual void dropEvent(QDropEvent *event) override;
-	virtual void paintEvent(QPaintEvent *event) override;
-
-	virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
-};
-
-class SourceTreeDelegate : public QStyledItemDelegate {
-	Q_OBJECT
-
-public:
-	SourceTreeDelegate(QObject *parent);
-	virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
-};

+ 8 - 564
frontend/components/TextSourceToolbar.cpp

@@ -1,574 +1,18 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
-
-#include "ui_browser-source-toolbar.h"
-#include "ui_device-select-toolbar.h"
-#include "ui_game-capture-toolbar.h"
-#include "ui_image-source-toolbar.h"
-#include "ui_color-source-toolbar.h"
+#include "TextSourceToolbar.hpp"
 #include "ui_text-source-toolbar.h"
 
-#ifdef _WIN32
-#define get_os_module(win, mac, linux) obs_get_module(win)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)
-#elif __APPLE__
-#define get_os_module(win, mac, linux) obs_get_module(mac)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)
-#else
-#define get_os_module(win, mac, linux) obs_get_module(linux)
-#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
-#endif
-
-/* ========================================================================= */
-
-SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  props(obs_source_properties(source), obs_properties_destroy)
-{
-}
-
-void SourceToolbar::SaveOldProperties(obs_source_t *source)
-{
-	oldData = obs_data_create();
-
-	OBSDataAutoRelease oldSettings = obs_source_get_settings(source);
-	obs_data_apply(oldData, oldSettings);
-	obs_data_set_string(oldData, "undo_suuid", obs_source_get_uuid(source));
-}
-
-void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
-{
-	if (!oldData) {
-		blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__);
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-
-	OBSSource currentSceneSource = main->GetCurrentSceneSource();
-	if (!currentSceneSource)
-		return;
-	std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
-	auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
-		OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
-		obs_source_reset_settings(source, settings);
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-
-		main->UpdateContextBar();
-	};
-
-	OBSDataAutoRelease new_settings = obs_data_create();
-	OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
-	obs_data_apply(new_settings, curr_settings);
-	obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
-
-	std::string undo_data(obs_data_get_json(oldData));
-	std::string redo_data(obs_data_get_json(new_settings));
-
-	if (undo_data.compare(redo_data) != 0)
-		main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
-					undo_data, redo_data, repeatable);
-
-	oldData = nullptr;
-}
-
-/* ========================================================================= */
-
-BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_BrowserSourceToolbar)
-{
-	ui->setupUi(this);
-}
-
-BrowserToolbar::~BrowserToolbar() {}
-
-void BrowserToolbar::on_refresh_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
-	obs_property_button_clicked(p, source.Get());
-}
-
-/* ========================================================================= */
-
-ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-}
-
-ComboSelectToolbar::~ComboSelectToolbar() {}
-
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
-{
-	size_t count = obs_property_list_item_count(p);
-	int cur_idx = -1;
-
-	for (size_t i = 0; i < count; i++) {
-		const char *name = obs_property_list_item_name(p, i);
-		std::string id;
-
-		if (is_int) {
-			id = std::to_string(obs_property_list_item_int(p, i));
-		} else {
-			const char *val = obs_property_list_item_string(p, i);
-			id = val ? val : "";
-		}
-
-		if (cur_id == id)
-			cur_idx = (int)i;
-
-		c->addItem(name, id.c_str());
-	}
-
-	return cur_idx;
-}
-
-void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
-					const char *prop_name, bool is_int)
-{
-	std::string cur_id;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (is_int) {
-		cur_id = std::to_string(obs_data_get_int(settings, prop_name));
-	} else {
-		cur_id = obs_data_get_string(settings, prop_name);
-	}
-
-	combo->blockSignals(true);
-
-	obs_property_t *p = obs_properties_get(props, prop_name);
-	int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);
-
-	if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
-		if (cur_idx == -1) {
-			combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
-			cur_idx = 0;
-		}
-
-		SetComboItemEnabled(combo, cur_idx, false);
-	}
-
-	combo->setCurrentIndex(cur_idx);
-	combo->blockSignals(false);
-}
-
-void ComboSelectToolbar::Init()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
-}
-
-void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
-{
-	QString id = combo->itemData(idx).toString();
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (is_int) {
-		obs_data_set_int(settings, prop_name, id.toInt());
-	} else {
-		obs_data_set_string(settings, prop_name, QT_TO_UTF8(id));
-	}
-	obs_source_update(source, settings);
-}
-
-void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	SaveOldProperties(source);
-	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
-	SetUndoProperties(source);
-}
-
-AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void AudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "device_id";
-
-	ComboSelectToolbar::Init();
-}
-
-WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void WindowCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "WindowCapture.Window", "WindowUtils.Window", "Window");
-	ui->deviceLabel->setText(device_str);
-
-#if !defined(_WIN32) && !defined(__APPLE__) //linux
-	prop_name = "capture_window";
-#else
-	prop_name = "window";
-#endif
-
-#ifdef __APPLE__
-	is_int = true;
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void ApplicationAudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = obs_get_module("win-wasapi");
-	const char *device_str = obs_module_get_locale_text(mod, "Window");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "window";
-
-	ComboSelectToolbar::Init();
-}
-
-DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void DisplayCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "monitor_id";
-#elif __APPLE__
-	prop_name = "display_uuid";
-#else
-	is_int = true;
-	prop_name = "screen";
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-/* ========================================================================= */
-
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-
-	delete ui->deviceLabel;
-	delete ui->device;
-	ui->deviceLabel = nullptr;
-	ui->device = nullptr;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	active = obs_data_get_bool(settings, "active");
-
-	obs_module_t *mod = obs_get_module("win-dshow");
-	if (!mod)
-		return;
+#include <OBSApp.hpp>
 
-	activateText = obs_module_get_locale_text(mod, "Activate");
-	deactivateText = obs_module_get_locale_text(mod, "Deactivate");
-
-	ui->activateButton->setText(active ? deactivateText : activateText);
-}
-
-DeviceCaptureToolbar::~DeviceCaptureToolbar() {}
-
-void DeviceCaptureToolbar::on_activateButton_clicked()
-{
-	OBSSource source = OBSGetStrongRef(weakSource);
-	if (!source) {
-		return;
-	}
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	bool now_active = obs_data_get_bool(settings, "active");
-
-	bool desyncedSetting = now_active != active;
-
-	active = !active;
-
-	const char *text = active ? deactivateText : activateText;
-	ui->activateButton->setText(text);
-
-	if (desyncedSetting) {
-		return;
-	}
-
-	calldata_t cd = {};
-	calldata_set_bool(&cd, "active", active);
-	proc_handler_t *ph = obs_source_get_proc_handler(source);
-	proc_handler_call(ph, "activate", &cd);
-	calldata_free(&cd);
-}
-
-/* ========================================================================= */
-
-GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_GameCaptureToolbar)
-{
-	obs_property_t *p;
-	int cur_idx;
-
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("win-capture");
-	if (!mod)
-		return;
-
-	ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
-	ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string cur_mode = obs_data_get_string(settings, "capture_mode");
-	std::string cur_window = obs_data_get_string(settings, "window");
-
-	ui->mode->blockSignals(true);
-	p = obs_properties_get(props.get(), "capture_mode");
-	cur_idx = FillPropertyCombo(ui->mode, p, cur_mode);
-	ui->mode->setCurrentIndex(cur_idx);
-	ui->mode->blockSignals(false);
-
-	ui->window->blockSignals(true);
-	p = obs_properties_get(props.get(), "window");
-	cur_idx = FillPropertyCombo(ui->window, p, cur_window);
-	ui->window->setCurrentIndex(cur_idx);
-	ui->window->blockSignals(false);
-
-	if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {
-		SetComboItemEnabled(ui->window, cur_idx, false);
-	}
-
-	UpdateWindowVisibility();
-}
-
-GameCaptureToolbar::~GameCaptureToolbar() {}
-
-void GameCaptureToolbar::UpdateWindowVisibility()
-{
-	QString mode = ui->mode->currentData().toString();
-	bool is_window = (mode == "window");
-	ui->windowLabel->setVisible(is_window);
-	ui->window->setVisible(is_window);
-}
-
-void GameCaptureToolbar::on_mode_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->mode->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-
-	UpdateWindowVisibility();
-}
-
-void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->window->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "window", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ImageSourceToolbar)
-{
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("image-source");
-	ui->pathLabel->setText(obs_module_get_locale_text(mod, "File"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string file = obs_data_get_string(settings, "file");
-
-	ui->path->setText(file.c_str());
-}
-
-ImageSourceToolbar::~ImageSourceToolbar() {}
-
-void ImageSourceToolbar::on_browse_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "file");
-	const char *desc = obs_property_description(p);
-	const char *filter = obs_property_path_filter(p);
-	const char *default_path = obs_property_path_default_path(p);
-
-	QString startDir = ui->path->text();
-	if (startDir.isEmpty())
-		startDir = default_path;
-
-	QString path = OpenFile(this, desc, startDir, filter);
-	if (path.isEmpty()) {
-		return;
-	}
-
-	ui->path->setText(path);
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "file", QT_TO_UTF8(path));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-static inline QColor color_from_int(long long val)
-{
-	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
-}
-
-static inline long long color_to_int(QColor color)
-{
-	auto shift = [&](unsigned val, int shift) {
-		return ((val & 0xff) << shift);
-	};
-
-	return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
-}
-
-ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ColorSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	unsigned int val = (unsigned int)obs_data_get_int(settings, "color");
-
-	color = color_from_int(val);
-	UpdateColor();
-}
-
-ColorSourceToolbar::~ColorSourceToolbar() {}
-
-void ColorSourceToolbar::UpdateColor()
-{
-	QPalette palette = QPalette(color);
-	ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
-	ui->color->setText(color.name(QColor::HexRgb));
-	ui->color->setPalette(palette);
-	ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
-					 .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
-					 .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
-	ui->color->setAutoFillBackground(true);
-	ui->color->setAlignment(Qt::AlignCenter);
-}
-
-void ColorSourceToolbar::on_choose_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "color");
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-	UpdateColor();
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_int(settings, "color", color_to_int(color));
-	obs_source_update(source, settings);
+#include <qt-wrappers.hpp>
 
-	SetUndoProperties(source);
-}
+#include <QColorDialog>
+#include <QFontDialog>
 
-/* ========================================================================= */
+#include "moc_TextSourceToolbar.cpp"
 
 extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
+extern QColor color_from_int(long long val);
+extern long long color_to_int(QColor color);
 
 TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
 	: SourceToolbar(parent, source),

+ 1 - 157
frontend/components/TextSourceToolbar.hpp

@@ -1,165 +1,9 @@
 #pragma once
 
-#include <memory>
-#include <obs.hpp>
-#include <QWidget>
+#include "SourceToolbar.hpp"
 
-class Ui_BrowserSourceToolbar;
-class Ui_DeviceSelectToolbar;
-class Ui_GameCaptureToolbar;
-class Ui_ImageSourceToolbar;
-class Ui_ColorSourceToolbar;
 class Ui_TextSourceToolbar;
 
-class SourceToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-protected:
-	using properties_delete_t = decltype(&obs_properties_destroy);
-	using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
-
-	properties_t props;
-	OBSDataAutoRelease oldData;
-
-	void SaveOldProperties(obs_source_t *source);
-	void SetUndoProperties(obs_source_t *source, bool repeatable = false);
-
-public:
-	SourceToolbar(QWidget *parent, OBSSource source);
-
-	OBSSource GetSource() { return OBSGetStrongRef(weakSource); }
-
-public slots:
-	virtual void Update() {}
-};
-
-class BrowserToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_BrowserSourceToolbar> ui;
-
-public:
-	BrowserToolbar(QWidget *parent, OBSSource source);
-	~BrowserToolbar();
-
-public slots:
-	void on_refresh_clicked();
-};
-
-class ComboSelectToolbar : public SourceToolbar {
-	Q_OBJECT
-
-protected:
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *prop_name;
-	bool is_int = false;
-
-public:
-	ComboSelectToolbar(QWidget *parent, OBSSource source);
-	~ComboSelectToolbar();
-	virtual void Init();
-
-public slots:
-	void on_device_currentIndexChanged(int idx);
-};
-
-class AudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	AudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class WindowCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	WindowCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DisplayCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DeviceCaptureToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *activateText;
-	const char *deactivateText;
-	bool active;
-
-public:
-	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	~DeviceCaptureToolbar();
-
-public slots:
-	void on_activateButton_clicked();
-};
-
-class GameCaptureToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_GameCaptureToolbar> ui;
-
-	void UpdateWindowVisibility();
-
-public:
-	GameCaptureToolbar(QWidget *parent, OBSSource source);
-	~GameCaptureToolbar();
-
-public slots:
-	void on_mode_currentIndexChanged(int idx);
-	void on_window_currentIndexChanged(int idx);
-};
-
-class ImageSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ImageSourceToolbar> ui;
-
-public:
-	ImageSourceToolbar(QWidget *parent, OBSSource source);
-	~ImageSourceToolbar();
-
-public slots:
-	void on_browse_clicked();
-};
-
-class ColorSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ColorSourceToolbar> ui;
-	QColor color;
-
-	void UpdateColor();
-
-public:
-	ColorSourceToolbar(QWidget *parent, OBSSource source);
-	~ColorSourceToolbar();
-
-public slots:
-	void on_choose_clicked();
-};
-
 class TextSourceToolbar : public SourceToolbar {
 	Q_OBJECT
 

+ 4 - 71
frontend/components/VisibilityItemDelegate.cpp

@@ -1,69 +1,9 @@
-#include "moc_visibility-item-widget.cpp"
-#include "obs-app.hpp"
-#include "source-label.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QListWidget>
-#include <QLineEdit>
-#include <QHBoxLayout>
-#include <QMessageBox>
-#include <QLabel>
-#include <QKeyEvent>
-#include <QCheckBox>
-
-VisibilityItemWidget::VisibilityItemWidget(obs_source_t *source_)
-	: source(source_),
-	  enabledSignal(obs_source_get_signal_handler(source), "enable", OBSSourceEnabled, this)
-{
-	bool enabled = obs_source_enabled(source);
-
-	vis = new QCheckBox();
-	vis->setProperty("class", "checkbox-icon indicator-visibility");
-	vis->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
-	vis->setChecked(enabled);
-
-	label = new OBSSourceLabel(source);
-	label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
-
-	QHBoxLayout *itemLayout = new QHBoxLayout();
-	itemLayout->addWidget(vis);
-	itemLayout->addWidget(label);
-	itemLayout->setContentsMargins(0, 0, 0, 0);
-
-	setLayout(itemLayout);
-
-	connect(vis, &QCheckBox::clicked, [this](bool visible) { obs_source_set_enabled(source, visible); });
-}
-
-void VisibilityItemWidget::OBSSourceEnabled(void *param, calldata_t *data)
-{
-	VisibilityItemWidget *window = reinterpret_cast<VisibilityItemWidget *>(param);
-	bool enabled = calldata_bool(data, "enabled");
-
-	QMetaObject::invokeMethod(window, "SourceEnabled", Q_ARG(bool, enabled));
-}
+#include "VisibilityItemWidget.hpp"
+#include "VisibilityItemDelegate.hpp"
 
-void VisibilityItemWidget::SourceEnabled(bool enabled)
-{
-	if (vis->isChecked() != enabled)
-		vis->setChecked(enabled);
-}
-
-void VisibilityItemWidget::SetColor(const QColor &color, bool active_, bool selected_)
-{
-	/* Do not update unless the state has actually changed */
-	if (active_ == active && selected_ == selected)
-		return;
-
-	QPalette pal = vis->palette();
-	pal.setColor(QPalette::WindowText, color);
-	vis->setPalette(pal);
-
-	label->setStyleSheet(QString("color: %1;").arg(color.name()));
+#include <QKeyEvent>
 
-	active = active_;
-	selected = selected_;
-}
+#include "moc_VisibilityItemDelegate.cpp"
 
 VisibilityItemDelegate::VisibilityItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
 
@@ -124,10 +64,3 @@ bool VisibilityItemDelegate::eventFilter(QObject *object, QEvent *event)
 
 	return QStyledItemDelegate::eventFilter(object, event);
 }
-
-void SetupVisibilityItem(QListWidget *list, QListWidgetItem *item, obs_source_t *source)
-{
-	VisibilityItemWidget *baseWidget = new VisibilityItemWidget(source);
-
-	list->setItemWidget(item, baseWidget);
-}

+ 1 - 34
frontend/components/VisibilityItemDelegate.hpp

@@ -1,39 +1,8 @@
 #pragma once
 
-#include <QWidget>
 #include <QStyledItemDelegate>
-#include <obs.hpp>
 
-class QLabel;
-class QLineEdit;
-class QListWidget;
-class QListWidgetItem;
-class QCheckBox;
-class OBSSourceLabel;
-
-class VisibilityItemWidget : public QWidget {
-	Q_OBJECT
-
-private:
-	OBSSource source;
-	OBSSourceLabel *label = nullptr;
-	QCheckBox *vis = nullptr;
-
-	OBSSignal enabledSignal;
-
-	bool active = false;
-	bool selected = false;
-
-	static void OBSSourceEnabled(void *param, calldata_t *data);
-
-private slots:
-	void SourceEnabled(bool enabled);
-
-public:
-	VisibilityItemWidget(obs_source_t *source);
-
-	void SetColor(const QColor &color, bool active, bool selected);
-};
+class QObject;
 
 class VisibilityItemDelegate : public QStyledItemDelegate {
 	Q_OBJECT
@@ -46,5 +15,3 @@ public:
 protected:
 	bool eventFilter(QObject *object, QEvent *event) override;
 };
-
-void SetupVisibilityItem(QListWidget *list, QListWidgetItem *item, obs_source_t *source);

+ 6 - 70
frontend/components/VisibilityItemWidget.cpp

@@ -1,15 +1,11 @@
-#include "moc_visibility-item-widget.cpp"
-#include "obs-app.hpp"
-#include "source-label.hpp"
+#include "VisibilityItemWidget.hpp"
+
+#include <components/OBSSourceLabel.hpp>
 
-#include <qt-wrappers.hpp>
-#include <QListWidget>
-#include <QLineEdit>
-#include <QHBoxLayout>
-#include <QMessageBox>
-#include <QLabel>
-#include <QKeyEvent>
 #include <QCheckBox>
+#include <QHBoxLayout>
+
+#include "moc_VisibilityItemWidget.cpp"
 
 VisibilityItemWidget::VisibilityItemWidget(obs_source_t *source_)
 	: source(source_),
@@ -65,66 +61,6 @@ void VisibilityItemWidget::SetColor(const QColor &color, bool active_, bool sele
 	selected = selected_;
 }
 
-VisibilityItemDelegate::VisibilityItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
-
-void VisibilityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
-				   const QModelIndex &index) const
-{
-	QStyledItemDelegate::paint(painter, option, index);
-
-	QObject *parentObj = parent();
-	QListWidget *list = qobject_cast<QListWidget *>(parentObj);
-	if (!list)
-		return;
-
-	QListWidgetItem *item = list->item(index.row());
-	VisibilityItemWidget *widget = qobject_cast<VisibilityItemWidget *>(list->itemWidget(item));
-	if (!widget)
-		return;
-
-	bool selected = option.state.testFlag(QStyle::State_Selected);
-	bool active = option.state.testFlag(QStyle::State_Active);
-
-	QPalette palette = list->palette();
-#if defined(_WIN32) || defined(__APPLE__)
-	QPalette::ColorGroup group = active ? QPalette::Active : QPalette::Inactive;
-#else
-	QPalette::ColorGroup group = QPalette::Active;
-#endif
-
-#ifdef _WIN32
-	QPalette::ColorRole highlightRole = QPalette::WindowText;
-#else
-	QPalette::ColorRole highlightRole = QPalette::HighlightedText;
-#endif
-
-	QPalette::ColorRole role;
-
-	if (selected && active)
-		role = highlightRole;
-	else
-		role = QPalette::WindowText;
-
-	widget->SetColor(palette.color(group, role), active, selected);
-}
-
-bool VisibilityItemDelegate::eventFilter(QObject *object, QEvent *event)
-{
-	QWidget *editor = qobject_cast<QWidget *>(object);
-	if (!editor)
-		return false;
-
-	if (event->type() == QEvent::KeyPress) {
-		QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
-
-		if (keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab) {
-			return false;
-		}
-	}
-
-	return QStyledItemDelegate::eventFilter(object, event);
-}
-
 void SetupVisibilityItem(QListWidget *list, QListWidgetItem *item, obs_source_t *source)
 {
 	VisibilityItemWidget *baseWidget = new VisibilityItemWidget(source);

+ 4 - 19
frontend/components/VisibilityItemWidget.hpp

@@ -1,15 +1,12 @@
 #pragma once
 
-#include <QWidget>
-#include <QStyledItemDelegate>
 #include <obs.hpp>
 
-class QLabel;
-class QLineEdit;
-class QListWidget;
-class QListWidgetItem;
-class QCheckBox;
+#include <QListWidget>
+#include <QWidget>
+
 class OBSSourceLabel;
+class QCheckBox;
 
 class VisibilityItemWidget : public QWidget {
 	Q_OBJECT
@@ -35,16 +32,4 @@ public:
 	void SetColor(const QColor &color, bool active, bool selected);
 };
 
-class VisibilityItemDelegate : public QStyledItemDelegate {
-	Q_OBJECT
-
-public:
-	VisibilityItemDelegate(QObject *parent = nullptr);
-
-	void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
-
-protected:
-	bool eventFilter(QObject *object, QEvent *event) override;
-};
-
 void SetupVisibilityItem(QListWidget *list, QListWidgetItem *item, obs_source_t *source);

+ 2 - 668
frontend/components/WindowCaptureToolbar.cpp

@@ -1,18 +1,6 @@
-#include "window-basic-main.hpp"
-#include "moc_context-bar-controls.cpp"
-#include "obs-app.hpp"
-
-#include <qt-wrappers.hpp>
-#include <QStandardItemModel>
-#include <QColorDialog>
-#include <QFontDialog>
-
-#include "ui_browser-source-toolbar.h"
+#include "WindowCaptureToolbar.hpp"
 #include "ui_device-select-toolbar.h"
-#include "ui_game-capture-toolbar.h"
-#include "ui_image-source-toolbar.h"
-#include "ui_color-source-toolbar.h"
-#include "ui_text-source-toolbar.h"
+#include "moc_WindowCaptureToolbar.cpp"
 
 #ifdef _WIN32
 #define get_os_module(win, mac, linux) obs_get_module(win)
@@ -25,205 +13,6 @@
 #define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
 #endif
 
-/* ========================================================================= */
-
-SourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  props(obs_source_properties(source), obs_properties_destroy)
-{
-}
-
-void SourceToolbar::SaveOldProperties(obs_source_t *source)
-{
-	oldData = obs_data_create();
-
-	OBSDataAutoRelease oldSettings = obs_source_get_settings(source);
-	obs_data_apply(oldData, oldSettings);
-	obs_data_set_string(oldData, "undo_suuid", obs_source_get_uuid(source));
-}
-
-void SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)
-{
-	if (!oldData) {
-		blog(LOG_ERROR, "%s: somehow oldData was null.", __FUNCTION__);
-		return;
-	}
-
-	OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
-
-	OBSSource currentSceneSource = main->GetCurrentSceneSource();
-	if (!currentSceneSource)
-		return;
-	std::string scene_uuid = obs_source_get_uuid(currentSceneSource);
-	auto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {
-		OBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());
-		OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, "undo_suuid"));
-		obs_source_reset_settings(source, settings);
-
-		OBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());
-		main->SetCurrentScene(scene_source.Get(), true);
-
-		main->UpdateContextBar();
-	};
-
-	OBSDataAutoRelease new_settings = obs_data_create();
-	OBSDataAutoRelease curr_settings = obs_source_get_settings(source);
-	obs_data_apply(new_settings, curr_settings);
-	obs_data_set_string(new_settings, "undo_suuid", obs_source_get_uuid(source));
-
-	std::string undo_data(obs_data_get_json(oldData));
-	std::string redo_data(obs_data_get_json(new_settings));
-
-	if (undo_data.compare(redo_data) != 0)
-		main->undo_s.add_action(QTStr("Undo.Properties").arg(obs_source_get_name(source)), undo_redo, undo_redo,
-					undo_data, redo_data, repeatable);
-
-	oldData = nullptr;
-}
-
-/* ========================================================================= */
-
-BrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_BrowserSourceToolbar)
-{
-	ui->setupUi(this);
-}
-
-BrowserToolbar::~BrowserToolbar() {}
-
-void BrowserToolbar::on_refresh_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "refreshnocache");
-	obs_property_button_clicked(p, source.Get());
-}
-
-/* ========================================================================= */
-
-ComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-}
-
-ComboSelectToolbar::~ComboSelectToolbar() {}
-
-static int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)
-{
-	size_t count = obs_property_list_item_count(p);
-	int cur_idx = -1;
-
-	for (size_t i = 0; i < count; i++) {
-		const char *name = obs_property_list_item_name(p, i);
-		std::string id;
-
-		if (is_int) {
-			id = std::to_string(obs_property_list_item_int(p, i));
-		} else {
-			const char *val = obs_property_list_item_string(p, i);
-			id = val ? val : "";
-		}
-
-		if (cur_id == id)
-			cur_idx = (int)i;
-
-		c->addItem(name, id.c_str());
-	}
-
-	return cur_idx;
-}
-
-void UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,
-					const char *prop_name, bool is_int)
-{
-	std::string cur_id;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (is_int) {
-		cur_id = std::to_string(obs_data_get_int(settings, prop_name));
-	} else {
-		cur_id = obs_data_get_string(settings, prop_name);
-	}
-
-	combo->blockSignals(true);
-
-	obs_property_t *p = obs_properties_get(props, prop_name);
-	int cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);
-
-	if (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {
-		if (cur_idx == -1) {
-			combo->insertItem(0, QTStr("Basic.Settings.Audio.UnknownAudioDevice"));
-			cur_idx = 0;
-		}
-
-		SetComboItemEnabled(combo, cur_idx, false);
-	}
-
-	combo->setCurrentIndex(cur_idx);
-	combo->blockSignals(false);
-}
-
-void ComboSelectToolbar::Init()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	UpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);
-}
-
-void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)
-{
-	QString id = combo->itemData(idx).toString();
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (is_int) {
-		obs_data_set_int(settings, prop_name, id.toInt());
-	} else {
-		obs_data_set_string(settings, prop_name, QT_TO_UTF8(id));
-	}
-	obs_source_update(source, settings);
-}
-
-void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	SaveOldProperties(source);
-	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);
-	SetUndoProperties(source);
-}
-
-AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void AudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-wasapi", "mac-capture", "linux-pulseaudio");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Device", "CoreAudio.Device", "Device");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "device_id";
-
-	ComboSelectToolbar::Init();
-}
-
 WindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
 
 void WindowCaptureToolbar::Init()
@@ -250,458 +39,3 @@ void WindowCaptureToolbar::Init()
 
 	ComboSelectToolbar::Init();
 }
-
-ApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void ApplicationAudioCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = obs_get_module("win-wasapi");
-	const char *device_str = obs_module_get_locale_text(mod, "Window");
-	ui->deviceLabel->setText(device_str);
-
-	prop_name = "window";
-
-	ComboSelectToolbar::Init();
-}
-
-DisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}
-
-void DisplayCaptureToolbar::Init()
-{
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-
-	obs_module_t *mod = get_os_module("win-capture", "mac-capture", "linux-capture");
-	if (!mod)
-		return;
-
-	const char *device_str = get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "monitor_id";
-#elif __APPLE__
-	prop_name = "display_uuid";
-#else
-	is_int = true;
-	prop_name = "screen";
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
-/* ========================================================================= */
-
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: QWidget(parent),
-	  weakSource(OBSGetWeakRef(source)),
-	  ui(new Ui_DeviceSelectToolbar)
-{
-	ui->setupUi(this);
-
-	delete ui->deviceLabel;
-	delete ui->device;
-	ui->deviceLabel = nullptr;
-	ui->device = nullptr;
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	active = obs_data_get_bool(settings, "active");
-
-	obs_module_t *mod = obs_get_module("win-dshow");
-	if (!mod)
-		return;
-
-	activateText = obs_module_get_locale_text(mod, "Activate");
-	deactivateText = obs_module_get_locale_text(mod, "Deactivate");
-
-	ui->activateButton->setText(active ? deactivateText : activateText);
-}
-
-DeviceCaptureToolbar::~DeviceCaptureToolbar() {}
-
-void DeviceCaptureToolbar::on_activateButton_clicked()
-{
-	OBSSource source = OBSGetStrongRef(weakSource);
-	if (!source) {
-		return;
-	}
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	bool now_active = obs_data_get_bool(settings, "active");
-
-	bool desyncedSetting = now_active != active;
-
-	active = !active;
-
-	const char *text = active ? deactivateText : activateText;
-	ui->activateButton->setText(text);
-
-	if (desyncedSetting) {
-		return;
-	}
-
-	calldata_t cd = {};
-	calldata_set_bool(&cd, "active", active);
-	proc_handler_t *ph = obs_source_get_proc_handler(source);
-	proc_handler_call(ph, "activate", &cd);
-	calldata_free(&cd);
-}
-
-/* ========================================================================= */
-
-GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_GameCaptureToolbar)
-{
-	obs_property_t *p;
-	int cur_idx;
-
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("win-capture");
-	if (!mod)
-		return;
-
-	ui->modeLabel->setText(obs_module_get_locale_text(mod, "Mode"));
-	ui->windowLabel->setText(obs_module_get_locale_text(mod, "WindowCapture.Window"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string cur_mode = obs_data_get_string(settings, "capture_mode");
-	std::string cur_window = obs_data_get_string(settings, "window");
-
-	ui->mode->blockSignals(true);
-	p = obs_properties_get(props.get(), "capture_mode");
-	cur_idx = FillPropertyCombo(ui->mode, p, cur_mode);
-	ui->mode->setCurrentIndex(cur_idx);
-	ui->mode->blockSignals(false);
-
-	ui->window->blockSignals(true);
-	p = obs_properties_get(props.get(), "window");
-	cur_idx = FillPropertyCombo(ui->window, p, cur_window);
-	ui->window->setCurrentIndex(cur_idx);
-	ui->window->blockSignals(false);
-
-	if (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {
-		SetComboItemEnabled(ui->window, cur_idx, false);
-	}
-
-	UpdateWindowVisibility();
-}
-
-GameCaptureToolbar::~GameCaptureToolbar() {}
-
-void GameCaptureToolbar::UpdateWindowVisibility()
-{
-	QString mode = ui->mode->currentData().toString();
-	bool is_window = (mode == "window");
-	ui->windowLabel->setVisible(is_window);
-	ui->window->setVisible(is_window);
-}
-
-void GameCaptureToolbar::on_mode_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->mode->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "capture_mode", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-
-	UpdateWindowVisibility();
-}
-
-void GameCaptureToolbar::on_window_currentIndexChanged(int idx)
-{
-	OBSSource source = GetSource();
-	if (idx == -1 || !source) {
-		return;
-	}
-
-	QString id = ui->window->itemData(idx).toString();
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "window", QT_TO_UTF8(id));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-ImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ImageSourceToolbar)
-{
-	ui->setupUi(this);
-
-	obs_module_t *mod = obs_get_module("image-source");
-	ui->pathLabel->setText(obs_module_get_locale_text(mod, "File"));
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	std::string file = obs_data_get_string(settings, "file");
-
-	ui->path->setText(file.c_str());
-}
-
-ImageSourceToolbar::~ImageSourceToolbar() {}
-
-void ImageSourceToolbar::on_browse_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "file");
-	const char *desc = obs_property_description(p);
-	const char *filter = obs_property_path_filter(p);
-	const char *default_path = obs_property_path_default_path(p);
-
-	QString startDir = ui->path->text();
-	if (startDir.isEmpty())
-		startDir = default_path;
-
-	QString path = OpenFile(this, desc, startDir, filter);
-	if (path.isEmpty()) {
-		return;
-	}
-
-	ui->path->setText(path);
-
-	SaveOldProperties(source);
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_string(settings, "file", QT_TO_UTF8(path));
-	obs_source_update(source, settings);
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-static inline QColor color_from_int(long long val)
-{
-	return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
-}
-
-static inline long long color_to_int(QColor color)
-{
-	auto shift = [&](unsigned val, int shift) {
-		return ((val & 0xff) << shift);
-	};
-
-	return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
-}
-
-ColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_ColorSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	unsigned int val = (unsigned int)obs_data_get_int(settings, "color");
-
-	color = color_from_int(val);
-	UpdateColor();
-}
-
-ColorSourceToolbar::~ColorSourceToolbar() {}
-
-void ColorSourceToolbar::UpdateColor()
-{
-	QPalette palette = QPalette(color);
-	ui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);
-	ui->color->setText(color.name(QColor::HexRgb));
-	ui->color->setPalette(palette);
-	ui->color->setStyleSheet(QString("background-color :%1; color: %2;")
-					 .arg(palette.color(QPalette::Window).name(QColor::HexRgb))
-					 .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));
-	ui->color->setAutoFillBackground(true);
-	ui->color->setAlignment(Qt::AlignCenter);
-}
-
-void ColorSourceToolbar::on_choose_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "color");
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-	UpdateColor();
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	obs_data_set_int(settings, "color", color_to_int(color));
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-/* ========================================================================= */
-
-extern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);
-
-TextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)
-	: SourceToolbar(parent, source),
-	  ui(new Ui_TextSourceToolbar)
-{
-	ui->setupUi(this);
-
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-
-	const char *id = obs_source_get_unversioned_id(source);
-	bool ft2 = strcmp(id, "text_ft2_source") == 0;
-	bool read_from_file = obs_data_get_bool(settings, ft2 ? "from_file" : "read_from_file");
-
-	OBSDataAutoRelease font_obj = obs_data_get_obj(settings, "font");
-	MakeQFont(font_obj, font);
-
-	// Use "color1" if it's a freetype source and "color" elsewise
-	unsigned int val = (unsigned int)obs_data_get_int(
-		settings, (strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0) ? "color1" : "color");
-
-	color = color_from_int(val);
-
-	const char *text = obs_data_get_string(settings, "text");
-
-	bool single_line = !read_from_file && (!text || (strchr(text, '\n') == nullptr));
-	ui->emptySpace->setVisible(!single_line);
-	ui->text->setVisible(single_line);
-	if (single_line)
-		ui->text->setText(text);
-}
-
-TextSourceToolbar::~TextSourceToolbar() {}
-
-void TextSourceToolbar::on_selectFont_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	QFontDialog::FontDialogOptions options;
-	uint32_t flags;
-	bool success;
-
-#ifndef _WIN32
-	options = QFontDialog::DontUseNativeDialog;
-#endif
-
-	font = QFontDialog::getFont(&success, font, this, QTStr("Basic.PropertiesWindow.SelectFont.WindowTitle"),
-				    options);
-	if (!success) {
-		return;
-	}
-
-	OBSDataAutoRelease font_obj = obs_data_create();
-
-	obs_data_set_string(font_obj, "face", QT_TO_UTF8(font.family()));
-	obs_data_set_string(font_obj, "style", QT_TO_UTF8(font.styleName()));
-	obs_data_set_int(font_obj, "size", font.pointSize());
-	flags = font.bold() ? OBS_FONT_BOLD : 0;
-	flags |= font.italic() ? OBS_FONT_ITALIC : 0;
-	flags |= font.underline() ? OBS_FONT_UNDERLINE : 0;
-	flags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;
-	obs_data_set_int(font_obj, "flags", flags);
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-
-	obs_data_set_obj(settings, "font", font_obj);
-
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_selectColor_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	bool freetype = strncmp(obs_source_get_id(source), "text_ft2_source", 15) == 0;
-
-	obs_property_t *p = obs_properties_get(props.get(), freetype ? "color1" : "color");
-
-	const char *desc = obs_property_description(p);
-
-	QColorDialog::ColorDialogOptions options;
-
-	options |= QColorDialog::ShowAlphaChannel;
-#ifdef __linux__
-	// TODO: Revisit hang on Ubuntu with native dialog
-	options |= QColorDialog::DontUseNativeDialog;
-#endif
-
-	QColor newColor = QColorDialog::getColor(color, this, desc, options);
-	if (!newColor.isValid()) {
-		return;
-	}
-
-	color = newColor;
-
-	SaveOldProperties(source);
-
-	OBSDataAutoRelease settings = obs_data_create();
-	if (freetype) {
-		obs_data_set_int(settings, "color1", color_to_int(color));
-		obs_data_set_int(settings, "color2", color_to_int(color));
-	} else {
-		obs_data_set_int(settings, "color", color_to_int(color));
-	}
-	obs_source_update(source, settings);
-
-	SetUndoProperties(source);
-}
-
-void TextSourceToolbar::on_text_textChanged()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-	std::string newText = QT_TO_UTF8(ui->text->text());
-	OBSDataAutoRelease settings = obs_source_get_settings(source);
-	if (newText == obs_data_get_string(settings, "text")) {
-		return;
-	}
-	SaveOldProperties(source);
-
-	obs_data_set_string(settings, "text", newText.c_str());
-	obs_source_update(source, nullptr);
-
-	SetUndoProperties(source, true);
-}

+ 1 - 168
frontend/components/WindowCaptureToolbar.hpp

@@ -1,77 +1,6 @@
 #pragma once
 
-#include <memory>
-#include <obs.hpp>
-#include <QWidget>
-
-class Ui_BrowserSourceToolbar;
-class Ui_DeviceSelectToolbar;
-class Ui_GameCaptureToolbar;
-class Ui_ImageSourceToolbar;
-class Ui_ColorSourceToolbar;
-class Ui_TextSourceToolbar;
-
-class SourceToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-protected:
-	using properties_delete_t = decltype(&obs_properties_destroy);
-	using properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;
-
-	properties_t props;
-	OBSDataAutoRelease oldData;
-
-	void SaveOldProperties(obs_source_t *source);
-	void SetUndoProperties(obs_source_t *source, bool repeatable = false);
-
-public:
-	SourceToolbar(QWidget *parent, OBSSource source);
-
-	OBSSource GetSource() { return OBSGetStrongRef(weakSource); }
-
-public slots:
-	virtual void Update() {}
-};
-
-class BrowserToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_BrowserSourceToolbar> ui;
-
-public:
-	BrowserToolbar(QWidget *parent, OBSSource source);
-	~BrowserToolbar();
-
-public slots:
-	void on_refresh_clicked();
-};
-
-class ComboSelectToolbar : public SourceToolbar {
-	Q_OBJECT
-
-protected:
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *prop_name;
-	bool is_int = false;
-
-public:
-	ComboSelectToolbar(QWidget *parent, OBSSource source);
-	~ComboSelectToolbar();
-	virtual void Init();
-
-public slots:
-	void on_device_currentIndexChanged(int idx);
-};
-
-class AudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	AudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
+#include "ComboSelectToolbar.hpp"
 
 class WindowCaptureToolbar : public ComboSelectToolbar {
 	Q_OBJECT
@@ -80,99 +9,3 @@ public:
 	WindowCaptureToolbar(QWidget *parent, OBSSource source);
 	void Init() override;
 };
-
-class ApplicationAudioCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DisplayCaptureToolbar : public ComboSelectToolbar {
-	Q_OBJECT
-
-public:
-	DisplayCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
-};
-
-class DeviceCaptureToolbar : public QWidget {
-	Q_OBJECT
-
-	OBSWeakSource weakSource;
-
-	std::unique_ptr<Ui_DeviceSelectToolbar> ui;
-	const char *activateText;
-	const char *deactivateText;
-	bool active;
-
-public:
-	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	~DeviceCaptureToolbar();
-
-public slots:
-	void on_activateButton_clicked();
-};
-
-class GameCaptureToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_GameCaptureToolbar> ui;
-
-	void UpdateWindowVisibility();
-
-public:
-	GameCaptureToolbar(QWidget *parent, OBSSource source);
-	~GameCaptureToolbar();
-
-public slots:
-	void on_mode_currentIndexChanged(int idx);
-	void on_window_currentIndexChanged(int idx);
-};
-
-class ImageSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ImageSourceToolbar> ui;
-
-public:
-	ImageSourceToolbar(QWidget *parent, OBSSource source);
-	~ImageSourceToolbar();
-
-public slots:
-	void on_browse_clicked();
-};
-
-class ColorSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_ColorSourceToolbar> ui;
-	QColor color;
-
-	void UpdateColor();
-
-public:
-	ColorSourceToolbar(QWidget *parent, OBSSource source);
-	~ColorSourceToolbar();
-
-public slots:
-	void on_choose_clicked();
-};
-
-class TextSourceToolbar : public SourceToolbar {
-	Q_OBJECT
-
-	std::unique_ptr<Ui_TextSourceToolbar> ui;
-	QFont font;
-	QColor color;
-
-public:
-	TextSourceToolbar(QWidget *parent, OBSSource source);
-	~TextSourceToolbar();
-
-public slots:
-	void on_selectFont_clicked();
-	void on_selectColor_clicked();
-	void on_text_textChanged();
-};