1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Merge branch TheSpiritXIII:video

This commit is contained in:
agilob 2015-10-10 13:49:30 +01:00
commit 266c0fb989
No known key found for this signature in database
GPG Key ID: 296F0B764741106C
30 changed files with 1328 additions and 105 deletions

View File

@ -358,7 +358,6 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) {
src/widget/form/loadhistorydialog.h \ src/widget/form/loadhistorydialog.h \
src/widget/form/setpassworddialog.h \ src/widget/form/setpassworddialog.h \
src/widget/form/tabcompleter.h \ src/widget/form/tabcompleter.h \
src/widget/flowlayout.h \
src/ipc.h \ src/ipc.h \
src/net/autoupdate.h \ src/net/autoupdate.h \
src/widget/tool/callconfirmwidget.h \ src/widget/tool/callconfirmwidget.h \
@ -384,8 +383,8 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) {
src/widget/form/loadhistorydialog.cpp \ src/widget/form/loadhistorydialog.cpp \
src/widget/form/setpassworddialog.cpp \ src/widget/form/setpassworddialog.cpp \
src/widget/form/tabcompleter.cpp \ src/widget/form/tabcompleter.cpp \
src/widget/flowlayout.cpp \
src/ipc.cpp \ src/ipc.cpp \
src/widget/flowlayout.cpp \
src/net/autoupdate.cpp \ src/net/autoupdate.cpp \
src/widget/tool/callconfirmwidget.cpp \ src/widget/tool/callconfirmwidget.cpp \
src/widget/systemtrayicon.cpp \ src/widget/systemtrayicon.cpp \
@ -498,7 +497,10 @@ SOURCES += \
src/widget/tool/removefrienddialog.cpp \ src/widget/tool/removefrienddialog.cpp \
src/widget/contentlayout.cpp \ src/widget/contentlayout.cpp \
src/widget/contentdialog.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 += \ HEADERS += \
src/audio/audio.h \ src/audio/audio.h \
@ -545,4 +547,7 @@ HEADERS += \
src/widget/contentlayout.h \ src/widget/contentlayout.h \
src/widget/contentdialog.h \ src/widget/contentdialog.h \
src/widget/tool/activatedialog.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

View File

@ -267,7 +267,7 @@ void Audio::playMono16Sound(const QByteArray& data)
alSourcePlay(alMainSource); alSourcePlay(alMainSource);
alDeleteBuffers(1, &buffer); alDeleteBuffers(1, &buffer);
} }
#include <QDebug>
void Audio::playGroupAudioQueued(Tox*,int group, int peer, const int16_t* data, void Audio::playGroupAudioQueued(Tox*,int group, int peer, const int16_t* data,
unsigned samples, uint8_t channels, unsigned sample_rate, void* core) 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); 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<float>(bufsize));
playAudioBuffer(call.alSources[peer], data, samples, channels, sample_rate); playAudioBuffer(call.alSources[peer], data, samples, channels, sample_rate);
} }

View File

@ -41,7 +41,7 @@ class QMutex;
struct Tox; struct Tox;
class AudioFilterer; class AudioFilterer;
class Audio : QObject class Audio : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -81,6 +81,9 @@ public slots:
void playGroupAudio(int group, int peer, const int16_t* data, void playGroupAudio(int group, int peer, const int16_t* data,
unsigned samples, uint8_t channels, unsigned sample_rate); unsigned samples, uint8_t channels, unsigned sample_rate);
signals:
void groupAudioPlayed(int group, int peer, unsigned short volume);
private: private:
explicit Audio()=default; explicit Audio()=default;
~Audio(); ~Audio();

View File

@ -75,12 +75,17 @@ void Group::updatePeer(int peerId, QString name)
QString toxid = id.publicKey; QString toxid = id.publicKey;
peers[peerId] = name; peers[peerId] = name;
toxids[toxid] = name; toxids[toxid] = name;
Friend *f = FriendList::findFriend(id); Friend *f = FriendList::findFriend(id);
if (f && f->hasAlias()) 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(); peers[peerId] = f->getDisplayedName();
toxids[toxid] = f->getDisplayedName(); toxids[toxid] = f->getDisplayedName();
} }*/
widget->onUserListChanged(); widget->onUserListChanged();
chatForm->onUserListChanged(); chatForm->onUserListChanged();

View File

@ -268,8 +268,8 @@ int main(int argc, char *argv[])
logFile = nullptr; logFile = nullptr;
#endif #endif
CameraSource::destroyInstance();
Nexus::destroyInstance(); Nexus::destroyInstance();
CameraSource::destroyInstance();
Settings::destroyInstance(); Settings::destroyInstance();
qDebug() << "Clean exit with status"<<errorcode; qDebug() << "Clean exit with status"<<errorcode;
return errorcode; return errorcode;

View File

