mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support tags of notes
- Adding/Removing tags of notes; - Auto-completion of tags;
This commit is contained in:
parent
3bd80387fa
commit
ffd653ef55
@ -59,11 +59,18 @@ void VFileInfoDialog::setupUI(const QString &p_title, const QString &p_info)
|
||||
QLabel *modifiedTimeLabel = new QLabel(createdTimeStr);
|
||||
modifiedTimeLabel->setToolTip(tr("Last modified time within VNote"));
|
||||
|
||||
// Tags.
|
||||
QLineEdit *tagEdit = new QLineEdit();
|
||||
tagEdit->setToolTip(tr("Tags of this note separated by ,"));
|
||||
tagEdit->setReadOnly(true);
|
||||
tagEdit->setText(m_file->getTags().join(", "));
|
||||
|
||||
QFormLayout *topLayout = new QFormLayout();
|
||||
topLayout->addRow(tr("Note &name:"), m_nameEdit);
|
||||
topLayout->addRow(tr("Attachment folder:"), attachmentFolderEdit);
|
||||
topLayout->addRow(tr("Created time:"), createdTimeLabel);
|
||||
topLayout->addRow(tr("Modified time:"), modifiedTimeLabel);
|
||||
topLayout->addRow(tr("Tags:"), tagEdit);
|
||||
|
||||
m_warnLabel = new QLabel();
|
||||
m_warnLabel->setWordWrap(true);
|
||||
|
16
src/resources/icons/tags.svg
Normal file
16
src/resources/icons/tags.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<polygon fill="#000000" points="464,64 464,201.377 227.153,467.48 240,480 480,208 480,64 "/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#000000" d="M288,32L32,320l160,160l23.471-23.904l11.348-11.375L448,192V80V64V32H288z M352,160c-17.645,0-32-14.355-32-32
|
||||
s14.355-32,32-32s32,14.355,32,32S369.645,160,352,160z"/>
|
||||
<circle fill="#000000" cx="352" cy="128" r="16"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 851 B |
@ -9,7 +9,7 @@ mdhl_file=v_detorte.mdhl
|
||||
css_file=v_detorte.css
|
||||
codeblock_css_file=v_detorte_codeblock.css
|
||||
mermaid_css_file=v_detorte_mermaid.css
|
||||
version=2
|
||||
version=3
|
||||
|
||||
; This mapping will be used to translate colors when the content of HTML is copied
|
||||
; without background. You could just specify the foreground colors mapping here.
|
||||
@ -108,6 +108,8 @@ vim_indicator_cmd_edit_pending_bg=@selected_bg
|
||||
|
||||
; VTabIndicator.
|
||||
tab_indicator_label_fg=@base_fg
|
||||
tab_indicator_tag_label_fg=#222222
|
||||
tab_indicator_tag_label_bg=#ce93db
|
||||
|
||||
; Html template.
|
||||
template_title_flash_light_fg=@master_light_bg
|
||||
|
@ -568,6 +568,15 @@ QLabel[MenuSeparator="true"] {
|
||||
border-top: 1px solid @menu_separator_bg
|
||||
}
|
||||
|
||||
QLabel[TagLabel="true"] {
|
||||
padding-left: $5px;
|
||||
padding-right: $5px;
|
||||
color: @tab_indicator_tag_label_fg;
|
||||
border-top-left-radius: $10px $8px;
|
||||
border-bottom-left-radius: $10px $8px;
|
||||
background-color: @tab_indicator_tag_label_bg;
|
||||
}
|
||||
|
||||
VVimIndicator QLabel[VimIndicatorKeyLabel="true"] {
|
||||
font: bold;
|
||||
color: @vim_indicator_key_label_fg;
|
||||
|
@ -7,7 +7,7 @@ mdhl_file=v_moonlight.mdhl
|
||||
css_file=v_moonlight.css
|
||||
codeblock_css_file=v_moonlight_codeblock.css
|
||||
mermaid_css_file=v_moonlight_mermaid.css
|
||||
version=13
|
||||
version=14
|
||||
|
||||
; This mapping will be used to translate colors when the content of HTML is copied
|
||||
; without background. You could just specify the foreground colors mapping here.
|
||||
@ -106,6 +106,8 @@ vim_indicator_cmd_edit_pending_bg=@selected_bg
|
||||
|
||||
; VTabIndicator.
|
||||
tab_indicator_label_fg=@base_fg
|
||||
tab_indicator_tag_label_fg=#222222
|
||||
tab_indicator_tag_label_bg=#ce93db
|
||||
|
||||
; Html template.
|
||||
template_title_flash_light_fg=@master_light_bg
|
||||
|
@ -568,6 +568,15 @@ QLabel[MenuSeparator="true"] {
|
||||
border-top: 1px solid @menu_separator_bg
|
||||
}
|
||||
|
||||
QLabel[TagLabel="true"] {
|
||||
padding-left: $5px;
|
||||
padding-right: $5px;
|
||||
color: @tab_indicator_tag_label_fg;
|
||||
border-top-left-radius: $10px $8px;
|
||||
border-bottom-left-radius: $10px $8px;
|
||||
background-color: @tab_indicator_tag_label_bg;
|
||||
}
|
||||
|
||||
VVimIndicator QLabel[VimIndicatorKeyLabel="true"] {
|
||||
font: bold;
|
||||
color: @vim_indicator_key_label_fg;
|
||||
|
@ -7,7 +7,7 @@ mdhl_file=v_native.mdhl
|
||||
css_file=v_native.css
|
||||
codeblock_css_file=v_native_codeblock.css
|
||||
mermaid_css_file=v_native_mermaid.css
|
||||
version=11
|
||||
version=12
|
||||
|
||||
[phony]
|
||||
; Abstract color attributes.
|
||||
@ -74,6 +74,8 @@ vim_indicator_cmd_edit_pending_bg=@selected_bg
|
||||
|
||||
; VTabIndicator.
|
||||
tab_indicator_label_fg=@base_fg
|
||||
tab_indicator_tag_label_fg=#222222
|
||||
tab_indicator_tag_label_bg=#ce93db
|
||||
|
||||
; Html template.
|
||||
template_title_flash_light_fg=#80CBC4
|
||||
|
@ -411,6 +411,15 @@ QLabel[MenuSeparator="true"] {
|
||||
border-top: 1px solid @menu_separator_bg
|
||||
}
|
||||
|
||||
QLabel[TagLabel="true"] {
|
||||
padding-left: $5px;
|
||||
padding-right: $5px;
|
||||
color: @tab_indicator_tag_label_fg;
|
||||
border-top-left-radius: $10px $8px;
|
||||
border-bottom-left-radius: $10px $8px;
|
||||
background-color: @tab_indicator_tag_label_bg;
|
||||
}
|
||||
|
||||
VVimIndicator QLabel[VimIndicatorKeyLabel="true"] {
|
||||
font: bold;
|
||||
color: @vim_indicator_key_label_fg;
|
||||
|
@ -7,7 +7,7 @@ mdhl_file=v_pure.mdhl
|
||||
css_file=v_pure.css
|
||||
codeblock_css_file=v_pure_codeblock.css
|
||||
mermaid_css_file=v_pure_mermaid.css
|
||||
version=12
|
||||
version=13
|
||||
|
||||
[phony]
|
||||
; Abstract color attributes.
|
||||
@ -100,6 +100,8 @@ vim_indicator_cmd_edit_pending_bg=@selected_bg
|
||||
|
||||
; VTabIndicator.
|
||||
tab_indicator_label_fg=@base_fg
|
||||
tab_indicator_tag_label_fg=#222222
|
||||
tab_indicator_tag_label_bg=#ce93db
|
||||
|
||||
; Html template.
|
||||
template_title_flash_light_fg=@master_light_bg
|
||||
|
@ -568,6 +568,15 @@ QLabel[MenuSeparator="true"] {
|
||||
border-top: 1px solid @menu_separator_bg
|
||||
}
|
||||
|
||||
QLabel[TagLabel="true"] {
|
||||
padding-left: $5px;
|
||||
padding-right: $5px;
|
||||
color: @tab_indicator_tag_label_fg;
|
||||
border-top-left-radius: $10px $8px;
|
||||
border-bottom-left-radius: $10px $8px;
|
||||
background-color: @tab_indicator_tag_label_bg;
|
||||
}
|
||||
|
||||
VVimIndicator QLabel[VimIndicatorKeyLabel="true"] {
|
||||
font: bold;
|
||||
color: @vim_indicator_key_label_fg;
|
||||
|
10
src/src.pro
10
src/src.pro
@ -136,7 +136,10 @@ SOURCES += main.cpp\
|
||||
vexplorer.cpp \
|
||||
vlistue.cpp \
|
||||
vuetitlecontentpanel.cpp \
|
||||
utils/vprocessutils.cpp
|
||||
utils/vprocessutils.cpp \
|
||||
vtagpanel.cpp \
|
||||
valltagspanel.cpp \
|
||||
vtaglabel.cpp
|
||||
|
||||
HEADERS += vmainwindow.h \
|
||||
vdirectorytree.h \
|
||||
@ -266,7 +269,10 @@ HEADERS += vmainwindow.h \
|
||||
vexplorerentry.h \
|
||||
vlistue.h \
|
||||
vuetitlecontentpanel.h \
|
||||
utils/vprocessutils.h
|
||||
utils/vprocessutils.h \
|
||||
vtagpanel.h \
|
||||
valltagspanel.h \
|
||||
vtaglabel.h
|
||||
|
||||
RESOURCES += \
|
||||
vnote.qrc \
|
||||
|
55
src/valltagspanel.cpp
Normal file
55
src/valltagspanel.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include "valltagspanel.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "vtaglabel.h"
|
||||
|
||||
VAllTagsPanel::VAllTagsPanel(QWidget *p_parent)
|
||||
: QWidget(p_parent)
|
||||
{
|
||||
m_list = new QListWidget(this);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
layout->addWidget(m_list);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void VAllTagsPanel::clear()
|
||||
{
|
||||
while (m_list->count() > 0) {
|
||||
removeItem(m_list->item(0));
|
||||
}
|
||||
}
|
||||
|
||||
void VAllTagsPanel::removeItem(QListWidgetItem *p_item)
|
||||
{
|
||||
QWidget *wid = m_list->itemWidget(p_item);
|
||||
m_list->removeItemWidget(p_item);
|
||||
wid->deleteLater();
|
||||
|
||||
int row = m_list->row(p_item);
|
||||
Q_ASSERT(row >= 0);
|
||||
m_list->takeItem(row);
|
||||
delete p_item;
|
||||
}
|
||||
|
||||
VTagLabel *VAllTagsPanel::addTag(const QString &p_text)
|
||||
{
|
||||
VTagLabel *label = new VTagLabel(p_text, true, this);
|
||||
QSize sz = label->sizeHint();
|
||||
sz.setHeight(sz.height() * 2 + 10);
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem();
|
||||
item->setSizeHint(sz);
|
||||
|
||||
connect(label, &VTagLabel::removalRequested,
|
||||
this, [this, item](const QString &p_text) {
|
||||
removeItem(item);
|
||||
emit tagRemoved(p_text);
|
||||
});
|
||||
|
||||
m_list->addItem(item);
|
||||
m_list->setItemWidget(item, label);
|
||||
return label;
|
||||
}
|
29
src/valltagspanel.h
Normal file
29
src/valltagspanel.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef VALLTAGSPANEL_H
|
||||
#define VALLTAGSPANEL_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class VTagLabel;
|
||||
|
||||
class VAllTagsPanel : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VAllTagsPanel(QWidget *p_parent = nullptr);
|
||||
|
||||
void clear();
|
||||
|
||||
VTagLabel *addTag(const QString &p_text);
|
||||
|
||||
signals:
|
||||
void tagRemoved(const QString &p_text);
|
||||
|
||||
private:
|
||||
void removeItem(QListWidgetItem *p_item);
|
||||
|
||||
QListWidget *m_list;
|
||||
};
|
||||
|
||||
#endif // VALLTAGSPANEL_H
|
@ -72,6 +72,11 @@ void VButtonWithWidget::showPopupWidget()
|
||||
showMenu();
|
||||
}
|
||||
|
||||
void VButtonWithWidget::hidePopupWidget()
|
||||
{
|
||||
menu()->hide();
|
||||
}
|
||||
|
||||
void VButtonWithWidget::dragEnterEvent(QDragEnterEvent *p_event)
|
||||
{
|
||||
VButtonPopupWidget *popup = getButtonPopupWidget();
|
||||
|
@ -73,6 +73,8 @@ public:
|
||||
// Show the popup widget.
|
||||
void showPopupWidget();
|
||||
|
||||
void hidePopupWidget();
|
||||
|
||||
// Set the bubble to display a number @p_num.
|
||||
// @p_num: -1 to hide the bubble.
|
||||
void setBubbleNumber(int p_num);
|
||||
|
@ -60,6 +60,7 @@ namespace DirConfig
|
||||
static const QString c_imageFolder = "image_folder";
|
||||
static const QString c_attachmentFolder = "attachment_folder";
|
||||
static const QString c_recycleBinFolder = "recycle_bin_folder";
|
||||
static const QString c_tags = "tags";
|
||||
static const QString c_name = "name";
|
||||
static const QString c_createdTime = "created_time";
|
||||
static const QString c_modifiedTime = "modified_time";
|
||||
|
@ -258,5 +258,6 @@
|
||||
<file>resources/themes/v_detorte/v_detorte.qss</file>
|
||||
<file>resources/themes/v_detorte/v_detorte_codeblock.css</file>
|
||||
<file>resources/themes/v_detorte/v_detorte_mermaid.css</file>
|
||||
<file>resources/icons/tags.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QCoreApplication>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include "vdirectory.h"
|
||||
#include "utils/vutils.h"
|
||||
@ -57,6 +58,12 @@ bool VNotebook::readConfigNotebook()
|
||||
m_recycleBinFolder = it.value().toString();
|
||||
}
|
||||
|
||||
// [tags] section.
|
||||
QJsonArray tagsJson = configJson[DirConfig::c_tags].toArray();
|
||||
for (int i = 0; i < tagsJson.size(); ++i) {
|
||||
m_tags.append(tagsJson[i].toString());
|
||||
}
|
||||
|
||||
// [attachment_folder] section.
|
||||
// SHOULD be processed at last.
|
||||
it = configJson.find(DirConfig::c_attachmentFolder);
|
||||
@ -88,6 +95,14 @@ QJsonObject VNotebook::toConfigJsonNotebook() const
|
||||
// [recycle_bin_folder] section.
|
||||
json[DirConfig::c_recycleBinFolder] = m_recycleBinFolder;
|
||||
|
||||
// [tags] section.
|
||||
QJsonArray tags;
|
||||
for (auto const & tag : m_tags) {
|
||||
tags.append(tag);
|
||||
}
|
||||
|
||||
json[DirConfig::c_tags] = tags;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
@ -408,3 +423,22 @@ void VNotebook::updatePath(const QString &p_path)
|
||||
|
||||
readConfigNotebook();
|
||||
}
|
||||
|
||||
bool VNotebook::addTag(const QString &p_tag)
|
||||
{
|
||||
Q_ASSERT(isOpened());
|
||||
|
||||
if (p_tag.isEmpty() || hasTag(p_tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_tags.append(p_tag);
|
||||
if (!writeConfigNotebook()) {
|
||||
qWarning() << "fail to update config of notebook" << m_name
|
||||
<< "in directory" << m_path;
|
||||
m_tags.removeAll(p_tag);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QDateTime>
|
||||
#include <QStringList>
|
||||
|
||||
class VDirectory;
|
||||
class VFile;
|
||||
@ -55,6 +56,12 @@ public:
|
||||
|
||||
void rename(const QString &p_name);
|
||||
|
||||
const QStringList &getTags() const;
|
||||
|
||||
bool addTag(const QString &p_tag);
|
||||
|
||||
bool hasTag(const QString &p_tag) const;
|
||||
|
||||
static VNotebook *createNotebook(const QString &p_name,
|
||||
const QString &p_path,
|
||||
bool p_import,
|
||||
@ -132,6 +139,10 @@ private:
|
||||
// Could be relative or absolute.
|
||||
QString m_recycleBinFolder;
|
||||
|
||||
// List of all the tags of notes.
|
||||
// Used for index and auto-completion.
|
||||
QStringList m_tags;
|
||||
|
||||
// Parent is NULL for root directory
|
||||
VDirectory *m_rootDir;
|
||||
|
||||
@ -170,4 +181,13 @@ inline const QString &VNotebook::getName() const
|
||||
return m_name;
|
||||
}
|
||||
|
||||
inline bool VNotebook::hasTag(const QString &p_tag) const
|
||||
{
|
||||
return m_tags.contains(p_tag);
|
||||
}
|
||||
|
||||
inline const QStringList &VNotebook::getTags() const
|
||||
{
|
||||
return m_tags;
|
||||
}
|
||||
#endif // VNOTEBOOK_H
|
||||
|
@ -15,12 +15,8 @@ VNoteFile::VNoteFile(VDirectory *p_directory,
|
||||
FileType p_type,
|
||||
bool p_modifiable,
|
||||
QDateTime p_createdTimeUtc,
|
||||
QDateTime p_modifiedTimeUtc,
|
||||
const QString &p_attachmentFolder,
|
||||
const QVector<VAttachment> &p_attachments)
|
||||
: VFile(p_directory, p_name, p_type, p_modifiable, p_createdTimeUtc, p_modifiedTimeUtc),
|
||||
m_attachmentFolder(p_attachmentFolder),
|
||||
m_attachments(p_attachments)
|
||||
QDateTime p_modifiedTimeUtc)
|
||||
: VFile(p_directory, p_name, p_type, p_modifiable, p_createdTimeUtc, p_modifiedTimeUtc)
|
||||
{
|
||||
}
|
||||
|
||||
@ -129,24 +125,32 @@ VNoteFile *VNoteFile::fromJson(VDirectory *p_directory,
|
||||
FileType p_type,
|
||||
bool p_modifiable)
|
||||
{
|
||||
// Attachments.
|
||||
QJsonArray attachmentJson = p_json[DirConfig::c_attachments].toArray();
|
||||
QVector<VAttachment> attachments;
|
||||
for (int i = 0; i < attachmentJson.size(); ++i) {
|
||||
QJsonObject attachmentItem = attachmentJson[i].toObject();
|
||||
attachments.push_back(VAttachment(attachmentItem[DirConfig::c_name].toString()));
|
||||
}
|
||||
|
||||
return new VNoteFile(p_directory,
|
||||
VNoteFile *file = new VNoteFile(p_directory,
|
||||
p_json[DirConfig::c_name].toString(),
|
||||
p_type,
|
||||
p_modifiable,
|
||||
QDateTime::fromString(p_json[DirConfig::c_createdTime].toString(),
|
||||
Qt::ISODate),
|
||||
QDateTime::fromString(p_json[DirConfig::c_modifiedTime].toString(),
|
||||
Qt::ISODate),
|
||||
p_json[DirConfig::c_attachmentFolder].toString(),
|
||||
attachments);
|
||||
Qt::ISODate));
|
||||
|
||||
// Attachment Folder.
|
||||
file->m_attachmentFolder = p_json[DirConfig::c_attachmentFolder].toString();
|
||||
|
||||
// Attachments.
|
||||
QJsonArray attachmentJson = p_json[DirConfig::c_attachments].toArray();
|
||||
for (int i = 0; i < attachmentJson.size(); ++i) {
|
||||
QJsonObject attachmentItem = attachmentJson[i].toObject();
|
||||
file->m_attachments.push_back(VAttachment(attachmentItem[DirConfig::c_name].toString()));
|
||||
}
|
||||
|
||||
// Tags.
|
||||
QJsonArray tagsJson = p_json[DirConfig::c_tags].toArray();
|
||||
for (int i = 0; i < tagsJson.size(); ++i) {
|
||||
file->m_tags.append(tagsJson[i].toString());
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
QJsonObject VNoteFile::toConfigJson() const
|
||||
@ -168,6 +172,14 @@ QJsonObject VNoteFile::toConfigJson() const
|
||||
|
||||
item[DirConfig::c_attachments] = attachmentJson;
|
||||
|
||||
// Tags.
|
||||
QJsonArray tags;
|
||||
for (auto const & tag : m_tags) {
|
||||
tags.append(tag);
|
||||
}
|
||||
|
||||
item[DirConfig::c_tags] = tags;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@ -606,3 +618,37 @@ bool VNoteFile::copyInternalImages(const QVector<ImageLink> &p_images,
|
||||
*p_nrImageCopied = nrImageCopied;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VNoteFile::removeTag(const QString &p_tag)
|
||||
{
|
||||
if (p_tag.isEmpty() || m_tags.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int nr = m_tags.removeAll(p_tag);
|
||||
if (nr > 0) {
|
||||
if (!getDirectory()->updateFileConfig(this)) {
|
||||
qWarning() << "fail to update config of file" << m_name
|
||||
<< "in directory" << fetchBasePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool VNoteFile::addTag(const QString &p_tag)
|
||||
{
|
||||
Q_ASSERT(isOpened());
|
||||
|
||||
if (p_tag.isEmpty() || hasTag(p_tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_tags.append(p_tag);
|
||||
if (!getDirectory()->updateFileConfig(this)) {
|
||||
qWarning() << "fail to update config of file" << m_name
|
||||
<< "in directory" << fetchBasePath();
|
||||
m_tags.removeAll(p_tag);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -35,9 +35,7 @@ public:
|
||||
FileType p_type,
|
||||
bool p_modifiable,
|
||||
QDateTime p_createdTimeUtc,
|
||||
QDateTime p_modifiedTimeUtc,
|
||||
const QString &p_attachmentFolder = "",
|
||||
const QVector<VAttachment> &p_attachments = QVector<VAttachment>());
|
||||
QDateTime p_modifiedTimeUtc);
|
||||
|
||||
QString fetchPath() const Q_DECL_OVERRIDE;
|
||||
|
||||
@ -109,6 +107,14 @@ public:
|
||||
// Return the missing attachments' names.
|
||||
QVector<QString> checkAttachments();
|
||||
|
||||
const QStringList &getTags() const;
|
||||
|
||||
void removeTag(const QString &p_tag);
|
||||
|
||||
bool addTag(const QString &p_tag);
|
||||
|
||||
bool hasTag(const QString &p_tag) const;
|
||||
|
||||
// Create a VNoteFile from @p_json Json object.
|
||||
static VNoteFile *fromJson(VDirectory *p_directory,
|
||||
const QJsonObject &p_json,
|
||||
@ -151,6 +157,9 @@ private:
|
||||
|
||||
// Attachments.
|
||||
QVector<VAttachment> m_attachments;
|
||||
|
||||
// Tags of this file.
|
||||
QStringList m_tags;
|
||||
};
|
||||
|
||||
inline const QString &VNoteFile::getAttachmentFolder() const
|
||||
@ -173,4 +182,13 @@ inline void VNoteFile::setAttachments(const QVector<VAttachment> &p_attas)
|
||||
m_attachments = p_attas;
|
||||
}
|
||||
|
||||
inline const QStringList &VNoteFile::getTags() const
|
||||
{
|
||||
return m_tags;
|
||||
}
|
||||
|
||||
inline bool VNoteFile::hasTag(const QString &p_tag) const
|
||||
{
|
||||
return m_tags.contains(p_tag);
|
||||
}
|
||||
#endif // VNOTEFILE_H
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "vbuttonwithwidget.h"
|
||||
#include "vwordcountinfo.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "vtagpanel.h"
|
||||
#include "vnotefile.h"
|
||||
|
||||
VWordCountPanel::VWordCountPanel(QWidget *p_parent)
|
||||
: QWidget(p_parent)
|
||||
@ -99,6 +101,8 @@ VTabIndicator::VTabIndicator(QWidget *p_parent)
|
||||
|
||||
void VTabIndicator::setupUI()
|
||||
{
|
||||
m_tagPanel = new VTagPanel(this);
|
||||
|
||||
m_docTypeLabel = new QLabel(this);
|
||||
m_docTypeLabel->setToolTip(tr("The type of the file"));
|
||||
m_docTypeLabel->setProperty("ColorGreyLabel", true);
|
||||
@ -129,6 +133,7 @@ void VTabIndicator::setupUI()
|
||||
this, &VTabIndicator::updateWordCountInfo);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout(this);
|
||||
mainLayout->addWidget(m_tagPanel);
|
||||
mainLayout->addWidget(m_cursorLabel);
|
||||
mainLayout->addWidget(m_wordCountBtn);
|
||||
mainLayout->addWidget(m_externalLabel);
|
||||
@ -171,7 +176,7 @@ static QString docTypeToString(DocType p_type)
|
||||
|
||||
void VTabIndicator::update(const VEditTabInfo &p_info)
|
||||
{
|
||||
const VFile *file = NULL;
|
||||
VFile *file = NULL;
|
||||
DocType docType = DocType::Html;
|
||||
bool readonly = false;
|
||||
bool external = false;
|
||||
@ -186,7 +191,7 @@ void VTabIndicator::update(const VEditTabInfo &p_info)
|
||||
docType = file->getDocType();
|
||||
readonly = !file->isModifiable();
|
||||
external = file->getType() == FileType::Orphan;
|
||||
system = external && dynamic_cast<const VOrphanFile *>(file)->isSystemFile();
|
||||
system = external && dynamic_cast<VOrphanFile *>(file)->isSystemFile();
|
||||
|
||||
if (m_editTab->isEditMode()) {
|
||||
int line = p_info.m_cursorBlockNumber + 1;
|
||||
@ -209,6 +214,13 @@ void VTabIndicator::update(const VEditTabInfo &p_info)
|
||||
}
|
||||
}
|
||||
|
||||
m_tagPanel->setVisible(!external);
|
||||
if (external) {
|
||||
m_tagPanel->updateTags(NULL);
|
||||
} else {
|
||||
m_tagPanel->updateTags(dynamic_cast<VNoteFile *>(file));
|
||||
}
|
||||
|
||||
updateWordCountBtn(p_info);
|
||||
|
||||
if (p_info.m_wordCountInfo.m_mode == VWordCountInfo::Read) {
|
||||
|
@ -9,6 +9,7 @@ class VButtonWithWidget;
|
||||
class VEditTab;
|
||||
class VWordCountPanel;
|
||||
class QGroupBox;
|
||||
class VTagPanel;
|
||||
|
||||
class VWordCountPanel : public QWidget
|
||||
{
|
||||
@ -54,6 +55,9 @@ private:
|
||||
|
||||
void updateWordCountBtn(const VEditTabInfo &p_info);
|
||||
|
||||
// Tag panel.
|
||||
VTagPanel *m_tagPanel;
|
||||
|
||||
// Indicate the doc type.
|
||||
QLabel *m_docTypeLabel;
|
||||
|
||||
|
96
src/vtaglabel.cpp
Normal file
96
src/vtaglabel.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include "vtaglabel.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "utils/viconutils.h"
|
||||
|
||||
#define MAX_DISPLAY_SIZE_PER_LABEL 5
|
||||
|
||||
VTagLabel::VTagLabel(const QString &p_text,
|
||||
bool p_useFullText,
|
||||
QWidget *p_parent)
|
||||
: QWidget(p_parent),
|
||||
m_useFullText(p_useFullText),
|
||||
m_text(p_text)
|
||||
{
|
||||
setupUI();
|
||||
}
|
||||
|
||||
VTagLabel::VTagLabel(QWidget *p_parent)
|
||||
: QWidget(p_parent),
|
||||
m_useFullText(false)
|
||||
{
|
||||
setupUI();
|
||||
}
|
||||
|
||||
void VTagLabel::setupUI()
|
||||
{
|
||||
m_label = new QLabel(this);
|
||||
m_label->setProperty("TagLabel", true);
|
||||
|
||||
updateLabel();
|
||||
|
||||
m_closeBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/close.svg"), "", this);
|
||||
m_closeBtn->setProperty("StatusBtn", true);
|
||||
m_closeBtn->setToolTip(tr("Remove"));
|
||||
m_closeBtn->setFocusPolicy(Qt::NoFocus);
|
||||
m_closeBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||
m_closeBtn->hide();
|
||||
connect(m_closeBtn, &QPushButton::clicked,
|
||||
this, [this]() {
|
||||
emit removalRequested(m_text);
|
||||
});
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout();
|
||||
layout->addWidget(m_label);
|
||||
layout->addWidget(m_closeBtn);
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void VTagLabel::updateLabel()
|
||||
{
|
||||
QString tag(m_text);
|
||||
|
||||
if (!m_useFullText) {
|
||||
if (tag.size() > MAX_DISPLAY_SIZE_PER_LABEL) {
|
||||
tag.resize(MAX_DISPLAY_SIZE_PER_LABEL);
|
||||
tag += QStringLiteral("...");
|
||||
}
|
||||
}
|
||||
|
||||
m_label->setText(tag);
|
||||
m_label->setToolTip(m_text);
|
||||
}
|
||||
|
||||
void VTagLabel::clear()
|
||||
{
|
||||
m_label->clear();
|
||||
}
|
||||
|
||||
void VTagLabel::setText(const QString &p_text)
|
||||
{
|
||||
m_text = p_text;
|
||||
updateLabel();
|
||||
}
|
||||
|
||||
bool VTagLabel::event(QEvent *p_event)
|
||||
{
|
||||
switch (p_event->type()) {
|
||||
case QEvent::Enter:
|
||||
m_closeBtn->show();
|
||||
break;
|
||||
|
||||
case QEvent::Leave:
|
||||
m_closeBtn->hide();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QWidget::event(p_event);
|
||||
}
|
49
src/vtaglabel.h
Normal file
49
src/vtaglabel.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef VTAGLABEL_H
|
||||
#define VTAGLABEL_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
class VTagLabel : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VTagLabel(const QString &p_text,
|
||||
bool p_useFullText,
|
||||
QWidget *p_parent = nullptr);
|
||||
|
||||
explicit VTagLabel(QWidget *p_parent = nullptr);
|
||||
|
||||
void clear();
|
||||
|
||||
void setText(const QString &p_text);
|
||||
|
||||
const QString &text() const;
|
||||
|
||||
signals:
|
||||
void removalRequested(const QString &p_text);
|
||||
|
||||
protected:
|
||||
virtual bool event(QEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void updateLabel();
|
||||
|
||||
QString m_text;
|
||||
|
||||
bool m_useFullText;
|
||||
|
||||
QLabel *m_label;
|
||||
QPushButton *m_closeBtn;
|
||||
};
|
||||
|
||||
inline const QString &VTagLabel::text() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
|
||||
#endif // VTAGLABEL_H
|
212
src/vtagpanel.cpp
Normal file
212
src/vtagpanel.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
#include "vtagpanel.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "vbuttonwithwidget.h"
|
||||
#include "utils/viconutils.h"
|
||||
#include "vnotefile.h"
|
||||
#include "valltagspanel.h"
|
||||
#include "vtaglabel.h"
|
||||
#include "vlineedit.h"
|
||||
#include "vmainwindow.h"
|
||||
|
||||
extern VPalette *g_palette;
|
||||
|
||||
extern VMainWindow *g_mainWin;
|
||||
|
||||
#define MAX_DISPLAY_LABEL 3
|
||||
|
||||
VTagPanel::VTagPanel(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
m_file(NULL),
|
||||
m_notebookOfCompleter(NULL)
|
||||
{
|
||||
setupUI();
|
||||
}
|
||||
|
||||
void VTagPanel::setupUI()
|
||||
{
|
||||
for (int i = 0; i < MAX_DISPLAY_LABEL; ++i) {
|
||||
VTagLabel *label = new VTagLabel(this);
|
||||
connect(label, &VTagLabel::removalRequested,
|
||||
this, [this](const QString &p_text) {
|
||||
removeTag(p_text);
|
||||
updateTags();
|
||||
});
|
||||
|
||||
m_labels.append(label);
|
||||
}
|
||||
|
||||
m_tagsPanel = new VAllTagsPanel(this);
|
||||
connect(m_tagsPanel, &VAllTagsPanel::tagRemoved,
|
||||
this, [this](const QString &p_text) {
|
||||
removeTag(p_text);
|
||||
|
||||
if (m_file->getTags().size() <= MAX_DISPLAY_LABEL) {
|
||||
// Hide the more panel.
|
||||
m_btn->hidePopupWidget();
|
||||
m_btn->hide();
|
||||
}
|
||||
});
|
||||
|
||||
m_btn = new VButtonWithWidget(VIconUtils::icon(":/resources/icons/tags.svg",
|
||||
g_palette->color("tab_indicator_tag_label_bg")),
|
||||
"",
|
||||
m_tagsPanel,
|
||||
this);
|
||||
m_btn->setToolTip(tr("View and edit tags of current note"));
|
||||
m_btn->setProperty("StatusBtn", true);
|
||||
m_btn->setFocusPolicy(Qt::NoFocus);
|
||||
connect(m_btn, &VButtonWithWidget::popupWidgetAboutToShow,
|
||||
this, &VTagPanel::updateAllTagsPanel);
|
||||
|
||||
m_tagEdit = new VLineEdit(this);
|
||||
m_tagEdit->setToolTip(tr("Press Enter to add a tag"));
|
||||
m_tagEdit->setPlaceholderText(tr("Add a tag"));
|
||||
m_tagEdit->setProperty("EmbeddedEdit", true);
|
||||
m_tagEdit->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
|
||||
connect(m_tagEdit, &QLineEdit::returnPressed,
|
||||
this, [this]() {
|
||||
Q_ASSERT(m_file);
|
||||
QString text = m_tagEdit->text();
|
||||
if (addTag(text)) {
|
||||
if (m_file->getNotebook()->addTag(text)) {
|
||||
updateCompleter();
|
||||
}
|
||||
|
||||
updateTags();
|
||||
|
||||
g_mainWin->showStatusMessage(tr("Tag \"%1\" added").arg(text));
|
||||
}
|
||||
|
||||
m_tagEdit->clear();
|
||||
});
|
||||
|
||||
QValidator *validator = new QRegExpValidator(QRegExp("[^,]+"), m_tagEdit);
|
||||
m_tagEdit->setValidator(validator);
|
||||
|
||||
// Completer.
|
||||
m_tagsModel = new QStringListModel(this);
|
||||
QCompleter *completer = new QCompleter(m_tagsModel, this);
|
||||
completer->setCaseSensitivity(Qt::CaseSensitive);
|
||||
completer->popup()->setItemDelegate(new QStyledItemDelegate(this));
|
||||
m_tagEdit->setCompleter(completer);
|
||||
m_tagEdit->installEventFilter(this);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout();
|
||||
for (auto label : m_labels) {
|
||||
mainLayout->addWidget(label);
|
||||
label->hide();
|
||||
}
|
||||
|
||||
mainLayout->addWidget(m_btn);
|
||||
mainLayout->addWidget(m_tagEdit);
|
||||
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
void VTagPanel::clearLabels()
|
||||
{
|
||||
for (auto label : m_labels) {
|
||||
label->clear();
|
||||
label->hide();
|
||||
}
|
||||
|
||||
m_tagsPanel->clear();
|
||||
}
|
||||
|
||||
void VTagPanel::updateTags(VNoteFile *p_file)
|
||||
{
|
||||
if (m_file == p_file) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_file = p_file;
|
||||
|
||||
updateTags();
|
||||
}
|
||||
|
||||
void VTagPanel::updateTags()
|
||||
{
|
||||
clearLabels();
|
||||
|
||||
if (!m_file) {
|
||||
m_btn->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const QStringList &tags = m_file->getTags();
|
||||
int idx = 0;
|
||||
for (; idx < tags.size() && idx < m_labels.size(); ++idx) {
|
||||
m_labels[idx]->setText(tags[idx]);
|
||||
m_labels[idx]->show();
|
||||
}
|
||||
|
||||
m_btn->setVisible(idx < tags.size());
|
||||
}
|
||||
|
||||
void VTagPanel::updateAllTagsPanel()
|
||||
{
|
||||
Q_ASSERT(m_file);
|
||||
m_tagsPanel->clear();
|
||||
|
||||
const QStringList &tags = m_file->getTags();
|
||||
for (int idx = MAX_DISPLAY_LABEL; idx < tags.size(); ++idx) {
|
||||
m_tagsPanel->addTag(tags[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
void VTagPanel::removeTag(const QString &p_text)
|
||||
{
|
||||
if (!m_file) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_file->removeTag(p_text);
|
||||
g_mainWin->showStatusMessage(tr("Tag \"%1\" removed").arg(p_text));
|
||||
}
|
||||
|
||||
bool VTagPanel::addTag(const QString &p_text)
|
||||
{
|
||||
if (!m_file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = m_file->addTag(p_text);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VTagPanel::eventFilter(QObject *p_obj, QEvent *p_event)
|
||||
{
|
||||
Q_ASSERT(p_obj == m_tagEdit);
|
||||
|
||||
if (p_event->type() == QEvent::FocusIn) {
|
||||
QFocusEvent *eve = static_cast<QFocusEvent *>(p_event);
|
||||
if (eve->gotFocus()) {
|
||||
// Just check completer.
|
||||
updateCompleter(m_file);
|
||||
}
|
||||
}
|
||||
|
||||
return QWidget::eventFilter(p_obj, p_event);
|
||||
}
|
||||
|
||||
void VTagPanel::updateCompleter(const VNoteFile *p_file)
|
||||
{
|
||||
const VNotebook *nb = p_file ? p_file->getNotebook() : NULL;
|
||||
if (nb == m_notebookOfCompleter) {
|
||||
// No need to update.
|
||||
return;
|
||||
}
|
||||
|
||||
m_notebookOfCompleter = nb;
|
||||
updateCompleter();
|
||||
}
|
||||
|
||||
void VTagPanel::updateCompleter()
|
||||
{
|
||||
m_tagsModel->setStringList(m_notebookOfCompleter ? m_notebookOfCompleter->getTags()
|
||||
: QStringList());
|
||||
}
|
60
src/vtagpanel.h
Normal file
60
src/vtagpanel.h
Normal file
@ -0,0 +1,60 @@
|
||||
#ifndef VTAGPANEL_H
|
||||
#define VTAGPANEL_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
|
||||
class VTagLabel;
|
||||
class VButtonWithWidget;
|
||||
class VNoteFile;
|
||||
class VAllTagsPanel;
|
||||
class VLineEdit;
|
||||
class QStringListModel;
|
||||
class VNotebook;
|
||||
|
||||
class VTagPanel : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VTagPanel(QWidget *parent = nullptr);
|
||||
|
||||
void updateTags(VNoteFile *p_file);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *p_obj, QEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void updateAllTagsPanel();
|
||||
|
||||
void removeTag(const QString &p_text);
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void clearLabels();
|
||||
|
||||
void updateTags();
|
||||
|
||||
bool addTag(const QString &p_text);
|
||||
|
||||
void updateCompleter(const VNoteFile *p_file);
|
||||
|
||||
void updateCompleter();
|
||||
|
||||
QVector<VTagLabel *> m_labels;
|
||||
|
||||
VButtonWithWidget *m_btn;
|
||||
|
||||
VAllTagsPanel *m_tagsPanel;
|
||||
|
||||
VLineEdit *m_tagEdit;
|
||||
|
||||
// Used for auto completion.
|
||||
QStringListModel *m_tagsModel;
|
||||
|
||||
VNoteFile *m_file;
|
||||
|
||||
const VNotebook *m_notebookOfCompleter;
|
||||
};
|
||||
|
||||
#endif // VTAGPANEL_H
|
Loading…
x
Reference in New Issue
Block a user