1use gpui::{
2 AnyElement, IntoElement, ParentElement, Pixels, RenderOnce, Styled, px, transparent_black,
3};
4use smallvec::SmallVec;
5use theme::ActiveTheme;
6
7use crate::{h_flex, utils::CornerSolver};
8
9/// An outline is a stylistic focus indicator that draws a ring around
10/// an element with some space between the element and ring.
11#[derive(IntoElement)]
12pub struct FocusOutline {
13 corner_radius: Pixels,
14 border_width: Pixels,
15 padding: Pixels,
16 focused: bool,
17 active: bool,
18 children: SmallVec<[AnyElement; 2]>,
19}
20
21impl FocusOutline {
22 pub fn new(child_corner_radius: Pixels, focused: bool, offset: Pixels) -> Self {
23 let ring_width = px(1.);
24 let corner_radius =
25 CornerSolver::parent_radius(child_corner_radius, ring_width, offset, px(0.));
26 Self {
27 corner_radius,
28 border_width: ring_width,
29 padding: offset,
30 focused,
31 active: false,
32 children: SmallVec::new(),
33 }
34 }
35
36 pub fn active(mut self, active: bool) -> Self {
37 self.active = active;
38 self
39 }
40}
41
42impl RenderOnce for FocusOutline {
43 fn render(self, _window: &mut gpui::Window, cx: &mut gpui::App) -> impl IntoElement {
44 let border_color = if self.focused && self.active {
45 cx.theme().colors().border_focused.opacity(0.48)
46 } else if self.focused {
47 cx.theme().colors().border_variant
48 } else {
49 transparent_black()
50 };
51
52 h_flex()
53 .border(self.border_width)
54 .border_color(border_color)
55 .rounded(self.corner_radius)
56 .p(self.padding)
57 .children(self.children)
58 }
59}
60
61impl ParentElement for FocusOutline {
62 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
63 self.children.extend(elements)
64 }
65}