@ -1292,6 +1292,20 @@ QSplitter:handle{
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QMenuBar" name="menubar">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>775</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionClose"> <action name="actionClose">
<property name="text"> <property name="text">
<string>Close</string> <string>Close</string>

View File

@ -232,6 +232,7 @@ void Settings::loadGlobal()
s.beginGroup("Video"); s.beginGroup("Video");
videoDev = s.value("videoDev", "").toString(); videoDev = s.value("videoDev", "").toString();
camVideoRes = s.value("camVideoRes",QSize()).toSize(); camVideoRes = s.value("camVideoRes",QSize()).toSize();
camVideoFPS = s.value("camVideoFPS", 0).toUInt();
s.endGroup(); s.endGroup();
// Read the embedded DHT bootsrap nodes list if needed // Read the embedded DHT bootsrap nodes list if needed
@ -431,6 +432,7 @@ void Settings::saveGlobal()
s.beginGroup("Video"); s.beginGroup("Video");
s.setValue("videoDev", videoDev); s.setValue("videoDev", videoDev);
s.setValue("camVideoRes",camVideoRes); s.setValue("camVideoRes",camVideoRes);
s.setValue("camVideoFPS",camVideoFPS);
s.endGroup(); s.endGroup();
} }
@ -1220,6 +1222,19 @@ void Settings::setCamVideoRes(QSize newValue)
camVideoRes = 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 QString Settings::getFriendAdress(const QString &publicKey) const
{ {
QMutexLocker locker{&bigLock}; QMutexLocker locker{&bigLock};

View File

@ -163,6 +163,9 @@ public:
QSize getCamVideoRes() const; QSize getCamVideoRes() const;
void setCamVideoRes(QSize newValue); void setCamVideoRes(QSize newValue);
unsigned short getCamVideoFPS() const;
void setCamVideoFPS(unsigned short newValue);
bool isAnimationEnabled() const; bool isAnimationEnabled() const;
void setAnimationEnabled(bool newValue); void setAnimationEnabled(bool newValue);
@ -376,6 +379,7 @@ private:
// Video // Video
QString videoDev; QString videoDev;
QSize camVideoRes; QSize camVideoRes;
unsigned short camVideoFPS;
struct friendProp struct friendProp
{ {

View File

@ -286,6 +286,8 @@ bool CameraSource::openDevice()
while (!streamFuture.isRunning()) while (!streamFuture.isRunning())
QThread::yieldCurrentThread(); QThread::yieldCurrentThread();
emit deviceOpened();
return true; return true;
} }

View File

@ -59,6 +59,9 @@ public:
virtual bool subscribe() override; virtual bool subscribe() override;
virtual void unsubscribe() override; virtual void unsubscribe() override;
signals:
void deviceOpened();
private: private:
CameraSource(); CameraSource();
~CameraSource(); ~CameraSource();

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "genericnetcamview.h"
#include <QBoxLayout>
#include <QPushButton>
#include <QFrame>
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());
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef GENERICNETCAMVIEW_H
#define GENERICNETCAMVIEW_H
#include <QWidget>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 <QBoxLayout>
#include <QScrollArea>
#include <QSplitter>
#include <QTimer>
#include <QMap>
#include <QDebug>
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<int> 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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef GROUPNETCAMVIEW_H
#define GROUPNETCAMVIEW_H
#include "genericnetcamview.h"
#include <QMap>
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<int, PeerVideo> videoList;
LabeledVideo* videoLabelSurface;
LabeledVideo* selfVideoSurface;
int activePeer;
int group;
};
#endif // GROUPNETCAMVIEW_H

View File

