diff --git a/interface/resources/images/pin.svg b/interface/resources/images/pin.svg deleted file mode 100644 index ec968a1ec1..0000000000 --- a/interface/resources/images/pin.svg +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - image/svg+xml - - Slice 1 - - - - - Slice 1 - Created with Sketch (http://www.bohemiancoding.com/sketch) - - - - - - - - diff --git a/interface/resources/images/pinned.svg b/interface/resources/images/pinned.svg deleted file mode 100644 index bda6f0e747..0000000000 --- a/interface/resources/images/pinned.svg +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - image/svg+xml - - Slice 1 - - - - - Slice 1 - Created with Sketch (http://www.bohemiancoding.com/sketch) - - - - - - - - - diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4adb2f772a..ab505ede93 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3520,35 +3520,30 @@ void Application::parseVersionXml() { QString operatingSystem("ubuntu"); #endif - QString releaseDate; - QString releaseNotes; QString latestVersion; QUrl downloadUrl; + QString releaseNotes("Unavailable"); QObject* sender = QObject::sender(); QXmlStreamReader xml(qobject_cast(sender)); + while (!xml.atEnd() && !xml.hasError()) { - QXmlStreamReader::TokenType token = xml.readNext(); - - if (token == QXmlStreamReader::StartElement) { - if (xml.name() == "ReleaseDate") { + if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == operatingSystem) { + while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == operatingSystem)) { + if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "version") { + xml.readNext(); + latestVersion = xml.text().toString(); + } + if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "url") { + xml.readNext(); + downloadUrl = QUrl(xml.text().toString()); + } xml.readNext(); - releaseDate = xml.text().toString(); - } - if (xml.name() == "ReleaseNotes") { - xml.readNext(); - releaseNotes = xml.text().toString(); - } - if (xml.name() == "Version") { - xml.readNext(); - latestVersion = xml.text().toString(); - } - if (xml.name() == operatingSystem) { - xml.readNext(); - downloadUrl = QUrl(xml.text().toString()); } } + xml.readNext(); } + if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) { new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 5983b0d1a9..325770a8df 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -16,6 +16,7 @@ #include #include +#include #include #include #include diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index c182f6e842..68e38615bf 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -644,10 +644,10 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) { const int NUM_INITIAL_PACKETS_DISCARD = 3; const int STANDARD_DEVIATION_SAMPLE_COUNT = 500; - _timeSinceLastRecieved.start(); _totalPacketsReceived++; - double timeDiff = (double)_timeSinceLastRecieved.nsecsElapsed() / 1000000.0; // ns to ms + double timeDiff = (double)_timeSinceLastReceived.nsecsElapsed() / 1000000.0; // ns to ms + _timeSinceLastReceived.start(); // Discard first few received packets for computing jitter (often they pile up on start) if (_totalPacketsReceived > NUM_INITIAL_PACKETS_DISCARD) { @@ -1265,7 +1265,7 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) // setup a procedural audio output device _proceduralAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); - _timeSinceLastRecieved.start(); + _timeSinceLastReceived.start(); // setup spatial audio ringbuffer int numFrameSamples = _outputFormat.sampleRate() * _desiredOutputFormat.channelCount(); diff --git a/interface/src/Audio.h b/interface/src/Audio.h index e1b8a7dddc..277c606d4b 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -129,7 +129,7 @@ private: QString _outputAudioDeviceName; StDev _stdev; - QElapsedTimer _timeSinceLastRecieved; + QElapsedTimer _timeSinceLastReceived; float _averagedLatency; float _measuredJitter; int16_t _jitterBufferSamples; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index a7012d838d..5006bec608 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -970,6 +971,17 @@ void Menu::goToUser(const QString& user) { connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision); } +/// Open a url, shortcutting any "hifi" scheme URLs to the local application. +void Menu::openUrl(const QUrl& url) { + if (url.scheme() == "hifi") { + QString path = url.toString(QUrl::RemoveScheme); + path = path.remove(QRegExp("^:?/*")); + goTo(path); + } else { + QDesktopServices::openUrl(url); + } +} + void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData) { QMessageBox msgBox; msgBox.setText("Both user and location exists with same name"); @@ -1145,23 +1157,22 @@ void Menu::showScriptEditor() { void Menu::showChat() { QMainWindow* mainWindow = Application::getInstance()->getWindow(); if (!_chatWindow) { - mainWindow->addDockWidget(Qt::RightDockWidgetArea, _chatWindow = new ChatWindow()); + _chatWindow = new ChatWindow(mainWindow); } - if (!_chatWindow->toggleViewAction()->isChecked()) { - const QRect& windowGeometry = mainWindow->geometry(); - _chatWindow->move(windowGeometry.topRight().x() - _chatWindow->width(), - windowGeometry.topRight().y() + (windowGeometry.height() / 2) - (_chatWindow->height() / 2)); - - _chatWindow->resize(0, _chatWindow->height()); - _chatWindow->toggleViewAction()->trigger(); + if (_chatWindow->isHidden()) { + _chatWindow->show(); } } void Menu::toggleChat() { #ifdef HAVE_QXMPP _chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected()); - if (!_chatAction->isEnabled() && _chatWindow && _chatWindow->toggleViewAction()->isChecked()) { - _chatWindow->toggleViewAction()->trigger(); + if (!_chatAction->isEnabled() && _chatWindow) { + if (_chatWindow->isHidden()) { + _chatWindow->show(); + } else { + _chatWindow->hide(); + } } #endif } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 09b5fabfc8..88de62a260 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -154,6 +154,7 @@ public slots: void goTo(); void goToUser(const QString& user); void pasteToVoxel(); + void openUrl(const QUrl& url); void toggleLoginMenuItem(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fb547da13e..140756e9d7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -1184,6 +1185,8 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) { coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; setPosition(newPosition); emit transformChanged(); + } else { + QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found."); } } diff --git a/interface/src/location/LocationManager.cpp b/interface/src/location/LocationManager.cpp index 8009551b6c..f80c331df4 100644 --- a/interface/src/location/LocationManager.cpp +++ b/interface/src/location/LocationManager.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "Application.h" #include "LocationManager.h" @@ -118,6 +120,8 @@ void LocationManager::checkForMultipleDestinations() { Application::getInstance()->getAvatar()->goToLocationFromResponse(_placeData); return; } + + QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found."); } } diff --git a/interface/src/ui/ChatInputArea.cpp b/interface/src/ui/ChatInputArea.cpp new file mode 100644 index 0000000000..3e8fc84fe2 --- /dev/null +++ b/interface/src/ui/ChatInputArea.cpp @@ -0,0 +1,19 @@ +// +// ChatInputArea.cpp +// interface/src/ui +// +// Created by Ryan Huffman on 4/24/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ChatInputArea.h" + +ChatInputArea::ChatInputArea(QWidget* parent) : QTextEdit(parent) { +}; + +void ChatInputArea::insertFromMimeData(const QMimeData* source) { + insertPlainText(source->text()); +}; diff --git a/interface/src/ui/ChatInputArea.h b/interface/src/ui/ChatInputArea.h new file mode 100644 index 0000000000..31d1584df7 --- /dev/null +++ b/interface/src/ui/ChatInputArea.h @@ -0,0 +1,27 @@ +// +// ChatInputArea.h +// interface/src/ui +// +// Created by Ryan Huffman on 4/11/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ChatInputArea_h +#define hifi_ChatInputArea_h + +#include +#include + +class ChatInputArea : public QTextEdit { + Q_OBJECT +public: + ChatInputArea(QWidget* parent); + +protected: + void insertFromMimeData(const QMimeData* source); +}; + +#endif // hifi_ChatInputArea_h diff --git a/interface/src/ui/ChatMessageArea.cpp b/interface/src/ui/ChatMessageArea.cpp index f15b788990..1e16a8a2db 100644 --- a/interface/src/ui/ChatMessageArea.cpp +++ b/interface/src/ui/ChatMessageArea.cpp @@ -9,13 +9,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Application.h" #include "ChatMessageArea.h" #include #include -ChatMessageArea::ChatMessageArea() : QTextBrowser() { +ChatMessageArea::ChatMessageArea(bool useFixedHeight) : QTextBrowser(), _useFixedHeight(useFixedHeight) { + setOpenLinks(false); + connect(document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, this, &ChatMessageArea::updateLayout); + connect(this, &QTextBrowser::anchorClicked, + Menu::getInstance(), &Menu::openUrl); } void ChatMessageArea::setHtml(const QString& html) { @@ -34,7 +39,15 @@ void ChatMessageArea::setHtml(const QString& html) { } void ChatMessageArea::updateLayout() { - setFixedHeight(document()->size().height()); + if (_useFixedHeight) { + setFixedHeight(document()->size().height()); + updateGeometry(); + emit sizeChanged(size()); + } +} + +void ChatMessageArea::setSize(const QSize& size) { + setFixedHeight(size.height()); updateGeometry(); } diff --git a/interface/src/ui/ChatMessageArea.h b/interface/src/ui/ChatMessageArea.h index 1c49c60b08..57199538fd 100644 --- a/interface/src/ui/ChatMessageArea.h +++ b/interface/src/ui/ChatMessageArea.h @@ -19,15 +19,19 @@ const int CHAT_MESSAGE_LINE_HEIGHT = 130; class ChatMessageArea : public QTextBrowser { Q_OBJECT public: - ChatMessageArea(); + ChatMessageArea(bool useFixedHeight = true); virtual void setHtml(const QString& html); public slots: void updateLayout(); + void setSize(const QSize& size); + +signals: + void sizeChanged(QSize newSize); protected: virtual void wheelEvent(QWheelEvent* event); - + bool _useFixedHeight; }; #endif // hifi_ChatMessageArea_h diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 635f1f3d10..e0802c6bc5 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -12,12 +12,10 @@ #include #include #include -#include #include #include #include #include -#include #include "Application.h" #include "FlowLayout.h" @@ -31,32 +29,40 @@ const int NUM_MESSAGES_TO_TIME_STAMP = 20; const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)"); +const QRegularExpression regexHifiLinks("([#@]\\S+)"); -ChatWindow::ChatWindow() : +ChatWindow::ChatWindow(QWidget* parent) : + FramelessDialog(parent, 0, POSITION_RIGHT), ui(new Ui::ChatWindow), numMessagesAfterLastTimeStamp(0), _mousePressed(false), _mouseStartPosition() { - ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, false); - // remove the title bar (see the Qt docs on setTitleBarWidget), but we keep it for undocking - // - titleBar = titleBarWidget(); - setTitleBarWidget(new QWidget()); + ui->setupUi(this); FlowLayout* flowLayout = new FlowLayout(0, 4, 4); ui->usersWidget->setLayout(flowLayout); - ui->messagesGridLayout->setColumnStretch(0, 1); - ui->messagesGridLayout->setColumnStretch(1, 3); - ui->messagePlainTextEdit->installEventFilter(this); + ui->messagePlainTextEdit->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + + QTextCursor cursor(ui->messagePlainTextEdit->textCursor()); + + cursor.movePosition(QTextCursor::Start); + + QTextBlockFormat format = cursor.blockFormat(); + format.setLineHeight(130, QTextBlockFormat::ProportionalHeight); + + cursor.setBlockFormat(format); + + ui->messagePlainTextEdit->setTextCursor(cursor); if (!AccountManager::getInstance().isLoggedIn()) { ui->connectingToXMPPLabel->setText(tr("You must be logged in to chat with others.")); } - + #ifdef HAVE_QXMPP const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); if (xmppClient.isConnected()) { @@ -89,36 +95,17 @@ ChatWindow::~ChatWindow() { delete ui; } -void ChatWindow::mousePressEvent(QMouseEvent *e) { - if (e->button() == Qt::LeftButton && isFloating()) { - _mousePressed = true; - _mouseStartPosition = e->pos(); - } -} - -void ChatWindow::mouseMoveEvent(QMouseEvent *e) { - if (_mousePressed) { - move(mapToParent(e->pos() - _mouseStartPosition)); - } -} - -void ChatWindow::mouseReleaseEvent( QMouseEvent *e ) { - if ( e->button() == Qt::LeftButton ) { - _mousePressed = false; - } -} - void ChatWindow::keyPressEvent(QKeyEvent* event) { - QDockWidget::keyPressEvent(event); if (event->key() == Qt::Key_Escape) { hide(); + } else { + FramelessDialog::keyPressEvent(event); } } void ChatWindow::showEvent(QShowEvent* event) { - QDockWidget::showEvent(event); + FramelessDialog::showEvent(event); if (!event->spontaneous()) { - activateWindow(); ui->messagePlainTextEdit->setFocus(); } } @@ -141,18 +128,20 @@ bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { message.setBody(messageText); XmppClient::getInstance().getXMPPClient().sendPacket(message); #endif - ui->messagePlainTextEdit->document()->clear(); + QTextCursor cursor = ui->messagePlainTextEdit->textCursor(); + cursor.select(QTextCursor::Document); + cursor.removeSelectedText(); } return true; } - } else { - if (event->type() != QEvent::MouseButtonRelease) { - return false; + } else if (event->type() == QEvent::MouseButtonRelease) { + QVariant userVar = sender->property("user"); + if (userVar.isValid()) { + Menu::getInstance()->goToUser("@" + userVar.toString()); + return true; } - QString user = sender->property("user").toString(); - Menu::getInstance()->goToUser(user); } - return false; + return FramelessDialog::eventFilter(sender, event); } #ifdef HAVE_QXMPP @@ -175,16 +164,17 @@ void ChatWindow::addTimeStamp() { timeString.chop(1); if (!timeString.isEmpty()) { QLabel* timeLabel = new QLabel(timeString); - timeLabel->setStyleSheet("color: palette(shadow);" - "background-color: palette(highlight);" + timeLabel->setStyleSheet("color: #333333;" + "background-color: white;" + "font-size: 14pt;" "padding: 4px;"); timeLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - timeLabel->setAlignment(Qt::AlignHCenter); + timeLabel->setAlignment(Qt::AlignLeft); - bool atBottom = isAtBottom(); + bool atBottom = isNearBottom(); - ui->messagesGridLayout->addWidget(timeLabel, ui->messagesGridLayout->rowCount(), 0, 1, 2); - ui->messagesGridLayout->parentWidget()->updateGeometry(); + ui->messagesVBoxLayout->addWidget(timeLabel); + ui->messagesVBoxLayout->parentWidget()->updateGeometry(); Application::processEvents(); numMessagesAfterLastTimeStamp = 0; @@ -249,6 +239,7 @@ void ChatWindow::participantsChanged() { "padding-bottom: 2px;" "padding-left: 2px;" "border: 1px solid palette(shadow);" + "font-size: 14pt;" "font-weight: bold"); userLabel->setProperty("user", participantName); userLabel->setCursor(Qt::PointingHandCursor); @@ -262,15 +253,11 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { return; } - QLabel* userLabel = new QLabel(getParticipantName(message.from())); - userLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - userLabel->setStyleSheet("padding: 2px; font-weight: bold"); - userLabel->setAlignment(Qt::AlignTop | Qt::AlignRight); + // Update background if this is a message from the current user + bool fromSelf = getParticipantName(message.from()) == AccountManager::getInstance().getUsername(); - ChatMessageArea* messageArea = new ChatMessageArea(); - - messageArea->setOpenLinks(true); - messageArea->setOpenExternalLinks(true); + // Create message area + ChatMessageArea* messageArea = new ChatMessageArea(true); messageArea->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); messageArea->setTextInteractionFlags(Qt::TextBrowserInteraction); messageArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -281,22 +268,30 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { "padding-left: 2px;" "padding-top: 2px;" "padding-right: 20px;" + "margin: 0px;" + "color: #333333;" + "font-size: 14pt;" "background-color: rgba(0, 0, 0, 0%);" "border: 0;"); - bool fromSelf = getParticipantName(message.from()) == AccountManager::getInstance().getUsername(); + QString userLabel = getParticipantName(message.from()); if (fromSelf) { - userLabel->setStyleSheet(userLabel->styleSheet() + "; background-color: #e1e8ea"); - messageArea->setStyleSheet(messageArea->styleSheet() + "; background-color: #e1e8ea"); + userLabel = "" + userLabel + ": "; + messageArea->setStyleSheet(messageArea->styleSheet() + "background-color: #e1e8ea"); + } else { + userLabel = "" + userLabel + ": "; } - messageArea->setHtml(message.body().replace(regexLinks, "\\1")); + messageArea->document()->setDefaultStyleSheet("a { text-decoration: none; font-weight: bold; color: #267077;}"); + QString messageText = message.body().toHtmlEscaped(); + messageText = messageText.replace(regexLinks, "\\1"); + messageText = messageText.replace(regexHifiLinks, "\\1"); + messageArea->setHtml(userLabel + messageText); - bool atBottom = isAtBottom(); - ui->messagesGridLayout->addWidget(userLabel, ui->messagesGridLayout->rowCount(), 0); - ui->messagesGridLayout->addWidget(messageArea, ui->messagesGridLayout->rowCount() - 1, 1); + bool atBottom = isNearBottom(); - ui->messagesGridLayout->parentWidget()->updateGeometry(); + ui->messagesVBoxLayout->addWidget(messageArea); + ui->messagesVBoxLayout->parentWidget()->updateGeometry(); Application::processEvents(); if (atBottom || fromSelf) { @@ -313,26 +308,13 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { #endif -bool ChatWindow::isAtBottom() { +bool ChatWindow::isNearBottom() { QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar(); - return verticalScrollBar->sliderPosition() == verticalScrollBar->maximum(); + return verticalScrollBar->value() >= verticalScrollBar->maximum() - Ui::AUTO_SCROLL_THRESHOLD; } // Scroll chat message area to bottom. void ChatWindow::scrollToBottom() { QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar(); - verticalScrollBar->setSliderPosition(verticalScrollBar->maximum()); -} - -void ChatWindow::togglePinned() { - QMainWindow* mainWindow = Application::getInstance()->getWindow(); - mainWindow->removeDockWidget(this); - if (ui->togglePinnedButton->isChecked()) { - mainWindow->addDockWidget(ui->togglePinnedButton->isChecked() ? Qt::RightDockWidgetArea : Qt::NoDockWidgetArea, this); - } - if (!this->toggleViewAction()->isChecked()) { - this->toggleViewAction()->trigger(); - } - this->setFloating(!ui->togglePinnedButton->isChecked()); - setTitleBarWidget(ui->togglePinnedButton->isChecked()?new QWidget():titleBar); + verticalScrollBar->setValue(verticalScrollBar->maximum()); } diff --git a/interface/src/ui/ChatWindow.h b/interface/src/ui/ChatWindow.h index 6a807f9b81..104fbe1746 100644 --- a/interface/src/ui/ChatWindow.h +++ b/interface/src/ui/ChatWindow.h @@ -17,6 +17,7 @@ #include #include +#include "FramelessDialog.h" #ifdef HAVE_QXMPP @@ -26,37 +27,38 @@ #endif namespace Ui { + + +// Maximum amount the chat can be scrolled up in order to auto scroll. +const int AUTO_SCROLL_THRESHOLD = 20; + + class ChatWindow; } -class ChatWindow : public QDockWidget { +class ChatWindow : public FramelessDialog { Q_OBJECT public: - ChatWindow(); + ChatWindow(QWidget* parent); ~ChatWindow(); - virtual void keyPressEvent(QKeyEvent *event); - virtual void showEvent(QShowEvent* event); - - virtual void mousePressEvent(QMouseEvent *e); - virtual void mouseMoveEvent(QMouseEvent *e); - virtual void mouseReleaseEvent(QMouseEvent *e); - protected: bool eventFilter(QObject* sender, QEvent* event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void showEvent(QShowEvent* event); + private: #ifdef HAVE_QXMPP QString getParticipantName(const QString& participant); #endif void startTimerForTimeStamps(); void addTimeStamp(); - bool isAtBottom(); + bool isNearBottom(); void scrollToBottom(); Ui::ChatWindow* ui; - QWidget* titleBar; int numMessagesAfterLastTimeStamp; QDateTime lastMessageStamp; bool _mousePressed; @@ -65,7 +67,6 @@ private: private slots: void connected(); void timeout(); - void togglePinned(); #ifdef HAVE_QXMPP void error(QXmppClient::Error error); void participantsChanged(); diff --git a/interface/src/ui/FramelessDialog.cpp b/interface/src/ui/FramelessDialog.cpp index 18e3bca89a..4919e99db6 100644 --- a/interface/src/ui/FramelessDialog.cpp +++ b/interface/src/ui/FramelessDialog.cpp @@ -14,8 +14,13 @@ const int RESIZE_HANDLE_WIDTH = 7; -FramelessDialog::FramelessDialog(QWidget *parent, Qt::WindowFlags flags) : -QDialog(parent, flags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) { +FramelessDialog::FramelessDialog(QWidget *parent, Qt::WindowFlags flags, Position position) : + QDialog(parent, flags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint), + _isResizing(false), + _resizeInitialWidth(0), + _selfHidden(false), + _position(position) { + setAttribute(Qt::WA_DeleteOnClose); // handle rezize and move events @@ -29,29 +34,37 @@ bool FramelessDialog::eventFilter(QObject* sender, QEvent* event) { switch (event->type()) { case QEvent::Move: if (sender == parentWidget()) { - // move to upper left corner on app move - move(parentWidget()->geometry().topLeft()); + resizeAndPosition(false); } break; case QEvent::Resize: if (sender == parentWidget()) { - // keep full app height on resizing the app - setFixedHeight(parentWidget()->size().height()); + resizeAndPosition(false); } break; case QEvent::WindowStateChange: if (parentWidget()->isMinimized()) { - setHidden(true); - } else { + if (isVisible()) { + _selfHidden = true; + setHidden(true); + } + } else if (_selfHidden) { + _selfHidden = false; setHidden(false); } break; case QEvent::ApplicationDeactivate: // hide on minimize and focus lost - setHidden(true); + if (isVisible()) { + _selfHidden = true; + setHidden(true); + } break; case QEvent::ApplicationActivate: - setHidden(false); + if (_selfHidden) { + _selfHidden = false; + setHidden(false); + } break; default: break; @@ -70,21 +83,38 @@ void FramelessDialog::setStyleSheetFile(const QString& fileName) { } void FramelessDialog::showEvent(QShowEvent* event) { - // move to upper left corner - move(parentWidget()->geometry().topLeft()); + resizeAndPosition(); +} +void FramelessDialog::resizeAndPosition(bool resizeParent) { // keep full app height setFixedHeight(parentWidget()->size().height()); // resize parrent if width is smaller than this dialog - if (parentWidget()->size().width() < size().width()) { + if (resizeParent && parentWidget()->size().width() < size().width()) { parentWidget()->resize(size().width(), parentWidget()->size().height()); } + + if (_position == POSITION_LEFT) { + // move to upper left corner + move(parentWidget()->geometry().topLeft()); + } else if (_position == POSITION_RIGHT) { + // move to upper right corner + QPoint pos = parentWidget()->geometry().topRight(); + pos.setX(pos.x() - size().width()); + move(pos); + } } + void FramelessDialog::mousePressEvent(QMouseEvent* mouseEvent) { - if (abs(mouseEvent->pos().x() - size().width()) < RESIZE_HANDLE_WIDTH && mouseEvent->button() == Qt::LeftButton) { - _isResizing = true; - QApplication::setOverrideCursor(Qt::SizeHorCursor); + if (mouseEvent->button() == Qt::LeftButton) { + bool hitLeft = _position == POSITION_LEFT && abs(mouseEvent->pos().x() - size().width()) < RESIZE_HANDLE_WIDTH; + bool hitRight = _position == POSITION_RIGHT && mouseEvent->pos().x() < RESIZE_HANDLE_WIDTH; + if (hitLeft || hitRight) { + _isResizing = true; + _resizeInitialWidth = size().width(); + QApplication::setOverrideCursor(Qt::SizeHorCursor); + } } } @@ -95,6 +125,14 @@ void FramelessDialog::mouseReleaseEvent(QMouseEvent* mouseEvent) { void FramelessDialog::mouseMoveEvent(QMouseEvent* mouseEvent) { if (_isResizing) { - resize(mouseEvent->pos().x(), size().height()); + if (_position == POSITION_LEFT) { + resize(mouseEvent->pos().x(), size().height()); + } else if (_position == POSITION_RIGHT) { + setUpdatesEnabled(false); + resize(_resizeInitialWidth - mouseEvent->pos().x(), size().height()); + resizeAndPosition(); + _resizeInitialWidth = size().width(); + setUpdatesEnabled(true); + } } } diff --git a/interface/src/ui/FramelessDialog.h b/interface/src/ui/FramelessDialog.h index db9f6dfd6c..828602a5db 100644 --- a/interface/src/ui/FramelessDialog.h +++ b/interface/src/ui/FramelessDialog.h @@ -19,7 +19,9 @@ class FramelessDialog : public QDialog { Q_OBJECT public: - FramelessDialog(QWidget* parent = 0, Qt::WindowFlags flags = 0); + enum Position { POSITION_LEFT, POSITION_RIGHT }; + + FramelessDialog(QWidget* parent = 0, Qt::WindowFlags flags = 0, Position position = POSITION_LEFT); void setStyleSheetFile(const QString& fileName); protected: @@ -31,7 +33,12 @@ protected: bool eventFilter(QObject* sender, QEvent* event); private: + void resizeAndPosition(bool resizeParent = true); + bool _isResizing; + int _resizeInitialWidth; + bool _selfHidden; ///< true when the dialog itself because of a window event (deactivation or minimization) + Position _position; }; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 36508e94d1..7a70b743bd 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -19,7 +19,7 @@ const int SCROLL_PANEL_BOTTOM_MARGIN = 30; const int OK_BUTTON_RIGHT_MARGIN = 30; const int BUTTONS_TOP_MARGIN = 24; -PreferencesDialog::PreferencesDialog(QWidget* parent, Qt::WindowFlags flags) : FramelessDialog(parent, flags) { +PreferencesDialog::PreferencesDialog(QWidget* parent, Qt::WindowFlags flags) : FramelessDialog(parent, flags, POSITION_LEFT) { ui.setupUi(this); setStyleSheetFile("styles/preferences.qss"); @@ -38,26 +38,34 @@ void PreferencesDialog::accept() { void PreferencesDialog::setHeadUrl(QString modelUrl) { ui.faceURLEdit->setText(modelUrl); - setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); } void PreferencesDialog::setSkeletonUrl(QString modelUrl) { ui.skeletonURLEdit->setText(modelUrl); - setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); } void PreferencesDialog::openHeadModelBrowser() { setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint); + show(); + ModelsBrowser modelBrowser(Head); connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setHeadUrl); modelBrowser.browse(); + + setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); + show(); } void PreferencesDialog::openBodyModelBrowser() { setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint); + show(); + ModelsBrowser modelBrowser(Skeleton); connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setSkeletonUrl); modelBrowser.browse(); + + setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); + show(); } void PreferencesDialog::openSnapshotLocationBrowser() { @@ -176,6 +184,7 @@ void PreferencesDialog::savePreferences() { Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value()); Menu::getInstance()->setAudioJitterBufferSamples(ui.audioJitterSpin->value()); + Application::getInstance()->getAudio()->setJitterBufferSamples(ui.audioJitterSpin->value()); Application::getInstance()->resizeGL(Application::getInstance()->getGLWidget()->width(), Application::getInstance()->getGLWidget()->height()); diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index 0372e00c09..4d223b2665 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -1,13 +1,13 @@ ChatWindow - + 0 0 400 - 608 + 440 @@ -16,127 +16,95 @@ 238 - - font-family: Helvetica, Arial, sans-serif; - - - QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable - - - 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 - - - - - - - ../resources/images/pin.svg - ../resources/images/pinned.svg../resources/images/pin.svg - - - true - - - true - - - false - - - true - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - Qt::NoFocus - - - QPushButton { + + font-family: Helvetica, Arial, sans-serif; + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 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 + + + QPushButton { background-color: rgba( 0, 0, 0, 0% ); border: none; image: url(../resources/images/close.svg) @@ -148,50 +116,104 @@ QPushButton:pressed { border: none; image: url(../resources/images/close_down.svg) } - - - - - - true - - - - - - - - - - - - margin-top: 12px; - - - Qt::ScrollBarAlwaysOff - - - true - - - - - 0 - 0 - 382 - 16 - + + + + + + true + + + + + + + + + #usersWidget { + margin-right: 20px; +} + + + + + + margin-top: 12px; + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 382 + 16 + + + + + 0 + 0 + + + + margin-top: 0px; + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + - + 0 0 - - margin-top: 0px; + + + 0 + 78 + - + + #chatFrame { +border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px; +} + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + 0 @@ -204,69 +226,65 @@ QPushButton:pressed { 0 - - 0 - + + + + + 0 + 0 + + + + + 0 + 60 + + + + + Helvetica,Arial,sans-serif + 14 + + + + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + true + + + false + + + - - - - - - - 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 - - - - - + + + + + + + ChatInputArea + QTextEdit +
ui/ChatInputArea.h
+
+
- messagePlainTextEdit messagesScrollArea - - togglePinnedButton - clicked() - ChatWindow - togglePinned() - - - 390 - 42 - - - 550 - 42 - - - closeButton clicked()