Merge pull request #6347

bodwok (4):
      refactor(ui): separation of responsibility for sorting the contact list
      fix(build): include QVector type
      refactor(ui): code improvement
      refactor(ui): code improvement
reviewable/pr6360/r1
Anthony Bilinski 2021-06-30 09:35:10 -07:00
commit a24f99df0d
No known key found for this signature in database
GPG Key ID: 2AA8E0DA1B31FB3C
14 changed files with 634 additions and 375 deletions

View File

@ -277,6 +277,9 @@ set(${PROJECT_NAME}_SOURCES
src/model/dialogs/idialogs.h
src/model/exiftransform.cpp
src/model/exiftransform.h
src/model/friendlist/friendlistmanager.cpp
src/model/friendlist/friendlistmanager.h
src/model/friendlist/ifriendlistitem.h
src/model/friendmessagedispatcher.cpp
src/model/friendmessagedispatcher.h
src/model/friend.cpp

View File

@ -0,0 +1,215 @@
/*
Copyright © 2021 by The qTox Project Contributors
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 "friendlistmanager.h"
#include "src/widget/genericchatroomwidget.h"
FriendListManager::FriendListManager(QObject *parent) : QObject(parent)
{
}
QVector<FriendListManager::IFriendListItemPtr> FriendListManager::getItems() const
{
return items;
}
bool FriendListManager::needHideCircles() const
{
return hideCircles;
}
void FriendListManager::addFriendListItem(IFriendListItem *item)
{
if (item->isGroup()) {
items.push_back(IFriendListItemPtr(item, [](IFriendListItem* groupItem){
groupItem->getWidget()->deleteLater();}));
} else {
items.push_back(IFriendListItemPtr(item));
}
updatePositions();
emit itemsChanged();
}
void FriendListManager::removeFriendListItem(IFriendListItem *item)
{
removeAll(item);
updatePositions();
emit itemsChanged();
}
void FriendListManager::sortByName()
{
byName = true;
updatePositions();
}
void FriendListManager::sortByActivity()
{
byName = false;
updatePositions();
}
void FriendListManager::resetParents()
{
for (int i = 0; i < items.size(); ++i) {
IFriendListItem* itemTmp = items[i].get();
itemTmp->getWidget()->setParent(nullptr);
}
}
void FriendListManager::setFilter(const QString &searchString, bool hideOnline, bool hideOffline,
bool hideGroups)
{
if (filterParams.searchString == searchString && filterParams.hideOnline == hideOnline &&
filterParams.hideOffline == hideOffline && filterParams.hideGroups == hideGroups) {
return;
}
filterParams.searchString = searchString;
filterParams.hideOnline = hideOnline;
filterParams.hideOffline = hideOffline;
filterParams.hideGroups = hideGroups;
emit itemsChanged();
}
void FriendListManager::applyFilter()
{
QString searchString = filterParams.searchString;
for (IFriendListItemPtr itemTmp : items) {
if (searchString.isEmpty()) {
itemTmp->getWidget()->setVisible(true);
} else {
QString tmp_name = itemTmp->getNameItem();
itemTmp->getWidget()->setVisible(tmp_name.contains(searchString, Qt::CaseInsensitive));
}
if (filterParams.hideOnline && itemTmp->isOnline()) {
if (itemTmp->isFriend()) {
itemTmp->getWidget()->setVisible(false);
}
}
if (filterParams.hideOffline && !itemTmp->isOnline()) {
itemTmp->getWidget()->setVisible(false);
}
if (filterParams.hideGroups && itemTmp->isGroup()) {
itemTmp->getWidget()->setVisible(false);
}
}
if (filterParams.hideOnline && filterParams.hideOffline) {
hideCircles = true;
} else {
hideCircles = false;
}
}
void FriendListManager::updatePositions()
{
if (byName) {
std::sort(items.begin(), items.end(),
[&](const IFriendListItemPtr &a, const IFriendListItemPtr &b) {
return cmpByName(a, b, groupsOnTop);
}
);
} else {
std::sort(items.begin(), items.end(),
[&](const IFriendListItemPtr &a, const IFriendListItemPtr &b) {
return cmpByActivity(a, b);
}
);
}
}
void FriendListManager::setGroupsOnTop(bool v)
{
groupsOnTop = v;
}
void FriendListManager::removeAll(IFriendListItem* item)
{
for (int i = 0; i < items.size(); ++i) {
if (items[i].get() == item) {
items.remove(i);
--i;
}
}
}
bool FriendListManager::cmpByName(const IFriendListItemPtr &a, const IFriendListItemPtr &b,
bool groupsOnTop)
{
if (a->isGroup() && !b->isGroup()) {
if (groupsOnTop) {
return true;
}
return !b->isOnline();
}
if (!a->isGroup() && b->isGroup()) {
if (groupsOnTop) {
return false;
}
return a->isOnline();
}
if (a->isOnline() && !b->isOnline()) {
return true;
}
if (!a->isOnline() && b->isOnline()) {
return false;
}
return a->getNameItem().toUpper() < b->getNameItem().toUpper();
}
bool FriendListManager::cmpByActivity(const IFriendListItemPtr &a, const IFriendListItemPtr &b)
{
if (a->isGroup() || b->isGroup()) {
if (a->isGroup() && !b->isGroup()) {
return true;
}
if (!a->isGroup() && b->isGroup()) {
return false;
}
return a->getNameItem().toUpper() < b->getNameItem().toUpper();
}
QDateTime dateA = a->getLastActivity();
QDateTime dateB = b->getLastActivity();
if (dateA.date() == dateB.date()) {
if (a->isOnline() && !b->isOnline()) {
return true;
}
if (!a->isOnline() && b->isOnline()) {
return false;
}
return a->getNameItem().toUpper() < b->getNameItem().toUpper();
}
return a->getLastActivity() > b->getLastActivity();
}

View File

@ -0,0 +1,72 @@
/*
Copyright © 2021 by The qTox Project Contributors
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/>.
*/
#pragma once
#include "ifriendlistitem.h"
#include <QObject>
#include <QVector>
#include <memory>
class FriendListManager : public QObject
{
Q_OBJECT
public:
using IFriendListItemPtr = std::shared_ptr<IFriendListItem>;
explicit FriendListManager(QObject *parent = nullptr);
QVector<IFriendListItemPtr> getItems() const;
bool needHideCircles() const;
void addFriendListItem(IFriendListItem* item);
void removeFriendListItem(IFriendListItem* item);
void sortByName();
void sortByActivity();
void resetParents();
void setFilter(const QString& searchString, bool hideOnline,
bool hideOffline, bool hideGroups);
void applyFilter();
void updatePositions();
void setGroupsOnTop(bool v);
signals:
void itemsChanged();
private:
struct FilterParams {
QString searchString = "";
bool hideOnline = false;
bool hideOffline = false;
bool hideGroups = false;
} filterParams;
void removeAll(IFriendListItem*);
bool cmpByName(const IFriendListItemPtr&, const IFriendListItemPtr&, bool groupsOnTop);
bool cmpByActivity(const IFriendListItemPtr&, const IFriendListItemPtr&);
bool byName = true;
bool hideCircles = false;
bool groupsOnTop;
QVector<IFriendListItemPtr> items;
};

View File

@ -0,0 +1,56 @@
/*
Copyright © 2021 by The qTox Project Contributors
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/>.
*/
#pragma once
#include <QDate>
class QWidget;
class IFriendListItem
{
public:
virtual ~IFriendListItem() = default;
virtual bool isFriend() const = 0;
virtual bool isGroup() const = 0;
virtual bool isOnline() const = 0;
virtual QString getNameItem() const = 0;
virtual QDateTime getLastActivity() const = 0;
virtual QWidget* getWidget() = 0;
virtual int getCircleId() const
{
return -1;
}
int getNameSortedPos() const
{
return nameSortedPos;
}
void setNameSortedPos(int pos)
{
nameSortedPos = pos;
}
private:
int nameSortedPos = -1;
};

View File

@ -87,7 +87,12 @@ void CategoryWidget::setExpanded(bool isExpanded, bool save)
}
expanded = isExpanded;
setMouseTracking(true);
// The listWidget will recieve a enterEvent for some reason if now visible.
// Using the following, we prevent that.
listWidget->setAttribute(Qt::WA_TransparentForMouseEvents, true);
listWidget->setVisible(isExpanded);
listWidget->setAttribute(Qt::WA_TransparentForMouseEvents, false);
QString pixmapPath;
if (isExpanded)
@ -95,11 +100,6 @@ void CategoryWidget::setExpanded(bool isExpanded, bool save)
else
pixmapPath = Style::getImagePath("chatArea/scrollBarRightArrow.svg");
statusPic.setPixmap(QPixmap(pixmapPath));
// The listWidget will recieve a enterEvent for some reason if now visible.
// Using the following, we prevent that.
QApplication::processEvents(QEventLoop::ExcludeSocketNotifiers);
container->hide();
container->show();
if (save)
onExpand();

