Skip to content
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
109 changes: 109 additions & 0 deletions qml/AttachBoneDialog.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
import PropertiesPanel 1.0
import MaterialEditorQML 1.0

Window {
id: dialog
title: "Attach Bone to Entity"
width: 400
height: 200
minimumWidth: 340
flags: Qt.Dialog
modality: Qt.ApplicationModal
color: PropertiesPanelController.panelColor

property string boneName: ""
property var targetEntities: []

signal attachRequested(string dstEntityName)
signal cancelled()

function openForBone(name, targets) {
dialog.boneName = name || ""
dialog.targetEntities = targets || []
var names = []
for (var i = 0; i < dialog.targetEntities.length; i++)
names.push(dialog.targetEntities[i].name)
targetCombo.model = names
targetCombo.currentIndex = names.length > 0 ? 0 : -1
dialog.show()
dialog.raise()
dialog.requestActivate()
}

ColumnLayout {
anchors.fill: parent
anchors.margins: 16
spacing: 12

Text {
Layout.fillWidth: true
wrapMode: Text.Wrap
font.pixelSize: 12
color: PropertiesPanelController.textColor
text: dialog.boneName.length > 0
? "Copy \"" + dialog.boneName + "\" (and descendants) onto another entity's skeleton (rig only):"
: "Attach the selected bone onto another entity's skeleton:"
}

ThemedComboBox {
id: targetCombo
Layout.fillWidth: true
Layout.preferredHeight: 24
font.pixelSize: 11
displayText: currentIndex >= 0 ? currentText : "(no other entities)"
}

Item { Layout.fillHeight: true }

RowLayout {
Layout.alignment: Qt.AlignRight
spacing: 8
Rectangle {
width: cancelLabel.implicitWidth + 20
height: 28
radius: 3
color: PropertiesPanelController.headerColor
border.color: PropertiesPanelController.borderColor
Text {
id: cancelLabel
anchors.centerIn: parent
text: "Cancel"
color: PropertiesPanelController.textColor
font.pixelSize: 12
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: { dialog.cancelled(); dialog.close() }
}
}
Rectangle {
width: okLabel.implicitWidth + 20
height: 28
radius: 3
opacity: targetCombo.currentIndex >= 0 ? 1.0 : 0.45
color: PropertiesPanelController.highlightColor
Text {
id: okLabel
anchors.centerIn: parent
text: "Attach"
color: "white"
font.pixelSize: 12
}
MouseArea {
anchors.fill: parent
enabled: targetCombo.currentIndex >= 0
cursorShape: Qt.PointingHandCursor
onClicked: {
dialog.attachRequested(targetCombo.currentText)
dialog.close()
}
}
}
}
}
}
166 changes: 166 additions & 0 deletions qml/BoneContextMenu.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Window
import PropertiesPanel 1.0
import AnimationControl 1.0

// Floating inspector-styled context menu for bone hierarchy ops.
// Opened from the Skeleton Tools bone picker or a viewport bone right-click.
Window {
id: menu
flags: Qt.Popup | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint
color: "transparent"
width: menuFrame.implicitWidth
height: menuFrame.implicitHeight

property bool closeOnDeactivate: false
property bool boneConnectedSnapshot: false

signal setParentRequested()
signal detachRequested()
signal splitRequested()
signal connectToggleRequested()
signal attachRequested()
signal duplicateRequested()
signal renameRequested()
signal removeRequested()

Timer {
id: armCloseTimer
interval: 200
onTriggered: menu.closeOnDeactivate = true
}

function openAt(globalX, globalY) {
menu.closeOnDeactivate = false
menu.boneConnectedSnapshot = SkeletonEditor.isSelectedBoneConnected()
menu.x = globalX
menu.y = globalY
menu.show()
menu.raise()
menu.requestActivate()
armCloseTimer.restart()
}

function closeMenu() {
armCloseTimer.stop()
menu.closeOnDeactivate = false
menu.close()
}

readonly property bool hasBone: AnimationControlController.selectedBone.length > 0

Rectangle {
id: menuFrame
implicitWidth: Math.max(180, menuCol.implicitWidth + 8)
implicitHeight: menuCol.implicitHeight + 8
color: PropertiesPanelController.inputColor
border.color: PropertiesPanelController.borderColor
border.width: 1
radius: 4

Column {
id: menuCol
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 4
spacing: 1

component MenuRow: Rectangle {
id: row
property string label: ""
property bool enabled: true
property bool danger: false
signal activated()

width: parent.width
height: 24
radius: 3
opacity: row.enabled ? 1.0 : 0.4
color: rowMa.containsMouse && row.enabled
? PropertiesPanelController.highlightColor
: "transparent"

Text {
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
text: row.label
color: rowMa.containsMouse && row.enabled
? "white"
: (row.danger ? "#e07070" : PropertiesPanelController.textColor)
font.pixelSize: 11
}
MouseArea {
id: rowMa
anchors.fill: parent
hoverEnabled: true
enabled: row.enabled
cursorShape: row.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
row.activated()
menu.closeMenu()
}
}
}

MenuRow {
label: "Set parent…"
enabled: menu.hasBone
onActivated: menu.setParentRequested()
}
MenuRow {
label: "Detach"
enabled: menu.hasBone
onActivated: menu.detachRequested()
}
MenuRow {
label: "Split…"
enabled: menu.hasBone
onActivated: menu.splitRequested()
}
MenuRow {
label: menu.boneConnectedSnapshot ? "Disconnect" : "Connect"
enabled: menu.hasBone
onActivated: menu.connectToggleRequested()
}
MenuRow {
label: "Attach to entity…"
enabled: menu.hasBone
onActivated: menu.attachRequested()
}

Rectangle {
width: parent.width
height: 1
color: PropertiesPanelController.borderColor
opacity: 0.6
}

MenuRow {
label: "Duplicate"
enabled: menu.hasBone
onActivated: menu.duplicateRequested()
}
MenuRow {
label: "Rename…"
enabled: menu.hasBone
onActivated: menu.renameRequested()
}
MenuRow {
label: "Remove…"
enabled: menu.hasBone
danger: true
onActivated: menu.removeRequested()
}
}
}

// Click outside closes — armed after a short delay so the viewport
// keeping focus on open doesn't dismiss the menu immediately.
onActiveChanged: {
if (!active && visible && menu.closeOnDeactivate)
Qt.callLater(menu.closeMenu)
}
}
Loading
Loading