diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 970b6518ec..aa20f2ff29 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -18,6 +18,7 @@ #include #include "AssignmentFactory.h" +#include "AssignmentThread.h" #include "AssignmentClient.h" @@ -28,7 +29,7 @@ int hifiSockAddrMeta = qRegisterMetaType("HifiSockAddr"); AssignmentClient::AssignmentClient(int &argc, char **argv) : QCoreApplication(argc, argv), - _currentAssignment(NULL) + _currentAssignment() { setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); @@ -124,7 +125,7 @@ void AssignmentClient::readPendingDatagrams() { if (nodeList->packetVersionAndHashMatch(receivedPacket)) { if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) { // construct the deployed assignment from the packet data - _currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket); + _currentAssignment = SharedAssignmentPointer(AssignmentFactory::unpackAssignment(receivedPacket)); if (_currentAssignment) { qDebug() << "Received an assignment -" << *_currentAssignment; @@ -137,14 +138,13 @@ void AssignmentClient::readPendingDatagrams() { qDebug() << "Destination IP for assignment is" << nodeList->getDomainInfo().getIP().toString(); // start the deployed assignment - QThread* workerThread = new QThread(this); + AssignmentThread* workerThread = new AssignmentThread(_currentAssignment, this); - connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); - - connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); - connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); - connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); - connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); + connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run); + connect(_currentAssignment.data(), &ThreadedAssignment::finished, workerThread, &QThread::quit); + connect(_currentAssignment.data(), &ThreadedAssignment::finished, + this, &AssignmentClient::assignmentCompleted); + connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater); _currentAssignment->moveToThread(workerThread); @@ -153,7 +153,7 @@ void AssignmentClient::readPendingDatagrams() { // let the assignment handle the incoming datagrams for its duration disconnect(&nodeList->getNodeSocket(), 0, this, 0); - connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment, + connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment.data(), &ThreadedAssignment::readPendingDatagrams); // Starts an event loop, and emits workerThread->started() @@ -202,10 +202,12 @@ void AssignmentClient::assignmentCompleted() { NodeList* nodeList = NodeList::getInstance(); // have us handle incoming NodeList datagrams again - disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment, 0); + disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment.data(), 0); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams); - _currentAssignment = NULL; + // clear our current assignment shared pointer now that we're done with it + // if the assignment thread is still around it has its own shared pointer to the assignment + _currentAssignment.clear(); // reset our NodeList by switching back to unassigned and clearing the list nodeList->setOwnerType(NodeType::Unassigned); diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 29187fa3d6..c267c6238b 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -24,7 +24,7 @@ private slots: void handleAuthenticationRequest(); private: Assignment _requestAssignment; - ThreadedAssignment* _currentAssignment; + SharedAssignmentPointer _currentAssignment; }; #endif /* defined(__hifi__AssignmentClient__) */ diff --git a/assignment-client/src/AssignmentThread.cpp b/assignment-client/src/AssignmentThread.cpp new file mode 100644 index 0000000000..dfe093aa7b --- /dev/null +++ b/assignment-client/src/AssignmentThread.cpp @@ -0,0 +1,16 @@ +// +// AssignmentThread.cpp +// hifi +// +// Created by Stephen Birarda on 2014-03-28. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include "AssignmentThread.h" + +AssignmentThread::AssignmentThread(const SharedAssignmentPointer& assignment, QObject* parent) : + QThread(parent), + _assignment(assignment) +{ + +} \ No newline at end of file diff --git a/assignment-client/src/AssignmentThread.h b/assignment-client/src/AssignmentThread.h new file mode 100644 index 0000000000..b55ac10676 --- /dev/null +++ b/assignment-client/src/AssignmentThread.h @@ -0,0 +1,23 @@ +// +// AssignmentThread.h +// hifi +// +// Created by Stephen Birarda on 2014-03-28. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__AssignmentThread__ +#define __hifi__AssignmentThread__ + +#include + +#include + +class AssignmentThread : public QThread { +public: + AssignmentThread(const SharedAssignmentPointer& assignment, QObject* parent); +private: + SharedAssignmentPointer _assignment; +}; + +#endif /* defined(__hifi__AssignmentThread__) */ diff --git a/interface/interface_en.ts b/interface/interface_en.ts index c1d16e878f..519e2b61c1 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -27,25 +27,25 @@ ChatWindow - - + + Chat - - + + Connecting to XMPP... - - + + online now: - + day %n day @@ -53,7 +53,7 @@ - + hour %n hour @@ -61,7 +61,7 @@ - + minute %n minute @@ -76,7 +76,7 @@ - + %1 online now: diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e1764374ea..7748c466c7 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -924,14 +924,17 @@ void Menu::goTo() { int dialogReturn = gotoDialog.exec(); if (dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty()) { - LocationManager* manager = &LocationManager::getInstance(); - manager->goTo(gotoDialog.textValue()); - connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision); + goToUser(gotoDialog.textValue()); } - sendFakeEnterEvent(); } +void Menu::goToUser(const QString& user) { + LocationManager* manager = &LocationManager::getInstance(); + manager->goTo(user); + connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision); +} + void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData) { QMessageBox msgBox; msgBox.setText("Both user and location exists with same name"); @@ -1099,13 +1102,23 @@ void Menu::showMetavoxelEditor() { } void Menu::showChat() { + QMainWindow* mainWindow = Application::getInstance()->getWindow(); if (!_chatWindow) { - Application::getInstance()->getWindow()->addDockWidget(Qt::RightDockWidgetArea, _chatWindow = new ChatWindow()); - - } else { - if (!_chatWindow->toggleViewAction()->isChecked()) { - _chatWindow->toggleViewAction()->trigger(); - } + mainWindow->addDockWidget(Qt::NoDockWidgetArea, _chatWindow = new ChatWindow()); + } + if (!_chatWindow->toggleViewAction()->isChecked()) { + int width = _chatWindow->width(); + int y = qMax((mainWindow->height() - _chatWindow->height()) / 2, 0); + _chatWindow->move(mainWindow->width(), y); + _chatWindow->resize(0, _chatWindow->height()); + _chatWindow->toggleViewAction()->trigger(); + + QPropertyAnimation* slideAnimation = new QPropertyAnimation(_chatWindow, "geometry", _chatWindow); + slideAnimation->setStartValue(_chatWindow->geometry()); + slideAnimation->setEndValue(QRect(mainWindow->width() - width, _chatWindow->y(), + width, _chatWindow->height())); + slideAnimation->setDuration(250); + slideAnimation->start(QAbstractAnimation::DeleteWhenStopped); } } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 6b41430eaf..c7c4c6ecea 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -122,6 +122,7 @@ public slots: void importSettings(); void exportSettings(); void goTo(); + void goToUser(const QString& user); void pasteToVoxel(); void toggleLoginMenuItem(); diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 55f32c5c7c..76e9c4ec2d 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -6,13 +6,12 @@ // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. // -#include +#include #include #include #include #include #include -#include #include #include "Application.h" @@ -31,17 +30,18 @@ ChatWindow::ChatWindow() : ui(new Ui::ChatWindow), numMessagesAfterLastTimeStamp(0) { - QWidget* widget = new QWidget(); - setWidget(widget); - - ui->setupUi(widget); + ui->setupUi(this); + + // remove the title bar (see the Qt docs on setTitleBarWidget) + setTitleBarWidget(new QWidget()); FlowLayout* flowLayout = new FlowLayout(0, 4, 4); ui->usersWidget->setLayout(flowLayout); - ui->messagePlainTextEdit->installEventFilter(this); + ui->messagesGridLayout->setColumnStretch(0, 1); + ui->messagesGridLayout->setColumnStretch(1, 3); - ui->closeButton->hide(); + ui->messagePlainTextEdit->installEventFilter(this); #ifdef HAVE_QXMPP const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); @@ -76,41 +76,48 @@ ChatWindow::~ChatWindow() { } void ChatWindow::keyPressEvent(QKeyEvent* event) { - QWidget::keyPressEvent(event); + QDockWidget::keyPressEvent(event); if (event->key() == Qt::Key_Escape) { hide(); } } void ChatWindow::showEvent(QShowEvent* event) { - QWidget::showEvent(event); + QDockWidget::showEvent(event); if (!event->spontaneous()) { + activateWindow(); ui->messagePlainTextEdit->setFocus(); } } bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { - Q_UNUSED(sender); - - if (event->type() != QEvent::KeyPress) { - return false; - } - QKeyEvent* keyEvent = static_cast(event); - if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && - (keyEvent->modifiers() & Qt::ShiftModifier) == 0) { - QString messageText = ui->messagePlainTextEdit->document()->toPlainText().trimmed(); - if (!messageText.isEmpty()) { -#ifdef HAVE_QXMPP - const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); - QXmppMessage message; - message.setTo(publicChatRoom->jid()); - message.setType(QXmppMessage::GroupChat); - message.setBody(messageText); - XmppClient::getInstance().getXMPPClient().sendPacket(message); -#endif - ui->messagePlainTextEdit->document()->clear(); + if (sender == ui->messagePlainTextEdit) { + if (event->type() != QEvent::KeyPress) { + return false; } - return true; + QKeyEvent* keyEvent = static_cast(event); + if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && + (keyEvent->modifiers() & Qt::ShiftModifier) == 0) { + QString messageText = ui->messagePlainTextEdit->document()->toPlainText().trimmed(); + if (!messageText.isEmpty()) { + #ifdef HAVE_QXMPP + const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); + QXmppMessage message; + message.setTo(publicChatRoom->jid()); + message.setType(QXmppMessage::GroupChat); + message.setBody(messageText); + XmppClient::getInstance().getXMPPClient().sendPacket(message); + #endif + ui->messagePlainTextEdit->document()->clear(); + } + return true; + } + } else { + if (event->type() != QEvent::MouseButtonRelease) { + return false; + } + QString user = sender->property("user").toString(); + Menu::getInstance()->goToUser(user); } return false; } @@ -138,8 +145,9 @@ void ChatWindow::addTimeStamp() { timeLabel->setStyleSheet("color: palette(shadow);" "background-color: palette(highlight);" "padding: 4px;"); + timeLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); timeLabel->setAlignment(Qt::AlignHCenter); - ui->messagesFormLayout->addRow(timeLabel); + ui->messagesGridLayout->addWidget(timeLabel, ui->messagesGridLayout->rowCount(), 0, 1, 2); numMessagesAfterLastTimeStamp = 0; } } @@ -187,13 +195,21 @@ void ChatWindow::participantsChanged() { delete item; } foreach (const QString& participant, participants) { - QLabel* userLabel = new QLabel(getParticipantName(participant)); + QString participantName = getParticipantName(participant); + QLabel* userLabel = new QLabel(); + userLabel->setText(participantName); userLabel->setStyleSheet("background-color: palette(light);" "border-radius: 5px;" "color: #267077;" - "padding: 2px;" + "padding-top: 3px;" + "padding-right: 2px;" + "padding-bottom: 2px;" + "padding-left: 2px;" "border: 1px solid palette(shadow);" "font-weight: bold"); + userLabel->setProperty("user", participantName); + userLabel->setCursor(Qt::PointingHandCursor); + userLabel->installEventFilter(this); ui->usersWidget->layout()->addWidget(userLabel); } } @@ -204,19 +220,25 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { } QLabel* userLabel = new QLabel(getParticipantName(message.from())); - userLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + userLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); userLabel->setStyleSheet("padding: 2px; font-weight: bold"); - userLabel->setAlignment(Qt::AlignTop); + userLabel->setAlignment(Qt::AlignTop | Qt::AlignRight); QLabel* messageLabel = new QLabel(message.body().replace(regexLinks, "\\1")); messageLabel->setWordWrap(true); messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); messageLabel->setOpenExternalLinks(true); - messageLabel->setStyleSheet("padding: 2px; margin-right: 20px"); - messageLabel->setAlignment(Qt::AlignTop); + messageLabel->setStyleSheet("padding-bottom: 2px; padding-right: 2px; padding-top: 2px; padding-right: 20px"); + messageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); - ui->messagesFormLayout->addRow(userLabel, messageLabel); - ui->messagesFormLayout->parentWidget()->updateGeometry(); + if (getParticipantName(message.from()) == AccountManager::getInstance().getUsername()) { + userLabel->setStyleSheet(userLabel->styleSheet() + "; background-color: #e1e8ea"); + messageLabel->setStyleSheet(messageLabel->styleSheet() + "; background-color: #e1e8ea"); + } + + ui->messagesGridLayout->addWidget(userLabel, ui->messagesGridLayout->rowCount(), 0); + ui->messagesGridLayout->addWidget(messageLabel, ui->messagesGridLayout->rowCount() - 1, 1); + ui->messagesGridLayout->parentWidget()->updateGeometry(); Application::processEvents(); QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar(); verticalScrollBar->setSliderPosition(verticalScrollBar->maximum()); diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index 1106fca3cd..60a0c6badd 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -1,7 +1,7 @@ ChatWindow - + 0 @@ -13,180 +13,188 @@ 400 - 0 + 238 - - Chat - font-family: Helvetica, Arial, sans-serif; - - - 0 - - - 8 - - - 8 - - - 8 - - - 8 - - - - - - 0 - 0 - - - - Connecting to XMPP... - - - Qt::AlignCenter - - - - - - - + + QDockWidget::NoDockWidgetFeatures + + + Qt::NoDockWidgetArea + + + Chat + + + + + 0 + + + 8 + + + 8 + + + 8 + + + 8 + + + + + + 0 + 0 + + + + Connecting to XMPP... + + + Qt::AlignCenter + + + + + + + + + + 0 + 0 + + + + font-weight: bold; color: palette(shadow); margin-bottom: 4px; + + + online now: + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + Qt::NoFocus + + + + + + + :/images/close.svg:/images/close.svg + + + true + + + + + + + + + + + + margin-top: 12px; + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 382 + 16 + + - + 0 0 - font-weight: bold; color: palette(shadow); margin-bottom: 4px; - - - online now: + margin-top: 0px; + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + - - - - - - 0 - 0 - - - - - 16 - 16 - - - - Qt::NoFocus - - - - - - - :/images/close.svg:/images/close.svg - - - true - - - - - - - - - - - - margin-top: 12px; - - - Qt::ScrollBarAlwaysOff - - - true - - - - - 0 - 0 - 358 - 464 - + + + + + + + 0 + 0 + + + + + 0 + 60 + - margin-top: 0px; + border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px; + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + true - - - QFormLayout::AllNonFixedFieldsGrow - - - 0 - - - 0 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - - - - 0 - 0 - - - - - 0 - 60 - - - - border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px; - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - true - - - - + + + messagePlainTextEdit diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp index be49b18055..fdf2d91c36 100644 --- a/libraries/shared/src/ThreadedAssignment.cpp +++ b/libraries/shared/src/ThreadedAssignment.cpp @@ -20,18 +20,15 @@ ThreadedAssignment::ThreadedAssignment(const QByteArray& packet) : } -void ThreadedAssignment::deleteLater() { - // move the NodeList back to the QCoreApplication instance's thread - NodeList::getInstance()->moveToThread(QCoreApplication::instance()->thread()); - QObject::deleteLater(); -} - void ThreadedAssignment::setFinished(bool isFinished) { _isFinished = isFinished; if (_isFinished) { aboutToFinish(); emit finished(); + + // move the NodeList back to the QCoreApplication instance's thread + NodeList::getInstance()->moveToThread(QCoreApplication::instance()->thread()); } } diff --git a/libraries/shared/src/ThreadedAssignment.h b/libraries/shared/src/ThreadedAssignment.h index 5b78eed56d..f9652dd98d 100644 --- a/libraries/shared/src/ThreadedAssignment.h +++ b/libraries/shared/src/ThreadedAssignment.h @@ -9,6 +9,8 @@ #ifndef __hifi__ThreadedAssignment__ #define __hifi__ThreadedAssignment__ +#include + #include "Assignment.h" class ThreadedAssignment : public Assignment { @@ -22,7 +24,6 @@ public: public slots: /// threaded run of assignment virtual void run() = 0; - virtual void deleteLater(); virtual void readPendingDatagrams() = 0; virtual void sendStatsPacket(); @@ -36,5 +37,6 @@ signals: void finished(); }; +typedef QSharedPointer SharedAssignmentPointer; #endif /* defined(__hifi__ThreadedAssignment__) */