View File

@ -187,7 +187,6 @@ void CircleWidget::dropEvent(QDropEvent* event)
if (circleWidget != nullptr) {
circleWidget->updateStatus();
emit searchCircle(*circleWidget);
}
setContainerAttribute(Qt::WA_UnderMouse, false);

View File

@ -36,7 +36,6 @@ public:
signals:
void renameRequested(CircleWidget* circleWidget, const QString& newName);
void searchCircle(CircleWidget& circletWidget);
void newContentDialog(ContentDialog& contentDialog);
protected:

View File

@ -19,7 +19,6 @@
#include "friendlistwidget.h"
#include "circlewidget.h"
#include "friendlistlayout.h"
#include "friendwidget.h"
#include "groupwidget.h"
#include "widget.h"
@ -27,6 +26,7 @@
#include "src/model/friend.h"
#include "src/model/group.h"
#include "src/model/status.h"
#include "src/model/friendlist/friendlistmanager.h"
#include "src/persistence/settings.h"
#include "src/widget/categorywidget.h"
@ -99,25 +99,19 @@ qint64 timeUntilTomorrow()
FriendListWidget::FriendListWidget(const Core &_core, Widget* parent, bool groupsOnTop)
: QWidget(parent)
, groupsOnTop(groupsOnTop)
, core{_core}
{
listLayout = new FriendListLayout();
manager = new FriendListManager(this);
manager->setGroupsOnTop(groupsOnTop);
connect(manager, &FriendListManager::itemsChanged, this, &FriendListWidget::itemsChanged);
listLayout = new QVBoxLayout;
setLayout(listLayout);
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
groupLayout.getLayout()->setSpacing(0);
groupLayout.getLayout()->setMargin(0);
// Prevent QLayout's add child warning before setting the mode.
listLayout->removeItem(listLayout->getLayoutOnline());
listLayout->removeItem(listLayout->getLayoutOffline());
listLayout->setSpacing(0);
listLayout->setMargin(0);
mode = Settings::getInstance().getFriendSortingMode();
sortByMode(mode);
if (mode != SortingMode::Name) {
listLayout->insertLayout(0, groupLayout.getLayout());
}
dayTimer = new QTimer(this);
dayTimer->setTimerType(Qt::VeryCoarseTimer);
@ -129,22 +123,9 @@ FriendListWidget::FriendListWidget(const Core &_core, Widget* parent, bool group
FriendListWidget::~FriendListWidget()
{
if (activityLayout != nullptr) {
QLayoutItem* item;
while ((item = activityLayout->takeAt(0)) != nullptr) {
delete item->widget();
delete item;
}
delete activityLayout;
}
if (circleLayout != nullptr) {
QLayoutItem* item;
while ((item = circleLayout->getLayout()->takeAt(0)) != nullptr) {
delete item->widget();
delete item;
}
delete circleLayout;
for (int i = 0; i < Settings::getInstance().getCircleCount(); ++i) {
CircleWidget* circle = CircleWidget::getFromID(i);
delete circle;
}
}
@ -161,47 +142,65 @@ void FriendListWidget::setMode(SortingMode mode)
void FriendListWidget::sortByMode(SortingMode mode)
{
cleanMainLayout();
if (mode == SortingMode::Name) {
circleLayout = new GenericChatItemLayout;
circleLayout->getLayout()->setSpacing(0);
circleLayout->getLayout()->setMargin(0);
manager->sortByName();
for (int i = 0; i < Settings::getInstance().getCircleCount(); ++i) {
addCircleWidget(i);
CircleWidget::getFromID(i)->setVisible(false);
}
// Only display circles once all created to avoid artifacts.
for (int i = 0; i < Settings::getInstance().getCircleCount(); ++i)
CircleWidget::getFromID(i)->setVisible(true);
QVector<std::shared_ptr<IFriendListItem>> itemsTmp = manager->getItems(); // Sorted items
QVector<IFriendListItem*> friendItems; // Items that are not included in the circle
int posByName = 0; // Needed for scroll contacts
// Linking a friend with a circle and setting scroll position
for (int i = 0; i < itemsTmp.size(); ++i) {
if (itemsTmp[i]->isFriend() && itemsTmp[i]->getCircleId() >= 0) {
CircleWidget* circleWgt = CircleWidget::getFromID(itemsTmp[i]->getCircleId());
if (circleWgt != nullptr) {
// Place a friend in the circle and continue
FriendWidget* frndTmp =
qobject_cast<FriendWidget*>((itemsTmp[i].get())->getWidget());
circleWgt->addFriendWidget(frndTmp, frndTmp->getFriend()->getStatus());
continue;
}
}
// Place the item without the circle in the vector and set the position
itemsTmp[i]->setNameSortedPos(posByName++);
friendItems.push_back(itemsTmp[i].get());
}
int count = activityLayout ? activityLayout->count() : 0;
for (int i = 0; i < count; i++) {
QWidget* widget = activityLayout->itemAt(i)->widget();
CategoryWidget* categoryWidget = qobject_cast<CategoryWidget*>(widget);
if (categoryWidget) {
categoryWidget->moveFriendWidgets(this);
} else {
qWarning() << "Unexpected widget";
// Add groups and friends without circles
for (int i = 0; i < friendItems.size(); ++i) {
listLayout->addWidget(friendItems[i]->getWidget());
}
// TODO: Try to remove
manager->applyFilter();
if (!manager->needHideCircles()) {
//Sorts circles alphabetically and adds them to the layout
QVector<CircleWidget*> circles;
for (int i = 0; i < Settings::getInstance().getCircleCount(); ++i) {
circles.push_back(CircleWidget::getFromID(i));
}
std::sort(circles.begin(), circles.end(),
[](CircleWidget* a, CircleWidget* b) {
return a->getName().toUpper() < b->getName().toUpper();
});
for (int i = 0; i < circles.size(); ++i) {
QVector<std::shared_ptr<IFriendListItem>> itemsInCircle = getItemsFromCircle(circles.at(i));
for (int i = 0; i < itemsInCircle.size(); ++i) {
itemsInCircle.at(i)->setNameSortedPos(posByName++);
}
listLayout->addWidget(circles.at(i));
}
}
listLayout->addLayout(listLayout->getLayoutOnline());
listLayout->addLayout(listLayout->getLayoutOffline());
listLayout->addLayout(circleLayout->getLayout());
onGroupchatPositionChanged(groupsOnTop);
if (activityLayout != nullptr) {
QLayoutItem* item;
while ((item = activityLayout->takeAt(0)) != nullptr) {
delete item->widget();
delete item;
}
delete activityLayout;
activityLayout = nullptr;
}
reDraw();
} else if (mode == SortingMode::Activity) {
QLocale ql(Settings::getInstance().getTranslation());
QDate today = QDate::currentDate();
@ -223,6 +222,13 @@ void FriendListWidget::sortByMode(SortingMode mode)
// clang-format on
#undef COMMENT
manager->sortByActivity();
QVector<std::shared_ptr<IFriendListItem>> itemsTmp = manager->getItems();
for (int i = 0; i < itemsTmp.size(); ++i) {
listLayout->addWidget(itemsTmp[i]->getWidget());
}
activityLayout = new QVBoxLayout();
bool compact = Settings::getInstance().getCompactLayout();
for (Time t : names.keys()) {
@ -231,53 +237,89 @@ void FriendListWidget::sortByMode(SortingMode mode)
activityLayout->addWidget(category);
}
moveFriends(listLayout->getLayoutOffline());
moveFriends(listLayout->getLayoutOnline());
if (circleLayout != nullptr) {
moveFriends(circleLayout->getLayout());
// TODO: Try to remove
manager->applyFilter();
// Insert widgets to CategoryWidget
for (int i = 0; i < itemsTmp.size(); ++i) {
if (itemsTmp[i]->isFriend()) {
int timeIndex = static_cast<int>(getTimeBucket(itemsTmp[i]->getLastActivity()));
QWidget* widget = activityLayout->itemAt(timeIndex)->widget();
CategoryWidget* categoryWidget = qobject_cast<CategoryWidget*>(widget);
FriendWidget* frnd = qobject_cast<FriendWidget*>((itemsTmp[i].get())->getWidget());
if (!isVisible() || (isVisible() && frnd->isVisible())) {
categoryWidget->addFriendWidget(frnd, frnd->getFriend()->getStatus());
}
}
}
//Hide empty categories
for (int i = 0; i < activityLayout->count(); ++i) {
QWidget* widget = activityLayout->itemAt(i)->widget();
CategoryWidget* categoryWidget = qobject_cast<CategoryWidget*>(widget);
categoryWidget->setVisible(categoryWidget->hasChatrooms());
}
listLayout->removeItem(listLayout->getLayoutOnline());
listLayout->removeItem(listLayout->getLayoutOffline());
if (circleLayout != nullptr) {
listLayout->removeItem(circleLayout->getLayout());
QLayoutItem* item;
while ((item = circleLayout->getLayout()->takeAt(0)) != nullptr) {
delete item->widget();
delete item;
}
delete circleLayout;
circleLayout = nullptr;
}
listLayout->insertLayout(1, activityLayout);
reDraw();
listLayout->addLayout(activityLayout);
}
}
void FriendListWidget::moveFriends(QLayout* layout)
/**
* @brief Clears the listLayout by performing the creation and ownership inverse of sortByMode.
*/
void FriendListWidget::cleanMainLayout()
{
while (!layout->isEmpty()) {
QWidget* widget = layout->itemAt(0)->widget();
FriendWidget* friendWidget = qobject_cast<FriendWidget*>(widget);
CircleWidget* circleWidget = qobject_cast<CircleWidget*>(widget);
if (circleWidget) {
circleWidget->moveFriendWidgets(this);
} else if (friendWidget) {
const Friend* contact = friendWidget->getFriend();
auto* categoryWidget = getTimeCategoryWidget(contact);
categoryWidget->addFriendWidget(friendWidget, contact->getStatus());
manager->resetParents();
QLayoutItem* itemForDel;
while ((itemForDel = listLayout->takeAt(0)) != nullptr) {
listLayout->removeWidget(itemForDel->widget());
QWidget* wgt = itemForDel->widget();
if (wgt != nullptr) {
wgt->setParent(nullptr);
} else if (itemForDel->layout() != nullptr) {
QLayout* layout = itemForDel->layout();
QLayoutItem* itemTmp;
while ((itemTmp = layout->takeAt(0)) != nullptr) {
wgt = itemTmp->widget();
delete wgt;
delete itemTmp;
}
}
delete itemForDel;
}
}
QWidget* FriendListWidget::getNextWidgetForName(IFriendListItem *currentPos, bool forward) const
{
int pos = currentPos->getNameSortedPos();
int nextPos = forward ? pos + 1 : pos - 1;
if (nextPos >= manager->getItems().size()) {
nextPos = 0;
} else if (nextPos < 0) {
nextPos = manager->getItems().size() - 1;
}
for (int i = 0; i < manager->getItems().size(); ++i) {
if (manager->getItems().at(i)->getNameSortedPos() == nextPos) {
return manager->getItems().at(i)->getWidget();
}
}
return nullptr;
}
QVector<std::shared_ptr<IFriendListItem>>
FriendListWidget::getItemsFromCircle(CircleWidget *circle) const
{
QVector<std::shared_ptr<IFriendListItem>> itemsTmp = manager->getItems();
QVector<std::shared_ptr<IFriendListItem>> itemsInCircle;
for (int i = 0; i < itemsTmp.size(); ++i) {
int circleId = itemsTmp.at(i)->getCircleId();
if (CircleWidget::getFromID(circleId) == circle) {
itemsInCircle.push_back(itemsTmp.at(i));
}
}
return itemsInCircle;
}
CategoryWidget* FriendListWidget::getTimeCategoryWidget(const Friend* frd) const
@ -295,46 +337,36 @@ FriendListWidget::SortingMode FriendListWidget::getMode() const
void FriendListWidget::addGroupWidget(GroupWidget* widget)
{
groupLayout.addSortedWidget(widget);
Group* g = widget->getGroup();
connect(g, &Group::titleChanged, [=](const QString& author, const QString& name) {
Q_UNUSED(author)
renameGroupWidget(widget, name);
});
manager->addFriendListItem(widget);
}
void FriendListWidget::addFriendWidget(FriendWidget* w, Status::Status s, int circleIndex)
void FriendListWidget::addFriendWidget(FriendWidget* w)
{
CircleWidget* circleWidget = CircleWidget::getFromID(circleIndex);
if (circleWidget == nullptr)
moveWidget(w, s, true);
else
circleWidget->addFriendWidget(w, s);
connect(w, &FriendWidget::friendWidgetRenamed, this, &FriendListWidget::onFriendWidgetRenamed);
manager->addFriendListItem(w);
}
void FriendListWidget::removeGroupWidget(GroupWidget* w)
{
groupLayout.removeSortedWidget(w);
w->deleteLater();
manager->removeFriendListItem(w);
}
void FriendListWidget::removeFriendWidget(FriendWidget* w)
{
const Friend* contact = w->getFriend();
if (mode == SortingMode::Activity) {
auto* categoryWidget = getTimeCategoryWidget(contact);
categoryWidget->removeFriendWidget(w, contact->getStatus());
categoryWidget->setVisible(categoryWidget->hasChatrooms());
} else {
int id = Settings::getInstance().getFriendCircleID(contact->getPublicKey());
CircleWidget* circleWidget = CircleWidget::getFromID(id);
if (circleWidget != nullptr) {
circleWidget->removeFriendWidget(w, contact->getStatus());
emit searchCircle(*circleWidget);
}
int id = Settings::getInstance().getFriendCircleID(contact->getPublicKey());
CircleWidget* circleWidget = CircleWidget::getFromID(id);
if (circleWidget != nullptr) {
circleWidget->removeFriendWidget(w, contact->getStatus());
emit searchCircle(*circleWidget);
}
manager->removeFriendListItem(w);
}
void FriendListWidget::addCircleWidget(int id)
@ -348,103 +380,51 @@ void FriendListWidget::addCircleWidget(FriendWidget* friendWidget)
if (circleWidget != nullptr) {
if (friendWidget != nullptr) {
const Friend* f = friendWidget->getFriend();
ToxPk toxPk = f->getPublicKey();
int circleId = Settings::getInstance().getFriendCircleID(toxPk);
CircleWidget* circleOriginal = CircleWidget::getFromID(circleId);
circleWidget->addFriendWidget(friendWidget, f->getStatus());
circleWidget->setExpanded(true);
if (circleOriginal != nullptr)
emit searchCircle(*circleOriginal);
}
emit searchCircle(*circleWidget);
if (window()->isActiveWindow())
circleWidget->editName();
}
reDraw();
itemsChanged();
}
void FriendListWidget::removeCircleWidget(CircleWidget* widget)
{
circleLayout->removeSortedWidget(widget);
widget->deleteLater();
}
void FriendListWidget::searchChatrooms(const QString& searchString, bool hideOnline,
bool hideOffline, bool hideGroups)
{
groupLayout.search(searchString, hideGroups);
listLayout->searchChatrooms(searchString, hideOnline, hideOffline);
if (circleLayout != nullptr) {
for (int i = 0; i != circleLayout->getLayout()->count(); ++i) {
CircleWidget* circleWidget =
static_cast<CircleWidget*>(circleLayout->getLayout()->itemAt(i)->widget());
circleWidget->search(searchString, true, hideOnline, hideOffline);
}
} else if (activityLayout != nullptr) {
for (int i = 0; i != activityLayout->count(); ++i) {
CategoryWidget* categoryWidget =
static_cast<CategoryWidget*>(activityLayout->itemAt(i)->widget());
categoryWidget->search(searchString, true, hideOnline, hideOffline);
categoryWidget->setVisible(categoryWidget->hasChatrooms());
}
}
manager->setFilter(searchString, hideOnline, hideOffline, hideGroups);
}
void FriendListWidget::renameGroupWidget(GroupWidget* groupWidget, const QString& newName)
{
groupLayout.removeSortedWidget(groupWidget);
groupLayout.addSortedWidget(groupWidget);
itemsChanged();
}
void FriendListWidget::renameCircleWidget(CircleWidget* circleWidget, const QString& newName)
{
circleLayout->removeSortedWidget(circleWidget);
circleWidget->setName(newName);
circleLayout->addSortedWidget(circleWidget);
}
void FriendListWidget::onFriendWidgetRenamed(FriendWidget* friendWidget)
{
const Friend* contact = friendWidget->getFriend();
auto status = contact->getStatus();
if (mode == SortingMode::Activity) {
auto* categoryWidget = getTimeCategoryWidget(contact);
categoryWidget->removeFriendWidget(friendWidget, status);
categoryWidget->addFriendWidget(friendWidget, status);
} else {
int id = Settings::getInstance().getFriendCircleID(contact->getPublicKey());
CircleWidget* circleWidget = CircleWidget::getFromID(id);
if (circleWidget != nullptr) {
circleWidget->removeFriendWidget(friendWidget, status);
circleWidget->addFriendWidget(friendWidget, status);
emit searchCircle(*circleWidget);
} else {
listLayout->removeFriendWidget(friendWidget, status);
listLayout->addFriendWidget(friendWidget, status);
}
if (mode == SortingMode::Name) {
itemsChanged();
}
}
void FriendListWidget::onGroupchatPositionChanged(bool top)
{
groupsOnTop = top;
manager->setGroupsOnTop(top);
if (mode != SortingMode::Name)
return;
listLayout->removeItem(groupLayout.getLayout());
if (top)
listLayout->insertLayout(0, groupLayout.getLayout());
else
listLayout->insertLayout(1, groupLayout.getLayout());
reDraw();
manager->updatePositions();
itemsChanged();
}
void FriendListWidget::cycleContacts(GenericChatroomWidget* activeChatroomWidget, bool forward)
@ -499,77 +479,26 @@ void FriendListWidget::cycleContacts(GenericChatroomWidget* activeChatroomWidget
return;
}
QLayout* currentLayout = nullptr;
CircleWidget* circleWidget = nullptr;
QWidget* wgt = nullptr;
if (friendWidget != nullptr) {
const ToxPk& pk = friendWidget->getFriend()->getPublicKey();
uint32_t circleId = Settings::getInstance().getFriendCircleID(pk);
circleWidget = CircleWidget::getFromID(circleId);
if (circleWidget != nullptr) {
if (circleWidget->cycleContacts(friendWidget, forward)) {
return;
}
index = circleLayout->indexOfSortedWidget(circleWidget);
currentLayout = circleLayout->getLayout();
} else {
currentLayout = listLayout->getLayoutOnline();
index = listLayout->indexOfFriendWidget(friendWidget, true);
if (index == -1) {
currentLayout = listLayout->getLayoutOffline();
index = listLayout->indexOfFriendWidget(friendWidget, false);
}
}
wgt = getNextWidgetForName(friendWidget, forward);
} else {
GroupWidget* groupWidget = qobject_cast<GroupWidget*>(activeChatroomWidget);
if (groupWidget != nullptr) {
currentLayout = groupLayout.getLayout();
index = groupLayout.indexOfSortedWidget(groupWidget);
} else {
return;
wgt = getNextWidgetForName(groupWidget, forward);
}
FriendWidget* friendTmp = qobject_cast<FriendWidget*>(wgt);
if (friendTmp != nullptr) {
CircleWidget* circleWidget = CircleWidget::getFromID(friendTmp->getCircleId());
if (circleWidget != nullptr) {
circleWidget->setExpanded(true);
}
}
index += forward ? 1 : -1;
for (;;) {
// Bounds checking.
if (index < 0) {
currentLayout = nextLayout(currentLayout, forward);
index = currentLayout->count() - 1;
continue;
} else if (index >= currentLayout->count()) {
currentLayout = nextLayout(currentLayout, forward);
index = 0;
continue;
}
// Go to the actual next index.
if (currentLayout == listLayout->getLayoutOnline()
|| currentLayout == listLayout->getLayoutOffline()
|| currentLayout == groupLayout.getLayout()) {
GenericChatroomWidget* chatWidget =
qobject_cast<GenericChatroomWidget*>(currentLayout->itemAt(index)->widget());
if (chatWidget != nullptr)
emit chatWidget->chatroomWidgetClicked(chatWidget);
return;
} else if (currentLayout == circleLayout->getLayout()) {
circleWidget = qobject_cast<CircleWidget*>(currentLayout->itemAt(index)->widget());
if (circleWidget != nullptr) {
if (!circleWidget->cycleContacts(forward)) {
// Skip empty or finished circles.
index += forward ? 1 : -1;
continue;
}
}
return;
} else {
return;
}
}
GenericChatroomWidget* chatWidget = qobject_cast<GenericChatroomWidget*>(wgt);
if (chatWidget != nullptr)
emit chatWidget->chatroomWidgetClicked(chatWidget);
}
void FriendListWidget::dragEnterEvent(QDragEnterEvent* event)
@ -611,13 +540,17 @@ void FriendListWidget::dropEvent(QDropEvent* event)
void FriendListWidget::dayTimeout()
{
if (mode == SortingMode::Activity) {
setMode(SortingMode::Name);
setMode(SortingMode::Activity); // Refresh all.
itemsChanged();
}
dayTimer->start(timeUntilTomorrow());
}
void FriendListWidget::itemsChanged()
{
sortByMode(mode);
}
void FriendListWidget::moveWidget(FriendWidget* widget, Status::Status s, bool add)
{
if (mode == SortingMode::Name) {
@ -629,7 +562,7 @@ void FriendListWidget::moveWidget(FriendWidget* widget, Status::Status s, bool a
if (circleId != -1)
Settings::getInstance().setFriendCircleID(f->getPublicKey(), -1);
listLayout->addFriendWidget(widget, s);
itemsChanged();
return;
}
@ -640,6 +573,7 @@ void FriendListWidget::moveWidget(FriendWidget* widget, Status::Status s, bool a
categoryWidget->addFriendWidget(widget, contact->getStatus());
categoryWidget->show();
}
itemsChanged();
}
void FriendListWidget::updateActivityTime(const QDateTime& time)
@ -655,76 +589,19 @@ void FriendListWidget::updateActivityTime(const QDateTime& time)
categoryWidget->setVisible(categoryWidget->hasChatrooms());
}
// update widget after add/delete/hide/show
void FriendListWidget::reDraw()
{
hide();
show();
resize(QSize()); // lifehack
}
CircleWidget* FriendListWidget::createCircleWidget(int id)
{
if (id == -1)
id = Settings::getInstance().addCircle();
// Stop, after it has been created. Code after this is for displaying.
if (mode == SortingMode::Activity)
return nullptr;
assert(circleLayout != nullptr);
if (CircleWidget::getFromID(id) != nullptr) {
return CircleWidget::getFromID(id);
}
CircleWidget* circleWidget = new CircleWidget(core, this, id);
emit connectCircleWidget(*circleWidget);
circleLayout->addSortedWidget(circleWidget);
connect(this, &FriendListWidget::onCompactChanged, circleWidget, &CircleWidget::onCompactChanged);
connect(circleWidget, &CircleWidget::renameRequested, this, &FriendListWidget::renameCircleWidget);
circleWidget->show(); // Avoid flickering.
return circleWidget;
}
QLayout* FriendListWidget::nextLayout(QLayout* layout, bool forward) const
{
if (layout == groupLayout.getLayout()) {
if (forward) {
if (groupsOnTop)
return listLayout->getLayoutOnline();
return listLayout->getLayoutOffline();
} else {
if (groupsOnTop)
return circleLayout->getLayout();
return listLayout->getLayoutOnline();
}
} else if (layout == listLayout->getLayoutOnline()) {
if (forward) {
if (groupsOnTop)
return listLayout->getLayoutOffline();
return groupLayout.getLayout();
} else {
if (groupsOnTop)
return groupLayout.getLayout();
return circleLayout->getLayout();
}
} else if (layout == listLayout->getLayoutOffline()) {
if (forward)
return circleLayout->getLayout();
else if (groupsOnTop)
return listLayout->getLayoutOnline();
return groupLayout.getLayout();
} else if (layout == circleLayout->getLayout()) {
if (forward) {
if (groupsOnTop)
return groupLayout.getLayout();
return listLayout->getLayoutOnline();
} else
return listLayout->getLayoutOffline();
}
return nullptr;
}

View File

@ -23,6 +23,7 @@
#include "src/core/core.h"
#include "src/model/status.h"
#include "src/persistence/settings.h"
#include <QWidget>
class QVBoxLayout;
@ -32,10 +33,11 @@ class Widget;
class FriendWidget;
class GroupWidget;
class CircleWidget;
class FriendListLayout;
class FriendListManager;
class GenericChatroomWidget;
class CategoryWidget;
class Friend;
class IFriendListItem;
class FriendListWidget : public QWidget
{
@ -48,7 +50,7 @@ public:
SortingMode getMode() const;
void addGroupWidget(GroupWidget* widget);
void addFriendWidget(FriendWidget* w, Status::Status s, int circleIndex);
void addFriendWidget(FriendWidget* w);
void removeGroupWidget(GroupWidget* w);
void removeFriendWidget(FriendWidget* w);
void addCircleWidget(int id);
@ -60,7 +62,6 @@ public:
void cycleContacts(GenericChatroomWidget* activeChatroomWidget, bool forward);
void updateActivityTime(const QDateTime& date);
void reDraw();
signals:
void onCompactChanged(bool compact);
@ -70,9 +71,9 @@ signals:
public slots:
void renameGroupWidget(GroupWidget* groupWidget, const QString& newName);
void renameCircleWidget(CircleWidget* circleWidget, const QString& newName);
void onFriendWidgetRenamed(FriendWidget* friendWidget);
void onGroupchatPositionChanged(bool top);
void moveWidget(FriendWidget* w, Status::Status s, bool add = false);
void itemsChanged();
protected:
void dragEnterEvent(QDragEnterEvent* event) override;
@ -83,18 +84,17 @@ private slots:
private:
CircleWidget* createCircleWidget(int id = -1);
QLayout* nextLayout(QLayout* layout, bool forward) const;
void moveFriends(QLayout* layout);
CategoryWidget* getTimeCategoryWidget(const Friend* frd) const;
void sortByMode(SortingMode mode);
void cleanMainLayout();
QWidget* getNextWidgetForName(IFriendListItem* currentPos, bool forward) const;
QVector<std::shared_ptr<IFriendListItem> > getItemsFromCircle(CircleWidget* circle) const;
SortingMode mode;
bool groupsOnTop;
FriendListLayout* listLayout;
GenericChatItemLayout* circleLayout = nullptr;
GenericChatItemLayout groupLayout;
QVBoxLayout* listLayout = nullptr;
QVBoxLayout* activityLayout = nullptr;
QTimer* dayTimer;
FriendListManager* manager;
const Core& core;
};

View File

@ -41,7 +41,6 @@
#include <QApplication>
#include <QBitmap>
#include <QContextMenuEvent>
#include <QDebug>
#include <QDrag>
#include <QFileDialog>
#include <QInputDialog>
@ -72,8 +71,6 @@ FriendWidget::FriendWidget(std::shared_ptr<FriendChatroom> chatroom, bool compac
connect(nameLabel, &CroppingLabel::editFinished, frnd, &Friend::setAlias);
// update on changes of the displayed name
connect(frnd, &Friend::displayedNameChanged, nameLabel, &CroppingLabel::setText);
connect(frnd, &Friend::displayedNameChanged, this,
[this](const QString /* &newName */) { emit friendWidgetRenamed(this); });
connect(chatroom.get(), &FriendChatroom::activeChanged, this, &FriendWidget::setActive);
statusMessageLabel->setTextFormat(Qt::PlainText);
}
@ -241,7 +238,6 @@ void FriendWidget::removeFromCircle()
if (circleWidget != nullptr) {
circleWidget->updateStatus();
emit searchCircle(*circleWidget);
}
}
@ -257,7 +253,6 @@ void FriendWidget::moveToCircle(int newCircleId)
if (newCircleWidget) {
newCircleWidget->addFriendWidget(this, frnd->getStatus());
newCircleWidget->setExpanded(true);
emit searchCircle(*newCircleWidget);
s.savePersonal();
} else {
s.setFriendCircleID(pk, newCircleId);
@ -265,7 +260,6 @@ void FriendWidget::moveToCircle(int newCircleId)
if (oldCircleWidget) {
oldCircleWidget->updateStatus();
emit searchCircle(*oldCircleWidget);
}
}
@ -360,16 +354,41 @@ const Contact* FriendWidget::getContact() const
return getFriend();
}
void FriendWidget::search(const QString& searchString, bool hide)
bool FriendWidget::isFriend() const
{
return true;
}
bool FriendWidget::isGroup() const
{
return false;
}
bool FriendWidget::isOnline() const
{
const auto frnd = getFriend();
return Status::isOnline(frnd->getStatus());
}
QString FriendWidget::getNameItem() const
{
return nameLabel->fullText();
}
QDateTime FriendWidget::getLastActivity() const
{
const auto frnd = chatroom->getFriend();
searchName(searchString, hide);
const Settings& s = Settings::getInstance();
const uint32_t circleId = s.getFriendCircleID(frnd->getPublicKey());
CircleWidget* circleWidget = CircleWidget::getFromID(circleId);
if (circleWidget) {
circleWidget->search(searchString);
}
return Settings::getInstance().getFriendActivity(frnd->getPublicKey());
}
QWidget *FriendWidget::getWidget()
{
return this;
}
int FriendWidget::getCircleId() const
{
return chatroom->getCircleId();
}
void FriendWidget::resetEventFlags()

View File

@ -21,6 +21,7 @@
#include "genericchatroomwidget.h"
#include "src/core/toxpk.h"
#include "src/model/friendlist/ifriendlistitem.h"
#include <memory>
@ -29,7 +30,7 @@ class QPixmap;
class MaskablePixmapWidget;
class CircleWidget;
class FriendWidget : public GenericChatroomWidget
class FriendWidget : public GenericChatroomWidget, public IFriendListItem
{
Q_OBJECT
public:
@ -44,7 +45,13 @@ public:
const Friend* getFriend() const final;
const Contact* getContact() const final;
void search(const QString& searchString, bool hide = false);
bool isFriend() const final;
bool isGroup() const final;
bool isOnline() const final;
QString getNameItem() const final;
QDateTime getLastActivity() const final;
int getCircleId() const final;
QWidget* getWidget() final;
signals:
void friendWidgetClicked(FriendWidget* widget);
@ -52,8 +59,6 @@ signals:
void copyFriendIdToClipboard(const ToxPk& friendPk);
void contextMenuCalled(QContextMenuEvent* event);
void friendHistoryRemoved();
void friendWidgetRenamed(FriendWidget* friendWidget);
void searchCircle(CircleWidget& circleWidget);
void updateFriendActivity(Friend& frnd);
public slots:

View File

@ -189,6 +189,36 @@ void GroupWidget::editName()
nameLabel->editBegin();
}
bool GroupWidget::isFriend() const
{
return false;
}
bool GroupWidget::isGroup() const
{
return true;
}
QString GroupWidget::getNameItem() const
{
return nameLabel->fullText();
}
bool GroupWidget::isOnline() const
{
return true;
}
QDateTime GroupWidget::getLastActivity() const
{
return QDateTime::currentDateTime();
}
QWidget *GroupWidget::getWidget()
{
return this;
}
// TODO: Remove
Group* GroupWidget::getGroup() const
{

View File

@ -22,17 +22,18 @@
#include "genericchatroomwidget.h"
#include "src/model/chatroom/groupchatroom.h"
#include "src/model/friendlist/ifriendlistitem.h"
#include "src/core/groupid.h"
#include <memory>
class GroupWidget final : public GenericChatroomWidget
class GroupWidget final : public GenericChatroomWidget, public IFriendListItem
{
Q_OBJECT
public:
GroupWidget(std::shared_ptr<GroupChatroom> chatroom, bool compact);
~GroupWidget();
void setAsInactiveChatroom() final;
void setAsInactiveChatroom() final;
void setAsActiveChatroom() final;
void updateStatusLight() final;
void resetEventFlags() final;
@ -42,6 +43,13 @@ public:
void setName(const QString& name);
void editName();
bool isFriend() const final;
bool isGroup() const final;
QString getNameItem() const final;
bool isOnline() const final;
QDateTime getLastActivity() const final;
QWidget* getWidget() final;
signals:
void groupWidgetClicked(GroupWidget* widget);
void removeGroup(const GroupId& groupId);

View File

@ -1178,8 +1178,7 @@ void Widget::addFriend(uint32_t friendId, const ToxPk& friendPk)
settings.setFriendActivity(friendPk, chatTime);
}
contactListWidget->addFriendWidget(widget, Status::Status::Offline,
settings.getFriendCircleID(friendPk));
contactListWidget->addFriendWidget(widget);
auto notifyReceivedCallback = [this, friendPk](const ToxPk& author, const Message& message) {
@ -1218,9 +1217,6 @@ void Widget::addFriend(uint32_t friendId, const ToxPk& friendPk)
friendForm->onAvatarChanged(friendPk, avatar);
widget->onAvatarSet(friendPk, avatar);
}
FilterCriteria filter = getFilterCriteria();
widget->search(ui->searchContactText->text(), filterOffline(filter));
}
void Widget::addFriendFailed(const ToxPk&, const QString& errorInfo)
@ -1308,6 +1304,8 @@ void Widget::onFriendDisplayedNameChanged(const QString& displayed)
if (friendWidget->isActive()) {
GUI::setWindowTitle(displayed);
}
contactListWidget->itemsChanged();
}
void Widget::onFriendUsernameChanged(int friendId, const QString& username)
@ -1325,16 +1323,6 @@ void Widget::onFriendUsernameChanged(int friendId, const QString& username)
void Widget::onFriendAliasChanged(const ToxPk& friendId, const QString& alias)
{
Friend* f = qobject_cast<Friend*>(sender());
// TODO(sudden6): don't update the contact list here, make it update itself
FriendWidget* friendWidget = friendWidgets[friendId];
Status::Status status = f->getStatus();
contactListWidget->moveWidget(friendWidget, status);
FilterCriteria criteria = getFilterCriteria();
bool filter = status == Status::Status::Offline ? filterOffline(criteria) : filterOnline(criteria);
friendWidget->searchName(ui->searchContactText->text(), filter);
settings.setFriendAlias(friendId, alias);
settings.savePersonal();
}
@ -1779,7 +1767,6 @@ void Widget::removeFriend(Friend* f, bool fake)
}
friendWidgets.remove(friendPk);
delete widget;
auto chatForm = chatForms[friendPk];
chatForms.remove(friendPk);
@ -1789,8 +1776,6 @@ void Widget::removeFriend(Friend* f, bool fake)
if (contentLayout && contentLayout->mainHead->layout()->isEmpty()) {
onAddClicked();
}
contactListWidget->reDraw();
}
void Widget::removeFriend(const ToxPk& friendId)
@ -2032,8 +2017,7 @@ void Widget::onGroupTitleChanged(uint32_t groupnumber, const QString& author, co
}
g->setTitle(author, title);
FilterCriteria filter = getFilterCriteria();
widget->searchName(ui->searchContactText->text(), filterGroups(filter));
contactListWidget->itemsChanged();
}
void Widget::titleChangedByUser(const QString& title)
@ -2092,8 +2076,6 @@ void Widget::removeGroup(Group* g, bool fake)
}
groupAlertConnections.remove(groupId);
contactListWidget->reDraw();
}
void Widget::removeGroup(const GroupId& groupId)
@ -2184,9 +2166,6 @@ Group* Widget::createGroup(uint32_t groupnumber, const GroupId& groupId)
connect(newgroup, &Group::titleChangedByUser, this, &Widget::titleChangedByUser);
connect(core, &Core::usernameSet, newgroup, &Group::setSelfName);
FilterCriteria filter = getFilterCriteria();
widget->searchName(ui->searchContactText->text(), filterGroups(filter));
return newgroup;
}
@ -2467,7 +2446,6 @@ void Widget::reloadTheme()
ui->statusHead->setStyleSheet(statusPanelStyle);
ui->friendList->setStyleSheet(Style::getStylesheet("friendList/friendList.css"));
ui->statusButton->setStyleSheet(Style::getStylesheet("statusButton/statusButton.css"));
contactListWidget->reDraw();
profilePicture->setStyleSheet(Style::getStylesheet("window/profile.css"));
}
@ -2518,8 +2496,6 @@ void Widget::searchContacts()
filterGroups(filter));
updateFilterText();
contactListWidget->reDraw();
}
void Widget::changeDisplayMode()
@ -2564,9 +2540,11 @@ Widget::FilterCriteria Widget::getFilterCriteria() const
void Widget::searchCircle(CircleWidget& circleWidget)
{
FilterCriteria filter = getFilterCriteria();
QString text = ui->searchContactText->text();
circleWidget.search(text, true, filterOnline(filter), filterOffline(filter));
if (contactListWidget->getMode() == FriendListWidget::SortingMode::Name) {
FilterCriteria filter = getFilterCriteria();
QString text = ui->searchContactText->text();
circleWidget.search(text, true, filterOnline(filter), filterOffline(filter));
}
}
bool Widget::groupsVisible() const
@ -2716,12 +2694,10 @@ void Widget::refreshPeerListsLocal(const QString& username)
void Widget::connectCircleWidget(CircleWidget& circleWidget)
{
connect(&circleWidget, &CircleWidget::searchCircle, this, &Widget::searchCircle);
connect(&circleWidget, &CircleWidget::newContentDialog, this, &Widget::registerContentDialog);
}
void Widget::connectFriendWidget(FriendWidget& friendWidget)
{
connect(&friendWidget, &FriendWidget::searchCircle, this, &Widget::searchCircle);
connect(&friendWidget, &FriendWidget::updateFriendActivity, this, &Widget::updateFriendActivity);
}