Skip to main content

btcli_lib/ui/
index.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::*;
8use cursive::traits::{Nameable, Resizable};
9use cursive::{
10    Cursive,
11    views::{Button, Dialog, LinearLayout, TextArea, TextView},
12};
13
14use std::cell::Cell;
15
16// 使用 Cell 来安全地存储可变状态
17thread_local! {
18    static ASK_ABOUT_SETTINGS: Cell<bool> = Cell::new(true);
19}
20
21pub fn build_main_view() -> LinearLayout {
22    let mut layout = LinearLayout::vertical();
23
24    // 创建带标签的输入区域
25    let input_layout = LinearLayout::horizontal()
26        .child(TextView::new("源文字: ").fixed_width(10))
27        .child(
28            TextArea::new()
29                .with_name("input_textarea")
30                .min_size((30, 5)),
31        );
32
33    // 创建带标签的输出区域
34    let output_layout = LinearLayout::horizontal()
35        .child(TextView::new("下面翻译结果: ").fixed_width(10))
36        .child(
37            TextView::new("")
38                .with_name("output_textview")
39                .min_size((30, 5)),
40        );
41
42    let button_row = LinearLayout::horizontal()
43        .child(Button::new("[翻译(T)]", |s| translate_with_ask(s)))
44        .child(Button::new("[清空(C)]", |s| clear_texts(s)))
45        .child(Button::new("[查看设置(V)]", |s| {
46            s.add_layer(settings::build_view_only_settings_view());
47            // 延迟填充设置,确保UI控件已完全加载
48            s.cb_sink()
49                .send(Box::new(|s| {
50                    settings::populate_view_only_settings_view(s);
51                }))
52                .unwrap_or(());
53        }))
54        .child(Button::new("[帮助(H)]", |s| {
55            s.add_layer(help::build_help_view())
56        }))
57        .child(Button::new("[关于(A)]", |s| {
58            s.add_layer(about::build_about_view())
59        }))
60        .child(Button::new("[退出(Q)]", |s| s.quit()));
61
62    layout.add_child(input_layout);
63    layout.add_child(output_layout);
64    layout.add_child(button_row);
65
66    layout
67}
68
69fn translate(s: &mut Cursive) {
70    // 先获取输入内容,避免生命周期问题
71    let input_content_opt = s.call_on_name("input_textarea", |view: &mut TextArea| {
72        view.get_content().to_string() // 转换为拥有所有权的String
73    });
74
75    // 检查是否有内容
76    let input_content = match input_content_opt {
77        Some(content) => {
78            if content.trim().is_empty() {
79                lovely_items::show_error(s, "请输入要翻译的文本");
80                return;
81            }
82            content
83        }
84        None => {
85            lovely_items::show_error(s, "无法获取输入文本");
86            return;
87        }
88    };
89
90    // 获取配置
91    let config = match crate::conf::try_init_conf() {
92        Ok(config) => config,
93        Err(error_msg) => {
94            lovely_items::show_error(s, &error_msg);
95            return;
96        }
97    };
98
99    // 执行翻译
100    match crate::fycore::translate(
101        &config.appid,
102        &config.source_lang,
103        &config.target_lang,
104        &input_content,
105        config.clone(),
106    ) {
107        Ok(result) => {
108            s.call_on_name("output_textview", |view: &mut TextView| {
109                view.set_content(&result);
110            });
111        }
112        Err(error_msg) => {
113            lovely_items::show_error(s, &error_msg);
114        }
115    }
116}
117
118pub fn translate_with_ask(s: &mut Cursive) {
119    if ASK_ABOUT_SETTINGS.with(|ask| ask.get()) {
120        let dialog = Dialog::new()
121            .title("翻译设置")
122            .content(cursive::views::TextView::new("是否需要修改翻译设置?"))
123            .button("是", |s| {
124                s.add_layer(settings::build_view_only_settings_view());
125                // 延迟填充设置,确保UI控件已完全加载
126                s.cb_sink()
127                    .send(Box::new(|s| {
128                        settings::populate_view_only_settings_view(s);
129                    }))
130                    .unwrap_or(());
131            })
132            .button("否,本次不再询问", |s| {
133                ASK_ABOUT_SETTINGS.with(|ask| ask.set(false));
134                s.pop_layer();
135                translate(s);
136            })
137            .button("否", |s| {
138                s.pop_layer();
139                translate(s);
140            });
141
142        s.add_layer(dialog);
143    } else {
144        translate(s);
145    }
146}
147
148pub fn clear_texts(s: &mut Cursive) {
149    s.call_on_name("input_textarea", |view: &mut TextArea| view.set_content(""));
150    s.call_on_name("output_textview", |view: &mut TextView| {
151        view.set_content("");
152    });
153}