Compare commits

...
Sign in to create a new pull request.

20 commits

Author SHA1 Message Date
Piotr Osiewicz
62949d20a7 chore: fmt 2023-08-31 15:43:50 +02:00
Piotr Osiewicz
110bb84261 Merge branch 'main' into gpui_extract_platform 2023-08-31 15:43:32 +02:00
Piotr Osiewicz
576849c98a WIP. Fix up some more test scenarios 2023-08-26 23:16:58 +02:00
Piotr Osiewicz
f16105f391 fix up platform namespace in test macro 2023-08-26 23:00:13 +02:00
Piotr Osiewicz
ac329e40dc Allow dead code for is_empty fn 2023-08-26 22:42:55 +02:00
Piotr Osiewicz
a9db466e67 chore: fmt 2023-08-26 22:40:11 +02:00
Piotr Osiewicz
6f32431d33 Merge branch 'main' into gpui_extract_platform 2023-08-26 22:39:57 +02:00
Piotr Osiewicz
8342803ba5 WIP test migration 2023-08-26 00:13:57 +02:00
Piotr Osiewicz
d033475565 Fix playground build 2023-08-25 23:43:45 +02:00
Piotr Osiewicz
2a350e91b2 chore: fmt 2023-08-25 23:29:28 +02:00
Piotr Osiewicz
fe17505100 Use gpui_platform in Zed 2023-08-25 23:29:12 +02:00
Piotr Osiewicz
79179e8fff Merge branch 'main' into gpui_extract_platform 2023-08-25 23:28:27 +02:00
Piotr Osiewicz
95b4bd467b chore: fmt 2023-08-25 23:24:23 +02:00
Piotr Osiewicz
978d074b1d WIP 2023-08-23 01:33:19 +02:00
Piotr Osiewicz
d0f287772b chore: fmt 2023-08-23 01:13:04 +02:00
Piotr Osiewicz
cfecbc5522 Start moving tests 2023-08-22 01:06:06 +02:00
Piotr Osiewicz
94cf1c3336 Use gpui_platform in test_support 2023-08-22 00:50:05 +02:00
Piotr Osiewicz
f3b76e0571 Add gpui_platform crate for a simple dispatch to implementation for current platform 2023-08-22 00:37:19 +02:00
Piotr Osiewicz
b4cba64fd6 Start fixing up platform tests 2023-08-22 00:30:52 +02:00
Piotr Osiewicz
7516e91a56 Extract gpui_mac out of gpui 2023-08-21 22:29:49 +02:00
61 changed files with 3108 additions and 2966 deletions

73
Cargo.lock generated
View file

