Unified · Split
| @@ -0,0 +1,123 @@ | ||
| 1 | +import QtQuick 2.5 | |
| 2 | +import QtQuick.Controls 1.4 | |
| 3 | +import Qt.labs.settings 1.0 | |
| 4 | + | |
| 5 | +import "../../windows" | |
| 6 | +import "attachments" | |
| 7 | + | |
| 8 | +Window { | |
| 9 | + id: root | |
| 10 | + title: "Edit Attachments" | |
| 11 | + width: 600 | |
| 12 | + height: 600 | |
| 13 | + resizable: true | |
| 14 | + // User must click OK or cancel to close the window | |
| 15 | + closable: false | |
| 16 | + | |
| 17 | + readonly property var originalAttachments: MyAvatar.getAttachmentsVariant(); | |
| 18 | + property var attachments: []; | |
| 19 | + | |
| 20 | + property var settings: Settings { | |
| 21 | + category: "AttachmentsDialog" | |
| 22 | + property alias x: root.x | |
| 23 | + property alias y: root.y | |
| 24 | + property alias width: root.width | |
| 25 | + property alias height: root.height | |
| 26 | + } | |
| 27 | + | |
| 28 | + Component.onCompleted: { | |
| 29 | + for (var i = 0; i < originalAttachments.length; ++i) { | |
| 30 | + var attachment = originalAttachments[i]; | |
| 31 | + root.attachments.push(attachment); | |
| 32 | + listView.model.append({}); | |
| 33 | + } | |
| 34 | + } | |
| 35 | + | |
| 36 | + Rectangle { | |
| 37 | + anchors.fill: parent | |
| 38 | + radius: 4 | |
| 39 | + | |
| 40 | + Rectangle { | |
| 41 | + id: attachmentsBackground | |
| 42 | + anchors { left: parent.left; right: parent.right; top: parent.top; bottom: newAttachmentButton.top; margins: 8 } | |
| 43 | + color: "gray" | |
| 44 | + radius: 4 | |
| 45 | + | |
| 46 | + ScrollView{ | |
| 47 | + id: scrollView | |
| 48 | + anchors.fill: parent | |
| 49 | + anchors.margins: 4 | |
| 50 | + ListView { | |
| 51 | + id: listView | |
| 52 | + model: ListModel {} | |
| 53 | + delegate: Item { | |
| 54 | + implicitHeight: attachmentView.height + 8; | |
| 55 | + implicitWidth: attachmentView.width; | |
| 56 | + Attachment { | |
| 57 | + id: attachmentView | |
| 58 | + width: scrollView.width | |
| 59 | + attachment: root.attachments[index] | |
| 60 | + onDeleteAttachment: { | |
| 61 | + attachments.splice(index, 1); | |
| 62 | + listView.model.remove(index, 1); | |
| 63 | + } | |
| 64 | + onUpdateAttachment: MyAvatar.setAttachmentsVariant(attachments); | |
| 65 | + } | |
| 66 | + } | |
| 67 | + onCountChanged: MyAvatar.setAttachmentsVariant(attachments); | |
| 68 | + } | |
| 69 | + } | |
| 70 | + } | |
| 71 | + | |
| 72 | + Button { | |
| 73 | + id: newAttachmentButton | |
| 74 | + anchors { left: parent.left; right: parent.right; bottom: buttonRow.top; margins: 8 } | |
| 75 | + text: "New Attachment" | |
| 76 | + | |
| 77 | + onClicked: { | |
| 78 | + var template = { | |
| 79 | + modelUrl: "", | |
| 80 | + translation: { x: 0, y: 0, z: 0 }, | |
| 81 | + rotation: { x: 0, y: 0, z: 0 }, | |
| 82 | + scale: 1, | |
| 83 | + jointName: MyAvatar.jointNames[0], | |
| 84 | + soft: false | |
| 85 | + }; | |
| 86 | + attachments.push(template); | |
| 87 | + listView.model.append({}); | |
| 88 | + MyAvatar.setAttachmentsVariant(attachments); | |
| 89 | + } | |
| 90 | + } | |
| 91 | + | |
| 92 | + Row { | |
| 93 | + id: buttonRow | |
| 94 | + spacing: 8 | |
| 95 | + anchors { right: parent.right; bottom: parent.bottom; margins: 8 } | |
| 96 | + Button { action: cancelAction } | |
| 97 | + Button { action: okAction } | |
| 98 | + } | |
| 99 | + | |
| 100 | + Action { | |
| 101 | + id: cancelAction | |
| 102 | + text: "Cancel" | |
| 103 | + onTriggered: { | |
| 104 | + MyAvatar.setAttachmentsVariant(originalAttachments); | |
| 105 | + root.destroy() | |
| 106 | + } | |
| 107 | + } | |
| 108 | + | |
| 109 | + Action { | |
| 110 | + id: okAction | |
| 111 | + text: "OK" | |
| 112 | + onTriggered: { | |
| 113 | + for (var i = 0; i < attachments.length; ++i) { | |
| 114 | + console.log("Attachment " + i + ": " + attachments[i]); | |
| 115 | + } | |
| 116 | + | |
| 117 | + MyAvatar.setAttachmentsVariant(attachments); | |
| 118 | + root.destroy() | |
| 119 | + } | |
| 120 | + } | |
| 121 | + } | |
| 122 | +} | |
| 123 | + | |
| @@ -0,0 +1,128 @@ | ||
| 1 | +import QtQuick 2.5 | |
| 2 | +import QtQuick.Controls 1.4 | |
| 3 | +import QtQuick.XmlListModel 2.0 | |
| 4 | + | |
| 5 | +import "../../windows" | |
| 6 | +import "../../js/Utils.js" as Utils | |
| 7 | +import "../models" | |
| 8 | + | |
| 9 | +ModalWindow { | |
| 10 | + id: root | |
| 11 | + resizable: true | |
| 12 | + width: 640 | |
| 13 | + height: 480 | |
| 14 | + | |
| 15 | + property var result; | |
| 16 | + | |
| 17 | + signal selected(var modelUrl); | |
| 18 | + signal canceled(); | |
| 19 | + | |
| 20 | + Rectangle { | |
| 21 | + anchors.fill: parent | |
| 22 | + color: "white" | |
| 23 | + | |
| 24 | + Item { | |
| 25 | + anchors { fill: parent; margins: 8 } | |
| 26 | + | |
| 27 | + TextField { | |
| 28 | + id: filterEdit | |
| 29 | + anchors { left: parent.left; right: parent.right; top: parent.top } | |
| 30 | + placeholderText: "filter" | |
| 31 | + onTextChanged: tableView.model.filter = text | |
| 32 | + } | |
| 33 | + | |
| 34 | + TableView { | |
| 35 | + id: tableView | |
| 36 | + anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 } | |
| 37 | + model: S3Model{} | |
| 38 | + onCurrentRowChanged: { | |
| 39 | + if (currentRow == -1) { | |
| 40 | + root.result = null; | |
| 41 | + return; | |
| 42 | + } | |
| 43 | + result = model.baseUrl + "/" + model.get(tableView.currentRow).key; | |
| 44 | + } | |
| 45 | + itemDelegate: Component { | |
| 46 | + Item { | |
| 47 | + clip: true | |
| 48 | + Text { | |
| 49 | + x: 3 | |
| 50 | + anchors.verticalCenter: parent.verticalCenter | |
| 51 | + color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor | |
| 52 | + elide: styleData.elideMode | |
| 53 | + text: getText() | |
| 54 | + | |
| 55 | + function getText() { | |
| 56 | + switch(styleData.column) { | |
| 57 | + case 1: | |
| 58 | + return Utils.formatSize(styleData.value) | |
| 59 | + default: | |
| 60 | + return styleData.value; | |
| 61 | + } | |
| 62 | + } | |
| 63 | + | |
| 64 | + } | |
| 65 | + } | |
| 66 | + } | |
| 67 | + TableViewColumn { | |
| 68 | + role: "name" | |
| 69 | + title: "Name" | |
| 70 | + width: 200 | |
| 71 | + } | |
| 72 | + TableViewColumn { | |
| 73 | + role: "size" | |
| 74 | + title: "Size" | |
| 75 | + width: 100 | |
| 76 | + } | |
| 77 | + TableViewColumn { | |
| 78 | + role: "modified" | |
| 79 | + title: "Last Modified" | |
| 80 | + width: 200 | |
| 81 | + } | |
| 82 | + Rectangle { | |
| 83 | + anchors.fill: parent | |
| 84 | + visible: tableView.model.status !== XmlListModel.Ready | |
| 85 | + color: "#7fffffff" | |
| 86 | + BusyIndicator { | |
| 87 | + anchors.centerIn: parent | |
| 88 | + width: 48; height: 48 | |
| 89 | + running: true | |
| 90 | + } | |
| 91 | + } | |
| 92 | + } | |
| 93 | + | |
| 94 | + Row { | |
| 95 | + id: buttonRow | |
| 96 | + anchors { right: parent.right; bottom: parent.bottom } | |
| 97 | + Button { action: acceptAction } | |
| 98 | + Button { action: cancelAction } | |
| 99 | + } | |
| 100 | + | |
| 101 | + Action { | |
| 102 | + id: acceptAction | |
| 103 | + text: qsTr("OK") | |
| 104 | + enabled: root.result ? true : false | |
| 105 | + shortcut: Qt.Key_Return | |
| 106 | + onTriggered: { | |
| 107 | + root.selected(root.result); | |
| 108 | + root.destroy(); | |
| 109 | + } | |
| 110 | + } | |
| 111 | + | |
| 112 | + Action { | |
| 113 | + id: cancelAction | |
| 114 | + text: qsTr("Cancel") | |
| 115 | + shortcut: Qt.Key_Escape | |
| 116 | + onTriggered: { | |
| 117 | + root.canceled(); | |
| 118 | + root.destroy(); | |
| 119 | + } | |
| 120 | + } | |
| 121 | + } | |
| 122 | + | |
| 123 | + } | |
| 124 | +} | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| @@ -0,0 +1,128 @@ | ||
| 1 | +import QtQuick 2.5 | |
| 2 | +import QtQuick.Controls 1.4 | |
| 3 | +import QtQuick.XmlListModel 2.0 | |
| 4 | + | |
| 5 | +import "../../windows" | |
| 6 | +import "../../js/Utils.js" as Utils | |
| 7 | +import "../models" | |
| 8 | + | |
| 9 | +ModalWindow { | |
| 10 | + id: root | |
| 11 | + resizable: true | |
| 12 | + width: 640 | |
| 13 | + height: 480 | |
| 14 | + | |
| 15 | + property var result; | |
| 16 | + | |
| 17 | + signal selected(var modelUrl); | |
| 18 | + signal canceled(); | |
| 19 | + | |
| 20 | + Rectangle { | |
| 21 | + anchors.fill: parent | |
| 22 | + color: "white" | |
| 23 | + | |
| 24 | + Item { | |
| 25 | + anchors { fill: parent; margins: 8 } | |
| 26 | + | |
| 27 | + TextField { | |
| 28 | + id: filterEdit | |
| 29 | + anchors { left: parent.left; right: parent.right; top: parent.top } | |
| 30 | + placeholderText: "filter" | |
| 31 | + onTextChanged: tableView.model.filter = text | |
| 32 | + } | |
| 33 | + | |
| 34 | + TableView { | |
| 35 | + id: tableView | |
| 36 | + anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 } | |
| 37 | + model: S3Model{} | |
| 38 | + onCurrentRowChanged: { | |
| 39 | + if (currentRow == -1) { | |
| 40 | + root.result = null; | |
| 41 | + return; | |
| 42 | + } | |
| 43 | + result = model.baseUrl + "/" + model.get(tableView.currentRow).key; | |
| 44 | + } | |
| 45 | + itemDelegate: Component { | |
| 46 | + Item { | |
| 47 | + clip: true | |
| 48 | + Text { | |
| 49 | + x: 3 | |
| 50 | + anchors.verticalCenter: parent.verticalCenter | |
| 51 | + color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor | |
| 52 | + elide: styleData.elideMode | |
| 53 | + text: getText() | |
| 54 | + | |
| 55 | + function getText() { | |
| 56 | + switch(styleData.column) { | |
| 57 | + case 1: | |
| 58 | + return Utils.formatSize(styleData.value) | |
| 59 | + default: | |
| 60 | + return styleData.value; | |
| 61 | + } | |
| 62 | + } | |
| 63 | + | |
| 64 | + } | |
| 65 | + } | |
| 66 | + } | |
| 67 | + TableViewColumn { | |
| 68 | + role: "name" | |
| 69 | + title: "Name" | |
| 70 | + width: 200 | |
| 71 | + } | |
| 72 | + TableViewColumn { | |
| 73 | + role: "size" | |
| 74 | + title: "Size" | |
| 75 | + width: 100 | |
| 76 | + } | |
| 77 | + TableViewColumn { | |
| 78 | + role: "modified" | |
| 79 | + title: "Last Modified" | |
| 80 | + width: 200 | |
| 81 | + } | |
| 82 | + Rectangle { | |
| 83 | + anchors.fill: parent | |
| 84 | + visible: tableView.model.status !== XmlListModel.Ready | |
| 85 | + color: "#7fffffff" | |
| 86 | + BusyIndicator { | |
| 87 | + anchors.centerIn: parent | |
| 88 | + width: 48; height: 48 | |
| 89 | + running: true | |
| 90 | + } | |
| 91 | + } | |
| 92 | + } | |
| 93 | + | |
| 94 | + Row { | |
| 95 | + id: buttonRow | |
| 96 | + anchors { right: parent.right; bottom: parent.bottom } | |
| 97 | + Button { action: acceptAction } | |
| 98 | + Button { action: cancelAction } | |
| 99 | + } | |
| 100 | + | |
| 101 | + Action { | |
| 102 | + id: acceptAction | |
| 103 | + text: qsTr("OK") | |
| 104 | + enabled: root.result ? true : false | |
| 105 | + shortcut: Qt.Key_Return | |
| 106 | + onTriggered: { | |
| 107 | + root.selected(root.result); | |
| 108 | + root.destroy(); | |
| 109 | + } | |
| 110 | + } | |
| 111 | + | |
| 112 | + Action { | |
| 113 | + id: cancelAction | |
| 114 | + text: qsTr("Cancel") | |
| 115 | + shortcut: Qt.Key_Escape | |
| 116 | + onTriggered: { | |
| 117 | + root.canceled(); | |
| 118 | + root.destroy(); | |
| 119 | + } | |
| 120 | + } | |
| 121 | + } | |
| 122 | + | |
| 123 | + } | |
| 124 | +} | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| @@ -0,0 +1,164 @@ | ||
| 1 | +import QtQuick 2.5 | |
| 2 | +import QtQuick.Controls 1.4 | |
| 3 | + | |
| 4 | +import "../../../windows" | |
| 5 | +import "." | |
| 6 | +import ".." | |
| 7 | + | |
| 8 | +Item { | |
| 9 | + height: column.height + 2 * 8 | |
| 10 | + | |
| 11 | + property var attachment; | |
| 12 | + | |
| 13 | + signal deleteAttachment(var attachment); | |
| 14 | + signal updateAttachment(); | |
| 15 | + property bool completed: false; | |
| 16 | + | |
| 17 | + Rectangle { color: "white"; anchors.fill: parent; radius: 4 } | |
| 18 | + | |
| 19 | + Component.onCompleted: { | |
| 20 | + completed = true; | |
| 21 | + } | |
| 22 | + | |
| 23 | + Column { | |
| 24 | + y: 8 | |
| 25 | + id: column | |
| 26 | + anchors { left: parent.left; right: parent.right; margins: 8 } | |
| 27 | + spacing: 8 | |
| 28 | + | |
| 29 | + Item { | |
| 30 | + height: modelChooserButton.height | |
| 31 | + anchors { left: parent.left; right: parent.right; } | |
| 32 | + Text { id: urlLabel; text: "Model URL:"; width: 80; anchors.verticalCenter: modelUrl.verticalCenter } | |
| 33 | + TextField { | |
| 34 | + id: modelUrl; | |
| 35 | + height: jointChooser.height; | |
| 36 | + anchors { left: urlLabel.right; leftMargin: 8; rightMargin: 8; right: modelChooserButton.left } | |
| 37 | + text: attachment ? attachment.modelUrl : "" | |
| 38 | + onTextChanged: { | |
| 39 | + if (completed && attachment && attachment.modelUrl !== text) { | |
| 40 | + attachment.modelUrl = text; | |
| 41 | + updateAttachment(); | |
| 42 | + } | |
| 43 | + } | |
| 44 | + } | |
| 45 | + Button { | |
| 46 | + id: modelChooserButton; | |
| 47 | + text: "Choose"; | |
| 48 | + anchors { right: parent.right; verticalCenter: modelUrl.verticalCenter } | |
| 49 | + Component { | |
| 50 | + id: modelBrowserBuiler; | |
| 51 | + ModelBrowserDialog {} | |
| 52 | + } | |
| 53 | + | |
| 54 | + onClicked: { | |
| 55 | + var browser = modelBrowserBuiler.createObject(desktop); | |
| 56 | + browser.selected.connect(function(newModelUrl){ | |
| 57 | + modelUrl.text = newModelUrl; | |
| 58 | + }) | |
| 59 | + } | |
| 60 | + } | |
| 61 | + } | |
| 62 | + | |
| 63 | + Item { | |
| 64 | + height: jointChooser.height | |
| 65 | + anchors { left: parent.left; right: parent.right; } | |
| 66 | + Text { | |
| 67 | + id: jointLabel; | |
| 68 | + width: 80; | |
| 69 | + text: "Joint:"; | |
| 70 | + anchors.verticalCenter: jointChooser.verticalCenter; | |
| 71 | + } | |
| 72 | + ComboBox { | |
| 73 | + id: jointChooser; | |
| 74 | + anchors { left: jointLabel.right; leftMargin: 8; right: parent.right } | |
| 75 | + model: MyAvatar.jointNames | |
| 76 | + currentIndex: attachment ? model.indexOf(attachment.jointName) : -1 | |
| 77 | + onCurrentIndexChanged: { | |
| 78 | + if (completed && attachment && currentIndex != -1 && currentText && currentText !== attachment.jointName) { | |
| 79 | + attachment.jointName = currentText; | |
| 80 | + updateAttachment(); | |
| 81 | + } | |
| 82 | + } | |
| 83 | + } | |
| 84 | + } | |
| 85 | + | |
| 86 | + Item { | |
| 87 | + height: translation.height | |
| 88 | + anchors { left: parent.left; right: parent.right; } | |
| 89 | + Text { id: translationLabel; width: 80; text: "Translation:"; anchors.verticalCenter: translation.verticalCenter; } | |
| 90 | + Translation { | |
| 91 | + id: translation; | |
| 92 | + anchors { left: translationLabel.right; leftMargin: 8; right: parent.right } | |
| 93 | + vector: attachment ? attachment.translation : {x: 0, y: 0, z: 0}; | |
| 94 | + onValueChanged: { | |
| 95 | + if (completed && attachment) { | |
| 96 | + attachment.translation = vector; | |
| 97 | + updateAttachment(); | |
| 98 | + } | |
| 99 | + } | |
| 100 | + } | |
| 101 | + } | |
| 102 | + | |
| 103 | + Item { | |
| 104 | + height: rotation.height | |
| 105 | + anchors { left: parent.left; right: parent.right; } | |
| 106 | + Text { id: rotationLabel; width: 80; text: "Rotation:"; anchors.verticalCenter: rotation.verticalCenter; } | |
| 107 | + Rotation { | |
| 108 | + id: rotation; | |
| 109 | + anchors { left: rotationLabel.right; leftMargin: 8; right: parent.right } | |
| 110 | + vector: attachment ? attachment.rotation : {x: 0, y: 0, z: 0}; | |
| 111 | + onValueChanged: { | |
| 112 | + if (completed && attachment) { | |
| 113 | + attachment.rotation = vector; | |
| 114 | + updateAttachment(); | |
| 115 | + } | |
| 116 | + } | |
| 117 | + } | |
| 118 | + } | |
| 119 | + | |
| 120 | + Item { | |
| 121 | + height: scaleSpinner.height | |
| 122 | + anchors { left: parent.left; right: parent.right; } | |
| 123 | + Text { id: scaleLabel; width: 80; text: "Scale:"; anchors.verticalCenter: scale.verticalCenter; } | |
| 124 | + SpinBox { | |
| 125 | + id: scaleSpinner; | |
| 126 | + anchors { left: scaleLabel.right; leftMargin: 8; right: parent.right } | |
| 127 | + decimals: 1; | |
| 128 | + minimumValue: 0.1 | |
| 129 | + maximumValue: 10 | |
| 130 | + stepSize: 0.1; | |
| 131 | + value: attachment ? attachment.scale : 1.0 | |
| 132 | + onValueChanged: { | |
| 133 | + if (completed && attachment && attachment.scale !== value) { | |
| 134 | + attachment.scale = value; | |
| 135 | + updateAttachment(); | |
| 136 | + } | |
| 137 | + } | |
| 138 | + } | |
| 139 | + } | |
| 140 | + | |
| 141 | + Item { | |
| 142 | + height: soft.height | |
| 143 | + anchors { left: parent.left; right: parent.right; } | |
| 144 | + Text { id: softLabel; width: 80; text: "Is soft:"; anchors.verticalCenter: soft.verticalCenter; } | |
| 145 | + CheckBox { | |
| 146 | + id: soft; | |
| 147 | + anchors { left: softLabel.right; leftMargin: 8; right: parent.right } | |
| 148 | + checked: attachment ? attachment.soft : false | |
| 149 | + onCheckedChanged: { | |
| 150 | + if (completed && attachment && attachment.soft !== checked) { | |
| 151 | + attachment.soft = checked; | |
| 152 | + updateAttachment(); | |
| 153 | + } | |
| 154 | + } | |
| 155 | + } | |
| 156 | + } | |
| 157 | + | |
| 158 | + Button { | |
| 159 | + anchors { left: parent.left; right: parent.right; } | |
| 160 | + text: "Delete" | |
| 161 | + onClicked: deleteAttachment(root.attachment); | |
| 162 | + } | |
| 163 | + } | |
| 164 | +} | |
| @@ -0,0 +1,9 @@ | ||
| 1 | +import "." | |
| 2 | + | |
| 3 | +Vector3 { | |
| 4 | + decimals: 1; | |
| 5 | + stepSize: 1; | |
| 6 | + maximumValue: 180 | |
| 7 | + minimumValue: -180 | |
| 8 | +} | |
| 9 | + | |
| @@ -0,0 +1,9 @@ | ||
| 1 | +import "." | |
| 2 | + | |
| 3 | +Vector3 { | |
| 4 | + decimals: 2; | |
| 5 | + stepSize: 0.01; | |
| 6 | + maximumValue: 10 | |
| 7 | + minimumValue: -10 | |
| 8 | +} | |
| 9 | + | |
| @@ -0,0 +1,71 @@ | ||
| 1 | +import QtQuick 2.5 | |
| 2 | +import QtQuick.Controls 1.4 | |
| 3 | + | |
| 4 | +Item { | |
| 5 | + id: root | |
| 6 | + implicitHeight: xspinner.height | |
| 7 | + readonly property real spacing: 8 | |
| 8 | + property real spinboxWidth: (width / 3) - spacing | |
| 9 | + property var vector; | |
| 10 | + property real decimals: 0 | |
| 11 | + property real stepSize: 1 | |
| 12 | + property real maximumValue: 99 | |
| 13 | + property real minimumValue: 0 | |
| 14 | + | |
| 15 | + signal valueChanged(); | |
| 16 | + | |
| 17 | + SpinBox { | |
| 18 | + id: xspinner | |
| 19 | + width: root.spinboxWidth | |
| 20 | + anchors { left: parent.left } | |
| 21 | + value: root.vector.x | |
| 22 | + | |
| 23 | + decimals: root.decimals | |
| 24 | + stepSize: root.stepSize | |
| 25 | + maximumValue: root.maximumValue | |
| 26 | + minimumValue: root.minimumValue | |
| 27 | + onValueChanged: { | |
| 28 | + if (value !== vector.x) { | |
| 29 | + vector.x = value | |
| 30 | + root.valueChanged(); | |
| 31 | + } | |
| 32 | + } | |
| 33 | + } | |
| 34 | + | |
| 35 | + SpinBox { | |
| 36 | + id: yspinner | |
| 37 | + width: root.spinboxWidth | |
| 38 | + anchors { horizontalCenter: parent.horizontalCenter } | |
| 39 | + value: root.vector.y | |
| 40 | + | |
| 41 | + decimals: root.decimals | |
| 42 | + stepSize: root.stepSize | |
| 43 | + maximumValue: root.maximumValue | |
| 44 | + minimumValue: root.minimumValue | |
| 45 | + onValueChanged: { | |
| 46 | + if (value !== vector.y) { | |
| 47 | + vector.y = value | |
| 48 | + root.valueChanged(); | |
| 49 | + } | |
| 50 | + } | |
| 51 | + } | |
| 52 | + | |
| 53 | + SpinBox { | |
| 54 | + id: zspinner | |
| 55 | + width: root.spinboxWidth | |
| 56 | + anchors { right: parent.right; } | |
| 57 | + value: root.vector.z | |
| 58 | + | |
| 59 | + decimals: root.decimals | |
| 60 | + stepSize: root.stepSize | |
| 61 | + maximumValue: root.maximumValue | |
| 62 | + minimumValue: root.minimumValue | |
| 63 | + onValueChanged: { | |
| 64 | + if (value !== vector.z) { | |
| 65 | + vector.z = value | |
| 66 | + root.valueChanged(); | |
| 67 | + } | |
| 68 | + } | |
| 69 | + } | |
| 70 | +} | |
| 71 | + | |
| @@ -0,0 +1,41 @@ | ||
| 1 | +import QtQuick 2.0 | |
| 2 | +import QtQuick.XmlListModel 2.0 | |
| 3 | + | |
| 4 | +//http://s3.amazonaws.com/hifi-public?prefix=models/attachments | |
| 5 | +/* | |
| 6 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 7 | +<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> | |
| 8 | + <Contents> | |
| 9 | + <Key>models/attachments/guitar.fst</Key> | |
| 10 | + <LastModified>2015-11-10T00:28:22.000Z</LastModified> | |
| 11 | + <ETag>"236c00c4802ba9c2605cabd5601d138e"</ETag> | |
| 12 | + <Size>2992</Size> | |
| 13 | + <StorageClass>STANDARD</StorageClass> | |
| 14 | + </Contents> | |
| 15 | +</ListBucketResult> | |
| 16 | +*/ | |
| 17 | + | |
| 18 | +// FIXME how to deal with truncated results? Store the marker? | |
| 19 | +XmlListModel { | |
| 20 | + id: xmlModel | |
| 21 | + property string prefix: "models/attachments/" | |
| 22 | + property string extension: "fst" | |
| 23 | + property string filter; | |
| 24 | + | |
| 25 | + readonly property string realPrefix: prefix.match('.*/$') ? prefix : (prefix + "/") | |
| 26 | + readonly property string nameRegex: realPrefix + (filter ? (".*" + filter) : "") + ".*\." + extension | |
| 27 | + readonly property string nameQuery: "Key/substring-before(substring-after(string(), '" + prefix + "'), '." + extension + "')" | |
| 28 | + readonly property string baseUrl: "http://s3.amazonaws.com/hifi-public" | |
| 29 | + | |
| 30 | + // FIXME need to urlencode prefix? | |
| 31 | + source: baseUrl + "?prefix=" + realPrefix | |
| 32 | + query: "/ListBucketResult/Contents[matches(Key, '" + nameRegex + "')]" | |
| 33 | + namespaceDeclarations: "declare default element namespace 'http://s3.amazonaws.com/doc/2006-03-01/';" | |
| 34 | + | |
| 35 | + XmlRole { name: "name"; query: nameQuery } | |
| 36 | + XmlRole { name: "size"; query: "Size/string()" } | |
| 37 | + XmlRole { name: "tag"; query: "ETag/string()" } | |
| 38 | + XmlRole { name: "modified"; query: "LastModified/string()" } | |
| 39 | + XmlRole { name: "key"; query: "Key/string()" } | |
| 40 | +} | |
| 41 | + | |
| @@ -14,3 +14,17 @@ function randomPosition(min, max) { · 13 unmodified lines | ||
| 14 | 14 | Math.random() * (max.x - min.x), |
| 15 | 15 | Math.random() * (max.y - min.y)); |
| 16 | 16 | } |
| 17 | + | |
| 18 | +function formatSize(size) { | |
| 19 | + var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ]; | |
| 20 | + var suffixIndex = 0 | |
| 21 | + while ((size / 1024.0) > 1.1) { | |
| 22 | + size /= 1024.0; | |
| 23 | + ++suffixIndex; | |
| 24 | + } | |
| 25 | + | |
| 26 | + size = Math.round(size*1000)/1000; | |
| 27 | + size = size.toLocaleString() | |
| 28 | + | |
| 29 | + return size + " " + suffixes[suffixIndex]; | |
| 30 | +} | |
| @@ -1186,6 +1186,7 @@ void Application::initializeUi() { · 1185 unmodified lines | ||
| 1186 | 1186 | UpdateDialog::registerType(); |
| 1187 | 1187 | qmlRegisterType<Preference>("Hifi", 1, 0, "Preference"); |
| 1188 | 1188 | |
| 1189 | + | |
| 1189 | 1190 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 1190 | 1191 | offscreenUi->create(_offscreenContext->getContext()); |
| 1191 | 1192 | offscreenUi->setProxyWindow(_window->windowHandle()); |
| @@ -1842,6 +1843,10 @@ void Application::keyPressEvent(QKeyEvent* event) { · 650 unmodified lines | ||
| 1842 | 1843 | |
| 1843 | 1844 | case Qt::Key_X: |
| 1844 | 1845 | if (isShifted && isMeta) { |
| 1846 | + auto offscreenUi = DependencyManager::get<OffscreenUi>(); | |
| 1847 | + offscreenUi->getRootContext()->engine()->clearComponentCache(); | |
| 1848 | + offscreenUi->load("hifi/dialogs/AttachmentsDialog.qml"); | |
| 1849 | + // OffscreenUi::information("Debugging", "Component cache cleared"); | |
| 1845 | 1850 | // placeholder for dialogs being converted to QML. |
| 1846 | 1851 | } |
| 1847 | 1852 | break; |
| @@ -25,6 +25,7 @@ · 24 unmodified lines | ||
| 25 | 25 | #include <QtNetwork/QNetworkReply> |
| 26 | 26 | #include <QtNetwork/QNetworkRequest> |
| 27 | 27 | |
| 28 | +#include <QVariantGLM.h> | |
| 28 | 29 | #include <Transform.h> |
| 29 | 30 | #include <NetworkAccessManager.h> |
| 30 | 31 | #include <NodeList.h> |
| @@ -1673,3 +1674,65 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const · 1642 unmodified lines | ||
| 1673 | 1674 | assert(false); |
| 1674 | 1675 | return glm::vec3(); |
| 1675 | 1676 | } |
| 1677 | + | |
| 1678 | +QVariant AttachmentData::toVariant() const { | |
| 1679 | + QVariantMap result; | |
| 1680 | + result["modelUrl"] = modelURL; | |
| 1681 | + result["jointName"] = jointName; | |
| 1682 | + result["translation"] = glmToQMap(translation); | |
| 1683 | + result["rotation"] = glmToQMap(glm::degrees(safeEulerAngles(rotation))); | |
| 1684 | + result["scale"] = scale; | |
| 1685 | + result["soft"] = isSoft; | |
| 1686 | + return result; | |
| 1687 | +} | |
| 1688 | + | |
| 1689 | +glm::vec3 variantToVec3(const QVariant& var) { | |
| 1690 | + auto map = var.toMap(); | |
| 1691 | + glm::vec3 result; | |
| 1692 | + result.x = map["x"].toFloat(); | |
| 1693 | + result.y = map["y"].toFloat(); | |
| 1694 | + result.z = map["z"].toFloat(); | |
| 1695 | + return result; | |
| 1696 | +} | |
| 1697 | + | |
| 1698 | +void AttachmentData::fromVariant(const QVariant& variant) { | |
| 1699 | + auto map = variant.toMap(); | |
| 1700 | + if (map.contains("modelUrl")) { | |
| 1701 | + auto urlString = map["modelUrl"].toString(); | |
| 1702 | + modelURL = urlString; | |
| 1703 | + } | |
| 1704 | + if (map.contains("jointName")) { | |
| 1705 | + jointName = map["jointName"].toString(); | |
| 1706 | + } | |
| 1707 | + if (map.contains("translation")) { | |
| 1708 | + translation = variantToVec3(map["translation"]); | |
| 1709 | + } | |
| 1710 | + if (map.contains("rotation")) { | |
| 1711 | + rotation = glm::quat(glm::radians(variantToVec3(map["rotation"]))); | |
| 1712 | + } | |
| 1713 | + if (map.contains("scale")) { | |
| 1714 | + scale = map["scale"].toFloat(); | |
| 1715 | + } | |
| 1716 | + if (map.contains("soft")) { | |
| 1717 | + isSoft = map["soft"].toBool(); | |
| 1718 | + } | |
| 1719 | +} | |
| 1720 | + | |
| 1721 | +QVariantList AvatarData::getAttachmentsVariant() const { | |
| 1722 | + QVariantList result; | |
| 1723 | + for (const auto& attachment : getAttachmentData()) { | |
| 1724 | + result.append(attachment.toVariant()); | |
| 1725 | + } | |
| 1726 | + return result; | |
| 1727 | +} | |
| 1728 | + | |
| 1729 | +void AvatarData::setAttachmentsVariant(const QVariantList& variant) { | |
| 1730 | + QVector<AttachmentData> newAttachments; | |
| 1731 | + newAttachments.reserve(variant.size()); | |
| 1732 | + for (const auto& attachmentVar : variant) { | |
| 1733 | + AttachmentData attachment; | |
| 1734 | + attachment.fromVariant(attachmentVar); | |
| 1735 | + newAttachments.append(attachment); | |
| 1736 | + } | |
| 1737 | + setAttachmentData(newAttachments); | |
| 1738 | +} | |
| @@ -277,6 +277,9 @@ public: · 276 unmodified lines | ||
| 277 | 277 | |
| 278 | 278 | Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); } |
| 279 | 279 | |
| 280 | + Q_INVOKABLE QVariantList getAttachmentsVariant() const; | |
| 281 | + Q_INVOKABLE void setAttachmentsVariant(const QVariantList& variant); | |
| 282 | + | |
| 280 | 283 | void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; } |
| 281 | 284 | |
| 282 | 285 | // key state |
| @@ -448,6 +451,9 @@ public: · 165 unmodified lines | ||
| 448 | 451 | |
| 449 | 452 | QJsonObject toJson() const; |
| 450 | 453 | void fromJson(const QJsonObject& json); |
| 454 | + | |
| 455 | + QVariant toVariant() const; | |
| 456 | + void fromVariant(const QVariant& variant); | |
| 451 | 457 | }; |
| 452 | 458 | |
| 453 | 459 | QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment); |
| @@ -959,7 +959,7 @@ void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* p · 958 unmodified lines | ||
| 959 | 959 | bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) { |
| 960 | 960 | QSharedMemory sharedMem(key); |
| 961 | 961 | if (!sharedMem.attach(QSharedMemory::ReadOnly)) { |
| 962 | − qWarning() << "Could not attach to shared memory at key" << key; | |
| 962 | + qCWarning(networking) << "Could not attach to shared memory at key" << key; | |
| 963 | 963 | return false; |
| 964 | 964 | } else { |
| 965 | 965 | sharedMem.lock(); |
| @@ -7,6 +7,7 @@ import "../../../interface/resources/qml" · 6 unmodified lines | ||
| 7 | 7 | import "../../../interface/resources/qml/windows" |
| 8 | 8 | import "../../../interface/resources/qml/dialogs" |
| 9 | 9 | import "../../../interface/resources/qml/hifi" |
| 10 | +import "../../../interface/resources/qml/hifi/dialogs" | |
| 10 | 11 | |
| 11 | 12 | ApplicationWindow { |
| 12 | 13 | id: appWindow |
| @@ -196,4 +197,15 @@ ApplicationWindow { · 183 unmodified lines | ||
| 196 | 197 | } |
| 197 | 198 | */ |
| 198 | 199 | } |
| 200 | + | |
| 201 | + Action { | |
| 202 | + text: "Open Browser" | |
| 203 | + shortcut: "Ctrl+Shift+X" | |
| 204 | + onTriggered: { | |
| 205 | + builder.createObject(desktop); | |
| 206 | + } | |
| 207 | + property var builder: Component { | |
| 208 | + ModelBrowserDialog{} | |
| 209 | + } | |
| 210 | + } | |
| 199 | 211 | } |