@ -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. This file is part of qTox, a Qt-based graphical interface for Tox.
@ -18,35 +18,97 @@
*/ */
#include "netcamview.h" #include "netcamview.h"
#include "camerasource.h"
#include "src/friend.h"
#include "src/friendlist.h"
#include "src/core/core.h" #include "src/core/core.h"
#include "src/video/videosurface.h" #include "src/video/videosurface.h"
#include "src/widget/tool/movablewidget.h"
#include "src/persistence/settings.h"
#include <QLabel> #include <QLabel>
#include <QHBoxLayout> #include <QBoxLayout>
#include <QFrame>
NetCamView::NetCamView(QWidget* parent) NetCamView::NetCamView(int friendId, QWidget* parent)
: QWidget(parent) : GenericNetCamView(parent)
, mainLayout(new QHBoxLayout()) , selfFrame{nullptr}
, friendId{friendId}
{ {
setLayout(mainLayout); QString id = FriendList::findFriend(friendId)->getToxId().toString();
setWindowTitle(tr("Tox video")); videoSurface = new VideoSurface(Settings::getInstance().getSavedAvatar(id), this);
setMinimumSize(320,240); 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) void NetCamView::show(VideoSource *source, const QString &title)
{ {
setSource(source); setSource(source);
setTitle(title); selfVideoSurface->setSource(&CameraSource::getInstance());
setTitle(title);
QWidget::show(); QWidget::show();
} }
void NetCamView::hide() void NetCamView::hide()
{ {
setSource(nullptr); setSource(nullptr);
selfVideoSurface->setSource(nullptr);
if (selfFrame)
selfFrame->deleteLater();
selfFrame = nullptr;
QWidget::hide(); QWidget::hide();
} }
@ -60,3 +122,24 @@ void NetCamView::setTitle(const QString &title)
{ {
setWindowTitle(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());
}

View File

@ -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. This file is part of qTox, a Qt-based graphical interface for Tox.
@ -20,19 +20,20 @@
#ifndef NETCAMVIEW_H #ifndef NETCAMVIEW_H
#define NETCAMVIEW_H #define NETCAMVIEW_H
#include <QWidget> #include "genericnetcamview.h"
class QHBoxLayout; class QHBoxLayout;
struct vpx_image; struct vpx_image;
class VideoSurface;
class VideoSource; class VideoSource;
class QFrame;
class MovableWidget;
class NetCamView : public QWidget class NetCamView : public GenericNetCamView
{ {
Q_OBJECT Q_OBJECT
public: public:
NetCamView(QWidget *parent=0); NetCamView(int friendId, QWidget *parent=0);
virtual void show(VideoSource* source, const QString& title); virtual void show(VideoSource* source, const QString& title);
virtual void hide(); virtual void hide();
@ -40,9 +41,19 @@ public:
void setSource(VideoSource* s); void setSource(VideoSource* s);
void setTitle(const QString& title); void setTitle(const QString& title);
protected:
void showEvent(QShowEvent* event) final override;
private slots:
void updateRatio();
private: private:
QHBoxLayout* mainLayout; void updateFrameSize(QSize size);
VideoSurface* videoSurface;
VideoSurface* selfVideoSurface;
MovableWidget* selfFrame;
int friendId;
bool e = false;
}; };
#endif // NETCAMVIEW_H #endif // NETCAMVIEW_H

View File

@ -6,33 +6,50 @@
qTox is libre software: you can redistribute it and/or modify qTox is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or 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, qTox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of 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. 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 <http://www.gnu.org/licenses/>. along with qTox. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "videosurface.h" #include "videosurface.h"
#include "src/video/videoframe.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 <QPainter> #include <QPainter>
#include <QLabel> #include <QLabel>
#include <cassert>
#include <QDebug>
VideoSurface::VideoSurface(QWidget* parent) float getSizeRatio(const QSize size)
{
return size.width() / static_cast<float>(size.height());
}
VideoSurface::VideoSurface(const QPixmap& avatar, QWidget* parent, bool expanding)
: QWidget{parent} : QWidget{parent}
, source{nullptr} , source{nullptr}
, frameLock{false} , frameLock{false}
, hasSubscribed{false} , hasSubscribed{0}
, avatar{avatar}
, ratio{1.0f}
, expanding{expanding}
{ {
recalulateBounds();
} }
VideoSurface::VideoSurface(VideoSource *source, QWidget* parent) VideoSurface::VideoSurface(const QPixmap& avatar, VideoSource *source, QWidget* parent)
: VideoSurface(parent) : VideoSurface(avatar, parent)
{ {
setSource(source); setSource(source);
} }
@ -42,6 +59,11 @@ VideoSurface::~VideoSurface()
unsubscribe(); unsubscribe();
} }
bool VideoSurface::isExpanding() const
{
return expanding;
}
void VideoSurface::setSource(VideoSource *src) void VideoSurface::setSource(VideoSource *src)
{ {
if (source == src) if (source == src)
@ -52,70 +74,157 @@ void VideoSurface::setSource(VideoSource *src)
subscribe(); 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() void VideoSurface::subscribe()
{ {
if (source && !hasSubscribed) assert(hasSubscribed >= 0);
if (source && hasSubscribed++ == 0)
{ {
source->subscribe(); source->subscribe();
hasSubscribed = true;
connect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable); connect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable);
} }
} }
void VideoSurface::unsubscribe() void VideoSurface::unsubscribe()
{ {
if (!source || !hasSubscribed) assert(hasSubscribed >= 0);
if (!source || hasSubscribed == 0)
return; return;
// Fast lock if (--hasSubscribed != 0)
{ return;
bool expected = false;
while (!frameLock.compare_exchange_weak(expected, true)) lock();
expected = false;
}
lastFrame.reset(); lastFrame.reset();
frameLock = false; unlock();
ratio = 1.0f;
recalulateBounds();
emit ratioChanged();
emit boundaryChanged();
source->unsubscribe(); source->unsubscribe();
hasSubscribed = false;
disconnect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable); disconnect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable);
} }
void VideoSurface::onNewFrameAvailable(std::shared_ptr<VideoFrame> newFrame) void VideoSurface::onNewFrameAvailable(std::shared_ptr<VideoFrame> newFrame)
{ {
// Fast lock QSize newSize;
lock();
lastFrame = newFrame;
newSize = lastFrame->getSize();
unlock();
float newRatio = getSizeRatio(newSize);
if (newRatio != ratio && isVisible())
{ {
bool expected = false; ratio = newRatio;
while (!frameLock.compare_exchange_weak(expected, true)) recalulateBounds();
expected = false; emit ratioChanged();
emit boundaryChanged();
} }
lastFrame = newFrame;
frameLock = false;
update(); update();
} }
void VideoSurface::paintEvent(QPaintEvent*) void VideoSurface::paintEvent(QPaintEvent*)
{ {
// Fast lock lock();
{
bool expected = false;
while (!frameLock.compare_exchange_weak(expected, true))
expected = false;
}
QPainter painter(this); QPainter painter(this);
painter.fillRect(painter.viewport(), Qt::black); painter.fillRect(painter.viewport(), Qt::black);
if (lastFrame) if (lastFrame)
{ {
QSize frameSize = lastFrame->getSize(); QImage frame = lastFrame->toQImage(rect().size());
QRect rect = painter.viewport(); painter.drawImage(boundingRect, frame, frame.rect(), Qt::NoFormatConversion);
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);
} }
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; frameLock = false;
} }

View File

@ -30,26 +30,45 @@ class VideoSurface : public QWidget
Q_OBJECT Q_OBJECT
public: public:
VideoSurface(QWidget* parent=0); VideoSurface(const QPixmap& avatar, QWidget* parent = 0, bool expanding = false);
VideoSurface(VideoSource* source, QWidget* parent=0); VideoSurface(const QPixmap& avatar, VideoSource* source, QWidget* parent = 0);
~VideoSurface(); ~VideoSurface();
bool isExpanding() const;
void setSource(VideoSource* src); //NULL is a valid option 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: protected:
void subscribe(); void subscribe();
void unsubscribe(); 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: private slots:
void onNewFrameAvailable(std::shared_ptr<VideoFrame> newFrame); void onNewFrameAvailable(std::shared_ptr<VideoFrame> newFrame);
private: private:
void recalulateBounds();
void lock();
void unlock();
QRect boundingRect;
VideoSource* source; VideoSource* source;
std::shared_ptr<VideoFrame> lastFrame; std::shared_ptr<VideoFrame> lastFrame;
std::atomic_bool frameLock; ///< Fast lock for lastFrame std::atomic_bool frameLock; ///< Fast lock for lastFrame
bool hasSubscribed; uint8_t hasSubscribed;
QPixmap avatar;
float ratio;
bool expanding;
}; };
#endif // SELFCAMVIEW_H #endif // SELFCAMVIEW_H

View File

