Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor URL source callbacks and add mapping data #91

Merged
merged 3 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,16 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE vendor/nlohmann-json)
target_sources(
${CMAKE_PROJECT_NAME}
PRIVATE src/plugin-main.c
src/url-source.cpp
src/ui/RequestBuilder.cpp
src/obs-source-util.cpp
src/mapping-data.cpp
src/request-data.cpp
src/ui/RequestBuilder.cpp
src/ui/text-render-helper.cpp
src/url-source-info.c
src/obs-source-util.cpp
src/ui/outputmapping.cpp
src/url-source-callbacks.cpp
src/url-source-thread.cpp)
src/url-source-info.c
src/url-source-thread.cpp
src/url-source.cpp)
add_subdirectory(src/parsers)

set_target_properties_plugin(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${_name})
34 changes: 34 additions & 0 deletions src/mapping-data.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

#include "mapping-data.h"
#include <nlohmann/json.hpp>

std::string serialize_output_mapping_data(const output_mapping_data &data)
{
nlohmann::json j;
for (const auto &mapping : data.mappings) {
nlohmann::json j_mapping;
j_mapping["name"] = mapping.name;
j_mapping["output_source"] = mapping.output_source;
j_mapping["template_string"] = mapping.template_string;
j_mapping["css_props"] = mapping.css_props;
j_mapping["unhide_output_source"] = mapping.unhide_output_source;
j.push_back(j_mapping);
}
return j.dump();
}

output_mapping_data deserialize_output_mapping_data(const std::string &data)
{
output_mapping_data result;
nlohmann::json j = nlohmann::json::parse(data);
for (const auto &j_mapping : j) {
output_mapping mapping;
mapping.name = j_mapping.value("name", "");
mapping.output_source = j_mapping.value("output_source", "");
mapping.template_string = j_mapping.value("template_string", "");
mapping.css_props = j_mapping.value("css_props", "");
mapping.unhide_output_source = j_mapping.value("unhide_output_source", false);
result.mappings.push_back(mapping);
}
return result;
}
24 changes: 24 additions & 0 deletions src/mapping-data.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef MAPPING_DATA_H
#define MAPPING_DATA_H

#include <string>
#include <vector>

const std::string none_internal_rendering = "None / Internal rendering";

struct output_mapping {
std::string name;
std::string output_source;
std::string template_string;
std::string css_props;
bool unhide_output_source = false;
};

struct output_mapping_data {
std::vector<output_mapping> mappings;
};

std::string serialize_output_mapping_data(const output_mapping_data &data);
output_mapping_data deserialize_output_mapping_data(const std::string &data);

#endif // MAPPING_DATA_H
204 changes: 204 additions & 0 deletions src/ui/outputmapping.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#include "outputmapping.h"
#include "ui_outputmapping.h"

#include <QComboBox>
#include <QHeaderView>
#include <QStandardItem>

#include <obs.h>
#include <obs-module.h>

namespace {
// add_sources_to_list is a helper function that adds all text and media sources to the list
bool add_sources_to_list(void *list_property, obs_source_t *source)
{
// add all text and media sources to the list
auto source_id = obs_source_get_id(source);
if (strcmp(source_id, "text_ft2_source_v2") != 0 &&
strcmp(source_id, "text_gdiplus_v2") != 0 && strcmp(source_id, "ffmpeg_source") != 0 &&
strcmp(source_id, "image_source") != 0) {
return true;
}

QComboBox *sources = static_cast<QComboBox *>(list_property);
const char *name = obs_source_get_name(source);
std::string name_with_prefix;
// add a prefix to the name to indicate the source type
if (strcmp(source_id, "text_ft2_source_v2") == 0 ||
strcmp(source_id, "text_gdiplus_v2") == 0) {
name_with_prefix = std::string("(Text) ").append(name);
} else if (strcmp(source_id, "image_source") == 0) {
name_with_prefix = std::string("(Image) ").append(name);
} else if (strcmp(source_id, "ffmpeg_source") == 0) {
name_with_prefix = std::string("(Media) ").append(name);
}
sources->addItem(name_with_prefix.c_str());
return true;
}

const std::string default_css_props = R"(background-color: transparent;
color: #FFFFFF;
font-size: 48px;
)";
const std::string default_template_string = R"({{output}})";
} // namespace

OutputMapping::OutputMapping(output_mapping_data *mapping_data_in,
std::function<void()> update_handler_in, QWidget *parent)
: QDialog(parent),
ui(new Ui::OutputMapping),
mapping_data(mapping_data_in),
update_handler(update_handler_in)
{
ui->setupUi(this);

model.setHorizontalHeaderLabels(QStringList() << "Mapping Name"
<< "Output");
ui->tableView->setModel(&model);
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

// if an item is selected in the tableView, enable the remove button
connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
[this](const QItemSelection &selected) {
const bool enable = !selected.indexes().isEmpty();
ui->toolButton_removeMapping->setEnabled(enable);
ui->plainTextEdit_template->setEnabled(enable);
ui->plainTextEdit_cssProps->setEnabled(enable);

if (enable) {
// get the selected row
const auto row = selected.indexes().first().row();
// get the data and output_source of the selected row
const auto data = model.item(row, 0)->text();
const auto output_source = model.item(row, 1)->text();
ui->plainTextEdit_template->blockSignals(true);
ui->plainTextEdit_cssProps->blockSignals(true);
ui->checkBox_unhide_Source->blockSignals(true);
// set the plainTextEdit_template and plainTextEdit_cssProps to the template_string and css_props of the selected row
ui->plainTextEdit_template->setPlainText(
this->mapping_data->mappings[row].template_string.c_str());
ui->plainTextEdit_cssProps->setPlainText(
this->mapping_data->mappings[row].css_props.c_str());
ui->checkBox_unhide_Source->setChecked(
this->mapping_data->mappings[row].unhide_output_source);
ui->plainTextEdit_template->blockSignals(false);
ui->plainTextEdit_cssProps->blockSignals(false);
ui->checkBox_unhide_Source->blockSignals(false);
}
});

