obs-studio/UI/context-bar-controls.cpp

736 lines
19 KiB
C++

#include "window-basic-main.hpp"
#include "context-bar-controls.hpp"
#include "qt-wrappers.hpp"
#include "obs-app.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 "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_sname", obs_source_get_name(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_name = obs_source_get_name(currentSceneSource);
auto undo_redo = [scene_name,
main = std::move(main)](const std::string &data) {
OBSDataAutoRelease settings =
obs_data_create_from_json(data.c_str());
OBSSourceAutoRelease source = obs_get_source_by_name(
obs_data_get_string(settings, "undo_sname"));
obs_source_reset_settings(source, settings);
OBSSourceAutoRelease scene_source =
obs_get_source_by_name(scene_name.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_sname",
obs_source_get_name(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);
is_int = true;
#ifdef _WIN32
prop_name = "monitor";
#elif __APPLE__
prop_name = "display";
#else
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 path = OpenFile(this, desc, default_path, 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;
#ifndef _WIN32
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, "Pick a Font",
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;
#ifndef _WIN32
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;
}
SaveOldProperties(source);
OBSDataAutoRelease settings = obs_data_create();
obs_data_set_string(settings, "text", QT_TO_UTF8(ui->text->text()));
obs_source_update(source, settings);
SetUndoProperties(source, true);
}