mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 07:17:43 +02:00
Merge pull request #8179 from SamGondelman/snapshotFix3
Various snapshot improvements
This commit is contained in:
commit
b8d79ec263
10 changed files with 99 additions and 356 deletions
|
@ -7,7 +7,7 @@ PreferencesDialog {
|
||||||
id: root
|
id: root
|
||||||
objectName: "AvatarPreferencesDialog"
|
objectName: "AvatarPreferencesDialog"
|
||||||
title: "Avatar Settings"
|
title: "Avatar Settings"
|
||||||
showCategories: [ "Avatar Basics", "Avatar Tuning", "Avatar Camera" ]
|
showCategories: [ "Avatar Basics", "Snapshots", "Avatar Tuning", "Avatar Camera" ]
|
||||||
property var settings: Settings {
|
property var settings: Settings {
|
||||||
category: root.objectName
|
category: root.objectName
|
||||||
property alias x: root.x
|
property alias x: root.x
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
import QtQuick 2.5
|
|
||||||
import QtQuick.Controls 1.4
|
|
||||||
import QtQuick.Controls.Styles 1.4
|
|
||||||
import QtQuick.XmlListModel 2.0
|
|
||||||
|
|
||||||
import "../../windows"
|
|
||||||
import "../../js/Utils.js" as Utils
|
|
||||||
import "../models"
|
|
||||||
|
|
||||||
Window {
|
|
||||||
id: root
|
|
||||||
resizable: true
|
|
||||||
width: 516
|
|
||||||
height: 616
|
|
||||||
minSize: Qt.vector2d(500, 600);
|
|
||||||
maxSize: Qt.vector2d(1000, 800);
|
|
||||||
|
|
||||||
property alias source: image.source
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: "white"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
anchors { fill: parent; margins: 8 }
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: image
|
|
||||||
anchors { top: parent.top; left: parent.left; right: parent.right; bottom: notesLabel.top; bottomMargin: 8 }
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: notesLabel
|
|
||||||
anchors { left: parent.left; bottom: notes.top; bottomMargin: 8; }
|
|
||||||
text: "Notes about this image"
|
|
||||||
font.pointSize: 14
|
|
||||||
font.bold: true
|
|
||||||
color: "#666"
|
|
||||||
}
|
|
||||||
|
|
||||||
TextArea {
|
|
||||||
id: notes
|
|
||||||
anchors { left: parent.left; bottom: parent.bottom; right: shareButton.left; rightMargin: 8 }
|
|
||||||
height: 60
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: shareButton
|
|
||||||
anchors { verticalCenter: notes.verticalCenter; right: parent.right; }
|
|
||||||
width: 120; height: 50
|
|
||||||
text: "Share"
|
|
||||||
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
implicitWidth: 120
|
|
||||||
implicitHeight: 50
|
|
||||||
border.width: control.activeFocus ? 2 : 1
|
|
||||||
color: "#333"
|
|
||||||
radius: 9
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
color: shareButton.enabled ? "white" : "gray"
|
|
||||||
font.pixelSize: 18
|
|
||||||
font.bold: true
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
anchors.fill: parent
|
|
||||||
text: shareButton.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
enabled = false;
|
|
||||||
uploadTimer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: uploadTimer
|
|
||||||
running: false
|
|
||||||
interval: 5
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
var uploaded = SnapshotUploader.uploadSnapshot(root.source.toString())
|
|
||||||
console.log("Uploaded result " + uploaded)
|
|
||||||
if (!uploaded) {
|
|
||||||
console.log("Upload failed ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Action {
|
|
||||||
id: shareAction
|
|
||||||
text: qsTr("OK")
|
|
||||||
enabled: root.result ? true : false
|
|
||||||
shortcut: Qt.Key_Return
|
|
||||||
onTriggered: {
|
|
||||||
root.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Action {
|
|
||||||
id: cancelAction
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
shortcut: Qt.Key_Escape
|
|
||||||
onTriggered: {
|
|
||||||
root.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1525,7 +1525,6 @@ void Application::initializeUi() {
|
||||||
|
|
||||||
// For some reason there is already an "Application" object in the QML context,
|
// For some reason there is already an "Application" object in the QML context,
|
||||||
// though I can't find it. Hence, "ApplicationInterface"
|
// though I can't find it. Hence, "ApplicationInterface"
|
||||||
rootContext->setContextProperty("SnapshotUploader", new SnapshotUploader());
|
|
||||||
rootContext->setContextProperty("ApplicationInterface", this);
|
rootContext->setContextProperty("ApplicationInterface", this);
|
||||||
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
|
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
|
||||||
rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||||
|
@ -5046,16 +5045,9 @@ void Application::takeSnapshot() {
|
||||||
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||||
player->play();
|
player->play();
|
||||||
|
|
||||||
QString fileName = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot());
|
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot());
|
||||||
|
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
emit DependencyManager::get<WindowScriptingInterface>()->snapshotTaken(path);
|
||||||
if (!accountManager->isLoggedIn()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DependencyManager::get<OffscreenUi>()->load("hifi/dialogs/SnapshotShareDialog.qml", [=](QQmlContext*, QObject* dialog) {
|
|
||||||
dialog->setProperty("source", QUrl::fromLocalFile(fileName));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Application::getRenderResolutionScale() const {
|
float Application::getRenderResolutionScale() const {
|
||||||
|
|
|
@ -46,6 +46,7 @@ signals:
|
||||||
void domainChanged(const QString& domainHostname);
|
void domainChanged(const QString& domainHostname);
|
||||||
void svoImportRequested(const QString& url);
|
void svoImportRequested(const QString& url);
|
||||||
void domainConnectionRefused(const QString& reasonMessage, int reasonCode);
|
void domainConnectionRefused(const QString& reasonMessage, int reasonCode);
|
||||||
|
void snapshotTaken(const QString& path);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height);
|
WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height);
|
||||||
|
|
|
@ -35,7 +35,7 @@ void setupPreferences() {
|
||||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||||
static const QString AVATAR_BASICS { "Avatar Basics" };
|
static const QString AVATAR_BASICS { "Avatar Basics" };
|
||||||
{
|
{
|
||||||
auto getter = [=]()->QString {return myAvatar->getDisplayName(); };
|
auto getter = [=]()->QString { return myAvatar->getDisplayName(); };
|
||||||
auto setter = [=](const QString& value) { myAvatar->setDisplayName(value); };
|
auto setter = [=](const QString& value) { myAvatar->setDisplayName(value); };
|
||||||
auto preference = new EditPreference(AVATAR_BASICS, "Avatar display name (optional)", getter, setter);
|
auto preference = new EditPreference(AVATAR_BASICS, "Avatar display name (optional)", getter, setter);
|
||||||
preference->setPlaceholderText("Not showing a name");
|
preference->setPlaceholderText("Not showing a name");
|
||||||
|
@ -43,7 +43,7 @@ void setupPreferences() {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto getter = [=]()->QString {return myAvatar->getCollisionSoundURL(); };
|
auto getter = [=]()->QString { return myAvatar->getCollisionSoundURL(); };
|
||||||
auto setter = [=](const QString& value) { myAvatar->setCollisionSoundURL(value); };
|
auto setter = [=](const QString& value) { myAvatar->setCollisionSoundURL(value); };
|
||||||
auto preference = new EditPreference(AVATAR_BASICS, "Avatar collision sound URL (optional)", getter, setter);
|
auto preference = new EditPreference(AVATAR_BASICS, "Avatar collision sound URL (optional)", getter, setter);
|
||||||
preference->setPlaceholderText("Enter the URL of a sound to play when you bump into something");
|
preference->setPlaceholderText("Enter the URL of a sound to play when you bump into something");
|
||||||
|
@ -56,20 +56,24 @@ void setupPreferences() {
|
||||||
auto preference = new AvatarPreference(AVATAR_BASICS, "Appearance", getter, setter);
|
auto preference = new AvatarPreference(AVATAR_BASICS, "Appearance", getter, setter);
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto getter = [=]()->bool {return myAvatar->getSnapTurn(); };
|
auto getter = [=]()->bool { return myAvatar->getSnapTurn(); };
|
||||||
auto setter = [=](bool value) { myAvatar->setSnapTurn(value); };
|
auto setter = [=](bool value) { myAvatar->setSnapTurn(value); };
|
||||||
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Snap turn when in HMD", getter, setter));
|
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Snap turn when in HMD", getter, setter));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = [=]()->bool {return myAvatar->getClearOverlayWhenMoving(); };
|
auto getter = [=]()->bool { return myAvatar->getClearOverlayWhenMoving(); };
|
||||||
auto setter = [=](bool value) { myAvatar->setClearOverlayWhenMoving(value); };
|
auto setter = [=](bool value) { myAvatar->setClearOverlayWhenMoving(value); };
|
||||||
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Clear overlays when moving", getter, setter));
|
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Clear overlays when moving", getter, setter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Snapshots
|
||||||
|
static const QString SNAPSHOTS { "Snapshots" };
|
||||||
{
|
{
|
||||||
auto getter = []()->QString { return Snapshot::snapshotsLocation.get(); };
|
auto getter = []()->QString { return Snapshot::snapshotsLocation.get(); };
|
||||||
auto setter = [](const QString& value) { Snapshot::snapshotsLocation.set(value); };
|
auto setter = [](const QString& value) { Snapshot::snapshotsLocation.set(value); };
|
||||||
auto preference = new BrowsePreference("Snapshots", "Put my snapshots here", getter, setter);
|
auto preference = new BrowsePreference(SNAPSHOTS, "Put my snapshots here", getter, setter);
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +89,7 @@ void setupPreferences() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
{
|
{
|
||||||
auto getter = []()->bool {return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); };
|
auto getter = []()->bool { return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); };
|
||||||
auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); };
|
auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); };
|
||||||
preferences->addPreference(new CheckPreference("Privacy", "Send data", getter, setter));
|
preferences->addPreference(new CheckPreference("Privacy", "Send data", getter, setter));
|
||||||
}
|
}
|
||||||
|
@ -184,7 +188,7 @@ void setupPreferences() {
|
||||||
|
|
||||||
static const QString AUDIO("Audio");
|
static const QString AUDIO("Audio");
|
||||||
{
|
{
|
||||||
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getDynamicJitterBuffers(); };
|
auto getter = []()->bool { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getDynamicJitterBuffers(); };
|
||||||
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setDynamicJitterBuffers(value); };
|
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setDynamicJitterBuffers(value); };
|
||||||
preferences->addPreference(new CheckPreference(AUDIO, "Enable dynamic jitter buffers", getter, setter));
|
preferences->addPreference(new CheckPreference(AUDIO, "Enable dynamic jitter buffers", getter, setter));
|
||||||
}
|
}
|
||||||
|
@ -207,7 +211,7 @@ void setupPreferences() {
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getUseStDevForJitterCalc(); };
|
auto getter = []()->bool { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getUseStDevForJitterCalc(); };
|
||||||
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setUseStDevForJitterCalc(value); };
|
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setUseStDevForJitterCalc(value); };
|
||||||
preferences->addPreference(new CheckPreference(AUDIO, "Use standard deviation for dynamic jitter calc", getter, setter));
|
preferences->addPreference(new CheckPreference(AUDIO, "Use standard deviation for dynamic jitter calc", getter, setter));
|
||||||
}
|
}
|
||||||
|
@ -236,7 +240,7 @@ void setupPreferences() {
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getRepetitionWithFade(); };
|
auto getter = []()->bool { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getRepetitionWithFade(); };
|
||||||
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setRepetitionWithFade(value); };
|
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setRepetitionWithFade(value); };
|
||||||
preferences->addPreference(new CheckPreference(AUDIO, "Repetition with fade", getter, setter));
|
preferences->addPreference(new CheckPreference(AUDIO, "Repetition with fade", getter, setter));
|
||||||
}
|
}
|
||||||
|
@ -250,7 +254,7 @@ void setupPreferences() {
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getOutputStarveDetectionEnabled(); };
|
auto getter = []()->bool { return DependencyManager::get<AudioClient>()->getOutputStarveDetectionEnabled(); };
|
||||||
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->setOutputStarveDetectionEnabled(value); };
|
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->setOutputStarveDetectionEnabled(value); };
|
||||||
auto preference = new CheckPreference(AUDIO, "Output starve detection (automatic buffer size increase)", getter, setter);
|
auto preference = new CheckPreference(AUDIO, "Output starve detection (automatic buffer size increase)", getter, setter);
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
|
|
|
@ -44,6 +44,7 @@ const QString URL = "highfidelity_url";
|
||||||
|
|
||||||
Setting::Handle<QString> Snapshot::snapshotsLocation("snapshotsLocation",
|
Setting::Handle<QString> Snapshot::snapshotsLocation("snapshotsLocation",
|
||||||
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
|
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
|
||||||
|
Setting::Handle<bool> Snapshot::hasSetSnapshotsLocation("hasSetSnapshotsLocation", false);
|
||||||
|
|
||||||
SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
|
SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
|
||||||
|
|
||||||
|
@ -103,7 +104,14 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) {
|
||||||
const int IMAGE_QUALITY = 100;
|
const int IMAGE_QUALITY = 100;
|
||||||
|
|
||||||
if (!isTemporary) {
|
if (!isTemporary) {
|
||||||
QString snapshotFullPath = snapshotsLocation.get();
|
QString snapshotFullPath;
|
||||||
|
if (!hasSetSnapshotsLocation.get()) {
|
||||||
|
snapshotFullPath = QFileDialog::getExistingDirectory(nullptr, "Choose Snapshots Directory", snapshotsLocation.get());
|
||||||
|
hasSetSnapshotsLocation.set(true);
|
||||||
|
snapshotsLocation.set(snapshotFullPath);
|
||||||
|
} else {
|
||||||
|
snapshotFullPath = snapshotsLocation.get();
|
||||||
|
}
|
||||||
|
|
||||||
if (!snapshotFullPath.endsWith(QDir::separator())) {
|
if (!snapshotFullPath.endsWith(QDir::separator())) {
|
||||||
snapshotFullPath.append(QDir::separator());
|
snapshotFullPath.append(QDir::separator());
|
||||||
|
@ -133,118 +141,3 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) {
|
||||||
return imageTempFile;
|
return imageTempFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString FORUM_URL = "https://alphas.highfidelity.io";
|
|
||||||
const QString FORUM_UPLOADS_URL = FORUM_URL + "/uploads";
|
|
||||||
const QString FORUM_POST_URL = FORUM_URL + "/posts";
|
|
||||||
const QString FORUM_REPLY_TO_TOPIC = "244";
|
|
||||||
const QString FORUM_POST_TEMPLATE = "<img src='%1'/><p>%2</p>";
|
|
||||||
const QString SHARE_DEFAULT_ERROR = "The server isn't responding. Please try again in a few minutes.";
|
|
||||||
const QString SUCCESS_LABEL_TEMPLATE = "Success!!! Go check out your image ...<br/><a style='color:#333;text-decoration:none' href='%1'>%1</a>";
|
|
||||||
|
|
||||||
|
|
||||||
QString SnapshotUploader::uploadSnapshot(const QUrl& fileUrl) {
|
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
|
||||||
if (accountManager->getAccountInfo().getDiscourseApiKey().isEmpty()) {
|
|
||||||
OffscreenUi::warning(nullptr, "", "Your Discourse API key is missing, you cannot share snapshots. Please try to relog.");
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QHttpPart apiKeyPart;
|
|
||||||
apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"api_key\""));
|
|
||||||
apiKeyPart.setBody(accountManager->getAccountInfo().getDiscourseApiKey().toLatin1());
|
|
||||||
|
|
||||||
QString filename = fileUrl.toLocalFile();
|
|
||||||
qDebug() << filename;
|
|
||||||
QFile* file = new QFile(filename);
|
|
||||||
Q_ASSERT(file->exists());
|
|
||||||
file->open(QIODevice::ReadOnly);
|
|
||||||
|
|
||||||
QHttpPart imagePart;
|
|
||||||
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
|
|
||||||
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
|
|
||||||
QVariant("form-data; name=\"file\"; filename=\"" + file->fileName() + "\""));
|
|
||||||
imagePart.setBodyDevice(file);
|
|
||||||
|
|
||||||
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
|
||||||
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
|
|
||||||
multiPart->append(apiKeyPart);
|
|
||||||
multiPart->append(imagePart);
|
|
||||||
|
|
||||||
QUrl url(FORUM_UPLOADS_URL);
|
|
||||||
QNetworkRequest request(url);
|
|
||||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
|
||||||
|
|
||||||
QString result;
|
|
||||||
QEventLoop loop;
|
|
||||||
|
|
||||||
QSharedPointer<QNetworkReply> reply(NetworkAccessManager::getInstance().post(request, multiPart));
|
|
||||||
QObject::connect(reply.data(), &QNetworkReply::finished, [&] {
|
|
||||||
loop.quit();
|
|
||||||
|
|
||||||
qDebug() << reply->errorString();
|
|
||||||
for (const auto& header : reply->rawHeaderList()) {
|
|
||||||
qDebug() << "Header " << QString(header);
|
|
||||||
}
|
|
||||||
auto replyResult = reply->readAll();
|
|
||||||
qDebug() << QString(replyResult);
|
|
||||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(replyResult);
|
|
||||||
const QJsonObject& responseObject = jsonResponse.object();
|
|
||||||
if (!responseObject.contains("url")) {
|
|
||||||
OffscreenUi::warning(this, "", SHARE_DEFAULT_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
result = responseObject["url"].toString();
|
|
||||||
});
|
|
||||||
loop.exec();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SnapshotUploader::sendForumPost(const QString& snapshotPath, const QString& notes) {
|
|
||||||
// post to Discourse forum
|
|
||||||
QNetworkRequest request;
|
|
||||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
|
||||||
QUrl forumUrl(FORUM_POST_URL);
|
|
||||||
|
|
||||||
QUrlQuery query;
|
|
||||||
query.addQueryItem("api_key", DependencyManager::get<AccountManager>()->getAccountInfo().getDiscourseApiKey());
|
|
||||||
query.addQueryItem("topic_id", FORUM_REPLY_TO_TOPIC);
|
|
||||||
query.addQueryItem("raw", FORUM_POST_TEMPLATE.arg(snapshotPath, notes));
|
|
||||||
forumUrl.setQuery(query);
|
|
||||||
|
|
||||||
QByteArray postData = forumUrl.toEncoded(QUrl::RemoveFragment);
|
|
||||||
request.setUrl(forumUrl);
|
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
|
||||||
|
|
||||||
QNetworkReply* requestReply = NetworkAccessManager::getInstance().post(request, postData);
|
|
||||||
|
|
||||||
QEventLoop loop;
|
|
||||||
QString result;
|
|
||||||
connect(requestReply, &QNetworkReply::finished, [&] {
|
|
||||||
loop.quit();
|
|
||||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
|
||||||
requestReply->deleteLater();
|
|
||||||
const QJsonObject& responseObject = jsonResponse.object();
|
|
||||||
|
|
||||||
if (!responseObject.contains("id")) {
|
|
||||||
QString errorMessage(SHARE_DEFAULT_ERROR);
|
|
||||||
if (responseObject.contains("errors")) {
|
|
||||||
QJsonArray errorArray = responseObject["errors"].toArray();
|
|
||||||
if (!errorArray.first().toString().isEmpty()) {
|
|
||||||
errorMessage = errorArray.first().toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OffscreenUi::warning(this, "", errorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString urlTemplate = "%1/t/%2/%3/%4";
|
|
||||||
result = urlTemplate.arg(FORUM_URL,
|
|
||||||
responseObject["topic_slug"].toString(),
|
|
||||||
QString::number(responseObject["topic_id"].toDouble()),
|
|
||||||
QString::number(responseObject["post_number"].toDouble()));
|
|
||||||
});
|
|
||||||
loop.exec();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -39,16 +39,9 @@ public:
|
||||||
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
|
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
|
||||||
|
|
||||||
static Setting::Handle<QString> snapshotsLocation;
|
static Setting::Handle<QString> snapshotsLocation;
|
||||||
|
static Setting::Handle<bool> hasSetSnapshotsLocation;
|
||||||
private:
|
private:
|
||||||
static QFile* savedFileForSnapshot(QImage & image, bool isTemporary);
|
static QFile* savedFileForSnapshot(QImage & image, bool isTemporary);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SnapshotUploader : public QObject{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
SnapshotUploader(QObject* parent = nullptr) : QObject(parent) {}
|
|
||||||
Q_INVOKABLE QString uploadSnapshot(const QUrl& fileUrl);
|
|
||||||
Q_INVOKABLE QString sendForumPost(const QString& snapshotPath, const QString& notes);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_Snapshot_h
|
#endif // hifi_Snapshot_h
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
//
|
|
||||||
// SnapshotShareDialog.cpp
|
|
||||||
// interface/src/ui
|
|
||||||
//
|
|
||||||
// Created by Stojce Slavkovski on 2/16/14.
|
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
|
|
||||||
#include <OffscreenUi.h>
|
|
||||||
|
|
||||||
const int NARROW_SNAPSHOT_DIALOG_SIZE = 500;
|
|
||||||
const int WIDE_SNAPSHOT_DIALOG_WIDTH = 650;
|
|
||||||
const int SUCCESS_LABEL_HEIGHT = 140;
|
|
||||||
|
|
||||||
const QString SHARE_BUTTON_STYLE = "border-width:0;border-radius:9px;border-radius:9px;font-family:Arial;font-size:18px;"
|
|
||||||
"font-weight:100;color:#FFFFFF;width: 120px;height: 50px;";
|
|
||||||
const QString SHARE_BUTTON_ENABLED_STYLE = "background-color: #333;";
|
|
||||||
const QString SHARE_BUTTON_DISABLED_STYLE = "background-color: #999;";
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
|
|
||||||
|
|
||||||
SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) :
|
|
||||||
QDialog(parent),
|
|
||||||
_fileName(fileName)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
_ui.snapshotWidget->setPixmap(snaphsotPixmap);
|
|
||||||
_ui.snapshotWidget->adjustSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SnapshotShareDialog::accept() {
|
|
||||||
// prevent multiple clicks on share button
|
|
||||||
_ui.shareButton->setEnabled(false);
|
|
||||||
// gray out share button
|
|
||||||
_ui.shareButton->setStyleSheet(SHARE_BUTTON_STYLE + SHARE_BUTTON_DISABLED_STYLE);
|
|
||||||
uploadSnapshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -50,9 +50,11 @@ void KeyboardMouseDevice::InputDevice::focusOutEvent() {
|
||||||
|
|
||||||
void KeyboardMouseDevice::keyPressEvent(QKeyEvent* event) {
|
void KeyboardMouseDevice::keyPressEvent(QKeyEvent* event) {
|
||||||
auto input = _inputDevice->makeInput((Qt::Key) event->key());
|
auto input = _inputDevice->makeInput((Qt::Key) event->key());
|
||||||
auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel());
|
if (!(event->modifiers() & Qt::KeyboardModifier::ControlModifier)) {
|
||||||
if (!result.second) {
|
auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel());
|
||||||
// key pressed again ? without catching the release event ?
|
if (result.second) {
|
||||||
|
// key pressed again ? without catching the release event ?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,10 +49,10 @@
|
||||||
// 2. Declare a text string.
|
// 2. Declare a text string.
|
||||||
// 3. Call createNotifications(text, NotificationType) parsing the text.
|
// 3. Call createNotifications(text, NotificationType) parsing the text.
|
||||||
// example:
|
// example:
|
||||||
// if (key.text === "s") {
|
// if (key.text === "o") {
|
||||||
// if (ctrlIsPressed === true) {
|
// if (ctrlIsPressed === true) {
|
||||||
// noteString = "Snapshot taken.";
|
// noteString = "Open script";
|
||||||
// createNotification(noteString, NotificationType.SNAPSHOT);
|
// createNotification(noteString, NotificationType.OPEN_SCRIPT);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -233,8 +233,9 @@ function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) {
|
||||||
// Pushes data to each array and sets up data for 2nd dimension array
|
// Pushes data to each array and sets up data for 2nd dimension array
|
||||||
// to handle auxiliary data not carried by the overlay class
|
// to handle auxiliary data not carried by the overlay class
|
||||||
// specifically notification "heights", "times" of creation, and .
|
// specifically notification "heights", "times" of creation, and .
|
||||||
function notify(notice, button, height) {
|
function notify(notice, button, height, imageProperties, image) {
|
||||||
var noticeWidth,
|
var notificationText,
|
||||||
|
noticeWidth,
|
||||||
noticeHeight,
|
noticeHeight,
|
||||||
positions,
|
positions,
|
||||||
last;
|
last;
|
||||||
|
@ -246,20 +247,27 @@ function notify(notice, button, height) {
|
||||||
noticeHeight = notice.height * NOTIFICATION_3D_SCALE;
|
noticeHeight = notice.height * NOTIFICATION_3D_SCALE;
|
||||||
|
|
||||||
notice.size = { x: noticeWidth, y: noticeHeight };
|
notice.size = { x: noticeWidth, y: noticeHeight };
|
||||||
notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE;
|
|
||||||
notice.leftMargin = 2 * notice.leftMargin * NOTIFICATION_3D_SCALE;
|
positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y);
|
||||||
notice.bottomMargin = 0;
|
|
||||||
notice.rightMargin = 0;
|
if (!image) {
|
||||||
notice.lineHeight = 10.0 * (fontSize / 12.0) * NOTIFICATION_3D_SCALE;
|
notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE;
|
||||||
notice.isFacingAvatar = false;
|
notice.leftMargin = 2 * notice.leftMargin * NOTIFICATION_3D_SCALE;
|
||||||
|
notice.bottomMargin = 0;
|
||||||
|
notice.rightMargin = 0;
|
||||||
|
notice.lineHeight = 10.0 * (fontSize / 12.0) * NOTIFICATION_3D_SCALE;
|
||||||
|
notice.isFacingAvatar = false;
|
||||||
|
|
||||||
|
notificationText = Overlays.addOverlay("text3d", notice);
|
||||||
|
notifications.push(notificationText);
|
||||||
|
} else {
|
||||||
|
notifications.push(Overlays.addOverlay("image3d", notice));
|
||||||
|
}
|
||||||
|
|
||||||
button.url = button.imageURL;
|
button.url = button.imageURL;
|
||||||
button.scale = button.width * NOTIFICATION_3D_SCALE;
|
button.scale = button.width * NOTIFICATION_3D_SCALE;
|
||||||
button.isFacingAvatar = false;
|
button.isFacingAvatar = false;
|
||||||
|
|
||||||
positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y);
|
|
||||||
|
|
||||||
notifications.push((Overlays.addOverlay("text3d", notice)));
|
|
||||||
buttons.push((Overlays.addOverlay("image3d", button)));
|
buttons.push((Overlays.addOverlay("image3d", button)));
|
||||||
overlay3DDetails.push({
|
overlay3DDetails.push({
|
||||||
notificationOrientation: positions.notificationOrientation,
|
notificationOrientation: positions.notificationOrientation,
|
||||||
|
@ -269,23 +277,44 @@ function notify(notice, button, height) {
|
||||||
height: noticeHeight
|
height: noticeHeight
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var notificationText = Overlays.addOverlay("text", notice);
|
if (!image) {
|
||||||
notifications.push((notificationText));
|
notificationText = Overlays.addOverlay("text", notice);
|
||||||
buttons.push((Overlays.addOverlay("image", button)));
|
notifications.push((notificationText));
|
||||||
|
} else {
|
||||||
|
notifications.push(Overlays.addOverlay("image", notice));
|
||||||
|
}
|
||||||
|
buttons.push(Overlays.addOverlay("image", button));
|
||||||
}
|
}
|
||||||
|
|
||||||
height = height + 1.0;
|
height = height + 1.0;
|
||||||
heights.push(height);
|
heights.push(height);
|
||||||
times.push(new Date().getTime() / 1000);
|
times.push(new Date().getTime() / 1000);
|
||||||
myAlpha.push(0);
|
|
||||||
last = notifications.length - 1;
|
last = notifications.length - 1;
|
||||||
|
myAlpha.push(notifications[last].alpha);
|
||||||
createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]);
|
createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]);
|
||||||
fadeIn(notifications[last], buttons[last]);
|
fadeIn(notifications[last], buttons[last]);
|
||||||
|
|
||||||
|
if (imageProperties && !image) {
|
||||||
|
var imageHeight = notice.width / imageProperties.aspectRatio;
|
||||||
|
notice = {
|
||||||
|
x: notice.x,
|
||||||
|
y: notice.y + height,
|
||||||
|
width: notice.width,
|
||||||
|
height: imageHeight,
|
||||||
|
subImage: { x: 0, y: 0 },
|
||||||
|
color: { red: 255, green: 255, blue: 255},
|
||||||
|
visible: true,
|
||||||
|
imageURL: imageProperties.path,
|
||||||
|
alpha: backgroundAlpha
|
||||||
|
};
|
||||||
|
notify(notice, button, imageHeight, imageProperties, true);
|
||||||
|
}
|
||||||
|
|
||||||
return notificationText;
|
return notificationText;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function creates and sizes the overlays
|
// This function creates and sizes the overlays
|
||||||
function createNotification(text, notificationType) {
|
function createNotification(text, notificationType, imageProperties) {
|
||||||
var count = (text.match(/\n/g) || []).length,
|
var count = (text.match(/\n/g) || []).length,
|
||||||
breakPoint = 43.0, // length when new line is added
|
breakPoint = 43.0, // length when new line is added
|
||||||
extraLine = 0,
|
extraLine = 0,
|
||||||
|
@ -308,6 +337,7 @@ function createNotification(text, notificationType) {
|
||||||
|
|
||||||
level = (stack + 20.0);
|
level = (stack + 20.0);
|
||||||
height = height + extraLine;
|
height = height + extraLine;
|
||||||
|
|
||||||
noticeProperties = {
|
noticeProperties = {
|
||||||
x: overlayLocationX,
|
x: overlayLocationX,
|
||||||
y: level,
|
y: level,
|
||||||
|
@ -336,12 +366,11 @@ function createNotification(text, notificationType) {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) &&
|
if (Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) &&
|
||||||
Menu.isOptionChecked(NotificationType.getMenuString(notificationType)))
|
Menu.isOptionChecked(NotificationType.getMenuString(notificationType))) {
|
||||||
{
|
|
||||||
randomSounds.playRandom();
|
randomSounds.playRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
return notify(noticeProperties, buttonProperties, height);
|
return notify(noticeProperties, buttonProperties, height, imageProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteNotification(index) {
|
function deleteNotification(index) {
|
||||||
|
@ -362,21 +391,12 @@ function deleteNotification(index) {
|
||||||
|
|
||||||
// wraps whole word to newline
|
// wraps whole word to newline
|
||||||
function stringDivider(str, slotWidth, spaceReplacer) {
|
function stringDivider(str, slotWidth, spaceReplacer) {
|
||||||
var p,
|
var left, right;
|
||||||
left,
|
|
||||||
right;
|
|
||||||
|
|
||||||
if (str.length > slotWidth) {
|
if (str.length > slotWidth && slotWidth > 0) {
|
||||||
p = slotWidth;
|
left = str.substring(0, slotWidth);
|
||||||
while (p > 0 && str[p] !== ' ') {
|
right = str.substring(slotWidth);
|
||||||
p -= 1;
|
return left + spaceReplacer + stringDivider(right, slotWidth, spaceReplacer);
|
||||||
}
|
|
||||||
|
|
||||||
if (p > 0) {
|
|
||||||
left = str.substring(0, p);
|
|
||||||
right = str.substring(p + 1);
|
|
||||||
return left + spaceReplacer + stringDivider(right, slotWidth, spaceReplacer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
@ -504,7 +524,15 @@ function onMuteStateChanged() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDomainConnectionRefused(reason) {
|
function onDomainConnectionRefused(reason) {
|
||||||
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED );
|
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSnapshotTaken(path) {
|
||||||
|
var imageProperties = {
|
||||||
|
path: Script.resolvePath("file:///" + path),
|
||||||
|
aspectRatio: Window.innerWidth / Window.innerHeight
|
||||||
|
}
|
||||||
|
createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handles mouse clicks on buttons
|
// handles mouse clicks on buttons
|
||||||
|
@ -541,13 +569,6 @@ function keyPressEvent(key) {
|
||||||
if (key.key === 16777249) {
|
if (key.key === 16777249) {
|
||||||
ctrlIsPressed = true;
|
ctrlIsPressed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.text === "s") {
|
|
||||||
if (ctrlIsPressed === true) {
|
|
||||||
noteString = "Snapshot taken.";
|
|
||||||
createNotification(noteString, NotificationType.SNAPSHOT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup() {
|
function setup() {
|
||||||
|
@ -615,5 +636,6 @@ Script.update.connect(update);
|
||||||
Script.scriptEnding.connect(scriptEnding);
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
Menu.menuItemEvent.connect(menuItemEvent);
|
Menu.menuItemEvent.connect(menuItemEvent);
|
||||||
Window.domainConnectionRefused.connect(onDomainConnectionRefused);
|
Window.domainConnectionRefused.connect(onDomainConnectionRefused);
|
||||||
|
Window.snapshotTaken.connect(onSnapshotTaken);
|
||||||
|
|
||||||
setup();
|
setup();
|
||||||
|
|
Loading…
Reference in a new issue