Acord/viewport/src/handle.rs

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,
},
);
}