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

Merge remote-tracking branch 'origin/new_video_view'

This commit is contained in:
agilob 2015-10-18 20:55:01 +01:00
commit 7117083e86
No known key found for this signature in database
GPG Key ID: 296F0B764741106C
32 changed files with 1390 additions and 237 deletions

View File

@ -358,14 +358,13 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) {
src/widget/form/loadhistorydialog.h \
src/widget/form/setpassworddialog.h \
src/widget/form/tabcompleter.h \
src/widget/flowlayout.h \
src/ipc.h \
src/net/autoupdate.h \
src/widget/tool/callconfirmwidget.h \
src/widget/systemtrayicon.h \
src/widget/qrwidget.h \
src/widget/systemtrayicon_private.h \
src/widget/loginscreen.h
src/widget/loginscreen.h \
src/ipc.h
SOURCES += \
src/widget/form/addfriendform.cpp \
@ -385,7 +384,6 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) {
src/widget/form/setpassworddialog.cpp \
src/widget/form/tabcompleter.cpp \
src/widget/flowlayout.cpp \
src/ipc.cpp \
src/net/autoupdate.cpp \
src/widget/tool/callconfirmwidget.cpp \
src/widget/systemtrayicon.cpp \
@ -427,7 +425,8 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) {
src/persistence/offlinemsgengine.cpp \
src/widget/qrwidget.cpp \
src/widget/genericchatroomwidget.cpp \
src/widget/loginscreen.cpp
src/widget/loginscreen.cpp \
src/ipc.cpp
}
win32 {
@ -498,7 +497,10 @@ SOURCES += \
src/widget/tool/removefrienddialog.cpp \
src/widget/contentlayout.cpp \
src/widget/contentdialog.cpp \
src/widget/tool/activatedialog.cpp
src/widget/tool/activatedialog.cpp \
src/widget/tool/movablewidget.cpp \
src/video/genericnetcamview.cpp \
src/video/groupnetcamview.cpp
HEADERS += \
src/audio/audio.h \
@ -545,4 +547,7 @@ HEADERS += \
src/widget/contentlayout.h \
src/widget/contentdialog.h \
src/widget/tool/activatedialog.h \
src/widget/tool/removefrienddialog.h
src/widget/tool/removefrienddialog.h \
src/widget/tool/movablewidget.h \
src/video/genericnetcamview.h \
src/video/groupnetcamview.h

View File

@ -295,6 +295,13 @@ void Audio::playGroupAudio(int group, int peer, const int16_t* data,
alSourcef(call.alSources[peer], AL_GAIN, outputVolume);
}
qreal volume = 0.;
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 / bufsize);
playAudioBuffer(call.alSources[peer], data, samples, channels, sample_rate);
}

View File

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

View File

