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