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
21 let repo_icon = icon!(icons::FOLDER_OPEN, 14, c.accent);
23
24 let repo_name = text(state.repo_display_name())
25 .size(14)
26 .color(c.text_primary);
27
28 let separator = || text("|").size(14).color(c.border);
29
30 let branch_icon = icon!(icons::GIT_BRANCH, 14, c.green);
32
33 let branch_name_str = tab.current_branch.as_deref().unwrap_or("(detached)");
34
35 let branch_label = text(branch_name_str).size(14).color(c.text_primary);
36
37 let state_badge: Element<'_, Message> = if let Some(ref info) = tab.repo_info {
39 if info.state != gitkraft_core::RepoState::Clean {
40 text(format!(" [{}]", info.state))
41 .size(12)
42 .color(c.yellow)
43 .into()
44 } else {
45 Space::new().into()
46 }
47 } else {
48 Space::new().into()
49 };
50
51 let fetch_icon = icon!(icons::CLOUD_ARROW_DOWN, 14, c.accent);
53
54 let fetch_msg = (!tab.remotes.is_empty()).then_some(Message::Fetch);
55 let fetch_btn = crate::view_utils::on_press_maybe(
56 button(
57 row![fetch_icon, Space::new().width(4), text("Fetch").size(12)]
58 .align_y(Alignment::Center),
59 )
60 .padding([4, 10])
61 .style(theme::toolbar_button),
62 fetch_msg,
63 );
64
65 let open_icon = icon!(icons::FOLDER_OPEN, 14, c.text_secondary);
67
68 let open_btn = view_utils::toolbar_btn(open_icon, "Open", Message::OpenRepo);
69
70 let close_icon = icon!(icons::X_CIRCLE, 14, c.text_secondary);
72
73 let close_btn = view_utils::toolbar_btn(close_icon, "Close", Message::CloseRepo);
74
75 let sidebar_icon_char = if state.sidebar_expanded {
77 icons::CHEVRON_LEFT
78 } else {
79 icons::CHEVRON_RIGHT
80 };
81 let sidebar_icon = icon!(sidebar_icon_char, 14, c.text_secondary);
82
83 let sidebar_btn = button(sidebar_icon)
84 .padding([4, 8])
85 .style(theme::icon_button)
86 .on_press(Message::ToggleSidebar);
87
88 let spinner_frame: String = {
92 let frames = tui_spinner::FluxFrames::CORNERS; let ch = frames[state.animation_tick as usize % frames.len()];
94 ch.to_string()
95 };
96
97 let loading_indicator: Element<'_, Message> = if tab.is_loading {
98 row![
99 text(spinner_frame)
100 .size(15)
101 .color(c.accent)
102 .font(iced::Font::MONOSPACE),
103 iced::widget::Space::new().width(4),
104 text("Loading…").size(12).color(c.yellow),
105 ]
106 .align_y(iced::Alignment::Center)
107 .into()
108 } else {
109 Space::new().into()
110 };
111
112 let left_items = row![
114 sidebar_btn,
115 Space::new().width(8),
116 repo_icon,
117 Space::new().width(6),
118 repo_name,
119 Space::new().width(10),
120 separator(),
121 Space::new().width(10),
122 branch_icon,
123 Space::new().width(6),
124 branch_label,
125 state_badge,
126 Space::new().width(10),
127 separator(),
128 Space::new().width(10),
129 loading_indicator,
130 ]
131 .align_y(Alignment::Center);
132
133 let right_items = row![
134 fetch_btn,
135 Space::new().width(4),
136 open_btn,
137 Space::new().width(4),
138 close_btn,
139 Space::new().width(8),
140 theme_selector(state.current_theme_index),
141 Space::new().width(4),
142 editor_selector(&state.editor),
143 ]
144 .align_y(Alignment::Center);
145
146 let toolbar = row![
147 container(left_items).width(Length::Fill).clip(true),
148 right_items,
149 ]
150 .align_y(Alignment::Center)
151 .padding([6, 12])
152 .width(Length::Fill);
153
154 container(toolbar)
155 .width(Length::Fill)
156 .style(theme::header_style)
157 .into()
158}