use std::marker::PhantomData;
use std::num::NonZeroU32;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::{Duration, Instant};
use appit::winit::dpi::{PhysicalPosition, PhysicalSize};
use appit::winit::error::{EventLoopError, OsError};
use appit::winit::event::{
AxisId, DeviceId, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta, Touch,
TouchPhase,
};
use appit::winit::keyboard::PhysicalKey;
use appit::winit::window::{ImePurpose, Theme, WindowId};
pub use appit::{winit, Application, AsApplication, Message, WindowAttributes};
use appit::{RunningWindow, WindowBehavior as _};
use figures::units::{Px, UPx};
use figures::{Point, Rect, Size};
use intentional::{Assert, Cast};
use crate::drawing::{Drawing, Renderer};
use crate::{Color, Graphics, Kludgine, RenderingGraphics};
pub struct PendingApp<WindowEvent = ()>(appit::PendingApp<AppEvent<WindowEvent>>)
where
AppEvent<WindowEvent>: Message<Window = WindowEvent, Response = wgpu::Surface<'static>>;
impl<WindowEvent> Default for PendingApp<WindowEvent>
where
AppEvent<WindowEvent>: Message<Window = WindowEvent, Response = wgpu::Surface<'static>>,
{
fn default() -> Self {
Self::new()
}
}
impl<WindowEvent> AsApplication<AppEvent<WindowEvent>> for PendingApp<WindowEvent>
where
AppEvent<WindowEvent>: Message<Window = WindowEvent, Response = wgpu::Surface<'static>>,
{
fn as_application(&self) -> &dyn Application<AppEvent<WindowEvent>>
where
AppEvent<WindowEvent>: Message,
{
&self.0
}
fn as_application_mut(&mut self) -> &mut dyn Application<AppEvent<WindowEvent>>
where
AppEvent<WindowEvent>: Message,
{
&mut self.0
}
}
impl<WindowEvent> PendingApp<WindowEvent>
where
AppEvent<WindowEvent>: Message<Window = WindowEvent, Response = wgpu::Surface<'static>>,
{
#[must_use]
#[allow(clippy::missing_panics_doc)] pub fn new() -> Self {
Self(appit::PendingApp::new_with_event_callback(
|request: AppEvent<WindowEvent>, windows: &appit::Windows<WindowEvent>| {
let window = windows.get(request.0.window).expect("window not found");
request
.0
.wgpu
.create_surface(window)
.expect("error creating surface")
},
))
}
#[must_use]
pub fn as_app(&self) -> App<WindowEvent> {
self.0.app()
}
pub fn run(self) -> Result<(), EventLoopError> {
self.0.run()
}
}
pub type App<WindowEvent = ()> = appit::App<AppEvent<WindowEvent>>;
pub struct Window<'window, WindowEvent = ()>
where
WindowEvent: Send + 'static,
{
window: &'window mut RunningWindow<AppEvent<WindowEvent>>,
elapsed: Duration,
last_frame_rendered_in: Duration,
}
impl<'window, WindowEvent> Window<'window, WindowEvent>
where
WindowEvent: Send + 'static,
{
fn new(
window: &'window mut RunningWindow<AppEvent<WindowEvent>>,
elapsed: Duration,
last_frame_rendered_in: Duration,
) -> Self {
Self {
window,
elapsed,
last_frame_rendered_in,
}
}
#[must_use]
pub fn handle(&self) -> WindowHandle<WindowEvent> {
WindowHandle(self.window.handle())
}
#[must_use]
pub fn app(&self) -> App<WindowEvent> {
self.window.app()
}
pub fn close(&mut self) {
self.window.close();
}
#[must_use]
pub fn position(&self) -> Point<Px> {
self.window.position().into()
}
pub fn set_position(&self, position: Point<Px>) {
self.window.set_position(position.into());
}
#[must_use]
pub fn inner_size(&self) -> Size<UPx> {
self.window.inner_size().into()
}
#[must_use]
pub fn winit(&self) -> &winit::window::Window {
self.window.winit()
}
pub fn set_inner_size(&self, inner_size: Size<UPx>) {
self.window.set_inner_size(inner_size.into());
}
#[must_use]
pub const fn focused(&self) -> bool {
self.window.focused()
}
#[must_use]
pub const fn theme(&self) -> Theme {
self.window.theme()
}
#[must_use]
pub const fn ocluded(&self) -> bool {
self.window.occluded()
}
#[must_use]
pub fn title(&self) -> String {
self.window.title()
}
pub fn set_title(&mut self, new_title: &str) {
self.window.set_title(new_title);
}
pub fn set_ime_allowed(&self, allowed: bool) {
self.window.winit().set_ime_allowed(allowed);
}
pub fn set_ime_purpose(&self, purpose: ImePurpose) {
self.window.winit().set_ime_purpose(purpose);
}
pub fn set_ime_cursor_area(&self, area: Rect<UPx>) {
self.window.winit().set_ime_cursor_area(
PhysicalPosition::<u32>::new(area.origin.x.into(), area.origin.y.into()),
PhysicalSize::<u32>::new(area.size.width.into(), area.size.height.into()),
);
}
pub fn redraw_in(&mut self, duration: Duration) {
self.window.redraw_in(duration);
}
pub fn redraw_at(&mut self, time: Instant) {
self.window.redraw_at(time);
}
pub fn set_needs_redraw(&mut self) {
self.window.set_needs_redraw();
}
#[must_use]
pub const fn elapsed(&self) -> Duration {
self.elapsed
}
#[must_use]
pub const fn last_frame_rendered_in(&self) -> Duration {
self.last_frame_rendered_in
}
pub fn cursor_position(&self) -> Option<Point<Px>> {
self.window.cursor_position().map(Point::from)
}
#[must_use]
pub fn mouse_button_pressed(&self, button: MouseButton) -> bool {
self.window.mouse_button_pressed(&button)
}
#[must_use]
pub fn key_pressed(&self, key: impl Into<PhysicalKey>) -> bool {
self.window.key_pressed(&key.into())
}
#[must_use]
pub fn modifiers(&self) -> Modifiers {
self.window.modifiers()
}
pub fn set_min_inner_size(&self, min_size: Option<Size<UPx>>) {
self.window.set_min_inner_size(min_size.map(Into::into));
}
pub fn set_max_inner_size(&self, max_size: Option<Size<UPx>>) {
self.window.set_max_inner_size(max_size.map(Into::into));
}
}
pub trait WindowBehavior<WindowEvent = ()>: Sized + 'static
where
WindowEvent: Send + 'static,
{
type Context: Send + 'static;
fn initialize(
window: Window<'_, WindowEvent>,
graphics: &mut Graphics<'_>,
context: Self::Context,
) -> Self;
#[must_use]
#[allow(unused_variables)]
fn initial_window_attributes(context: &Self::Context) -> WindowAttributes {
WindowAttributes::default()
}
#[must_use]
#[allow(unused_variables)]
fn power_preference(context: &Self::Context) -> wgpu::PowerPreference {
wgpu::PowerPreference::default()
}
#[must_use]
#[allow(unused_variables)]
fn limits(adapter_limits: wgpu::Limits, context: &Self::Context) -> wgpu::Limits {
wgpu::Limits::downlevel_webgl2_defaults().using_resolution(adapter_limits)
}
#[must_use]
#[allow(unused_variables)]
fn multisample_count(context: &Self::Context) -> NonZeroU32 {
NonZeroU32::new(4).assert("4 is less than u32::MAX")
}
#[allow(unused_variables)]
fn prepare(&mut self, window: Window<'_, WindowEvent>, graphics: &mut Graphics<'_>) {}
fn render<'pass>(
&'pass mut self,
window: Window<'_, WindowEvent>,
graphics: &mut RenderingGraphics<'_, 'pass>,
) -> bool;
#[must_use]
fn present_mode(&self) -> wgpu::PresentMode {
wgpu::PresentMode::AutoVsync
}
#[must_use]
fn clear_color(&self) -> Option<Color> {
Some(Color::BLACK)
}
#[must_use]
fn composite_alpha_mode(
&self,
supported_modes: &[wgpu::CompositeAlphaMode],
) -> wgpu::CompositeAlphaMode {
supported_modes[0]
}
fn run() -> Result<(), EventLoopError>
where
Self::Context: Default,
{
Self::run_with(<Self::Context>::default())
}
fn run_with(context: Self::Context) -> Result<(), EventLoopError> {
let mut app = PendingApp::new();
KludgineWindow::<Self>::new(&mut app, context).open()?;
app.0.run()
}
fn open<App>(app: &mut App) -> Result<Option<WindowHandle<WindowEvent>>, OsError>
where
App: AsApplication<AppEvent<WindowEvent>> + ?Sized,
Self::Context: Default,
{
KludgineWindow::<Self>::new(app, <Self::Context>::default())
.open()
.map(|opt| opt.map(WindowHandle))
}
fn open_with<App>(
app: &mut App,
context: Self::Context,
) -> Result<Option<WindowHandle<WindowEvent>>, OsError>
where
App: AsApplication<AppEvent<WindowEvent>> + ?Sized,
{
KludgineWindow::<Self>::new(app, context)
.open()
.map(|opt| opt.map(WindowHandle))
}
#[allow(unused_variables)]
fn close_requested(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
) -> bool {
true
}
#[allow(unused_variables)]
fn focus_changed(&mut self, window: Window<'_, WindowEvent>, kludgine: &mut Kludgine) {}
#[allow(unused_variables)]
fn occlusion_changed(&mut self, window: Window<'_, WindowEvent>, kludgine: &mut Kludgine) {}
#[allow(unused_variables)]
fn scale_factor_changed(&mut self, window: Window<'_, WindowEvent>, kludgine: &mut Kludgine) {}
#[allow(unused_variables)]
fn resized(&mut self, window: Window<'_, WindowEvent>, kludgine: &mut Kludgine) {}
#[allow(unused_variables)]
fn theme_changed(&mut self, window: Window<'_, WindowEvent>, kludgine: &mut Kludgine) {}
#[allow(unused_variables)]
fn dropped_file(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
path: PathBuf,
) {
}
#[allow(unused_variables)]
fn hovered_file(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
path: PathBuf,
) {
}
#[allow(unused_variables)]
fn hovered_file_cancelled(&mut self, window: Window<'_, WindowEvent>, kludgine: &mut Kludgine) {
}
#[allow(unused_variables)]
fn received_character(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
char: char,
) {
}
#[allow(unused_variables)]
fn keyboard_input(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
input: KeyEvent,
is_synthetic: bool,
) {
}
#[allow(unused_variables)]
fn modifiers_changed(&mut self, window: Window<'_, WindowEvent>, kludgine: &mut Kludgine) {}
#[allow(unused_variables)]
fn ime(&mut self, window: Window<'_, WindowEvent>, kludgine: &mut Kludgine, ime: Ime) {}
#[allow(unused_variables)]
fn cursor_moved(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
position: PhysicalPosition<f64>,
) {
}
#[allow(unused_variables)]
fn cursor_entered(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
) {
}
#[allow(unused_variables)]
fn cursor_left(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
) {
}
#[allow(unused_variables)]
fn mouse_wheel(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
) {
}
#[allow(unused_variables)]
fn mouse_input(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
state: ElementState,
button: MouseButton,
) {
}
#[allow(unused_variables)]
fn touchpad_pressure(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
pressure: f32,
stage: i64,
) {
}
#[allow(unused_variables)]
fn axis_motion(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
axis: AxisId,
value: f64,
) {
}
#[allow(unused_variables)]
fn touch(&mut self, window: Window<'_, WindowEvent>, kludgine: &mut Kludgine, touch: Touch) {}
#[allow(unused_variables)]
fn pinch_gesture(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
delta: f64,
phase: TouchPhase,
) {
}
#[allow(unused_variables)]
fn pan_gesture(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
delta: Point<f32>,
phase: TouchPhase,
) {
}
#[allow(unused_variables)]
fn double_tap_gesture(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
) {
}
#[allow(unused_variables)]
fn touchpad_rotate(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
device_id: DeviceId,
delta: f32,
phase: TouchPhase,
) {
}
#[allow(unused_variables)]
fn event(
&mut self,
window: Window<'_, WindowEvent>,
kludgine: &mut Kludgine,
event: WindowEvent,
) {
}
}
pub struct AppEvent<User>(CreateSurfaceRequest<User>);
struct CreateSurfaceRequest<User> {
wgpu: Arc<wgpu::Instance>,
window: WindowId,
data: PhantomData<User>,
}
impl<User> Message for AppEvent<User>
where
User: Send + 'static,
{
type Response = wgpu::Surface<'static>;
type Window = User;
}
struct KludgineWindow<Behavior> {
behavior: Behavior,
kludgine: Kludgine,
last_render: Instant,
last_render_duration: Duration,
config: wgpu::SurfaceConfiguration,
surface: wgpu::Surface<'static>,
msaa_texture: Option<wgpu::Texture>,
queue: wgpu::Queue,
wgpu: Arc<wgpu::Instance>,
device: wgpu::Device,
multisample_count: u32,
}
impl<Behavior> KludgineWindow<Behavior> {
fn new<App, User>(
app: &mut App,
context: Behavior::Context,
) -> appit::WindowBuilder<'_, Self, App, AppEvent<User>>
where
App: AsApplication<AppEvent<User>> + ?Sized,
Behavior: WindowBehavior<User> + 'static,
User: Send + 'static,
{
let window_attributes = Behavior::initial_window_attributes(&context);
let mut window = Self::build_with(app, context);
*window = window_attributes;
window
}
fn current_surface_texture<User>(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
) -> Option<wgpu::SurfaceTexture>
where
AppEvent<User>: Message<Response = wgpu::Surface<'static>>,
{
loop {
match self.surface.get_current_texture() {
Ok(frame) => break Some(frame),
Err(other) => match other {
wgpu::SurfaceError::Timeout => continue,
wgpu::SurfaceError::Outdated => {
return None;
}
wgpu::SurfaceError::Lost => {
self.surface = window
.send(AppEvent(CreateSurfaceRequest {
wgpu: self.wgpu.clone(),
window: window.winit().id(),
data: PhantomData,
}))
.expect("app not running");
self.surface.configure(&self.device, &self.config);
}
wgpu::SurfaceError::OutOfMemory => {
unreachable!(
"out of memory error when requesting current swap chain texture"
)
}
},
}
}
}
fn render_to_surface<User>(
&mut self,
surface: wgpu::SurfaceTexture,
render_start: Instant,
window: &mut RunningWindow<AppEvent<User>>,
) where
AppEvent<User>: Message,
Behavior: WindowBehavior<User> + 'static,
User: Send + 'static,
{
let mut frame = self.kludgine.next_frame();
let elapsed = render_start - self.last_render;
self.behavior.prepare(
Window::new(window, elapsed, self.last_render_duration),
&mut frame.prepare(&self.device, &self.queue),
);
let surface_view = surface
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let (view, resolve_target) = if self.multisample_count > 1 {
if self.msaa_texture.as_ref().map_or(true, |msaa| {
msaa.width() != surface.texture.width() || msaa.height() != surface.texture.height()
}) {
self.msaa_texture = Some(self.device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: surface.texture.width(),
height: surface.texture.height(),
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: self.multisample_count,
dimension: wgpu::TextureDimension::D2,
format: surface.texture.format(),
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
}));
}
(
self.msaa_texture
.as_ref()
.assert("always initialized")
.create_view(&wgpu::TextureViewDescriptor::default()),
Some(surface_view),
)
} else {
(surface_view, None)
};
let mut gfx = frame.render(
&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: resolve_target.as_ref(),
ops: wgpu::Operations {
load: self
.behavior
.clear_color()
.map_or(wgpu::LoadOp::Load, |color| {
wgpu::LoadOp::Clear(color.into())
}),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
},
&self.device,
&self.queue,
);
let close_after_frame = !self.behavior.render(
Window::new(window, elapsed, self.last_render_duration),
&mut gfx,
);
drop(gfx);
let id = frame.submit(&self.queue);
surface.present();
if let Some(id) = id {
self.device.poll(wgpu::Maintain::WaitForSubmissionIndex(id));
}
if close_after_frame {
window.close();
}
}
}
fn new_wgpu_instance() -> wgpu::Instance {
let flags;
#[cfg(debug_assertions)]
{
flags = wgpu::InstanceFlags::debugging();
}
#[cfg(not(debug_assertions))]
{
flags = wgpu::InstanceFlags::empty();
}
wgpu::Instance::new(wgpu::InstanceDescriptor {
flags,
..wgpu::InstanceDescriptor::default()
})
}
impl<T, User> appit::WindowBehavior<AppEvent<User>> for KludgineWindow<T>
where
T: WindowBehavior<User> + 'static,
User: Send + 'static,
{
type Context = T::Context;
fn initialize(window: &mut RunningWindow<AppEvent<User>>, context: Self::Context) -> Self {
let wgpu = Arc::new(new_wgpu_instance());
let surface = window
.send(AppEvent(CreateSurfaceRequest {
wgpu: wgpu.clone(),
window: window.winit().id(),
data: PhantomData,
}))
.expect("app not running");
let adapter = pollster::block_on(wgpu.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: T::power_preference(&context),
force_fallback_adapter: false,
compatible_surface: Some(&surface),
}))
.expect("no compatible graphics adapters found");
let (device, queue) = pollster::block_on(adapter.request_device(
&wgpu::DeviceDescriptor {
label: None,
required_features: Kludgine::REQURED_FEATURES,
required_limits: Kludgine::adjust_limits(T::limits(adapter.limits(), &context)),
},
None,
))
.expect("no compatible graphics devices found");
let swapchain_capabilities = surface.get_capabilities(&adapter);
let swapchain_format = swapchain_capabilities.formats[0];
let multisample_count = T::multisample_count(&context).get();
let multisample = wgpu::MultisampleState {
count: multisample_count,
..Default::default()
};
let mut state = Kludgine::new(
&device,
&queue,
swapchain_format,
multisample,
window.inner_size().into(),
window.scale().cast::<f32>(),
);
let mut graphics = Graphics::new(&mut state, &device, &queue);
let last_render = Instant::now();
let behavior = T::initialize(
Window {
window,
elapsed: Duration::ZERO,
last_frame_rendered_in: Duration::ZERO,
},
&mut graphics,
context,
);
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: swapchain_format,
width: window.inner_size().width,
height: window.inner_size().height,
present_mode: behavior.present_mode(),
alpha_mode: behavior.composite_alpha_mode(&swapchain_capabilities.alpha_modes),
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
Self {
kludgine: state,
last_render,
last_render_duration: Duration::ZERO,
msaa_texture: None,
behavior,
config,
surface,
device,
queue,
wgpu,
multisample_count,
}
}
fn redraw(&mut self, window: &mut RunningWindow<AppEvent<User>>) {
if self.config.width > 0 && self.config.height > 0 {
let Some(surface) = self.current_surface_texture(window) else {
return;
};
let render_start = Instant::now();
self.render_to_surface(surface, render_start, window);
self.last_render_duration = render_start.elapsed();
self.last_render = render_start;
}
}
fn close_requested(&mut self, window: &mut RunningWindow<AppEvent<User>>) -> bool {
self.behavior.close_requested(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
)
}
fn focus_changed(&mut self, window: &mut RunningWindow<AppEvent<User>>) {
self.behavior.focus_changed(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
);
}
fn occlusion_changed(&mut self, window: &mut RunningWindow<AppEvent<User>>) {
self.behavior.occlusion_changed(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
);
}
fn resized(&mut self, window: &mut RunningWindow<AppEvent<User>>) {
self.config.width = window.inner_size().width;
self.config.height = window.inner_size().height;
if self.config.width > 0 && self.config.height > 0 {
self.surface.configure(&self.device, &self.config);
self.kludgine.resize(
window.inner_size().into(),
window.scale().cast::<f32>(),
&self.queue,
);
window.set_needs_redraw();
}
self.behavior.resized(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
);
}
fn scale_factor_changed(&mut self, window: &mut RunningWindow<AppEvent<User>>) {
self.behavior.scale_factor_changed(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
);
}
fn theme_changed(&mut self, window: &mut RunningWindow<AppEvent<User>>) {
self.behavior.theme_changed(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
);
}
fn dropped_file(&mut self, window: &mut RunningWindow<AppEvent<User>>, path: PathBuf) {
self.behavior.dropped_file(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
path,
);
}
fn hovered_file(&mut self, window: &mut RunningWindow<AppEvent<User>>, path: PathBuf) {
self.behavior.hovered_file(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
path,
);
}
fn hovered_file_cancelled(&mut self, window: &mut RunningWindow<AppEvent<User>>) {
self.behavior.hovered_file_cancelled(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
);
}
fn received_character(&mut self, window: &mut RunningWindow<AppEvent<User>>, char: char) {
self.behavior.received_character(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
char,
);
}
fn keyboard_input(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
device_id: DeviceId,
event: KeyEvent,
is_synthetic: bool,
) {
self.behavior.keyboard_input(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
event,
is_synthetic,
);
}
fn modifiers_changed(&mut self, window: &mut RunningWindow<AppEvent<User>>) {
self.behavior.modifiers_changed(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
);
}
fn ime(&mut self, window: &mut RunningWindow<AppEvent<User>>, ime: Ime) {
self.behavior.ime(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
ime,
);
}
fn cursor_moved(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
device_id: DeviceId,
position: PhysicalPosition<f64>,
) {
self.behavior.cursor_moved(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
position,
);
}
fn cursor_entered(&mut self, window: &mut RunningWindow<AppEvent<User>>, device_id: DeviceId) {
self.behavior.cursor_entered(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
);
}
fn cursor_left(&mut self, window: &mut RunningWindow<AppEvent<User>>, device_id: DeviceId) {
self.behavior.cursor_left(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
);
}
fn mouse_wheel(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
) {
self.behavior.mouse_wheel(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
delta,
phase,
);
}
fn mouse_input(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
device_id: DeviceId,
state: ElementState,
button: MouseButton,
) {
self.behavior.mouse_input(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
state,
button,
);
}
fn touchpad_pressure(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
device_id: DeviceId,
pressure: f32,
stage: i64,
) {
self.behavior.touchpad_pressure(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
pressure,
stage,
);
}
fn axis_motion(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
device_id: DeviceId,
axis: AxisId,
value: f64,
) {
self.behavior.axis_motion(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
axis,
value,
);
}
fn touch(&mut self, window: &mut RunningWindow<AppEvent<User>>, touch: Touch) {
self.behavior.touch(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
touch,
);
}
fn pinch_gesture(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
device_id: DeviceId,
delta: f64,
phase: TouchPhase,
) {
self.behavior.pinch_gesture(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
delta,
phase,
);
}
fn pan_gesture(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
device_id: DeviceId,
delta: PhysicalPosition<f32>,
phase: TouchPhase,
) {
self.behavior.pan_gesture(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
Point::new(delta.x, delta.y),
phase,
);
}
fn double_tap_gesture(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
device_id: DeviceId,
) {
self.behavior.double_tap_gesture(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
);
}
fn touchpad_rotate(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
device_id: DeviceId,
delta: f32,
phase: TouchPhase,
) {
self.behavior.touchpad_rotate(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
device_id,
delta,
phase,
);
}
fn event(
&mut self,
window: &mut RunningWindow<AppEvent<User>>,
event: <AppEvent<User> as Message>::Window,
) {
self.behavior.event(
Window::new(
window,
self.last_render.elapsed(),
self.last_render_duration,
),
&mut self.kludgine,
event,
);
}
}
struct CallbackWindow<C> {
callback: C,
rendering: Drawing,
keep_running: bool,
}
impl<T> WindowBehavior for CallbackWindow<T>
where
T: for<'render, 'gfx, 'window> FnMut(Renderer<'render, 'gfx>, Window<'window>) -> bool
+ Send
+ 'static,
{
type Context = T;
fn initialize(
_window: Window<'_>,
_graphics: &mut Graphics<'_>,
context: Self::Context,
) -> Self {
Self {
callback: context,
rendering: Drawing::default(),
keep_running: true,
}
}
fn prepare(&mut self, window: Window<'_>, graphics: &mut Graphics<'_>) {
let renderer = self.rendering.new_frame(graphics);
self.keep_running = (self.callback)(renderer, window);
}
fn render<'pass>(
&'pass mut self,
_window: Window<'_>,
graphics: &mut RenderingGraphics<'_, 'pass>,
) -> bool {
self.rendering.render(1., graphics);
self.keep_running
}
}
pub fn run<RenderFn>(render_fn: RenderFn) -> Result<(), EventLoopError>
where
RenderFn: for<'render, 'gfx, 'window> FnMut(Renderer<'render, 'gfx>, Window<'window>) -> bool
+ Send
+ 'static,
{
CallbackWindow::run_with(render_fn)
}
#[derive(Debug, Clone)]
pub struct WindowHandle<Message = ()>(appit::Window<Message>);
impl<Message> WindowHandle<Message> {
pub fn send(&self, message: Message) -> Result<(), Message> {
self.0.send(message)
}
}