From e6df668153ea996b233984dfcb0e86b1de0a5eb3 Mon Sep 17 00:00:00 2001 From: amlo Date: Sat, 15 Jan 2022 21:13:52 +0800 Subject: [PATCH] feat: toast & check new version --- Cargo.lock | 14 ++ Cargo.toml | 9 ++ api_cpp_binding/Cargo.toml | 3 +- api_cpp_binding/src/lib.rs | 2 + api_cpp_binding/src/version.rs | 7 + .../bas_files/3d\350\277\267\345\256\253.txt" | 2 +- gui/src/gvb/gvbsim_window.cpp | 9 +- gui/src/mainwindow.cpp | 153 +++++++++++++----- gui/src/mainwindow.h | 6 +- gui/src/toast.cpp | 57 +++---- gui/src/toast.h | 7 +- src/lib.rs | 7 + 12 files changed, 193 insertions(+), 83 deletions(-) create mode 100644 api_cpp_binding/src/version.rs create mode 100644 src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 1f78571..8186bdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,7 @@ version = "0.1.0" dependencies = [ "config", "gvb_interp", + "wqxtools", ] [[package]] @@ -634,6 +635,12 @@ version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58f57ca1d128a43733fd71d583e837b1f22239a37ebea09cde11d8d9a9080f47" +[[package]] +name = "semver" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" + [[package]] name = "serde" version = "1.0.130" @@ -818,6 +825,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "wqxtools" +version = "0.0.1" +dependencies = [ + "semver", +] + [[package]] name = "xoroshiro128" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 51035b5..c0c56e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,12 @@ +[package] +name = "wqxtools" +version = "0.0.1" +authors = ["amlo "] +edition = "2021" + +[dependencies] +semver = "1.0.4" + [workspace] members = [ "bin_dasm", diff --git a/api_cpp_binding/Cargo.toml b/api_cpp_binding/Cargo.toml index 0a607cd..981aa83 100644 --- a/api_cpp_binding/Cargo.toml +++ b/api_cpp_binding/Cargo.toml @@ -10,4 +10,5 @@ crate-type = ["staticlib"] [dependencies] config = { version = "0.1.0", path = "../config" } -gvb_interp = { version = "0.1.0", path = "../gvb_interp" } \ No newline at end of file +gvb_interp = { version = "0.1.0", path = "../gvb_interp" } +wqxtools = { path = ".." } diff --git a/api_cpp_binding/src/lib.rs b/api_cpp_binding/src/lib.rs index d4e33d6..8caeb1d 100644 --- a/api_cpp_binding/src/lib.rs +++ b/api_cpp_binding/src/lib.rs @@ -5,12 +5,14 @@ pub mod config; pub mod gvb; pub mod string; pub mod types; +pub mod version; pub use self::array::*; pub use self::config::*; pub use self::gvb::*; pub use self::string::*; pub use self::types::*; +pub use self::version::*; #[repr(C)] pub struct Unit(pub i32); diff --git a/api_cpp_binding/src/version.rs b/api_cpp_binding/src/version.rs new file mode 100644 index 0000000..1158a59 --- /dev/null +++ b/api_cpp_binding/src/version.rs @@ -0,0 +1,7 @@ +use crate::{Maybe, Utf8Str}; + +#[no_mangle] +pub extern "C" fn is_new_version(ver: Utf8Str) -> Maybe { + wqxtools::is_new_version(unsafe { ver.as_str() }) + .map_or(Maybe::Nothing, Maybe::Just) +} diff --git "a/gui/bas_files/3d\350\277\267\345\256\253.txt" "b/gui/bas_files/3d\350\277\267\345\256\253.txt" index 67d9738..974e12f 100644 --- "a/gui/bas_files/3d\350\277\267\345\256\253.txt" +++ "b/gui/bas_files/3d\350\277\267\345\256\253.txt" @@ -14,7 +14,7 @@ 95 IF B%(I,J,1)THEN 92 ELSE B%(I,J,1)=1:98 96 IF B%(I+1,J,0)THEN 92 ELSE B%(I+1,J,0)=1:98 97 IF B%(I,J+1,1)THEN 92 ELSE B%(I,J+1,1)=1 -98 NEXT:NEXT +98 NEXT:NEXT 100 FOR K=1 TO L:B%(K,0,0)=1:B%(K,L,0)=1:B%(0,K,1)=1:B%(L,K,1)=1:NEXT 104 LB=(6-L/2)*5:LBB=(6+L/2)*5 110 cls:X=1:Y=INT(RND(1)*L)+1:P=L:Q=INT(RND(1)*L)+1:YX=(GB-1)*4+L/2-2 diff --git a/gui/src/gvb/gvbsim_window.cpp b/gui/src/gvb/gvbsim_window.cpp index ac8c09e..3b0953c 100644 --- a/gui/src/gvb/gvbsim_window.cpp +++ b/gui/src/gvb/gvbsim_window.cpp @@ -1,9 +1,9 @@ #include "gvbsim_window.h" #include -#include #include #include +#include #include #include #include @@ -240,11 +240,8 @@ void GvbSimWindow::stop() { auto result = api::gvb_vm_stop(m_vm); if (result.tag == api::GvbStopVmResult::Tag::Left) { - QMessageBox::critical( - this, - "错误", - QString("运行时错误:%1") - .arg(QString::fromUtf8(result.left._0.data, result.left._0.len))); + auto msg = QString::fromUtf8(result.left._0.data, result.left._0.len); + m_message.setValue(QString("程序运行出错:") + msg); destroy_string(result.left._0); } api::gvb_reset_exec_result(&m_execResult); diff --git a/gui/src/mainwindow.cpp b/gui/src/mainwindow.cpp index 28afe0b..8f65105 100644 --- a/gui/src/mainwindow.cpp +++ b/gui/src/mainwindow.cpp @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -20,7 +22,6 @@ #include #include -#include "toast.h" #include "about_dialog.h" #include "action.h" #include "api.h" @@ -39,14 +40,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), - m_networkMan(new QNetworkAccessManager(this)), - m_manualUpdate(false) { + m_networkMan(new QNetworkAccessManager(this)) { m_networkMan->setTransferTimeout(3000); - connect( - m_networkMan, - &QNetworkAccessManager::finished, - this, - &MainWindow::versionRequestFinished); initUi(); @@ -76,8 +71,7 @@ MainWindow::MainWindow(QWidget *parent) : openFileByPath(args.at(1), qApp->primaryScreen()); } - m_manualUpdate = false; - requestVersions(); + checkNewVersion(false); }); } @@ -192,8 +186,15 @@ void MainWindow::initMenu() { auto mnuHelp = menuBar()->addMenu("帮助"); - auto actAbout = mnuHelp->addAction("关于"); + auto actCheckVer = mnuHelp->addAction("检查新版本"); + connect(actCheckVer, &QAction::triggered, this, [this] { + showMessage("正在检查版本更新", 1000, MessageType::Info); + checkNewVersion(true); + }); + + mnuHelp->addSeparator(); + auto actAbout = mnuHelp->addAction("关于"); connect(actAbout, &QAction::triggered, this, [this] { AboutDialog(this).exec(); }); @@ -654,36 +655,116 @@ void MainWindow::dropEvent(QDropEvent *ev) { } } -void MainWindow::requestVersions() { - m_networkMan->get(QNetworkRequest(QUrl(VERSION_API_ENDPOINT))); -} +void MainWindow::checkNewVersion(bool manual) { + auto reply = m_networkMan->get(QNetworkRequest(QUrl(VERSION_API_ENDPOINT))); + connect(reply, &QNetworkReply::finished, [=] { + reply->deleteLater(); + + if (reply->error()) { + if (manual) { + QString msg; + switch (reply->error()) { + case QNetworkReply::TimeoutError: + msg = "连接超时"; + break; + case QNetworkReply::TemporaryNetworkFailureError: + msg = "网络断开"; + break; + default: + msg = reply->errorString(); + break; + } + QMessageBox::critical(this, "错误", QString("检查版本失败:") + msg); + } -void MainWindow::versionRequestFinished(QNetworkReply *reply) { - reply->deleteLater(); - if (reply->error()) { - if (!m_manualUpdate) { + return; + } + auto resp = reply->readAll(); + QJsonParseError error; + auto json = QJsonDocument::fromJson(resp, &error); + if (error.error != QJsonParseError::NoError) { + if (manual) { + QMessageBox::critical(this, "错误", "检查版本失败:JSON parse error"); + } return; } - QString msg; - switch (reply->error()) { - case QNetworkReply::TimeoutError: - msg = "连接超时"; - break; - case QNetworkReply::TemporaryNetworkFailureError: - msg = "网络断开"; - break; - default: - msg = reply->errorString(); - break; + auto tag = json.array().at(0).toObject()["tag_name"].toString().toUtf8(); + auto result = + api::is_new_version({tag.data(), static_cast(tag.size())}); + if (result.tag == api::Maybe::Tag::Nothing) { + if (manual) { + QMessageBox::critical( + this, + "错误", + "检查版本失败:release tag_name is not semver"); + } + return; } - QMessageBox::critical(this, "错误", QString("检查版本失败:") + msg); - return; - } - auto resp = reply->readAll(); - QJsonParseError error; - auto json = QJsonDocument::fromJson(resp, &error); - // TODO + + if (manual) { + if (result.just._0) { + notifyNewVersion(tag); + } else { + showMessage("已经是最新版本", 700, MessageType::Info); + } + } else if (result.just._0) { + showMessage( + "有新版本,请点击菜单 [帮助] -> [检查新版本] 查看新版本", + 1500, + MessageType::Info); + } + }); +} + +void MainWindow::notifyNewVersion(const QString &tag) { + auto reply = m_networkMan->get( + QNetworkRequest(QUrl(QString(VERSION_API_ENDPOINT "/%1?include_html_description=true").arg(tag)))); + connect(reply, &QNetworkReply::finished, [=] { + reply->deleteLater(); + if (reply->error()) { + QString msg; + switch (reply->error()) { + case QNetworkReply::TimeoutError: + msg = "连接超时"; + break; + case QNetworkReply::TemporaryNetworkFailureError: + msg = "网络断开"; + break; + default: + msg = reply->errorString(); + break; + } + QMessageBox::critical( + this, + "错误", + QString("获取新版本信息失败:") + msg); + } + auto resp = reply->readAll(); + QJsonParseError error; + auto json = QJsonDocument::fromJson(resp, &error); + if (error.error != QJsonParseError::NoError) { + QMessageBox::critical( + this, + "错误", + "获取新版本信息失败:JSON parse error"); + return; + } + + auto release = json.object(); + auto description = release["description_html"].toString(); + auto url = release["_links"].toObject()["self"].toString(); + + m_toast->hide(); + + QMessageBox::information( + this, + "新版本", + QString("