@ -31,6 +31,7 @@
#include <QTemporaryFile> #include <QTemporaryFile>
#include <QGuiApplication> #include <QGuiApplication>
#include <QStyle> #include <QStyle>
#include <QSplitter>
#include <cassert> #include <cassert>
#include "chatform.h" #include "chatform.h"
#include "src/core/core.h" #include "src/core/core.h"
@ -41,7 +42,6 @@
#include "src/core/cstring.h" #include "src/core/cstring.h"
#include "src/widget/tool/callconfirmwidget.h" #include "src/widget/tool/callconfirmwidget.h"
#include "src/widget/friendwidget.h" #include "src/widget/friendwidget.h"
#include "src/video/netcamview.h"
#include "src/widget/form/loadhistorydialog.h" #include "src/widget/form/loadhistorydialog.h"
#include "src/widget/tool/chattextedit.h" #include "src/widget/tool/chattextedit.h"
#include "src/widget/widget.h" #include "src/widget/widget.h"
@ -52,6 +52,7 @@
#include "src/chatlog/chatlinecontentproxy.h" #include "src/chatlog/chatlinecontentproxy.h"
#include "src/chatlog/content/text.h" #include "src/chatlog/content/text.h"
#include "src/chatlog/chatlog.h" #include "src/chatlog/chatlog.h"
#include "src/video/netcamview.h"
#include "src/persistence/offlinemsgengine.h" #include "src/persistence/offlinemsgengine.h"
#include "src/widget/tool/screenshotgrabber.h" #include "src/widget/tool/screenshotgrabber.h"
#include "src/widget/tool/flyoutoverlaywidget.h" #include "src/widget/tool/flyoutoverlaywidget.h"
@ -76,7 +77,6 @@ ChatForm::ChatForm(Friend* chatFriend)
typingTimer.setSingleShot(true); typingTimer.setSingleShot(true);
netcam = nullptr;
callDurationTimer = nullptr; callDurationTimer = nullptr;
disableCallButtonsTimer = nullptr; disableCallButtonsTimer = nullptr;
@ -88,6 +88,9 @@ ChatForm::ChatForm(Friend* chatFriend)
headTextLayout->addWidget(callDuration, 1, Qt::AlignCenter); headTextLayout->addWidget(callDuration, 1, Qt::AlignCenter);
callDuration->hide(); callDuration->hide();
chatWidget->setMinimumHeight(160);
connect(this, &GenericChatForm::messageInserted, this, &ChatForm::onMessageInserted);
loadHistoryAction = menu.addAction(QString(), this, SLOT(onLoadHistory())); loadHistoryAction = menu.addAction(QString(), this, SLOT(onLoadHistory()));
connect(Core::getInstance(), &Core::fileSendStarted, this, &ChatForm::startFileSend); connect(Core::getInstance(), &Core::fileSendStarted, this, &ChatForm::startFileSend);
@ -114,6 +117,8 @@ ChatForm::ChatForm(Friend* chatFriend)
retranslateUi(); retranslateUi();
Translator::registerHandler(std::bind(&ChatForm::retranslateUi, this), this); Translator::registerHandler(std::bind(&ChatForm::retranslateUi, this), this);
//showNetcam();
} }
ChatForm::~ChatForm() ChatForm::~ChatForm()
@ -747,6 +752,13 @@ void ChatForm::onAvatarChange(uint32_t FriendId, const QPixmap &pic)
avatar->setPixmap(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) void ChatForm::dragEnterEvent(QDragEnterEvent *ev)
{ {
if (ev->mimeData()->hasUrls()) 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() void ChatForm::startCounter()
{ {
if (!callDurationTimer) 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() void ChatForm::retranslateUi()
{ {
QString volObjectName = volButton->objectName(); QString volObjectName = volButton->objectName();
@ -1106,4 +1108,7 @@ void ChatForm::retranslateUi()
micButton->setToolTip(tr("Mute microphone")); micButton->setToolTip(tr("Mute microphone"));
else if (micObjectName == QStringLiteral("red")) else if (micObjectName == QStringLiteral("red"))
micButton->setToolTip(tr("Unmute microphone")); micButton->setToolTip(tr("Unmute microphone"));
if (netcam)
netcam->setShowMessages(chatWidget->isVisible());
} }

View File

@ -29,7 +29,6 @@
class Friend; class Friend;
class FileTransferInstance; class FileTransferInstance;
class NetCamView;
class QPixmap; class QPixmap;
class CallConfirmWidget; class CallConfirmWidget;
class QHideEvent; class QHideEvent;
@ -99,13 +98,13 @@ private slots:
void onScreenshotClicked(); void onScreenshotClicked();
void onScreenshotTaken(const QPixmap &pixmap); void onScreenshotTaken(const QPixmap &pixmap);
void doScreenshot(); void doScreenshot();
void onMessageInserted();
private: private:
void retranslateUi(); void retranslateUi();
protected: protected:
void showNetcam(); virtual GenericNetCamView* createNetcam() final override;
void hideNetcam();
// drag & drop // drag & drop
virtual void dragEnterEvent(QDragEnterEvent* ev) final override; virtual void dragEnterEvent(QDragEnterEvent* ev) final override;
virtual void dropEvent(QDropEvent* ev) final override; virtual void dropEvent(QDropEvent* ev) final override;
@ -115,7 +114,6 @@ protected:
private: private:
Friend* f; Friend* f;
CroppingLabel *statusMessageLabel; CroppingLabel *statusMessageLabel;
NetCamView* netcam;
int callId; int callId;
QLabel *callDuration; QLabel *callDuration;
QTimer *callDurationTimer; QTimer *callDurationTimer;

View File

@ -24,6 +24,7 @@
#include <QDebug> #include <QDebug>
#include <QShortcut> #include <QShortcut>
#include <QKeyEvent> #include <QKeyEvent>
#include <QSplitter>
#include "src/persistence/smileypack.h" #include "src/persistence/smileypack.h"
#include "src/widget/emoticonswidget.h" #include "src/widget/emoticonswidget.h"
@ -44,6 +45,7 @@
#include "src/widget/contentlayout.h" #include "src/widget/contentlayout.h"
#include "src/widget/tool/croppinglabel.h" #include "src/widget/tool/croppinglabel.h"
#include <QPushButton> #include <QPushButton>
#include "src/video/genericnetcamview.h"
GenericChatForm::GenericChatForm(QWidget *parent) GenericChatForm::GenericChatForm(QWidget *parent)
: QWidget(parent, Qt::Window) : QWidget(parent, Qt::Window)
@ -127,8 +129,17 @@ GenericChatForm::GenericChatForm(QWidget *parent)
micButton->setStyleSheet(micButtonStylesheet); micButton->setStyleSheet(micButtonStylesheet);
setLayout(mainLayout); 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); mainLayout->setMargin(0);
footButtonsSmall->addWidget(emoteButton); footButtonsSmall->addWidget(emoteButton);
@ -194,6 +205,8 @@ GenericChatForm::GenericChatForm(QWidget *parent)
retranslateUi(); retranslateUi();
Translator::registerHandler(std::bind(&GenericChatForm::retranslateUi, this), this); Translator::registerHandler(std::bind(&GenericChatForm::retranslateUi, this), this);
netcam = nullptr;
} }
GenericChatForm::~GenericChatForm() GenericChatForm::~GenericChatForm()
@ -462,6 +475,7 @@ QString GenericChatForm::resolveToxId(const ToxId &id)
void GenericChatForm::insertChatMessage(ChatMessage::Ptr msg) void GenericChatForm::insertChatMessage(ChatMessage::Ptr msg)
{ {
chatWidget->insertChatlineAtBottom(std::dynamic_pointer_cast<ChatLine>(msg)); chatWidget->insertChatlineAtBottom(std::dynamic_pointer_cast<ChatLine>(msg));
emit messageInserted();
} }
void GenericChatForm::hideEvent(QHideEvent* event) void GenericChatForm::hideEvent(QHideEvent* event)
@ -511,6 +525,25 @@ bool GenericChatForm::eventFilter(QObject* object, QEvent* event)
return false; 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() void GenericChatForm::retranslateUi()
{ {
QString callObjectName = callButton->objectName(); QString callObjectName = callButton->objectName();
@ -537,3 +570,23 @@ void GenericChatForm::retranslateUi()
saveChatAction->setText(tr("Save chat log")); saveChatAction->setText(tr("Save chat log"));
clearAction->setText(tr("Clear displayed messages")); 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;
}

View File

@ -41,6 +41,12 @@ class MaskablePixmapWidget;
class Widget; class Widget;
class FlyoutOverlayWidget; class FlyoutOverlayWidget;
class ContentLayout; class ContentLayout;
class QSplitter;
class GenericNetCamView;
namespace Ui {
class MainWindow;
}
class GenericChatForm : public QWidget class GenericChatForm : public QWidget
{ {
@ -67,6 +73,7 @@ signals:
void sendMessage(uint32_t, QString); void sendMessage(uint32_t, QString);
void sendAction(uint32_t, QString); void sendAction(uint32_t, QString);
void chatAreaCleared(); void chatAreaCleared();
void messageInserted();
public slots: public slots:
void focusInput(); void focusInput();
@ -82,11 +89,16 @@ protected slots:
void onSelectAllClicked(); void onSelectAllClicked();
void showFileMenu(); void showFileMenu();
void hideFileMenu(); void hideFileMenu();
void onShowMessagesClicked();
void onSplitterMoved(int pos, int index);
private: private:
void retranslateUi(); void retranslateUi();
protected: protected:
void showNetcam();
void hideNetcam();
virtual GenericNetCamView* createNetcam() = 0;
QString resolveToxId(const ToxId &id); QString resolveToxId(const ToxId &id);
void insertChatMessage(ChatMessage::Ptr msg); void insertChatMessage(ChatMessage::Ptr msg);
void adjustFileMenuPosition(); void adjustFileMenuPosition();
@ -116,6 +128,8 @@ protected:
QDateTime historyBaselineDate = QDateTime::currentDateTime(); // used by HistoryKeeper to load messages from t to historyBaselineDate (excluded) QDateTime historyBaselineDate = QDateTime::currentDateTime(); // used by HistoryKeeper to load messages from t to historyBaselineDate (excluded)
bool audioInputFlag; bool audioInputFlag;
bool audioOutputFlag; bool audioOutputFlag;
QSplitter* bodySplitter;
GenericNetCamView* netcam;
}; };
#endif // GENERICCHATFORM_H #endif // GENERICCHATFORM_H

View File

@ -29,6 +29,7 @@
#include "src/persistence/historykeeper.h" #include "src/persistence/historykeeper.h"
#include "src/widget/flowlayout.h" #include "src/widget/flowlayout.h"
#include "src/widget/translator.h" #include "src/widget/translator.h"
#include "src/video/groupnetcamview.h"
#include <QDebug> #include <QDebug>
#include <QTimer> #include <QTimer>
#include <QPushButton> #include <QPushButton>
@ -195,7 +196,14 @@ void GroupChatForm::onUserListChanged()
orderizer[names[i]] = peerLabels[i]; orderizer[names[i]] = peerLabels[i];
if (group->isSelfPeerNumber(i)) if (group->isSelfPeerNumber(i))
peerLabels[i]->setStyleSheet("QLabel {color : green;}"); peerLabels[i]->setStyleSheet("QLabel {color : green;}");
if (netcam && !group->isSelfPeerNumber(i))
static_cast<GroupNetCamView*>(netcam)->addPeer(i, names[i]);
} }
if (netcam)
static_cast<GroupNetCamView*>(netcam)->clearPeers();
// now alphabetize and add to layout // now alphabetize and add to layout
names.sort(Qt::CaseInsensitive); names.sort(Qt::CaseInsensitive);
for (unsigned i=0; i<nNames; ++i) for (unsigned i=0; i<nNames; ++i)
@ -231,9 +239,24 @@ void GroupChatForm::peerAudioPlaying(int peer)
{ {
peerAudioTimers[peer] = new QTimer(this); peerAudioTimers[peer] = new QTimer(this);
peerAudioTimers[peer]->setSingleShot(true); peerAudioTimers[peer]->setSingleShot(true);
connect(peerAudioTimers[peer], &QTimer::timeout, [=]{this->peerLabels[peer]->setStyleSheet(""); connect(peerAudioTimers[peer], &QTimer::timeout, [this, peer]
delete this->peerAudioTimers[peer]; {
this->peerAudioTimers[peer] = nullptr;}); if (netcam)
static_cast<GroupNetCamView*>(netcam)->removePeer(peer);
if (peer >= peerLabels.size())
return;
peerLabels[peer]->setStyleSheet("");
delete peerAudioTimers[peer];
peerAudioTimers[peer] = nullptr;
});
if (netcam)
{
static_cast<GroupNetCamView*>(netcam)->removePeer(peer);
static_cast<GroupNetCamView*>(netcam)->addPeer(peer, group->getPeerList()[peer]);
}
} }
peerAudioTimers[peer]->start(500); peerAudioTimers[peer]->start(500);
} }
@ -312,6 +335,7 @@ void GroupChatForm::onCallClicked()
volButton->style()->polish(volButton); volButton->style()->polish(volButton);
volButton->setToolTip(tr("Mute call")); volButton->setToolTip(tr("Mute call"));
inCall = true; inCall = true;
showNetcam();
} }
else else
{ {
@ -328,9 +352,24 @@ void GroupChatForm::onCallClicked()
volButton->style()->polish(volButton); volButton->style()->polish(volButton);
volButton->setToolTip(""); volButton->setToolTip("");
inCall = false; inCall = false;
hideNetcam();
} }
} }
GenericNetCamView *GroupChatForm::createNetcam()
{
GroupNetCamView* view = new GroupNetCamView(group->getGroupId(), this);
QStringList names = group->getPeerList();
for (unsigned i=0; i<names.size(); ++i)
{
if (!group->isSelfPeerNumber(i))
static_cast<GroupNetCamView*>(view)->addPeer(i, names[i]);
}
return view;
}
void GroupChatForm::keyPressEvent(QKeyEvent* ev) void GroupChatForm::keyPressEvent(QKeyEvent* ev)
{ {
// Push to talk (CTRL+P) // Push to talk (CTRL+P)

View File

@ -49,6 +49,7 @@ private slots:
void onCallClicked(); void onCallClicked();
protected: protected:
virtual GenericNetCamView* createNetcam() final override;
virtual void keyPressEvent(QKeyEvent* ev) final override; virtual void keyPressEvent(QKeyEvent* ev) final override;
virtual void keyReleaseEvent(QKeyEvent* ev) final override; virtual void keyReleaseEvent(QKeyEvent* ev) final override;
// drag & drop // drag & drop

View File

@ -102,6 +102,7 @@ void AVForm::on_videoModescomboBox_currentIndexChanged(int index)
QString devName = videoDeviceList[devIndex].first; QString devName = videoDeviceList[devIndex].first;
VideoMode mode = videoModes[index]; VideoMode mode = videoModes[index];
Settings::getInstance().setCamVideoRes(QSize(mode.width, mode.height)); Settings::getInstance().setCamVideoRes(QSize(mode.width, mode.height));
Settings::getInstance().setCamVideoFPS(mode.FPS);
camera.open(devName, mode); camera.open(devName, mode);
} }
@ -123,10 +124,11 @@ void AVForm::updateVideoModes(int curIndex)
bodyUI->videoModescomboBox->clear(); bodyUI->videoModescomboBox->clear();
int prefResIndex = -1; int prefResIndex = -1;
QSize prefRes = Settings::getInstance().getCamVideoRes(); QSize prefRes = Settings::getInstance().getCamVideoRes();
unsigned short prefFPS = Settings::getInstance().getCamVideoFPS();
for (int i=0; i<videoModes.size(); ++i) for (int i=0; i<videoModes.size(); ++i)
{ {
VideoMode mode = videoModes[i]; VideoMode mode = videoModes[i];
if (mode.width==prefRes.width() && mode.height==prefRes.height() && prefResIndex==-1) if (mode.width==prefRes.width() && mode.height==prefRes.height() && mode.FPS == prefFPS && prefResIndex==-1)
prefResIndex = i; prefResIndex = i;
QString str; QString str;
if (mode.height && mode.width) if (mode.height && mode.width)
@ -176,6 +178,14 @@ void AVForm::updateVideoModes(int curIndex)
break; break;
} }
} }
if (videoModes.size())
{
bodyUI->videoModescomboBox->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); updateVideoModes(index);
bodyUI->videoModescomboBox->blockSignals(previouslyBlocked); bodyUI->videoModescomboBox->blockSignals(previouslyBlocked);
camera.open(dev); camera.open(dev);
killVideoSurface();
createVideoSurface();
} }
void AVForm::onResProbingFinished(QList<QSize> res) /*void AVForm::onResProbingFinished(QList<QSize> res)
{ {
QSize savedRes = Settings::getInstance().getCamVideoRes(); QSize savedRes = Settings::getInstance().getCamVideoRes();
int savedResIndex = -1; int savedResIndex = -1;
@ -215,7 +227,7 @@ void AVForm::onResProbingFinished(QList<QSize> res)
bodyUI->videoModescomboBox->setCurrentIndex(savedResIndex); bodyUI->videoModescomboBox->setCurrentIndex(savedResIndex);
else else
bodyUI->videoModescomboBox->setCurrentIndex(bodyUI->videoModescomboBox->count()-1); bodyUI->videoModescomboBox->setCurrentIndex(bodyUI->videoModescomboBox->count()-1);
} }*/
void AVForm::hideEvent(QHideEvent *) void AVForm::hideEvent(QHideEvent *)
{ {
@ -242,7 +254,7 @@ void AVForm::getVideoDevices()
videoDevIndex = bodyUI->videoDevCombobox->count()-1; videoDevIndex = bodyUI->videoDevCombobox->count()-1;
} }
//addItem changes currentIndex -> reset //addItem changes currentIndex -> reset
bodyUI->videoDevCombobox->setCurrentIndex(-1); //bodyUI->videoDevCombobox->setCurrentIndex(-1);
bodyUI->videoDevCombobox->setCurrentIndex(videoDevIndex); bodyUI->videoDevCombobox->setCurrentIndex(videoDevIndex);
bodyUI->videoDevCombobox->blockSignals(false); bodyUI->videoDevCombobox->blockSignals(false);
updateVideoModes(videoDevIndex); updateVideoModes(videoDevIndex);
@ -361,7 +373,7 @@ void AVForm::createVideoSurface()
{ {
if (camVideoSurface) if (camVideoSurface)
return; return;
camVideoSurface = new VideoSurface(bodyUI->CamFrame); camVideoSurface = new VideoSurface(QPixmap(), bodyUI->CamFrame);
camVideoSurface->setObjectName(QStringLiteral("CamVideoSurface")); camVideoSurface->setObjectName(QStringLiteral("CamVideoSurface"));
camVideoSurface->setMinimumSize(QSize(160, 120)); camVideoSurface->setMinimumSize(QSize(160, 120));
camVideoSurface->setSource(&camera); camVideoSurface->setSource(&camera);

View File

@ -63,7 +63,7 @@ private slots:
// camera // camera
void onVideoDevChanged(int index); void onVideoDevChanged(int index);
void onResProbingFinished(QList<QSize> res); //void onResProbingFinished(QList<QSize> res);
virtual void hideEvent(QHideEvent*) final override; virtual void hideEvent(QHideEvent*) final override;
virtual void showEvent(QShowEvent*) final override; virtual void showEvent(QShowEvent*) final override;

View File

@ -116,8 +116,8 @@ void NotificationScrollArea::resizeEvent(QResizeEvent *event)
void NotificationScrollArea::findNextWidget() void NotificationScrollArea::findNextWidget()
{ {
GenericChatroomWidget* next = nullptr;
int value; int value;
GenericChatroomWidget* next = nullptr;
QHash<GenericChatroomWidget*, Visibility>::iterator i = trackedWidgets.begin(); QHash<GenericChatroomWidget*, Visibility>::iterator i = trackedWidgets.begin();
// Find the first next, to avoid nullptr. // Find the first next, to avoid nullptr.
@ -151,8 +151,8 @@ void NotificationScrollArea::findNextWidget()
void NotificationScrollArea::findPreviousWidget() void NotificationScrollArea::findPreviousWidget()
{ {
GenericChatroomWidget* next = nullptr;
int value; int value;
GenericChatroomWidget* next = nullptr;
QHash<GenericChatroomWidget*, Visibility>::iterator i = trackedWidgets.begin(); QHash<GenericChatroomWidget*, Visibility>::iterator i = trackedWidgets.begin();
// Find the first next, to avoid nullptr. // Find the first next, to avoid nullptr.

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "movablewidget.h"
#include <QMouseEvent>
#include <QGraphicsOpacityEffect>
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<float>(boundaryRect.width());
float changeY = newBoundary.height() / static_cast<float>(boundaryRect.height());
float percentageX = (x() - boundaryRect.x()) / static_cast<float>(boundaryRect.width() - width());
float percentageY = (y() - boundaryRect.y()) / static_cast<float>(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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef MOVABLEWIDGET_H
#define MOVABLEWIDGET_H
#include <QWidget>
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

View File

@ -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... 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);
} }
} }