WIP: Work toward a decoupled example of UI vs app code
This commit is contained in:
parent
1485bd3cfc
commit
84fb47d930
20 changed files with 687 additions and 377 deletions
31
Cargo.lock
generated
31
Cargo.lock
generated
|
@ -351,6 +351,16 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
|
||||
|
||||
[[package]]
|
||||
name = "assets"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gpui2",
|
||||
"parking_lot 0.11.2",
|
||||
"rust-embed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assistant"
|
||||
version = "0.1.0"
|
||||
|
@ -9301,6 +9311,7 @@ name = "storybook2"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assets",
|
||||
"backtrace-on-stack-overflow",
|
||||
"chrono",
|
||||
"clap 4.4.4",
|
||||
|
@ -11941,6 +11952,25 @@ dependencies = [
|
|||
"uuid 1.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "workspace2_ui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assets",
|
||||
"clap 3.2.25",
|
||||
"gpui2",
|
||||
"log",
|
||||
"picker2",
|
||||
"rust-embed",
|
||||
"settings2",
|
||||
"simplelog",
|
||||
"theme2",
|
||||
"ui2",
|
||||
"util",
|
||||
"workspace2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ws2_32-sys"
|
||||
version = "0.2.1"
|
||||
|
@ -12167,6 +12197,7 @@ dependencies = [
|
|||
"activity_indicator2",
|
||||
"ai2",
|
||||
"anyhow",
|
||||
"assets",
|
||||
"assistant2",
|
||||
"async-compression",
|
||||
"async-recursion 0.3.2",
|
||||
|
|
|
@ -3,6 +3,7 @@ members = [
|
|||
"crates/activity_indicator",
|
||||
"crates/activity_indicator2",
|
||||
"crates/ai",
|
||||
"crates/assets",
|
||||
"crates/assistant",
|
||||
"crates/assistant2",
|
||||
"crates/audio",
|
||||
|
@ -127,6 +128,7 @@ members = [
|
|||
"crates/vcs_menu",
|
||||
"crates/vcs_menu2",
|
||||
"crates/workspace2",
|
||||
"crates/workspace2_ui",
|
||||
"crates/welcome",
|
||||
"crates/welcome2",
|
||||
"crates/xtask",
|
||||
|
|
14
crates/assets/Cargo.toml
Normal file
14
crates/assets/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "assets"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
path = "src/assets.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
gpui = { path = "../gpui2", package = "gpui2" }
|
||||
parking_lot.workspace = true
|
||||
rust-embed.workspace = true
|
63
crates/assets/src/assets.rs
Normal file
63
crates/assets/src/assets.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use gpui::{AppContext, AssetSource, Result, SharedString};
|
||||
use parking_lot::Mutex;
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "../../assets"]
|
||||
#[include = "fonts/**/*"]
|
||||
#[include = "icons/**/*"]
|
||||
#[include = "themes/**/*"]
|
||||
#[exclude = "themes/src/*"]
|
||||
#[include = "sounds/**/*"]
|
||||
#[include = "*.md"]
|
||||
#[exclude = "*.DS_Store"]
|
||||
pub struct Assets;
|
||||
|
||||
impl AssetSource for Assets {
|
||||
fn load(&self, path: &str) -> Result<std::borrow::Cow<[u8]>> {
|
||||
Self::get(path)
|
||||
.map(|f| f.data)
|
||||
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
||||
}
|
||||
|
||||
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
|
||||
Ok(Self::iter()
|
||||
.filter_map(|p| {
|
||||
if p.starts_with(path) {
|
||||
Some(p.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl Assets {
|
||||
pub fn load_embedded_fonts(&self, app: &AppContext) {
|
||||
let font_paths = self.list("fonts").unwrap();
|
||||
let embedded_fonts = Mutex::new(Vec::new());
|
||||
|
||||
app.background_executor()
|
||||
.block(app.background_executor().scoped(|scope| {
|
||||
for font_path in &font_paths {
|
||||
if !font_path.ends_with(".ttf") {
|
||||
continue;
|
||||
}
|
||||
|
||||
scope.spawn(async {
|
||||
let font_path = &*font_path;
|
||||
let font_bytes = Assets.load(font_path).unwrap().to_vec();
|
||||
embedded_fonts.lock().push(Arc::from(font_bytes));
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
app.text_system()
|
||||
.add_fonts(&embedded_fonts.into_inner())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
|
@ -109,7 +109,7 @@ pub struct Component<C> {
|
|||
component: Option<C>,
|
||||
}
|
||||
|
||||
pub struct CompositeElementState<C: RenderOnce> {
|
||||
pub struct ComponentState<C: RenderOnce> {
|
||||
rendered_element: Option<<C::Rendered as IntoElement>::Element>,
|
||||
rendered_element_state: Option<<<C::Rendered as IntoElement>::Element as Element>::State>,
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ impl<C> Component<C> {
|
|||
}
|
||||
|
||||
impl<C: RenderOnce> Element for Component<C> {
|
||||
type State = CompositeElementState<C>;
|
||||
type State = ComponentState<C>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
|
@ -134,7 +134,7 @@ impl<C: RenderOnce> Element for Component<C> {
|
|||
if let Some(element_id) = element.element_id() {
|
||||
let layout_id =
|
||||
cx.with_element_state(element_id, |state, cx| element.layout(state, cx));
|
||||
let state = CompositeElementState {
|
||||
let state = ComponentState {
|
||||
rendered_element: Some(element),
|
||||
rendered_element_state: None,
|
||||
};
|
||||
|
@ -142,7 +142,7 @@ impl<C: RenderOnce> Element for Component<C> {
|
|||
} else {
|
||||
let (layout_id, state) =
|
||||
element.layout(state.and_then(|s| s.rendered_element_state), cx);
|
||||
let state = CompositeElementState {
|
||||
let state = ComponentState {
|
||||
rendered_element: Some(element),
|
||||
rendered_element_state: Some(state),
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ pub fn derive_into_element(input: TokenStream) -> TokenStream {
|
|||
{
|
||||
type Element = gpui::Component<Self>;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
fn element_id(&self) -> Option<gpui::ElementId> {
|
||||
None
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ mod keymap_file;
|
|||
mod settings_file;
|
||||
mod settings_store;
|
||||
|
||||
use gpui::AppContext;
|
||||
use rust_embed::RustEmbed;
|
||||
use std::{borrow::Cow, str};
|
||||
use util::asset_str;
|
||||
|
@ -17,6 +18,14 @@ pub use settings_store::{Settings, SettingsJsonSchemaParams, SettingsStore};
|
|||
#[exclude = "*.DS_Store"]
|
||||
pub struct SettingsAssets;
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
let mut store = SettingsStore::default();
|
||||
store
|
||||
.set_default_settings(default_settings().as_ref(), cx)
|
||||
.unwrap();
|
||||
cx.set_global(store);
|
||||
}
|
||||
|
||||
pub fn default_settings() -> Cow<'static, str> {
|
||||
asset_str::<SettingsAssets>("settings/default.json")
|
||||
}
|
||||
|
|
|
@ -109,6 +109,17 @@ pub trait Settings: 'static + Send + Sync {
|
|||
{
|
||||
cx.global_mut::<SettingsStore>().override_global(settings)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn override_global_with<F>(cx: &mut AppContext, f: F)
|
||||
where
|
||||
F: for<'a> FnOnce(&'a mut Self, &'a mut AppContext),
|
||||
Self: Sized + Clone,
|
||||
{
|
||||
let mut settings = Self::get_global(cx).clone();
|
||||
f(&mut settings, cx);
|
||||
Self::override_global(settings, cx);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SettingsJsonSchemaParams<'a> {
|
||||
|
|
|
@ -10,6 +10,7 @@ path = "src/storybook2.rs"
|
|||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
assets = { path = "../assets" }
|
||||
# TODO: Remove after diagnosing stack overflow.
|
||||
backtrace-on-stack-overflow = "0.3.0"
|
||||
chrono = "0.4"
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use gpui::{AssetSource, SharedString};
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "../../assets"]
|
||||
#[include = "fonts/**/*"]
|
||||
#[include = "icons/**/*"]
|
||||
#[include = "themes/**/*"]
|
||||
#[include = "sounds/**/*"]
|
||||
#[include = "*.md"]
|
||||
#[exclude = "*.DS_Store"]
|
||||
pub struct Assets;
|
||||
|
||||
impl AssetSource for Assets {
|
||||
fn load(&self, path: &str) -> Result<Cow<[u8]>> {
|
||||
Self::get(path)
|
||||
.map(|f| f.data)
|
||||
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
||||
}
|
||||
|
||||
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
|
||||
Ok(Self::iter()
|
||||
.filter(|p| p.starts_with(path))
|
||||
.map(SharedString::from)
|
||||
.collect())
|
||||
}
|
||||
}
|
|
@ -1,26 +1,22 @@
|
|||
mod assets;
|
||||
mod stories;
|
||||
mod story_selector;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::story_selector::{ComponentStory, StorySelector};
|
||||
use assets::Assets;
|
||||
use clap::Parser;
|
||||
use dialoguer::FuzzySelect;
|
||||
use gpui::{
|
||||
div, px, size, AnyView, AppContext, Bounds, Div, Render, ViewContext, VisualContext,
|
||||
WindowBounds, WindowOptions,
|
||||
div, px, size, AnyView, Bounds, Div, Render, ViewContext, VisualContext, WindowBounds,
|
||||
WindowOptions,
|
||||
};
|
||||
pub use indoc::indoc;
|
||||
use log::LevelFilter;
|
||||
use settings2::{default_settings, Settings, SettingsStore};
|
||||
use settings2::Settings;
|
||||
use simplelog::SimpleLogger;
|
||||
use std::sync::Arc;
|
||||
use strum::IntoEnumIterator;
|
||||
use theme2::{ThemeRegistry, ThemeSettings};
|
||||
use ui::prelude::*;
|
||||
|
||||
use crate::assets::Assets;
|
||||
use crate::story_selector::{ComponentStory, StorySelector};
|
||||
pub use indoc::indoc;
|
||||
|
||||
// gpui::actions! {
|
||||
// storybook,
|
||||
// [ToggleInspector]
|
||||
|
@ -50,7 +46,7 @@ fn main() {
|
|||
let stories = ComponentStory::iter().collect::<Vec<_>>();
|
||||
|
||||
let selection = FuzzySelect::new()
|
||||
.with_prompt("Choose a story to run:")
|
||||
.with_prompt("Choose a story to rungit :")
|
||||
.items(&stories)
|
||||
.interact()
|
||||
.unwrap();
|
||||
|
@ -59,16 +55,10 @@ fn main() {
|
|||
});
|
||||
let theme_name = args.theme.unwrap_or("One Dark".to_string());
|
||||
|
||||
let asset_source = Arc::new(Assets);
|
||||
gpui::App::production(asset_source).run(move |cx| {
|
||||
load_embedded_fonts(cx).unwrap();
|
||||
|
||||
let mut store = SettingsStore::default();
|
||||
store
|
||||
.set_default_settings(default_settings().as_ref(), cx)
|
||||
.unwrap();
|
||||
cx.set_global(store);
|
||||
|
||||
let assets = Arc::new(Assets);
|
||||
gpui::App::production(assets.clone()).run(move |cx| {
|
||||
assets.load_embedded_fonts(cx);
|
||||
settings2::init(cx);
|
||||
theme2::init(theme2::LoadThemes::All, cx);
|
||||
|
||||
let selector = story_selector;
|
||||
|
@ -124,16 +114,3 @@ impl Render for StoryWrapper {
|
|||
.child(self.story.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn load_embedded_fonts(cx: &AppContext) -> gpui::Result<()> {
|
||||
let font_paths = cx.asset_source().list("fonts")?;
|
||||
let mut embedded_fonts = Vec::new();
|
||||
for font_path in font_paths {
|
||||
if font_path.ends_with(".ttf") {
|
||||
let font_bytes = cx.asset_source().load(&font_path)?.to_vec();
|
||||
embedded_fonts.push(Arc::from(font_bytes));
|
||||
}
|
||||
}
|
||||
|
||||
cx.text_system().add_fonts(&embedded_fonts)
|
||||
}
|
||||
|
|
|
@ -1,267 +0,0 @@
|
|||
use gpui::Rgba;
|
||||
use indoc::indoc;
|
||||
use serde::Deserialize;
|
||||
use theme::{FabricSurface, FabricSurfaceState, FabricTheme};
|
||||
|
||||
fn main() {
|
||||
use std::fs::{self, DirEntry};
|
||||
use std::path::Path;
|
||||
|
||||
let legacy_themes_path = Path::new(env!("PWD")).join("crates/theme2/legacy_themes");
|
||||
dbg!(&legacy_themes_path);
|
||||
if legacy_themes_path.exists() {
|
||||
let legacy_theme_files =
|
||||
fs::read_dir(legacy_themes_path).expect("Failed to read legacy themes directory");
|
||||
let mut mods = Vec::new();
|
||||
|
||||
for entry in legacy_theme_files {
|
||||
let entry: DirEntry = entry.expect("Failed to read directory entry");
|
||||
let path = entry.path();
|
||||
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("json") {
|
||||
let theme_json =
|
||||
fs::read_to_string(&path).expect(&format!("Failed to read {:?}", path));
|
||||
let legacy_theme: LegacyTheme = serde_json::from_str(&theme_json).expect(&format!(
|
||||
"Failed to parse JSON to LegacyTheme from {:?}",
|
||||
path
|
||||
));
|
||||
|
||||
let name = path.file_stem().unwrap().to_str().unwrap().to_string();
|
||||
let mod_name = format!(
|
||||
"{}",
|
||||
name.replace(" ", "_").to_lowercase().replace("é", "e") // Hack for Rosé Pine
|
||||
);
|
||||
mods.push(mod_name.clone());
|
||||
|
||||
let theme = FabricTheme {
|
||||
name,
|
||||
|
||||
cotton: FabricSurface {
|
||||
default: FabricSurfaceState {
|
||||
background: legacy_theme.middle.base.default.background,
|
||||
border: legacy_theme.middle.base.default.border,
|
||||
foreground: legacy_theme.middle.base.default.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.middle.variant.default.foreground,
|
||||
),
|
||||
},
|
||||
hovered: FabricSurfaceState {
|
||||
background: legacy_theme.middle.base.hovered.background,
|
||||
border: legacy_theme.middle.base.hovered.border,
|
||||
foreground: legacy_theme.middle.base.hovered.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.middle.variant.hovered.foreground,
|
||||
),
|
||||
},
|
||||
pressed: FabricSurfaceState {
|
||||
background: legacy_theme.middle.base.pressed.background,
|
||||
border: legacy_theme.middle.base.pressed.border,
|
||||
foreground: legacy_theme.middle.base.pressed.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.middle.variant.pressed.foreground,
|
||||
),
|
||||
},
|
||||
active: FabricSurfaceState {
|
||||
background: legacy_theme.middle.base.active.background,
|
||||
border: legacy_theme.middle.base.active.border,
|
||||
foreground: legacy_theme.middle.base.active.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.middle.variant.active.foreground,
|
||||
),
|
||||
},
|
||||
disabled: FabricSurfaceState {
|
||||
background: legacy_theme.middle.base.disabled.background,
|
||||
border: legacy_theme.middle.base.disabled.border,
|
||||
foreground: legacy_theme.middle.base.disabled.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.middle.variant.disabled.foreground,
|
||||
),
|
||||
},
|
||||
inverted: FabricSurfaceState {
|
||||
background: legacy_theme.middle.base.inverted.background,
|
||||
border: legacy_theme.middle.base.inverted.border,
|
||||
foreground: legacy_theme.middle.base.inverted.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.middle.variant.inverted.foreground,
|
||||
),
|
||||
},
|
||||
},
|
||||
linen: FabricSurface::from(legacy_theme.lowest.on.clone()),
|
||||
denim: FabricSurface {
|
||||
default: FabricSurfaceState {
|
||||
background: legacy_theme.lowest.base.default.background,
|
||||
border: legacy_theme.lowest.base.default.border,
|
||||
foreground: legacy_theme.lowest.base.default.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.lowest.variant.default.foreground,
|
||||
),
|
||||
},
|
||||
hovered: FabricSurfaceState {
|
||||
background: legacy_theme.lowest.base.hovered.background,
|
||||
border: legacy_theme.lowest.base.hovered.border,
|
||||
foreground: legacy_theme.lowest.base.hovered.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.lowest.variant.hovered.foreground,
|
||||
),
|
||||
},
|
||||
pressed: FabricSurfaceState {
|
||||
background: legacy_theme.lowest.base.pressed.background,
|
||||
border: legacy_theme.lowest.base.pressed.border,
|
||||
foreground: legacy_theme.lowest.base.pressed.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.lowest.variant.pressed.foreground,
|
||||
),
|
||||
},
|
||||
active: FabricSurfaceState {
|
||||
background: legacy_theme.lowest.base.active.background,
|
||||
border: legacy_theme.lowest.base.active.border,
|
||||
foreground: legacy_theme.lowest.base.active.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.lowest.variant.active.foreground,
|
||||
),
|
||||
},
|
||||
disabled: FabricSurfaceState {
|
||||
background: legacy_theme.lowest.base.disabled.background,
|
||||
border: legacy_theme.lowest.base.disabled.border,
|
||||
foreground: legacy_theme.lowest.base.disabled.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.lowest.variant.disabled.foreground,
|
||||
),
|
||||
},
|
||||
inverted: FabricSurfaceState {
|
||||
background: legacy_theme.lowest.base.inverted.background,
|
||||
border: legacy_theme.lowest.base.inverted.border,
|
||||
foreground: legacy_theme.lowest.base.inverted.foreground,
|
||||
secondary_foreground: Some(
|
||||
legacy_theme.lowest.variant.inverted.foreground,
|
||||
),
|
||||
},
|
||||
}, // Assuming silk maps to 'on' at middle elevation
|
||||
silk: FabricSurface::from(legacy_theme.middle.on.clone()),
|
||||
satin: FabricSurface::from(legacy_theme.lowest.accent.clone()),
|
||||
positive: FabricSurface::from(legacy_theme.lowest.positive.clone()),
|
||||
warning: FabricSurface::from(legacy_theme.lowest.warning.clone()),
|
||||
negative: FabricSurface::from(legacy_theme.lowest.negative.clone()),
|
||||
};
|
||||
|
||||
let indented_theme = format!("{:#?}", theme)
|
||||
.lines()
|
||||
.map(|line| format!(" {}", line))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
let module_source = format!(
|
||||
indoc! {r#"
|
||||
use crate::{{FabricSurface, FabricSurfaceState, FabricTheme}};
|
||||
use gpui::rgba;
|
||||
|
||||
pub fn {}() -> FabricTheme {{
|
||||
{}
|
||||
}}
|
||||
"#},
|
||||
mod_name, indented_theme,
|
||||
);
|
||||
|
||||
let module_path = Path::new(env!("PWD"))
|
||||
.join(format!("crates/theme2/src/fabric_themes/{}.rs", mod_name));
|
||||
fs::write(&module_path, module_source)
|
||||
.expect(&format!("Failed to write to {:?}", module_path));
|
||||
println!("Wrote FabricTheme to file {:?}", module_path);
|
||||
}
|
||||
}
|
||||
|
||||
let mod_rs_path = Path::new(env!("PWD")).join("crates/theme2/src/fabric_themes/mod.rs");
|
||||
let mut mod_file_content = String::new();
|
||||
|
||||
for mod_name in mods.iter() {
|
||||
mod_file_content.push_str(&format!("pub mod {};\n", mod_name));
|
||||
}
|
||||
mod_file_content.push_str("\n");
|
||||
for mod_name in mods.iter() {
|
||||
mod_file_content.push_str(&format!("pub use {}::{};\n", mod_name, mod_name));
|
||||
}
|
||||
|
||||
fs::write(&mod_rs_path, mod_file_content)
|
||||
.expect(&format!("Failed to write to {:?}", mod_rs_path));
|
||||
println!("Wrote module declarations to file {:?}", mod_rs_path);
|
||||
} else {
|
||||
eprintln!("Legacy themes directory does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LegacySurface> for FabricSurface {
|
||||
fn from(legacy: LegacySurface) -> Self {
|
||||
FabricSurface {
|
||||
default: FabricSurfaceState {
|
||||
background: legacy.default.background,
|
||||
border: legacy.default.border,
|
||||
foreground: legacy.default.foreground,
|
||||
secondary_foreground: None, // Assuming no secondary_foreground in LegacySurface
|
||||
},
|
||||
hovered: FabricSurfaceState {
|
||||
background: legacy.hovered.background,
|
||||
border: legacy.hovered.border,
|
||||
foreground: legacy.hovered.foreground,
|
||||
secondary_foreground: None,
|
||||
},
|
||||
pressed: FabricSurfaceState {
|
||||
background: legacy.pressed.background,
|
||||
border: legacy.pressed.border,
|
||||
foreground: legacy.pressed.foreground,
|
||||
secondary_foreground: None,
|
||||
},
|
||||
active: FabricSurfaceState {
|
||||
background: legacy.active.background,
|
||||
border: legacy.active.border,
|
||||
foreground: legacy.active.foreground,
|
||||
secondary_foreground: None,
|
||||
},
|
||||
disabled: FabricSurfaceState {
|
||||
background: legacy.disabled.background,
|
||||
border: legacy.disabled.border,
|
||||
foreground: legacy.disabled.foreground,
|
||||
secondary_foreground: None,
|
||||
},
|
||||
inverted: FabricSurfaceState {
|
||||
background: legacy.inverted.background,
|
||||
border: legacy.inverted.border,
|
||||
foreground: legacy.inverted.foreground,
|
||||
secondary_foreground: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize)]
|
||||
pub struct LegacySurfaceState {
|
||||
background: Rgba,
|
||||
border: Rgba,
|
||||
foreground: Rgba,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize)]
|
||||
pub struct LegacySurface {
|
||||
default: LegacySurfaceState,
|
||||
hovered: LegacySurfaceState,
|
||||
pressed: LegacySurfaceState,
|
||||
active: LegacySurfaceState,
|
||||
disabled: LegacySurfaceState,
|
||||
inverted: LegacySurfaceState,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize)]
|
||||
pub struct LegacyElevation {
|
||||
base: LegacySurface,
|
||||
variant: LegacySurface,
|
||||
on: LegacySurface,
|
||||
accent: LegacySurface,
|
||||
positive: LegacySurface,
|
||||
warning: LegacySurface,
|
||||
negative: LegacySurface,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize)]
|
||||
pub struct LegacyTheme {
|
||||
lowest: LegacyElevation,
|
||||
middle: LegacyElevation,
|
||||
highest: LegacyElevation,
|
||||
}
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use gpui::{HighlightStyle, SharedString};
|
||||
use gpui::{AppContext, HighlightStyle, SharedString};
|
||||
use refineable::Refineable;
|
||||
|
||||
use crate::{
|
||||
|
@ -21,6 +21,10 @@ pub struct ThemeRegistry {
|
|||
}
|
||||
|
||||
impl ThemeRegistry {
|
||||
pub fn global(cx: &AppContext) -> &Self {
|
||||
cx.global()
|
||||
}
|
||||
|
||||
fn insert_theme_families(&mut self, families: impl IntoIterator<Item = ThemeFamily>) {
|
||||
for family in families.into_iter() {
|
||||
self.insert_themes(family.themes);
|
||||
|
|
24
crates/workspace2_ui/Cargo.toml
Normal file
24
crates/workspace2_ui/Cargo.toml
Normal file
|
@ -0,0 +1,24 @@
|
|||
[package]
|
||||
name = "workspace2_ui"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "src/workspace2_ui.rs"
|
||||
|
||||
[dependencies]
|
||||
ui = { package = "ui2", path = "../ui2", features = ["stories"] }
|
||||
gpui = { package = "gpui2", path = "../gpui2" }
|
||||
theme = { path = "../theme2", package = "theme2" }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
assets = { path = "../assets" }
|
||||
clap = { version = "3.1", features = ["derive"] }
|
||||
log = { workspace = true }
|
||||
picker = { package = "picker2", path = "../picker2" }
|
||||
rust-embed = { workspace = true }
|
||||
settings = { path = "../settings2", package = "settings2" }
|
||||
simplelog = "0.9"
|
||||
util = { path = "../util" }
|
||||
workspace = { path = "../workspace2", package = "workspace2" }
|
62
crates/workspace2_ui/examples/titlebar.rs
Normal file
62
crates/workspace2_ui/examples/titlebar.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
use assets::Assets;
|
||||
use clap::Parser;
|
||||
use gpui::{px, App, Bounds, Render, Size, VisualContext, WindowBounds, WindowOptions};
|
||||
use log::LevelFilter;
|
||||
use settings::Settings;
|
||||
use simplelog::SimpleLogger;
|
||||
use std::sync::Arc;
|
||||
use theme::{LoadThemes, ThemeRegistry, ThemeSettings};
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
theme: Option<String>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::init(LevelFilter::Info, Default::default()).unwrap();
|
||||
|
||||
let args = Args::parse();
|
||||
let theme_name = args.theme.unwrap_or("One Light".to_string());
|
||||
|
||||
let assets = Arc::new(Assets);
|
||||
App::production(assets.clone()).run(move |cx| {
|
||||
assets.load_embedded_fonts(cx);
|
||||
settings::init(cx);
|
||||
theme::init(LoadThemes::All, cx);
|
||||
|
||||
ThemeSettings::override_global_with(cx, |settings, cx| {
|
||||
settings.active_theme = ThemeRegistry::global(cx).get(&theme_name).unwrap()
|
||||
});
|
||||
|
||||
cx.open_window(
|
||||
WindowOptions {
|
||||
bounds: WindowBounds::Fixed(Bounds {
|
||||
origin: Default::default(),
|
||||
size: Size {
|
||||
width: px(1500.),
|
||||
height: px(780.),
|
||||
}
|
||||
.into(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
move |cx| {
|
||||
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
|
||||
cx.set_rem_size(ui_font_size);
|
||||
cx.build_view(|cx| TitlebarExample)
|
||||
},
|
||||
);
|
||||
|
||||
cx.activate(true);
|
||||
})
|
||||
}
|
||||
|
||||
struct TitlebarExample;
|
||||
|
||||
impl Render for TitlebarExample {
|
||||
type Element = ();
|
||||
|
||||
fn render(&mut self, cx: &mut ui::prelude::ViewContext<Self>) -> Self::Element {
|
||||
todo!()
|
||||
}
|
||||
}
|
439
crates/workspace2_ui/src/titlebar.rs
Normal file
439
crates/workspace2_ui/src/titlebar.rs
Normal file
|
@ -0,0 +1,439 @@
|
|||
use gpui::{div, prelude::*, px, rems, Div, SharedString, Stateful, WindowContext};
|
||||
use theme::ActiveTheme;
|
||||
use ui::Shape;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Titlebar {
|
||||
full_screen: bool,
|
||||
}
|
||||
|
||||
// pub struct TitlebarCall {
|
||||
// current_user: TitlebarUser,
|
||||
// }
|
||||
|
||||
// pub struct TitlebarUser {}
|
||||
|
||||
impl RenderOnce for Titlebar {
|
||||
type Rendered = Stateful<Div>;
|
||||
|
||||
fn render(self, cx: &mut ui::prelude::WindowContext) -> Self::Rendered {
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.id("titlebar")
|
||||
.justify_between()
|
||||
.w_full()
|
||||
.h(rems(1.75))
|
||||
// Set a non-scaling min-height here to ensure the titlebar is
|
||||
// always at least the height of the traffic lights.
|
||||
.min_h(px(32.))
|
||||
.pl_2()
|
||||
.when(self.full_screen, |this| {
|
||||
// Use pixels here instead of a rem-based size because the macOS traffic
|
||||
// lights are a static size, and don't scale with the rest of the UI.
|
||||
this.pl(px(80.))
|
||||
})
|
||||
.bg(cx.theme().colors().title_bar_background)
|
||||
.on_click(|event, cx| {
|
||||
if event.up.click_count == 2 {
|
||||
cx.zoom_window();
|
||||
}
|
||||
})
|
||||
// left side
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.gap_1()
|
||||
.children(self.render_project_host(cx))
|
||||
.child(self.render_project_name(cx))
|
||||
.children(self.render_project_branch(cx))
|
||||
.when_some(
|
||||
current_user.clone().zip(client.peer_id()).zip(room.clone()),
|
||||
|this, ((current_user, peer_id), room)| {
|
||||
let player_colors = cx.theme().players();
|
||||
let room = room.read(cx);
|
||||
let mut remote_participants =
|
||||
room.remote_participants().values().collect::<Vec<_>>();
|
||||
remote_participants.sort_by_key(|p| p.participant_index.0);
|
||||
this.children(self.render_collaborator(
|
||||
¤t_user,
|
||||
peer_id,
|
||||
true,
|
||||
room.is_speaking(),
|
||||
room.is_muted(cx),
|
||||
&room,
|
||||
project_id,
|
||||
¤t_user,
|
||||
))
|
||||
.children(
|
||||
remote_participants.iter().filter_map(|collaborator| {
|
||||
let is_present = project_id.map_or(false, |project_id| {
|
||||
collaborator.location
|
||||
== ParticipantLocation::SharedProject { project_id }
|
||||
});
|
||||
let face_pile = self.render_collaborator(
|
||||
&collaborator.user,
|
||||
collaborator.peer_id,
|
||||
is_present,
|
||||
collaborator.speaking,
|
||||
collaborator.muted,
|
||||
&room,
|
||||
project_id,
|
||||
¤t_user,
|
||||
)?;
|
||||
Some(
|
||||
v_stack()
|
||||
.id(("collaborator", collaborator.user.id))
|
||||
.child(face_pile)
|
||||
.child(render_color_ribbon(
|
||||
collaborator.participant_index,
|
||||
player_colors,
|
||||
))
|
||||
.cursor_pointer()
|
||||
.on_click({
|
||||
let peer_id = collaborator.peer_id;
|
||||
cx.listener(move |this, _, cx| {
|
||||
this.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.follow(peer_id, cx);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
})
|
||||
.tooltip({
|
||||
let login = collaborator.user.github_login.clone();
|
||||
move |cx| {
|
||||
Tooltip::text(format!("Follow {login}"), cx)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
// right side
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.gap_1()
|
||||
.pr_1()
|
||||
.when_some(room, |this, room| {
|
||||
let room = room.read(cx);
|
||||
let project = self.project.read(cx);
|
||||
let is_local = project.is_local();
|
||||
let is_shared = is_local && project.is_shared();
|
||||
let is_muted = room.is_muted(cx);
|
||||
let is_deafened = room.is_deafened().unwrap_or(false);
|
||||
let is_screen_sharing = room.is_screen_sharing();
|
||||
|
||||
this.when(is_local, |this| {
|
||||
this.child(
|
||||
Button::new(
|
||||
"toggle_sharing",
|
||||
if is_shared { "Unshare" } else { "Share" },
|
||||
)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(
|
||||
move |this, _, cx| {
|
||||
if is_shared {
|
||||
this.unshare_project(&Default::default(), cx);
|
||||
} else {
|
||||
this.share_project(&Default::default(), cx);
|
||||
}
|
||||
},
|
||||
)),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
IconButton::new("leave-call", ui::Icon::Exit)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(move |_, cx| {
|
||||
ActiveCall::global(cx)
|
||||
.update(cx, |call, cx| call.hang_up(cx))
|
||||
.detach_and_log_err(cx);
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
IconButton::new(
|
||||
"mute-microphone",
|
||||
if is_muted {
|
||||
ui::Icon::MicMute
|
||||
} else {
|
||||
ui::Icon::Mic
|
||||
},
|
||||
)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_muted)
|
||||
.on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
|
||||
)
|
||||
.child(
|
||||
IconButton::new(
|
||||
"mute-sound",
|
||||
if is_deafened {
|
||||
ui::Icon::AudioOff
|
||||
} else {
|
||||
ui::Icon::AudioOn
|
||||
},
|
||||
)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_deafened)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::with_meta("Deafen Audio", None, "Mic will be muted", cx)
|
||||
})
|
||||
.on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("screen-share", ui::Icon::Screen)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.icon_size(IconSize::Small)
|
||||
.selected(is_screen_sharing)
|
||||
.on_click(move |_, cx| {
|
||||
crate::toggle_screen_sharing(&Default::default(), cx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
.map(|el| {
|
||||
let status = self.client.status();
|
||||
let status = &*status.borrow();
|
||||
if matches!(status, client::Status::Connected { .. }) {
|
||||
el.child(self.render_user_menu_button(cx))
|
||||
} else {
|
||||
el.children(self.render_connection_status(status, cx))
|
||||
.child(self.render_sign_in_button(cx))
|
||||
.child(self.render_user_menu_button(cx))
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Titlebar {
|
||||
pub fn render_project_host(&self, cx: &mut ViewContext<Self>) -> Option<impl Element> {
|
||||
let host = self.project.read(cx).host()?;
|
||||
let host = self.user_store.read(cx).get_cached_user(host.user_id)?;
|
||||
let participant_index = self
|
||||
.user_store
|
||||
.read(cx)
|
||||
.participant_indices()
|
||||
.get(&host.id)?;
|
||||
Some(
|
||||
div().border().border_color(gpui::red()).child(
|
||||
Button::new("project_owner_trigger", host.github_login.clone())
|
||||
.color(Color::Player(participant_index.0))
|
||||
.style(ButtonStyle::Subtle)
|
||||
.label_size(LabelSize::Small)
|
||||
.tooltip(move |cx| Tooltip::text("Toggle following", cx)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn render_project_name(&self, cx: &mut ViewContext<Self>) -> impl Element {
|
||||
let name = {
|
||||
let mut names = self.project.read(cx).visible_worktrees(cx).map(|worktree| {
|
||||
let worktree = worktree.read(cx);
|
||||
worktree.root_name()
|
||||
});
|
||||
|
||||
names.next().unwrap_or("")
|
||||
};
|
||||
|
||||
let name = util::truncate_and_trailoff(name, MAX_PROJECT_NAME_LENGTH);
|
||||
let workspace = self.workspace.clone();
|
||||
popover_menu("project_name_trigger")
|
||||
.trigger(
|
||||
Button::new("project_name_trigger", name)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.label_size(LabelSize::Small)
|
||||
.tooltip(move |cx| Tooltip::text("Recent Projects", cx)),
|
||||
)
|
||||
.menu(move |cx| Some(Self::render_project_popover(workspace.clone(), cx)))
|
||||
}
|
||||
|
||||
pub fn render_project_branch(&self, cx: &mut ViewContext<Self>) -> Option<impl Element> {
|
||||
let entry = {
|
||||
let mut names_and_branches =
|
||||
self.project.read(cx).visible_worktrees(cx).map(|worktree| {
|
||||
let worktree = worktree.read(cx);
|
||||
worktree.root_git_entry()
|
||||
});
|
||||
|
||||
names_and_branches.next().flatten()
|
||||
};
|
||||
let workspace = self.workspace.upgrade()?;
|
||||
let branch_name = entry
|
||||
.as_ref()
|
||||
.and_then(RepositoryEntry::branch)
|
||||
.map(|branch| util::truncate_and_trailoff(&branch, MAX_BRANCH_NAME_LENGTH))?;
|
||||
Some(
|
||||
popover_menu("project_branch_trigger")
|
||||
.trigger(
|
||||
Button::new("project_branch_trigger", branch_name)
|
||||
.color(Color::Muted)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.label_size(LabelSize::Small)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::with_meta(
|
||||
"Recent Branches",
|
||||
Some(&ToggleVcsMenu),
|
||||
"Local branches only",
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
)
|
||||
.menu(move |cx| Self::render_vcs_popover(workspace.clone(), cx)),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_collaborator(
|
||||
&self,
|
||||
user: &Arc<User>,
|
||||
peer_id: PeerId,
|
||||
is_present: bool,
|
||||
is_speaking: bool,
|
||||
is_muted: bool,
|
||||
room: &Room,
|
||||
project_id: Option<u64>,
|
||||
current_user: &Arc<User>,
|
||||
) -> Option<FacePile> {
|
||||
let followers = project_id.map_or(&[] as &[_], |id| room.followers_for(peer_id, id));
|
||||
|
||||
let pile = FacePile::default()
|
||||
.child(
|
||||
Avatar::new(user.avatar_uri.clone())
|
||||
.grayscale(!is_present)
|
||||
.border_color(if is_speaking {
|
||||
gpui::blue()
|
||||
} else if is_muted {
|
||||
gpui::red()
|
||||
} else {
|
||||
Hsla::default()
|
||||
}),
|
||||
)
|
||||
.children(followers.iter().filter_map(|follower_peer_id| {
|
||||
let follower = room
|
||||
.remote_participants()
|
||||
.values()
|
||||
.find_map(|p| (p.peer_id == *follower_peer_id).then_some(&p.user))
|
||||
.or_else(|| {
|
||||
(self.client.peer_id() == Some(*follower_peer_id)).then_some(current_user)
|
||||
})?
|
||||
.clone();
|
||||
|
||||
Some(Avatar::new(follower.avatar_uri.clone()))
|
||||
}));
|
||||
|
||||
Some(pile)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TitlebarParticipant {
|
||||
avatar_uri: SharedString,
|
||||
follower_avatar_uris: Vec<SharedString>,
|
||||
}
|
||||
|
||||
pub struct Facepile {}
|
||||
|
||||
#[derive(Default, IntoElement)]
|
||||
pub struct FacePile {
|
||||
pub faces: Vec<Avatar>,
|
||||
}
|
||||
|
||||
impl RenderOnce for FacePile {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, _: &mut WindowContext) -> Self::Rendered {
|
||||
let face_count = self.faces.len();
|
||||
div()
|
||||
.p_1()
|
||||
.flex()
|
||||
.items_center()
|
||||
.children(self.faces.into_iter().enumerate().map(|(ix, avatar)| {
|
||||
let last_child = ix == face_count - 1;
|
||||
div()
|
||||
.z_index((face_count - ix) as u8)
|
||||
.when(!last_child, |div| div.neg_mr_1())
|
||||
.child(avatar)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Avatar {
|
||||
pub image_uri: SharedString,
|
||||
pub audio_status: AudioStatus,
|
||||
pub shape: AvatarShape,
|
||||
}
|
||||
|
||||
pub enum AvatarShape {
|
||||
Square,
|
||||
Circle,
|
||||
}
|
||||
|
||||
impl RenderOnce for Avatar {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
div()
|
||||
.map(|this| match self.shape {
|
||||
AvatarShape::Square => this.rounded_md(),
|
||||
AvatarShape::Circle => this.rounded_full(),
|
||||
})
|
||||
.map(|this| match self.audio_status {
|
||||
AudioStatus::None => this,
|
||||
AudioStatus::Muted => todo!(),
|
||||
AudioStatus::Speaking => todo!(),
|
||||
})
|
||||
.size(cx.rem_size() + px(2.));
|
||||
|
||||
let size = cx.rem_size();
|
||||
|
||||
div()
|
||||
.when_some(self.border_color, |this, color| {
|
||||
this.border().border_color(color)
|
||||
})
|
||||
.child(
|
||||
self.image
|
||||
.size(size)
|
||||
// todo!(Pull the avatar fallback background from the theme.)
|
||||
.bg(gpui::red()),
|
||||
)
|
||||
.children(self.is_available.map(|is_free| {
|
||||
// HACK: non-integer sizes result in oval indicators.
|
||||
let indicator_size = (size * 0.4).round();
|
||||
|
||||
div()
|
||||
.absolute()
|
||||
.z_index(1)
|
||||
.bg(if is_free {
|
||||
cx.theme().status().created
|
||||
} else {
|
||||
cx.theme().status().deleted
|
||||
})
|
||||
.size(indicator_size)
|
||||
.rounded(indicator_size)
|
||||
.bottom_0()
|
||||
.right_0()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AudioStatus {
|
||||
None,
|
||||
Muted,
|
||||
Speaking,
|
||||
}
|
||||
|
||||
impl RenderOnce for Avatar {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
todo!()
|
||||
}
|
||||
}
|
3
crates/workspace2_ui/src/workspace2_ui.rs
Normal file
3
crates/workspace2_ui/src/workspace2_ui.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
mod titlebar;
|
||||
|
||||
pub use titlebar::*;
|
|
@ -16,6 +16,7 @@ path = "src/main.rs"
|
|||
|
||||
[dependencies]
|
||||
ai = { package = "ai2", path = "../ai2"}
|
||||
assets = { path = "../assets" }
|
||||
audio = { package = "audio2", path = "../audio2" }
|
||||
activity_indicator = { package = "activity_indicator2", path = "../activity_indicator2"}
|
||||
auto_update = { package = "auto_update2", path = "../auto_update2" }
|
||||
|
|
|
@ -17,11 +17,8 @@ use language::LanguageRegistry;
|
|||
use log::LevelFilter;
|
||||
|
||||
use node_runtime::RealNodeRuntime;
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{
|
||||
default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore,
|
||||
};
|
||||
use settings::{handle_settings_file_changes, watch_config_file, Settings, SettingsStore};
|
||||
use simplelog::ConfigBuilder;
|
||||
use smol::process::Command;
|
||||
use std::{
|
||||
|
@ -66,7 +63,8 @@ fn main() {
|
|||
}
|
||||
|
||||
log::info!("========== starting zed ==========");
|
||||
let app = App::production(Arc::new(Assets));
|
||||
let assets = Arc::new(Assets);
|
||||
let app = App::production(assets.clone());
|
||||
|
||||
let (installation_id, existing_installation_id_found) = app
|
||||
.background_executor()
|
||||
|
@ -116,16 +114,9 @@ fn main() {
|
|||
if let Some(build_sha) = option_env!("ZED_COMMIT_SHA") {
|
||||
cx.set_global(AppCommitSha(build_sha.into()))
|
||||
}
|
||||
|
||||
cx.set_global(listener.clone());
|
||||
|
||||
load_embedded_fonts(cx);
|
||||
|
||||
let mut store = SettingsStore::default();
|
||||
store
|
||||
.set_default_settings(default_settings().as_ref(), cx)
|
||||
.unwrap();
|
||||
cx.set_global(store);
|
||||
assets.load_embedded_fonts(cx);
|
||||
settings::init(cx);
|
||||
handle_settings_file_changes(user_settings_file_rx, cx);
|
||||
handle_keymap_file_changes(user_keymap_file_rx, cx);
|
||||
|
||||
|
@ -722,30 +713,6 @@ fn collect_url_args() -> Vec<String> {
|
|||
.collect()
|
||||
}
|
||||
|
||||
fn load_embedded_fonts(cx: &AppContext) {
|
||||
let asset_source = cx.asset_source();
|
||||
let font_paths = asset_source.list("fonts").unwrap();
|
||||
let embedded_fonts = Mutex::new(Vec::new());
|
||||
let executor = cx.background_executor();
|
||||
|
||||
executor.block(executor.scoped(|scope| {
|
||||
for font_path in &font_paths {
|
||||
if !font_path.ends_with(".ttf") {
|
||||
continue;
|
||||
}
|
||||
|
||||
scope.spawn(async {
|
||||
let font_bytes = asset_source.load(font_path).unwrap().to_vec();
|
||||
embedded_fonts.lock().push(Arc::from(font_bytes));
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
cx.text_system()
|
||||
.add_fonts(&embedded_fonts.into_inner())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
async fn watch_languages(fs: Arc<dyn fs::Fs>, languages: Arc<LanguageRegistry>) -> Option<()> {
|
||||
use std::time::Duration;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
mod app_menus;
|
||||
mod assets;
|
||||
pub mod languages;
|
||||
mod only_instance;
|
||||
mod open_listener;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue