mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 04:24:47 +02:00
Attachment dialog
This commit is contained in:
parent
881a94cd19
commit
664100b9b1
14 changed files with 774 additions and 1 deletions
123
interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml
Normal file
123
interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import Qt.labs.settings 1.0
|
||||||
|
|
||||||
|
import "../../windows"
|
||||||
|
import "attachments"
|
||||||
|
|
||||||
|
Window {
|
||||||
|
id: root
|
||||||
|
title: "Edit Attachments"
|
||||||
|
width: 600
|
||||||
|
height: 600
|
||||||
|
resizable: true
|
||||||
|
// User must click OK or cancel to close the window
|
||||||
|
closable: false
|
||||||
|
|
||||||
|
readonly property var originalAttachments: MyAvatar.getAttachmentsVariant();
|
||||||
|
property var attachments: [];
|
||||||
|
|
||||||
|
property var settings: Settings {
|
||||||
|
category: "AttachmentsDialog"
|
||||||
|
property alias x: root.x
|
||||||
|
property alias y: root.y
|
||||||
|
property alias width: root.width
|
||||||
|
property alias height: root.height
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
for (var i = 0; i < originalAttachments.length; ++i) {
|
||||||
|
var attachment = originalAttachments[i];
|
||||||
|
root.attachments.push(attachment);
|
||||||
|
listView.model.append({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: attachmentsBackground
|
||||||
|
anchors { left: parent.left; right: parent.right; top: parent.top; bottom: newAttachmentButton.top; margins: 8 }
|
||||||
|
color: "gray"
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
ScrollView{
|
||||||
|
id: scrollView
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 4
|
||||||
|
ListView {
|
||||||
|
id: listView
|
||||||
|
model: ListModel {}
|
||||||
|
delegate: Item {
|
||||||
|
implicitHeight: attachmentView.height + 8;
|
||||||
|
implicitWidth: attachmentView.width;
|
||||||
|
Attachment {
|
||||||
|
id: attachmentView
|
||||||
|
width: scrollView.width
|
||||||
|
attachment: root.attachments[index]
|
||||||
|
onDeleteAttachment: {
|
||||||
|
attachments.splice(index, 1);
|
||||||
|
listView.model.remove(index, 1);
|
||||||
|
}
|
||||||
|
onUpdateAttachment: MyAvatar.setAttachmentsVariant(attachments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCountChanged: MyAvatar.setAttachmentsVariant(attachments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: newAttachmentButton
|
||||||
|
anchors { left: parent.left; right: parent.right; bottom: buttonRow.top; margins: 8 }
|
||||||
|
text: "New Attachment"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
var template = {
|
||||||
|
modelUrl: "",
|
||||||
|
translation: { x: 0, y: 0, z: 0 },
|
||||||
|
rotation: { x: 0, y: 0, z: 0 },
|
||||||
|
scale: 1,
|
||||||
|
jointName: MyAvatar.jointNames[0],
|
||||||
|
soft: false
|
||||||
|
};
|
||||||
|
attachments.push(template);
|
||||||
|
listView.model.append({});
|
||||||
|
MyAvatar.setAttachmentsVariant(attachments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: buttonRow
|
||||||
|
spacing: 8
|
||||||
|
anchors { right: parent.right; bottom: parent.bottom; margins: 8 }
|
||||||
|
Button { action: cancelAction }
|
||||||
|
Button { action: okAction }
|
||||||
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
id: cancelAction
|
||||||
|
text: "Cancel"
|
||||||
|
onTriggered: {
|
||||||
|
MyAvatar.setAttachmentsVariant(originalAttachments);
|
||||||
|
root.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
id: okAction
|
||||||
|
text: "OK"
|
||||||
|
onTriggered: {
|
||||||
|
for (var i = 0; i < attachments.length; ++i) {
|
||||||
|
console.log("Attachment " + i + ": " + attachments[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
MyAvatar.setAttachmentsVariant(attachments);
|
||||||
|
root.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
128
interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml
Normal file
128
interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.XmlListModel 2.0
|
||||||
|
|
||||||
|
import "../../windows"
|
||||||
|
import "../../js/Utils.js" as Utils
|
||||||
|
import "../models"
|
||||||
|
|
||||||
|
ModalWindow {
|
||||||
|
id: root
|
||||||
|
resizable: true
|
||||||
|
width: 640
|
||||||
|
height: 480
|
||||||
|
|
||||||
|
property var result;
|
||||||
|
|
||||||
|
signal selected(var modelUrl);
|
||||||
|
signal canceled();
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "white"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors { fill: parent; margins: 8 }
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: filterEdit
|
||||||
|
anchors { left: parent.left; right: parent.right; top: parent.top }
|
||||||
|
placeholderText: "filter"
|
||||||
|
onTextChanged: tableView.model.filter = text
|
||||||
|
}
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
id: tableView
|
||||||
|
anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 }
|
||||||
|
model: S3Model{}
|
||||||
|
onCurrentRowChanged: {
|
||||||
|
if (currentRow == -1) {
|
||||||
|
root.result = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result = model.baseUrl + "/" + model.get(tableView.currentRow).key;
|
||||||
|
}
|
||||||
|
itemDelegate: Component {
|
||||||
|
Item {
|
||||||
|
clip: true
|
||||||
|
Text {
|
||||||
|
x: 3
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor
|
||||||
|
elide: styleData.elideMode
|
||||||
|
text: getText()
|
||||||
|
|
||||||
|
function getText() {
|
||||||
|
switch(styleData.column) {
|
||||||
|
case 1:
|
||||||
|
return Utils.formatSize(styleData.value)
|
||||||
|
default:
|
||||||
|
return styleData.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TableViewColumn {
|
||||||
|
role: "name"
|
||||||
|
title: "Name"
|
||||||
|
width: 200
|
||||||
|
}
|
||||||
|
TableViewColumn {
|
||||||
|
role: "size"
|
||||||
|
title: "Size"
|
||||||
|
width: 100
|
||||||
|
}
|
||||||
|
TableViewColumn {
|
||||||
|
role: "modified"
|
||||||
|
title: "Last Modified"
|
||||||
|
width: 200
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: tableView.model.status !== XmlListModel.Ready
|
||||||
|
color: "#7fffffff"
|
||||||
|
BusyIndicator {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 48; height: 48
|
||||||
|
running: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: buttonRow
|
||||||
|
anchors { right: parent.right; bottom: parent.bottom }
|
||||||
|
Button { action: acceptAction }
|
||||||
|
Button { action: cancelAction }
|
||||||
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
id: acceptAction
|
||||||
|
text: qsTr("OK")
|
||||||
|
enabled: root.result ? true : false
|
||||||
|
shortcut: Qt.Key_Return
|
||||||
|
onTriggered: {
|
||||||
|
root.selected(root.result);
|
||||||
|
root.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
id: cancelAction
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
shortcut: Qt.Key_Escape
|
||||||
|
onTriggered: {
|
||||||
|
root.canceled();
|
||||||
|
root.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
128
interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml
Normal file
128
interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.XmlListModel 2.0
|
||||||
|
|
||||||
|
import "../../windows"
|
||||||
|
import "../../js/Utils.js" as Utils
|
||||||
|
import "../models"
|
||||||
|
|
||||||
|
ModalWindow {
|
||||||
|
id: root
|
||||||
|
resizable: true
|
||||||
|
width: 640
|
||||||
|
height: 480
|
||||||
|
|
||||||
|
property var result;
|
||||||
|
|
||||||
|
signal selected(var modelUrl);
|
||||||
|
signal canceled();
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "white"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors { fill: parent; margins: 8 }
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: filterEdit
|
||||||
|
anchors { left: parent.left; right: parent.right; top: parent.top }
|
||||||
|
placeholderText: "filter"
|
||||||
|
onTextChanged: tableView.model.filter = text
|
||||||
|
}
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
id: tableView
|
||||||
|
anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 }
|
||||||
|
model: S3Model{}
|
||||||
|
onCurrentRowChanged: {
|
||||||
|
if (currentRow == -1) {
|
||||||
|
root.result = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result = model.baseUrl + "/" + model.get(tableView.currentRow).key;
|
||||||
|
}
|
||||||
|
itemDelegate: Component {
|
||||||
|
Item {
|
||||||
|
clip: true
|
||||||
|
Text {
|
||||||
|
x: 3
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor
|
||||||
|
elide: styleData.elideMode
|
||||||
|
text: getText()
|
||||||
|
|
||||||
|
function getText() {
|
||||||
|
switch(styleData.column) {
|
||||||
|
case 1:
|
||||||
|
return Utils.formatSize(styleData.value)
|
||||||
|
default:
|
||||||
|
return styleData.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TableViewColumn {
|
||||||
|
role: "name"
|
||||||
|
title: "Name"
|
||||||
|
width: 200
|
||||||
|
}
|
||||||
|
TableViewColumn {
|
||||||
|
role: "size"
|
||||||
|
title: "Size"
|
||||||
|
width: 100
|
||||||
|
}
|
||||||
|
TableViewColumn {
|
||||||
|
role: "modified"
|
||||||
|
title: "Last Modified"
|
||||||
|
width: 200
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: tableView.model.status !== XmlListModel.Ready
|
||||||
|
color: "#7fffffff"
|
||||||
|
BusyIndicator {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 48; height: 48
|
||||||
|
running: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: buttonRow
|
||||||
|
anchors { right: parent.right; bottom: parent.bottom }
|
||||||
|
Button { action: acceptAction }
|
||||||
|
Button { action: cancelAction }
|
||||||
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
id: acceptAction
|
||||||
|
text: qsTr("OK")
|
||||||
|
enabled: root.result ? true : false
|
||||||
|
shortcut: Qt.Key_Return
|
||||||
|
onTriggered: {
|
||||||
|
root.selected(root.result);
|
||||||
|
root.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
id: cancelAction
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
shortcut: Qt.Key_Escape
|
||||||
|
onTriggered: {
|
||||||
|
root.canceled();
|
||||||
|
root.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
164
interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
Normal file
164
interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
|
||||||
|
import "../../../windows"
|
||||||
|
import "."
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: column.height + 2 * 8
|
||||||
|
|
||||||
|
property var attachment;
|
||||||
|
|
||||||
|
signal deleteAttachment(var attachment);
|
||||||
|
signal updateAttachment();
|
||||||
|
property bool completed: false;
|
||||||
|
|
||||||
|
Rectangle { color: "white"; anchors.fill: parent; radius: 4 }
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
completed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
y: 8
|
||||||
|
id: column
|
||||||
|
anchors { left: parent.left; right: parent.right; margins: 8 }
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: modelChooserButton.height
|
||||||
|
anchors { left: parent.left; right: parent.right; }
|
||||||
|
Text { id: urlLabel; text: "Model URL:"; width: 80; anchors.verticalCenter: modelUrl.verticalCenter }
|
||||||
|
TextField {
|
||||||
|
id: modelUrl;
|
||||||
|
height: jointChooser.height;
|
||||||
|
anchors { left: urlLabel.right; leftMargin: 8; rightMargin: 8; right: modelChooserButton.left }
|
||||||
|
text: attachment ? attachment.modelUrl : ""
|
||||||
|
onTextChanged: {
|
||||||
|
if (completed && attachment && attachment.modelUrl !== text) {
|
||||||
|
attachment.modelUrl = text;
|
||||||
|
updateAttachment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: modelChooserButton;
|
||||||
|
text: "Choose";
|
||||||
|
anchors { right: parent.right; verticalCenter: modelUrl.verticalCenter }
|
||||||
|
Component {
|
||||||
|
id: modelBrowserBuiler;
|
||||||
|
ModelBrowserDialog {}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
var browser = modelBrowserBuiler.createObject(desktop);
|
||||||
|
browser.selected.connect(function(newModelUrl){
|
||||||
|
modelUrl.text = newModelUrl;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: jointChooser.height
|
||||||
|
anchors { left: parent.left; right: parent.right; }
|
||||||
|
Text {
|
||||||
|
id: jointLabel;
|
||||||
|
width: 80;
|
||||||
|
text: "Joint:";
|
||||||
|
anchors.verticalCenter: jointChooser.verticalCenter;
|
||||||
|
}
|
||||||
|
ComboBox {
|
||||||
|
id: jointChooser;
|
||||||
|
anchors { left: jointLabel.right; leftMargin: 8; right: parent.right }
|
||||||
|
model: MyAvatar.jointNames
|
||||||
|
currentIndex: attachment ? model.indexOf(attachment.jointName) : -1
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
if (completed && attachment && currentIndex != -1 && currentText && currentText !== attachment.jointName) {
|
||||||
|
attachment.jointName = currentText;
|
||||||
|
updateAttachment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: translation.height
|
||||||
|
anchors { left: parent.left; right: parent.right; }
|
||||||
|
Text { id: translationLabel; width: 80; text: "Translation:"; anchors.verticalCenter: translation.verticalCenter; }
|
||||||
|
Translation {
|
||||||
|
id: translation;
|
||||||
|
anchors { left: translationLabel.right; leftMargin: 8; right: parent.right }
|
||||||
|
vector: attachment ? attachment.translation : {x: 0, y: 0, z: 0};
|
||||||
|
onValueChanged: {
|
||||||
|
if (completed && attachment) {
|
||||||
|
attachment.translation = vector;
|
||||||
|
updateAttachment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: rotation.height
|
||||||
|
anchors { left: parent.left; right: parent.right; }
|
||||||
|
Text { id: rotationLabel; width: 80; text: "Rotation:"; anchors.verticalCenter: rotation.verticalCenter; }
|
||||||
|
Rotation {
|
||||||
|
id: rotation;
|
||||||
|
anchors { left: rotationLabel.right; leftMargin: 8; right: parent.right }
|
||||||
|
vector: attachment ? attachment.rotation : {x: 0, y: 0, z: 0};
|
||||||
|
onValueChanged: {
|
||||||
|
if (completed && attachment) {
|
||||||
|
attachment.rotation = vector;
|
||||||
|
updateAttachment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: scaleSpinner.height
|
||||||
|
anchors { left: parent.left; right: parent.right; }
|
||||||
|
Text { id: scaleLabel; width: 80; text: "Scale:"; anchors.verticalCenter: scale.verticalCenter; }
|
||||||
|
SpinBox {
|
||||||
|
id: scaleSpinner;
|
||||||
|
anchors { left: scaleLabel.right; leftMargin: 8; right: parent.right }
|
||||||
|
decimals: 1;
|
||||||
|
minimumValue: 0.1
|
||||||
|
maximumValue: 10
|
||||||
|
stepSize: 0.1;
|
||||||
|
value: attachment ? attachment.scale : 1.0
|
||||||
|
onValueChanged: {
|
||||||
|
if (completed && attachment && attachment.scale !== value) {
|
||||||
|
attachment.scale = value;
|
||||||
|
updateAttachment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: soft.height
|
||||||
|
anchors { left: parent.left; right: parent.right; }
|
||||||
|
Text { id: softLabel; width: 80; text: "Is soft:"; anchors.verticalCenter: soft.verticalCenter; }
|
||||||
|
CheckBox {
|
||||||
|
id: soft;
|
||||||
|
anchors { left: softLabel.right; leftMargin: 8; right: parent.right }
|
||||||
|
checked: attachment ? attachment.soft : false
|
||||||
|
onCheckedChanged: {
|
||||||
|
if (completed && attachment && attachment.soft !== checked) {
|
||||||
|
attachment.soft = checked;
|
||||||
|
updateAttachment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
anchors { left: parent.left; right: parent.right; }
|
||||||
|
text: "Delete"
|
||||||
|
onClicked: deleteAttachment(root.attachment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import "."
|
||||||
|
|
||||||
|
Vector3 {
|
||||||
|
decimals: 1;
|
||||||
|
stepSize: 1;
|
||||||
|
maximumValue: 180
|
||||||
|
minimumValue: -180
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import "."
|
||||||
|
|
||||||
|
Vector3 {
|
||||||
|
decimals: 2;
|
||||||
|
stepSize: 0.01;
|
||||||
|
maximumValue: 10
|
||||||
|
minimumValue: -10
|
||||||
|
}
|
||||||
|
|
71
interface/resources/qml/hifi/dialogs/attachments/Vector3.qml
Normal file
71
interface/resources/qml/hifi/dialogs/attachments/Vector3.qml
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
implicitHeight: xspinner.height
|
||||||
|
readonly property real spacing: 8
|
||||||
|
property real spinboxWidth: (width / 3) - spacing
|
||||||
|
property var vector;
|
||||||
|
property real decimals: 0
|
||||||
|
property real stepSize: 1
|
||||||
|
property real maximumValue: 99
|
||||||
|
property real minimumValue: 0
|
||||||
|
|
||||||
|
signal valueChanged();
|
||||||
|
|
||||||
|
SpinBox {
|
||||||
|
id: xspinner
|
||||||
|
width: root.spinboxWidth
|
||||||
|
anchors { left: parent.left }
|
||||||
|
value: root.vector.x
|
||||||
|
|
||||||
|
decimals: root.decimals
|
||||||
|
stepSize: root.stepSize
|
||||||
|
maximumValue: root.maximumValue
|
||||||
|
minimumValue: root.minimumValue
|
||||||
|
onValueChanged: {
|
||||||
|
if (value !== vector.x) {
|
||||||
|
vector.x = value
|
||||||
|
root.valueChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpinBox {
|
||||||
|
id: yspinner
|
||||||
|
width: root.spinboxWidth
|
||||||
|
anchors { horizontalCenter: parent.horizontalCenter }
|
||||||
|
value: root.vector.y
|
||||||
|
|
||||||
|
decimals: root.decimals
|
||||||
|
stepSize: root.stepSize
|
||||||
|
maximumValue: root.maximumValue
|
||||||
|
minimumValue: root.minimumValue
|
||||||
|
onValueChanged: {
|
||||||
|
if (value !== vector.y) {
|
||||||
|
vector.y = value
|
||||||
|
root.valueChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpinBox {
|
||||||
|
id: zspinner
|
||||||
|
width: root.spinboxWidth
|
||||||
|
anchors { right: parent.right; }
|
||||||
|
value: root.vector.z
|
||||||
|
|
||||||
|
decimals: root.decimals
|
||||||
|
stepSize: root.stepSize
|
||||||
|
maximumValue: root.maximumValue
|
||||||
|
minimumValue: root.minimumValue
|
||||||
|
onValueChanged: {
|
||||||
|
if (value !== vector.z) {
|
||||||
|
vector.z = value
|
||||||
|
root.valueChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
41
interface/resources/qml/hifi/models/S3Model.qml
Normal file
41
interface/resources/qml/hifi/models/S3Model.qml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.XmlListModel 2.0
|
||||||
|
|
||||||
|
//http://s3.amazonaws.com/hifi-public?prefix=models/attachments
|
||||||
|
/*
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||||
|
<Contents>
|
||||||
|
<Key>models/attachments/guitar.fst</Key>
|
||||||
|
<LastModified>2015-11-10T00:28:22.000Z</LastModified>
|
||||||
|
<ETag>"236c00c4802ba9c2605cabd5601d138e"</ETag>
|
||||||
|
<Size>2992</Size>
|
||||||
|
<StorageClass>STANDARD</StorageClass>
|
||||||
|
</Contents>
|
||||||
|
</ListBucketResult>
|
||||||
|
*/
|
||||||
|
|
||||||
|
// FIXME how to deal with truncated results? Store the marker?
|
||||||
|
XmlListModel {
|
||||||
|
id: xmlModel
|
||||||
|
property string prefix: "models/attachments/"
|
||||||
|
property string extension: "fst"
|
||||||
|
property string filter;
|
||||||
|
|
||||||
|
readonly property string realPrefix: prefix.match('.*/$') ? prefix : (prefix + "/")
|
||||||
|
readonly property string nameRegex: realPrefix + (filter ? (".*" + filter) : "") + ".*\." + extension
|
||||||
|
readonly property string nameQuery: "Key/substring-before(substring-after(string(), '" + prefix + "'), '." + extension + "')"
|
||||||
|
readonly property string baseUrl: "http://s3.amazonaws.com/hifi-public"
|
||||||
|
|
||||||
|
// FIXME need to urlencode prefix?
|
||||||
|
source: baseUrl + "?prefix=" + realPrefix
|
||||||
|
query: "/ListBucketResult/Contents[matches(Key, '" + nameRegex + "')]"
|
||||||
|
namespaceDeclarations: "declare default element namespace 'http://s3.amazonaws.com/doc/2006-03-01/';"
|
||||||
|
|
||||||
|
XmlRole { name: "name"; query: nameQuery }
|
||||||
|
XmlRole { name: "size"; query: "Size/string()" }
|
||||||
|
XmlRole { name: "tag"; query: "ETag/string()" }
|
||||||
|
XmlRole { name: "modified"; query: "LastModified/string()" }
|
||||||
|
XmlRole { name: "key"; query: "Key/string()" }
|
||||||
|
}
|
||||||
|
|
|
@ -14,3 +14,17 @@ function randomPosition(min, max) {
|
||||||
Math.random() * (max.x - min.x),
|
Math.random() * (max.x - min.x),
|
||||||
Math.random() * (max.y - min.y));
|
Math.random() * (max.y - min.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatSize(size) {
|
||||||
|
var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ];
|
||||||
|
var suffixIndex = 0
|
||||||
|
while ((size / 1024.0) > 1.1) {
|
||||||
|
size /= 1024.0;
|
||||||
|
++suffixIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = Math.round(size*1000)/1000;
|
||||||
|
size = size.toLocaleString()
|
||||||
|
|
||||||
|
return size + " " + suffixes[suffixIndex];
|
||||||
|
}
|
||||||
|
|
|
@ -1186,6 +1186,7 @@ void Application::initializeUi() {
|
||||||
UpdateDialog::registerType();
|
UpdateDialog::registerType();
|
||||||
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
||||||
|
|
||||||
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
offscreenUi->create(_offscreenContext->getContext());
|
offscreenUi->create(_offscreenContext->getContext());
|
||||||
offscreenUi->setProxyWindow(_window->windowHandle());
|
offscreenUi->setProxyWindow(_window->windowHandle());
|
||||||
|
@ -1842,6 +1843,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
|
|
||||||
case Qt::Key_X:
|
case Qt::Key_X:
|
||||||
if (isShifted && isMeta) {
|
if (isShifted && isMeta) {
|
||||||
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
|
offscreenUi->getRootContext()->engine()->clearComponentCache();
|
||||||
|
offscreenUi->load("hifi/dialogs/AttachmentsDialog.qml");
|
||||||
|
// OffscreenUi::information("Debugging", "Component cache cleared");
|
||||||
// placeholder for dialogs being converted to QML.
|
// placeholder for dialogs being converted to QML.
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <QtNetwork/QNetworkReply>
|
#include <QtNetwork/QNetworkReply>
|
||||||
#include <QtNetwork/QNetworkRequest>
|
#include <QtNetwork/QNetworkRequest>
|
||||||
|
|
||||||
|
#include <QVariantGLM.h>
|
||||||
#include <Transform.h>
|
#include <Transform.h>
|
||||||
#include <NetworkAccessManager.h>
|
#include <NetworkAccessManager.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
|
@ -1673,3 +1674,65 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const
|
||||||
assert(false);
|
assert(false);
|
||||||
return glm::vec3();
|
return glm::vec3();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariant AttachmentData::toVariant() const {
|
||||||
|
QVariantMap result;
|
||||||
|
result["modelUrl"] = modelURL;
|
||||||
|
result["jointName"] = jointName;
|
||||||
|
result["translation"] = glmToQMap(translation);
|
||||||
|
result["rotation"] = glmToQMap(glm::degrees(safeEulerAngles(rotation)));
|
||||||
|
result["scale"] = scale;
|
||||||
|
result["soft"] = isSoft;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 variantToVec3(const QVariant& var) {
|
||||||
|
auto map = var.toMap();
|
||||||
|
glm::vec3 result;
|
||||||
|
result.x = map["x"].toFloat();
|
||||||
|
result.y = map["y"].toFloat();
|
||||||
|
result.z = map["z"].toFloat();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachmentData::fromVariant(const QVariant& variant) {
|
||||||
|
auto map = variant.toMap();
|
||||||
|
if (map.contains("modelUrl")) {
|
||||||
|
auto urlString = map["modelUrl"].toString();
|
||||||
|
modelURL = urlString;
|
||||||
|
}
|
||||||
|
if (map.contains("jointName")) {
|
||||||
|
jointName = map["jointName"].toString();
|
||||||
|
}
|
||||||
|
if (map.contains("translation")) {
|
||||||
|
translation = variantToVec3(map["translation"]);
|
||||||
|
}
|
||||||
|
if (map.contains("rotation")) {
|
||||||
|
rotation = glm::quat(glm::radians(variantToVec3(map["rotation"])));
|
||||||
|
}
|
||||||
|
if (map.contains("scale")) {
|
||||||
|
scale = map["scale"].toFloat();
|
||||||
|
}
|
||||||
|
if (map.contains("soft")) {
|
||||||
|
isSoft = map["soft"].toBool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList AvatarData::getAttachmentsVariant() const {
|
||||||
|
QVariantList result;
|
||||||
|
for (const auto& attachment : getAttachmentData()) {
|
||||||
|
result.append(attachment.toVariant());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarData::setAttachmentsVariant(const QVariantList& variant) {
|
||||||
|
QVector<AttachmentData> newAttachments;
|
||||||
|
newAttachments.reserve(variant.size());
|
||||||
|
for (const auto& attachmentVar : variant) {
|
||||||
|
AttachmentData attachment;
|
||||||
|
attachment.fromVariant(attachmentVar);
|
||||||
|
newAttachments.append(attachment);
|
||||||
|
}
|
||||||
|
setAttachmentData(newAttachments);
|
||||||
|
}
|
||||||
|
|
|
@ -277,6 +277,9 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); }
|
Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); }
|
||||||
|
|
||||||
|
Q_INVOKABLE QVariantList getAttachmentsVariant() const;
|
||||||
|
Q_INVOKABLE void setAttachmentsVariant(const QVariantList& variant);
|
||||||
|
|
||||||
void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; }
|
void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; }
|
||||||
|
|
||||||
// key state
|
// key state
|
||||||
|
@ -448,6 +451,9 @@ public:
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
void fromJson(const QJsonObject& json);
|
void fromJson(const QJsonObject& json);
|
||||||
|
|
||||||
|
QVariant toVariant() const;
|
||||||
|
void fromVariant(const QVariant& variant);
|
||||||
};
|
};
|
||||||
|
|
||||||
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment);
|
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment);
|
||||||
|
|
|
@ -959,7 +959,7 @@ void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* p
|
||||||
bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) {
|
bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) {
|
||||||
QSharedMemory sharedMem(key);
|
QSharedMemory sharedMem(key);
|
||||||
if (!sharedMem.attach(QSharedMemory::ReadOnly)) {
|
if (!sharedMem.attach(QSharedMemory::ReadOnly)) {
|
||||||
qWarning() << "Could not attach to shared memory at key" << key;
|
qCWarning(networking) << "Could not attach to shared memory at key" << key;
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
sharedMem.lock();
|
sharedMem.lock();
|
||||||
|
|
|
@ -7,6 +7,7 @@ import "../../../interface/resources/qml"
|
||||||
import "../../../interface/resources/qml/windows"
|
import "../../../interface/resources/qml/windows"
|
||||||
import "../../../interface/resources/qml/dialogs"
|
import "../../../interface/resources/qml/dialogs"
|
||||||
import "../../../interface/resources/qml/hifi"
|
import "../../../interface/resources/qml/hifi"
|
||||||
|
import "../../../interface/resources/qml/hifi/dialogs"
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: appWindow
|
id: appWindow
|
||||||
|
@ -196,4 +197,15 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Action {
|
||||||
|
text: "Open Browser"
|
||||||
|
shortcut: "Ctrl+Shift+X"
|
||||||
|
onTriggered: {
|
||||||
|
builder.createObject(desktop);
|
||||||
|
}
|
||||||
|
property var builder: Component {
|
||||||
|
ModelBrowserDialog{}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue