mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
refine attachment list
- Add shortcut Ctrl+E A to show attachment list; - Add Vim-like navigation shortcut to attachment list; - Support drag-and-drop to add attachments; - Add bubble to indicate the number of attachments at the right top corner;
This commit is contained in:
parent
e5cd014762
commit
fb4e818e20
@ -1,14 +0,0 @@
|
||||
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<title>Layer 1</title>
|
||||
<g id="Icon_3_">
|
||||
<g id="svg_1">
|
||||
<path d="m341.334,128l0,234.666c0,46.938 -38.396,85.334 -85.334,85.334c-46.937,0 -85.333,-38.396 -85.333,-85.334l0,-245.332c0,-29.865 23.468,-53.334 53.333,-53.334c29.864,0 53.333,23.469 53.333,53.334l0,245.333c0,11.729 -9.605,21.333 -21.334,21.333c-11.729,0 -21.333,-9.604 -21.333,-21.333l0,-202.667l-32,0l0,202.667c0.001,29.864 23.469,53.333 53.334,53.333c29.865,0 53.334,-23.469 53.334,-53.333l0,-245.333c0,-46.933 -38.396,-85.334 -85.334,-85.334c-46.938,0 -85.334,38.401 -85.334,85.334l0,245.332c0.001,65.063 52.272,117.334 117.334,117.334c65.062,0 117.334,-52.271 117.334,-117.334l0,-234.666l-32,0z" id="svg_2"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<title>Layer 2</title>
|
||||
<circle stroke="#000000" fill="#15ae67" stroke-width="5" stroke-opacity="0" cx="435.5" cy="75.50001" r="70.05334" id="svg_3"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1001 B |
@ -28,6 +28,7 @@ void VAttachmentList::setupUI()
|
||||
m_addBtn = new QPushButton(QIcon(":/resources/icons/add_attachment.svg"), "");
|
||||
m_addBtn->setToolTip(tr("Add"));
|
||||
m_addBtn->setProperty("FlatBtn", true);
|
||||
m_addBtn->setDefault(true);
|
||||
connect(m_addBtn, &QPushButton::clicked,
|
||||
this, &VAttachmentList::addAttachment);
|
||||
|
||||
@ -68,6 +69,8 @@ void VAttachmentList::setupUI()
|
||||
}
|
||||
|
||||
m_attachmentList->clear();
|
||||
|
||||
updateButtonState();
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -138,7 +141,8 @@ void VAttachmentList::initActions()
|
||||
void VAttachmentList::setFile(VNoteFile *p_file)
|
||||
{
|
||||
m_file = p_file;
|
||||
updateContent();
|
||||
|
||||
updateButtonState();
|
||||
}
|
||||
|
||||
void VAttachmentList::updateContent()
|
||||
@ -169,8 +173,13 @@ void VAttachmentList::updateContent()
|
||||
int cnt = m_attachmentList->count();
|
||||
if (cnt > 0) {
|
||||
m_numLabel->setText(tr("%1 %2").arg(cnt).arg(cnt > 1 ? tr("Files") : tr("File")));
|
||||
m_attachmentList->setFocus();
|
||||
} else {
|
||||
m_numLabel->setText("");
|
||||
|
||||
if (m_file) {
|
||||
m_addBtn->setFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,13 +213,23 @@ void VAttachmentList::addAttachment()
|
||||
// Update lastPath
|
||||
lastPath = QFileInfo(files[0]).path();
|
||||
|
||||
addAttachments(files);
|
||||
|
||||
updateButtonState();
|
||||
|
||||
updateContent();
|
||||
}
|
||||
|
||||
void VAttachmentList::addAttachments(const QStringList &p_files)
|
||||
{
|
||||
Q_ASSERT(m_file);
|
||||
int addedFiles = 0;
|
||||
for (int i = 0; i < files.size(); ++i) {
|
||||
if (!m_file->addAttachment(files[i])) {
|
||||
for (int i = 0; i < p_files.size(); ++i) {
|
||||
if (!m_file->addAttachment(p_files[i])) {
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Fail to add attachment %1 for note <span style=\"%2\">%3</span>.")
|
||||
.arg(files[i])
|
||||
.arg(p_files[i])
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(m_file->getName()),
|
||||
"",
|
||||
@ -222,8 +241,6 @@ void VAttachmentList::addAttachment()
|
||||
}
|
||||
}
|
||||
|
||||
updateContent();
|
||||
|
||||
if (addedFiles > 0) {
|
||||
g_vnote->getMainWindow()->showStatusMessage(tr("Added %1 %2 as attachments")
|
||||
.arg(addedFiles)
|
||||
@ -322,6 +339,8 @@ void VAttachmentList::deleteSelectedItems()
|
||||
g_vnote->getMainWindow());
|
||||
}
|
||||
|
||||
updateButtonState();
|
||||
|
||||
updateContent();
|
||||
}
|
||||
}
|
||||
@ -418,3 +437,130 @@ void VAttachmentList::handleListItemCommitData(QWidget *p_itemEdit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VAttachmentList::keyPressEvent(QKeyEvent *p_event)
|
||||
{
|
||||
int key = p_event->key();
|
||||
int modifiers = p_event->modifiers();
|
||||
switch (key) {
|
||||
case Qt::Key_BracketLeft:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
QKeyEvent *escEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Escape,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(this, escEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_J:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
QKeyEvent *downEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(m_attachmentList, downEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_K:
|
||||
{
|
||||
if (modifiers == Qt::ControlModifier) {
|
||||
QKeyEvent *upEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up,
|
||||
Qt::NoModifier);
|
||||
QCoreApplication::postEvent(m_attachmentList, upEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
QWidget::keyPressEvent(p_event);
|
||||
}
|
||||
|
||||
bool VAttachmentList::isAcceptDrops() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VAttachmentList::handleDragEnterEvent(QDragEnterEvent *p_event)
|
||||
{
|
||||
if (!m_file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_event->mimeData()->hasFormat("text/uri-list")) {
|
||||
p_event->acceptProposedAction();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VAttachmentList::handleDropEvent(QDropEvent *p_event)
|
||||
{
|
||||
if (!m_file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QMimeData *mime = p_event->mimeData();
|
||||
if (mime->hasFormat("text/uri-list") && mime->hasUrls()) {
|
||||
// Add attachments.
|
||||
QStringList files;
|
||||
QList<QUrl> urls = mime->urls();
|
||||
for (int i = 0; i < urls.size(); ++i) {
|
||||
QString file;
|
||||
if (urls[i].isLocalFile()) {
|
||||
file = urls[i].toLocalFile();
|
||||
QFileInfo fi(file);
|
||||
if (fi.exists() && fi.isFile()) {
|
||||
file = QDir::cleanPath(fi.absoluteFilePath());
|
||||
files.append(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!files.isEmpty()) {
|
||||
addAttachments(files);
|
||||
|
||||
updateButtonState();
|
||||
}
|
||||
|
||||
p_event->acceptProposedAction();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void VAttachmentList::handleAboutToShow()
|
||||
{
|
||||
updateContent();
|
||||
}
|
||||
|
||||
void VAttachmentList::updateButtonState() const
|
||||
{
|
||||
VButtonWithWidget *btn = getButton();
|
||||
Q_ASSERT(btn);
|
||||
if (!btn) {
|
||||
return;
|
||||
}
|
||||
|
||||
int numOfAttachments = -1;
|
||||
if (m_file) {
|
||||
numOfAttachments = m_file->getAttachments().size();
|
||||
if (numOfAttachments == 0) {
|
||||
numOfAttachments = -1;
|
||||
}
|
||||
}
|
||||
|
||||
btn->setBubbleNumber(numOfAttachments);
|
||||
}
|
||||
|
@ -3,7 +3,9 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
#include <QStringList>
|
||||
#include "vnotefile.h"
|
||||
#include "vbuttonwithwidget.h"
|
||||
|
||||
class QPushButton;
|
||||
class QListWidget;
|
||||
@ -12,14 +14,29 @@ class QLabel;
|
||||
class VNoteFile;
|
||||
class QAction;
|
||||
|
||||
class VAttachmentList : public QWidget
|
||||
class VAttachmentList : public QWidget, public VButtonPopupWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VAttachmentList(QWidget *p_parent = 0);
|
||||
|
||||
// Need to call updateContent() to update the list.
|
||||
void setFile(VNoteFile *p_file);
|
||||
|
||||
// Update attachment info of m_file.
|
||||
void updateContent();
|
||||
|
||||
bool isAcceptDrops() const Q_DECL_OVERRIDE;
|
||||
|
||||
bool handleDragEnterEvent(QDragEnterEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
bool handleDropEvent(QDropEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
void handleAboutToShow() Q_DECL_OVERRIDE;
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void addAttachment();
|
||||
|
||||
@ -38,11 +55,13 @@ private:
|
||||
|
||||
void initActions();
|
||||
|
||||
// Update attachment info of m_file.
|
||||
void updateContent();
|
||||
|
||||
void fillAttachmentList(const QVector<VAttachment> &p_attachments);
|
||||
|
||||
void addAttachments(const QStringList &p_files);
|
||||
|
||||
// Update the state of VButtonWithWidget.
|
||||
void updateButtonState() const;
|
||||
|
||||
QPushButton *m_addBtn;
|
||||
QPushButton *m_clearBtn;
|
||||
QPushButton *m_locateBtn;
|
||||
|
@ -1,6 +1,13 @@
|
||||
#include "vbuttonwithwidget.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QDropEvent>
|
||||
#include <QMimeData>
|
||||
#include <QRect>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QBrush>
|
||||
|
||||
VButtonWithWidget::VButtonWithWidget(QWidget *p_widget,
|
||||
QWidget *p_parent)
|
||||
@ -30,6 +37,9 @@ void VButtonWithWidget::init()
|
||||
{
|
||||
m_popupWidget->setParent(this);
|
||||
|
||||
m_bubbleFg = QColor(Qt::white);
|
||||
m_bubbleBg = QColor("#15AE67");
|
||||
|
||||
QMenu *menu = new QMenu(this);
|
||||
VButtonWidgetAction *act = new VButtonWidgetAction(m_popupWidget, menu);
|
||||
menu->addAction(act);
|
||||
@ -39,6 +49,16 @@ void VButtonWithWidget::init()
|
||||
});
|
||||
|
||||
setMenu(menu);
|
||||
|
||||
VButtonPopupWidget *popup = getButtonPopupWidget();
|
||||
if (popup) {
|
||||
popup->setButton(this);
|
||||
setAcceptDrops(popup->isAcceptDrops());
|
||||
connect(this, &VButtonWithWidget::popupWidgetAboutToShow,
|
||||
this, [this]() {
|
||||
getButtonPopupWidget()->handleAboutToShow();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *VButtonWithWidget::getPopupWidget() const
|
||||
@ -50,3 +70,64 @@ void VButtonWithWidget::showPopupWidget()
|
||||
{
|
||||
showMenu();
|
||||
}
|
||||
|
||||
void VButtonWithWidget::dragEnterEvent(QDragEnterEvent *p_event)
|
||||
{
|
||||
VButtonPopupWidget *popup = getButtonPopupWidget();
|
||||
Q_ASSERT(popup);
|
||||
|
||||
if (popup->handleDragEnterEvent(p_event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QPushButton::dragEnterEvent(p_event);
|
||||
}
|
||||
|
||||
void VButtonWithWidget::dropEvent(QDropEvent *p_event)
|
||||
{
|
||||
VButtonPopupWidget *popup = getButtonPopupWidget();
|
||||
Q_ASSERT(popup);
|
||||
|
||||
if (popup->handleDropEvent(p_event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QPushButton::dropEvent(p_event);
|
||||
}
|
||||
|
||||
void VButtonWithWidget::paintEvent(QPaintEvent *p_event)
|
||||
{
|
||||
QPushButton::paintEvent(p_event);
|
||||
|
||||
if (!isEnabled() || m_bubbleStr.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QRect re = rect();
|
||||
int bubbleWidth = re.width() * 3.0 / 7;
|
||||
int x = re.width() - bubbleWidth;
|
||||
int y = 0;
|
||||
QRect bubbleRect(x, y, bubbleWidth, bubbleWidth);
|
||||
|
||||
QPainter painter(this);
|
||||
QPainterPath bgPath;
|
||||
bgPath.addEllipse(bubbleRect);
|
||||
painter.fillPath(bgPath, m_bubbleBg);
|
||||
|
||||
QFont font = painter.font();
|
||||
font.setPixelSize(bubbleWidth / 1.3);
|
||||
painter.setFont(font);
|
||||
painter.setPen(m_bubbleFg);
|
||||
painter.drawText(bubbleRect, Qt::AlignCenter, m_bubbleStr);
|
||||
}
|
||||
|
||||
void VButtonWithWidget::setBubbleNumber(int p_num)
|
||||
{
|
||||
if (p_num < 0) {
|
||||
m_bubbleStr.clear();
|
||||
} else {
|
||||
m_bubbleStr = QString::number(p_num);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
@ -6,6 +6,39 @@
|
||||
#include <QIcon>
|
||||
#include <QWidgetAction>
|
||||
|
||||
class QDragEnterEvent;
|
||||
class QDropEvent;
|
||||
class QPaintEvent;
|
||||
class VButtonWithWidget;
|
||||
|
||||
// Abstract class for the widget used by VButtonWithWidget.
|
||||
// Widget need to inherit this class if drag/drop is needed.
|
||||
class VButtonPopupWidget
|
||||
{
|
||||
public:
|
||||
VButtonPopupWidget() : m_btn(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool isAcceptDrops() const = 0;
|
||||
virtual bool handleDragEnterEvent(QDragEnterEvent *p_event) = 0;
|
||||
virtual bool handleDropEvent(QDropEvent *p_event) = 0;
|
||||
virtual void handleAboutToShow() = 0;
|
||||
|
||||
void setButton(VButtonWithWidget *p_btn)
|
||||
{
|
||||
m_btn = p_btn;
|
||||
}
|
||||
|
||||
VButtonWithWidget *getButton() const
|
||||
{
|
||||
return m_btn;
|
||||
}
|
||||
|
||||
private:
|
||||
VButtonWithWidget *m_btn;
|
||||
};
|
||||
|
||||
class VButtonWidgetAction : public QWidgetAction
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -47,14 +80,42 @@ public:
|
||||
// Show the popup widget.
|
||||
void showPopupWidget();
|
||||
|
||||
// Set the bubble to display a number @p_num.
|
||||
// @p_num: -1 to hide the bubble.
|
||||
void setBubbleNumber(int p_num);
|
||||
|
||||
signals:
|
||||
// Emit when popup widget is about to show.
|
||||
void popupWidgetAboutToShow(QWidget *p_widget);
|
||||
|
||||
protected:
|
||||
// To accept specific drop.
|
||||
void dragEnterEvent(QDragEnterEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
// Drop the data.
|
||||
void dropEvent(QDropEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
void paintEvent(QPaintEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
// Get VButtonWithWidget from m_popupWidget.
|
||||
VButtonPopupWidget *getButtonPopupWidget() const;
|
||||
|
||||
QWidget *m_popupWidget;
|
||||
|
||||
QColor m_bubbleBg;
|
||||
QColor m_bubbleFg;
|
||||
|
||||
// String to display in the bubble.
|
||||
// Empty to hide bubble.
|
||||
QString m_bubbleStr;
|
||||
};
|
||||
|
||||
inline VButtonPopupWidget *VButtonWithWidget::getButtonPopupWidget() const
|
||||
{
|
||||
return dynamic_cast<VButtonPopupWidget *>(m_popupWidget);
|
||||
}
|
||||
|
||||
#endif // VBUTTONWITHWIDGET_H
|
||||
|
@ -171,6 +171,13 @@ bool VCaptain::handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers)
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_A:
|
||||
{
|
||||
// Show attachment list of current note.
|
||||
m_mainWindow->showAttachmentList();
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_D:
|
||||
// Locate current tab.
|
||||
if (m_mainWindow->locateCurrentFile()) {
|
||||
|
@ -976,7 +976,10 @@ void VEditWindow::dragEnterEvent(QDragEnterEvent *p_event)
|
||||
{
|
||||
if (p_event->mimeData()->hasFormat("text/uri-list")) {
|
||||
p_event->acceptProposedAction();
|
||||
return;
|
||||
}
|
||||
|
||||
QTabWidget::dragEnterEvent(p_event);
|
||||
}
|
||||
|
||||
void VEditWindow::dropEvent(QDropEvent *p_event)
|
||||
@ -1004,5 +1007,8 @@ void VEditWindow::dropEvent(QDropEvent *p_event)
|
||||
}
|
||||
|
||||
p_event->acceptProposedAction();
|
||||
return;
|
||||
}
|
||||
|
||||
QTabWidget::dropEvent(p_event);
|
||||
}
|
||||
|
@ -344,16 +344,9 @@ void VMainWindow::initNoteToolBar(QSize p_iconSize)
|
||||
"",
|
||||
m_attachmentList,
|
||||
this);
|
||||
m_attachmentBtn->setToolTip(tr("Attachments"));
|
||||
m_attachmentBtn->setStatusTip(tr("Manage current note's attachments"));
|
||||
m_attachmentBtn->setToolTip(tr("Attachments (drag files here to add attachments)"));
|
||||
m_attachmentBtn->setProperty("CornerBtn", true);
|
||||
m_attachmentBtn->setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
connect(m_attachmentBtn, &VButtonWithWidget::popupWidgetAboutToShow,
|
||||
this, [this]() {
|
||||
m_attachmentList->setFile(dynamic_cast<VNoteFile *>(m_curFile.data()));
|
||||
});
|
||||
|
||||
m_attachmentBtn->setEnabled(false);
|
||||
|
||||
noteToolBar->addWidget(m_attachmentBtn);
|
||||
@ -1552,12 +1545,6 @@ void VMainWindow::updateActionStateFromTabStatusChange(const VFile *p_file,
|
||||
noteInfoAct->setEnabled(p_file && !systemFile);
|
||||
|
||||
m_attachmentBtn->setEnabled(p_file && p_file->getType() == FileType::Note);
|
||||
if (m_attachmentBtn->isEnabled()
|
||||
&& !dynamic_cast<const VNoteFile *>(p_file)->getAttachments().isEmpty()) {
|
||||
m_attachmentBtn->setIcon(QIcon(":/resources/icons/attachment_full.svg"));
|
||||
} else {
|
||||
m_attachmentBtn->setIcon(QIcon(":/resources/icons/attachment.svg"));
|
||||
}
|
||||
|
||||
m_insertImageAct->setEnabled(p_file && p_editMode);
|
||||
|
||||
@ -1589,6 +1576,8 @@ void VMainWindow::handleAreaTabStatusUpdated(const VEditTabInfo &p_info)
|
||||
|
||||
updateActionStateFromTabStatusChange(m_curFile, editMode);
|
||||
|
||||
m_attachmentList->setFile(dynamic_cast<VNoteFile *>(m_curFile.data()));
|
||||
|
||||
QString title;
|
||||
if (m_curFile) {
|
||||
m_findReplaceDialog->updateState(m_curFile->getDocType(), editMode);
|
||||
@ -2193,3 +2182,10 @@ void VMainWindow::showMainWindow()
|
||||
|
||||
this->activateWindow();
|
||||
}
|
||||
|
||||
void VMainWindow::showAttachmentList()
|
||||
{
|
||||
if (m_attachmentBtn->isEnabled()) {
|
||||
m_attachmentBtn->showPopupWidget();
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,9 @@ public:
|
||||
// Show a temporary message in status bar.
|
||||
void showStatusMessage(const QString &p_msg);
|
||||
|
||||
// Popup the attachment list if it is enabled.
|
||||
void showAttachmentList();
|
||||
|
||||
private slots:
|
||||
void importNoteFromFile();
|
||||
void viewSettings();
|
||||
|
@ -124,7 +124,6 @@
|
||||
<file>resources/icons/recycle_bin.svg</file>
|
||||
<file>resources/icons/empty_recycle_bin.svg</file>
|
||||
<file>resources/icons/attachment.svg</file>
|
||||
<file>resources/icons/attachment_full.svg</file>
|
||||
<file>resources/icons/add_attachment.svg</file>
|
||||
<file>resources/icons/clear_attachment.svg</file>
|
||||
<file>resources/icons/locate_attachment.svg</file>
|
||||
|
Loading…
x
Reference in New Issue
Block a user