diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4756069..026b74f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -92,6 +92,7 @@ set(PROJECT_HEADERS
src/view_models/nodelist.h
src/view_models/nodemodel.h
src/view_models/nodeformmodel.h
+ src/view_models/notificationmodel.h
src/view_models/buildinfo.h.in
src/view_models/systemtray.h
src/view_models/imageprovider.h
@@ -122,6 +123,7 @@ set(PROJECT_SOURCES
src/view_models/nodelist.cpp
src/view_models/nodemodel.cpp
src/view_models/nodeformmodel.cpp
+ src/view_models/notificationmodel.cpp
src/view_models/systemtray.cpp
src/view_models/imageprovider.cpp
src/view_models/jsonhighlighter.cpp
diff --git a/i18n/across_zh_CN.ts b/i18n/across_zh_CN.ts
index bb2e5a6..72237ab 100644
--- a/i18n/across_zh_CN.ts
+++ b/i18n/across_zh_CN.ts
@@ -614,7 +614,7 @@
编辑
-
+
复制链接
@@ -623,32 +623,32 @@
ICMP Ping
-
+
复制节点
-
+
TCP Ping
-
+
更新
-
+
[%1] 更新中...
-
+
更新于: %1
-
+
删除
@@ -1225,10 +1225,21 @@
across::GroupList
-
-
-
-
+
+
+ [%1] TCP Pinging...
+
+
+
+
+
+ 測試中:%1/%2
+
+
+
+
+
+
复制 [%1] 链接到剪贴板
@@ -1236,7 +1247,7 @@
across::NodeFormModel
-
+
未支持自定义配置编码到链接格式
@@ -1295,22 +1306,22 @@
across::setting::ConfigTools
-
+
点击应用或托盘图标以显示
-
+
最小化启动
-
+
无法解析版本
-
+
新版本:v%1
@@ -1319,7 +1330,7 @@
新版本:%1
-
+
已经是最新版本
diff --git a/src/app.cpp b/src/app.cpp
index 4157370..8565f75 100644
--- a/src/app.cpp
+++ b/src/app.cpp
@@ -39,6 +39,7 @@ bool Application::initialize() {
p_nodes = QSharedPointer::create();
p_groups = QSharedPointer::create();
p_tray = QSharedPointer::create();
+ p_notifications = QSharedPointer::create();
p_image_provider = new ImageProvider; // free by qml engine
p_config->init(p_curl);
@@ -93,6 +94,8 @@ void Application::setRootContext() {
p_groups.get());
m_engine.rootContext()->setContextProperty(QStringLiteral("acrossTray"),
p_tray.get());
+ m_engine.rootContext()->setContextProperty(QStringLiteral("acrossNotifications"),
+ p_notifications.get());
m_engine.rootContext()->setContextProperty(
QStringLiteral("fixedFont"),
QFontDatabase::systemFont(QFontDatabase::FixedFont));
@@ -109,10 +112,10 @@ void Application::setRootContext() {
p_tray->init(p_config, p_core, p_nodes);
#if !defined(Q_CC_MINGW) && !defined(Q_OS_MACOS)
p_nodes->init(p_config, p_core, p_db);
- p_groups->init(p_config, p_db, p_nodes, p_curl);
+ p_groups->init(p_config, p_db, p_nodes, p_curl, p_notifications);
#else
p_nodes->init(p_config, p_core, p_db, p_tray->getTrayIcon());
- p_groups->init(p_config, p_db, p_nodes, p_curl, p_tray->getTrayIcon());
+ p_groups->init(p_config, p_db, p_nodes, p_curl, p_notifications, p_tray->getTrayIcon());
#endif
}
@@ -143,6 +146,8 @@ void Application::registerModels() {
"NodeModel");
qmlRegisterType(qml_model_name.c_str(), 1, 0,
"NodeFormModel");
+ qmlRegisterType(qml_model_name.c_str(), 1, 0,
+ "Notification");
}
void Application::onMessageReceived(quint32 clientId, const QByteArray &msg) {
diff --git a/src/app.h b/src/app.h
index a271cb3..4461a3c 100644
--- a/src/app.h
+++ b/src/app.h
@@ -22,6 +22,7 @@
#include "view_models/configtools.h"
#include "view_models/grouplist.h"
#include "view_models/groupmodel.h"
+#include "view_models/notificationmodel.h"
#include "view_models/imageprovider.h"
#include "view_models/logview.h"
#include "view_models/nodeformmodel.h"
@@ -73,6 +74,7 @@ class Application : public SingleApplication {
QSharedPointer p_nodes;
QSharedPointer p_groups;
QSharedPointer p_tray;
+ QSharedPointer p_notifications;
across::ImageProvider *p_image_provider;
ACrossExitReason exitReason = EXIT_NORMAL;
diff --git a/src/view_models/configtools.cpp b/src/view_models/configtools.cpp
index ec7d5ba..29f0851 100644
--- a/src/view_models/configtools.cpp
+++ b/src/view_models/configtools.cpp
@@ -117,7 +117,8 @@ void ConfigTools::setInboundObject(v2ray::config::V2RayConfig &config) {
http->set_timeout(http_setting.timeout());
http->set_userlevel(http_setting.user_level());
- if (const auto& auth_setting = http_setting.auth(); auth_setting.enable()) {
+ if (const auto &auth_setting = http_setting.auth();
+ auth_setting.enable()) {
auto auth = http->add_accounts();
auth->set_user(auth_setting.username());
auth->set_pass(auth_setting.password());
@@ -134,7 +135,8 @@ void ConfigTools::setInboundObject(v2ray::config::V2RayConfig &config) {
auto socks = inbound->mutable_settings()->mutable_socks();
socks->set_userlevel(socks5_setting.user_level());
- if (const auto& auth_setting = socks5_setting.auth(); auth_setting.enable()) {
+ if (const auto &auth_setting = socks5_setting.auth();
+ auth_setting.enable()) {
auto auth = socks->add_accounts();
auth->set_user(auth_setting.username());
auth->set_pass(auth_setting.password());
@@ -306,7 +308,7 @@ void ConfigTools::freshInbound() {
emit httpPasswordChanged();
}
-void ConfigTools::saveConfig(const QString& config_path) {
+void ConfigTools::saveConfig(const QString &config_path) {
auto json_str = SerializeTools::MessageToJson(m_config);
if (!config_path.isEmpty()) {
@@ -822,7 +824,7 @@ void ConfigTools::setHttpPassword(const QString &val) {
emit configChanged();
}
-bool ConfigTools::isFileExist(const QString& file_path) {
+bool ConfigTools::isFileExist(const QString &file_path) {
return QFile(file_path).exists();
}
diff --git a/src/view_models/grouplist.cpp b/src/view_models/grouplist.cpp
index 2faa8e9..92a784e 100644
--- a/src/view_models/grouplist.cpp
+++ b/src/view_models/grouplist.cpp
@@ -14,7 +14,8 @@ void GroupList::init(QSharedPointer config,
QSharedPointer db,
QSharedPointer nodes,
QSharedPointer curl,
- const QSharedPointer& tray) {
+ QSharedPointer notifications,
+ const QSharedPointer &tray ) {
if (auto app_logger = spdlog::get("app"); app_logger != nullptr) {
p_logger = app_logger->clone("groups");
} else {
@@ -26,6 +27,7 @@ void GroupList::init(QSharedPointer config,
p_db = std::move(db);
p_curl = std::move(curl);
p_nodes = std::move(nodes);
+ p_notifications = std::move(notifications);
if (tray != nullptr) {
p_tray = tray;
@@ -37,6 +39,9 @@ void GroupList::init(QSharedPointer config,
connect(p_curl.get(), &across::network::CURLTools::downloadFinished, this,
&GroupList::handleDownloaded);
+ connect(this, &GroupList::nodeLatencyChanged, this,
+ &GroupList::handleNodeLatencyChanged);
+
reloadItems();
checkAllUpdate();
}
@@ -98,18 +103,57 @@ void GroupList::checkUpdate(int index, bool force) {
} while (false);
}
-Q_INVOKABLE void GroupList::testTcpPing(int index) {
+Q_INVOKABLE int GroupList::testTcpPing(int index) {
if (index >= m_groups.size())
- return;
+ return 1;
if (auto &group = m_groups[index]; group.items != 0) {
+ if (m_is_tcpPinging.contains(group.id)) {
+ return 1;
+ }
+ m_is_tcpPinging[group.id] = true;
+
auto nodes = p_db->listAllNodesFromGroupID(group.id);
+ m_tcpPinging_count[group.id] = 0;
+ m_group_size[group.id] = nodes.size();
+ m_tcpPinging_notifications[group.id] = p_notifications->append(
+ tr("[%1] TCP Pinging...").arg(group.name),
+ tr("Testing: %1/%2").arg("0").arg(QString::number(m_group_size[group.id])),
+ 0.0,
+ m_group_size[group.id],
+ 0.0
+ );
+
for (int i = 0; i < nodes.size(); ++i) {
auto &node = nodes[i];
- p_nodes->testLatency(node, i);
+ p_nodes->testLatency(node, i, [this, node, i] {
+ emit nodeLatencyChanged(node.group_id, i, node);
+ });
}
}
+ return 0;
+}
+Q_INVOKABLE int GroupList::testTcpPingLeft(int index) {
+ auto &group = m_groups[index];
+ if (m_tcpPinging_count.contains(group.id)) {
+ return -m_tcpPinging_count[group.id];
+ }
+ return 0;
+}
+void GroupList::handleNodeLatencyChanged(qint64 group_id, int index,
+ const across::NodeInfo &node) {
+ m_tcpPinging_count[group_id]++;
+ m_tcpPinging_notifications[group_id]->setMessage(tr("Testing: %1/%2")
+ .arg(QString::number(m_tcpPinging_count[group_id]))
+ .arg(QString::number(m_group_size[group_id])));
+ m_tcpPinging_notifications[group_id]->setValue(m_tcpPinging_count[group_id]);
+ if (m_tcpPinging_count[group_id] >= m_group_size[group_id]) {
+ p_notifications->remove(m_tcpPinging_notifications[group_id]->getIndex());
+ m_tcpPinging_count.remove(group_id);
+ m_tcpPinging_notifications.remove(group_id);
+ m_is_tcpPinging.remove(group_id);
+ }
}
Q_INVOKABLE int GroupList::getIndexByID(int id) {
@@ -227,7 +271,7 @@ bool GroupList::insertSIP008(const GroupInfo &group_info,
auto url = SerializeTools::sip002Encode(meta).value();
auto outbound = meta.outbound;
auto shadowsocks = outbound.settings().shadowsocks();
- const auto& server = shadowsocks.servers(0);
+ const auto &server = shadowsocks.servers(0);
std::string json_str;
@@ -438,8 +482,9 @@ void GroupList::copyNodesToClipboard(int index) {
void GroupList::handleDownloaded(const QVariant &content) {
auto task = content.value();
- if (task.content.isEmpty()){
- if(task.is_updated) m_is_updating.remove(task.id);
+ if (task.content.isEmpty()) {
+ if (task.is_updated)
+ m_is_updating.remove(task.id);
return;
}
diff --git a/src/view_models/grouplist.h b/src/view_models/grouplist.h
index 81c21e9..f8dc88f 100644
--- a/src/view_models/grouplist.h
+++ b/src/view_models/grouplist.h
@@ -21,6 +21,7 @@
#include "configtools.h"
#include "logtools.h"
#include "nodelist.h"
+#include "notificationmodel.h"
namespace across {
@@ -33,7 +34,8 @@ class GroupList : public QObject {
QSharedPointer db,
QSharedPointer nodes,
QSharedPointer curl,
- const QSharedPointer& tray = nullptr);
+ QSharedPointer notifications,
+ const QSharedPointer &tray = nullptr);
bool insert(const GroupInfo &group_info, const QString &content);
bool insertSIP008(const GroupInfo &group_info, const QString &content);
@@ -44,7 +46,8 @@ class GroupList : public QObject {
Q_INVOKABLE void checkAllUpdate(bool force = false);
Q_INVOKABLE void checkUpdate(int index, bool force = true);
- Q_INVOKABLE void testTcpPing(int index);
+ Q_INVOKABLE int testTcpPing(int index);
+ Q_INVOKABLE int testTcpPingLeft(int index);
Q_INVOKABLE int getIndexByID(int id);
Q_INVOKABLE void search(const QString &value);
@@ -65,18 +68,23 @@ class GroupList : public QObject {
void copyNodesToClipboard(int index);
void handleDownloaded(const QVariant &content);
void handleItemsChanged(int64_t group_id, int size);
+ void handleNodeLatencyChanged(qint64 group_id, int index,
+ const across::NodeInfo &node);
signals:
void preItemsReset();
void postItemsReset();
void itemInfoChanged(int index);
+ void nodeLatencyChanged(qint64 group_id, int index,
+ const across::NodeInfo &node);
private:
QSharedPointer p_config;
QSharedPointer p_db;
QSharedPointer p_nodes;
QSharedPointer p_curl;
+ QSharedPointer p_notifications;
QSharedPointer p_tray;
std::shared_ptr p_logger;
@@ -85,6 +93,10 @@ class GroupList : public QObject {
QList m_origin_groups;
QList m_pre_groups;
QMap m_is_updating;
+ QMap m_is_tcpPinging;
+ QMap m_tcpPinging_count;
+ QMap m_group_size;
+ QMap m_tcpPinging_notifications;
};
} // namespace across
diff --git a/src/view_models/imageprovider.h b/src/view_models/imageprovider.h
index 98ecc84..baaac08 100644
--- a/src/view_models/imageprovider.h
+++ b/src/view_models/imageprovider.h
@@ -9,7 +9,8 @@
namespace across {
class ImageProvider : public QQuickImageProvider {
public:
- explicit ImageProvider(ImageType type = ImageType::Image, Flags flags = Flags());
+ explicit ImageProvider(ImageType type = ImageType::Image,
+ Flags flags = Flags());
public slots:
void setContent(const QString &id, const QString &content);
diff --git a/src/view_models/jsonhighlighter.cpp b/src/view_models/jsonhighlighter.cpp
index 7ea400d..2c39112 100644
--- a/src/view_models/jsonhighlighter.cpp
+++ b/src/view_models/jsonhighlighter.cpp
@@ -38,7 +38,7 @@ void JSONHighlighter::init() {
}
void JSONHighlighter::setTheme(config::Theme *p_theme) {
- const auto& colors = p_theme->colors();
+ const auto &colors = p_theme->colors();
val_format.setForeground(QColor(colors.text_color().c_str()));
string_format.setForeground(QColor(colors.text_color().c_str()));
diff --git a/src/view_models/loghighlighter.cpp b/src/view_models/loghighlighter.cpp
index 4a406ce..b30eda3 100644
--- a/src/view_models/loghighlighter.cpp
+++ b/src/view_models/loghighlighter.cpp
@@ -84,7 +84,7 @@ void LogHighlighter::init() {
}
void LogHighlighter::setTheme(const config::Theme &theme) {
- const auto& colors = theme.colors();
+ const auto &colors = theme.colors();
info_format->setForeground(QColor(colors.style_color().c_str()));
warning_format->setForeground(QColor(colors.warn_color().c_str()));
diff --git a/src/view_models/logtools.h b/src/view_models/logtools.h
index 471be8d..940a8ae 100644
--- a/src/view_models/logtools.h
+++ b/src/view_models/logtools.h
@@ -14,8 +14,9 @@ enum LoggerEnum { core, app };
class LogTools : public LogView {
public:
- explicit LogTools(QSharedPointer log_view, const QString &name = "",
- LoggerEnum log_enum = LoggerEnum::app);
+ explicit LogTools(QSharedPointer log_view,
+ const QString &name = "",
+ LoggerEnum log_enum = LoggerEnum::app);
template
inline void trace(fmt::format_string fmt, Args &&...args) {
@@ -50,5 +51,5 @@ class LogTools : public LogView {
private:
std::shared_ptr p_logger;
};
-} // namespace across
+} // namespace across::utils
#endif // LOGTOOLS_H
diff --git a/src/view_models/nodeformmodel.cpp b/src/view_models/nodeformmodel.cpp
index 3a5374d..b479d52 100644
--- a/src/view_models/nodeformmodel.cpp
+++ b/src/view_models/nodeformmodel.cpp
@@ -49,7 +49,6 @@ void NodeFormModel::accept(const QVariantMap &values) {
} else {
p_list->updateNode(node);
}
-
}
QString NodeFormModel::refreshPreview(const QVariantMap &values) {
diff --git a/src/view_models/nodelist.cpp b/src/view_models/nodelist.cpp
index e04270b..fbe93b2 100644
--- a/src/view_models/nodelist.cpp
+++ b/src/view_models/nodelist.cpp
@@ -18,7 +18,7 @@ NodeList::~NodeList() {
void NodeList::init(QSharedPointer config,
QSharedPointer core, QSharedPointer db,
- const QSharedPointer& tray) {
+ const QSharedPointer &tray) {
if (auto app_logger = spdlog::get("app"); app_logger != nullptr) {
p_logger = app_logger->clone("nodes");
} else {
@@ -396,7 +396,8 @@ void NodeList::setCurrentNodeByID(int id) {
}
}
-void NodeList::handleLatencyChanged(qint64 group_id, int index, const NodeInfo& node) {
+void NodeList::handleLatencyChanged(qint64 group_id, int index,
+ const NodeInfo &node) {
auto db_future = QtConcurrent::run([&, node] {
auto t_node = node;
p_db->update(t_node);
@@ -468,15 +469,19 @@ Q_INVOKABLE void NodeList::testLatency(int id) {
}
}
-void NodeList::testLatency(const NodeInfo& node, int index) {
- m_tasks.enqueue(QtConcurrent::run([this, index, node] {
- auto current_node = node;
- TCPPing ping;
- ping.setAddr(node.address);
- ping.setPort(node.port);
- current_node.latency = ping.getAvgLatency();
- emit itemLatencyChanged(node.group_id, index, current_node);
- }));
+void NodeList::testLatency(const NodeInfo &node, int index,
+ std::function after) {
+ m_tasks.enqueue(
+ QtConcurrent::run([this, index, node, after{std::move(after)}] {
+ auto current_node = node;
+ TCPPing ping;
+ ping.setAddr(node.address);
+ ping.setPort(node.port);
+ current_node.latency = ping.getAvgLatency();
+
+ after();
+ emit itemLatencyChanged(node.group_id, index, current_node);
+ }));
}
bool NodeList::isRunning() {
diff --git a/src/view_models/nodelist.h b/src/view_models/nodelist.h
index 583fb3f..cfa90f4 100644
--- a/src/view_models/nodelist.h
+++ b/src/view_models/nodelist.h
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
namespace across {
using Json = nlohmann::json;
@@ -48,7 +49,7 @@ class NodeList : public QObject {
void init(QSharedPointer config,
QSharedPointer core,
QSharedPointer db,
- const QSharedPointer& tray = nullptr);
+ const QSharedPointer &tray = nullptr);
bool run();
@@ -66,7 +67,8 @@ class NodeList : public QObject {
void setUploadTraffic(double newUploadTraffic);
void setDownloadTraffic(double newDownloadTraffic);
- void testLatency(const NodeInfo& node, int index);
+ void testLatency(
+ const NodeInfo &node, int index, std::function after = [] {});
void setDownloadProxy(across::network::DownloadTask &task);
@@ -99,7 +101,8 @@ class NodeList : public QObject {
public slots:
void setDisplayGroupID(int group_id);
- void handleLatencyChanged(qint64 group_id, int index, const across::NodeInfo& node);
+ void handleLatencyChanged(qint64 group_id, int index,
+ const across::NodeInfo &node);
signals:
void itemReset(int index);
diff --git a/src/view_models/nodemodel.h b/src/view_models/nodemodel.h
index 71ebcf4..7d07579 100644
--- a/src/view_models/nodemodel.h
+++ b/src/view_models/nodemodel.h
@@ -35,10 +35,11 @@ class NodeModel : public QAbstractListModel {
ModifiedAtRole,
};
- [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ [[nodiscard]] int
+ rowCount(const QModelIndex &parent = QModelIndex()) const override;
[[nodiscard]] QVariant data(const QModelIndex &index,
- int role = Qt::DisplayRole) const override;
+ int role = Qt::DisplayRole) const override;
[[nodiscard]] QHash roleNames() const override;
diff --git a/src/view_models/notificationmodel.cpp b/src/view_models/notificationmodel.cpp
new file mode 100644
index 0000000..d2fbba9
--- /dev/null
+++ b/src/view_models/notificationmodel.cpp
@@ -0,0 +1,126 @@
+#include "notificationmodel.h"
+
+using namespace across;
+
+Notification::Notification(int index, QObject *parent) : QObject(parent) {
+ this->index = index;
+
+ connect(this, &Notification::titleChanged,
+ &Notification::propertiesChanged);
+ connect(this, &Notification::messageChanged,
+ &Notification::propertiesChanged);
+ connect(this, &Notification::fromChanged, &Notification::propertiesChanged);
+ connect(this, &Notification::toChanged, &Notification::propertiesChanged);
+ connect(this, &Notification::valueChanged,
+ &Notification::propertiesChanged);
+ connect(this, &Notification::pinChanged, &Notification::propertiesChanged);
+ connect(this, &Notification::indexChanged, &Notification::propertiesChanged);
+}
+
+NotificationModel::NotificationModel(QObject *parent)
+ : QAbstractListModel(parent) {}
+
+QVariant NotificationModel::headerData(int section, Qt::Orientation orientation,
+ int role) const {
+ if (section >= rowCount())
+ return {};
+
+ if (role == NotificationRoles::TitleRole)
+ return notifications.at(section)->getTitle();
+
+ return {};
+}
+
+int NotificationModel::rowCount(const QModelIndex &parent) const {
+ if (parent.isValid())
+ return 0;
+ else
+ return notifications.size();
+}
+
+QVariant NotificationModel::data(const QModelIndex &index, int role) const {
+ if (!index.isValid() || index.row() >= rowCount())
+ return QVariant();
+
+ const auto item = notifications.at(index.row());
+
+ switch (role) {
+ case NotificationIndexRole:
+ return static_cast(item->getIndex());
+ case TitleRole:
+ return item->getTitle();
+ case MessageRole:
+ return item->getMessage();
+ case FromRole:
+ return item->getFrom();
+ case ToRole:
+ return item->getTo();
+ case ValueRole:
+ return item->getValue();
+ default:
+ return {};
+ }
+}
+
+QHash NotificationModel::roleNames() const {
+ static const QHash roles{
+ {NotificationIndexRole, "id"},
+ {TitleRole, "title"},
+ {MessageRole, "message"},
+ {FromRole, "from"},
+ {ToRole, "to"},
+ {ValueRole, "value"},
+ };
+
+ return roles;
+}
+
+Qt::ItemFlags NotificationModel::flags(const QModelIndex &index) const {
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+
+ return Qt::ItemIsEnabled;
+}
+
+across::Notification *
+NotificationModel::append(const QString &title, const QString &message = "",
+ qreal from = 0.0, qreal to = 1.0, qreal value = 0.0) {
+ int index = notifications.size();
+ Notification *noti = new Notification(index, this);
+
+ noti->setTitle(title);
+ noti->setMessage(message);
+ noti->setFrom(from);
+ noti->setTo(to);
+ noti->setValue(value);
+
+ auto rcnt = rowCount();
+ beginInsertRows(QModelIndex(), rcnt, rcnt);
+ connect(noti, &Notification::propertiesChanged, this, [this,noti]() {
+ QModelIndex topLeft = createIndex(noti->getIndex(), 0);
+ QModelIndex bottomRight = createIndex(noti->getIndex(), 0);
+ emit NotificationModel::dataChanged(topLeft, bottomRight);
+ });
+ notifications.emplace_back(noti);
+ endInsertRows();
+ return noti;
+}
+
+void NotificationModel::remove(const int index) {
+ if (index >= notifications.size() || index < 0)
+ return;
+
+ auto rcnt = index;
+ beginRemoveRows(QModelIndex(), rcnt, rcnt);
+
+ QModelIndex topLeft = createIndex(rcnt, 0);
+ QModelIndex bottomRight = createIndex(rcnt, 0);
+ emit NotificationModel::dataChanged(topLeft, bottomRight);
+ Notification *noti = notifications[index];
+ for(int i=index+1;isetIndex(notifications[i]->getIndex()-1);
+ }
+ notifications.remove(index);
+ noti->deleteLater();
+ endRemoveRows();
+}
\ No newline at end of file
diff --git a/src/view_models/notificationmodel.h b/src/view_models/notificationmodel.h
new file mode 100644
index 0000000..b548863
--- /dev/null
+++ b/src/view_models/notificationmodel.h
@@ -0,0 +1,118 @@
+#ifndef NOTIFICATIONMODEL_H
+#define NOTIFICATIONMODEL_H
+
+#include
+#include
+#include
+#include
+
+namespace across {
+
+class Notification : public QObject {
+ Q_OBJECT
+ Q_PROPERTY(QString title READ getTitle WRITE setTitle BINDABLE bindableTitle
+ NOTIFY titleChanged);
+ Q_PROPERTY(QString message READ getMessage WRITE setMessage BINDABLE
+ bindableMessage NOTIFY messageChanged);
+ Q_PROPERTY(qreal from READ getFrom WRITE setFrom BINDABLE bindableFrom
+ NOTIFY fromChanged);
+ Q_PROPERTY(
+ qreal to READ getTo WRITE setTo BINDABLE bindableTo NOTIFY toChanged);
+ Q_PROPERTY(qreal value READ getValue WRITE setValue BINDABLE bindableValue
+ NOTIFY valueChanged);
+ Q_PROPERTY(bool pin READ getPin WRITE setPin NOTIFY pinChanged);
+ Q_PROPERTY(int index READ getIndex WRITE setIndex NOTIFY indexChanged);
+
+ public:
+ explicit Notification(int id, QObject *parent = nullptr);
+
+ QString getTitle() const { return title.value(); }
+ void setTitle(const QString &title) { this->title = title; };
+ QBindable bindableTitle() { return &title; }
+ QString getMessage() const { return message.value(); }
+ void setMessage(const QString &message) { this->message = message; }
+ QBindable bindableMessage() { return &message; }
+ qreal getFrom() const { return from.value(); }
+ void setFrom(const qreal &from) { this->from = from; }
+ QBindable bindableFrom() { return &from; }
+ qreal getTo() const { return to.value(); }
+ void setTo(const qreal &to) { this->to = to; }
+ QBindable bindableTo() { return &to; }
+ qreal getValue() const { return value.value(); }
+ void setValue(const qreal &value) { this->value = value; }
+ QBindable bindableValue() { return &value; }
+ bool getPin() const { return pin; }
+ void setPin(const bool pin) { this->pin = pin; }
+ int getIndex() const { return index; }
+ void setIndex(int index) { this->index = index;}
+
+ signals:
+ void titleChanged();
+ void messageChanged();
+ void fromChanged();
+ void toChanged();
+ void valueChanged();
+ void pinChanged();
+ void indexChanged();
+ void propertiesChanged();
+
+ private:
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(Notification, QString, title, "",
+ &Notification::titleChanged);
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(Notification, QString, message, "",
+ &Notification::messageChanged);
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(Notification, qreal, from, 0.0,
+ &Notification::fromChanged);
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(Notification, qreal, to, 1.0,
+ &Notification::toChanged);
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(Notification, qreal, value, 0.0,
+ &Notification::valueChanged);
+
+ bool pin;
+ int index = 0;
+};
+
+class NotificationModel : public QAbstractListModel {
+ Q_OBJECT
+
+ public:
+ explicit NotificationModel(QObject *parent = nullptr);
+
+ enum NotificationRoles {
+ NotificationIndexRole = Qt::UserRole,
+ TitleRole,
+ MessageRole,
+ FromRole,
+ ToRole,
+ ValueRole,
+ };
+
+ Q_ENUM(NotificationRoles);
+
+ // Header:
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const override;
+
+ // Basic functionality:
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index,
+ int role = Qt::DisplayRole) const override;
+
+ virtual QHash roleNames() const override;
+
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+
+ Q_INVOKABLE across::Notification *append(const QString &title,
+ const QString &message, qreal from,
+ qreal to, qreal value);
+
+ Q_INVOKABLE void remove(const int id);
+
+ private:
+ QVector notifications;
+};
+
+} // namespace across
+
+#endif // NOTIFICATIONMODEL_H
diff --git a/src/views/home/CurrentPanel.qml b/src/views/home/CurrentPanel.qml
index d37f1f9..3407145 100644
--- a/src/views/home/CurrentPanel.qml
+++ b/src/views/home/CurrentPanel.qml
@@ -253,7 +253,7 @@ Item {
acrossCore.stop();
} else {
if (acrossCore.run() < 0)
- popNotify.notify(qsTr("Core Error"), qsTr("Failed to start the process"));
+ popNotify.notify(qsTr("Core Error"), qsTr("Failed to start the process"), 0.0, 1.0, 0.0, 2000);
}
}
diff --git a/src/views/home/GroupItemPopMenu.qml b/src/views/home/GroupItemPopMenu.qml
index 02e4763..7c2fbec 100644
--- a/src/views/home/GroupItemPopMenu.qml
+++ b/src/views/home/GroupItemPopMenu.qml
@@ -17,33 +17,45 @@ Menu {
Action {
text: qsTr("Edit")
onTriggered: {
- darkBackground.show();
- var groupInfo = acrossGroups.getGroupInfo(index);
- groupEditForm.groupInfo = groupInfo;
- groupEditForm.index = index;
- groupEditForm.open();
+ darkBackground.show()
+ var groupInfo = acrossGroups.getGroupInfo(index)
+
+ groupEditForm.groupInfo = groupInfo
+ groupEditForm.index = index
+ groupEditForm.open()
}
}
Action {
enabled: isSubscription
+
text: qsTr("Copy URL")
+
onTriggered: {
- acrossGroups.copyUrlToClipboard(index);
+ acrossGroups.copyUrlToClipboard(index)
}
}
Action {
text: qsTr("Copy Nodes")
+
onTriggered: {
- acrossGroups.copyNodesToClipboard(index);
+ acrossGroups.copyNodesToClipboard(index)
}
}
Action {
text: qsTr("TCP Ping")
onTriggered: {
- acrossGroups.testTcpPing(index);
+ /*
+ if(acrossGroups.testTcpPing(index) === 0){
+ popNotify.notify(qsTr("[%1] TCP Pinging...").arg(name),
+ qsTr("Testing:"),
+ index,
+ items)
+ }
+ */
+ acrossGroups.testTcpPing(index);
}
}
@@ -54,25 +66,31 @@ Menu {
height: 1
color: acrossConfig.deepColor
}
-
}
Action {
enabled: isSubscription
+
text: qsTr("Update")
onTriggered: {
- acrossGroups.checkUpdate(index);
- popNotify.notify(qsTr("[%1] Updating...").arg(name), qsTr("Updated: %1").arg(modifiedAt));
+ acrossGroups.checkUpdate(index)
+ popNotify.notify(qsTr("[%1] Updating...").arg(name),
+ qsTr("Updated: %1").arg(modifiedAt),
+ 0.0,
+ 1.0,
+ 0.0,
+ 2000)
}
}
Action {
text: qsTr("Delete")
enabled: 0 === model.index ? false : true
+
onTriggered: {
- darkBackground.show();
- removeConfirmDialog.index = index;
- removeConfirmDialog.open();
+ darkBackground.show()
+ removeConfirmDialog.index = index
+ removeConfirmDialog.open()
}
}
@@ -106,5 +124,4 @@ Menu {
}
}
-
}
diff --git a/src/views/home/GroupListPanel.qml b/src/views/home/GroupListPanel.qml
index 93457ee..683a4a0 100644
--- a/src/views/home/GroupListPanel.qml
+++ b/src/views/home/GroupListPanel.qml
@@ -84,7 +84,7 @@ Item {
updateIcon.visible = false;
}
if (updateToken && this.contentY === 0) {
- popNotify.notify(qsTr("Updating..."), qsTr("The default node will be reset"));
+ popNotify.notify(qsTr("Updating..."), qsTr("The default node will be reset"), 0.0, 1.0, 0.0, 2000);
acrossGroups.checkAllUpdate(true); // force update
updateToken = false;
}
diff --git a/src/views/home/NodeItemCard.qml b/src/views/home/NodeItemCard.qml
index b6c07c2..037b47e 100644
--- a/src/views/home/NodeItemCard.qml
+++ b/src/views/home/NodeItemCard.qml
@@ -136,7 +136,7 @@ Item {
if (mouse.button === Qt.LeftButton) {
acrossNodes.setCurrentNodeByID(nodeID);
if (!acrossCore.isRunning)
- popNotify.notify(qsTr("Core Error"), qsTr("Failed to start the process"));
+ popNotify.notify(qsTr("Core Error"), qsTr("Failed to start the process"), 0.0, 1.0, 0.0, 2000);
}
}
diff --git a/src/views/main/PopMessageBox.qml b/src/views/main/PopMessageBox.qml
index 8116534..0b1f205 100644
--- a/src/views/main/PopMessageBox.qml
+++ b/src/views/main/PopMessageBox.qml
@@ -7,9 +7,11 @@ CardBox {
id: control
property int fontSize: 12
- property string title: ""
- property string message: ""
- property int intervalTime: 5000
+ property alias title: displayTitle.text
+ property alias message: displayMessage.text
+ property alias from: progressBar.from
+ property alias to: progressBar.to
+ property alias value: progressBar.value
implicitWidth: 320
implicitHeight: 84
@@ -23,7 +25,6 @@ CardBox {
id: displayTitle
Layout.fillWidth: true
- text: control.title
color: acrossConfig.textColor
font.pointSize: fontSize
}
@@ -31,8 +32,6 @@ CardBox {
Label {
id: displayMessage
- Layout.fillWidth: true
- text: control.message
color: acrossConfig.textColor
}
@@ -42,7 +41,6 @@ CardBox {
Layout.fillWidth: true
Layout.preferredHeight: 4
padding: 2
- value: 0
background: Rectangle {
implicitWidth: 240
@@ -64,27 +62,8 @@ CardBox {
}
- NumberAnimation on value {
- id: progressBarAnimation
-
- from: 0
- to: 1
- duration: control.intervalTime
- }
-
}
}
- Timer {
- repeat: true
- running: true
- interval: intervalTime
- onTriggered: {
- if (popNotifyModel.hasChildren())
- popNotifyModel.remove(0);
-
- }
- }
-
}
diff --git a/src/views/main/PopNotify.qml b/src/views/main/PopNotify.qml
index 43fea20..91fcb11 100644
--- a/src/views/main/PopNotify.qml
+++ b/src/views/main/PopNotify.qml
@@ -6,14 +6,6 @@ import QtQuick.Layouts
Item {
id: popNotifyControl
- function notify(title = "", message = "") {
- popNotifyControl.visible = true;
- popNotifyModel.append({
- "title": title,
- "message": message
- });
- }
-
implicitWidth: 320
implicitHeight: popNotifyListView.count >= 3 ? 84 * 3 : 84 * popNotifyListView.count
clip: true
@@ -28,17 +20,22 @@ Item {
anchors.fill: parent
verticalLayoutDirection: ListView.BottomToTop
- model: popNotifyModel
+ model: acrossNotifications
move: add
+
onCountChanged: {
if (count === 0)
popNotifyControl.visible = false;
-
+ else
+ popNotifyControl.visible = true;
}
delegate: PopMessageBox {
title: model.title
message: model.message
+ from: model.from
+ value: model.value
+ to: model.to
}
add: Transition {
@@ -50,5 +47,49 @@ Item {
}
}
+ function numAnimation(parent) {
+ return Qt.createQmlObject("import QtQuick; NumberAnimation {}", parent);
+ }
+
+ function notify(title = "", message = "", from = 0.0, to = 1.0, value = 0.0, duration = -1) {
+ let notification = acrossNotifications.append(title,message,0.0,1.0,0.0);
+ if(duration != -1) {
+ let animation = new numAnimation(notification);
+ animation.from = from;
+ animation.to = to;
+ animation.duration = duration;
+ animation.target = notification;
+ animation.property = "value";
+ animation.start();
+
+ function removeNotification() {
+ acrossNotifications.remove(notification.index);
+ }
+
+ animation.finished.connect(removeNotification);
+ }
+
+ return notification;
+/*
+ // Debug
+ var noti1 = acrossNotifications.append(title,message,0.0,1.0,0.0);
+
+ function Timer() {
+ return Qt.createQmlObject("import QtQuick; Timer {}", popNotifyControl);
+ }
+
+ var timer = new Timer();
+ timer.interval = 3000;
+ timer.repeat = false;
+ noti1.title = noti1.id
+ timer.triggered.connect(function () {
+ console.log("remove",noti1.id);
+ noti1.value = 1.0
+ noti1.title = "Done"
+ acrossNotifications.remove(noti1.id)
+ })
+ timer.start();
+ */
+ }
}
diff --git a/src/views/setting/ApplicationItem.qml b/src/views/setting/ApplicationItem.qml
index 0a759ee..1711485 100644
--- a/src/views/setting/ApplicationItem.qml
+++ b/src/views/setting/ApplicationItem.qml
@@ -98,7 +98,7 @@ Item {
onCheckedChanged: {
acrossConfig.enableAutoConnect = checked;
if (checked)
- popNotify.notify(qsTr("Auto Connect"), qsTr("Set as Default > Last Connected"));
+ popNotify.notify(qsTr("Auto Connect"), qsTr("Set as Default > Last Connected"), 0.0, 1.0, 0.0, 2000);
}
}
@@ -117,7 +117,7 @@ Item {
onCheckedChanged: {
acrossConfig.enableAutoExport = checked;
if (checked)
- popNotify.notify(qsTr("Auto Export"), acrossConfig.dataDir);
+ popNotify.notify(qsTr("Auto Export"), acrossConfig.dataDir, 0.0, 1.0, 0.0, 2000);
}
}