lua: expose most fields for window component
This commit is contained in:
parent
64099f598c
commit
76b7cac699
|
@ -57,6 +57,13 @@ function on_update()
|
|||
t:translate(0, 0.15 * dt, 0)
|
||||
return t
|
||||
end, Transform)
|
||||
|
||||
world:view(function (w)
|
||||
print("cursor pos: " .. tostring(w.cursor_position))
|
||||
print("mode: " .. w.window_mode)
|
||||
print("pos: " .. tostring(w.position))
|
||||
print("theme: " .. tostring(w.theme))
|
||||
end, Window)
|
||||
end
|
||||
|
||||
--[[ function on_post_update()
|
||||
|
|
|
@ -132,7 +132,33 @@ impl World {
|
|||
B: Bundle
|
||||
{
|
||||
let tick = self.current_tick();
|
||||
let record = self.entities.entity_record(entity).unwrap();
|
||||
let record = self.entities.entity_record(entity);
|
||||
|
||||
if record.is_none() {
|
||||
//let mut combined_column_infos: Vec<ComponentInfo> = bundle.info().columns.iter().map(|c| c.info).collect();
|
||||
let new_arch_id = self.next_archetype_id.increment();
|
||||
let mut archetype = Archetype::from_bundle_info(new_arch_id, bundle.info());
|
||||
|
||||
let mut dbun = DynamicBundle::new();
|
||||
dbun.push_bundle(bundle);
|
||||
|
||||
let entity_arch_id = archetype.add_entity(entity, dbun, &tick);
|
||||
|
||||
self.archetypes.insert(new_arch_id, archetype);
|
||||
|
||||
// Create entity record and store it
|
||||
let record = Record {
|
||||
id: new_arch_id,
|
||||
index: entity_arch_id,
|
||||
};
|
||||
|
||||
self.entities.insert_entity_record(entity, record);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let record = record.unwrap();
|
||||
|
||||
let current_arch = self.archetypes.get(&record.id).unwrap();
|
||||
let current_arch_len = current_arch.len();
|
||||
|
||||
|
@ -167,6 +193,7 @@ impl World {
|
|||
.map(|c| unsafe { (NonNull::new_unchecked(c.borrow_ptr().as_ptr()), c.info) })
|
||||
.collect();
|
||||
|
||||
// try to find an archetype that this entity and its new components can fit into
|
||||
if let Some(arch) = self.archetypes.values_mut().find(|a| a.is_archetype_for(&combined_column_types)) {
|
||||
let mut dbun = DynamicBundle::new();
|
||||
// move old entity components into new archetype columns
|
||||
|
|
|
@ -2,10 +2,9 @@ use lyra_ecs::CommandQueue;
|
|||
use lyra_resource::ResourceManager;
|
||||
|
||||
use crate::game::App;
|
||||
use crate::winit::WinitPlugin;
|
||||
use crate::winit::{WinitPlugin, WindowPlugin};
|
||||
use crate::DeltaTimePlugin;
|
||||
use crate::input::InputPlugin;
|
||||
use crate::render::window::WindowPlugin;
|
||||
|
||||
/// A Plugin is something you can add to a `Game` that can be used to define systems, or spawn initial entities.
|
||||
pub trait Plugin {
|
||||
|
|
|
@ -9,7 +9,6 @@ pub mod texture;
|
|||
pub mod shader_loader;
|
||||
pub mod material;
|
||||
pub mod camera;
|
||||
pub mod window;
|
||||
pub mod transform_buffer_storage;
|
||||
pub mod light;
|
||||
//pub mod light_cull_compute;
|
||||
|
|
|
@ -1,315 +1,6 @@
|
|||
use std::{collections::VecDeque, sync::Arc};
|
||||
|
||||
use async_std::task::block_on;
|
||||
use glam::{DVec2, IVec2, UVec2};
|
||||
use lyra_ecs::Entity;
|
||||
use rustc_hash::FxHashMap;
|
||||
use tracing::{debug, error, warn};
|
||||
use winit::{
|
||||
application::ApplicationHandler,
|
||||
event::WindowEvent,
|
||||
event_loop::{ActiveEventLoop, EventLoop},
|
||||
window::{Window, WindowAttributes, WindowId},
|
||||
};
|
||||
mod plugin;
|
||||
pub use plugin::*;
|
||||
|
||||
use crate::{
|
||||
game::{App, WindowState},
|
||||
plugin::Plugin,
|
||||
render::{
|
||||
renderer::BasicRenderer,
|
||||
window::{LastWindow, PrimaryWindow, WindowOptions},
|
||||
},
|
||||
};
|
||||
|
||||
/// A struct that contains a [`DeviceEvent`](winit::event::DeviceEvent) with its source
|
||||
/// [`DeviceId`](winit::event::DeviceId).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeviceEventPair {
|
||||
pub device_src: winit::event::DeviceId,
|
||||
pub event: winit::event::DeviceEvent,
|
||||
}
|
||||
|
||||
pub struct WinitPlugin {
|
||||
/// The primary window that will be created.
|
||||
///
|
||||
/// This will become `None` after the window is created. If you want to get the
|
||||
/// primary world later, query for an entity with the [`PrimaryWindow`] and
|
||||
/// [`WindowOptions`] components.
|
||||
pub primary_window: Option<WindowOptions>,
|
||||
}
|
||||
|
||||
impl Default for WinitPlugin {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
primary_window: Some(WindowOptions::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for WinitPlugin {
|
||||
fn setup(&mut self, app: &mut crate::game::App) {
|
||||
app.set_run_fn(winit_app_runner);
|
||||
app.register_event::<WindowEvent>();
|
||||
app.register_event::<DeviceEventPair>();
|
||||
|
||||
if let Some(prim) = self.primary_window.take() {
|
||||
app.add_resource(WinitWindows::with_window(prim));
|
||||
} else {
|
||||
app.add_resource(WinitWindows::default());
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ready(&self, _app: &mut crate::game::App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn complete(&self, _app: &mut crate::game::App) {}
|
||||
|
||||
fn cleanup(&self, _app: &mut crate::game::App) {}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WinitWindows {
|
||||
pub windows: FxHashMap<WindowId, Arc<Window>>,
|
||||
pub entity_to_window: FxHashMap<Entity, WindowId>,
|
||||
pub window_to_entity: FxHashMap<WindowId, Entity>,
|
||||
/// windows that will be created when the Winit runner first starts.
|
||||
window_queue: VecDeque<WindowOptions>,
|
||||
}
|
||||
|
||||
impl WinitWindows {
|
||||
pub fn with_window(window: WindowOptions) -> Self {
|
||||
Self {
|
||||
window_queue: vec![window].into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_window(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
entity: Entity,
|
||||
attr: WindowAttributes,
|
||||
) -> Result<WindowId, winit::error::OsError> {
|
||||
let win = event_loop.create_window(attr)?;
|
||||
let id = win.id();
|
||||
|
||||
self.windows.insert(id, Arc::new(win));
|
||||
self.entity_to_window.insert(entity, id);
|
||||
self.window_to_entity.insert(id, entity);
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub fn get_entity_window(&self, entity: Entity) -> Option<&Arc<Window>> {
|
||||
self.entity_to_window
|
||||
.get(&entity)
|
||||
.and_then(|id| self.windows.get(id))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn winit_app_runner(app: App) {
|
||||
let evloop = EventLoop::new().expect("failed to create winit EventLoop");
|
||||
|
||||
let mut winit_runner = WinitRunner { app };
|
||||
evloop.run_app(&mut winit_runner).expect("loop error");
|
||||
}
|
||||
|
||||
struct WinitRunner {
|
||||
app: App,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for WinitRunner {
|
||||
fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
self.app.update();
|
||||
|
||||
let renderer = self
|
||||
.app
|
||||
.renderer
|
||||
.get_mut()
|
||||
.expect("renderer was not initialized");
|
||||
renderer.prepare(&mut self.app.world);
|
||||
|
||||
match renderer.render() {
|
||||
Ok(_) => {}
|
||||
// Reconfigure the surface if lost
|
||||
//Err(wgpu::SurfaceError::Lost) => self.on_resize(.surface_size()),
|
||||
// The system is out of memory, we should probably quit
|
||||
Err(wgpu::SurfaceError::OutOfMemory) => {
|
||||
error!("OOM");
|
||||
event_loop.exit();
|
||||
}
|
||||
// All other errors (Outdated, Timeout) should be resolved by the next frame
|
||||
Err(e) => eprintln!("{:?}", e),
|
||||
}
|
||||
|
||||
let windows = self.app.world.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource");
|
||||
for window in windows.windows.values() {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
let world = &mut self.app.world;
|
||||
|
||||
let mut windows = world.get_resource_mut::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource");
|
||||
let to_create_window = windows.window_queue.pop_front().unwrap_or_default();
|
||||
let window_attr = to_create_window.as_attributes();
|
||||
drop(windows);
|
||||
let en = world.spawn((to_create_window.clone(), LastWindow { last: to_create_window }, PrimaryWindow));
|
||||
|
||||
let mut windows = world.get_resource_mut::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource");
|
||||
let wid = windows.create_window(event_loop, en, window_attr).unwrap();
|
||||
let window = windows.windows.get(&wid).unwrap().clone();
|
||||
drop(windows);
|
||||
|
||||
debug!("Created window after resume");
|
||||
|
||||
let renderer = block_on(BasicRenderer::create_with_window(world, window));
|
||||
if self.app.renderer.set(Box::new(renderer)).is_err() {
|
||||
warn!("renderer was re-initialized");
|
||||
}
|
||||
}
|
||||
|
||||
fn device_event(
|
||||
&mut self,
|
||||
_: &ActiveEventLoop,
|
||||
device_src: winit::event::DeviceId,
|
||||
event: winit::event::DeviceEvent,
|
||||
) {
|
||||
self.app.push_event(DeviceEventPair { device_src, event });
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &winit::event_loop::ActiveEventLoop,
|
||||
window_id: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
/* let windows = self.app.world.get_resource::<WinitWindows>();
|
||||
let window = match windows.windows.get(&window_id) {
|
||||
Some(w) => w.clone(),
|
||||
None => return,
|
||||
};
|
||||
drop(windows); */
|
||||
|
||||
self.app.push_event(event.clone());
|
||||
match event {
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let windows = self.app.world.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource");
|
||||
let en = windows.window_to_entity.get(&window_id)
|
||||
.expect("missing window entity");
|
||||
|
||||
// update the window and its cache so the sync system doesn't try to update the window
|
||||
let (mut en_window, mut en_last_win) = self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*en).get().unwrap();
|
||||
let pos = Some(DVec2::new(position.x, position.y));
|
||||
en_window.set_physical_cursor_position(pos);
|
||||
en_last_win.set_physical_cursor_position(pos);
|
||||
},
|
||||
WindowEvent::ActivationTokenDone { .. } => todo!(),
|
||||
WindowEvent::Resized(physical_size) => {
|
||||
self.app.on_resize(physical_size);
|
||||
|
||||
let (mut window, mut last_window) = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*e).get())
|
||||
.unwrap();
|
||||
|
||||
// update the window and its cache so the sync system doesn't try to update the window
|
||||
let size = UVec2::new(physical_size.width, physical_size.height);
|
||||
window.set_physical_size(size);
|
||||
last_window.set_physical_size(size);
|
||||
},
|
||||
// Mark the cursor as outside the window when it leaves
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
let (mut window, mut last_window) = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*e).get())
|
||||
.unwrap();
|
||||
window.set_physical_cursor_position(None);
|
||||
last_window.set_physical_cursor_position(None);
|
||||
},
|
||||
WindowEvent::Moved(physical_position) => {
|
||||
let mut state = self.app.world.get_resource_or_else(WindowState::new);
|
||||
state.position = IVec2::new(physical_position.x, physical_position.y);
|
||||
},
|
||||
WindowEvent::CloseRequested => {
|
||||
self.app.on_exit();
|
||||
event_loop.exit();
|
||||
},
|
||||
WindowEvent::Destroyed => todo!(),
|
||||
WindowEvent::DroppedFile(_path_buf) => todo!(),
|
||||
WindowEvent::HoveredFile(_path_buf) => todo!(),
|
||||
WindowEvent::HoveredFileCancelled => todo!(),
|
||||
WindowEvent::Focused(focused) => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.focused = focused;
|
||||
},
|
||||
WindowEvent::ModifiersChanged(modifiers) => {
|
||||
debug!("modifiers changed: {:?}", modifiers)
|
||||
},
|
||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.scale_factor = scale_factor;
|
||||
},
|
||||
WindowEvent::ThemeChanged(theme) => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.theme = Some(theme);
|
||||
},
|
||||
WindowEvent::Occluded(occ) => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.occluded = occ;
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
//debug!("should redraw");
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
mod window;
|
||||
pub use window::*;
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
use std::{collections::VecDeque, sync::Arc};
|
||||
|
||||
use async_std::task::block_on;
|
||||
use glam::{DVec2, IVec2, UVec2};
|
||||
use lyra_ecs::Entity;
|
||||
use rustc_hash::FxHashMap;
|
||||
use tracing::{debug, error, warn};
|
||||
use winit::{
|
||||
application::ApplicationHandler,
|
||||
event::WindowEvent,
|
||||
event_loop::{ActiveEventLoop, EventLoop},
|
||||
window::{Window, WindowAttributes, WindowId},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
game::{App, WindowState},
|
||||
plugin::Plugin,
|
||||
render::renderer::BasicRenderer, winit::{FullscreenMode, LastWindow, PrimaryWindow},
|
||||
};
|
||||
|
||||
use super::WindowOptions;
|
||||
|
||||
/// A struct that contains a [`DeviceEvent`](winit::event::DeviceEvent) with its source
|
||||
/// [`DeviceId`](winit::event::DeviceId).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeviceEventPair {
|
||||
pub device_src: winit::event::DeviceId,
|
||||
pub event: winit::event::DeviceEvent,
|
||||
}
|
||||
|
||||
pub struct WinitPlugin {
|
||||
/// The primary window that will be created.
|
||||
///
|
||||
/// This will become `None` after the window is created. If you want to get the
|
||||
/// primary world later, query for an entity with the [`PrimaryWindow`] and
|
||||
/// [`WindowOptions`] components.
|
||||
pub primary_window: Option<WindowOptions>,
|
||||
}
|
||||
|
||||
impl Default for WinitPlugin {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
primary_window: Some(WindowOptions::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for WinitPlugin {
|
||||
fn setup(&mut self, app: &mut crate::game::App) {
|
||||
app.set_run_fn(winit_app_runner);
|
||||
app.register_event::<WindowEvent>();
|
||||
app.register_event::<DeviceEventPair>();
|
||||
|
||||
if let Some(prim) = self.primary_window.take() {
|
||||
app.add_resource(WinitWindows::with_window(prim));
|
||||
} else {
|
||||
app.add_resource(WinitWindows::default());
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ready(&self, _app: &mut crate::game::App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn complete(&self, _app: &mut crate::game::App) {}
|
||||
|
||||
fn cleanup(&self, _app: &mut crate::game::App) {}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WinitWindows {
|
||||
pub windows: FxHashMap<WindowId, Arc<Window>>,
|
||||
pub entity_to_window: FxHashMap<Entity, WindowId>,
|
||||
pub window_to_entity: FxHashMap<WindowId, Entity>,
|
||||
/// windows that will be created when the Winit runner first starts.
|
||||
window_queue: VecDeque<WindowOptions>,
|
||||
}
|
||||
|
||||
impl WinitWindows {
|
||||
pub fn with_window(window: WindowOptions) -> Self {
|
||||
Self {
|
||||
window_queue: vec![window].into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_window(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
entity: Entity,
|
||||
attr: WindowAttributes,
|
||||
) -> Result<WindowId, winit::error::OsError> {
|
||||
let win = event_loop.create_window(attr)?;
|
||||
let id = win.id();
|
||||
|
||||
self.windows.insert(id, Arc::new(win));
|
||||
self.entity_to_window.insert(entity, id);
|
||||
self.window_to_entity.insert(id, entity);
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub fn get_entity_window(&self, entity: Entity) -> Option<&Arc<Window>> {
|
||||
self.entity_to_window
|
||||
.get(&entity)
|
||||
.and_then(|id| self.windows.get(id))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn winit_app_runner(app: App) {
|
||||
let evloop = EventLoop::new().expect("failed to create winit EventLoop");
|
||||
|
||||
let mut winit_runner = WinitRunner { app };
|
||||
evloop.run_app(&mut winit_runner).expect("loop error");
|
||||
}
|
||||
|
||||
struct WinitRunner {
|
||||
app: App,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for WinitRunner {
|
||||
fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
self.app.update();
|
||||
|
||||
let renderer = self
|
||||
.app
|
||||
.renderer
|
||||
.get_mut()
|
||||
.expect("renderer was not initialized");
|
||||
renderer.prepare(&mut self.app.world);
|
||||
|
||||
match renderer.render() {
|
||||
Ok(_) => {}
|
||||
// Reconfigure the surface if lost
|
||||
//Err(wgpu::SurfaceError::Lost) => self.on_resize(.surface_size()),
|
||||
// The system is out of memory, we should probably quit
|
||||
Err(wgpu::SurfaceError::OutOfMemory) => {
|
||||
error!("OOM");
|
||||
event_loop.exit();
|
||||
}
|
||||
// All other errors (Outdated, Timeout) should be resolved by the next frame
|
||||
Err(e) => eprintln!("{:?}", e),
|
||||
}
|
||||
|
||||
let windows = self.app.world.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource");
|
||||
for window in windows.windows.values() {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
let world = &mut self.app.world;
|
||||
let en = world.reserve_entity();
|
||||
|
||||
let mut windows = world.get_resource_mut::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource");
|
||||
let mut to_create_window = windows.window_queue.pop_front().unwrap_or_default();
|
||||
let window_attr = to_create_window.as_attributes();
|
||||
//drop(windows);
|
||||
|
||||
|
||||
//let en = world.spawn((to_create_window, last, PrimaryWindow));
|
||||
|
||||
//let mut windows = world.get_resource_mut::<WinitWindows>()
|
||||
//.expect("world missing WinitWindows resource");
|
||||
let wid = windows.create_window(event_loop, en, window_attr).unwrap();
|
||||
let window = windows.windows.get(&wid).unwrap().clone();
|
||||
drop(windows);
|
||||
|
||||
// update fields that default to `None`
|
||||
to_create_window.position = window.outer_position()
|
||||
.or_else(|_| window.inner_position())
|
||||
.ok()
|
||||
.map(|p| IVec2::new(p.x, p.y));
|
||||
|
||||
// See [`WindowOptions::as_attributes`], it defaults to Windowed fullscreen mode, so we
|
||||
// must trigger an update in the sync system;
|
||||
let mut last = LastWindow { last: to_create_window.clone() };
|
||||
last.last.fullscreen_mode = FullscreenMode::Windowed;
|
||||
|
||||
world.insert(en, (to_create_window, last, PrimaryWindow));
|
||||
|
||||
debug!("Created window after resume");
|
||||
|
||||
let renderer = block_on(BasicRenderer::create_with_window(world, window));
|
||||
if self.app.renderer.set(Box::new(renderer)).is_err() {
|
||||
warn!("renderer was re-initialized");
|
||||
}
|
||||
}
|
||||
|
||||
fn device_event(
|
||||
&mut self,
|
||||
_: &ActiveEventLoop,
|
||||
device_src: winit::event::DeviceId,
|
||||
event: winit::event::DeviceEvent,
|
||||
) {
|
||||
self.app.push_event(DeviceEventPair { device_src, event });
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &winit::event_loop::ActiveEventLoop,
|
||||
window_id: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
/* let windows = self.app.world.get_resource::<WinitWindows>();
|
||||
let window = match windows.windows.get(&window_id) {
|
||||
Some(w) => w.clone(),
|
||||
None => return,
|
||||
};
|
||||
drop(windows); */
|
||||
|
||||
self.app.push_event(event.clone());
|
||||
match event {
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let windows = self.app.world.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource");
|
||||
let en = windows.window_to_entity.get(&window_id)
|
||||
.expect("missing window entity");
|
||||
|
||||
// update the window and its cache so the sync system doesn't try to update the window
|
||||
let (mut en_window, mut en_last_win) = self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*en).get().unwrap();
|
||||
let pos = Some(DVec2::new(position.x, position.y));
|
||||
en_window.set_physical_cursor_position(pos);
|
||||
en_last_win.set_physical_cursor_position(pos);
|
||||
},
|
||||
WindowEvent::ActivationTokenDone { .. } => todo!(),
|
||||
WindowEvent::Resized(physical_size) => {
|
||||
self.app.on_resize(physical_size);
|
||||
|
||||
let (mut window, mut last_window) = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*e).get())
|
||||
.unwrap();
|
||||
|
||||
// update the window and its cache so the sync system doesn't try to update the window
|
||||
let size = UVec2::new(physical_size.width, physical_size.height);
|
||||
window.set_physical_size(size);
|
||||
last_window.set_physical_size(size);
|
||||
},
|
||||
// Mark the cursor as outside the window when it leaves
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
let (mut window, mut last_window) = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*e).get())
|
||||
.unwrap();
|
||||
window.set_physical_cursor_position(None);
|
||||
last_window.set_physical_cursor_position(None);
|
||||
},
|
||||
WindowEvent::Moved(physical_position) => {
|
||||
let mut state = self.app.world.get_resource_or_else(WindowState::new);
|
||||
state.position = IVec2::new(physical_position.x, physical_position.y);
|
||||
},
|
||||
WindowEvent::CloseRequested => {
|
||||
self.app.on_exit();
|
||||
event_loop.exit();
|
||||
},
|
||||
WindowEvent::Destroyed => todo!(),
|
||||
WindowEvent::DroppedFile(_path_buf) => todo!(),
|
||||
WindowEvent::HoveredFile(_path_buf) => todo!(),
|
||||
WindowEvent::HoveredFileCancelled => todo!(),
|
||||
WindowEvent::Focused(focused) => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.focused = focused;
|
||||
},
|
||||
WindowEvent::ModifiersChanged(modifiers) => {
|
||||
debug!("modifiers changed: {:?}", modifiers)
|
||||
},
|
||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.scale_factor = scale_factor;
|
||||
},
|
||||
WindowEvent::ThemeChanged(theme) => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.theme = Some(theme);
|
||||
},
|
||||
WindowEvent::Occluded(occ) => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.occluded = occ;
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
//debug!("should redraw");
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,13 +3,14 @@ use std::ops::{Deref, DerefMut};
|
|||
use glam::{DVec2, IVec2, UVec2, Vec2};
|
||||
use lyra_ecs::{query::{filter::Changed, Entities, Res, View}, Component};
|
||||
use lyra_math::Area;
|
||||
use lyra_reflect::Reflect;
|
||||
use lyra_resource::Image;
|
||||
use tracing::error;
|
||||
use winit::{dpi::{PhysicalPosition, PhysicalSize, Position, Size}, window::{CustomCursor, Fullscreen, Window}};
|
||||
use tracing::{error, warn};
|
||||
use winit::{dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, monitor::{MonitorHandle, VideoModeHandle}, window::{CustomCursor, Window}};
|
||||
|
||||
pub use winit::window::{CursorGrabMode, CursorIcon, Icon, Theme, WindowButtons, WindowLevel};
|
||||
|
||||
use crate::{plugin::Plugin, winit::WinitWindows};
|
||||
use crate::{plugin::Plugin, winit::WinitWindows, lyra_engine};
|
||||
|
||||
/// Flag component that
|
||||
#[derive(Clone, Component)]
|
||||
|
@ -21,6 +22,35 @@ pub enum CursorAppearance {
|
|||
Custom(CustomCursor)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Reflect)]
|
||||
pub enum FullscreenMode{
|
||||
#[default]
|
||||
Windowed,
|
||||
BorderlessFullscreen,
|
||||
SizedFullscreen,
|
||||
Fullscreen,
|
||||
}
|
||||
|
||||
impl FullscreenMode {
|
||||
pub fn as_winit_fullscreen(&self, monitor: MonitorHandle, physical_size: UVec2) -> Option<winit::window::Fullscreen> {
|
||||
match &self {
|
||||
FullscreenMode::Windowed => None,
|
||||
FullscreenMode::BorderlessFullscreen => Some(winit::window::Fullscreen::Borderless(None)),
|
||||
// find closest video mode for full screen sizes
|
||||
_ => {
|
||||
let closest = find_closest_video_mode(monitor, physical_size);
|
||||
|
||||
if let Some(closest) = closest {
|
||||
Some(winit::window::Fullscreen::Exclusive(closest))
|
||||
} else {
|
||||
warn!("Could not find closest video mode, falling back to windowed.");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Cursor {
|
||||
/// Modifies the cursor icon of the window.
|
||||
|
@ -28,13 +58,13 @@ pub struct Cursor {
|
|||
/// Platform-specific
|
||||
/// * **iOS / Android / Orbital:** Unsupported.
|
||||
/// * **Web:** Custom cursors have to be loaded and decoded first, until then the previous cursor is shown.
|
||||
appearance: CursorAppearance,
|
||||
pub appearance: CursorAppearance,
|
||||
|
||||
/// Gets/sets the window's cursor grab mode
|
||||
///
|
||||
/// # Tip:
|
||||
/// First try confining the cursor, and if it fails, try locking it instead.
|
||||
grab: CursorGrabMode,
|
||||
pub grab: CursorGrabMode,
|
||||
|
||||
/// Gets/sets whether the window catches cursor events.
|
||||
///
|
||||
|
@ -43,7 +73,7 @@ pub struct Cursor {
|
|||
///
|
||||
/// Platform-specific
|
||||
/// * **iOS / Android / Web / Orbital:** Unsupported.
|
||||
hittest: bool,
|
||||
pub hittest: bool,
|
||||
|
||||
/// Gets/sets the cursor's visibility
|
||||
///
|
||||
|
@ -52,18 +82,19 @@ pub struct Cursor {
|
|||
/// * **macOS:** The cursor is hidden as long as the window has input focus, even if the
|
||||
/// cursor is outside of the window.
|
||||
/// * **iOS / Android:** Unsupported.
|
||||
visible: bool,
|
||||
pub visible: bool,
|
||||
//cursor_position: Option<PhysicalPosition<i32>>,
|
||||
}
|
||||
|
||||
/// Options that the window will be created with.
|
||||
#[derive(Clone, Component)]
|
||||
#[derive(Clone, Component, Reflect)]
|
||||
pub struct WindowOptions {
|
||||
/// The enabled window buttons.
|
||||
///
|
||||
/// Platform-specific
|
||||
/// * **Wayland / X11 / Orbital:** Not implemented. Always set to [`WindowButtons::all`].
|
||||
/// * **Web / iOS / Android:** Unsupported. Always set to [`WindowButtons::all`].
|
||||
#[reflect(skip)]
|
||||
pub enabled_buttons: WindowButtons,
|
||||
|
||||
/// Gets or sets if the window is in focus.
|
||||
|
@ -73,15 +104,7 @@ pub struct WindowOptions {
|
|||
pub focused: bool,
|
||||
|
||||
/// Gets or sets the fullscreen setting.
|
||||
///
|
||||
/// If this is `None`, the window is windowed.
|
||||
///
|
||||
/// Platform-specific
|
||||
/// * **iOS:** Can only be called on the main thread.
|
||||
/// * **Android / Orbital:** Will always return None.
|
||||
/// * **Wayland:** Can return Borderless(None) when there are no monitors.
|
||||
/// * **Web:** Can only return None or Borderless(None).
|
||||
pub fullscreen: Option<Fullscreen>,
|
||||
pub fullscreen_mode: FullscreenMode,
|
||||
|
||||
/// Gets/sets the position of the top-left hand corner of the window relative to
|
||||
/// the top-left hand corner of the desktop.
|
||||
|
@ -100,6 +123,7 @@ pub struct WindowOptions {
|
|||
/// * **Web:** Value is the top-left coordinates relative to the viewport. Note: this will be
|
||||
/// the same value as [`WindowOptions::outer_position`].
|
||||
/// * **Android / Wayland:** Unsupported.
|
||||
#[reflect(skip)]
|
||||
pub position: Option<IVec2>,
|
||||
|
||||
/// Gets/sets the size of the view in the window.
|
||||
|
@ -108,6 +132,7 @@ pub struct WindowOptions {
|
|||
///
|
||||
/// Platform-specific
|
||||
/// * **Web:** The size of the canvas element. Doesn’t account for CSS `transform`.
|
||||
#[reflect(skip)]
|
||||
physical_size: UVec2,
|
||||
|
||||
/// Gets/sets if the window has decorations.
|
||||
|
@ -161,6 +186,7 @@ pub struct WindowOptions {
|
|||
/// * **macOS:** Increments are converted to logical size and then macOS rounds them to whole numbers.
|
||||
/// * **Wayland:** Not implemented, always `None`.
|
||||
/// * **iOS / Android / Web / Orbital:** Unsupported.
|
||||
#[reflect(skip)]
|
||||
pub resize_increments: Option<Size>,
|
||||
|
||||
/// Gets the scale factor.
|
||||
|
@ -177,15 +203,7 @@ pub struct WindowOptions {
|
|||
/// * **Wayland:** Only works with org_kde_kwin_blur_manager protocol.
|
||||
pub blur: bool,
|
||||
|
||||
/// Prevents the window contents from being captured by other apps.
|
||||
///
|
||||
/// Platform-specific
|
||||
/// * **macOS:** if false, [`NSWindowSharingNone`](https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone)
|
||||
/// is used but doesn’t completely prevent all apps from reading the window content,
|
||||
/// for instance, QuickTime.
|
||||
/// * **iOS / Android / x11 / Wayland / Web / Orbital:** Unsupported.
|
||||
pub content_protected: bool,
|
||||
|
||||
#[reflect(skip)]
|
||||
pub cursor: Cursor,
|
||||
|
||||
/// Sets whether the window should get IME events
|
||||
|
@ -210,19 +228,26 @@ pub struct WindowOptions {
|
|||
/// Platform-specific
|
||||
/// * **X11:** - area is not supported, only position.
|
||||
/// * **iOS / Android / Web / Orbital:** Unsupported.
|
||||
pub physical_ime_cursor_area: Option<Area<Vec2, Vec2>>,
|
||||
#[reflect(skip)]
|
||||
physical_ime_cursor_area: Option<Area<Vec2, Vec2>>,
|
||||
|
||||
/// Gets/sets the minimum size of the window.
|
||||
///
|
||||
/// Platform-specific
|
||||
/// * **iOS / Android / Orbital:** Unsupported.
|
||||
pub min_size: Option<Size>,
|
||||
|
||||
/// Gets/sets the maximum size of the window.
|
||||
/// Units are in logical pixels.
|
||||
///
|
||||
/// Platform-specific
|
||||
/// * **iOS / Android / Orbital:** Unsupported.
|
||||
pub max_size: Option<Size>,
|
||||
#[reflect(skip)]
|
||||
pub min_size: Option<Vec2>,
|
||||
|
||||
/// Gets/sets the maximum size of the window.
|
||||
///
|
||||
/// Units are in logical pixels.
|
||||
///
|
||||
/// Platform-specific
|
||||
/// * **iOS / Android / Orbital:** Unsupported.
|
||||
#[reflect(skip)]
|
||||
pub max_size: Option<Vec2>,
|
||||
|
||||
/// Gets/sets the current window theme.
|
||||
///
|
||||
|
@ -235,6 +260,7 @@ pub struct WindowOptions {
|
|||
/// * **X11:** Sets `_GTK_THEME_VARIANT` hint to `dark` or `light` and if `None` is used,
|
||||
/// it will default to [`Theme::Dark`](winit::window::Theme::Dark).
|
||||
/// * **iOS / Android / Web / Orbital:** Unsupported.
|
||||
#[reflect(skip)]
|
||||
pub theme: Option<Theme>,
|
||||
|
||||
/// Gets/sets the title of the window.
|
||||
|
@ -274,6 +300,7 @@ pub struct WindowOptions {
|
|||
/// This is just a hint to the OS, and the system could ignore it.
|
||||
///
|
||||
/// See [`WindowLevel`] for details.
|
||||
#[reflect(skip)]
|
||||
pub window_level: WindowLevel,
|
||||
|
||||
/// Show [window menu](https://en.wikipedia.org/wiki/Common_menus_in_Microsoft_Windows#System_menu)
|
||||
|
@ -282,7 +309,7 @@ pub struct WindowOptions {
|
|||
/// This is the context menu that is normally shown when interacting with the title bar. This is useful when implementing custom decorations.
|
||||
/// Platform-specific
|
||||
/// * **Android / iOS / macOS / Orbital / Wayland / Web / X11:** Unsupported.
|
||||
pub physical_window_menu_pos: Option<Vec2>,
|
||||
//pub physical_window_menu_pos: Option<Vec2>,
|
||||
|
||||
/// Gets the window's occluded state (completely hidden from view).
|
||||
///
|
||||
|
@ -296,22 +323,42 @@ pub struct WindowOptions {
|
|||
/// the application should free resources (according to the iOS application lifecycle).
|
||||
/// * **Web:** Doesn't take into account CSS border, padding, or transform.
|
||||
/// * **Android / Wayland / Windows / Orbital:** Unsupported.
|
||||
pub occluded: bool,
|
||||
// TODO: update
|
||||
pub(crate) occluded: bool,
|
||||
|
||||
/// Gets/sets the position of the cursor in physical coordinates.
|
||||
///
|
||||
/// Platform-specific
|
||||
/// * **Wayland:** Cursor must be in [`CursorGrabMode::Locked`].
|
||||
/// * **iOS / Android / Web / Orbital:** Unsupported.
|
||||
#[reflect(skip)]
|
||||
physical_cursor_position: Option<DVec2>,
|
||||
}
|
||||
|
||||
/* fn physical_to_vec2<P: winit::dpi::Pixel>(size: PhysicalSize<P>) -> Vec2 {
|
||||
let size = size.cast::<f32>();
|
||||
Vec2::new(size.width, size.height)
|
||||
} */
|
||||
|
||||
fn logical_to_vec2(size: LogicalSize<f32>) -> Vec2 {
|
||||
Vec2::new(size.width, size.height)
|
||||
}
|
||||
|
||||
impl From<winit::window::WindowAttributes> for WindowOptions {
|
||||
fn from(value: winit::window::WindowAttributes) -> Self {
|
||||
Self {
|
||||
enabled_buttons: value.enabled_buttons,
|
||||
focused: false,
|
||||
fullscreen: value.fullscreen,
|
||||
fullscreen_mode: value.fullscreen.map(|m| match m {
|
||||
winit::window::Fullscreen::Exclusive(video_mode_handle) => {
|
||||
if video_mode_handle.size() == video_mode_handle.monitor().size() {
|
||||
FullscreenMode::Fullscreen
|
||||
} else {
|
||||
FullscreenMode::SizedFullscreen
|
||||
}
|
||||
},
|
||||
winit::window::Fullscreen::Borderless(_) => FullscreenMode::BorderlessFullscreen,
|
||||
}).unwrap_or(FullscreenMode::Windowed),
|
||||
position: value.position.map(|p| {
|
||||
let s = p.to_physical::<i32>(1.0);
|
||||
IVec2::new(s.x, s.y)
|
||||
|
@ -328,7 +375,6 @@ impl From<winit::window::WindowAttributes> for WindowOptions {
|
|||
resize_increments: value.resize_increments.map(|r| r.into()),
|
||||
scale_factor: 1.0,
|
||||
blur: value.blur,
|
||||
content_protected: value.content_protected,
|
||||
cursor: Cursor {
|
||||
appearance: match value.cursor {
|
||||
winit::window::Cursor::Icon(icon) => CursorAppearance::Icon(icon),
|
||||
|
@ -340,14 +386,13 @@ impl From<winit::window::WindowAttributes> for WindowOptions {
|
|||
},
|
||||
ime_allowed: false,
|
||||
physical_ime_cursor_area: None,
|
||||
min_size: value.min_inner_size.map(|m| m.into()),
|
||||
max_size: value.max_inner_size.map(|m| m.into()),
|
||||
min_size: value.min_inner_size.map(|m| logical_to_vec2(m.to_logical(1.0))),
|
||||
max_size: value.max_inner_size.map(|m| logical_to_vec2(m.to_logical(1.0))),
|
||||
theme: value.preferred_theme,
|
||||
title: value.title,
|
||||
transparent: value.transparent,
|
||||
window_icon: None,
|
||||
window_level: value.window_level,
|
||||
physical_window_menu_pos: None,
|
||||
occluded: false,
|
||||
physical_cursor_position: None,
|
||||
}
|
||||
|
@ -360,13 +405,35 @@ impl Default for WindowOptions {
|
|||
}
|
||||
}
|
||||
|
||||
fn find_closest_video_mode(monitor: MonitorHandle, physical_size: UVec2) -> Option<VideoModeHandle> {
|
||||
let mut modes = monitor.video_modes();
|
||||
let mut closest = modes.next()?;
|
||||
let closest_size = closest.size();
|
||||
let mut closest_size = UVec2::new(closest_size.width, closest_size.height);
|
||||
|
||||
for mode in modes {
|
||||
let s = closest.size();
|
||||
let s = UVec2::new(s.width, s.height);
|
||||
|
||||
if (physical_size - s).length_squared() < (physical_size - closest_size).length_squared() {
|
||||
closest = mode;
|
||||
closest_size = s;
|
||||
}
|
||||
}
|
||||
|
||||
Some(closest)
|
||||
}
|
||||
|
||||
impl WindowOptions {
|
||||
/// Create winit [`WindowAttributes`] from self.
|
||||
///
|
||||
/// This will ignore [`WindowOptions::fullscreen`] mode on self, defaulting to
|
||||
/// [`FullscreenMode::Windowed`]. It will be updated on first run of the sync system.
|
||||
pub(crate) fn as_attributes(&self) -> winit::window::WindowAttributes {
|
||||
let mut att = winit::window::Window::default_attributes();
|
||||
|
||||
att.enabled_buttons = self.enabled_buttons.clone();
|
||||
att.fullscreen = self.fullscreen.clone();
|
||||
att.fullscreen = None;
|
||||
att.inner_size = Some(Size::Physical(PhysicalSize::new(self.physical_size.x, self.physical_size.y)));
|
||||
att.decorations = self.decorated;
|
||||
att.maximized = self.maximized;
|
||||
|
@ -375,13 +442,12 @@ impl WindowOptions {
|
|||
att.position = self.position.map(|p| Position::Physical(PhysicalPosition::new(p.x, p.y)));
|
||||
att.resize_increments = self.resize_increments.map(|i| i.into());
|
||||
att.blur = self.blur;
|
||||
att.content_protected = self.content_protected;
|
||||
att.cursor = match self.cursor.appearance.clone() {
|
||||
CursorAppearance::Icon(icon) => winit::window::Cursor::Icon(icon),
|
||||
CursorAppearance::Custom(custom) => winit::window::Cursor::Custom(custom),
|
||||
};
|
||||
att.min_inner_size = self.min_size.map(|s| s.into());
|
||||
att.max_inner_size = self.max_size.map(|s| s.into());
|
||||
att.min_inner_size = self.min_size.map(|s| Size::Logical(LogicalSize::new(s.x as _, s.y as _)));
|
||||
att.max_inner_size = self.max_size.map(|s| Size::Logical(LogicalSize::new(s.x as _, s.y as _)));
|
||||
att.preferred_theme = self.theme;
|
||||
att.title = self.title.clone();
|
||||
att.transparent = self.transparent;
|
||||
|
@ -459,6 +525,22 @@ impl WindowOptions {
|
|||
pub fn set_physical_cursor_position(&mut self, pos: Option<DVec2>) {
|
||||
self.physical_cursor_position = pos;
|
||||
}
|
||||
|
||||
/// The window's occluded state (completely hidden from view).
|
||||
///
|
||||
/// This is different to window visibility as it depends on whether the window is
|
||||
/// closed, minimised, set invisible, or fully occluded by another window.
|
||||
///
|
||||
/// Platform-specific
|
||||
/// * **iOS:** this is set to `false` in response to an applicationWillEnterForeground
|
||||
/// callback which means the application should start preparing its data.
|
||||
/// Its `true` in response to an applicationDidEnterBackground callback which means
|
||||
/// the application should free resources (according to the iOS application lifecycle).
|
||||
/// * **Web:** Doesn't take into account CSS border, padding, or transform.
|
||||
/// * **Android / Wayland / Windows / Orbital:** Unsupported.
|
||||
pub fn occluded(&self) -> bool {
|
||||
self.occluded
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of the window last time it was changed.
|
||||
|
@ -504,8 +586,13 @@ pub fn window_sync_system(windows: Res<WinitWindows>, view: View<(Entities, &Win
|
|||
window.focus_window();
|
||||
}
|
||||
|
||||
if opts.fullscreen != last.fullscreen {
|
||||
window.set_fullscreen(opts.fullscreen.clone());
|
||||
if opts.fullscreen_mode != last.fullscreen_mode {
|
||||
let monitor = window.primary_monitor().unwrap_or_else(|| {
|
||||
let mut m = window.available_monitors();
|
||||
m.next().expect("failed to find any available monitor")
|
||||
});
|
||||
|
||||
window.set_fullscreen(opts.fullscreen_mode.as_winit_fullscreen(monitor, opts.physical_size));
|
||||
}
|
||||
|
||||
if opts.physical_size != last.physical_size {
|
||||
|
@ -545,10 +632,6 @@ pub fn window_sync_system(windows: Res<WinitWindows>, view: View<(Entities, &Win
|
|||
window.set_blur(opts.blur);
|
||||
}
|
||||
|
||||
if opts.content_protected != last.content_protected {
|
||||
window.set_content_protected(opts.content_protected);
|
||||
}
|
||||
|
||||
if opts.cursor.appearance != last.cursor.appearance {
|
||||
match opts.cursor.appearance.clone() {
|
||||
CursorAppearance::Icon(icon) => window.set_cursor(winit::window::Cursor::Icon(icon)),
|
||||
|
@ -584,11 +667,13 @@ pub fn window_sync_system(windows: Res<WinitWindows>, view: View<(Entities, &Win
|
|||
}
|
||||
|
||||
if opts.min_size != last.min_size {
|
||||
window.set_min_inner_size(opts.min_size);
|
||||
let s = opts.min_size.map(|s| LogicalSize::new(s.x, s.y));
|
||||
window.set_min_inner_size(s);
|
||||
}
|
||||
|
||||
if opts.max_size != last.max_size {
|
||||
window.set_max_inner_size(opts.max_size);
|
||||
let s = opts.max_size.map(|s| LogicalSize::new(s.x, s.y));
|
||||
window.set_max_inner_size(s);
|
||||
}
|
||||
|
||||
if opts.theme != last.theme {
|
||||
|
@ -617,12 +702,6 @@ pub fn window_sync_system(windows: Res<WinitWindows>, view: View<(Entities, &Win
|
|||
window.set_window_level(opts.window_level);
|
||||
}
|
||||
|
||||
if opts.physical_window_menu_pos != last.physical_window_menu_pos && opts.physical_window_menu_pos.is_some() {
|
||||
let pos = opts.physical_window_menu_pos.unwrap();
|
||||
let pos = PhysicalPosition::new(pos.x, pos.y);
|
||||
window.show_window_menu(pos);
|
||||
}
|
||||
|
||||
if opts.physical_cursor_position != last.physical_cursor_position && opts.physical_cursor_position.is_some() {
|
||||
let pos = opts.physical_cursor_position.unwrap();
|
||||
let pos = PhysicalPosition::new(pos.x, pos.y);
|
|
@ -302,7 +302,8 @@ struct WrapUsage {
|
|||
meta_methods: Vec<MetaMethod>,
|
||||
skips: Vec<SkipType>,
|
||||
|
||||
extra_builds: Option<syn::Block>,
|
||||
extra_fields: Option<syn::Block>,
|
||||
extra_methods: Option<syn::Block>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for WrapUsage {
|
||||
|
@ -313,7 +314,8 @@ impl syn::parse::Parse for WrapUsage {
|
|||
override_name: None,
|
||||
auto_fields: vec![],
|
||||
auto_derives: vec![],
|
||||
extra_builds: None,
|
||||
extra_fields: None,
|
||||
extra_methods: None,
|
||||
auto_new: false,
|
||||
meta_methods: vec![],
|
||||
skips: vec![],
|
||||
|
@ -374,15 +376,19 @@ impl syn::parse::Parse for WrapUsage {
|
|||
s.meta_methods = meta_methods.into_iter().collect();
|
||||
}
|
||||
},
|
||||
"extra_fields" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
s.extra_fields = Some(input.parse::<syn::Block>()?);
|
||||
},
|
||||
"extra_methods" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
s.extra_methods = Some(input.parse::<syn::Block>()?);
|
||||
},
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(ident, "unknown wrapper command"));
|
||||
return Err(syn::Error::new_spanned(ident, format!("unknown wrapper command: '{}'", ident_str)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(block) = input.parse::<syn::Block>() {
|
||||
s.extra_builds = Some(block);
|
||||
}
|
||||
}
|
||||
|
||||
if s.auto_new && s.auto_fields.is_empty() {
|
||||
|
@ -405,7 +411,8 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
.unwrap_or_else(|| Ident::new(&format!("Lua{}", type_name), Span::call_site()));
|
||||
|
||||
let derive_idents_iter = input.auto_derives.iter();
|
||||
let extra_builds = input.extra_builds;
|
||||
let extra_fields = input.extra_fields;
|
||||
let extra_methods = input.extra_methods;
|
||||
|
||||
let field_get_set_pairs = input.auto_fields.iter().map(|i| {
|
||||
let is = i.to_string();
|
||||
|
@ -492,6 +499,8 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
impl mlua::UserData for #wrapper_typename {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
#(#field_get_set_pairs)*
|
||||
|
||||
#extra_fields
|
||||
}
|
||||
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
|
@ -499,7 +508,7 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
|
||||
#new_func_tokens
|
||||
#meta_methods_tokens
|
||||
#extra_builds
|
||||
#extra_methods
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
WindowMode = {
|
||||
WNDOWED = "windowed",
|
||||
BORDERLESS_FULLSCREEN = "borderless_fullscreen",
|
||||
SIZED_FULLSCREEN = "sized_fullscreen",
|
||||
FULLSCREEN = "fullscreen",
|
||||
}
|
||||
|
||||
CursorGrabMode = {
|
||||
NONE = "none",
|
||||
CONFINED = "confined",
|
||||
LOCKED = "locked",
|
||||
}
|
||||
|
||||
WindowTheme = {
|
||||
LIGHT = "light",
|
||||
DARK = "dark",
|
||||
}
|
||||
|
||||
WindowLevel = {
|
||||
ALWAYS_ON_BOTTOM = "always_on_bottom",
|
||||
NORMAL = "normal",
|
||||
ALWAYS_ON_TOP = "always_on_top",
|
||||
}
|
|
@ -38,7 +38,9 @@ pub enum Error {
|
|||
#[error("received nil value from Lua")]
|
||||
Nil,
|
||||
#[error("{0}")]
|
||||
Mlua(#[from] mlua::Error)
|
||||
Mlua(#[from] mlua::Error),
|
||||
#[error("unimplemented: {0}")]
|
||||
Unimplemented(String)
|
||||
}
|
||||
|
||||
/* impl Into<mlua::Error> for Error {
|
||||
|
@ -63,6 +65,10 @@ impl Error {
|
|||
pub fn type_mismatch(expected: &str, got: &str) -> Self {
|
||||
Self::TypeMismatch { expected: expected.into(), got: got.into() }
|
||||
}
|
||||
|
||||
pub fn unimplemented(msg: &str) -> Self {
|
||||
Self::Unimplemented(msg.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Name of a Lua function that is used to Reflect the Userdata, but without a value.
|
||||
|
|
|
@ -4,7 +4,7 @@ use lyra_ecs::ResourceObject;
|
|||
use lyra_reflect::{Reflect, TypeRegistry};
|
||||
use lyra_resource::gltf::Gltf;
|
||||
|
||||
use crate::{lua::{wrappers::{LuaGltfHandle, LuaActionHandler, LuaDeltaTime, LuaResHandleToComponent, LuaSceneHandle}, LuaContext, ReflectLuaProxy, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
|
||||
use crate::{lua::{wrappers::{LuaActionHandler, LuaDeltaTime, LuaGltfHandle, LuaResHandleToComponent, LuaSceneHandle, LuaWindow}, LuaContext, ReflectLuaProxy, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LyraEcsApiProvider;
|
||||
|
@ -16,6 +16,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
|
|||
world.register_lua_convert::<LuaDeltaTime>();
|
||||
world.register_lua_wrapper::<LuaSceneHandle>();
|
||||
world.register_lua_wrapper::<LuaActionHandler>();
|
||||
world.register_lua_wrapper::<LuaWindow>();
|
||||
|
||||
let mut registry = world.get_resource_mut::<TypeRegistry>().unwrap();
|
||||
|
||||
|
@ -39,11 +40,16 @@ impl ScriptApiProvider for LyraEcsApiProvider {
|
|||
fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
|
||||
let ctx = ctx.lock().unwrap();
|
||||
|
||||
// load window util
|
||||
let bytes = include_str!("../../../scripts/lua/window.lua");
|
||||
ctx.load(bytes).exec().unwrap();
|
||||
|
||||
let globals = ctx.globals();
|
||||
globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?;
|
||||
globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?;
|
||||
globals.set("SceneComponent", ctx.create_proxy::<LuaSceneHandle>()?)?;
|
||||
globals.set("ActionHandler", ctx.create_proxy::<LuaActionHandler>()?)?;
|
||||
globals.set("Window", ctx.create_proxy::<LuaWindow>()?)?;
|
||||
|
||||
let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?;
|
||||
globals.set("DeltaTime", dt_table)?;
|
||||
|
|
|
@ -18,7 +18,7 @@ wrap_lua_struct!(
|
|||
Mod(LuaVec2, f32),
|
||||
Eq, Unm, ToString
|
||||
),
|
||||
{
|
||||
extra_methods = {
|
||||
lua_vec_wrap_extension!(math::Vec2, LuaVec2);
|
||||
|
||||
methods.add_method_mut("move_by", |lua, this, vals: mlua::MultiValue| {
|
||||
|
@ -50,7 +50,7 @@ wrap_lua_struct!(
|
|||
Mod(LuaVec3, f32),
|
||||
Eq, Unm, ToString
|
||||
),
|
||||
{
|
||||
extra_methods = {
|
||||
lua_vec_wrap_extension!(math::Vec3, LuaVec3);
|
||||
|
||||
methods.add_method_mut("move_by", |lua, this, vals: mlua::MultiValue| {
|
||||
|
@ -83,7 +83,7 @@ wrap_lua_struct!(
|
|||
Mod(LuaVec4, f32),
|
||||
Eq, Unm, ToString
|
||||
),
|
||||
{
|
||||
extra_methods = {
|
||||
lua_vec_wrap_extension!(math::Vec4, LuaVec4);
|
||||
}
|
||||
);
|
||||
|
@ -101,7 +101,7 @@ wrap_lua_struct!(
|
|||
// __mul for LuaVec3 is manually implemented below since it doesn't return Self
|
||||
//Mul(LuaQuat, f32),
|
||||
),
|
||||
{
|
||||
extra_methods = {
|
||||
// manually implemented since Quat doesn't have a `new` function
|
||||
methods.add_function("new", |_, (x, y, z, w)| {
|
||||
Ok(Self(math::Quat::from_xyzw(x, y, z, w)))
|
||||
|
@ -184,7 +184,7 @@ wrap_lua_struct!(
|
|||
math::Transform,
|
||||
derives(PartialEq, Copy),
|
||||
metamethods(ToString, Eq),
|
||||
{
|
||||
extra_methods = {
|
||||
methods.add_function("default", |_, ()| {
|
||||
Ok(Self(math::Transform::default()))
|
||||
});
|
||||
|
|
|
@ -9,3 +9,6 @@ pub use asset::*;
|
|||
|
||||
mod delta_time;
|
||||
pub use delta_time::*;
|
||||
|
||||
mod window;
|
||||
pub use window::*;
|
|
@ -0,0 +1,326 @@
|
|||
use lyra_scripting_derive::wrap_lua_struct;
|
||||
|
||||
use lyra_game::winit::{WindowOptions, FullscreenMode, Theme, CursorGrabMode, WindowLevel};
|
||||
use mlua::IntoLua;
|
||||
|
||||
use crate::lyra_engine;
|
||||
use crate as lyra_scripting;
|
||||
|
||||
use super::super::Error;
|
||||
use super::LuaVec2;
|
||||
|
||||
use lyra_game::math::{Vec2, IVec2, UVec2};
|
||||
|
||||
wrap_lua_struct!(
|
||||
WindowOptions,
|
||||
//derives(Clone),
|
||||
name=LuaWindow,
|
||||
fields(focused),
|
||||
extra_fields={
|
||||
fields.add_field_method_get("window_mode", |lua, this| {
|
||||
let s = match &this.fullscreen_mode {
|
||||
FullscreenMode::Windowed => "windowed",
|
||||
FullscreenMode::BorderlessFullscreen => "borderless_fullscreen",
|
||||
FullscreenMode::SizedFullscreen => "sized_fullscreen",
|
||||
FullscreenMode::Fullscreen => "fullscreen"
|
||||
};
|
||||
|
||||
s.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("window_mode", |_, this, mode: String| {
|
||||
let mode = mode.as_str();
|
||||
|
||||
match mode {
|
||||
"windowed" => {
|
||||
this.fullscreen_mode = FullscreenMode::Windowed;
|
||||
},
|
||||
"borderless_fullscreen" => {
|
||||
this.fullscreen_mode = FullscreenMode::BorderlessFullscreen;
|
||||
},
|
||||
"sized_fullscreen" => {
|
||||
this.fullscreen_mode = FullscreenMode::SizedFullscreen;
|
||||
},
|
||||
"fullscreen" => {
|
||||
this.fullscreen_mode = FullscreenMode::Fullscreen;
|
||||
},
|
||||
_ => {
|
||||
return Err(mlua::Error::runtime(format!("invalid window mode {}", mode)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// nil if its unsupported
|
||||
fields.add_field_method_get("position", |lua, this| {
|
||||
this.position.map(|p| {
|
||||
LuaVec2(Vec2::new(p.x as f32, p.y as f32))
|
||||
}).into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("position", |_, this, pos: Option<LuaVec2>| {
|
||||
this.position = pos.map(|p| IVec2::new(p.x as i32, p.y as i32));
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("physical_size", |lua, this| {
|
||||
let p = this.physical_size();
|
||||
LuaVec2(Vec2::new(p.x as f32, p.y as f32))
|
||||
.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("physical_size", |_, this, size: LuaVec2| {
|
||||
this.set_physical_size(UVec2::new(size.x as _, size.y as _));
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("size", |lua, this| {
|
||||
LuaVec2(this.size())
|
||||
.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("size", |_, this, size: LuaVec2| {
|
||||
this.set_size(*size);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("decorated", |lua, this| {
|
||||
this.decorated.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("decorated", |_, this, val: bool| {
|
||||
this.decorated = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("maximized", |lua, this| {
|
||||
this.maximized.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("maximized", |_, this, val: bool| {
|
||||
this.maximized = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// returns `nil` if minimized state could not be determined
|
||||
fields.add_field_method_get("minimized", |lua, this| {
|
||||
this.minimized.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("minimized", |_, this, val: bool| {
|
||||
this.minimized = Some(val);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("resizable", |lua, this| {
|
||||
this.resizable.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("resizable", |_, this, val: bool| {
|
||||
this.resizable = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// returns `nil` if minimized state could not be determined
|
||||
fields.add_field_method_get("visible", |lua, this| {
|
||||
this.visible.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("visible", |_, this, val: bool| {
|
||||
this.visible = Some(val);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
//todo!("resize_increments");
|
||||
/* fields.add_field_method_get("resize_increments", |lua, this| {
|
||||
this.visible.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("resize_increments", |_, this, val: bool| {
|
||||
this.visible = Some(val);
|
||||
Ok(())
|
||||
}); */
|
||||
|
||||
fields.add_field_method_get("scale_factor", |lua, this| {
|
||||
this.scale_factor.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("scale_factor", |_, this, val: f64| {
|
||||
this.scale_factor = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("blur", |lua, this| {
|
||||
this.blur.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("blur", |_, this, val: bool| {
|
||||
this.blur = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// TODO: implement get/setting window icon
|
||||
// must have ResHandle<Image> exposed to Lua.
|
||||
fields.add_field_method_get::<_, bool>("cursor_appearance", |_, _| {
|
||||
Err(mlua::Error::external(Error::unimplemented("field 'cursor_appearance' is unimplemented")))
|
||||
});
|
||||
|
||||
fields.add_field_method_get("cursor_grab_mode", |lua, this| {
|
||||
let v = match &this.cursor.grab {
|
||||
CursorGrabMode::None => "none",
|
||||
CursorGrabMode::Confined => "confined",
|
||||
CursorGrabMode::Locked => "locked",
|
||||
};
|
||||
|
||||
v.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("cursor_grab_mode", |_, this, val: String| {
|
||||
let val = val.as_str();
|
||||
|
||||
let v = match val {
|
||||
"none" => CursorGrabMode::None,
|
||||
"confined" => CursorGrabMode::Confined,
|
||||
"locked" => CursorGrabMode::Locked,
|
||||
_ => {
|
||||
return Err(mlua::Error::runtime(format!("invalid cursor grab mode {}", val)));
|
||||
}
|
||||
};
|
||||
|
||||
this.cursor.grab = v;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("cursor_hittest", |lua, this| {
|
||||
this.cursor.hittest.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("cursor_hittest", |_, this, val: bool| {
|
||||
this.cursor.hittest = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("cursor_visible", |lua, this| {
|
||||
this.cursor.visible.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("cursor_visible", |_, this, val: bool| {
|
||||
this.cursor.visible = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
//todo!("window cursor");
|
||||
|
||||
fields.add_field_method_get("ime_allowed", |lua, this| {
|
||||
this.ime_allowed.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("ime_allowed", |_, this, val: bool| {
|
||||
this.ime_allowed = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("min_size", |lua, this| {
|
||||
this.min_size.map(|p| LuaVec2(p))
|
||||
.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("min_size", |_, this, size: Option<LuaVec2>| {
|
||||
this.min_size = size.map(|p| *p);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("max_size", |lua, this| {
|
||||
this.max_size.map(|p| LuaVec2(p))
|
||||
.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("max_size", |_, this, size: Option<LuaVec2>| {
|
||||
this.max_size = size.map(|p| *p);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("theme", |lua, this| {
|
||||
this.theme.as_ref().map(|t| match t {
|
||||
Theme::Light => "light",
|
||||
Theme::Dark => "dark"
|
||||
}).into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("theme", |_, this, theme: Option<String>| {
|
||||
let t = if let Some(theme) = theme.as_ref() {
|
||||
let theme = theme.as_str();
|
||||
|
||||
match theme {
|
||||
"light" => Some(Theme::Light),
|
||||
"dark" => Some(Theme::Dark),
|
||||
_ => {
|
||||
return Err(mlua::Error::runtime(format!("invalid theme {}", theme)));
|
||||
}
|
||||
}
|
||||
} else { None };
|
||||
|
||||
this.theme = t;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("title", |lua, this| {
|
||||
this.title.clone().into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("title", |_, this, title: String| {
|
||||
this.title = title;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("transparent", |lua, this| {
|
||||
this.transparent.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("transparent", |_, this, val: bool| {
|
||||
this.transparent = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// TODO: implement get/setting window icon
|
||||
// must have ResHandle<Image> exposed to Lua.
|
||||
fields.add_field_method_get::<_, bool>("window_icon", |_, _| {
|
||||
Err(mlua::Error::external(Error::unimplemented("field 'window_icon' is unimplemented")))
|
||||
});
|
||||
|
||||
fields.add_field_method_get("window_level", |lua, this| {
|
||||
let v = match &this.window_level {
|
||||
WindowLevel::AlwaysOnBottom => "always_on_bottom",
|
||||
WindowLevel::Normal => "normal",
|
||||
WindowLevel::AlwaysOnTop => "always_on_top",
|
||||
};
|
||||
|
||||
v.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("window_level", |_, this, val: String| {
|
||||
let window_level = val.as_str();
|
||||
|
||||
let l = match window_level {
|
||||
"always_on_bottom" => WindowLevel::AlwaysOnBottom,
|
||||
"normal" => WindowLevel::Normal,
|
||||
"always_on_top" => WindowLevel::AlwaysOnTop,
|
||||
_ => {
|
||||
return Err(mlua::Error::runtime(format!("invalid window level {}", window_level)));
|
||||
}
|
||||
};
|
||||
|
||||
this.window_level = l;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("occluded", |lua, this| {
|
||||
this.occluded().into_lua(lua)
|
||||
});
|
||||
|
||||
fields.add_field_method_get("cursor_position", |lua, this| {
|
||||
this.cursor_position().map(|p| LuaVec2(p))
|
||||
.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("cursor_position", |_, this, val: Option<LuaVec2>| {
|
||||
this.set_cursor_position(val.map(|p| *p));
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("physical_cursor_position", |lua, this| {
|
||||
this.physical_cursor_position().map(|p| LuaVec2(p))
|
||||
.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("physical_cursor_position", |_, this, val: Option<LuaVec2>| {
|
||||
this.set_physical_cursor_position(val.map(|p| p.as_dvec2()));
|
||||
Ok(())
|
||||
});
|
||||
},
|
||||
extra_methods={
|
||||
methods.add_method("is_mouse_inside", |lua, this, ()| {
|
||||
this.is_mouse_inside().into_lua(lua)
|
||||
});
|
||||
}
|
||||
);
|
Loading…
Reference in New Issue