Skip to main content

ferrix_app/widgets/
card.rs

1/* card.rs
2 *
3 * Copyright 2025-2026 Michail Krasnov <mskrasnov07@ya.ru>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21//! Card widget for dashboard and OSD
22
23use crate::messages::Message;
24use iced::{
25    Element, Font, Length, Vector, color,
26    font::Weight,
27    never,
28    widget::{button, column, container, rich_text, space, span},
29};
30
31pub struct Card {
32    header: String,
33    on_press: Message,
34    is_transparent: bool,
35}
36
37impl Card {
38    pub fn new(header: String, on_press: Message) -> Self {
39        Self {
40            is_transparent: false,
41            on_press,
42            header,
43        }
44    }
45
46    pub fn set_transparent(mut self, trans: bool) -> Self {
47        self.is_transparent = trans;
48        self
49    }
50
51    pub fn widget<'a, C>(&self, contents: C) -> Element<'a, Message>
52    where
53        C: Into<Element<'a, Message>>,
54    {
55        let is_transparent = self.is_transparent;
56        let cont = container(column![
57            rich_text![
58                span(self.header.clone())
59                    .font(Font {
60                        weight: Weight::Bold,
61                        ..Default::default()
62                    })
63                    .size(16)
64            ]
65            .on_link_click(never),
66            space().width(Length::Fill).height(Length::Fill),
67            contents.into(),
68        ])
69        .padding(5)
70        .width(135)
71        .height(135)
72        .max_width(135)
73        .max_height(135)
74        .style(move |t| {
75            let is_dark = t.extended_palette().is_dark;
76            let shadow = match is_transparent {
77                true => iced::Shadow::default(),
78                false => iced::Shadow {
79                    color: match is_dark {
80                        true => color!(0x1d2021),
81                        false => color!(0xebdbb2),
82                    },
83                    offset: Vector::new(2., 2.),
84                    blur_radius: 2.,
85                },
86            };
87            let mut style = container::rounded_box(t);
88            style.shadow = shadow;
89            if is_transparent {
90                style.background = style.background.and_then(|b| Some(b.scale_alpha(0.6)));
91            }
92            style
93        });
94
95        button(cont)
96            .padding(0)
97            .style(button::text)
98            .on_press(self.on_press.clone())
99            .into()
100    }
101}