@ -1075,6 +1075,7 @@ dependencies = [
"fs",
"futures 0.3.28",
"gpui",
"gpui_platform",
"language",
"live_kit_client",
"log",
@ -1383,6 +1384,7 @@ dependencies = [
"feature_flags",
"futures 0.3.28",
"gpui",
"gpui_platform",
"image",
"lazy_static",
"log",
@ -1539,6 +1541,7 @@ dependencies = [
"futures 0.3.28",
"fuzzy",
"gpui",
"gpui_platform",
"language",
"log",
"menu",
@ -1674,6 +1677,7 @@ dependencies = [
"fs",
"futures 0.3.28",
"gpui",
"gpui_platform",
"language",
"log",
"lsp",
@ -2343,6 +2347,7 @@ dependencies = [
"fuzzy",
"git",
"gpui",
"gpui_platform",
"indoc",
"itertools",
"language",
@ -3116,21 +3121,13 @@ dependencies = [
"anyhow",
"async-task",
"backtrace",
"bindgen 0.65.1",
"block",
"cc",
"cocoa",
"collections",
"core-foundation",
"core-graphics",
"core-text",
"ctor",
"derive_more",
"dhat",
"env_logger 0.9.3",
"etagere",
"font-kit",
"foreign-types",
"futures 0.3.28",
"gpui_macros",
"image",
@ -3138,9 +3135,7 @@ dependencies = [
"lazy_static",
"log",
"media",
"metal",
"num_cpus",
"objc",
"ordered-float",
"parking",
"parking_lot 0.11.2",
@ -3170,6 +3165,41 @@ dependencies = [
"waker-fn",
]
[[package]]
name = "gpui_mac"
version = "0.1.0"
dependencies = [
"anyhow",
"async-task",
"bindgen 0.65.1",
"block",
"cc",
"cocoa",
"collections",
"core-foundation",
"core-graphics",
"core-text",
"ctor",
"etagere",
"font-kit",
"foreign-types",
"gpui",
"log",
"media",
"metal",
"objc",
"ordered-float",
"parking_lot 0.11.2",
"pathfinder_geometry",
"postage",
"resvg",
"smol",
"time 0.3.27",
"tiny-skia",
"usvg",
"uuid 1.4.1",
]
[[package]]
name = "gpui_macros"
version = "0.1.0"
@ -3180,6 +3210,21 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "gpui_platform"
version = "0.1.0"
dependencies = [
"gpui",
"gpui_mac",
"gpui_macros",
"itertools",
"log",
"postage",
"rand 0.8.5",
"serde",
"smol",
]
[[package]]
name = "grid"
version = "0.10.0"
@ -4096,6 +4141,7 @@ dependencies = [
"foreign-types",
"futures 0.3.28",
"gpui",
"gpui_platform",
"hmac 0.12.1",
"jwt",
"live_kit_server",
@ -5179,6 +5225,7 @@ dependencies = [
"anyhow",
"derive_more",
"gpui",
"gpui_platform",
"log",
"parking_lot 0.11.2",
"playground_macros",
@ -6116,6 +6163,7 @@ dependencies = [
"arrayvec 0.7.4",
"bromberg_sl2",
"gpui",
"gpui_platform",
"log",
"rand 0.8.5",
"smallvec",
@ -6146,6 +6194,7 @@ dependencies = [
"env_logger 0.9.3",
"futures 0.3.28",
"gpui",
"gpui_platform",
"parking_lot 0.11.2",
"prost 0.8.0",
"prost-build",
@ -6854,6 +6903,7 @@ dependencies = [
"fs",
"futures 0.3.28",
"gpui",
"gpui_platform",
"indoc",
"lazy_static",
"postage",
@ -7663,6 +7713,7 @@ dependencies = [
"anyhow",
"fs",
"gpui",
"gpui_platform",
"indexmap 1.9.3",
"parking_lot 0.11.2",
"schemars",
@ -9600,6 +9651,7 @@ dependencies = [
"fs",
"futures 0.3.28",
"gpui",
"gpui_platform",
"indoc",
"install_cli",
"itertools",
@ -9742,6 +9794,7 @@ dependencies = [
"fuzzy",
"go_to_line",
"gpui",
"gpui_platform",
"ignore",
"image",
"indexmap 1.9.3",

View file

@ -32,6 +32,8 @@ members = [
"crates/git",
"crates/go_to_line",
"crates/gpui",
"crates/gpui_mac",
"crates/gpui_platform",
"crates/gpui/playground",
"crates/gpui/playground_macros",
"crates/gpui_macros",

View file

@ -13,6 +13,7 @@ test-support = [
"client/test-support",
"collections/test-support",
"gpui/test-support",
"gpui_platform",
"live_kit_client/test-support",
"project/test-support",
"util/test-support"
@ -24,6 +25,7 @@ channel = { path = "../channel" }
client = { path = "../client" }
collections = { path = "../collections" }
gpui = { path = "../gpui" }
gpui_platform = {path = "../gpui_platform", optional = true}
log.workspace = true
live_kit_client = { path = "../live_kit_client" }
fs = { path = "../fs" }

View file

@ -9,12 +9,13 @@ path = "src/client.rs"
doctest = false
[features]
test-support = ["collections/test-support", "gpui/test-support", "rpc/test-support"]
test-support = ["collections/test-support", "gpui/test-support", "gpui_platform", "rpc/test-support"]
[dependencies]
collections = { path = "../collections" }
db = { path = "../db" }
gpui = { path = "../gpui" }
gpui_platform = {path = "../gpui_platform", optional = true}
util = { path = "../util" }
rpc = { path = "../rpc" }
text = { path = "../text" }

View file

@ -15,6 +15,7 @@ test-support = [
"collections/test-support",
"editor/test-support",
"gpui/test-support",
"gpui_platform",
"project/test-support",
"settings/test-support",
"util/test-support",
@ -34,6 +35,7 @@ editor = { path = "../editor" }
feedback = { path = "../feedback" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
gpui_platform = {path = "../gpui_platform", optional = true}
language = { path = "../language" }
menu = { path = "../menu" }
picker = { path = "../picker" }

View file

@ -12,6 +12,7 @@ doctest = false
test-support = [
"collections/test-support",
"gpui/test-support",
"gpui_platform",
"language/test-support",
"lsp/test-support",
"settings/test-support",
@ -22,6 +23,7 @@ test-support = [
collections = { path = "../collections" }
context_menu = { path = "../context_menu" }
gpui = { path = "../gpui" }
gpui_platform = {path = "../gpui_platform", optional = true}
language = { path = "../language" }
settings = { path = "../settings" }
theme = { path = "../theme" }

View file

@ -14,11 +14,13 @@ test-support = [
"text/test-support",
"language/test-support",
"gpui/test-support",
"gpui_platform",
"project/test-support",
"util/test-support",
"workspace/test-support",
"tree-sitter-rust",
"tree-sitter-typescript"
"tree-sitter-typescript",
]
[dependencies]
@ -32,6 +34,7 @@ context_menu = { path = "../context_menu" }
fuzzy = { path = "../fuzzy" }
git = { path = "../git" }
gpui = { path = "../gpui" }
gpui_platform = { path = "../gpui_platform", optional = true}
language = { path = "../language" }
lsp = { path = "../lsp" }
project = { path = "../project" }

View file

@ -14,6 +14,7 @@ doctest = false
test-support = ["backtrace", "dhat", "env_logger", "collections/test-support"]
[dependencies]
anyhow.workspace = true
collections = { path = "../collections" }
gpui_macros = { path = "../gpui_macros" }
util = { path = "../util" }
@ -55,10 +56,6 @@ usvg = { version = "0.14", features = [] }
uuid = { version = "1.1.2", features = ["v4"] }
waker-fn = "1.1.0"
[build-dependencies]
bindgen = "0.65.1"
cc = "1.0.67"
[dev-dependencies]
backtrace = "0.3"
collections = { path = "../collections", features = ["test-support"] }
@ -69,14 +66,4 @@ simplelog = "0.9"
[target.'cfg(target_os = "macos")'.dependencies]
media = { path = "../media" }
anyhow.workspace = true
block = "0.1"
cocoa = "0.24"
core-foundation = { version = "0.9.3", features = ["with-uuid"] }
core-graphics = "0.22.3"
core-text = "19.2"
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "b2f77d56f450338aa4f7dd2f0197d8c9acb0cf18" }
foreign-types = "0.3"
log.workspace = true
metal = "0.21.0"
objc = "0.2"

View file

@ -12,6 +12,7 @@ path = "src/playground.rs"
anyhow.workspace = true
derive_more.workspace = true
gpui = { path = ".." }
gpui_platform = { path = "../../gpui_platform" }
log.workspace = true
playground_macros = { path = "../playground_macros" }
parking_lot.workspace = true

View file

@ -26,8 +26,11 @@ mod view;
fn main() {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
gpui::App::new(()).unwrap().run(|cx| {
let platform = gpui_platform::platform();
let foreground =
std::rc::Rc::new(gpui::executor::Foreground::platform(platform.dispatcher()).unwrap());
let foreground = gpui_platform::foreground_platform(foreground);
gpui::App::new((), platform, foreground).unwrap().run(|cx| {
cx.add_window(
WindowOptions {
bounds: gpui::platform::WindowBounds::Fixed(RectF::new(

File diff suppressed because it is too large Load diff

View file

@ -54,6 +54,7 @@ impl<K: Clone + Hash + Eq + Copy, F> Default for CallbackCollection<K, F> {
}
impl<K: Clone + Hash + Eq + Copy, F> CallbackCollection<K, F> {
#[allow(dead_code)]
#[cfg(test)]
pub fn is_empty(&self) -> bool {
self.internal.lock().callbacks.is_empty()

View file

@ -33,8 +33,11 @@ impl ClipboardItem {
.as_ref()
.and_then(|m| serde_json::from_str(m).ok())
}
pub fn raw_metadata(&self) -> Option<&str> {
self.metadata.as_deref()
}
pub(crate) fn text_hash(text: &str) -> u64 {
pub fn text_hash(text: &str) -> u64 {
let mut hasher = SeaHasher::new();
text.hash(&mut hasher);
hasher.finish()

View file

@ -216,72 +216,3 @@ impl ToJson for LabelStyle {
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::color::Color;
use crate::fonts::{Properties as FontProperties, Weight};
#[crate::test(self)]
fn test_layout_label_with_highlights(cx: &mut crate::AppContext) {
let default_style = TextStyle::new(
"Menlo",
12.,
Default::default(),
Default::default(),
Default::default(),
Color::black(),
cx.font_cache(),
)
.unwrap();
let highlight_style = TextStyle::new(
"Menlo",
12.,
*FontProperties::new().weight(Weight::BOLD),
Default::default(),
Default::default(),
Color::new(255, 0, 0, 255),
cx.font_cache(),
)
.unwrap();
let label = Label::new(
".αβγδε.ⓐⓑⓒⓓⓔ.abcde.".to_string(),
LabelStyle {
text: default_style.clone(),
highlight_text: Some(highlight_style.clone()),
},
)
.with_highlights(vec![
".α".len(),
".αβ".len(),
".αβγδ".len(),
".αβγδε.ⓐ".len(),
".αβγδε.ⓐⓑ".len(),
]);
let default_run_style = RunStyle {
font_id: default_style.font_id,
color: default_style.color,
underline: default_style.underline,
};
let highlight_run_style = RunStyle {
font_id: highlight_style.font_id,
color: highlight_style.color,
underline: highlight_style.underline,
};
let runs = label.compute_runs();
assert_eq!(
runs.as_slice(),
&[
(".α".len(), default_run_style),
("βγ".len(), highlight_run_style),
("δ".len(), default_run_style),
("ε".len(), highlight_run_style),
(".ⓐ".len(), default_run_style),
("ⓑⓒ".len(), highlight_run_style),
("ⓓⓔ.abcde.".len(), default_run_style),
]
);
}
}

View file

@ -643,375 +643,3 @@ impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Height {
self.0.partial_cmp(&other.height).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{elements::Empty, geometry::vector::vec2f, Entity, PaintContext};
use rand::prelude::*;
use std::env;
#[crate::test(self)]
fn test_layout(cx: &mut crate::AppContext) {
cx.add_window(Default::default(), |cx| {
let mut view = TestView;
let constraint = SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.));
let elements = Rc::new(RefCell::new(vec![(0, 20.), (1, 30.), (2, 100.)]));
let state = ListState::new(elements.borrow().len(), Orientation::Top, 1000.0, {
let elements = elements.clone();
move |_, ix, _| {
let (id, height) = elements.borrow()[ix];
TestElement::new(id, height).into_any()
}
});
let mut list = List::new(state.clone());
let mut new_parents = Default::default();
let mut notify_views_if_parents_change = Default::default();
let mut layout_cx = LayoutContext::new(
cx,
&mut new_parents,
&mut notify_views_if_parents_change,
false,
);
let (size, _) = list.layout(constraint, &mut view, &mut layout_cx);
assert_eq!(size, vec2f(100., 40.));
assert_eq!(
state.0.borrow().items.summary().clone(),
ListItemSummary {
count: 3,
rendered_count: 3,
unrendered_count: 0,
height: 150.
}
);
state.0.borrow_mut().scroll(
&ListOffset {
item_ix: 0,
offset_in_item: 0.,
},
40.,
vec2f(0., -54.),
true,
&mut view,
cx,
);
let mut layout_cx = LayoutContext::new(
cx,
&mut new_parents,
&mut notify_views_if_parents_change,
false,
);
let (_, logical_scroll_top) = list.layout(constraint, &mut view, &mut layout_cx);
assert_eq!(
logical_scroll_top,
ListOffset {
item_ix: 2,
offset_in_item: 4.
}
);
assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 54.);
elements.borrow_mut().splice(1..2, vec![(3, 40.), (4, 50.)]);
elements.borrow_mut().push((5, 60.));
state.splice(1..2, 2);
state.splice(4..4, 1);
assert_eq!(
state.0.borrow().items.summary().clone(),
ListItemSummary {
count: 5,
rendered_count: 2,
unrendered_count: 3,
height: 120.
}
);
let mut layout_cx = LayoutContext::new(
cx,
&mut new_parents,
&mut notify_views_if_parents_change,
false,
);
let (size, logical_scroll_top) = list.layout(constraint, &mut view, &mut layout_cx);
assert_eq!(size, vec2f(100., 40.));
assert_eq!(
state.0.borrow().items.summary().clone(),
ListItemSummary {
count: 5,
rendered_count: 5,
unrendered_count: 0,
height: 270.
}
);
assert_eq!(
logical_scroll_top,
ListOffset {
item_ix: 3,
offset_in_item: 4.
}
);
assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 114.);
view
});
}
#[crate::test(self, iterations = 10)]
fn test_random(cx: &mut crate::AppContext, mut rng: StdRng) {
let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
cx.add_window(Default::default(), |cx| {
let mut view = TestView;
let mut next_id = 0;
let elements = Rc::new(RefCell::new(
(0..rng.gen_range(0..=20))
.map(|_| {
let id = next_id;
next_id += 1;
(id, rng.gen_range(0..=200) as f32 / 2.0)
})
.collect::<Vec<_>>(),
));
let orientation = *[Orientation::Top, Orientation::Bottom]
.choose(&mut rng)
.unwrap();
let overdraw = rng.gen_range(1..=100) as f32;
let state = ListState::new(elements.borrow().len(), orientation, overdraw, {
let elements = elements.clone();
move |_, ix, _| {
let (id, height) = elements.borrow()[ix];
TestElement::new(id, height).into_any()
}
});
let mut width = rng.gen_range(0..=2000) as f32 / 2.;
let mut height = rng.gen_range(0..=2000) as f32 / 2.;
log::info!("orientation: {:?}", orientation);
log::info!("overdraw: {}", overdraw);
log::info!("elements: {:?}", elements.borrow());
log::info!("size: ({:?}, {:?})", width, height);
log::info!("==================");
let mut last_logical_scroll_top = None;
for _ in 0..operations {
match rng.gen_range(0..=100) {
0..=29 if last_logical_scroll_top.is_some() => {
let delta = vec2f(0., rng.gen_range(-overdraw..=overdraw));
log::info!(
"Scrolling by {:?}, previous scroll top: {:?}",
delta,
last_logical_scroll_top.unwrap()
);
state.0.borrow_mut().scroll(
last_logical_scroll_top.as_ref().unwrap(),
height,
delta,
true,
&mut view,
cx,
);
}
30..=34 => {
width = rng.gen_range(0..=2000) as f32 / 2.;
log::info!("changing width: {:?}", width);
}
35..=54 => {
height = rng.gen_range(0..=1000) as f32 / 2.;
log::info!("changing height: {:?}", height);
}
_ => {
let mut elements = elements.borrow_mut();
let end_ix = rng.gen_range(0..=elements.len());
let start_ix = rng.gen_range(0..=end_ix);
let new_elements = (0..rng.gen_range(0..10))
.map(|_| {
let id = next_id;
next_id += 1;
(id, rng.gen_range(0..=200) as f32 / 2.)
})
.collect::<Vec<_>>();
log::info!("splice({:?}, {:?})", start_ix..end_ix, new_elements);
state.splice(start_ix..end_ix, new_elements.len());
elements.splice(start_ix..end_ix, new_elements);
for (ix, item) in state.0.borrow().items.cursor::<()>().enumerate() {
if let ListItem::Rendered(element) = item {
let (expected_id, _) = elements[ix];
element.borrow().with_metadata(|metadata: Option<&usize>| {
assert_eq!(*metadata.unwrap(), expected_id);
});
}
}
}
}
let mut list = List::new(state.clone());
let window_size = vec2f(width, height);
let mut new_parents = Default::default();
let mut notify_views_if_parents_change = Default::default();
let mut layout_cx = LayoutContext::new(
cx,
&mut new_parents,
&mut notify_views_if_parents_change,
false,
);
let (size, logical_scroll_top) = list.layout(
SizeConstraint::new(vec2f(0., 0.), window_size),
&mut view,
&mut layout_cx,
);
assert_eq!(size, window_size);
last_logical_scroll_top = Some(logical_scroll_top);
let state = state.0.borrow();
log::info!("items {:?}", state.items.items(&()));
let scroll_top = state.scroll_top(&logical_scroll_top);
let rendered_top = (scroll_top - overdraw).max(0.);
let rendered_bottom = scroll_top + height + overdraw;
let mut item_top = 0.;
log::info!(
"rendered top {:?}, rendered bottom {:?}, scroll top {:?}",
rendered_top,
rendered_bottom,
scroll_top,
);
let mut first_rendered_element_top = None;
let mut last_rendered_element_bottom = None;
assert_eq!(state.items.summary().count, elements.borrow().len());
for (ix, item) in state.items.cursor::<()>().enumerate() {
match item {
ListItem::Unrendered => {
let item_bottom = item_top;
assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
item_top = item_bottom;
}
ListItem::Removed(height) => {
let (id, expected_height) = elements.borrow()[ix];
assert_eq!(
*height, expected_height,
"element {} height didn't match",
id
);
let item_bottom = item_top + height;
assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
item_top = item_bottom;
}
ListItem::Rendered(element) => {
let (expected_id, expected_height) = elements.borrow()[ix];
let element = element.borrow();
element.with_metadata(|metadata: Option<&usize>| {
assert_eq!(*metadata.unwrap(), expected_id);
});
assert_eq!(element.size().y(), expected_height);
let item_bottom = item_top + element.size().y();
first_rendered_element_top.get_or_insert(item_top);
last_rendered_element_bottom = Some(item_bottom);
assert!(item_bottom > rendered_top || item_top < rendered_bottom);
item_top = item_bottom;
}
}
}
match orientation {
Orientation::Top => {
if let Some(first_rendered_element_top) = first_rendered_element_top {
assert!(first_rendered_element_top <= scroll_top);
}
}
Orientation::Bottom => {
if let Some(last_rendered_element_bottom) = last_rendered_element_bottom {
assert!(last_rendered_element_bottom >= scroll_top + height);
}
}
}
}
view
});
}
struct TestView;
impl Entity for TestView {
type Event = ();
}
impl crate::View for TestView {
fn ui_name() -> &'static str {
"TestView"
}
fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
Empty::new().into_any()
}
}
struct TestElement {
id: usize,
size: Vector2F,
}
impl TestElement {
fn new(id: usize, height: f32) -> Self {
Self {
id,
size: vec2f(100., height),
}
}
}
impl<V: 'static> Element<V> for TestElement {
type LayoutState = ();
type PaintState = ();
fn layout(
&mut self,
_: SizeConstraint,
_: &mut V,
_: &mut LayoutContext<V>,
) -> (Vector2F, ()) {
(self.size, ())
}
fn paint(
&mut self,
_: &mut SceneBuilder,
_: RectF,
_: RectF,
_: &mut (),
_: &mut V,
_: &mut PaintContext<V>,
) {
unimplemented!()
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &V,
_: &ViewContext<V>,
) -> Option<RectF> {
unimplemented!()
}
fn debug(&self, _: RectF, _: &(), _: &(), _: &V, _: &ViewContext<V>) -> serde_json::Value {
self.id.into()
}
fn metadata(&self) -> Option<&dyn std::any::Any> {
Some(&self.id)
}
}
}

View file

@ -399,51 +399,3 @@ pub fn layout_highlighted_chunks<'a>(
layouts
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{elements::Empty, fonts, AnyElement, AppContext, Entity, View, ViewContext};
#[crate::test(self)]
fn test_soft_wrapping_with_carriage_returns(cx: &mut AppContext) {
cx.add_window(Default::default(), |cx| {
let mut view = TestView;
fonts::with_font_cache(cx.font_cache().clone(), || {
let mut text = Text::new("Hello\r\n", Default::default()).with_soft_wrap(true);
let mut new_parents = Default::default();
let mut notify_views_if_parents_change = Default::default();
let mut layout_cx = LayoutContext::new(
cx,
&mut new_parents,
&mut notify_views_if_parents_change,
false,
);
let (_, state) = text.layout(
SizeConstraint::new(Default::default(), vec2f(f32::INFINITY, f32::INFINITY)),
&mut view,
&mut layout_cx,
);
assert_eq!(state.shaped_lines.len(), 2);
assert_eq!(state.wrap_boundaries.len(), 2);
});
view
});
}
struct TestView;
impl Entity for TestView {
type Event = ();
}
impl View for TestView {
fn ui_name() -> &'static str {
"TestView"
}
fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
Empty::new().into_any()
}
}
}

View file

@ -278,48 +278,3 @@ impl DerefMut for LineWrapperHandle {
self.wrapper.as_mut().unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
fonts::{Style, Weight},
platform::{test, Platform as _},
};
#[test]
fn test_select_font() {
let platform = test::platform();
let fonts = FontCache::new(platform.fonts());
let arial = fonts
.load_family(
&["Arial"],
&Features {
calt: Some(false),
..Default::default()
},
)
.unwrap();
let arial_regular = fonts.select_font(arial, &Properties::new()).unwrap();
let arial_italic = fonts
.select_font(arial, Properties::new().style(Style::Italic))
.unwrap();
let arial_bold = fonts
.select_font(arial, Properties::new().weight(Weight::BOLD))
.unwrap();
assert_ne!(arial_regular, arial_italic);
assert_ne!(arial_regular, arial_bold);
assert_ne!(arial_italic, arial_bold);
let arial_with_calt = fonts
.load_family(
&["Arial"],
&Features {
calt: Some(true),
..Default::default()
},
)
.unwrap();
assert_ne!(arial_with_calt, arial);
}
}

View file

@ -1,8 +1,6 @@
mod app;
pub use app::*;
mod assets;
#[cfg(any(test, feature = "test-support"))]
pub mod test;
pub use assets::*;
pub mod elements;
pub mod font_cache;
@ -32,6 +30,8 @@ pub use window::{
WindowContext,
};
#[cfg(any(test, feature = "test-support"))]
pub mod test;
pub use anyhow;
pub use serde_json;

View file

@ -1,11 +1,6 @@
mod event;
#[cfg(target_os = "macos")]
pub mod mac;
pub mod test;
pub mod current {
#[cfg(target_os = "macos")]
pub use super::mac::*;
}
#[cfg(any(test, feature = "test-support"))]
pub(super) mod test;
use crate::{
executor,
@ -88,7 +83,7 @@ pub trait Platform: Send + Sync {
fn restart(&self);
}
pub(crate) trait ForegroundPlatform {
pub trait ForegroundPlatform {
fn on_become_active(&self, callback: Box<dyn FnMut()>);
fn on_resign_active(&self, callback: Box<dyn FnMut()>);
fn on_quit(&self, callback: Box<dyn FnMut()>);
@ -312,9 +307,9 @@ impl Default for CursorStyle {
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct AppVersion {
major: usize,
minor: usize,
patch: usize,
pub major: usize,
pub minor: usize,
pub patch: usize,
}
impl FromStr for AppVersion {

View file

@ -64,7 +64,7 @@ impl super::ForegroundPlatform for ForegroundPlatform {
fn on_resign_active(&self, _: Box<dyn FnMut()>) {}
fn on_quit(&self, _: Box<dyn FnMut()>) {}
fn on_reopen(&self, _: Box<dyn FnMut()>) {}
fn on_event(&self, _: Box<dyn FnMut(crate::platform::Event) -> bool>) {}
fn on_event(&self, _: Box<dyn FnMut(super::Event) -> bool>) {}
fn on_open_urls(&self, _: Box<dyn FnMut(Vec<String>)>) {}
fn run(&self, _on_finish_launching: Box<dyn FnOnce()>) {
@ -93,10 +93,6 @@ impl super::ForegroundPlatform for ForegroundPlatform {
fn reveal_path(&self, _: &Path) {}
}
pub fn platform() -> Platform {
Platform::new()
}
pub struct Platform {
dispatcher: Arc<dyn super::Dispatcher>,
fonts: Arc<dyn super::FontSystem>,
@ -106,10 +102,10 @@ pub struct Platform {
}
impl Platform {
fn new() -> Self {
pub fn new(fonts: Arc<dyn super::FontSystem>) -> Self {
Self {
dispatcher: Arc::new(Dispatcher),
fonts: Arc::new(super::current::FontSystem::new()),
fonts,
current_clipboard_item: Default::default(),
cursor: Mutex::new(CursorStyle::Arrow),
active_window: Default::default(),
@ -136,11 +132,11 @@ impl super::Platform for Platform {
fn quit(&self) {}
fn screen_by_id(&self, _id: uuid::Uuid) -> Option<Rc<dyn crate::platform::Screen>> {
fn screen_by_id(&self, _id: uuid::Uuid) -> Option<Rc<dyn super::Screen>> {
None
}
fn screens(&self) -> Vec<Rc<dyn crate::platform::Screen>> {
fn screens(&self) -> Vec<Rc<dyn super::Screen>> {
Default::default()
}
@ -165,7 +161,7 @@ impl super::Platform for Platform {
self.active_window.lock().clone()
}
fn add_status_item(&self, handle: AnyWindowHandle) -> Box<dyn crate::platform::Window> {
fn add_status_item(&self, handle: AnyWindowHandle) -> Box<dyn super::Window> {
Box::new(Window::new(
handle,
vec2f(24., 24.),
@ -301,10 +297,6 @@ impl Window {
active_window,
}
}
pub fn title(&self) -> Option<String> {
self.title.clone()
}
}
impl super::Window for Window {
@ -324,11 +316,11 @@ impl super::Window for Window {
24.
}
fn appearance(&self) -> crate::platform::Appearance {
crate::platform::Appearance::Light
fn appearance(&self) -> super::Appearance {
super::Appearance::Light
}
fn screen(&self) -> Rc<dyn crate::platform::Screen> {
fn screen(&self) -> Rc<dyn super::Screen> {
Rc::new(Screen)
}
@ -336,14 +328,9 @@ impl super::Window for Window {
self
}
fn set_input_handler(&mut self, _: Box<dyn crate::platform::InputHandler>) {}
fn set_input_handler(&mut self, _: Box<dyn super::InputHandler>) {}
fn prompt(
&self,
_: crate::platform::PromptLevel,
_: &str,
_: &[&str],
) -> oneshot::Receiver<usize> {
fn prompt(&self, _: super::PromptLevel, _: &str, _: &[&str]) -> oneshot::Receiver<usize> {
let (done_tx, done_rx) = oneshot::channel();
self.pending_prompts.borrow_mut().push_back(done_tx);
done_rx
@ -373,7 +360,7 @@ impl super::Window for Window {
fn toggle_full_screen(&self) {}
fn on_event(&mut self, callback: Box<dyn FnMut(crate::platform::Event) -> bool>) {
fn on_event(&mut self, callback: Box<dyn FnMut(super::Event) -> bool>) {
self.event_handlers.push(callback);
}

View file

@ -21,7 +21,7 @@ use crate::{
fonts::{FontId, GlyphId},
geometry::{rect::RectF, vector::Vector2F},
json::ToJson,
platform::{current::Surface, CursorStyle},
platform::CursorStyle,
ImageData, WindowContext,
};
pub use mouse_event::*;
@ -171,6 +171,11 @@ pub struct Icon {
pub color: Color,
}
pub struct Surface {
pub bounds: RectF,
pub image_buffer: media::core_video::CVImageBuffer,
}
#[derive(Clone, Copy, Default, Debug, JsonSchema)]
pub struct Border {
pub width: f32,

View file

@ -17,7 +17,6 @@ use crate::{
elements::Empty,
executor::{self, ExecutorEvent},
platform,
platform::Platform,
util::CwdBacktrace,
AnyElement, AppContext, Element, Entity, FontCache, Handle, Subscription, TestAppContext, View,
ViewContext,
@ -35,6 +34,7 @@ fn init_logger() {
// static ALLOC: dhat::Alloc = dhat::Alloc;
pub fn run_test(
fonts: fn() -> std::sync::Arc<dyn crate::platform::FontSystem>,
mut num_iterations: u64,
mut starting_seed: u64,
max_retries: usize,
@ -66,10 +66,10 @@ pub fn run_test(
loop {
let result = panic::catch_unwind(|| {
let fonts = fonts();
let foreground_platform = Rc::new(platform::test::foreground_platform());
let platform = Arc::new(platform::test::platform());
let font_system = platform.fonts();
let font_cache = Arc::new(FontCache::new(font_system));
let platform = Arc::new(platform::test::Platform::new(fonts.clone()));
let font_cache = Arc::new(FontCache::new(fonts.clone()));
let mut prev_runnable_history: Option<Vec<ExecutorEvent>> = None;
for _ in 0..num_iterations {

View file

@ -528,7 +528,7 @@ pub struct ShapedBoundary {
}
impl Boundary {
fn new(ix: usize, next_indent: u32) -> Self {
pub fn new(ix: usize, next_indent: u32) -> Self {
Self { ix, next_indent }
}
}
@ -727,129 +727,3 @@ impl LineWrapper {
.width
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fonts::{Properties, Weight};
#[crate::test(self)]
fn test_wrap_line(cx: &mut crate::AppContext) {
let font_cache = cx.font_cache().clone();
let font_system = cx.platform().fonts();
let family = font_cache
.load_family(&["Courier"], &Default::default())
.unwrap();
let font_id = font_cache.select_font(family, &Default::default()).unwrap();
let mut wrapper = LineWrapper::new(font_id, 16., font_system);
assert_eq!(
wrapper
.wrap_line("aa bbb cccc ddddd eeee", 72.0)
.collect::<Vec<_>>(),
&[
Boundary::new(7, 0),
Boundary::new(12, 0),
Boundary::new(18, 0)
],
);
assert_eq!(
wrapper
.wrap_line("aaa aaaaaaaaaaaaaaaaaa", 72.0)
.collect::<Vec<_>>(),
&[
Boundary::new(4, 0),
Boundary::new(11, 0),
Boundary::new(18, 0)
],
);
assert_eq!(
wrapper.wrap_line(" aaaaaaa", 72.).collect::<Vec<_>>(),
&[
Boundary::new(7, 5),
Boundary::new(9, 5),
Boundary::new(11, 5),
]
);
assert_eq!(
wrapper
.wrap_line(" ", 72.)
.collect::<Vec<_>>(),
&[
Boundary::new(7, 0),
Boundary::new(14, 0),
Boundary::new(21, 0)
]
);
assert_eq!(
wrapper
.wrap_line(" aaaaaaaaaaaaaa", 72.)
.collect::<Vec<_>>(),
&[
Boundary::new(7, 0),
Boundary::new(14, 3),
Boundary::new(18, 3),
Boundary::new(22, 3),
]
);
}
#[crate::test(self, retries = 5)]
fn test_wrap_shaped_line(cx: &mut crate::AppContext) {
// This is failing intermittently on CI and we don't have time to figure it out
let font_cache = cx.font_cache().clone();
let font_system = cx.platform().fonts();
let text_layout_cache = TextLayoutCache::new(font_system.clone());
let family = font_cache
.load_family(&["Helvetica"], &Default::default())
.unwrap();
let font_id = font_cache.select_font(family, &Default::default()).unwrap();
let normal = RunStyle {
font_id,
color: Default::default(),
underline: Default::default(),
};
let bold = RunStyle {
font_id: font_cache
.select_font(
family,
&Properties {
weight: Weight::BOLD,
..Default::default()
},
)
.unwrap(),
color: Default::default(),
underline: Default::default(),
};
let text = "aa bbb cccc ddddd eeee";
let line = text_layout_cache.layout_str(
text,
16.0,
&[(4, normal), (5, bold), (6, normal), (1, bold), (7, normal)],
);
let mut wrapper = LineWrapper::new(font_id, 16., font_system);
assert_eq!(
wrapper
.wrap_shaped_line(text, &line, 72.0)
.collect::<Vec<_>>(),
&[
ShapedBoundary {
run_ix: 1,
glyph_ix: 3
},
ShapedBoundary {
run_ix: 2,
glyph_ix: 3
},
ShapedBoundary {
run_ix: 4,
glyph_ix: 2
}
],
);
}
}

View file

@ -0,0 +1,41 @@
[package]
name = "gpui_mac"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
resvg = "0.14"
gpui = {path = "../gpui"}
usvg = { version = "0.14", features = [] }
tiny-skia = "0.5"
uuid = { version = "1.1.2", features = ["v4"] }
time.workspace = true
etagere = "0.2"
ordered-float.workspace = true
postage.workspace = true
smol.workspace = true
ctor.workspace = true
parking_lot.workspace = true
collections = { path = "../collections" }
async-task = "4.0.3"
pathfinder_geometry = "0.5"
[target.'cfg(target_os = "macos")'.dependencies]
media = { path = "../media" }
anyhow.workspace = true
block = "0.1"
cocoa = "0.24"
core-foundation = { version = "0.9.3", features = ["with-uuid"] }
core-graphics = "0.22.3"
core-text = "19.2"
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "b2f77d56f450338aa4f7dd2f0197d8c9acb0cf18" }
foreign-types = "0.3"
log.workspace = true
metal = "0.21.0"
objc = "0.2"
[build-dependencies]
bindgen = "0.65.1"
cc = "1.0.67"

View file

@ -12,10 +12,10 @@ fn main() {
fn generate_dispatch_bindings() {
println!("cargo:rustc-link-lib=framework=System");
println!("cargo:rerun-if-changed=src/platform/mac/dispatch.h");
println!("cargo:rerun-if-changed=src/dispatch.h");
let bindings = bindgen::Builder::default()
.header("src/platform/mac/dispatch.h")
.header("src/dispatch.h")
.allowlist_var("_dispatch_main_q")
.allowlist_function("dispatch_async_f")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
@ -29,10 +29,10 @@ fn generate_dispatch_bindings() {
.expect("couldn't write dispatch bindings");
}
const SHADER_HEADER_PATH: &str = "./src/platform/mac/shaders/shaders.h";
const SHADER_HEADER_PATH: &str = "./src/shaders/shaders.h";
fn compile_metal_shaders() {
let shader_path = "./src/platform/mac/shaders/shaders.metal";
let shader_path = "./src/shaders/shaders.metal";
let air_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.air");
let metallib_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.metallib");

View file

@ -1,30 +1,33 @@
use std::ffi::CStr;
use crate::platform::Appearance;
use cocoa::{
appkit::{NSAppearanceNameVibrantDark, NSAppearanceNameVibrantLight},
base::id,
foundation::NSString,
};
use gpui::platform::Appearance;
use objc::{msg_send, sel, sel_impl};
impl Appearance {
pub unsafe fn from_native(appearance: id) -> Self {
pub trait AppearanceFromNative {
unsafe fn from_native(appearance: id) -> Self;
}
impl AppearanceFromNative for Appearance {
unsafe fn from_native(appearance: id) -> Appearance {
let name: id = msg_send![appearance, name];
if name == NSAppearanceNameVibrantLight {
Self::VibrantLight
Appearance::VibrantLight
} else if name == NSAppearanceNameVibrantDark {
Self::VibrantDark
Appearance::VibrantDark
} else if name == NSAppearanceNameAqua {
Self::Light
Appearance::Light
} else if name == NSAppearanceNameDarkAqua {
Self::Dark
Appearance::Dark
} else {
println!(
"unknown appearance: {:?}",
CStr::from_ptr(name.UTF8String())
);
Self::Light
Appearance::Light
}
}
}

View file

@ -1,9 +1,9 @@
use crate::geometry::{
use etagere::BucketedAtlasAllocator;
use foreign_types::ForeignType;
use gpui::geometry::{
rect::RectI,
vector::{vec2i, Vector2I},
};
use etagere::BucketedAtlasAllocator;
use foreign_types::ForeignType;
use log::warn;
use metal::{Device, TextureDescriptor};
use objc::{msg_send, sel, sel_impl};

View file

@ -10,7 +10,7 @@ use objc::{
};
use std::ffi::c_void;
use crate::platform;
use gpui::platform;
include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));

View file

@ -1,12 +1,3 @@
use crate::{
geometry::vector::vec2f,
keymap_matcher::Keystroke,
platform::{
Event, KeyDownEvent, KeyUpEvent, Modifiers, ModifiersChangedEvent, MouseButton,
MouseButtonEvent, MouseExitedEvent, MouseMovedEvent, NavigationDirection, ScrollDelta,
ScrollWheelEvent, TouchPhase,
},
};
use cocoa::{
appkit::{NSEvent, NSEventModifierFlags, NSEventPhase, NSEventType},
base::{id, YES},
@ -18,6 +9,15 @@ use core_graphics::{
};
use ctor::ctor;
use foreign_types::ForeignType;
use gpui::{
geometry::vector::vec2f,
keymap_matcher::Keystroke,
platform::{
Event, KeyDownEvent, KeyUpEvent, Modifiers, ModifiersChangedEvent, MouseButton,
MouseButtonEvent, MouseExitedEvent, MouseMovedEvent, NavigationDirection, ScrollDelta,
ScrollWheelEvent, TouchPhase,
},
};
use objc::{class, msg_send, sel, sel_impl};
use std::{borrow::Cow, ffi::CStr, mem, os::raw::c_char, ptr};
@ -86,8 +86,11 @@ unsafe fn read_modifiers(native_event: id) -> Modifiers {
}
}
impl Event {
pub unsafe fn from_native(native_event: id, window_height: Option<f32>) -> Option<Self> {
pub trait EventFromNative {
unsafe fn from_native(native_event: id, window_height: Option<f32>) -> Option<Event>;
}
impl EventFromNative for Event {
unsafe fn from_native(native_event: id, window_height: Option<f32>) -> Option<Event> {
let event_type = native_event.eventType();
// Filter out event types that aren't in the NSEventType enum.

View file

@ -1,15 +1,5 @@
mod open_type;
use crate::{
fonts::{Features, FontId, GlyphId, Metrics, Properties},
geometry::{
rect::{RectF, RectI},
transform2d::Transform2F,
vector::{vec2f, Vector2F},
},
platform::{self, RasterizationOptions},
text_layout::{Glyph, LineLayout, Run, RunStyle},
};
use cocoa::appkit::{CGFloat, CGPoint};
use collections::HashMap;
use core_foundation::{
@ -27,6 +17,16 @@ use core_text::{font::CTFont, line::CTLine, string_attributes::kCTFontAttributeN
use font_kit::{
handle::Handle, hinting::HintingOptions, source::SystemSource, sources::mem::MemSource,
};
use gpui::{
fonts::{Features, FontId, GlyphId, Metrics, Properties},
geometry::{
rect::{RectF, RectI},
transform2d::Transform2F,
vector::{vec2f, Vector2F},
},
platform::{self, RasterizationOptions},
text_layout::{Glyph, LineLayout, Run, RunStyle},
};
use parking_lot::RwLock;
use std::{cell::RefCell, char, cmp, convert::TryFrom, ffi::c_void, sync::Arc};
@ -506,8 +506,8 @@ extern "C" {
#[cfg(test)]
mod tests {
use super::*;
use crate::AppContext;
use font_kit::properties::{Style, Weight};
use gpui::AppContext;
use platform::FontSystem as _;
#[crate::test(self, retries = 5)]
@ -668,4 +668,39 @@ mod tests {
// There's no glyph for \u{feff}
assert_eq!(layout.runs[0].glyphs[1].id, 69); // b
}
#[test]
fn test_select_font() {
let platform = test::platform(FontSystem::new());
let fonts = FontCache::new(platform.fonts());
let arial = fonts
.load_family(
&["Arial"],
&Features {
calt: Some(false),
..Default::default()
},
)
.unwrap();
let arial_regular = fonts.select_font(arial, &Properties::new()).unwrap();
let arial_italic = fonts
.select_font(arial, Properties::new().style(Style::Italic))
.unwrap();
let arial_bold = fonts
.select_font(arial, Properties::new().weight(Weight::BOLD))
.unwrap();
assert_ne!(arial_regular, arial_italic);
assert_ne!(arial_regular, arial_bold);
assert_ne!(arial_italic, arial_bold);
let arial_with_calt = fonts
.load_family(
&["Arial"],
&Features {
calt: Some(true),
..Default::default()
},
)
.unwrap();
assert_ne!(arial_with_calt, arial);
}
}

View file

@ -2,7 +2,6 @@
use std::ptr;
use crate::fonts::Features;
use cocoa::appkit::CGFloat;
use core_foundation::{base::TCFType, number::CFNumber};
use core_graphics::geometry::CGAffineTransform;
@ -13,6 +12,7 @@ use core_text::{
},
};
use font_kit::font::Font;
use gpui::fonts::Features;
const kCaseSensitiveLayoutOffSelector: i32 = 1;
const kCaseSensitiveLayoutOnSelector: i32 = 0;

View file

@ -1,12 +1,12 @@
use super::atlas::{AllocId, AtlasAllocator};
use crate::{
use anyhow::anyhow;
use gpui::{
fonts::{FontId, GlyphId},
geometry::{rect::RectI, vector::Vector2I},
platform::{FontSystem, RasterizationOptions},
scene::ImageGlyph,
ImageData,
};
use anyhow::anyhow;
use metal::{MTLPixelFormat, TextureDescriptor, TextureRef};
use ordered_float::OrderedFloat;
use std::{collections::HashMap, mem, sync::Arc};

View file

@ -10,6 +10,7 @@ mod renderer;
mod screen;
mod sprite_cache;
mod status_item;
mod window;
use cocoa::{
@ -23,15 +24,15 @@ pub use renderer::Surface;
use std::{ops::Range, rc::Rc, sync::Arc};
use window::MacWindow;
use crate::executor;
use gpui::executor;
pub(crate) fn platform() -> Arc<dyn super::Platform> {
pub fn platform() -> Arc<dyn gpui::platform::Platform> {
Arc::new(MacPlatform::new())
}
pub(crate) fn foreground_platform(
pub fn foreground_platform(
foreground: Rc<executor::Foreground>,
) -> Rc<dyn super::ForegroundPlatform> {
) -> Rc<dyn gpui::platform::ForegroundPlatform> {
Rc::new(MacForegroundPlatform::new(foreground))
}
@ -102,3 +103,9 @@ unsafe impl objc::Encode for NSRange {
unsafe fn ns_string(string: &str) -> id {
NSString::alloc(nil).init_str(string).autorelease()
}
#[doc(hidden)]
#[allow(dead_code)]
pub fn font_system() -> Arc<dyn gpui::platform::FontSystem> {
Arc::new(FontSystem::new())
}

View file

@ -1,13 +1,9 @@
use crate::event::EventFromNative;
use super::{
event::key_to_native, screen::Screen, status_item::StatusItem, BoolExt as _, Dispatcher,
FontSystem, MacWindow,
};
use crate::{
executor,
keymap_matcher::KeymapMatcher,
platform::{self, AppVersion, CursorStyle, Event},
Action, AnyWindowHandle, ClipboardItem, Menu, MenuItem,
};
use anyhow::{anyhow, Result};
use block::ConcreteBlock;
use cocoa::{
@ -30,6 +26,12 @@ use core_foundation::{
string::{CFString, CFStringRef},
};
use ctor::ctor;
use gpui::{
executor,
keymap_matcher::KeymapMatcher,
platform::{self, AppVersion, CursorStyle, Event},
Action, AnyWindowHandle, ClipboardItem, Menu, MenuItem,
};
use objc::{
class,
declare::ClassDecl,
@ -235,12 +237,12 @@ impl MacForegroundPlatform {
.find(|binding| binding.action().eq(action.as_ref()))
.map(|binding| binding.keystrokes());
let selector = match os_action {
Some(crate::OsAction::Cut) => selector("cut:"),
Some(crate::OsAction::Copy) => selector("copy:"),
Some(crate::OsAction::Paste) => selector("paste:"),
Some(crate::OsAction::SelectAll) => selector("selectAll:"),
Some(crate::OsAction::Undo) => selector("undo:"),
Some(crate::OsAction::Redo) => selector("redo:"),
Some(gpui::OsAction::Cut) => selector("cut:"),
Some(gpui::OsAction::Copy) => selector("copy:"),
Some(gpui::OsAction::Paste) => selector("paste:"),
Some(gpui::OsAction::SelectAll) => selector("selectAll:"),
Some(gpui::OsAction::Undo) => selector("undo:"),
Some(gpui::OsAction::Redo) => selector("redo:"),
None => selector("handleGPUIMenuItem:"),
};
@ -611,14 +613,14 @@ impl platform::Platform for MacPlatform {
let text_bytes = NSData::dataWithBytes_length_(
nil,
item.text.as_ptr() as *const c_void,
item.text.len() as u64,
item.text().as_ptr() as *const c_void,
item.text().len() as u64,
);
self.pasteboard
.setData_forType(text_bytes, NSPasteboardTypeString);
if let Some(metadata) = item.metadata.as_ref() {
let hash_bytes = ClipboardItem::text_hash(&item.text).to_be_bytes();
if let Some(metadata) = item.raw_metadata() {
let hash_bytes = ClipboardItem::text_hash(&item.text()).to_be_bytes();
let hash_bytes = NSData::dataWithBytes_length_(
nil,
hash_bytes.as_ptr() as *const c_void,
@ -652,21 +654,12 @@ impl platform::Platform for MacPlatform {
if let Some((hash, metadata)) = hash_bytes.zip(metadata_bytes) {
if hash == ClipboardItem::text_hash(&text) {
Some(ClipboardItem {
text,
metadata: Some(metadata),
})
Some(ClipboardItem::new(text).with_metadata(metadata))
} else {
Some(ClipboardItem {
text,
metadata: None,
})
Some(ClipboardItem::new(text))
}
} else {
Some(ClipboardItem {
text,
metadata: None,
})
Some(ClipboardItem::new(text))
}
} else {
None
@ -868,7 +861,7 @@ impl platform::Platform for MacPlatform {
"macOS"
}
fn os_version(&self) -> Result<crate::platform::AppVersion> {
fn os_version(&self) -> Result<gpui::platform::AppVersion> {
unsafe {
let process_info = NSProcessInfo::processInfo(nil);
let version = process_info.operatingSystemVersion();
@ -1096,7 +1089,7 @@ mod security {
#[cfg(test)]
mod tests {
use crate::platform::Platform;
use gpui::platform::Platform;
use super::*;

View file

@ -1,5 +1,14 @@
use super::{atlas::AtlasAllocator, image_cache::ImageCache, sprite_cache::SpriteCache};
use crate::{
use crate::renderer::shaders::ColorToUchar4;
use cocoa::{
base::{NO, YES},
foundation::NSUInteger,
quartzcore::AutoresizingMask,
};
use core_foundation::base::TCFType;
use foreign_types::ForeignTypeRef;
pub use gpui::scene::Surface;
use gpui::{
color::Color,
geometry::{
rect::RectF,
@ -8,13 +17,6 @@ use crate::{
platform,
scene::{Glyph, Icon, Image, ImageGlyph, Layer, Quad, Scene, Shadow, Underline},
};
use cocoa::{
base::{NO, YES},
foundation::NSUInteger,
quartzcore::AutoresizingMask,
};
use core_foundation::base::TCFType;
use foreign_types::ForeignTypeRef;
use log::warn;
use media::core_video::{self, CVMetalTextureCache};
use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
@ -49,11 +51,6 @@ struct PathSprite {
shader_data: shaders::GPUISprite,
}
pub struct Surface {
pub bounds: RectF,
pub image_buffer: core_video::CVImageBuffer,
}
impl Renderer {
pub fn new(is_opaque: bool, fonts: Arc<dyn platform::FontSystem>) -> Self {
const PIXEL_FORMAT: MTLPixelFormat = MTLPixelFormat::BGRA8Unorm;
@ -1221,7 +1218,7 @@ mod shaders {
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use crate::{
use gpui::{
color::Color,
geometry::vector::{Vector2F, Vector2I},
};
@ -1260,9 +1257,11 @@ mod shaders {
self.to_f32().to_float2()
}
}
impl Color {
pub fn to_uchar4(&self) -> vector_uchar4 {
pub trait ColorToUchar4 {
fn to_uchar4(&self) -> vector_uchar4;
}
impl ColorToUchar4 for Color {
fn to_uchar4(&self) -> vector_uchar4 {
let mut vec = self.a as vector_uchar4;
vec <<= 8;
vec |= self.b as vector_uchar4;

View file

@ -1,5 +1,4 @@
use super::ns_string;
use crate::platform;
use cocoa::{
appkit::NSScreen,
base::{id, nil},
@ -10,6 +9,7 @@ use core_foundation::{
uuid::{CFUUIDGetUUIDBytes, CFUUIDRef},
};
use core_graphics::display::CGDirectDisplayID;
use gpui::platform;
use pathfinder_geometry::{rect::RectF, vector::vec2f};
use std::{any::Any, ffi::c_void};
use uuid::Uuid;

View file

@ -1,10 +1,10 @@
use super::atlas::AtlasAllocator;
use crate::{
use collections::hash_map::Entry;
use gpui::{
fonts::{FontId, GlyphId},
geometry::vector::{vec2f, Vector2F, Vector2I},
platform::{self, RasterizationOptions},
};
use collections::hash_map::Entry;
use metal::{MTLPixelFormat, TextureDescriptor};
use ordered_float::OrderedFloat;
use std::{borrow::Cow, collections::HashMap, sync::Arc};

View file

@ -1,15 +1,6 @@
use crate::{
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
platform::{
self,
mac::{platform::NSViewLayerContentsRedrawDuringViewResize, renderer::Renderer},
Event, FontSystem, WindowBounds,
},
Scene,
};
use crate::appearance::AppearanceFromNative;
use crate::event::EventFromNative;
use crate::{platform::NSViewLayerContentsRedrawDuringViewResize, renderer::Renderer};
use cocoa::{
appkit::{NSScreen, NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, NSWindow},
base::{id, nil, YES},
@ -17,6 +8,14 @@ use cocoa::{
};
use ctor::ctor;
use foreign_types::ForeignTypeRef;
use gpui::{
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
platform::{self, Event, FontSystem, WindowBounds},
Scene,
};
use objc::{
class,
declare::ClassDecl,
@ -210,7 +209,7 @@ impl platform::Window for StatusItem {
fn prompt(
&self,
_: crate::platform::PromptLevel,
_: gpui::platform::PromptLevel,
_: &str,
_: &[&str],
) -> postage::oneshot::Receiver<usize> {

View file

@ -1,19 +1,7 @@
use crate::appearance::AppearanceFromNative;
use crate::event::EventFromNative;
use crate::{
executor,
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
keymap_matcher::Keystroke,
platform::{
self,
mac::{
platform::NSViewLayerContentsRedrawDuringViewResize, renderer::Renderer, screen::Screen,
},
Event, InputHandler, KeyDownEvent, Modifiers, ModifiersChangedEvent, MouseButton,
MouseButtonEvent, MouseMovedEvent, Scene, WindowBounds, WindowKind,
},
AnyWindowHandle,
platform::NSViewLayerContentsRedrawDuringViewResize, renderer::Renderer, screen::Screen,
};
use block::ConcreteBlock;
use cocoa::{
@ -28,6 +16,19 @@ use cocoa::{
use core_graphics::display::CGRect;
use ctor::ctor;
use foreign_types::ForeignTypeRef;
use gpui::{
executor,
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
keymap_matcher::Keystroke,
platform::{
self, Event, InputHandler, KeyDownEvent, Modifiers, ModifiersChangedEvent, MouseButton,
MouseButtonEvent, MouseMovedEvent, WindowBounds, WindowKind,
},
AnyWindowHandle, Scene,
};
use objc::{
class,
declare::ClassDecl,

View file

@ -9,8 +9,8 @@ use syn::{
#[proc_macro_attribute]
pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
let mut namespace = format_ident!("gpui");
let namespace = format_ident!("gpui");
let mut platform_namespace = format_ident!("gpui_platform");
let args = syn::parse_macro_input!(args as AttributeArgs);
let mut max_retries = 0;
let mut num_iterations = 1;
@ -23,7 +23,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
NestedMeta::Meta(Meta::Path(name))
if name.get_ident().map_or(false, |n| n == "self") =>
{
namespace = format_ident!("crate");
platform_namespace = format_ident!("crate");
}
NestedMeta::Meta(Meta::NameValue(meta)) => {
let key_name = meta.path.get_ident().map(|i| i.to_string());
@ -157,8 +157,8 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
#[test]
fn #outer_fn_name() {
#inner_fn
#namespace::test::run_test(
#platform_namespace::font_system,
#num_iterations as u64,
#starting_seed as u64,
#max_retries,
@ -234,8 +234,8 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
#[test]
fn #outer_fn_name() {
#inner_fn
#namespace::test::run_test(
#platform_namespace::font_system,
#num_iterations as u64,
#starting_seed as u64,
#max_retries,

View file

@ -0,0 +1,25 @@
[package]
name = "gpui_platform"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
test-support = ["gpui/test-support"]
[dependencies]
gpui = {path = "../gpui", optional = true}
gpui_macros = { path = "../gpui_macros" }
[target.'cfg(target_os = "macos")'.dependencies]
current_platform = {path = "../gpui_mac", package = "gpui_mac"}
[dev-dependencies]
gpui = {path = "../gpui", features = ["test-support"]}
postage.workspace = true
log.workspace = true
itertools = "0.10"
smol.workspace = true
serde.workspace = true
rand.workspace = true

View file

@ -0,0 +1,5 @@
pub use current_platform::*;
#[cfg(any(test, feature = "test-support"))]
mod tests;
#[cfg(any(test, feature = "test-support"))]
use gpui_macros::test;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,67 @@
use gpui::color::Color;
use gpui::elements::{Label, LabelStyle};
use gpui::fonts::{Properties as FontProperties, TextStyle, Weight};
use gpui::text_layout::RunStyle;
use gpui::AppContext;
#[crate::test(self)]
fn test_layout_label_with_highlights(cx: &mut AppContext) {
let default_style = TextStyle::new(
"Menlo",
12.,
Default::default(),
Default::default(),
Default::default(),
Color::black(),
cx.font_cache(),
)
.unwrap();
let highlight_style = TextStyle::new(
"Menlo",
12.,
*FontProperties::new().weight(Weight::BOLD),
Default::default(),
Default::default(),
Color::new(255, 0, 0, 255),
cx.font_cache(),
)
.unwrap();
let label = Label::new(
".αβγδε.ⓐⓑⓒⓓⓔ.abcde.".to_string(),
LabelStyle {
text: default_style.clone(),
highlight_text: Some(highlight_style.clone()),
},
)
.with_highlights(vec![
".α".len(),
".αβ".len(),
".αβγδ".len(),
".αβγδε.ⓐ".len(),
".αβγδε.ⓐⓑ".len(),
]);
let default_run_style = RunStyle {
font_id: default_style.font_id,
color: default_style.color,
underline: default_style.underline,
};
let highlight_run_style = RunStyle {
font_id: highlight_style.font_id,
color: highlight_style.color,
underline: highlight_style.underline,
};
let runs = label.compute_runs();
assert_eq!(
runs.as_slice(),
&[
(".α".len(), default_run_style),
("βγ".len(), highlight_run_style),
("δ".len(), default_run_style),
("ε".len(), highlight_run_style),
(".ⓐ".len(), default_run_style),
("ⓑⓒ".len(), highlight_run_style),
("ⓓⓔ.abcde.".len(), default_run_style),
]
);
}

View file

@ -0,0 +1,380 @@
use gpui::elements::list::ListItem;
use gpui::elements::List;
use gpui::elements::ListOffset;
use gpui::elements::ListState;
use gpui::elements::Orientation;
use gpui::geometry::rect::RectF;
use gpui::geometry::vector::Vector2F;
use gpui::serde_json;
use gpui::AnyElement;
use gpui::AppContext;
use gpui::Element;
use gpui::LayoutContext;
use gpui::SceneBuilder;
use gpui::SizeConstraint;
use gpui::View;
use gpui::ViewContext;
use gpui::{elements::Empty, geometry::vector::vec2f, Entity, PaintContext};
use rand::prelude::*;
use std::cell::RefCell;
use std::env;
use std::ops::Range;
use std::rc::Rc;
#[crate::test(self)]
fn test_layout(cx: &mut AppContext) {
cx.add_window(Default::default(), |cx| {
let mut view = TestView;
let constraint = SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.));
let elements = Rc::new(RefCell::new(vec![(0, 20.), (1, 30.), (2, 100.)]));
let state = ListState::new(elements.borrow().len(), Orientation::Top, 1000.0, {
let elements = elements.clone();
move |_, ix, _| {
let (id, height) = elements.borrow()[ix];
TestElement::new(id, height).into_any()
}
});
let mut list = List::new(state.clone());
let mut new_parents = Default::default();
let mut notify_views_if_parents_change = Default::default();
let mut layout_cx = LayoutContext::new(
cx,
&mut new_parents,
&mut notify_views_if_parents_change,
false,
);
let (size, _) = list.layout(constraint, &mut view, &mut layout_cx);
assert_eq!(size, vec2f(100., 40.));
assert_eq!(
state.0.borrow().items.summary().clone(),
ListItemSummary {
count: 3,
rendered_count: 3,
unrendered_count: 0,
height: 150.
}
);
state.0.borrow_mut().scroll(
&ListOffset {
item_ix: 0,
offset_in_item: 0.,
},
40.,
vec2f(0., -54.),
true,
&mut view,
cx,
);
let mut layout_cx = LayoutContext::new(
cx,
&mut new_parents,
&mut notify_views_if_parents_change,
false,
);
let (_, logical_scroll_top) = list.layout(constraint, &mut view, &mut layout_cx);
assert_eq!(
logical_scroll_top,
ListOffset {
item_ix: 2,
offset_in_item: 4.
}
);
assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 54.);
elements.borrow_mut().splice(1..2, vec![(3, 40.), (4, 50.)]);
elements.borrow_mut().push((5, 60.));
state.splice(1..2, 2);
state.splice(4..4, 1);
assert_eq!(
state.0.borrow().items.summary().clone(),
ListItemSummary {
count: 5,
rendered_count: 2,
unrendered_count: 3,
height: 120.
}
);
let mut layout_cx = LayoutContext::new(
cx,
&mut new_parents,
&mut notify_views_if_parents_change,
false,
);
let (size, logical_scroll_top) = list.layout(constraint, &mut view, &mut layout_cx);
assert_eq!(size, vec2f(100., 40.));
assert_eq!(
state.0.borrow().items.summary().clone(),
ListItemSummary {
count: 5,
rendered_count: 5,
unrendered_count: 0,
height: 270.
}
);
assert_eq!(
logical_scroll_top,
ListOffset {
item_ix: 3,
offset_in_item: 4.
}
);
assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 114.);
view
});
}
#[crate::test(self, iterations = 10)]
fn test_random(cx: &mut AppContext, mut rng: StdRng) {
let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
cx.add_window(Default::default(), |cx| {
let mut view = TestView;
let mut next_id = 0;
let elements = Rc::new(RefCell::new(
(0..rng.gen_range(0..=20))
.map(|_| {
let id = next_id;
next_id += 1;
(id, rng.gen_range(0..=200) as f32 / 2.0)
})
.collect::<Vec<_>>(),
));
let orientation = *[Orientation::Top, Orientation::Bottom]
.choose(&mut rng)
.unwrap();
let overdraw = rng.gen_range(1..=100) as f32;
let state = ListState::new(elements.borrow().len(), orientation, overdraw, {
let elements = elements.clone();
move |_, ix, _| {
let (id, height) = elements.borrow()[ix];
TestElement::new(id, height).into_any()
}
});
let mut width = rng.gen_range(0..=2000) as f32 / 2.;
let mut height = rng.gen_range(0..=2000) as f32 / 2.;
log::info!("orientation: {:?}", orientation);
log::info!("overdraw: {}", overdraw);
log::info!("elements: {:?}", elements.borrow());
log::info!("size: ({:?}, {:?})", width, height);
log::info!("==================");
let mut last_logical_scroll_top = None;
for _ in 0..operations {
match rng.gen_range(0..=100) {
0..=29 if last_logical_scroll_top.is_some() => {
let delta = vec2f(0., rng.gen_range(-overdraw..=overdraw));
log::info!(
"Scrolling by {:?}, previous scroll top: {:?}",
delta,
last_logical_scroll_top.unwrap()
);
state.0.borrow_mut().scroll(
last_logical_scroll_top.as_ref().unwrap(),
height,
delta,
true,
&mut view,
cx,
);
}
30..=34 => {
width = rng.gen_range(0..=2000) as f32 / 2.;
log::info!("changing width: {:?}", width);
}
35..=54 => {
height = rng.gen_range(0..=1000) as f32 / 2.;
log::info!("changing height: {:?}", height);
}
_ => {
let mut elements = elements.borrow_mut();
let end_ix = rng.gen_range(0..=elements.len());
let start_ix = rng.gen_range(0..=end_ix);
let new_elements = (0..rng.gen_range(0..10))
.map(|_| {
let id = next_id;
next_id += 1;
(id, rng.gen_range(0..=200) as f32 / 2.)
})
.collect::<Vec<_>>();
log::info!("splice({:?}, {:?})", start_ix..end_ix, new_elements);
state.splice(start_ix..end_ix, new_elements.len());
elements.splice(start_ix..end_ix, new_elements);
for (ix, item) in state.0.borrow().items.cursor::<()>().enumerate() {
if let ListItem::Rendered(element) = item {
let (expected_id, _) = elements[ix];
element.borrow().with_metadata(|metadata: Option<&usize>| {
assert_eq!(*metadata.unwrap(), expected_id);
});
}
}
}
}
let mut list = List::new(state.clone());
let window_size = vec2f(width, height);
let mut new_parents = Default::default();
let mut notify_views_if_parents_change = Default::default();
let mut layout_cx = LayoutContext::new(
cx,
&mut new_parents,
&mut notify_views_if_parents_change,
false,
);
let (size, logical_scroll_top) = list.layout(
SizeConstraint::new(vec2f(0., 0.), window_size),
&mut view,
&mut layout_cx,
);
assert_eq!(size, window_size);
last_logical_scroll_top = Some(logical_scroll_top);
let state = state.0.borrow();
log::info!("items {:?}", state.items.items(&()));
let scroll_top = state.scroll_top(&logical_scroll_top);
let rendered_top = (scroll_top - overdraw).max(0.);
let rendered_bottom = scroll_top + height + overdraw;
let mut item_top = 0.;
log::info!(
"rendered top {:?}, rendered bottom {:?}, scroll top {:?}",
rendered_top,
rendered_bottom,
scroll_top,
);
let mut first_rendered_element_top = None;
let mut last_rendered_element_bottom = None;
assert_eq!(state.items.summary().count, elements.borrow().len());
for (ix, item) in state.items.cursor::<()>().enumerate() {
match item {
ListItem::Unrendered => {
let item_bottom = item_top;
assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
item_top = item_bottom;
}
ListItem::Removed(height) => {
let (id, expected_height) = elements.borrow()[ix];
assert_eq!(
*height, expected_height,
"element {} height didn't match",
id
);
let item_bottom = item_top + height;
assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
item_top = item_bottom;
}
ListItem::Rendered(element) => {
let (expected_id, expected_height) = elements.borrow()[ix];
let element = element.borrow();
element.with_metadata(|metadata: Option<&usize>| {
assert_eq!(*metadata.unwrap(), expected_id);
});
assert_eq!(element.size().y(), expected_height);
let item_bottom = item_top + element.size().y();
first_rendered_element_top.get_or_insert(item_top);
last_rendered_element_bottom = Some(item_bottom);
assert!(item_bottom > rendered_top || item_top < rendered_bottom);
item_top = item_bottom;
}
}
}
match orientation {
Orientation::Top => {
if let Some(first_rendered_element_top) = first_rendered_element_top {
assert!(first_rendered_element_top <= scroll_top);
}
}
Orientation::Bottom => {
if let Some(last_rendered_element_bottom) = last_rendered_element_bottom {
assert!(last_rendered_element_bottom >= scroll_top + height);
}
}
}
}
view
});
}
struct TestView;
impl Entity for TestView {
type Event = ();
}
impl View for TestView {
fn ui_name() -> &'static str {
"TestView"
}
fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
Empty::new().into_any()
}
}
struct TestElement {
id: usize,
size: Vector2F,
}
impl TestElement {
fn new(id: usize, height: f32) -> Self {
Self {
id,
size: vec2f(100., height),
}
}
}
impl<V: 'static> Element<V> for TestElement {
type LayoutState = ();
type PaintState = ();
fn layout(&mut self, _: SizeConstraint, _: &mut V, _: &mut LayoutContext<V>) -> (Vector2F, ()) {
(self.size, ())
}
fn paint(
&mut self,
_: &mut SceneBuilder,
_: RectF,
_: RectF,
_: &mut (),
_: &mut V,
_: &mut PaintContext<V>,
) {
unimplemented!()
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &V,
_: &ViewContext<V>,
) -> Option<RectF> {
unimplemented!()
}
fn debug(&self, _: RectF, _: &(), _: &(), _: &V, _: &ViewContext<V>) -> serde_json::Value {
self.id.into()
}
fn metadata(&self) -> Option<&dyn std::any::Any> {
Some(&self.id)
}
}

View file

@ -0,0 +1,3 @@
mod label;
mod list;
mod text;

View file

@ -0,0 +1,48 @@
use gpui::elements::Text;
use gpui::geometry::vector::vec2f;
use gpui::Element;
use gpui::LayoutContext;
use gpui::SizeConstraint;
use gpui::{elements::Empty, fonts, AnyElement, AppContext, Entity, View, ViewContext};
#[crate::test(self)]
fn test_soft_wrapping_with_carriage_returns(cx: &mut AppContext) {
cx.add_window(Default::default(), |cx| {
let mut view = TestView;
fonts::with_font_cache(cx.font_cache().clone(), || {
let mut text = Text::new("Hello\r\n", Default::default()).with_soft_wrap(true);
let mut new_parents = Default::default();
let mut notify_views_if_parents_change = Default::default();
let mut layout_cx = LayoutContext::new(
cx,
&mut new_parents,
&mut notify_views_if_parents_change,
false,
);
let (_, state) = text.layout(
SizeConstraint::new(Default::default(), vec2f(f32::INFINITY, f32::INFINITY)),
&mut view,
&mut layout_cx,
);
assert_eq!(state.shaped_lines.len(), 2);
assert_eq!(state.wrap_boundaries.len(), 2);
});
view
});
}
struct TestView;
impl Entity for TestView {
type Event = ();
}
impl View for TestView {
fn ui_name() -> &'static str {
"TestView"
}
fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
Empty::new().into_any()
}
}

View file

@ -0,0 +1,125 @@
mod app;
mod elements;
use gpui::fonts::{Properties, Weight};
use gpui::text_layout::*;
#[crate::test]
fn test_wrap_line(cx: &mut gpui::AppContext) {
let font_cache = cx.font_cache().clone();
let font_system = cx.platform().fonts();
let family = font_cache
.load_family(&["Courier"], &Default::default())
.unwrap();
let font_id = font_cache.select_font(family, &Default::default()).unwrap();
let mut wrapper = LineWrapper::new(font_id, 16., font_system);
assert_eq!(
wrapper
.wrap_line("aa bbb cccc ddddd eeee", 72.0)
.collect::<Vec<_>>(),
&[
Boundary::new(7, 0),
Boundary::new(12, 0),
Boundary::new(18, 0)
],
);
assert_eq!(
wrapper
.wrap_line("aaa aaaaaaaaaaaaaaaaaa", 72.0)
.collect::<Vec<_>>(),
&[
Boundary::new(4, 0),
Boundary::new(11, 0),
Boundary::new(18, 0)
],
);
assert_eq!(
wrapper.wrap_line(" aaaaaaa", 72.).collect::<Vec<_>>(),
&[
Boundary::new(7, 5),
Boundary::new(9, 5),
Boundary::new(11, 5),
]
);
assert_eq!(
wrapper
.wrap_line(" ", 72.)
.collect::<Vec<_>>(),
&[
Boundary::new(7, 0),
Boundary::new(14, 0),
Boundary::new(21, 0)
]
);
assert_eq!(
wrapper
.wrap_line(" aaaaaaaaaaaaaa", 72.)
.collect::<Vec<_>>(),
&[
Boundary::new(7, 0),
Boundary::new(14, 3),
Boundary::new(18, 3),
Boundary::new(22, 3),
]
);
}
#[crate::test(retries = 5)]
fn test_wrap_shaped_line(cx: &mut gpui::AppContext) {
// This is failing intermittently on CI and we don't have time to figure it out
let font_cache = cx.font_cache().clone();
let font_system = cx.platform().fonts();
let text_layout_cache = TextLayoutCache::new(font_system.clone());
let family = font_cache
.load_family(&["Helvetica"], &Default::default())
.unwrap();
let font_id = font_cache.select_font(family, &Default::default()).unwrap();
let normal = RunStyle {
font_id,
color: Default::default(),
underline: Default::default(),
};
let bold = RunStyle {
font_id: font_cache
.select_font(
family,
&Properties {
weight: Weight::BOLD,
..Default::default()
},
)
.unwrap(),
color: Default::default(),
underline: Default::default(),
};
let text = "aa bbb cccc ddddd eeee";
let line = text_layout_cache.layout_str(
text,
16.0,
&[(4, normal), (5, bold), (6, normal), (1, bold), (7, normal)],
);
let mut wrapper = LineWrapper::new(font_id, 16., font_system);
assert_eq!(
wrapper
.wrap_shaped_line(text, &line, 72.0)
.collect::<Vec<_>>(),
&[
ShapedBoundary {
run_ix: 1,
glyph_ix: 3
},
ShapedBoundary {
run_ix: 2,
glyph_ix: 3
},
ShapedBoundary {
run_ix: 4,
glyph_ix: 2
}
],
);
}

View file

@ -17,6 +17,7 @@ test-support = [
"async-trait",
"collections/test-support",
"gpui/test-support",
"gpui_platform",
"live_kit_server",
"nanoid",
]
@ -24,6 +25,7 @@ test-support = [
[dependencies]
collections = { path = "../collections", optional = true }
gpui = { path = "../gpui", optional = true }
gpui_platform = {path = "../gpui_platform", optional = true}
live_kit_server = { path = "../live_kit_server", optional = true }
media = { path = "../media" }

View file

@ -19,3 +19,4 @@ util = { path = "../util" }
rand.workspace = true
util = { path = "../util", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
gpui_platform = {path = "../gpui_platform" }

View file

@ -10,12 +10,13 @@ path = "src/rpc.rs"
doctest = false
[features]
test-support = ["collections/test-support", "gpui/test-support"]
test-support = ["collections/test-support", "gpui/test-support", "gpui_platform"]
[dependencies]
clock = { path = "../clock" }
collections = { path = "../collections" }
gpui = { path = "../gpui", optional = true }
gpui_platform = {path = "../gpui_platform", optional = true}
util = { path = "../util" }
anyhow.workspace = true
async-lock = "2.4"

View file

@ -9,11 +9,12 @@ path = "src/settings.rs"
doctest = false
[features]
test-support = ["gpui/test-support", "fs/test-support"]
test-support = ["gpui/test-support", "fs/test-support", "gpui_platform"]
[dependencies]
collections = { path = "../collections" }
gpui = { path = "../gpui" }
gpui_platform = { path = "../gpui_platform", optional = true}
sqlez = { path = "../sqlez" }
fs = { path = "../fs" }
feature_flags = { path = "../feature_flags" }

View file

@ -7,6 +7,7 @@ publish = false
[features]
test-support = [
"gpui/test-support",
"gpui_platform",
"fs/test-support",
"settings/test-support"
]
@ -17,6 +18,7 @@ doctest = false
[dependencies]
gpui = { path = "../gpui" }
gpui_platform = {path = "../gpui_platform", optional = true}
fs = { path = "../fs" }
settings = { path = "../settings" }
util = { path = "../util" }

View file

@ -28,6 +28,7 @@ context_menu = { path = "../context_menu" }
drag_and_drop = { path = "../drag_and_drop" }
fs = { path = "../fs" }
gpui = { path = "../gpui" }
gpui_platform = { path = "../gpui_platform", optional = true }
install_cli = { path = "../install_cli" }
language = { path = "../language" }
menu = { path = "../menu" }

View file

@ -80,7 +80,7 @@ impl View for SharedScreen {
vec2f(frame.width() as f32, frame.height() as f32),
);
let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
scene.push_surface(gpui::platform::mac::Surface {
scene.push_surface(gpui::scene::Surface {
bounds: RectF::new(origin, size),
image_buffer: frame.image(),
});

View file

@ -72,6 +72,7 @@ vim = { path = "../vim" }
workspace = { path = "../workspace" }
welcome = { path = "../welcome" }
zed-actions = {path = "../zed-actions"}
gpui_platform = {path = "../gpui_platform"}
anyhow.workspace = true
async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] }
async-tar = "0.4.2"

View file

@ -53,6 +53,7 @@ use uuid::Uuid;
use welcome::{show_welcome_experience, FIRST_OPEN};
use fs::RealFs;
use gpui_platform::{foreground_platform, platform};
use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt};
use workspace::AppState;
use zed::{
@ -60,7 +61,6 @@ use zed::{
build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus,
only_instance::{ensure_only_instance, IsOnlyInstance},
};
fn main() {
let http = http::client();
init_paths();
@ -71,7 +71,11 @@ fn main() {
}
log::info!("========== starting zed ==========");
let mut app = gpui::App::new(Assets).unwrap();
let platform = platform();
let foreground =
std::rc::Rc::new(gpui::executor::Foreground::platform(platform.dispatcher()).unwrap());
let fplatform = foreground_platform(foreground);
let mut app = gpui::App::new(Assets, platform, fplatform).unwrap();
let installation_id = app.background().block(installation_id()).ok();
init_panic_hook(&app, installation_id.clone());