Skip to main content

gitkraft_gui/features/stash/
view.rs

1//! Stash list view — shows stash entries in the sidebar with save, pop, and
2//! drop actions for each entry.
3
4use iced::widget::{
5    button, column, container, mouse_area, row, scrollable, text, text_input, Space,
6};
7use iced::{Alignment, Element, Length};
8
9use crate::icons;
10use crate::message::Message;
11use crate::state::GitKraft;
12use crate::theme;
13use crate::view_utils;
14use crate::view_utils::truncate_to_fit;
15
16/// Render the stash panel (typically shown in the sidebar beneath branches).
17pub fn view(state: &GitKraft) -> Element<'_, Message> {
18    let tab = state.active_tab();
19    let c = state.colors();
20    let sidebar_width = state.sidebar_width;
21
22    let header_icon = icon!(icons::STACK, 14, c.accent);
23
24    let header_label = text("Stashes").size(14).color(c.text_primary);
25
26    let count_label = text(format!("({})", tab.stashes.len()))
27        .size(11)
28        .color(c.muted);
29
30    let save_icon = icon!(icons::PLUS_CIRCLE, 14, c.green);
31
32    let save_btn = button(save_icon)
33        .padding([2, 6])
34        .style(theme::icon_button)
35        .on_press(Message::StashSave);
36
37    let header_row = row![
38        header_icon,
39        Space::new().width(6),
40        header_label,
41        Space::new().width(4),
42        count_label,
43        Space::new().width(Length::Fill),
44        save_btn,
45    ]
46    .align_y(Alignment::Center)
47    .padding([8, 10]);
48
49    // ── Stash message input ───────────────────────────────────────────────
50    let stash_input = text_input("Stash message (optional)…", &tab.stash_message)
51        .on_input(Message::StashMessageChanged)
52        .padding(4)
53        .size(12);
54
55    let input_row = container(stash_input).padding([2, 10]).width(Length::Fill);
56
57    // ── Stash entries ─────────────────────────────────────────────────────
58    let stash_entries: Vec<Element<'_, Message>> = tab
59        .stashes
60        .iter()
61        .map(|entry| {
62            let index_label = text(format!("stash@{{{}}}", entry.index))
63                .size(11)
64                .color(c.accent)
65                .font(iced::Font::MONOSPACE);
66
67            // Available px: sidebar minus stash-index label (~60px) + gap(6)
68            // + pop-btn(22) + gap(2) + drop-btn(22) + padding(16) + gap(4)
69            // ≈ 132 px overhead.
70            let msg_available = (sidebar_width - 132.0).max(30.0);
71            let display_msg = truncate_to_fit(entry.message.as_str(), msg_available, 6.5);
72
73            let msg_label = text(display_msg)
74                .size(11)
75                .color(c.text_secondary)
76                .wrapping(iced::widget::text::Wrapping::None);
77
78            let pop_icon = icon!(icons::BOX_ARROW_UP, 11, c.green);
79
80            let pop_btn = button(pop_icon)
81                .padding([2, 4])
82                .style(theme::icon_button)
83                .on_press(Message::StashPop(entry.index));
84
85            let drop_icon = icon!(icons::TRASH, 11, c.red);
86
87            let drop_btn = button(drop_icon)
88                .padding([2, 4])
89                .style(theme::icon_button)
90                .on_press(Message::StashDrop(entry.index));
91
92            let entry_row = row![
93                index_label,
94                Space::new().width(6),
95                msg_label,
96                Space::new().width(Length::Fill),
97                pop_btn,
98                Space::new().width(2),
99                drop_btn,
100            ]
101            .align_y(Alignment::Center)
102            .padding([3, 8]);
103
104            let entry_container = container(entry_row)
105                .width(Length::Fill)
106                .height(Length::Fixed(26.0))
107                .clip(true);
108
109            mouse_area(entry_container)
110                .on_right_press(Message::OpenStashContextMenu(entry.index))
111                .into()
112        })
113        .collect();
114
115    let mut list_col = column![].spacing(1).width(Length::Fill);
116
117    if stash_entries.is_empty() {
118        list_col = list_col.push(view_utils::empty_list_hint("No stashes", c.muted));
119    } else {
120        for entry_el in stash_entries {
121            list_col = list_col.push(entry_el);
122        }
123    }
124
125    let content = column![
126        header_row,
127        input_row,
128        scrollable(list_col)
129            .height(Length::Fill)
130            .direction(view_utils::thin_scrollbar())
131            .style(crate::theme::overlay_scrollbar),
132    ]
133    .width(Length::Fill);
134
135    container(content).width(Length::Fill).into()
136}