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

Basic contact grouping

This commit is contained in:
Daniel Hrabovcak 2015-05-27 13:17:12 -04:00 committed by tux3
parent 1db80a3951
commit 631148cdae
10 changed files with 396 additions and 89 deletions

View File

@ -488,7 +488,8 @@ SOURCES += \
src/widget/translator.cpp \
src/persistence/settingsserializer.cpp \
src/widget/notificationscrollarea.cpp \
src/widget/notificationedgewidget.cpp
src/widget/notificationedgewidget.cpp \
src/widget/circlewidget.cpp
HEADERS += \
src/audio/audio.h \
@ -526,4 +527,5 @@ HEADERS += \
src/widget/translator.h \
src/persistence/settingsserializer.h \
src/widget/notificationscrollarea.h \
src/widget/notificationedgewidget.h
src/widget/notificationedgewidget.h \
src/widget/circlewidget.hpp

View File

@ -118,5 +118,6 @@
<file>ui/volButton/volButtonDisabled.png</file>
<file>img/login_logo.svg</file>
<file>ui/notificationEdge/notificationEdge.css</file>
<file>ui/chatroomWidgets/circleWidget.css</file>
</qresource>
</RCC>

173
src/widget/circlewidget.cpp Normal file
View File

@ -0,0 +1,173 @@
/*
This file is part of qTox, a Qt-based graphical interface for Tox.
This program 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.
This program 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 COPYING file for more details.
*/
#include "circlewidget.hpp"
#include "src/misc/style.h"
#include "src/misc/settings.h"
#include "src/friendlist.h"
#include "src/friend.h"
#include "src/widget/friendwidget.h"
#include <QVariant>
#include <QLabel>
#include <QBoxLayout>
#include <QDragEnterEvent>
#include <QMimeData>
#include <QDebug>
#include <cassert>
CircleWidget::CircleWidget(QWidget *parent)
: QFrame(parent)
{
setProperty("compact", Settings::getInstance().getCompactLayout());
setProperty("active", false);
setStyleSheet(Style::getStylesheet(":/ui/chatroomWidgets/circleWidget.css"));
QWidget *container = new QWidget(this);
container->setObjectName("circleWidgetContainer");
container->setProperty("active", false);
mainLayout = new QVBoxLayout(this);
groupLayout = new QVBoxLayout(this);
QHBoxLayout *layout = new QHBoxLayout();
QVBoxLayout *midLayout = new QVBoxLayout;
QHBoxLayout *topLayout = new QHBoxLayout;
this->layout()->setSpacing(0);
this->layout()->setMargin(0);
container->setFixedHeight(55);
setLayoutDirection(Qt::LeftToRight);
midLayout->addStretch();
arrowLabel = new QLabel(">", container);
arrowLabel->setPixmap(QPixmap(":/ui/chatArea/scrollBarRightArrow.svg"));
arrowLabel->setStyleSheet("color: white;");
topLayout->addWidget(arrowLabel);
topLayout->addSpacing(5);
QLabel *nameLabel = new QLabel("Circle", container);
nameLabel->setObjectName("name");
topLayout->addWidget(nameLabel);
QFrame *lineFrame = new QFrame(container);
lineFrame->setObjectName("line");
lineFrame->setFrameShape(QFrame::HLine);
topLayout->addSpacing(5);
topLayout->addWidget(lineFrame, 1);
midLayout->addLayout(topLayout);
midLayout->addStretch();
QHBoxLayout *statusLayout = new QHBoxLayout();
QLabel *onlineIconLabel = new QLabel(container);
onlineIconLabel->setAlignment(Qt::AlignCenter);
onlineIconLabel->setPixmap(QPixmap(":img/status/dot_online.svg"));
QLabel *onlineLabel = new QLabel("0", container);
onlineLabel->setObjectName("status");
QLabel *awayIconLabel = new QLabel(container);
awayIconLabel->setAlignment(Qt::AlignCenter);
awayIconLabel->setPixmap(QPixmap(":img/status/dot_away.svg"));
QLabel *awayLabel = new QLabel("0", container);
awayLabel->setObjectName("status");
QLabel *offlineIconLabel = new QLabel(container);
offlineIconLabel->setAlignment(Qt::AlignCenter);
offlineIconLabel->setPixmap(QPixmap(":img/status/dot_offline.svg"));
QLabel *offlineLabel = new QLabel("0", container);
offlineLabel->setObjectName("status");
statusLayout->addWidget(onlineIconLabel);
statusLayout->addSpacing(5);
statusLayout->addWidget(onlineLabel);
statusLayout->addSpacing(10);
statusLayout->addWidget(awayIconLabel);
statusLayout->addSpacing(5);
statusLayout->addWidget(awayLabel);
statusLayout->addSpacing(10);
statusLayout->addWidget(offlineIconLabel);
statusLayout->addSpacing(5);
statusLayout->addWidget(offlineLabel);
statusLayout->addStretch();
midLayout->addLayout(statusLayout);
midLayout->addStretch();
layout->addSpacing(10);
layout->addLayout(midLayout);
layout->addSpacing(10);
container->setLayout(layout);
mainLayout->addWidget(container);
setAcceptDrops(true);
}
bool CircleWidget::isCompact() const
{
return compact;
}
void CircleWidget::setCompact(bool compact)
{
this->compact = compact;
Style::repolish(this);
}
void CircleWidget::toggle()
{
visible = !visible;
if (visible)
{
mainLayout->addLayout(groupLayout);
arrowLabel->setPixmap(QPixmap(":/ui/chatArea/scrollBarDownArrow.svg"));
}
else
{
mainLayout->removeItem(groupLayout);
arrowLabel->setPixmap(QPixmap(":/ui/chatArea/scrollBarRightArrow.svg"));
}
}
void CircleWidget::mousePressEvent(QMouseEvent *event)
{
toggle();
}
void CircleWidget::dragEnterEvent(QDragEnterEvent *event)
{
qDebug() << event->mimeData();
if (event->mimeData()->hasFormat("friend"))
event->acceptProposedAction();
}
void CircleWidget::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasFormat("friend"))
{
int friendId = event->mimeData()->data("friend").toInt();
Friend *f = FriendList::findFriend(friendId);
assert(f != nullptr);
FriendWidget *widget = f->getFriendWidget();
assert(widget != nullptr);
groupLayout->addWidget(widget);
}
}

