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

Fixed group and circle illegal chars, inline renaming for friend list widgets, sort circles alphabetically

This commit is contained in:
Daniel Hrabovcak 2015-06-01 14:39:40 -04:00 committed by tux3
parent 27237e6164
commit a0b312dd97
11 changed files with 239 additions and 82 deletions

View File

@ -532,4 +532,5 @@ HEADERS += \
src/widget/notificationedgewidget.h \
src/widget/circlewidget.h \
src/widget/genericchatitemwidget.h \
src/widget/friendlistlayout.h
src/widget/friendlistlayout.h \
src/widget/sortingboxlayout.h

View File

@ -246,7 +246,7 @@ public:
void setAutoLogin(bool state);
int getCircleCount() const;
void addCircle(const QString &name);
int addCircle(const QString &name);
QString getCircleName(int index) const;
void setCircleName(int index);

View File

@ -35,6 +35,15 @@
#include "friendlistlayout.h"
#include "friendlistwidget.h"
#include "croppinglabel.h"
void maxCropLabel(CroppingLabel* label)
{
QFontMetrics metrics = label->fontMetrics();
// Text width + padding. Without padding, we'll have elipses.
label->setMaximumWidth(metrics.width(label->fullText()) + metrics.width("..."));
}
CircleWidget::CircleWidget(FriendListWidget *parent)
: GenericChatItemWidget(parent)
{
@ -50,7 +59,7 @@ CircleWidget::CircleWidget(FriendListWidget *parent)
statusLabel->setTextFormat(Qt::PlainText);
// name text
nameLabel = new QLabel(this);
nameLabel = new CroppingLabel(this);
nameLabel->setObjectName("name");
nameLabel->setTextFormat(Qt::PlainText);
nameLabel->setText("Circle");
@ -66,6 +75,10 @@ CircleWidget::CircleWidget(FriendListWidget *parent)
lineFrame = new QFrame(container);
lineFrame->setObjectName("line");
lineFrame->setFrameShape(QFrame::HLine);
lineFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
qDebug() << lineFrame->sizeHint();
lineFrame->resize(0, 0);
qDebug() << QSpacerItem(0, 0).sizeHint();
listLayout = new FriendListLayout();
@ -73,7 +86,15 @@ CircleWidget::CircleWidget(FriendListWidget *parent)
onCompactChanged(isCompact());
//renameCircle();
connect(nameLabel, &CroppingLabel::textChanged, [this](const QString &newString, const QString &oldString)
{
if (isCompact())
maxCropLabel(nameLabel);
nameLabel->setText(oldString);
emit renameRequested(newString);
});
renameCircle();
}
void CircleWidget::addFriendWidget(FriendWidget *w, Status s)
@ -121,29 +142,13 @@ void CircleWidget::setName(const QString &name)
void CircleWidget::renameCircle()
{
qDebug() << nameLabel->parentWidget()->layout();
QLineEdit *lineEdit = new QLineEdit(nameLabel->text());
topLayout->removeWidget(nameLabel);
topLayout->insertWidget(3, lineEdit);
nameLabel->setVisible(false);
//topLayout->replaceWidget(nameLabel, lineEdit);
//nameLabel->parentWidget()->layout()
//static_cast<QBoxLayout*>(nameLabel->parentWidget()->layout())->insertWidget(nameLabel->parentWidget()->layout()->indexOf(nameLabel) - 1, lineEdit);
//nameLabel->parentWidget()->layout()->replaceWidget(nameLabel, lineEdit);
//nameLabel->setVisible(false);
lineEdit->selectAll();
lineEdit->setFocus();
connect(lineEdit, &QLineEdit::editingFinished, [this, lineEdit]()
{
this->topLayout->removeWidget(lineEdit);
this->topLayout->insertWidget(3, nameLabel);
//this->topLayout->replaceWidget(lineEdit, nameLabel);
//this->nameLabel->setVisible(true);
//lineEdit->parentWidget()->layout()->replaceWidget(lineEdit, nameLabel);
this->nameLabel->setText(lineEdit->text());
nameLabel->setVisible(true);
lineEdit->deleteLater();
});
nameLabel->editStart();
nameLabel->setMaximumWidth(QWIDGETSIZE_MAX);
}
bool CircleWidget::operator<(const CircleWidget& other) const
{
return nameLabel->text().localeAwareCompare(other.nameLabel->text()) < 0;
}
void CircleWidget::onCompactChanged(bool _compact)
@ -159,6 +164,8 @@ void CircleWidget::onCompactChanged(bool _compact)
if (property("compact").toBool())
{
maxCropLabel(nameLabel);
mainLayout = nullptr;
container->setFixedHeight(25);
@ -167,8 +174,7 @@ void CircleWidget::onCompactChanged(bool _compact)
topLayout->addSpacing(18);
topLayout->addWidget(arrowLabel);
topLayout->addSpacing(5);
topLayout->addWidget(nameLabel);
topLayout->addSpacing(5);
topLayout->addWidget(nameLabel, 100);
topLayout->addWidget(lineFrame, 1);
topLayout->addSpacing(5);
topLayout->addWidget(statusLabel);
@ -177,7 +183,8 @@ void CircleWidget::onCompactChanged(bool _compact)
}
else
{
qDebug() << "LTSE";
nameLabel->setMaximumWidth(QWIDGETSIZE_MAX);
mainLayout = new QVBoxLayout();
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(20, 0, 20, 0);
@ -187,8 +194,8 @@ void CircleWidget::onCompactChanged(bool _compact)
topLayout->addWidget(arrowLabel);
topLayout->addSpacing(10);
topLayout->addWidget(nameLabel);
topLayout->addStretch();
topLayout->addWidget(nameLabel, 1);
topLayout->addSpacing(5);
topLayout->addWidget(statusLabel);
topLayout->activate();

View File

@ -25,6 +25,7 @@ class FriendListWidget;
class FriendListLayout;
class FriendWidget;
class QLineEdit;
class CroppingLabel;
class CircleWidget : public GenericChatItemWidget
{
@ -45,6 +46,11 @@ public:
void setName(const QString &name);
void renameCircle();
bool operator<(const CircleWidget& other) const;
signals:
void renameRequested(const QString &newName);
public slots:
void onCompactChanged(bool compact);
@ -69,7 +75,7 @@ private:
QVBoxLayout* fullLayout;
QVBoxLayout* mainLayout = nullptr;
QLabel* arrowLabel;
QLabel* nameLabel;
CroppingLabel* nameLabel;
QLabel* statusLabel;
QFrame* lineFrame;
QWidget* container;

View File

@ -41,7 +41,10 @@ FriendListWidget::FriendListWidget(QWidget *parent, bool groupsOnTop) :
circleLayout->setSpacing(0);
circleLayout->setMargin(0);
listLayout->addLayout(circleLayout);
circleLayout2.getLayout()->setSpacing(0);
circleLayout2.getLayout()->setMargin(0);
listLayout->addLayout(circleLayout2.getLayout());
setAcceptDrops(true);
}
@ -55,8 +58,8 @@ void FriendListWidget::addFriendWidget(FriendWidget *w, Status s, int circleInde
{
CircleWidget *circleWidget = nullptr;
qDebug() << circleIndex;
if (circleIndex >= 0 && circleIndex < circleLayout->count())
circleWidget = dynamic_cast<CircleWidget*>(circleLayout->itemAt(circleIndex)->widget());
if (circleIndex >= 0 && circleIndex < circleLayout2.getLayout()->count())
circleWidget = dynamic_cast<CircleWidget*>(circleLayout2.getLayout()->itemAt(circleIndex)->widget());
if (circleWidget == nullptr)
circleIndex = -1;
@ -71,13 +74,17 @@ void FriendListWidget::addCircleWidget(const QString &name)
{
CircleWidget *circleWidget = new CircleWidget(this);
circleWidget->setName(name);
circleLayout->addWidget(circleWidget);
circleLayout2.addSortedWidget(circleWidget);
connect(circleWidget, &CircleWidget::renameRequested, this, &FriendListWidget::renameCircleWidget);
//circleLayout->addWidget(circleWidget);
}
CircleWidget* FriendListWidget::addCircleWidget(FriendWidget *friendWidget)
{
CircleWidget *circleWidget = new CircleWidget(this);
circleLayout->addWidget(circleWidget);
circleLayout2.addSortedWidget(circleWidget);
connect(circleWidget, &CircleWidget::renameRequested, this, &FriendListWidget::renameCircleWidget);
//circleLayout->addWidget(circleWidget);
if (friendWidget != nullptr)
{
circleWidget->addFriendWidget(friendWidget, FriendList::findFriend(friendWidget->friendId)->getStatus());
@ -89,20 +96,16 @@ CircleWidget* FriendListWidget::addCircleWidget(FriendWidget *friendWidget)
void FriendListWidget::removeCircleWidget(CircleWidget *widget)
{
//setUpdatesEnabled(false);
//widget->setVisible(false);
circleLayout->removeWidget(widget);
circleLayout2.removeSortedWidget(widget);
widget->deleteLater();
//setUpdatesEnabled(true);
//widget->deleteLater();
}
void FriendListWidget::searchChatrooms(const QString &searchString, bool hideOnline, bool hideOffline, bool hideGroups)
{
listLayout->searchChatrooms(searchString, hideOnline, hideOffline, hideGroups);
for (int i = 0; i != circleLayout->count(); ++i)
for (int i = 0; i != circleLayout2.getLayout()->count(); ++i)
{
CircleWidget *circleWidget = static_cast<CircleWidget*>(circleLayout->itemAt(i)->widget());
CircleWidget *circleWidget = static_cast<CircleWidget*>(circleLayout2.getLayout()->itemAt(i)->widget());
circleWidget->searchChatrooms(searchString, hideOnline, hideOffline, hideGroups);
}
}
@ -112,9 +115,22 @@ QVBoxLayout* FriendListWidget::getFriendLayout(Status s)
return s == Status::Offline ? listLayout->friendLayouts[Offline] : listLayout->friendLayouts[Online];
}
void FriendListWidget::renameCircleWidget(const QString &newName)
{
assert(sender() != nullptr);
CircleWidget* circleWidget = dynamic_cast<CircleWidget*>(sender());
assert(circleWidget != nullptr);
// Rename before removing so you can find it successfully.
circleLayout2.removeSortedWidget(circleWidget);
circleWidget->setName(newName);
circleLayout2.addSortedWidget(circleWidget);
}
void FriendListWidget::onGroupchatPositionChanged(bool top)
{
listLayout->removeItem(circleLayout);
listLayout->removeItem(circleLayout2.getLayout());
listLayout->removeItem(listLayout->groupLayout);
listLayout->removeItem(listLayout->friendLayouts[Online]);
if (top)
@ -127,7 +143,7 @@ void FriendListWidget::onGroupchatPositionChanged(bool top)
listLayout->addLayout(listLayout->friendLayouts[Online]);
listLayout->addLayout(listLayout->groupLayout);
}
listLayout->addLayout(circleLayout);
listLayout->addLayout(circleLayout2.getLayout());
}
QList<GenericChatroomWidget*> FriendListWidget::getAllFriends()
@ -159,10 +175,10 @@ QList<GenericChatroomWidget*> FriendListWidget::getAllFriends()
QVector<CircleWidget*> FriendListWidget::getAllCircles()
{
QVector<CircleWidget*> vec;
vec.reserve(circleLayout->count());
for (int i = 0; i < circleLayout->count(); ++i)
vec.reserve(circleLayout2.getLayout()->count());
for (int i = 0; i < circleLayout2.getLayout()->count(); ++i)
{
vec.push_back(dynamic_cast<CircleWidget*>(circleLayout->itemAt(i)->widget()));
vec.push_back(dynamic_cast<CircleWidget*>(circleLayout2.getLayout()->itemAt(i)->widget()));
}
return vec;
}

View File

@ -27,6 +27,10 @@
#include "src/core/corestructs.h"
#include "src/widget/genericchatroomwidget.h"
#include "sortingboxlayout.h"
#include "circlewidget.h"
class QVBoxLayout;
class QGridLayout;
class QPixmap;
@ -50,12 +54,15 @@ public:
void searchChatrooms(const QString &searchString, bool hideOnline = false, bool hideOffline = false, bool hideGroups = false);
void cycleContacts(int index);
QList<GenericChatroomWidget*> getAllFriends();
QVector<CircleWidget*> getAllCircles();
void reDraw();
public slots:
void renameCircleWidget(const QString &newName);
//void onCompactChanged(bool compact);
void onGroupchatPositionChanged(bool top);
void moveWidget(FriendWidget *w, Status s, bool add = false);
@ -72,6 +79,7 @@ private:
};
FriendListLayout *listLayout;
QVBoxLayout *circleLayout;
VSortingBoxLayout<CircleWidget> circleLayout2;
};
#endif // FRIENDLISTWIDGET_H

View File

@ -52,6 +52,7 @@ FriendWidget::FriendWidget(int FriendId, QString id)
statusPic.setMargin(3);
nameLabel->setText(id);
nameLabel->setTextFormat(Qt::PlainText);
connect(nameLabel, &CroppingLabel::editFinished, this, &FriendWidget::setAlias);
statusMessageLabel->setTextFormat(Qt::PlainText);
}
@ -121,7 +122,7 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event)
return;
} else if (selectedItem == setAlias)
{
setFriendAlias();
nameLabel->editStart();
}
else if (selectedItem == removeFriendAction)
{
@ -148,7 +149,9 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event)
}
else if (selectedItem == newCircleAction)
{
friendList->addCircleWidget(this);
qDebug() << friendList->parentWidget();
CircleWidget *newcircle = friendList->addCircleWidget(this);
//connect(settingsWidget, &SettingsWidget::compactToggled, newcircle, &CircleWidget::onCompactChanged);
}
else if (groupActions.contains(selectedItem))
{
@ -293,9 +296,7 @@ void FriendWidget::mouseMoveEvent(QMouseEvent *ev)
void FriendWidget::setAlias(const QString& _alias)
{
QString alias = _alias.trimmed();
alias.remove(QRegExp("[\\t\\n\\v\\f\\r\\x0000]")); // we should really treat regular names this way as well (*ahem* zetok)
alias = alias.left(128); // same as TOX_MAX_NAME_LENGTH
QString alias = _alias.left(128); // same as TOX_MAX_NAME_LENGTH
Friend* f = FriendList::findFriend(friendId);
f->setAlias(alias);
Settings::getInstance().setFriendAlias(f->getToxId(), alias);
@ -303,15 +304,3 @@ void FriendWidget::setAlias(const QString& _alias)
hide();
show();
}
void FriendWidget::setFriendAlias()
{
bool ok;
Friend* f = FriendList::findFriend(friendId);
QString alias = QInputDialog::getText(nullptr, tr("User alias"), tr("You can also set this by clicking the chat form name.\nAlias:"), QLineEdit::Normal,
f->getDisplayedName(), &ok);
if (ok)
setAlias(alias);
}

View File

@ -50,6 +50,22 @@ GroupWidget::GroupWidget(int GroupId, QString Name)
statusMessageLabel->setText(GroupWidget::tr("0 users in chat"));
setAcceptDrops(true);
connect(nameLabel, &CroppingLabel::textChanged, [this](const QString &newText, const QString &oldText)
{
Group* g = GroupList::findGroup(groupId);
if (newText != oldText)
{
emit g->getChatForm()->groupTitleChanged(groupId, newText.left(128));
}
/* according to agilob:
* Moving mouse pointer over groupwidget results in CSS effect
* mouse-over(?). Changing group title repaints only changed
* element - title, the rest of the widget stays in the same CSS as it
* was on mouse over. Repainting whole widget fixes style problem.
*/
this->repaint();
});
}
void GroupWidget::contextMenuEvent(QContextMenuEvent * event)
@ -68,21 +84,7 @@ void GroupWidget::contextMenuEvent(QContextMenuEvent * event)
}
else if (selectedItem == setTitle)
{
bool ok;
Group* g = GroupList::findGroup(groupId);
QString alias = QInputDialog::getText(nullptr, tr("Group title"), tr("You can also set this by clicking the chat form name.\nTitle:"), QLineEdit::Normal,
nameLabel->fullText(), &ok);
if (ok && alias != nameLabel->fullText())
emit g->getChatForm()->groupTitleChanged(groupId, alias.left(128));
/* according to agilob:
* Moving mouse pointer over groupwidget results in CSS effect
* mouse-over(?). Changing group title repaints only changed
* element - title, the rest of the widget stays in the same CSS as it
* was on mouse over. Repainting whole widget fixes style problem.
*/
this->repaint();
nameLabel->editStart();
}
}
}

View File

@ -0,0 +1,118 @@
/*
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 SORTINGBOXLAYOUT_H
#define SORTINGBOXLAYOUT_H
#include <QBoxLayout>
#include <cassert>
#include <QDebug>
template <typename T, QBoxLayout::Direction Dir>
class SortingBoxLayout
{
public:
static_assert(std::is_base_of<QWidget,T>::value == true, "T must be base of QWidget*.");
SortingBoxLayout();
~SortingBoxLayout();
void addSortedWidget(T* widget);
bool existsSortedWidget(T* widget) const;
void removeSortedWidget(T* widget);
QLayout* getLayout() const;
private:
int indexOfClosestSortedWidget(T* widget);
QBoxLayout* layout;
};
template <typename T, QBoxLayout::Direction Dir>
SortingBoxLayout<T, Dir>::SortingBoxLayout()
: layout(new QBoxLayout(Dir))
{
}
template <typename T, QBoxLayout::Direction Dir>
SortingBoxLayout<T, Dir>::~SortingBoxLayout()
{
delete layout;
}
template <typename T, QBoxLayout::Direction Dir>
void SortingBoxLayout<T, Dir>::addSortedWidget(T* widget)
{
int closest = indexOfClosestSortedWidget(widget);
layout->insertWidget(closest, widget);
}
template <typename T, QBoxLayout::Direction Dir>
bool SortingBoxLayout<T, Dir>::existsSortedWidget(T* widget) const
{
int index = indexOfClosestSortedWidget(widget);
T* atMid = dynamic_cast<T*>(layout->itemAt(index)->widget());
assert(atMid != nullptr);
if (atMid == widget)
return true;
return false;
}
template <typename T, QBoxLayout::Direction Dir>
void SortingBoxLayout<T, Dir>::removeSortedWidget(T* widget)
{
if (layout->isEmpty())
return;
int index = indexOfClosestSortedWidget(widget);
if (layout->itemAt(index) == nullptr)
return;
T* atMid = dynamic_cast<T*>(layout->itemAt(index)->widget());
assert(atMid != nullptr);
if (atMid == widget)
layout->removeWidget(widget);
}
template <typename T, QBoxLayout::Direction Dir>
QLayout* SortingBoxLayout<T, Dir>::getLayout() const
{
return layout;
}
template <typename T, QBoxLayout::Direction Dir>
int SortingBoxLayout<T, Dir>::indexOfClosestSortedWidget(T* widget)
{
int min = 0, max = layout->count() - 1, mid;
while (min < max)
{
mid = (max - min) / 2 + min;
T* atMid = dynamic_cast<T*>(layout->itemAt(mid)->widget());
assert(atMid != nullptr);
if (*widget < *atMid)
max = mid;
else
min = mid + 1;
}
return min;
}
template <typename T>
using VSortingBoxLayout = SortingBoxLayout<T, QBoxLayout::TopToBottom>;
#endif // SORTINGBOXLAYOUT_H

View File

@ -39,6 +39,12 @@ CroppingLabel::CroppingLabel(QWidget* parent)
textEdit->installEventFilter(this);
}
void CroppingLabel::editStart()
{
showTextEdit();
textEdit->selectAll();
}
void CroppingLabel::setEditable(bool editable)
{
this->editable = editable;
@ -135,9 +141,11 @@ void CroppingLabel::hideTextEdit(bool acceptText)
{
if (acceptText)
{
textEdit->setText(textEdit->text().trimmed().remove(QRegExp("[\\t\\n\\v\\f\\r\\x0000]"))); // we should really treat regular names this way as well (*ahem* zetok)
QString oldOrigText = origText;
setText(textEdit->text()); // set before emitting so we don't override external reactions to signal
emit textChanged(textEdit->text(), oldOrigText);
emit editFinished(textEdit->text());
}
textEdit->hide();

View File

@ -30,6 +30,7 @@ class CroppingLabel final : public QLabel
public:
explicit CroppingLabel(QWidget *parent = 0);
void editStart();
void setEditable(bool editable);
void setEdlideMode(Qt::TextElideMode elide);
@ -37,6 +38,7 @@ public:
QString fullText(); ///< Returns the un-cropped text
signals:
void editFinished(QString newText);
void textChanged(QString newText, QString oldText);
void clicked();