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

@ -19,20 +19,37 @@
#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);
} }
} }