// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 pragma ComponentBehavior: Bound import QtQuick import QtQuick.Window import QtQuick.Controls import QtQuick.Layouts import QtQuick.Dialogs import QtQuick3D.MaterialEditor Pane { id: uniformManagerPane property alias uniformModel: uniformModel required property MaterialAdapter materialAdapter SplitView { anchors.fill: parent ColumnLayout { clip: true RowLayout { id: tableControls function insertUniform() { let rowCount = uniformManagerPane.materialAdapter.uniformModel.rowCount; if (uniformManagerPane.materialAdapter.uniformModel.insertRow(rowCount, typeComboBox.currentIndex, uniformNameTextInput.text)) uniformNameTextInput.text = "" } Label { text: "Type:" } ComboBox { id: typeComboBox textRole: "text" valueRole: "value" model: [ { value: UniformModel.Bool, text: "bool" }, { value: UniformModel.Int, text: "int" }, { value: UniformModel.Float, text: "float" }, { value: UniformModel.Vec2, text: "vec2" }, { value: UniformModel.Vec3, text: "vec3" }, { value: UniformModel.Vec4, text: "vec4" }, { value: UniformModel.Mat44, text: "mat44" }, { value: UniformModel.Sampler, text: "sampler" } ] } TextField { id: uniformNameTextInput validator: RegularExpressionValidator { regularExpression: /[a-zA-Z_][a-zA-Z0-9_]+/ } Layout.fillWidth: true placeholderText: "Uniform Name" onAccepted: tableControls.insertUniform() } Button { id: addButton text: "Add" enabled: uniformNameTextInput.text != "" onClicked: tableControls.insertUniform() } } // Column Header Row { id: columnsHeader Layout.fillWidth: true Label { width: uniformTable.columnWidth(0) text: "Type" verticalAlignment: Text.AlignVCenter } Label { width: uniformTable.columnWidth(1) text: "Name" verticalAlignment: Text.AlignVCenter } Label { width: uniformTable.columnWidth(2) text: "Value" verticalAlignment: Text.AlignVCenter } } ListView { id: uniformTable Layout.fillHeight: true Layout.fillWidth: true flickableDirection: Flickable.VerticalFlick model: UniformModel { id: uniformModel } clip: true ScrollBar.vertical: ScrollBar { } highlight: Rectangle { color: palette.highlight } property var typeStrings: [ "bool", "int", "float", "vec2", "vec3", "vec4", "mat44", "sampler" ] function convertValueToString(value, type) { if (type === 0) { // bool return String(value); } if (type === 1) { // int return String(value); } if (type === 2) { // float return String(value); } if (type === 3) { // vec2 return "(" + value.x + ", " + value.y + ")" } if (type === 4) { // vec3 return "(" + value.x + ", " + value.y + ", " + value.z + ")" } if (type === 5) { // vec4 return "(" + value.x + ", " + value.y + ", " + value.z + ", " + value.w + ")" } if (type === 6) { // mat44 return value.toString() } if (type === 7) { // sampler return "[Texture]" } } function columnWidth(column) { if (column === 0) return 50; if (column === 1) return 100; return 100; } delegate: Item { id: delegateRoot required property int type required property string name required property var value required property int index width: ListView.view.width height: typeLabel.implicitHeight Row { Label { id: typeLabel width: uniformTable.columnWidth(0) text: uniformTable.typeStrings[delegateRoot.type] } Label { width: uniformTable.columnWidth(1) text: delegateRoot.name } Label { width: uniformTable.columnWidth(2) Layout.fillWidth: true text: uniformTable.convertValueToString(delegateRoot.value, delegateRoot.type) } } MouseArea { anchors.fill: parent onClicked: { uniformTable.currentIndex = delegateRoot.index } } } } } Item { id: uniformValueEditor width: parent.width * 0.5 clip: true Label { id: emptyLabel visible: uniformTable.currentIndex == -1 anchors.centerIn: parent text: "Select a uniform to edit" } Repeater { id: delegateRepeater anchors.fill: parent model: uniformModel Item { id: editorRoot required property int index required property int type required property string name required property var model anchors.fill: parent anchors.margins: 10 visible: index === uniformTable.currentIndex Item { id: header width: parent.width anchors.top: parent.top height: removeButton.implicitHeight RowLayout { anchors.fill: parent id: headerLayout Label { text: "Uniform: " + editorRoot.name Layout.fillWidth: true elide: Text.ElideRight } Button { id: removeButton text: "Remove" Layout.alignment: Qt.AlignRight onClicked: { uniformManagerPane.materialAdapter.uniformModel.removeRow(uniformTable.currentIndex, 1) } } } } Loader { id: editorLoader anchors.top: header.bottom anchors.right: parent.right anchors.left: parent.left anchors.bottom: parent.bottom sourceComponent: editors[editorRoot.type] readonly property list editors: [ boolEditor, intEditor, floatEditor, vec2Editor, vec3Editor, vec4Editor, mat44Editor, samplerEditor ] Component { id: boolEditor CheckBox { text: "value" checked: editorRoot.model.value onCheckedChanged: editorRoot.model.value = checked } } Component { id: intEditor TextField { text: editorRoot.model.value validator: IntValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = parseInt(text) } } } Component { id: floatEditor ColumnLayout { TextField { Layout.fillWidth: true text: editorRoot.model.value validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) { var floatValue = parseFloat(text); floatSlider.updateMinMax(floatValue); editorRoot.value = floatValue; } } } Slider { id: floatSlider // Grow slider min & max based on given values function updateMinMax(newValue) { if (from > newValue) from = newValue; if (to < newValue) to = newValue; value = newValue; } from: 0.0 to: 1.0 onValueChanged: { editorRoot.model.value = value; } Component.onCompleted: { updateMinMax(editorRoot.model.value); } } } } Component { id: vec2Editor ColumnLayout { RowLayout { Label { text: "X:" } TextField { id: xField text: editorRoot.model.value.x validator: DoubleValidator { locale: "C" } onEditingFinished: { if (acceptableInput) editorRoot.model.value = Qt.vector2d(parseFloat(text), editorRoot.model.value.y) } } } RowLayout { Label { text: "Y:" } TextField { id: yField text: editorRoot.model.value.y validator: DoubleValidator { locale: "C" } onEditingFinished: { if (acceptableInput) editorRoot.model.value = Qt.vector2d(editorRoot.model.value.x, parseFloat(text)) } } } } } Component { id: vec3Editor ColumnLayout { RowLayout { Label { text: "X:" } TextField { id: xField text: editorRoot.model.value.x validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.vector3d(parseFloat(text), editorRoot.model.value.y, editorRoot.model.value.z) } } } RowLayout { Label { text: "Y:" } TextField { id: yField text: editorRoot.model.value.y validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.vector3d(editorRoot.model.value.x, parseFloat(text), editorRoot.model.value.z) } } } RowLayout { Label { text: "Z:" } TextField { id: zField text: editorRoot.model.value.z validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.vector3d(editorRoot.model.value.x, editorRoot.model.value.y, parseFloat(text)) } } } } } Component { id: vec4Editor ColumnLayout { RowLayout { Label { text: "X:" } TextField { id: xField text: editorRoot.model.value.x validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.vector4d(parseFloat(text), editorRoot.model.value.y, editorRoot.model.value.z, editorRoot.model.value.w) } } } RowLayout { Label { text: "Y:" } TextField { id: yField text: editorRoot.model.value.y validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.vector4d(editorRoot.model.value.x, parseFloat(text), editorRoot.model.value.z, editorRoot.model.value.w) } } } RowLayout { Label { text: "Z:" } TextField { id: zField text: editorRoot.model.value.z validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.vector4d(editorRoot.model.value.x, editorRoot.model.value.y, parseFloat(text), editorRoot.model.value.w) } } } RowLayout { Label { text: "W:" } TextField { id: wField text: editorRoot.model.value.w validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.vector4d(editorRoot.model.value.x, editorRoot.model.value.y, editorRoot.model.value.z, parseFloat(text)) } } } } } Component { id: mat44Editor ColumnLayout { RowLayout { TextField { text: editorRoot.model.value.m11 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(parseFloat(text), editorRoot.model.value.m12, editorRoot.model.value.m13 , editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m12 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, parseFloat(text), editorRoot.model.value.m13 , editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m13 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, parseFloat(text), editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m14 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, parseFloat(text), editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } } RowLayout { TextField { text: editorRoot.model.value.m21 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, parseFloat(text), editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m22 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, editorRoot.model.value.m21, parseFloat(text), editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m23 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, parseFloat(text), editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m24 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, parseFloat(text), editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } } RowLayout { TextField { text: editorRoot.model.value.m31 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, parseFloat(text), editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m32 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, parseFloat(text), editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m33 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, parseFloat(text), editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m34 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, parseFloat(text), editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } } RowLayout { TextField { text: editorRoot.model.value.m41 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, parseFloat(text), editorRoot.model.value.m42, editorRoot.model.value.m43, editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m42 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, parseFloat(text), editorRoot.model.value.m43, editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m43 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, parseFloat(text), editorRoot.model.value.m44) } } TextField { text: editorRoot.model.value.m44 validator: DoubleValidator { locale: "C" } onEditingFinished:{ if (acceptableInput) editorRoot.model.value = Qt.matrix4x4(editorRoot.model.value.m11, editorRoot.model.value.m12, editorRoot.model.value.m13, editorRoot.model.value.m14, editorRoot.model.value.m21, editorRoot.model.value.m22, editorRoot.model.value.m23, editorRoot.model.value.m24, editorRoot.model.value.m31, editorRoot.model.value.m32, editorRoot.model.value.m33, editorRoot.model.value.m34, editorRoot.model.value.m41, editorRoot.model.value.m42, editorRoot.model.value.m43, parseFloat(text)) } } } } } Component { id: samplerEditor ColumnLayout { Image { id: previewImage sourceSize.width: 128 sourceSize.height: 128 fillMode: Image.PreserveAspectFit } Button { text: "Choose Image" onClicked: { textureSourceDialog.open() } } FileDialog { id: textureSourceDialog title: "Open an Image File" nameFilters: [ uniformManagerPane.materialAdapter.getSupportedImageFormatsFilter()] onAccepted: { if (textureSourceDialog.selectedFile !== null) { editorRoot.model.value = textureSourceDialog.selectedFile previewImage.source = textureSourceDialog.selectedFile } } } } } } } } } } }