// connect toolButton_addMapping to addMapping
connect(ui->toolButton_addMapping, &QToolButton::clicked, this, &OutputMapping::addMapping);
// connect toolButton_removeMapping to removeMapping
connect(ui->toolButton_removeMapping, &QToolButton::clicked, this,
&OutputMapping::removeMapping);

// connect plainTextEdit_template textChanged to update the mapping template_string
connect(ui->plainTextEdit_template, &QPlainTextEdit::textChanged, [this]() {
// get the selected row
const auto row = ui->tableView->currentIndex().row();
// set the template_string of the selected row to the plainTextEdit_template text
this->mapping_data->mappings[row].template_string =
ui->plainTextEdit_template->toPlainText().toStdString();
// call update_handler
this->update_handler();
});
connect(ui->plainTextEdit_cssProps, &QPlainTextEdit::textChanged, [this]() {
// get the selected row
const auto row = ui->tableView->currentIndex().row();
// set the css_props of the selected row to the plainTextEdit_cssProps text
this->mapping_data->mappings[row].css_props =
ui->plainTextEdit_cssProps->toPlainText().toStdString();
// call update_handler
this->update_handler();
});

// populate the model with the mapping data
for (const auto &mapping : mapping_data->mappings) {
model.appendRow(QList<QStandardItem *>() << new QStandardItem(mapping.name.c_str())
<< new QStandardItem(""));
QComboBox *comboBox = createSourcesComboBox();
if (!mapping.output_source.empty()) {
// select the output_source of the mapping in the comboBox
comboBox->blockSignals(true);
comboBox->setCurrentText(mapping.output_source.c_str());
comboBox->blockSignals(false);
}
// add a row to the model with the data and output_source of the mapping
// set comboBox as the index widget of the last item in the model
ui->tableView->setIndexWidget(model.index(model.rowCount() - 1, 1), comboBox);
}

// connect item edit on tableView
connect(&model, &QStandardItemModel::itemChanged, [this](QStandardItem *item) {
// update mapping name
if (item->column() == 0) {
this->mapping_data->mappings[item->row()].name = item->text().toStdString();
}
});
}

OutputMapping::~OutputMapping()
{
delete ui;
}

/// @brief a helper function that creates a comboBox and adds all text and media sources to the list.
/// The sources are added with a prefix to indicate the source type (Text, Image, or Media).
/// @return a pointer to the comboBox
QComboBox *OutputMapping::createSourcesComboBox()
{
QComboBox *comboBox = new QComboBox(this);
// add "Internal Renderer" to the comboBox
comboBox->addItem(QString::fromStdString(none_internal_rendering));
// add all text and media sources to the comboBox
obs_enum_sources(add_sources_to_list, comboBox);
// connect comboBox to update_handler
connect(comboBox, &QComboBox::currentIndexChanged, [this, comboBox]() {
// get the selected row
const auto row = ui->tableView->currentIndex().row();
// get the output_name of the selected item in the comboBox
const auto output_name = comboBox->currentText().toStdString();
// remove the prefix from the output_name if it exists
std::string output_name_without_prefix = output_name;
if (output_name.find("(Text) ") == 0) {
output_name_without_prefix = output_name.substr(7);
} else if (output_name.find("(Image) ") == 0) {
output_name_without_prefix = output_name.substr(8);
} else if (output_name.find("(Media) ") == 0) {
output_name_without_prefix = output_name.substr(8);
}
// set the css_props of the selected row to the plainTextEdit_cssProps text
this->mapping_data->mappings[row].output_source = output_name_without_prefix;
// call update_handler
this->update_handler();
});

return comboBox;
}

void OutputMapping::addMapping()
{
// add row to model
model.appendRow(QList<QStandardItem *>()
<< new QStandardItem("Mapping") << new QStandardItem("Output"));
QComboBox *comboBox = createSourcesComboBox();
// set comboBox as the index widget of the last item in the model
ui->tableView->setIndexWidget(model.index(model.rowCount() - 1, 1), comboBox);
// add a new mapping to the mapping_data
this->mapping_data->mappings.push_back(output_mapping{
"Mapping", none_internal_rendering, default_template_string, default_css_props});
// call update_handler
this->update_handler();
}

void OutputMapping::removeMapping()
{
// remove the mapping from the mapping_data
this->mapping_data->mappings.erase(this->mapping_data->mappings.begin() +
ui->tableView->currentIndex().row());
// remove row from model
model.removeRow(ui->tableView->currentIndex().row());
// call update_handler
this->update_handler();
}
38 changes: 38 additions & 0 deletions src/ui/outputmapping.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#ifndef OUTPUTMAPPING_H
#define OUTPUTMAPPING_H

#include <QDialog>
#include <QStandardItemModel>
#include <QComboBox>
#include <functional>

#include "mapping-data.h"

namespace Ui {
class OutputMapping;
}

class OutputMapping : public QDialog {
Q_OBJECT

public:
explicit OutputMapping(output_mapping_data *mapping_data_in,
std::function<void()> update_handler, QWidget *parent = nullptr);
~OutputMapping();

OutputMapping(const OutputMapping &) = delete;
OutputMapping &operator=(const OutputMapping &) = delete;

private:
Ui::OutputMapping *ui;
QStandardItemModel model;
output_mapping_data *mapping_data;
QComboBox *createSourcesComboBox();
std::function<void()> update_handler;

private slots:
void addMapping();
void removeMapping();
};

#endif // OUTPUTMAPPING_H
Loading
Loading