gitkraft_gui/widgets/
header.rs1use iced::widget::{button, container, row, text, Space};
6use iced::{Alignment, Element, Length};
7
8use crate::features::editor::editor_selector;
9use crate::features::theme::view::theme_selector;
10use crate::icons;
11use crate::message::Message;
12use crate::state::GitKraft;
13use crate::theme;
14use crate::view_utils;
15
16pub fn view(state: &GitKraft) -> Element<'_, Message> {
18 let tab = state.active_tab();
19 let c = state.colors();
20 let dims = state.layout();
21 let compact = dims.compact_toolbar;
22
23 let repo_icon = icon!(icons::FOLDER_OPEN, 14, c.accent);
25
26 let repo_name = text(state.repo_display_name())
27 .size(14)
28 .color(c.text_primary)
29 .wrapping(iced::widget::text::Wrapping::None);
30
31 let separator = || text("|").size(14).color(c.border);
32
33 let branch_icon = icon!(icons::GIT_BRANCH, 14, c.green);
35
36 let branch_name_str = tab.current_branch.as_deref().unwrap_or("(detached)");
37
38 let branch_label = text(branch_name_str)
39 .size(14)
40 .color(c.text_primary)
41 .wrapping(iced::widget::text::Wrapping::None);
42
43 let state_badge: Element<'_, Message> = if let Some(ref info) = tab.repo_info {
45 if info.state != gitkraft_core::RepoState::Clean {
46 text(format!(" [{}]", info.state))
47 .size(12)
48 .color(c.yellow)
49 .into()
50 } else {
51 Space::new().into()
52 }
53 } else {
54 Space::new().into()
55 };
56
57 let fetch_icon = icon!(icons::CLOUD_ARROW_DOWN, 14, c.accent);
59
60 let fetch_msg = (!tab.remotes.is_empty()).then_some(Message::Fetch);
61 let fetch_btn = crate::view_utils::on_press_maybe(
62 button(if compact {
63 row![fetch_icon].align_y(Alignment::Center)
64 } else {
65 row![fetch_icon, Space::new().width(4), text("Fetch").size(12)]
66 .align_y(Alignment::Center)
67 })
68 .padding([4, if compact { 8 } else { 10 }])
69 .style(theme::toolbar_button),
70 fetch_msg,
71 );
72
73 let open_icon = icon!(icons::FOLDER_OPEN, 14, c.text_secondary);
75
76 let open_btn = view_utils::toolbar_btn_adaptive(open_icon, "Open", Message::OpenRepo, compact);
77
78 let close_icon = icon!(icons::X_CIRCLE, 14, c.text_secondary);
80
81 let close_btn =
82 view_utils::toolbar_btn_adaptive(close_icon, "Close", Message::CloseRepo, compact);
83
84 let sidebar_icon_char = if state.sidebar_expanded {
86 icons::CHEVRON_LEFT
87 } else {
88 icons::CHEVRON_RIGHT
89 };
90 let sidebar_icon = icon!(sidebar_icon_char, 14, c.text_secondary);
91
92 let sidebar_btn = button(sidebar_icon)
93 .padding([4, 8])
94 .style(theme::icon_button)
95 .on_press(Message::ToggleSidebar);
96
97 let spinner_frame: String = {
101 let frames = tui_spinner::FluxFrames::CORNERS; let ch = frames[state.animation_tick as usize % frames.len()];
103 ch.to_string()
104 };
105
106 let loading_indicator: Element<'_, Message> = if tab.is_loading {
107 let spinner = text(spinner_frame)
108 .size(14)
109 .color(c.accent)
110 .font(iced::Font::MONOSPACE);
111 if compact {
112 spinner.into()
113 } else {
114 row![
115 spinner,
116 iced::widget::Space::new().width(4),
117 text("Loading…").size(12).color(c.yellow),
118 ]
119 .align_y(iced::Alignment::Center)
120 .into()
121 }
122 } else {
123 Space::new().into()
124 };
125
126 let left_items = row![
128 sidebar_btn,
129 Space::new().width(8),
130 repo_icon,
131 Space::new().width(6),
132 repo_name,
133 Space::new().width(10),
134 separator(),
135 Space::new().width(10),
136 branch_icon,
137 Space::new().width(6),
138 branch_label,
139 state_badge,
140 Space::new().width(10),
141 separator(),
142 Space::new().width(10),
143 loading_indicator,
144 ]
145 .align_y(Alignment::Center);
146
147 let theme_w = if compact { 140.0 } else { 180.0 };
148 let mut right_items = row![
149 fetch_btn,
150 Space::new().width(4),
151 open_btn,
152 Space::new().width(4),
153 close_btn,
154 ]
155 .align_y(Alignment::Center);
156 if !dims.tiny_toolbar {
157 right_items = right_items.push(Space::new().width(8)).push(theme_selector(
158 state.current_theme_index,
159 state.follow_system,
160 theme_w,
161 ));
162 }
163 if !dims.minimal_toolbar {
164 right_items = right_items
165 .push(Space::new().width(4))
166 .push(editor_selector(&state.editor));
167 }
168
169 let toolbar = row![
170 container(left_items).width(Length::Fill).clip(true),
171 right_items,
172 ]
173 .align_y(Alignment::Center)
174 .padding([
175 if dims.compact_vertical { 2 } else { 6 },
176 if compact { 8 } else { 12 },
177 ])
178 .width(Length::Fill);
179
180 container(toolbar)
181 .width(Length::Fill)
182 .style(theme::header_style)
183 .into()
184}