Files
Ely-GPUI-Components/src/navigation/palette/launcher.rs
T
ZacharyZhang-NYandClaude Opus 5.5 94deeaed52 Port · Build: Zed's gpui at 1a28cff
gpui and gpui_platform now come from Zed's repo by git at 1a28cff,
the code gpui-pre 0.3.7 republishes, so the gallery can later build
for the browser. gpui takes stacker, as 0.2.2 always did, and
gpui_platform takes font-kit, without which macOS draws no text;
runtime_shaders moves to gpui_platform.

The API moved: focus, blur and Tab steps take cx, Corner is Anchor,
shaped lines paint with an alignment, defer_draw takes a content
mask, the inspector registers a factory, async updates return their
value, and wrapped lines are shared, not cloned. Updates through a
window context go through the window, so a closed window still ends
them as it did.

Where the new gpui behaves differently, Ely keeps its old intent.
Holds and reveals under a resting pointer survive typing through
gpui's input-modality-independent hover; previews the keyboard also
moves keep the default, and the calendar's leave ignores a key press.
Enter reaches a button inside the code editor, since gpui now arms a
key click on the press. The event logger skips a lone modifier. A
video and an image grid keep their shape in a flex column under
taffy 0.13, as a picture does.

AGENTS.md records the new stack and the rules this port changes; the
rest of the old gpui and taffy rules are re-tested in T44c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 20:30:17 -04:00

92 lines
3.2 KiB
Rust

use std::rc::Rc;
use anyhow::Context as _;
use gpui::{
App, AppContext, Bounds, Context, IntoElement, ParentElement, Render, SharedString, Styled,
Subscription, Window, WindowBackgroundAppearance, WindowBounds, WindowHandle, WindowKind,
WindowOptions, div, point, size,
};
use super::kinds::{Search, SearchPalette};
use crate::{
forms::{Choice, OnValue},
theme::ActiveTheme,
};
/// A search palette alone in a window above all others, as Spotlight is. A pick, Escape or a click elsewhere closes it.
pub struct QuickLauncher {
search: Search,
on_open: OnValue,
focused: bool,
_away: Subscription,
}
impl QuickLauncher {
/// Opens centered across the main display, a fifth of the way down.
pub fn open(
search: impl Fn(&str) -> Vec<(SharedString, Vec<Choice>)> + 'static,
on_open: impl Fn(&SharedString, &mut Window, &mut App) + 'static,
cx: &mut App,
) -> anyhow::Result<WindowHandle<QuickLauncher>> {
let theme = cx.theme();
let (card, rem) = (theme.palette_size(), theme.base_rem());
let card = size(card.width.to_pixels(rem), card.height.to_pixels(rem));
let area = cx
.primary_display()
.context("a launcher needs a display")?
.bounds();
let origin = point(
area.center().x - card.width / 2.0,
area.top() + area.size.height / 5.0,
);
let options = WindowOptions {
window_bounds: Some(WindowBounds::Windowed(Bounds::new(origin, card))),
titlebar: None,
kind: WindowKind::PopUp,
is_resizable: false,
is_minimizable: false,
window_background: WindowBackgroundAppearance::Transparent,
..Default::default()
};
let (search, on_open): (Search, OnValue) = (Rc::new(search), Rc::new(on_open));
let handle = cx.open_window(options, |window, cx| {
cx.new(|cx: &mut Context<QuickLauncher>| {
let away = cx.observe_window_activation(window, |_, window, _| {
if !window.is_window_active() {
log::info!("quick launcher: closed on leaving");
window.remove_window();
}
});
QuickLauncher {
search,
on_open,
focused: false,
_away: away,
}
})
})?;
log::info!("quick launcher: opened");
Ok(handle)
}
}
impl Render for QuickLauncher {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let palette = SearchPalette::from_parts(
"quick-launcher".into(),
self.search.clone(),
Some(self.on_open.clone()),
Rc::new(|window, _| window.remove_window()),
)
.palette(window, cx);
if !self.focused {
self.focused = true;
window.focus(&palette.input.read(cx).focus().clone(), cx);
}
div()
.size_full()
.font_family(cx.theme().font_family.clone())
.child(palette.card(window, cx))
}
}