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 | Math.random() * (max.x - min.x), | 14 | Math.random() * (max.x - min.x), |
| 15 | Math.random() * (max.y - min.y)); | 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 | UpdateDialog::registerType(); | 1186 | UpdateDialog::registerType(); |
| 1187 | qmlRegisterType<Preference>("Hifi", 1, 0, "Preference"); | 1187 | qmlRegisterType<Preference>("Hifi", 1, 0, "Preference"); |
| 1188 | 1188 | ||
| 1189 | |||
| 1189 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); | 1190 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 1190 | offscreenUi->create(_offscreenContext->getContext()); | 1191 | offscreenUi->create(_offscreenContext->getContext()); |
| 1191 | offscreenUi->setProxyWindow(_window->windowHandle()); | 1192 | offscreenUi->setProxyWindow(_window->windowHandle()); |
| @@ -1842,6 +1843,10 @@ void Application::keyPressEvent(QKeyEvent* event) { · 650 unmodified lines | |||
| 1842 | 1843 | ||
| 1843 | case Qt::Key_X: | 1844 | case Qt::Key_X: |
| 1844 | if (isShifted && isMeta) { | 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 | // placeholder for dialogs being converted to QML. | 1850 | // placeholder for dialogs being converted to QML. |
| 1846 | } | 1851 | } |
| 1847 | break; | 1852 | break; |
| @@ -25,6 +25,7 @@ · 24 unmodified lines | |||
| 25 | #include <QtNetwork/QNetworkReply> | 25 | #include <QtNetwork/QNetworkReply> |
| 26 | #include <QtNetwork/QNetworkRequest> | 26 | #include <QtNetwork/QNetworkRequest> |
| 27 | 27 | ||
| 28 | #include <QVariantGLM.h> | ||
| 28 | #include <Transform.h> | 29 | #include <Transform.h> |
| 29 | #include <NetworkAccessManager.h> | 30 | #include <NetworkAccessManager.h> |
| 30 | #include <NodeList.h> | 31 | #include <NodeList.h> |
| @@ -1673,3 +1674,65 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const · 1642 unmodified lines | |||
| 1673 | assert(false); | 1674 | assert(false); |
| 1674 | return glm::vec3(); | 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 | Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); } | 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 | void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; } | 283 | void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; } |
| 281 | 284 | ||
| 282 | // key state | 285 | // key state |
| @@ -448,6 +451,9 @@ public: · 165 unmodified lines | |||
| 448 | 451 | ||
| 449 | QJsonObject toJson() const; | 452 | QJsonObject toJson() const; |
| 450 | void fromJson(const QJsonObject& json); | 453 | void fromJson(const QJsonObject& json); |
| 454 | |||
| 455 | QVariant toVariant() const; | ||
| 456 | void fromVariant(const QVariant& variant); | ||
| 451 | }; | 457 | }; |
| 452 | 458 | ||
| 453 | QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment); | 459 | QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment); |
| @@ -959,7 +959,7 @@ void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* p · 958 unmodified lines | |||
| 959 | bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) { | 959 | bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) { |
| 960 | QSharedMemory sharedMem(key); | 960 | QSharedMemory sharedMem(key); |
| 961 | if (!sharedMem.attach(QSharedMemory::ReadOnly)) { | 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 | return false; | 963 | return false; |
| 964 | } else { | 964 | } else { |
| 965 | sharedMem.lock(); | 965 | sharedMem.lock(); |
| @@ -7,6 +7,7 @@ import "../../../interface/resources/qml" · 6 unmodified lines | |||
| 7 | import "../../../interface/resources/qml/windows" | 7 | import "../../../interface/resources/qml/windows" |
| 8 | import "../../../interface/resources/qml/dialogs" | 8 | import "../../../interface/resources/qml/dialogs" |
| 9 | import "../../../interface/resources/qml/hifi" | 9 | import "../../../interface/resources/qml/hifi" |
| 10 | import "../../../interface/resources/qml/hifi/dialogs" | ||
| 10 | 11 | ||
| 11 | ApplicationWindow { | 12 | ApplicationWindow { |
| 12 | id: appWindow | 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 | } |