co-authored by
Nathan Sobo
parent
d1791a999d
commit
3dc1e917bf
+16
-8
@@ -72,10 +72,14 @@ impl AppContext {
|
||||
&self.text_system
|
||||
}
|
||||
|
||||
pub fn with_platform<R: Send + 'static>(
|
||||
pub fn spawn_on_main<F, R>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&dyn Platform, &mut Self) -> R + Send + 'static,
|
||||
) -> impl Future<Output = R> {
|
||||
f: impl FnOnce(&dyn Platform, &mut Self) -> F + Send + 'static,
|
||||
) -> impl Future<Output = R>
|
||||
where
|
||||
F: Future<Output = R> + Send + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
let this = self.this.upgrade().unwrap();
|
||||
self.platform.read(move |platform| {
|
||||
let cx = &mut *this.lock();
|
||||
@@ -88,17 +92,21 @@ impl AppContext {
|
||||
options: crate::WindowOptions,
|
||||
build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S> + Send + 'static,
|
||||
) -> impl Future<Output = WindowHandle<S>> {
|
||||
self.with_platform(move |platform, cx| {
|
||||
let id = cx.windows.insert(None);
|
||||
let handle = WindowHandle::new(id);
|
||||
let id = self.windows.insert(None);
|
||||
let handle = WindowHandle::new(id);
|
||||
let window =
|
||||
self.spawn_on_main(move |platform, _cx| Window::new(handle.into(), options, platform));
|
||||
|
||||
let mut window = Window::new(handle.into(), options, platform);
|
||||
let this = self.this.upgrade().unwrap();
|
||||
async move {
|
||||
let mut window = window.await;
|
||||
let cx = &mut *this.lock();
|
||||
let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
|
||||
window.root_view.replace(Box::new(root_view));
|
||||
|
||||
cx.windows.get_mut(id).unwrap().replace(window);
|
||||
handle
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update_window<R>(
|
||||
|
||||
@@ -19,18 +19,25 @@ use std::{
|
||||
|
||||
/// Enqueues the given closure to be run on the application's event loop. Can be
|
||||
/// called on any thread.
|
||||
pub(crate) fn spawn_on_main<R>(
|
||||
pub(crate) fn spawn_on_main<F, R>(
|
||||
dispatcher: Arc<dyn PlatformDispatcher>,
|
||||
future: impl Future<Output = R> + Send + 'static,
|
||||
func: impl FnOnce() -> F + Send + 'static,
|
||||
) -> impl Future<Output = R>
|
||||
where
|
||||
F: Future<Output = R> + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let (runnable, task) = async_task::spawn(
|
||||
async move {
|
||||
let result = future.await;
|
||||
let _ = tx.send(result);
|
||||
{
|
||||
let dispatcher = dispatcher.clone();
|
||||
async move {
|
||||
let future = func();
|
||||
let _ = spawn_on_main_local(dispatcher, async move {
|
||||
let result = future.await;
|
||||
let _ = tx.send(result);
|
||||
});
|
||||
}
|
||||
},
|
||||
move |runnable| dispatcher.run_on_main_thread(runnable),
|
||||
);
|
||||
|
||||
@@ -235,10 +235,26 @@ impl Edges<Pixels> {
|
||||
#[repr(transparent)]
|
||||
pub struct Pixels(pub(crate) f32);
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd,
|
||||
)]
|
||||
#[repr(transparent)]
|
||||
pub struct DevicePixels(pub(crate) u32);
|
||||
|
||||
impl From<DevicePixels> for u32 {
|
||||
fn from(device_pixels: DevicePixels) -> Self {
|
||||
device_pixels.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Pixels {
|
||||
pub fn round(&self) -> Self {
|
||||
Self(self.0.round())
|
||||
}
|
||||
|
||||
pub fn to_device_pixels(&self, scale: f32) -> DevicePixels {
|
||||
DevicePixels((self.0 * scale).ceil() as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Pixels {
|
||||
|
||||
@@ -22,11 +22,11 @@ pub use color::*;
|
||||
pub use element::*;
|
||||
pub use elements::*;
|
||||
pub use executor::*;
|
||||
use futures::channel::oneshot;
|
||||
pub use geometry::*;
|
||||
pub use gpui3_macros::*;
|
||||
pub use platform::*;
|
||||
pub use refineable::*;
|
||||
use renderer::*;
|
||||
pub use scene::*;
|
||||
pub use serde;
|
||||
pub use serde_json;
|
||||
@@ -142,18 +142,20 @@ impl<T: 'static + ?Sized> MainThreadOnly<T> {
|
||||
&self.value
|
||||
}
|
||||
|
||||
pub(crate) fn read<R>(
|
||||
pub(crate) fn read<R, F>(
|
||||
&self,
|
||||
f: impl FnOnce(&T) -> R + Send + 'static,
|
||||
f: impl FnOnce(&T) -> F + Send + 'static,
|
||||
) -> impl Future<Output = R>
|
||||
where
|
||||
F: Future<Output = R> + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
let this = self.clone();
|
||||
crate::spawn_on_main(self.dispatcher.clone(), async move {
|
||||
crate::spawn_on_main(self.dispatcher.clone(), || async move {
|
||||
// Required so we move `this` instead of this.value. Only `this` is `Send`.
|
||||
let this = this;
|
||||
f(&this.value)
|
||||
let result = f(&this.value);
|
||||
result.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,14 @@ mod test;
|
||||
|
||||
use crate::{
|
||||
AnyWindowHandle, Bounds, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, GlyphId,
|
||||
LineLayout, Pixels, Point, Result, RunStyle, SharedString, Size,
|
||||
LineLayout, Pixels, Point, RenderTarget, Result, RunStyle, SharedString, Size,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use async_task::Runnable;
|
||||
use futures::channel::oneshot;
|
||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
use raw_window_handle::{
|
||||
HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
|
||||
};
|
||||
use seahash::SeaHasher;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ffi::c_void;
|
||||
@@ -114,7 +116,10 @@ impl Debug for PlatformScreenHandle {
|
||||
|
||||
unsafe impl Send for PlatformScreenHandle {}
|
||||
|
||||
pub trait PlatformWindow: HasRawWindowHandle + HasRawDisplayHandle {
|
||||
pub trait PlatformWindow {
|
||||
fn raw_window_handle(&self) -> RawWindowHandle;
|
||||
fn raw_display_handle(&self) -> RawDisplayHandle;
|
||||
|
||||
fn bounds(&self) -> WindowBounds;
|
||||
fn content_size(&self) -> Size<Pixels>;
|
||||
fn scale_factor(&self) -> f32;
|
||||
@@ -148,6 +153,25 @@ pub trait PlatformWindow: HasRawWindowHandle + HasRawDisplayHandle {
|
||||
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
|
||||
}
|
||||
|
||||
unsafe impl<'a> HasRawWindowHandle for &'a dyn PlatformWindow {
|
||||
fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
|
||||
self.raw_window_handle()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> HasRawDisplayHandle for &'a dyn PlatformWindow {
|
||||
fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
|
||||
self.raw_display_handle()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RenderTarget for &'a dyn PlatformWindow {
|
||||
fn content_device_size(&self) -> Size<crate::DevicePixels> {
|
||||
self.content_size()
|
||||
.map(|d| d.to_device_pixels(self.scale_factor()))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PlatformDispatcher: Send + Sync {
|
||||
fn is_main_thread(&self) -> bool;
|
||||
fn run_on_main_thread(&self, task: Runnable);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
point, px, size, AnyWindowHandle, Bounds, Event, InputHandler, KeyDownEvent, Keystroke,
|
||||
MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent,
|
||||
MouseUpEvent, NSRectExt, Pixels, Platform, PlatformDispatcher, PlatformScreen, PlatformWindow,
|
||||
Point, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
|
||||
WindowPromptLevel,
|
||||
point, px, size, AnyWindowHandle, Bounds, DevicePixels, Event, InputHandler, KeyDownEvent,
|
||||
Keystroke, MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
|
||||
MouseMovedEvent, MouseUpEvent, NSRectExt, Pixels, Platform, PlatformDispatcher, PlatformScreen,
|
||||
PlatformWindow, Point, RenderTarget, Size, Timer, WindowAppearance, WindowBounds, WindowKind,
|
||||
WindowOptions, WindowPromptLevel,
|
||||
};
|
||||
use block::ConcreteBlock;
|
||||
use cocoa::{
|
||||
@@ -617,7 +617,7 @@ impl Drop for MacWindow {
|
||||
fn drop(&mut self) {
|
||||
let this = self.0.clone();
|
||||
let dispatcher = self.0.lock().dispatcher.clone();
|
||||
let _ = crate::spawn_on_main(dispatcher, async move {
|
||||
let _ = crate::spawn_on_main(dispatcher, || async move {
|
||||
unsafe {
|
||||
this.lock().native_window.close();
|
||||
}
|
||||
@@ -625,7 +625,7 @@ impl Drop for MacWindow {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl HasRawWindowHandle for MacWindow {
|
||||
impl PlatformWindow for MacWindow {
|
||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let ns_window = self.0.lock().native_window;
|
||||
let ns_view = unsafe { ns_window.contentView() };
|
||||
@@ -634,15 +634,11 @@ unsafe impl HasRawWindowHandle for MacWindow {
|
||||
handle.ns_view = ns_view as *mut c_void;
|
||||
handle.into()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl HasRawDisplayHandle for MacWindow {
|
||||
fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
AppKitDisplayHandle::empty().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformWindow for MacWindow {
|
||||
fn bounds(&self) -> WindowBounds {
|
||||
self.0.as_ref().lock().bounds()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{DevicePixels, Scene, Size};
|
||||
use futures::{future::BoxFuture, FutureExt};
|
||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
|
||||
use super::{Scene, Size};
|
||||
|
||||
pub struct Renderer {
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
@@ -12,113 +12,118 @@ pub struct Renderer {
|
||||
vertex_count: u32,
|
||||
}
|
||||
|
||||
pub trait Window: HasRawWindowHandle + HasRawDisplayHandle {
|
||||
fn inner_size(&self) -> Size<u32>;
|
||||
pub(crate) trait RenderTarget: HasRawWindowHandle + HasRawDisplayHandle {
|
||||
fn content_device_size(&self) -> Size<DevicePixels>;
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub async fn new<W>(window: &W) -> Self
|
||||
pub(crate) fn new<'a, W>(window: &'a W) -> BoxFuture<'static, Self>
|
||||
where
|
||||
W: Window,
|
||||
W: RenderTarget,
|
||||
{
|
||||
let instance = wgpu::Instance::new(Default::default());
|
||||
let surface = unsafe { instance.create_surface(window).unwrap() };
|
||||
let width = window.content_device_size().width;
|
||||
let height = window.content_device_size().height;
|
||||
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions::default())
|
||||
.await
|
||||
.unwrap();
|
||||
async move {
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions::default())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let (device, queue) = adapter
|
||||
.request_device(&wgpu::DeviceDescriptor::default(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
let (device, queue) = adapter
|
||||
.request_device(&wgpu::DeviceDescriptor::default(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let surface_config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
width: window.inner_size().width,
|
||||
height: window.inner_size().height,
|
||||
let surface_config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
width: width.into(),
|
||||
height: height.into(),
|
||||
|
||||
// "FIFO" mode renders frames in queue synced with the display's refresh rate.
|
||||
// Avoids screen tearing but may not offer the lowest latency. Ideal when image
|
||||
// quality takes priority over input latency.
|
||||
present_mode: wgpu::PresentMode::Fifo,
|
||||
// "FIFO" mode renders frames in queue synced with the display's refresh rate.
|
||||
// Avoids screen tearing but may not offer the lowest latency. Ideal when image
|
||||
// quality takes priority over input latency.
|
||||
present_mode: wgpu::PresentMode::Fifo,
|
||||
|
||||
// Use the Premultiplied alpha mode. With premultiplication, the color components
|
||||
// are multiplied by the alpha value before storage or blending, meaning calculations
|
||||
// with colors already factor in the influence of alpha. This typically results
|
||||
// in better performance and avoids a separate multiplication operation during blending.
|
||||
alpha_mode: wgpu::CompositeAlphaMode::PreMultiplied,
|
||||
// Use the Premultiplied alpha mode. With premultiplication, the color components
|
||||
// are multiplied by the alpha value before storage or blending, meaning calculations
|
||||
// with colors already factor in the influence of alpha. This typically results
|
||||
// in better performance and avoids a separate multiplication operation during blending.
|
||||
alpha_mode: wgpu::CompositeAlphaMode::PreMultiplied,
|
||||
|
||||
// Specify the color formats for the views the surface can have.
|
||||
// In this case, the format is BGRA (blue, green, red, alpha) with unsigned
|
||||
// normalised integers in the 8-bit range and the color space is sRGB (standard RGB).
|
||||
// sRGB is the standard color space for displaying images and video on digital displays,
|
||||
// as it optimises color accuracy and consistency.
|
||||
view_formats: vec![wgpu::TextureFormat::Bgra8UnormSrgb],
|
||||
};
|
||||
// Specify the color formats for the views the surface can have.
|
||||
// In this case, the format is BGRA (blue, green, red, alpha) with unsigned
|
||||
// normalised integers in the 8-bit range and the color space is sRGB (standard RGB).
|
||||
// sRGB is the standard color space for displaying images and video on digital displays,
|
||||
// as it optimises color accuracy and consistency.
|
||||
view_formats: vec![wgpu::TextureFormat::Bgra8UnormSrgb],
|
||||
};
|
||||
|
||||
surface.configure(&device, &surface_config);
|
||||
surface.configure(&device, &surface_config);
|
||||
|
||||
let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Vertex Shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shader.vert.wgsl").into()),
|
||||
});
|
||||
let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Vertex Shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shader.vert.wgsl").into()),
|
||||
});
|
||||
|
||||
let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Fragment Shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shader.frag.wgsl").into()),
|
||||
});
|
||||
let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Fragment Shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shader.frag.wgsl").into()),
|
||||
});
|
||||
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Render Pipeline Layout"),
|
||||
bind_group_layouts: &[],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Render Pipeline Layout"),
|
||||
bind_group_layouts: &[],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
size: 0,
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
size: 0,
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Render Pipeline"),
|
||||
layout: Some(&pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &vs_module,
|
||||
entry_point: "main",
|
||||
buffers: &[],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &fs_module,
|
||||
entry_point: "main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: surface_config.format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Render Pipeline"),
|
||||
layout: Some(&pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &vs_module,
|
||||
entry_point: "main",
|
||||
buffers: &[],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &fs_module,
|
||||
entry_point: "main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: surface_config.format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
Self {
|
||||
device,
|
||||
queue,
|
||||
surface,
|
||||
surface_config,
|
||||
pipeline,
|
||||
vertex_buffer,
|
||||
vertex_count: 0,
|
||||
Self {
|
||||
device,
|
||||
queue,
|
||||
surface,
|
||||
surface_config,
|
||||
pipeline,
|
||||
vertex_buffer,
|
||||
vertex_count: 0,
|
||||
}
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn render(&mut self, scene: &Scene) {
|
||||
|
||||
+25
-12
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
AvailableSpace, MainThreadOnly, Platform, PlatformWindow, Point, Size, Style, TextStyle,
|
||||
TextStyleRefinement, WindowOptions,
|
||||
renderer::Renderer, AvailableSpace, MainThreadOnly, Platform, PlatformWindow, Point, Size,
|
||||
Style, TextStyle, TextStyleRefinement, WindowOptions,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -12,6 +12,7 @@ use derive_more::{Deref, DerefMut};
|
||||
use refineable::Refineable;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
sync::Arc,
|
||||
};
|
||||
@@ -21,6 +22,7 @@ pub struct AnyWindow {}
|
||||
pub struct Window {
|
||||
handle: AnyWindowHandle,
|
||||
platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
|
||||
renderer: Renderer,
|
||||
rem_size: Pixels,
|
||||
layout_engine: TaffyLayoutEngine,
|
||||
text_style_stack: Vec<TextStyleRefinement>,
|
||||
@@ -29,17 +31,28 @@ pub struct Window {
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Window {
|
||||
let platform_window = Arc::new(platform.open_window(handle, options));
|
||||
pub fn new(
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowOptions,
|
||||
platform: &dyn Platform,
|
||||
) -> impl Future<Output = Window> + 'static {
|
||||
let platform_window = platform.open_window(handle, options);
|
||||
let renderer = Renderer::new(&platform_window.as_ref());
|
||||
let mouse_position = platform_window.mouse_position();
|
||||
Window {
|
||||
handle,
|
||||
platform_window: MainThreadOnly::new(platform_window, platform.dispatcher()),
|
||||
rem_size: px(16.),
|
||||
layout_engine: TaffyLayoutEngine::new(),
|
||||
text_style_stack: Vec::new(),
|
||||
root_view: None,
|
||||
mouse_position,
|
||||
let platform_window = MainThreadOnly::new(Arc::new(platform_window), platform.dispatcher());
|
||||
|
||||
async move {
|
||||
let renderer = renderer.await;
|
||||
Window {
|
||||
handle,
|
||||
platform_window,
|
||||
renderer,
|
||||
rem_size: px(16.),
|
||||
layout_engine: TaffyLayoutEngine::new(),
|
||||
text_style_stack: Vec::new(),
|
||||
root_view: None,
|
||||
mouse_position,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user