chore: add module to project

This commit is contained in:
x-tools-author 2025-05-28 16:41:45 +08:00
parent 4f129825f5
commit 6bfb2e3605
25 changed files with 1502 additions and 0 deletions

BIN
xapps/xsvg/300x300.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>MainWindow</name>
<message>
<location filename="../../Source/MainWindow.ui" line="14"/>
<source>Form</source>
<translation></translation>
</message>
</context>
<context>
<name>SettingsMenu</name>
<message>
<location filename="../../Source/SettingsMenu.ui" line="26"/>
<source>Padding</source>
<translation></translation>
</message>
<message>
<location filename="../../Source/SettingsMenu.ui" line="40"/>
<source>Enable export parameters</source>
<translation></translation>
</message>
</context>
<context>
<name>xSvg::MainWindow</name>
<message>
<location filename="../../Source/MainWindow.cpp" line="76"/>
<source>Import SVG Files</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="77"/>
<source>Select a folder to import SVG files now ?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="132"/>
<source>Open Path</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="137"/>
<source>Open File Location</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="142"/>
<source>Save As...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="146"/>
<source>Copy Full Path</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="150"/>
<source>Copy File Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="157"/>
<source>Filter
Text</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="178"/>
<source>Export
Format</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="203"/>
<source>Icon
Size</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="230"/>
<source>Fill
Color</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="246"/>
<source>Select Color</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="276"/>
<source>Open SVG Path</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="368"/>
<source>Trial Limit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="369"/>
<source>The operation is not supported by trial edition, please purchase full license.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="382"/>
<source>Copy to...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="457"/>
<source>NO SVG FOUND</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="458"/>
<source>The path you selected has no svg file(s), please selected one has svg file(s) then try again</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

Binary file not shown.

View File

@ -0,0 +1,206 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN">
<context>
<name>MainWindow</name>
<message>
<location filename="../../Source/MainWindow.ui" line="14"/>
<source>Form</source>
<translation></translation>
</message>
<message>
<source>Import SVG Files</source>
<translation type="vanished">SVG文件</translation>
</message>
<message>
<source>Select a folder to import SVG files now ?</source>
<translation type="vanished">SVG文件来预览</translation>
</message>
<message>
<source>Open Path</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Open File Location</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Save As...</source>
<translation type="vanished">...</translation>
</message>
<message>
<source>Copy Full Path</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Copy File Name</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Filter
Text</source>
<translation type="vanished">
</translation>
</message>
<message>
<source>Export
Format</source>
<translation type="vanished">
</translation>
</message>
<message>
<source>Icon
Size</source>
<translation type="vanished">
</translation>
</message>
<message>
<source>Fill
Color</source>
<translation type="vanished">
</translation>
</message>
<message>
<source>Select Color</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Open SVG Path</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Trial Limit</source>
<translation type="vanished"></translation>
</message>
<message>
<source>The operation is not supported by trial edition, please purchase full license.</source>
<translation type="vanished"></translation>
</message>
<message>
<source>Copy to...</source>
<translation type="vanished">...</translation>
</message>
<message>
<source>NO SVG FOUND</source>
<translation type="vanished">SVG文件</translation>
</message>
<message>
<source>The path you selected has no svg file(s), please selected one has svg file(s) then try again</source>
<translation type="vanished">SVG文件SVG文件的文件夹后重试</translation>
</message>
</context>
<context>
<name>SettingsMenu</name>
<message>
<location filename="../../Source/SettingsMenu.ui" line="26"/>
<source>Padding</source>
<translation></translation>
</message>
<message>
<location filename="../../Source/SettingsMenu.ui" line="40"/>
<source>Enable export parameters</source>
<translation>使</translation>
</message>
</context>
<context>
<name>xSvg::MainWindow</name>
<message>
<location filename="../../Source/MainWindow.cpp" line="76"/>
<source>Import SVG Files</source>
<translation type="unfinished">SVG文件</translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="77"/>
<source>Select a folder to import SVG files now ?</source>
<translation type="unfinished">SVG文件来预览</translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="132"/>
<source>Open Path</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="137"/>
<source>Open File Location</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="142"/>
<source>Save As...</source>
<translation type="unfinished">...</translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="146"/>
<source>Copy Full Path</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="150"/>
<source>Copy File Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="157"/>
<source>Filter
Text</source>
<translation type="unfinished">
</translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="178"/>
<source>Export
Format</source>
<translation type="unfinished">
</translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="203"/>
<source>Icon
Size</source>
<translation type="unfinished">
</translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="230"/>
<source>Fill
Color</source>
<translation type="unfinished">
</translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="246"/>
<source>Select Color</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="276"/>
<source>Open SVG Path</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="368"/>
<source>Trial Limit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="369"/>
<source>The operation is not supported by trial edition, please purchase full license.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="382"/>
<source>Copy to...</source>
<translation type="unfinished">...</translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="457"/>
<source>NO SVG FOUND</source>
<translation type="unfinished">SVG文件</translation>
</message>
<message>
<location filename="../../Source/MainWindow.cpp" line="458"/>
<source>The path you selected has no svg file(s), please selected one has svg file(s) then try again</source>
<translation type="unfinished">SVG文件SVG文件的文件夹后重试</translation>
</message>
</context>
</TS>