View File

@ -0,0 +1,57 @@
/*
This file is part of qTox, a Qt-based graphical interface for Tox.
This program 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.
This program 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 COPYING file for more details.
*/
#ifndef CIRCLEWIDGET_H
#define CIRCLEWIDGET_H
#include <QFrame>
class QVBoxLayout;
class QHBoxLayout;
class QLabel;
class CircleWidget : public QFrame
{
Q_OBJECT
public:
CircleWidget(QWidget *parent = 0);
bool isCompact() const;
void setCompact(bool compact);
void toggle();
Q_PROPERTY(bool compact READ isCompact WRITE setCompact)
protected:
void mousePressEvent(QMouseEvent *event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
private:
enum FriendLayoutType
{
Online = 0,
Offline = 1
};
bool compact, visible = false;
QVBoxLayout *friendLayouts[2];
QVBoxLayout *groupLayout;
QVBoxLayout *mainLayout;
QLabel *arrowLabel;
};
#endif // CIRCLEWIDGET_H

View File

@ -22,11 +22,14 @@
#include "src/friend.h"
#include "src/friendlist.h"
#include "src/widget/friendwidget.h"
#include "groupwidget.h"
#include "circlewidget.hpp"
#include <cassert>
FriendListWidget::FriendListWidget(QWidget *parent, bool groupchatPosition) :
QWidget(parent)
{
mainLayout = new QGridLayout();
mainLayout = new QVBoxLayout();
setLayout(mainLayout);
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
layout()->setSpacing(0);
@ -36,57 +39,102 @@ FriendListWidget::FriendListWidget(QWidget *parent, bool groupchatPosition) :
groupLayout->setSpacing(0);
groupLayout->setMargin(0);
for (Status s : {Status::Online, Status::Offline})
{
QVBoxLayout *l = new QVBoxLayout();
l->setSpacing(0);
l->setMargin(0);
friendLayouts[Online] = new QVBoxLayout();
friendLayouts[Online]->setSpacing(0);
friendLayouts[Online]->setMargin(0);
layouts[static_cast<int>(s)] = l;
}
friendLayouts[Offline] = new QVBoxLayout();
friendLayouts[Offline]->setSpacing(0);
friendLayouts[Offline]->setMargin(0);
circleLayout = new QVBoxLayout();
circleLayout->setSpacing(0);
circleLayout->setMargin(0);
if (groupchatPosition)
{
mainLayout->addLayout(groupLayout, 0, 0);
mainLayout->addLayout(layouts[static_cast<int>(Status::Online)], 1, 0);
mainLayout->addLayout(layouts[static_cast<int>(Status::Offline)], 2, 0);
mainLayout->addLayout(groupLayout);
mainLayout->addLayout(friendLayouts[Online]);
mainLayout->addLayout(friendLayouts[Offline]);
}
else
{
mainLayout->addLayout(layouts[static_cast<int>(Status::Online)], 0, 0);
mainLayout->addLayout(groupLayout, 1, 0);
mainLayout->addLayout(layouts[static_cast<int>(Status::Offline)], 2, 0);
mainLayout->addLayout(friendLayouts[Online]);
mainLayout->addLayout(groupLayout);
mainLayout->addLayout(friendLayouts[Offline]);
}
mainLayout->addLayout(circleLayout);
}
void FriendListWidget::addGroupWidget(GroupWidget *widget)
{
groupLayout->addWidget(widget);
}
void FriendListWidget::hideGroups(QString searchString, bool hideAll)
{
QVBoxLayout* groups = groupLayout;
int groupCount = groups->count(), index;
for (index = 0; index<groupCount; index++)
{
GroupWidget* groupWidget = static_cast<GroupWidget*>(groups->itemAt(index)->widget());
QString groupName = groupWidget->getName();
if (!groupName.contains(searchString, Qt::CaseInsensitive) | hideAll)
groupWidget->setVisible(false);
else
groupWidget->setVisible(true);
}
}
QVBoxLayout* FriendListWidget::getGroupLayout()
void FriendListWidget::addCircleWidget(CircleWidget *widget)
{
return groupLayout;
circleLayout->addWidget(widget);
}
void FriendListWidget::hideFriends(QString searchString, Status status, bool hideAll)
{
QVBoxLayout* friends = getFriendLayout(status);
int friendCount = friends->count(), index;
for (index = 0; index<friendCount; index++)
{
FriendWidget* friendWidget = static_cast<FriendWidget*>(friends->itemAt(index)->widget());
QString friendName = friendWidget->getName();
if (!friendName.contains(searchString, Qt::CaseInsensitive) | hideAll)
friendWidget->setVisible(false);
else
friendWidget->setVisible(true);
}
}
QVBoxLayout* FriendListWidget::getFriendLayout(Status s)
{
auto res = layouts.find(static_cast<int>(s));
if (res != layouts.end())
return res.value();
return layouts[static_cast<int>(Status::Online)];
if (s == Status::Offline)
{
return friendLayouts[Offline];
}
return friendLayouts[Online];
}
void FriendListWidget::onGroupchatPositionChanged(bool top)
{
mainLayout->removeItem(circleLayout);
mainLayout->removeItem(groupLayout);
mainLayout->removeItem(getFriendLayout(Status::Online));
if (top)
{
mainLayout->addLayout(groupLayout, 0, 0);
mainLayout->addLayout(layouts[static_cast<int>(Status::Online)], 1, 0);
mainLayout->addLayout(groupLayout);
mainLayout->addLayout(friendLayouts[Online]);
}
else
{
mainLayout->addLayout(layouts[static_cast<int>(Status::Online)], 0, 0);
mainLayout->addLayout(groupLayout, 1, 0);
mainLayout->addLayout(friendLayouts[Online]);
mainLayout->addLayout(groupLayout);
}
mainLayout->addLayout(circleLayout);
}
QList<GenericChatroomWidget*> FriendListWidget::getAllFriends()
@ -118,20 +166,26 @@ QList<GenericChatroomWidget*> FriendListWidget::getAllFriends()
void FriendListWidget::moveWidget(FriendWidget *w, Status s)
{
QVBoxLayout* l = getFriendLayout(s);
l->removeWidget(w);
Friend* g = FriendList::findFriend(w->friendId);
for (int i = 0; i < l->count(); i++)
l->removeWidget(w); // In case the widget is already in this layout.
Friend* g = FriendList::findFriend(static_cast<FriendWidget*>(w)->friendId);
// Binary search.
int min = 0, max = l->count(), mid;
while (min < max)
{
FriendWidget* w1 = static_cast<FriendWidget*>(l->itemAt(i)->widget());
mid = (max - min) / 2 + min;
FriendWidget* w1 = static_cast<FriendWidget*>(l->itemAt(mid)->widget());
assert(w1 != nullptr);
Friend* f = FriendList::findFriend(w1->friendId);
if (f->getDisplayedName().localeAwareCompare(g->getDisplayedName()) > 0)
{
l->insertWidget(i,w);
return;
}
int compareValue = f->getDisplayedName().localeAwareCompare(g->getDisplayedName());
if (compareValue > 0)
max = mid;
else
min = mid + 1;
}
static_assert(std::is_same<decltype(w), FriendWidget*>(), "The layout must only contain FriendWidget*");
l->addWidget(w);
l->insertWidget(min, w);
}
// update widget after add/delete/hide/show

View File

@ -31,12 +31,18 @@ class QGridLayout;
class QPixmap;
struct FriendWidget;
class GroupWidget;
class CircleWidget;
class FriendListWidget : public QWidget
{
Q_OBJECT
public:
explicit FriendListWidget(QWidget *parent = 0, bool groupchatPosition = true);
QVBoxLayout* getGroupLayout();
void addGroupWidget(GroupWidget *widget);
void hideGroups(QString searchString, bool hideAll = false);
void addCircleWidget(CircleWidget *widget);
void hideFriends(QString searchString, Status status, bool hideAll = false);
QVBoxLayout* getFriendLayout(Status s);
QList<GenericChatroomWidget*> getAllFriends();
@ -48,9 +54,16 @@ public slots:
void moveWidget(FriendWidget *w, Status s);
private:
QHash<int, QVBoxLayout*> layouts;
enum FriendLayoutType
{
Online = 0,
Offline = 1
};
QVBoxLayout *friendLayouts[2];
QVBoxLayout *groupLayout;
QGridLayout *mainLayout;
QVBoxLayout *circleLayout;
QVBoxLayout *mainLayout;
QList<CircleWidget*> circles;
};
#endif // FRIENDLISTWIDGET_H

View File

@ -21,8 +21,7 @@
#define GENERICCHATROOMWIDGET_H
#include <QFrame>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QBoxLayout>
#include <QLabel>
class CroppingLabel;

View File

@ -68,6 +68,8 @@
#include <QProcess>
#include <tox/tox.h>
#include "circlewidget.hpp"
#ifdef Q_OS_ANDROID
#define IS_ON_DESKTOP_GUI 0
#else
@ -155,6 +157,7 @@ void Widget::init()
contactListWidget = new FriendListWidget(0, Settings::getInstance().getGroupchatPosition());
ui->friendList->setWidget(contactListWidget);
ui->friendList->setLayoutDirection(Qt::RightToLeft);
ui->friendList->setContextMenuPolicy(Qt::CustomContextMenu);
ui->statusLabel->setEditable(true);
@ -204,6 +207,7 @@ void Widget::init()
connect(offlineMsgTimer, &QTimer::timeout, this, &Widget::processOfflineMsgs);
connect(ui->searchContactText, &QLineEdit::textChanged, this, &Widget::searchContacts);
connect(ui->searchContactFilterCBox, &QComboBox::currentTextChanged, this, &Widget::searchContacts);
connect(ui->friendList, &QWidget::customContextMenuRequested, this, &Widget::friendListContextMenu);
// keyboard shortcuts
new QShortcut(Qt::CTRL + Qt::Key_Q, this, SLOT(close()));
@ -1033,8 +1037,9 @@ Group *Widget::createGroup(int groupId)
QString groupName = QString("Groupchat #%1").arg(groupId);
Group* newgroup = GroupList::addGroup(groupId, groupName, core->isGroupAvEnabled(groupId));
QLayout* layout = contactListWidget->getGroupLayout();
layout->addWidget(newgroup->getGroupWidget());
//QLayout* layout = contactListWidget->getGroupLayout();
//layout->addWidget(newgroup->getGroupWidget());
contactListWidget->addGroupWidget(newgroup->getGroupWidget());
newgroup->getGroupWidget()->updateStatusLight();
connect(settingsWidget, &SettingsWidget::compactToggled, newgroup->getGroupWidget(), &GenericChatroomWidget::setCompact);
@ -1383,34 +1388,34 @@ void Widget::searchContacts()
switch(filter)
{
case FilterCriteria::All:
hideFriends(searchString, Status::Online);
hideFriends(searchString, Status::Offline);
contactListWidget->hideFriends(searchString, Status::Online);
contactListWidget->hideFriends(searchString, Status::Offline);
hideGroups(searchString);
contactListWidget->hideGroups(searchString);
break;
case FilterCriteria::Online:
hideFriends(searchString, Status::Online);
hideFriends(QString(), Status::Offline, true);
contactListWidget->hideFriends(searchString, Status::Online);
contactListWidget->hideFriends(QString(), Status::Offline, true);
hideGroups(searchString);
contactListWidget->hideGroups(searchString);
break;
case FilterCriteria::Offline:
hideFriends(QString(), Status::Online, true);
hideFriends(searchString, Status::Offline);
contactListWidget->hideFriends(QString(), Status::Online, true);
contactListWidget->hideFriends(searchString, Status::Offline);
hideGroups(QString(), true);
contactListWidget->hideGroups(QString(), true);
break;
case FilterCriteria::Friends:
hideFriends(searchString, Status::Online);
hideFriends(searchString, Status::Offline);
contactListWidget->hideFriends(searchString, Status::Online);
contactListWidget->hideFriends(searchString, Status::Offline);
hideGroups(QString(), true);
contactListWidget->hideGroups(QString(), true);
break;
case FilterCriteria::Groups:
hideFriends(QString(), Status::Online, true);
hideFriends(QString(), Status::Offline, true);
contactListWidget->hideFriends(QString(), Status::Online, true);
contactListWidget->hideFriends(QString(), Status::Offline, true);
hideGroups(searchString);
contactListWidget->hideGroups(searchString);
break;
default:
return;
@ -1419,37 +1424,15 @@ void Widget::searchContacts()
contactListWidget->reDraw();
}
void Widget::hideFriends(QString searchString, Status status, bool hideAll)
void Widget::friendListContextMenu(const QPoint &pos)
{
QVBoxLayout* friends = contactListWidget->getFriendLayout(status);
int friendCount = friends->count(), index;
QMenu menu(this);
QAction *addCircleAction = menu.addAction(tr("Add new circle..."));
QAction *chosenAction = menu.exec(ui->friendList->mapToGlobal(pos));
for (index = 0; index<friendCount; index++)
if (chosenAction == addCircleAction)
{
FriendWidget* friendWidget = static_cast<FriendWidget*>(friends->itemAt(index)->widget());
QString friendName = friendWidget->getName();
if (!friendName.contains(searchString, Qt::CaseInsensitive) || hideAll)
friendWidget->setVisible(false);
else
friendWidget->setVisible(true);
}
}
void Widget::hideGroups(QString searchString, bool hideAll)
{
QVBoxLayout* groups = contactListWidget->getGroupLayout();
int groupCount = groups->count(), index;
for (index = 0; index<groupCount; index++)
{
GroupWidget* groupWidget = static_cast<GroupWidget*>(groups->itemAt(index)->widget());
QString groupName = groupWidget->getName();
if (!groupName.contains(searchString, Qt::CaseInsensitive) || hideAll)
groupWidget->setVisible(false);
else
groupWidget->setVisible(true);
contactListWidget->addCircleWidget(new CircleWidget);
}
}

View File

@ -148,8 +148,7 @@ private slots:
void onSplitterMoved(int pos, int index);
void processOfflineMsgs();
void searchContacts();
void hideFriends(QString searchString, Status status, bool hideAll = false);
void hideGroups(QString searchString, bool hideAll = false);
void friendListContextMenu(const QPoint &pos);
private:
enum ActiveToolMenuButton {

View File

@ -0,0 +1,26 @@
QWidget#circleWidgetContainer > QFrame#line
{
color: white;
}
QWidget#circleWidgetContainer
{
background-color: @themeMedium;
}
QWidget#circleWidgetContainer:hover
{
background-color: @themeLight;
}
QWidget#circleWidgetContainer > QLabel#status
{
font: @small;
color: @lightGrey;
}
QWidget#circleWidgetContainer > QLabel#name
{
font: @big;
color: @white;
}