242 lines
6.8 KiB
Rust
242 lines
6.8 KiB
Rust
use std::ffi::c_void;
|
|
use std::ptr::NonNull;
|
|
|
|
use iced_graphics::{Shell, Viewport};
|
|
use iced_runtime::user_interface::{self, UserInterface};
|
|
use iced_wgpu::core::renderer::Style;
|
|
use iced_wgpu::core::time::Instant;
|
|
use iced_wgpu::core::{clipboard, keyboard, mouse, window, Color, Event, Font, Pixels, Point, Size, Theme};
|
|
use iced_wgpu::Engine;
|
|
use raw_window_handle::{
|
|
AppKitDisplayHandle, AppKitWindowHandle, RawDisplayHandle, RawWindowHandle,
|
|
};
|
|
|
|
use crate::editor::{EditorState, Message};
|
|
use crate::ViewportHandle;
|
|
|
|
struct MacClipboard;
|
|
|
|
impl clipboard::Clipboard for MacClipboard {
|
|
fn read(&self, _kind: clipboard::Kind) -> Option<String> {
|
|
std::process::Command::new("pbpaste")
|
|
.output()
|
|
.ok()
|
|
.and_then(|o| String::from_utf8(o.stdout).ok())
|
|
}
|
|
|
|
fn write(&mut self, _kind: clipboard::Kind, contents: String) {
|
|
use std::io::Write;
|
|
if let Ok(mut child) = std::process::Command::new("pbcopy")
|
|
.stdin(std::process::Stdio::piped())
|
|
.spawn()
|
|
{
|
|
if let Some(stdin) = child.stdin.as_mut() {
|
|
let _ = stdin.write_all(contents.as_bytes());
|
|
}
|
|
let _ = child.wait();
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn create(
|
|
nsview: *mut c_void,
|
|
width: f32,
|
|
height: f32,
|
|
scale: f32,
|
|
) -> Option<ViewportHandle> {
|
|
let ptr = NonNull::new(nsview)?;
|
|
|
|
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
|
backends: wgpu::Backends::METAL,
|
|
..Default::default()
|
|
});
|
|
|
|
let raw_window = RawWindowHandle::AppKit(AppKitWindowHandle::new(ptr));
|
|
let raw_display = RawDisplayHandle::AppKit(AppKitDisplayHandle::new());
|
|
|
|
let target = wgpu::SurfaceTargetUnsafe::RawHandle {
|
|
raw_display_handle: raw_display,
|
|
raw_window_handle: raw_window,
|
|
};
|
|
|
|
let surface = unsafe { instance.create_surface_unsafe(target).ok()? };
|
|
|
|
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
|
power_preference: wgpu::PowerPreference::HighPerformance,
|
|
compatible_surface: Some(&surface),
|
|
force_fallback_adapter: false,
|
|
}))
|
|
.ok()?;
|
|
|
|
let (device, queue) =
|
|
pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor::default())).ok()?;
|
|
|
|
let phys_w = (width * scale) as u32;
|
|
let phys_h = (height * scale) as u32;
|
|
|
|
let caps = surface.get_capabilities(&adapter);
|
|
let format = caps.formats.first().copied()?;
|
|
|
|
surface.configure(
|
|
&device,
|
|
&wgpu::SurfaceConfiguration {
|
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
format,
|
|
width: phys_w.max(1),
|
|
height: phys_h.max(1),
|
|
present_mode: wgpu::PresentMode::AutoVsync,
|
|
alpha_mode: caps
|
|
.alpha_modes
|
|
.first()
|
|
.copied()
|
|
.unwrap_or(wgpu::CompositeAlphaMode::Auto),
|
|
view_formats: vec![],
|
|
desired_maximum_frame_latency: 2,
|
|
},
|
|
);
|
|
|
|
let engine = Engine::new(
|
|
&adapter,
|
|
device.clone(),
|
|
queue.clone(),
|
|
format,
|
|
None,
|
|
Shell::headless(),
|
|
);
|
|
|
|
let renderer = iced_wgpu::Renderer::new(engine, Font::DEFAULT, Pixels(16.0));
|
|
|
|
let viewport =
|
|
Viewport::with_physical_size(Size::new(phys_w.max(1), phys_h.max(1)), scale);
|
|
|
|
let focus_point = Point::new(width / 2.0, height / 2.0);
|
|
let initial_events = vec![
|
|
Event::Mouse(mouse::Event::CursorMoved { position: focus_point }),
|
|
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)),
|
|
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)),
|
|
];
|
|
|
|
Some(ViewportHandle {
|
|
surface,
|
|
device,
|
|
queue,
|
|
format,
|
|
width: phys_w,
|
|
height: phys_h,
|
|
scale,
|
|
renderer,
|
|
viewport,
|
|
cache: user_interface::Cache::new(),
|
|
state: EditorState::new(),
|
|
events: initial_events,
|
|
cursor: mouse::Cursor::Available(focus_point),
|
|
})
|
|
}
|
|
|
|
pub fn render(handle: &mut ViewportHandle) {
|
|
let frame = match handle.surface.get_current_texture() {
|
|
Ok(f) => f,
|
|
Err(_) => return,
|
|
};
|
|
let view = frame.texture.create_view(&Default::default());
|
|
|
|
let logical_size = handle.viewport.logical_size();
|
|
|
|
handle
|
|
.events
|
|
.push(Event::Window(window::Event::RedrawRequested(Instant::now())));
|
|
|
|
let cache = std::mem::take(&mut handle.cache);
|
|
let mut ui = UserInterface::build(
|
|
handle.state.view(),
|
|
Size::new(logical_size.width, logical_size.height),
|
|
cache,
|
|
&mut handle.renderer,
|
|
);
|
|
|
|
let mut clipboard = MacClipboard;
|
|
let mut messages: Vec<Message> = Vec::new();
|
|
|
|
for event in &handle.events {
|
|
if let Event::Keyboard(keyboard::Event::KeyPressed {
|
|
key: keyboard::Key::Character(c),
|
|
modifiers,
|
|
..
|
|
}) = event
|
|
{
|
|
if modifiers.logo() {
|
|
match c.as_str() {
|
|
"p" => messages.push(Message::TogglePreview),
|
|
"t" => messages.push(Message::InsertTable),
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let _ = ui.update(
|
|
&handle.events,
|
|
handle.cursor,
|
|
&mut handle.renderer,
|
|
&mut clipboard,
|
|
&mut messages,
|
|
);
|
|
handle.events.clear();
|
|
|
|
let cache = ui.into_cache();
|
|
|
|
for msg in messages.drain(..) {
|
|
handle.state.update(msg);
|
|
}
|
|
|
|
let theme = Theme::Dark;
|
|
let style = Style {
|
|
text_color: Color::WHITE,
|
|
};
|
|
|
|
let mut ui = UserInterface::build(
|
|
handle.state.view(),
|
|
Size::new(logical_size.width, logical_size.height),
|
|
cache,
|
|
&mut handle.renderer,
|
|
);
|
|
|
|
ui.draw(&mut handle.renderer, &theme, &style, handle.cursor);
|
|
handle.cache = ui.into_cache();
|
|
|
|
let bg = Color::from_rgb(0.08, 0.08, 0.10);
|
|
handle
|
|
.renderer
|
|
.present(Some(bg), handle.format, &view, &handle.viewport);
|
|
|
|
frame.present();
|
|
}
|
|
|
|
pub fn resize(handle: &mut ViewportHandle, width: f32, height: f32, scale: f32) {
|
|
let phys_w = (width * scale) as u32;
|
|
let phys_h = (height * scale) as u32;
|
|
if phys_w == 0 || phys_h == 0 {
|
|
return;
|
|
}
|
|
|
|
handle.width = phys_w;
|
|
handle.height = phys_h;
|
|
handle.scale = scale;
|
|
|
|
handle.viewport = Viewport::with_physical_size(Size::new(phys_w, phys_h), scale);
|
|
|
|
handle.surface.configure(
|
|
&handle.device,
|
|
&wgpu::SurfaceConfiguration {
|
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
format: handle.format,
|
|
width: phys_w,
|
|
height: phys_h,
|
|
present_mode: wgpu::PresentMode::AutoVsync,
|
|
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
|
view_formats: vec![],
|
|
desired_maximum_frame_latency: 2,
|
|
},
|
|
);
|
|
}
|