@ -78,6 +78,7 @@ void Core::prepareCall(uint32_t friendId, int32_t callId, ToxAv* toxav, bool vid
calls[callId].sendAudioTimer->setSingleShot(true);
connect(calls[callId].sendAudioTimer, &QTimer::timeout, [=](){sendCallAudio(callId,toxav);});
calls[callId].sendAudioTimer->start();
if (calls[callId].videoEnabled)
{
calls[callId].videoSource = new CoreVideoSource;
@ -237,6 +238,7 @@ void Core::cleanupCall(int32_t callId)
calls[callId].active = false;
disconnect(calls[callId].sendAudioTimer,0,0,0);
calls[callId].sendAudioTimer->stop();
if (calls[callId].videoEnabled)
{
CameraSource::getInstance().unsubscribe();

View File

@ -46,45 +46,21 @@ Group::~Group()
widget->deleteLater();
}
/*
void Group::addPeer(int peerId, QString name)
{
if (peers.contains(peerId))
qWarning() << "addPeer: peerId already used, overwriting anyway";
if (name.isEmpty())
peers[peerId] = "<Unknown>";
else
peers[peerId] = name;
nPeers++;
widget->onUserListChanged();
chatForm->onUserListChanged();
}
void Group::removePeer(int peerId)
{
peers.remove(peerId);
nPeers--;
widget->onUserListChanged();
chatForm->onUserListChanged();
}
*/
void Group::updatePeer(int peerId, QString name)
{
ToxId id = Core::getInstance()->getGroupPeerToxId(groupId, peerId);
QString toxid = id.publicKey;
peers[peerId] = name;
toxids[toxid] = name;
Friend *f = FriendList::findFriend(id);
if (f && f->hasAlias())
{
peers[peerId] = f->getDisplayedName();
toxids[toxid] = f->getDisplayedName();
}
widget->onUserListChanged();
chatForm->onUserListChanged();
emit userListChanged(getGroupWidget());
Friend *f = FriendList::findFriend(id);
if (f != nullptr && f->hasAlias())
{
widget->onUserListChanged();
chatForm->onUserListChanged();
emit userListChanged(getGroupWidget());
}
}
void Group::setName(const QString& name)

View File

@ -54,11 +54,6 @@ public:
void setMentionedFlag(int f);
int getMentionedFlag() const;
/*
void addPeer(int peerId, QString name);
void removePeer(int peerId);
*/
void updatePeer(int peerId, QString newName);
void setName(const QString& name);
QString getName() const;

View File

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

View File

@ -1087,8 +1087,8 @@ QSplitter:handle{
<rect>
<x>0</x>
<y>0</y>
<width>284</width>
<height>398</height>
<width>775</width>
<height>284</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5"/>

View File

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

View File

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

View File

@ -40,6 +40,7 @@ CameraSource::CameraSource()
biglock{false}, freelistLock{false},
isOpen{false}, subscriptions{0}
{
subscriptions = 0;
av_register_all();
avdevice_register_all();
}
@ -119,7 +120,7 @@ CameraSource::~CameraSource()
// Free all remaining VideoFrame
// Locking must be done precisely this way to avoid races
for (int i=0; i<freelist.size(); i++)
for (int i = 0; i < freelist.size(); i++)
{
std::shared_ptr<VideoFrame> vframe = freelist[i].lock();
if (!vframe)
@ -132,8 +133,9 @@ CameraSource::~CameraSource()
if (cctxOrig)
avcodec_close(cctxOrig);
for (int i=subscriptions; i; --i)
for(int i = 0; i < subscriptions; i++)
device->close();
device = nullptr;
// Memfence so the stream thread sees a nullptr device
std::atomic_thread_fence(std::memory_order_release);
@ -177,6 +179,7 @@ bool CameraSource::subscribe()
biglock = false;
return false;
}
}
void CameraSource::unsubscribe()
@ -190,7 +193,7 @@ void CameraSource::unsubscribe()
if (!isOpen)
{
--subscriptions;
subscriptions--;
biglock = false;
return;
}
@ -202,10 +205,9 @@ void CameraSource::unsubscribe()
return;
}
if (--subscriptions == 0)
if (subscriptions - 1 == 0)
{
closeDevice();
biglock = false;
// Synchronize with our stream thread
@ -217,12 +219,12 @@ void CameraSource::unsubscribe()
device->close();
biglock = false;
}
subscriptions--;
}
bool CameraSource::openDevice()
{
qDebug() << "Opening device "<<deviceName;
if (device)
{
device->open();
@ -243,15 +245,15 @@ bool CameraSource::openDevice()
// We need to open the device as many time as we already have subscribers,
// otherwise the device could get closed while we still have subscribers
for (int i=subscriptions; i>0; i--)
for (int i = 0; i < subscriptions; i++)
device->open();
// Find the first video stream
for (unsigned i=0; i<device->context->nb_streams; i++)
for (unsigned i = 0; i < device->context->nb_streams; i++)
{
if(device->context->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
if(device->context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStreamIndex=i;
videoStreamIndex = i;
break;
}
}
@ -259,8 +261,8 @@ bool CameraSource::openDevice()
return false;
// Get a pointer to the codec context for the video stream
cctxOrig=device->context->streams[videoStreamIndex]->codec;
codec=avcodec_find_decoder(cctxOrig->codec_id);
cctxOrig = device->context->streams[videoStreamIndex]->codec;
codec = avcodec_find_decoder(cctxOrig->codec_id);
if(!codec)
return false;
@ -268,6 +270,7 @@ bool CameraSource::openDevice()
cctx = avcodec_alloc_context3(codec);
if(avcodec_copy_context(cctx, cctxOrig) != 0)
return false;
cctx->refcounted_frames = 1;
// Open codec
@ -286,16 +289,16 @@ bool CameraSource::openDevice()
while (!streamFuture.isRunning())
QThread::yieldCurrentThread();
emit deviceOpened();
return true;
}
void CameraSource::closeDevice()
{
qDebug() << "Closing device "<<deviceName;
// Free all remaining VideoFrame
// Locking must be done precisely this way to avoid races
for (int i=0; i<freelist.size(); i++)
for (int i = 0; i < freelist.size(); i++)
{
std::shared_ptr<VideoFrame> vframe = freelist[i].lock();
if (!vframe)
@ -396,9 +399,10 @@ void CameraSource::freelistCallback(int freelistIndex)
int CameraSource::getFreelistSlotLockless()
{
int size = freelist.size();
for (int i=0; i<size; ++i)
for (int i = 0; i < size; ++i)
if (freelist[i].expired())
return i;
freelist.resize(size+(size>>1)+4); // Arbitrary growth strategy, should work well
freelist.resize(size + (size>>1) + 4); // Arbitrary growth strategy, should work well
return size;
}

View File

@ -45,6 +45,7 @@ struct AVCodecContext;
class CameraSource : public VideoSource
{
Q_OBJECT
public:
static CameraSource& getInstance();
static void destroyInstance();
@ -59,6 +60,9 @@ public:
virtual bool subscribe() override;
virtual void unsubscribe() override;
signals:
void deviceOpened();
private:
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,281 @@
/*
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)
{
qDebug() << "Created expanding? " << expanding;
videoSurface = new VideoSurface(avatar, 0, expanding);
videoSurface->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
videoSurface->setMinimumHeight(32);
connect(videoSurface, &VideoSurface::ratioChanged, this, &LabeledVideo::updateSize);
label = new CroppingLabel(this);
label->setTextFormat(Qt::PlainText);
label->setStyleSheet("color: white");
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);
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->setMinimumHeight(256);
videoSurface->setContentsMargins(6, 6, 6, 0);
videoLabelSurface->setContentsMargins(0, 0, 0, 0);
videoLabelSurface->layout()->setMargin(0);
videoLabelSurface->setStyleSheet("QFrame { background-color: black; }");
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);
horLayout->addStretch(1);
selfVideoSurface = new LabeledVideo(Settings::getInstance().getSavedAvatar(Core::getInstance()->getSelfId().toString()), this);
horLayout->addWidget(selfVideoSurface);
horLayout->addStretch(1);
splitter->addWidget(scrollArea);
scrollArea->setWidget(widget);
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.
@ -18,35 +18,97 @@
*/
#include "netcamview.h"
#include "camerasource.h"
#include "src/friend.h"
#include "src/friendlist.h"
#include "src/core/core.h"
#include "src/video/videosurface.h"
#include "src/widget/tool/movablewidget.h"
#include "src/persistence/settings.h"
#include <QLabel>
#include <QHBoxLayout>
#include <QBoxLayout>
#include <QFrame>
NetCamView::NetCamView(QWidget* parent)
: QWidget(parent)
, mainLayout(new QHBoxLayout())
NetCamView::NetCamView(int friendId, QWidget* parent)
: GenericNetCamView(parent)
, selfFrame{nullptr}
, friendId{friendId}
{
setLayout(mainLayout);
setWindowTitle(tr("Tox video"));
setMinimumSize(320,240);
QString id = FriendList::findFriend(friendId)->getToxId().toString();
videoSurface = new VideoSurface(Settings::getInstance().getSavedAvatar(id), this);
videoSurface->setMinimumHeight(256);
videoSurface->setContentsMargins(6, 6, 6, 6);
videoSurface = new VideoSurface(this);
verLayout->insertWidget(0, videoSurface, 1);
mainLayout->addWidget(videoSurface);
selfVideoSurface = new VideoSurface(Settings::getInstance().getSavedAvatar(Core::getInstance()->getSelfId().toString()), this, true);
selfVideoSurface->setObjectName(QStringLiteral("CamVideoSurface"));
selfVideoSurface->setMouseTracking(true);
selfVideoSurface->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
selfFrame = new MovableWidget(videoSurface);
selfFrame->show();
QHBoxLayout* frameLayout = new QHBoxLayout(selfFrame);
frameLayout->addWidget(selfVideoSurface);
frameLayout->setMargin(0);
updateRatio();
connect(selfVideoSurface, &VideoSurface::ratioChanged, this, &NetCamView::updateRatio);
connect(videoSurface, &VideoSurface::boundaryChanged, [this]()
{
QRect boundingRect = videoSurface->getBoundingRect();
updateFrameSize(boundingRect.size());
selfFrame->setBoundary(boundingRect);
});
connect(videoSurface, &VideoSurface::ratioChanged, [this]()
{
selfFrame->setMinimumWidth(selfFrame->minimumHeight() * selfVideoSurface->getRatio());
QRect boundingRect = videoSurface->getBoundingRect();
updateFrameSize(boundingRect.size());
selfFrame->resetBoundary(boundingRect);
});
connect(Core::getInstance(), &Core::selfAvatarChanged, [this](const QPixmap& pixmap)
{
selfVideoSurface->setAvatar(pixmap);
});
connect(Core::getInstance(), &Core::friendAvatarChanged, [this](int FriendId, const QPixmap& pixmap)
{
if (this->friendId == FriendId)
videoSurface->setAvatar(pixmap);
});
VideoMode videoMode;
QSize videoSize = Settings::getInstance().getCamVideoRes();
videoMode.width = videoSize.width();
videoMode.height = videoSize.height();
qDebug() << "SIZER" << videoSize;
videoMode.FPS = Settings::getInstance().getCamVideoFPS();
CameraSource::getInstance().open(Settings::getInstance().getVideoDev(), videoMode);
}
void NetCamView::show(VideoSource *source, const QString &title)
{
setSource(source);
setTitle(title);
selfVideoSurface->setSource(&CameraSource::getInstance());
setTitle(title);
QWidget::show();
}
void NetCamView::hide()
{
setSource(nullptr);
selfVideoSurface->setSource(nullptr);
if (selfFrame)
selfFrame->deleteLater();
selfFrame = nullptr;
QWidget::hide();
}
@ -60,3 +122,25 @@ void NetCamView::setTitle(const QString &title)
{
setWindowTitle(title);
}
void NetCamView::showEvent(QShowEvent *event)
{
Q_UNUSED(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.
@ -20,19 +20,20 @@
#ifndef NETCAMVIEW_H
#define NETCAMVIEW_H
#include <QWidget>
#include "genericnetcamview.h"
class QHBoxLayout;
struct vpx_image;
class VideoSurface;
class VideoSource;
class QFrame;
class MovableWidget;
class NetCamView : public QWidget
class NetCamView : public GenericNetCamView
{
Q_OBJECT
public:
NetCamView(QWidget *parent=0);
NetCamView(int friendId, QWidget *parent=0);
virtual void show(VideoSource* source, const QString& title);
virtual void hide();
@ -40,9 +41,19 @@ public:
void setSource(VideoSource* s);
void setTitle(const QString& title);
protected:
void showEvent(QShowEvent* event) final override;
private slots:
void updateRatio();
private:
QHBoxLayout* mainLayout;
VideoSurface* videoSurface;
void updateFrameSize(QSize size);
VideoSurface* selfVideoSurface;
MovableWidget* selfFrame;
int friendId;
bool e = false;
};
#endif // NETCAMVIEW_H

View File

@ -6,33 +6,49 @@
qTox is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
(at your option) any later version.
qTox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU General Public License
along with qTox. If not, see <http://www.gnu.org/licenses/>.
*/
#include "videosurface.h"
#include "src/video/videoframe.h"
#include "src/friend.h"
#include "src/friendlist.h"
#include "src/widget/friendwidget.h"
#include "src/persistence/settings.h"
#include "src/core/core.h"
#include "src/widget/style.h"
#include <QPainter>
#include <QLabel>
#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}
, source{nullptr}
, frameLock{false}
, hasSubscribed{false}
, hasSubscribed{0}
, avatar{avatar}
, ratio{1.0f}
, expanding{expanding}
{
recalulateBounds();
}
VideoSurface::VideoSurface(VideoSource *source, QWidget* parent)
: VideoSurface(parent)
VideoSurface::VideoSurface(const QPixmap& avatar, VideoSource *source, QWidget* parent)
: VideoSurface(avatar, parent)
{
setSource(source);
}
@ -42,6 +58,11 @@ VideoSurface::~VideoSurface()
unsubscribe();
}
bool VideoSurface::isExpanding() const
{
return expanding;
}
void VideoSurface::setSource(VideoSource *src)
{
if (source == src)
@ -52,70 +73,154 @@ void VideoSurface::setSource(VideoSource *src)
subscribe();
}
QRect VideoSurface::getBoundingRect() const
{
QRect bRect = boundingRect;
bRect.setBottomRight(QPoint(boundingRect.bottom() + 1, boundingRect.right() + 1));
return boundingRect;
}
float VideoSurface::getRatio() const
{
return ratio;
}
void VideoSurface::setAvatar(const QPixmap &pixmap)
{
avatar = pixmap;
update();
}
QPixmap VideoSurface::getAvatar() const
{
return avatar;
}
void VideoSurface::subscribe()
{
if (source && !hasSubscribed)
if (source && hasSubscribed++ == 0)
{
source->subscribe();
hasSubscribed = true;
connect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable);
}
}
void VideoSurface::unsubscribe()
{
if (!source || !hasSubscribed)
if (!source || hasSubscribed == 0)
return;
// Fast lock
{
bool expected = false;
while (!frameLock.compare_exchange_weak(expected, true))
expected = false;
}
if (--hasSubscribed != 0)
return;
lock();
lastFrame.reset();
frameLock = false;
unlock();
ratio = 1.0f;
recalulateBounds();
emit ratioChanged();
emit boundaryChanged();
source->unsubscribe();
hasSubscribed = false;
disconnect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable);
}
void VideoSurface::onNewFrameAvailable(std::shared_ptr<VideoFrame> newFrame)
{
// Fast lock
QSize newSize;
lock();
lastFrame = newFrame;
newSize = lastFrame->getSize();
unlock();
float newRatio = getSizeRatio(newSize);
if (newRatio != ratio && isVisible())
{
bool expected = false;
while (!frameLock.compare_exchange_weak(expected, true))
expected = false;
ratio = newRatio;
recalulateBounds();
emit ratioChanged();
emit boundaryChanged();
}
lastFrame = newFrame;
frameLock = false;
update();
}
void VideoSurface::paintEvent(QPaintEvent*)
{
// Fast lock
{
bool expected = false;
while (!frameLock.compare_exchange_weak(expected, true))
expected = false;
}
lock();
QPainter painter(this);
painter.fillRect(painter.viewport(), Qt::black);
if (lastFrame)
{
QSize frameSize = lastFrame->getSize();
QRect rect = painter.viewport();
int width = frameSize.width()*rect.height()/frameSize.height();
rect.setLeft((rect.width()-width)/2);
rect.setWidth(width);
QImage frame = lastFrame->toQImage(rect.size());
painter.drawImage(rect, frame, frame.rect(), Qt::NoFormatConversion);
QImage frame = lastFrame->toQImage(rect().size());
painter.drawImage(boundingRect, frame, frame.rect(), Qt::NoFormatConversion);
}
else
{
painter.fillRect(boundingRect, Qt::white);
QPixmap drawnAvatar = avatar;
if (drawnAvatar.isNull())
drawnAvatar = Style::scaleSvgImage(":/img/contact_dark.svg", boundingRect.width(), boundingRect.height());
painter.drawPixmap(boundingRect, drawnAvatar, drawnAvatar.rect());
}
unlock();
}
void VideoSurface::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
recalulateBounds();
emit boundaryChanged();
}
void VideoSurface::showEvent(QShowEvent* e)
{
Q_UNUSED(e);
//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;
}

View File

@ -30,26 +30,45 @@ class VideoSurface : public QWidget
Q_OBJECT
public:
VideoSurface(QWidget* parent=0);
VideoSurface(VideoSource* source, QWidget* parent=0);
VideoSurface(const QPixmap& avatar, QWidget* parent = 0, bool expanding = false);
VideoSurface(const QPixmap& avatar, VideoSource* source, QWidget* parent = 0);
~VideoSurface();
bool isExpanding() const;
void setSource(VideoSource* src); //NULL is a valid option
QRect getBoundingRect() const;
float getRatio() const;
void setAvatar(const QPixmap& pixmap);
QPixmap getAvatar() const;
signals:
void ratioChanged();
void boundaryChanged();
protected:
void subscribe();
void unsubscribe();
virtual void paintEvent(QPaintEvent * event) final override;
virtual void paintEvent(QPaintEvent* event) final override;
virtual void resizeEvent(QResizeEvent* event) final override;
virtual void showEvent(QShowEvent* event) final override;
private slots:
void onNewFrameAvailable(std::shared_ptr<VideoFrame> newFrame);
private:
void recalulateBounds();
void lock();
void unlock();
QRect boundingRect;
VideoSource* source;
std::shared_ptr<VideoFrame> lastFrame;
std::atomic_bool frameLock; ///< Fast lock for lastFrame
bool hasSubscribed;
uint8_t hasSubscribed;
QPixmap avatar;
float ratio;
bool expanding;
};
#endif // SELFCAMVIEW_H

View File

@ -31,6 +31,7 @@
#include <QTemporaryFile>
#include <QGuiApplication>
#include <QStyle>
#include <QSplitter>
#include <cassert>
#include "chatform.h"
#include "src/core/core.h"
@ -41,7 +42,6 @@
#include "src/core/cstring.h"
#include "src/widget/tool/callconfirmwidget.h"
#include "src/widget/friendwidget.h"
#include "src/video/netcamview.h"
#include "src/widget/form/loadhistorydialog.h"
#include "src/widget/tool/chattextedit.h"
#include "src/widget/widget.h"
@ -52,10 +52,13 @@
#include "src/chatlog/chatlinecontentproxy.h"
#include "src/chatlog/content/text.h"
#include "src/chatlog/chatlog.h"
#include "src/video/netcamview.h"
#include "src/persistence/offlinemsgengine.h"
#include "src/widget/tool/screenshotgrabber.h"
#include "src/widget/tool/flyoutoverlaywidget.h"
#include "src/widget/translator.h"
#include "src/video/videosource.h"
#include "src/video/camerasource.h"
ChatForm::ChatForm(Friend* chatFriend)
: f(chatFriend)
@ -76,7 +79,6 @@ ChatForm::ChatForm(Friend* chatFriend)
typingTimer.setSingleShot(true);
netcam = nullptr;
callDurationTimer = nullptr;
disableCallButtonsTimer = nullptr;
@ -88,6 +90,9 @@ ChatForm::ChatForm(Friend* chatFriend)
headTextLayout->addWidget(callDuration, 1, Qt::AlignCenter);
callDuration->hide();
chatWidget->setMinimumHeight(160);
connect(this, &GenericChatForm::messageInserted, this, &ChatForm::onMessageInserted);
loadHistoryAction = menu.addAction(QString(), this, SLOT(onLoadHistory()));
connect(Core::getInstance(), &Core::fileSendStarted, this, &ChatForm::startFileSend);
@ -133,7 +138,6 @@ void ChatForm::setStatusMessage(QString newMessage)
void ChatForm::onSendTriggered()
{
SendMessageStr(msgEdit->toPlainText());
msgEdit->clear();
}
@ -251,7 +255,7 @@ void ChatForm::onAvInvite(uint32_t FriendId, int CallId, bool video)
if (FriendId != f->getFriendID())
return;
qDebug() << "onAvInvite";
qDebug() << "onAvInvite, callId: " << CallId;
callId = CallId;
callButton->disconnect();
@ -302,7 +306,7 @@ void ChatForm::onAvStart(uint32_t FriendId, int CallId, bool video)
if (FriendId != f->getFriendID())
return;
qDebug() << "onAvStart";
qDebug() << "onAvStart, callId: " << CallId;
audioInputFlag = true;
audioOutputFlag = true;
@ -329,6 +333,7 @@ void ChatForm::onAvStart(uint32_t FriendId, int CallId, bool video)
videoButton->setToolTip("");
connect(callButton, SIGNAL(clicked()),
this, SLOT(onHangupCallTriggered()));
hideNetcam();
}
callButton->style()->polish(callButton);
videoButton->style()->polish(videoButton);
@ -348,12 +353,12 @@ void ChatForm::onAvStart(uint32_t FriendId, int CallId, bool video)
startCounter();
}
void ChatForm::onAvCancel(uint32_t FriendId, int)
void ChatForm::onAvCancel(uint32_t FriendId, int CallId)
{
if (FriendId != f->getFriendID())
return;
qDebug() << "onAvCancel";
qDebug() << "onAvCancel, callId: " << CallId;
delete callConfirm;
callConfirm = nullptr;
@ -368,27 +373,12 @@ void ChatForm::onAvCancel(uint32_t FriendId, int)
QDateTime::currentDateTime());
}
void ChatForm::onAvEnd(uint32_t FriendId, int)
{
if (FriendId != f->getFriendID())
return;
qDebug() << "onAvEnd";
delete callConfirm;
callConfirm = nullptr;
enableCallButtons();
stopCounter();
hideNetcam();
}
void ChatForm::onAvRinging(uint32_t FriendId, int CallId, bool video)
{
if (FriendId != f->getFriendID())
return;
qDebug() << "onAvRinging";
qDebug() << "onAvRinging, callId: " << CallId;
callId = CallId;
callButton->disconnect();
@ -428,7 +418,7 @@ void ChatForm::onAvStarting(uint32_t FriendId, int CallId, bool video)
if (FriendId != f->getFriendID())
return;
qDebug() << "onAvStarting";
qDebug() << "onAvStarting, callId:" << CallId;
callId = CallId;
@ -460,28 +450,43 @@ void ChatForm::onAvStarting(uint32_t FriendId, int CallId, bool video)
startCounter();
}
void ChatForm::onAvEnding(uint32_t FriendId, int)
void ChatForm::onAvEnding(uint32_t FriendId, int CallId)
{
if (FriendId != f->getFriendID())
return;
qDebug() << "onAvEnding";
qDebug() << "onAvEnding, callId: " << CallId;
delete callConfirm;
callConfirm = nullptr;
enableCallButtons();
stopCounter();
CameraSource::getInstance().unsubscribe();
hideNetcam();
}
void ChatForm::onAvRequestTimeout(uint32_t FriendId, int)
void ChatForm::onAvEnd(uint32_t FriendId, int CallId)
{
if (FriendId != f->getFriendID())
return;
qDebug() << "onAvRequestTimeout";
qDebug() << "onAvEnd, callId: " << CallId;
delete callConfirm;
callConfirm = nullptr;
enableCallButtons();
stopCounter();
hideNetcam();
}
void ChatForm::onAvRequestTimeout(uint32_t FriendId, int CallId)
{
if (FriendId != f->getFriendID())
return;
qDebug() << "onAvRequestTimeout, callId: " << CallId;
delete callConfirm;
callConfirm = nullptr;
@ -532,7 +537,7 @@ void ChatForm::onAvMediaChange(uint32_t FriendId, int CallId, bool video)
if (FriendId != f->getFriendID() || CallId != callId)
return;
qDebug() << "onAvMediaChange";
qDebug() << "onAvMediaChange, callId: " << CallId;
if (video)
showNetcam();
@ -747,6 +752,14 @@ void ChatForm::onAvatarChange(uint32_t FriendId, const QPixmap &pic)
avatar->setPixmap(pic);
}
GenericNetCamView *ChatForm::createNetcam()
{
qDebug() << "creating netcam";
NetCamView* view = new NetCamView(f->getFriendID(), this);
view->show(Core::getInstance()->getVideoSourceFromCall(callId), f->getDisplayedName());
return view;
}
void ChatForm::dragEnterEvent(QDragEnterEvent *ev)
{
if (ev->mimeData()->hasUrls())
@ -943,6 +956,12 @@ void ChatForm::onLoadHistory()
}
}
void ChatForm::onMessageInserted()
{
if (netcam && bodySplitter->sizes()[1] == 0)
netcam->setShowMessages(true, true);
}
void ChatForm::startCounter()
{
if (!callDurationTimer)
@ -1075,22 +1094,6 @@ void ChatForm::SendMessageStr(QString msg)
}
}
void ChatForm::showNetcam()
{
if (!netcam)
netcam = new NetCamView();
netcam->show(Core::getInstance()->getVideoSourceFromCall(callId), f->getDisplayedName());
}
void ChatForm::hideNetcam()
{
if (!netcam)
return;
netcam->hide();
delete netcam;
netcam = nullptr;
}
void ChatForm::retranslateUi()
{
QString volObjectName = volButton->objectName();
@ -1106,4 +1109,7 @@ void ChatForm::retranslateUi()
micButton->setToolTip(tr("Mute microphone"));
else if (micObjectName == QStringLiteral("red"))
micButton->setToolTip(tr("Unmute microphone"));
if (netcam)
netcam->setShowMessages(chatWidget->isVisible());
}

