Compare commits
20 commits
main
...
gpui_extra
Author | SHA1 | Date | |
---|---|---|---|
![]() |
62949d20a7 | ||
![]() |
110bb84261 | ||
![]() |
576849c98a | ||
![]() |
f16105f391 | ||
![]() |
ac329e40dc | ||
![]() |
a9db466e67 | ||
![]() |
6f32431d33 | ||
![]() |
8342803ba5 | ||
![]() |
d033475565 | ||
![]() |
2a350e91b2 | ||
![]() |
fe17505100 | ||
![]() |
79179e8fff | ||
![]() |
95b4bd467b | ||
![]() |
978d074b1d | ||
![]() |
d0f287772b | ||
![]() |
cfecbc5522 | ||
![]() |
94cf1c3336 | ||
![]() |
f3b76e0571 | ||
![]() |
b4cba64fd6 | ||
![]() |
7516e91a56 |
61 changed files with 3108 additions and 2966 deletions
73
Cargo.lock
generated
73
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
41
crates/gpui_mac/Cargo.toml
Normal file
41
crates/gpui_mac/Cargo.toml
Normal 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"
|
|
@ -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");
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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};
|
|
@ -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"));
|
||||
|
|
@ -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.
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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};
|
|
@ -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())
|
||||
}
|
|
@ -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::*;
|
||||
|
|
@ -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;
|
|
@ -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;
|
|
@ -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};
|
|
@ -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> {
|
|
@ -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,
|
|
@ -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,
|
||||
|
|
25
crates/gpui_platform/Cargo.toml
Normal file
25
crates/gpui_platform/Cargo.toml
Normal 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
|
5
crates/gpui_platform/src/lib.rs
Normal file
5
crates/gpui_platform/src/lib.rs
Normal 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;
|
2103
crates/gpui_platform/src/tests/app.rs
Normal file
2103
crates/gpui_platform/src/tests/app.rs
Normal file
File diff suppressed because it is too large
Load diff
67
crates/gpui_platform/src/tests/elements/label.rs
Normal file
67
crates/gpui_platform/src/tests/elements/label.rs
Normal 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),
|
||||
]
|
||||
);
|
||||
}
|
380
crates/gpui_platform/src/tests/elements/list.rs
Normal file
380
crates/gpui_platform/src/tests/elements/list.rs
Normal 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)
|
||||
}
|
||||
}
|
3
crates/gpui_platform/src/tests/elements/mod.rs
Normal file
3
crates/gpui_platform/src/tests/elements/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
mod label;
|
||||
mod list;
|
||||
mod text;
|
48
crates/gpui_platform/src/tests/elements/text.rs
Normal file
48
crates/gpui_platform/src/tests/elements/text.rs
Normal 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()
|
||||
}
|
||||
}
|
125
crates/gpui_platform/src/tests/mod.rs
Normal file
125
crates/gpui_platform/src/tests/mod.rs
Normal 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
|
||||
}
|
||||
],
|
||||
);
|
||||
}
|
|
@ -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" }
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue