From 100b2e8943dc0bf2a98302982b345dd039e4f287 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 19 Sep 2019 17:31:27 +0300 Subject: [PATCH] some creepy file attaching gui, not sending yet --- ui/CMakeLists.txt | 6 +- ui/utils/badge.cpp | 60 +++++++++ ui/utils/badge.h | 56 +++++++++ ui/utils/flowlayout.cpp | 172 ++++++++++++++++++++++++++ ui/utils/flowlayout.h | 59 +++++++++ ui/{widgets => utils}/message.cpp | 0 ui/{widgets => utils}/message.h | 0 ui/{widgets => utils}/messageline.cpp | 0 ui/{widgets => utils}/messageline.h | 0 ui/widgets/conversation.cpp | 56 ++++++++- ui/widgets/conversation.h | 11 +- ui/widgets/conversation.ui | 10 +- 12 files changed, 424 insertions(+), 6 deletions(-) create mode 100644 ui/utils/badge.cpp create mode 100644 ui/utils/badge.h create mode 100644 ui/utils/flowlayout.cpp create mode 100644 ui/utils/flowlayout.h rename ui/{widgets => utils}/message.cpp (100%) rename ui/{widgets => utils}/message.h (100%) rename ui/{widgets => utils}/messageline.cpp (100%) rename ui/{widgets => utils}/messageline.h (100%) diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index d9ede46..3ca0148 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -25,14 +25,16 @@ set(squawkUI_SRC widgets/conversation.cpp widgets/chat.cpp widgets/room.cpp - widgets/messageline.cpp widgets/newcontact.cpp widgets/accounts.cpp widgets/account.cpp widgets/joinconference.cpp - widgets/message.cpp + utils/messageline.cpp + utils//message.cpp utils/resizer.cpp utils/image.cpp + utils/flowlayout.cpp + utils/badge.cpp ) # Tell CMake to create the helloworld executable diff --git a/ui/utils/badge.cpp b/ui/utils/badge.cpp new file mode 100644 index 0000000..94277f2 --- /dev/null +++ b/ui/utils/badge.cpp @@ -0,0 +1,60 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#include "badge.h" + +Badge::Badge(const QString& p_id, const QString& p_text, const QIcon& icon, QWidget* parent): + QFrame(parent), + id(p_id), + image(new QLabel()), + text(new QLabel(p_text)), + closeButton(new QPushButton()), + layout(new QHBoxLayout(this)) +{ + setBackgroundRole(QPalette::Base); + //setAutoFillBackground(true); + setFrameStyle(QFrame::StyledPanel); + setFrameShadow(QFrame::Raised); + + image->setPixmap(icon.pixmap(25, 25)); + closeButton->setIcon(QIcon::fromTheme("tab-close")); + closeButton->setMaximumHeight(25); + closeButton->setMaximumWidth(25); + + layout->addWidget(image); + layout->addWidget(text); + layout->addWidget(closeButton); + + layout->setContentsMargins(2, 2, 2, 2); + + connect(closeButton, SIGNAL(clicked()), this, SIGNAL(close())); +} + +Badge::~Badge() +{ +} + +bool Badge::Comparator::operator()(const Badge* a, const Badge* b) const +{ + return a->id < b->id; +} + +bool Badge::Comparator::operator()(const Badge& a, const Badge& b) const +{ + return a.id < b.id; +} diff --git a/ui/utils/badge.h b/ui/utils/badge.h new file mode 100644 index 0000000..93a7f00 --- /dev/null +++ b/ui/utils/badge.h @@ -0,0 +1,56 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#ifndef BADGE_H +#define BADGE_H + +#include +#include +#include +#include +#include + +/** + * @todo write docs + */ +class Badge : public QFrame +{ + Q_OBJECT +public: + Badge(const QString& id, const QString& text, const QIcon& icon, QWidget* parent = nullptr); + ~Badge(); + + const QString id; + +signals: + void close(); + +private: + QLabel* image; + QLabel* text; + QPushButton* closeButton; + QHBoxLayout* layout; + +public: + struct Comparator { + bool operator()(const Badge& a, const Badge& b) const; + bool operator()(const Badge* a, const Badge* b) const; + }; +}; + +#endif // BADGE_H diff --git a/ui/utils/flowlayout.cpp b/ui/utils/flowlayout.cpp new file mode 100644 index 0000000..ed53f51 --- /dev/null +++ b/ui/utils/flowlayout.cpp @@ -0,0 +1,172 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#include "flowlayout.h" + +FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing): + QLayout(parent), + m_hSpace(hSpacing), + m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing): + m_hSpace(hSpacing), + m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +FlowLayout::~FlowLayout() +{ + QLayoutItem *item; + while ((item = takeAt(0))) { + delete item; + } +} + +void FlowLayout::addItem(QLayoutItem *item) +{ + itemList.append(item); +} + +int FlowLayout::horizontalSpacing() const +{ + if (m_hSpace >= 0) { + return m_hSpace; + } else { + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + } +} + +int FlowLayout::verticalSpacing() const +{ + if (m_vSpace >= 0) { + return m_vSpace; + } else { + return smartSpacing(QStyle::PM_LayoutVerticalSpacing); + } +} + +int FlowLayout::count() const +{ + return itemList.size(); +} + +QLayoutItem *FlowLayout::itemAt(int index) const +{ + return itemList.value(index); +} + +QLayoutItem *FlowLayout::takeAt(int index) +{ + if (index >= 0 && index < itemList.size()) { + return itemList.takeAt(index); + } + return nullptr; +} + +Qt::Orientations FlowLayout::expandingDirections() const +{ + return 0; +} + +bool FlowLayout::hasHeightForWidth() const +{ + return true; +} + +int FlowLayout::heightForWidth(int width) const +{ + int height = doLayout(QRect(0, 0, width, 0), true); + return height; +} + +void FlowLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize FlowLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize FlowLayout::minimumSize() const +{ + QSize size; + for (const QLayoutItem *item : qAsConst(itemList)) { + size = size.expandedTo(item->minimumSize()); + } + + const QMargins margins = contentsMargins(); + size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); + return size; +} + +int FlowLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; + + for (QLayoutItem *item : qAsConst(itemList)) { + const QWidget *wid = item->widget(); + int spaceX = horizontalSpacing(); + if (spaceX == -1) { + spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + } + int spaceY = verticalSpacing(); + if (spaceY == -1) { + spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); + } + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + } + + if (!testOnly) { + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + } + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + } + return y + lineHeight - rect.y() + bottom; +} + +int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const +{ + QObject *parent = this->parent(); + if (!parent) { + return -1; + } else if (parent->isWidgetType()) { + QWidget *pw = static_cast(parent); + return pw->style()->pixelMetric(pm, nullptr, pw); + } else { + return static_cast(parent)->spacing(); + } +} diff --git a/ui/utils/flowlayout.h b/ui/utils/flowlayout.h new file mode 100644 index 0000000..0e52c87 --- /dev/null +++ b/ui/utils/flowlayout.h @@ -0,0 +1,59 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#ifndef FLOWLAYOUT_H +#define FLOWLAYOUT_H + +#include +#include +#include + +/** + * @todo write docs + */ +class FlowLayout : public QLayout +{ + Q_OBJECT +public: + explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); + explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~FlowLayout(); + + void addItem(QLayoutItem *item) override; + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const override; + bool hasHeightForWidth() const override; + int heightForWidth(int) const override; + int count() const override; + QLayoutItem *itemAt(int index) const override; + QSize minimumSize() const override; + void setGeometry(const QRect &rect) override; + QSize sizeHint() const override; + QLayoutItem *takeAt(int index) override; + +private: + int doLayout(const QRect &rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + + QList itemList; + int m_hSpace; + int m_vSpace; +}; + +#endif // FLOWLAYOUT_H diff --git a/ui/widgets/message.cpp b/ui/utils/message.cpp similarity index 100% rename from ui/widgets/message.cpp rename to ui/utils/message.cpp diff --git a/ui/widgets/message.h b/ui/utils/message.h similarity index 100% rename from ui/widgets/message.h rename to ui/utils/message.h diff --git a/ui/widgets/messageline.cpp b/ui/utils/messageline.cpp similarity index 100% rename from ui/widgets/messageline.cpp rename to ui/utils/messageline.cpp diff --git a/ui/widgets/messageline.h b/ui/utils/messageline.h similarity index 100% rename from ui/widgets/messageline.h rename to ui/utils/messageline.h diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 9210d9e..d661a5c 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -23,6 +23,7 @@ #include #include #include +#include Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, const QString pJid, const QString pRes, const QString& acc, QWidget* parent): QWidget(parent), @@ -40,12 +41,18 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co thread(), statusIcon(0), statusLabel(0), + filesLayout(0), + filesToAttach(), scroll(down), manualSliderChange(false), requestingHistory(false), everShown(false) { m_ui->setupUi(this); + + filesLayout = new FlowLayout(m_ui->filesPanel, 0); + m_ui->filesPanel->setLayout(filesLayout); + m_ui->splitter->setSizes({300, 0}); m_ui->splitter->setStretchFactor(1, 0); @@ -60,7 +67,7 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co connect(line, SIGNAL(resize(int)), this, SLOT(onMessagesResize(int))); connect(line, SIGNAL(downloadFile(const QString&, const QString&)), this, SIGNAL(downloadFile(const QString&, const QString&))); connect(line, SIGNAL(requestLocalFile(const QString&, const QString&)), this, SIGNAL(requestLocalFile(const QString&, const QString&))); - //connect(m_ui->attachButton, SIGNAL(clicked(bool)), this, SLOT(onAttach())); + connect(m_ui->attachButton, SIGNAL(clicked(bool)), this, SLOT(onAttach())); m_ui->messageEditor->installEventFilter(&ker); @@ -263,7 +270,9 @@ void Conversation::onFileSelected() { QFileDialog* d = static_cast(sender()); - qDebug() << d->selectedFiles(); + for (const QString& path : d->selectedFiles()) { + addAttachedFile(path); + } d->deleteLater(); } @@ -300,6 +309,49 @@ void Conversation::responseLocalFile(const QString& messageId, const QString& pa line->responseLocalFile(messageId, path); } +void Conversation::addAttachedFile(const QString& path) +{ + QMimeDatabase db; + QMimeType type = db.mimeTypeForFile(path); + QFileInfo info(path); + + Badge* badge = new Badge(path, info.fileName(), QIcon::fromTheme(type.iconName())); + + connect(badge, SIGNAL(close()), this, SLOT(onBadgeClose())); + filesToAttach.push_back(badge); //TODO neet to check if there are any duplicated ids + filesLayout->addWidget(badge); + if (filesLayout->count() == 1) { + filesLayout->setContentsMargins(3, 3, 3, 3); + } +} + +void Conversation::removeAttachedFile(Badge* badge) +{ + W::Order::const_iterator itr = filesToAttach.find(badge); + if (itr != filesToAttach.end()) { + filesToAttach.erase(badge); + if (filesLayout->count() == 1) { + filesLayout->setContentsMargins(0, 0, 0, 0); + } + badge->deleteLater(); + } +} + +void Conversation::onBadgeClose() +{ + Badge* badge = static_cast(sender()); + removeAttachedFile(badge); +} + +void Conversation::clearAttachedFiles() +{ + for (Badge* badge : filesToAttach) { + badge->deleteLater(); + } + filesToAttach.clear(); + filesLayout->setContentsMargins(0, 0, 0, 0); +} + bool VisibilityCatcher::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::Show) { diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 0a2fa41..5bc2d33 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -22,8 +22,11 @@ #include #include #include "../../global.h" -#include "messageline.h" +#include "../../order.h" +#include "../utils/messageline.h" #include "../utils/resizer.h" +#include "../utils/flowlayout.h" +#include "../utils/badge.h" namespace Ui { @@ -87,6 +90,9 @@ protected: void applyVisualEffects(); virtual void handleSendMessage(const QString& text) = 0; void setStatus(const QString& status); + void addAttachedFile(const QString& path); + void removeAttachedFile(Badge* badge); + void clearAttachedFiles(); protected slots: void onEnterPressed(); @@ -95,6 +101,7 @@ protected slots: void onAttach(); void onFileSelected(); void onScrollResize(); + void onBadgeClose(); public: const bool isMuc; @@ -118,6 +125,8 @@ protected: QString thread; QLabel* statusIcon; QLabel* statusLabel; + FlowLayout* filesLayout; + W::Order filesToAttach; Scroll scroll; bool manualSliderChange; bool requestingHistory; diff --git a/ui/widgets/conversation.ui b/ui/widgets/conversation.ui index 5ae4a95..0298603 100644 --- a/ui/widgets/conversation.ui +++ b/ui/widgets/conversation.ui @@ -202,7 +202,7 @@ 0 0 572 - 123 + 118 @@ -384,6 +384,13 @@ + + + + true + + + @@ -443,6 +450,7 @@ messageEditor ut + filesPanel panel