feat: 重写为 GPUI 原生桌面应用并接入 Gitea 软件包发布
- gpui-kit 0.6 原生 UI:连接管理、消息流/日志、发布预设、全局变量、 设置、阿里云连接预设、原生 Logo - MQTT 5.0/3.1.1 引擎(rumqttc,TCP/TLS/WS/WSS),独立 tokio runtime + 事件泵,真实 broker 端到端测试 - 连接表单:保存前可一键测试连接(宽松校验,自动生成临时 Client ID), 高级属性(认证/MQTT 5 属性/遗嘱)按需折叠显示,WS 路径仅 WS/WSS 显示 - bug 修复:Hex 奇数长度显式报错、测试连接唯一 Client ID、Base64/Hex 发布发送真实字节、v5 发布记录携带 Content-Type/用户属性、预设应用 清除旧 Content-Type、模板时间基准统一;补 8 个单元测试 - Gitea 发布:Generic Package 上传脚本(bash/PowerShell)+ Actions release 工作流(tag 触发,双平台构建),README 使用说明
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
//! 应用设置对话框:主题、消息缓存条数、时间戳显示。
|
||||
|
||||
use gpui_kit::component::button::{Button, ButtonVariants as _};
|
||||
use gpui_kit::component::input::{Input, InputState};
|
||||
use gpui_kit::component::select::{Select, SelectState};
|
||||
use gpui_kit::component::switch::Switch;
|
||||
use gpui_kit::component::{
|
||||
h_flex, notification::Notification, v_flex, WindowExt as _,
|
||||
};
|
||||
use gpui_kit::{
|
||||
div, px, App, AppContext as _, Context, Entity, IntoElement, ParentElement as _, Render,
|
||||
Styled as _, Window,
|
||||
};
|
||||
|
||||
use crate::model::{AppSettings, ThemeModePref};
|
||||
use crate::ui::app::MqttXApp;
|
||||
use crate::ui::widgets::{field, make_select, OptionDelegate};
|
||||
|
||||
const THEMES: [&str; 3] = ["跟随系统", "浅色", "深色"];
|
||||
|
||||
struct SettingsDialog {
|
||||
theme: Entity<SelectState<OptionDelegate>>,
|
||||
max_messages: Entity<InputState>,
|
||||
show_millis: bool,
|
||||
}
|
||||
|
||||
impl SettingsDialog {
|
||||
fn new(initial: AppSettings, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let theme_idx = match initial.theme {
|
||||
ThemeModePref::System => 0,
|
||||
ThemeModePref::Light => 1,
|
||||
ThemeModePref::Dark => 2,
|
||||
};
|
||||
Self {
|
||||
theme: make_select(&THEMES, theme_idx, window, cx),
|
||||
max_messages: cx.new(|cx| {
|
||||
InputState::new(window, cx).default_value(initial.max_messages.to_string())
|
||||
}),
|
||||
show_millis: initial.show_millis,
|
||||
}
|
||||
}
|
||||
|
||||
fn collect(&self, cx: &App) -> Result<AppSettings, String> {
|
||||
let theme = match self.theme.read(cx).selected_value() {
|
||||
Some(1) => ThemeModePref::Light,
|
||||
Some(2) => ThemeModePref::Dark,
|
||||
_ => ThemeModePref::System,
|
||||
};
|
||||
let max_messages = self
|
||||
.max_messages
|
||||
.read(cx)
|
||||
.value()
|
||||
.trim()
|
||||
.parse::<usize>()
|
||||
.map_err(|_| "消息缓存条数必须是正整数".to_string())?
|
||||
.max(100);
|
||||
Ok(AppSettings {
|
||||
theme,
|
||||
max_messages,
|
||||
show_millis: self.show_millis,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for SettingsDialog {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.gap_4()
|
||||
.w(px(420.))
|
||||
.child(field("主题", Select::new(&self.theme)))
|
||||
.child(field(
|
||||
"每条连接内存中保留的消息条数",
|
||||
Input::new(&self.max_messages),
|
||||
))
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.child(div().text_sm().child("时间戳显示毫秒"))
|
||||
.child(
|
||||
Switch::new("show-millis")
|
||||
.checked(self.show_millis)
|
||||
.on_change(cx.listener(|this, v, _, cx| {
|
||||
this.show_millis = *v;
|
||||
cx.notify();
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(app: Entity<MqttXApp>, window: &mut Window, cx: &mut App) {
|
||||
let initial = app.read(cx).settings.clone();
|
||||
let dialog_view: Entity<SettingsDialog> =
|
||||
cx.new(|cx| SettingsDialog::new(initial, window, cx));
|
||||
let app_save = app.clone();
|
||||
window.open_dialog(cx, move |dialog, _window, _cx| {
|
||||
let body = dialog_view.clone();
|
||||
let for_collect = dialog_view.clone();
|
||||
let app_for_save = app_save.clone();
|
||||
dialog
|
||||
.w(px(480.))
|
||||
.title("设置")
|
||||
.child(body)
|
||||
.footer(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.justify_end()
|
||||
.w_full()
|
||||
.child(
|
||||
Button::new("settings-cancel")
|
||||
.label("取消")
|
||||
.outline()
|
||||
.on_click(|_, window, cx| window.close_dialog(cx)),
|
||||
)
|
||||
.child(
|
||||
Button::new("settings-ok")
|
||||
.label("保存")
|
||||
.primary()
|
||||
.on_click(move |_, window, cx| {
|
||||
let result = for_collect.read(cx).collect(cx);
|
||||
match result {
|
||||
Ok(settings) => {
|
||||
app_for_save.update(cx, |a, cx| {
|
||||
a.save_settings(settings, cx);
|
||||
cx.notify();
|
||||
});
|
||||
window.close_dialog(cx);
|
||||
}
|
||||
Err(e) => {
|
||||
window.push_notification(Notification::error(e), cx);
|
||||
}
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user