%1

%2

点击链接下载新版本") + .arg(tag) + .arg(description) + .arg(url)); + }); } void MainWindow::showMessage(const QString &text, int ms, MessageType type) { diff --git a/gui/src/mainwindow.h b/gui/src/mainwindow.h index 9a2e81c..a6b82a0 100644 --- a/gui/src/mainwindow.h +++ b/gui/src/mainwindow.h @@ -35,7 +35,8 @@ class MainWindow: public QMainWindow { void initUi(); void initMenu(); ActionResult confirmSaveIfDirty(ToolWidget *); - void requestVersions(); + void checkNewVersion(bool manual); + void notifyNewVersion(const QString &tag); private slots: void openFile(); @@ -43,7 +44,6 @@ private slots: ActionResult saveFile(); ActionResult saveFileAs(bool save = false); void setTitle(); - void versionRequestFinished(QNetworkReply *); void showMessage(const QString &, int ms, MessageType); private: @@ -83,6 +83,4 @@ private slots: BoolValue m_loaded; QVector m_extraEditActions; - - bool m_manualUpdate; }; \ No newline at end of file diff --git a/gui/src/toast.cpp b/gui/src/toast.cpp index 1d7ba75..c14c5bd 100644 --- a/gui/src/toast.cpp +++ b/gui/src/toast.cpp @@ -1,7 +1,6 @@ #include "toast.h" #include -#include #include #include #include @@ -10,21 +9,29 @@ #include Toast::Toast(QWidget *parent) : - QFrame(parent, Qt::FramelessWindowHint | Qt::Tool), + QWidget(parent, Qt::FramelessWindowHint | Qt::Tool), m_label(new QLabel(this)), m_opacityEffect(new QGraphicsOpacityEffect(this)), - m_fade(new QPropertyAnimation(m_opacityEffect, "opacity", this)), + m_fadeIn(new QPropertyAnimation(m_opacityEffect, "opacity", this)), + m_fadeOut(new QPropertyAnimation(m_opacityEffect, "opacity", this)), m_timer(0) { setAttribute(Qt::WA_TranslucentBackground); setGraphicsEffect(m_opacityEffect); - setFrameStyle(QFrame::Box); - m_fade->setEndValue(1); + m_fadeIn->setEndValue(1); + m_fadeIn->setDuration(100); + connect( + m_fadeIn, + &QPropertyAnimation::finished, + this, + &Toast::startFadeOutTimer); - auto layout = new QHBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); + m_fadeOut->setStartValue(1); + m_fadeOut->setEndValue(0); + m_fadeOut->setDuration(250); + connect(m_fadeOut, &QPropertyAnimation::finished, this, &QWidget::hide); - m_label->setContentsMargins(15, 8, 15, 8); + auto layout = new QHBoxLayout(this); layout->addWidget(m_label); hide(); @@ -32,20 +39,14 @@ Toast::Toast(QWidget *parent) : void Toast::showText(const QString &text, int ms) { m_label->setText(text); + m_label->adjustSize(); + adjustSize(); m_delay = ms; if (m_timer == 0) { show(); - m_fade->stop(); - disconnect(m_fade, &QPropertyAnimation::finished, this, &QWidget::hide); - m_fade->setDirection(QAbstractAnimation::Forward); - m_fade->setStartValue(m_opacityEffect->opacity()); - m_fade->setDuration(100); - connect( - m_fade, - &QPropertyAnimation::finished, - this, - &Toast::startFadeOutTimer); - m_fade->start(); + m_fadeOut->stop(); + m_fadeIn->setStartValue(m_opacityEffect->opacity()); + m_fadeIn->start(); } else { killTimer(m_timer); m_timer = 0; @@ -67,16 +68,7 @@ void Toast::timerEvent(QTimerEvent *) { killTimer(m_timer); m_timer = 0; - m_fade->setDuration(250); - m_fade->setStartValue(0); - m_fade->setDirection(QAbstractAnimation::Backward); - m_fade->start(); - disconnect( - m_fade, - &QPropertyAnimation::finished, - this, - &Toast::startFadeOutTimer); - connect(m_fade, &QPropertyAnimation::finished, this, &QWidget::hide); + m_fadeOut->start(); } void Toast::startFadeOutTimer() { @@ -84,7 +76,8 @@ void Toast::startFadeOutTimer() { } void Toast::paintEvent(QPaintEvent *) { - QPainter painter(this); - painter.setBrush(palette().window()); - painter.drawRect(0, 0, width(), height()); + QPainter p(this); + p.setBrush(palette().window()); + p.setPen(palette().windowText().color()); + p.drawRect(rect() - QMargins {1, 1, 1, 1}); } \ No newline at end of file diff --git a/gui/src/toast.h b/gui/src/toast.h index f92d84a..4ad62cd 100644 --- a/gui/src/toast.h +++ b/gui/src/toast.h @@ -1,6 +1,6 @@ #pragma once -#include +#include class QString; class QTimerEvent; @@ -9,7 +9,7 @@ class QLabel; class QPropertyAnimation; class QGraphicsOpacityEffect; -class Toast : public QFrame { +class Toast : public QWidget { Q_OBJECT public: @@ -28,7 +28,8 @@ class Toast : public QFrame { private: QLabel *m_label; QGraphicsOpacityEffect *m_opacityEffect; - QPropertyAnimation *m_fade; + QPropertyAnimation *m_fadeIn; + QPropertyAnimation *m_fadeOut; int m_timer; int m_delay; }; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9fd907a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +use semver::Version; + +pub fn is_new_version(ver: &str) -> Result { + let ver = ver.parse::()?; + let cur_ver = env!("CARGO_PKG_VERSION").parse::().unwrap(); + Ok(ver > cur_ver) +}