vizia_core/views/frame.rs
1use crate::prelude::*;
2
3/// The position of the title on the frame border.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub enum FrameTitlePosition {
6 /// Title at the top-left corner
7 #[default]
8 TopLeft,
9 /// Title at the top-center
10 TopCenter,
11 /// Title at the top-right corner
12 TopRight,
13}
14
15/// A container widget that groups related content with a border and optional title.
16///
17/// The frame displays a border around its content and supports an optional title
18/// that is positioned to intersect the frame's border, similar to an HTML fieldset.
19///
20/// # Examples
21///
22/// ```no_run
23/// # use vizia_core::prelude::*;
24/// # let cx = &mut Context::default();
25/// Frame::new(cx, |cx| {
26/// Label::new(cx, "Frame content").hoverable(false);
27/// }).title_position(FrameTitlePosition::TopCenter);
28/// ```
29pub struct Frame {
30 title_position: Signal<FrameTitlePosition>,
31}
32
33impl Frame {
34 /// Creates a new frame with content but no title.
35 pub fn new(cx: &mut Context, content: impl FnOnce(&mut Context)) -> Handle<Self> {
36 let title_position = Signal::new(FrameTitlePosition::default());
37
38 Self { title_position }.build(cx, |cx| {
39 // Content (top layer)
40 content(cx);
41 })
42 }
43
44 /// Creates a new frame with a title and content.
45 ///
46 /// The title is positioned to intersect the frame's border.
47 pub fn with_title<S: View>(
48 cx: &mut Context,
49 title: impl FnOnce(&mut Context) -> Handle<S>,
50 content: impl FnOnce(&mut Context),
51 ) -> Handle<Self> {
52 let title_position = Signal::new(FrameTitlePosition::default());
53
54 Self { title_position }.build(cx, |cx| {
55 // Title - positioned absolutely with negative top offset
56
57 title(cx).class("frame-title").bind(title_position, move |handle| {
58 let pos = title_position.get();
59 match pos {
60 FrameTitlePosition::TopLeft => {
61 handle
62 .toggle_class("left", true)
63 .toggle_class("center", false)
64 .toggle_class("right", false);
65 }
66 FrameTitlePosition::TopCenter => {
67 handle
68 .toggle_class("left", false)
69 .toggle_class("center", true)
70 .toggle_class("right", false);
71 }
72 FrameTitlePosition::TopRight => {
73 handle
74 .toggle_class("left", false)
75 .toggle_class("center", false)
76 .toggle_class("right", true);
77 }
78 }
79 });
80
81 // Content
82 content(cx);
83 })
84 }
85}
86
87impl View for Frame {
88 fn element(&self) -> Option<&'static str> {
89 Some("frame")
90 }
91}
92
93impl Handle<'_, Frame> {
94 /// Set the position of the title on the frame border.
95 pub fn title_position(self, position: FrameTitlePosition) -> Self {
96 self.modify(|frame| frame.title_position.set(position))
97 }
98}