Files
Ely-GPUI-Components/src/canvas/panel.rs
T
ZacharyZhang-NY 1879f6f08b Onboarding · Flows and highlights: focus that holds as the flow turns, a tip that closes
navigation::Wizard keeps one focus handle for Next and Finish, so a
second press on the step before last finishes; Back that reaches the
first step, and a finished step picked by key, hand focus to Next.
Popover::with_opener hands its content a close that gives focus back:
Got it closes the hotspot's tip before the owner hides it, and the
event details close before Edit and Delete run, so focus never lands
on a panel that left the tree. The wizard's count and the setup
checklist's tally name their selectors after their words, and the bar
takes the count's own value.
2026-09-27 14:48:20 -04:00

93 lines
2.6 KiB
Rust

use std::{fmt::Debug, rc::Rc};
use gpui::{
App, ElementId, FontWeight, Hsla, IntoElement, ParentElement, SharedString, Styled, Window, div,
};
use crate::{
forms::{ColorPicker, ColorSwatch},
overlays::Popover,
theme::{ActiveTheme, TextSize},
};
/// A panel's handler for its whole value.
pub(crate) type OnEdit<V> = Rc<dyn Fn(V, &mut Window, &mut App)>;
/// A handler that hands `on_change` a copy of `value` changed by `edit`.
pub(crate) fn editing<V, T>(
what: &'static str,
value: &V,
on_change: &OnEdit<V>,
edit: impl Fn(&mut V, T) + 'static,
) -> impl Fn(T, &mut Window, &mut App) + 'static
where
V: Clone + Debug + 'static,
T: 'static,
{
let (value, on_change) = (value.clone(), on_change.clone());
move |input, window, cx| {
let mut next = value.clone();
edit(&mut next, input);
log::info!("{what}: {next:?}");
on_change(next, window, cx);
}
}
/// Two fields side by side, sharing the width.
pub(crate) fn pair(left: impl IntoElement, right: impl IntoElement) -> gpui::Div {
div()
.flex()
.gap_2()
.child(div().flex_1().min_w_0().child(left))
.child(div().flex_1().min_w_0().child(right))
}
/// A quiet line naming what follows.
pub(crate) fn caption(text: impl Into<SharedString>, cx: &App) -> gpui::Div {
let theme = cx.theme();
div()
.text_size(theme.text_size(TextSize::Xs))
.text_color(theme.colors.fg_subtle)
.child(text.into())
}
/// A panel part's title, with an action at the end of its line.
pub(crate) fn heading(
title: impl Into<SharedString>,
action: impl IntoElement,
cx: &App,
) -> gpui::Div {
let theme = cx.theme();
div()
.flex()
.items_center()
.justify_between()
.gap_2()
.child(
div()
.text_size(theme.text_size(TextSize::Sm))
.font_weight(FontWeight::SEMIBOLD)
.text_color(theme.colors.fg)
.child(title.into()),
)
.child(action)
}
/// A swatch that opens a color picker.
pub(crate) fn color_well(
id: ElementId,
color: Hsla,
on_pick: impl Fn(Hsla, &mut Window, &mut App) + 'static,
) -> Popover {
let swatch = (id.clone(), "swatch");
Popover::with_opener(
id.clone(),
move |toggle| {
ColorSwatch::new(swatch, color)
.tooltip("Change the color")
.on_click(move |window, cx| toggle(window, cx))
},
move |_, _, _| ColorPicker::new((id, "picker"), color).on_change(on_pick),
)
}