1use fluent_core::{Theme, ThemeProvider as _};
2use gpui::{
3 div, prelude::*, px, svg, ClickEvent, Context, IntoElement, MouseButton, MouseDownEvent,
4 Render, SharedString, Window,
5};
6
7pub struct TitleBar {
13 pub title: SharedString,
14 pub show_controls: bool,
15}
16
17impl TitleBar {
18 pub fn new(cx: &mut Context<Self>, title: impl Into<SharedString>) -> Self {
19 cx.observe_global::<Theme>(|_, cx| cx.notify()).detach();
20 Self {
21 title: title.into(),
22 show_controls: true,
23 }
24 }
25
26 pub fn show_controls(mut self, show: bool) -> Self {
27 self.show_controls = show;
28 self
29 }
30}
31
32impl Render for TitleBar {
33 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
34 let theme = cx.theme();
35 let colors = theme.colors.clone();
36 let spacing = theme.spacing;
37 let typography = theme.typography;
38
39 let title = self.title.clone();
40 let show_controls = self.show_controls;
41
42 let bar_bg = colors.surface_dim;
43 let fg = colors.on_neutral;
44 let ctrl_hover = colors.subtle_hover;
45 let close_hover: gpui::Hsla = gpui::rgb(0xC42B1C).into();
46
47 let drag_handler = cx.listener(
49 |_: &mut TitleBar, _: &MouseDownEvent, window: &mut Window, _| {
50 window.start_window_move();
51 },
52 );
53
54 let min_handler =
55 cx.listener(|_: &mut TitleBar, _: &ClickEvent, window: &mut Window, _| {
56 window.minimize_window();
57 });
58 let max_handler =
59 cx.listener(|_: &mut TitleBar, _: &ClickEvent, window: &mut Window, _| {
60 window.zoom_window();
61 });
62 let close_handler =
63 cx.listener(|_: &mut TitleBar, _: &ClickEvent, window: &mut Window, _| {
64 window.remove_window();
65 });
66
67 let title_area = div()
70 .flex_1()
71 .h_full()
72 .flex()
73 .items_center()
74 .pl(px(spacing.md))
75 .text_size(px(typography.caption.size))
76 .text_color(fg)
77 .on_mouse_down(MouseButton::Left, drag_handler)
78 .child(title);
79
80 let bar = div()
81 .flex()
82 .flex_row()
83 .h(px(36.0))
84 .bg(bar_bg)
85 .child(title_area);
86
87 if !show_controls {
88 return bar;
89 }
90
91 bar.child(
93 div()
94 .flex()
95 .flex_row()
96 .h_full()
97 .child(
98 div()
99 .id("titlebar-min")
100 .w(px(46.0))
101 .h_full()
102 .flex()
103 .items_center()
104 .justify_center()
105 .cursor_pointer()
106 .hover(move |s| s.bg(ctrl_hover))
107 .on_click(min_handler)
108 .child(
109 svg()
110 .path("icons/minimize.svg")
111 .size(px(10.0))
112 .text_color(fg),
113 ),
114 )
115 .child(
116 div()
117 .id("titlebar-max")
118 .w(px(46.0))
119 .h_full()
120 .flex()
121 .items_center()
122 .justify_center()
123 .cursor_pointer()
124 .hover(move |s| s.bg(ctrl_hover))
125 .on_click(max_handler)
126 .child(
127 svg()
128 .path("icons/maximize.svg")
129 .size(px(10.0))
130 .text_color(fg),
131 ),
132 )
133 .child(
134 div()
135 .id("titlebar-close")
136 .w(px(46.0))
137 .h_full()
138 .flex()
139 .items_center()
140 .justify_center()
141 .cursor_pointer()
142 .hover(move |s| s.bg(close_hover))
143 .on_click(close_handler)
144 .child(
145 svg()
146 .path("icons/dismiss.svg")
147 .size(px(10.0))
148 .text_color(fg),
149 ),
150 ),
151 )
152 }
153}