Skip to main content

btcli_lib/ui/
settings.rs

1// Copyright (C) 2026 S.A. (@snoware)
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use crate::ui::lovely_items;
8use cursive::Cursive;
9use cursive::traits::{Nameable, Resizable};
10use cursive::views::{Button, Checkbox, Dialog, EditView, LinearLayout, TextView};
11
12use log::debug;
13use std::fs;
14
15pub fn build_settings_view() -> Dialog {
16    let settings_layout = LinearLayout::vertical()
17        .child(
18            LinearLayout::horizontal()
19                .child(TextView::new("AppID: ").fixed_width(10))
20                .child(EditView::new().with_name("appid").fixed_width(20)),
21        )
22        .child(
23            LinearLayout::horizontal()
24                .child(TextView::new("API Key: ").fixed_width(10))
25                .child(EditView::new().with_name("key").fixed_width(20)),
26        )
27        .child(
28            LinearLayout::horizontal()
29                .child(TextView::new("目标语言: ").fixed_width(10))
30                .child(EditView::new().with_name("source_lang").fixed_width(20)),
31        )
32        .child(
33            LinearLayout::horizontal()
34                .child(TextView::new("目标语言: ").fixed_width(10))
35                .child(EditView::new().with_name("target_lang").fixed_width(20)),
36        )
37        .child(
38            LinearLayout::horizontal()
39                .child(TextView::new("开启调试: ").fixed_width(10))
40                .child(
41                    Checkbox::new()
42                        .with_checked(false)
43                        .with_name("enable_debug"),
44                ),
45        )
46        .child(
47            LinearLayout::horizontal()
48                .child(Button::new("保存", |s| confirm_save_settings(s)))
49                .child(Button::new("返回", |s| {
50                    let _ = s.pop_layer();
51                })),
52        );
53
54    let dialog = Dialog::around(settings_layout).title("设置");
55
56    // 立即返回对话框,然后在下一个事件循环中填充配置
57    // 但我们需要一种方式在对话框添加后填充配置
58    dialog
59}
60
61// 添加只读设置视图
62pub fn build_view_only_settings_view() -> Dialog {
63    let settings_layout = LinearLayout::vertical()
64        .child(
65            LinearLayout::horizontal()
66                .child(TextView::new("AppID: ").fixed_width(10))
67                .child(TextView::new("").with_name("view_appid").fixed_width(20)),
68        )
69        .child(
70            LinearLayout::horizontal()
71                .child(TextView::new("API Key: ").fixed_width(10))
72                .child(TextView::new("").with_name("view_key").fixed_width(20)),
73        )
74        .child(
75            LinearLayout::horizontal()
76                .child(TextView::new("目标语言: ").fixed_width(10))
77                .child(
78                    TextView::new("")
79                        .with_name("view_source_lang")
80                        .fixed_width(20),
81                ),
82        )
83        .child(
84            LinearLayout::horizontal()
85                .child(TextView::new("目标语言: ").fixed_width(10))
86                .child(
87                    TextView::new("")
88                        .with_name("view_target_lang")
89                        .fixed_width(20),
90                ),
91        )
92        .child(
93            LinearLayout::horizontal()
94                .child(TextView::new("开启调试: ").fixed_width(10))
95                .child(
96                    TextView::new("")
97                        .with_name("view_enable_debug")
98                        .fixed_width(20),
99                ),
100        )
101        .child(
102            LinearLayout::horizontal()
103                .child(Button::new("修改设置", |s| {
104                    // 移除当前层,显示编辑设置界面
105                    s.pop_layer();
106                    s.add_layer(build_settings_view());
107                    // 延迟填充设置,确保UI控件已完全加载
108                    s.cb_sink()
109                        .send(Box::new(|s| {
110                            populate_settings_view(s);
111                        }))
112                        .unwrap_or(());
113                }))
114                .child(Button::new("清理日志", |s| {
115                    // 清理日志文件
116                    clear_log_file(s);
117                }))
118                .child(Button::new("返回", |s| {
119                    let _ = s.pop_layer();
120                })),
121        );
122
123    let dialog = Dialog::around(settings_layout).title("查看设置");
124
125    // 立即返回对话框,然后在下一个事件循环中填充配置
126    dialog
127}
128
129// 清理日志文件的函数
130fn clear_log_file(s: &mut Cursive) {
131    match fs::OpenOptions::new()
132        .write(true)
133        .truncate(true)
134        .open("btcli_app.log")
135    {
136        Ok(_) => {
137            log_to_file!("日志文件已清理");
138            lovely_items::show_info(s, "日志文件已清理");
139        }
140        Err(e) => {
141            log_to_file!("清理日志文件失败: {}", e);
142            lovely_items::show_error(s, &format!("清理日志文件失败: {}", e));
143        }
144    }
145}
146
147// 专门用于设置初始值的函数
148pub fn populate_settings_view(s: &mut Cursive) {
149    debug!("Attempting to populate settings view");
150    log_to_file!("开始填充设置界面");
151
152    match crate::conf::try_init_conf() {
153        Ok(config) => {
154            debug!("Configuration loaded successfully, populating fields");
155            debug!("AppID: {}", config.appid);
156            debug!("Key length: {}", config.key.len());
157            debug!("Source Language: {}", config.source_lang);
158            debug!("Target Language: {}", config.target_lang);
159            debug!("Enable Logging: {}", config.enable_logging);
160
161            log_to_file!(
162                "成功加载配置,开始填充界面: AppID={}, SourceLang={}, TargetLang={}",
163                config.appid,
164                config.source_lang,
165                config.target_lang
166            );
167
168            // 使用call_on_name来更新每个控件的值
169            if let Some(_) = s.call_on_name("appid", |view: &mut EditView| {
170                debug!("Setting appid field: {}", &config.appid);
171                log_to_file!("设置AppID字段: {}", &config.appid);
172                view.set_content(&config.appid);
173            }) {
174                debug!("Appid field updated");
175                log_to_file!("AppID字段已更新");
176            } else {
177                debug!("Failed to update appid field - control may not exist");
178                log_to_file!("更新AppID字段失败 - 控件可能不存在");
179            }
180
181            if let Some(_) = s.call_on_name("key", |view: &mut EditView| {
182                debug!(
183                    "Setting key field: {}",
184                    format!("{}...", &config.key.chars().take(3).collect::<String>())
185                );
186                log_to_file!("设置API Key字段");
187                view.set_content(&config.key);
188            }) {
189                debug!("Key field updated");
190                log_to_file!("API Key字段已更新");
191            } else {
192                debug!("Failed to update key field - control may not exist");
193                log_to_file!("更新API Key字段失败 - 控件可能不存在");
194            }
195
196            if let Some(_) = s.call_on_name("source_lang", |view: &mut EditView| {
197                debug!("Setting source_lang field: {}", &config.source_lang);
198                log_to_file!("设置目标语言字段: {}", &config.source_lang);
199                view.set_content(&config.source_lang);
200            }) {
201                debug!("Source language field updated");
202                log_to_file!("目标语言字段已更新");
203            } else {
204                debug!("Failed to update source_lang field - control may not exist");
205                log_to_file!("更新目标语言字段失败 - 控件可能不存在");
206            }
207
208            if let Some(_) = s.call_on_name("target_lang", |view: &mut EditView| {
209                debug!("Setting target_lang field: {}", &config.target_lang);
210                log_to_file!("设置目标语言字段: {}", &config.target_lang);
211                view.set_content(&config.target_lang);
212            }) {
213                debug!("Target language field updated");
214                log_to_file!("目标语言字段已更新");
215            } else {
216                debug!("Failed to update target_lang field - control may not exist");
217                log_to_file!("更新目标语言字段失败 - 控件可能不存在");
218            }
219
220            if let Some(_) = s.call_on_name("enable_debug", |view: &mut Checkbox| {
221                debug!("Setting enable_debug checkbox: {}", config.enable_logging);
222                log_to_file!("设置调试复选框: {}", config.enable_logging);
223                view.set_checked(config.enable_logging);
224            }) {
225                debug!("Debug checkbox updated");
226                log_to_file!("调试复选框已更新");
227            } else {
228                debug!("Failed to update enable_debug checkbox - control may not exist");
229                log_to_file!("更新调试复选框失败 - 控件可能不存在");
230            }
231        }
232        Err(e) => {
233            debug!("Failed to load configuration: {}", e);
234            log_to_file!("加载配置失败: {}", e);
235            // 如果配置文件不存在或有错误,不填充任何值,让用户从头开始配置
236        }
237    }
238}
239
240// 专门用于填充只读设置视图的函数
241pub fn populate_view_only_settings_view(s: &mut Cursive) {
242    debug!("Attempting to populate view-only settings view");
243    log_to_file!("开始填充只读设置界面");
244
245    match crate::conf::try_init_conf() {
246        Ok(config) => {
247            debug!("Configuration loaded successfully, populating view-only fields");
248            debug!("AppID: {}", config.appid);
249            debug!("Key length: {}", config.key.len());
250            debug!("Source Language: {}", config.source_lang);
251            debug!("Target Language: {}", config.target_lang);
252            debug!("Enable Logging: {}", config.enable_logging);
253
254            log_to_file!(
255                "成功加载配置,开始填充只读界面: AppID={}, SourceLang={}, TargetLang={}",
256                config.appid,
257                config.source_lang,
258                config.target_lang
259            );
260
261            // 使用call_on_name来更新每个只读控件的值
262            if let Some(_) = s.call_on_name("view_appid", |view: &mut TextView| {
263                debug!("Setting view_appid field: {}", &config.appid);
264                log_to_file!("设置只读AppID字段: {}", &config.appid);
265                view.set_content(&config.appid);
266            }) {
267                debug!("View appid field updated");
268                log_to_file!("只读AppID字段已更新");
269            } else {
270                debug!("Failed to update view_appid field - control may not exist");
271                log_to_file!("更新只读AppID字段失败 - 控件可能不存在");
272            }
273
274            if let Some(_) = s.call_on_name("view_key", |view: &mut TextView| {
275                // 显示隐藏的密钥
276                let masked_key = if config.key.len() > 6 {
277                    let chars: Vec<char> = config.key.chars().collect();
278                    let visible_part: String = chars.iter().take(3).collect();
279                    let hidden_count = chars.len() - 6;
280                    format!("{}***{}", visible_part, &config.key[hidden_count..])
281                } else {
282                    "*".repeat(config.key.len())
283                };
284
285                debug!("Setting view_key field: {}", &masked_key);
286                log_to_file!("设置只读API Key字段");
287                view.set_content(masked_key);
288            }) {
289                debug!("View key field updated");
290                log_to_file!("只读API Key字段已更新");
291            } else {
292                debug!("Failed to update view_key field - control may not exist");
293                log_to_file!("更新只读API Key字段失败 - 控件可能不存在");
294            }
295
296            if let Some(_) = s.call_on_name("view_source_lang", |view: &mut TextView| {
297                debug!("Setting view_source_lang field: {}", &config.source_lang);
298                log_to_file!("设置只读目标语言字段: {}", &config.source_lang);
299                view.set_content(&config.source_lang);
300            }) {
301                debug!("View source language field updated");
302                log_to_file!("只读目标语言字段已更新");
303            } else {
304                debug!("Failed to update view_source_lang field - control may not exist");
305                log_to_file!("更新只读目标语言字段失败 - 控件可能不存在");
306            }
307
308            if let Some(_) = s.call_on_name("view_target_lang", |view: &mut TextView| {
309                debug!("Setting view_target_lang field: {}", &config.target_lang);
310                log_to_file!("设置只读目标语言字段: {}", &config.target_lang);
311                view.set_content(&config.target_lang);
312            }) {
313                debug!("View target language field updated");
314                log_to_file!("只读目标语言字段已更新");
315            } else {
316                debug!("Failed to update view_target_lang field - control may not exist");
317                log_to_file!("更新只读目标语言字段失败 - 控件可能不存在");
318            }
319
320            if let Some(_) = s.call_on_name("view_enable_debug", |view: &mut TextView| {
321                let debug_status = if config.enable_logging { "是" } else { "否" };
322                debug!("Setting view_enable_debug field: {}", debug_status);
323                log_to_file!("设置只读调试字段: {}", debug_status);
324                view.set_content(debug_status);
325            }) {
326                debug!("View debug checkbox updated");
327                log_to_file!("只读调试字段已更新");
328            } else {
329                debug!("Failed to update view_enable_debug field - control may not exist");
330                log_to_file!("更新只读调试字段失败 - 控件可能不存在");
331            }
332        }
333        Err(e) => {
334            debug!("Failed to load configuration: {}", e);
335            log_to_file!("加载配置失败: {}", e);
336            // 如果配置文件不存在或有错误,显示提示信息
337            let error_msg = format!("配置未设置: {}", e);
338            s.call_on_name("view_appid", |view: &mut TextView| {
339                view.set_content(&error_msg);
340            });
341            s.call_on_name("view_key", |view: &mut TextView| {
342                view.set_content(&error_msg);
343            });
344            s.call_on_name("view_source_lang", |view: &mut TextView| {
345                view.set_content(&error_msg);
346            });
347            s.call_on_name("view_target_lang", |view: &mut TextView| {
348                view.set_content(&error_msg);
349            });
350            s.call_on_name("view_enable_debug", |view: &mut TextView| {
351                view.set_content("未设置");
352            });
353        }
354    }
355}
356
357fn confirm_save_settings(s: &mut Cursive) {
358    log_to_file!("显示保存确认对话框");
359    lovely_items::show_confirmation(
360        s,
361        "确认保存",
362        "您确定要保存这些设置吗?",
363        |s| save_settings(s), // "是" 回调
364        |_s| {},              // "否" 回调 - 什么也不做,返回到设置界面
365    );
366}
367
368fn save_settings(s: &mut Cursive) {
369    log_to_file!("开始保存设置");
370    // 获取输入并保存
371    let appid = s
372        .call_on_name("appid", |view: &mut EditView| view.get_content())
373        .unwrap_or_default();
374    let key = s
375        .call_on_name("key", |view: &mut EditView| view.get_content())
376        .unwrap_or_default();
377    let source_lang = s
378        .call_on_name("source_lang", |view: &mut EditView| view.get_content())
379        .unwrap_or_default();
380    let target_lang = s
381        .call_on_name("target_lang", |view: &mut EditView| view.get_content())
382        .unwrap_or_default();
383    let enable_debug = s
384        .call_on_name("enable_debug", |view: &mut Checkbox| view.is_checked())
385        .unwrap_or_default();
386
387    log_to_file!(
388        "从UI获取的设置 - AppID: {}, Key长度: {}, SourceLang: {}, TargetLang: {}, EnableDebug: {}",
389        appid,
390        key.len(),
391        source_lang,
392        target_lang,
393        enable_debug
394    );
395
396    // 验证输入
397    if appid.is_empty() || key.is_empty() || source_lang.is_empty() || target_lang.is_empty() {
398        log_to_file!("输入验证失败 - 存在空字段");
399        lovely_items::show_error(s, "请填写所有字段");
400        return;
401    }
402
403    // 保存到配置
404    match crate::conf::save_conf_with_debug(&appid, &key, &source_lang, &target_lang, enable_debug)
405    {
406        Ok(_) => {
407            log_to_file!(
408                "配置保存成功 - AppID: {}, SourceLang: {}, TargetLang: {}",
409                appid,
410                source_lang,
411                target_lang
412            );
413            lovely_items::show_error(s, "配置保存成功");
414            // 自动关闭设置窗口
415            s.pop_layer();
416        }
417        Err(e) => {
418            log_to_file!("配置保存失败: {}", e);
419            lovely_items::show_error(s, &format!("保存配置失败: {}", e));
420        }
421    }
422}