Skip to content
Merged
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
205 changes: 203 additions & 2 deletions qml/PropertiesPanel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -4363,7 +4363,45 @@ Rectangle {
font.bold: true
anchors.verticalCenter: parent.verticalCenter
}
Item { width: parent.width - 260; height: 1 }
Item { width: parent.width - 320; height: 1 }
// Add from current edit — captures the user's current
// edit-mode geometry minus the bind-pose baseline as
// a new morph target. Disabled (greyed out, forbidden
// cursor) when outside edit mode because
// EditableSubMesh::originalPositions is only
// populated by EditModeController and the C++ method
// would return false anyway.
Rectangle {
id: addBtn
property bool canAddFromEdit: EditModeController.editModeActive
width: 56; height: 20; radius: 3
opacity: canAddFromEdit ? 1.0 : 0.45
color: addMa.containsMouse && canAddFromEdit
? Qt.lighter(PropertiesPanelController.headerColor, 1.3)
: PropertiesPanelController.controlBgColor
border.color: PropertiesPanelController.borderColor
anchors.verticalCenter: parent.verticalCenter
Text {
anchors.centerIn: parent
text: "+ Add…"
color: PropertiesPanelController.textColor
font.pixelSize: 9
}
MouseArea {
id: addMa
anchors.fill: parent
hoverEnabled: true
enabled: addBtn.canAddFromEdit
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ForbiddenCursor
onClicked: {
addNameField.text = ""
addError.text = ""
addNamePopup.open()
}
ToolTip.visible: containsMouse && !enabled
ToolTip.text: "Enter Edit Mode (Tab) to add morph targets from current edit."
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
// Reset all: walks every target and sets weight to 0.
Rectangle {
width: 60; height: 20; radius: 3
Expand Down Expand Up @@ -4391,6 +4429,100 @@ Rectangle {
}
}

// Inline name-entry popup for "Add from edit…". Kept
// simple (no styled component) so a misbehaving custom
// dialog can't break the rest of the panel — Popup is
// a built-in Qt Quick Controls primitive with no
// singleton dependencies.
Popup {
id: addNamePopup
modal: true
focus: true
width: 240
contentItem: Column {
spacing: 6
Text {
text: "New morph target name:"
color: PropertiesPanelController.textColor
font.pixelSize: 11
}
TextField {
id: addNameField
width: 220
font.pixelSize: 11
onAccepted: addConfirmMa.confirm()
onTextChanged: addError.text = ""
Component.onCompleted: forceActiveFocus()
}
// Inline error: shown when the C++ side rejects
// the request (duplicate name, no vertex moved,
// not in edit mode, …). We deliberately keep the
// popup open so the user can fix the input
// without retyping.
Text {
id: addError
text: ""
visible: text.length > 0
color: "#d65d5d"
font.pixelSize: 10
width: 220
wrapMode: Text.Wrap
}
Row {
spacing: 6
Rectangle {
width: 60; height: 20; radius: 3
color: addConfirmMa.containsMouse
? Qt.lighter(PropertiesPanelController.headerColor, 1.3)
: PropertiesPanelController.controlBgColor
border.color: PropertiesPanelController.borderColor
Text { anchors.centerIn: parent; text: "Save"; color: PropertiesPanelController.textColor; font.pixelSize: 10 }
MouseArea {
id: addConfirmMa
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
function confirm() {
var n = addNameField.text.trim()
if (n.length === 0) {
addError.text = "Name cannot be empty."
return
}
if (!EditModeController.editModeActive) {
addError.text = "Enter Edit Mode (Tab) before saving."
return
}
var ok = MorphAnimationManager.addMorphTargetFromCurrentEdit(n)
if (ok) {
addNamePopup.close()
} else {
// C++ rejected — likely name collision or
// no vertex moved vs the bind baseline.
addError.text = "Couldn't save: name already in use, or no vertex was edited."
}
}
onClicked: confirm()
}
}
Rectangle {
width: 60; height: 20; radius: 3
color: addCancelMa.containsMouse
? Qt.lighter(PropertiesPanelController.headerColor, 1.3)
: PropertiesPanelController.controlBgColor
border.color: PropertiesPanelController.borderColor
Text { anchors.centerIn: parent; text: "Cancel"; color: PropertiesPanelController.textColor; font.pixelSize: 10 }
MouseArea {
id: addCancelMa
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: addNamePopup.close()
}
}
}
}
}

// Filter / search — characters often have 50+ blend
// shapes, scanning a flat list is hopeless without
// a typeahead box.
Expand All @@ -4413,18 +4545,63 @@ Rectangle {
|| modelData.toLowerCase().indexOf(morphCol.filter.toLowerCase()) >= 0
height: visible ? 22 : 0

// Name — double-click to rename in place,
// matching the per-animation rename UX above.
Text {
id: morphNameText
visible: !morphNameEdit.visible
text: modelData
color: PropertiesPanelController.textColor
font.pixelSize: 10
width: 120
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
onDoubleClicked: {
morphNameEdit.text = modelData
morphNameEdit.visible = true
morphNameEdit.forceActiveFocus()
morphNameEdit.selectAll()
}
}
}
TextInput {
id: morphNameEdit
visible: false
width: 120
color: PropertiesPanelController.textColor
font.pixelSize: 10
anchors.verticalCenter: parent.verticalCenter
selectByMouse: true
// Set by `Keys.onEscapePressed`; checked in
// `onEditingFinished` so that hiding the
// input on Escape (which causes focus loss
// and fires `editingFinished`) doesn't
// accidentally commit the rename.
property bool cancelled: false
Rectangle {
anchors.fill: parent
anchors.margins: -2
z: -1
color: PropertiesPanelController.inputColor
border.color: PropertiesPanelController.highlightColor
border.width: 1
radius: 2
}
onEditingFinished: {
if (cancelled) { cancelled = false; visible = false; return }
var trimmed = text.trim()
if (trimmed.length > 0 && trimmed !== modelData)
MorphAnimationManager.renameMorphTarget(modelData, trimmed)
visible = false
}
Keys.onEscapePressed: { cancelled = true; visible = false }
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Slider {
id: weightSlider
from: 0; to: 1; stepSize: 0.01
width: parent.width - 200
width: parent.width - 222
// Bind to `weightTick` so changes that
// bypass user drag (Reset all, MCP, future
// dope-sheet scrubs) refresh the readout.
Expand All @@ -4440,6 +4617,30 @@ Rectangle {
width: 36
anchors.verticalCenter: parent.verticalCenter
}
// Delete (×) — drops the pose + animation
// through DeleteMorphTargetCommand so Ctrl+Z
// restores it.
Rectangle {
width: 18; height: 18; radius: 3
anchors.verticalCenter: parent.verticalCenter
color: morphDelMa.containsMouse
? Qt.lighter(PropertiesPanelController.headerColor, 1.3)
: "transparent"
Text {
anchors.centerIn: parent
text: "×"
color: PropertiesPanelController.textColor
font.pixelSize: 12
font.bold: true
}
MouseArea {
id: morphDelMa
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: MorphAnimationManager.deleteMorphTarget(modelData)
}
}
}
}
}
Expand Down
Loading