title_bar.rs

  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}