Checkpoint

This commit is contained in:
Nathan Sobo 2023-09-19 20:55:13 -06:00
parent 8406c0d9a3
commit 37d0f06e07
24 changed files with 5138 additions and 54 deletions

42
Cargo.lock generated
View file

@ -3345,23 +3345,61 @@ name = "gpui3"
version = "0.1.0"
dependencies = [
"anyhow",
"async-task",
"backtrace",
"bindgen 0.65.1",
"block",
"bytemuck",
"cocoa",
"collections",
"core-foundation",
"core-graphics",
"core-text",
"ctor",
"derive_more",
"dhat",
"env_logger 0.9.3",
"etagere",
"font-kit",
"itertools 0.11.0",
"foreign-types 0.3.2",
"futures 0.3.28",
"gpui_macros",
"image",
"itertools 0.10.5",
"lazy_static",
"log",
"media",
"num_cpus",
"objc",
"ordered-float",
"parking",
"parking_lot 0.11.2",
"plane-split",
"png",
"postage",
"rand 0.8.5",
"raw-window-handle",
"refineable",
"rust-embed",
"resvg",
"schemars",
"seahash",
"serde",
"serde_derive",
"serde_json",
"simplelog",
"slotmap",
"smallvec",
"smol",
"sqlez",
"sum_tree",
"taffy",
"thiserror",
"time 0.3.27",
"tiny-skia",
"usvg",
"util",
"uuid 1.4.1",
"waker-fn",
"wgpu",
]

View file

