use std::sync::Arc;
use ccutils::{log::log_error, sync::ArcRwLock};
use crossbeam::channel::Receiver;
use masonry::{
accesskit::{Node, Role},
core::{
AccessCtx, AccessEvent, BoxConstraints, ChildrenIds, EventCtx, LayoutCtx, PaintCtx,
PointerButton, PointerButtonEvent, PointerEvent, PointerScrollEvent, PointerUpdate,
PropertiesMut, PropertiesRef, RegisterCtx, ScrollDelta, TextEvent, Update, UpdateCtx, Widget,
},
kurbo::Size,
peniko::ImageSampler,
vello::Scene,
};
use xilem::{
Blob, Pod, ViewCtx,
core::{MessageContext, MessageResult, Mut, View, ViewMarker},
};
use crate::{Result, map, renderer, styling};
pub struct Renderer
{
vello_renderer: crate::vello::ThreadedRenderer,
cpu_image_sub: Receiver<crate::vello::CpuImage>,
pub(crate) latest_image: Option<(Arc<Vec<u8>>, u32, u32)>,
}
impl Renderer
{
pub fn new<TFeature: map::Feature + 'static>(
map: Arc<map::Map<TFeature>>,
style: styling::Style<TFeature>,
) -> Result<Self>
{
let (device, queue) = crate::vello::ThreadedRenderer::new_device()?;
let mut vello_renderer = crate::vello::ThreadedRenderer::new_thread(device, queue, map, style)?;
let cpu_image_sub = vello_renderer.subscribe_to_cpu_image()?;
Ok(Self {
vello_renderer,
cpu_image_sub,
latest_image: None,
})
}
pub fn update(&mut self) -> Result<()>
{
self.vello_renderer.update()
}
pub fn add_overlay<TFeature: map::Feature + 'static>(
&mut self,
map: Arc<map::Map<TFeature>>,
style: styling::Style<TFeature>,
z_order: i32,
) -> Result<()>
{
self.vello_renderer.add_overlay(map, style, z_order)
}
pub fn update_overlay(&mut self, z_order: i32) -> Result<()>
{
self.vello_renderer.update_overlay(z_order)
}
pub fn update_view_controller(&mut self, u: impl Fn(&mut renderer::ViewController))
{
self.vello_renderer.update_view_controller(u)
}
pub fn use_view_controller<T>(&self, u: impl FnOnce(&renderer::ViewController) -> T) -> T
{
self.vello_renderer.use_view_controller(u)
}
pub(crate) fn poll_image(&mut self) -> bool
{
if let Ok(cpu_image) = self.cpu_image_sub.try_recv()
{
self.latest_image = Some((Arc::new(cpu_image.data), cpu_image.width, cpu_image.height));
true
}
else
{
false
}
}
pub(crate) fn resize(&mut self, width: u32, height: u32) -> Result<()>
{
self
.vello_renderer
.resize(width, height, Option::<fn(&crate::vello::Texture)>::None)?;
self.vello_renderer.update_view_controller(|vc| {
vc.set_view(geo::Rect::new(
geo::Coord { x: 0.0, y: 0.0 },
geo::Coord {
x: width as f64,
y: height as f64,
},
));
});
self.update()
}
}
pub type SharedRenderer = ArcRwLock<Renderer>;
pub fn shared_renderer<TFeature: map::Feature + 'static>(
map: Arc<map::Map<TFeature>>,
style: styling::Style<TFeature>,
) -> Result<SharedRenderer>
{
Ok(ArcRwLock::new(Renderer::new(map, style)?))
}
pub fn shared_renderer_with_overlays<TBase, TOverlay>(
base_map: Arc<map::Map<TBase>>,
base_style: styling::Style<TBase>,
overlay_map: Arc<map::Map<TOverlay>>,
overlay_style: styling::Style<TOverlay>,
) -> Result<SharedRenderer>
where
TBase: map::Feature + 'static,
TOverlay: map::Feature + 'static,
{
let mut renderer = Renderer::new(base_map, base_style)?;
renderer.add_overlay(overlay_map, overlay_style, 10)?;
Ok(ArcRwLock::new(renderer))
}
pub struct MapWidget
{
renderer: SharedRenderer,
rendering_size: (u32, u32),
layout_size: Size,
last_drag_pos: Option<masonry::kurbo::Point>,
}
#[derive(Debug)]
pub enum MapAction {}
impl MapWidget
{
pub fn new(renderer: SharedRenderer) -> Self
{
Self {
renderer,
rendering_size: (0, 0),
layout_size: Size::ZERO,
last_drag_pos: None,
}
}
}
impl Widget for MapWidget
{
type Action = MapAction;
fn on_pointer_event(
&mut self,
ctx: &mut EventCtx<'_>,
_props: &mut PropertiesMut<'_>,
event: &PointerEvent,
)
{
match event
{
PointerEvent::Down(PointerButtonEvent {
button: Some(PointerButton::Primary),
state,
..
}) =>
{
self.last_drag_pos = Some(ctx.local_position(state.position));
ctx.capture_pointer();
}
PointerEvent::Up(PointerButtonEvent {
button: Some(PointerButton::Primary),
..
}) =>
{
self.last_drag_pos = None;
}
PointerEvent::Move(PointerUpdate { current: state, .. }) =>
{
if let Some(last) = self.last_drag_pos
{
let pos = ctx.local_position(state.position);
let dx = pos.x - last.x;
let dy = pos.y - last.y;
self.last_drag_pos = Some(pos);
let mut r = self.renderer.write().unwrap();
r.update_view_controller(|vc| {
let m = vc.motion_view_to_coord(&(dx, dy).into());
vc.pan(-m.x, -m.y);
});
log_error!(r.update(), "While panning map");
ctx.request_render();
}
}
PointerEvent::Scroll(PointerScrollEvent { delta, .. }) =>
{
let dy = match delta
{
ScrollDelta::LineDelta(_, y) => *y as f64,
ScrollDelta::PixelDelta(p) => p.y,
ScrollDelta::PageDelta(_, y) => *y as f64,
};
let factor = if dy < 0.0 { 0.75 } else { 1.0 / 0.75 };
let mut r = self.renderer.write().unwrap();
r.update_view_controller(|vc| vc.zoom(factor));
log_error!(r.update(), "While zooming map");
ctx.request_render();
}
_ =>
{}
}
}
fn on_text_event(
&mut self,
_ctx: &mut EventCtx<'_>,
_props: &mut PropertiesMut<'_>,
_event: &TextEvent,
)
{
}
fn on_access_event(
&mut self,
_ctx: &mut EventCtx<'_>,
_props: &mut PropertiesMut<'_>,
_event: &AccessEvent,
)
{
}
fn on_anim_frame(
&mut self,
ctx: &mut UpdateCtx<'_>,
_props: &mut PropertiesMut<'_>,
_interval: u64,
)
{
let new = self.renderer.write().unwrap().poll_image();
if new
{
ctx.request_render();
}
ctx.request_anim_frame();
}
fn update(&mut self, _ctx: &mut UpdateCtx<'_>, _props: &mut PropertiesMut<'_>, _event: &Update) {}
fn layout(
&mut self,
_ctx: &mut LayoutCtx<'_>,
_props: &mut PropertiesMut<'_>,
bc: &BoxConstraints,
) -> Size
{
let size = bc.max();
let size = if size.width.is_infinite() || size.height.is_infinite()
{
bc.constrain(Size::new(512.0, 512.0))
}
else
{
size
};
let rsize = (size.width.ceil() as u32, size.height.ceil() as u32);
if rsize != self.rendering_size && rsize.0 > 0 && rsize.1 > 0
{
self.rendering_size = rsize;
log_error!(
self.renderer.write().unwrap().resize(rsize.0, rsize.1),
"While resizing map"
);
}
self.layout_size = size;
size
}
fn paint(&mut self, _ctx: &mut PaintCtx<'_>, _props: &PropertiesRef<'_>, scene: &mut Scene)
{
use masonry::kurbo::{Affine, Rect};
use masonry::vello::peniko::{ImageBrush, ImageData, ImageFormat};
let Some((image_data, image_width, image_height)) =
self.renderer.read().unwrap().latest_image.clone()
else
{
return;
};
if self.rendering_size.0 == 0 || self.rendering_size.1 == 0
{
return;
}
let image = ImageData {
data: Blob::new(image_data),
format: ImageFormat::Rgba8,
width: image_width,
height: image_height,
alpha_type: masonry::peniko::ImageAlphaType::Alpha,
};
let brush = ImageBrush {
image,
sampler: ImageSampler::new(),
};
scene.fill(
masonry::vello::peniko::Fill::NonZero,
Affine::IDENTITY,
&brush,
None,
&Rect::new(0.0, 0.0, self.layout_size.width, self.layout_size.height),
);
}
fn accessibility_role(&self) -> Role
{
Role::Image
}
fn accessibility(
&mut self,
_ctx: &mut AccessCtx<'_>,
_props: &PropertiesRef<'_>,
_node: &mut Node,
)
{
}
fn register_children(&mut self, _ctx: &mut RegisterCtx<'_>) {}
fn children_ids(&self) -> ChildrenIds
{
ChildrenIds::new()
}
}
pub struct MapView
{
renderer: SharedRenderer,
}
impl MapView
{
pub fn new(renderer: SharedRenderer) -> Self
{
Self { renderer }
}
}
impl ViewMarker for MapView {}
impl<State: 'static, Action: 'static> View<State, Action, ViewCtx> for MapView
{
type Element = Pod<MapWidget>;
type ViewState = ();
fn build(&self, ctx: &mut ViewCtx, _app_state: &mut State) -> (Self::Element, Self::ViewState)
{
let pod = ctx.create_pod(MapWidget::new(self.renderer.clone()));
(pod, ())
}
fn rebuild(
&self,
_prev: &Self,
_view_state: &mut Self::ViewState,
_ctx: &mut ViewCtx,
_element: Mut<'_, Self::Element>,
_app_state: &mut State,
)
{
}
fn teardown(
&self,
_view_state: &mut Self::ViewState,
ctx: &mut ViewCtx,
element: Mut<'_, Self::Element>,
)
{
ctx.teardown_leaf(element);
}
fn message(
&self,
_view_state: &mut Self::ViewState,
_message: &mut MessageContext,
_element: Mut<'_, Self::Element>,
_app_state: &mut State,
) -> MessageResult<Action>
{
MessageResult::Stale
}
}