Merge branch 'master' of https://github.com/highfidelity/hifi
|
@ -66,6 +66,9 @@ else ()
|
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter")
|
||||
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -Woverloaded-virtual -Wdouble-promotion")
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "5.1") # gcc 5.1 and on have suggest-override
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override")
|
||||
endif ()
|
||||
endif ()
|
||||
endif(WIN32)
|
||||
|
||||
|
|
|
@ -52,10 +52,10 @@ public:
|
|||
float getLastReceivedAudioLoudness() const { return _lastReceivedAudioLoudness; }
|
||||
QUuid getSessionUUID() const;
|
||||
|
||||
virtual void aboutToFinish();
|
||||
virtual void aboutToFinish() override;
|
||||
|
||||
public slots:
|
||||
void run();
|
||||
void run() override;
|
||||
void playAvatarSound(SharedSoundPointer avatarSound) { setAvatarSound(avatarSound); }
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -24,27 +24,27 @@ public:
|
|||
AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AssignmentAction();
|
||||
|
||||
virtual void removeFromSimulation(EntitySimulationPointer simulation) const;
|
||||
virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; }
|
||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; }
|
||||
virtual bool updateArguments(QVariantMap arguments);
|
||||
virtual QVariantMap getArguments();
|
||||
virtual void removeFromSimulation(EntitySimulationPointer simulation) const override;
|
||||
virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; }
|
||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; }
|
||||
virtual bool updateArguments(QVariantMap arguments) override;
|
||||
virtual QVariantMap getArguments() override;
|
||||
|
||||
virtual QByteArray serialize() const;
|
||||
virtual void deserialize(QByteArray serializedArguments);
|
||||
virtual QByteArray serialize() const override;
|
||||
virtual void deserialize(QByteArray serializedArguments) override;
|
||||
|
||||
private:
|
||||
QByteArray _data;
|
||||
|
||||
protected:
|
||||
virtual glm::vec3 getPosition();
|
||||
virtual void setPosition(glm::vec3 position);
|
||||
virtual glm::quat getRotation();
|
||||
virtual void setRotation(glm::quat rotation);
|
||||
virtual glm::vec3 getLinearVelocity();
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity);
|
||||
virtual glm::vec3 getAngularVelocity();
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity);
|
||||
virtual glm::vec3 getPosition() override;
|
||||
virtual void setPosition(glm::vec3 position) override;
|
||||
virtual glm::quat getRotation() override;
|
||||
virtual void setRotation(glm::quat rotation) override;
|
||||
virtual glm::vec3 getLinearVelocity() override;
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
|
||||
virtual glm::vec3 getAngularVelocity() override;
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity) override;
|
||||
|
||||
bool _active;
|
||||
EntityItemWeakPointer _ownerEntity;
|
||||
|
|
|
@ -22,8 +22,8 @@ public:
|
|||
virtual EntityActionPointer factory(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments);
|
||||
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data);
|
||||
QVariantMap arguments) override;
|
||||
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentActionFactory_h
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
AssetServer(ReceivedMessage& message);
|
||||
|
||||
public slots:
|
||||
void run();
|
||||
void run() override;
|
||||
|
||||
private slots:
|
||||
void completeSetup();
|
||||
|
@ -35,9 +35,9 @@ private slots:
|
|||
void handleAssetGet(QSharedPointer<ReceivedMessage> packet, SharedNodePointer senderNode);
|
||||
void handleAssetUpload(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer senderNode);
|
||||
void handleAssetMappingOperation(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
||||
void sendStatsPacket();
|
||||
|
||||
|
||||
void sendStatsPacket() override;
|
||||
|
||||
private:
|
||||
using Mappings = QVariantHash;
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class SendAssetTask : public QRunnable {
|
|||
public:
|
||||
SendAssetTask(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& sendToNode, const QDir& resourcesDir);
|
||||
|
||||
void run();
|
||||
void run() override;
|
||||
|
||||
private:
|
||||
QSharedPointer<ReceivedMessage> _message;
|
||||
|
|
|
@ -27,9 +27,9 @@ class Node;
|
|||
class UploadAssetTask : public QRunnable {
|
||||
public:
|
||||
UploadAssetTask(QSharedPointer<ReceivedMessage> message, QSharedPointer<Node> senderNode, const QDir& resourcesDir);
|
||||
|
||||
void run();
|
||||
|
||||
|
||||
void run() override;
|
||||
|
||||
private:
|
||||
QSharedPointer<ReceivedMessage> _receivedMessage;
|
||||
QSharedPointer<Node> _senderNode;
|
||||
|
|
|
@ -35,9 +35,9 @@ public:
|
|||
|
||||
public slots:
|
||||
/// threaded run of assignment
|
||||
void run();
|
||||
void run() override;
|
||||
|
||||
void sendStatsPacket();
|
||||
void sendStatsPacket() override;
|
||||
|
||||
static const InboundAudioStream::Settings& getStreamSettings() { return _streamSettings; }
|
||||
|
||||
|
|
|
@ -49,17 +49,17 @@ public:
|
|||
|
||||
// removes an AudioHRTF object for a given stream
|
||||
void removeHRTFForStream(const QUuid& nodeID, const QUuid& streamID = QUuid());
|
||||
|
||||
int parseData(ReceivedMessage& message);
|
||||
|
||||
int parseData(ReceivedMessage& message) override;
|
||||
|
||||
void checkBuffersBeforeFrameSend();
|
||||
|
||||
void removeDeadInjectedStreams();
|
||||
|
||||
QJsonObject getAudioStreamStats();
|
||||
|
||||
|
||||
void sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode);
|
||||
|
||||
|
||||
void incrementOutgoingMixedAudioSequenceNumber() { _outgoingMixedAudioSequenceNumber++; }
|
||||
quint16 getOutgoingSequenceNumber() const { return _outgoingMixedAudioSequenceNumber; }
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ private:
|
|||
AvatarAudioStream(const AvatarAudioStream&);
|
||||
AvatarAudioStream& operator= (const AvatarAudioStream&);
|
||||
|
||||
int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
|
||||
int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) override;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarAudioStream_h
|
||||
|
|
|
@ -27,11 +27,11 @@ public:
|
|||
~AvatarMixer();
|
||||
public slots:
|
||||
/// runs the avatar mixer
|
||||
void run();
|
||||
void run() override;
|
||||
|
||||
void nodeKilled(SharedNodePointer killedNode);
|
||||
|
||||
void sendStatsPacket();
|
||||
|
||||
void sendStatsPacket() override;
|
||||
|
||||
private slots:
|
||||
void handleAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
@ -45,14 +45,14 @@ private slots:
|
|||
private:
|
||||
void broadcastAvatarData();
|
||||
void parseDomainServerSettings(const QJsonObject& domainSettings);
|
||||
|
||||
|
||||
QThread _broadcastThread;
|
||||
|
||||
|
||||
p_high_resolution_clock::time_point _lastFrameTimestamp;
|
||||
|
||||
|
||||
float _trailingSleepRatio { 1.0f };
|
||||
float _performanceThrottlingRatio { 0.0f };
|
||||
|
||||
|
||||
int _sumListeners { 0 };
|
||||
int _numStatFrames { 0 };
|
||||
int _sumIdentityPackets { 0 };
|
||||
|
|
|
@ -25,7 +25,8 @@ class AssignmentParentFinder : public SpatialParentFinder {
|
|||
public:
|
||||
AssignmentParentFinder(EntityTreePointer tree) : _tree(tree) { }
|
||||
virtual ~AssignmentParentFinder() { }
|
||||
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const;
|
||||
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success,
|
||||
SpatialParentTree* entityTree = nullptr) const override;
|
||||
|
||||
protected:
|
||||
EntityTreePointer _tree;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
class EntityNodeData : public OctreeQueryNode {
|
||||
public:
|
||||
virtual PacketType getMyPacketType() const { return PacketType::EntityData; }
|
||||
virtual PacketType getMyPacketType() const override { return PacketType::EntityData; }
|
||||
|
||||
quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; }
|
||||
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }
|
||||
|
|
|
@ -24,9 +24,9 @@ public:
|
|||
MessagesMixer(ReceivedMessage& message);
|
||||
|
||||
public slots:
|
||||
void run();
|
||||
void run() override;
|
||||
void nodeKilled(SharedNodePointer killedNode);
|
||||
void sendStatsPacket();
|
||||
void sendStatsPacket() override;
|
||||
|
||||
private slots:
|
||||
void handleMessages(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
|
|
@ -74,15 +74,15 @@ public:
|
|||
|
||||
NodeToSenderStatsMap getSingleSenderStats() { QReadLocker locker(&_senderStatsLock); return _singleSenderStats; }
|
||||
|
||||
virtual void terminating() { _shuttingDown = true; ReceivedPacketProcessor::terminating(); }
|
||||
virtual void terminating() override { _shuttingDown = true; ReceivedPacketProcessor::terminating(); }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
|
||||
|
||||
virtual unsigned long getMaxWait() const;
|
||||
virtual void preProcess();
|
||||
virtual void midProcess();
|
||||
virtual unsigned long getMaxWait() const override;
|
||||
virtual void preProcess() override;
|
||||
virtual void midProcess() override;
|
||||
|
||||
private:
|
||||
int sendNackPackets();
|
||||
|
|
|
@ -47,7 +47,7 @@ public:
|
|||
|
||||
protected:
|
||||
/// Implements generic processing behavior for this thread.
|
||||
virtual bool process();
|
||||
virtual bool process() override;
|
||||
|
||||
private:
|
||||
int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent, bool dontSuppressDuplicate = false);
|
||||
|
|
|
@ -120,16 +120,16 @@ public:
|
|||
static int howManyThreadsDidHandlePacketSend(quint64 since = 0);
|
||||
static int howManyThreadsDidCallWriteDatagram(quint64 since = 0);
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler);
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) override;
|
||||
|
||||
virtual void aboutToFinish();
|
||||
virtual void aboutToFinish() override;
|
||||
|
||||
public slots:
|
||||
/// runs the octree server assignment
|
||||
void run();
|
||||
void run() override;
|
||||
virtual void nodeAdded(SharedNodePointer node);
|
||||
virtual void nodeKilled(SharedNodePointer node);
|
||||
void sendStatsPacket();
|
||||
void sendStatsPacket() override;
|
||||
|
||||
private slots:
|
||||
void domainSettingsRequestComplete();
|
||||
|
|
|
@ -509,9 +509,7 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
|||
}
|
||||
} else {
|
||||
if (!senderSockAddr.isNull()) {
|
||||
qDebug() << "Insufficient data to decrypt username signature - denying connection.";
|
||||
sendConnectionDeniedPacket("Insufficient data", senderSockAddr,
|
||||
DomainHandler::ConnectionRefusedReason::LoginError);
|
||||
qDebug() << "Insufficient data to decrypt username signature - delaying connection.";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -118,8 +118,8 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
setupNodeListAndAssignments();
|
||||
|
||||
if (_type == MetaverseDomain) {
|
||||
// if we have a metaverse domain, we'll need an access token to heartbeat handle auto-networking
|
||||
if (_type != NonMetaverse) {
|
||||
// if we have a metaverse domain, we'll use an access token for API calls
|
||||
resetAccountManagerAccessToken();
|
||||
}
|
||||
|
||||
|
@ -469,8 +469,8 @@ bool DomainServer::resetAccountManagerAccessToken() {
|
|||
if (accessTokenVariant && accessTokenVariant->canConvert(QMetaType::QString)) {
|
||||
accessToken = accessTokenVariant->toString();
|
||||
} else {
|
||||
qDebug() << "A domain-server feature that requires authentication is enabled but no access token is present.";
|
||||
qDebug() << "Set an access token via the web interface, in your user or master config"
|
||||
qWarning() << "No access token is present. Some operations that use the metaverse API will fail.";
|
||||
qDebug() << "Set an access token via the web interface, in your user config"
|
||||
<< "at keypath metaverse.access_token or in your ENV at key DOMAIN_SERVER_ACCESS_TOKEN";
|
||||
|
||||
// clear any existing access token from AccountManager
|
||||
|
@ -480,7 +480,7 @@ bool DomainServer::resetAccountManagerAccessToken() {
|
|||
}
|
||||
} else {
|
||||
qDebug() << "Using access token from DOMAIN_SERVER_ACCESS_TOKEN in env. This overrides any access token present"
|
||||
<< " in the user or master config.";
|
||||
<< " in the user config.";
|
||||
}
|
||||
|
||||
// give this access token to the AccountManager
|
||||
|
|
|
@ -50,8 +50,8 @@ public:
|
|||
|
||||
static int const EXIT_CODE_REBOOT;
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
|
||||
bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false);
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override;
|
||||
bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false) override;
|
||||
|
||||
public slots:
|
||||
/// Called by NodeList to inform us a node has been added
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[
|
||||
{ "type": "deadZone", "min": 0.15 },
|
||||
"constrainToInteger",
|
||||
{ "type": "pulse", "interval": 0.5 },
|
||||
{ "type": "pulse", "interval": 0.25 },
|
||||
{ "type": "scale", "scale": 22.5 }
|
||||
]
|
||||
},
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
{ "from": "Vive.LY", "when": "Vive.LSY", "filters": ["invert"], "to": "Standard.LY" },
|
||||
{ "from": "Vive.LX", "when": "Vive.LSX", "to": "Standard.LX" },
|
||||
{
|
||||
"from": "Vive.LT", "to": "Standard.LT",
|
||||
"filters": [
|
||||
"from": "Vive.LT", "to": "Standard.LT",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.05 }
|
||||
]
|
||||
},
|
||||
|
@ -18,8 +18,8 @@
|
|||
{ "from": "Vive.RY", "when": "Vive.RSY", "filters": ["invert"], "to": "Standard.RY" },
|
||||
{ "from": "Vive.RX", "when": "Vive.RSX", "to": "Standard.RX" },
|
||||
{
|
||||
"from": "Vive.RT", "to": "Standard.RT",
|
||||
"filters": [
|
||||
"from": "Vive.RT", "to": "Standard.RT",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.05 }
|
||||
]
|
||||
},
|
||||
|
@ -37,4 +37,4 @@
|
|||
{ "from": "Vive.LeftHand", "to": "Standard.LeftHand" },
|
||||
{ "from": "Vive.RightHand", "to": "Standard.RightHand" }
|
||||
]
|
||||
}
|
||||
}
|
42
interface/resources/images/Swipe-Icon-single.svg
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path d="M25.9,20.6c0.29,0.15,0.47,0.38,0.48,0.72c0,0.7,0.1,2.92,0.08,3.62c1,0.3,3.61,1.19,4.56,1.45
|
||||
c0.15,0.01,0.27,0.03,0.38,0.09c0.2,0.1,0.33,0.3,0.36,0.58c0.01,0.11,0.02,0.22,0.02,0.33c-0.03,1.5,0.01,1.08-0.05,2.58
|
||||
c-0.06,1.55-0.78,2.72-2.21,3.34c-1.12,0.49-2.01,0.57-2.92,0.1c-0.25-0.13-0.5-0.3-0.76-0.51c-1.46-1.21-2.95-2.39-4.42-3.59
|
||||
c-0.37-0.3-0.48-0.75-0.29-1.15c0.19-0.39,0.63-0.61,1.05-0.49c0.06,0.02,0.12,0.04,0.18,0.07c0.1,0.05,0.19,0.11,0.27,0.18
|
||||
c0.64,0.51,1,1.02,1.63,1.53c0,0,0.01,0,0.01,0.01c0.01,0.01,0.04,0.01,0.09,0.04c0-0.12,0.01-0.23,0.01-0.33
|
||||
c0.06-2.53,0.12-5.06,0.18-7.59c0.01-0.36,0.06-0.69,0.37-0.92c0.28-0.2,0.58-0.25,0.89-0.12C25.85,20.57,25.88,20.58,25.9,20.6
|
||||
M26.64,19.16c-0.06-0.03-0.12-0.06-0.18-0.09c-0.83-0.36-1.74-0.25-2.48,0.3c-1,0.73-1.03,1.83-1.04,2.19
|
||||
c-0.04,1.54-0.07,3.11-0.11,4.64c-0.07-0.02-0.13-0.05-0.2-0.07c-1.17-0.34-2.41,0.22-2.96,1.33c-0.26,0.53-0.32,1.14-0.18,1.71
|
||||
c0.14,0.55,0.45,1.03,0.91,1.4c0.49,0.4,0.98,0.8,1.46,1.18c0.97,0.78,1.97,1.59,2.94,2.4c0.35,0.29,0.7,0.52,1.05,0.71
|
||||
c1.72,0.88,3.28,0.39,4.3-0.06c1.96-0.86,3.09-2.55,3.18-4.76c0.03-0.89,0.04-1.13,0.04-1.47c0-0.23,0-0.51,0.01-1.13
|
||||
c0-0.18,0-0.37-0.03-0.56c-0.1-0.82-0.54-1.49-1.23-1.84c-0.25-0.13-0.53-0.21-0.82-0.25c-0.56-0.16-1.57-0.49-2.48-0.79
|
||||
c-0.27-0.09-0.53-0.17-0.78-0.25c-0.01-0.34-0.02-0.7-0.03-1.06C28.02,22.12,28,21.58,28,21.31
|
||||
C27.99,20.39,27.49,19.59,26.64,19.16L26.64,19.16z"/>
|
||||
</g>
|
||||
<path d="M33.4,17.76l-2.87-2.64l-0.13,1.65c-1.42-0.61-3.13-0.95-4.95-0.95c-1.91,0-3.66,0.37-5.06,1.04l-0.23-1.76l-2.73,2.79
|
||||
l3.35,2l-0.22-1.66c1.27-0.71,3.01-1.11,4.89-1.11c1.8,0,3.52,0.37,4.84,1.03l-0.14,1.79L33.4,17.76z"/>
|
||||
<g>
|
||||
<path class="st0" d="M25.64,19.86c0.29,0.15,0.47,0.38,0.48,0.72c0,0.7,0.1,2.92,0.08,3.62c1,0.3,3.61,1.19,4.56,1.45
|
||||
c0.16,0.04,0.28,0.08,0.39,0.13c0.2,0.1,0.31,0.26,0.35,0.54c0.01,0.11,0.02,0.22,0.02,0.33c-0.03,1.5,0.01,1.08-0.05,2.58
|
||||
c-0.06,1.55-0.78,2.72-2.21,3.34c-1.12,0.49-2.01,0.57-2.92,0.1c-0.25-0.13-0.5-0.3-0.76-0.51c-1.46-1.21-2.95-2.39-4.42-3.59
|
||||
c-0.37-0.3-0.48-0.75-0.29-1.15c0.19-0.39,0.63-0.61,1.05-0.49c0.06,0.02,0.12,0.04,0.18,0.07c0.1,0.05,0.19,0.11,0.27,0.18
|
||||
c0.64,0.51,1,1.02,1.63,1.53c0,0,0.01,0,0.01,0.01c0.01,0.01,0.04,0.01,0.09,0.04c0-0.12,0.01-0.23,0.01-0.33
|
||||
c0.06-2.53,0.12-5.06,0.18-7.59c0.01-0.36,0.06-0.69,0.37-0.92c0.28-0.2,0.58-0.25,0.89-0.12C25.59,19.83,25.62,19.84,25.64,19.86
|
||||
M26.38,18.42c-0.06-0.03-0.12-0.06-0.18-0.09c-0.83-0.36-1.74-0.25-2.48,0.3c-1,0.73-1.03,1.83-1.04,2.19
|
||||
c-0.04,1.54-0.07,3.11-0.11,4.64c-0.07-0.02-0.13-0.05-0.2-0.07c-1.17-0.34-2.41,0.22-2.96,1.33c-0.26,0.53-0.32,1.14-0.18,1.71
|
||||
c0.14,0.55,0.45,1.03,0.91,1.4c0.49,0.4,0.98,0.8,1.46,1.18c0.97,0.78,1.97,1.59,2.94,2.4c0.35,0.29,0.7,0.52,1.05,0.71
|
||||
c1.72,0.88,3.28,0.39,4.3-0.06c1.96-0.86,3.09-2.55,3.18-4.76c0.03-0.89,0.04-1.13,0.04-1.47c0-0.23,0-0.51,0.01-1.13
|
||||
c0-0.18,0-0.37-0.03-0.56c-0.1-0.82-0.45-1.41-1.13-1.76c-0.25-0.13-0.61-0.24-0.91-0.32c-0.56-0.16-1.57-0.49-2.48-0.79
|
||||
c-0.27-0.09-0.53-0.17-0.78-0.25c-0.01-0.34-0.02-0.7-0.03-1.06c-0.02-0.57-0.03-1.11-0.04-1.38
|
||||
C27.73,19.65,27.23,18.85,26.38,18.42L26.38,18.42z"/>
|
||||
</g>
|
||||
<path class="st0" d="M33.14,17.02l-2.87-2.64l-0.13,1.65c-1.42-0.61-3.13-0.95-4.95-0.95c-1.91,0-3.66,0.37-5.06,1.04l-0.23-1.76
|
||||
l-2.73,2.79l3.35,2l-0.22-1.66c1.27-0.71,3.01-1.11,4.89-1.11c1.8,0,3.52,0.37,4.84,1.03l-0.14,1.79L33.14,17.02z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -1,81 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
width="1440"
|
||||
height="200"
|
||||
data-icon="map-marker"
|
||||
data-container-transform="translate(24)"
|
||||
viewBox="0 0 1440 200"
|
||||
id="svg4136"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="address-bar.svg">
|
||||
<metadata
|
||||
id="metadata4144">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs4142" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1536"
|
||||
inkscape:window-height="687"
|
||||
id="namedview4140"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.61319416"
|
||||
inkscape:cx="670.06567"
|
||||
inkscape:cy="52.468468"
|
||||
inkscape:window-x="105"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg4136" />
|
||||
<rect
|
||||
style="fill:#ededed;fill-opacity:1;stroke:none;stroke-linejoin:round;stroke-opacity:1"
|
||||
id="rect4141"
|
||||
width="1280"
|
||||
height="140"
|
||||
x="160"
|
||||
y="30"
|
||||
rx="16.025024"
|
||||
ry="17.019567" />
|
||||
<rect
|
||||
style="fill:#dadada;fill-opacity:1;stroke:#cbcbcb;stroke-width:0.35830048;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4135"
|
||||
width="328.72031"
|
||||
height="139.64169"
|
||||
x="150.33546"
|
||||
y="30.179144"
|
||||
rx="18.876532"
|
||||
ry="20.609974" />
|
||||
<circle
|
||||
style="fill:#b8b8b8;fill-opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="path4146"
|
||||
cx="100"
|
||||
cy="100"
|
||||
r="100" />
|
||||
<path
|
||||
d="m 100,36.000005 c -22.1,0 -40,17.9 -40,39.999995 0,30 40,88 40,88 0,0 40,-58 40,-88 0,-22.099995 -17.9,-39.999995 -40,-39.999995 z m 0,22 c 9.9,0 18,8.099995 18,17.999995 0,9.9 -8.1,18 -18,18 -9.9,0 -18,-8.1 -18,-18 0,-9.9 8.1,-17.999995 18,-17.999995 z"
|
||||
id="path4138"
|
||||
inkscape:connector-curvature="0" />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1"
|
||||
id="svg4136" inkscape:version="0.91 r13725" sodipodi:docname="address-bar.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1440 200"
|
||||
style="enable-background:new 0 0 1440 200;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#1E1E1E;}
|
||||
.st1{fill:#E6E7E8;}
|
||||
.st2{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M1428.61,172H11.46c-6.27,0-11.39-5.13-11.39-11.39V49.58c0-6.27,5.13-11.39,11.39-11.39h1417.15
|
||||
c6.27,0,11.39,5.13,11.39,11.39v111.03C1440,166.87,1434.87,172,1428.61,172z"/>
|
||||
<path class="st1" d="M1428.61,165.81H11.46c-6.27,0-11.39-5.13-11.39-11.39V43.39c0-6.27,5.13-11.39,11.39-11.39h1417.15
|
||||
c6.27,0,11.39,5.13,11.39,11.39v111.03C1440,160.68,1434.87,165.81,1428.61,165.81z"/>
|
||||
<path class="st2" d="M1133.24,165.81H417.95c-4.47,0-8.12-3.65-8.12-8.12V40.11c0-4.47,3.65-8.12,8.12-8.12h715.28
|
||||
c4.47,0,8.12,3.65,8.12,8.12v117.57C1141.36,162.15,1137.7,165.81,1133.24,165.81z"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.4 KiB |
28
interface/resources/images/backward.svg
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 150" style="enable-background:new 0 0 50 150;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#414042;}
|
||||
.st1{fill:#CCCCCC;}
|
||||
.st2{fill:#1398BB;}
|
||||
.st3{fill:#31D8FF;}
|
||||
</style>
|
||||
<g id="Layer_1">
|
||||
<path class="st0" d="M33.72,85.08l-9.15-9.15l-0.74-0.74l0.74-0.74l9.35-9.35c0.59-0.59,0.59-1.56,0-2.15
|
||||
c-0.29-0.29-0.67-0.45-1.08-0.45c-0.41,0-0.79,0.16-1.08,0.45L19.52,75.19l12.04,12.04c0.29,0.29,0.67,0.45,1.08,0.45
|
||||
c0.41,0,0.79-0.16,1.08-0.45C34.31,86.64,34.31,85.67,33.72,85.08z"/>
|
||||
<path class="st1" d="M33.72,33.45l-9.15-9.15l-0.74-0.74l0.74-0.74l9.35-9.35c0.59-0.59,0.59-1.56,0-2.15
|
||||
c-0.29-0.29-0.67-0.45-1.08-0.45c-0.41,0-0.79,0.16-1.08,0.45L19.52,23.56L31.56,35.6c0.29,0.29,0.67,0.45,1.08,0.45
|
||||
c0.41,0,0.79-0.16,1.08-0.45C34.31,35.01,34.31,34.04,33.72,33.45z"/>
|
||||
<path class="st2" d="M17.99,124.82l12.78,12.78c1,1,2.63,1,3.63,0c1-1,1-2.63,0-3.63l-9.15-9.15l9.35-9.35c1-1,1-2.63,0-3.63
|
||||
c-1-1-2.63-1-3.63,0L17.99,124.82z"/>
|
||||
<path class="st3" d="M32.79,112.13c0.41,0,0.79,0.16,1.08,0.45c0.59,0.59,0.59,1.56,0,2.15l-9.35,9.35l-0.74,0.74l0.74,0.74
|
||||
l9.15,9.15c0.59,0.59,0.59,1.56,0,2.15c-0.29,0.29-0.67,0.45-1.08,0.45c-0.41,0-0.79-0.16-1.08-0.45l-12.04-12.04l12.24-12.24
|
||||
C32,112.29,32.38,112.13,32.79,112.13 M32.79,111.08c-0.66,0-1.31,0.25-1.82,0.75l-12.98,12.98l12.78,12.78
|
||||
c0.5,0.5,1.16,0.75,1.82,0.75c0.66,0,1.31-0.25,1.82-0.75c1-1,1-2.63,0-3.63l-9.15-9.15l9.35-9.35c1-1,1-2.63,0-3.63
|
||||
C34.1,111.34,33.44,111.08,32.79,111.08L32.79,111.08z"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
28
interface/resources/images/forward.svg
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 150" style="enable-background:new 0 0 50 150;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#414042;}
|
||||
.st1{fill:#CCCCCC;}
|
||||
.st2{fill:#1398BB;}
|
||||
.st3{fill:#31D8FF;}
|
||||
</style>
|
||||
<g id="Layer_1">
|
||||
<path class="st0" d="M21.12,62.95c-0.29-0.29-0.67-0.45-1.08-0.45c-0.41,0-0.79,0.16-1.08,0.45c-0.59,0.59-0.59,1.56,0,2.15
|
||||
l9.35,9.35l0.74,0.74l-0.74,0.74l-9.15,9.15c-0.59,0.59-0.59,1.56,0,2.15c0.29,0.29,0.67,0.45,1.08,0.45
|
||||
c0.41,0,0.79-0.16,1.08-0.45l12.04-12.04L21.12,62.95z"/>
|
||||
<path class="st1" d="M21.12,11.32c-0.29-0.29-0.67-0.45-1.08-0.45c-0.41,0-0.79,0.16-1.08,0.45c-0.59,0.59-0.59,1.56,0,2.15
|
||||
l9.35,9.35l0.74,0.74l-0.74,0.74l-9.15,9.15c-0.59,0.59-0.59,1.56,0,2.15c0.29,0.29,0.67,0.45,1.08,0.45
|
||||
c0.41,0,0.79-0.16,1.08-0.45l12.04-12.04L21.12,11.32z"/>
|
||||
<path class="st2" d="M34.9,124.82L22.11,137.6c-1,1-2.63,1-3.63,0c-1-1-1-2.63,0-3.63l9.15-9.15l-9.35-9.35c-1-1-1-2.63,0-3.63
|
||||
c1-1,2.63-1,3.63,0L34.9,124.82z"/>
|
||||
<path class="st3" d="M20.1,112.13c0.41,0,0.79,0.16,1.08,0.45l12.24,12.24l-12.04,12.04c-0.29,0.29-0.67,0.45-1.08,0.45
|
||||
c-0.41,0-0.79-0.16-1.08-0.45c-0.59-0.59-0.59-1.56,0-2.15l9.15-9.15l0.74-0.74l-0.74-0.74l-9.35-9.35c-0.59-0.59-0.59-1.56,0-2.15
|
||||
C19.31,112.29,19.69,112.13,20.1,112.13 M20.1,111.08c-0.66,0-1.31,0.25-1.82,0.75c-1,1-1,2.63,0,3.63l9.35,9.35l-9.15,9.15
|
||||
c-1,1-1,2.63,0,3.63c0.5,0.5,1.16,0.75,1.82,0.75s1.31-0.25,1.82-0.75l12.78-12.78l-12.98-12.98
|
||||
C21.41,111.34,20.76,111.08,20.1,111.08L20.1,111.08z"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
50
interface/resources/images/home.svg
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 150" style="enable-background:new 0 0 50 150;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#414042;}
|
||||
.st1{fill:#CCCCCC;}
|
||||
.st2{fill:#1398BB;}
|
||||
.st3{fill:#31D8FF;}
|
||||
</style>
|
||||
<g id="Layer_1">
|
||||
<g>
|
||||
<path class="st0" d="M36.75,71.26c-0.05-0.31-0.22-0.57-0.47-0.76l-10.72-7.73l-8.21,5.71l0,0l0,0l-2.98,1.99
|
||||
c-0.26,0.17-0.43,0.43-0.5,0.74c-0.06,0.3,0,0.61,0.17,0.87c0.22,0.32,0.58,0.52,0.97,0.52c0.23,0,0.45-0.07,0.64-0.19l2.99-1.99
|
||||
l6.62-4.61l0.28-0.2l0.28,0.2l9.11,6.57c0.2,0.14,0.43,0.22,0.68,0.22c0.37,0,0.73-0.18,0.94-0.48
|
||||
C36.73,71.88,36.8,71.57,36.75,71.26z"/>
|
||||
<path class="st0" d="M23.59,79.62c0-1.13,0.97-2.03,2.1-2.03c1.13,0,2.1,0.93,2.1,2.06v6.06h7.58v-8.97l-3.28-2.41l-6.46-4.68
|
||||
l-6.88,4.64l-3.23,2.1v9.32h8.07V79.62z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M36.75,19.64c-0.05-0.31-0.22-0.57-0.47-0.76l-10.72-7.73l-8.21,5.71l0,0l0,0l-2.98,1.99
|
||||
c-0.26,0.17-0.43,0.43-0.5,0.74c-0.06,0.3,0,0.61,0.17,0.87c0.22,0.32,0.58,0.52,0.97,0.52c0.23,0,0.45-0.07,0.64-0.19l2.99-1.99
|
||||
l6.62-4.61l0.28-0.2l0.28,0.2l9.11,6.57c0.2,0.14,0.43,0.22,0.68,0.22c0.37,0,0.73-0.18,0.94-0.48
|
||||
C36.73,20.25,36.8,19.94,36.75,19.64z"/>
|
||||
<path class="st1" d="M23.59,27.99c0-1.13,0.97-2.03,2.1-2.03c1.13,0,2.1,0.93,2.1,2.06v6.06h7.58v-8.97l-3.28-2.41l-6.46-4.68
|
||||
l-6.88,4.64l-3.23,2.1v9.32h8.07V27.99z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M15,122.61c-0.53,0-1.05-0.26-1.37-0.73c-0.5-0.76-0.3-1.78,0.46-2.28l2.98-1.99l8.5-5.91l11,7.93
|
||||
c0.74,0.53,0.9,1.56,0.37,2.3c-0.53,0.74-1.56,0.9-2.3,0.37l-9.11-6.57l-6.63,4.61l-3,1.99C15.63,122.52,15.31,122.61,15,122.61z"
|
||||
/>
|
||||
<path class="st2" d="M35.6,122.69c-0.36,0-0.71-0.11-1.01-0.33l-9.07-6.53l-6.58,4.58l-3,2c-0.28,0.19-0.61,0.29-0.95,0.29
|
||||
c-0.58,0-1.12-0.29-1.44-0.77c-0.53-0.79-0.31-1.87,0.48-2.39l2.98-1.99l8.54-5.94l11.05,7.96c0.37,0.27,0.62,0.67,0.69,1.12
|
||||
c0.07,0.46-0.03,0.91-0.3,1.29c-0.27,0.37-0.67,0.62-1.12,0.69C35.79,122.68,35.7,122.69,35.6,122.69z M25.53,115.63l9.16,6.6
|
||||
c0.34,0.24,0.75,0.34,1.17,0.28s0.77-0.29,1.02-0.63c0.24-0.34,0.34-0.75,0.28-1.17c-0.07-0.41-0.29-0.77-0.63-1.02l-10.96-7.9
|
||||
l-8.45,5.88l-2.98,1.99c-0.72,0.48-0.91,1.45-0.44,2.17c0.29,0.44,0.78,0.7,1.3,0.7c0.31,0,0.61-0.09,0.87-0.26l3-1.99
|
||||
L25.53,115.63z"/>
|
||||
</g>
|
||||
<path class="st3" d="M36.62,119.56l-11.05-7.96l-8.54,5.94l-2.98,1.99c-0.79,0.53-1.01,1.6-0.48,2.39
|
||||
c0.32,0.48,0.86,0.77,1.44,0.77c0.34,0,0.67-0.1,0.95-0.29l3-2l6.58-4.58l9.07,6.53c0.3,0.21,0.64,0.33,1.01,0.33
|
||||
c0.55,0,1.08-0.27,1.4-0.72C37.56,121.2,37.39,120.12,36.62,119.56z M17.39,118.07l8.17-5.68l10.68,7.7
|
||||
c0.23,0.17,0.39,0.42,0.43,0.7c0.05,0.28-0.02,0.57-0.19,0.8c-0.2,0.28-0.53,0.45-0.88,0.45c-0.23,0-0.45-0.07-0.63-0.2l-9.44-6.8
|
||||
l-6.95,4.83l-2.99,1.99c-0.18,0.12-0.38,0.18-0.6,0.18c-0.36,0-0.7-0.18-0.9-0.48c-0.16-0.24-0.22-0.53-0.16-0.81
|
||||
c0.06-0.28,0.22-0.53,0.46-0.69L17.39,118.07z"/>
|
||||
<path class="st2" d="M23.59,129.14c0-1.13,0.97-2.03,2.1-2.03c1.13,0,2.1,0.93,2.1,2.06v6.06h7.58v-8.97l-3.28-2.41l-6.46-4.68
|
||||
l-6.88,4.64l-3.23,2.1v9.32h8.07V129.14z"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
33
interface/resources/images/places.svg
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 150" style="enable-background:new 0 0 50 150;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#414042;}
|
||||
.st1{fill:#CCCCCC;}
|
||||
.st2{fill:#1398BB;}
|
||||
.st3{fill:#31D8FF;}
|
||||
</style>
|
||||
<g id="Layer_1">
|
||||
<path class="st0" d="M25.71,59.91c-4.83,0-8.75,3.92-8.75,8.75c0,4.83,8.75,16.5,8.75,16.5s8.75-11.67,8.75-16.5
|
||||
C34.46,63.83,30.54,59.91,25.71,59.91z M25.71,72.53c-2.48,0-4.5-2.01-4.5-4.5c0-2.48,2.01-4.5,4.5-4.5s4.5,2.01,4.5,4.5
|
||||
C30.2,70.52,28.19,72.53,25.71,72.53z"/>
|
||||
<path class="st0" d="M36.43,86.1H14.28c-0.49,0-0.9,0.39-0.9,0.89c0,0.49,0.4,0.89,0.9,0.89h22.15c0.49,0,0.9-0.39,0.9-0.89
|
||||
C37.32,86.49,36.92,86.1,36.43,86.1z"/>
|
||||
<path class="st1" d="M25.71,8.28c-4.83,0-8.75,3.92-8.75,8.75c0,4.83,8.75,16.5,8.75,16.5s8.75-11.67,8.75-16.5
|
||||
C34.46,12.2,30.54,8.28,25.71,8.28z M25.71,20.91c-2.48,0-4.5-2.01-4.5-4.5c0-2.48,2.01-4.5,4.5-4.5s4.5,2.01,4.5,4.5
|
||||
C30.2,18.89,28.19,20.91,25.71,20.91z"/>
|
||||
<path class="st1" d="M36.43,34.47H14.28c-0.49,0-0.9,0.39-0.9,0.89s0.4,0.89,0.9,0.89h22.15c0.49,0,0.9-0.39,0.9-0.89
|
||||
S36.92,34.47,36.43,34.47z"/>
|
||||
<path class="st2" d="M25.71,109.66c-4.83,0-8.75,3.92-8.75,8.75c0,4.83,8.75,16.5,8.75,16.5s8.75-11.67,8.75-16.5
|
||||
C34.46,113.57,30.54,109.66,25.71,109.66z M25.71,122.28c-2.48,0-4.5-2.01-4.5-4.5c0-2.48,2.01-4.5,4.5-4.5s4.5,2.01,4.5,4.5
|
||||
C30.2,120.27,28.19,122.28,25.71,122.28z"/>
|
||||
<path class="st2" d="M36.43,138.33H14.28c-0.85,0-1.54-0.69-1.54-1.54s0.69-1.54,1.54-1.54h22.15c0.85,0,1.54,0.69,1.54,1.54
|
||||
S37.28,138.33,36.43,138.33z"/>
|
||||
<path class="st3" d="M36.43,135.89c0.49,0,0.9,0.4,0.9,0.9c0,0.49-0.4,0.9-0.9,0.9H14.28c-0.49,0-0.9-0.4-0.9-0.9
|
||||
c0-0.49,0.4-0.9,0.9-0.9H36.43 M36.43,135.25H14.28c-0.85,0-1.54,0.69-1.54,1.54c0,0.85,0.69,1.54,1.54,1.54h22.15
|
||||
c0.85,0,1.54-0.69,1.54-1.54C37.97,135.94,37.28,135.25,36.43,135.25L36.43,135.25z"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2 KiB |
41
interface/resources/images/snap-feed.svg
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 150" style="enable-background:new 0 0 50 150;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#414042;}
|
||||
.st1{fill:#CCCCCC;}
|
||||
.st2{fill:#1398BB;}
|
||||
.st3{fill:#31D8FF;}
|
||||
</style>
|
||||
<g id="Layer_1">
|
||||
<circle class="st0" cx="26.54" cy="69.8" r="4.44"/>
|
||||
<path class="st0" d="M34.87,82.89c0.01-0.01,0.02-0.02,0.03-0.03v-1.86c0-2.68-2.33-4.77-5-4.77h-6.42c-2.68,0-4.85,2.09-4.85,4.77
|
||||
v1.88H34.87z"/>
|
||||
<path class="st0" d="M44.17,67.05c0-3.97-3.22-7.19-7.19-7.19H16.67c-3.97,0-7.19,3.22-7.19,7.19v14.18c0,3.97,3.22,7.19,7.19,7.19
|
||||
h20.31c3.97,0,7.19-3.22,7.19-7.19V67.05z M42.39,81.32c0,3.03-2.46,5.49-5.49,5.49H16.58c-3.03,0-5.49-2.46-5.49-5.49v-14.2
|
||||
c0-3.03,2.46-5.49,5.49-5.49h20.33c3.03,0,5.49,2.46,5.49,5.49V81.32z"/>
|
||||
<circle class="st1" cx="26.54" cy="18.17" r="4.44"/>
|
||||
<path class="st1" d="M34.87,31.26c0.01-0.01,0.02-0.02,0.03-0.03v-1.86c0-2.68-2.33-4.77-5-4.77h-6.42c-2.68,0-4.85,2.09-4.85,4.77
|
||||
v1.88H34.87z"/>
|
||||
<path class="st1" d="M44.17,15.42c0-3.97-3.22-7.19-7.19-7.19H16.67c-3.97,0-7.19,3.22-7.19,7.19V29.6c0,3.97,3.22,7.19,7.19,7.19
|
||||
h20.31c3.97,0,7.19-3.22,7.19-7.19V15.42z M42.39,29.69c0,3.03-2.46,5.49-5.49,5.49H16.58c-3.03,0-5.49-2.46-5.49-5.49V15.5
|
||||
c0-3.03,2.46-5.49,5.49-5.49h20.33c3.03,0,5.49,2.46,5.49,5.49V29.69z"/>
|
||||
<circle class="st2" cx="26.52" cy="119.45" r="4.44"/>
|
||||
<path class="st2" d="M34.85,132.54c0.01-0.01,0.02-0.02,0.03-0.03v-1.86c0-2.68-2.33-4.77-5-4.77h-6.42
|
||||
c-2.68,0-4.85,2.09-4.85,4.77v1.88H34.85z"/>
|
||||
<g>
|
||||
<path class="st2" d="M36.9,138.76H16.6c-4.32,0-7.83-3.51-7.83-7.83v-14.16c0-4.32,3.51-7.83,7.83-7.83h20.3
|
||||
c4.32,0,7.83,3.51,7.83,7.83v14.16C44.73,135.25,41.22,138.76,36.9,138.76z M16.6,111.93c-2.67,0-4.84,2.17-4.84,4.84v14.16
|
||||
c0,2.67,2.17,4.84,4.84,4.84h20.3c2.67,0,4.84-2.17,4.84-4.84v-14.16c0-2.67-2.17-4.84-4.84-4.84H16.6z"/>
|
||||
<path class="st3" d="M36.9,109.58c3.96,0,7.19,3.22,7.19,7.19v14.16c0,3.96-3.22,7.19-7.19,7.19H16.6c-3.96,0-7.19-3.22-7.19-7.19
|
||||
v-14.16c0-3.96,3.22-7.19,7.19-7.19H36.9 M16.6,136.42h20.3c3.02,0,5.49-2.46,5.49-5.49v-14.16c0-3.02-2.46-5.49-5.49-5.49H16.6
|
||||
c-3.02,0-5.49,2.46-5.49,5.49v14.16C11.11,133.95,13.57,136.42,16.6,136.42 M36.9,108.93H16.6c-4.32,0-7.83,3.51-7.83,7.83v14.16
|
||||
c0,4.32,3.51,7.83,7.83,7.83h20.3c4.32,0,7.83-3.51,7.83-7.83v-14.16C44.73,112.45,41.22,108.93,36.9,108.93L36.9,108.93z
|
||||
M16.6,135.77c-2.67,0-4.84-2.17-4.84-4.84v-14.16c0-2.67,2.17-4.84,4.84-4.84h20.3c2.67,0,4.84,2.17,4.84,4.84v14.16
|
||||
c0,2.67-2.17,4.84-4.84,4.84H16.6L16.6,135.77z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -14,6 +14,8 @@ import "controls"
|
|||
import "styles"
|
||||
import "windows"
|
||||
import "hifi"
|
||||
import "hifi/toolbars"
|
||||
import "controls-uit" as HifiControls
|
||||
|
||||
Window {
|
||||
id: root
|
||||
|
@ -45,50 +47,80 @@ Window {
|
|||
anchors.centerIn = parent;
|
||||
}
|
||||
|
||||
function resetAfterTeleport() {
|
||||
storyCardFrame.shown = root.shown = false;
|
||||
}
|
||||
function goCard(card) {
|
||||
addressLine.text = card.userStory.name;
|
||||
if (addressBarDialog.useFeed) {
|
||||
storyCardHTML.url = addressBarDialog.metaverseServerUrl + "/user_stories/" + card.storyId;
|
||||
storyCardFrame.shown = true;
|
||||
return;
|
||||
}
|
||||
addressLine.text = card.hifiUrl;
|
||||
toggleOrGo(true);
|
||||
}
|
||||
property var allDomains: [];
|
||||
property var suggestionChoices: [];
|
||||
property var domainsBaseUrl: null;
|
||||
property var allPlaces: [];
|
||||
property var allStories: [];
|
||||
property int cardWidth: 200;
|
||||
property int cardHeight: 152;
|
||||
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
|
||||
|
||||
AddressBarDialog {
|
||||
id: addressBarDialog
|
||||
implicitWidth: backgroundImage.width
|
||||
implicitHeight: backgroundImage.height
|
||||
// The buttons have their button state changed on hover, so we have to manually fix them up here
|
||||
onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0;
|
||||
onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0;
|
||||
onUseFeedChanged: updateFeedState();
|
||||
onReceivedHifiSchemeURL: resetAfterTeleport();
|
||||
|
||||
Row {
|
||||
ListModel { id: suggestions }
|
||||
|
||||
ListView {
|
||||
id: scroll
|
||||
width: backgroundImage.width;
|
||||
height: cardHeight;
|
||||
spacing: hifi.layout.spacing;
|
||||
clip: true;
|
||||
anchors {
|
||||
bottom: backgroundImage.top;
|
||||
bottomMargin: 2 * hifi.layout.spacing;
|
||||
right: backgroundImage.right;
|
||||
rightMargin: -104; // FIXME
|
||||
horizontalCenter: backgroundImage.horizontalCenter
|
||||
}
|
||||
spacing: hifi.layout.spacing;
|
||||
Card {
|
||||
id: s0;
|
||||
model: suggestions;
|
||||
orientation: ListView.Horizontal;
|
||||
delegate: Card {
|
||||
width: cardWidth;
|
||||
height: cardHeight;
|
||||
goFunction: goCard
|
||||
goFunction: goCard;
|
||||
userName: model.username;
|
||||
placeName: model.place_name;
|
||||
hifiUrl: model.place_name + model.path;
|
||||
imageUrl: model.image_url;
|
||||
thumbnail: model.thumbnail_url;
|
||||
action: model.action;
|
||||
timestamp: model.created_at;
|
||||
onlineUsers: model.online_users;
|
||||
storyId: model.metaverseId;
|
||||
hoverThunk: function () { ListView.view.currentIndex = index; }
|
||||
unhoverThunk: function () { ListView.view.currentIndex = -1; }
|
||||
}
|
||||
Card {
|
||||
id: s1;
|
||||
width: cardWidth;
|
||||
height: cardHeight;
|
||||
goFunction: goCard
|
||||
}
|
||||
Card {
|
||||
id: s2;
|
||||
width: cardWidth;
|
||||
height: cardHeight;
|
||||
goFunction: goCard
|
||||
highlightMoveDuration: -1;
|
||||
highlightMoveVelocity: -1;
|
||||
highlight: Rectangle { color: "transparent"; border.width: 4; border.color: "#1DB5ED"; z: 1; }
|
||||
leftMargin: 50; // Start the first item over by about the same amount as the last item peeks through on the other side.
|
||||
rightMargin: 50;
|
||||
}
|
||||
Image { // Just a visual indicator that the user can swipe the cards over to see more.
|
||||
source: "../images/Swipe-Icon-single.svg"
|
||||
width: 50;
|
||||
visible: suggestions.count > 3;
|
||||
anchors {
|
||||
right: scroll.right;
|
||||
verticalCenter: scroll.verticalCenter;
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: backgroundImage
|
||||
source: "../images/address-bar.svg"
|
||||
|
@ -97,64 +129,43 @@ Window {
|
|||
property int inputAreaHeight: 56.0 * root.scale // Height of the background's input area
|
||||
property int inputAreaStep: (height - inputAreaHeight) / 2
|
||||
|
||||
Image {
|
||||
ToolbarButton {
|
||||
id: homeButton
|
||||
source: "../images/home-button.svg"
|
||||
width: 29
|
||||
height: 26
|
||||
imageURL: "../images/home.svg"
|
||||
buttonState: 1
|
||||
defaultState: 1
|
||||
hoverState: 2
|
||||
onClicked: addressBarDialog.loadHome();
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: parent.height + 2 * hifi.layout.spacing
|
||||
leftMargin: homeButton.width / 2
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
addressBarDialog.loadHome()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: backArrow
|
||||
source: addressBarDialog.backEnabled ? "../images/left-arrow.svg" : "../images/left-arrow-disabled.svg"
|
||||
width: 22
|
||||
height: 26
|
||||
ToolbarButton {
|
||||
id: backArrow;
|
||||
imageURL: "../images/backward.svg";
|
||||
hoverState: addressBarDialog.backEnabled ? 2 : 0;
|
||||
defaultState: addressBarDialog.backEnabled ? 1 : 0;
|
||||
buttonState: addressBarDialog.backEnabled ? 1 : 0;
|
||||
onClicked: addressBarDialog.loadBack();
|
||||
anchors {
|
||||
left: homeButton.right
|
||||
leftMargin: 2 * hifi.layout.spacing
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
addressBarDialog.loadBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: forwardArrow
|
||||
source: addressBarDialog.forwardEnabled ? "../images/right-arrow.svg" : "../images/right-arrow-disabled.svg"
|
||||
width: 22
|
||||
height: 26
|
||||
ToolbarButton {
|
||||
id: forwardArrow;
|
||||
imageURL: "../images/forward.svg";
|
||||
hoverState: addressBarDialog.forwardEnabled ? 2 : 0;
|
||||
defaultState: addressBarDialog.forwardEnabled ? 1 : 0;
|
||||
buttonState: addressBarDialog.forwardEnabled ? 1 : 0;
|
||||
onClicked: addressBarDialog.loadForward();
|
||||
anchors {
|
||||
left: backArrow.right
|
||||
leftMargin: 2 * hifi.layout.spacing
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
addressBarDialog.loadForward()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME replace with TextField
|
||||
|
@ -162,20 +173,80 @@ Window {
|
|||
id: addressLine
|
||||
focus: true
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: parent.height + parent.height + hifi.layout.spacing * 7
|
||||
rightMargin: hifi.layout.spacing * 2
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: forwardArrow.right
|
||||
right: placesButton.left
|
||||
leftMargin: forwardArrow.width
|
||||
rightMargin: placesButton.width
|
||||
topMargin: parent.inputAreaStep + hifi.layout.spacing
|
||||
bottomMargin: parent.inputAreaStep + hifi.layout.spacing
|
||||
}
|
||||
font.pixelSize: hifi.fonts.pixelSize * root.scale * 0.75
|
||||
helperText: "Go to: place, @user, /path, network address"
|
||||
helperPixelSize: font.pixelSize * 0.75
|
||||
helperItalic: true
|
||||
onTextChanged: filterChoicesByText()
|
||||
}
|
||||
// These two are radio buttons.
|
||||
ToolbarButton {
|
||||
id: placesButton
|
||||
imageURL: "../images/places.svg"
|
||||
buttonState: 1
|
||||
defaultState: addressBarDialog.useFeed ? 0 : 1;
|
||||
hoverState: addressBarDialog.useFeed ? 2 : -1;
|
||||
onClicked: addressBarDialog.useFeed ? toggleFeed() : identity()
|
||||
anchors {
|
||||
right: feedButton.left;
|
||||
bottom: addressLine.bottom;
|
||||
}
|
||||
}
|
||||
ToolbarButton {
|
||||
id: feedButton;
|
||||
imageURL: "../images/snap-feed.svg";
|
||||
buttonState: 0
|
||||
defaultState: addressBarDialog.useFeed ? 1 : 0;
|
||||
hoverState: addressBarDialog.useFeed ? -1 : 2;
|
||||
onClicked: addressBarDialog.useFeed ? identity() : toggleFeed();
|
||||
anchors {
|
||||
right: parent.right;
|
||||
bottom: addressLine.bottom;
|
||||
rightMargin: feedButton.width / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Window {
|
||||
width: 938;
|
||||
height: 625;
|
||||
scale: 0.8 // Reset scale of Window to 1.0 (counteract address bar's scale value of 1.25)
|
||||
HifiControls.WebView {
|
||||
anchors.fill: parent;
|
||||
id: storyCardHTML;
|
||||
}
|
||||
id: storyCardFrame;
|
||||
|
||||
shown: false;
|
||||
destroyOnCloseButton: false;
|
||||
pinnable: false;
|
||||
|
||||
anchors {
|
||||
verticalCenter: backgroundImage.verticalCenter;
|
||||
horizontalCenter: scroll.horizontalCenter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function toggleFeed() {
|
||||
addressBarDialog.useFeed = !addressBarDialog.useFeed;
|
||||
updateFeedState();
|
||||
}
|
||||
function updateFeedState() {
|
||||
placesButton.buttonState = addressBarDialog.useFeed ? 0 : 1;
|
||||
feedButton.buttonState = addressBarDialog.useFeed ? 1 : 0;
|
||||
filterChoicesByText();
|
||||
}
|
||||
function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects.
|
||||
// TODO: make available to other .qml.
|
||||
var request = new XMLHttpRequest();
|
||||
|
@ -200,133 +271,217 @@ Window {
|
|||
request.open("GET", url, true);
|
||||
request.send();
|
||||
}
|
||||
// call iterator(element, icb) once for each element of array, and then cb(error) when icb(error) has been called by each iterator.
|
||||
// short-circuits if error. Note that iterator MUST be an asynchronous function. (Use setTimeout if necessary.)
|
||||
function asyncEach(array, iterator, cb) {
|
||||
var count = array.length;
|
||||
function icb(error) {
|
||||
if (!--count || error) {
|
||||
count = -1; // don't cb multiple times (e.g., if error)
|
||||
cb(error);
|
||||
}
|
||||
}
|
||||
function asyncMap(array, iterator, cb) {
|
||||
// call iterator(element, icb) once for each element of array, and then cb(error, mappedResult)
|
||||
// when icb(error, mappedElement) has been called by each iterator.
|
||||
// Calls to iterator are overlapped and may call icb in any order, but the mappedResults are collected in the same
|
||||
// order as the elements of the array.
|
||||
// Short-circuits if error. Note that iterator MUST be an asynchronous function. (Use setTimeout if necessary.)
|
||||
var count = array.length, results = [];
|
||||
if (!count) {
|
||||
return cb();
|
||||
return cb(null, results);
|
||||
}
|
||||
array.forEach(function (element) {
|
||||
iterator(element, icb);
|
||||
array.forEach(function (element, index) {
|
||||
if (count < 0) { // don't keep iterating after we short-circuit
|
||||
return;
|
||||
}
|
||||
iterator(element, function (error, mapped) {
|
||||
results[index] = mapped;
|
||||
if (error || !--count) {
|
||||
count = 0; // don't cb multiple times if error
|
||||
cb(error, results);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// Example:
|
||||
/*asyncMap([0, 1, 2, 3, 4, 5, 6], function (elt, icb) {
|
||||
console.log('called', elt);
|
||||
setTimeout(function () {
|
||||
console.log('answering', elt);
|
||||
icb(null, elt);
|
||||
}, Math.random() * 1000);
|
||||
}, console.log); */
|
||||
|
||||
function identity(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error)
|
||||
// This requests data for all the names at once, and just uses the first one to come back.
|
||||
// We might change this to check one at a time, which would be less requests and more latency.
|
||||
asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(identity), function (name, icb) {
|
||||
var url = "https://metaverse.highfidelity.com/api/v1/places/" + name;
|
||||
getRequest(url, function (error, json) {
|
||||
var previews = !error && json.data.place.previews;
|
||||
if (previews) {
|
||||
if (!domainInfo.thumbnail) { // just grab the first one
|
||||
domainInfo.thumbnail = previews.thumbnail;
|
||||
}
|
||||
if (!domainInfo.lobby) {
|
||||
domainInfo.lobby = previews.lobby;
|
||||
}
|
||||
}
|
||||
icb(error);
|
||||
});
|
||||
}, cb);
|
||||
function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey
|
||||
if (!error && (data.status === 'success')) {
|
||||
return;
|
||||
}
|
||||
if (!error) { // Create a message from the data
|
||||
error = data.status + ': ' + data.error;
|
||||
}
|
||||
if (typeof(error) === 'string') { // Make a proper Error object
|
||||
error = new Error(error);
|
||||
}
|
||||
error.message += ' in ' + url; // Include the url.
|
||||
cb(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
function getDomains(options, cb) { // cb(error, arrayOfData)
|
||||
if (!options.page) {
|
||||
options.page = 1;
|
||||
}
|
||||
if (!domainsBaseUrl) {
|
||||
var domainsOptions = [
|
||||
'open', // published hours handle now
|
||||
'active', // has at least one person connected. FIXME: really want any place that is verified accessible.
|
||||
// FIXME: really want places I'm allowed in, not just open ones.
|
||||
'restriction=open', // Not by whitelist, etc. FIXME: If logged in, add hifi to the restriction options, in order to include places that require login.
|
||||
// FIXME add maturity
|
||||
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
|
||||
'sort_by=users',
|
||||
'sort_order=desc',
|
||||
];
|
||||
domainsBaseUrl = "https://metaverse.highfidelity.com/api/v1/domains/all?" + domainsOptions.join('&');
|
||||
}
|
||||
var url = domainsBaseUrl + "&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers;
|
||||
getRequest(url, function (error, json) {
|
||||
if (!error && (json.status !== 'success')) {
|
||||
error = new Error("Bad response: " + JSON.stringify(json));
|
||||
}
|
||||
if (error) {
|
||||
error.message += ' for ' + url;
|
||||
return cb(error);
|
||||
}
|
||||
var domains = json.data.domains;
|
||||
if (json.current_page < json.total_pages) {
|
||||
options.page++;
|
||||
return getDomains(options, function (error, others) {
|
||||
cb(error, domains.concat(others));
|
||||
});
|
||||
}
|
||||
cb(null, domains);
|
||||
});
|
||||
}
|
||||
|
||||
function filterChoicesByText() {
|
||||
function fill1(target, data) {
|
||||
if (!data) {
|
||||
target.visible = false;
|
||||
function getPlace(placeData, cb) { // cb(error, side-effected-placeData), after adding path, thumbnails, and description
|
||||
var url = metaverseBase + 'places/' + placeData.place_name;
|
||||
getRequest(url, function (error, data) {
|
||||
if (handleError(url, error, data, cb)) {
|
||||
return;
|
||||
}
|
||||
console.log('suggestion:', JSON.stringify(data));
|
||||
target.userStory = data;
|
||||
target.image.source = data.lobby || target.defaultPicture;
|
||||
target.placeText = data.name;
|
||||
target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users');
|
||||
target.visible = true;
|
||||
}
|
||||
var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity);
|
||||
var filtered = !words.length ? suggestionChoices : allDomains.filter(function (domain) {
|
||||
var text = domain.names.concat(domain.tags).join(' ');
|
||||
if (domain.description) {
|
||||
text += domain.description;
|
||||
var place = data.data.place, previews = place.previews;
|
||||
placeData.path = place.path;
|
||||
if (previews && previews.thumbnail) {
|
||||
placeData.thumbnail_url = previews.thumbnail;
|
||||
}
|
||||
text = text.toUpperCase();
|
||||
return words.every(function (word) {
|
||||
return text.indexOf(word) >= 0;
|
||||
if (place.description) {
|
||||
placeData.description = place.description;
|
||||
placeData.searchText += ' ' + place.description.toUpperCase();
|
||||
}
|
||||
cb(error, placeData);
|
||||
});
|
||||
}
|
||||
function makeModelData(data, optionalPlaceName) { // create a new obj from data
|
||||
// ListModel elements will only ever have those properties that are defined by the first obj that is added.
|
||||
// So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story.
|
||||
var name = optionalPlaceName || data.place_name,
|
||||
tags = data.tags || [data.action, data.username],
|
||||
description = data.description || "",
|
||||
thumbnail_url = data.thumbnail_url || "",
|
||||
image_url = thumbnail_url;
|
||||
if (data.details) {
|
||||
try {
|
||||
image_url = JSON.parse(data.details).image_url || thumbnail_url;
|
||||
} catch (e) {
|
||||
console.log(name, "has bad details", data.details);
|
||||
}
|
||||
}
|
||||
return {
|
||||
place_name: name,
|
||||
username: data.username || "",
|
||||
path: data.path || "",
|
||||
created_at: data.created_at || "",
|
||||
action: data.action || "",
|
||||
thumbnail_url: thumbnail_url,
|
||||
image_url: image_url,
|
||||
|
||||
metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
|
||||
|
||||
tags: tags,
|
||||
description: description,
|
||||
online_users: data.online_users || 0,
|
||||
|
||||
searchText: [name].concat(tags, description || []).join(' ').toUpperCase()
|
||||
}
|
||||
}
|
||||
function mapDomainPlaces(domain, cb) { // cb(error, arrayOfDomainPlaceData)
|
||||
function addPlace(name, icb) {
|
||||
getPlace(makeModelData(domain, name), icb);
|
||||
}
|
||||
// IWBNI we could get these results in order with most-recent-entered first.
|
||||
// In any case, we don't really need to preserve the domain.names order in the results.
|
||||
asyncMap(domain.names || [], addPlace, cb);
|
||||
}
|
||||
|
||||
function suggestable(place) {
|
||||
if (addressBarDialog.useFeed) {
|
||||
return true;
|
||||
}
|
||||
return (place.place_name !== AddressManager.hostname) // Not our entry, but do show other entry points to current domain.
|
||||
&& place.thumbnail_url
|
||||
&& place.online_users // at least one present means it's actually online
|
||||
&& place.online_users <= 20;
|
||||
}
|
||||
function getDomainPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model
|
||||
// Each page of results is processed completely before we start on the next page.
|
||||
// For each page of domains, we process each domain in parallel, and for each domain, process each place name in parallel.
|
||||
// This gives us minimum latency within the page, but we do preserve the order within the page by using asyncMap and
|
||||
// only appending the collected results.
|
||||
var params = [
|
||||
'open', // published hours handle now
|
||||
// TBD: should determine if place is actually running?
|
||||
'restriction=open', // Not by whitelist, etc. TBD: If logged in, add hifi to the restriction options, in order to include places that require login?
|
||||
// TBD: add maturity?
|
||||
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
|
||||
'sort_by=users',
|
||||
'sort_order=desc',
|
||||
'page=' + pageNumber
|
||||
];
|
||||
var url = metaverseBase + 'domains/all?' + params.join('&');
|
||||
getRequest(url, function (error, data) {
|
||||
if (handleError(url, error, data, cb)) {
|
||||
return;
|
||||
}
|
||||
asyncMap(data.data.domains, mapDomainPlaces, function (error, pageResults) {
|
||||
if (error) {
|
||||
return cb(error);
|
||||
}
|
||||
// pageResults is now [ [ placeDataOneForDomainOne, placeDataTwoForDomainOne, ...], [ placeDataTwoForDomainTwo...] ]
|
||||
pageResults.forEach(function (domainResults) {
|
||||
allPlaces = allPlaces.concat(domainResults);
|
||||
if (!addressLine.text && !addressBarDialog.useFeed) { // Don't add if the user is already filtering
|
||||
domainResults.forEach(function (place) {
|
||||
if (suggestable(place)) {
|
||||
suggestions.append(place);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (data.current_page < data.total_pages) {
|
||||
return getDomainPage(pageNumber + 1, cb);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
});
|
||||
fill1(s0, filtered[0]);
|
||||
fill1(s1, filtered[1]);
|
||||
fill1(s2, filtered[2]);
|
||||
}
|
||||
function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model
|
||||
var url = metaverseBase + 'user_stories?page=' + pageNumber;
|
||||
getRequest(url, function (error, data) {
|
||||
if (handleError(url, error, data, cb)) {
|
||||
return;
|
||||
}
|
||||
var stories = data.user_stories.map(function (story) { // explicit single-argument function
|
||||
return makeModelData(story);
|
||||
});
|
||||
allStories = allStories.concat(stories);
|
||||
if (!addressLine.text && addressBarDialog.useFeed) { // Don't add if the user is already filtering
|
||||
stories.forEach(function (story) {
|
||||
suggestions.append(story);
|
||||
});
|
||||
}
|
||||
if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now
|
||||
return getUserStoryPage(pageNumber + 1, cb);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}
|
||||
function filterChoicesByText() {
|
||||
suggestions.clear();
|
||||
var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity),
|
||||
data = addressBarDialog.useFeed ? allStories : allPlaces;
|
||||
function matches(place) {
|
||||
if (!words.length) {
|
||||
return suggestable(place);
|
||||
}
|
||||
return words.every(function (word) {
|
||||
return place.searchText.indexOf(word) >= 0;
|
||||
});
|
||||
}
|
||||
data.forEach(function (place) {
|
||||
if (matches(place)) {
|
||||
suggestions.append(place);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fillDestinations() {
|
||||
allDomains = suggestionChoices = [];
|
||||
getDomains({minUsers: 0, maxUsers: 20}, function (error, domains) {
|
||||
if (error) {
|
||||
console.log('domain query failed:', error);
|
||||
return filterChoicesByText();
|
||||
}
|
||||
var here = AddressManager.hostname; // don't show where we are now.
|
||||
allDomains = domains.filter(function (domain) { return domain.name !== here; });
|
||||
// Whittle down suggestions to those that have at least one user, and try to get pictures.
|
||||
suggestionChoices = allDomains.filter(function (domain) { return domain.online_users; });
|
||||
asyncEach(domains, addPictureToDomain, function (error) {
|
||||
if (error) {
|
||||
console.log('place picture query failed:', error);
|
||||
}
|
||||
// Whittle down more by requiring a picture.
|
||||
suggestionChoices = suggestionChoices.filter(function (domain) { return domain.lobby; });
|
||||
filterChoicesByText();
|
||||
});
|
||||
allPlaces = [];
|
||||
allStories = [];
|
||||
suggestions.clear();
|
||||
getDomainPage(1, function (error) {
|
||||
console.log('domain query', error || 'ok', allPlaces.length);
|
||||
});
|
||||
getUserStoryPage(1, function (error) {
|
||||
console.log('user stories query', error || 'ok', allStories.length);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtWebEngine 1.1
|
||||
|
||||
WebEngineView {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
objectName: "webview"
|
||||
url: "about:blank"
|
||||
}
|
|
@ -21,6 +21,8 @@ Original.Button {
|
|||
width: 120
|
||||
height: hifi.dimensions.controlLineHeight
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
style: ButtonStyle {
|
||||
|
||||
background: Rectangle {
|
||||
|
|
|
@ -12,6 +12,8 @@ Original.TextInput {
|
|||
verticalAlignment: Original.TextInput.AlignVCenter
|
||||
font.family: hifi.fonts.fontFamily
|
||||
font.pixelSize: hifi.fonts.pixelSize
|
||||
property int helperPixelSize: font.pixelSize
|
||||
property bool helperItalic: false
|
||||
|
||||
/*
|
||||
Original.Rectangle {
|
||||
|
@ -23,7 +25,8 @@ Original.TextInput {
|
|||
*/
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
font.pixelSize: parent.font.pixelSize
|
||||
font.pixelSize: helperPixelSize
|
||||
font.italic: helperItalic
|
||||
font.family: parent.font.family
|
||||
verticalAlignment: parent.verticalAlignment
|
||||
horizontalAlignment: parent.horizontalAlignment
|
||||
|
|
|
@ -17,28 +17,69 @@ import QtGraphicalEffects 1.0
|
|||
import "../styles-uit"
|
||||
|
||||
Rectangle {
|
||||
property string userName: "";
|
||||
property string placeName: "";
|
||||
property string action: "";
|
||||
property string timestamp: "";
|
||||
property string hifiUrl: "";
|
||||
property string thumbnail: defaultThumbnail;
|
||||
property string imageUrl: "";
|
||||
property var goFunction: null;
|
||||
property var userStory: null;
|
||||
property alias image: lobby;
|
||||
property alias placeText: place.text;
|
||||
property alias usersText: users.text;
|
||||
property string storyId: "";
|
||||
|
||||
property string timePhrase: pastTime(timestamp);
|
||||
property string actionPhrase: makeActionPhrase(action);
|
||||
property int onlineUsers: 0;
|
||||
property bool isUserStory: userName && !onlineUsers;
|
||||
|
||||
property int textPadding: 20;
|
||||
property int textSize: 24;
|
||||
property string defaultPicture: "../../images/default-domain.gif";
|
||||
property int textSizeSmall: 18;
|
||||
property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif");
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
function pastTime(timestamp) { // Answer a descriptive string
|
||||
timestamp = new Date(timestamp);
|
||||
var then = timestamp.getTime(),
|
||||
now = Date.now(),
|
||||
since = now - then,
|
||||
ONE_MINUTE = 1000 * 60,
|
||||
ONE_HOUR = ONE_MINUTE * 60,
|
||||
hours = since / ONE_HOUR,
|
||||
minutes = (hours % 1) * 60;
|
||||
if (hours > 24) {
|
||||
return timestamp.toDateString();
|
||||
}
|
||||
if (hours > 1) {
|
||||
return Math.floor(hours).toString() + ' hr ' + Math.floor(minutes) + ' min ago';
|
||||
}
|
||||
if (minutes >= 2) {
|
||||
return Math.floor(minutes).toString() + ' min ago';
|
||||
}
|
||||
return 'about a minute ago';
|
||||
}
|
||||
function makeActionPhrase(actionLabel) {
|
||||
switch (actionLabel) {
|
||||
case "snapshot":
|
||||
return "took a snapshot";
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: lobby;
|
||||
width: parent.width;
|
||||
height: parent.height;
|
||||
source: defaultPicture;
|
||||
source: thumbnail || defaultThumbnail;
|
||||
fillMode: Image.PreserveAspectCrop;
|
||||
// source gets filled in later
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
anchors.left: parent.left;
|
||||
onStatusChanged: {
|
||||
if (status == Image.Error) {
|
||||
console.log("source: " + source + ": failed to load " + JSON.stringify(userStory));
|
||||
source = defaultPicture;
|
||||
console.log("source: " + source + ": failed to load " + hifiUrl);
|
||||
source = defaultThumbnail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +110,7 @@ Rectangle {
|
|||
}
|
||||
RalewaySemiBold {
|
||||
id: place;
|
||||
text: isUserStory ? "" : placeName;
|
||||
color: hifi.colors.white;
|
||||
size: textSize;
|
||||
anchors {
|
||||
|
@ -79,7 +121,8 @@ Rectangle {
|
|||
}
|
||||
RalewayRegular {
|
||||
id: users;
|
||||
size: textSize;
|
||||
text: isUserStory ? timePhrase : (onlineUsers + ((onlineUsers === 1) ? ' person' : ' people'));
|
||||
size: textSizeSmall;
|
||||
color: hifi.colors.white;
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
|
@ -87,10 +130,18 @@ Rectangle {
|
|||
margins: textPadding;
|
||||
}
|
||||
}
|
||||
// These two can be supplied to provide hover behavior.
|
||||
// For example, AddressBarDialog provides functions that set the current list view item
|
||||
// to that which is being hovered over.
|
||||
property var hoverThunk: function () { };
|
||||
property var unhoverThunk: function () { };
|
||||
MouseArea {
|
||||
id: zmouseArea;
|
||||
anchors.fill: parent;
|
||||
acceptedButtons: Qt.LeftButton;
|
||||
onClicked: goFunction(parent);
|
||||
hoverEnabled: true;
|
||||
onEntered: hoverThunk();
|
||||
onExited: unhoverThunk();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1067,70 +1067,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
// If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity,
|
||||
[this, entityScriptingInterface](const EntityItemID& entityItemID, const MouseEvent& event) {
|
||||
if (_keyboardFocusedItem != entityItemID) {
|
||||
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
||||
auto properties = entityScriptingInterface->getEntityProperties(entityItemID);
|
||||
if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) {
|
||||
auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID);
|
||||
RenderableWebEntityItem* webEntity = dynamic_cast<RenderableWebEntityItem*>(entity.get());
|
||||
if (webEntity) {
|
||||
webEntity->setProxyWindow(_window->windowHandle());
|
||||
if (_keyboardMouseDevice->isActive()) {
|
||||
_keyboardMouseDevice->pluginFocusOutEvent();
|
||||
}
|
||||
_keyboardFocusedItem = entityItemID;
|
||||
_lastAcceptedKeyPress = usecTimestampNow();
|
||||
if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) {
|
||||
_keyboardFocusHighlight = new Cube3DOverlay();
|
||||
_keyboardFocusHighlight->setAlpha(1.0f);
|
||||
_keyboardFocusHighlight->setBorderSize(1.0f);
|
||||
_keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 });
|
||||
_keyboardFocusHighlight->setIsSolid(false);
|
||||
_keyboardFocusHighlight->setPulseMin(0.5);
|
||||
_keyboardFocusHighlight->setPulseMax(1.0);
|
||||
_keyboardFocusHighlight->setColorPulse(1.0);
|
||||
_keyboardFocusHighlight->setIgnoreRayIntersection(true);
|
||||
_keyboardFocusHighlight->setDrawInFront(true);
|
||||
}
|
||||
_keyboardFocusHighlight->setRotation(webEntity->getRotation());
|
||||
_keyboardFocusHighlight->setPosition(webEntity->getPosition());
|
||||
_keyboardFocusHighlight->setDimensions(webEntity->getDimensions() * 1.05f);
|
||||
_keyboardFocusHighlight->setVisible(true);
|
||||
_keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight);
|
||||
}
|
||||
}
|
||||
if (_keyboardFocusedItem == UNKNOWN_ENTITY_ID && _keyboardFocusHighlight) {
|
||||
_keyboardFocusHighlight->setVisible(false);
|
||||
}
|
||||
|
||||
}
|
||||
[this](const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||
setKeyboardFocusEntity(entityItemID);
|
||||
});
|
||||
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity,
|
||||
[=](const EntityItemID& entityItemID) {
|
||||
if (entityItemID == _keyboardFocusedItem) {
|
||||
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
||||
if (_keyboardFocusHighlight) {
|
||||
_keyboardFocusHighlight->setVisible(false);
|
||||
}
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [=](const EntityItemID& entityItemID) {
|
||||
if (entityItemID == _keyboardFocusedItem.get()) {
|
||||
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
|
||||
}
|
||||
});
|
||||
|
||||
// If the user clicks somewhere where there is NO entity at all, we will release focus
|
||||
connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity,
|
||||
[=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event) {
|
||||
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
||||
if (_keyboardFocusHighlight) {
|
||||
_keyboardFocusHighlight->setVisible(false);
|
||||
}
|
||||
connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity, [=]() {
|
||||
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
|
||||
});
|
||||
|
||||
connect(this, &Application::aboutToQuit, [=]() {
|
||||
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
||||
if (_keyboardFocusHighlight) {
|
||||
_keyboardFocusHighlight->setVisible(false);
|
||||
}
|
||||
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
|
||||
});
|
||||
|
||||
// Add periodic checks to send user activity data
|
||||
|
@ -1403,11 +1356,13 @@ void Application::cleanupBeforeQuit() {
|
|||
// FIXME: once we move to shared pointer for the INputDevice we shoud remove this naked delete:
|
||||
_applicationStateDevice.reset();
|
||||
|
||||
if (_keyboardFocusHighlightID > 0) {
|
||||
getOverlays().deleteOverlay(_keyboardFocusHighlightID);
|
||||
_keyboardFocusHighlightID = -1;
|
||||
{
|
||||
if (_keyboardFocusHighlightID > 0) {
|
||||
getOverlays().deleteOverlay(_keyboardFocusHighlightID);
|
||||
_keyboardFocusHighlightID = -1;
|
||||
}
|
||||
_keyboardFocusHighlight = nullptr;
|
||||
}
|
||||
_keyboardFocusHighlight = nullptr;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
@ -2122,16 +2077,16 @@ bool Application::event(QEvent* event) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!_keyboardFocusedItem.isInvalidID()) {
|
||||
switch (event->type()) {
|
||||
{
|
||||
if (!_keyboardFocusedItem.get().isInvalidID()) {
|
||||
switch (event->type()) {
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease: {
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem);
|
||||
RenderableWebEntityItem* webEntity = dynamic_cast<RenderableWebEntityItem*>(entity.get());
|
||||
if (webEntity && webEntity->getEventHandler()) {
|
||||
auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedItem.get());
|
||||
if (entity && entity->getEventHandler()) {
|
||||
event->setAccepted(false);
|
||||
QCoreApplication::sendEvent(webEntity->getEventHandler(), event);
|
||||
QCoreApplication::sendEvent(entity->getEventHandler(), event);
|
||||
if (event->isAccepted()) {
|
||||
_lastAcceptedKeyPress = usecTimestampNow();
|
||||
return true;
|
||||
|
@ -2142,6 +2097,7 @@ bool Application::event(QEvent* event) {
|
|||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2340,7 +2296,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
} else if (isOption && !isShifted && !isMeta) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::ScriptEditor);
|
||||
} else if (!isOption && !isShifted && isMeta) {
|
||||
takeSnapshot();
|
||||
takeSnapshot(true);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -2691,7 +2647,10 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
|||
event->screenPos(), button,
|
||||
buttons, event->modifiers());
|
||||
|
||||
getEntities()->mouseMoveEvent(&mappedEvent);
|
||||
if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() ||
|
||||
getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y()))) {
|
||||
getEntities()->mouseMoveEvent(&mappedEvent);
|
||||
}
|
||||
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
|
@ -2981,14 +2940,6 @@ void Application::idle(float nsecsElapsed) {
|
|||
_simCounter.increment();
|
||||
|
||||
PerformanceTimer perfTimer("idle");
|
||||
// Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds
|
||||
if (!_keyboardFocusedItem.isInvalidID()) {
|
||||
const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus
|
||||
quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress;
|
||||
if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) {
|
||||
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
||||
}
|
||||
}
|
||||
|
||||
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
|
||||
// details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing
|
||||
|
@ -3002,6 +2953,27 @@ void Application::idle(float nsecsElapsed) {
|
|||
static const float BIGGEST_DELTA_TIME_SECS = 0.25f;
|
||||
update(glm::clamp(secondsSinceLastUpdate, 0.0f, BIGGEST_DELTA_TIME_SECS));
|
||||
}
|
||||
|
||||
|
||||
// Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds
|
||||
{
|
||||
if (!_keyboardFocusedItem.get().isInvalidID()) {
|
||||
const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus
|
||||
quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress;
|
||||
if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) {
|
||||
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
|
||||
} else {
|
||||
// update position of highlight overlay
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedItem.get());
|
||||
if (entity && _keyboardFocusHighlight) {
|
||||
_keyboardFocusHighlight->setRotation(entity->getRotation());
|
||||
_keyboardFocusHighlight->setPosition(entity->getPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("pluginIdle");
|
||||
PerformanceWarning warn(showWarnings, "Application::idle()... pluginIdle()");
|
||||
|
@ -3586,6 +3558,54 @@ void Application::rotationModeChanged() const {
|
|||
}
|
||||
}
|
||||
|
||||
QUuid Application::getKeyboardFocusEntity() const {
|
||||
return _keyboardFocusedItem.get();
|
||||
}
|
||||
|
||||
void Application::setKeyboardFocusEntity(QUuid id) {
|
||||
EntityItemID entityItemID(id);
|
||||
setKeyboardFocusEntity(entityItemID);
|
||||
}
|
||||
|
||||
void Application::setKeyboardFocusEntity(EntityItemID entityItemID) {
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
if (_keyboardFocusedItem.get() != entityItemID) {
|
||||
_keyboardFocusedItem.set(UNKNOWN_ENTITY_ID);
|
||||
auto properties = entityScriptingInterface->getEntityProperties(entityItemID);
|
||||
if (!properties.getLocked() && properties.getVisible()) {
|
||||
auto entity = getEntities()->getTree()->findEntityByID(entityItemID);
|
||||
if (entity && entity->wantsKeyboardFocus()) {
|
||||
entity->setProxyWindow(_window->windowHandle());
|
||||
if (_keyboardMouseDevice->isActive()) {
|
||||
_keyboardMouseDevice->pluginFocusOutEvent();
|
||||
}
|
||||
_keyboardFocusedItem.set(entityItemID);
|
||||
_lastAcceptedKeyPress = usecTimestampNow();
|
||||
if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) {
|
||||
_keyboardFocusHighlight = new Cube3DOverlay();
|
||||
_keyboardFocusHighlight->setAlpha(1.0f);
|
||||
_keyboardFocusHighlight->setBorderSize(1.0f);
|
||||
_keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 });
|
||||
_keyboardFocusHighlight->setIsSolid(false);
|
||||
_keyboardFocusHighlight->setPulseMin(0.5);
|
||||
_keyboardFocusHighlight->setPulseMax(1.0);
|
||||
_keyboardFocusHighlight->setColorPulse(1.0);
|
||||
_keyboardFocusHighlight->setIgnoreRayIntersection(true);
|
||||
_keyboardFocusHighlight->setDrawInFront(false);
|
||||
}
|
||||
_keyboardFocusHighlight->setRotation(entity->getRotation());
|
||||
_keyboardFocusHighlight->setPosition(entity->getPosition());
|
||||
_keyboardFocusHighlight->setDimensions(entity->getDimensions() * 1.05f);
|
||||
_keyboardFocusHighlight->setVisible(true);
|
||||
_keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight);
|
||||
}
|
||||
}
|
||||
if (_keyboardFocusedItem.get() == UNKNOWN_ENTITY_ID && _keyboardFocusHighlight) {
|
||||
_keyboardFocusHighlight->setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateDialogs(float deltaTime) const {
|
||||
PerformanceTimer perfTimer("updateDialogs");
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
|
@ -4893,6 +4913,7 @@ bool Application::canAcceptURL(const QString& urlString) const {
|
|||
bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
|
||||
if (urlString.startsWith(HIFI_URL_SCHEME)) {
|
||||
// this is a hifi URL - have the AddressManager handle it
|
||||
emit receivedHifiSchemeURL(urlString);
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString",
|
||||
Qt::AutoConnection, Q_ARG(const QString&, urlString));
|
||||
return true;
|
||||
|
@ -4941,7 +4962,7 @@ bool Application::askToSetAvatarUrl(const QString& url) {
|
|||
modelLicense = simpleWordWrap(modelLicense, MAX_CHARACTERS_PER_LINE);
|
||||
|
||||
agreeToLicence = QMessageBox::Yes == OffscreenUi::question("Avatar Usage License",
|
||||
modelLicense + "\nDo you argee to these terms?",
|
||||
modelLicense + "\nDo you agree to these terms?",
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
}
|
||||
|
||||
|
@ -5169,15 +5190,24 @@ void Application::toggleLogDialog() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::takeSnapshot() {
|
||||
QMediaPlayer* player = new QMediaPlayer();
|
||||
QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
|
||||
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||
player->play();
|
||||
void Application::takeSnapshot(bool notify, float aspectRatio) {
|
||||
postLambdaEvent([notify, aspectRatio, this] {
|
||||
QMediaPlayer* player = new QMediaPlayer();
|
||||
QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
|
||||
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||
player->play();
|
||||
|
||||
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot());
|
||||
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio));
|
||||
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotTaken(path);
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotTaken(path, notify);
|
||||
});
|
||||
}
|
||||
|
||||
void Application::shareSnapshot(const QString& path) {
|
||||
postLambdaEvent([path] {
|
||||
// not much to do here, everything is done in snapshot code...
|
||||
Snapshot::uploadSnapshot(path);
|
||||
});
|
||||
}
|
||||
|
||||
float Application::getRenderResolutionScale() const {
|
||||
|
@ -5642,3 +5672,48 @@ bool Application::makeRenderingContextCurrent() {
|
|||
bool Application::isForeground() const {
|
||||
return _isForeground && !_window->isMinimized();
|
||||
}
|
||||
|
||||
void Application::sendMousePressOnEntity(QUuid id, PointerEvent event) {
|
||||
EntityItemID entityItemID(id);
|
||||
emit getEntities()->mousePressOnEntity(entityItemID, event);
|
||||
}
|
||||
|
||||
void Application::sendMouseMoveOnEntity(QUuid id, PointerEvent event) {
|
||||
EntityItemID entityItemID(id);
|
||||
emit getEntities()->mouseMoveOnEntity(entityItemID, event);
|
||||
}
|
||||
|
||||
void Application::sendMouseReleaseOnEntity(QUuid id, PointerEvent event) {
|
||||
EntityItemID entityItemID(id);
|
||||
emit getEntities()->mouseReleaseOnEntity(entityItemID, event);
|
||||
}
|
||||
|
||||
void Application::sendClickDownOnEntity(QUuid id, PointerEvent event) {
|
||||
EntityItemID entityItemID(id);
|
||||
emit getEntities()->clickDownOnEntity(entityItemID, event);
|
||||
}
|
||||
|
||||
void Application::sendHoldingClickOnEntity(QUuid id, PointerEvent event) {
|
||||
EntityItemID entityItemID(id);
|
||||
emit getEntities()->holdingClickOnEntity(entityItemID, event);
|
||||
}
|
||||
|
||||
void Application::sendClickReleaseOnEntity(QUuid id, PointerEvent event) {
|
||||
EntityItemID entityItemID(id);
|
||||
emit getEntities()->clickReleaseOnEntity(entityItemID, event);
|
||||
}
|
||||
|
||||
void Application::sendHoverEnterEntity(QUuid id, PointerEvent event) {
|
||||
EntityItemID entityItemID(id);
|
||||
emit getEntities()->hoverEnterEntity(entityItemID, event);
|
||||
}
|
||||
|
||||
void Application::sendHoverOverEntity(QUuid id, PointerEvent event) {
|
||||
EntityItemID entityItemID(id);
|
||||
emit getEntities()->hoverOverEntity(entityItemID, event);
|
||||
}
|
||||
|
||||
void Application::sendHoverLeaveEntity(QUuid id, PointerEvent event) {
|
||||
EntityItemID entityItemID(id);
|
||||
emit getEntities()->hoverLeaveEntity(entityItemID, event);
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <ViewFrustum.h>
|
||||
#include <AbstractUriHandler.h>
|
||||
#include <shared/RateCounter.h>
|
||||
#include <ThreadSafeValueCache.h>
|
||||
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "Bookmarks.h"
|
||||
|
@ -249,11 +250,26 @@ public:
|
|||
|
||||
float getAvatarSimrate() const { return _avatarSimCounter.rate(); }
|
||||
float getAverageSimsPerSecond() const { return _simCounter.rate(); }
|
||||
|
||||
void takeSnapshot(bool notify, float aspectRatio = 0.0f);
|
||||
void shareSnapshot(const QString& filename);
|
||||
|
||||
model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
|
||||
gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture; }
|
||||
gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; }
|
||||
|
||||
Q_INVOKABLE void sendMousePressOnEntity(QUuid id, PointerEvent event);
|
||||
Q_INVOKABLE void sendMouseMoveOnEntity(QUuid id, PointerEvent event);
|
||||
Q_INVOKABLE void sendMouseReleaseOnEntity(QUuid id, PointerEvent event);
|
||||
|
||||
Q_INVOKABLE void sendClickDownOnEntity(QUuid id, PointerEvent event);
|
||||
Q_INVOKABLE void sendHoldingClickOnEntity(QUuid id, PointerEvent event);
|
||||
Q_INVOKABLE void sendClickReleaseOnEntity(QUuid id, PointerEvent event);
|
||||
|
||||
Q_INVOKABLE void sendHoverEnterEntity(QUuid id, PointerEvent event);
|
||||
Q_INVOKABLE void sendHoverOverEntity(QUuid id, PointerEvent event);
|
||||
Q_INVOKABLE void sendHoverLeaveEntity(QUuid id, PointerEvent event);
|
||||
|
||||
signals:
|
||||
void svoImportRequested(const QString& url);
|
||||
|
||||
|
@ -263,6 +279,7 @@ signals:
|
|||
void activeDisplayPluginChanged();
|
||||
|
||||
void uploadRequest(QString path);
|
||||
void receivedHifiSchemeURL(const QString& url);
|
||||
|
||||
public slots:
|
||||
QVector<EntityItemID> pasteEntities(float x, float y, float z);
|
||||
|
@ -319,6 +336,10 @@ public slots:
|
|||
|
||||
static void runTests();
|
||||
|
||||
QUuid getKeyboardFocusEntity() const; // thread-safe
|
||||
void setKeyboardFocusEntity(QUuid id);
|
||||
void setKeyboardFocusEntity(EntityItemID entityItemID);
|
||||
|
||||
private slots:
|
||||
void showDesktop();
|
||||
void clearDomainOctreeDetails();
|
||||
|
@ -379,8 +400,6 @@ private:
|
|||
|
||||
int sendNackPackets();
|
||||
|
||||
void takeSnapshot();
|
||||
|
||||
MyAvatar* getMyAvatar() const;
|
||||
|
||||
void checkSkeleton() const;
|
||||
|
@ -531,7 +550,7 @@ private:
|
|||
|
||||
DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();
|
||||
|
||||
EntityItemID _keyboardFocusedItem;
|
||||
ThreadSafeValueCache<EntityItemID> _keyboardFocusedItem;
|
||||
quint64 _lastAcceptedKeyPress = 0;
|
||||
bool _isForeground = true; // starts out assumed to be in foreground
|
||||
bool _inPaint = false;
|
||||
|
|
|
@ -27,10 +27,6 @@ void ConnectionMonitor::init() {
|
|||
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &ConnectionMonitor::disconnectedFromDomain);
|
||||
connect(&domainHandler, &DomainHandler::connectedToDomain, this, &ConnectionMonitor::connectedToDomain);
|
||||
|
||||
// Connect to AddressManager::hostChanged
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
connect(addressManager.data(), &AddressManager::hostChanged, this, &ConnectionMonitor::hostChanged);
|
||||
|
||||
_timer.setSingleShot(true);
|
||||
_timer.setInterval(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS);
|
||||
_timer.start();
|
||||
|
@ -46,7 +42,3 @@ void ConnectionMonitor::disconnectedFromDomain() {
|
|||
void ConnectionMonitor::connectedToDomain(const QString& name) {
|
||||
_timer.stop();
|
||||
}
|
||||
|
||||
void ConnectionMonitor::hostChanged(const QString& name) {
|
||||
_timer.start();
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ public:
|
|||
private slots:
|
||||
void disconnectedFromDomain();
|
||||
void connectedToDomain(const QString& name);
|
||||
void hostChanged(const QString& name);
|
||||
|
||||
private:
|
||||
QTimer _timer;
|
||||
|
|
|
@ -48,7 +48,7 @@ signals:
|
|||
|
||||
protected:
|
||||
void rollFileIfNecessary(QFile& file, bool notifyListenersIfRolled = true);
|
||||
virtual bool processQueueItems(const Queue& messages);
|
||||
virtual bool processQueueItems(const Queue& messages) override;
|
||||
|
||||
private:
|
||||
const FileLogger& _logger;
|
||||
|
|
|
@ -21,9 +21,9 @@ public:
|
|||
virtual EntityActionPointer factory(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments);
|
||||
QVariantMap arguments) override;
|
||||
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
|
||||
QByteArray data);
|
||||
QByteArray data) override;
|
||||
};
|
||||
|
||||
#endif // hifi_InterfaceActionFactory_h
|
||||
|
|
|
@ -21,7 +21,8 @@ class InterfaceParentFinder : public SpatialParentFinder {
|
|||
public:
|
||||
InterfaceParentFinder() { }
|
||||
virtual ~InterfaceParentFinder() { }
|
||||
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const;
|
||||
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success,
|
||||
SpatialParentTree* entityTree = nullptr) const override;
|
||||
};
|
||||
|
||||
#endif // hifi_InterfaceParentFinder_h
|
||||
|
|
|
@ -24,23 +24,23 @@ class QPushButton;
|
|||
|
||||
class ModelSelector : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public:
|
||||
ModelSelector();
|
||||
|
||||
|
||||
QFileInfo getFileInfo() const;
|
||||
FSTReader::ModelType getModelType() const;
|
||||
|
||||
|
||||
public slots:
|
||||
virtual void accept();
|
||||
|
||||
virtual void accept() override;
|
||||
|
||||
private slots:
|
||||
void browse();
|
||||
|
||||
|
||||
private:
|
||||
QFileInfo _modelFile;
|
||||
QPushButton* _browseButton;
|
||||
QComboBox* _modelType;
|
||||
};
|
||||
|
||||
#endif // hifi_ModelSelector_h
|
||||
#endif // hifi_ModelSelector_h
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
};
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString& text);
|
||||
void highlightBlock(const QString& text) override;
|
||||
void highlightKeywords(const QString& text);
|
||||
void formatComments(const QString& text);
|
||||
void formatQuotedText(const QString& text);
|
||||
|
|
|
@ -82,12 +82,12 @@ public:
|
|||
void setDeltaRoll(float roll) { _deltaRoll = roll; }
|
||||
float getDeltaRoll() const { return _deltaRoll; }
|
||||
|
||||
virtual void setFinalYaw(float finalYaw);
|
||||
virtual void setFinalPitch(float finalPitch);
|
||||
virtual void setFinalRoll(float finalRoll);
|
||||
virtual float getFinalPitch() const;
|
||||
virtual float getFinalYaw() const;
|
||||
virtual float getFinalRoll() const;
|
||||
virtual void setFinalYaw(float finalYaw) override;
|
||||
virtual void setFinalPitch(float finalPitch) override;
|
||||
virtual void setFinalRoll(float finalRoll) override;
|
||||
virtual float getFinalPitch() const override;
|
||||
virtual float getFinalYaw() const override;
|
||||
virtual float getFinalRoll() const override;
|
||||
|
||||
void relax(float deltaTime);
|
||||
|
||||
|
|
|
@ -27,26 +27,26 @@
|
|||
class DdeFaceTracker : public FaceTracker, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
virtual void init();
|
||||
virtual void reset();
|
||||
virtual void update(float deltaTime);
|
||||
|
||||
virtual bool isActive() const;
|
||||
virtual bool isTracking() const;
|
||||
|
||||
public:
|
||||
virtual void init() override;
|
||||
virtual void reset() override;
|
||||
virtual void update(float deltaTime) override;
|
||||
|
||||
virtual bool isActive() const override;
|
||||
virtual bool isTracking() const override;
|
||||
|
||||
float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); }
|
||||
float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); }
|
||||
float getLeftEyeOpen() const { return getBlendshapeCoefficient(_leftEyeOpenIndex); }
|
||||
float getRightEyeOpen() const { return getBlendshapeCoefficient(_rightEyeOpenIndex); }
|
||||
|
||||
|
||||
float getBrowDownLeft() const { return getBlendshapeCoefficient(_browDownLeftIndex); }
|
||||
float getBrowDownRight() const { return getBlendshapeCoefficient(_browDownRightIndex); }
|
||||
float getBrowUpCenter() const { return getBlendshapeCoefficient(_browUpCenterIndex); }
|
||||
float getBrowUpLeft() const { return getBlendshapeCoefficient(_browUpLeftIndex); }
|
||||
float getBrowUpRight() const { return getBlendshapeCoefficient(_browUpRightIndex); }
|
||||
|
||||
|
||||
float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); }
|
||||
float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); }
|
||||
float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); }
|
||||
|
@ -55,7 +55,7 @@ public:
|
|||
void setEyeClosingThreshold(float eyeClosingThreshold);
|
||||
|
||||
public slots:
|
||||
void setEnabled(bool enabled);
|
||||
void setEnabled(bool enabled) override;
|
||||
void calibrate();
|
||||
|
||||
private slots:
|
||||
|
@ -77,18 +77,18 @@ private:
|
|||
QHostAddress _host;
|
||||
quint16 _serverPort;
|
||||
quint16 _controlPort;
|
||||
|
||||
|
||||
float getBlendshapeCoefficient(int index) const;
|
||||
void decodePacket(const QByteArray& buffer);
|
||||
|
||||
|
||||
// sockets
|
||||
QUdpSocket _udpSocket;
|
||||
quint64 _lastReceiveTimestamp;
|
||||
|
||||
|
||||
bool _reset;
|
||||
glm::vec3 _referenceTranslation;
|
||||
glm::quat _referenceRotation;
|
||||
|
||||
|
||||
int _leftBlinkIndex;
|
||||
int _rightBlinkIndex;
|
||||
int _leftEyeDownIndex;
|
||||
|
@ -103,10 +103,10 @@ private:
|
|||
int _browUpCenterIndex;
|
||||
int _browUpLeftIndex;
|
||||
int _browUpRightIndex;
|
||||
|
||||
|
||||
int _mouthSmileLeftIndex;
|
||||
int _mouthSmileRightIndex;
|
||||
|
||||
|
||||
int _jawOpenIndex;
|
||||
|
||||
QVector<float> _coefficients;
|
||||
|
|
|
@ -34,12 +34,12 @@ class Faceshift : public FaceTracker, public Dependency {
|
|||
public:
|
||||
#ifdef HAVE_FACESHIFT
|
||||
// If we don't have faceshift, use the base class' methods
|
||||
virtual void init();
|
||||
virtual void update(float deltaTime);
|
||||
virtual void reset();
|
||||
virtual void init() override;
|
||||
virtual void update(float deltaTime) override;
|
||||
virtual void reset() override;
|
||||
|
||||
virtual bool isActive() const;
|
||||
virtual bool isTracking() const;
|
||||
virtual bool isActive() const override;
|
||||
virtual bool isTracking() const override;
|
||||
#endif
|
||||
|
||||
bool isConnectedOrConnecting() const;
|
||||
|
@ -49,7 +49,7 @@ public:
|
|||
// these pitch/yaw angles are in degrees
|
||||
float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; }
|
||||
float getEyeGazeLeftYaw() const { return _eyeGazeLeftYaw; }
|
||||
|
||||
|
||||
float getEyeGazeRightPitch() const { return _eyeGazeRightPitch; }
|
||||
float getEyeGazeRightYaw() const { return _eyeGazeRightYaw; }
|
||||
|
||||
|
@ -67,10 +67,10 @@ public:
|
|||
float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); }
|
||||
float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); }
|
||||
float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); }
|
||||
|
||||
|
||||
QString getHostname() { return _hostname.get(); }
|
||||
void setHostname(const QString& hostname);
|
||||
|
||||
|
||||
void updateFakeCoefficients(float leftBlink,
|
||||
float rightBlink,
|
||||
float browUp,
|
||||
|
@ -79,76 +79,76 @@ public:
|
|||
float mouth3,
|
||||
float mouth4,
|
||||
QVector<float>& coefficients) const;
|
||||
|
||||
|
||||
signals:
|
||||
void connectionStateChanged();
|
||||
|
||||
public slots:
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
void setEnabled(bool enabled) override;
|
||||
|
||||
private slots:
|
||||
void connectSocket();
|
||||
void noteConnected();
|
||||
void noteError(QAbstractSocket::SocketError error);
|
||||
void readPendingDatagrams();
|
||||
void readFromSocket();
|
||||
void readFromSocket();
|
||||
void noteDisconnected();
|
||||
|
||||
private:
|
||||
Faceshift();
|
||||
virtual ~Faceshift() {}
|
||||
|
||||
|
||||
void send(const std::string& message);
|
||||
void receive(const QByteArray& buffer);
|
||||
|
||||
|
||||
QTcpSocket _tcpSocket;
|
||||
QUdpSocket _udpSocket;
|
||||
|
||||
#ifdef HAVE_FACESHIFT
|
||||
fs::fsBinaryStream _stream;
|
||||
#endif
|
||||
|
||||
|
||||
bool _tcpEnabled = true;
|
||||
int _tcpRetryCount = 0;
|
||||
bool _tracking = false;
|
||||
quint64 _lastReceiveTimestamp = 0;
|
||||
quint64 _lastMessageReceived = 0;
|
||||
float _averageFrameTime = STARTING_FACESHIFT_FRAME_TIME;
|
||||
|
||||
|
||||
glm::vec3 _headAngularVelocity = glm::vec3(0.0f);
|
||||
glm::vec3 _headLinearVelocity = glm::vec3(0.0f);
|
||||
glm::vec3 _lastHeadTranslation = glm::vec3(0.0f);
|
||||
glm::vec3 _filteredHeadTranslation = glm::vec3(0.0f);
|
||||
|
||||
|
||||
// degrees
|
||||
float _eyeGazeLeftPitch = 0.0f;
|
||||
float _eyeGazeLeftYaw = 0.0f;
|
||||
float _eyeGazeRightPitch = 0.0f;
|
||||
float _eyeGazeRightYaw = 0.0f;
|
||||
|
||||
|
||||
// degrees
|
||||
float _longTermAverageEyePitch = 0.0f;
|
||||
float _longTermAverageEyeYaw = 0.0f;
|
||||
bool _longTermAverageInitialized = false;
|
||||
|
||||
|
||||
Setting::Handle<QString> _hostname;
|
||||
|
||||
|
||||
// see http://support.faceshift.com/support/articles/35129-export-of-blendshapes
|
||||
int _leftBlinkIndex = 0;
|
||||
int _rightBlinkIndex = 1;
|
||||
int _leftEyeOpenIndex = 8;
|
||||
int _rightEyeOpenIndex = 9;
|
||||
|
||||
|
||||
// Brows
|
||||
int _browDownLeftIndex = 14;
|
||||
int _browDownRightIndex = 15;
|
||||
int _browUpCenterIndex = 16;
|
||||
int _browUpLeftIndex = 17;
|
||||
int _browUpRightIndex = 18;
|
||||
|
||||
|
||||
int _mouthSmileLeftIndex = 28;
|
||||
int _mouthSmileRightIndex = 29;
|
||||
|
||||
|
||||
int _jawOpenIndex = 21;
|
||||
};
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
|
||||
bool isActive() const { return _active; }
|
||||
|
||||
virtual void update();
|
||||
virtual void update() override;
|
||||
|
||||
protected:
|
||||
Leapmotion();
|
||||
|
|
|
@ -26,6 +26,11 @@ bool AccountScriptingInterface::isLoggedIn() {
|
|||
return accountManager->isLoggedIn();
|
||||
}
|
||||
|
||||
bool AccountScriptingInterface::checkAndSignalForAccessToken() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return accountManager->checkAndSignalForAccessToken();
|
||||
}
|
||||
|
||||
QString AccountScriptingInterface::getUsername() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
if (accountManager->isLoggedIn()) {
|
||||
|
|
|
@ -26,6 +26,7 @@ public slots:
|
|||
static AccountScriptingInterface* getInstance();
|
||||
QString getUsername();
|
||||
bool isLoggedIn();
|
||||
bool checkAndSignalForAccessToken();
|
||||
};
|
||||
|
||||
#endif // hifi_AccountScriptingInterface_h
|
||||
|
|
|
@ -33,16 +33,16 @@ class InputController : public controller::InputController {
|
|||
public:
|
||||
InputController(int deviceTrackerId, int subTrackerId, QObject* parent = NULL);
|
||||
|
||||
virtual void update();
|
||||
virtual Key getKey() const;
|
||||
virtual void update() override;
|
||||
virtual Key getKey() const override;
|
||||
|
||||
public slots:
|
||||
|
||||
virtual bool isActive() const { return _isActive; }
|
||||
virtual glm::vec3 getAbsTranslation() const { return _eventCache.absTranslation; }
|
||||
virtual glm::quat getAbsRotation() const { return _eventCache.absRotation; }
|
||||
virtual glm::vec3 getLocTranslation() const { return _eventCache.locTranslation; }
|
||||
virtual glm::quat getLocRotation() const { return _eventCache.locRotation; }
|
||||
virtual bool isActive() const override { return _isActive; }
|
||||
virtual glm::vec3 getAbsTranslation() const override { return _eventCache.absTranslation; }
|
||||
virtual glm::quat getAbsRotation() const override { return _eventCache.absRotation; }
|
||||
virtual glm::vec3 getLocTranslation() const override { return _eventCache.locTranslation; }
|
||||
virtual glm::quat getLocRotation() const override { return _eventCache.locRotation; }
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include "Application.h"
|
||||
#include "MainWindow.h"
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
int DesktopScriptingInterface::getWidth() {
|
||||
QSize size = qApp->getWindow()->windowHandle()->screen()->virtualSize();
|
||||
|
@ -31,3 +33,11 @@ void DesktopScriptingInterface::setOverlayAlpha(float alpha) {
|
|||
qApp->getApplicationCompositor().setAlpha(alpha);
|
||||
}
|
||||
|
||||
void DesktopScriptingInterface::show(const QString& path, const QString& title) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "show", Qt::QueuedConnection, Q_ARG(QString, path), Q_ARG(QString, title));
|
||||
return;
|
||||
}
|
||||
DependencyManager::get<OffscreenUi>()->show(path, title);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ class DesktopScriptingInterface : public QObject, public Dependency {
|
|||
|
||||
public:
|
||||
Q_INVOKABLE void setOverlayAlpha(float alpha);
|
||||
Q_INVOKABLE void show(const QString& path, const QString& title);
|
||||
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
|
|
|
@ -26,3 +26,8 @@ void DialogsManagerScriptingInterface::toggleAddressBar() {
|
|||
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
|
||||
"toggleAddressBar", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void DialogsManagerScriptingInterface::showFeed() {
|
||||
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
|
||||
"showFeed", Qt::QueuedConnection);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ class DialogsManagerScriptingInterface : public QObject {
|
|||
Q_OBJECT
|
||||
public:
|
||||
DialogsManagerScriptingInterface();
|
||||
Q_INVOKABLE void showFeed();
|
||||
|
||||
public slots:
|
||||
void toggleAddressBar();
|
||||
|
|
|
@ -66,7 +66,7 @@ signals:
|
|||
void closed();
|
||||
|
||||
protected:
|
||||
virtual bool eventFilter(QObject* sender, QEvent* event);
|
||||
virtual bool eventFilter(QObject* sender, QEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void hasClosed();
|
||||
|
|
|
@ -96,7 +96,7 @@ void WindowScriptingInterface::alert(const QString& message) {
|
|||
/// \param const QString& message message to display
|
||||
/// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise
|
||||
QScriptValue WindowScriptingInterface::confirm(const QString& message) {
|
||||
return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message)));
|
||||
return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No)));
|
||||
}
|
||||
|
||||
/// Display a prompt with a text box
|
||||
|
@ -203,3 +203,11 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) {
|
|||
qDebug() << "Copying";
|
||||
QApplication::clipboard()->setText(text);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::takeSnapshot(bool notify, float aspectRatio) {
|
||||
qApp->takeSnapshot(notify, aspectRatio);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::shareSnapshot(const QString& path) {
|
||||
qApp->shareSnapshot(path);
|
||||
}
|
||||
|
|
|
@ -55,12 +55,15 @@ public slots:
|
|||
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||
void showAssetServer(const QString& upload = "");
|
||||
void copyToClipboard(const QString& text);
|
||||
void takeSnapshot(bool notify = true, float aspectRatio = 0.0f);
|
||||
void shareSnapshot(const QString& path);
|
||||
|
||||
signals:
|
||||
void domainChanged(const QString& domainHostname);
|
||||
void svoImportRequested(const QString& url);
|
||||
void domainConnectionRefused(const QString& reasonMessage, int reasonCode);
|
||||
void snapshotTaken(const QString& path);
|
||||
void snapshotTaken(const QString& path, bool notify);
|
||||
void snapshotShared(const QString& error);
|
||||
|
||||
private slots:
|
||||
WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height);
|
||||
|
|
|
@ -38,6 +38,8 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare
|
|||
});
|
||||
_backEnabled = !(DependencyManager::get<AddressManager>()->getBackStack().isEmpty());
|
||||
_forwardEnabled = !(DependencyManager::get<AddressManager>()->getForwardStack().isEmpty());
|
||||
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed);
|
||||
connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL);
|
||||
}
|
||||
|
||||
void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions) {
|
||||
|
|
|
@ -14,21 +14,29 @@
|
|||
#define hifi_AddressBarDialog_h
|
||||
|
||||
#include <OffscreenQmlDialog.h>
|
||||
#include <NetworkingConstants.h>
|
||||
|
||||
class AddressBarDialog : public OffscreenQmlDialog {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged)
|
||||
Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged)
|
||||
Q_PROPERTY(bool useFeed READ useFeed WRITE setUseFeed NOTIFY useFeedChanged)
|
||||
Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl)
|
||||
|
||||
public:
|
||||
AddressBarDialog(QQuickItem* parent = nullptr);
|
||||
bool backEnabled() { return _backEnabled; }
|
||||
bool forwardEnabled() { return _forwardEnabled; }
|
||||
bool useFeed() { return _useFeed; }
|
||||
void setUseFeed(bool useFeed) { if (_useFeed != useFeed) { _useFeed = useFeed; emit useFeedChanged(); } }
|
||||
QString metaverseServerUrl() { return NetworkingConstants::METAVERSE_SERVER_URL.toString(); }
|
||||
|
||||
signals:
|
||||
void backEnabledChanged();
|
||||
void forwardEnabledChanged();
|
||||
void useFeedChanged();
|
||||
void receivedHifiSchemeURL(const QString& url);
|
||||
|
||||
protected:
|
||||
void displayAddressOfflineMessage();
|
||||
|
@ -42,6 +50,7 @@ protected:
|
|||
|
||||
bool _backEnabled;
|
||||
bool _forwardEnabled;
|
||||
bool _useFeed { false };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -36,13 +36,13 @@ public:
|
|||
AudioStatsDisplay(QFormLayout* form, QString text, unsigned colorRGBA);
|
||||
void updatedDisplay(QString str);
|
||||
void paint();
|
||||
|
||||
|
||||
private:
|
||||
QString _strBuf;
|
||||
QLabel* _label;
|
||||
QString _text;
|
||||
unsigned _colorRGBA;
|
||||
|
||||
|
||||
};
|
||||
|
||||
//dialog
|
||||
|
@ -51,9 +51,9 @@ class AudioStatsDialog : public QDialog {
|
|||
public:
|
||||
AudioStatsDialog(QWidget* parent);
|
||||
~AudioStatsDialog();
|
||||
|
||||
void paintEvent(QPaintEvent*);
|
||||
|
||||
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
|
||||
private:
|
||||
// audio stats methods for rendering
|
||||
QVector<QString> _audioMixerStats;
|
||||
|
@ -61,48 +61,47 @@ private:
|
|||
QVector<QString> _upstreamMixerStats;
|
||||
QVector<QString> _downstreamStats;
|
||||
QVector<QString> _upstreamInjectedStats;
|
||||
|
||||
|
||||
int _audioMixerID;
|
||||
int _upstreamClientID;
|
||||
int _upstreamMixerID;
|
||||
int _downstreamID;
|
||||
int _upstreamInjectedID;
|
||||
|
||||
|
||||
QVector<QVector<AudioStatsDisplay*>> _audioDisplayChannels;
|
||||
|
||||
|
||||
int addChannel(QFormLayout* form, QVector<QString>& stats, const unsigned color);
|
||||
void updateStats(QVector<QString>& stats, const int channelID);
|
||||
void renderStats();
|
||||
void clearAllChannels();
|
||||
void renderAudioStreamStats(const AudioStreamStats* streamStats, QVector<QString>* audioStreamstats, bool isDownstreamStats);
|
||||
|
||||
|
||||
|
||||
|
||||
const AudioIOStats* _stats;
|
||||
QFormLayout* _form;
|
||||
|
||||
|
||||
bool _isEnabled;
|
||||
bool _shouldShowInjectedStreams;
|
||||
|
||||
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
|
||||
|
||||
|
||||
void closed();
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
void reject();
|
||||
|
||||
|
||||
void reject() override;
|
||||
void updateTimerTimeout();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
// Emits a 'closed' signal when this dialog is closed.
|
||||
void closeEvent(QCloseEvent*);
|
||||
|
||||
void closeEvent(QCloseEvent*) override;
|
||||
|
||||
private:
|
||||
QTimer* averageUpdateTimer = new QTimer(this);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
BandwidthDialog(QWidget* parent);
|
||||
~BandwidthDialog();
|
||||
|
||||
void paintEvent(QPaintEvent*);
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
|
||||
private:
|
||||
BandwidthChannelDisplay* _audioChannelDisplay;
|
||||
|
@ -77,14 +77,14 @@ signals:
|
|||
|
||||
public slots:
|
||||
|
||||
void reject();
|
||||
void reject() override;
|
||||
void updateTimerTimeout();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// Emits a 'closed' signal when this dialog is closed.
|
||||
void closeEvent(QCloseEvent*);
|
||||
void closeEvent(QCloseEvent*) override;
|
||||
|
||||
private:
|
||||
QTimer* averageUpdateTimer = new QTimer(this);
|
||||
|
|
|
@ -21,19 +21,19 @@ class CachesSizeDialog : public QDialog {
|
|||
public:
|
||||
// Sets up the UI
|
||||
CachesSizeDialog(QWidget* parent);
|
||||
|
||||
|
||||
signals:
|
||||
void closed();
|
||||
|
||||
|
||||
public slots:
|
||||
void reject();
|
||||
void reject() override;
|
||||
void confirmClicked(bool checked);
|
||||
void resetClicked(bool checked);
|
||||
|
||||
|
||||
protected:
|
||||
// Emits a 'closed' signal when this dialog is closed.
|
||||
void closeEvent(QCloseEvent* event);
|
||||
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
private:
|
||||
QDoubleSpinBox* _animations = nullptr;
|
||||
QDoubleSpinBox* _geometries = nullptr;
|
||||
|
@ -42,4 +42,4 @@ private:
|
|||
QDoubleSpinBox* _textures = nullptr;
|
||||
};
|
||||
|
||||
#endif // hifi_CachesSizeDialog_h
|
||||
#endif // hifi_CachesSizeDialog_h
|
||||
|
|
|
@ -18,9 +18,9 @@ class DataWebPage : public QWebPage {
|
|||
public:
|
||||
DataWebPage(QObject* parent = 0);
|
||||
protected:
|
||||
void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID);
|
||||
bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type);
|
||||
virtual QString userAgentForUrl(const QUrl& url) const;
|
||||
void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID) override;
|
||||
bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type) override;
|
||||
virtual QString userAgentForUrl(const QUrl& url) const override;
|
||||
};
|
||||
|
||||
#endif // hifi_DataWebPage_h
|
||||
|
|
|
@ -54,6 +54,11 @@ void DialogsManager::showAddressBar() {
|
|||
AddressBarDialog::show();
|
||||
}
|
||||
|
||||
void DialogsManager::showFeed() {
|
||||
AddressBarDialog::show();
|
||||
emit setUseFeed(true);
|
||||
}
|
||||
|
||||
void DialogsManager::toggleDiskCacheEditor() {
|
||||
maybeCreateDialog(_diskCacheEditor);
|
||||
_diskCacheEditor->toggle();
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
public slots:
|
||||
void toggleAddressBar();
|
||||
void showAddressBar();
|
||||
void showFeed();
|
||||
void toggleDiskCacheEditor();
|
||||
void toggleLoginDialog();
|
||||
void showLoginDialog();
|
||||
|
@ -63,6 +64,7 @@ public slots:
|
|||
signals:
|
||||
void addressBarToggled();
|
||||
void addressBarShown(bool visible);
|
||||
void setUseFeed(bool useFeed);
|
||||
|
||||
private slots:
|
||||
void hmdToolsClosed();
|
||||
|
|
|
@ -29,18 +29,18 @@ public:
|
|||
QScreen* getLastApplicationScreen() const { return _previousScreen; }
|
||||
bool hasHMDScreen() const { return _hmdScreenNumber >= -1; }
|
||||
void watchWindow(QWindow* window);
|
||||
|
||||
|
||||
signals:
|
||||
void closed();
|
||||
|
||||
public slots:
|
||||
void reject();
|
||||
void reject() override;
|
||||
void screenCountChanged(int newCount);
|
||||
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent*); // Emits a 'closed' signal when this dialog is closed.
|
||||
virtual void showEvent(QShowEvent* event);
|
||||
virtual void hideEvent(QHideEvent* event);
|
||||
virtual void closeEvent(QCloseEvent*) override; // Emits a 'closed' signal when this dialog is closed.
|
||||
virtual void showEvent(QShowEvent* event) override;
|
||||
virtual void hideEvent(QHideEvent* event) override;
|
||||
|
||||
private:
|
||||
void centerCursorOnWidget(QWidget* widget);
|
||||
|
@ -59,7 +59,7 @@ private:
|
|||
QScreen* _previousDialogScreen{ nullptr };
|
||||
QString _hmdPluginName;
|
||||
QString _defaultPluginName;
|
||||
|
||||
|
||||
QHash<QWindow*, HMDWindowWatcher*> _windowWatchers;
|
||||
friend class HMDWindowWatcher;
|
||||
};
|
||||
|
@ -75,7 +75,7 @@ public:
|
|||
public slots:
|
||||
void windowScreenChanged(QScreen* screen);
|
||||
void windowGeometryChanged(int arg);
|
||||
|
||||
|
||||
private:
|
||||
QWindow* _window;
|
||||
HMDToolsDialog* _hmdTools;
|
||||
|
|
|
@ -40,9 +40,9 @@ public slots:
|
|||
|
||||
protected:
|
||||
void setAndSelectCommand(const QString& command);
|
||||
virtual bool eventFilter(QObject* sender, QEvent* event);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event);
|
||||
virtual void showEvent(QShowEvent* event);
|
||||
virtual bool eventFilter(QObject* sender, QEvent* event) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
virtual void showEvent(QShowEvent* event) override;
|
||||
|
||||
protected slots:
|
||||
void scrollToBottom();
|
||||
|
|
|
@ -24,12 +24,12 @@ class LodToolsDialog : public QDialog {
|
|||
public:
|
||||
// Sets up the UI
|
||||
LodToolsDialog(QWidget* parent);
|
||||
|
||||
|
||||
signals:
|
||||
void closed();
|
||||
|
||||
public slots:
|
||||
void reject();
|
||||
void reject() override;
|
||||
void sizeScaleValueChanged(int value);
|
||||
void resetClicked(bool checked);
|
||||
void reloadSliders();
|
||||
|
@ -38,7 +38,7 @@ public slots:
|
|||
protected:
|
||||
|
||||
// Emits a 'closed' signal when this dialog is closed.
|
||||
void closeEvent(QCloseEvent* event);
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
private:
|
||||
QSlider* _lodSize;
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
QString keyword;
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString &text);
|
||||
void highlightBlock(const QString &text) override;
|
||||
|
||||
private:
|
||||
QTextCharFormat keywordFormat;
|
||||
|
@ -54,8 +54,8 @@ private slots:
|
|||
void handleSearchTextChanged(const QString);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent*);
|
||||
void showEvent(QShowEvent*);
|
||||
void resizeEvent(QResizeEvent*) override;
|
||||
void showEvent(QShowEvent*) override;
|
||||
|
||||
private:
|
||||
QPushButton* _searchButton;
|
||||
|
|
|
@ -33,15 +33,15 @@ signals:
|
|||
void closed();
|
||||
|
||||
public slots:
|
||||
void reject();
|
||||
void reject() override;
|
||||
void moreless(const QString& link);
|
||||
|
||||
protected:
|
||||
// State <- data model held by BandwidthMeter
|
||||
void paintEvent(QPaintEvent*);
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
|
||||
// Emits a 'closed' signal when this dialog is closed.
|
||||
void closeEvent(QCloseEvent*);
|
||||
void closeEvent(QCloseEvent*) override;
|
||||
|
||||
int AddStatItem(const char* caption, unsigned colorRGBA = DEFAULT_COLOR);
|
||||
void RemoveStatItem(int item);
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
int lineNumberAreaWidth();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event);
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void updateLineNumberAreaWidth(int blockCount);
|
||||
|
|
|
@ -35,8 +35,8 @@ signals:
|
|||
void windowActivated();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* event);
|
||||
virtual bool event(QEvent* event);
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
virtual bool event(QEvent* event) override;
|
||||
|
||||
private:
|
||||
Ui::ScriptEditorWindow* _ScriptEditorWindowUI;
|
||||
|
|
|
@ -20,10 +20,10 @@ class ScriptLineNumberArea : public QWidget {
|
|||
|
||||
public:
|
||||
ScriptLineNumberArea(ScriptEditBox* scriptEditBox);
|
||||
QSize sizeHint() const;
|
||||
QSize sizeHint() const override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event);
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
ScriptEditBox* _scriptEditBox;
|
||||
|
|
|
@ -21,8 +21,8 @@ public:
|
|||
explicit ScriptsTableWidget(QWidget* parent);
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent* event);
|
||||
virtual void keyPressEvent(QKeyEvent* event);
|
||||
virtual void paintEvent(QPaintEvent* event) override;
|
||||
virtual void keyPressEvent(QKeyEvent* event) override;
|
||||
};
|
||||
|
||||
#endif // hifi__ScriptsTableWidget_h
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "Application.h"
|
||||
#include "Snapshot.h"
|
||||
#include "SnapshotUploader.h"
|
||||
|
||||
// filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg
|
||||
// %1 <= username, %2 <= date and time, %3 <= current location
|
||||
|
@ -141,3 +142,34 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) {
|
|||
return imageTempFile;
|
||||
}
|
||||
}
|
||||
|
||||
void Snapshot::uploadSnapshot(const QString& filename) {
|
||||
|
||||
const QString SNAPSHOT_UPLOAD_URL = "/api/v1/snapshots";
|
||||
static SnapshotUploader uploader;
|
||||
|
||||
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=\"image\"; 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(imagePart);
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
JSONCallbackParameters callbackParams(&uploader, "uploadSuccess", &uploader, "uploadFailure");
|
||||
|
||||
accountManager->sendRequest(SNAPSHOT_UPLOAD_URL,
|
||||
AccountManagerAuth::Required,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
callbackParams,
|
||||
nullptr,
|
||||
multiPart);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ public:
|
|||
|
||||
static Setting::Handle<QString> snapshotsLocation;
|
||||
static Setting::Handle<bool> hasSetSnapshotsLocation;
|
||||
static void uploadSnapshot(const QString& filename);
|
||||
private:
|
||||
static QFile* savedFileForSnapshot(QImage & image, bool isTemporary);
|
||||
};
|
||||
|
|
75
interface/src/ui/SnapshotUploader.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// SnapshotUploader.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Howard Stearns on 8/22/2016
|
||||
// Copyright 2016 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
|
||||
//
|
||||
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <AddressManager.h>
|
||||
#include "scripting/WindowScriptingInterface.h"
|
||||
#include "SnapshotUploader.h"
|
||||
|
||||
void SnapshotUploader::uploadSuccess(QNetworkReply& reply) {
|
||||
const QString STORY_UPLOAD_URL = "/api/v1/user_stories";
|
||||
static SnapshotUploader uploader;
|
||||
|
||||
// parse the reply for the thumbnail_url
|
||||
QByteArray contents = reply.readAll();
|
||||
QJsonParseError jsonError;
|
||||
auto doc = QJsonDocument::fromJson(contents, &jsonError);
|
||||
if (jsonError.error == QJsonParseError::NoError) {
|
||||
auto dataObject = doc.object().value("data").toObject();
|
||||
QString thumbnailUrl = dataObject.value("thumbnail_url").toString();
|
||||
QString imageUrl = dataObject.value("image_url").toString();
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
QString placeName = addressManager->getPlaceName();
|
||||
if (placeName.isEmpty()) {
|
||||
placeName = addressManager->getHost();
|
||||
}
|
||||
QString currentPath = addressManager->currentPath(true);
|
||||
|
||||
// create json post data
|
||||
QJsonObject rootObject;
|
||||
QJsonObject userStoryObject;
|
||||
QJsonObject detailsObject;
|
||||
detailsObject.insert("image_url", imageUrl);
|
||||
QString pickledDetails = QJsonDocument(detailsObject).toJson();
|
||||
userStoryObject.insert("details", pickledDetails);
|
||||
userStoryObject.insert("thumbnail_url", thumbnailUrl);
|
||||
userStoryObject.insert("place_name", placeName);
|
||||
userStoryObject.insert("path", currentPath);
|
||||
userStoryObject.insert("action", "snapshot");
|
||||
rootObject.insert("user_story", userStoryObject);
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
JSONCallbackParameters callbackParams(&uploader, "createStorySuccess", &uploader, "createStoryFailure");
|
||||
|
||||
accountManager->sendRequest(STORY_UPLOAD_URL,
|
||||
AccountManagerAuth::Required,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
callbackParams,
|
||||
QJsonDocument(rootObject).toJson());
|
||||
|
||||
}
|
||||
else {
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(contents);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapshotUploader::uploadFailure(QNetworkReply& reply) {
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(reply.readAll());
|
||||
}
|
||||
|
||||
void SnapshotUploader::createStorySuccess(QNetworkReply& reply) {
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(QString());
|
||||
}
|
||||
|
||||
void SnapshotUploader::createStoryFailure(QNetworkReply& reply) {
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotShared(reply.readAll());
|
||||
}
|
26
interface/src/ui/SnapshotUploader.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// SnapshotUploader.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Howard Stearns on 8/22/2016
|
||||
// Copyright 2016 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
|
||||
//
|
||||
|
||||
#ifndef hifi_SnapshotUploader_h
|
||||
#define hifi_SnapshotUploader_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
class SnapshotUploader : public QObject {
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void uploadSuccess(QNetworkReply& reply);
|
||||
void uploadFailure(QNetworkReply& reply);
|
||||
void createStorySuccess(QNetworkReply& reply);
|
||||
void createStoryFailure(QNetworkReply& reply);
|
||||
};
|
||||
#endif // hifi_SnapshotUploader_h
|
|
@ -20,15 +20,15 @@ class LocalModelsOverlay : public Volume3DOverlay {
|
|||
Q_OBJECT
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
virtual QString getType() const override { return TYPE; }
|
||||
|
||||
LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer);
|
||||
LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay);
|
||||
|
||||
virtual void update(float deltatime);
|
||||
virtual void render(RenderArgs* args);
|
||||
|
||||
virtual LocalModelsOverlay* createClone() const;
|
||||
virtual void update(float deltatime) override;
|
||||
virtual void render(RenderArgs* args) override;
|
||||
|
||||
virtual LocalModelsOverlay* createClone() const override;
|
||||
|
||||
private:
|
||||
EntityTreeRenderer* _entityTreeRenderer;
|
||||
|
|
|
@ -62,7 +62,7 @@ public:
|
|||
void setProperties(const QVariantMap& properties);
|
||||
QVariant getProperty(const QString& property);
|
||||
|
||||
virtual void applyTransformTo(Transform& transform, bool force = false);
|
||||
virtual void applyTransformTo(Transform& transform, bool force = false) override;
|
||||
|
||||
private:
|
||||
Transform _anchorTransform;
|
||||
|
|
|
@ -14,14 +14,13 @@
|
|||
class RectangleOverlay : public QmlOverlay {
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
virtual QString getType() const override { return TYPE; }
|
||||
static QUrl const URL;
|
||||
|
||||
RectangleOverlay();
|
||||
RectangleOverlay(const RectangleOverlay* RectangleOverlay);
|
||||
|
||||
virtual RectangleOverlay* createClone() const;
|
||||
virtual RectangleOverlay* createClone() const override;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_RectangleOverlay_h
|
||||
|
|
|
@ -74,8 +74,8 @@ void Web3DOverlay::render(RenderArgs* args) {
|
|||
if (!_webSurface) {
|
||||
_webSurface = new OffscreenQmlSurface();
|
||||
_webSurface->create(currentContext);
|
||||
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
||||
_webSurface->load("WebEntity.qml");
|
||||
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/controls/"));
|
||||
_webSurface->load("WebView.qml");
|
||||
_webSurface->resume();
|
||||
_webSurface->getRootItem()->setProperty("url", _url);
|
||||
_webSurface->resize(QSize(_resolution.x, _resolution.y));
|
||||
|
|
|
@ -34,9 +34,9 @@ public:
|
|||
Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||
const void* extra);
|
||||
const void* extra) override;
|
||||
private:
|
||||
explicit AnimationCache(QObject* parent = NULL);
|
||||
virtual ~AnimationCache() { }
|
||||
|
@ -82,7 +82,7 @@ class AnimationReader : public QObject, public QRunnable {
|
|||
|
||||
public:
|
||||
AnimationReader(const QUrl& url, const QByteArray& data);
|
||||
virtual void run();
|
||||
virtual void run() override;
|
||||
|
||||
signals:
|
||||
void onSuccess(FBXGeometry::Pointer geometry);
|
||||
|
|
|
@ -86,7 +86,7 @@ public:
|
|||
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
|
||||
|
||||
class AudioOutputIODevice : public QIODevice {
|
||||
public:
|
||||
AudioOutputIODevice(MixedProcessedAudioStream& receivedAudioStream, AudioClient* audio) :
|
||||
|
@ -94,8 +94,8 @@ public:
|
|||
|
||||
void start() { open(QIODevice::ReadOnly); }
|
||||
void stop() { close(); }
|
||||
qint64 readData(char * data, qint64 maxSize);
|
||||
qint64 writeData(const char * data, qint64 maxSize) { return 0; }
|
||||
qint64 readData(char * data, qint64 maxSize) override;
|
||||
qint64 writeData(const char * data, qint64 maxSize) override { return 0; }
|
||||
int getRecentUnfulfilledReads() { int unfulfilledReads = _unfulfilledReads; _unfulfilledReads = 0; return unfulfilledReads; }
|
||||
private:
|
||||
MixedProcessedAudioStream& _receivedAudioStream;
|
||||
|
@ -136,7 +136,7 @@ public:
|
|||
|
||||
void setPositionGetter(AudioPositionGetter positionGetter) { _positionGetter = positionGetter; }
|
||||
void setOrientationGetter(AudioOrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
|
||||
|
||||
|
||||
QVector<AudioInjector*>& getActiveLocalAudioInjectors() { return _activeLocalAudioInjectors; }
|
||||
|
||||
static const float CALLBACK_ACCELERATOR_RATIO;
|
||||
|
@ -163,7 +163,7 @@ public slots:
|
|||
void audioMixerKilled();
|
||||
void toggleMute();
|
||||
|
||||
virtual void setIsStereoInput(bool stereo);
|
||||
virtual void setIsStereoInput(bool stereo) override;
|
||||
|
||||
void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; }
|
||||
|
||||
|
@ -175,7 +175,7 @@ public slots:
|
|||
|
||||
int setOutputBufferSize(int numFrames, bool persist = true);
|
||||
|
||||
virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector);
|
||||
virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector) override;
|
||||
|
||||
bool switchInputToAudioDevice(const QString& inputDeviceName);
|
||||
bool switchOutputToAudioDevice(const QString& outputDeviceName);
|
||||
|
@ -215,7 +215,7 @@ protected:
|
|||
AudioClient();
|
||||
~AudioClient();
|
||||
|
||||
virtual void customDeleter() {
|
||||
virtual void customDeleter() override {
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
|
@ -316,7 +316,7 @@ private:
|
|||
void checkDevices();
|
||||
|
||||
bool _hasReceivedFirstPacket = false;
|
||||
|
||||
|
||||
QVector<AudioInjector*> _activeLocalAudioInjectors;
|
||||
|
||||
CodecPluginPointer _codec;
|
||||
|
|
|
@ -20,27 +20,27 @@ class AudioInjectorLocalBuffer : public QIODevice {
|
|||
Q_OBJECT
|
||||
public:
|
||||
AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent);
|
||||
|
||||
|
||||
void stop();
|
||||
|
||||
bool seek(qint64 pos);
|
||||
|
||||
qint64 readData(char* data, qint64 maxSize);
|
||||
qint64 writeData(const char* data, qint64 maxSize) { return 0; }
|
||||
|
||||
|
||||
bool seek(qint64 pos) override;
|
||||
|
||||
qint64 readData(char* data, qint64 maxSize) override;
|
||||
qint64 writeData(const char* data, qint64 maxSize) override { return 0; }
|
||||
|
||||
void setShouldLoop(bool shouldLoop) { _shouldLoop = shouldLoop; }
|
||||
void setCurrentOffset(int currentOffset) { _currentOffset = currentOffset; }
|
||||
void setVolume(float volume) { _volume = glm::clamp(volume, 0.0f, 1.0f); }
|
||||
|
||||
|
||||
private:
|
||||
qint64 recursiveReadFromFront(char* data, qint64 maxSize);
|
||||
|
||||
|
||||
QByteArray _rawAudioArray;
|
||||
bool _shouldLoop;
|
||||
bool _isStopped;
|
||||
|
||||
|
||||
int _currentOffset;
|
||||
float _volume;
|
||||
};
|
||||
|
||||
#endif // hifi_AudioInjectorLocalBuffer_h
|
||||
#endif // hifi_AudioInjectorLocalBuffer_h
|
||||
|
|
|
@ -567,7 +567,7 @@ class LimiterMono : public LimiterImpl {
|
|||
public:
|
||||
LimiterMono(int sampleRate) : LimiterImpl(sampleRate) {}
|
||||
|
||||
void process(float* input, int16_t* output, int numFrames);
|
||||
void process(float* input, int16_t* output, int numFrames) override;
|
||||
};
|
||||
|
||||
template<int N>
|
||||
|
@ -619,7 +619,7 @@ public:
|
|||
LimiterStereo(int sampleRate) : LimiterImpl(sampleRate) {}
|
||||
|
||||
// interleaved stereo input/output
|
||||
void process(float* input, int16_t* output, int numFrames);
|
||||
void process(float* input, int16_t* output, int numFrames) override;
|
||||
};
|
||||
|
||||
template<int N>
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
MixedProcessedAudioStream(int numFrameSamples, int numFramesCapacity, const InboundAudioStream::Settings& settings);
|
||||
|
||||
signals:
|
||||
|
||||
|
||||
void addedSilence(int silentSamplesPerChannel);
|
||||
void addedLastFrameRepeatedWithFade(int samplesPerChannel);
|
||||
void addedStereoSamples(const QByteArray& samples);
|
||||
|
@ -33,9 +33,9 @@ public:
|
|||
void outputFormatChanged(int outputFormatChannelCountTimesSampleRate);
|
||||
|
||||
protected:
|
||||
int writeDroppableSilentSamples(int silentSamples);
|
||||
int writeLastFrameRepeatedWithFade(int samples);
|
||||
int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties);
|
||||
int writeDroppableSilentSamples(int silentSamples) override;
|
||||
int writeLastFrameRepeatedWithFade(int samples) override;
|
||||
int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) override;
|
||||
|
||||
private:
|
||||
int networkToDeviceSamples(int networkSamples);
|
||||
|
|
|
@ -32,9 +32,9 @@ public:
|
|||
const QUuid DEFAULT_STREAM_IDENTIFIER = QUuid();
|
||||
virtual const QUuid& getStreamIdentifier() const { return DEFAULT_STREAM_IDENTIFIER; }
|
||||
|
||||
virtual void resetStats();
|
||||
virtual void resetStats() override;
|
||||
|
||||
virtual AudioStreamStats getAudioStreamStats() const;
|
||||
virtual AudioStreamStats getAudioStreamStats() const override;
|
||||
|
||||
void updateLastPopOutputLoudnessAndTrailingLoudness();
|
||||
float getLastPopOutputTrailingLoudness() const { return _lastPopOutputTrailingLoudness; }
|
||||
|
@ -46,7 +46,7 @@ public:
|
|||
PositionalAudioStream::Type getType() const { return _type; }
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
// disallow copying of PositionalAudioStream objects
|
||||
|
|
|
@ -23,12 +23,12 @@ class SoundCache : public ResourceCache, public Dependency {
|
|||
|
||||
public:
|
||||
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
|
||||
|
||||
|
||||
protected:
|
||||
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||
const void* extra);
|
||||
const void* extra) override;
|
||||
private:
|
||||
SoundCache(QObject* parent = NULL);
|
||||
};
|
||||
|
||||
#endif // hifi_SoundCache_h
|
||||
#endif // hifi_SoundCache_h
|
||||
|
|
|
@ -24,11 +24,11 @@ public:
|
|||
: Endpoint(Input::INVALID_INPUT), _callable(callable) {
|
||||
}
|
||||
|
||||
virtual float peek() const {
|
||||
virtual float peek() const override {
|
||||
return (float)const_cast<JSEndpoint*>(this)->_callable.call().toNumber();
|
||||
}
|
||||
|
||||
virtual void apply(float newValue, const Pointer& source) {
|
||||
virtual void apply(float newValue, const Pointer& source) override {
|
||||
_callable.call(QJSValueList({ QJSValue(newValue) }));
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,6 @@ void NullDisplayPlugin::submitFrame(const gpu::FramePointer& frame) {
|
|||
}
|
||||
}
|
||||
|
||||
QImage NullDisplayPlugin::getScreenshot() const {
|
||||
QImage NullDisplayPlugin::getScreenshot(float aspectRatio) const {
|
||||
return QImage();
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ public:
|
|||
glm::uvec2 getRecommendedRenderSize() const override;
|
||||
bool hasFocus() const override;
|
||||
void submitFrame(const gpu::FramePointer& newFrame) override;
|
||||
QImage getScreenshot() const override;
|
||||
QImage getScreenshot(float aspectRatio = 0.0f) const override;
|
||||
private:
|
||||
static const QString NAME;
|
||||
};
|
||||
|
|
|
@ -659,12 +659,26 @@ void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const {
|
|||
_container->makeRenderingContextCurrent();
|
||||
}
|
||||
|
||||
QImage OpenGLDisplayPlugin::getScreenshot() const {
|
||||
QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const {
|
||||
auto size = _compositeFramebuffer->getSize();
|
||||
if (isHmd()) {
|
||||
size.x /= 2;
|
||||
}
|
||||
auto bestSize = size;
|
||||
uvec2 corner(0);
|
||||
if (aspectRatio != 0.0f) { // Pick out the largest piece of the center that produces the requested width/height aspectRatio
|
||||
if (ceil(size.y * aspectRatio) < size.x) {
|
||||
bestSize.x = round(size.y * aspectRatio);
|
||||
} else {
|
||||
bestSize.y = round(size.x / aspectRatio);
|
||||
}
|
||||
corner.x = round((size.x - bestSize.x) / 2.0f);
|
||||
corner.y = round((size.y - bestSize.y) / 2.0f);
|
||||
}
|
||||
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
||||
QImage screenshot(size.x, size.y, QImage::Format_ARGB32);
|
||||
QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32);
|
||||
withMainThreadContext([&] {
|
||||
glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(uvec2(0), size), screenshot);
|
||||
glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot);
|
||||
});
|
||||
return screenshot.mirrored(false, true);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ public:
|
|||
return getSurfaceSize();
|
||||
}
|
||||
|
||||
QImage getScreenshot() const override;
|
||||
QImage getScreenshot(float aspectRatio = 0.0f) const override;
|
||||
|
||||
float presentRate() const override;
|
||||
|
||||
|
|
|
@ -305,7 +305,7 @@ void HmdDisplayPlugin::updateFrameData() {
|
|||
{
|
||||
vec2 xdir = glm::normalize(vec2(intersectionPosition.x, -intersectionPosition.z));
|
||||
yawPitch.x = glm::atan(xdir.x, xdir.y);
|
||||
yawPitch.y = (acosf(intersectionPosition.y) * -1.0f) + M_PI_2;
|
||||
yawPitch.y = (acosf(intersectionPosition.y) * -1.0f) + (float)M_PI_2;
|
||||
}
|
||||
vec2 halfFov = CompositorHelper::VIRTUAL_UI_TARGET_FOV / 2.0f;
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
/// Initializes the manager.
|
||||
HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0);
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override;
|
||||
|
||||
private slots:
|
||||
void isTcpServerListening();
|
||||
|
@ -46,7 +46,7 @@ private:
|
|||
|
||||
protected:
|
||||
/// Accepts all pending connections
|
||||
virtual void incomingConnection(qintptr socketDescriptor);
|
||||
virtual void incomingConnection(qintptr socketDescriptor) override;
|
||||
virtual bool requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url);
|
||||
|
||||
QHostAddress _listenAddress;
|
||||
|
|
|
@ -48,7 +48,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
OctreeRenderer(),
|
||||
_wantScripts(wantScripts),
|
||||
_entitiesScriptEngine(NULL),
|
||||
_lastMouseEventValid(false),
|
||||
_lastPointerEventValid(false),
|
||||
_viewState(viewState),
|
||||
_scriptingServices(scriptingServices),
|
||||
_displayModelBounds(false),
|
||||
|
@ -195,9 +195,9 @@ void EntityTreeRenderer::update() {
|
|||
// Even if we're not moving the mouse, if we started clicking on an entity and we have
|
||||
// not yet released the hold then this is still considered a holdingClickOnEntity event
|
||||
// and we want to simulate this message here as well as in mouse move
|
||||
if (_lastMouseEventValid && !_currentClickingOnEntityID.isInvalidID()) {
|
||||
emit holdingClickOnEntity(_currentClickingOnEntityID, _lastMouseEvent);
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", _lastMouseEvent);
|
||||
if (_lastPointerEventValid && !_currentClickingOnEntityID.isInvalidID()) {
|
||||
emit holdingClickOnEntity(_currentClickingOnEntityID, _lastPointerEvent);
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", _lastPointerEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -599,18 +599,10 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons
|
|||
}
|
||||
|
||||
void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) {
|
||||
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface,
|
||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event){
|
||||
entityScriptingInterface->mousePressOnEntity(intersection.entityID, MouseEvent(*event));
|
||||
});
|
||||
connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface,
|
||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) {
|
||||
entityScriptingInterface->mouseMoveOnEntity(intersection.entityID, MouseEvent(*event));
|
||||
});
|
||||
connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface,
|
||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) {
|
||||
entityScriptingInterface->mouseReleaseOnEntity(intersection.entityID, MouseEvent(*event));
|
||||
});
|
||||
|
||||
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
|
||||
connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity);
|
||||
connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity);
|
||||
|
||||
connect(this, &EntityTreeRenderer::clickDownOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickDownOnEntity);
|
||||
connect(this, &EntityTreeRenderer::holdingClickOnEntity, entityScriptingInterface, &EntityScriptingInterface::holdingClickOnEntity);
|
||||
|
@ -627,6 +619,59 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS
|
|||
connect(DependencyManager::get<SceneScriptingInterface>().data(), &SceneScriptingInterface::shouldRenderEntitiesChanged, this, &EntityTreeRenderer::updateEntityRenderStatus, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
static glm::vec2 projectOntoEntityXYPlane(EntityItemPointer entity, const PickRay& pickRay, const RayToEntityIntersectionResult& rayPickResult) {
|
||||
|
||||
if (entity) {
|
||||
|
||||
glm::vec3 entityPosition = entity->getPosition();
|
||||
glm::quat entityRotation = entity->getRotation();
|
||||
glm::vec3 entityDimensions = entity->getDimensions();
|
||||
glm::vec3 entityRegistrationPoint = entity->getRegistrationPoint();
|
||||
|
||||
// project the intersection point onto the local xy plane of the object.
|
||||
float distance;
|
||||
glm::vec3 planePosition = entityPosition;
|
||||
glm::vec3 planeNormal = entityRotation * Vectors::UNIT_Z;
|
||||
glm::vec3 rayDirection = pickRay.direction;
|
||||
glm::vec3 rayStart = pickRay.origin;
|
||||
glm::vec3 p;
|
||||
if (rayPlaneIntersection(planePosition, planeNormal, rayStart, rayDirection, distance)) {
|
||||
p = rayStart + rayDirection * distance;
|
||||
} else {
|
||||
p = rayPickResult.intersection;
|
||||
}
|
||||
glm::vec3 localP = glm::inverse(entityRotation) * (p - entityPosition);
|
||||
glm::vec3 normalizedP = (localP / entityDimensions) + entityRegistrationPoint;
|
||||
return glm::vec2(normalizedP.x * entityDimensions.x,
|
||||
(1.0f - normalizedP.y) * entityDimensions.y); // flip y-axis
|
||||
} else {
|
||||
return glm::vec2();
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t toPointerButtons(const QMouseEvent& event) {
|
||||
uint32_t buttons = 0;
|
||||
buttons |= event.buttons().testFlag(Qt::LeftButton) ? PointerEvent::PrimaryButton : 0;
|
||||
buttons |= event.buttons().testFlag(Qt::RightButton) ? PointerEvent::SecondaryButton : 0;
|
||||
buttons |= event.buttons().testFlag(Qt::MiddleButton) ? PointerEvent::TertiaryButton : 0;
|
||||
return buttons;
|
||||
}
|
||||
|
||||
static PointerEvent::Button toPointerButton(const QMouseEvent& event) {
|
||||
switch (event.button()) {
|
||||
case Qt::LeftButton:
|
||||
return PointerEvent::PrimaryButton;
|
||||
case Qt::RightButton:
|
||||
return PointerEvent::SecondaryButton;
|
||||
case Qt::MiddleButton:
|
||||
return PointerEvent::TertiaryButton;
|
||||
default:
|
||||
return PointerEvent::NoButtons;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint32_t MOUSE_POINTER_ID = 0;
|
||||
|
||||
void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
|
||||
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||
// process these events.
|
||||
|
@ -645,24 +690,32 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
|
|||
QUrl url = QUrl(urlString, QUrl::StrictMode);
|
||||
if (url.isValid() && !url.isEmpty()){
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(urlString);
|
||||
|
||||
}
|
||||
|
||||
emit mousePressOnEntity(rayPickResult, event);
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Press, MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event));
|
||||
|
||||
emit mousePressOnEntity(rayPickResult.entityID, pointerEvent);
|
||||
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", pointerEvent);
|
||||
}
|
||||
|
||||
|
||||
_currentClickingOnEntityID = rayPickResult.entityID;
|
||||
emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event));
|
||||
emit clickDownOnEntity(_currentClickingOnEntityID, pointerEvent);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", pointerEvent);
|
||||
}
|
||||
|
||||
_lastPointerEvent = pointerEvent;
|
||||
_lastPointerEventValid = true;
|
||||
|
||||
} else {
|
||||
emit mousePressOffEntity(rayPickResult, event);
|
||||
emit mousePressOffEntity();
|
||||
}
|
||||
_lastMouseEvent = MouseEvent(*event);
|
||||
_lastMouseEventValid = true;
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) {
|
||||
|
@ -671,31 +724,48 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) {
|
|||
if (!_tree || _shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent");
|
||||
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
||||
bool precisionPicking = !_dontDoPrecisionPicking;
|
||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||
if (rayPickResult.intersects) {
|
||||
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
||||
emit mouseReleaseOnEntity(rayPickResult, event);
|
||||
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Release, MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event));
|
||||
|
||||
emit mouseReleaseOnEntity(rayPickResult.entityID, pointerEvent);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseReleaseOnEntity", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseReleaseOnEntity", pointerEvent);
|
||||
}
|
||||
|
||||
_lastPointerEvent = pointerEvent;
|
||||
_lastPointerEventValid = true;
|
||||
}
|
||||
|
||||
// Even if we're no longer intersecting with an entity, if we started clicking on it, and now
|
||||
// we're releasing the button, then this is considered a clickOn event
|
||||
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||
emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event));
|
||||
|
||||
auto entity = getTree()->findEntityByID(_currentClickingOnEntityID);
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Release, MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event));
|
||||
|
||||
emit clickReleaseOnEntity(_currentClickingOnEntityID, pointerEvent);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// makes it the unknown ID, we just released so we can't be clicking on anything
|
||||
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
|
||||
_lastMouseEvent = MouseEvent(*event);
|
||||
_lastMouseEventValid = true;
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
|
||||
|
@ -712,19 +782,35 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
|
|||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
|
||||
if (rayPickResult.intersects) {
|
||||
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event));
|
||||
|
||||
emit mouseMoveOnEntity(rayPickResult.entityID, pointerEvent);
|
||||
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", pointerEvent);
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", pointerEvent);
|
||||
}
|
||||
|
||||
|
||||
// handle the hover logic...
|
||||
|
||||
|
||||
// if we were previously hovering over an entity, and this new entity is not the same as our previous entity
|
||||
// then we need to send the hover leave.
|
||||
if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) {
|
||||
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event));
|
||||
|
||||
auto entity = getTree()->findEntityByID(_currentHoverOverEntityID);
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event));
|
||||
|
||||
emit hoverLeaveEntity(_currentHoverOverEntityID, pointerEvent);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -732,28 +818,39 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
|
|||
// this is true if the _currentHoverOverEntityID is known or unknown
|
||||
if (rayPickResult.entityID != _currentHoverOverEntityID) {
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", pointerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and
|
||||
// we should send our hover over event
|
||||
emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event));
|
||||
emit hoverOverEntity(rayPickResult.entityID, pointerEvent);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", pointerEvent);
|
||||
}
|
||||
|
||||
// remember what we're hovering over
|
||||
_currentHoverOverEntityID = rayPickResult.entityID;
|
||||
|
||||
_lastPointerEvent = pointerEvent;
|
||||
_lastPointerEventValid = true;
|
||||
|
||||
} else {
|
||||
// handle the hover logic...
|
||||
// if we were previously hovering over an entity, and we're no longer hovering over any entity then we need to
|
||||
// send the hover leave for our previous entity
|
||||
if (!_currentHoverOverEntityID.isInvalidID()) {
|
||||
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event));
|
||||
|
||||
auto entity = getTree()->findEntityByID(_currentHoverOverEntityID);
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event));
|
||||
|
||||
emit hoverLeaveEntity(_currentHoverOverEntityID, pointerEvent);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", pointerEvent);
|
||||
}
|
||||
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID; // makes it the unknown ID
|
||||
}
|
||||
|
@ -762,13 +859,19 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
|
|||
// Even if we're no longer intersecting with an entity, if we started clicking on an entity and we have
|
||||
// not yet released the hold then this is still considered a holdingClickOnEntity event
|
||||
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||
emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event));
|
||||
|
||||
auto entity = getTree()->findEntityByID(_currentClickingOnEntityID);
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event));
|
||||
|
||||
emit holdingClickOnEntity(_currentClickingOnEntityID, pointerEvent);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", pointerEvent);
|
||||
}
|
||||
}
|
||||
_lastMouseEvent = MouseEvent(*event);
|
||||
_lastMouseEventValid = true;
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
#include <AbstractAudioInterface.h>
|
||||
#include <EntityScriptingInterface.h> // for RayToEntityIntersectionResult
|
||||
#include <EntityTree.h>
|
||||
#include <MouseEvent.h>
|
||||
#include <QMouseEvent>
|
||||
#include <PointerEvent.h>
|
||||
#include <OctreeRenderer.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <TextureCache.h>
|
||||
|
@ -44,10 +45,10 @@ public:
|
|||
AbstractScriptingServicesInterface* scriptingServices);
|
||||
virtual ~EntityTreeRenderer();
|
||||
|
||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; }
|
||||
virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; }
|
||||
virtual void setTree(OctreePointer newTree);
|
||||
virtual char getMyNodeType() const override { return NodeType::EntityServer; }
|
||||
virtual PacketType getMyQueryMessageType() const override { return PacketType::EntityQuery; }
|
||||
virtual PacketType getExpectedPacketType() const override { return PacketType::EntityData; }
|
||||
virtual void setTree(OctreePointer newTree) override;
|
||||
|
||||
// Returns the priority at which an entity should be loaded. Higher values indicate higher priority.
|
||||
float getEntityLoadingPriority(const EntityItem& item) const { return _calculateEntityLoadingPriorityFunc(item); }
|
||||
|
@ -60,29 +61,29 @@ public:
|
|||
|
||||
void processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode);
|
||||
|
||||
virtual void init();
|
||||
virtual void init() override;
|
||||
|
||||
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) override;
|
||||
virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) override;
|
||||
virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) override;
|
||||
|
||||
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem);
|
||||
virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem);
|
||||
virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem);
|
||||
|
||||
/// clears the tree
|
||||
virtual void clear();
|
||||
virtual void clear() override;
|
||||
|
||||
/// reloads the entity scripts, calling unload and preload
|
||||
void reloadEntityScripts();
|
||||
|
||||
/// if a renderable entity item needs a model, we will allocate it for them
|
||||
Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority = 0.0f);
|
||||
|
||||
|
||||
/// if a renderable entity item needs to update the URL of a model, we will handle that for the entity
|
||||
Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl);
|
||||
|
||||
/// if a renderable entity item is done with a model, it should return it to us
|
||||
void releaseModel(ModelPointer model);
|
||||
|
||||
|
||||
void deleteReleasedModels();
|
||||
|
||||
|
||||
// event handles which may generate entity related events
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
|
@ -98,18 +99,18 @@ public:
|
|||
std::shared_ptr<ZoneEntityItem> myAvatarZone() { return _bestZone; }
|
||||
|
||||
signals:
|
||||
void mousePressOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||
void mousePressOffEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||
void mouseMoveOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||
void mouseReleaseOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||
void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void mouseMoveOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void mousePressOffEntity();
|
||||
|
||||
void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||
void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||
void clickReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||
void clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void clickReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
|
||||
void hoverEnterEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||
void hoverOverEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||
void hoverLeaveEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||
void hoverEnterEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void hoverOverEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void hoverLeaveEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
|
||||
void enterEntity(const EntityItemID& entityItemID);
|
||||
void leaveEntity(const EntityItemID& entityItemID);
|
||||
|
@ -128,7 +129,7 @@ public slots:
|
|||
void setDontDoPrecisionPicking(bool value) { _dontDoPrecisionPicking = value; }
|
||||
|
||||
protected:
|
||||
virtual OctreePointer createTree() {
|
||||
virtual OctreePointer createTree() override {
|
||||
EntityTreePointer newTree = EntityTreePointer(new EntityTree(true));
|
||||
newTree->createRootElement();
|
||||
return newTree;
|
||||
|
@ -174,13 +175,13 @@ private:
|
|||
void playEntityCollisionSound(const QUuid& myNodeID, EntityTreePointer entityTree,
|
||||
const EntityItemID& id, const Collision& collision);
|
||||
|
||||
bool _lastMouseEventValid;
|
||||
MouseEvent _lastMouseEvent;
|
||||
bool _lastPointerEventValid;
|
||||
PointerEvent _lastPointerEvent;
|
||||
AbstractViewStateInterface* _viewState;
|
||||
AbstractScriptingServicesInterface* _scriptingServices;
|
||||
bool _displayModelBounds;
|
||||
bool _dontDoPrecisionPicking;
|
||||
|
||||
|
||||
bool _shuttingDown { false };
|
||||
|
||||
QMultiMap<QUrl, EntityItemID> _waitingOnPreload;
|
||||
|
@ -194,6 +195,17 @@ private:
|
|||
const quint64 ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz
|
||||
const float ZONE_CHECK_DISTANCE = 0.001f;
|
||||
|
||||
glm::vec3 _previousKeyLightColor;
|
||||
float _previousKeyLightIntensity;
|
||||
float _previousKeyLightAmbientIntensity;
|
||||
glm::vec3 _previousKeyLightDirection;
|
||||
bool _previousStageSunModelEnabled;
|
||||
float _previousStageLongitude;
|
||||
float _previousStageLatitude;
|
||||
float _previousStageAltitude;
|
||||
float _previousStageHour;
|
||||
int _previousStageDay;
|
||||
|
||||
QHash<EntityItemID, EntityItemPointer> _entitiesInScene;
|
||||
// For Scene.shouldRenderEntities
|
||||
QList<EntityItemID> _entityIDsLastInScene;
|
||||
|
|
|
@ -595,6 +595,9 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
|
|||
}
|
||||
|
||||
void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||
const uint32_t TRIANGLE_STRIDE = 3;
|
||||
const uint32_t QUAD_STRIDE = 4;
|
||||
|
||||
ShapeType type = getShapeType();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
if (type == SHAPE_TYPE_COMPOUND) {
|
||||
|
@ -611,8 +614,6 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
|
||||
// the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect
|
||||
// to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case.
|
||||
const uint32_t TRIANGLE_STRIDE = 3;
|
||||
const uint32_t QUAD_STRIDE = 4;
|
||||
foreach (const FBXMesh& mesh, collisionGeometry.meshes) {
|
||||
// each meshPart is a convex hull
|
||||
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
||||
|
@ -621,7 +622,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
|
||||
// run through all the triangles and (uniquely) add each point to the hull
|
||||
uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size();
|
||||
assert(numIndices % TRIANGLE_STRIDE == 0);
|
||||
// TODO: assert rather than workaround after we start sanitizing FBXMesh higher up
|
||||
//assert(numIndices % TRIANGLE_STRIDE == 0);
|
||||
numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader
|
||||
|
||||
for (uint32_t j = 0; j < numIndices; j += TRIANGLE_STRIDE) {
|
||||
glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[j]];
|
||||
glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[j + 1]];
|
||||
|
@ -639,7 +643,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
|
||||
// run through all the quads and (uniquely) add each point to the hull
|
||||
numIndices = (uint32_t)meshPart.quadIndices.size();
|
||||
assert(numIndices % QUAD_STRIDE == 0);
|
||||
// TODO: assert rather than workaround after we start sanitizing FBXMesh higher up
|
||||
//assert(numIndices % QUAD_STRIDE == 0);
|
||||
numIndices -= numIndices % QUAD_STRIDE; // WORKAROUND lack of sanity checking in FBXReader
|
||||
|
||||
for (uint32_t j = 0; j < numIndices; j += QUAD_STRIDE) {
|
||||
glm::vec3 p0 = mesh.vertices[meshPart.quadIndices[j]];
|
||||
glm::vec3 p1 = mesh.vertices[meshPart.quadIndices[j + 1]];
|
||||
|
@ -735,6 +742,9 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
int32_t meshCount = 0;
|
||||
int32_t pointListIndex = 0;
|
||||
for (auto& mesh : meshes) {
|
||||
if (!mesh) {
|
||||
continue;
|
||||
}
|
||||
const gpu::BufferView& vertices = mesh->getVertexBuffer();
|
||||
const gpu::BufferView& indices = mesh->getIndexBuffer();
|
||||
const gpu::BufferView& parts = mesh->getPartBuffer();
|
||||
|
@ -768,24 +778,30 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements()));
|
||||
gpu::BufferView::Iterator<const model::Mesh::Part> partItr = parts.cbegin<const model::Mesh::Part>();
|
||||
while (partItr != parts.cend<const model::Mesh::Part>()) {
|
||||
auto numIndices = partItr->_numIndices;
|
||||
if (partItr->_topology == model::Mesh::TRIANGLES) {
|
||||
assert(partItr->_numIndices % 3 == 0);
|
||||
// TODO: assert rather than workaround after we start sanitizing FBXMesh higher up
|
||||
//assert(numIndices % TRIANGLE_STRIDE == 0);
|
||||
numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader
|
||||
|
||||
auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex;
|
||||
auto indexEnd = indexItr + partItr->_numIndices;
|
||||
auto indexEnd = indexItr + numIndices;
|
||||
while (indexItr != indexEnd) {
|
||||
triangleIndices.push_back(*indexItr + meshIndexOffset);
|
||||
++indexItr;
|
||||
}
|
||||
} else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) {
|
||||
assert(partItr->_numIndices > 2);
|
||||
uint32_t approxNumIndices = 3 * partItr->_numIndices;
|
||||
// TODO: resurrect assert after we start sanitizing FBXMesh higher up
|
||||
//assert(numIndices > 2);
|
||||
|
||||
uint32_t approxNumIndices = TRIANGLE_STRIDE * numIndices;
|
||||
if (approxNumIndices > (uint32_t)(triangleIndices.capacity() - triangleIndices.size())) {
|
||||
// we underestimated the final size of triangleIndices so we pre-emptively expand it
|
||||
triangleIndices.reserve(triangleIndices.size() + approxNumIndices);
|
||||
}
|
||||
|
||||
auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex;
|
||||
auto indexEnd = indexItr + (partItr->_numIndices - 2);
|
||||
auto indexEnd = indexItr + (numIndices - 2);
|
||||
|
||||
// first triangle uses the first three indices
|
||||
triangleIndices.push_back(*(indexItr++) + meshIndexOffset);
|
||||
|
@ -819,18 +835,24 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
while (partItr != parts.cend<const model::Mesh::Part>()) {
|
||||
// collect unique list of indices for this part
|
||||
std::set<int32_t> uniqueIndices;
|
||||
auto numIndices = partItr->_numIndices;
|
||||
if (partItr->_topology == model::Mesh::TRIANGLES) {
|
||||
assert(partItr->_numIndices % 3 == 0);
|
||||
// TODO: assert rather than workaround after we start sanitizing FBXMesh higher up
|
||||
//assert(numIndices% TRIANGLE_STRIDE == 0);
|
||||
numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader
|
||||
|
||||
auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex;
|
||||
auto indexEnd = indexItr + partItr->_numIndices;
|
||||
auto indexEnd = indexItr + numIndices;
|
||||
while (indexItr != indexEnd) {
|
||||
uniqueIndices.insert(*indexItr);
|
||||
++indexItr;
|
||||
}
|
||||
} else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) {
|
||||
assert(partItr->_numIndices > 2);
|
||||
// TODO: resurrect assert after we start sanitizing FBXMesh higher up
|
||||
//assert(numIndices > TRIANGLE_STRIDE - 1);
|
||||
|
||||
auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex;
|
||||
auto indexEnd = indexItr + (partItr->_numIndices - 2);
|
||||
auto indexEnd = indexItr + (numIndices - 2);
|
||||
|
||||
// first triangle uses the first three indices
|
||||
uniqueIndices.insert(*(indexItr++));
|
||||
|
@ -842,11 +864,11 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
while (indexItr != indexEnd) {
|
||||
if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) {
|
||||
if (triangleCount % 2 == 0) {
|
||||
// even triangles use first two indices in order
|
||||
// EVEN triangles use first two indices in order
|
||||
uniqueIndices.insert(*(indexItr - 2));
|
||||
uniqueIndices.insert(*(indexItr - 1));
|
||||
} else {
|
||||
// odd triangles swap order of first two indices
|
||||
// ODD triangles swap order of first two indices
|
||||
uniqueIndices.insert(*(indexItr - 1));
|
||||
uniqueIndices.insert(*(indexItr - 2));
|
||||
}
|
||||
|
|
|
@ -63,8 +63,8 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
|
|||
QSurface * currentSurface = currentContext->surface();
|
||||
_webSurface = new OffscreenQmlSurface();
|
||||
_webSurface->create(currentContext);
|
||||
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
||||
_webSurface->load("WebEntity.qml");
|
||||
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/controls/"));
|
||||
_webSurface->load("WebView.qml");
|
||||
_webSurface->resume();
|
||||
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
||||
_connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) {
|
||||
|
@ -73,91 +73,27 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
|
|||
// Restore the original GL context
|
||||
currentContext->makeCurrent(currentSurface);
|
||||
|
||||
auto forwardMouseEvent = [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) {
|
||||
// Ignore mouse interaction if we're locked
|
||||
if (this->getLocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() == Qt::MouseButton::RightButton) {
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
const QMouseEvent* mouseEvent = static_cast<const QMouseEvent*>(event);
|
||||
_lastPress = toGlm(mouseEvent->pos());
|
||||
}
|
||||
}
|
||||
|
||||
if (intersection.entityID == getID()) {
|
||||
if (event->button() == Qt::MouseButton::RightButton) {
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
const QMouseEvent* mouseEvent = static_cast<const QMouseEvent*>(event);
|
||||
ivec2 dist = glm::abs(toGlm(mouseEvent->pos()) - _lastPress);
|
||||
if (!glm::any(glm::greaterThan(dist, ivec2(1)))) {
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||
QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack");
|
||||
});
|
||||
}
|
||||
_lastPress = ivec2(INT_MIN);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME doesn't work... double click events not received
|
||||
if (event->type() == QEvent::MouseButtonDblClick) {
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
||||
});
|
||||
}
|
||||
|
||||
if (event->button() == Qt::MouseButton::MiddleButton) {
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Map the intersection point to an actual offscreen pixel
|
||||
glm::vec3 point = intersection.intersection;
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
point -= getPosition();
|
||||
point = glm::inverse(getRotation()) * point;
|
||||
point /= dimensions;
|
||||
point += 0.5f;
|
||||
point.y = 1.0f - point.y;
|
||||
point *= dimensions * (METERS_TO_INCHES * DPI);
|
||||
|
||||
if (event->button() == Qt::MouseButton::LeftButton) {
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
this->_pressed = true;
|
||||
this->_lastMove = ivec2((int)point.x, (int)point.y);
|
||||
} else if (event->type() == QEvent::MouseButtonRelease) {
|
||||
this->_pressed = false;
|
||||
}
|
||||
}
|
||||
if (event->type() == QEvent::MouseMove) {
|
||||
this->_lastMove = ivec2((int)point.x, (int)point.y);
|
||||
}
|
||||
|
||||
// Forward the mouse event.
|
||||
QMouseEvent mappedEvent(event->type(),
|
||||
QPoint((int)point.x, (int)point.y),
|
||||
event->screenPos(), event->button(),
|
||||
event->buttons(), event->modifiers());
|
||||
QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent);
|
||||
auto forwardPointerEvent = [=](const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||
if (entityItemID == getID()) {
|
||||
handlePointerEvent(event);
|
||||
}
|
||||
};
|
||||
_mousePressConnection = QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardMouseEvent);
|
||||
_mouseReleaseConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseReleaseOnEntity, forwardMouseEvent);
|
||||
_mouseMoveConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseMoveOnEntity, forwardMouseEvent);
|
||||
_hoverLeaveConnection = QObject::connect(renderer, &EntityTreeRenderer::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
|
||||
_mousePressConnection = QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardPointerEvent);
|
||||
_mouseReleaseConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseReleaseOnEntity, forwardPointerEvent);
|
||||
_mouseMoveConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseMoveOnEntity, forwardPointerEvent);
|
||||
_hoverLeaveConnection = QObject::connect(renderer, &EntityTreeRenderer::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||
if (this->_pressed && this->getID() == entityItemID) {
|
||||
// If the user mouses off the entity while the button is down, simulate a mouse release
|
||||
QMouseEvent mappedEvent(QEvent::MouseButtonRelease,
|
||||
QPoint(_lastMove.x, _lastMove.y),
|
||||
Qt::MouseButton::LeftButton,
|
||||
Qt::MouseButtons(), Qt::KeyboardModifiers());
|
||||
QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent);
|
||||
// If the user mouses off the entity while the button is down, simulate a touch end.
|
||||
QTouchEvent::TouchPoint point;
|
||||
point.setId(event.getID());
|
||||
point.setState(Qt::TouchPointReleased);
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * DPI);
|
||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||
point.setPos(windowPoint);
|
||||
QList<QTouchEvent::TouchPoint> touchPoints;
|
||||
touchPoints.push_back(point);
|
||||
QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased, touchPoints);
|
||||
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
|
@ -242,6 +178,69 @@ QObject* RenderableWebEntityItem::getEventHandler() {
|
|||
return _webSurface->getEventHandler();
|
||||
}
|
||||
|
||||
void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) {
|
||||
|
||||
// Ignore mouse interaction if we're locked
|
||||
if (getLocked() || !_webSurface) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * DPI);
|
||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||
|
||||
if (event.getType() == PointerEvent::Move) {
|
||||
// Forward a mouse move event to webSurface
|
||||
QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
|
||||
QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent);
|
||||
}
|
||||
|
||||
{
|
||||
// Forward a touch update event to webSurface
|
||||
if (event.getType() == PointerEvent::Press) {
|
||||
this->_pressed = true;
|
||||
} else if (event.getType() == PointerEvent::Release) {
|
||||
this->_pressed = false;
|
||||
}
|
||||
|
||||
QEvent::Type type;
|
||||
Qt::TouchPointState touchPointState;
|
||||
switch (event.getType()) {
|
||||
case PointerEvent::Press:
|
||||
type = QEvent::TouchBegin;
|
||||
touchPointState = Qt::TouchPointPressed;
|
||||
break;
|
||||
case PointerEvent::Release:
|
||||
type = QEvent::TouchEnd;
|
||||
touchPointState = Qt::TouchPointReleased;
|
||||
break;
|
||||
case PointerEvent::Move:
|
||||
default:
|
||||
type = QEvent::TouchUpdate;
|
||||
touchPointState = Qt::TouchPointMoved;
|
||||
break;
|
||||
}
|
||||
|
||||
QTouchEvent::TouchPoint point;
|
||||
point.setId(event.getID());
|
||||
point.setState(touchPointState);
|
||||
point.setPos(windowPoint);
|
||||
point.setScreenPos(windowPoint);
|
||||
QList<QTouchEvent::TouchPoint> touchPoints;
|
||||
touchPoints.push_back(point);
|
||||
|
||||
QTouchEvent* touchEvent = new QTouchEvent(type);
|
||||
touchEvent->setWindow(nullptr);
|
||||
touchEvent->setDevice(nullptr);
|
||||
touchEvent->setTarget(nullptr);
|
||||
touchEvent->setTouchPoints(touchPoints);
|
||||
touchEvent->setTouchPointStates(touchPointState);
|
||||
|
||||
_lastTouchEvent = *touchEvent;
|
||||
|
||||
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableWebEntityItem::destroyWebSurface() {
|
||||
if (_webSurface) {
|
||||
--_currentWebCount;
|
||||
|
|