1use iced::widget::{button, column, container, row, scrollable, text, toggler};
2use iced::{color, Alignment, Element, Length};
3
4use crate::app::{Message, ToolState, ToolUiEntry};
5
6pub fn view<'a>(state: &'a ToolState) -> Element<'a, Message> {
8 let title_bar = row![
9 text("Gaming Tools").size(20),
10 iced::widget::space::horizontal(),
11 button(text("Refresh").size(14))
12 .on_press(Message::RefreshTools)
13 .style(button::secondary)
14 .padding([6, 14]),
15 ]
16 .align_y(Alignment::Center);
17
18 let content: Element<'_, Message> = if state.entries.is_empty() {
19 container(
20 text("Select a game to manage tools, or click Refresh.").size(14),
21 )
22 .padding(20)
23 .width(Length::Fill)
24 .center_x(Length::Fill)
25 .into()
26 } else {
27 let cards = state.entries.iter().fold(column![].spacing(8), |col: iced::widget::Column<'_, Message>, entry| {
28 col.push(tool_card(entry))
29 });
30
31 scrollable(
32 container(cards)
33 .padding(12)
34 .width(Length::Fill),
35 )
36 .into()
37 };
38
39 column![title_bar, content]
40 .spacing(12)
41 .padding(12)
42 .width(Length::Fill)
43 .into()
44}
45
46fn tool_card(entry: &ToolUiEntry) -> Element<'_, Message> {
48 let tool_id = entry.tool_id.clone();
49 let available = entry.available;
50
51 let name_text = text(entry.display_name.as_str()).size(16);
53 let category_text = text(entry.category.as_str())
54 .size(11)
55 .color(color!(0x888888));
56
57 let avail_text = if available {
58 text("installed").size(11).color(color!(0x88CC88))
59 } else {
60 text("not installed").size(11).color(color!(0xFF6666))
61 };
62
63 let toggle = toggler(entry.enabled)
64 .on_toggle({
65 let tid = tool_id.clone();
66 move |enabled| Message::ToggleTool {
67 tool_id: tid.clone(),
68 enabled,
69 }
70 })
71 .size(18.0);
72
73 let header = row![
74 column![name_text, category_text].spacing(2),
75 iced::widget::space::horizontal(),
76 avail_text,
77 toggle,
78 ]
79 .align_y(Alignment::Center)
80 .spacing(12);
81
82 let mut body = column![].spacing(4);
84
85 if entry.applied_files > 0 {
86 body = body.push(
87 text(format!("{} file(s) applied to game directory", entry.applied_files))
88 .size(12)
89 .color(color!(0xAAAA66)),
90 );
91 }
92
93 if entry.has_file_patching {
95 let tid_apply = tool_id.clone();
96 let tid_revert = tool_id.clone();
97
98 let actions = row![
99 button(text("Apply").size(12))
100 .on_press_maybe(if available {
101 Some(Message::ApplyTool(tid_apply))
102 } else {
103 None
104 })
105 .style(button::primary)
106 .padding([4, 10]),
107 button(text("Revert").size(12))
108 .on_press_maybe(if entry.applied_files > 0 {
109 Some(Message::RevertTool(tid_revert))
110 } else {
111 None
112 })
113 .style(button::danger)
114 .padding([4, 10]),
115 ]
116 .spacing(6);
117
118 body = body.push(actions);
119 }
120
121 if let Some(ref msg) = entry.status_message {
123 body = body.push(
124 text(msg.as_str())
125 .size(12)
126 .color(color!(0x88CC88)),
127 );
128 }
129
130 let card = column![header, body].spacing(6);
131
132 container(card)
133 .padding(10)
134 .width(Length::Fill)
135 .style(container::rounded_box)
136 .into()
137}