@ -2,30 +2,83 @@
name = "gpui3"
version = "0.1.0"
edition = "2021"
authors = ["Nathan Sobo <nathansobo@gmail.com>"]
description = "The next version of Zed's GPU-accelerated UI framework"
publish = false
[features]
test = []
test-support = ["backtrace", "dhat", "env_logger", "collections/test-support"]
[lib]
path = "src/gpui3.rs"
doctest = false
[dependencies]
anyhow.workspace = true
bytemuck = "1.14.0"
derive_more.workspace = true
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "b2f77d56f450338aa4f7dd2f0197d8c9acb0cf18" }
itertools = "0.11.0"
log.workspace = true
parking_lot.workspace = true
plane-split = "0.18.0"
raw-window-handle = "0.5.2"
refineable = { path = "../refineable" }
rust-embed.workspace = true
schemars = "0.8"
serde.workspace = true
simplelog = "0.9"
slotmap = "1.0.6"
smallvec.workspace = true
taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e" }
collections = { path = "../collections" }
gpui_macros = { path = "../gpui_macros" }
util = { path = "../util" }
sum_tree = { path = "../sum_tree" }
sqlez = { path = "../sqlez" }
async-task = "4.0.3"
backtrace = { version = "0.3", optional = true }
ctor.workspace = true
derive_more.workspace = true
dhat = { version = "0.3", optional = true }
env_logger = { version = "0.9", optional = true }
etagere = "0.2"
futures.workspace = true
image = "0.23"
itertools = "0.10"
lazy_static.workspace = true
log.workspace = true
num_cpus = "1.13"
ordered-float.workspace = true
parking = "2.0.0"
parking_lot.workspace = true
postage.workspace = true
rand.workspace = true
refineable.workspace = true
resvg = "0.14"
seahash = "4.1"
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
smallvec.workspace = true
smol.workspace = true
taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e" }
thiserror.workspace = true
time.workspace = true
tiny-skia = "0.5"
usvg = { version = "0.14", features = [] }
uuid = { version = "1.1.2", features = ["v4"] }
waker-fn = "1.1.0"
slotmap = "1.0.6"
bytemuck = "1.14.0"
schemars.workspace = true
raw-window-handle = "0.5.2"
wgpu = "0.17.0"
plane-split = "0.18.0"
[dev-dependencies]
backtrace = "0.3"
collections = { path = "../collections", features = ["test-support"] }
dhat = "0.3"
env_logger.workspace = true
png = "0.16"
simplelog = "0.9"
[build-dependencies]
bindgen = "0.65.1"
[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
objc = "0.2"

24
crates/gpui3/build.rs Normal file
View file

@ -0,0 +1,24 @@
use std::{env, path::PathBuf};
fn main() {
generate_dispatch_bindings();
}
fn generate_dispatch_bindings() {
println!("cargo:rustc-link-lib=framework=System");
println!("cargo:rerun-if-changed=src/platform/mac/dispatch.h");
let bindings = bindgen::Builder::default()
.header("src/platform/mac/dispatch.h")
.allowlist_var("_dispatch_main_q")
.allowlist_function("dispatch_async_f")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.layout_tests(false)
.generate()
.expect("unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("dispatch_sys.rs"))
.expect("couldn't write dispatch bindings");
}

View file

@ -51,9 +51,9 @@ impl AppContext {
) -> WindowHandle<S> {
let id = self.windows.insert(None);
let handle = WindowHandle::new(id);
self.platform.open_window(handle.into(), options);
let platform_window = self.platform.open_window(handle.into(), options);
let mut window = Window::new(id);
let mut window = Window::new(id, platform_window);
let root_view = build_root_view(&mut WindowContext::mutable(self, &mut window));
window.root_view.replace(Box::new(root_view));

1030
crates/gpui3/src/executor.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
use crate::{px, Bounds, LineWrapper, Pixels, PlatformFontSystem, Result, Size};
use crate::{px, Bounds, LineWrapper, Pixels, PlatformTextSystem, Result, Size};
use anyhow::anyhow;
pub use font_kit::properties::{
Properties as FontProperties, Stretch as FontStretch, Style as FontStyle, Weight as FontWeight,
@ -79,7 +79,7 @@ struct Family {
pub struct FontCache(RwLock<FontCacheState>);
pub struct FontCacheState {
font_system: Arc<dyn PlatformFontSystem>,
font_system: Arc<dyn PlatformTextSystem>,
families: Vec<Family>,
default_family: Option<FontFamilyId>,
font_selections: HashMap<FontFamilyId, HashMap<(FontWeight, FontStyle), FontId>>,
@ -90,7 +90,7 @@ pub struct FontCacheState {
unsafe impl Send for FontCache {}
impl FontCache {
pub fn new(fonts: Arc<dyn PlatformFontSystem>) -> Self {
pub fn new(fonts: Arc<dyn PlatformTextSystem>) -> Self {
Self(RwLock::new(FontCacheState {
font_system: fonts,
families: Default::default(),

View file

@ -20,6 +20,13 @@ impl<T: Clone + Debug> Point<T> {
pub fn new(x: T, y: T) -> Self {
Self { x, y }
}
pub fn map<U: Clone + Debug, F: Fn(T) -> U>(&self, f: F) -> Point<U> {
Point {
x: f(self.x.clone()),
y: f(self.y.clone()),
}
}
}
impl<T: Clone + Debug> Clone for Point<T> {
@ -42,6 +49,10 @@ pub struct Size<T: Clone + Debug> {
pub height: T,
}
pub fn size<T: Clone + Debug>(width: T, height: T) -> Size<T> {
Size { width, height }
}
impl Size<Length> {
pub fn full() -> Self {
Self {
@ -157,6 +168,12 @@ impl Edges<Pixels> {
#[repr(transparent)]
pub struct Pixels(pub(crate) f32);
impl From<Pixels> for f64 {
fn from(pixels: Pixels) -> Self {
pixels.0.into()
}
}
impl Mul<f32> for Pixels {
type Output = Pixels;
@ -326,11 +343,11 @@ pub fn relative<T: From<DefiniteLength>>(fraction: f32) -> T {
}
pub fn rems(rems: f32) -> Rems {
Rems(rems).into()
Rems(rems)
}
pub fn px(pixels: f32) -> Pixels {
Pixels(pixels).into()
Pixels(pixels)
}
pub fn auto() -> Length {

View file

@ -2,6 +2,7 @@ mod app;
mod color;
mod element;
mod elements;
mod executor;
mod fonts;
mod geometry;
mod platform;
@ -10,6 +11,7 @@ mod scene;
mod style;
mod taffy;
mod text;
mod util;
mod window;
use anyhow::Result;
@ -17,10 +19,12 @@ pub use app::*;
pub use color::*;
pub use element::*;
pub use elements::*;
pub use executor::*;
pub use fonts::*;
pub use geometry::*;
pub use platform::*;
pub use scene::*;
pub use smol::Timer;
use std::ops::{Deref, DerefMut};
pub use style::*;
pub use taffy::LayoutId;
@ -47,6 +51,12 @@ pub trait Context {
#[derive(Clone, Eq, PartialEq)]
pub struct SharedString(ArcCow<'static, str>);
impl AsRef<str> for SharedString {
fn as_ref(&self) -> &str {
&self.0
}
}
impl std::fmt::Debug for SharedString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
@ -98,7 +108,7 @@ mod tests {
let workspace = cx.entity(|cx| Workspace {
left_panel: collab_panel(cx).into_any(),
});
view(workspace, |workspace, cx| {
view(workspace, |workspace, _cx| {
div().child(workspace.left_panel.clone())
})
}
@ -109,7 +119,7 @@ mod tests {
fn collab_panel(cx: &mut WindowContext) -> View<CollabPanel> {
let panel = cx.entity(|cx| CollabPanel::new(cx));
view(panel, |panel, cx| {
view(panel, |panel, _cx| {
div().child(div()).child(
field(panel.filter_editor.clone()).placeholder_text("Search channels, contacts"),
)
@ -124,14 +134,6 @@ mod tests {
}
}
struct Editor {}
impl Editor {
pub fn new(cx: &mut ViewContext<Self>) -> Self {
Self {}
}
}
#[test]
fn test() {
let mut cx = AppContext::test();

View file

@ -1,17 +1,30 @@
mod events;
mod keystroke;
#[cfg(target_os = "macos")]
mod mac;
#[cfg(any(test, feature = "test"))]
mod test;
use crate::{
AnyWindowHandle, Bounds, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, GlyphId,
LineLayout, Pixels, Point, RunStyle, SharedString,
LineLayout, Pixels, Point, RunStyle, SharedString, Size,
};
use async_task::Runnable;
use futures::channel::oneshot;
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use std::sync::Arc;
use std::{any::Any, fmt::Debug, ops::Range, rc::Rc, sync::Arc};
use uuid::Uuid;
pub use events::*;
pub use keystroke::*;
#[cfg(target_os = "macos")]
pub use mac::*;
#[cfg(any(test, feature = "test"))]
pub use test::*;
pub trait Platform {
fn font_system(&self) -> Arc<dyn PlatformFontSystem>;
fn dispatcher(&self) -> Arc<dyn PlatformDispatcher>;
fn font_system(&self) -> Arc<dyn PlatformTextSystem>;
fn open_window(
&self,
@ -20,9 +33,53 @@ pub trait Platform {
) -> Box<dyn PlatformWindow>;
}
pub trait PlatformWindow: HasRawWindowHandle + HasRawDisplayHandle {}
pub trait PlatformScreen: Debug {
fn as_any(&self) -> &dyn Any;
fn bounds(&self) -> Bounds<Pixels>;
fn content_bounds(&self) -> Bounds<Pixels>;
fn display_uuid(&self) -> Option<Uuid>;
}
pub trait PlatformFontSystem: Send + Sync {
pub trait PlatformWindow: HasRawWindowHandle + HasRawDisplayHandle {
fn bounds(&self) -> WindowBounds;
fn content_size(&self) -> Size<Pixels>;
fn scale_factor(&self) -> f32;
fn titlebar_height(&self) -> Pixels;
fn appearance(&self) -> WindowAppearance;
fn screen(&self) -> Rc<dyn PlatformScreen>;
fn mouse_position(&self) -> Point<Pixels>;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>);
fn prompt(
&self,
level: WindowPromptLevel,
msg: &str,
answers: &[&str],
) -> oneshot::Receiver<usize>;
fn activate(&self);
fn set_title(&mut self, title: &str);
fn set_edited(&mut self, edited: bool);
fn show_character_palette(&self);
fn minimize(&self);
fn zoom(&self);
fn toggle_full_screen(&self);
fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>);
fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>);
fn on_resize(&mut self, callback: Box<dyn FnMut()>);
fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>);
fn on_moved(&mut self, callback: Box<dyn FnMut()>);
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>);
fn on_close(&mut self, callback: Box<dyn FnOnce()>);
fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>);
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
}
pub trait PlatformDispatcher: Send + Sync {
fn is_main_thread(&self) -> bool;
fn run_on_main_thread(&self, task: Runnable);
}
pub trait PlatformTextSystem: Send + Sync {
fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> anyhow::Result<()>;
fn all_families(&self) -> Vec<String>;
fn load_family(&self, name: &str, features: &FontFeatures) -> anyhow::Result<Vec<FontId>>;
@ -59,6 +116,21 @@ pub trait PlatformFontSystem: Send + Sync {
) -> Vec<usize>;
}
pub trait InputHandler {
fn selected_text_range(&self) -> Option<Range<usize>>;
fn marked_text_range(&self) -> Option<Range<usize>>;
fn text_for_range(&self, range_utf16: Range<usize>) -> Option<String>;
fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str);
fn replace_and_mark_text_in_range(
&mut self,
range_utf16: Option<Range<usize>>,
new_text: &str,
new_selected_range: Option<Range<usize>>,
);
fn unmark_text(&mut self);
fn bounds_for_range(&self, range_utf16: Range<usize>) -> Option<Bounds<f32>>;
}
#[derive(Copy, Clone, Debug)]
pub enum RasterizationOptions {
Alpha,
@ -74,6 +146,7 @@ pub struct WindowOptions {
pub show: bool,
pub kind: WindowKind,
pub is_movable: bool,
pub screen: Option<Rc<dyn PlatformScreen>>,
}
impl Default for WindowOptions {
@ -90,16 +163,16 @@ impl Default for WindowOptions {
show: true,
kind: WindowKind::Normal,
is_movable: true,
screen: None,
}
}
}
#[derive(Debug, Default)]
pub struct TitlebarOptions {
pub title: Option<SharedString>,
pub appears_transparent: bool,
pub traffic_light_position: Option<Point<f32>>,
pub traffic_light_position: Option<Point<Pixels>>,
}
#[derive(Copy, Clone, Debug)]
@ -127,5 +200,27 @@ pub enum WindowBounds {
Fullscreen,
#[default]
Maximized,
Fixed(Bounds<f32>),
Fixed(Bounds<Pixels>),
}
#[derive(Copy, Clone, Debug)]
pub enum WindowAppearance {
Light,
VibrantLight,
Dark,
VibrantDark,
}
impl Default for WindowAppearance {
fn default() -> Self {
Self::Light
}
}
#[derive(Copy, Clone, Debug, PartialEq, Default)]
pub enum WindowPromptLevel {
#[default]
Info,
Warning,
Critical,
}

View file

@ -0,0 +1,204 @@
use crate::{point, Keystroke, Modifiers, Pixels, Point};
use std::{any::Any, ops::Deref};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct KeyDownEvent {
pub keystroke: Keystroke,
pub is_held: bool,
}
#[derive(Clone, Debug)]
pub struct KeyUpEvent {
pub keystroke: Keystroke,
}
#[derive(Clone, Debug, Default)]
pub struct ModifiersChangedEvent {
pub modifiers: Modifiers,
}
impl Deref for ModifiersChangedEvent {
type Target = Modifiers;
fn deref(&self) -> &Self::Target {
&self.modifiers
}
}
/// The phase of a touch motion event.
/// Based on the winit enum of the same name,
#[derive(Clone, Copy, Debug)]
pub enum TouchPhase {
Started,
Moved,
Ended,
}
#[derive(Clone, Copy, Debug)]
pub enum ScrollDelta {
Pixels(Point<Pixels>),
Lines(Point<f32>),
}
impl Default for ScrollDelta {
fn default() -> Self {
Self::Lines(Default::default())
}
}
impl ScrollDelta {
pub fn precise(&self) -> bool {
match self {
ScrollDelta::Pixels(_) => true,
ScrollDelta::Lines(_) => false,
}
}
pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
match self {
ScrollDelta::Pixels(delta) => *delta,
ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct ScrollWheelEvent {
pub position: Point<Pixels>,
pub delta: ScrollDelta,
pub modifiers: Modifiers,
/// If the platform supports returning the phase of a scroll wheel event, it will be stored here
pub phase: Option<TouchPhase>,
}
impl Deref for ScrollWheelEvent {
type Target = Modifiers;
fn deref(&self) -> &Self::Target {
&self.modifiers
}
}
#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
pub enum NavigationDirection {
Back,
Forward,
}
impl Default for NavigationDirection {
fn default() -> Self {
Self::Back
}
}
#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
pub enum MouseButton {
Left,
Right,
Middle,
Navigate(NavigationDirection),
}
impl MouseButton {
pub fn all() -> Vec<Self> {
vec![
MouseButton::Left,
MouseButton::Right,
MouseButton::Middle,
MouseButton::Navigate(NavigationDirection::Back),
MouseButton::Navigate(NavigationDirection::Forward),
]
}
}
impl Default for MouseButton {
fn default() -> Self {
Self::Left
}
}
#[derive(Clone, Debug, Default)]
pub struct MouseDownEvent {
pub button: MouseButton,
pub position: Point<Pixels>,
pub modifiers: Modifiers,
pub click_count: usize,
}
#[derive(Clone, Debug, Default)]
pub struct MouseUpEvent {
pub button: MouseButton,
pub position: Point<Pixels>,
pub modifiers: Modifiers,
pub click_count: usize,
}
#[derive(Clone, Debug, Default)]
pub struct MouseUp {
pub button: MouseButton,
pub position: Point<Pixels>,
pub modifiers: Modifiers,
pub click_count: usize,
}
#[derive(Clone, Debug, Default)]
pub struct MouseMovedEvent {
pub position: Point<Pixels>,
pub pressed_button: Option<MouseButton>,
pub modifiers: Modifiers,
}
#[derive(Clone, Debug, Default)]
pub struct MouseExitedEvent {
pub position: Point<Pixels>,
pub pressed_button: Option<MouseButton>,
pub modifiers: Modifiers,
}
impl Deref for MouseExitedEvent {
type Target = Modifiers;
fn deref(&self) -> &Self::Target {
&self.modifiers
}
}
#[derive(Clone, Debug)]
pub enum Event {
KeyDown(KeyDownEvent),
KeyUp(KeyUpEvent),
ModifiersChanged(ModifiersChangedEvent),
MouseDown(MouseDownEvent),
MouseUp(MouseUpEvent),
MouseMoved(MouseMovedEvent),
MouseExited(MouseExitedEvent),
ScrollWheel(ScrollWheelEvent),
}
impl Event {
pub fn position(&self) -> Option<Point<Pixels>> {
match self {
Event::KeyDown { .. } => None,
Event::KeyUp { .. } => None,
Event::ModifiersChanged { .. } => None,
Event::MouseDown(event) => Some(event.position),
Event::MouseUp(event) => Some(event.position),
Event::MouseMoved(event) => Some(event.position),
Event::MouseExited(event) => Some(event.position),
Event::ScrollWheel(event) => Some(event.position),
}
}
pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
match self {
Event::KeyDown { .. } => None,
Event::KeyUp { .. } => None,
Event::ModifiersChanged { .. } => None,
Event::MouseDown(event) => Some(event),
Event::MouseUp(event) => Some(event),
Event::MouseMoved(event) => Some(event),
Event::MouseExited(event) => Some(event),
Event::ScrollWheel(event) => Some(event),
}
}
}

View file

@ -0,0 +1,121 @@
use anyhow::anyhow;
use serde::Deserialize;
use std::fmt::Write;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, Deserialize, Hash)]
pub struct Modifiers {
pub control: bool,
pub alt: bool,
pub shift: bool,
pub command: bool,
pub function: bool,
}
impl Modifiers {
pub fn modified(&self) -> bool {
self.control || self.alt || self.shift || self.command || self.function
}
}
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Hash)]
pub struct Keystroke {
pub key: String,
pub modifiers: Modifiers,
}
impl Keystroke {
pub fn parse(source: &str) -> anyhow::Result<Self> {
let mut control = false;
let mut alt = false;
let mut shift = false;
let mut command = false;
let mut function = false;
let mut key = None;
let mut components = source.split('-').peekable();
while let Some(component) = components.next() {
match component {
"ctrl" => control = true,
"alt" => alt = true,
"shift" => shift = true,
"cmd" => command = true,
"fn" => function = true,
_ => {
if let Some(component) = components.peek() {
if component.is_empty() && source.ends_with('-') {
key = Some(String::from("-"));
break;
} else {
return Err(anyhow!("Invalid keystroke `{}`", source));
}
} else {
key = Some(String::from(component));
}
}
}
}
let key = key.ok_or_else(|| anyhow!("Invalid keystroke `{}`", source))?;
Ok(Keystroke {
modifiers: Modifiers {
control,
alt,
shift,
command,
function,
},
key,
})
}
pub fn modified(&self) -> bool {
self.modifiers.modified()
}
}
impl std::fmt::Display for Keystroke {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Modifiers {
control,
alt,
shift,
command,
function,
} = self.modifiers;
if control {
f.write_char('^')?;
}
if alt {
f.write_char('⎇')?;
}
if command {
f.write_char('⌘')?;
}
if shift {
f.write_char('⇧')?;
}
if function {
f.write_char('𝙛')?;
}
let key = match self.key.as_str() {
"backspace" => '⌫',
"up" => '↑',
"down" => '↓',
"left" => '←',
"right" => '→',
"tab" => '⇥',
"escape" => '⎋',
key => {
if key.len() == 1 {
key.chars().next().unwrap().to_ascii_uppercase()
} else {
return f.write_str(key);
}
}
};
f.write_char(key)
}
}

View file

@ -0,0 +1,144 @@
///! Macos screen have a y axis that goings up from the bottom of the screen and
///! an origin at the bottom left of the main display.
mod dispatcher;
mod events;
mod platform;
mod screen;
mod window;
mod window_appearence;
use std::{
ffi::{c_char, CStr, OsStr},
ops::Range,
os::unix::prelude::OsStrExt,
path::PathBuf,
};
use crate::{px, size, Pixels, Size};
use anyhow::anyhow;
use cocoa::{
base::{id, nil},
foundation::{NSAutoreleasePool, NSNotFound, NSRect, NSSize, NSString, NSUInteger, NSURL},
};
use objc::{
msg_send,
runtime::{BOOL, NO, YES},
sel, sel_impl,
};
pub use platform::*;
pub use screen::*;
pub use window::*;
use window_appearence::*;
trait BoolExt {
fn to_objc(self) -> BOOL;
}
impl BoolExt for bool {
fn to_objc(self) -> BOOL {
if self {
YES
} else {
NO
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
struct NSRange {
pub location: NSUInteger,
pub length: NSUInteger,
}
impl NSRange {
fn invalid() -> Self {
Self {
location: NSNotFound as NSUInteger,
length: 0,
}
}
fn is_valid(&self) -> bool {
self.location != NSNotFound as NSUInteger
}
fn to_range(self) -> Option<Range<usize>> {
if self.is_valid() {
let start = self.location as usize;
let end = start + self.length as usize;
Some(start..end)
} else {
None
}
}
}
impl From<Range<usize>> for NSRange {
fn from(range: Range<usize>) -> Self {
NSRange {
location: range.start as NSUInteger,
length: range.len() as NSUInteger,
}
}
}
unsafe impl objc::Encode for NSRange {
fn encode() -> objc::Encoding {
let encoding = format!(
"{{NSRange={}{}}}",
NSUInteger::encode().as_str(),
NSUInteger::encode().as_str()
);
unsafe { objc::Encoding::from_str(&encoding) }
}
}
unsafe fn ns_string(string: &str) -> id {
NSString::alloc(nil).init_str(string).autorelease()
}
impl From<NSSize> for Size<Pixels> {
fn from(value: NSSize) -> Self {
Size {
width: px(value.width as f32),
height: px(value.height as f32),
}
}
}
pub trait NSRectExt {
fn size(&self) -> Size<Pixels>;
fn intersects(&self, other: Self) -> bool;
}
impl NSRectExt for NSRect {
fn size(&self) -> Size<Pixels> {
size(px(self.size.width as f32), px(self.size.height as f32))
}
fn intersects(&self, other: Self) -> bool {
self.size.width > 0.
&& self.size.height > 0.
&& other.size.width > 0.
&& other.size.height > 0.
&& self.origin.x <= other.origin.x + other.size.width
&& self.origin.x + self.size.width >= other.origin.x
&& self.origin.y <= other.origin.y + other.size.height
&& self.origin.y + self.size.height >= other.origin.y
}
}
unsafe fn ns_url_to_path(url: id) -> crate::Result<PathBuf> {
let path: *mut c_char = msg_send![url, fileSystemRepresentation];
if path.is_null() {
Err(anyhow!(
"url is not a file path: {}",
CStr::from_ptr(url.absoluteString().UTF8String()).to_string_lossy()
))
} else {
Ok(PathBuf::from(OsStr::from_bytes(
CStr::from_ptr(path).to_bytes(),
)))
}
}

View file

@ -0,0 +1 @@
#include <dispatch/dispatch.h>

View file

@ -0,0 +1,42 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use crate::PlatformDispatcher;
use async_task::Runnable;
use objc::{
class, msg_send,
runtime::{BOOL, YES},
sel, sel_impl,
};
use std::ffi::c_void;
include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));
pub fn dispatch_get_main_queue() -> dispatch_queue_t {
unsafe { &_dispatch_main_q as *const _ as dispatch_queue_t }
}
pub struct MacDispatcher;
impl PlatformDispatcher for MacDispatcher {
fn is_main_thread(&self) -> bool {
let is_main_thread: BOOL = unsafe { msg_send![class!(NSThread), isMainThread] };
is_main_thread == YES
}
fn run_on_main_thread(&self, runnable: Runnable) {
unsafe {
dispatch_async_f(
dispatch_get_main_queue(),
runnable.into_raw() as *mut c_void,
Some(trampoline),
);
}
extern "C" fn trampoline(runnable: *mut c_void) {
let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
task.run();
}
}
}

View file

@ -0,0 +1,354 @@
use crate::{
point, px, Event, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent,
MouseButton, MouseDownEvent, MouseExitedEvent, MouseMovedEvent, MouseUpEvent,
NavigationDirection, Pixels, ScrollDelta, ScrollWheelEvent, TouchPhase,
};
use cocoa::{
appkit::{NSEvent, NSEventModifierFlags, NSEventPhase, NSEventType},
base::{id, YES},
foundation::NSString as _,
};
use core_graphics::{
event::{CGEvent, CGEventFlags, CGKeyCode},
event_source::{CGEventSource, CGEventSourceStateID},
};
use ctor::ctor;
use foreign_types::ForeignType;
use objc::{class, msg_send, sel, sel_impl};
use std::{borrow::Cow, ffi::CStr, mem, os::raw::c_char, ptr};
const BACKSPACE_KEY: u16 = 0x7f;
const SPACE_KEY: u16 = b' ' as u16;
const ENTER_KEY: u16 = 0x0d;
const NUMPAD_ENTER_KEY: u16 = 0x03;
const ESCAPE_KEY: u16 = 0x1b;
const TAB_KEY: u16 = 0x09;
const SHIFT_TAB_KEY: u16 = 0x19;
static mut EVENT_SOURCE: core_graphics::sys::CGEventSourceRef = ptr::null_mut();
#[ctor]
unsafe fn build_event_source() {
let source = CGEventSource::new(CGEventSourceStateID::Private).unwrap();
EVENT_SOURCE = source.as_ptr();
mem::forget(source);
}
pub fn key_to_native(key: &str) -> Cow<str> {
use cocoa::appkit::*;
let code = match key {
"space" => SPACE_KEY,
"backspace" => BACKSPACE_KEY,
"up" => NSUpArrowFunctionKey,
"down" => NSDownArrowFunctionKey,
"left" => NSLeftArrowFunctionKey,
"right" => NSRightArrowFunctionKey,
"pageup" => NSPageUpFunctionKey,
"pagedown" => NSPageDownFunctionKey,
"home" => NSHomeFunctionKey,
"end" => NSEndFunctionKey,
"delete" => NSDeleteFunctionKey,
"f1" => NSF1FunctionKey,
"f2" => NSF2FunctionKey,
"f3" => NSF3FunctionKey,
"f4" => NSF4FunctionKey,
"f5" => NSF5FunctionKey,
"f6" => NSF6FunctionKey,
"f7" => NSF7FunctionKey,
"f8" => NSF8FunctionKey,
"f9" => NSF9FunctionKey,
"f10" => NSF10FunctionKey,
"f11" => NSF11FunctionKey,
"f12" => NSF12FunctionKey,
_ => return Cow::Borrowed(key),
};
Cow::Owned(String::from_utf16(&[code]).unwrap())
}
unsafe fn read_modifiers(native_event: id) -> Modifiers {
let modifiers = native_event.modifierFlags();
let control = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
let command = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask);
Modifiers {
control,
alt,
shift,
command,
function,
}
}
impl Event {
pub unsafe fn from_native(native_event: id, window_height: Option<Pixels>) -> Option<Self> {
let event_type = native_event.eventType();
// Filter out event types that aren't in the NSEventType enum.
// See https://github.com/servo/cocoa-rs/issues/155#issuecomment-323482792 for details.
match event_type as u64 {
0 | 21 | 32 | 33 | 35 | 36 | 37 => {
return None;
}
_ => {}
}
match event_type {
NSEventType::NSFlagsChanged => Some(Self::ModifiersChanged(ModifiersChangedEvent {
modifiers: read_modifiers(native_event),
})),
NSEventType::NSKeyDown => Some(Self::KeyDown(KeyDownEvent {
keystroke: parse_keystroke(native_event),
is_held: native_event.isARepeat() == YES,
})),
NSEventType::NSKeyUp => Some(Self::KeyUp(KeyUpEvent {
keystroke: parse_keystroke(native_event),
})),
NSEventType::NSLeftMouseDown
| NSEventType::NSRightMouseDown
| NSEventType::NSOtherMouseDown => {
let button = match native_event.buttonNumber() {
0 => MouseButton::Left,
1 => MouseButton::Right,
2 => MouseButton::Middle,
3 => MouseButton::Navigate(NavigationDirection::Back),
4 => MouseButton::Navigate(NavigationDirection::Forward),
// Other mouse buttons aren't tracked currently
_ => return None,
};
window_height.map(|window_height| {
Self::MouseDown(MouseDownEvent {
button,
position: point(
px(native_event.locationInWindow().x as f32),
// MacOS screen coordinates are relative to bottom left
window_height - px(native_event.locationInWindow().y as f32),
),
modifiers: read_modifiers(native_event),
click_count: native_event.clickCount() as usize,
})
})
}
NSEventType::NSLeftMouseUp
| NSEventType::NSRightMouseUp
| NSEventType::NSOtherMouseUp => {
let button = match native_event.buttonNumber() {
0 => MouseButton::Left,
1 => MouseButton::Right,
2 => MouseButton::Middle,
3 => MouseButton::Navigate(NavigationDirection::Back),
4 => MouseButton::Navigate(NavigationDirection::Forward),
// Other mouse buttons aren't tracked currently
_ => return None,
};
window_height.map(|window_height| {
Self::MouseUp(MouseUpEvent {
button,
position: point(
px(native_event.locationInWindow().x as f32),
window_height - px(native_event.locationInWindow().y as f32),
),
modifiers: read_modifiers(native_event),
click_count: native_event.clickCount() as usize,
})
})
}
NSEventType::NSScrollWheel => window_height.map(|window_height| {
let phase = match native_event.phase() {
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => {
Some(TouchPhase::Started)
}
NSEventPhase::NSEventPhaseEnded => Some(TouchPhase::Ended),
_ => Some(TouchPhase::Moved),
};
let raw_data = point(
native_event.scrollingDeltaX() as f32,
native_event.scrollingDeltaY() as f32,
);
let delta = if native_event.hasPreciseScrollingDeltas() == YES {
ScrollDelta::Pixels(raw_data.map(px))
} else {
ScrollDelta::Lines(raw_data)
};
Self::ScrollWheel(ScrollWheelEvent {
position: point(
px(native_event.locationInWindow().x as f32),
window_height - px(native_event.locationInWindow().y as f32),
),
delta,
phase,
modifiers: read_modifiers(native_event),
})
}),
NSEventType::NSLeftMouseDragged
| NSEventType::NSRightMouseDragged
| NSEventType::NSOtherMouseDragged => {
let pressed_button = match native_event.buttonNumber() {
0 => MouseButton::Left,
1 => MouseButton::Right,
2 => MouseButton::Middle,
3 => MouseButton::Navigate(NavigationDirection::Back),
4 => MouseButton::Navigate(NavigationDirection::Forward),
// Other mouse buttons aren't tracked currently
_ => return None,
};
window_height.map(|window_height| {
Self::MouseMoved(MouseMovedEvent {
pressed_button: Some(pressed_button),
position: point(
px(native_event.locationInWindow().x as f32),
window_height - px(native_event.locationInWindow().y as f32),
),
modifiers: read_modifiers(native_event),
})
})
}
NSEventType::NSMouseMoved => window_height.map(|window_height| {
Self::MouseMoved(MouseMovedEvent {
position: point(
px(native_event.locationInWindow().x as f32),
window_height - px(native_event.locationInWindow().y as f32),
),
pressed_button: None,
modifiers: read_modifiers(native_event),
})
}),
NSEventType::NSMouseExited => window_height.map(|window_height| {
Self::MouseExited(MouseExitedEvent {
position: point(
px(native_event.locationInWindow().x as f32),
window_height - px(native_event.locationInWindow().y as f32),
),
pressed_button: None,
modifiers: read_modifiers(native_event),
})
}),
_ => None,
}
}
}
unsafe fn parse_keystroke(native_event: id) -> Keystroke {
use cocoa::appkit::*;
let mut chars_ignoring_modifiers =
CStr::from_ptr(native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char)
.to_str()
.unwrap()
.to_string();
let first_char = chars_ignoring_modifiers.chars().next().map(|ch| ch as u16);
let modifiers = native_event.modifierFlags();
let control = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
let mut shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
let command = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask)
&& first_char.map_or(true, |ch| {
!(NSUpArrowFunctionKey..=NSModeSwitchFunctionKey).contains(&ch)
});
#[allow(non_upper_case_globals)]
let key = match first_char {
Some(SPACE_KEY) => "space".to_string(),
Some(BACKSPACE_KEY) => "backspace".to_string(),
Some(ENTER_KEY) | Some(NUMPAD_ENTER_KEY) => "enter".to_string(),
Some(ESCAPE_KEY) => "escape".to_string(),
Some(TAB_KEY) => "tab".to_string(),
Some(SHIFT_TAB_KEY) => "tab".to_string(),
Some(NSUpArrowFunctionKey) => "up".to_string(),
Some(NSDownArrowFunctionKey) => "down".to_string(),
Some(NSLeftArrowFunctionKey) => "left".to_string(),
Some(NSRightArrowFunctionKey) => "right".to_string(),
Some(NSPageUpFunctionKey) => "pageup".to_string(),
Some(NSPageDownFunctionKey) => "pagedown".to_string(),
Some(NSHomeFunctionKey) => "home".to_string(),
Some(NSEndFunctionKey) => "end".to_string(),
Some(NSDeleteFunctionKey) => "delete".to_string(),
Some(NSF1FunctionKey) => "f1".to_string(),
Some(NSF2FunctionKey) => "f2".to_string(),
Some(NSF3FunctionKey) => "f3".to_string(),
Some(NSF4FunctionKey) => "f4".to_string(),
Some(NSF5FunctionKey) => "f5".to_string(),
Some(NSF6FunctionKey) => "f6".to_string(),
Some(NSF7FunctionKey) => "f7".to_string(),
Some(NSF8FunctionKey) => "f8".to_string(),
Some(NSF9FunctionKey) => "f9".to_string(),
Some(NSF10FunctionKey) => "f10".to_string(),
Some(NSF11FunctionKey) => "f11".to_string(),
Some(NSF12FunctionKey) => "f12".to_string(),
_ => {
let mut chars_ignoring_modifiers_and_shift =
chars_for_modified_key(native_event.keyCode(), false, false);
// Honor ⌘ when Dvorak-QWERTY is used.
let chars_with_cmd = chars_for_modified_key(native_event.keyCode(), true, false);
if command && chars_ignoring_modifiers_and_shift != chars_with_cmd {
chars_ignoring_modifiers =
chars_for_modified_key(native_event.keyCode(), true, shift);
chars_ignoring_modifiers_and_shift = chars_with_cmd;
}
if shift {
if chars_ignoring_modifiers_and_shift
== chars_ignoring_modifiers.to_ascii_lowercase()
{
chars_ignoring_modifiers_and_shift
} else if chars_ignoring_modifiers_and_shift != chars_ignoring_modifiers {
shift = false;
chars_ignoring_modifiers
} else {
chars_ignoring_modifiers
}
} else {
chars_ignoring_modifiers
}
}
};
Keystroke {
modifiers: Modifiers {
control,
alt,
shift,
command,
function,
},
key,
}
}
fn chars_for_modified_key(code: CGKeyCode, cmd: bool, shift: bool) -> String {
// Ideally, we would use `[NSEvent charactersByApplyingModifiers]` but that
// always returns an empty string with certain keyboards, e.g. Japanese. Synthesizing
// an event with the given flags instead lets us access `characters`, which always
// returns a valid string.
let source = unsafe { core_graphics::event_source::CGEventSource::from_ptr(EVENT_SOURCE) };
let event = CGEvent::new_keyboard_event(source.clone(), code, true).unwrap();
mem::forget(source);
let mut flags = CGEventFlags::empty();
if cmd {
flags |= CGEventFlags::CGEventFlagCommand;
}
if shift {
flags |= CGEventFlags::CGEventFlagShift;
}
event.set_flags(flags);
unsafe {
let event: id = msg_send![class!(NSEvent), eventWithCGEvent: &*event];
CStr::from_ptr(event.characters().UTF8String())
.to_str()
.unwrap()
.to_string()
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,145 @@
use super::ns_string;
use crate::{platform, point, px, size, Bounds, Pixels, PlatformScreen};
use cocoa::{
appkit::NSScreen,
base::{id, nil},
foundation::{NSArray, NSDictionary, NSPoint, NSRect, NSSize},
};
use core_foundation::{
number::{kCFNumberIntType, CFNumberGetValue, CFNumberRef},
uuid::{CFUUIDGetUUIDBytes, CFUUIDRef},
};
use core_graphics::display::CGDirectDisplayID;
use std::{any::Any, ffi::c_void};
use uuid::Uuid;
#[link(name = "ApplicationServices", kind = "framework")]
extern "C" {
pub fn CGDisplayCreateUUIDFromDisplayID(display: CGDirectDisplayID) -> CFUUIDRef;
}
#[derive(Debug)]
pub struct MacScreen {
pub(crate) native_screen: id,
}
impl MacScreen {
/// Get the screen with the given UUID.
pub fn find_by_id(uuid: Uuid) -> Option<Self> {
Self::all().find(|screen| platform::MacScreen::display_uuid(screen) == Some(uuid))
}
/// Get the primary screen - the one with the menu bar, and whose bottom left
/// corner is at the origin of the AppKit coordinate system.
fn primary() -> Self {
Self::all().next().unwrap()
}
pub fn all() -> impl Iterator<Item = Self> {
unsafe {
let native_screens = NSScreen::screens(nil);
(0..NSArray::count(native_screens)).map(move |ix| MacScreen {
native_screen: native_screens.objectAtIndex(ix),
})
}
}
/// Convert the given rectangle in screen coordinates from GPUI's
/// coordinate system to the AppKit coordinate system.
///
/// In GPUI's coordinates, the origin is at the top left of the primary screen, with
/// the Y axis pointing downward. In the AppKit coordindate system, the origin is at the
/// bottom left of the primary screen, with the Y axis pointing upward.
pub(crate) fn screen_bounds_to_native(bounds: Bounds<Pixels>) -> NSRect {
let primary_screen_height =
px(unsafe { Self::primary().native_screen.frame().size.height } as f32);
NSRect::new(
NSPoint::new(
bounds.origin.x.into(),
(primary_screen_height - bounds.origin.y - bounds.size.height).into(),
),
NSSize::new(bounds.size.width.into(), bounds.size.height.into()),
)
}
/// Convert the given rectangle in screen coordinates from the AppKit
/// coordinate system to GPUI's coordinate system.
///
/// In GPUI's coordinates, the origin is at the top left of the primary screen, with
/// the Y axis pointing downward. In the AppKit coordindate system, the origin is at the
/// bottom left of the primary screen, with the Y axis pointing upward.
pub(crate) fn screen_bounds_from_native(rect: NSRect) -> Bounds<Pixels> {
let primary_screen_height = unsafe { Self::primary().native_screen.frame().size.height };
Bounds {
origin: point(
px(rect.origin.x as f32),
px((primary_screen_height - rect.origin.y - rect.size.height) as f32),
),
size: size(px(rect.size.width as f32), px(rect.size.height as f32)),
}
}
}
impl PlatformScreen for MacScreen {
fn as_any(&self) -> &dyn Any {
self
}
fn display_uuid(&self) -> Option<uuid::Uuid> {
unsafe {
// Screen ids are not stable. Further, the default device id is also unstable across restarts.
// CGDisplayCreateUUIDFromDisplayID is stable but not exposed in the bindings we use.
// This approach is similar to that which winit takes
// https://github.com/rust-windowing/winit/blob/402cbd55f932e95dbfb4e8b5e8551c49e56ff9ac/src/platform_impl/macos/monitor.rs#L99
let device_description = self.native_screen.deviceDescription();
let key = ns_string("NSScreenNumber");
let device_id_obj = device_description.objectForKey_(key);
if device_id_obj.is_null() {
// Under some circumstances, especially display re-arrangements or display locking, we seem to get a null pointer
// to the device id. See: https://linear.app/zed-industries/issue/Z-257/lock-screen-crash-with-multiple-monitors
return None;
}
let mut device_id: u32 = 0;
CFNumberGetValue(
device_id_obj as CFNumberRef,
kCFNumberIntType,
(&mut device_id) as *mut _ as *mut c_void,
);
let cfuuid = CGDisplayCreateUUIDFromDisplayID(device_id as CGDirectDisplayID);
if cfuuid.is_null() {
return None;
}
let bytes = CFUUIDGetUUIDBytes(cfuuid);
Some(Uuid::from_bytes([
bytes.byte0,
bytes.byte1,
bytes.byte2,
bytes.byte3,
bytes.byte4,
bytes.byte5,
bytes.byte6,
bytes.byte7,
bytes.byte8,
bytes.byte9,
bytes.byte10,
bytes.byte11,
bytes.byte12,
bytes.byte13,
bytes.byte14,
bytes.byte15,
]))
}
}
fn bounds(&self) -> Bounds<Pixels> {
unsafe { Self::screen_bounds_from_native(self.native_screen.frame()) }
}
fn content_bounds(&self) -> Bounds<Pixels> {
unsafe { Self::screen_bounds_from_native(self.native_screen.visibleFrame()) }
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,35 @@
use crate::WindowAppearance;
use cocoa::{
appkit::{NSAppearanceNameVibrantDark, NSAppearanceNameVibrantLight},
base::id,
foundation::NSString,
};
use objc::{msg_send, sel, sel_impl};
use std::ffi::CStr;
impl WindowAppearance {
pub unsafe fn from_native(appearance: id) -> Self {
let name: id = msg_send![appearance, name];
if name == NSAppearanceNameVibrantLight {
Self::VibrantLight
} else if name == NSAppearanceNameVibrantDark {
Self::VibrantDark
} else if name == NSAppearanceNameAqua {
Self::Light
} else if name == NSAppearanceNameDarkAqua {
Self::Dark
} else {
println!(
"unknown appearance: {:?}",
CStr::from_ptr(name.UTF8String())
);
Self::Light
}
}
}
#[link(name = "AppKit", kind = "framework")]
extern "C" {
pub static NSAppearanceNameAqua: id;
pub static NSAppearanceNameDarkAqua: id;
}

View file

@ -9,7 +9,7 @@ impl TestPlatform {
}
impl Platform for TestPlatform {
fn font_system(&self) -> std::sync::Arc<dyn crate::PlatformFontSystem> {
fn font_system(&self) -> std::sync::Arc<dyn crate::PlatformTextSystem> {
todo!()
}
@ -20,4 +20,8 @@ impl Platform for TestPlatform {
) -> Box<dyn crate::PlatformWindow> {
todo!()
}
fn dispatcher(&self) -> std::sync::Arc<dyn crate::PlatformDispatcher> {
todo!()
}
}

View file

@ -3,7 +3,7 @@ use super::{
Hsla, Length, Pixels, Point, PointRefinement, Rems, Result, RunStyle, SharedString, Size,
SizeRefinement, ViewContext, WindowContext,
};
use crate::{FontCache};
use crate::FontCache;
use refineable::Refineable;
pub use taffy::style::{
AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,

View file

@ -1,7 +1,7 @@
use crate::{black, px};
use super::{
point, Bounds, FontId, Glyph, Hsla, Pixels, PlatformFontSystem, Point, UnderlineStyle,
point, Bounds, FontId, Glyph, Hsla, Pixels, PlatformTextSystem, Point, UnderlineStyle,
WindowContext,
};
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
@ -17,7 +17,7 @@ use std::{
pub struct TextLayoutCache {
prev_frame: Mutex<HashMap<CacheKeyValue, Arc<LineLayout>>>,
curr_frame: RwLock<HashMap<CacheKeyValue, Arc<LineLayout>>>,
fonts: Arc<dyn PlatformFontSystem>,
fonts: Arc<dyn PlatformTextSystem>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@ -28,7 +28,7 @@ pub struct RunStyle {
}
impl TextLayoutCache {
pub fn new(fonts: Arc<dyn PlatformFontSystem>) -> Self {
pub fn new(fonts: Arc<dyn PlatformTextSystem>) -> Self {
Self {
prev_frame: Mutex::new(HashMap::new()),
curr_frame: RwLock::new(HashMap::new()),
@ -520,7 +520,7 @@ impl Boundary {
}
pub struct LineWrapper {
font_system: Arc<dyn PlatformFontSystem>,
font_system: Arc<dyn PlatformTextSystem>,
pub(crate) font_id: FontId,
pub(crate) font_size: Pixels,
cached_ascii_char_widths: [Option<Pixels>; 128],
@ -533,7 +533,7 @@ impl LineWrapper {
pub fn new(
font_id: FontId,
font_size: Pixels,
font_system: Arc<dyn PlatformFontSystem>,
font_system: Arc<dyn PlatformTextSystem>,
) -> Self {
Self {
font_system,

49
crates/gpui3/src/util.rs Normal file
View file

@ -0,0 +1,49 @@
use smol::future::FutureExt;
use std::{future::Future, time::Duration};
pub use util::*;
pub fn post_inc(value: &mut usize) -> usize {
let prev = *value;
*value += 1;
prev
}
pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
where
F: Future<Output = T>,
{
let timer = async {
smol::Timer::after(timeout).await;
Err(())
};
let future = async move { Ok(f.await) };
timer.race(future).await
}
#[cfg(any(test, feature = "test"))]
pub struct CwdBacktrace<'a>(pub &'a backtrace::Backtrace);
#[cfg(any(test, feature = "test"))]
impl<'a> std::fmt::Debug for CwdBacktrace<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use backtrace::{BacktraceFmt, BytesOrWideString};
let cwd = std::env::current_dir().unwrap();
let cwd = cwd.parent().unwrap();
let mut print_path = |fmt: &mut std::fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
std::fmt::Display::fmt(&path, fmt)
};
let mut fmt = BacktraceFmt::new(f, backtrace::PrintFmt::Full, &mut print_path);
for frame in self.0.frames() {
let mut formatted_frame = fmt.frame();
if frame
.symbols()
.iter()
.any(|s| s.filename().map_or(false, |f| f.starts_with(&cwd)))
{
formatted_frame.backtrace_frame(frame)?;
}
}
fmt.finish()
}
}

View file

@ -1,3 +1,5 @@
use crate::PlatformWindow;
use super::{
px, taffy::LayoutId, AppContext, Bounds, Context, EntityId, Handle, Pixels, Reference, Style,
TaffyLayoutEngine,
@ -13,17 +15,19 @@ pub struct AnyWindow {}
pub struct Window {
id: WindowId,
platform_window: Box<dyn PlatformWindow>,
rem_size: Pixels,
layout_engine: TaffyLayoutEngine,
pub(crate) root_view: Option<Box<dyn Any>>,
}
impl Window {
pub fn new(id: WindowId) -> Window {
pub fn new(id: WindowId, platform_window: Box<dyn PlatformWindow>) -> Window {
Window {
id,
layout_engine: TaffyLayoutEngine::new(),
platform_window,
rem_size: px(16.),
layout_engine: TaffyLayoutEngine::new(),
root_view: None,
}
}
@ -239,6 +243,7 @@ impl<S: 'static> Into<AnyWindowHandle> for WindowHandle<S> {
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct AnyWindowHandle {
id: WindowId,
state_type: TypeId,