1//! # panel
2use editor::{Editor, EditorElement, EditorStyle};
3use gpui::{Entity, TextStyle, actions};
4use settings::Settings;
5use theme::ThemeSettings;
6use ui::{Tab, prelude::*};
7
8actions!(
9 panel,
10 [
11 /// Navigates to the next tab in the panel.
12 NextPanelTab,
13 /// Navigates to the previous tab in the panel.
14 PreviousPanelTab
15 ]
16);
17
18pub trait PanelHeader: workspace::Panel {
19 fn header_height(&self, cx: &mut App) -> Pixels {
20 Tab::container_height(cx)
21 }
22
23 fn panel_header_container(&self, _window: &mut Window, cx: &mut App) -> Div {
24 h_flex()
25 .h(self.header_height(cx))
26 .w_full()
27 .px_1()
28 .flex_none()
29 }
30}
31
32/// Implement this trait to enable a panel to have tabs.
33pub trait PanelTabs: PanelHeader {
34 /// Returns the index of the currently selected tab.
35 fn selected_tab(&self, cx: &mut App) -> usize;
36 /// Selects the tab at the given index.
37 fn select_tab(&self, cx: &mut App, index: usize);
38 /// Moves to the next tab.
39 fn next_tab(&self, _: NextPanelTab, cx: &mut App) -> Self;
40 /// Moves to the previous tab.
41 fn previous_tab(&self, _: PreviousPanelTab, cx: &mut App) -> Self;
42}
43
44#[derive(IntoElement)]
45pub struct PanelTab {}
46
47impl RenderOnce for PanelTab {
48 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
49 div()
50 }
51}
52
53pub fn panel_button(label: impl Into<SharedString>) -> ui::Button {
54 let label = label.into();
55 let id = ElementId::Name(label.to_lowercase().replace(' ', "_").into());
56 ui::Button::new(id, label)
57 .label_size(ui::LabelSize::Small)
58 .icon_size(ui::IconSize::Small)
59 // TODO: Change this once we use on_surface_bg in button_like
60 .layer(ui::ElevationIndex::ModalSurface)
61 .size(ui::ButtonSize::Compact)
62}
63
64pub fn panel_filled_button(label: impl Into<SharedString>) -> ui::Button {
65 panel_button(label).style(ui::ButtonStyle::Filled)
66}
67
68pub fn panel_icon_button(id: impl Into<SharedString>, icon: IconName) -> ui::IconButton {
69 let id = ElementId::Name(id.into());
70
71 IconButton::new(id, icon)
72 // TODO: Change this once we use on_surface_bg in button_like
73 .layer(ui::ElevationIndex::ModalSurface)
74}
75
76pub fn panel_filled_icon_button(id: impl Into<SharedString>, icon: IconName) -> ui::IconButton {
77 panel_icon_button(id, icon).style(ui::ButtonStyle::Filled)
78}
79
80pub fn panel_editor_container(_window: &mut Window, cx: &mut App) -> Div {
81 v_flex()
82 .size_full()
83 .gap(px(8.))
84 .p_2()
85 .bg(cx.theme().colors().editor_background)
86}
87
88pub fn panel_editor_style(monospace: bool, window: &Window, cx: &App) -> EditorStyle {
89 let settings = ThemeSettings::get_global(cx);
90
91 let font_size = TextSize::Small.rems(cx).to_pixels(window.rem_size());
92
93 let (font_family, font_fallbacks, font_features, font_weight, line_height) = if monospace {
94 (
95 settings.buffer_font.family.clone(),
96 settings.buffer_font.fallbacks.clone(),
97 settings.buffer_font.features.clone(),
98 settings.buffer_font.weight,
99 font_size * settings.buffer_line_height.value(),
100 )
101 } else {
102 (
103 settings.ui_font.family.clone(),
104 settings.ui_font.fallbacks.clone(),
105 settings.ui_font.features.clone(),
106 settings.ui_font.weight,
107 window.line_height(),
108 )
109 };
110
111 EditorStyle {
112 background: cx.theme().colors().editor_background,
113 local_player: cx.theme().players().local(),
114 text: TextStyle {
115 color: cx.theme().colors().text,
116 font_family,
117 font_fallbacks,
118 font_features,
119 font_size: TextSize::Small.rems(cx).into(),
120 font_weight,
121 line_height: line_height.into(),
122 ..Default::default()
123 },
124 syntax: cx.theme().syntax().clone(),
125 ..Default::default()
126 }
127}
128
129pub fn panel_editor_element(
130 editor: &Entity<Editor>,
131 monospace: bool,
132 window: &mut Window,
133 cx: &mut App,
134) -> EditorElement {
135 EditorElement::new(editor, panel_editor_style(monospace, window, cx))
136}