This commit is contained in:
Laurent Stéphenne 2025-08-26 00:07:40 -04:00 committed by GitHub
commit dde8df7d5c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 40 additions and 15 deletions

1
Cargo.lock generated
View file

@ -7483,6 +7483,7 @@ dependencies = [
"taffy", "taffy",
"thiserror 2.0.12", "thiserror 2.0.12",
"unicode-segmentation", "unicode-segmentation",
"url",
"usvg", "usvg",
"util", "util",
"uuid", "uuid",

View file

@ -50,6 +50,7 @@ wayland = [
"filedescriptor", "filedescriptor",
"xkbcommon", "xkbcommon",
"open", "open",
"url",
] ]
x11 = [ x11 = [
"blade-graphics", "blade-graphics",
@ -194,6 +195,7 @@ wayland-protocols = { version = "0.31.2", features = [
wayland-protocols-plasma = { version = "0.2.0", features = [ wayland-protocols-plasma = { version = "0.2.0", features = [
"client", "client",
], optional = true } ], optional = true }
url = { workspace = true, optional = true }
# X11 # X11
as-raw-xcb-connection = { version = "1", optional = true } as-raw-xcb-connection = { version = "1", optional = true }

View file

@ -24,6 +24,7 @@ pub use async_context::*;
use collections::{FxHashMap, FxHashSet, HashMap, VecDeque}; use collections::{FxHashMap, FxHashSet, HashMap, VecDeque};
pub use context::*; pub use context::*;
pub use entity_map::*; pub use entity_map::*;
#[cfg(feature = "http_client")]
use http_client::{HttpClient, Url}; use http_client::{HttpClient, Url};
use smallvec::SmallVec; use smallvec::SmallVec;
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
@ -132,22 +133,14 @@ impl Application {
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
log::info!("GPUI was compiled in test mode"); log::info!("GPUI was compiled in test mode");
Self(App::new_app( Self(App::new_app(current_platform(false), Arc::new(())))
current_platform(false),
Arc::new(()),
Arc::new(NullHttpClient),
))
} }
/// Build an app in headless mode. This prevents opening windows, /// Build an app in headless mode. This prevents opening windows,
/// but makes it possible to run an application in an context like /// but makes it possible to run an application in an context like
/// SSH, where GUI applications are not allowed. /// SSH, where GUI applications are not allowed.
pub fn headless() -> Self { pub fn headless() -> Self {
Self(App::new_app( Self(App::new_app(current_platform(true), Arc::new(())))
current_platform(true),
Arc::new(()),
Arc::new(NullHttpClient),
))
} }
/// Assign /// Assign
@ -161,6 +154,7 @@ impl Application {
} }
/// Sets the HTTP client for the application. /// Sets the HTTP client for the application.
#[cfg(feature = "http_client")]
pub fn with_http_client(self, http_client: Arc<dyn HttpClient>) -> Self { pub fn with_http_client(self, http_client: Arc<dyn HttpClient>) -> Self {
let mut context_lock = self.0.borrow_mut(); let mut context_lock = self.0.borrow_mut();
context_lock.http_client = http_client; context_lock.http_client = http_client;
@ -253,6 +247,7 @@ pub struct App {
pub(crate) loading_assets: FxHashMap<(TypeId, u64), Box<dyn Any>>, pub(crate) loading_assets: FxHashMap<(TypeId, u64), Box<dyn Any>>,
asset_source: Arc<dyn AssetSource>, asset_source: Arc<dyn AssetSource>,
pub(crate) svg_renderer: SvgRenderer, pub(crate) svg_renderer: SvgRenderer,
#[cfg(feature = "http_client")]
http_client: Arc<dyn HttpClient>, http_client: Arc<dyn HttpClient>,
pub(crate) globals_by_type: FxHashMap<TypeId, Box<dyn Any>>, pub(crate) globals_by_type: FxHashMap<TypeId, Box<dyn Any>>,
pub(crate) entities: EntityMap, pub(crate) entities: EntityMap,
@ -300,7 +295,6 @@ impl App {
pub(crate) fn new_app( pub(crate) fn new_app(
platform: Rc<dyn Platform>, platform: Rc<dyn Platform>,
asset_source: Arc<dyn AssetSource>, asset_source: Arc<dyn AssetSource>,
http_client: Arc<dyn HttpClient>,
) -> Rc<AppCell> { ) -> Rc<AppCell> {
let executor = platform.background_executor(); let executor = platform.background_executor();
let foreground_executor = platform.foreground_executor(); let foreground_executor = platform.foreground_executor();
@ -327,7 +321,8 @@ impl App {
svg_renderer: SvgRenderer::new(asset_source.clone()), svg_renderer: SvgRenderer::new(asset_source.clone()),
loading_assets: Default::default(), loading_assets: Default::default(),
asset_source, asset_source,
http_client, #[cfg(feature = "http_client")]
http_client: Arc::new(NullHttpClient),
globals_by_type: FxHashMap::default(), globals_by_type: FxHashMap::default(),
entities, entities,
new_entity_observers: SubscriberSet::new(), new_entity_observers: SubscriberSet::new(),
@ -850,11 +845,13 @@ impl App {
} }
/// Returns the HTTP client for the application. /// Returns the HTTP client for the application.
#[cfg(feature = "http_client")]
pub fn http_client(&self) -> Arc<dyn HttpClient> { pub fn http_client(&self) -> Arc<dyn HttpClient> {
self.http_client.clone() self.http_client.clone()
} }
/// Sets the HTTP client for the application. /// Sets the HTTP client for the application.
#[cfg(feature = "http_client")]
pub fn set_http_client(&mut self, new_client: Arc<dyn HttpClient>) { pub fn set_http_client(&mut self, new_client: Arc<dyn HttpClient>) {
self.http_client = new_client; self.http_client = new_client;
} }
@ -2033,8 +2030,10 @@ pub struct KeystrokeEvent {
pub context_stack: Vec<KeyContext>, pub context_stack: Vec<KeyContext>,
} }
#[cfg(feature = "http_client")]
struct NullHttpClient; struct NullHttpClient;
#[cfg(feature = "http_client")]
impl HttpClient for NullHttpClient { impl HttpClient for NullHttpClient {
fn send( fn send(
&self, &self,

View file

@ -131,7 +131,7 @@ impl TestAppContext {
let text_system = Arc::new(TextSystem::new(platform.text_system())); let text_system = Arc::new(TextSystem::new(platform.text_system()));
Self { Self {
app: App::new_app(platform.clone(), asset_source, http_client), app: App::new_app(platform.clone(), asset_source).with_http_client(http_client),
background_executor, background_executor,
foreground_executor, foreground_executor,
dispatcher, dispatcher,

View file

@ -11,6 +11,7 @@ use std::sync::Arc;
#[derive(Debug, PartialEq, Eq, Hash, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum Resource { pub enum Resource {
/// This resource is at a given URI /// This resource is at a given URI
#[cfg(feature = "http_client")]
Uri(SharedUri), Uri(SharedUri),
/// This resource is at a given path in the file system /// This resource is at a given path in the file system
Path(Arc<Path>), Path(Arc<Path>),
@ -18,6 +19,7 @@ pub enum Resource {
Embedded(SharedString), Embedded(SharedString),
} }
#[cfg(feature = "http_client")]
impl From<SharedUri> for Resource { impl From<SharedUri> for Resource {
fn from(value: SharedUri) -> Self { fn from(value: SharedUri) -> Self {
Self::Uri(value) Self::Uri(value)

View file

@ -49,16 +49,19 @@ pub enum ImageSource {
Custom(Arc<dyn Fn(&mut Window, &mut App) -> Option<Result<Arc<RenderImage>, ImageCacheError>>>), Custom(Arc<dyn Fn(&mut Window, &mut App) -> Option<Result<Arc<RenderImage>, ImageCacheError>>>),
} }
#[cfg(feature = "http_client")]
fn is_uri(uri: &str) -> bool { fn is_uri(uri: &str) -> bool {
http_client::Uri::from_str(uri).is_ok() http_client::Uri::from_str(uri).is_ok()
} }
#[cfg(feature = "http_client")]
impl From<SharedUri> for ImageSource { impl From<SharedUri> for ImageSource {
fn from(value: SharedUri) -> Self { fn from(value: SharedUri) -> Self {
Self::Resource(Resource::Uri(value)) Self::Resource(Resource::Uri(value))
} }
} }
#[cfg(feature = "http_client")]
impl<'a> From<&'a str> for ImageSource { impl<'a> From<&'a str> for ImageSource {
fn from(s: &'a str) -> Self { fn from(s: &'a str) -> Self {
if is_uri(s) { if is_uri(s) {
@ -69,6 +72,14 @@ impl<'a> From<&'a str> for ImageSource {
} }
} }
#[cfg(not(feature = "http_client"))]
impl<'a> From<&'a str> for ImageSource {
fn from(s: &'a str) -> Self {
Self::Resource(Resource::Embedded(s.to_string().into()))
}
}
#[cfg(feature = "http_client")]
impl From<String> for ImageSource { impl From<String> for ImageSource {
fn from(s: String) -> Self { fn from(s: String) -> Self {
if is_uri(&s) { if is_uri(&s) {
@ -79,6 +90,13 @@ impl From<String> for ImageSource {
} }
} }
#[cfg(not(feature = "http_client"))]
impl From<String> for ImageSource {
fn from(s: String) -> Self {
Self::Resource(Resource::Embedded(s.into()))
}
}
impl From<SharedString> for ImageSource { impl From<SharedString> for ImageSource {
fn from(s: SharedString) -> Self { fn from(s: SharedString) -> Self {
s.as_ref().into() s.as_ref().into()
@ -591,7 +609,6 @@ impl Asset for ImageAssetLoader {
source: Self::Source, source: Self::Source,
cx: &mut App, cx: &mut App,
) -> impl Future<Output = Self::Output> + Send + 'static { ) -> impl Future<Output = Self::Output> + Send + 'static {
let client = cx.http_client();
// TODO: Can we make SVGs always rescale? // TODO: Can we make SVGs always rescale?
// let scale_factor = cx.scale_factor(); // let scale_factor = cx.scale_factor();
let svg_renderer = cx.svg_renderer(); let svg_renderer = cx.svg_renderer();
@ -599,7 +616,9 @@ impl Asset for ImageAssetLoader {
async move { async move {
let bytes = match source.clone() { let bytes = match source.clone() {
Resource::Path(uri) => fs::read(uri.as_ref())?, Resource::Path(uri) => fs::read(uri.as_ref())?,
#[cfg(feature = "http_client")]
Resource::Uri(uri) => { Resource::Uri(uri) => {
let client = cx.http_client();
let mut response = client let mut response = client
.get(uri.as_ref(), ().into(), true) .get(uri.as_ref(), ().into(), true)
.await .await
@ -720,6 +739,7 @@ pub enum ImageCacheError {
Io(Arc<std::io::Error>), Io(Arc<std::io::Error>),
/// An error that occurred while processing an image. /// An error that occurred while processing an image.
#[error("unexpected http status for {uri}: {status}, body: {body}")] #[error("unexpected http status for {uri}: {status}, body: {body}")]
#[cfg(feature = "http_client")]
BadStatus { BadStatus {
/// The URI of the image. /// The URI of the image.
uri: SharedUri, uri: SharedUri,

View file

@ -135,6 +135,7 @@ pub use executor::*;
pub use geometry::*; pub use geometry::*;
pub use global::*; pub use global::*;
pub use gpui_macros::{AppContext, IntoElement, Render, VisualContext, register_action, test}; pub use gpui_macros::{AppContext, IntoElement, Render, VisualContext, register_action, test};
#[cfg(feature = "http_client")]
pub use http_client; pub use http_client;
pub use input::*; pub use input::*;
pub use inspector::*; pub use inspector::*;

View file

@ -14,8 +14,8 @@ use calloop::{
use calloop_wayland_source::WaylandSource; use calloop_wayland_source::WaylandSource;
use collections::HashMap; use collections::HashMap;
use filedescriptor::Pipe; use filedescriptor::Pipe;
use http_client::Url;
use smallvec::SmallVec; use smallvec::SmallVec;
use url::Url;
use util::ResultExt; use util::ResultExt;
use wayland_backend::client::ObjectId; use wayland_backend::client::ObjectId;
use wayland_backend::protocol::WEnum; use wayland_backend::protocol::WEnum;