diff --git a/qtox.pro b/qtox.pro index 150ded145..de081f7b0 100644 --- a/qtox.pro +++ b/qtox.pro @@ -358,7 +358,6 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) { src/widget/form/loadhistorydialog.h \ src/widget/form/setpassworddialog.h \ src/widget/form/tabcompleter.h \ - src/widget/flowlayout.h \ src/ipc.h \ src/net/autoupdate.h \ src/widget/tool/callconfirmwidget.h \ @@ -384,8 +383,8 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) { src/widget/form/loadhistorydialog.cpp \ src/widget/form/setpassworddialog.cpp \ src/widget/form/tabcompleter.cpp \ - src/widget/flowlayout.cpp \ src/ipc.cpp \ + src/widget/flowlayout.cpp \ src/net/autoupdate.cpp \ src/widget/tool/callconfirmwidget.cpp \ src/widget/systemtrayicon.cpp \ @@ -498,7 +497,10 @@ SOURCES += \ src/widget/tool/removefrienddialog.cpp \ src/widget/contentlayout.cpp \ src/widget/contentdialog.cpp \ - src/widget/tool/activatedialog.cpp + src/widget/tool/activatedialog.cpp \ + src/widget/tool/movablewidget.cpp \ + src/video/genericnetcamview.cpp \ + src/video/groupnetcamview.cpp HEADERS += \ src/audio/audio.h \ @@ -545,4 +547,7 @@ HEADERS += \ src/widget/contentlayout.h \ src/widget/contentdialog.h \ src/widget/tool/activatedialog.h \ - src/widget/tool/removefrienddialog.h + src/widget/tool/removefrienddialog.h \ + src/widget/tool/movablewidget.h \ + src/video/genericnetcamview.h \ + src/video/groupnetcamview.h diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 7801c89c3..d1b7dffa2 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -267,7 +267,7 @@ void Audio::playMono16Sound(const QByteArray& data) alSourcePlay(alMainSource); alDeleteBuffers(1, &buffer); } - +#include void Audio::playGroupAudioQueued(Tox*,int group, int peer, const int16_t* data, unsigned samples, uint8_t channels, unsigned sample_rate, void* core) { @@ -295,6 +295,13 @@ void Audio::playGroupAudio(int group, int peer, const int16_t* data, alSourcef(call.alSources[peer], AL_GAIN, outputVolume); } + size_t volume = 0;//data[1]; + int bufsize = samples * 2 * channels; + for (int i = 0; i < bufsize; ++i) + volume += abs(data[i]);//std::max(volume, data[i]); + + emit groupAudioPlayed(group, peer, volume / static_cast(bufsize)); + playAudioBuffer(call.alSources[peer], data, samples, channels, sample_rate); } diff --git a/src/audio/audio.h b/src/audio/audio.h index 68f1b7c2e..a81791c73 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -41,7 +41,7 @@ class QMutex; struct Tox; class AudioFilterer; -class Audio : QObject +class Audio : public QObject { Q_OBJECT @@ -81,6 +81,9 @@ public slots: void playGroupAudio(int group, int peer, const int16_t* data, unsigned samples, uint8_t channels, unsigned sample_rate); +signals: + void groupAudioPlayed(int group, int peer, unsigned short volume); + private: explicit Audio()=default; ~Audio(); diff --git a/src/group.cpp b/src/group.cpp index b8a9e5321..cd7a2928a 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -75,12 +75,17 @@ void Group::updatePeer(int peerId, QString name) QString toxid = id.publicKey; peers[peerId] = name; toxids[toxid] = name; + Friend *f = FriendList::findFriend(id); if (f && f->hasAlias()) + + // I don't know what this is doing here but it changes back to the old name. + /*Friend *f = FriendList::findFriend(id); + if (f) { peers[peerId] = f->getDisplayedName(); toxids[toxid] = f->getDisplayedName(); - } + }*/ widget->onUserListChanged(); chatForm->onUserListChanged(); diff --git a/src/main.cpp b/src/main.cpp index b564c5425..6b5d9da47 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -268,8 +268,8 @@ int main(int argc, char *argv[]) logFile = nullptr; #endif - CameraSource::destroyInstance(); Nexus::destroyInstance(); + CameraSource::destroyInstance(); Settings::destroyInstance(); qDebug() << "Clean exit with status"< + + + true + + + + 0 + 0 + 775 + 26 + + + + Close diff --git a/src/persistence/settings.cpp b/src/persistence/settings.cpp index be4d7c41f..403592bd2 100644 --- a/src/persistence/settings.cpp +++ b/src/persistence/settings.cpp @@ -232,6 +232,7 @@ void Settings::loadGlobal() s.beginGroup("Video"); videoDev = s.value("videoDev", "").toString(); camVideoRes = s.value("camVideoRes",QSize()).toSize(); + camVideoFPS = s.value("camVideoFPS", 0).toUInt(); s.endGroup(); // Read the embedded DHT bootsrap nodes list if needed @@ -431,6 +432,7 @@ void Settings::saveGlobal() s.beginGroup("Video"); s.setValue("videoDev", videoDev); s.setValue("camVideoRes",camVideoRes); + s.setValue("camVideoFPS",camVideoFPS); s.endGroup(); } @@ -1220,6 +1222,19 @@ void Settings::setCamVideoRes(QSize newValue) camVideoRes = newValue; } +unsigned short Settings::getCamVideoFPS() const +{ + QMutexLocker locker{&bigLock}; + return camVideoFPS; +} + +void Settings::setCamVideoFPS(unsigned short newValue) +{ + QMutexLocker locker{&bigLock}; + camVideoFPS = newValue; +} + + QString Settings::getFriendAdress(const QString &publicKey) const { QMutexLocker locker{&bigLock}; diff --git a/src/persistence/settings.h b/src/persistence/settings.h index 16be7f728..3d02b5f1a 100644 --- a/src/persistence/settings.h +++ b/src/persistence/settings.h @@ -163,6 +163,9 @@ public: QSize getCamVideoRes() const; void setCamVideoRes(QSize newValue); + unsigned short getCamVideoFPS() const; + void setCamVideoFPS(unsigned short newValue); + bool isAnimationEnabled() const; void setAnimationEnabled(bool newValue); @@ -376,6 +379,7 @@ private: // Video QString videoDev; QSize camVideoRes; + unsigned short camVideoFPS; struct friendProp { diff --git a/src/video/camerasource.cpp b/src/video/camerasource.cpp index 3558124f8..fde26ccd4 100644 --- a/src/video/camerasource.cpp +++ b/src/video/camerasource.cpp @@ -286,6 +286,8 @@ bool CameraSource::openDevice() while (!streamFuture.isRunning()) QThread::yieldCurrentThread(); + emit deviceOpened(); + return true; } diff --git a/src/video/camerasource.h b/src/video/camerasource.h index 234a67a7e..a87b98489 100644 --- a/src/video/camerasource.h +++ b/src/video/camerasource.h @@ -59,6 +59,9 @@ public: virtual bool subscribe() override; virtual void unsubscribe() override; +signals: + void deviceOpened(); + private: CameraSource(); ~CameraSource(); diff --git a/src/video/genericnetcamview.cpp b/src/video/genericnetcamview.cpp new file mode 100644 index 000000000..5c1632d58 --- /dev/null +++ b/src/video/genericnetcamview.cpp @@ -0,0 +1,69 @@ +/* + Copyright © 2015 by The qTox Project + + This file is part of qTox, a Qt-based graphical interface for Tox. + + qTox is libre software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + qTox is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with qTox. If not, see . +*/ + +#include "genericnetcamview.h" +#include +#include +#include + +GenericNetCamView::GenericNetCamView(QWidget *parent) + : QWidget(parent) +{ + verLayout = new QVBoxLayout(this); + setWindowTitle(tr("Tox video")); + + int spacing = verLayout->spacing(); + verLayout->setSpacing(0); + + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->addStretch(); + button = new QPushButton(); + buttonLayout->addWidget(button); + connect(button, &QPushButton::clicked, this, &GenericNetCamView::showMessageClicked); + + verLayout->addSpacing(spacing); + verLayout->addLayout(buttonLayout); + verLayout->addSpacing(spacing); + + QFrame* lineFrame = new QFrame(this); + lineFrame->setStyleSheet("border: 1px solid #c1c1c1;"); + lineFrame->setFrameShape(QFrame::HLine); + lineFrame->setMaximumHeight(1); + verLayout->addWidget(lineFrame); + + setShowMessages(false); + + setStyleSheet("NetCamView { background-color: #c1c1c1; }"); +} + +void GenericNetCamView::setShowMessages(bool show, bool notify) +{ + if (show) + { + button->setText(tr("Show Messages")); + + if (notify) + button->setIcon(QIcon("://ui/chatArea/info.svg")); + } + else + { + button->setText(tr("Hide Messages")); + button->setIcon(QIcon()); + } +} diff --git a/src/video/genericnetcamview.h b/src/video/genericnetcamview.h new file mode 100644 index 000000000..05d3bc6bd --- /dev/null +++ b/src/video/genericnetcamview.h @@ -0,0 +1,49 @@ +/* + Copyright © 2015 by The qTox Project + + This file is part of qTox, a Qt-based graphical interface for Tox. + + qTox is libre software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + qTox is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with qTox. If not, see . +*/ + +#ifndef GENERICNETCAMVIEW_H +#define GENERICNETCAMVIEW_H + +#include + +class VideoSurface; +class QPushButton; +class QVBoxLayout; + +class GenericNetCamView : public QWidget +{ + Q_OBJECT +public: + GenericNetCamView(QWidget* parent); + +signals: + void showMessageClicked(); + +public slots: + void setShowMessages(bool show, bool notify = false); + +protected: + QVBoxLayout* verLayout; + VideoSurface* videoSurface; + +private: + QPushButton* button; +}; + +#endif // GENERICNETCAMVIEW_H diff --git a/src/video/groupnetcamview.cpp b/src/video/groupnetcamview.cpp new file mode 100644 index 000000000..3176f45a8 --- /dev/null +++ b/src/video/groupnetcamview.cpp @@ -0,0 +1,291 @@ +/* + Copyright © 2015 by The qTox Project + + This file is part of qTox, a Qt-based graphical interface for Tox. + + qTox is libre software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + qTox is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with qTox. If not, see . +*/ + +#include "groupnetcamview.h" +#include "src/widget/tool/croppinglabel.h" +#include "src/video/videosurface.h" +#include "src/persistence/settings.h" +#include "src/audio/audio.h" +#include "src/core/core.h" +#include "src/friendlist.h" +#include "src/friend.h" +#include +#include +#include +#include +#include + +#include +class LabeledVideo : public QFrame +{ +public: + LabeledVideo(const QPixmap& avatar, QWidget* parent = 0, bool expanding = true) + : QFrame(parent) + { + //setFrameStyle(QFrame::Box); + qDebug() << "Created expanding? " << expanding; + videoSurface = new VideoSurface(avatar, 0, expanding); + videoSurface->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + videoSurface->setMinimumHeight(32); + //videoSurface->setMaximumHeight(96); + connect(videoSurface, &VideoSurface::ratioChanged, this, &LabeledVideo::updateSize); + label = new CroppingLabel(this); + label->setTextFormat(Qt::PlainText); + label->setStyleSheet("color: white"); + //label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + //qDebug() << label->sizePolicy(); + label->setAlignment(Qt::AlignCenter); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addWidget(videoSurface, 1); + layout->addWidget(label); + } + + ~LabeledVideo() + { + + } + + VideoSurface* getVideoSurface() const + { + return videoSurface; + } + + void setText(const QString& text) + { + label->setText(text); + } + + QString getText() const + { + return label->text(); + } + + void setActive(bool active = true) + { + if (active) + setStyleSheet("QFrame { background-color: #414141; border-radius: 10px; }"); + else + setStyleSheet(QString()); + } + +protected: + void resizeEvent(QResizeEvent* event) final override + { + qDebug() << "Resize!"; + updateSize(); + QWidget::resizeEvent(event); + } + +private slots: + void updateSize() + { + qDebug() << videoSurface->isExpanding(); + if (videoSurface->isExpanding()) + { + int width = videoSurface->height() * videoSurface->getRatio(); + videoSurface->setMinimumWidth(width); + videoSurface->setMaximumWidth(width); + //setMaximumWidth(width + layout()->margin() * 2); + qDebug() << videoSurface->minimumWidth(); + } + } + +private: + CroppingLabel* label; + VideoSurface* videoSurface; + bool selected = false; +}; + +GroupNetCamView::GroupNetCamView(int group, QWidget *parent) + : GenericNetCamView(parent) + , group(group) +{ + videoLabelSurface = new LabeledVideo(QPixmap(), this, false); + videoSurface = videoLabelSurface->getVideoSurface(); + //videoSurface->setExpanding(false); + videoSurface->setMinimumHeight(256); + videoSurface->setContentsMargins(6, 6, 6, 0); + videoLabelSurface->setContentsMargins(0, 0, 0, 0); + videoLabelSurface->layout()->setMargin(0); + videoLabelSurface->setStyleSheet("QFrame { background-color: black; }"); + + //verLayout->insertWidget(0, videoLabelSurface, 1); + + QSplitter* splitter = new QSplitter(Qt::Vertical, this); + splitter->setChildrenCollapsible(false); + verLayout->insertWidget(0, splitter, 1); + splitter->addWidget(videoLabelSurface); + splitter->setStyleSheet("QSplitter { background-color: black; } QSplitter::handle { background-color: black; }"); + + QScrollArea* scrollArea = new QScrollArea(); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setFrameStyle(QFrame::NoFrame); + QWidget* widget = new QWidget(nullptr); + scrollArea->setWidgetResizable(true); + horLayout = new QHBoxLayout(widget); + //FlowLayout* horLayout = new FlowLayout(widget); + horLayout->addStretch(1); + + selfVideoSurface = new LabeledVideo(Settings::getInstance().getSavedAvatar(Core::getInstance()->getSelfId().toString()), this); + horLayout->addWidget(selfVideoSurface); + //horLayout->setAlignment(selfVideoSurface, Qt::AlignCenter | Qt::AlignHCenter); + + horLayout->addStretch(1); + splitter->addWidget(scrollArea); + scrollArea->setWidget(widget); + //verLayout->insertWidget(1, scrollArea); + //scrollArea->setMinimumHeight(selfVideoSurface->height()); + + connect(&Audio::getInstance(), &Audio::groupAudioPlayed, this, &GroupNetCamView::groupAudioPlayed); + + QTimer* timer = new QTimer(this); + timer->setInterval(1000); + connect(timer, &QTimer::timeout, this, &GroupNetCamView::findActivePeer); + timer->start(); + + connect(Core::getInstance(), &Core::selfAvatarChanged, [this](const QPixmap& pixmap) + { + selfVideoSurface->getVideoSurface()->setAvatar(pixmap); + findActivePeer(); + }); + connect(Core::getInstance(), &Core::usernameSet, [this](const QString& username) + { + selfVideoSurface->setText(username); + findActivePeer(); + }); + connect(Core::getInstance(), &Core::friendAvatarChanged, this, &GroupNetCamView::friendAvatarChanged); + + selfVideoSurface->setText(Core::getInstance()->getUsername()); +} + +void GroupNetCamView::clearPeers() +{ + QList keys = videoList.keys(); + + for (int &i : keys) + removePeer(i); +} + +void GroupNetCamView::addPeer(int peer, const QString& name) +{ + QPixmap groupAvatar = Settings::getInstance().getSavedAvatar(Core::getInstance()->getGroupPeerToxId(group, peer).toString()); + LabeledVideo* labeledVideo = new LabeledVideo(groupAvatar, this); + labeledVideo->setText(name); + horLayout->insertWidget(horLayout->count() - 1, labeledVideo); + PeerVideo peerVideo; + peerVideo.video = labeledVideo; + videoList.insert(peer, peerVideo); + + findActivePeer(); +} + +void GroupNetCamView::removePeer(int peer) +{ + auto peerVideo = videoList.find(peer); + + if (peerVideo != videoList.end()) + { + LabeledVideo* labeledVideo = peerVideo.value().video; + horLayout->removeWidget(labeledVideo); + labeledVideo->deleteLater(); + videoList.remove(peer); + + findActivePeer(); + } +} + +void GroupNetCamView::setActive(int peer) +{ + if (peer == -1) + { + videoLabelSurface->setText(selfVideoSurface->getText()); + activePeer = -1; + return; + } + + auto peerVideo = videoList.find(peer); + + if (peerVideo != videoList.end()) + { + // When group video exists: + // videoSurface->setSource(peerVideo.value()->getVideoSurface()->source); + + auto lastVideo = videoList.find(activePeer); + + if (lastVideo != videoList.end()) + lastVideo.value().video->setActive(false); + + LabeledVideo *labeledVideo = peerVideo.value().video; + videoLabelSurface->setText(labeledVideo->getText()); + videoLabelSurface->getVideoSurface()->setAvatar(labeledVideo->getVideoSurface()->getAvatar()); + labeledVideo->setActive(); + + activePeer = peer; + } +} + +void GroupNetCamView::groupAudioPlayed(int Group, int peer, unsigned short volume) +{ + if (group != Group) + return; + + auto peerVideo = videoList.find(peer); + + if (peerVideo != videoList.end()) + peerVideo.value().volume = volume; +} + +void GroupNetCamView::findActivePeer() +{ + int candidate = -1; + int maximum = 0; + + for (auto peer = videoList.begin(); peer != videoList.end(); ++peer) + { + if (peer.value().volume > maximum) + { + maximum = peer.value().volume; + candidate = peer.key(); + } + } + + setActive(candidate); +} + +void GroupNetCamView::friendAvatarChanged(int FriendId, const QPixmap &pixmap) +{ + Friend* f = FriendList::findFriend(FriendId); + + for (int i = 0; i < Core::getInstance()->getGroupNumberPeers(group); ++i) + { + if (Core::getInstance()->getGroupPeerToxId(group, i) == f->getToxId()) + { + auto peerVideo = videoList.find(i); + + if (peerVideo != videoList.end()) + { + peerVideo.value().video->getVideoSurface()->setAvatar(pixmap); + findActivePeer(); + } + + break; + } + } +} diff --git a/src/video/groupnetcamview.h b/src/video/groupnetcamview.h new file mode 100644 index 000000000..aab80f34c --- /dev/null +++ b/src/video/groupnetcamview.h @@ -0,0 +1,61 @@ +/* + Copyright © 2015 by The qTox Project + + This file is part of qTox, a Qt-based graphical interface for Tox. + + qTox is libre software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + qTox is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with qTox. If not, see . +*/ + +#ifndef GROUPNETCAMVIEW_H +#define GROUPNETCAMVIEW_H + +#include "genericnetcamview.h" +#include + +class LabeledVideo; +class QHBoxLayout; + +class GroupNetCamView : public GenericNetCamView +{ +public: + GroupNetCamView(int group, QWidget* parent = 0); + void clearPeers(); + void addPeer(int peer, const QString &name); + void removePeer(int peer); + +public slots: + void groupAudioPlayed(int group, int peer, unsigned short volume); + +private slots: + void findActivePeer(); + void friendAvatarChanged(int FriendId, const QPixmap& pixmap); + +private: + struct PeerVideo + { + LabeledVideo* video; + unsigned short volume = 0; + }; + + void setActive(int peer); + + QHBoxLayout* horLayout; + QMap videoList; + LabeledVideo* videoLabelSurface; + LabeledVideo* selfVideoSurface; + int activePeer; + int group; +}; + +#endif // GROUPNETCAMVIEW_H diff --git a/src/video/netcamview.cpp b/src/video/netcamview.cpp index eb0cc54e7..231ec1b3c 100644 --- a/src/video/netcamview.cpp +++ b/src/video/netcamview.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2014 by The qTox Project + Copyright © 2014-2015 by The qTox Project This file is part of qTox, a Qt-based graphical interface for Tox. @@ -18,35 +18,97 @@ */ #include "netcamview.h" +#include "camerasource.h" +#include "src/friend.h" +#include "src/friendlist.h" #include "src/core/core.h" #include "src/video/videosurface.h" +#include "src/widget/tool/movablewidget.h" +#include "src/persistence/settings.h" #include -#include +#include +#include -NetCamView::NetCamView(QWidget* parent) - : QWidget(parent) - , mainLayout(new QHBoxLayout()) +NetCamView::NetCamView(int friendId, QWidget* parent) + : GenericNetCamView(parent) + , selfFrame{nullptr} + , friendId{friendId} { - setLayout(mainLayout); - setWindowTitle(tr("Tox video")); - setMinimumSize(320,240); + QString id = FriendList::findFriend(friendId)->getToxId().toString(); + videoSurface = new VideoSurface(Settings::getInstance().getSavedAvatar(id), this); + videoSurface->setMinimumHeight(256); + videoSurface->setContentsMargins(6, 6, 6, 6); - videoSurface = new VideoSurface(this); + verLayout->insertWidget(0, videoSurface, 1); - mainLayout->addWidget(videoSurface); + selfVideoSurface = new VideoSurface(Settings::getInstance().getSavedAvatar(Core::getInstance()->getSelfId().toString()), this, true); + selfVideoSurface->setObjectName(QStringLiteral("CamVideoSurface")); + selfVideoSurface->setMouseTracking(true); + selfVideoSurface->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + selfFrame = new MovableWidget(videoSurface); + selfFrame->show(); + + QHBoxLayout* frameLayout = new QHBoxLayout(selfFrame); + frameLayout->addWidget(selfVideoSurface); + frameLayout->setMargin(0); + + updateRatio(); + connect(selfVideoSurface, &VideoSurface::ratioChanged, this, &NetCamView::updateRatio); + + connect(videoSurface, &VideoSurface::boundaryChanged, [this]() + { + QRect boundingRect = videoSurface->getBoundingRect(); + updateFrameSize(boundingRect.size()); + selfFrame->setBoundary(boundingRect); + }); + + connect(videoSurface, &VideoSurface::ratioChanged, [this]() + { + selfFrame->setMinimumWidth(selfFrame->minimumHeight() * selfVideoSurface->getRatio()); + QRect boundingRect = videoSurface->getBoundingRect(); + updateFrameSize(boundingRect.size()); + selfFrame->resetBoundary(boundingRect); + }); + + connect(Core::getInstance(), &Core::selfAvatarChanged, [this](const QPixmap& pixmap) + { + selfVideoSurface->setAvatar(pixmap); + }); + + connect(Core::getInstance(), &Core::friendAvatarChanged, [this](int FriendId, const QPixmap& pixmap) + { + if (this->friendId == FriendId) + videoSurface->setAvatar(pixmap); + }); + + VideoMode videoMode; + QSize videoSize = Settings::getInstance().getCamVideoRes(); + videoMode.width = videoSize.width(); + videoMode.height = videoSize.height(); + qDebug() << "SIZER" << videoSize; + videoMode.FPS = Settings::getInstance().getCamVideoFPS(); + CameraSource::getInstance().open(Settings::getInstance().getVideoDev(), videoMode); } void NetCamView::show(VideoSource *source, const QString &title) { setSource(source); - setTitle(title); + selfVideoSurface->setSource(&CameraSource::getInstance()); + setTitle(title); QWidget::show(); } void NetCamView::hide() { setSource(nullptr); + selfVideoSurface->setSource(nullptr); + + if (selfFrame) + selfFrame->deleteLater(); + + selfFrame = nullptr; QWidget::hide(); } @@ -60,3 +122,24 @@ void NetCamView::setTitle(const QString &title) { setWindowTitle(title); } + +void NetCamView::showEvent(QShowEvent *event) +{ + selfFrame->resetBoundary(videoSurface->getBoundingRect()); +} + +void NetCamView::updateRatio() +{ + selfFrame->setMinimumWidth(selfFrame->minimumHeight() * selfVideoSurface->getRatio()); + selfFrame->setRatio(selfVideoSurface->getRatio()); +} + +void NetCamView::updateFrameSize(QSize size) +{ + selfFrame->setMaximumSize(size.height() / 3, size.width() / 3); + + if (selfFrame->maximumWidth() > selfFrame->maximumHeight()) + selfFrame->setMaximumWidth(selfFrame->maximumHeight() * selfVideoSurface->getRatio()); + else + selfFrame->setMaximumHeight(selfFrame->maximumWidth() / selfVideoSurface->getRatio()); +} diff --git a/src/video/netcamview.h b/src/video/netcamview.h index a208c10a6..2aa1309c7 100644 --- a/src/video/netcamview.h +++ b/src/video/netcamview.h @@ -1,5 +1,5 @@ /* - Copyright © 2014 by The qTox Project + Copyright © 2014-2015 by The qTox Project This file is part of qTox, a Qt-based graphical interface for Tox. @@ -20,19 +20,20 @@ #ifndef NETCAMVIEW_H #define NETCAMVIEW_H -#include +#include "genericnetcamview.h" class QHBoxLayout; struct vpx_image; -class VideoSurface; class VideoSource; +class QFrame; +class MovableWidget; -class NetCamView : public QWidget +class NetCamView : public GenericNetCamView { Q_OBJECT public: - NetCamView(QWidget *parent=0); + NetCamView(int friendId, QWidget *parent=0); virtual void show(VideoSource* source, const QString& title); virtual void hide(); @@ -40,9 +41,19 @@ public: void setSource(VideoSource* s); void setTitle(const QString& title); +protected: + void showEvent(QShowEvent* event) final override; + +private slots: + void updateRatio(); + private: - QHBoxLayout* mainLayout; - VideoSurface* videoSurface; + void updateFrameSize(QSize size); + + VideoSurface* selfVideoSurface; + MovableWidget* selfFrame; + int friendId; + bool e = false; }; #endif // NETCAMVIEW_H diff --git a/src/video/videosurface.cpp b/src/video/videosurface.cpp index 290539ef3..05d70ffbb 100644 --- a/src/video/videosurface.cpp +++ b/src/video/videosurface.cpp @@ -6,33 +6,50 @@ qTox is libre software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + (at your option) any later version. qTox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU General Public License along with qTox. If not, see . */ #include "videosurface.h" #include "src/video/videoframe.h" +#include "src/friend.h" +#include "src/friendlist.h" +#include "src/widget/friendwidget.h" +#include "src/persistence/settings.h" +#include "src/core/core.h" +#include "src/widget/style.h" + #include #include +#include +#include -VideoSurface::VideoSurface(QWidget* parent) +float getSizeRatio(const QSize size) +{ + return size.width() / static_cast(size.height()); +} + +VideoSurface::VideoSurface(const QPixmap& avatar, QWidget* parent, bool expanding) : QWidget{parent} , source{nullptr} , frameLock{false} - , hasSubscribed{false} + , hasSubscribed{0} + , avatar{avatar} + , ratio{1.0f} + , expanding{expanding} { - + recalulateBounds(); } -VideoSurface::VideoSurface(VideoSource *source, QWidget* parent) - : VideoSurface(parent) +VideoSurface::VideoSurface(const QPixmap& avatar, VideoSource *source, QWidget* parent) + : VideoSurface(avatar, parent) { setSource(source); } @@ -42,6 +59,11 @@ VideoSurface::~VideoSurface() unsubscribe(); } +bool VideoSurface::isExpanding() const +{ + return expanding; +} + void VideoSurface::setSource(VideoSource *src) { if (source == src) @@ -52,70 +74,157 @@ void VideoSurface::setSource(VideoSource *src) subscribe(); } +QRect VideoSurface::getBoundingRect() const +{ + QRect bRect = boundingRect; + bRect.setBottomRight(QPoint(boundingRect.bottom() + 1, boundingRect.right() + 1)); + return boundingRect; +} + +float VideoSurface::getRatio() const +{ + return ratio; +} + +void VideoSurface::setAvatar(const QPixmap &pixmap) +{ + avatar = pixmap; + update(); +} + +QPixmap VideoSurface::getAvatar() const +{ + return avatar; +} + void VideoSurface::subscribe() { - if (source && !hasSubscribed) + assert(hasSubscribed >= 0); + + if (source && hasSubscribed++ == 0) { source->subscribe(); - hasSubscribed = true; connect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable); } } void VideoSurface::unsubscribe() { - if (!source || !hasSubscribed) + assert(hasSubscribed >= 0); + + if (!source || hasSubscribed == 0) return; - // Fast lock - { - bool expected = false; - while (!frameLock.compare_exchange_weak(expected, true)) - expected = false; - } + if (--hasSubscribed != 0) + return; + + lock(); lastFrame.reset(); - frameLock = false; + unlock(); + + ratio = 1.0f; + recalulateBounds(); + emit ratioChanged(); + emit boundaryChanged(); source->unsubscribe(); - hasSubscribed = false; disconnect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable); } void VideoSurface::onNewFrameAvailable(std::shared_ptr newFrame) { - // Fast lock + QSize newSize; + + lock(); + lastFrame = newFrame; + newSize = lastFrame->getSize(); + unlock(); + + float newRatio = getSizeRatio(newSize); + + if (newRatio != ratio && isVisible()) { - bool expected = false; - while (!frameLock.compare_exchange_weak(expected, true)) - expected = false; + ratio = newRatio; + recalulateBounds(); + emit ratioChanged(); + emit boundaryChanged(); } - lastFrame = newFrame; - frameLock = false; update(); } void VideoSurface::paintEvent(QPaintEvent*) { - // Fast lock - { - bool expected = false; - while (!frameLock.compare_exchange_weak(expected, true)) - expected = false; - } + lock(); QPainter painter(this); painter.fillRect(painter.viewport(), Qt::black); if (lastFrame) { - QSize frameSize = lastFrame->getSize(); - QRect rect = painter.viewport(); - int width = frameSize.width()*rect.height()/frameSize.height(); - rect.setLeft((rect.width()-width)/2); - rect.setWidth(width); - - QImage frame = lastFrame->toQImage(rect.size()); - painter.drawImage(rect, frame, frame.rect(), Qt::NoFormatConversion); + QImage frame = lastFrame->toQImage(rect().size()); + painter.drawImage(boundingRect, frame, frame.rect(), Qt::NoFormatConversion); } + else + { + painter.fillRect(boundingRect, Qt::white); + QPixmap drawnAvatar = avatar; + + if (drawnAvatar.isNull()) + drawnAvatar = Style::scaleSvgImage(":/img/contact_dark.svg", boundingRect.width(), boundingRect.height()); + + painter.drawPixmap(boundingRect, drawnAvatar, drawnAvatar.rect()); + } + + unlock(); +} + +void VideoSurface::resizeEvent(QResizeEvent* event) +{ + QWidget::resizeEvent(event); + recalulateBounds(); + emit boundaryChanged(); +} + +void VideoSurface::showEvent(QShowEvent*) +{ + //emit ratioChanged(); +} + +void VideoSurface::recalulateBounds() +{ + if (expanding) + { + boundingRect = contentsRect(); + } + else + { + QPoint pos; + QSize size; + QSize usableSize = contentsRect().size(); + int possibleWidth = usableSize.height() * ratio; + + if (possibleWidth > usableSize.width()) + size = (QSize(usableSize.width(), usableSize.width() / ratio)); + else + size = (QSize(possibleWidth, usableSize.height())); + + pos.setX(width() / 2 - size.width() / 2); + pos.setY(height() / 2 - size.height() / 2); + boundingRect.setRect(pos.x(), pos.y(), size.width(), size.height()); + } + + update(); +} + +void VideoSurface::lock() +{ + // Fast lock + bool expected = false; + while (!frameLock.compare_exchange_weak(expected, true)) + expected = false; +} + +void VideoSurface::unlock() +{ frameLock = false; } diff --git a/src/video/videosurface.h b/src/video/videosurface.h index 2b8fe17e7..533776b14 100644 --- a/src/video/videosurface.h +++ b/src/video/videosurface.h @@ -30,26 +30,45 @@ class VideoSurface : public QWidget Q_OBJECT public: - VideoSurface(QWidget* parent=0); - VideoSurface(VideoSource* source, QWidget* parent=0); + VideoSurface(const QPixmap& avatar, QWidget* parent = 0, bool expanding = false); + VideoSurface(const QPixmap& avatar, VideoSource* source, QWidget* parent = 0); ~VideoSurface(); + bool isExpanding() const; void setSource(VideoSource* src); //NULL is a valid option + QRect getBoundingRect() const; + float getRatio() const; + void setAvatar(const QPixmap& pixmap); + QPixmap getAvatar() const; + +signals: + void ratioChanged(); + void boundaryChanged(); protected: void subscribe(); void unsubscribe(); - virtual void paintEvent(QPaintEvent * event) final override; + virtual void paintEvent(QPaintEvent* event) final override; + virtual void resizeEvent(QResizeEvent* event) final override; + virtual void showEvent(QShowEvent* event) final override; private slots: void onNewFrameAvailable(std::shared_ptr newFrame); private: + void recalulateBounds(); + void lock(); + void unlock(); + + QRect boundingRect; VideoSource* source; std::shared_ptr lastFrame; std::atomic_bool frameLock; ///< Fast lock for lastFrame - bool hasSubscribed; + uint8_t hasSubscribed; + QPixmap avatar; + float ratio; + bool expanding; }; #endif // SELFCAMVIEW_H diff --git a/src/widget/form/chatform.cpp b/src/widget/form/chatform.cpp index 1b11c84ee..252a79096 100644 --- a/src/widget/form/chatform.cpp +++ b/src/widget/form/chatform.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "chatform.h" #include "src/core/core.h" @@ -41,7 +42,6 @@ #include "src/core/cstring.h" #include "src/widget/tool/callconfirmwidget.h" #include "src/widget/friendwidget.h" -#include "src/video/netcamview.h" #include "src/widget/form/loadhistorydialog.h" #include "src/widget/tool/chattextedit.h" #include "src/widget/widget.h" @@ -52,6 +52,7 @@ #include "src/chatlog/chatlinecontentproxy.h" #include "src/chatlog/content/text.h" #include "src/chatlog/chatlog.h" +#include "src/video/netcamview.h" #include "src/persistence/offlinemsgengine.h" #include "src/widget/tool/screenshotgrabber.h" #include "src/widget/tool/flyoutoverlaywidget.h" @@ -76,7 +77,6 @@ ChatForm::ChatForm(Friend* chatFriend) typingTimer.setSingleShot(true); - netcam = nullptr; callDurationTimer = nullptr; disableCallButtonsTimer = nullptr; @@ -88,6 +88,9 @@ ChatForm::ChatForm(Friend* chatFriend) headTextLayout->addWidget(callDuration, 1, Qt::AlignCenter); callDuration->hide(); + chatWidget->setMinimumHeight(160); + connect(this, &GenericChatForm::messageInserted, this, &ChatForm::onMessageInserted); + loadHistoryAction = menu.addAction(QString(), this, SLOT(onLoadHistory())); connect(Core::getInstance(), &Core::fileSendStarted, this, &ChatForm::startFileSend); @@ -114,6 +117,8 @@ ChatForm::ChatForm(Friend* chatFriend) retranslateUi(); Translator::registerHandler(std::bind(&ChatForm::retranslateUi, this), this); + + //showNetcam(); } ChatForm::~ChatForm() @@ -747,6 +752,13 @@ void ChatForm::onAvatarChange(uint32_t FriendId, const QPixmap &pic) avatar->setPixmap(pic); } +GenericNetCamView *ChatForm::createNetcam() +{ + NetCamView* view = new NetCamView(f->getFriendID(), this); + view->show(Core::getInstance()->getVideoSourceFromCall(callId), f->getDisplayedName()); + return view; +} + void ChatForm::dragEnterEvent(QDragEnterEvent *ev) { if (ev->mimeData()->hasUrls()) @@ -943,6 +955,12 @@ void ChatForm::onLoadHistory() } } +void ChatForm::onMessageInserted() +{ + if (netcam && bodySplitter->sizes()[1] == 0) + netcam->setShowMessages(true, true); +} + void ChatForm::startCounter() { if (!callDurationTimer) @@ -1075,22 +1093,6 @@ void ChatForm::SendMessageStr(QString msg) } } -void ChatForm::showNetcam() -{ - if (!netcam) - netcam = new NetCamView(); - netcam->show(Core::getInstance()->getVideoSourceFromCall(callId), f->getDisplayedName()); -} - -void ChatForm::hideNetcam() -{ - if (!netcam) - return; - netcam->hide(); - delete netcam; - netcam = nullptr; -} - void ChatForm::retranslateUi() { QString volObjectName = volButton->objectName(); @@ -1106,4 +1108,7 @@ void ChatForm::retranslateUi() micButton->setToolTip(tr("Mute microphone")); else if (micObjectName == QStringLiteral("red")) micButton->setToolTip(tr("Unmute microphone")); + + if (netcam) + netcam->setShowMessages(chatWidget->isVisible()); } diff --git a/src/widget/form/chatform.h b/src/widget/form/chatform.h index e2bfd2c49..c12ad3511 100644 --- a/src/widget/form/chatform.h +++ b/src/widget/form/chatform.h @@ -29,7 +29,6 @@ class Friend; class FileTransferInstance; -class NetCamView; class QPixmap; class CallConfirmWidget; class QHideEvent; @@ -99,13 +98,13 @@ private slots: void onScreenshotClicked(); void onScreenshotTaken(const QPixmap &pixmap); void doScreenshot(); + void onMessageInserted(); private: void retranslateUi(); protected: - void showNetcam(); - void hideNetcam(); + virtual GenericNetCamView* createNetcam() final override; // drag & drop virtual void dragEnterEvent(QDragEnterEvent* ev) final override; virtual void dropEvent(QDropEvent* ev) final override; @@ -115,7 +114,6 @@ protected: private: Friend* f; CroppingLabel *statusMessageLabel; - NetCamView* netcam; int callId; QLabel *callDuration; QTimer *callDurationTimer; diff --git a/src/widget/form/genericchatform.cpp b/src/widget/form/genericchatform.cpp index 7fd9e2f1c..f3b0690fd 100644 --- a/src/widget/form/genericchatform.cpp +++ b/src/widget/form/genericchatform.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "src/persistence/smileypack.h" #include "src/widget/emoticonswidget.h" @@ -44,6 +45,7 @@ #include "src/widget/contentlayout.h" #include "src/widget/tool/croppinglabel.h" #include +#include "src/video/genericnetcamview.h" GenericChatForm::GenericChatForm(QWidget *parent) : QWidget(parent, Qt::Window) @@ -127,8 +129,17 @@ GenericChatForm::GenericChatForm(QWidget *parent) micButton->setStyleSheet(micButtonStylesheet); setLayout(mainLayout); - mainLayout->addWidget(chatWidget); - mainLayout->addLayout(mainFootLayout); + + bodySplitter = new QSplitter(Qt::Vertical, this); + connect(bodySplitter, &QSplitter::splitterMoved, this, &GenericChatForm::onSplitterMoved); + + QWidget* contentWidget = new QWidget(this); + QVBoxLayout* contentLayout = new QVBoxLayout(contentWidget); + contentLayout->addWidget(chatWidget); + contentLayout->addLayout(mainFootLayout); + bodySplitter->addWidget(contentWidget); + + mainLayout->addWidget(bodySplitter); mainLayout->setMargin(0); footButtonsSmall->addWidget(emoteButton); @@ -194,6 +205,8 @@ GenericChatForm::GenericChatForm(QWidget *parent) retranslateUi(); Translator::registerHandler(std::bind(&GenericChatForm::retranslateUi, this), this); + + netcam = nullptr; } GenericChatForm::~GenericChatForm() @@ -462,6 +475,7 @@ QString GenericChatForm::resolveToxId(const ToxId &id) void GenericChatForm::insertChatMessage(ChatMessage::Ptr msg) { chatWidget->insertChatlineAtBottom(std::dynamic_pointer_cast(msg)); + emit messageInserted(); } void GenericChatForm::hideEvent(QHideEvent* event) @@ -511,6 +525,25 @@ bool GenericChatForm::eventFilter(QObject* object, QEvent* event) return false; } +void GenericChatForm::onSplitterMoved(int, int) +{ + if (netcam) + netcam->setShowMessages(bodySplitter->sizes()[1] == 0); +} + +void GenericChatForm::onShowMessagesClicked() +{ + if (netcam) + { + if (bodySplitter->sizes()[1] == 0) + bodySplitter->setSizes({1, 1}); + else + bodySplitter->setSizes({1, 0}); + + onSplitterMoved(0, 0); + } +} + void GenericChatForm::retranslateUi() { QString callObjectName = callButton->objectName(); @@ -537,3 +570,23 @@ void GenericChatForm::retranslateUi() saveChatAction->setText(tr("Save chat log")); clearAction->setText(tr("Clear displayed messages")); } + +void GenericChatForm::showNetcam() +{ + if (!netcam) + netcam = createNetcam(); + + connect(netcam, &GenericNetCamView::showMessageClicked, this, &GenericChatForm::onShowMessagesClicked); + + bodySplitter->insertWidget(0, netcam); + bodySplitter->setCollapsible(0, false); +} + +void GenericChatForm::hideNetcam() +{ + if (!netcam) + return; + netcam->hide(); + delete netcam; + netcam = nullptr; +} diff --git a/src/widget/form/genericchatform.h b/src/widget/form/genericchatform.h index 237afdc03..9f09246fb 100644 --- a/src/widget/form/genericchatform.h +++ b/src/widget/form/genericchatform.h @@ -41,6 +41,12 @@ class MaskablePixmapWidget; class Widget; class FlyoutOverlayWidget; class ContentLayout; +class QSplitter; +class GenericNetCamView; + +namespace Ui { + class MainWindow; +} class GenericChatForm : public QWidget { @@ -67,6 +73,7 @@ signals: void sendMessage(uint32_t, QString); void sendAction(uint32_t, QString); void chatAreaCleared(); + void messageInserted(); public slots: void focusInput(); @@ -82,11 +89,16 @@ protected slots: void onSelectAllClicked(); void showFileMenu(); void hideFileMenu(); + void onShowMessagesClicked(); + void onSplitterMoved(int pos, int index); private: void retranslateUi(); protected: + void showNetcam(); + void hideNetcam(); + virtual GenericNetCamView* createNetcam() = 0; QString resolveToxId(const ToxId &id); void insertChatMessage(ChatMessage::Ptr msg); void adjustFileMenuPosition(); @@ -116,6 +128,8 @@ protected: QDateTime historyBaselineDate = QDateTime::currentDateTime(); // used by HistoryKeeper to load messages from t to historyBaselineDate (excluded) bool audioInputFlag; bool audioOutputFlag; + QSplitter* bodySplitter; + GenericNetCamView* netcam; }; #endif // GENERICCHATFORM_H diff --git a/src/widget/form/groupchatform.cpp b/src/widget/form/groupchatform.cpp index 44493a116..fa79b7c6d 100644 --- a/src/widget/form/groupchatform.cpp +++ b/src/widget/form/groupchatform.cpp @@ -29,6 +29,7 @@ #include "src/persistence/historykeeper.h" #include "src/widget/flowlayout.h" #include "src/widget/translator.h" +#include "src/video/groupnetcamview.h" #include #include #include @@ -195,7 +196,14 @@ void GroupChatForm::onUserListChanged() orderizer[names[i]] = peerLabels[i]; if (group->isSelfPeerNumber(i)) peerLabels[i]->setStyleSheet("QLabel {color : green;}"); + + if (netcam && !group->isSelfPeerNumber(i)) + static_cast(netcam)->addPeer(i, names[i]); } + + if (netcam) + static_cast(netcam)->clearPeers(); + // now alphabetize and add to layout names.sort(Qt::CaseInsensitive); for (unsigned i=0; isetSingleShot(true); - connect(peerAudioTimers[peer], &QTimer::timeout, [=]{this->peerLabels[peer]->setStyleSheet(""); - delete this->peerAudioTimers[peer]; - this->peerAudioTimers[peer] = nullptr;}); + connect(peerAudioTimers[peer], &QTimer::timeout, [this, peer] + { + if (netcam) + static_cast(netcam)->removePeer(peer); + + if (peer >= peerLabels.size()) + return; + + peerLabels[peer]->setStyleSheet(""); + delete peerAudioTimers[peer]; + peerAudioTimers[peer] = nullptr; + }); + + if (netcam) + { + static_cast(netcam)->removePeer(peer); + static_cast(netcam)->addPeer(peer, group->getPeerList()[peer]); + } } peerAudioTimers[peer]->start(500); } @@ -312,6 +335,7 @@ void GroupChatForm::onCallClicked() volButton->style()->polish(volButton); volButton->setToolTip(tr("Mute call")); inCall = true; + showNetcam(); } else { @@ -328,9 +352,24 @@ void GroupChatForm::onCallClicked() volButton->style()->polish(volButton); volButton->setToolTip(""); inCall = false; + hideNetcam(); } } +GenericNetCamView *GroupChatForm::createNetcam() +{ + GroupNetCamView* view = new GroupNetCamView(group->getGroupId(), this); + + QStringList names = group->getPeerList(); + for (unsigned i=0; iisSelfPeerNumber(i)) + static_cast(view)->addPeer(i, names[i]); + } + + return view; +} + void GroupChatForm::keyPressEvent(QKeyEvent* ev) { // Push to talk (CTRL+P) diff --git a/src/widget/form/groupchatform.h b/src/widget/form/groupchatform.h index c3b13e3e9..73eaeed5c 100644 --- a/src/widget/form/groupchatform.h +++ b/src/widget/form/groupchatform.h @@ -49,6 +49,7 @@ private slots: void onCallClicked(); protected: + virtual GenericNetCamView* createNetcam() final override; virtual void keyPressEvent(QKeyEvent* ev) final override; virtual void keyReleaseEvent(QKeyEvent* ev) final override; // drag & drop diff --git a/src/widget/form/settings/avform.cpp b/src/widget/form/settings/avform.cpp index 30dc58a18..1f3bba48c 100644 --- a/src/widget/form/settings/avform.cpp +++ b/src/widget/form/settings/avform.cpp @@ -102,6 +102,7 @@ void AVForm::on_videoModescomboBox_currentIndexChanged(int index) QString devName = videoDeviceList[devIndex].first; VideoMode mode = videoModes[index]; Settings::getInstance().setCamVideoRes(QSize(mode.width, mode.height)); + Settings::getInstance().setCamVideoFPS(mode.FPS); camera.open(devName, mode); } @@ -123,10 +124,11 @@ void AVForm::updateVideoModes(int curIndex) bodyUI->videoModescomboBox->clear(); int prefResIndex = -1; QSize prefRes = Settings::getInstance().getCamVideoRes(); + unsigned short prefFPS = Settings::getInstance().getCamVideoFPS(); for (int i=0; ivideoModescomboBox->setUpdatesEnabled(false); + bodyUI->videoModescomboBox->setCurrentIndex(-1); + bodyUI->videoModescomboBox->setUpdatesEnabled(true); + bodyUI->videoModescomboBox->setCurrentIndex(0); + } } } @@ -192,9 +202,11 @@ void AVForm::onVideoDevChanged(int index) updateVideoModes(index); bodyUI->videoModescomboBox->blockSignals(previouslyBlocked); camera.open(dev); + killVideoSurface(); + createVideoSurface(); } -void AVForm::onResProbingFinished(QList res) +/*void AVForm::onResProbingFinished(QList res) { QSize savedRes = Settings::getInstance().getCamVideoRes(); int savedResIndex = -1; @@ -215,7 +227,7 @@ void AVForm::onResProbingFinished(QList res) bodyUI->videoModescomboBox->setCurrentIndex(savedResIndex); else bodyUI->videoModescomboBox->setCurrentIndex(bodyUI->videoModescomboBox->count()-1); -} +}*/ void AVForm::hideEvent(QHideEvent *) { @@ -242,7 +254,7 @@ void AVForm::getVideoDevices() videoDevIndex = bodyUI->videoDevCombobox->count()-1; } //addItem changes currentIndex -> reset - bodyUI->videoDevCombobox->setCurrentIndex(-1); + //bodyUI->videoDevCombobox->setCurrentIndex(-1); bodyUI->videoDevCombobox->setCurrentIndex(videoDevIndex); bodyUI->videoDevCombobox->blockSignals(false); updateVideoModes(videoDevIndex); @@ -361,7 +373,7 @@ void AVForm::createVideoSurface() { if (camVideoSurface) return; - camVideoSurface = new VideoSurface(bodyUI->CamFrame); + camVideoSurface = new VideoSurface(QPixmap(), bodyUI->CamFrame); camVideoSurface->setObjectName(QStringLiteral("CamVideoSurface")); camVideoSurface->setMinimumSize(QSize(160, 120)); camVideoSurface->setSource(&camera); diff --git a/src/widget/form/settings/avform.h b/src/widget/form/settings/avform.h index 63fcd5580..d4c1a748f 100644 --- a/src/widget/form/settings/avform.h +++ b/src/widget/form/settings/avform.h @@ -63,7 +63,7 @@ private slots: // camera void onVideoDevChanged(int index); - void onResProbingFinished(QList res); + //void onResProbingFinished(QList res); virtual void hideEvent(QHideEvent*) final override; virtual void showEvent(QShowEvent*) final override; diff --git a/src/widget/notificationscrollarea.cpp b/src/widget/notificationscrollarea.cpp index 79e3bed2d..4432d1503 100644 --- a/src/widget/notificationscrollarea.cpp +++ b/src/widget/notificationscrollarea.cpp @@ -116,8 +116,8 @@ void NotificationScrollArea::resizeEvent(QResizeEvent *event) void NotificationScrollArea::findNextWidget() { - GenericChatroomWidget* next = nullptr; int value; + GenericChatroomWidget* next = nullptr; QHash::iterator i = trackedWidgets.begin(); // Find the first next, to avoid nullptr. @@ -151,8 +151,8 @@ void NotificationScrollArea::findNextWidget() void NotificationScrollArea::findPreviousWidget() { - GenericChatroomWidget* next = nullptr; int value; + GenericChatroomWidget* next = nullptr; QHash::iterator i = trackedWidgets.begin(); // Find the first next, to avoid nullptr. diff --git a/src/widget/tool/movablewidget.cpp b/src/widget/tool/movablewidget.cpp new file mode 100644 index 000000000..737e6350e --- /dev/null +++ b/src/widget/tool/movablewidget.cpp @@ -0,0 +1,286 @@ +/* + Copyright © 2015 by The qTox Project + + This file is part of qTox, a Qt-based graphical interface for Tox. + + qTox is libre software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + qTox is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with qTox. If not, see . +*/ + +#include "movablewidget.h" +#include +#include + +MovableWidget::MovableWidget(QWidget *parent) + : QWidget(parent) +{ + setMinimumHeight(64); + setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored)); + actualSize = minimumSize(); + boundaryRect = QRect(0, 0, 0, 0); + setRatio(1.0f); + resize(minimumSize()); + actualPos = QPoint(0, 0); +} + +void MovableWidget::resetBoundary(QRect newBoundary) +{ + boundaryRect = newBoundary; + resize(QSize(round(actualSize.width()), round(actualSize.height()))); + + QPoint moveTo = QPoint(round(actualPos.x()), round(actualPos.y())); + checkBoundary(moveTo); + move(moveTo); + actualPos = moveTo; +} + +void MovableWidget::setBoundary(QRect newBoundary) +{ + if (boundaryRect.isNull()) + { + boundaryRect = newBoundary; + return; + } + + float changeX = newBoundary.width() / static_cast(boundaryRect.width()); + float changeY = newBoundary.height() / static_cast(boundaryRect.height()); + + float percentageX = (x() - boundaryRect.x()) / static_cast(boundaryRect.width() - width()); + float percentageY = (y() - boundaryRect.y()) / static_cast(boundaryRect.height() - height()); + + actualSize.setWidth(actualSize.width() * changeX); + actualSize.setHeight(actualSize.height() * changeY); + + if (actualSize.width() == 0) + actualSize.setWidth(1); + + if (actualSize.height() == 0) + actualSize.setHeight(1); + + resize(QSize(round(actualSize.width()), round(actualSize.height()))); + + actualPos = QPointF(percentageX * (newBoundary.width() - width()), percentageY * (newBoundary.height() - height())); + actualPos += QPointF(newBoundary.topLeft()); + + QPoint moveTo = QPoint(round(actualPos.x()), round(actualPos.y())); + move(moveTo); + + boundaryRect = newBoundary; + +} + +float MovableWidget::getRatio() const +{ + return ratio; +} + +void MovableWidget::setRatio(float r) +{ + ratio = r; + resize(width(), width() / ratio); + QPoint position = QPoint(actualPos.x(), actualPos.y()); + checkBoundary(position); + move(position); + + actualPos = pos(); + actualSize = size(); +} + +void MovableWidget::mousePressEvent(QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton) + { + if (!(mode & Resize)) + mode |= Moving; + + lastPoint = event->globalPos(); + } +} + +void MovableWidget::mouseMoveEvent(QMouseEvent* event) +{ + if (mode & Moving) + { + QPoint moveTo = pos() - (lastPoint - event->globalPos()); + checkBoundary(moveTo); + + move(moveTo); + lastPoint = event->globalPos(); + + actualPos = pos(); + } + else + { + if (!(event->buttons() & Qt::LeftButton)) + { + if (event->x() < 6) + mode |= ResizeLeft; + else + mode &= ~ResizeLeft; + + if (event->y() < 6) + mode |= ResizeUp; + else + mode &= ~ResizeUp; + + if (event->x() > width() - 6) + mode |= ResizeRight; + else + mode &= ~ResizeRight; + + if (event->y() > height() - 6) + mode |= ResizeDown; + else + mode &= ~ResizeDown; + } + + if (mode & Resize) + { + const Modes ResizeUpRight = ResizeUp | ResizeRight; + const Modes ResizeUpLeft = ResizeUp | ResizeLeft; + const Modes ResizeDownRight = ResizeDown | ResizeRight; + const Modes ResizeDownLeft = ResizeDown | ResizeLeft; + + if ((mode & ResizeUpRight) == ResizeUpRight || (mode & ResizeDownLeft) == ResizeDownLeft) + setCursor(Qt::SizeBDiagCursor); + else if ((mode & ResizeUpLeft) == ResizeUpLeft || (mode & ResizeDownRight) == ResizeDownRight) + setCursor(Qt::SizeFDiagCursor); + else if (mode & (ResizeLeft | ResizeRight)) + setCursor(Qt::SizeHorCursor); + else + setCursor(Qt::SizeVerCursor); + + if (event->buttons() & Qt::LeftButton) + { + QPoint lastPosition = pos(); + QPoint displacement = lastPoint - event->globalPos(); + QSize lastSize = size(); + + + if (mode & ResizeUp) + { + lastSize.setHeight(height() + displacement.y()); + + if (lastSize.height() > maximumHeight()) + lastPosition.setY(y() - displacement.y() + (lastSize.height() - maximumHeight())); + else + lastPosition.setY(y() - displacement.y()); + } + + if (mode & ResizeLeft) + { + lastSize.setWidth(width() + displacement.x()); + if (lastSize.width() > maximumWidth()) + lastPosition.setX(x() - displacement.x() + (lastSize.width() - maximumWidth())); + else + lastPosition.setX(x() - displacement.x()); + } + + if (mode & ResizeRight) + lastSize.setWidth(width() - displacement.x()); + + if (mode & ResizeDown) + lastSize.setHeight(height() - displacement.y()); + + if (lastSize.height() > maximumHeight()) + lastSize.setHeight(maximumHeight()); + + if (lastSize.width() > maximumWidth()) + lastSize.setWidth(maximumWidth()); + + if (mode & (ResizeLeft | ResizeRight)) + { + if (mode & (ResizeUp | ResizeDown)) + { + int height = lastSize.width() / getRatio(); + + if (!(mode & ResizeDown)) + lastPosition.setY(lastPosition.y() - (height - lastSize.height())); + + resize(lastSize.width(), height); + + if (lastSize.width() < minimumWidth()) + lastPosition.setX(pos().x()); + + if (height < minimumHeight()) + lastPosition.setY(pos().y()); + } + else + { + resize(lastSize.width(), lastSize.width() / getRatio()); + } + } + else + { + resize(lastSize.height() * getRatio(), lastSize.height()); + } + + updateGeometry(); + + checkBoundary(lastPosition); + + move(lastPosition); + + lastPoint = event->globalPos(); + actualSize = size(); + actualPos = pos(); + } + } + else + { + unsetCursor(); + } + } + +} + +void MovableWidget::mouseReleaseEvent(QMouseEvent* event) +{ + if (!(event->buttons() & Qt::LeftButton)) + mode = 0; +} + +void MovableWidget::mouseDoubleClickEvent(QMouseEvent* event) +{ + if (!(event->buttons() & Qt::LeftButton)) + return; + + if (!graphicsEffect()) + { + QGraphicsOpacityEffect* opacityEffect = new QGraphicsOpacityEffect(this); + opacityEffect->setOpacity(0.5); + setGraphicsEffect(opacityEffect); + } + else + { + setGraphicsEffect(nullptr); + } +} + +void MovableWidget::checkBoundary(QPoint& point) const +{ + int x1, y1, x2, y2; + boundaryRect.getCoords(&x1, &y1, &x2, &y2); + + if (point.x() < boundaryRect.left()) + point.setX(boundaryRect.left()); + + if (point.y() < boundaryRect.top()) + point.setY(boundaryRect.top()); + + if (point.x() + width() > boundaryRect.right() + 1) + point.setX(boundaryRect.right() - width() + 1); + + if (point.y() + height() > boundaryRect.bottom() + 1) + point.setY(boundaryRect.bottom() - height() + 1); +} diff --git a/src/widget/tool/movablewidget.h b/src/widget/tool/movablewidget.h new file mode 100644 index 000000000..ffa7354c0 --- /dev/null +++ b/src/widget/tool/movablewidget.h @@ -0,0 +1,64 @@ +/* + Copyright © 2015 by The qTox Project + + This file is part of qTox, a Qt-based graphical interface for Tox. + + qTox is libre software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + qTox is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with qTox. If not, see . +*/ + +#ifndef MOVABLEWIDGET_H +#define MOVABLEWIDGET_H + +#include + +class MovableWidget : public QWidget +{ +public: + MovableWidget(QWidget* parent); + void resetBoundary(QRect newBoundary); + void setBoundary(QRect newBoundary); + float getRatio() const; + void setRatio(float r); + +protected: + void mousePressEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event); + void mouseReleaseEvent(QMouseEvent* event); + void mouseDoubleClickEvent(QMouseEvent* event); + +private: + void checkBoundary(QPoint& point) const; + void checkBoundaryLeft(int &x) const; + + typedef uint8_t Modes; + + enum Mode : Modes + { + Moving = 0x01, + ResizeLeft = 0x02, + ResizeRight = 0x04, + ResizeUp = 0x08, + ResizeDown = 0x10, + Resize = ResizeLeft | ResizeRight | ResizeUp | ResizeDown + }; + + Modes mode = 0; + QPoint lastPoint; + QRect boundaryRect; + QSizeF actualSize; + QPointF actualPos; + float ratio; +}; + +#endif // MOVABLEWIDGET_H diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 1e3d51b55..410d3fd70 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -1507,7 +1507,8 @@ void Widget::onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t Cha } else if (change == TOX_CHAT_CHANGE_PEER_NAME) // core overwrites old name before telling us it changed... { - g->updatePeer(peernumber,Nexus::getCore()->getGroupPeerName(groupnumber, peernumber)); + qDebug() << "UPDATING PEER"; + g->updatePeer(peernumber, name); } }