/* Creates a text overlay with the provided string @param _string The text to be included in the overlay @param fontHeight The pixel height of the text @param color The rgba(0-1) color of the font @param fontFile The path to the desired font @param faceIndex The face within the font file to be used (most likely 0) */ Text::Text(const char *_string, unsigned int fontHeight, glm::vec4 color, char const *fontFile, unsigned int faceIndex) : Overlay(std::make_shared<Shaders>(Stock::Shaders::TEXT)) , padding(5) , lineSpacing(-0.1f) , color(color) , backgroundColor(0.0f) , library() , font() , string(0) , fontHeight(fontHeight) , wrapDistance(800) , printMono(false) , tex(std::make_unique<TextureString>()) { getShaders()->addDynamicUniform("_col", glm::value_ptr(this->color), 4); getShaders()->addDynamicUniform("_backCol", glm::value_ptr(this->backgroundColor), 4); if (!fontFile) fontFile = Stock::Font::ARIAL; FT_Error error = FT_Init_FreeType(&library); if (error) { fprintf(stderr, "An unexpected error occured whilst initialising FreeType: %i\n", error); return; } error = FT_New_Face(library, fontFile, faceIndex, &font); if (error == FT_Err_Unknown_File_Format) { fprintf(stderr, "The font file %s is of an unsupport format, defaulting to Arial\n", fontFile); fontFile = Stock::Font::ARIAL; error = FT_New_Face(library, fontFile, 0, &this->font); } if (error) { fprintf(stderr, "An unexpected error occured whilst loading font file %s: %i, defaulting to Arial\n", fontFile, error); fontFile = Stock::Font::ARIAL; error = FT_New_Face(library, fontFile, 0, &this->font); } error = FT_Set_Pixel_Sizes( this->font, /* handle to face object */ 0, /* pixel_width */ this->fontHeight);/* pixel_height */ if (error) { fprintf(stderr, "An unexpected error occured whilst setting font size: %i\n", error); return; } printf("Font %s was loaded successfully.\n", fontFile); setString(_string); }
/* Creates a new 2D Sprite overlay from the given image @param imagePath Path to the image to be used @param width The width of the overlay, if 0 is passed the image width will be used @param height The height of the overlay, if 0 is passed, the image height will be used unless width was specified, in which case the image will be scaled, preserving aspect ratio */ Sprite2D::Sprite2D(char *imagePath, unsigned int _width, unsigned int _height) : Overlay(std::make_shared<Shaders>(Stock::Shaders::SPRITE2D)) , tex(imagePath) { unsigned int width = _width == 0 ? tex.getWidth() : _width; unsigned int height = _height == 0 ? (_width == 0 ? tex.getHeight() : (tex.getHeight()*width / tex.getWidth())) : _height; setDimensions(width, height); tex.bindToShader(getShaders().get()); }
void Panel::updateCamera() { float a = (static_cast<float>(m_camera_angle) / 16.f) * static_cast<float>(M_PI); float dist = m_model_size / (1.f + abs(m_camera_height - 2.f) * 0.1f); Essence::Graphics::vector3_t eye = {cos(a) * dist, m_camera_height, sin(a) * dist}; Essence::Graphics::vector3_t at = {0.f, (m_camera_height - 2.f) * 0.01f, 0.f}; lookAtFrom(at, eye); getShaders()->variablesUpdated(); refresh(); }
GLProgram::GLProgram(void) { ID = glCreateProgram(); if(!ID) { fprintf(stderr, "Error creating shader Program\n"); } getShaders(); }
void ShaderParamsDialog::onScaleComboBoxIndexChanged(int) { QComboBox *comboBox = qobject_cast<QComboBox*>(sender()); QVariant passVariant; int pass = 0; bool ok = false; struct video_shader *menu_shader = NULL; struct video_shader *video_shader = NULL; getShaders(&menu_shader, &video_shader); if (!comboBox) return; passVariant = comboBox->property("pass"); if (!passVariant.isValid()) return; pass = passVariant.toInt(&ok); if (!ok) return; if (menu_shader && pass >= 0 && pass < static_cast<int>(menu_shader->passes)) { QVariant data = comboBox->currentData(); if (data.isValid()) { unsigned scale = data.toUInt(&ok); if (ok) { if (menu_shader) { menu_shader->pass[pass].fbo.scale_x = scale; menu_shader->pass[pass].fbo.scale_y = scale; menu_shader->pass[pass].fbo.valid = scale; } if (video_shader) { video_shader->pass[pass].fbo.scale_x = scale; video_shader->pass[pass].fbo.scale_y = scale; video_shader->pass[pass].fbo.valid = scale; } command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL); } } } }
void ShaderParamsDialog::onShaderLoadPresetClicked() { QString path; QString filter; QByteArray pathArray; struct video_shader *menu_shader = NULL; struct video_shader *video_shader = NULL; const char *pathData = NULL; settings_t *settings = config_get_ptr(); enum rarch_shader_type type = RARCH_SHADER_NONE; bool is_preset = false; if (!settings) return; getShaders(&menu_shader, &video_shader); if (!menu_shader) return; filter = "Shader Preset ("; /* NOTE: Maybe we should have a way to get a list of all shader types instead of hard-coding this? */ if (video_shader_is_supported(RARCH_SHADER_CG) && video_shader_get_type_from_ext(file_path_str(FILE_PATH_CGP_EXTENSION), &is_preset) != RARCH_SHADER_NONE) filter += QLatin1Literal("*") + file_path_str(FILE_PATH_CGP_EXTENSION); if (video_shader_is_supported(RARCH_SHADER_GLSL) && video_shader_get_type_from_ext(file_path_str(FILE_PATH_GLSLP_EXTENSION), &is_preset) != RARCH_SHADER_NONE) filter += QLatin1Literal(" *") + file_path_str(FILE_PATH_GLSLP_EXTENSION); if (video_shader_is_supported(RARCH_SHADER_SLANG) && video_shader_get_type_from_ext(file_path_str(FILE_PATH_SLANGP_EXTENSION), &is_preset) != RARCH_SHADER_NONE) filter += QLatin1Literal(" *") + file_path_str(FILE_PATH_SLANGP_EXTENSION); filter += ")"; path = QFileDialog::getOpenFileName(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET), settings->paths.directory_video_shader, filter); if (path.isEmpty()) return; pathArray = path.toUtf8(); pathData = pathArray.constData(); type = video_shader_parse_type(pathData, RARCH_SHADER_NONE); menu_shader_manager_set_preset(menu_shader, type, pathData); }
void ShaderParamsDialog::onShaderClearAllPassesClicked() { struct video_shader *menu_shader = NULL; struct video_shader *video_shader = NULL; getShaders(&menu_shader, &video_shader); if (!menu_shader) return; while (menu_shader->passes > 0) menu_shader_manager_decrement_amount_passes(); onShaderApplyClicked(); }
void ShaderParamsDialog::onShaderResetParameter(QString parameter) { struct video_shader *menu_shader = NULL; struct video_shader *video_shader = NULL; getShaders(&menu_shader, &video_shader); if (menu_shader) { struct video_shader_parameter *param = NULL; int i; for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++) { QString id = menu_shader->parameters[i].id; if (id == parameter) param = &menu_shader->parameters[i]; } if (param) param->current = param->initial; } if (video_shader) { struct video_shader_parameter *param = NULL; int i; for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++) { QString id = video_shader->parameters[i].id; if (id == parameter) param = &video_shader->parameters[i]; } if (param) param->current = param->initial; } reload(); }
void ShaderParamsDialog::onShaderRemovePassClicked() { int i; QVariant passVariant; QAction *action = qobject_cast<QAction*>(sender()); struct video_shader *menu_shader = NULL; struct video_shader *video_shader = NULL; int pass = 0; bool ok = false; getShaders(&menu_shader, &video_shader); if (!menu_shader || menu_shader->passes == 0 || !action) return; passVariant = action->data(); if (!passVariant.isValid()) return; pass = passVariant.toInt(&ok); if (!ok) return; if (pass < 0 || pass > static_cast<int>(menu_shader->passes)) return; /* move selected pass to the bottom */ for (i = pass; i < static_cast<int>(menu_shader->passes) - 1; i++) { std::swap(menu_shader->pass[i], menu_shader->pass[i + 1]); } menu_shader_manager_decrement_amount_passes(); onShaderApplyClicked(); }
void ShaderParamsDialog::onShaderResetPass(int pass) { struct video_shader *menu_shader = NULL; struct video_shader *video_shader = NULL; unsigned i; getShaders(&menu_shader, &video_shader); if (menu_shader) { for (i = 0; i < menu_shader->num_parameters; i++) { struct video_shader_parameter *param = &menu_shader->parameters[i]; /* if pass < 0, reset all params, otherwise only reset the selected pass */ if (pass >= 0 && param->pass != pass) continue; param->current = param->initial; } } if (video_shader) { for (i = 0; i < video_shader->num_parameters; i++) { struct video_shader_parameter *param = &video_shader->parameters[i]; /* if pass < 0, reset all params, otherwise only reset the selected pass */ if (pass >= 0 && param->pass != pass) continue; param->current = param->initial; } } reload(); }
void Panel::setModel(FileSource* mod_fs, const std::string& path) { setModel(LoadModel(mod_fs, getShaders(), path, getDevice())); }
void ShaderParamsDialog::buildLayout() { QPushButton *loadButton = NULL; QPushButton *saveButton = NULL; QPushButton *removeButton = NULL; QPushButton *applyButton = NULL; QHBoxLayout *topButtonLayout = NULL; QMenu *loadMenu = NULL; QMenu *saveMenu = NULL; QMenu *removeMenu = NULL; struct video_shader *menu_shader = NULL; struct video_shader *video_shader = NULL; struct video_shader *avail_shader = NULL; const char *shader_path = NULL; unsigned i; bool hasPasses = false; getShaders(&menu_shader, &video_shader); /* NOTE: For some reason, menu_shader_get() returns a COPY of what get_current_shader() gives us. * And if you want to be able to change shader settings/parameters from both the raster menu and * Qt at the same time... you must change BOTH or one will overwrite the other. * * AND, during a context reset, video_shader will be NULL but not menu_shader, so don't totally bail * just because video_shader is NULL. * * Someone please fix this mess. */ if (video_shader) { avail_shader = video_shader; if (video_shader->passes == 0) setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS)); } /* Normally we'd only use video_shader, but the vulkan driver returns a NULL shader when there * are zero passes, so just fall back to menu_shader. */ else if (menu_shader) { avail_shader = menu_shader; if (menu_shader->passes == 0) setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS)); } else { setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS)); /* no shader is available yet, just keep retrying until it is */ QTimer::singleShot(0, this, SLOT(buildLayout())); return; } clearLayout(); /* Only check video_shader for the path, menu_shader seems stale... e.g. if you remove all the shader passes, * it still has the old path in it, but video_shader does not */ if (video_shader) { if (!string_is_empty(video_shader->path)) { shader_path = video_shader->path; setWindowTitle(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CURRENT_SHADER)) + ": " + QFileInfo(shader_path).fileName()); } } else if (menu_shader) { if (!string_is_empty(menu_shader->path)) { shader_path = menu_shader->path; setWindowTitle(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CURRENT_SHADER)) + ": " + QFileInfo(shader_path).fileName()); } } else setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS)); loadButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD), this); saveButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SAVE), this); removeButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_REMOVE), this); applyButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_APPLY), this); loadMenu = new QMenu(loadButton); loadMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET), this, SLOT(onShaderLoadPresetClicked())); loadMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SHADER_ADD_PASS), this, SLOT(onShaderAddPassClicked())); loadButton->setMenu(loadMenu); saveMenu = new QMenu(saveButton); saveMenu->addAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS)) + "...", this, SLOT(onShaderSavePresetAsClicked())); saveMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_CORE), this, SLOT(onShaderSaveCorePresetClicked())); saveMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_PARENT), this, SLOT(onShaderSaveParentPresetClicked())); saveMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_GAME), this, SLOT(onShaderSaveGamePresetClicked())); saveButton->setMenu(saveMenu); removeMenu = new QMenu(removeButton); /* When there are no passes, at least on first startup, it seems video_shader erroneously shows 1 pass, with an empty source file. * So we use menu_shader instead for that. */ if (menu_shader) { for (i = 0; i < menu_shader->passes; i++) { QFileInfo fileInfo(menu_shader->pass[i].source.path); QString shaderBasename = fileInfo.completeBaseName(); QAction *action = removeMenu->addAction(shaderBasename, this, SLOT(onShaderRemovePassClicked())); action->setData(i); } } removeMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SHADER_CLEAR_ALL_PASSES), this, SLOT(onShaderClearAllPassesClicked())); removeButton->setMenu(removeMenu); connect(applyButton, SIGNAL(clicked()), this, SLOT(onShaderApplyClicked())); topButtonLayout = new QHBoxLayout(); topButtonLayout->addWidget(loadButton); topButtonLayout->addWidget(saveButton); topButtonLayout->addWidget(removeButton); topButtonLayout->addWidget(applyButton); m_layout->addLayout(topButtonLayout); /* NOTE: We assume that parameters are always grouped in order by the pass number, e.g., all parameters for pass 0 come first, then params for pass 1, etc. */ for (i = 0; avail_shader && i < avail_shader->passes; i++) { QFormLayout *form = NULL; QGroupBox *groupBox = NULL; QFileInfo fileInfo(avail_shader->pass[i].source.path); QString shaderBasename = fileInfo.completeBaseName(); QHBoxLayout *filterScaleHBoxLayout = NULL; QComboBox *filterComboBox = new QComboBox(this); QComboBox *scaleComboBox = new QComboBox(this); QToolButton *moveDownButton = NULL; QToolButton *moveUpButton = NULL; unsigned j = 0; /* Sometimes video_shader shows 1 pass with no source file, when there are really 0 passes. */ if (shaderBasename.isEmpty()) continue; hasPasses = true; filterComboBox->setProperty("pass", i); scaleComboBox->setProperty("pass", i); moveDownButton = new QToolButton(this); moveDownButton->setText("↓"); moveDownButton->setProperty("pass", i); moveUpButton = new QToolButton(this); moveUpButton->setText("↑"); moveUpButton->setProperty("pass", i); /* Can't move down if we're already at the bottom. */ if (i < avail_shader->passes - 1) connect(moveDownButton, SIGNAL(clicked()), this, SLOT(onShaderPassMoveDownClicked())); else moveDownButton->setDisabled(true); /* Can't move up if we're already at the top. */ if (i > 0) connect(moveUpButton, SIGNAL(clicked()), this, SLOT(onShaderPassMoveUpClicked())); else moveUpButton->setDisabled(true); for (;;) { QString filterLabel = getFilterLabel(j); if (filterLabel.isEmpty()) break; if (j == 0) filterLabel = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE); filterComboBox->addItem(filterLabel, j); j++; } for (j = 0; j < 7; j++) { QString label; if (j == 0) label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE); else label = QString::number(j) + "x"; scaleComboBox->addItem(label, j); } filterComboBox->setCurrentIndex(static_cast<int>(avail_shader->pass[i].filter)); scaleComboBox->setCurrentIndex(static_cast<int>(avail_shader->pass[i].fbo.scale_x)); /* connect the signals only after the initial index is set */ connect(filterComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onFilterComboBoxIndexChanged(int))); connect(scaleComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onScaleComboBoxIndexChanged(int))); form = new QFormLayout(); groupBox = new QGroupBox(shaderBasename); groupBox->setLayout(form); groupBox->setProperty("pass", i); groupBox->setContextMenuPolicy(Qt::CustomContextMenu); connect(groupBox, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onGroupBoxContextMenuRequested(const QPoint&))); m_layout->addWidget(groupBox); filterScaleHBoxLayout = new QHBoxLayout(); filterScaleHBoxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); filterScaleHBoxLayout->addWidget(new QLabel(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FILTER)) + ":", this)); filterScaleHBoxLayout->addWidget(filterComboBox); filterScaleHBoxLayout->addWidget(new QLabel(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCALE)) + ":", this)); filterScaleHBoxLayout->addWidget(scaleComboBox); filterScaleHBoxLayout->addSpacerItem(new QSpacerItem(20, 0, QSizePolicy::Preferred, QSizePolicy::Preferred)); if (moveUpButton) filterScaleHBoxLayout->addWidget(moveUpButton); if (moveDownButton) filterScaleHBoxLayout->addWidget(moveDownButton); form->addRow("", filterScaleHBoxLayout); for (j = 0; j < avail_shader->num_parameters; j++) { struct video_shader_parameter *param = &avail_shader->parameters[j]; if (param->pass != static_cast<int>(i)) continue; addShaderParam(param, form); } } if (!hasPasses) { QLabel *noParamsLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SHADER_NO_PASSES), this); noParamsLabel->setAlignment(Qt::AlignCenter); m_layout->addWidget(noParamsLabel); } m_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); /* Why is this required?? The layout is corrupt without both resizes. */ resize(width() + 1, height()); show(); resize(width() - 1, height()); }
void ShaderParamsDialog::onShaderAddPassClicked() { QString path; QString filter; QByteArray pathArray; struct video_shader *menu_shader = NULL; struct video_shader *video_shader = NULL; struct video_shader_pass *shader_pass = NULL; const char *pathData = NULL; settings_t *settings = config_get_ptr(); bool is_preset = false; if (!settings) return; getShaders(&menu_shader, &video_shader); if (!menu_shader) return; filter = "Shader ("; /* NOTE: Maybe we should have a way to get a list of all shader types instead of hard-coding this? */ if (video_shader_is_supported(RARCH_SHADER_CG) && video_shader_get_type_from_ext(".cg", &is_preset) != RARCH_SHADER_NONE) filter += QLatin1Literal("*.cg"); if (video_shader_is_supported(RARCH_SHADER_GLSL) && video_shader_get_type_from_ext(".glsl", &is_preset) != RARCH_SHADER_NONE) filter += QLatin1Literal(" *.glsl"); if (video_shader_is_supported(RARCH_SHADER_SLANG) && video_shader_get_type_from_ext(".slang", &is_preset) != RARCH_SHADER_NONE) filter += QLatin1Literal(" *.slang"); filter += ")"; path = QFileDialog::getOpenFileName(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET), settings->paths.directory_video_shader, filter); if (path.isEmpty()) return; pathArray = path.toUtf8(); pathData = pathArray.constData(); if (menu_shader->passes < GFX_MAX_SHADERS) menu_shader_manager_increment_amount_passes(); else return; shader_pass = &menu_shader->pass[menu_shader->passes - 1]; if (!shader_pass) return; strlcpy(shader_pass->source.path, pathData, sizeof(shader_pass->source.path)); video_shader_resolve_parameters(NULL, menu_shader); command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL); }
void ShaderParamsDialog::onShaderPassMoveUpClicked() { QToolButton *button = qobject_cast<QToolButton*>(sender()); QVariant passVariant; struct video_shader *menu_shader = NULL; struct video_shader *video_shader = NULL; int pass = 0; bool ok = false; getShaders(&menu_shader, &video_shader); if (!button) return; passVariant = button->property("pass"); if (!passVariant.isValid()) return; pass = passVariant.toInt(&ok); if (!ok) return; if (pass <= 0) return; if (video_shader) { ShaderPass tempPass; int i; if (pass > static_cast<int>(video_shader->passes) - 1) return; for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++) { struct video_shader_parameter *param = &video_shader->parameters[i]; if (param->pass == pass) { param->pass -= 1; } else if (param->pass == pass - 1) { param->pass += 1; } } tempPass = ShaderPass(&video_shader->pass[pass - 1]); memcpy(&video_shader->pass[pass - 1], &video_shader->pass[pass], sizeof(struct video_shader_pass)); memcpy(&video_shader->pass[pass], tempPass.pass, sizeof(struct video_shader_pass)); } if (menu_shader) { ShaderPass tempPass; int i; if (pass > static_cast<int>(menu_shader->passes) - 1) return; for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++) { struct video_shader_parameter *param = &menu_shader->parameters[i]; if (param->pass == pass) { param->pass -= 1; } else if (param->pass == pass - 1) { param->pass += 1; } } tempPass = ShaderPass(&menu_shader->pass[pass - 1]); memcpy(&menu_shader->pass[pass - 1], &menu_shader->pass[pass], sizeof(struct video_shader_pass)); memcpy(&menu_shader->pass[pass], tempPass.pass, sizeof(struct video_shader_pass)); } reload(); }
void ShaderParamsDialog::addShaderParam(struct video_shader_parameter *param, QFormLayout *form) { QString desc = param->desc; QString parameter = param->id; QLabel *label = new QLabel(desc); label->setProperty("parameter", parameter); label->setContextMenuPolicy(Qt::CustomContextMenu); connect(label, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onParameterLabelContextMenuRequested(const QPoint&))); if ((param->minimum == 0.0) && (param->maximum == (param->minimum + param->step))) { /* option is basically a bool, so use a checkbox */ QCheckBox *checkBox = new QCheckBox(this); checkBox->setChecked(param->current == param->maximum ? true : false); checkBox->setProperty("param", parameter); connect(checkBox, SIGNAL(clicked()), this, SLOT(onShaderParamCheckBoxClicked())); form->addRow(label, checkBox); } else { QDoubleSpinBox *doubleSpinBox = NULL; QSpinBox *spinBox = NULL; QHBoxLayout *box = new QHBoxLayout(); QSlider *slider = new QSlider(Qt::Horizontal, this); double value = MainWindow::lerp(param->minimum, param->maximum, 0, 100, param->current); double intpart = 0; bool stepIsFractional = modf(param->step, &intpart); slider->setRange(0, 100); slider->setSingleStep(1); slider->setValue(value); slider->setProperty("param", parameter); struct video_shader *video_shader = NULL; getShaders(NULL, &video_shader); connect(slider, SIGNAL(valueChanged(int)), this, SLOT(onShaderParamSliderValueChanged(int))); box->addWidget(slider); if (stepIsFractional) { doubleSpinBox = new QDoubleSpinBox(this); doubleSpinBox->setRange(param->minimum, param->maximum); doubleSpinBox->setSingleStep(param->step); doubleSpinBox->setValue(param->current); doubleSpinBox->setProperty("slider", QVariant::fromValue(slider)); slider->setProperty("doubleSpinBox", QVariant::fromValue(doubleSpinBox)); connect(doubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onShaderParamDoubleSpinBoxValueChanged(double))); box->addWidget(doubleSpinBox); } else { spinBox = new QSpinBox(this); spinBox->setRange(param->minimum, param->maximum); spinBox->setSingleStep(param->step); spinBox->setValue(param->current); spinBox->setProperty("slider", QVariant::fromValue(slider)); slider->setProperty("spinBox", QVariant::fromValue(spinBox)); connect(spinBox, SIGNAL(valueChanged(int)), this, SLOT(onShaderParamSpinBoxValueChanged(int))); box->addWidget(spinBox); } form->addRow(label, box); }