Checkpoint
This commit is contained in:
parent
8406c0d9a3
commit
37d0f06e07
24 changed files with 5138 additions and 54 deletions
42
Cargo.lock
generated
42
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
||||
|
|
|
@ -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
24
crates/gpui3/build.rs
Normal 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");
|
||||
}
|
|
@ -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
1030
crates/gpui3/src/executor.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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(),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
204
crates/gpui3/src/platform/events.rs
Normal file
204
crates/gpui3/src/platform/events.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
121
crates/gpui3/src/platform/keystroke.rs
Normal file
121
crates/gpui3/src/platform/keystroke.rs
Normal 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)
|
||||
}
|
||||
}
|
144
crates/gpui3/src/platform/mac.rs
Normal file
144
crates/gpui3/src/platform/mac.rs
Normal 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(),
|
||||
)))
|
||||
}
|
||||
}
|
1
crates/gpui3/src/platform/mac/dispatch.h
Normal file
1
crates/gpui3/src/platform/mac/dispatch.h
Normal file
|
@ -0,0 +1 @@
|
|||
#include <dispatch/dispatch.h>
|
42
crates/gpui3/src/platform/mac/dispatcher.rs
Normal file
42
crates/gpui3/src/platform/mac/dispatcher.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
354
crates/gpui3/src/platform/mac/events.rs
Normal file
354
crates/gpui3/src/platform/mac/events.rs
Normal 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()
|
||||
}
|
||||
}
|
1102
crates/gpui3/src/platform/mac/platform.rs
Normal file
1102
crates/gpui3/src/platform/mac/platform.rs
Normal file
File diff suppressed because it is too large
Load diff
145
crates/gpui3/src/platform/mac/screen.rs
Normal file
145
crates/gpui3/src/platform/mac/screen.rs
Normal 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()) }
|
||||
}
|
||||
}
|
1619
crates/gpui3/src/platform/mac/window.rs
Normal file
1619
crates/gpui3/src/platform/mac/window.rs
Normal file
File diff suppressed because it is too large
Load diff
35
crates/gpui3/src/platform/mac/window_appearence.rs
Normal file
35
crates/gpui3/src/platform/mac/window_appearence.rs
Normal 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;
|
||||
}
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
49
crates/gpui3/src/util.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue