started refactoring of the VCard UI

This commit is contained in:
Blue 2023-02-01 18:56:00 +03:00
parent bb304ce774
commit 4af16b75bf
Signed by: blue
GPG key ID: 9B203B252A63EE38
32 changed files with 1250 additions and 166 deletions

View file

@ -0,0 +1,7 @@
target_sources(squawk PRIVATE
omemo.cpp
omemo.h
omemo.ui
keydelegate.cpp
keydelegate.h
)

View file

@ -0,0 +1,226 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free 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
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "keydelegate.h"
#include <QPainter>
#include <QDebug>
#include "ui/models/info/omemo/keys.h"
#include <shared/global.h>
constexpr uint8_t margin = 10;
constexpr uint8_t partSize = 8;
constexpr uint8_t maxSingleLineParts = 3;
UI::KeyDelegate::KeyDelegate(QObject* parent):
QStyledItemDelegate(parent),
defaultFont(Shared::Global::getInstance()->defaultFont),
fingerPrintFont(Shared::Global::getInstance()->monospaceFont),
labelFont(Shared::Global::getInstance()->smallFont),
defaultMetrics(Shared::Global::getInstance()->defaultFontMetrics),
fingerPrintMetrics(Shared::Global::getInstance()->monospaceMetrics),
labelMetrics(Shared::Global::getInstance()->smallFontMetrics),
spaceWidth(fingerPrintMetrics.horizontalAdvance(" "))
{}
UI::KeyDelegate::~KeyDelegate() {}
void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
QRect r;
int maxRight = 0;
int leftOrigin = option.rect.left() + margin;
QColor q = painter->pen().color();
q.setAlpha(180);
QRect rect = option.rect;
bool hover = option.state & QStyle::State_MouseOver;
if (hover) {
painter->save();
painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight));
painter->restore();
}
QVariant dirtyV = index.data(Models::Keys::Dirty);
if (dirtyV.isValid() && dirtyV.toBool()) {
painter->save();
rect.setWidth(margin / 2);
painter->fillRect(rect, option.palette.brush(QPalette::Active, QPalette::Highlight));
rect.setWidth(option.rect.width());
painter->restore();
}
rect.adjust(margin, margin, -margin, -margin);
QVariant labelV = index.data(Models::Keys::Label);
if (labelV.isValid()) {
QString label = labelV.toString();
if (label.size() > 0) {
painter->save();
painter->setFont(labelFont);
painter->setPen(q);
painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, label, &r);
rect.adjust(0, labelMetrics.lineSpacing(), 0, 0);
maxRight = std::max(r.width(), maxRight);
painter->restore();
}
}
QVariant fingerPrintV = index.data(Models::Keys::FingerPrint);
if (fingerPrintV.isValid()) {
painter->save();
painter->setFont(fingerPrintFont);
QByteArray fingerPrint = fingerPrintV.toByteArray();
std::vector<QString> parts = getParts(fingerPrint);
uint8_t partsLength = parts.size();
uint8_t firstLine;
if (partsLength > maxSingleLineParts)
firstLine = partsLength / 2 + partsLength % 2;
else
firstLine = partsLength;
for (uint8_t i = 0; i < partsLength; ++i) {
if (i == firstLine) {
maxRight = std::max(rect.left() - leftOrigin - margin, maxRight);
rect.setLeft(leftOrigin);
rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0);
}
painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, parts[i], &r);
rect.adjust(r.width() + spaceWidth ,0, 0, 0);
}
maxRight = std::max(rect.left() - leftOrigin - margin, maxRight);
rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0);
rect.setLeft(leftOrigin);
painter->restore();
}
QVariant lastV = index.data(Models::Keys::LastInteraction);
if (lastV.isValid()) {
QDateTime last = lastV.toDateTime();
if (last.isValid()) {
painter->save();
painter->setFont(labelFont);
painter->setPen(q);
painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, last.toString(), &r);
rect.adjust(0, labelMetrics.lineSpacing(), 0, 0);
maxRight = std::max(r.width(), maxRight);
painter->restore();
}
}
QVariant levelV = index.data(Models::Keys::TrustLevel);
if (levelV.isValid()) {
Shared::TrustLevel level = static_cast<Shared::TrustLevel>(levelV.toUInt());
QString levelName = Shared::Global::getName(level);
if (maxRight > 0)
maxRight += margin;
rect.setLeft(leftOrigin + maxRight);
rect.setTop(option.rect.top() + maxRight);
rect.setBottom(option.rect.bottom() - maxRight);
painter->setFont(defaultFont);
painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, levelName, &r);
}
painter->restore();
}
QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
QSize size = QStyledItemDelegate::sizeHint(option, index);
int mh = margin * 2;
int mw = margin * 2;
int width = 0;
QVariant labelV = index.data(Models::Keys::Label);
if (labelV.isValid()) {
QString label = labelV.toString();
if (label.size() > 0) {
mh += labelMetrics.lineSpacing();
width = labelMetrics.horizontalAdvance(label);
}
}
QVariant fingerPrintV = index.data(Models::Keys::FingerPrint);
if (fingerPrintV.isValid()) {
QString hex = fingerPrintV.toByteArray().toHex();
uint8_t parts = hex.size() / partSize;
mh += fingerPrintMetrics.height();
if (parts > maxSingleLineParts)
mh += fingerPrintMetrics.height() + fingerPrintMetrics.leading();
uint8_t firstLine;
if (parts > maxSingleLineParts)
firstLine = parts / 2 + parts % 2;
else
firstLine = parts;
width = std::max(width, firstLine * fingerPrintMetrics.horizontalAdvance(hex, partSize) + (firstLine - 1) * spaceWidth);
width += 1; //there is a mistake somewhere, this the cheapest way to compensate it
}
QVariant lastV = index.data(Models::Keys::LastInteraction);
if (lastV.isValid()) {
QDateTime last = lastV.toDateTime();
if (last.isValid()) {
mh += labelMetrics.lineSpacing();
QString dt = last.toString();
width = std::max(labelMetrics.horizontalAdvance(dt), width);
}
}
QVariant levelV = index.data(Models::Keys::TrustLevel);
if (levelV.isValid()) {
Shared::TrustLevel level = static_cast<Shared::TrustLevel>(levelV.toUInt());
QString levelName = Shared::Global::getName(level);
if (width > 0)
width += margin;
width += defaultMetrics.horizontalAdvance(levelName);
width += 1; //there is a mistake somewhere, this the cheapest way to compensate it
}
mw += width;
if (size.width() < mw)
size.setWidth(mw);
if (size.height() < mh)
size.setHeight(mh);
return size;
}
std::vector<QString> UI::KeyDelegate::getParts(const QByteArray& data) {
QString hex = data.toHex();
uint8_t parts = hex.size() / partSize;
std::vector<QString> result(parts);
for (uint8_t i = 0; i < parts; ++i) {
uint16_t index = i * partSize;
uint8_t length = partSize;
if (index + length > hex.size())
length = hex.size() - index;
QStringRef part(&hex, index, length);
result[i] = part.toString();
}
return result;
}

View file

@ -0,0 +1,50 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free 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
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef UI_KEYDELEGATE_H
#define UI_KEYDELEGATE_H
#include <QStyledItemDelegate>
#include <QString>
#include <vector>
namespace UI {
class KeyDelegate : public QStyledItemDelegate
{
public:
KeyDelegate(QObject *parent = nullptr);
~KeyDelegate();
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const override;
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
private:
const QFont& defaultFont;
const QFont& fingerPrintFont;
const QFont& labelFont;
const QFontMetrics& defaultMetrics;
const QFontMetrics& fingerPrintMetrics;
const QFontMetrics& labelMetrics;
int spaceWidth;
private:
static std::vector<QString> getParts(const QByteArray& data);
};
}
#endif // UI_KEYDELEGATE_H

View file

@ -0,0 +1,111 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free 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
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "omemo.h"
#include "ui_omemo.h"
#include <random>
constexpr uint8_t fingerprintLength = 32;
Omemo::Omemo(QWidget* parent):
QWidget(parent),
m_ui(new Ui::Omemo()),
keysDelegate(),
unusedKeysDelegate(),
keysModel(),
unusedKeysModel(),
contextMenu(new QMenu())
{
m_ui->setupUi(this);
generateMockData();
m_ui->keysView->setItemDelegate(&keysDelegate);
m_ui->keysView->setModel(&keysModel);
m_ui->unusedKeysView->setItemDelegate(&unusedKeysDelegate);
m_ui->unusedKeysView->setModel(&unusedKeysModel);
m_ui->keysView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui->keysView, &QWidget::customContextMenuRequested, this, &Omemo::onActiveKeysContextMenu);
}
Omemo::~Omemo()
{
contextMenu->deleteLater();
}
void Omemo::generateMockData() {
std::random_device rd;
std::uniform_int_distribution<char> dist(CHAR_MIN, CHAR_MAX);
for (int i = 0; i < 5; ++i) {
QByteArray fp(fingerprintLength, 0);
for (int i = 0; i < fingerprintLength; ++i) {
fp[i] = dist(rd);
}
Shared::KeyInfo info;
info.id = i;
if (i % 3 == 0)
info.label = QString("test_") + std::to_string(i).c_str();
info.fingerPrint = fp;
if (i % 2 == 0)
info.lastInteraction = QDateTime::currentDateTime();
keysModel.addKey(info);
}
}
void Omemo::onActiveKeysContextMenu(const QPoint& pos) {
contextMenu->clear();
QModelIndex index = m_ui->keysView->indexAt(pos);
if (index.isValid()) {
QVariant dirtyV = index.data(Models::Keys::Dirty);
if (dirtyV.isValid() && dirtyV.toBool()) {
QAction* rev = contextMenu->addAction(Shared::icon("clean"), tr("Revert changes"));
connect(rev, &QAction::triggered, std::bind(&Models::Keys::revertKey, &keysModel, index.row()));
}
QVariant levelV = index.data(Models::Keys::TrustLevel);
if (levelV.isValid()) {
Shared::TrustLevel level = static_cast<Shared::TrustLevel>(levelV.toUInt());
if (level == Shared::TrustLevel::undecided ||
level == Shared::TrustLevel::automaticallyDistrusted ||
level == Shared::TrustLevel::manuallyDistrusted
) {
QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Trust"));
connect(rev, &QAction::triggered,
std::bind(&Models::Keys::setTrustLevel, &keysModel,
index.row(), Shared::TrustLevel::manuallyTrusted
)
);
}
if (level == Shared::TrustLevel::undecided ||
level == Shared::TrustLevel::automaticallyTrusted ||
level == Shared::TrustLevel::manuallyTrusted ||
level == Shared::TrustLevel::authenticated
) {
QAction* rev = contextMenu->addAction(Shared::icon("unfavorite"), tr("Distrust"));
connect(rev, &QAction::triggered,
std::bind(&Models::Keys::setTrustLevel, &keysModel,
index.row(), Shared::TrustLevel::manuallyDistrusted
)
);
}
}
}
contextMenu->popup(m_ui->keysView->viewport()->mapToGlobal(pos));
}

View file

@ -0,0 +1,54 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free 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
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef VCARD_OMEMO_H
#define VCARD_OMEMO_H
#include <QWidget>
#include <QScopedPointer>
#include <QMenu>
#include "ui/models/info/omemo/keys.h"
#include "keydelegate.h"
#include "shared/icons.h"
namespace Ui
{
class Omemo;
}
class Omemo : public QWidget {
Q_OBJECT
public:
Omemo(QWidget* parent = nullptr);
~Omemo();
private slots:
void onActiveKeysContextMenu(const QPoint& pos);
private:
void generateMockData();
private:
QScopedPointer<Ui::Omemo> m_ui;
UI::KeyDelegate keysDelegate;
UI::KeyDelegate unusedKeysDelegate;
Models::Keys keysModel;
Models::Keys unusedKeysModel;
QMenu* contextMenu;
};
#endif // VCARD_OMEMO_H

View file

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Omemo</class>
<widget class="QWidget" name="Omemo">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>473</width>
<height>657</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="OMEMOHeading">
<property name="font">
<font>
<pointsize>24</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>OMEMO</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>473</width>
<height>592</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="1,3,1">
<item row="1" column="1">
<widget class="ExpandingList" name="keysView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="uniformItemSizes">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="keysHeading">
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Active keys</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="unusedKeysHeading">
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Unused keys</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="ExpandingList" name="unusedKeysView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="uniformItemSizes">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="6">
<spacer name="spacerLeft">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2" rowspan="6">
<spacer name="spacerRight">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ExpandingList</class>
<extends>QListView</extends>
<header location="global">ui/utils/expandinglist.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>