View File

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

View File

@ -24,6 +24,7 @@
#include <QDebug>
#include <QShortcut>
#include <QKeyEvent>
#include <QSplitter>
#include "src/persistence/smileypack.h"
#include "src/widget/emoticonswidget.h"
@ -44,6 +45,7 @@
#include "src/widget/contentlayout.h"
#include "src/widget/tool/croppinglabel.h"
#include <QPushButton>
#include "src/video/genericnetcamview.h"
GenericChatForm::GenericChatForm(QWidget *parent)
: QWidget(parent, Qt::Window)
@ -73,7 +75,8 @@ GenericChatForm::GenericChatForm(QWidget *parent)
chatWidget = new ChatLog(this);
chatWidget->setBusyNotification(ChatMessage::createBusyNotification());
connect(&Settings::getInstance(), &Settings::emojiFontChanged, this, [this]() { chatWidget->forceRelayout(); });
connect(&Settings::getInstance(), &Settings::emojiFontChanged,
this, [this]() { chatWidget->forceRelayout(); });
msgEdit = new ChatTextEdit();
@ -127,8 +130,17 @@ GenericChatForm::GenericChatForm(QWidget *parent)
micButton->setStyleSheet(micButtonStylesheet);
setLayout(mainLayout);
mainLayout->addWidget(chatWidget);
mainLayout->addLayout(mainFootLayout);
bodySplitter = new QSplitter(Qt::Vertical, this);
connect(bodySplitter, &QSplitter::splitterMoved, this, &GenericChatForm::onSplitterMoved);
QWidget* contentWidget = new QWidget(this);
QVBoxLayout* contentLayout = new QVBoxLayout(contentWidget);
contentLayout->addWidget(chatWidget);
contentLayout->addLayout(mainFootLayout);
bodySplitter->addWidget(contentWidget);
mainLayout->addWidget(bodySplitter);
mainLayout->setMargin(0);
footButtonsSmall->addWidget(emoteButton);
@ -175,12 +187,16 @@ GenericChatForm::GenericChatForm(QWidget *parent)
menu.addActions(chatWidget->actions());
menu.addSeparator();
saveChatAction = menu.addAction(QIcon::fromTheme("document-save"), QString(), this, SLOT(onSaveLogClicked()));
clearAction = menu.addAction(QIcon::fromTheme("edit-clear"), QString(), this, SLOT(clearChatArea(bool)));
saveChatAction = menu.addAction(QIcon::fromTheme("document-save"),
QString(), this, SLOT(onSaveLogClicked()));
clearAction = menu.addAction(QIcon::fromTheme("edit-clear"),
QString(), this, SLOT(clearChatArea(bool)));
menu.addSeparator();
connect(emoteButton, &QPushButton::clicked, this, &GenericChatForm::onEmoteButtonClicked);
connect(chatWidget, &ChatLog::customContextMenuRequested, this, &GenericChatForm::onChatContextMenuRequested);
connect(emoteButton, &QPushButton::clicked,
this, &GenericChatForm::onEmoteButtonClicked);
connect(chatWidget, &ChatLog::customContextMenuRequested,
this, &GenericChatForm::onChatContextMenuRequested);
new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_L, this, SLOT(clearChatArea()));
@ -194,6 +210,8 @@ GenericChatForm::GenericChatForm(QWidget *parent)
retranslateUi();
Translator::registerHandler(std::bind(&GenericChatForm::retranslateUi, this), this);
netcam = nullptr;
}
GenericChatForm::~GenericChatForm()
@ -462,6 +480,7 @@ QString GenericChatForm::resolveToxId(const ToxId &id)
void GenericChatForm::insertChatMessage(ChatMessage::Ptr msg)
{
chatWidget->insertChatlineAtBottom(std::dynamic_pointer_cast<ChatLine>(msg));
emit messageInserted();
}
void GenericChatForm::hideEvent(QHideEvent* event)
@ -511,6 +530,25 @@ bool GenericChatForm::eventFilter(QObject* object, QEvent* event)
return false;
}
void GenericChatForm::onSplitterMoved(int, int)
{
if (netcam)
netcam->setShowMessages(bodySplitter->sizes()[1] == 0);
}
void GenericChatForm::onShowMessagesClicked()
{
if (netcam)
{
if (bodySplitter->sizes()[1] == 0)
bodySplitter->setSizes({1, 1});
else
bodySplitter->setSizes({1, 0});
onSplitterMoved(0, 0);
}
}
void GenericChatForm::retranslateUi()
{
QString callObjectName = callButton->objectName();
@ -537,3 +575,26 @@ void GenericChatForm::retranslateUi()
saveChatAction->setText(tr("Save chat log"));
clearAction->setText(tr("Clear displayed messages"));
}
void GenericChatForm::showNetcam()
{
if (!netcam)
netcam = createNetcam();
connect(netcam, &GenericNetCamView::showMessageClicked,
this, &GenericChatForm::onShowMessagesClicked);
bodySplitter->insertWidget(0, netcam);
bodySplitter->setCollapsible(0, false);
}
void GenericChatForm::hideNetcam()
{
if (!netcam)
return;
netcam->close();
netcam->hide();
delete netcam;
netcam = nullptr;
}

