use std::rc::Rc;
use gpui_kit::{
App, Entity, Focusable as _, IntoElement, ParentElement as _, RenderOnce, SharedString,
Styled as _, Subscription, Window,
};
use crate::{ActiveTheme as _, Command, CommandEvent, CommandState, Dialog, menu::MenuLook};
type OpenChange = Rc<dyn Fn(bool, &mut Window, &mut App)>;
struct Closer {
handler: Option<OpenChange>,
open: bool,
_subscription: Subscription,
}
#[derive(IntoElement)]
pub struct CommandDialog {
id: gpui_kit::ElementId,
state: Entity<CommandState>,
open: bool,
empty: Option<SharedString>,
on_open_change: Option<OpenChange>,
}
impl CommandDialog {
pub fn new(id: impl Into<gpui_kit::ElementId>, state: &Entity<CommandState>) -> Self {
Self {
id: id.into(),
state: state.clone(),
open: false,
empty: None,
on_open_change: None,
}
}
pub fn open(mut self, open: bool) -> Self {
self.open = open;
self
}
pub fn empty(mut self, text: impl Into<SharedString>) -> Self {
self.empty = Some(text.into());
self
}
pub fn on_open_change(
mut self,
handler: impl Fn(bool, &mut Window, &mut App) + 'static,
) -> Self {
self.on_open_change = Some(Rc::new(handler));
self
}
}
impl RenderOnce for CommandDialog {
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
let child = |name: &'static str| {
gpui_kit::ElementId::NamedChild(self.id.clone().into(), name.into())
};
let state = self.state.clone();
let closer = window.use_keyed_state(child("closer"), cx, |window, cx| Closer {
handler: None,
open: false,
_subscription: cx.subscribe_in(
&state,
window,
|this: &mut Closer, _, event: &CommandEvent, window, cx| {
if this.open
&& matches!(
event,
CommandEvent::Confirmed(_) | CommandEvent::Submitted(_)
)
&& let Some(handler) = this.handler.clone()
{
handler(false, window, cx);
}
},
),
});
let (handler, open) = (self.on_open_change.clone(), self.open);
closer.update(cx, |closer, _| {
closer.handler = handler;
closer.open = open;
});
let theme = cx.theme();
let look = MenuLook::of(theme, window.rem_size());
let (width, top) = (
theme.metrics.command_dialog_width,
theme.metrics.command_dialog_top,
);
let focus = self.state.focus_handle(cx);
let mut command = Command::new(child("command"), &self.state)
.bordered(false)
.w_full()
.max_w_full()
.h(look.palette_height());
if let Some(empty) = self.empty {
command = command.empty(empty);
}
let on_open_change = self.on_open_change;
let mut dialog = Dialog::new(self.id.clone())
.open(self.open)
.show_close_button(false)
.top(top)
.w(width)
.p_0()
.overflow_hidden()
.track_focus(&focus)
.child(command);
if let Some(handler) = on_open_change {
dialog = dialog.on_open_change(move |open, window, cx| handler(open, window, cx));
}
dialog
}
}