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>
This commit is contained in:
1 parent
e49c93bb6f
commit
94deeaed52
199 files changed
+2513
-2643
No files matched your search
@@ -26,7 +26,7 @@ pub fn run(
|
||||
Theme::update(cx, |theme| theme.reduced_motion = true);
|
||||
cx.spawn(async move |cx| {
|
||||
let outcome = shoot_all(window, &dir, only, scripted, cx).await;
|
||||
if let Err(error) = outcome.and_then(|()| cx.update(|cx| cx.quit())) {
|
||||
if let Err(error) = outcome.map(|()| cx.update(|cx| cx.quit())) {
|
||||
log::error!("capture failed: {error:#}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ use std::path::PathBuf;
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use ely_gpui_component::Assets;
|
||||
use gpui::{
|
||||
App, AppContext, Application, Bounds, KeyBinding, Menu, MenuItem, TitlebarOptions,
|
||||
WindowBounds, WindowHandle, WindowOptions, actions, point, px, size,
|
||||
App, AppContext, Bounds, KeyBinding, Menu, MenuItem, TitlebarOptions, WindowBounds,
|
||||
WindowHandle, WindowOptions, actions, point, px, size,
|
||||
};
|
||||
|
||||
actions!(gallery, [Quit, About, LightMode, DarkMode, NewWindow]);
|
||||
@@ -38,6 +38,7 @@ fn menus(cx: &mut App) {
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Quit Ely", Quit),
|
||||
],
|
||||
disabled: false,
|
||||
},
|
||||
Menu {
|
||||
name: "View".into(),
|
||||
@@ -45,6 +46,7 @@ fn menus(cx: &mut App) {
|
||||
MenuItem::action("Light", LightMode),
|
||||
MenuItem::action("Dark", DarkMode),
|
||||
],
|
||||
disabled: false,
|
||||
},
|
||||
]);
|
||||
cx.set_dock_menu(vec![MenuItem::action("New Window", NewWindow)]);
|
||||
@@ -103,7 +105,7 @@ fn parse_args() -> Result<Args> {
|
||||
fn main() -> Result<()> {
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
||||
let args = parse_args()?;
|
||||
Application::new()
|
||||
gpui_platform::application()
|
||||
.with_assets(Assets)
|
||||
.run(move |cx: &mut App| {
|
||||
ely_gpui_component::init(cx);
|
||||
@@ -123,6 +125,8 @@ fn main() -> Result<()> {
|
||||
traffic_light_position: Some(point(px(16.0), px(18.0))),
|
||||
}),
|
||||
window_min_size: Some(size(px(880.0), px(560.0))),
|
||||
// Draws every frame while a demo window is key, as gpui 0.2.2 did.
|
||||
inactive_frame_interval: None,
|
||||
..Default::default()
|
||||
};
|
||||
let window = cx
|
||||
|
||||
@@ -105,6 +105,7 @@ pub fn styles(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
offset: point(px(shadow.x), px(shadow.y)),
|
||||
blur_radius: px(shadow.blur),
|
||||
spread_radius: px(shadow.spread),
|
||||
inset: false,
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
|
||||
@@ -185,7 +185,7 @@ pub fn realtime(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
cx.background_executor()
|
||||
.timer(Duration::from_millis(900))
|
||||
.await;
|
||||
let pushed = cx.update(|cx| {
|
||||
cx.update(|cx| {
|
||||
stream.update(cx, |(values, pushed), cx| {
|
||||
let last = values.last().copied().unwrap_or(60.0);
|
||||
values.push((last + (rng() - 0.5) * 10.0).clamp(20.0, 100.0));
|
||||
@@ -194,9 +194,6 @@ pub fn realtime(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
cx.notify();
|
||||
})
|
||||
});
|
||||
if pushed.is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
@@ -199,7 +199,7 @@ pub fn streaming(window: &mut Window, cx: &mut App) -> impl IntoElement + use<>
|
||||
cx.background_executor().timer(Duration::from_millis(600)).await;
|
||||
loop {
|
||||
cx.background_executor().timer(Duration::from_millis(45)).await;
|
||||
let more = feeder.update(cx, |stream, cx| {
|
||||
let more = cx.update(|_, cx| feeder.update(cx, |stream, cx| {
|
||||
let next = (stream.arrived + 7).min(ANSWER.len());
|
||||
stream.arrived = (next..=ANSWER.len())
|
||||
.find(|at| ANSWER.is_char_boundary(*at))
|
||||
@@ -207,8 +207,8 @@ pub fn streaming(window: &mut Window, cx: &mut App) -> impl IntoElement + use<>
|
||||
stream.live = stream.arrived < ANSWER.len();
|
||||
cx.notify();
|
||||
stream.live
|
||||
});
|
||||
if !matches!(more, Ok(true)) {
|
||||
}));
|
||||
if !matches!(more.and_then(|inner| inner), Ok(true)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,8 +221,7 @@ pub fn playground(window: &mut Window, cx: &mut App) -> impl IntoElement + use<>
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
|
||||
@@ -117,8 +117,7 @@ pub fn panel(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
settings.enhancing = false;
|
||||
settings.suggestion = Some(text.into());
|
||||
})
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
})
|
||||
|
||||
@@ -298,7 +298,7 @@ pub fn focus(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
.on_click(move |_, window, cx| {
|
||||
change(&show, cx, |focusing| focusing.field = !focusing.field);
|
||||
let focus = focus.clone();
|
||||
window.defer(cx, move |window, _| window.focus(&focus));
|
||||
window.defer(cx, move |window, cx| window.focus(&focus, cx));
|
||||
}),
|
||||
)
|
||||
.when(shown, |column| column.child(Input::new(&field))),
|
||||
|
||||
@@ -67,7 +67,7 @@ pub fn infinite(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let more = more.clone();
|
||||
cx.spawn(async move |cx| {
|
||||
cx.background_executor().timer(Duration::from_millis(700)).await;
|
||||
cx.update(|cx| set(&more, (rows + PAGE, false), cx)).ok();
|
||||
cx.update(|cx| set(&more, (rows + PAGE, false), cx));
|
||||
})
|
||||
.detach();
|
||||
}),
|
||||
|
||||
@@ -144,8 +144,7 @@ pub fn tree(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
items.push(item("logo", "logo.svg", Some(&key), false));
|
||||
items.push(item("inter", "Inter.ttf", Some(&key), false));
|
||||
})
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
})
|
||||
|
||||
@@ -4,7 +4,9 @@ use ely_gpui_component::media::{
|
||||
AudioPlayer, AudioSpectrum, AudioWaveform, MediaControls, PlaybackSpeedControl, Playlist,
|
||||
Repeat, Track, VolumeControl,
|
||||
};
|
||||
use gpui::{App, Entity, IntoElement, ParentElement, SharedString, Styled, Window, div, px};
|
||||
use gpui::{
|
||||
App, Entity, IntoElement, ParentElement, SharedString, Styled, TaskExt as _, Window, div, px,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
probe::probe,
|
||||
@@ -98,19 +100,21 @@ fn tick(state: &Entity<Listening>, window: &mut Window, cx: &mut App) {
|
||||
.spawn(cx, async move |cx| {
|
||||
loop {
|
||||
cx.background_executor().timer(TICK).await;
|
||||
let going = state.update(cx, |now, cx| {
|
||||
now.at = (now.at + TICK.mul_f32(now.speed)).min(now.length());
|
||||
if now.at == now.length() {
|
||||
match now.repeat {
|
||||
Repeat::One => now.at = Duration::ZERO,
|
||||
Repeat::All => now.skip(1),
|
||||
Repeat::Off if now.song + 1 < SONGS.len() => now.skip(1),
|
||||
Repeat::Off => now.playing = false,
|
||||
let going = cx.update(|_, cx| {
|
||||
state.update(cx, |now, cx| {
|
||||
now.at = (now.at + TICK.mul_f32(now.speed)).min(now.length());
|
||||
if now.at == now.length() {
|
||||
match now.repeat {
|
||||
Repeat::One => now.at = Duration::ZERO,
|
||||
Repeat::All => now.skip(1),
|
||||
Repeat::Off if now.song + 1 < SONGS.len() => now.skip(1),
|
||||
Repeat::Off => now.playing = false,
|
||||
}
|
||||
}
|
||||
}
|
||||
now.ticking = now.playing;
|
||||
cx.notify();
|
||||
now.playing
|
||||
now.ticking = now.playing;
|
||||
cx.notify();
|
||||
now.playing
|
||||
})
|
||||
})?;
|
||||
if !going {
|
||||
return anyhow::Ok(());
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use std::{path::Path, time::Duration};
|
||||
|
||||
use ely_gpui_component::media::{Cue, Scrubber, SubtitleEditor, VideoPlayer, VideoThumbnailStrip};
|
||||
use gpui::{App, Entity, IntoElement, ParentElement, SharedString, Styled, Window, div, px};
|
||||
use gpui::{
|
||||
App, Entity, IntoElement, ParentElement, SharedString, Styled, TaskExt as _, Window, div, px,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
probe::probe,
|
||||
@@ -61,13 +63,15 @@ fn tick(state: &Entity<Playback>, window: &mut Window, cx: &mut App) {
|
||||
.spawn(cx, async move |cx| {
|
||||
loop {
|
||||
cx.background_executor().timer(TICK).await;
|
||||
let going = state.update(cx, |now, cx| {
|
||||
let step = TICK.mul_f32(now.speed);
|
||||
now.at = (now.at + step).min(LENGTH);
|
||||
now.playing &= now.at < LENGTH;
|
||||
now.ticking = now.playing;
|
||||
cx.notify();
|
||||
now.playing
|
||||
let going = cx.update(|_, cx| {
|
||||
state.update(cx, |now, cx| {
|
||||
let step = TICK.mul_f32(now.speed);
|
||||
now.at = (now.at + step).min(LENGTH);
|
||||
now.playing &= now.at < LENGTH;
|
||||
now.ticking = now.playing;
|
||||
cx.notify();
|
||||
now.playing
|
||||
})
|
||||
})?;
|
||||
if !going {
|
||||
return anyhow::Ok(());
|
||||
|
||||
@@ -24,9 +24,7 @@ pub fn later<T: 'static>(state: &Entity<T>, value: T, wait: Duration, cx: &mut A
|
||||
let state = state.clone();
|
||||
cx.spawn(async move |cx| {
|
||||
cx.background_executor().timer(wait).await;
|
||||
if let Err(error) = cx.update(|cx| set(&state, value, cx)) {
|
||||
log::error!("gallery: the demo closed before its answer: {error:#}");
|
||||
}
|
||||
cx.update(|cx| set(&state, value, cx));
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
@@ -23,11 +23,13 @@ pub fn animated_number(window: &mut Window, cx: &mut App) -> impl IntoElement +
|
||||
_task: cx.spawn_in(window, async move |ticker, cx| {
|
||||
loop {
|
||||
cx.background_executor().timer(STEP).await;
|
||||
let advanced = ticker.update(cx, |ticker: &mut Ticker, cx| {
|
||||
ticker.index = (ticker.index + 1) % VALUES.len();
|
||||
cx.notify();
|
||||
let advanced = cx.update(|_, cx| {
|
||||
ticker.update(cx, |ticker: &mut Ticker, cx| {
|
||||
ticker.index = (ticker.index + 1) % VALUES.len();
|
||||
cx.notify();
|
||||
})
|
||||
});
|
||||
if advanced.is_err() {
|
||||
if advanced.and_then(|inner| inner).is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ pub fn park(window: WindowHandle<Gallery>, cx: &mut AsyncApp) -> Result<()> {
|
||||
}
|
||||
|
||||
fn opened(key: &str, cx: &mut AsyncApp) -> Result<gpui::AnyWindowHandle> {
|
||||
cx.update(|cx| Opened::get(key, cx))?
|
||||
cx.update(|cx| Opened::get(key, cx))
|
||||
.with_context(|| format!("no window opened as {key}"))
|
||||
}
|
||||
|
||||
@@ -231,15 +231,15 @@ pub async fn play(
|
||||
}
|
||||
Step::ExpectClosed(key) => {
|
||||
let handle = opened(key, cx)?;
|
||||
let open = cx.update(|cx| cx.windows().contains(&handle))?;
|
||||
let open = cx.update(|cx| cx.windows().contains(&handle));
|
||||
anyhow::ensure!(!open, "window {key} is still open");
|
||||
cx.update(|cx| Opened::take(key, cx))?;
|
||||
cx.update(|cx| Opened::take(key, cx));
|
||||
log::info!("script: {key} closed");
|
||||
}
|
||||
Step::CloseWindow(key) => {
|
||||
let handle = opened(key, cx)?;
|
||||
handle.update(cx, |_, window, _| window.remove_window())?;
|
||||
cx.update(|cx| Opened::take(key, cx))?;
|
||||
cx.update(|cx| Opened::take(key, cx));
|
||||
}
|
||||
}
|
||||
cx.background_executor().timer(FRAME).await;
|
||||
@@ -439,13 +439,13 @@ async fn target_bounds(
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<Bounds<Pixels>> {
|
||||
let bounds = cx
|
||||
.update(|cx| Probes::get(key, cx))?
|
||||
.update(|cx| Probes::get(key, cx))
|
||||
.with_context(|| format!("probe {key} never rendered"))?;
|
||||
if window.update(cx, |gallery, _, cx| gallery.reveal(bounds, cx))? {
|
||||
cx.background_executor().timer(FRAME).await;
|
||||
}
|
||||
let bounds = cx
|
||||
.update(|cx| Probes::get(key, cx))?
|
||||
.update(|cx| Probes::get(key, cx))
|
||||
.with_context(|| format!("probe {key} vanished after scrolling"))?;
|
||||
log::info!("script: probe {key} at {bounds:?}");
|
||||
Ok(bounds)
|
||||
|
||||
@@ -44,7 +44,7 @@ impl Gallery {
|
||||
}
|
||||
});
|
||||
let focus = cx.focus_handle();
|
||||
window.focus(&focus);
|
||||
window.focus(&focus, cx);
|
||||
Self {
|
||||
page,
|
||||
pass: 0,
|
||||
@@ -71,10 +71,7 @@ impl Gallery {
|
||||
|
||||
/// Viewport height and furthest scroll, in pixels.
|
||||
pub fn scroll_extent(&self) -> (Pixels, Pixels) {
|
||||
(
|
||||
self.scroll.bounds().size.height,
|
||||
self.scroll.max_offset().height,
|
||||
)
|
||||
(self.scroll.bounds().size.height, self.scroll.max_offset().y)
|
||||
}
|
||||
|
||||
/// Scrolls until `target` sits inside the page. True if it moved.
|
||||
@@ -87,7 +84,7 @@ impl Gallery {
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
let y = (-self.scroll.offset().y + shift).clamp(px(0.0), self.scroll.max_offset().height);
|
||||
let y = (-self.scroll.offset().y + shift).clamp(px(0.0), self.scroll.max_offset().y);
|
||||
self.scroll_to(y, cx);
|
||||
true
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user