View File

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

View File

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

View File

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

View File

@ -58,6 +58,8 @@ AVForm::AVForm() :
connect(bodyUI->inDevCombobox, qcbxIndexChangedStr, this, &AVForm::onInDevChanged);
connect(bodyUI->outDevCombobox, qcbxIndexChangedStr, this, &AVForm::onOutDevChanged);
connect(bodyUI->videoDevCombobox, qcbxIndexChangedInt, this, &AVForm::onVideoDevChanged);
connect(bodyUI->videoModescomboBox, qcbxIndexChangedInt, this, &AVForm::onVideoModesIndexChanged);
connect(bodyUI->filterAudio, &QCheckBox::toggled, this, &AVForm::onFilterAudioToggled);
connect(bodyUI->rescanButton, &QPushButton::clicked, this, [=](){getAudioInDevices(); getAudioOutDevices();});
bodyUI->playbackSlider->setValue(100);
@ -74,6 +76,7 @@ AVForm::AVForm() :
AVForm::~AVForm()
{
killVideoSurface();
Translator::unregister(this);
delete bodyUI;
}
@ -86,7 +89,7 @@ void AVForm::showEvent(QShowEvent*)
getVideoDevices();
}
void AVForm::on_videoModescomboBox_currentIndexChanged(int index)
void AVForm::onVideoModesIndexChanged(int index)
{
if (index<0 || index>=videoModes.size())
{
@ -102,6 +105,7 @@ void AVForm::on_videoModescomboBox_currentIndexChanged(int index)
QString devName = videoDeviceList[devIndex].first;
VideoMode mode = videoModes[index];
Settings::getInstance().setCamVideoRes(QSize(mode.width, mode.height));
Settings::getInstance().setCamVideoFPS(mode.FPS);
camera.open(devName, mode);
}
@ -123,10 +127,11 @@ void AVForm::updateVideoModes(int curIndex)
bodyUI->videoModescomboBox->clear();
int prefResIndex = -1;
QSize prefRes = Settings::getInstance().getCamVideoRes();
unsigned short prefFPS = Settings::getInstance().getCamVideoFPS();
for (int i=0; i<videoModes.size(); ++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;
QString str;
if (mode.height && mode.width)
@ -176,6 +181,14 @@ void AVForm::updateVideoModes(int curIndex)
break;
}
}
if (videoModes.size())
{
bodyUI->videoModescomboBox->setUpdatesEnabled(false);
bodyUI->videoModescomboBox->setCurrentIndex(-1);
bodyUI->videoModescomboBox->setUpdatesEnabled(true);
bodyUI->videoModescomboBox->setCurrentIndex(0);
}
}
}
@ -192,29 +205,8 @@ void AVForm::onVideoDevChanged(int index)
updateVideoModes(index);
bodyUI->videoModescomboBox->blockSignals(previouslyBlocked);
camera.open(dev);
}
void AVForm::onResProbingFinished(QList<QSize> res)
{
QSize savedRes = Settings::getInstance().getCamVideoRes();
int savedResIndex = -1;
bodyUI->videoModescomboBox->clear();
bodyUI->videoModescomboBox->blockSignals(true);
for (int i=0; i<res.size(); ++i)
{
QSize& r = res[i];
bodyUI->videoModescomboBox->addItem(QString("%1x%2").arg(QString::number(r.width()),QString::number(r.height())), r);
if (r == savedRes)
savedResIndex = i;
}
//reset index, otherwise cameras with only one resolution won't get initialized
bodyUI->videoModescomboBox->setCurrentIndex(-1);
bodyUI->videoModescomboBox->blockSignals(false);
if (savedResIndex != -1)
bodyUI->videoModescomboBox->setCurrentIndex(savedResIndex);
else
bodyUI->videoModescomboBox->setCurrentIndex(bodyUI->videoModescomboBox->count()-1);
killVideoSurface();
createVideoSurface();
}
void AVForm::hideEvent(QHideEvent *)
@ -241,8 +233,6 @@ void AVForm::getVideoDevices()
if (device.first == settingsInDev)
videoDevIndex = bodyUI->videoDevCombobox->count()-1;
}
//addItem changes currentIndex -> reset
bodyUI->videoDevCombobox->setCurrentIndex(-1);
bodyUI->videoDevCombobox->setCurrentIndex(videoDevIndex);
bodyUI->videoDevCombobox->blockSignals(false);
updateVideoModes(videoDevIndex);
@ -254,37 +244,37 @@ void AVForm::getVideoDevices()
void AVForm::getAudioInDevices()
{
QString settingsInDev = Settings::getInstance().getInDev();
int inDevIndex = 0;
int inDevIndex = 0;
bodyUI->inDevCombobox->clear();
const ALchar *pDeviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
if (pDeviceList)
{
//prevent currentIndexChanged to be fired while adding items
bodyUI->inDevCombobox->blockSignals(true);
//prevent currentIndexChanged to be fired while adding items
bodyUI->inDevCombobox->blockSignals(true);
while (*pDeviceList)
{
int len = strlen(pDeviceList);
#ifdef Q_OS_WIN
QString inDev = QString::fromUtf8(pDeviceList,len);
#else
QString inDev = QString::fromLocal8Bit(pDeviceList,len);
QString inDev = QString::fromLocal8Bit(pDeviceList,len);
#endif
bodyUI->inDevCombobox->addItem(inDev);
if (settingsInDev == inDev)
inDevIndex = bodyUI->inDevCombobox->count()-1;
inDevIndex = bodyUI->inDevCombobox->count()-1;
pDeviceList += len+1;
}
//addItem changes currentIndex -> reset
bodyUI->inDevCombobox->setCurrentIndex(-1);
bodyUI->inDevCombobox->blockSignals(false);
//addItem changes currentIndex -> reset
bodyUI->inDevCombobox->setCurrentIndex(-1);
bodyUI->inDevCombobox->blockSignals(false);
}
bodyUI->inDevCombobox->setCurrentIndex(inDevIndex);
bodyUI->inDevCombobox->setCurrentIndex(inDevIndex);
}
void AVForm::getAudioOutDevices()
{
QString settingsOutDev = Settings::getInstance().getOutDev();
int outDevIndex = 0;
int outDevIndex = 0;
bodyUI->outDevCombobox->clear();
const ALchar *pDeviceList;
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
@ -293,15 +283,15 @@ void AVForm::getAudioOutDevices()
pDeviceList = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
if (pDeviceList)
{
//prevent currentIndexChanged to be fired while adding items
bodyUI->outDevCombobox->blockSignals(true);
//prevent currentIndexChanged to be fired while adding items
bodyUI->outDevCombobox->blockSignals(true);
while (*pDeviceList)
{
int len = strlen(pDeviceList);
#ifdef Q_OS_WIN
QString outDev = QString::fromUtf8(pDeviceList,len);
#else
QString outDev = QString::fromLocal8Bit(pDeviceList,len);
QString outDev = QString::fromLocal8Bit(pDeviceList,len);
#endif
bodyUI->outDevCombobox->addItem(outDev);
if (settingsOutDev == outDev)
@ -310,11 +300,11 @@ void AVForm::getAudioOutDevices()
}
pDeviceList += len+1;
}
//addItem changes currentIndex -> reset
bodyUI->outDevCombobox->setCurrentIndex(-1);
bodyUI->outDevCombobox->blockSignals(false);
//addItem changes currentIndex -> reset
bodyUI->outDevCombobox->setCurrentIndex(-1);
bodyUI->outDevCombobox->blockSignals(false);
}
bodyUI->outDevCombobox->setCurrentIndex(outDevIndex);
bodyUI->outDevCombobox->setCurrentIndex(outDevIndex);
}
void AVForm::onInDevChanged(const QString &deviceDescriptor)
@ -361,7 +351,7 @@ void AVForm::createVideoSurface()
{
if (camVideoSurface)
return;
camVideoSurface = new VideoSurface(bodyUI->CamFrame);
camVideoSurface = new VideoSurface(QPixmap(), bodyUI->CamFrame);
camVideoSurface->setObjectName(QStringLiteral("CamVideoSurface"));
camVideoSurface->setMinimumSize(QSize(160, 120));
camVideoSurface->setSource(&camera);
@ -376,6 +366,7 @@ void AVForm::killVideoSurface()
while ((child = bodyUI->gridLayout->takeAt(0)) != 0)
delete child;
camVideoSurface->close();
delete camVideoSurface;
camVideoSurface = nullptr;
}

