1use std::marker::PhantomData;
2use std::sync::atomic::AtomicBool;
3use std::sync::Arc;
4
5use crate::prelude::*;
6use crate::{
7 theme, Avatar, Button, Icon, IconButton, IconColor, PlayerStack, PlayerWithCallStatus,
8 ToolDivider, TrafficLights,
9};
10
11#[derive(Clone)]
12pub struct Livestream {
13 pub players: Vec<PlayerWithCallStatus>,
14 pub channel: Option<String>, // projects
15 // windows
16}
17
18#[derive(Element)]
19pub struct TitleBar<S: 'static + Send + Sync + Clone> {
20 state_type: PhantomData<S>,
21 /// If the window is active from the OS's perspective.
22 is_active: Arc<AtomicBool>,
23 livestream: Option<Livestream>,
24}
25
26impl<S: 'static + Send + Sync + Clone> TitleBar<S> {
27 pub fn new(cx: &mut ViewContext<S>) -> Self {
28 let is_active = Arc::new(AtomicBool::new(true));
29 let active = is_active.clone();
30
31 // cx.observe_window_activation(move |_, is_active, cx| {
32 // active.store(is_active, std::sync::atomic::Ordering::SeqCst);
33 // cx.notify();
34 // })
35 // .detach();
36
37 Self {
38 state_type: PhantomData,
39 is_active,
40 livestream: None,
41 }
42 }
43
44 pub fn set_livestream(mut self, livestream: Option<Livestream>) -> Self {
45 self.livestream = livestream;
46 self
47 }
48
49 fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
50 let theme = theme(cx);
51 // let has_focus = cx.window_is_active();
52 let has_focus = true;
53
54 let player_list = if let Some(livestream) = &self.livestream {
55 livestream.players.clone().into_iter()
56 } else {
57 vec![].into_iter()
58 };
59
60 div()
61 .flex()
62 .items_center()
63 .justify_between()
64 .w_full()
65 .h_8()
66 .fill(theme.lowest.base.default.background)
67 .child(
68 div()
69 .flex()
70 .items_center()
71 .h_full()
72 .gap_4()
73 .px_2()
74 .child(TrafficLights::new().window_has_focus(has_focus))
75 // === Project Info === //
76 .child(
77 div()
78 .flex()
79 .items_center()
80 .gap_1()
81 .child(Button::new("zed"))
82 .child(Button::new("nate/gpui2-ui-components")),
83 )
84 .children(player_list.map(|p| PlayerStack::new(p)))
85 .child(IconButton::new(Icon::Plus)),
86 )
87 .child(
88 div()
89 .flex()
90 .items_center()
91 .child(
92 div()
93 .px_2()
94 .flex()
95 .items_center()
96 .gap_1()
97 .child(IconButton::new(Icon::FolderX))
98 .child(IconButton::new(Icon::Close)),
99 )
100 .child(ToolDivider::new())
101 .child(
102 div()
103 .px_2()
104 .flex()
105 .items_center()
106 .gap_1()
107 .child(IconButton::new(Icon::Mic))
108 .child(IconButton::new(Icon::AudioOn))
109 .child(IconButton::new(Icon::Screen).color(IconColor::Accent)),
110 )
111 .child(
112 div().px_2().flex().items_center().child(
113 Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
114 .shape(Shape::RoundedRectangle),
115 ),
116 ),
117 )
118 }
119}
120
121#[cfg(feature = "stories")]
122pub use stories::*;
123
124#[cfg(feature = "stories")]
125mod stories {
126 use crate::Story;
127
128 use super::*;
129
130 #[derive(Element)]
131 pub struct TitleBarStory<S: 'static + Send + Sync + Clone> {
132 state_type: PhantomData<S>,
133 }
134
135 impl<S: 'static + Send + Sync + Clone> TitleBarStory<S> {
136 pub fn new() -> Self {
137 Self {
138 state_type: PhantomData,
139 }
140 }
141
142 fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
143 Story::container(cx)
144 .child(Story::title_for::<_, TitleBar<S>>(cx))
145 .child(Story::label(cx, "Default"))
146 .child(TitleBar::new(cx))
147 }
148 }
149}