24
xapps/xsvg/src/main.cpp Normal file
View File

@ -0,0 +1,24 @@
/***************************************************************************************************
* Copyright 2024 x-tools-author(x-tools@outlook.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of xTools project.
*
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
* code directory.
**************************************************************************************************/
#include "MainWindow.h"
#include "xApp/Source/xApp.h"
#include "xLicense/Source/PayJsApi.h"
int main(const int argc, char *argv[])
{
Q_INIT_RESOURCE(xTools);
Q_INIT_RESOURCE(xApp);
xApp::PayJsApi::singleton().setPrice(10 * 10 * 10);
return xApp::exec<xSvg::MainWindow>(argc,
argv,
QString("xSvg"),
QString(X_SVG_VERSION),
QString("50263Qsaker2018.xSvg"));
}

View File

@ -0,0 +1,473 @@
/***************************************************************************************************
* Copyright 2024 x-tools-author(x-tools@outlook.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of xTools project.
*
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
* code directory.
**************************************************************************************************/
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QClipboard>
#include <QColorDialog>
#include <QComboBox>
#include <QDesktopServices>
#include <QFileDialog>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPainter>
#include <QPushButton>
#include <QString>
#include <QTimer>
#include <QToolBar>
#include <QVBoxLayout>
#include <QWidgetAction>
#include "Common/xTools.h"
#include "SettingsMenu.h"
#include "SvgFileDataModel.h"
#include "SvgItemDelegate.h"
#include "xLicense/Source/LicenseManager.h"
namespace xSvg {
MainWindow::MainWindow(QWidget *parent)
: xApp::MainWindow(parent)
, ui(new Ui::MainWindow)
, m_model{nullptr}
{
initToolBar();
initMenuBar();
m_centralWidget = new QWidget(this);
ui->setupUi(m_centralWidget);
ui->tableView->installEventFilter(this);
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Fixed);
ui->tableView->horizontalHeader()->hide();
ui->tableView->verticalHeader()->hide();
ui->tableView->setItemDelegate(new SvgItemDelegate(this));
ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
QMenu *menu = new QMenu(ui->tableView);
menu->addAction(m_actions.openPath);
menu->addAction(m_actions.openFileLocation);
menu->addAction(m_actions.saveAs);
menu->addAction(m_actions.copyFullPath);
menu->addAction(m_actions.copyFileName);
connect(ui->tableView, &QWidget::customContextMenuRequested, this, [=](const QPoint &pos) {
menu->exec(ui->tableView->mapToGlobal(pos));
});
setMinimumSize(1280, 680);
setCentralWidget(m_centralWidget);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::showTips()
{
QTimer::singleShot(500, this, [=]() {
int ret = QMessageBox::information(this,
tr("Import SVG Files"),
tr("Select a folder to import SVG files now ?"),
QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
openPath();
}
});
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == ui->tableView && event->type() == QEvent::Resize) {
updateView();
}
return xApp::MainWindow::eventFilter(obj, event);
}
void MainWindow::initMenuBar()
{
m_exitAction->setIcon(materialIcon(QString("close.svg")));
m_fileMenu->insertAction(m_exitAction, m_actions.openPath);
m_fileMenu->insertAction(m_exitAction, m_actions.openFileLocation);
//m_fileMenu->insertSeparator(m_exitAction);
m_fileMenu->insertAction(m_exitAction, m_actions.saveAs);
//m_fileMenu->insertSeparator(m_exitAction);
m_fileMenu->insertAction(m_exitAction, m_actions.copyFullPath);
m_fileMenu->insertAction(m_exitAction, m_actions.copyFileName);
}
void MainWindow::initToolBar()
{
QToolBar *toolBar = new QToolBar(this);
addToolBar(Qt::TopToolBarArea, toolBar);
toolBar->setToolButtonStyle(Qt::ToolButtonStyle::ToolButtonIconOnly);
toolBar->setMovable(false);
toolBar->setFloatable(false);
toolBar->setOrientation(Qt::Orientation::Horizontal);
initToolBarActions(toolBar);
toolBar->addSeparator();
addSpacer(toolBar, 4);
initToolBarFilter(toolBar);
addSpacer(toolBar, 4);
initToolBarFormat(toolBar);
addSpacer(toolBar, 4);
initToolBarSize(toolBar);
addSpacer(toolBar, 4);
initToolBarColor(toolBar);
addSpacer(toolBar, 4);
toolBar->addSeparator();
initToolBarSettings(toolBar);
}
void MainWindow::initToolBarActions(QToolBar *toolBar)
{
m_actions.openPath = toolBar->addAction(materialIcon("folder_open.svg"),
tr("Open Path"),
this,
&MainWindow::openPath);
m_actions.openFileLocation = toolBar->addAction(materialIcon("share_location.svg"),
tr("Open File Location"),
this,
&MainWindow::openFileLocation);
toolBar->addSeparator();
m_actions.saveAs = toolBar->addAction(materialIcon("exit_to_app.svg"),
tr("Save As..."),
this,
&MainWindow::saveAs);
m_actions.copyFullPath = toolBar->addAction(materialIcon("file_copy.svg"),
tr("Copy Full Path"),
this,
&MainWindow::copyFullPath);
m_actions.copyFileName = toolBar->addAction(materialIcon("copy_all.svg"),
tr("Copy File Name"),
this,
&MainWindow::copyFileName);
}
void MainWindow::initToolBarFilter(QToolBar *toolBar)
{
toolBar->addWidget(new QLabel(tr("Filter\nText")));
addSpacer(toolBar, 4);
m_searchLineEdit = new QLineEdit(this);
m_searchLineEdit->setMinimumWidth(200);
m_searchLineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_searchLineEdit->setClearButtonEnabled(true);
toolBar->addWidget(m_searchLineEdit);
addSpacer(toolBar, 4);
QString text = g_xTools.settings()->value(m_settingKeys.filter).toString();
m_searchLineEdit->setText(text);
connect(m_searchLineEdit, &QLineEdit::textEdited, this, [this]() {
g_xTools.settings()->setValue(m_settingKeys.filter, m_searchLineEdit->text().trimmed());
this->updateView();
});
}
void MainWindow::initToolBarFormat(QToolBar *toolBar)
{
toolBar->addWidget(new QLabel(tr("Export\nFormat")));
addSpacer(toolBar, 4);
m_formatComboBox = new QComboBox(this);
m_formatComboBox->setMinimumWidth(100);
m_formatComboBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
toolBar->addWidget(m_formatComboBox);
m_formatComboBox->addItems(QStringList() << "SVG"
<< "PNG"
<< "XPM"
<< "ICNS"
<< "ICO"
<< "TIFF"
<< "WEBP");
QString format = g_xTools.settings()->value(m_settingKeys.format).toString();
m_formatComboBox->setCurrentText(format);
connect(m_formatComboBox, QOverload<int>::of(&QComboBox::activated), this, [this]() {
g_xTools.settings()->setValue(m_settingKeys.format, m_formatComboBox->currentText());
});
}
void MainWindow::initToolBarSize(QToolBar *toolBar)
{
toolBar->addWidget(new QLabel(tr("Icon\nSize")));
addSpacer(toolBar, 4);
m_sizeComboBox = new QComboBox(this);
m_sizeComboBox->setMinimumWidth(100);
toolBar->addWidget(m_sizeComboBox);
m_sizeComboBox->addItems(QStringList() << "16"
<< "24"
<< "32"
<< "48"
<< "64"
<< "128"
<< "256"
<< "512");
QString size = g_xTools.settings()->value(m_settingKeys.size).toString();
m_sizeComboBox->setCurrentText(size);
SvgItemDelegate::setSize(size.toInt());
connect(m_sizeComboBox, QOverload<int>::of(&QComboBox::activated), this, [this]() {
g_xTools.settings()->setValue(m_settingKeys.size, m_sizeComboBox->currentText());
SvgItemDelegate::setSize(m_sizeComboBox->currentText().toInt());
updateView();
});
}
void MainWindow::initToolBarColor(QToolBar *toolBar)
{
toolBar->addWidget(new QLabel(tr("Fill\nColor")));
addSpacer(toolBar, 4);
m_colorButton = new QPushButton(this);
m_colorButton->setMinimumWidth(100);
m_colorButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
toolBar->addWidget(m_colorButton);
QString color = g_xTools.settings()->value(m_settingKeys.color).toString();
SvgItemDelegate::setColor(color);
if (color.isEmpty()) {
m_colorButton->setStyleSheet(QString("background-color:%1").arg("#00000000"));
} else {
m_colorButton->setStyleSheet(QString("background-color:%1").arg(color));
}
connect(m_colorButton, &QPushButton::clicked, this, [this]() {
QColor color = QColorDialog::getColor(Qt::white, this, tr("Select Color"));
if (!color.isValid()) {
SvgItemDelegate::setColor("");
g_xTools.settings()->setValue(m_settingKeys.color, "");
m_colorButton->setStyleSheet(QString("background-color:#00000000"));
} else {
SvgItemDelegate::setColor(color.name());
g_xTools.settings()->setValue(m_settingKeys.color, color.name());
m_colorButton->setStyleSheet(QString("background-color:%1").arg(color.name()));
}
updateView();
});
}
void MainWindow::initToolBarSettings(QToolBar *toolBar)
{
m_settingsMenu = new SettingsMenu(this);
QWidgetAction *action = new QWidgetAction(this);
action->setDefaultWidget(m_settingsMenu);
QMenu *menu = new QMenu(this);
menu->setIcon(materialIcon("settings.svg"));
menu->addAction(action);
toolBar->addAction(menu->menuAction());
}
void MainWindow::openPath()
{
QString path = g_xTools.settings()->value(m_settingKeys.path).toString();
path = QFileDialog::getExistingDirectory(this, tr("Open SVG Path"), path);
if (path.isEmpty()) {
return;
}
m_path = path;
g_xTools.settings()->setValue(m_settingKeys.path, path);
updateView();
}
void MainWindow::openFileLocation()
{
QModelIndexList list = ui->tableView->selectionModel()->selectedIndexes();
if (list.isEmpty()) {
return;
}
QStringList pathList;
for (const QModelIndex &index : list) {
QString fileName = m_model->data(index, Qt::DisplayRole).toString();
QString filePath = fileName.left(fileName.lastIndexOf('/'));
pathList.append(filePath);
}
pathList.removeDuplicates();
for (const QString &path : pathList) {
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}
}
void MainWindow::copyTo(const QModelIndexList &list, const QString &path)
{
for (const QModelIndex &index : list) {
QString oldName = m_model->data(index, Qt::DisplayRole).toString();
int lastIndexOf = oldName.lastIndexOf('/');
QString newName = path + "/" + oldName.right(oldName.length() - lastIndexOf);
if (QFile::copy(oldName, newName)) {
qInfo() << QString("Copy %1 to %2 successfully").arg(oldName, newName);
} else {
qWarning() << QString("Copy %1 to %2 failed").arg(oldName, newName);
}
}
}
void MainWindow::exportTo(const QModelIndexList &list, const QString &path, int padding)
{
for (const QModelIndex &index : list) {
QString fileName = m_model->data(index, Qt::DisplayRole).toString();
QPixmap pixmap(fileName);
if (pixmap.isNull()) {
qWarning() << QString("Load %1 failed").arg(fileName);
continue;
}
int size = m_sizeComboBox->currentText().toInt();
pixmap = pixmap.scaled(QSize(size, size), Qt::KeepAspectRatio, Qt::SmoothTransformation);
QUrl url(fileName);
QString newFileName = url.fileName();
newFileName = newFileName.left(newFileName.lastIndexOf('.'));
newFileName = path + "/" + newFileName;
if (padding > 0) {
QPixmap newPixmap(size, size);
newPixmap.fill(Qt::transparent);
QPainter painter(&newPixmap);
auto cookedPadding = QMargins(padding, padding, padding, padding);
painter.drawPixmap(newPixmap.rect() - cookedPadding, pixmap);
}
#if 1
QString fullPath = newFileName + "." + m_formatComboBox->currentText().toLower();
if (!pixmap.save(fullPath, m_formatComboBox->currentText().toStdString().c_str())) {
qWarning() << QString("Save as %1 failed").arg(fullPath);
}
#else
// Save all formats for testing...
for (int i = 0; i < m_formatComboBox->count(); ++i) {
QString format = m_formatComboBox->itemText(i).toLower();
if (!pixmap.save(newFileName + "." + format, format.toStdString().c_str())) {
qWarning() << QString("Save as %1 failed").arg(newFileName + "." + format);
}
}
#endif
}
}
void MainWindow::saveAs()
{
if (xApp::LicenseManager::singleton().isTrial()) {
int ret = QMessageBox::warning(
this,
tr("Trial Limit"),
tr("The operation is not supported by trial edition, please purchase full license."),
QMessageBox::Ok | QMessageBox::Cancel);
if (ret == QMessageBox::Ok) {
purchase();
}
return;
}
QModelIndexList list = ui->tableView->selectionModel()->selectedIndexes();
if (list.isEmpty()) {
return;
}
QString path = QFileDialog::getExistingDirectory(this, tr("Copy to..."), QString());
if (path.isEmpty()) {
return;
}
if (m_formatComboBox->currentText() == "SVG") {
copyTo(list, path);
} else {
if (m_settingsMenu->enablePadding()) {
exportTo(list, path, m_settingsMenu->padding());
} else {
exportTo(list, path);
}
}
}
void MainWindow::copyFullPath()
{
QModelIndexList list = ui->tableView->selectionModel()->selectedIndexes();
if (list.isEmpty()) {
return;
}
QStringList fileNames;
for (const QModelIndex &index : list) {
QString name = m_model->data(index, Qt::DisplayRole).toString();
fileNames.append(name);
}
return qApp->clipboard()->setText(fileNames.join('\n'));
}
void MainWindow::copyFileName()
{
QModelIndexList list = ui->tableView->selectionModel()->selectedIndexes();
if (list.isEmpty()) {
return;
}
QStringList fileNames;
for (const QModelIndex &index : list) {
QString name = m_model->data(index, Qt::DisplayRole).toString();
int lastIndexOf = name.lastIndexOf('/');
name = name.right(name.length() - lastIndexOf - 1);
fileNames.append(name);
}
return qApp->clipboard()->setText(fileNames.join(' '));
}
void MainWindow::updateView()
{
if (m_path.isEmpty()) {
return;
}
if (m_isUpdating) {
// 在更新过程中会忽略后续的更新请求,但更新完成后,如果需要跟新,会再次更新
m_needUpdateAgain = true;
return;
}
m_isUpdating = true;
m_needUpdateAgain = false;
if (ui->tableView->model()) {
ui->tableView->model()->deleteLater();
}
ui->tableView->setModel(nullptr);
int column = ui->tableView->width() / m_sizeComboBox->currentText().toInt();
m_model = new SvgFileDataModel(column, this);
connect(m_model, &SvgFileDataModel::dataReady, this, [this, column]() {
this->m_isUpdating = false;
if (m_model->svgFiles().isEmpty()) {
QMessageBox::warning(this,
tr("NO SVG FOUND"),
tr("The path you selected has no svg file(s), please selected one "
"has svg file(s) then try again"));
return;
}
ui->tableView->setModel(m_model);
for (int i = 0; i < column; ++i) {
ui->tableView->setColumnWidth(i, this->m_sizeComboBox->currentText().toInt());
}
for (int i = 0; i < m_model->rowCount(); ++i) {
ui->tableView->setRowHeight(i, this->m_sizeComboBox->currentText().toInt());
}
qInfo() << "Update view done, the with of column is "
<< this->m_sizeComboBox->currentText().toInt();
});
m_model->update(m_path, m_searchLineEdit->text().trimmed());
}
} // namespace xSvg

View File

@ -0,0 +1,90 @@
/***************************************************************************************************
* Copyright 2024 x-tools-author(x-tools@outlook.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of xTools project.
*
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
* code directory.
**************************************************************************************************/
#pragma once
#include <atomic>
#include <QModelIndexList>
#include "xApp/Source/MainWindow.h"
namespace Ui {
class MainWindow;
}
class SettingsMenu;
class SvgFileDataModel;
namespace xSvg {
class MainWindow : public xApp::MainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = Q_NULLPTR);
~MainWindow() override;
void showTips();
protected:
bool eventFilter(QObject* obj, QEvent* event) override;
private:
const struct
{
const QString path = "path";
const QString filter = "filter";
const QString format = "format";
const QString size = "size";
const QString color = "color";
} m_settingKeys;
private:
Ui::MainWindow* ui;
QWidget* m_centralWidget;
SvgFileDataModel* m_model{nullptr};
QString m_path;
std::atomic_bool m_isUpdating{false};
std::atomic_bool m_needUpdateAgain{false};
SettingsMenu* m_settingsMenu{nullptr};
QLineEdit* m_searchLineEdit{nullptr};
QComboBox* m_sizeComboBox{nullptr};
QComboBox* m_formatComboBox{nullptr};
QPushButton* m_colorButton{nullptr};
struct
{
QAction* openPath;
QAction* openFileLocation;
QAction* saveAs;
QAction* copyFullPath;
QAction* copyFileName;
QAction* settingsAction;
} m_actions;
private:
void initMenuBar();
void initToolBar();
void initToolBarActions(QToolBar* toolBar);
void initToolBarFilter(QToolBar* toolBar);
void initToolBarFormat(QToolBar* toolBar);
void initToolBarSize(QToolBar* toolBar);
void initToolBarColor(QToolBar* toolBar);
void initToolBarSettings(QToolBar* toolBar);
void openPath();
void openFileLocation();
void copyTo(const QModelIndexList& list, const QString& path);
void exportTo(const QModelIndexList& list, const QString& path, int padding = 0);
void saveAs();
void copyFullPath();
void copyFileName();
void updateView();
};
} // namespace xSvg

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QWidget" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>665</width>
<height>295</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTableView" name="tableView"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,50 @@
/***************************************************************************************************
* Copyright 2024 x-tools-author(x-tools@outlook.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of xTools project.
*
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
* code directory.
**************************************************************************************************/
#include "SettingsMenu.h"
#include "ui_SettingsMenu.h"
#include "Common/xTools.h"
SettingsMenu::SettingsMenu(QWidget *parent)
: QWidget{parent}
, ui{new Ui::SettingsMenu}
{
ui->setupUi(this);
int padding = g_xTools.settings()->value(m_settingKeys.padding, 0).toInt();
ui->spinBoxPadding->setValue(padding);
connect(ui->spinBoxPadding,
QOverload<int>::of(&QSpinBox::valueChanged),
this,
[this](int value) { g_xTools.settings()->setValue(m_settingKeys.padding, value); });
bool ret = g_xTools.settings()->value(m_settingKeys.enablePadding, false).toBool();
ui->checkBoxEnable->setChecked(ret);
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
connect(ui->checkBoxEnable, &QCheckBox::checkStateChanged, this, [this](int state) {
#else
connect(ui->checkBoxEnable, &QCheckBox::stateChanged, this, [this](int state) {
#endif
g_xTools.settings()->setValue(m_settingKeys.enablePadding, state == Qt::Checked);
});
}
SettingsMenu::~SettingsMenu()
{
delete ui;
}
bool SettingsMenu::enablePadding()
{
return ui->checkBoxEnable->isChecked();
}
int SettingsMenu::padding()
{
return ui->spinBoxPadding->value();
}

View File

@ -0,0 +1,36 @@
/***************************************************************************************************
* Copyright 2024 x-tools-author(x-tools@outlook.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of xTools project.
*
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
* code directory.
**************************************************************************************************/
#pragma once
#include <QWidget>
namespace Ui {
class SettingsMenu;
}
class SettingsMenu : public QWidget
{
Q_OBJECT
public:
explicit SettingsMenu(QWidget *parent = nullptr);
~SettingsMenu();
bool enablePadding();
int padding();
private:
const struct
{
const QString enablePadding = "enablePadding";
const QString padding = "padding";
} m_settingKeys;
private:
Ui::SettingsMenu *ui;
};

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsMenu</class>
<widget class="QWidget" name="SettingsMenu">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>200</width>
<height>90</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Padding</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QSpinBox" name="spinBoxPadding">
<property name="maximum">
<number>496</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxEnable">
<property name="text">
<string>Enable export parameters</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,96 @@
/***************************************************************************************************
* Copyright 2024 x-tools-author(x-tools@outlook.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of xTools project.
*
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
* code directory.
**************************************************************************************************/
#include "SvgFileDataModel.h"
#include <QDir>
#include "SvgScanner.h"
SvgFileDataModel::SvgFileDataModel(int column, QObject *parent)
: QAbstractTableModel{parent}
{
m_parameters.column = column;
m_scanner = new SvgScanner(this, this);
connect(m_scanner, &SvgScanner::finished, this, &SvgFileDataModel::onScannerFinished);
}
void SvgFileDataModel::update(const QString &path, const QString &filter)
{
m_svgFilesMutex.lock();
m_svgFiles.clear();
m_svgFilesMutex.unlock();
m_scanner->didStartScanner(path, filter);
}
void SvgFileDataModel::updateColumn(int column)
{
m_parameters.column = column;
}
void SvgFileDataModel::updateRecursive(bool recursive)
{
m_parameters.recursive = recursive;
}
void SvgFileDataModel::updateOverrideColor(const QString &overrideColor)
{
m_parameters.overrideColor = overrideColor;
}
void SvgFileDataModel::appendSvgFile(const QString &svgFile)
{
m_svgFilesMutex.lock();
m_svgFiles.append(svgFile);
m_svgFilesMutex.unlock();
}
QStringList SvgFileDataModel::svgFiles() const
{
return m_svgFiles;
}
int SvgFileDataModel::rowCount(const QModelIndex &index) const
{
if (m_svgFiles.isEmpty()) {
return 0;
}
int row1 = m_svgFiles.size() / m_parameters.column;
int row2 = m_svgFiles.size() % m_parameters.column;
return row1 + (row2 > 0 ? 1 : 0);
}
int SvgFileDataModel::columnCount(const QModelIndex &index) const
{
return m_parameters.column;
}
QVariant SvgFileDataModel::data(const QModelIndex &index, int role) const
{
int row = index.row();
int column = index.column();
int listIndex = row * m_parameters.column + column;
QString svgFile{"0"};
m_svgFilesMutex.lock();
if (listIndex >= 0 && listIndex < m_svgFiles.size()) {
svgFile = m_svgFiles.at(listIndex);
}
m_svgFilesMutex.unlock();
return QVariant::fromValue(svgFile);
}
void SvgFileDataModel::onScannerFinished()
{
auto info = QString("SVG scan finished, row: %1, column: %2").arg(rowCount()).arg(columnCount());
qInfo() << qPrintable(info);
emit dataReady();
}

View File

@ -0,0 +1,51 @@
/***************************************************************************************************
* Copyright 2024 x-tools-author(x-tools@outlook.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of xTools project.
*
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
* code directory.
**************************************************************************************************/
#pragma once
#include <QAbstractTableModel>
#include <QMutex>
#include <QObject>
class SvgScanner;
class SvgFileDataModel : public QAbstractTableModel
{
Q_OBJECT
public:
struct Parameters
{
bool recursive;
int column;
QString overrideColor;
} m_parameters;
public:
explicit SvgFileDataModel(int column, QObject *parent = nullptr);
void update(const QString &path, const QString &filter);
void updateColumn(int column);
void updateRecursive(bool recursive);
void updateOverrideColor(const QString &overrideColor);
void appendSvgFile(const QString &svgFile);
QStringList svgFiles() const;
public:
int rowCount(const QModelIndex & = QModelIndex()) const override;
int columnCount(const QModelIndex & = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
signals:
void dataReady();
private:
QStringList m_svgFiles;
mutable QMutex m_svgFilesMutex;
SvgScanner *m_scanner;
private:
void onScannerFinished();
};

View File

@ -0,0 +1,76 @@
/***************************************************************************************************
* Copyright 2024 x-tools-author(x-tools@outlook.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of xTools project.
*
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
* code directory.
**************************************************************************************************/
#include "SvgItemDelegate.h"
#include <QFile>
#include <QPainter>
#include <QPushButton>
#include <QSvgRenderer>
QString SvgItemDelegate::s_color;
int SvgItemDelegate::s_size{16};
SvgItemDelegate::SvgItemDelegate(QObject *parent)
: QItemDelegate{parent}
{}
void SvgItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
#if 0
QItemDelegate::paint(painter, option, index);
#endif
const QString svgFile = index.data().toString();
if (!QFile::exists(svgFile)) {
return;
}
if (!s_color.isEmpty()) {
#if 1
QSvgRenderer renderer(svgFile);
QImage image(QSize(s_size, s_size), QImage::Format_ARGB32);
image.fill(Qt::transparent); // Transparent background
QPainter tmpPainter(&image);
tmpPainter.setRenderHint(QPainter::Antialiasing);
renderer.render(&tmpPainter);
// Change color
for (int y = 0; y < image.height(); y++) {
for (int x = 0; x < image.width(); x++) {
QColor ic = image.pixelColor(x, y);
if (ic.alpha() > 0) { // If not transparent
image.setPixelColor(x, y, s_color);
}
}
}
painter->drawPixmap(option.rect, QPixmap::fromImage(image));
#else // TODO: This code does not work
QSvgRenderer renderer(svgFile);
QPixmap pixmap(s_size, s_size);
pixmap.fill(Qt::transparent);
QPainter tmpPainter(&pixmap);
tmpPainter.setRenderHint(QPainter::Antialiasing);
tmpPainter.setPen(QPen(QColor(s_color), 1));
renderer.render(&tmpPainter);
tmpPainter.end();
painter->drawPixmap(option.rect, pixmap);
#endif
} else {
const QPixmap pixmap{svgFile};
if (pixmap.isNull()) {
return;
}
painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
painter->drawPixmap(option.rect, pixmap);
}
}

View File

@ -0,0 +1,27 @@
/***************************************************************************************************
* Copyright 2024 x-tools-author(x-tools@outlook.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of xTools project.
*
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
* code directory.
**************************************************************************************************/
#pragma once
#include <QItemDelegate>
class SvgItemDelegate : public QItemDelegate
{
public:
explicit SvgItemDelegate(QObject *parent = nullptr);
void paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
static void setColor(const QString &color) { s_color = color; }
static void setSize(int size) { s_size = size < 16 ? 16 : size; }
private:
static QString s_color;
static int s_size;
};

View File

@ -0,0 +1,87 @@
/***************************************************************************************************
* Copyright 2024 x-tools-author(x-tools@outlook.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of xTools project.
*
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
* code directory.
**************************************************************************************************/
#include "SvgScanner.h"
#include <QDir>
#include <QTimer>
#include "SvgFileDataModel.h"
#if 0
#define USING_MULTITHREAD
#endif
SvgScanner::SvgScanner(SvgFileDataModel *model, QObject *parent)
: QThread{parent}
, m_model{model}
{}
void SvgScanner::didStartScanner(const QString &rootPath, const QString &filter)
{
if (isRunning()) {
return;
}
m_rootPath = rootPath;
m_filter = filter;
start();
}
void SvgScanner::run()
{
if (m_rootPath.isEmpty()) {
return;
}
didStartScannerActually(m_rootPath);
#if defined(USING_MULTITHREAD)
std::this_thread::sleep_for(std::chrono::seconds(1));
while (!this->m_threads.isEmpty()) {
m_threadsMutex.lock();
auto task = this->m_threads.takeFirst();
m_threadsMutex.unlock();
task->join();
delete task;
m_threadsMutex.lock();
if (this->m_threads.isEmpty()) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
m_threadsMutex.unlock();
}
#endif
}
void SvgScanner::didStartScannerActually(const QString &path)
{
QDir dir(path);
QList<QFileInfo> infos = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs
| QDir::NoSymLinks);
for (auto &info : infos) {
if (info.isDir()) {
#if defined(USING_MULTITHREAD)
qInfo() << info.absoluteFilePath();
auto task = new std::thread(&SvgScanner::didStartScannerActually,
this,
info.absoluteFilePath());
m_threadsMutex.lock();
m_threads.append(task);
m_threadsMutex.unlock();
#else
didStartScannerActually(info.absoluteFilePath());
#endif
} else {
bool endsWithSvg = info.fileName().endsWith(QString("svg"), Qt::CaseInsensitive);
bool hasFilter = info.fileName().contains(m_filter, Qt::CaseInsensitive);
if (endsWithSvg && hasFilter) {
m_model->appendSvgFile(info.absoluteFilePath());
}
}
}
}

View File

@ -0,0 +1,38 @@
/***************************************************************************************************
* Copyright 2024 x-tools-author(x-tools@outlook.com). All rights reserved.
*
* The file is encoded using "utf8 with bom", it is a part of xTools project.
*
* xTools is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
* code directory.
**************************************************************************************************/
#pragma once
#include <thread>
#include <QMutex>
#include <QThread>
class SvgFileDataModel;
class SvgScanner : public QThread
{
Q_OBJECT
public:
explicit SvgScanner(SvgFileDataModel *model, QObject *parent = nullptr);
void didStartScanner(const QString &rootPath, const QString &filter);
protected:
void run() override;
private:
QString m_rootPath;
QString m_filter;
QList<std::thread *> m_threads;
QMutex m_threadsMutex;
SvgFileDataModel *m_model;
private:
void didStartScannerActually(const QString &path);
};

34
xapps/xsvg/xSvg.cmake Normal file
View File

@ -0,0 +1,34 @@
set(X_SVG_SOURCE ${X_TOOLS_SOURCE})
file(GLOB X_SVG_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Source/*.*)
list(APPEND X_SVG_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/xSvg.rc)
list(APPEND X_SVG_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/xSvg.qrc)
x_tools_add_executable(xSvg ${X_SVG_SOURCE})
x_tools_add_stylesheet_resources(xSvg)
x_tools_deploy_qt(xSvg)
x_tools_tar_target(xSvg)
x_tools_copy_microsoft_store_dll(xSvg)
x_tools_get_latest_version_from_tag(xSvg X_SVG_VERSION)
target_link_libraries(xSvg PRIVATE xApp)
# --------------------------------------------------------------------------------------------------
# Generate installer(exe)
set(root_dir "${CMAKE_BINARY_DIR}/installer/xSvg")
set(icon ${CMAKE_CURRENT_SOURCE_DIR}/xSvg.ico)
x_tools_generate_installer(xSvg xSvg ${X_SVG_VERSION})
# --------------------------------------------------------------------------------------------------
# Generate Microsoft Store package(MSIX)
if(WIN32)
x_tools_generate_msix(xSvg "xSvg" "xSvg" "${X_SVG_VERSION}" OFF)
endif()
x_tools_generate_translations(xSvg)
set_target_properties(xSvg PROPERTIES FOLDER "xSvg")
set_target_properties(xSvg_lupdate PROPERTIES FOLDER "xSvg")
set_target_properties(xSvg_lrelease PROPERTIES FOLDER "xSvg")
set_target_properties(xSvg_lupgrade PROPERTIES FOLDER "xSvg")
set_target_properties(xSvg_installer PROPERTIES FOLDER "xSvg")
set_target_properties(xSvg_msix PROPERTIES FOLDER "xSvg")

BIN
xapps/xsvg/xSvg.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

9
xapps/xsvg/xSvg.md Normal file
View File

@ -0,0 +1,9 @@
# xSvg
xSvg 是一款SVG图片预览工具有以下亮点
- 高效预览支持巨量SVG图片的快速导入预览可以选择图片导入可以指定目录导入
- 快速搜索:支持模糊搜索,快速、高效、便捷地找到您想要的图片
- 一件配色可以更改SVG图片的填充颜色一键生成统一配色的图标
- 统一大小:导出为其他格式图片时,可以调整内容边距,统一图标显示效果
- 格式转换可以将SVG转换为以下格式:PNG, XPM, ICNS, ICO, TIFF, WEBP

6
xapps/xsvg/xSvg.qrc Normal file
View File

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>Resources/Translations/xSvg_en.qm</file>
<file>Resources/Translations/xSvg_zh_CN.qm</file>
</qresource>
</RCC>

1
xapps/xsvg/xSvg.rc Normal file
View File

@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "xSvg.ico"

1
xapps/xsvg/xSvg.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1715170296760" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6057" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M956 686.5l-0.1-0.1-0.1-0.1C911.7 593 843.4 545 752.5 545s-159.2 48.1-203.4 141.3v0.1c-5.4 11.5-5.4 24.9 0 36.4C593.3 816 661.6 864 752.5 864s159.2-48.1 203.4-141.3c5.4-11.5 5.4-24.8 0.1-36.2zM752.5 800c-62.1 0-107.4-30-141.1-95.5C645 639 690.4 609 752.5 609c62.1 0 107.4 30 141.1 95.5C860 770 814.6 800 752.5 800z" p-id="6058" fill="#1296db"></path><path d="M753 705m-56 0a56 56 0 1 0 112 0 56 56 0 1 0-112 0Z" p-id="6059" fill="#1296db"></path><path d="M136 232h704v253h72V192c0-17.7-14.3-32-32-32H96c-17.7 0-32 14.3-32 32v520c0 17.7 14.3 32 32 32h352v-72H136V232z" p-id="6060" fill="#1296db"></path><path d="M724.9 338.1l-36.8-36.8c-3.1-3.1-8.2-3.1-11.3 0L493 485.3l-86.1-86.2c-3.1-3.1-8.2-3.1-11.3 0L251.3 543.4c-3.1 3.1-3.1 8.2 0 11.3l36.8 36.8c3.1 3.1 8.2 3.1 11.3 0l101.8-101.8 86.1 86.2c3.1 3.1 8.2 3.1 11.3 0l226.3-226.5c3.2-3.1 3.2-8.2 0-11.3z" p-id="6061" fill="#1296db"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

3
xapps/xsvg/xSvg.txt Normal file
View File

@ -0,0 +1,3 @@
2024年5月6日 11:48:25