View File

@ -52,7 +52,6 @@ private:
void retranslateUi();
private slots:
void on_videoModescomboBox_currentIndexChanged(int index);
// audio
void onInDevChanged(const QString& deviceDescriptor);
@ -63,7 +62,7 @@ private slots:
// camera
void onVideoDevChanged(int index);
void onResProbingFinished(QList<QSize> res);
void onVideoModesIndexChanged(int index);
virtual void hideEvent(QHideEvent*) final override;
virtual void showEvent(QShowEvent*) final override;

View File

@ -116,8 +116,8 @@ void NotificationScrollArea::resizeEvent(QResizeEvent *event)
void NotificationScrollArea::findNextWidget()
{
GenericChatroomWidget* next = nullptr;
int value;
GenericChatroomWidget* next = nullptr;
QHash<GenericChatroomWidget*, Visibility>::iterator i = trackedWidgets.begin();
// Find the first next, to avoid nullptr.
@ -151,8 +151,8 @@ void NotificationScrollArea::findNextWidget()
void NotificationScrollArea::findPreviousWidget()
{
GenericChatroomWidget* next = nullptr;
int value;
GenericChatroomWidget* next = nullptr;
QHash<GenericChatroomWidget*, Visibility>::iterator i = trackedWidgets.begin();
// 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...
{
g->updatePeer(peernumber,Nexus::getCore()->getGroupPeerName(groupnumber, peernumber));
qDebug() << "UPDATING PEER";
g->updatePeer(peernumber, name);
}
}